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

一个接口覆盖 BNB 链上所有 Flap 代币,不管它在哪:
- 内盘(联合曲线):从发射到卖出 8 亿枚为止。
- 外盘(PancakeSwap):毕业之后,在 Flap 迁移过去的池子里。
你只传代币地址;API 从 Flap 的 Portal 读取状态并自动路由,返回里的 venue 会标明是 flap_curve 还是 pancakeswap。税币不需要任何特殊处理,税已经算在报价里。
你需要什么
- Node.js 或 Python。
- 一个钱包私钥,里面有一点 BNB 链上的 BNB。
- 一个代币地址。从 flap.sh 任意代币页面 或 实时发射 里拿一个。
示例
- JavaScript
- Python
const { Wallet } = require("ethers");
// ─── 改这三行 ────────────────────────────────────────
const PRIVATE_KEY = "0xYOUR_PRIVATE_KEY";
const TOKEN = "0xTHE_TOKEN_ADDRESS";
const AMOUNT = "0.01"; // 买入:要花的 BNB。卖出:"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/bnb/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" ? "tokens" : data.receive);
// 本地签名,把签好的交易交给 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/bnb/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.name} (${data.symbol}) ${data.token} 花费 ${data.amountInFormatted} ${data.payWith}`);
} else {
console.log(`卖出 ${data.amountInFormatted} ${data.name} (${data.symbol}) ${data.token} 得到 ${data.quote.expectedOutFormatted} ${data.receive}`);
}
}
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 = "0.01" # 买入:要花的 BNB。卖出:"1000" 个代币,或 "100%"
ACTION = "buy" # "buy" 或 "sell"
SLIPPAGE = 5 # 滑点百分比,允许价格对你不利的幅度
# ──────────────────────────────────────────────────────
account = Account.from_key(PRIVATE_KEY) # 只用来签名,不连任何节点
res = requests.post(
"https://api.shrine.trade/bnb/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 = "tokens" if ACTION == "buy" else data["receive"]
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/bnb/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['name']} ({data['symbol']}) {data['token']} 花费 {data['amountInFormatted']} {data['payWith']}")
else:
print(f"卖出 {data['amountInFormatted']} {data['name']} ({data['symbol']}) {data['token']} 得到 {data['quote']['expectedOutFormatted']} {data['receive']}")
保存为 trade.py,然后:
pip install eth-account requests
python trade.py
API 返回什么,脚本就按顺序发什么。买入是一笔交易;某个代币的第一次卖出是两笔:一次性的授权,然后是卖出本身。
交易有防夹子保护:链上 minOut 保证成交不会比滑点允许的更差;签好的交易通过 /api/send 走我们自己的节点广播,不经过公共节点,你也不需要自己的 RPC。
有些 Flap 代币限制单个钱包在内盘的累计买入量。报价已经按你剩余的额度计算;超出上限的买入会按上限成交,多出的 BNB 在同一笔交易里退回。代币信息 能看到上限和你还剩多少。
卖出
把 ACTION 改成 "sell"。金额可以是代币数量,也可以是钱包余额的百分比:
AMOUNT | 卖出 |
|---|---|
"1000" | 1000 枚代币 |
"50%" | 一半余额 |
"100%" | 全部 |
只要 Flap 能在卖出时兑换,无论代币用什么计价,收到的都是 BNB。返回里的 receive 会告诉你最终收到的是什么资产。
税币
Flap 上大部分新币都是税币:创作者从每笔买卖里抽一个百分比。税已经算进报价,expectedOut 就是你实际到手的数量。在内盘,税是交易上的额外费用;毕业后,税从经过池子的代币里扣。返回里的 creatorTaxBps 是你这一边的税率。脚本不用改。
以 USD1、股票或其他资产计价的代币
Flap 允许发币时用 USD1、代币化股票(NVDAB、TSLAB、SPYB、XAUT 等,见 支持的计价资产)或另一个 Flap 代币计价,而不是 BNB。脚本同样不用改:买入时 AMOUNT 永远是 BNB,Flap 在交易内部把它换成计价资产。quoteAsset 说明代币用什么计价,payWith 说明从你钱包里出去的是什么。
如果 Flap 无法在买入时兑换(quote_asset_required),就直接用那个资产买:钱包里持有它,请求里加 payWithQuote: true,此时 amount 以该资产为单位。这类代币卖出时收到的是计价资产;任何代币都可以加 receiveQuote: true 来指定收计价资产。
出错了怎么办
| 错误 | 怎么办 |
|---|---|
insufficient_funds | 往钱包充 BNB:交易金额,再加一点 Gas。 |
insufficient_balance | 卖出数量超过持仓。"100%" 永远不会超。 |
token_not_found | Flap 的 Portal 不认识这个地址。确认填的是代币合约,不是 PancakeSwap 池子。 |
slippage_exceeded | 报价和发送之间价格跑远了。重试,热门代币把 SLIPPAGE 调高。 |
buy_quota_exhausted | 这个钱包在该代币内盘的买入额度已用完。 |
quote_asset_required | 代币的计价资产 Flap 无法用 BNB 兑换。持有该资产并设置 payWithQuote。 |
完整列表见 错误。
发送
POST https://api.shrine.trade/bnb/api/send 接收一笔签好的交易,通过我们的 BNB 链节点广播,所以脚本完全不需要 RPC。它只转发本 API 构建的交易:Flap 买卖、发币、授权和领取,其他一律以 not_ours 拒绝。
| 字段 | ||
|---|---|---|
signedTx | 0x 十六进制 | 签好的交易,即 wallet.signTransaction 的返回值。 |
wait | 布尔,可选 | 默认 true:等待回执。false 则节点一接受就返回哈希。 |
{ "hash": "0x…", "status": "landed", "blockNumber": 120162431, "gasUsed": 514058, "explorer": "https://bscscan.com/tx/0x…" }
status 为 landed(已上链)、reverted(已回滚)或 pending(未等待,或 45 秒内未打包,通常是 nonce 或 Gas 卡住)。
请求与返回
POST https://api.shrine.trade/bnb/api/local-trade
请求
| 字段 | ||
|---|---|---|
action | "buy" 或 "sell" | |
token | 地址 | Flap 代币。 |
amount | 十进制字符串 | 买入:要花的 BNB,与代币的计价资产无关。卖出:代币数量如 "1000",或余额百分比如 "50%"、"100%"。 |
from | 地址 | 你的钱包。 |
slippage | 数字,可选 | 百分比,默认 5。 |
payWithQuote | 布尔,可选 | 仅买入、且代币以 ERC-20 计价时:用该资产支付,amount 以它为单位。 |
receiveQuote | 布尔,可选 | 仅卖出、且代币以 ERC-20 计价时:收取该资产而不是 BNB。 |
返回:一个真实的例子,用 0.001 BNB 在内盘买入一个税币:
{
"action": "buy",
"protocol": "FLAP",
"token": "0x2f1952E00F6BA7655993120B8c41E5ab511e7777",
"name": "星际巨舰",
"symbol": "星际巨舰",
"status": "Tradable",
"venue": "flap_curve",
"amountIn": "1000000000000000",
"amountInFormatted": "0.001",
"quoteAsset": {
"address": "0x0000000000000000000000000000000000000000",
"symbol": "BNB",
"decimals": 18,
"isNative": true
},
"payWith": "BNB",
"receive": "TOKEN",
"quote": {
"expectedOut": "160442982903129676642962",
"expectedOutFormatted": "160442.982903129676642962",
"minOut": "152420833757973192810813",
"curveFeeBps": 125,
"creatorTaxBps": 1000,
"shrineFeeBps": 50,
"slippagePct": 5.0
},
"txs": [
{
"to": "0xFLAP_ROUTER",
"data": "0x…",
"value": "1000000000000000",
"gas": 521904,
"maxFeePerGas": "62500000",
"maxPriorityFeePerGas": "50000000",
"nonce": 5,
"chainId": 56,
"type": 2,
"description": "buy"
}
]
}
txs:按此顺序签名并广播的交易。三个以 wei 计的字段(value、maxFeePerGas、maxPriorityFeePerGas)是十进制字符串,其余是普通数字。description标明每笔交易:buy、sell,或一次性的approve/approve_quote。name/symbol:从代币合约读取,方便记录交易了什么。status:Flap 的状态,内盘为Tradable,毕业后为DEX。venue:flap_curve或pancakeswap。两种情况下请求完全一样。quoteAsset:代币的计价资产,BNB 或某个 ERC-20。payWith/receive:实际离开或进入你钱包的东西(BNB、计价资产符号,或TOKEN)。amountIn:你花出去的数量,基础单位:买入是 BNB 的 wei,卖出是代币。quote.expectedOut:扣除 Flap 费用、代币税和我们的费用之后你应到手的数量。minOut是它再减去滑点,也是合约强制执行的下限。两者都是基础单位,展示时用带*Formatted的字段。quote里的费用:curveFeeBps是 Flap 在内盘的费用(毕业后为 0,改为 PancakeSwap 的 LP 费),creatorTaxBps是代币在你这一边的税率,shrineFeeBps是我们的费用(50 = 0.5%)。