Skip to content

Commit

Permalink
Merge pull request #430 from LevZed/main
Browse files Browse the repository at this point in the history
Added examples to dapps/cookbook
  • Loading branch information
reveloper committed Dec 8, 2023
2 parents ac53e1e + 6492ffe commit 4734382
Showing 1 changed file with 186 additions and 1 deletion.
187 changes: 186 additions & 1 deletion docs/develop/dapps/cookbook.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

<Tabs groupId="code-examples">
<TabItem value="js-ton" label="JS (@ton)">

```js
import { TonClient, WalletContractV4, internal } from "@ton/ton";
import { mnemonicNew, mnemonicToPrivateKey } from "@ton/crypto";
Expand Down Expand Up @@ -273,6 +276,45 @@ await contract.sendTransfer({
});
```

</TabItem>

<TabItem value="ton-kotlin" label="ton-kotlin">

```kotlin
// Setup liteClient
val context: CoroutineContext = Dispatchers.Default
val json = Json { ignoreUnknownKeys = true }
val config = json.decodeFromString<LiteClientConfigGlobal>(
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
})
}
```
</TabItem>

</Tabs>

### 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.
Expand Down Expand Up @@ -321,6 +363,45 @@ getUserWalletAddress(userAddress, jettonMasterAddress)
)
```
</TabItem>

<TabItem value="ton-kotlin" label="ton-kotlin">

```kotlin
// Setup liteClient
val context: CoroutineContext = Dispatchers.Default
val json = Json { ignoreUnknownKeys = true }
val config = json.decodeFromString<LiteClientConfigGlobal>(
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))

```
</TabItem>

</Tabs>

### How to construct a message for a jetton transfer with a comment?
Expand Down Expand Up @@ -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.

<Tabs groupId="code-examples">
<TabItem value="ton-kotlin" label="ton-kotlin">

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
})
}
```
</TabItem>
</Tabs>


### 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.
Expand Down Expand Up @@ -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)
* [Message Delivery Guarantees](/develop/smart-contracts/guidelines/message-delivery-guarantees)

0 comments on commit 4734382

Please sign in to comment.