一、说明
在TON区块链上创建、开发和部署智能合约,需要使用FunC编程语言和TON虚拟机(TVM)。在这篇文章中,我们将告诉你如何在TON上创建你的第一个可替代代币(Jetton)。
目前jetton合约部署可以通过两种方式:
第一种:直接通过TON官方提供的jetton minter铸造界面,一键发币部署。提供jetton的相关元数据信息后提交mint请求即可。
第二种:通过FUNC合约源码部署jetton,通过该方式可以高度灵活定制自己的jetton功能需求。按照预定的需求设计开发FUNC合约,build构建和test调试完成后即可deploy部署合约到TON主网。同步部署jetton wallet合约以适配jetton minter合约。这样就可以实现jetton代币的灵活定制和合约交互。
二、以下是上述两种方法的详细操作流程
方法 1:使用浏览器部署标准 Jetton
如果您想使用标准 Jetton 代码,这是迄今为止最简单的选择。您无需在机器上安装任何工具,只需打开 Web 浏览器,在 HTML 表单中填写有关您的令牌的一些数据,然后单击部署。
指示:
-
确保您拥有一个 TON 钱包,余额至少为 0.25 TON。支持的钱包包括TonHub和Chrome 扩展程序。
-
使用您的网络浏览器打开部署表单的站点:https ://jetton.live
安全声明:该表单是开源的,由GitHub Pages提供
-
在表格中填写有关您的 Jetton 的信息 – 选择名称、股票代码和图像。
-
点击“连接钱包”按钮,连接您的钱包。
-
在您的钱包中部署并批准部署交易。
-
一旦代币被部署,部署的钱包将收到所有已铸造的代币。
方法 2:编辑 Jetton 代码以添加自定义令牌行为
这要复杂得多,并且允许您将 Jetton 的实际行为更改为您想要使用FunC语言编程的任何自定义行为。例如,假设您想要一个特殊的 Jetton,每次在用户之间转移时都会向某个地址支付 1% 的费用。由于此行为与标准不同,因此对于此选项,您需要在您的机器上安装 FunC 编译器。
注意:该项目基于tonstarter-contracts repo,如果您需要更多帮助,请咨询它。
指示:
-
确保您拥有tonstarter-contracts repo 中描述的所有“依赖项和要求”。
-
Git 将 repo 克隆到本地,并将目录重命名为您自己的项目名称。
-
在根 repo 目录中,在终端中运行
npm install
-
编辑智能合约源文件来实现您的新自定义行为,它们在这里:
contracts/*.fc
-
完成编码后,通过在根 repo 目录中运行来构建项目
npm run build
-
如果您想在本地测试代码,请在此处实现 TypeScript 单元测试:
test/*.spec.ts
-
测试准备就绪后,在根 repo 目录中运行它们
npm run test
-
jettonParams
在中编辑您的代币元数据(如名称和股票代码)build/jetton-minter.deploy.ts
-
准备至少0.25 TON作为部署费用。
-
要部署令牌,请在根 repo 目录中运行
npm run deploy
并按照屏幕上的说明进行操作。
Jetton 元数据字段最佳实践
-
Jetton 名称- 例如:
Bitcoin Cash
通常为 1-3 个单词,未缩写的项目名称,每个单词大写,中间留有空格。我们的运行示例是bitcoincash.org项目,它是比特币代币的一个分支。 -
Jetton 符号- 例如:通常为 3-5 个大写字符,是代币的货币符号。显示
BCH
代币余额时,通常会在金额旁边显示。在以币代码列出的交易所也会显示。 -
小数- 例如:
9
您的代币的小数精度(TON 默认为 9)。区块链将浮点数(如 1.2345)存储为具有给定精度的整数。在 9 个小数精度下,余额 1.2345 BCH 将被编码为 1234500000,可编码的最小余额为 0.000000001 BCH,编码为 1。余额 1 BCH 被编码为 1000000000。 -
要铸造的代币- 例如:
21,000,000
要铸造并发送到您的钱包地址的初始代币数量(浮点数)。在我们的示例中,让我们铸造整个供应量(如比特币),即 2100 万枚。请注意,此处的值是浮点数,未根据上一个字段中的小数精度进行编码。因此,对于 21,000,000 BCH,我们将输入 21000000,而不是 210000000000000000。 -
描述- 例如:
Low fee peer-to-peer electronic cash alternative to Bitcoin
可选的自由格式句子,解释您的项目。这部分可以留空。其目的是提供除名称之外的更多项目背景细节。不要让这部分太长(超过一句话),因为它存储在链上,成本可能很高。 -
Jetton 徽标 URI – 例如:
https://bitcoincash-example.github.io/website/logo.png
具有透明背景的 256×256 像素 PNG 代币徽标图像的 URL。请注意,此徽标不是不可变的(与其他字段不同),并且可以在项目的未来进行更改,因为其更新不会对用户构成安全风险。它应该放在可以让多个维护者轻松访问的托管上,这使得 GitHub Pages 成为一个很好的解决方案,因为它可以支持多个社区贡献者和 PR。最佳做法是这样的:-
为您的项目创建一个新的免费 GitHub 组织,您可以按照此处的说明进行操作。在我们的示例中,我们创建了
bitcoincash-example
您可以在此处看到的组织。 -
在这个新的组织下,创建一个名为 的新公共存储库,您可以按照此处的
website
说明进行操作。在我们的示例中,您可以在此处看到存储库。 -
将您的 PNG 图像上传到此存储库并在其上启用 GitHub Pages,您可以按照此处和此处的说明进行操作。结果应该是一个实时网站,如https://bitcoincash-example.github.io/website/logo.png,您的图像托管于此。
-
如果您负担得起,我们建议为您的项目购买一个自定义域名,例如
bitcoincash.org
。使用任何域名卖家,例如Google Domains或GoDaddy 。然后,将您的自定义域名连接到上一步中的存储库,您可以按照此处的说明进行操作。 -
如果您有自定义域名,则您的图片 URL 应该
https://bitcoincash.org/logo.png
代替该github.io
URL。这将消除对 GitHub 的任何未来依赖,并允许您在将来切换托管,这是一个不错的选择。
-
-
这些元数据存储在哪里? – Jetton 标准支持将元数据存储在链上或链下 URL(托管在某处的 JSON 文件)中。我们认为最佳做法是将元数据存储在链上。为什么?让我们探索替代方案:
那么 Jetton Logo URI 呢?如果它存储在网站上,它不能更改吗?是的,它可以更改,这是一个功能。我们相信,徽标可以进行品牌重塑,而不会给用户带来风险。中本聪在编写初始代码时并没有设计比特币的当前徽标。
为什么我在钱包或区块浏览器中看不到元数据?有些工具尚不支持安全的链上元数据标准。请打开这些工具的问题以修复并显示链上元数据,该元数据受官方标准支持,是发布 Jetton 的安全方式。
-
链上– 链上数据是不可变的,用户可以保证符号等重要字段不会在未经他们同意的情况下发生变化。链上数据也保证始终可用。此部署器始终将元数据存储在链上。
-
链下 IPFS (
ipfs://
URL) – IPFS 数据是不可变的,因此它和链上数据一样安全。但 IPFS 数据不能保证始终可用。可用性取决于是否有人愿意固定数据(类似于在 torrent 中播种)。如果此人破产或遭遇停机,代币元数据就会消失。在我们看来,这是一种不必要的风险。 -
链下网站(
https://
URL)——这是迄今为止最糟糕的选择。网站所有者可以在未经用户同意的情况下更改元数据(如果网站被黑客入侵,则不一定是故意的)。该网站也可能被关闭,元数据也会消失。用户永远不应该投资以这种方式存储元数据的代币。
-
三、完整版本FUNC合约构建部署核心代码
1. jetton代币铸造功能接口
() mint_tokens(slice to_address, cell jetton_wallet_code, int amount, cell master_msg) impure {
cell state_init = calculate_jetton_wallet_state_init(to_address, my_address(), jetton_wallet_code);
slice to_wallet_address = calculate_jetton_wallet_address(state_init);
var msg = begin_cell()
.store_uint(0x18, 6)
.store_slice(to_wallet_address)
.store_coins(amount)
.store_uint(4 + 2 + 1, 1 + 4 + 4 + 64 + 32 + 1 + 1 + 1)
.store_ref(state_init)
.store_ref(master_msg);
send_raw_message(msg.end_cell(), 1); ;; pay transfer fees separately, revert on errors
}
2. jetton 转账功能函数接口
() send_tokens (slice in_msg_body, slice sender_address, int msg_value, int fwd_fee) impure {
int query_id = in_msg_body~load_uint(64);
int jetton_amount = in_msg_body~load_coins();
slice to_owner_address = in_msg_body~load_msg_addr();
force_chain(to_owner_address);
(int balance, slice owner_address, slice jetton_master_address, cell jetton_wallet_code) = load_data();
balance -= jetton_amount;
throw_unless(705, equal_slices(owner_address, sender_address));
throw_unless(706, balance >= 0);
cell state_init = calculate_jetton_wallet_state_init(to_owner_address, jetton_master_address, jetton_wallet_code);
slice to_wallet_address = calculate_jetton_wallet_address(state_init);
slice response_address = in_msg_body~load_msg_addr();
cell custom_payload = in_msg_body~load_dict();
int forward_ton_amount = in_msg_body~load_coins();
throw_unless(708, slice_bits(in_msg_body) >= 1);
slice either_forward_payload = in_msg_body;
var msg = begin_cell()
.store_uint(0x18, 6)
.store_slice(to_wallet_address)
.store_coins(0)
.store_uint(4 + 2 + 1, 1 + 4 + 4 + 64 + 32 + 1 + 1 + 1)
.store_ref(state_init);
var msg_body = begin_cell()
.store_uint(op::internal_transfer(), 32)
.store_uint(query_id, 64)
.store_coins(jetton_amount)
.store_slice(owner_address)
.store_slice(response_address)
.store_coins(forward_ton_amount)
.store_slice(either_forward_payload)
.end_cell();
msg = msg.store_ref(msg_body);
int fwd_count = forward_ton_amount ? 2 : 1;
throw_unless(709, msg_value >
forward_ton_amount +
;; 3 messages: wal1->wal2, wal2->owner, wal2->response
;; but last one is optional (it is ok if it fails)
fwd_count * fwd_fee +
(2 * gas_consumption + min_tons_for_storage));
;; universal message send fee calculation may be activated here
;; by using this instead of fwd_fee
;; msg_fwd_fee(to_wallet, msg_body, state_init, 15)
send_raw_message(msg.end_cell(), 64); ;; revert on errors
save_data(balance, owner_address, jetton_master_address, jetton_wallet_code);
}
3. jetton燃烧通缩功能接口函数:
() burn_tokens (slice in_msg_body, slice sender_address, int msg_value, int fwd_fee) impure {
;; NOTE we can not allow fails in action phase since in that case there will be
;; no bounce. Thus check and throw in computation phase.
(int balance, slice owner_address, slice jetton_master_address, cell jetton_wallet_code) = load_data();
int query_id = in_msg_body~load_uint(64);
int jetton_amount = in_msg_body~load_coins();
slice response_address = in_msg_body~load_msg_addr();
;; ignore custom payload
;; slice custom_payload = in_msg_body~load_dict();
balance -= jetton_amount;
throw_unless(705, equal_slices(owner_address, sender_address));
throw_unless(706, balance >= 0);
throw_unless(707, msg_value > fwd_fee + 2 * gas_consumption);
var msg_body = begin_cell()
.store_uint(op::burn_notification(), 32)
.store_uint(query_id, 64)
.store_coins(jetton_amount)
.store_slice(owner_address)
.store_slice(response_address)
.end_cell();
var msg = begin_cell()
.store_uint(0x18, 6)
.store_slice(jetton_master_address)
.store_coins(0)
.store_uint(1, 1 + 4 + 4 + 64 + 32 + 1 + 1)
.store_ref(msg_body);
send_raw_message(msg.end_cell(), 64);
save_data(balance, owner_address, jetton_master_address, jetton_wallet_code);
}
至此,完成TON电报链jetton合约开发详细操作流程及Func源代码构建部署教程所有操作流程。
添加VX或者telegram获取全程线上免费指导
评论前必须登录!
注册