在一上篇Jetton教学中,我们已经讨论过通过合约部署Jetton和收费铸造的部分,这一次我们的讨论焦点会放在如何令已发出的Jetton代币失效。因为TrumpVsHarris 的合约要求是败选一方的代币会失去价值,这可以通过下面三种方法达成:
- 直接销毁代币
- 锁定钱包,不让转帐
- 单纯移走池子
显然直接销毁代币是最强而有力的方法,令用户钱包的代币数直接归零,但是这在TON链上很难单靠智能合约实现,因为Jetton 的设计并不会由主合约记录所有的地址及对应的余额(和ERC20不同,在以太坊下,我们的智能合约需要一个映射(map)来保存所有用户的余额,并且可以无限增长,因为我们代币的持有者数量是无限的),故此,我们的Jetton主合约并不知道持币的地址,也很难统一处理。单纯移走池子也是一法,但这篇文章希望能探讨各种控制Jetton的特殊方法,例如合约锁仓、销毁代币等等,所以我们尝试通过在JettonWallet 合约和主合约上添加特殊的功能来实现令代币失效的效果。
快速检索
合約名稱 | 功能 |
TrumpVsHarris.tact 主合約 | 部署TrumpCoin 部署HarrisCoin 控制TrumpCoin 和 HarrisCoin 的鑄造價格 讓用戶可以進行鑄造 |
TrumpCoin.tact | TrumpCoin Jetton 合約 |
HarrisCoin.tact | HarrisCoin Jetton 合約 |
JettonWallet.tact | TrumpCoin 和 HarrisCoin 共用的Jetton錢包合約 |
添加启动功能
其中一个方法是加入启动功能,用户在铸造Jetton后并不能马上交易和转帐,要先通过主合约启动Jetton钱包后才可以执行各种操作。这里我们的做法是这样的。
// ============================================================ //
@interface("org.ton.jetton.wallet")
contract JettonDefaultWallet
{
const minTonsForStorage: Int = ton("0.019");
const gasConsumption: Int = ton("0.013");
balance: Int as coins = 0;
//加入activated,并预设为false
activated: bool = false;
owner: Address;
master: Address;
init(owner: Address, master: Address){
self.balance = 0;
self.owner = owner;
self.master = master;
}
receive(msg: TokenTransfer){
// 0xf8a7ea5
let ctx: Context = context(); // Check sender
require(ctx.sender == self.owner, "Invalid sender");
//每次Transfer 都会先检查 activated
require(activated, "Not activated yet")l
let final: Int =
(((ctx.readForwardFee() *
2) +
(2 *
self.gasConsumption)) +
self.minTonsForStorage) +
msg.forward_ton_amount; // Gas checks, forward_ton = 0.152
require(ctx.value > final, "Invalid value");
// Update balance
self.balance = self.balance - msg.amount;
加入activated 的变量后,钱包预设了不能转帐,同时这里不会影响铸造,因为MINT是通过TokenTransferInternal 的讯息来完成的。之后我们需要编写一个receiver 来处理activated 变量,这里的逻辑会是这样的:
主合約(TrumpVsHarris.tact)
receive("activateTrunpCoin"){
require( self.resultAnnounced , "no result yet");
require( self.trumpWon , "trump didn't win");
send(SendParameters{
to: self.trumpCoinAddress,
body: ActivateCoinInternal{owner: sender()}.toCell(),
value: self.MESSAGING_FEE + self.MESSAGING_FEE,
bounce: true,
mode: SendIgnoreErrors
}
);
}
在主合约中加入接收activateCoin 讯息的逻辑,在收到activateTrunpCoin 之后,主合约会先判断大选结果是否已经公布,然后再判断是Trump还是Harris 胜选,如果是Trump赢了的话,主合约便会发送一条ActivateCoinInternal讯息到代币合约(TrumpCoin.tact / HarrisCoin.tact),并附带owner 参数以找出对应的Jetton Wallet。
代幣合約(TrumpCoin.tact / HarrisCoin.tact)
receive(msg: ActivateCoinInternal){
let ctx: Context = context();
//只有主合约可以发送解锁指令
require(ctx.sender == self.master, "Invalid sender!");
let walletOwner = msg.owner;
send(SendParameters{
to: self.getWalletAddress(walletOwner),
body: "activate".asComment(),
value: self.MESSAGING_FEE,
bounce: true,
mode: SendIgnoreErrors
}
);
}
在代币合约中加入接收ActivateCoinInternal的逻辑,同时加入检查部分,确保只有主合约能发送相关指令,收到指令后代币合约再和JettonWallet 发送解锁讯息。
JettonWallet (Jetton.tact)
receive("activate"){
let ctx: Context = context();
//只有代币合约可以发送解锁指令
require(ctx.sender == self.master, "Invalid sender!");
self.activated = true;
}
在代币合约和钱包合约中都有 require(ctx.sender == self.master, “Invalid sender!”); 的代码,但这两个是不一样的,代币合约的master 是主合约的地址,而钱包合约中的master 则是代币合约。加入上面的代码后,用户便可以在大选结果公布后,和主合约发送解锁指令,之后便可以交易和转帐了。其实我们也可以在JettonWallet 中加入主合约的地址,这让就可以直接由主合约发送指令,但这会对JettonDefaultWallet的代码改动得比较多,因此我们决定使用上面的做法。