From ab83b3e9ba56b34a6b30e84131e8ac60d731b30f Mon Sep 17 00:00:00 2001 From: Revuza Date: Mon, 27 Nov 2023 14:03:42 +0500 Subject: [PATCH 1/5] Added examples --- docs/develop/dapps/cookbook.md | 80 ++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/docs/develop/dapps/cookbook.md b/docs/develop/dapps/cookbook.md index dc74967857..67587270bf 100644 --- a/docs/develop/dapps/cookbook.md +++ b/docs/develop/dapps/cookbook.md @@ -243,6 +243,9 @@ print(address.to_str(is_user_friendly=True, is_bounceable=False, is_url_safe=Tru To send a standard TON transfer message, first you need to open your wallet contract, after that, get your wallet seqno. And only after that can you send your TON transfer. Note that if you are using a non-V4 version of the wallet, you will need to rename WalletContractV4 to WalletContract{your wallet version}, for example, WalletContractV3R2. + + + ```js import { TonClient, WalletContractV4, internal } from "@ton/ton"; import { mnemonicNew, mnemonicToPrivateKey } from "@ton/crypto"; @@ -273,6 +276,43 @@ await contract.sendTransfer({ }); ``` + + + + +```kotlin +// Setup liteClient +val context: CoroutineContext = Dispatchers.Default +val json = Json { ignoreUnknownKeys = true } +val config = json.decodeFromString( + URI("https://ton.org/global-config.json").toURL().readText() +) +val liteClient = LiteClient(context, config) + +val WALLET_MNEMONIC = "word1 word2 ...".split(" ") + +val pk = PrivateKeyEd25519(Mnemonic.toSeed(WALLET_MNEMONIC)) +val walletAddress = WalletV3R2Contract.address(pk, 0) +println(walletAddress.toString(userFriendly = true, bounceable = false)) + +val wallet = WalletV3R2Contract(liteClient, walletAddress) +wallet.transfer(pk, WalletTransfer { + destination = AddrStd("EQCD39VS5jcptHL8vMjEXrzGaRcCVYto7HUn4bpAOg8xqB2N") + bounceable = true + coins = Coins(100000000) // 1 ton in nanotons + messageData = org.ton.contract.wallet.MessageData.raw( + body = buildCell { + storeUInt(0, 32) + storeBytes("Comment".toByteArray()) + } + ) + sendMode = 0 +}) +``` + + + + ### How to calculate user's Jetton wallet address? To calculate the user's jetton wallet address, we need to call the "get_wallet_address" get-method of the jetton master contract with user address actually. For this task we can easily use getWalletAddress method from JettonMaster or call master contract by ourselves. @@ -321,6 +361,46 @@ getUserWalletAddress(userAddress, jettonMasterAddress) ) ``` + + + +```kotlin +// Setup liteClient +val context: CoroutineContext = Dispatchers.Default +val json = Json { ignoreUnknownKeys = true } +val config = json.decodeFromString( + URI("https://ton.org/global-config.json").toURL().readText() +) +val liteClient = LiteClient(context, config) + +val USER_ADDR = AddrStd("Wallet address") +val JETTON_MASTER = AddrStd("Jetton Master contract address") // for example EQBlqsm144Dq6SjbPI4jjZvA1hqTIP3CvHovbIfW_t-SCALE + +// we need to send regular wallet address as a slice +val userAddressSlice = CellBuilder.beginCell() + .storeUInt(2, 2) + .storeUInt(0, 1) + .storeInt(USER_ADDR.workchainId, 8) + .storeBits(USER_ADDR.address) + .endCell() + .beginParse() + +val response = runBlocking { + liteClient.runSmcMethod( + LiteServerAccountId(JETTON_MASTER.workchainId, JETTON_MASTER.address), + "get_wallet_address", + VmStackValue.of(userAddressSlice) + ) +} + +val stack = response.toMutableVmStack() +val jettonWalletAddress = stack.popSlice().loadTlb(MsgAddressInt) as AddrStd +println("Calculated Jetton wallet:") +println(jettonWalletAddress.toString(userFriendly = true)) + +``` + + ### How to construct a message for a jetton transfer with a comment? From d881be0dbba7ee3ef6c76a68a5bd58b5731c676c Mon Sep 17 00:00:00 2001 From: Revuza Date: Wed, 29 Nov 2023 19:01:08 +0500 Subject: [PATCH 2/5] Update cookbook.md --- docs/develop/dapps/cookbook.md | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/docs/develop/dapps/cookbook.md b/docs/develop/dapps/cookbook.md index 67587270bf..3dd29d5658 100644 --- a/docs/develop/dapps/cookbook.md +++ b/docs/develop/dapps/cookbook.md @@ -296,18 +296,20 @@ val walletAddress = WalletV3R2Contract.address(pk, 0) println(walletAddress.toString(userFriendly = true, bounceable = false)) val wallet = WalletV3R2Contract(liteClient, walletAddress) -wallet.transfer(pk, WalletTransfer { - destination = AddrStd("EQCD39VS5jcptHL8vMjEXrzGaRcCVYto7HUn4bpAOg8xqB2N") - bounceable = true - coins = Coins(100000000) // 1 ton in nanotons - messageData = org.ton.contract.wallet.MessageData.raw( - body = buildCell { - storeUInt(0, 32) - storeBytes("Comment".toByteArray()) - } - ) - sendMode = 0 -}) +runBlocking { + wallet.transfer(pk, WalletTransfer { + destination = AddrStd("EQCD39VS5jcptHL8vMjEXrzGaRcCVYto7HUn4bpAOg8xqB2N") + bounceable = true + coins = Coins(100000000) // 1 ton in nanotons + messageData = org.ton.contract.wallet.MessageData.raw( + body = buildCell { + storeUInt(0, 32) + storeBytes("Comment".toByteArray()) + } + ) + sendMode = 0 + }) +} ``` @@ -865,4 +867,4 @@ console.log(text); -This example will help you understand how you can work with such cells using recursion. \ No newline at end of file +This example will help you understand how you can work with such cells using recursion. From 607301cc0d33a4994804e26bb0fef38edc158fff Mon Sep 17 00:00:00 2001 From: Revuza Date: Wed, 29 Nov 2023 19:03:11 +0500 Subject: [PATCH 3/5] Update cookbook.md --- docs/develop/dapps/cookbook.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/develop/dapps/cookbook.md b/docs/develop/dapps/cookbook.md index 3dd29d5658..0140d4dcb9 100644 --- a/docs/develop/dapps/cookbook.md +++ b/docs/develop/dapps/cookbook.md @@ -297,18 +297,18 @@ println(walletAddress.toString(userFriendly = true, bounceable = false)) val wallet = WalletV3R2Contract(liteClient, walletAddress) runBlocking { - wallet.transfer(pk, WalletTransfer { - destination = AddrStd("EQCD39VS5jcptHL8vMjEXrzGaRcCVYto7HUn4bpAOg8xqB2N") - bounceable = true - coins = Coins(100000000) // 1 ton in nanotons - messageData = org.ton.contract.wallet.MessageData.raw( - body = buildCell { - storeUInt(0, 32) - storeBytes("Comment".toByteArray()) - } - ) - sendMode = 0 - }) + wallet.transfer(pk, WalletTransfer { + destination = AddrStd("EQCD39VS5jcptHL8vMjEXrzGaRcCVYto7HUn4bpAOg8xqB2N") + bounceable = true + coins = Coins(100000000) // 1 ton in nanotons + messageData = org.ton.contract.wallet.MessageData.raw( + body = buildCell { + storeUInt(0, 32) + storeBytes("Comment".toByteArray()) + } + ) + sendMode = 0 + }) } ``` From 34567f1ba3fc27a88348caec573e262f0ebdb424 Mon Sep 17 00:00:00 2001 From: Revuza Date: Sat, 2 Dec 2023 21:20:11 +0500 Subject: [PATCH 4/5] send swap to dex draft --- docs/develop/dapps/cookbook.md | 79 ++++++++++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) diff --git a/docs/develop/dapps/cookbook.md b/docs/develop/dapps/cookbook.md index 67587270bf..11d5b086b5 100644 --- a/docs/develop/dapps/cookbook.md +++ b/docs/develop/dapps/cookbook.md @@ -515,6 +515,85 @@ main().finally(() => console.log("Exiting...")); To indicate that we want to include a comment, we specify 32 zero bits and then write our comment. We also specify the `response destination`, which means that a response regarding the successful transfer will be sent to this address. If we don't want a response, we can specify 2 zero bits instead of an address. +### How to send swap message to DEX (DeDust)? + +DEXes use different protocols for their work. You can find details for the DeDust protocol on their [website](https://docs.dedust.io/). + +DeDust has two exchange paths: jetton <-> jetton or toncoin <-> jetton. Each has a different scheme. To swap, you need to send jettons to a specific jetton (or toncoin) **vault** by passing a special payload. Here is the scheme for swapping jetton to jetton or jetton to toncoin: + +```tlb +swap#e3a0d482 _:SwapStep swap_params:^SwapParams = ForwardPayload; + step#_ pool_addr:MsgAddressInt params:SwapStepParams = SwapStep; + step_params#_ kind:SwapKind limit:Coins next:(Maybe ^SwapStep) = SwapStepParams; + swap_params#_ deadline:Timestamp recipient_addr:MsgAddressInt referral_addr:MsgAddress + fulfill_payload:(Maybe ^Cell) reject_payload:(Maybe ^Cell) = SwapParams; +``` +This scheme shows what should be in the forward_payload of a message from your toncoin wallet to your jetton wallet (`transfer#f8a7ea5`) + +And the scheme of toncoin to jetton swap: +```tlb +swap#ea06185d query_id:uint64 amount:Coins _:SwapStep swap_params:^SwapParams = InMsgBody; + step#_ pool_addr:MsgAddressInt params:SwapStepParams = SwapStep; + step_params#_ kind:SwapKind limit:Coins next:(Maybe ^SwapStep) = SwapStepParams; + swap_params#_ deadline:Timestamp recipient_addr:MsgAddressInt referral_addr:MsgAddress + fulfill_payload:(Maybe ^Cell) reject_payload:(Maybe ^Cell) = SwapParams; +``` +This is scheme for body of trabsfer to the toncoin **vault**. + +First, you need to know the **vault** addresses of the jettons you will swap or toncoin **vault** address. This can be done using the get methods of the contract [**FACTORY**](https://docs.dedust.io/reference/factory) `get_vault_address`. As an argument you need to pass a slice according to the scheme: +```tlb +native$0000 = Asset; // for ton +jetton$0001 workchain_id:int8 address:uint256 = Asset; // for jetton +``` +Also for the exchange itself we need the **pool** address - `get_pool_address`. As arguments - asset slices according to the scheme above. In response, both methods will return a slice of the address of the requested **vault** / **pool**. + +This is enough to build the message. + + + + +```kotlin +runBlocking { + wallet.transfer(pk, WalletTransfer { + destination = JETTON_WALLET_A // yours existing jetton wallet + bounceable = true + coins = Coins(300000000) // 0.3 ton in nanotons + messageData = MessageData.raw( + body = buildCell { + storeUInt(0xf8a7ea5, 32) // op Transfer + storeUInt(0, 64) // query_id + storeTlb(Coins, Coins(100000000)) // amount of jettons + storeSlice(addrToSlice(jettonAVaultAddress)) // destination address + storeSlice(addrToSlice(walletAddress)) // response address + storeUInt(0, 1) // custom payload + storeTlb(Coins, Coins(250000000)) // forward_ton_amount // 0.25 ton in nanotons + storeUInt(1, 1) + // forward_payload + storeRef { + storeUInt(0xe3a0d482, 32) // op swap + storeSlice(addrToSlice(poolAddress)) // pool_addr + storeUInt(0, 1) // kind + storeTlb(Coins, Coins(0)) // limit + storeUInt(0, 1) // next (for multihop) + storeRef { + storeUInt(System.currentTimeMillis() / 1000 + 60 * 5, 32) // deadline + storeSlice(addrToSlice(walletAddress)) // recipient address + storeSlice(buildCell { storeUInt(0, 2) }.beginParse()) // referral (null address) + storeUInt(0, 1) + storeUInt(0, 1) + endCell() + } + } + } + ) + sendMode = 3 + }) +} +``` + + + + ### How to use NFT batch deploy? Smart contracts for collections allow deploying up to 250 NFTs in a single transaction. However, it's essential to consider that, in practice, this maximum is around 100-130 NFTs due to the computation fee limit of 1 ton. To achieve this, we need to store information about the new NFTs in a dictionary. From 9e619394380b63d0d40fd038a37cd5b939e598d1 Mon Sep 17 00:00:00 2001 From: Revuza Date: Thu, 7 Dec 2023 17:41:45 +0500 Subject: [PATCH 5/5] fixes --- docs/develop/dapps/cookbook.md | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/docs/develop/dapps/cookbook.md b/docs/develop/dapps/cookbook.md index d4cabd93bf..19b93c2c3e 100644 --- a/docs/develop/dapps/cookbook.md +++ b/docs/develop/dapps/cookbook.md @@ -380,8 +380,7 @@ val JETTON_MASTER = AddrStd("Jetton Master contract address") // for example EQB // we need to send regular wallet address as a slice val userAddressSlice = CellBuilder.beginCell() - .storeUInt(2, 2) - .storeUInt(0, 1) + .storeUInt(4, 3) .storeInt(USER_ADDR.workchainId, 8) .storeBits(USER_ADDR.address) .endCell() @@ -554,6 +553,31 @@ This is enough to build the message. +Build Asset slice: +```kotlin +val assetASlice = buildCell { + storeUInt(1,4) + storeInt(JETTON_MASTER_A.workchainId, 8) + storeBits(JETTON_MASTER_A.address) +}.beginParse() +``` + +Run get methods: +```kotlin +val responsePool = runBlocking { + liteClient.runSmcMethod( + LiteServerAccountId(DEDUST_FACTORY.workchainId, DEDUST_FACTORY.address), + "get_pool_address", + VmStackValue.of(0), + VmStackValue.of(assetASlice), + VmStackValue.of(assetBSlice) + ) +} +stack = responsePool.toMutableVmStack() +val poolAddress = stack.popSlice().loadTlb(MsgAddressInt) as AddrStd +``` + +Build and transfer message: ```kotlin runBlocking { wallet.transfer(pk, WalletTransfer {