diff --git a/docs/develop/dapps/cookbook.md b/docs/develop/dapps/cookbook.md index 9df3ea95c7..ea30bb7ee8 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,45 @@ 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) +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 + }) +} +``` + + + + ### 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 +363,45 @@ 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(4, 3) + .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? @@ -435,6 +516,110 @@ 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. + + + + +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 { + 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. @@ -953,4 +1138,4 @@ main().finally(() => console.log('Exiting...')); Note that this example covers only the simplest case with incoming messages, where it is enough to fetch the transactions on a single account. If you want to go deeper and handle more complex chains of transactions and messages, you should take `tx.outMessages` field into an account. It contains the list of the output messages sent by smart-contract in the result of this transaction. To understand the whole logic better, you can read these articles: * [Internal messages](/develop/smart-contracts/guidelines/internal-messages) - * [Message Delivery Guarantees](/develop/smart-contracts/guidelines/message-delivery-guarantees) \ No newline at end of file + * [Message Delivery Guarantees](/develop/smart-contracts/guidelines/message-delivery-guarantees)