在Howard Pen 和Tact Lang 文档例子中的Jetton 都是免费铸造的,今天我们在这例子上作出几点修改,把合约改成收费铸造,同时加入一些其他元素,以符合TrumpVsHarris的要求。
快速检索
智能合约结构
这个项目的智能合约结构是这样的:
合約名稱 | 功能 |
TrumpVsHarris.tact 主合約 | 部署TrumpCoin 部署HarrisCoin 控制TrumpCoin 和 HarrisCoin 的鑄造價格 讓用戶可以進行鑄造 |
TrumpCoin.tact | TrumpCoin Jetton 合約 |
HarrisCoin.tact | HarrisCoin Jetton 合約 |
JettonWallet.tact | TrumpCoin 和 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)。