Skip to content

TEP: 74 - 同质化代币(Jettons)标准

  • TEP: 74
  • 标题: 同质化代币(Jettons)标准
  • 状态: 活跃
  • 类型: 合约接口
  • 作者: EmelyanenkoK, Tolya
  • 创建日期: 2022年3月12日
  • 取代: -
  • 被取代: -

摘要

这是一个关于 Jettons(TON 同质化代币)的标准接口。

动机

标准接口将大大简化不同代币化资产的交互和展示。

Jetton 标准描述了:

  • Jetton 的转移方式。
  • 获取给定 Jetton 资产的常见信息(如名称、流通供应量等)的方式。

指南

有用的链接

  1. Jetton 实现参考
  2. Jetton 部署器
  3. FunC Jetton 课程 (英文/俄文)

规范

在本文中,我们使用大写的 “Jetton” 作为同类型代币的整体标识,而小写的 “jetton” 作为某种类型代币数量的标识。

Jettons 组织如下:每个 Jetton 都有一个主智能合约,用于铸造新的 jettons,记录流通供应量并提供常见信息。

同时,每个用户拥有的 jettons 数量信息以去中心化的方式存储在各自(每个所有者)的智能合约中,称为 “jetton-wallets”。

例如:如果你发行了一个流通供应量为 200 jetton 的 Jetton,这些 jetton 由 3 个人拥有,那么你将部署 4 个合约:1 个 Jetton 主合约和 3 个 jetton-wallets。

Jetton 钱包智能合约

必须实现:

内部消息处理程序

1. transfer

请求

入站消息的 TL-B 方案:

transfer#0f8a7ea5 query_id:uint64 amount:(VarUInteger 16) destination:MsgAddress
                 response_destination:MsgAddress custom_payload:(Maybe ^Cell)
                 forward_ton_amount:(VarUInteger 16) forward_payload:(Either Cell ^Cell)
                 = InternalMsgBody;

query_id - 任意请求编号。

amount - 转移的 jettons 数量,以基本单位表示。

destination - jettons 新所有者的地址。

response_destination - 用于发送成功转移确认和剩余入站消息 Toncoins 的地址。

custom_payload - 可选的自定义数据(由发送者或接收者 jetton 钱包用于内部逻辑)。

forward_ton_amount - 发送到目标地址的 nanotons 数量。

forward_payload - 应发送到目标地址的可选自定义数据。

应被拒绝如果:

  1. 消息不是来自所有者。
  2. 发送者钱包中没有足够的 jettons。
  3. 没有足够的 TON(考虑到 jetton 自己的存储费指南和操作成本)来处理操作,部署接收者的 jetton-wallet 并发送 forward_ton_amount
  4. 处理请求后,接收者的 jetton-wallet 必须 至少发送 in_msg_value - forward_ton_amount - 2 * max_tx_gas_price - 2 * fwd_feeresponse_destination 地址。 如果发送者 jetton-wallet 无法保证这一点,它必须立即停止执行请求并抛出错误。 max_tx_gas_price 是 FT 栖息链的最大交易 gas 限制的 Toncoins 价格。对于基础链,可以从 ConfigParam 21gas_limit 字段中获取。fwd_fee 是转移请求的转发费,可以通过解析转移请求消息获取。

否则应做:

  1. 减少发送者钱包中的 jetton 数量 amount 并发送消息以增加接收者钱包中的 jetton 数量(并可选地部署它)。
  2. 如果 forward_amount > 0,确保接收者的 jetton-wallet 发送消息到 destination 地址,附带 forward_amount nanotons,并具有以下布局: TL-B 方案:
transfer_notification#7362d09c query_id:uint64 amount:(VarUInteger 16)
                              sender:MsgAddress forward_payload:(Either Cell ^Cell)
                              = InternalMsgBody;

query_id 应与请求的 query_id 相等。

amount 是转移的 jettons 数量。

sender 是转移 jettons 的前所有者的地址。

forward_payload 应与请求的 forward_payload 相等。

如果 forward_amount 等于零,则不应发送通知消息。

  1. 接收者的钱包应将入站消息币的所有剩余部分发送到 response_destination,具有以下布局: TL-B 方案:excesses#d53276db query_id:uint64 = InternalMsgBody;query_id 应与请求的 query_id 相等。
forward_payload 格式

如果你想在 forward_payload 中发送一个简单的评论,那么 forward_payload 必须以 0x00000000(32 位无符号整数等于零)开头,评论包含在 forward_payload 的剩余部分中。

如果评论不以字节 0xff 开头,则评论是一个文本评论;它可以“按原样”显示给钱包的最终用户(在过滤无效和控制字符并检查其是否为有效的 UTF-8 字符串后)。 例如,用户可以在这个文本字段中指明简单转移的目的(“用于咖啡”)。

另一方面,如果评论以字节 0xff 开头,则剩余部分是一个“二进制评论”,不应作为文本显示给最终用户(仅在必要时作为十六进制转储显示)。 “二进制评论”的预期用途是,例如,包含商店中支付的购买标识符,由商店的软件自动生成和处理。

如果 forward_payload 包含与目标智能合约(例如,与 DEX)的交互的二进制消息,则没有前缀。

这些规则与从常规钱包简单发送 Toncoins 时的有效载荷格式相同(智能合约指南:内部消息,第 3 节)。

2. burn

请求

入站消息的 TL-B 方案:

burn#595f07bc query_id:uint64 amount:(VarUInteger 16)
              response_destination:MsgAddress custom_payload:(Maybe ^Cell)
              = InternalMsgBody;

query_id - 任意请求编号。

amount - 烧毁的 jettons 数量。

response_destination - 用于发送成功烧毁确认和剩余入站消息币的地址。

custom_payload - 可选的自定义数据。

应被拒绝如果:

  1. 消息不是来自所有者。
  2. 发送者钱包中没有足够的 jettons。
  3. 没有足够的 TONs 在处理请求后至少发送 in_msg_value - max_tx_gas_priceresponse_destination 地址。 如果发送者 jetton-wallet 无法保证这一点,它必须立即停止执行请求并抛出错误。

否则应做:

  1. 减少燃烧钱包中的 jetton 数量 amount 并向 jetton 主合约发送包含燃烧信息的通知。
  2. Jetton 主合约应将入站消息币的所有剩余部分发送到 response_destination,具有以下布局: TL-B 方案:excesses#d53276db query_id:uint64 = InternalMsgBody;query_id 应与请求的 query_id 相等。

Get-methods

  1. get_wallet_data() 返回 (int balance, slice owner, slice jetton, cell jetton_wallet_code)balance - (uint256) 钱包中的 jettons 数量。 owner - (MsgAddress) 钱包所有者的地址。 jetton - (MsgAddress) Jetton 主地址。 jetton_wallet_code - (cell) 该钱包的代码。

Jetton 主合约

Get-methods

  1. get_jetton_data() 返回 (int total_supply, int mintable, slice admin_address, cell jetton_content, cell jetton_wallet_code)total_supply - (integer) - 已发行的 jettons 总数。 mintable - (-1/0) - 标志,指示 jettons 数量是否可以增加。 admin_address - (MsgAddressInt) - 控制 Jetton 的智能合约地址。 jetton_content - cell - 根据 代币数据标准 #64 的数据。 jetton_wallet_code - cell - 该 jetton 的钱包代码。
  2. get_wallet_address(slice owner_address) 返回 slice jetton_wallet_address 返回此所有者地址(MsgAddressInt)的 jetton 钱包地址(MsgAddressInt)。

TL-B 方案

nothing$0 {X:Type} = Maybe X;
just$1 {X:Type} value:X = Maybe X;
left$0 {X:Type} {Y:Type} value:X = Either X Y;
right$1 {X:Type} {Y:Type} value:Y = Either X Y;
var_uint$_ {n:#} len:(#< n) value:(uint (len * 8))
         = VarUInteger n;

addr_none$00 = MsgAddressExt;
addr_extern$01 len:(### 9) external_address:(bits len)
             = MsgAddressExt;
anycast_info$_ depth:(#<= 30) { depth >= 1 }
   rewrite_pfx:(bits depth) = Anycast;
addr_std$10 anycast:(Maybe Anycast)
   workchain_id:int8 address:bits256  = MsgAddressInt;
addr_var$11 anycast:(Maybe Anycast) addr_len:(### 9)
   workchain_id:int32 address:(bits addr_len) = MsgAddressInt;
_ _:MsgAddressInt = MsgAddress;
_ _:MsgAddressExt = MsgAddress;

transfer query_id:uint64 amount:(VarUInteger 16) destination:MsgAddress
           response_destination:MsgAddress custom_payload:(Maybe ^Cell)
           forward_ton_amount:(VarUInteger 16) forward_payload:(Either Cell ^Cell)
           = InternalMsgBody;

transfer_notification query_id:uint64 amount:(VarUInteger 16)
           sender:MsgAddress forward_payload:(Either Cell ^Cell)
           = InternalMsgBody;

excesses query_id:uint64 = InternalMsgBody;

burn query_id:uint64 amount:(VarUInteger 16)
       response_destination:MsgAddress custom_payload:(Maybe ^Cell)
       = InternalMsgBody;

// ----- 标准未指定,但建议的内部消息格式

internal_transfer  query_id:uint64 amount:(VarUInteger 16) from:MsgAddress
                     response_address:MsgAddress
                     forward_ton_amount:(VarUInteger 16)
                     forward_payload:(Either Cell ^Cell)
                     = InternalMsgBody;
burn_notification query_id:uint64 amount:(VarUInteger 16)
       sender:MsgAddress response_destination:MsgAddress
       = InternalMsgBody;

crc32('transfer query_id:uint64 amount:VarUInteger 16 destination:MsgAddress response_destination:MsgAddress custom_payload:Maybe ^Cell forward_ton_amount:VarUInteger 16 forward_payload:Either Cell ^Cell = InternalMsgBody') = 0x8f8a7ea5 & 0x7fffffff = 0xf8a7ea5

crc32('transfer_notification query_id:uint64 amount:VarUInteger 16 sender:MsgAddress forward_payload:Either Cell ^Cell = InternalMsgBody') = 0xf362d09c & 0x7fffffff = 0x7362d09c

crc32('excesses query_id:uint64 = InternalMsgBody') = 0x553276db | 0x80000000 = 0xd53276db

crc32('burn query_id:uint64 amount:VarUInteger 16 response_destination:MsgAddress custom_payload:Maybe ^Cell = InternalMsgBody') = 0x595f07bc & 0x7fffffff = 0x595f07bc

crc32('internal_transfer query_id:uint64 amount:VarUInteger 16 from:MsgAddress response_address:MsgAddress forward_ton_amount:VarUInteger 16 forward_payload:Either Cell ^Cell = InternalMsgBody') = 0x978d4519 & 0x7fffffff = 0x178d4519

crc32('burn_notification query_id:uint64 amount:VarUInteger 16 sender:MsgAddress response_destination:MsgAddress = InternalMsgBody') = 0x7bdd97de & 0x7fffffff = 0x7bdd97de

缺点

没有办法在链上获取实际钱包余额,因为当带有余额的消息到达时,钱包余额可能不准确。

理由和替代方案

分布式架构 “一个钱包 - 一个合约” 在 NFT 标准 中的 “理由” 段落中有详细描述。

先前的工作

  1. EIP-20 代币标准
  2. 分片智能合约开发者指南

未解决的问题

  1. 没有标准方法执行 “安全转移”,在合约执行失败的情况下回滚所有权转移。

未来的可能性

曾有一个想法实现 外部消息代币(由 EmelyanenkoK 提出)。

变更日志

2022年8月31日 - 添加了 forward_payload 格式。