TON 代币:Jetton教学(三)

在一上篇Jetton教学中,我们已经讨论过通过合约部署Jetton和收费铸造的部分,这一次我们的讨论焦点会放在如何令已发出的Jetton代币失效。因为TrumpVsHarris 的合约要求是败选一方的代币会失去价值,这可以通过下面三种方法达成:

  1. 直接销毁代币
  2. 锁定钱包,不让转帐
  3. 单纯移走池子

显然直接销毁代币是最强而有力的方法,令用户钱包的代币数直接归零,但是这在TON链上很难单靠智能合约实现,因为Jetton 的设计并不会由主合约记录所有的地址及对应的余额(和ERC20不同,在以太坊下,我们的智能合约需要一个映射(map)来保存所有用户的余额,并且可以无限增长,因为我们代币的持有者数量是无限的),故此,我们的Jetton主合约并不知道持币的地址,也很难统一处理。单纯移走池子也是一法,但这篇文章希望能探讨各种控制Jetton的特殊方法,例如合约锁仓、销毁代币等等,所以我们尝试通过在JettonWallet 合约和主合约上添加特殊的功能来实现令代币失效的效果。

合約名稱功能
TrumpVsHarris.tact 主合約部署TrumpCoin
部署HarrisCoin
控制TrumpCoin 和 HarrisCoin 的鑄造價格
讓用戶可以進行鑄造
TrumpCoin.tactTrumpCoin Jetton 合約
HarrisCoin.tactHarrisCoin Jetton 合約
JettonWallet.tactTrumpCoin 和 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的代码改动得比较多,因此我们决定使用上面的做法。

Leave a Comment