A collection of TON Tact templates and tools.
Provides ready-to-use templates for Jetton, NFT, Traits, as well as some commonly used tools.
Click to view my article on using Tact to implement Jetton & NFTs! 🌟
Tested on Node.js v22.9
Still undergoing frequent updates!
https://s3.laisky.com/public/nft/connect/demo/index.html
Install nodejs: https://github.com/nodesource/distributions
yarn
npx blueprint build
helloworld
is a simple example of a contract, while sample
is an example that includes complex contract calls.
Please do not use helloworld
and sample
directly in your development.
Instead, use the code in common
, jetton
, and nft
according to your needs.
npx blueprint build helloworld
npx blueprint run --testnet --tonconnect helloworld
To provide a more comprehensive code template, the sample deliberately includes a more complex Jetton implementation. You don't need to use the Sample directly in your project; rather, you should utilize the contracts and code in
jetton
andcommon
as per your requirements.
npx blueprint build sample
npx blueprint run --testnet --tonconnect jetton
? input the address of the jetton receiver(default to yourself):
0QARnduCSjymI91urfHE_jXlnTHrmr0e4yaPubtPQkgy553b
Sent transaction
-------------------------------------
jetton master address: EQD9PR60ImXHSE1KIemZGS30F0aHc0QUnfC6sMYyw9HtSGqA
Contract deployed at address EQD9PR60ImXHSE1KIemZGS30F0aHc0QUnfC6sMYyw9HtSGqA
You can view it at https://testnet.tonscan.org/address/EQD9PR60ImXHSE1KIemZGS30F0aHc0QUnfC6sMYyw9HtSGqA
mintable: true
owner: EQDRAI32YdVGZGDq18ygyPyflOpY5qIAA9ukd-OJ0CfYJ8SN
jetton content: https://s3.laisky.com/uploads/2024/09/jetton-sample.json
jetton total supply: 19000000000
-------------------------------------
jetton wallet address: EQDJiYKObYkxFFTR5v53TihdY723W8YCh34jvdu7qcwhBhVx
Contract deployed at address EQDJiYKObYkxFFTR5v53TihdY723W8YCh34jvdu7qcwhBhVx
You can view it at https://testnet.tonscan.org/address/EQDJiYKObYkxFFTR5v53TihdY723W8YCh34jvdu7qcwhBhVx
jetton wallet owner: EQARnduCSjymI91urfHE_jXlnTHrmr0e4yaPubtPQkgy53uU
jetton wallet master: EQD9PR60ImXHSE1KIemZGS30F0aHc0QUnfC6sMYyw9HtSGqA
jetton wallet balance: 19000000000
To provide a more comprehensive code template, the sample deliberately includes a more complex NFT implementation. You don't need to use the Sample directly in your project; rather, you should utilize the contracts and code in
nft
andcommon
as per your requirements.
npx blueprint build sample
npx blueprint run --testnet --tonconnect nft
Sent transaction
-------------------------------------
nft collection address: EQBHuZqwFHShebGvdOwRCeC1XbWPvYpOZsF7k7gkirDofyXG
Contract deployed at address EQBHuZqwFHShebGvdOwRCeC1XbWPvYpOZsF7k7gkirDofyXG
You can view it at https://testnet.tonscan.org/address/EQBHuZqwFHShebGvdOwRCeC1XbWPvYpOZsF7k7gkirDofyXG
nft collection owner: EQCVjlulLBzq9FSR2wQqZJU3uzE-TDXlvWKJAtHqu5SyHqoh
nft collection next index: 1
nft collection content: https://s3.laisky.com/uploads/2024/09/nft-sample-collection.json
-------------------------------------
nft item address: EQCub9bLM0sjI2qJGafmMFiPsDFJhq5RkDVQRlnNV9Rr_W77
Contract deployed at address EQCub9bLM0sjI2qJGafmMFiPsDFJhq5RkDVQRlnNV9Rr_W77
You can view it at https://testnet.tonscan.org/address/EQCub9bLM0sjI2qJGafmMFiPsDFJhq5RkDVQRlnNV9Rr_W77
nft item owner: EQCVjlulLBzq9FSR2wQqZJU3uzE-TDXlvWKJAtHqu5SyHqoh
nft item collection: EQBHuZqwFHShebGvdOwRCeC1XbWPvYpOZsF7k7gkirDofyXG
nft item index: 0
nft item content: https://s3.laisky.com/uploads/2024/09/nft-sample-item-0.json
Clone this repo to your project:
git clone https://github.com/Laisky/tact-utils.git
Then import the traits you need:
import './tact-utils/contracts/common/traits.tact';
import './tact-utils/contracts/common/messages.tact';
import './tact-utils/contracts/common/traits.tact';
contract YOUR_CONTRACT with Common {
owner: Address;
}
In the Common Trait, there is a function named int2hex(Int): String
that can convert a hash value of type Int
to a hexadecimal string of type String
.
In Tact, the sha256
function truncates the input string, keeping only the first 128 bytes. Therefore, a fullSha256
function is re-implemented in common
to compute the complete sha256.
In common/traits.tact
, there is both a function named fullSha256
and a method named fullSha256
that belongs to the Common trait.
// Contract
contract YOURCONTRACT with Common {
get fun testStrHash(v1: String, v2: String): String {
let hashed = beginString()
.concat(v1)
.concat(v2)
.toString();
return self.int2hex(self.fullSha256(hashed));
}
}
// Test script
it("hash string onchain", async () => {
const v1 = "4d2377d0bc3befe8a721e96b13e22d3b4e557024353e69e2b5d0f315ad49aa05";
const v2 = "551f6c3e8d7ae7d9b3ac53bca9b6f82cff322fb16113820776d14a3f93b93951";
const gotHash = await sampleMaster.getTestStrHash(v1, v2);
const expectHash = (await sha256(v1 + v2)).toString('hex');
console.log(BigInt("0x"+(await sha256(v1 + v2)).toString('hex')));
expect(gotHash).toEqual(expectHash);
});
In common/traits.tact
, there is a function named verifyMerkleSha256(MerkleProof)
that can verify the Merkle root on-chain.
// Contract
contract YOURCONTRACT with Common {
get fun testVerifyMerkleProof(msg: VerifyMerkleProof) {
self.verifyMerkleSha256(msg.proof);
}
}
// Test script
const generateMerkleProof = async (data: Cell) => {
const d0 = comment("hello");
const d1 = comment("world");
let proofs = [];
proofs.push(
d0.hash().toString("hex"),
d1.hash().toString("hex"),
);
let root;
root = (await sha256(data.hash().toString('hex') + d0.hash().toString('hex'))).toString('hex');
root = (await sha256(root + d1.hash().toString('hex'))).toString('hex');
console.log(`proofs: ${proofs}`);
return {
proofs,
root,
};
};
it("merkle onchain", async () => {
const data = comment('abc');
const { proofs, root } = await generateMerkleProof(data);
let proof = Dictionary.empty<number, bigint>();
for (let i = 0; i < proofs.length; i++) {
proof = proof.set(i, BigInt(`0x${proofs[i]}`));
}
await sampleMaster.getTestVerifyMerkleProof(
{
$$type: "VerifyMerkleProof",
queryId: BigInt(Math.floor(Date.now() / 1000)),
proof: {
$$type: "MerkleProof",
data: data,
root: BigInt(`0x${root}`),
proof: proof,
proofLen: BigInt(proofs.length),
},
}
);
});
Set a staticTax
to charge a fixed fee for every transaction, keeping it in the contract. Owners can adjust it anytime via SetStaticTax
msg.
contract YOUR_CONTRACT with Txable {
owner: Address;
staticTax: Int as coins = ton("0.001");
}
Allow the contract to be upgraded by the owner.
contract YOUR_CONTRACT with Upgradable {
owner: Address;
}
Easily implement your own Jetton contract using Jetton Template.
import './tact-utils/contracts/jetton/jetton.tact';
import './tact-utils/contracts/common/messages.tact';
contract YOUR_CONTRACT {
owner: Address;
receive("SOME_MSG") {
let jettonMaster = initOf JettonMasterTemplate(
self.owner,
Tep64TokenData{
flag: 1,
content: "https://s3.laisky.com/uploads/2024/09/jetton-sample.json",
},
)
}
}
You can also deeply customize Jetton contracts using Jetton Trait.
import './tact-utils/contracts/jetton/jetton.tact';
contract YOUR_CONTRACT with JettonMaster {
owner: Address;
staticTax: Int as coins = ton("0.001");
lockedValue: Int as coins = 0;
content: Cell;
totalSupply: Int as coins;
mintable: Bool;
init(owner: Address, content: Tep64TokenData) {
self.owner = owner;
self.content = content.toCell();
self.totalSupply = 0;
self.mintable = true;
}
}