跳转到内容

同质化代币(Jettons)

本页列出了使用 jettons 的常见示例。

Jettons 是TON区块链上的代币标准,旨在创建同质化代币(类似于以太坊上的ERC-20),并采用去中心化的方式。 它们以一对智能合约的形式实现,通常由两个核心组件组成:

  • Jetton Master Contracting (Jetton master)
  • Jetton Wallet Contract (Jetton wallet)

这些合同相互影响,以管理代币供应、分配、转让以及与捷通相关的其他操作。

Jetton Master Contract

Jetton 主合同是特定 Jetton 的中心实体。 它保留了有关Jetton本身的重要信息。 Jetton 主合同中存储的主要责任和数据包括:

  • Jetton元数据:诸如代币名称、符号、总供应量和小数点等信息。

  • 铸造和销毁:当新的Jettons被铸造(创建)时,Jetton主合约管理创建过程并将其分发到相应的钱包。 它还能根据需要管理代币的燃烧(销毁)。

  • 供应管理:Jetton Master 跟踪 Jettons 的总供应量,并确保对所有已发放的 Jettons 进行适当核算。

Jetton Wallet 合约

Jetton Wallet 合约是个人持有者的代币钱包,负责管理特定用户的余额和与代币相关的操作。 每个持有Jettons的用户或实体将拥有自己独特的Jetton钱包合约。 Jetton Wallet 合约的主要特点包括:

  • 余额跟踪:钱包合约存储用户的代币余额。

  • 代币转账:钱包负责处理用户之间的代币转账。 当用户发送Jetton时,Jetton Wallet 合约确保与收件人的钱包进行适当的转移和沟通。 Jetton Master 不参与这项活动,也不会造成瓶颈。 钱包可充分利用 TON 的分片功能

  • 代币销毁:Jetton钱包与Jetton Master交互以销毁代币。

  • 所有者控制:钱包合约由特定用户拥有和控制 表示只有钱包的所有者可以启动转账或其他代币操作。

示例:

处理Jettons的常见示例。

接受 jetton 转移

转账通知信息的结构如下

message(0x7362d09c) JettonTransferNotification {
queryId: Int as uint64;
amount: Int as coins;
sender: Address;
forwardPayload: Slice as remaining;
}

使用 receiver 功能接受代币通知信息。

必须验证传输通知的发件人,因为恶意行为者可能试图从未经授权的账户中伪造通知。 如果不进行这种确认,合约可接受未经授权的交易,从而造成潜在的安全脆弱性。

使用合约中的 Jetton 地址进行验证:

  1. 发送者向其 Jetton 钱包发送以 0xf8a7ea5 作为 32 位标题(操作码)的信息。
  2. Jetton钱包将资金转到合约的Jetton钱包。
  3. 在成功的转账接受后,合约的Jetton钱包将转账通知给他的所有者合约。
  4. 合约验证Jetton信息。

你可以使用 contractAddress() 函数获取合约的 Jetton 钱包,或链下计算此地址。

要获取 Jetton 钱包的初始状态,您需要钱包的数据和代码。 虽然初始数据布局有一个共同的结构,但在某些情况下可能不同,如 USDT

由于通知源自您的合约的 Jetton 钱包,函数myAddress() 应在 `owners’ 字段中使用。

import "@stdlib/deploy";
struct JettonWalletData {
balance: Int as coins;
ownerAddress: Address;
jettonMasterAddress: Address;
jettonWalletCode: Cell;
}
fun calculateJettonWalletAddress(
ownerAddress: Address,
jettonMasterAddress: Address,
jettonWalletCode: Cell
): Address {
let initData = JettonWalletData{
balance: 0,
ownerAddress,
jettonMasterAddress,
jettonWalletCode,
};
return contractAddress(StateInit{
code: jettonWalletCode,
data: initData.toCell(),
});
}
message(0x7362d09c) JettonTransferNotification {
queryId: Int as uint64;
amount: Int as coins;
sender: Address;
forwardPayload: Slice as remaining;
}
contract Example with Deployable {
myJettonWalletAddress: Address;
myJettonAmount: Int as coins = 0;
init(jettonWalletCode: Cell, jettonMasterAddress: Address) {
self.myJettonWalletAddress = calculateJettonWalletAddress(
myAddress(),
jettonMasterAddress,
jettonWalletCode,
);
}
receive(msg: JettonTransferNotification) {
require(
sender() == self.myJettonWalletAddress,
"Notification not from your jetton wallet!",
);
self.myJettonAmount += msg.amount;
// Forward excesses
self.forward(msg.sender, null, false, null);
}
}

发送Jetton转账

Jetton 转账是将指定数量的 Jetton 从一个钱包(合约)发送到另一个钱包的过程。

若要发送Jetton转账,请使用 send() 函数。

import "@stdlib/deploy";
message(0xf8a7ea5) JettonTransfer {
queryId: Int as uint64;
amount: Int as coins;
destination: Address;
responseDestination: Address?;
customPayload: Cell? = null;
forwardTonAmount: Int as coins;
forwardPayload: Slice as remaining;
}
const JettonTransferGas: Int = ton("0.05");
struct JettonWalletData {
balance: Int as coins;
ownerAddress: Address;
jettonMasterAddress: Address;
jettonWalletCode: Cell;
}
fun calculateJettonWalletAddress(
ownerAddress: Address,
jettonMasterAddress: Address,
jettonWalletCode: Cell,
): Address {
let initData = JettonWalletData{
balance: 0,
ownerAddress,
jettonMasterAddress,
jettonWalletCode,
};
return contractAddress(StateInit{
code: jettonWalletCode,
data: initData.toCell(),
});
}
message Withdraw {
amount: Int as coins;
}
contract Example with Deployable {
myJettonWalletAddress: Address;
myJettonAmount: Int as coins = 0;
init(jettonWalletCode: Cell, jettonMasterAddress: Address) {
self.myJettonWalletAddress = calculateJettonWalletAddress(
myAddress(),
jettonMasterAddress,
jettonWalletCode,
);
}
receive(msg: Withdraw) {
require(
msg.amount <= self.myJettonAmount,
"Not enough funds to withdraw"
);
send(SendParameters{
to: self.myJettonWalletAddress,
value: JettonTransferGas,
body: JettonTransfer{
// To prevent replay attacks
queryId: 42,
// Jetton amount to transfer
amount: msg.amount,
// Where to transfer Jettons:
// this is an address of the Jetton wallet
// owner and not the Jetton wallet itself
destination: sender(),
// Where to send a confirmation notice of a successful transfer
// and the rest of the incoming message value
responseDestination: sender(),
// Can be used for custom logic of Jettons themselves,
// and without such can be set to null
customPayload: null,
// Amount to transfer with JettonTransferNotification,
// which is needed for the execution of custom logic
forwardTonAmount: 1, // if its 0, the notification won't be sent!
// Compile-time way of expressing:
// beginCell().storeUint(0xF, 4).endCell().beginParse()
// For more complicated transfers, adjust accordingly
forwardPayload: rawSlice("F")
}.toCell(),
});
}
}

销毁 jetton

Jetton 销毁是将指定数量的 Jetton 永久移出流通的过程,无法恢复。

import "@stdlib/deploy";
message(0x595f07bc) JettonBurn {
queryId: Int as uint64;
amount: Int as coins;
responseDestination: Address?;
customPayload: Cell? = null;
}
const JettonBurnGas: Int = ton("0.05");
struct JettonWalletData {
balance: Int as coins;
ownerAddress: Address;
jettonMasterAddress: Address;
jettonWalletCode: Cell;
}
fun calculateJettonWalletAddress(
ownerAddress: Address,
jettonMasterAddress: Address,
jettonWalletCode: Cell,
): Address {
let initData = JettonWalletData{
balance: 0,
ownerAddress,
jettonMasterAddress,
jettonWalletCode,
};
return contractAddress(StateInit{
code: jettonWalletCode,
data: initData.toCell(),
});
}
message ThrowAway {
amount: Int as coins;
}
contract Example with Deployable {
myJettonWalletAddress: Address;
myJettonAmount: Int as coins = 0;
init(jettonWalletCode: Cell, jettonMasterAddress: Address) {
self.myJettonWalletAddress = calculateJettonWalletAddress(
myAddress(),
jettonMasterAddress,
jettonWalletCode,
);
}
receive(msg: ThrowAway) {
require(
msg.amount <= self.myJettonAmount,
"Not enough funds to throw away",
);
send(SendParameters{
to: self.myJettonWalletAddress,
value: JettonBurnGas,
body: JettonBurn{
// To prevent replay attacks
queryId: 42,
// Jetton amount you want to burn
amount: msg.amount,
// Where to send a confirmation notice of a successful burn
// and the rest of the incoming message value
responseDestination: sender(),
// Can be used for custom logic of Jettons themselves,
// and without such can be set to null
customPayload: null,
}.toCell(),
});
}
}

USDT jetton 业务

除了 JettonWalletData 将采用以下结构外,USDT(在 TON 上)的操作保持不变:

struct JettonWalletData {
status: Int as uint4;
balance: Int as coins;
ownerAddress: Address;
jettonMasterAddress: Address;
}
// And the function to calculate the wallet address may look like this:
fun calculateJettonWalletAddress(
ownerAddress: Address,
jettonMasterAddress: Address,
jettonWalletCode: Cell
): Address {
let initData = JettonWalletData{
status: 0,
balance: 0,
ownerAddress,
jettonMasterAddress,
};
return contractAddress(StateInit{
code: jettonWalletCode,
data: initData.toCell(),
});
}