What Solana's Transfer Fee Extension Taught Me About Trustless Payments
What Solana’s Transfer Fee Extension Taught Me About Trustless Payments
Solana 的转账手续费扩展教会了我什么:关于无需信任的支付
Every web2 payment flow I’ve ever shipped has the same shape: a request hits my server, my server calculates the amount and fees, and then my server updates the database. The fee only exists because my code enforces it. If someone finds a way around my API, say, using a direct database write, or a race condition, or even a webhook that never fires, then the fee just doesn’t happen. 我曾经发布过的每一个 Web2 支付流程都有着相同的模式:请求到达我的服务器,服务器计算金额和手续费,然后更新数据库。手续费之所以存在,仅仅是因为我的代码在强制执行它。如果有人找到了绕过我 API 的方法,比如直接写入数据库、利用竞态条件,甚至是 webhook 未能触发,那么手续费就不会产生。
In Epoch 2 of #100DaysOfSolana, I built a token where this entire problem disappears. It disappears, not because I wrote better backend code, but because there was no backend to bypass. 在 #100DaysOfSolana 的第二阶段,我构建了一个代币,彻底解决了这个问题。它之所以消失,不是因为我写出了更好的后端代码,而是因为根本没有后端可以绕过。
The Web2 Way: Fees Live in Application Code
Web2 方式:手续费存在于应用代码中
Think about how a typical marketplace fee works. A buyer pays $100, your Stripe webhook fires, your server calculates a 5% platform fee, and your server moves $95 to the seller’s payout and the $5 fee to your treasury. That logic lives entirely in your application layer. It is correct as long as nobody modifies to your database directly, nobody manipulates your webhook, and your cron job never double-fires. That’s a lot of “as long as.” 想想典型的市场手续费是如何运作的。买家支付 100 美元,你的 Stripe webhook 触发,服务器计算出 5% 的平台手续费,然后将 95 美元转给卖家,将 5 美元转入你的金库。这种逻辑完全存在于你的应用层。只要没有人直接修改你的数据库,没有人篡改你的 webhook,且你的定时任务(cron job)从不重复触发,它就是正确的。这其中有太多的“只要”。
How Solana Does It: Baking the Fee into the Currency Itself
Solana 的方式:将手续费植入货币本身
Solana’s Token Extensions Program (Token-2022) lets you attach a TransferFeeConfig directly to a token’s mint account. Once it’s there, the fee isn’t a suggestion your application makes, but a rule the network itself enforces on every single transfer, for every wallet, FOREVER.
Solana 的代币扩展程序(Token-2022)允许你直接将 TransferFeeConfig(转账手续费配置)附加到代币的铸造账户上。一旦配置完成,手续费就不再是你应用程序的一个“建议”,而是网络本身对每一笔转账、每一个钱包强制执行的规则,且永久有效。
Here’s the exact command I used to create a mint with a 1% fee and a fee cap: 以下是我用来创建 1% 手续费且设有上限的代币铸造命令:
spl-token create-token \
--program-id TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb \
--transfer-fee-basis-points 100 \
--transfer-fee-maximum-fee 1000000 \
--decimals 6
No webhook. No cron job. No server at all. The 100 basis points (1%) is stored in the mint’s own account data, and the Token-2022 program checks it on every transfer instruction before the transaction is even allowed to land. 没有 webhook,没有定时任务,完全不需要服务器。这 100 个基点(1%)存储在铸造账户的数据中,Token-2022 程序会在每一条转账指令执行前进行检查,只有通过检查,交易才会被允许上链。
Watching It Actually Refuse to Be Bypassed
亲眼见证它拒绝被绕过
The part that changed how I think about this wasn’t creating the mint, but it was when I tried to move tokens around it. When I sent 1,000 tokens to a second wallet, I had to tell the CLI what fee I expected: 改变我认知的并不是创建代币的过程,而是当我尝试转账时。当我向第二个钱包发送 1,000 个代币时,我必须在 CLI 中告知我预期的手续费:
spl-token transfer $MINT 1000 $RECIPIENT --expected-fee 10
If that number doesn’t match what the protocol calculates, the transfer fails outright. There’s no code path where the fee “doesn’t happen.” The 10 tokens land in a withheld balance sitting inside the recipient’s own token account, visible, auditable, and untouched until the mint’s withdraw authority explicitly harvests it: 如果这个数字与协议计算的结果不符,转账会直接失败。不存在任何代码路径能让手续费“不产生”。这 10 个代币会进入接收者代币账户中的预扣余额(withheld balance),它是可见、可审计的,并且在铸造方的提取权限(withdraw authority)明确提取之前,这些代币不会被触动:
spl-token withdraw-withheld-tokens $MY_TOKEN_ACCOUNT $RECIPIENT_TOKEN_ACCOUNT
I could see the withheld amount sitting there before I swept it. In Web2 terms, it’s like the database enforcing your business rule for you at the storage engine level, instead of trusting your application code to get it right every time. 在提取之前,我可以看到预扣金额就在那里。用 Web2 的术语来说,这就像是数据库在存储引擎层面为你强制执行业务规则,而不是寄希望于你的应用代码每次都能不出错。
Outside the Theory
超越理论
This isn’t just a devnet toy “in theory”. There’s a meme-adjacent token called BERN, which uses the Transfer Fee extension so that every transfer automatically skims a percentage split between burning BONK, burning BERN, and rewarding holders. Regulated stablecoin issuers also use the same Token-2022 toolbox for very different reasons. Another interesting use case is Paxos, where they specifically enabled the Permanent Delegate extension on their USDP stablecoin so it can claw back funds used for illegal purposes, meeting strict regulatory requirements it operates under. Same program, wildly different use cases, both enforced at the protocol layer instead of the application layer. 这不仅仅是“理论上”的开发网玩具。有一个名为 BERN 的模因代币,它使用了转账手续费扩展,使得每一笔转账都会自动扣除一定比例,用于销毁 BONK、销毁 BERN 以及奖励持有者。受监管的稳定币发行商也出于完全不同的原因使用相同的 Token-2022 工具箱。另一个有趣的用例是 Paxos,他们在 USDP 稳定币上专门启用了“永久代理”(Permanent Delegate)扩展,以便能够追回用于非法目的的资金,从而满足其运营所处的严格监管要求。相同的程序,截然不同的用例,两者都是在协议层而非应用层强制执行的。
The Best Part Yet
最棒的部分
I stacked the Transfer Fee extension with an Interest-Bearing rate on the same mint later in the week, and both behaviors lived independently in the same account’s data. The fee deducts on transfer, and the interest accrues on display; neither one needs to know the other exists. Composability at the protocol level means that, I get a “middleware” for my currency, without writing a single line of middleware. Cool. 本周晚些时候,我在同一个代币上叠加了“计息”(Interest-Bearing)扩展,这两种行为在同一个账户数据中独立运行。转账时扣除手续费,显示时计算利息;两者互不干扰。协议层面的可组合性意味着,我无需编写任何一行中间件代码,就能为我的货币获得“中间件”功能。太酷了。
The Way Forward
前进的方向
If a fee genuinely could not be bypassed, no matter who touched the raw ledger, what would you build with that guarantee? A treasury that funds itself? Or a royalty stream that survives even if the marketplace’s website goes down? Tell me the fee-based idea you’d never trust a Web2 backend to enforce correctly. 如果手续费真的无法被绕过,无论谁触碰原始账本,你将利用这一保证构建什么?一个能够自我融资的金库?还是一个即使市场网站宕机也能持续存在的版税流?告诉我那些你绝不敢信任 Web2 后端能正确执行的手续费创意吧。