TON 代币:Jetton教学(二)

在Howard Pen 和Tact Lang 文档例子中的Jetton 都是免费铸造的,今天我们在这例子上作出几点修改,把合约改成收费铸造,同时加入一些其他元素,以符合TrumpVsHarris的要求。

智能合约结构

这个项目的智能合约结构是这样的:

合約名稱功能
TrumpVsHarris.tact 主合約部署TrumpCoin
部署HarrisCoin
控制TrumpCoin 和 HarrisCoin 的鑄造價格
讓用戶可以進行鑄造
TrumpCoin.tact TrumpCoin Jetton 合約
HarrisCoin.tact HarrisCoin Jetton 合約
JettonWallet.tactTrumpCoin 和 HarrisCoin 共用的Jetton錢包合約

我们这次先讨论 TrumpVsHarris.tact 主合约的各个部分

部署TrumpCoin和 HarrisCoin

    //在这里我们会部署 Trump Coin 和 Harris Coin 的Jetton合约

    message DeployTrump{ content: Cell; }
    
    receive(msg: DeployTrump){
        self.requireOwner();
        require(self.trumpCoinAddress == self.owner, "contract deployed");
        let init: StateInit = initOf TrumpCoin(self.owner, myAddress(), msg.content);
        let address: Address = contractAddress(init);
        self.trumpCoinAddress = address;
        send(SendParameters{
            to: address,
            value: ton("0.1"),              // pay for message, the deployment and give some TON for storage
            mode: SendIgnoreErrors,
            code: init.code,                // attaching the state init will cause the message to deploy
            data: init.data
        });
    }

这边的部署合约不一定只由用户部署,也可以由其他合约部署。合约的初始代码和初始数据的组合被称为合约的状态初始化(stateInit)。当向合约发送任何消息时,我们可以通过指定消息的code和data 来附加其初始状态。如果合约尚未部署,这将部署该合约。如果合约已经部署,这些字段将被忽略。类似的代码在Jetton 转帐时也会出现,用户在接收Jetton时需要先部署对应的Jetton钱包合约,这一点我们在上一篇文章中已详细提及

铸造价格控制

由于这个项目是一个收费铸造项目,用户需要付费进行投票,每次投票后就能够获得对应的代币,如果他们选择的候选人最后胜出大选,理论上就可以获得受益,否则就会损失投入的全部资金。

在投票的价格上,我们的思路是最开始投票的人价格最低,往后的价格会随着获得的票数而增加,这个对应的就是热门的候选人代币赔率会较低。因此我们设计了这一段价格公式,当中的totalTonInTrump 和 totalTonInHarris 则是代表了两个候选人所获得的票数,亦即是压注在他们身上的Ton数量。


    //我们的代币是有价格的,而且价格会随投票数增加而上升,而且两边的价格并不一样。这里我们有两个调整价格的公式,一是每一投入一个TON,价格便会上调0.00001 TON (1/100,000)
    //然后,我们加入了multiplier,令价格进行非线性的增长。投入的TON在100个之后便会以倍数上升,超过100万之后会指数增长

    get fun priceForVote(pool: Int): Int {
        let target: Int = pool / 100000000;
        let digit: Int = 1;
        if (target <= 100) {
            digit = 1;
        }
        while (target > 100) {
            digit = digit + 1;
            target = target / 10;
        }
        let multiplier: Int = digit;
        if (digit > 5) {
            multiplier = digit * pow(10, digit - 6);
        }
        return (self.FIRST_PRICE + (pool / 100000) * multiplier);
    }

這個計算價格的方式只是用來作為例子,公式有一些問題,例如沒有辦法準確計算每票的價格,如果用戶一次過用1,000,000個TON來投票,他的所有票亦都會以第一票的價格來計算,另外價格上漲的方法也並不是很完美,不過作為例子這個就不作深究了。

投票部分

    //我们这边没有MINT,取而代之的是 vote
    receive("voteTrump"){
        self.vote(1);
    }

    receive("voteHarris"){
        self.vote(2);
    }

    fun vote(votingFor: Int) {
        let ctx: Context = context();
        let coinAddress = self.trumpCoinAddress;
        let voter = sender();
        self.requireNotStopped();
        let price: Int = self.priceForVote(self.totalTonInTrump);
        if (votingFor == 2) {
            //vote for harris
            price = self.priceForVote(self.totalTonInHarris);
            coinAddress = self.harrisCoinAddress;
        }
        let gastAfterValue = ctx.value - self.GAS_THRESHOLD;
        require(gastAfterValue >= price, "not enough ton to vote");

        if (votingFor == 1) {
            self.totalTonInTrump = self.totalTonInTrump + gastAfterValue
        }
        else{
            self.totalTonInHarris = self.totalTonInHarris + gastAfterValue
        }

        let numberOfVote = gastAfterValue / price * ton("1");
        // the only way to mint new coins
        send(SendParameters{
                to: coinAddress,
                body: Mint{amount: numberOfVote, receiver: voter}.toCell(),
                value: self.MESSAGING_FEE+self.MESSAGING_FEE,
                bounce: true,
                mode: SendIgnoreErrors
            }
        );
    }

这边的代码也非常简单,首先用户向智能合约发送讯息,选择投票给谁(voteTrump / voteHarris)。合约收到讯息之后就会读取用户信息中TON 的数量,我们在合约中减去 Gas 的费用之后,再把(TON÷票数的价格),得出用户所得的代币后,更新累积票数(self.totalTonInTrump / self.totalTonInHarris),然后再发送铸造讯息到代币合约(Mint)。

Leave a Comment