买入与卖出
一个请求,返回未签名的交易,由你自己的脚本签名并发送。下面的代码是完整的:填上私钥、代币和金额,直接运行。


一个接口覆盖 Arc 上所有 Argus 和 LIFT 代币,不管它在哪里:
- Argus 代币,在带 Argus 手续费 hook 的 Uniswap v4 池。
- LIFT 代币,在带 LIFT hook 的 Uniswap v4 池。LIFT 早期的 v1 发射在普通 Uniswap v3 池里,返回为
UNISWAP。 - Arc 上任何以 USDC 计价的其他 Uniswap 池。
你传入代币地址;API 找到池子并据此路由,protocol 返回 ARGUS、LIFT 或 UNISWAP,venue 返回 uniswap_v4 或 uniswap_v3。Arc 上没有联合曲线,也没有毕业:代币从第一个区块起就一直在同一个池子里交易。
你需要
- Node.js 或 Python。
- 一个持有 Arc 链上 USDC 的钱包私钥。USDC 是这条链的原生代币:既付交易也付 gas。
- 一个代币地址。从 argus.world 或 lift.fun 取一个。
示例
- JavaScript
- Python
const { Wallet } = require("ethers");
// ─── 改这三项 ─────────────────────────────────────────
const PRIVATE_KEY = "0xYOUR_PRIVATE_KEY";
const TOKEN = "0xTHE_TOKEN_ADDRESS";
const AMOUNT = "5"; // 买入:要花的 USDC。卖出:"1000" 个代币,或 "100%"
const ACTION = "buy"; // "buy" 或 "sell"
const SLIPPAGE = 5; // %,允许价格对你不利变动的幅度
// ──────────────────────────────────────────────────────
async function main() {
const wallet = new Wallet(PRIVATE_KEY); // 只用来签名;不需要节点
const res = await fetch("https://api.shrine.trade/arc/api/local-trade", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({
action: ACTION,
token: TOKEN,
amount: AMOUNT,
slippage: SLIPPAGE,
from: wallet.address,
}),
});
const data = await res.json();
if (data.error) throw new Error(`${data.error}: ${data.message}`);
console.log("预计到手", data.quote.expectedOutFormatted,
ACTION === "buy" ? "个代币" : "USDC");
// 本地签名,把签好的字节交给 API 广播。私钥不离开这个脚本,
// 也不需要自己的 RPC。某个代币的第一笔交易前面会带一次性授权。
for (const tx of data.txs) {
const signed = await wallet.signTransaction({
to: tx.to, data: tx.data, value: BigInt(tx.value), gasLimit: BigInt(tx.gas),
maxFeePerGas: BigInt(tx.maxFeePerGas), maxPriorityFeePerGas: BigInt(tx.maxPriorityFeePerGas),
nonce: tx.nonce, chainId: tx.chainId, type: 2,
});
const sent = await (await fetch("https://api.shrine.trade/arc/api/send", {
method: "POST", headers: { "content-type": "application/json" },
body: JSON.stringify({ signedTx: signed }),
})).json();
if (sent.error) throw new Error(`${sent.error}: ${sent.message}`);
if (sent.status !== "landed") throw new Error(`${tx.description} ${sent.status}: ${sent.explorer}`);
console.log(tx.description + ": " + sent.explorer);
}
if (ACTION === "buy") {
console.log(`用 ${data.amountInFormatted} USDC 在 ${data.protocol} 买入了 ${data.name} (${data.symbol}) ${data.token}`);
} else {
console.log(`卖出 ${data.amountInFormatted} ${data.name} (${data.symbol}) ${data.token},得到 ${data.quote.expectedOutFormatted} USDC`);
}
}
main();
保存为 trade.js,然后:
npm install ethers
node trade.js
import requests
from eth_account import Account
# ─── 改这三项 ─────────────────────────────────────────
PRIVATE_KEY = "0xYOUR_PRIVATE_KEY"
TOKEN = "0xTHE_TOKEN_ADDRESS"
AMOUNT = "5" # 买入:要花的 USDC。卖出:"1000" 个代币,或 "100%"
ACTION = "buy" # "buy" 或 "sell"
SLIPPAGE = 5 # %,允许价格对你不利变动的幅度
# ──────────────────────────────────────────────────────
account = Account.from_key(PRIVATE_KEY) # 只用来签名;不需要节点
res = requests.post(
"https://api.shrine.trade/arc/api/local-trade",
json={"action": ACTION, "token": TOKEN, "amount": AMOUNT, "slippage": SLIPPAGE, "from": account.address},
)
data = res.json()
if "error" in data:
raise SystemExit(f"{data['error']}: {data['message']}")
unit = "个代币" if ACTION == "buy" else "USDC"
print("预计到手", data["quote"]["expectedOutFormatted"], unit)
# 某个代币的第一笔交易前面会带一次性授权;把返回的交易按顺序全部发送。
for tx in data["txs"]:
signed = Account.sign_transaction(
{
"to": tx["to"],
"data": tx["data"],
"value": int(tx["value"]),
"gas": tx["gas"],
"maxFeePerGas": int(tx["maxFeePerGas"]),
"maxPriorityFeePerGas": int(tx["maxPriorityFeePerGas"]),
"nonce": tx["nonce"],
"chainId": tx["chainId"],
},
account.key,
)
sent = requests.post("https://api.shrine.trade/arc/api/send", json={"signedTx": "0x" + signed.raw_transaction.hex().removeprefix("0x")}).json()
if "error" in sent:
raise SystemExit(f"{sent['error']}: {sent['message']}")
if sent["status"] != "landed":
raise SystemExit(f"{tx['description']} {sent['status']}: {sent['explorer']}")
print(tx["description"] + ":", sent["explorer"])
if ACTION == "buy":
print(f"用 {data['amountInFormatted']} USDC 在 {data['protocol']} 买入了 {data['name']} ({data['symbol']}) {data['token']}")
else:
print(f"卖出 {data['amountInFormatted']} {data['name']} ({data['symbol']}) {data['token']},得到 {data['quote']['expectedOutFormatted']} USDC")
保存为 trade.py,然后:
pip install eth-account requests
python trade.py
API 返回什么,脚本就按顺序发什么。第一次交易之后,一笔买入或卖出就只有一笔交易。
交易防夹子:链上的 minOut 保证成交不会比你的滑点允许的更差,并且签名后的交易通过 /api/send 从我们自己的节点广播,而不是公共节点,所以你不需要自己的 RPC。
Arc 上的每个池子都以 USDC 计价,兑换通过 Permit2(Uniswap 的共享授权合约)拉取你的 USDC。钱包第一次买入时,兑换前面会多出两笔授权:approve_quote_permit2(USDC 授权给 Permit2,只需一次)和 approve_quote_router(Permit2 授权给 router,每年一次)。之后每次买入都是一笔交易。某个代币的第一次卖出需要对该代币做同样的两笔授权,approve_permit2 和 approve_router。上面的脚本会处理全部这些。
两个平台都会对发射后最初时刻的买入收取一笔很高的额外费用,最高 99%,Argus 在几秒内、LIFT 在 6 个区块内衰减到零。它已经算在报价里,所以在这个窗口内买入会看到明显偏小的 expectedOut。返回里的 snipeTaxBps 是实际适用的费率;代币信息 会在交易前显示它,机器人可以等它归零。
卖出
把 ACTION 改成 "sell"。金额可以是代币数量,也可以是钱包余额的百分比:
AMOUNT | 卖出 |
|---|---|
"1000" | 1000 个代币 |
"50%" | 一半余额 |
"100%" | 全部 |
所得以 USDC 到账。
创作者税
大多数发射都带创作者税:发射平台的 hook 从每笔买卖里划一个百分比给代币创作者,发射时固定。税已经算在报价里,所以 expectedOut 就是你实际到手的数量。返回里的 creatorTaxBps 是你这一边的费率。脚本不需要任何改动。
出错了
| 错误 | 怎么办 |
|---|---|
insufficient_funds | 往钱包里加 Arc 链上的 USDC:交易金额再加一点 gas。 |
insufficient_balance | 卖出数量超过余额。"100%" 永远合适。 |
token_not_found | 不是 Argus 或 LIFT 的代币,也没有对 USDC 的 Uniswap 池持有它。确认是代币合约地址,不是池子地址。 |
slippage_exceeded | 报价到发送之间价格跑了。重试,火热的代币可以调高 SLIPPAGE。 |
amount_too_small | 兑换什么都换不回来。增加 AMOUNT。 |
完整列表见 错误。
发送
POST https://api.shrine.trade/arc/api/send 接收一笔签好名的交易,通过我们的 Arc 节点广播,所以脚本完全不需要 RPC。它只转发本 API 组装的交易(兑换及其授权),其他一切返回 not_ours 拒绝。
| 字段 | ||
|---|---|---|
signedTx | 0x 十六进制 | 签名后的交易,即 wallet.signTransaction 的返回值。 |
wait | 布尔,可选 | 默认 true:等待回执。false 则节点一接受就返回哈希。 |
{ "hash": "0x…", "status": "landed", "blockNumber": 22171031, "gasUsed": 214058, "effectiveGasPrice": "20300000000", "explorer": "https://explorer.arc.io/tx/0x…" }
status 为 landed、reverted 或 pending(没有等待,或 45 秒内没有上链,卡在 nonce 或 gas 上)。
请求与返回
POST https://api.shrine.trade/arc/api/local-trade
请求
| 字段 | ||
|---|---|---|
action | "buy" 或 "sell" | |
token | 地址 | 代币。 |
amount | 十进制字符串 | 买入时是要花的 USDC,例如 "5"。卖出时是代币数量如 "1000",或余额百分比如 "50%"、"100%"。 |
from | 地址 | 你的钱包。 |
slippage | 数字,可选 | 百分比。省略时为 5。 |
返回,真实的一次:一个从未交易过的钱包用 5 USDC 买入 Argus 代币,所以两笔一次性授权排在兑换前面:
{
"action": "buy",
"protocol": "ARGUS",
"token": "0xb242508c29959Aa165379BCfdB576070b24ee5a8",
"name": "Xeusthegreat",
"symbol": "XEUS",
"venue": "uniswap_v4",
"pool": "0x6299828f0bbf42c06d571279bf83d06d930bb5bc4a99b98793c6ff47141030b1",
"amountIn": "5000000",
"amountInFormatted": "5",
"quoteAsset": {
"address": "0x3600000000000000000000000000000000000000",
"symbol": "USDC",
"decimals": 6,
"isNative": false
},
"quote": {
"expectedOut": "1917103258230312676855547",
"expectedOutFormatted": "1917103.258230312676855547",
"minOut": "1821248095318797043012770",
"poolFeeBps": 100,
"creatorTaxBps": 300,
"snipeTaxBps": 0,
"shrineFeeBps": 75,
"slippagePct": 5.0
},
"txs": [
{
"to": "0x3600000000000000000000000000000000000000",
"data": "0x095ea7b3…",
"value": "0",
"gas": 80000,
"maxFeePerGas": "27842282458",
"maxPriorityFeePerGas": "2842282458",
"nonce": 50,
"chainId": 5042,
"type": 2,
"description": "approve_quote_permit2"
},
{
"to": "0x000000000022D473030F116dDEE9F6B43aC78BA3",
"data": "0x87517c45…",
"value": "0",
"gas": 80000,
"maxFeePerGas": "27842282458",
"maxPriorityFeePerGas": "2842282458",
"nonce": 51,
"chainId": 5042,
"type": 2,
"description": "approve_quote_router"
},
{
"to": "0x4fcA4a51Ab4F23A7447b3284fBd7D73289A89Fb1",
"data": "0x3593564c…",
"value": "0",
"gas": 600000,
"maxFeePerGas": "27842282458",
"maxPriorityFeePerGas": "2842282458",
"nonce": 52,
"chainId": 5042,
"type": 2,
"description": "buy"
}
]
}
txs:要签名并广播的交易,按此顺序。紧跟在同批授权后面的兑换带固定 gas 上限,因为授权还不存在时无法估算;单独的兑换带真实估算值加 20%。三个以 wei 计的字段(value、maxFeePerGas、maxPriorityFeePerGas)是十进制字符串以便通过 JSON,其余是普通数字。description标明每笔的用途:buy、sell,或一次性授权approve_quote_permit2、approve_quote_router、approve_permit2、approve_router。name/symbol:从代币合约读取,方便记录交易了什么。protocol/venue/pool:代币来自哪个平台,以及在哪里交易:uniswap_v4配池子 id,或uniswap_v3配池子地址。quoteAsset:在 Arc 上永远是 USDC,6 位小数。买入时amountIn是它的最小单位(5 USDC =5000000),卖出时是代币数量。quote.expectedOut:扣掉池子手续费、创作者税、狙击税和我们的费用之后你应到手的数量。minOut是它减去你的滑点,也是 router 强制执行的下限。两者都是最小单位,展示请用*Formatted版本。quote中的费用:poolFeeBps是池子自己的手续费,creatorTaxBps是代币在你这一边的税,snipeTaxBps是实际适用的早期买入附加税(发射窗口过后为 0,普通 Uniswap 池为null),shrineFeeBps是我们的(75 = 0.75%)。