|
2 | 2 |
|
3 | 3 | import { Callout } from 'nextra-theme-docs';
|
4 | 4 |
|
5 |
| -{/* See: https://nextra.site/docs/guide/built-ins */} |
6 |
| - |
7 | 5 | [DeDust](https://dedust.io) is a decentralized exchange (DEX) and automated market maker (AMM) built natively on [TON Blockchain](https://ton.org) and [DeDust Protocol 2.0](https://docs.dedust.io/reference/tlb-schemes). DeDust is designed with a meticulous attention to user experience (UX), gas efficiency, and extensibility.
|
8 | 6 |
|
9 | 7 | ## Swaps
|
@@ -152,3 +150,198 @@ fun makeJettonSwap() {
|
152 | 150 | });
|
153 | 151 | }
|
154 | 152 | ```
|
| 153 | + |
| 154 | + |
| 155 | +## Liquidity Provisioning |
| 156 | + |
| 157 | +To provide liquidity to a specific DeDust pool, you must supply both assets. After doing so, the pool issues LP tokens to the depositor's address. |
| 158 | + |
| 159 | +You can read more about liquidity provisioning on [DeDust documentation](https://docs.dedust.io/docs/liquidity-provisioning). |
| 160 | + |
| 161 | +```tact |
| 162 | +const PoolTypeVolatile: Int = 0; |
| 163 | +const PoolTypeStable: Int = 1; |
| 164 | +
|
| 165 | +const AssetTypeNative: Int = 0b0000; |
| 166 | +const AssetTypeJetton: Int = 0b0001; |
| 167 | +
|
| 168 | +const JettonProvideLpGas: Int = ton("0.5"); |
| 169 | +const JettonProvideLpGasFwd: Int = ton("0.4"); |
| 170 | +const TonProvideLpGas: Int = ton("0.15"); |
| 171 | +
|
| 172 | +const JettonMaster: Address = address("kQDkRHlWaibL7Tww48T6xAUFevPflca7i8TIiQTacBmnDOrb"); |
| 173 | +//const JettonMasterRaw: RawAddress = JettonMaster.toRaw(); TODO: maybe there is contants evaluation in tact? Did not find anything |
| 174 | +const JettonVault: Address = address("kQDcUuH4xhKejjilZAIeGuBh5JRWpzbVDcO9Qfh_Q_K4q9vk"); |
| 175 | +const TonVault: Address = address("EQDa4VOnTYlLvDJ0gZjNYm5PXfSmmtL6Vs6A_CZEtXCNICq_"); |
| 176 | +
|
| 177 | +struct RawAddress { |
| 178 | + workchain: Int as uint8; |
| 179 | + hash: Int as uint256; |
| 180 | +} |
| 181 | +
|
| 182 | +extends fun toRaw(self: Address): RawAddress { |
| 183 | + let addressSlice: Slice = self.asSlice(); |
| 184 | +
|
| 185 | + addressSlice.skipBits(3); |
| 186 | + return RawAddress { |
| 187 | + workchain: addressSlice.loadUint(8), |
| 188 | + hash: addressSlice.loadUint(256), |
| 189 | + } |
| 190 | +} |
| 191 | +
|
| 192 | +message(0xf8a7ea5) JettonTransfer { |
| 193 | + queryId: Int as uint64; |
| 194 | + amount: Int as coins; |
| 195 | + destination: Address; |
| 196 | + responseDestination: Address?; |
| 197 | + customPayload: Cell? = null; |
| 198 | + forwardTonAmount: Int as coins; |
| 199 | + forwardPayload: Cell?; |
| 200 | +} |
| 201 | +
|
| 202 | +struct Asset { |
| 203 | + type: Int as uint4; |
| 204 | + workchain: Int? as uint8 = null; |
| 205 | + hash: Int? as uint256 = null; |
| 206 | +} |
| 207 | +
|
| 208 | +extends fun build(self: Asset): Cell { |
| 209 | + let assetBuilder = beginCell() |
| 210 | + .storeUint(self.type, 4); |
| 211 | +
|
| 212 | + if (self.type == AssetTypeNative) { |
| 213 | + return assetBuilder.endCell(); |
| 214 | + } |
| 215 | + if (self.type == AssetTypeJetton) { |
| 216 | + return assetBuilder |
| 217 | + .storeUint(self.workchain!!, 8) |
| 218 | + .storeUint(self.hash!!, 256) |
| 219 | + .endCell(); |
| 220 | + } |
| 221 | +
|
| 222 | + require(false, "Unknown asset type"); |
| 223 | + return beginCell().endCell(); |
| 224 | +} |
| 225 | +
|
| 226 | +message(0x40e108d6) JettonDepositLiquidity { |
| 227 | + poolType: Int as uint1; // PoolType |
| 228 | + asset0: Asset; |
| 229 | + asset1: Asset; |
| 230 | + minimalLpAmount: Int as coins = 0; |
| 231 | + targetBalances0: Int as coins; |
| 232 | + targetBalances1: Int as coins; |
| 233 | + fulfillPayload: Cell? = null; |
| 234 | + rejectPayload: Cell? = null; |
| 235 | +} |
| 236 | +
|
| 237 | +extends fun build(self: JettonDepositLiquidity): Cell { |
| 238 | + return beginCell() |
| 239 | + .storeUint(0x40e108d6, 32) |
| 240 | + .storeUint(self.poolType, 1) |
| 241 | + .storeSlice(self.asset0.build().asSlice()) |
| 242 | + .storeSlice(self.asset1.build().asSlice()) |
| 243 | + .storeCoins(self.minimalLpAmount) |
| 244 | + .storeCoins(self.targetBalances0) |
| 245 | + .storeCoins(self.targetBalances1) |
| 246 | + .storeMaybeRef(self.fulfillPayload) |
| 247 | + .storeMaybeRef(self.rejectPayload) |
| 248 | + .endCell(); |
| 249 | +} |
| 250 | +
|
| 251 | +message(0xd55e4686) NativeDepositLiquidity { |
| 252 | + queryId: Int as uint64; |
| 253 | + amount: Int as coins; |
| 254 | + poolType: Int as uint1; |
| 255 | + asset0: Asset; |
| 256 | + asset1: Asset; |
| 257 | + minimalLpAmount: Int as coins = 0; |
| 258 | + targetBalances0: Int as coins; |
| 259 | + targetBalances1: Int as coins; |
| 260 | + fulfillPayload: Cell? = null; |
| 261 | + rejectPayload: Cell? = null; |
| 262 | +} |
| 263 | +
|
| 264 | +extends fun build(self: NativeDepositLiquidity): Cell { |
| 265 | + return beginCell() |
| 266 | + .storeUint(0xd55e4686, 32) |
| 267 | + .storeUint(self.queryId, 64) |
| 268 | + .storeCoins(self.amount) |
| 269 | + .storeUint(self.poolType, 1) |
| 270 | + .storeSlice(self.asset0.build().asSlice()) |
| 271 | + .storeSlice(self.asset1.build().asSlice()) |
| 272 | + .storeRef( |
| 273 | + beginCell() |
| 274 | + .storeCoins(self.minimalLpAmount) |
| 275 | + .storeCoins(self.targetBalances0) |
| 276 | + .storeCoins(self.targetBalances1) |
| 277 | + .endCell() |
| 278 | + ) |
| 279 | + .storeMaybeRef(self.fulfillPayload) |
| 280 | + .storeMaybeRef(self.rejectPayload) |
| 281 | + .endCell(); |
| 282 | +} |
| 283 | +
|
| 284 | +
|
| 285 | +message ProvideLp { |
| 286 | + myJettonWalletAddress: Address; // calculated offchain for ease of example, in real world scenarios should be calculated onchain |
| 287 | +} |
| 288 | +
|
| 289 | +contract Example { |
| 290 | + receive() {} |
| 291 | + receive(msg: ProvideLp) { |
| 292 | + let jettonMasterRaw: RawAddress = JettonMaster.toRaw(); |
| 293 | +
|
| 294 | + // Step 1. Prepare input |
| 295 | + let jettonAmount = ton("1"); |
| 296 | + let tonAmount = ton("1"); |
| 297 | +
|
| 298 | + let asset0 = Asset{ |
| 299 | + type: AssetTypeNative, |
| 300 | + }; |
| 301 | + let asset1 = Asset{ |
| 302 | + type: AssetTypeJetton, |
| 303 | + workchain: jettonMasterRaw.workchain, |
| 304 | + hash: jettonMasterRaw.hash, |
| 305 | + }; |
| 306 | +
|
| 307 | + // Step 2. Deposit Jetton to Vault |
| 308 | + let jettonDepositBody = JettonDepositLiquidity{ |
| 309 | + poolType: PoolTypeVolatile, |
| 310 | + asset0, |
| 311 | + asset1, |
| 312 | + targetBalances0: tonAmount, |
| 313 | + targetBalances1: jettonAmount, |
| 314 | + }.build(); |
| 315 | +
|
| 316 | + send(SendParameters{ |
| 317 | + to: msg.myJettonWalletAddress, |
| 318 | + value: JettonProvideLpGas, |
| 319 | + body: JettonTransfer{ |
| 320 | + queryId: 42, |
| 321 | + amount: jettonAmount, |
| 322 | + destination: JettonVault, |
| 323 | + responseDestination: myAddress(), |
| 324 | + forwardTonAmount: JettonProvideLpGasFwd, |
| 325 | + forwardPayload: jettonDepositBody, |
| 326 | + }.toCell() |
| 327 | + }); |
| 328 | +
|
| 329 | + // Step 3. Deposit TON to Vault |
| 330 | + let nativeDepositBody = NativeDepositLiquidity{ |
| 331 | + queryId: 42, |
| 332 | + amount: tonAmount, |
| 333 | + poolType: PoolTypeVolatile, |
| 334 | + asset0, |
| 335 | + asset1, |
| 336 | + targetBalances0: tonAmount, |
| 337 | + targetBalances1: jettonAmount, |
| 338 | + }.build(); |
| 339 | +
|
| 340 | + send(SendParameters{ |
| 341 | + to: TonVault, |
| 342 | + value: tonAmount + TonProvideLpGas, |
| 343 | + body: nativeDepositBody, |
| 344 | + }); |
| 345 | + } |
| 346 | +} |
| 347 | +``` |
0 commit comments