-
Notifications
You must be signed in to change notification settings - Fork 47
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
How to create jetton transfer? #44
Comments
I think this is useful for you. import { beginCell, Address, TonClient, WalletContractV4, internal, external, storeMessage, toNano } from '@ton/ton';
import nacl from 'tweetnacl';
const apiKey = '...';
const client = new TonClient({ endpoint: 'https://toncenter.com/api/v2/jsonRPC', apiKey });
const usdtTokenContractAddress = 'EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs';
const toAddress = 'UQBcGtGIHLIQJuUHRRfWLhQxUJF4p49ywJyBdDr7UKTK60p9';
async function getUserJettonWalletAddress(userAddress: string, jettonMasterAddress: string) {
const userAddressCell = beginCell().storeAddress(Address.parse(userAddress)).endCell();
const response = await client.runMethod(Address.parse(jettonMasterAddress), 'get_wallet_address', [
{ type: 'slice', cell: userAddressCell },
]);
return response.stack.readAddress();
}
(async () => {
// Generate keyPair from mnemonic/secret key
const keyPair = nacl.sign.keyPair.fromSecretKey(Buffer.from('SecretKey', 'hex'));
const secretKey = Buffer.from(keyPair.secretKey);
const publicKey = Buffer.from(keyPair.publicKey);
const workchain = 0; // Usually you need a workchain 0
const wallet = WalletContractV4.create({ workchain, publicKey });
const address = wallet.address.toString({ urlSafe: true, bounceable: false, testOnly: false });
const contract = client.open(wallet);
const balance = await contract.getBalance();
console.log({ address, balance });
const seqno = await contract.getSeqno();
console.log({ address, seqno });
const { init } = contract;
const contractDeployed = await client.isContractDeployed(Address.parse(address));
let neededInit: null | typeof init = null;
if (init && !contractDeployed) {
neededInit = init;
}
const jettonWalletAddress = await getUserJettonWalletAddress(address, usdtTokenContractAddress);
// Comment payload
// const forwardPayload = beginCell()
// .storeUint(0, 32) // 0 opcode means we have a comment
// .storeStringTail('Hello, TON!')
// .endCell();
const messageBody = beginCell()
.storeUint(0x0f8a7ea5, 32) // opcode for jetton transfer
.storeUint(0, 64) // query id
.storeCoins(5001) // jetton amount, amount * 10^9
.storeAddress(toAddress)
.storeAddress(toAddress) // response destination
.storeBit(0) // no custom payload
.storeCoins(0) // forward amount - if > 0, will send notification message
.storeBit(0) // we store forwardPayload as a reference, set 1 and uncomment next line for have a comment
// .storeRef(forwardPayload)
.endCell();
const internalMessage = internal({
to: jettonWalletAddress,
value: toNano('0.1'),
bounce: true,
body: messageBody,
});
const body = wallet.createTransfer({
seqno,
secretKey,
messages: [internalMessage],
});
const externalMessage = external({
to: address,
init: neededInit,
body,
});
const externalMessageCell = beginCell().store(storeMessage(externalMessage)).endCell();
const signedTransaction = externalMessageCell.toBoc();
const hash = externalMessageCell.hash().toString('hex');
console.log('hash:', hash);
await client.sendFile(signedTransaction);
})(); |
If you need sync method of getUserJettonWalletAddress look: https://docs.ton.org/develop/dapps/cookbook#how-to-calculate-users-jetton-wallet-address-offline |
Error
|
If you still cant transfer jettons, here is project which do it. |
@vserpokryl Hello, I have a question about Jetton transfer code, why do I need to use |
@LI-YONG-QI const externalMessage = external({
to: address,
init: neededInit,
body: wallet.createTransfer({
seqno,
secretKey,
messages: [internalMessage],
}),
}); |
@vserpokryl Thanks for your response Here is the source code of export function createWalletTransferV4<T extends Wallet4SendArgsSignable | Wallet4SendArgsSigned>(
args: T & { sendMode: number, walletId: number }
) {
// Check number of messages
if (args.messages.length > 4) {
throw Error("Maximum number of messages in a single transfer is 4");
}
let signingMessage = beginCell()
.storeUint(args.walletId, 32);
if (args.seqno === 0) {
for (let i = 0; i < 32; i++) {
signingMessage.storeBit(1);
}
} else {
signingMessage.storeUint(args.timeout || Math.floor(Date.now() / 1e3) + 60, 32); // Default timeout: 60 seconds
}
signingMessage.storeUint(args.seqno, 32);
signingMessage.storeUint(0, 8); // Simple order
for (let m of args.messages) {
signingMessage.storeUint(args.sendMode, 8);
signingMessage.storeRef(beginCell().store(storeMessageRelaxed(m)));
}
return signPayload(
args,
signingMessage,
packSignatureToFront,
) as T extends Wallet4SendArgsSignable ? Promise<Cell> : Cell;
} I knew the Cell was serialized by TL-B scheme in TON, but I don't know what is TL-B scheme of this Cell in |
import "@stdlib/deploy"; contract InitiateJettonTransfer with Deployable {
} I tried this but my transaction still failed how to fix? |
When you run the above code, the following error occurs. contractDeployed false; |
I solved the problem Thank you |
You are great!!! |
Hello I need urgent help on how to import my Jetton transaction history |
@Omokami, hello! You can use https://tonapi.io for this. For example getAccountJettonsHistory - https://tonapi.io/api-v2#operations-Accounts-getAccountJettonsHistory |
Thank you but, I would appreciate it if you could explain how to get the account ID and jetton ID. |
@Omokami If you need all history of jetton transfers for all tokens use getAccountJettonsHistory (/v2/accounts/{account_id}/jettons/history) |
@Omokami |
Thank you very much |
Hi Is there a fee of 0.12 when transferring Jetton? I 'll wait for you reply |
@Omokami |
@vserpokryl |
FWIW, here's my code for preparing an USDT transfer tx using the latest versions of the tooling: import { Buffer } from "node:buffer"
import { randomBytes } from "@noble/hashes/utils"
import { Address, beginCell, comment, toNano } from "@ton/core"
import { JettonMaster } from "@ton/ton"
import { TonApiClient } from "@ton-api/client"
import { ContractAdapter } from "@ton-api/ton-adapter"
import { storeJettonTransferMessage } from "@ton-community/assets-sdk"
import { CHAIN, type SendTransactionRequest } from "@tonconnect/sdk"
const tonApi = new TonApiClient({
baseUrl: "https://tonapi.io",
apiKey: "...",
})
const adapter = new ContractAdapter(tonApi)
export async function createUsdtTxRequest(
{ sender, recipient, amount, comment: commentText }:
{ sender: string, recipient: string, amount: number, comment: string },
): Promise<SendTransactionRequest> {
const senderAddress = Address.parse(sender)
// https://docs.ton.org/v3/guidelines/dapps/cookbook#how-to-construct-a-message-for-a-jetton-transfer-with-a-comment
// https://github.com/ton-org/ton/issues/44
const messageCell = beginCell()
.store(storeJettonTransferMessage({
queryId: createJettonTransferQueryId(),
amount: toRawUsdt(amount),
destination: Address.parse(recipient),
// Return excessive TON back to sender.
responseDestination: senderAddress,
customPayload: null,
// Pay for forward payload.
forwardAmount: 1n,
forwardPayload: comment(commentText),
}))
.endCell()
const senderUsdtWalletAddress = await getUsdtWalletAddress(senderAddress)
return {
network: CHAIN.MAINNET,
messages: [{
address: senderUsdtWalletAddress.toString(),
// Make sure we have enough TON for commission. Extra will be returned back to sender.
amount: toNano("0.1").toString(),
payload: messageCell.toBoc().toString("base64"),
}],
validUntil: Math.floor(Date.now() / 1000) + 60, // 60 sec
}
}
function bigintFromUint8Array(arr: Uint8Array) {
return BigInt("0x" + Buffer.from(arr).toString("hex"))
}
/**
* Generate Jetton transfer query ID. Must be unique u64.
*/
function createJettonTransferQueryId() {
return bigintFromUint8Array(randomBytes(8))
}
async function getJettonWalletAddress(jettonAddress: Address, userAddress: Address): Promise<Address> {
const jettonMaster = adapter.open(JettonMaster.create(jettonAddress))
const userJettonAddress = await jettonMaster.getWalletAddress(userAddress)
return userJettonAddress
}
// Well-known address for USDT on TON.
const usdtContractAddress = Address.parse("EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs")
const usdtDecimals = 6
function getUsdtWalletAddress(userAddress: Address) {
return getJettonWalletAddress(usdtContractAddress, userAddress)
}
function toRawUsdt(amount: number) {
return BigInt(Math.round(amount * 10 ** usdtDecimals))
} |
Hello. I'm gonna make the transfer USDT function using my mnemonic in node.js. @vserpokryl. I tried to use your above example code but I don't get any issues and any result.
Please help me to complete this funciton |
I have experienced Jetton Wallet development, token automatic systems, and other experiences. maybe, I can help you |
Hi @Tvenus Nice to meet you.
And I'm trying to send test 1 USDT. This is my test USDT Master Address : kQAau4V6DsCRy_fmfDymJO5H_kLnIkjjKYLkXbBM0PZ1rtZy I don't understand the difference between jetton address and wallet address. It seems like difference between wallet address and token account address in solana. Please let me know what did I make mistake in my code. |
const messageBody = beginCell()
|
So is it because I entered the jetton number incorrectly? If then, where can I find the jetton number? |
//Enter Jetton value instead of 5001. |
Thank you @Tvenus 💕 It's working well. |
I am very interested in your work, and I wanna work with you. |
Could you please provide sample code (in the Readme?) how to create jetton/token transfer transactio?
The text was updated successfully, but these errors were encountered: