diff --git a/docs/cspell.json b/docs/cspell.json index 9d31dffea..623040802 100644 --- a/docs/cspell.json +++ b/docs/cspell.json @@ -85,8 +85,8 @@ "Seamus", "Sedov", "Stateinit", - "Ston", - "Stonfi", + "Ston", + "Stonfi", "Sánchez", "THROWIFNOT", "TIMELOCK", diff --git a/docs/src/content/docs/cookbook/dexes/stonfi.mdx b/docs/src/content/docs/cookbook/dexes/stonfi.mdx index 2e9d974d0..69fcdf381 100644 --- a/docs/src/content/docs/cookbook/dexes/stonfi.mdx +++ b/docs/src/content/docs/cookbook/dexes/stonfi.mdx @@ -11,46 +11,185 @@ sidebar: :::caution - This api is about STON.fi version 2, which is under development. Use it at your own risk. + The examples on this page use STON.fi's API v2, which is currently under development. Thus, all addresses are given in [testnet][testnet]. -::: + Proceed with caution and vigilance — do not attempt to send funds from the mainnet to the testnet and vice versa. -## Recommended Reading +::: -Before proceeding further, you may find it helpful to explore the following articles: +Before going further, familiarize yourself with the following: -* [Receive messages](/book/receive/) +* [Receiving messages](/book/receive/) * [Sending messages](/book/send/) * [Fungible Tokens (Jettons)](/cookbook/jettons/) +* [STON.fi Docs: Glossary](https://docs.ston.fi/docs/user-section/glossary) +* [STON.fi Docs: Architecture](https://docs.ston.fi/docs/developer-section/architecture) ## Swaps -More about swaps [here](https://docs.ston.fi/docs/developer-section/api-reference-v2/example_swap). +Read more about swaps in the [STON.fi documentation](https://docs.ston.fi/docs/developer-section/api-reference-v2/example_swap). + +Swaps use `StonfiSwap{:tact}` [Message][message] and `SwapAdditionalData{:tact}` [Struct][struct]: + +```tact +/// https://docs.ston.fi/docs/developer-section/api-reference-v2/router#swap-0x6664de2a +message(0x6664de2a) StonfiSwap { + // Address of the other Router token wallet + otherTokenWallet: Address; + + // Where to send refunds upon a failed swap + refundAddress: Address; + + // Where to send excesses upon a successful swap + excessesAddress: Address; + + // UNIX timestamp of execution deadline for the swap + deadline: Int as uint64; + + // Reference to another Cell with additional data, + // using the Tact's greedy auto-layout mechanism + additionalData: SwapAdditionalData; +} + +/// https://docs.ston.fi/docs/developer-section/api-reference-v2/router#additional_data-body +struct SwapAdditionalData { + // Minimum required amount of tokens to receive + // Defaults to 1, which causes the swap to fail + // only if no tokens are received + minOut: Int as coins = 1; + + // Where to send tokens upon a successful swap + receiverAddress: Address; + + // Forward fees for the `customPayload` if it's not `null` + // Defaults to 0 + fwdGas: Int as coins = 0; + + // Custom payload that will be sent upon a successful swap + // Defaults to `null`, which means no payload + customPayload: Cell? = null; + + // Forward fees for `refundPayload` if it's not `null` + // Defaults to 0 + refundFwdGas: Int as coins = 0; -Some variables like `offerAmount` are hardcoded for demonstration purposes. Don't forget to change them in real case scenarios. + // Custom payload that will be sent upon a failed swap + // Defaults to `null`, which means no payload + refundPayload: Cell? = null; -Swaps have several common structures and messages. Don't forget to use them with the code examples below! + // Referral fee, between 0 (no fee) and 100 (1%) + // Defaults to 10, which means 0.1% fee + refFee: Int as uint16 = 10; + + // Address of the referral + // Defaults to `null` + referralAddress: Address? = null; +} +``` + +The [STON.fi SDK](https://github.com/ston-fi/sdk) defines some [constants to deal with fees](https://github.com/ston-fi/sdk/blob/786ece758794bd5c575db8b38f5e5de19f43f0d1/packages/sdk/src/contracts/dex/v2_1/router/BaseRouterV2_1.ts). Note that these are hardcoded values, but the best practice is to [calculate fees dynamically using current config params](https://docs.ton.org/v3/guidelines/smart-contracts/fee-calculation) instead. + +```tact +/// Hardcoded fee value to pay for sending a message to the Jetton wallet +const FeeSwapJettonToJetton: Int = ton("0.3"); +/// Hardcoded fee value to pay forward fees from the Jetton wallet +const FeeSwapJettonToJettonFwd: Int = ton("0.24"); + +/// Hardcoded fee value to pay for sending a message to the Jetton wallet +const FeeSwapJettonToToncoin: Int = ton("0.3"); +/// Hardcoded fee value to pay for sending a message to the Jetton wallet +const FeeSwapJettonToToncoinFwd: Int = ton("0.24"); + +/// Hardcoded fee value to pay for sending a message and subsequent forwarding +const FeeSwapToncoinToJetton: Int = ton("0.01") + ton("0.3"); +``` + +:::note[Useful links:] + + [Fees Calculation in TON Docs][fees-calc] + +::: + +### Jetton to Jetton {#swaps-jetton-to-jetton} + +:::caution + + The following example uses STON.fi's API v2, which is currently under development. Thus, all addresses are given in [testnet][testnet]. + + In addition, some variables such as `offerAmount` are hardcoded for demonstration purposes. Don't forget to change them in real life scenarios. + +::: ```tact +// CPI Router v2.1.0 +const RouterAddress: Address = + address("kQALh-JBBIKK7gr0o4AVf9JZnEsFndqO0qTCyT-D-yBsWk0v"); + +// Router Jetton Wallet address +const RouterJettonWallet: Address = + address("kQAtX3x2s-wMtYTz8CfmAyloHAB73vONzJM5S2idqXl-_5xK"); + +/// NOTE: To calculate and provide Jetton wallet address for the target user, +/// make sure to check links after this code snippet +fun jettonToJetton(myJettonWalletAddress: Address) { + // Amount of Jettons to swap + let offerAmount: Int = 100_000; + + // Prepare the payload + let forwardPayload = StonfiSwap{ + otherTokenWallet: RouterJettonWallet, + refundAddress: myAddress(), + excessesAddress: myAddress(), + // Deadline is set to 10,000 seconds from now + deadline: now() + 10_000, + additionalData: SwapAdditionalData{ receiverAddress: myAddress() }, + }; + + // Start a swap with the message to the Jetton wallet + send(SendParameters{ + to: myJettonWalletAddress, + value: FeeSwapJettonToJetton, + body: JettonTransfer{ + queryId: 42, + amount: offerAmount, + destination: RouterAddress, + responseDestination: myAddress(), + forwardTonAmount: FeeSwapJettonToJettonFwd, + forwardPayload: forwardPayload.toCell(), + }.toCell(), + }); +} + +// +// Helper Messages, Structs and constants described earlier on this page +// + message(0x6664de2a) StonfiSwap { - otherTokenWallet: Address; // Address of the other Router token wallet - refundAddress: Address; // Address where refund will be sent if swap fails - excessesAddress: Address; // Address where TON excesses will be sent - deadline: Int as uint64; // Timestamp of execution deadline for this tx - additionalData: SwapAdditionalData; // Defined below + otherTokenWallet: Address; + refundAddress: Address; + excessesAddress: Address; + deadline: Int as uint64; + additionalData: SwapAdditionalData; } struct SwapAdditionalData { - minOut: Int as coins; // Minimum required amount of tokens to receive - receiver: Address; // Address where tokens will be sent after swap - fwdGas: Int as coins = 0; // Gas used to forward a message in transfer_notification after swap if custom_payload is present - customPayload: Cell? = null; // Payload sent in transfer_notification after swap - refundFwdGas: Int as coins = 0; // Gas used to forward a message in transfer_notification if swap fails if refund_payload is present - refundPayload: Cell? = null; // Payload sent in transfer_notification if swap fails - refFee: Int as uint16 = 10; // Referral fee. Max amount is 100 (1%) - referralAddress: Address? = null; // Referral address + minOut: Int as coins = 1; + receiverAddress: Address; + fwdGas: Int as coins = 0; + customPayload: Cell? = null; + refundFwdGas: Int as coins = 0; + refundPayload: Cell? = null; + refFee: Int as uint16 = 10; + referralAddress: Address? = null; } +const FeeSwapJettonToJetton: Int = ton("0.3"); +const FeeSwapJettonToJettonFwd: Int = ton("0.24"); + +// +// Messages from the Jetton standard +// + message(0xf8a7ea5) JettonTransfer { queryId: Int as uint64; amount: Int as coins; @@ -58,203 +197,386 @@ message(0xf8a7ea5) JettonTransfer { responseDestination: Address?; customPayload: Cell? = null; forwardTonAmount: Int as coins; - forwardPayload: Cell?; // hack for now, equivalent to Slice as remaining with first bit set to 1 + forwardPayload: Cell?; // slightly adjusted } ``` -Below are shown gas constants. All of them are taken from the sdk [STON.fi sdk](https://github.com/ston-fi/sdk). They are slightly differ from reality, but it is ok for now. +:::note[Useful links:] -```tact -const GasSwapJettonToJetton: Int = ton("0.3"); -const GasSwapJettonToJettonFwd: Int = ton("0.24"); + [Retrieving Jetton wallet address in TON Docs][jetton-addr-online]\ + [How to calculate user's Jetton wallet address (offline)?][jetton-addr-offline]\ + [Discoverable Jetton wallets][discoverable-jetton-wallets]\ + [Fees Calculation in TON Docs][fees-calc] -const GasSwapJettonToTon: Int = ton("0.3"); -const GasSwapJettonToTonFwd: Int = ton("0.24"); +::: -const GasSwapTonToJettonFwd: Int = ton("0.3"); -const GasSwapTonToJetton: Int = ton("0.01"); -``` +### Jetton to Toncoin {#swaps-jetton-to-toncoin} -### Jetton to Jetton +Jetton to Toncoin swap is very similar to [Jetton to Jetton swap](#swaps-jetton-to-jetton) with the only difference that the `RouterJettonWallet{:tact}` address is replaced with `RouterProxyTonWallet{:tact}`. + +:::caution + + The following example uses STON.fi's API v2, which is currently under development. Thus, all addresses are given in [testnet][testnet]. + + In addition, some variables such as `offerAmount` are hardcoded for demonstration purposes. Don't forget to change them in real life scenarios. + +::: ```tact -const RouterAddress: Address = address("kQALh-JBBIKK7gr0o4AVf9JZnEsFndqO0qTCyT-D-yBsWk0v"); // CPI Router v2.1.0 -const RouterJettonWallet: Address = address("kQAtX3x2s-wMtYTz8CfmAyloHAB73vONzJM5S2idqXl-_5xK"); // Router Jetton Wallet Address +// CPI Router v2.1.0 +const RouterAddress: Address = + address("kQALh-JBBIKK7gr0o4AVf9JZnEsFndqO0qTCyT-D-yBsWk0v"); + +// Router's pTON address +const RouterProxyTonWallet: Address = + address("kQBbJjnahBMGbMUJwhAXLn8BiigcGXMJhSC0l7DBhdYABhG7"); + +/// NOTE: To calculate and provide Jetton wallet address for the target user, +/// make sure to check links after this code snippet +fun jettonToToncoin(myJettonWalletAddress: Address) { + // Amount of Jettons to swap + let offerAmount: Int = 100_000; + + // Prepare the payload + let forwardPayload = StonfiSwap{ + otherTokenWallet: RouterProxyTonWallet, + refundAddress: myAddress(), + excessesAddress: myAddress(), + // Deadline is set to 10,000 seconds from now + deadline: now() + 10_000, + additionalData: SwapAdditionalData{ receiverAddress: myAddress() }, + }; + + // Start a swap with the message to the Jetton wallet + send(SendParameters{ + to: myJettonWalletAddress, + value: FeeSwapJettonToToncoin, + body: JettonTransfer{ + queryId: 42, + amount: offerAmount, + destination: RouterAddress, + responseDestination: myAddress(), + forwardTonAmount: FeeSwapJettonToToncoinFwd, + forwardPayload: forwardPayload.toCell(), + }.toCell(), + }); +} -message SwapJettonJetton { - myJettonWalletAddress: Address; +// +// Helper Messages, Structs and constants described earlier on this page +// + +message(0x6664de2a) StonfiSwap { + otherTokenWallet: Address; + refundAddress: Address; + excessesAddress: Address; + deadline: Int as uint64; + additionalData: SwapAdditionalData; } -contract JettonToJetton { - receive() {} - receive(msg: SwapJettonJetton) { - // This example directly uses the `myJettonWalletAddress` provided in the received message. - // In real-world scenarios, it's more reliable to calculate this address on-chain or save it during initialization to ensure accuracy and trustworthiness. - let myJettonWalletAddress: Address = msg.myJettonWalletAddress; +struct SwapAdditionalData { + minOut: Int as coins = 1; + receiverAddress: Address; + fwdGas: Int as coins = 0; + customPayload: Cell? = null; + refundFwdGas: Int as coins = 0; + refundPayload: Cell? = null; + refFee: Int as uint16 = 10; + referralAddress: Address? = null; +} - let offerAmount: Int = 100000; // the quantity of jettons being swapped +const FeeSwapJettonToToncoin: Int = ton("0.3"); +const FeeSwapJettonToToncoinFwd: Int = ton("0.24"); - let forwardPayload = StonfiSwap{ - otherTokenWallet: RouterJettonWallet, - refundAddress: myAddress(), - excessesAddress: myAddress(), - deadline: now() + 10000, - additionalData: SwapAdditionalData{ - minOut: 1, // swap will fail if a receiver should receive less than minOut of tokens as a result - receiver: myAddress(), - } - }; - - send(SendParameters{ - to: myJettonWalletAddress, - value: GasSwapJettonToJetton, - body: JettonTransfer{ - queryId: 42, - amount: offerAmount, - destination: RouterAddress, - responseDestination: myAddress(), - forwardTonAmount: GasSwapJettonToJettonFwd, - forwardPayload: forwardPayload.toCell(), - }.toCell() - }); - } +// +// Messages from the Jetton standard +// + +message(0xf8a7ea5) JettonTransfer { + queryId: Int as uint64; + amount: Int as coins; + destination: Address; + responseDestination: Address?; + customPayload: Cell? = null; + forwardTonAmount: Int as coins; + forwardPayload: Cell?; // slightly adjusted } ``` -### Jetton to TON +:::note[Useful links:] -Swapping jetton to TON is similar to jetton/jetton swap. The only one difference is that `RouterJettonWallet` address should be replaced with `RouterProxyTonWallet`. + [Retrieving Jetton wallet address in TON Docs][jetton-addr-online]\ + [How to calculate user's Jetton wallet address (offline)?][jetton-addr-offline]\ + [Discoverable Jetton wallets][discoverable-jetton-wallets]\ + [Fees Calculation in TON Docs][fees-calc] -```tact -const RouterAddress: Address = address("kQALh-JBBIKK7gr0o4AVf9JZnEsFndqO0qTCyT-D-yBsWk0v"); // CPI Router v2.1.0 -const RouterProxyTonWallet: Address = address("kQBbJjnahBMGbMUJwhAXLn8BiigcGXMJhSC0l7DBhdYABhG7"); // Router's pTON Address +::: -message SwapJettonTon { - myJettonWalletAddress: Address; -} +### Toncoin to Jetton {#swaps-jetton-to-toncoin} -contract JettonToTon { - receive() {} - receive(msg: SwapJettonTon) { - // This example directly uses the `myJettonWalletAddress` provided in the received message. - // In real-world scenarios, it's more reliable to calculate this address on-chain or save it during initialization to ensure accuracy and trustworthiness. - let myJettonWalletAddress: Address = msg.myJettonWalletAddress; +To swap Toncoin to Jetton, STON.fi requires the use of a so-called proxy Toncoin wallet (or pTON for short). To interact with it properly, we need to introduce a `ProxyToncoinTransfer{:tact}` [Message][message]: - let offerAmount: Int = 100000; // the quantity of jettons being swapped +```tact +/// https://github.com/ston-fi/sdk/blob/786ece758794bd5c575db8b38f5e5de19f43f0d1/packages/sdk/src/contracts/pTON/v2_1/PtonV2_1.ts +message(0x01f3835d) ProxyToncoinTransfer { + // Unique identifier used to trace transactions across multiple contracts + // Defaults to 0, which means we don't mark messages to trace their chains + queryId: Int as uint64 = 0; - let forwardPayload = StonfiSwap{ - otherTokenWallet: RouterProxyTonWallet, - refundAddress: myAddress(), - excessesAddress: myAddress(), - deadline: now() + 10000, - additionalData: SwapAdditionalData{ - minOut: 1, // swap will fail if a receiver should receive less than minOut of tokens as a result - receiver: myAddress(), - } - }; - - send(SendParameters{ - to: myJettonWalletAddress, - value: GasSwapJettonToTon, - body: JettonTransfer{ - queryId: 42, - amount: offerAmount, - destination: RouterAddress, - responseDestination: myAddress(), - forwardTonAmount: GasSwapJettonToTonFwd, - forwardPayload: forwardPayload.toCell(), - }.toCell() - }); - } + // Toncoin amount for the swap + tonAmount: Int as coins; + + // Where to send refunds upon a failed swap + refundAddress: Address; + + // Optional custom payload to attach to the swap + // Defaults to `null` + forwardPayload: Cell?; } ``` -### TON to Jetton +Notice that `ProxyToncoinTransfer{:tact}` is quite similar to `JettonTransfer{:tact}`, except that it doesn't require any addresses other than the refund address, nor does it require any forward amounts to be specified. + +:::caution -TON to jetton swap introduces `ProxyTonTransfer` cause all interaction is done with pTON contract. + The following example uses STON.fi's API v2, which is currently under development. Thus, all addresses are given in [testnet][testnet]. -Jetton `pTON` is a specialized asset within the STON.fi ecosystem on the TON blockchain. It functions as an intermediary within the STON.fi ecosystem to manage and facilitate efficient TON transfers. + In addition, some variables such as `offerAmount` are hardcoded for demonstration purposes. Don't forget to change them in real life scenarios. + +::: ```tact -message(0x01f3835d) ProxyTonTransfer { +// Router's pTON wallet address +const RouterProxyTonWallet: Address + = address("kQBbJjnahBMGbMUJwhAXLn8BiigcGXMJhSC0l7DBhdYABhG7"); + +// Router's Jetton wallet address +const RouterJettonWallet: Address = + address("kQAtX3x2s-wMtYTz8CfmAyloHAB73vONzJM5S2idqXl-_5xK"); + +fun toncoinToJetton() { + // Amount of Toncoin to swap + let offerAmount: Int = 1_000; + + // Prepare the payload + let forwardPayload = StonfiSwap{ + otherTokenWallet: RouterJettonWallet, + refundAddress: myAddress(), + excessesAddress: myAddress(), + // Deadline is set to 10,000 seconds from now + deadline: now() + 10_000, + additionalData: SwapAdditionalData{ receiverAddress: myAddress() }, + }; + + // Start a swap with the message to the proxy Toncoin wallet + send(SendParameters{ + to: RouterProxyTonWallet, + value: FeeSwapToncoinToJetton + offerAmount, + body: ProxyToncoinTransfer{ + tonAmount: offerAmount, + refundAddress: myAddress(), + forwardPayload: forwardPayload.toCell(), + }.toCell(), + }); +} + +// +// Helper Messages, Structs and constants described earlier on this page +// + +message(0x01f3835d) ProxyToncoinTransfer { queryId: Int as uint64 = 0; tonAmount: Int as coins; refundAddress: Address; forwardPayload: Cell?; } -const RouterProxyTonWallet: Address = address("kQBbJjnahBMGbMUJwhAXLn8BiigcGXMJhSC0l7DBhdYABhG7"); // Router's pTON wallet Address -const RouterJettonWallet: Address = address("kQAtX3x2s-wMtYTz8CfmAyloHAB73vONzJM5S2idqXl-_5xK"); // Router's Jetton Wallet Address - -contract TonToJetton { - receive() {} - receive("ton-jetton") { - let offerAmount: Int = 100000; // the quantity of TON being swapped +message(0x6664de2a) StonfiSwap { + otherTokenWallet: Address; + refundAddress: Address; + excessesAddress: Address; + deadline: Int as uint64; + additionalData: SwapAdditionalData; +} - let forwardPayload = StonfiSwap{ - otherTokenWallet: RouterJettonWallet, - refundAddress: myAddress(), - excessesAddress: myAddress(), - deadline: now() + 10000, - additionalData: SwapAdditionalData{ - minOut: 1, // swap will fail if a receiver should receive less than minOut of tokens as a result - receiver: myAddress(), - } - }; - - send(SendParameters{ - to: RouterProxyTonWallet, - value: GasSwapTonToJetton + GasSwapTonToJettonFwd + offerAmount, - body: ProxyTonTransfer{ - queryId: 42, - tonAmount: offerAmount, - refundAddress: myAddress(), - forwardPayload: forwardPayload.toCell(), - }.toCell() - }); - } +struct SwapAdditionalData { + minOut: Int as coins = 1; + receiverAddress: Address; + fwdGas: Int as coins = 0; + customPayload: Cell? = null; + refundFwdGas: Int as coins = 0; + refundPayload: Cell? = null; + refFee: Int as uint16 = 10; + referralAddress: Address? = null; } + +const FeeSwapToncoinToJetton: Int = ton("0.3"); ``` +:::note[Useful links:] + + [Fees Calculation in TON Docs][fees-calc] + +::: + ## Liquidity provision -More about liquidity provision [here](https://docs.ston.fi/docs/developer-section/api-reference-v2/example_lp_provide). +Read more about liquidity provision in the [STON.fi documentation](https://docs.ston.fi/docs/developer-section/api-reference-v2/example_lp_provide). -It is possible to deposit liquidity by sending just 1 type of token, the pool will automatically perform a swap and use the resulting amount to mint lp tokens. Non-single side provision is done just with `bothPositive` flag equals `true`. +STON.fi allows you to deposit liquidity by specifying only one type of token - the pool will automatically perform the swap and mint liquidity provider (LP) tokens. To do this you need to set `bothPositive` field of `ProvideLiquidity{:tact}` [Message][message] to `false{:tact}`. -Liquidity provisions have several common structures and messages. Don't forget to use them with the code examples below! +Liquidity deposits use `ProvideLiquidity{:tact}` [Message][message] and `ProvideLiqudityAdditionalData{:tact}` [Struct][struct]: ```tact -message(0x37c096df) ProvideLP { - otherTokenWallet: Address; // Address of the other Router token wallet - refundAddress: Address; // Address where refund will be sent if swap fails - excessesAddress: Address; // Address where TON excesses will be sent - deadline: Int as uint64; // Timestamp of execution deadline for this tx - additionalData: ProvideLPAdditionalData; +/// https://docs.ston.fi/docs/developer-section/api-reference-v2/router#provide_lp-0x37c096df +message(0x37c096df) ProvideLiquidity { + // Address of the other Router token wallet + otherTokenWallet: Address; + + // Where to send refunds if provisioning fails + refundAddress: Address; + + // Where to send excesses if provisioning succeeds + excessesAddress: Address; + + // UNIX timestamp of execution deadline for the provisioning + deadline: Int as uint64; + + // Reference to another Cell with additional data, + // using the Tact's greedy auto-layout mechanism + additionalData: ProvideLiquidityAdditionalData; } -struct ProvideLPAdditionalData { - minLpOut: Int as coins; // Minimum required amount of lp tokens to receive - receiverAddress: Address; // Address where lp tokens will be sent - bothPositive: Bool = true; // Trigger liquidity deposit only if both token amounts are non-zero - fwdGas: Int as coins = 0; // Gas used to forward a message in transfer notification after mint if customPayload is present +/// https://docs.ston.fi/docs/developer-section/api-reference-v2/router#additional_data-body-1 +struct ProvideLiquidityAdditionalData { + // Minimum required amount of LP tokens to receive + // Defaults to 1, which causes the provisioning to fail + // only if no tokens are received + minLpOut: Int as coins = 1; + + // Where to send LP tokens if provisioning succeeds + receiverAddress: Address; + + // Should both tokens in a pair have a positive quantity? + // If not, then the pool would perform an additional swap for the lacking token + // Defaults to `true`, which means that deposit would only go through + // when both token amounts are non-zero + bothPositive: Bool = true; + + // Forward fees for the `customPayload` if it's not `null` + // Defaults to 0 + fwdGas: Int as coins = 0; + + // Custom payload that will be sent if provisioning succeeds + // Defaults to `null`, which means no payload customPayload: Cell? = null; } ``` -Below are shown gas constants. All of them are taken from the sdk [STON.fi sdk](https://github.com/ston-fi/sdk). They are slightly differ from reality, but it is ok for now. +The [STON.fi SDK](https://github.com/ston-fi/sdk) defines some [constants to deal with fees](https://github.com/ston-fi/sdk/blob/786ece758794bd5c575db8b38f5e5de19f43f0d1/packages/sdk/src/contracts/dex/v2_1/router/BaseRouterV2_1.ts). Note that these are hardcoded values, but the best practice is to [calculate fees dynamically using current config params](https://docs.ton.org/v3/guidelines/smart-contracts/fee-calculation) instead. ```tact -const SingleSideProvideLpJettonGas: Int = ton("1"); -const SingleSideProvideLpJettonGasFwd: Int = ton("0.8"); -const SingleSideProvideLpTonGas: Int = ton("0.8"); -const TonTransferGas: Int = ton("0.01"); +/// Hardcoded fee value to pay for sending a liquidity provisioning message +/// when depositing a certain amount of Jettons +const FeeSingleSideProvideLpJetton: Int = ton("1"); + +/// Hardcoded fee value to pay forward fees of subsequent messages for liquidity provisioning +const FeeSingleSideProvideLpJettonFwd: Int = ton("0.8"); + +/// Hardcoded fee value to pay for sending a liquidity provisioning message +/// when depositing a certain amount of Toncoins +const FeeSingleSideProvideLpToncoin: Int = ton("0.01") + ton("0.8"); ``` +:::note[Useful links:] + + [Fees Calculation in TON Docs][fees-calc] + +::: + ### Jetton deposit +:::caution + + The following example uses STON.fi's API v2, which is currently under development. Thus, all addresses are given in [testnet][testnet]. + + In addition, some variables such as `offerAmount` are hardcoded for demonstration purposes. Don't forget to change them in real life scenarios. + +::: ```tact +// CPI Router v2.1.0 +const RouterAddress: Address = + address("kQALh-JBBIKK7gr0o4AVf9JZnEsFndqO0qTCyT-D-yBsWk0v"); + +// Router's pTON wallet address +const RouterProxyTonWallet: Address = + address("kQBbJjnahBMGbMUJwhAXLn8BiigcGXMJhSC0l7DBhdYABhG7"); + +// Router's Jetton wallet address +const RouterJettonWallet: Address = + address("kQAtX3x2s-wMtYTz8CfmAyloHAB73vONzJM5S2idqXl-_5xK"); + +/// NOTE: To calculate and provide Jetton wallet address for the target user, +/// make sure to check links after this code snippet +fun jettonDeposit(myJettonWalletAddress: Address) { + // Amount of Jettons for liquidity provisioning + let offerAmount = 100_000; + + // Prepare the payload + let forwardPayload = ProvideLiquidity{ + otherTokenWallet: RouterProxyTonWallet, + refundAddress: myAddress(), + excessesAddress: myAddress(), + // Deadline is set to 1,000 seconds from now + deadline: now() + 1_000, + additionalData: ProvideLiquidityAdditionalData{ + receiverAddress: myAddress(), + bothPositive: false, // i.e. single side + }, + }; + + send(SendParameters{ + to: myJettonWalletAddress, + value: FeeSingleSideProvideLpJetton, + body: JettonTransfer{ + queryId: 42, + amount: offerAmount, + destination: RouterAddress, + responseDestination: myAddress(), + forwardTonAmount: FeeSingleSideProvideLpJettonFwd, + forwardPayload: forwardPayload.toCell(), + }.toCell(), + }); +} + +// +// Helper Messages, Structs and constants described earlier on this page +// + +message(0x37c096df) ProvideLiquidity { + otherTokenWallet: Address; + refundAddress: Address; + excessesAddress: Address; + deadline: Int as uint64; + additionalData: ProvideLiquidityAdditionalData; +} + +struct ProvideLiquidityAdditionalData { + minLpOut: Int as coins = 1; + receiverAddress: Address; + bothPositive: Bool = true; + fwdGas: Int as coins = 0; + customPayload: Cell? = null; +} + +const FeeSingleSideProvideLpJetton: Int = ton("1"); +const FeeSingleSideProvideLpJettonFwd: Int = ton("0.8"); + +// +// Messages from the Jetton standard +// + message(0xf8a7ea5) JettonTransfer { queryId: Int as uint64; amount: Int as coins; @@ -262,105 +584,102 @@ message(0xf8a7ea5) JettonTransfer { responseDestination: Address?; customPayload: Cell? = null; forwardTonAmount: Int as coins; - forwardPayload: Cell?; // hack for now, equivalent to Slice as remaining with first bit set to 1 + forwardPayload: Cell?; // slightly adjusted } +``` -message SampleProvideLP { - myJettonWalletAddress: Address; -} +### Toncoin deposit -const RouterAddress: Address = address("kQALh-JBBIKK7gr0o4AVf9JZnEsFndqO0qTCyT-D-yBsWk0v"); // CPI Router v2.1.0 -const RouterProxyTonWallet: Address = address("kQBbJjnahBMGbMUJwhAXLn8BiigcGXMJhSC0l7DBhdYABhG7"); // Router's pTON wallet Address -const RouterJettonWallet: Address = address("kQAtX3x2s-wMtYTz8CfmAyloHAB73vONzJM5S2idqXl-_5xK"); // Router's Jetton Wallet Address +:::caution -contract Sample { - receive() {} - receive(msg: SampleProvideLP) { - // This example directly uses the `myJettonWalletAddress` provided in the received message. - // In real-world scenarios, it's more reliable to calculate this address on-chain or save it during initialization to ensure accuracy and trustworthiness. - let myJettonWalletAddress = msg.myJettonWalletAddress; + The following example uses STON.fi's API v2, which is currently under development. Thus, all addresses are given in [testnet][testnet]. - let offerAmount = 1000000; // the quantity of jettons being transferred for liquidity provisioning + In addition, some variables such as `offerAmount` are hardcoded for demonstration purposes. Don't forget to change them in real life scenarios. - let forwardPayload = ProvideLP{ - otherTokenWallet: RouterProxyTonWallet, +::: + +```tact +// Router's pTON wallet address +const RouterProxyTonWallet: Address = + address("kQBbJjnahBMGbMUJwhAXLn8BiigcGXMJhSC0l7DBhdYABhG7"); + +// Router's Jetton wallet address +const RouterJettonWallet: Address = + address("kQAtX3x2s-wMtYTz8CfmAyloHAB73vONzJM5S2idqXl-_5xK"); + +fun toncoinDeposit() { + // Amount of Jettons for liquidity provisioning + let offerAmount = 100_000; + + // Prepare the payload + let forwardPayload = ProvideLiquidity{ + otherTokenWallet: RouterJettonWallet, + refundAddress: myAddress(), + excessesAddress: myAddress(), + deadline: now() + 1000, + additionalData: ProvideLiquidityAdditionalData{ + receiverAddress: myAddress(), + bothPositive: false, // i.e. single side + }, + }; + + send(SendParameters{ + to: RouterProxyTonWallet, + value: FeeSingleSideProvideLpToncoin + offerAmount, + body: ProxyToncoinTransfer{ + queryId: 42, + tonAmount: offerAmount, refundAddress: myAddress(), - excessesAddress: myAddress(), - deadline: now() + 1000, - additionalData: ProvideLPAdditionalData{ - minLpOut: 1, - receiverAddress: myAddress(), - bothPositive: false, // false means single side - } - }; - - send(SendParameters{ - to: myJettonWalletAddress, - value: SingleSideProvideLpJettonGas, - body: JettonTransfer{ - queryId: 42, - amount: offerAmount, - destination: RouterAddress, - responseDestination: myAddress(), - forwardTonAmount: SingleSideProvideLpJettonGasFwd, - forwardPayload: forwardPayload.toCell(), - }.toCell(), - }); - } + forwardPayload: forwardPayload.toCell(), + }.toCell(), + }); } -``` -### TON Deposit +// +// Helper Messages, Structs and constants described earlier on this page +// -```tact -message(0x01f3835d) ProxyTonTransfer { +message(0x01f3835d) ProxyToncoinTransfer { queryId: Int as uint64 = 0; tonAmount: Int as coins; refundAddress: Address; forwardPayload: Cell?; } -const RouterProxyTonWallet: Address = address("kQBbJjnahBMGbMUJwhAXLn8BiigcGXMJhSC0l7DBhdYABhG7"); // Router's pTON wallet Address -const RouterJettonWallet: Address = address("kQAtX3x2s-wMtYTz8CfmAyloHAB73vONzJM5S2idqXl-_5xK"); // Router's Jetton Wallet Address - -contract Sample { - receive() {} - receive("provide-lp-ton") { - let offerAmount = 1000000; // the quantity of TON being transferred for liquidity provisioning +message(0x37c096df) ProvideLiquidity { + otherTokenWallet: Address; + refundAddress: Address; + excessesAddress: Address; + deadline: Int as uint64; + additionalData: ProvideLiquidityAdditionalData; +} - let forwardPayload = ProvideLP{ - otherTokenWallet: RouterJettonWallet, - refundAddress: myAddress(), - excessesAddress: myAddress(), - deadline: now() + 1000, - additionalData: ProvideLPAdditionalData{ - minLpOut: 1, - receiverAddress: myAddress(), - bothPositive: false, // false means single side - } - }; - - send(SendParameters{ - to: RouterProxyTonWallet, - value: SingleSideProvideLpTonGas + TonTransferGas + offerAmount, - body: ProxyTonTransfer{ - queryId: 42, - tonAmount: offerAmount, - refundAddress: myAddress(), - forwardPayload: forwardPayload.toCell(), - }.toCell(), - }); - } +struct ProvideLiquidityAdditionalData { + minLpOut: Int as coins = 1; + receiverAddress: Address; + bothPositive: Bool = true; + fwdGas: Int as coins = 0; + customPayload: Cell? = null; } + +const FeeSingleSideProvideLpToncoin: Int = ton("0.01") + ton("0.8"); ``` ### Withdraw liquidly -Liquidly is withdrawn from a pool by burning lp tokens, a user then receives both pool tokens based on the current exchange rate. -You can refer to examples of jetton burning [here](/cookbook/jettons#burning-jetton). +To withdraw liquidity, burning LP tokens is required. You can refer to examples of Jetton burning in the [respective section of Jettons Cookbook page](/cookbook/jettons#burning-jetton). However, more Toncoins should be added than for the normal burn, since adding too few may result in LP tokens being burned, but no (or only partial) liquidity being sent from the pool. Therefore, consider attaching at least $0.5$ Toncoin — excess amount will be returned. :::tip[Hey there!] Didn't find your favorite example of STON.fi interaction? Have cool implementations in mind? [Contributions are welcome!](https://github.com/tact-lang/tact/issues) ::: + +[struct]: /book/structs-and-messages#structs +[message]: /book/structs-and-messages#messages + +[testnet]: https://docs.ton.org/v3/documentation/smart-contracts/getting-started/testnet +[jetton-addr-online]: https://docs.ton.org/develop/dapps/asset-processing/jettons#retrieving-jetton-wallet-addresses-for-a-given-user +[jetton-addr-offline]: https://docs.ton.org/v3/guidelines/dapps/cookbook#how-to-calculate-users-jetton-wallet-address-offline +[discoverable-jetton-wallets]: https://github.com/ton-blockchain/TEPs/blob/master/text/0089-jetton-wallet-discovery.md +[fees-calc]: https://docs.ton.org/v3/guidelines/smart-contracts/fee-calculation