Skip to content

Commit 9bb2304

Browse files
Purple Guyvlad0137645rewitter-deland
authoredJun 16, 2024
add Defi 0.1.0 | fix Core 0.3.4 (continuation-team#104)
* GITBOOK-35: No subject * GITBOOK-36: No subject * Add StackItem parsing | Client 0.3.2 * implement JettonMinter.cs * Add NftCollection.cs * GITBOOK-37: No subject * update TonSdk.Contracts.csproj * GITBOOK-38: No subject * GITBOOK-39: No subject * GITBOOK-40: No subject * add sse client cancelation on disconnect * added tg wallet support * GITBOOK-42: No subject * update deps version * Update Transformers.cs continuation-team#86 (continuation-team#87) Co-authored-by: Purple Guy <89442150+purpleguy99@users.noreply.github.com> * GITBOOK-45: No subject * add TonCenter v3, TonWhales API * remove console writeline * make TonClient.cs disposable (continuation-team#90) * change package version, fix stack item parsing * improve: compatible with double types with more decimal places (continuation-team#98) * chore: make KeyPair usage simpler and more robust (continuation-team#95) * GITBOOK-46: No subject * add TonSdk.DeFi --------- Co-authored-by: vlad013 <53117013+vlad013@users.noreply.github.com> Co-authored-by: Daniel <89273037+7645re@users.noreply.github.com> Co-authored-by: witter-deland <87846830+witter-deland@users.noreply.github.com>
1 parent 5e85805 commit 9bb2304

19 files changed

+745
-57
lines changed
 

‎LICENSE

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
MIT License
22

3-
Copyright (c) 2023 Continuation Team
3+
Copyright (c) 2024 Continuation Team
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

‎README.md

+5
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,11 @@ Library to work with Ton Connect 2.0
2727
[![NuGet](https://img.shields.io/nuget/vpre/TonSdk.Adnl.svg)](https://www.nuget.org/packages/TonSdk.Adnl) \
2828
Library to work with Ton ADNL
2929

30+
### [TonSdk.DeFi](https://www.nuget.org/packages/TonSdk.DeFi/)
31+
[![NuGet](https://img.shields.io/nuget/dt/TonSdk.Defi.svg)](https://www.nuget.org/packages/TonSdk.DeFi)
32+
[![NuGet](https://img.shields.io/nuget/vpre/TonSdk.DeFi.svg)](https://www.nuget.org/packages/TonSdk.DeFi) \
33+
Library to work with Ton DeFi`s
34+
3035
## Features and status
3136

3237
- [x] Builder, Cell, Slice

‎TonSdk.Core/src/Coins.cs

+95-43
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,25 @@
11
using System;
2+
using System.Diagnostics;
23
using System.Globalization;
34
using System.Numerics;
45
using System.Text.RegularExpressions;
56

6-
namespace TonSdk.Core {
7-
public class CoinsOptions {
7+
namespace TonSdk.Core
8+
{
9+
public class CoinsOptions
10+
{
811
public bool IsNano { get; set; }
912
public int Decimals { get; set; }
1013

11-
public CoinsOptions(bool IsNano = false, int Decimals = 9) {
14+
public CoinsOptions(bool IsNano = false, int Decimals = 9)
15+
{
1216
this.IsNano = IsNano;
1317
this.Decimals = Decimals;
1418
}
1519
}
1620

17-
public class Coins {
21+
public class Coins
22+
{
1823
private decimal Value { get; set; }
1924
private int Decimals { get; set; }
2025
private decimal Multiplier { get; set; }
@@ -24,24 +29,29 @@ public class Coins {
2429
/// </summary>
2530
/// <param name="value">The value of the coins.</param>
2631
/// <param name="options">Optional options for customizing the coins.</param>
27-
public Coins(object value, CoinsOptions? options = null) {
32+
public Coins(object value, CoinsOptions? options = null)
33+
{
2834
bool isNano = false;
2935
int decimals = 9;
3036

31-
if (options != null) {
37+
if (options != null)
38+
{
3239
isNano = options?.IsNano != null ? options.IsNano : false;
3340
decimals = options?.Decimals != null ? options.Decimals : 9;
3441
}
3542

36-
var _value = value?.ToString().Replace(",", ".");
43+
if (value is string valueStr)
44+
{
45+
value = valueStr.Replace(",", ".");
46+
}
3747

38-
CheckCoinsType(_value);
48+
CheckCoinsType(value);
3949
CheckCoinsDecimals(decimals);
4050

41-
decimal decimalValue = decimal.Parse(_value, new CultureInfo("en-US"));
42-
51+
TryConvertToDecimal(value, out decimal decimalValue);
4352
int digitsValue = GetDigitsAfterDecimalPoint(decimalValue);
44-
if (digitsValue > decimals) {
53+
if (digitsValue > decimals)
54+
{
4555
throw new Exception(
4656
$"Invalid Coins value, decimals places \"{digitsValue}\" can't be greater than selected \"{decimals}\"");
4757
}
@@ -56,7 +66,8 @@ public Coins(object value, CoinsOptions? options = null) {
5666
/// </summary>
5767
/// <param name="coins">The Coins to add.</param>
5868
/// <returns>A new Coins instance with the sum of the values.</returns>
59-
public Coins Add(Coins coins) {
69+
public Coins Add(Coins coins)
70+
{
6071
CheckCoins(coins);
6172
CompareCoinsDecimals(this, coins);
6273

@@ -69,7 +80,8 @@ public Coins Add(Coins coins) {
6980
/// </summary>
7081
/// <param name="coins">The Coins to subtract.</param>
7182
/// <returns>A new Coins instance with the difference of the values.</returns>
72-
public Coins Sub(Coins coins) {
83+
public Coins Sub(Coins coins)
84+
{
7385
CheckCoins(coins);
7486
CompareCoinsDecimals(this, coins);
7587

@@ -82,7 +94,8 @@ public Coins Sub(Coins coins) {
8294
/// </summary>
8395
/// <param name="value">The value to multiply by.</param>
8496
/// <returns>A new Coins instance with the multiplied value.</returns>
85-
public Coins Mul(object value) {
97+
public Coins Mul(object value)
98+
{
8699
CheckValue(value);
87100
CheckConvertibility(value);
88101

@@ -97,7 +110,8 @@ public Coins Mul(object value) {
97110
/// </summary>
98111
/// <param name="value">The value to divide by.</param>
99112
/// <returns>A new Coins instance with the divided value.</returns>
100-
public Coins Div(object value) {
113+
public Coins Div(object value)
114+
{
101115
CheckValue(value);
102116
CheckConvertibility(value);
103117

@@ -112,7 +126,8 @@ public Coins Div(object value) {
112126
/// </summary>
113127
/// <param name="coins">The Coins to compare.</param>
114128
/// <returns>True if the values are equal, false otherwise.</returns>
115-
public bool Eq(Coins coins) {
129+
public bool Eq(Coins coins)
130+
{
116131
CheckCoins(coins);
117132
CompareCoinsDecimals(this, coins);
118133
return Value == coins.Value;
@@ -123,7 +138,8 @@ public bool Eq(Coins coins) {
123138
/// </summary>
124139
/// <param name="coins">The Coins to compare.</param>
125140
/// <returns>True if the current value is greater, false otherwise.</returns>
126-
public bool Gt(Coins coins) {
141+
public bool Gt(Coins coins)
142+
{
127143
CheckCoins(coins);
128144
CompareCoinsDecimals(this, coins);
129145
return Value > coins.Value;
@@ -134,7 +150,8 @@ public bool Gt(Coins coins) {
134150
/// </summary>
135151
/// <param name="coins">The Coins to compare.</param>
136152
/// <returns>True if the current value is greater or equal, false otherwise.</returns>
137-
public bool Gte(Coins coins) {
153+
public bool Gte(Coins coins)
154+
{
138155
CheckCoins(coins);
139156
CompareCoinsDecimals(this, coins);
140157
return Value >= coins.Value;
@@ -145,7 +162,8 @@ public bool Gte(Coins coins) {
145162
/// </summary>
146163
/// <param name="coins">The Coins to compare.</param>
147164
/// <returns>True if the current value is less, false otherwise.</returns>
148-
public bool Lt(Coins coins) {
165+
public bool Lt(Coins coins)
166+
{
149167
CheckCoins(coins);
150168
CompareCoinsDecimals(this, coins);
151169
return Value < coins.Value;
@@ -156,7 +174,8 @@ public bool Lt(Coins coins) {
156174
/// </summary>
157175
/// <param name="coins">The Coins to compare.</param>
158176
/// <returns>True if the current value is less or equal, false otherwise.</returns>
159-
public bool Lte(Coins coins) {
177+
public bool Lte(Coins coins)
178+
{
160179
CheckCoins(coins);
161180
CompareCoinsDecimals(this, coins);
162181
return Value <= coins.Value;
@@ -190,7 +209,8 @@ public bool Lte(Coins coins) {
190209
/// Returns a string representation of the Coins value.
191210
/// </summary>
192211
/// <returns>A string representation of the Coins value.</returns>
193-
public override string ToString() {
212+
public override string ToString()
213+
{
194214
decimal value = Value / Multiplier;
195215
string formattedValue = value.ToString("F" + Decimals);
196216

@@ -203,66 +223,96 @@ public override string ToString() {
203223
return coins;
204224
}
205225

206-
private static void CheckCoinsType(object value) {
226+
private static void CheckCoinsType(object value)
227+
{
207228
if (IsValid(value) && IsConvertable(value)) return;
208229
if (IsCoins(value)) return;
209230

210231
throw new Exception("Invalid Coins value");
211232
}
212233

213-
private static void CheckCoinsDecimals(int decimals) {
214-
if (decimals < 0 || decimals > 18) {
234+
private static void CheckCoinsDecimals(int decimals)
235+
{
236+
if (decimals < 0 || decimals > 18)
237+
{
215238
throw new Exception("Invalid decimals value, must be 0-18");
216239
}
217240
}
218241

219-
private static void CheckCoins(Coins value) {
242+
private static void CheckCoins(Coins value)
243+
{
220244
//if (IsCoins(value)) return;
221245
//throw new Exception("Invalid value");
222246
}
223247

224-
private static void CompareCoinsDecimals(Coins a, Coins b) {
225-
if (a.Decimals != b.Decimals) {
248+
private static void CompareCoinsDecimals(Coins a, Coins b)
249+
{
250+
if (a.Decimals != b.Decimals)
251+
{
226252
throw new Exception("Can't perform mathematical operation of Coins with different decimals");
227253
}
228254
}
229255

230-
private static void CheckValue(object value) {
256+
private static void CheckValue(object value)
257+
{
231258
if (IsValid(value)) return;
232259
throw new Exception("Invalid value");
233260
}
234261

235-
private static void CheckConvertibility(object value) {
262+
private static void CheckConvertibility(object value)
263+
{
236264
if (IsConvertable(value)) return;
237265

238266
throw new Exception("Invalid value");
239267
}
240268

241-
private static bool IsValid(object value) {
269+
private static bool IsValid(object value)
270+
{
242271
return value is string || value is int || value is decimal || value is double || value is float ||
243272
value is long;
244273
}
245274

246-
private static bool IsConvertable(object value) {
247-
try {
248-
decimal.Parse(value.ToString(), new CultureInfo("en-US"));
275+
private static bool IsConvertable(object value)
276+
{
277+
return TryConvertToDecimal(value, out _);
278+
}
279+
280+
private static bool TryConvertToDecimal(object value, out decimal result)
281+
{
282+
var strValue = value.ToString();
283+
if (double.TryParse(strValue, NumberStyles.Float, CultureInfo.InvariantCulture,
284+
out var doubleValue))
285+
{
286+
result = (decimal)doubleValue;
249287
return true;
250288
}
251-
catch (Exception) {
252-
return false;
289+
290+
if (decimal.TryParse(strValue, NumberStyles.Float, CultureInfo.InvariantCulture,
291+
out var decimalValue))
292+
{
293+
result = decimalValue;
294+
return true;
253295
}
296+
297+
result = 0;
298+
return false;
254299
}
255300

256-
private static bool IsCoins(object value) {
301+
302+
private static bool IsCoins(object value)
303+
{
257304
return value is Coins;
258305
}
259306

260-
private static int GetDigitsAfterDecimalPoint(decimal number) {
307+
private static int GetDigitsAfterDecimalPoint(decimal number)
308+
{
261309
string[] parts = number.ToString(new CultureInfo("en-US")).Split('.');
262-
if (parts.Length == 2) {
310+
if (parts.Length == 2)
311+
{
263312
return parts[1].Length;
264313
}
265-
else {
314+
else
315+
{
266316
return 0;
267317
}
268318
}
@@ -273,7 +323,8 @@ private static int GetDigitsAfterDecimalPoint(decimal number) {
273323
/// <param name="value">The value in nano.</param>
274324
/// <param name="decimals">The number of decimal places.</param>
275325
/// <returns>A new Coins instance representing the value in nano.</returns>
276-
public static Coins FromNano(object value, int decimals = 9) {
326+
public static Coins FromNano(object value, int decimals = 9)
327+
{
277328
CheckCoinsType(value);
278329
CheckCoinsDecimals(decimals);
279330

@@ -284,14 +335,15 @@ public static Coins FromNano(object value, int decimals = 9) {
284335
/// Converts the value of the Coins instance to a BigInteger.
285336
/// </summary>
286337
/// <returns>A BigInteger representation of the value.</returns>
287-
public BigInteger ToBigInt() {
338+
public BigInteger ToBigInt()
339+
{
288340
return new BigInteger(Value);
289341
}
290-
342+
291343
/// <summary>
292344
/// Return the pointed value of the Coins instance in decimal.
293345
/// </summary>
294346
/// <returns>A Decimal representation of the value.</returns>
295347
public decimal ToDecimal() => Value / Multiplier;
296348
}
297-
}
349+
}

‎TonSdk.Core/src/TonSdk.Core.csproj

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
<LangVersion>8.0</LangVersion>
77
<RootNamespace>TonSdk.Core</RootNamespace>
88
<PackageId>TonSdk.Core</PackageId>
9-
<Version>0.3.3</Version>
9+
<Version>0.3.4</Version>
1010
<Authors>Continuation Team</Authors>
1111
<PackageDescription>Core library with types and structures for TON Blockchain</PackageDescription>
1212
<RepositoryUrl>https://github.com/continuation-team/TonSdk.NET</RepositoryUrl>

‎TonSdk.Core/src/crypto/KeyPair.cs

+21-8
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,33 @@
1-
using Org.BouncyCastle.Crypto;
1+
using System;
2+
using Org.BouncyCastle.Crypto;
23
using Org.BouncyCastle.Crypto.Parameters;
34
using Org.BouncyCastle.Crypto.Signers;
45
using TonSdk.Core.Boc;
56

6-
namespace TonSdk.Core.Crypto {
7-
public class KeyPair {
7+
namespace TonSdk.Core.Crypto
8+
{
9+
public class KeyPair
10+
{
811
private readonly byte[] _privateKey;
912
private readonly byte[] _publicKey;
1013
public byte[] PrivateKey => _privateKey;
1114
public byte[] PublicKey => _publicKey;
1215

13-
public KeyPair(byte[] privateKey, byte[] publicKey) {
16+
public KeyPair(byte[] privateKey, byte[] publicKey = null)
17+
{
18+
var privateKeyParams = new Ed25519PrivateKeyParameters(privateKey, 0);
19+
var generatedPublicKey = privateKeyParams.GeneratePublicKey();
20+
var encodedPublicKey = generatedPublicKey.GetEncoded();
21+
22+
if (encodedPublicKey != null && publicKey != encodedPublicKey)
23+
throw new ArgumentException("Public key does not match the private key.");
24+
1425
_privateKey = privateKey;
15-
_publicKey = publicKey;
26+
_publicKey = encodedPublicKey;
1627
}
1728

18-
private static byte[] SignDetached(byte[] hash, byte[] privateKey) {
29+
private static byte[] SignDetached(byte[] hash, byte[] privateKey)
30+
{
1931
Ed25519PrivateKeyParameters privateKeyParams = new Ed25519PrivateKeyParameters(privateKey, 0);
2032

2133
ISigner signer = new Ed25519Signer();
@@ -33,11 +45,12 @@ private static byte[] SignDetached(byte[] hash, byte[] privateKey) {
3345
/// <param name="data">The Cell data to sign.</param>
3446
/// <param name="key">The key used for signing.</param>
3547
/// <returns>The signature of the hashed Cell data.</returns>
36-
public static byte[] Sign(Cell data, byte[] key) {
48+
public static byte[] Sign(Cell data, byte[] key)
49+
{
3750
byte[] hash = data.Hash.ToBytes();
3851
byte[] signature = SignDetached(hash, key);
3952

4053
return signature;
4154
}
4255
}
43-
}
56+
}

‎TonSdk.Core/test/Coins.test.cs

+13-4
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ public void Test_ParsingExceptions()
1919
Assert.Throws<Exception>(() => new Coins(string.Empty), "Invalid value");
2020
Assert.Throws<Exception>(() => new Coins("cat"), "Invalid value");
2121
Assert.Throws<Exception>(() => new Coins("10.5d"), "Invalid value");
22-
Assert.Throws<Exception>(() => new Coins(20.555, new CoinsOptions(false, 0)), "Invalid Coins value, decimals places \"3\" can't be greater than selected \"0\"");
22+
Assert.Throws<Exception>(() => new Coins(20.555, new CoinsOptions(false, 0)),
23+
"Invalid Coins value, decimals places \"3\" can't be greater than selected \"0\"");
2324
}
2425

2526
[Test]
@@ -67,8 +68,10 @@ public void Test_CheckCoins()
6768
Assert.That(new Coins("10").IsNegative, Is.EqualTo(false));
6869
Assert.That(new Coins("10").IsPositive, Is.EqualTo(true));
6970
Assert.That(new Coins(0).IsZero, Is.EqualTo(true));
70-
Assert.DoesNotThrow(() =>new Coins(10).ToBigInt());
71-
Assert.That(new Coins("10,641462085").ToDecimal() == decimal.Parse("10,641462085"), Is.EqualTo(true));
71+
Assert.DoesNotThrow(() => new Coins(10).ToBigInt());
72+
var d1 = new Coins("10,641462085").ToDecimal();
73+
var d2=decimal.Parse("10,641462085");
74+
Assert.Equals(new Coins("10,641462085").ToDecimal() ,decimal.Parse("10,641462085") );
7275
}
7376

7477
[Test]
@@ -145,4 +148,10 @@ public void Test_FromNanoCoins()
145148
Assert.That(Coins.FromNano(9007199254740992).ToString(), Is.EqualTo("9007199,254740992"));
146149
Assert.That(Coins.FromNano("9007199254740992").ToString(), Is.EqualTo("9007199,254740992"));
147150
}
148-
}
151+
152+
[Test]
153+
public void Test_LittleDoubleNumber()
154+
{
155+
Assert.DoesNotThrow(() => { new Coins(0.00000001); });
156+
}
157+
}

‎TonSdk.DeFi/DeDust/DeDustAsset.cs

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
using TonSdk.Core;
2+
using TonSdk.Core.Boc;
3+
4+
namespace TonSdk.DeFi.DeDust
5+
{
6+
public class DeDustAsset
7+
{
8+
private readonly DeDustAssetType _type;
9+
private readonly Address? _address;
10+
11+
private DeDustAsset(DeDustAssetType type, Address? address = null)
12+
{
13+
_type = type;
14+
_address = address;
15+
}
16+
17+
public static DeDustAsset Native() =>
18+
new DeDustAsset(DeDustAssetType.Native);
19+
20+
public static DeDustAsset Jetton(Address minter) =>
21+
new DeDustAsset(DeDustAssetType.Jetton, minter);
22+
23+
public Cell ToCell()
24+
{
25+
var builder = new CellBuilder();
26+
switch (_type)
27+
{
28+
case DeDustAssetType.Native:
29+
builder
30+
.StoreUInt((uint)DeDustAssetType.Native, 4);
31+
break;
32+
case DeDustAssetType.Jetton:
33+
builder
34+
.StoreUInt((uint)DeDustAssetType.Jetton, 4)
35+
.StoreInt(_address.GetWorkchain(), 8)
36+
.StoreBytes(_address.GetHash());
37+
break;
38+
}
39+
40+
return builder.Build();
41+
}
42+
43+
}
44+
}

‎TonSdk.DeFi/DeDust/DeDustConstants.cs

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
using TonSdk.Core;
2+
3+
namespace TonSdk.DeFi.DeDust
4+
{
5+
public class DeDustConstants
6+
{
7+
public static readonly Address MainNetFactory = new Address("EQBfBWT7X2BHg9tXAxzhz2aKiNTU1tpt5NsiK0uSDW_YAJ67");
8+
}
9+
10+
public enum DeDustAssetType
11+
{
12+
Native = 0b0000,
13+
Jetton = 0b0001,
14+
}
15+
16+
public enum DeDustReadinessStatus
17+
{
18+
NotDeployed,
19+
NotReady,
20+
Ready,
21+
}
22+
}

‎TonSdk.DeFi/DeDust/DeDustFactory.cs

+89
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
using System;
2+
using System.Threading.Tasks;
3+
using TonSdk.Client;
4+
using TonSdk.Client.Stack;
5+
using TonSdk.Core;
6+
using TonSdk.Core.Boc;
7+
using TonSdk.DeFi.DeDust.Vault;
8+
9+
namespace TonSdk.DeFi.DeDust
10+
{
11+
public class DeDustFactory
12+
{
13+
private readonly Address _address;
14+
15+
protected DeDustFactory(Address address)
16+
{
17+
_address = address;
18+
}
19+
20+
public Address Address => _address;
21+
22+
public static DeDustFactory CreateFromAddress(Address address)
23+
=> new DeDustFactory(address);
24+
25+
private async Task<Address> GetPoolAddress(TonClient client, DeDustPoolType type, DeDustAsset[] assets)
26+
{
27+
if (assets.Length != 2)
28+
throw new ArgumentException("Assets array length must be equal to 2.");
29+
30+
try
31+
{
32+
var result = await client.RunGetMethod(
33+
_address,
34+
"get_pool_address",
35+
new IStackItem[]
36+
{
37+
new VmStackInt((int)type),
38+
new VmStackSlice(assets[0].ToCell().Parse()),
39+
new VmStackSlice(assets[1].ToCell().Parse())
40+
});
41+
return ((Cell)result.Value.Stack[0]).Parse().ReadAddress();
42+
}
43+
catch
44+
{
45+
return await GetPoolAddress(client, type, assets);
46+
}
47+
}
48+
49+
public async Task<DeDustPool> GetPool(TonClient client, DeDustPoolType type, DeDustAsset[] assets)
50+
{
51+
var poolAddress = await GetPoolAddress(client, type, assets);
52+
return DeDustPool.CreateFromAddress(poolAddress);
53+
}
54+
55+
private async Task<Address> GetVaultAddress(TonClient client, DeDustAsset asset)
56+
{
57+
try
58+
{
59+
var result = await client.RunGetMethod(
60+
_address,
61+
"get_vault_address",
62+
new IStackItem[]
63+
{
64+
new VmStackSlice()
65+
{
66+
Value = asset.ToCell().Parse()
67+
}
68+
});
69+
return ((Cell)result.Value.Stack[0]).Parse().ReadAddress();
70+
}
71+
catch (Exception e)
72+
{
73+
return await GetVaultAddress(client, asset);
74+
}
75+
}
76+
77+
public async Task<DeDustNativeVault> GetNativeVault(TonClient client)
78+
{
79+
var nativeVaultAddress = await GetVaultAddress(client, DeDustAsset.Native());
80+
return DeDustNativeVault.CreateFromAddress(nativeVaultAddress);
81+
}
82+
83+
public async Task<DeDustJettonVault> GetJettonVault(TonClient client, Address address)
84+
{
85+
var jettonVaultAddress = await GetVaultAddress(client, DeDustAsset.Jetton(address));
86+
return DeDustJettonVault.CreateFromAddress(jettonVaultAddress);
87+
}
88+
}
89+
}

‎TonSdk.DeFi/DeDust/DeDustPool.cs

+64
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
using System;
2+
using System.Numerics;
3+
using System.Threading.Tasks;
4+
using TonSdk.Client;
5+
using TonSdk.Client.Stack;
6+
using TonSdk.Core;
7+
using TonSdk.Core.Boc;
8+
9+
namespace TonSdk.DeFi.DeDust
10+
{
11+
public class DeDustPool
12+
{
13+
private readonly Address _address;
14+
15+
protected DeDustPool(Address address)
16+
{
17+
_address = address;
18+
}
19+
20+
public static DeDustPool CreateFromAddress(Address address)
21+
=> new DeDustPool(address);
22+
23+
public Address Address => _address;
24+
25+
public async Task<DeDustReadinessStatus> GetReadinessStatus(TonClient client)
26+
{
27+
try
28+
{
29+
var state = (await client.GetAddressInformation(_address)).Value.State;
30+
if(state != AccountState.Active)
31+
return DeDustReadinessStatus.NotDeployed;
32+
33+
var reserves = await GetReserves(client);
34+
return reserves[0] > BigInteger.Zero && reserves[1] > BigInteger.Zero
35+
? DeDustReadinessStatus.Ready
36+
: DeDustReadinessStatus.NotReady;
37+
}
38+
catch (Exception e)
39+
{
40+
return await GetReadinessStatus(client);
41+
}
42+
}
43+
44+
private async Task<BigInteger[]> GetReserves(ITonClient client)
45+
{
46+
try
47+
{
48+
var result = await client.RunGetMethod(_address, "get_reserves", Array.Empty<IStackItem>());
49+
return new []{ (BigInteger)result.Value.Stack[0], (BigInteger)result.Value.Stack[1]};
50+
}
51+
catch (Exception e)
52+
{
53+
return await GetReserves(client);
54+
}
55+
56+
}
57+
}
58+
59+
public enum DeDustPoolType : int
60+
{
61+
Volatile = 0,
62+
Stable = 1,
63+
}
64+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
using System;
2+
using System.Numerics;
3+
using System.Threading.Tasks;
4+
using TonSdk.Client;
5+
using TonSdk.Client.Stack;
6+
using TonSdk.Core;
7+
using TonSdk.Core.Boc;
8+
9+
namespace TonSdk.DeFi.DeDust.Vault
10+
{
11+
public class DeDustJettonVault
12+
{
13+
public static readonly uint DepositLiquidity = 0x40e108d6;
14+
public static readonly uint Swap = 0xe3a0d482;
15+
16+
private readonly Address _address;
17+
18+
protected DeDustJettonVault(Address address)
19+
{
20+
_address = address;
21+
}
22+
23+
public Address Address => _address;
24+
25+
public static DeDustJettonVault CreateFromAddress(Address address)
26+
=> new DeDustJettonVault(address);
27+
28+
public async Task<DeDustReadinessStatus> GetReadinessStatus(TonClient client)
29+
{
30+
try
31+
{
32+
var state = (await client.GetAddressInformation(_address)).Value.State;
33+
if (state != AccountState.Active)
34+
return DeDustReadinessStatus.NotDeployed;
35+
36+
var result = await client.RunGetMethod(_address, "is_ready", new IStackItem[] { });
37+
return (int)(BigInteger)result.Value.Stack[0] == 0
38+
? DeDustReadinessStatus.NotReady
39+
: DeDustReadinessStatus.Ready;
40+
}
41+
catch (Exception e)
42+
{
43+
return await GetReadinessStatus(client);
44+
}
45+
46+
}
47+
48+
public static Cell CreateSwapPayload(DeDustJettonSwapOptions options)
49+
{
50+
return new CellBuilder()
51+
.StoreUInt(Swap, 32)
52+
.StoreAddress(options.PoolAddress)
53+
.StoreUInt(0, 1)
54+
.StoreCoins(options.Limit ?? new Coins(0))
55+
.StoreOptRef(options.Next == null ? null : Utils.PackSwapSteps(options.Next))
56+
.StoreRef(Utils.PackSwapParams(options.SwapParams ?? new DeDustSwapParams()))
57+
.Build();
58+
}
59+
}
60+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
using System;
2+
using System.Threading.Tasks;
3+
using TonSdk.Client;
4+
using TonSdk.Contracts;
5+
using TonSdk.Core;
6+
using TonSdk.Core.Boc;
7+
8+
namespace TonSdk.DeFi.DeDust.Vault
9+
{
10+
public class DeDustNativeVault
11+
{
12+
public static readonly uint DepositLiquidity = 0xd55e4686;
13+
public static readonly uint Swap = 0xea06185d;
14+
15+
private readonly Address _address;
16+
17+
protected DeDustNativeVault(Address address)
18+
{
19+
_address = address;
20+
}
21+
22+
public Address Address => _address;
23+
24+
public static DeDustNativeVault CreateFromAddress(Address address)
25+
=> new DeDustNativeVault(address);
26+
27+
public async Task<DeDustReadinessStatus> GetReadinessStatus(TonClient client)
28+
{
29+
try
30+
{
31+
var state = (await client.GetAddressInformation(_address)).Value.State;
32+
return state !=
33+
AccountState.Active
34+
? DeDustReadinessStatus.NotDeployed
35+
: DeDustReadinessStatus.Ready;
36+
}
37+
catch (Exception e)
38+
{
39+
return await GetReadinessStatus(client);
40+
}
41+
}
42+
43+
public static Cell CreateSwapBody(DeDustNativeSwapOptions options)
44+
{
45+
return new CellBuilder()
46+
.StoreUInt(Swap, 32)
47+
.StoreUInt(options.QueryId ?? SmcUtils.GenerateQueryId(60), 64)
48+
.StoreCoins(options.Amount)
49+
.StoreAddress(options.PoolAddress)
50+
.StoreUInt(0, 1)
51+
.StoreCoins(options.Limit ?? new Coins(0))
52+
.StoreOptRef(options.Next == null ? null : Utils.PackSwapSteps(options.Next))
53+
.StoreRef(Utils.PackSwapParams(options.SwapParams ?? new DeDustSwapParams()))
54+
.Build();
55+
}
56+
}
57+
}

‎TonSdk.DeFi/DeDust/Vault/Utils.cs

+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
using TonSdk.Core;
2+
using TonSdk.Core.Boc;
3+
4+
namespace TonSdk.DeFi.DeDust.Vault
5+
{
6+
public class Utils
7+
{
8+
public static Cell? PackSwapParams(DeDustSwapParams swapParams)
9+
{
10+
return new CellBuilder()
11+
.StoreUInt(swapParams.Deadline ?? 0, 32)
12+
.StoreAddress(swapParams.RecipientAddress ?? null)
13+
.StoreAddress(swapParams.RecipientAddress ?? null)
14+
.StoreOptRef(swapParams.FulfillPayload ?? null)
15+
.StoreOptRef(swapParams.RejectPayload ?? null)
16+
.Build();
17+
}
18+
19+
public static Cell? PackSwapSteps(DeDustSwapStep[] swapSteps)
20+
{
21+
Cell? nextRef = null;
22+
for (int i = swapSteps.Length - 1; i >= 0; i--)
23+
{
24+
nextRef = new CellBuilder()
25+
.StoreAddress(swapSteps[i].PoolAddress)
26+
.StoreUInt(0, 1)
27+
.StoreCoins(swapSteps[i].Limit ?? new Coins(0))
28+
.StoreOptRef(nextRef).Build();
29+
}
30+
31+
return nextRef;
32+
}
33+
}
34+
35+
public struct DeDustJettonSwapOptions
36+
{
37+
public Address PoolAddress { get; set; }
38+
public Coins? Limit { get; set; }
39+
public DeDustSwapStep[]? Next { get; set; }
40+
public DeDustSwapParams? SwapParams { get; set; }
41+
}
42+
43+
public struct DeDustNativeSwapOptions
44+
{
45+
public ulong? QueryId { get; set; }
46+
public Coins Amount { get; set; }
47+
public Address PoolAddress { get; set; }
48+
public Coins? Limit { get; set; }
49+
public DeDustSwapStep[]? Next { get; set; }
50+
public DeDustSwapParams? SwapParams { get; set; }
51+
}
52+
53+
public struct DeDustSwapStep
54+
{
55+
public Address PoolAddress { get; set; }
56+
public Coins? Limit { get; set; }
57+
}
58+
59+
public struct DeDustSwapParams
60+
{
61+
public uint? Deadline { get; set; }
62+
public Address? RecipientAddress { get; set; }
63+
public Address? ReferralAddress { get; set; }
64+
public Cell? FulfillPayload { get; set; }
65+
public Cell? RejectPayload { get; set; }
66+
}
67+
}

‎TonSdk.DeFi/TonSdk.DeFi.csproj

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFrameworks>net6.0;net7.0;netstandard2.1</TargetFrameworks>
5+
<ImplicitUsings>disable</ImplicitUsings>
6+
<LangVersion>8.0</LangVersion>
7+
<RootNamespace>TonSdk.DeFi</RootNamespace>
8+
<PackageId>TonSdk.DeFi</PackageId>
9+
<Version>0.1.0</Version>
10+
<Authors>Continuation Team</Authors>
11+
<PackageDescription>Library for work with popular TON DeFis.</PackageDescription>
12+
<RepositoryUrl>https://github.com/continuation-team/TonSdk.NET</RepositoryUrl>
13+
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
14+
</PropertyGroup>
15+
16+
<PropertyGroup>
17+
<PackageReadmeFile>README.md</PackageReadmeFile>
18+
<PackageLicenseFile>LICENSE</PackageLicenseFile>
19+
</PropertyGroup>
20+
21+
<ItemGroup>
22+
<None Include="../README.md" Pack="true" PackagePath="" />
23+
<None Include="../LICENSE" Pack="true" PackagePath="" />
24+
</ItemGroup>
25+
26+
<ItemGroup>
27+
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
28+
<PackageReference Include="TonSdk.Client" Version="0.3.4" />
29+
<PackageReference Include="TonSdk.Contracts" Version="0.3.1" />
30+
<PackageReference Include="TonSdk.Core" Version="0.3.3" />
31+
</ItemGroup>
32+
33+
</Project>

‎TonSdk.NET.sln

+6
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TonSdk.Adnl", "TonSdk.Adnl\
2121
EndProject
2222
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TonSdk.Adnl.Tests", "TonSdk.Adnl\test\TonSdk.Adnl.Tests.csproj", "{69DD2D24-5D2E-4B03-B3B3-D661AD4B335C}"
2323
EndProject
24+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TonSdk.DeFi", "TonSdk.DeFi\TonSdk.DeFi.csproj", "{B5C72F69-66CE-495F-8E5D-B61CE8AAE9BE}"
25+
EndProject
2426
Global
2527
GlobalSection(SolutionConfigurationPlatforms) = preSolution
2628
Debug|Any CPU = Debug|Any CPU
@@ -63,6 +65,10 @@ Global
6365
{69DD2D24-5D2E-4B03-B3B3-D661AD4B335C}.Debug|Any CPU.Build.0 = Debug|Any CPU
6466
{69DD2D24-5D2E-4B03-B3B3-D661AD4B335C}.Release|Any CPU.ActiveCfg = Release|Any CPU
6567
{69DD2D24-5D2E-4B03-B3B3-D661AD4B335C}.Release|Any CPU.Build.0 = Release|Any CPU
68+
{B5C72F69-66CE-495F-8E5D-B61CE8AAE9BE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
69+
{B5C72F69-66CE-495F-8E5D-B61CE8AAE9BE}.Debug|Any CPU.Build.0 = Debug|Any CPU
70+
{B5C72F69-66CE-495F-8E5D-B61CE8AAE9BE}.Release|Any CPU.ActiveCfg = Release|Any CPU
71+
{B5C72F69-66CE-495F-8E5D-B61CE8AAE9BE}.Release|Any CPU.Build.0 = Release|Any CPU
6672
EndGlobalSection
6773
GlobalSection(SolutionProperties) = preSolution
6874
HideSolutionNode = FALSE

‎docs/.gitbook/assets/8f3dd4e-swaps.svg

+1
Loading

‎docs/SUMMARY.md

+2
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
* [Wallet](user-manual/contracts/wallet/README.md)
2424
* [Wallet V3, V4](user-manual/contracts/wallet/wallet-v3-v4.md)
2525
* [Preprocessed V2](user-manual/contracts/wallet/preprocessed-v2.md)
26+
* [DeFi](user-manual/defi/README.md)
27+
* [DeDust](user-manual/defi/dedust.md)
2628
* [Unity TonConnect 2.0](user-manual/unity-tonconnect-2.0/README.md)
2729
* [Getting Started](user-manual/unity-tonconnect-2.0/getting-started.md)
2830
* [Use cases](user-manual/use-cases/README.md)

‎docs/user-manual/defi/README.md

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# DeFi
2+

‎docs/user-manual/defi/dedust.md

+162
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
# DeDust
2+
3+
{% hint style="info" %}
4+
For example, we will use `WalletV4`. You can find more examples for different contracts [here](https://docs.tonsdk.net/user-manual/contracts/wallet).
5+
{% endhint %}
6+
7+
### Getting Started
8+
9+
Initialize the SDK in your code using:
10+
11+
```csharp
12+
Mnemonic mnemonic = new Mnemonic();
13+
WalletV4 wallet = new WalletV4(new WalletV4Options()
14+
{
15+
PublicKey = mnemonic.Keys.PublicKey
16+
});
17+
18+
TonClient tonClient = new TonClient(TonClientType.HTTP_TONCENTERAPIV2, new HttpParameters() {});
19+
DeDustFactory factory = DeDustFactory.CreateFromAddress(DeDustConstants.MainNetFactory);
20+
21+
Address jettonAddress = new Address(/* here address of jetton master */);
22+
```
23+
24+
25+
26+
### Swapping TON to Jetton
27+
28+
```csharp
29+
// define asset instances
30+
DeDustAsset nativeAsset = DeDustAsset.Native();
31+
DeDustAsset jettonAsset = DeDustAsset.Jetton(jettonAddress);
32+
33+
// get actual pool of swap pair
34+
DeDustPool pool = await factory.GetPool(tonClient, DeDustPoolType.Volatile, new[] { nativeAsset, jettonAsset });
35+
36+
// get native TON vault
37+
DeDustNativeVault tonVault = await factory.GetNativeVault(tonClient);
38+
39+
// check if vault of TON exists
40+
if (await tonVault.GetReadinessStatus(tonClient) != DeDustReadinessStatus.Ready)
41+
{
42+
Console.WriteLine("[Ton To Jetton] Vault (TON) does not exist.");
43+
return;
44+
}
45+
46+
// check if pool of swap pair exists
47+
if (await pool.GetReadinessStatus(tonClient) != DeDustReadinessStatus.Ready)
48+
{
49+
Console.WriteLine("[Ton To Jetton] Pool (TON, Jetton) does not exist.");
50+
return;
51+
}
52+
53+
// amount of TONs to swap
54+
double amount = 5;
55+
56+
// create swap body cell
57+
Cell swapToJettonBody = DeDustNativeVault.CreateSwapBody(new DeDustNativeSwapOptions()
58+
{
59+
PoolAddress = pool.Address,
60+
Amount = new Coins(amount)
61+
});
62+
63+
// getting seqno using tonClient
64+
uint? seqno = await tonClient.Wallet.GetSeqno(walletV4.Address);
65+
66+
// create transfer message to vault contract
67+
var msg = wallet.CreateTransferMessage(new WalletTransfer[]
68+
{
69+
new WalletTransfer
70+
{
71+
Message = new InternalMessage(new InternalMessageOptions
72+
{
73+
Info = new IntMsgInfo(new IntMsgInfoOptions
74+
{
75+
Dest = tonVault.Address,
76+
Value = new Coins(amount).Add(new Coins(0.25)) // gas amount, dont change
77+
}),
78+
Body = swapToJettonBody
79+
}),
80+
Mode = 1
81+
}
82+
}, seqno.Value).Sign(mnemonic.Keys.PrivateKey);
83+
84+
// send signed message
85+
await tonClient.SendBoc(msg.Cell);
86+
```
87+
88+
89+
90+
### Swapping Jetton to TON
91+
92+
```csharp
93+
// get jetton wallet address
94+
Address jettonWallet = await tonClient.Jetton.GetWalletAddress(jettonAddress, wallet.Address);
95+
96+
// get jetton vault
97+
DeDustJettonVault jettonVault = await factory.GetJettonVault(tonClient, jettonAddress);
98+
99+
// define asset instances
100+
DeDustAsset nativeAsset = DeDustAsset.Native();
101+
DeDustAsset jettonAsset = DeDustAsset.Jetton(jettonAddress);
102+
103+
// get actual pool of swap pair
104+
DeDustPool pool = await factory.GetPool(tonClient, DeDustPoolType.Volatile, new[] { nativeAsset, jettonAsset });
105+
106+
// check if vault of Jetton exists
107+
if (await jettonVault.GetReadinessStatus(Core.TonSecondClient) != DeDustReadinessStatus.Ready)
108+
{
109+
Console.WriteLine("[Jetton To Ton] Vault (TON) does not exist.");
110+
return;
111+
}
112+
113+
// check if pool of swap pair exists
114+
if (await pool.GetReadinessStatus(Core.TonSecondClient) != DeDustReadinessStatus.Ready)
115+
{
116+
Console.WriteLine("[Jetton To Ton] Pool (TON, Jetton) does not exist.");
117+
return;
118+
}
119+
120+
// jetton amount to swap
121+
double amount = 5;
122+
123+
// create jetton transfer options
124+
JettonTransferOptions options = new JettonTransferOptions()
125+
{
126+
Amount = new Coins(amount),
127+
Destination = jettonVault.Address,
128+
ResponseDestination = wallet.Address,
129+
ForwardAmount = new Coins(0.25), // gas, dont change
130+
ForwardPayload = DeDustJettonVault.CreateSwapPayload(new DeDustJettonSwapOptions()
131+
{
132+
PoolAddress = pool.Address
133+
})
134+
};
135+
136+
// create jetton tranfer cell
137+
Cell jettonTransfer = JettonWallet.CreateTransferRequest(options);
138+
139+
// getting seqno using tonClient
140+
uint? seqno = await tonClient.Wallet.GetSeqno(walletV4.Address);
141+
142+
// create transfer message to jetton wallet with swap body
143+
var msg = taskWallet.CreateTransferMessage(new WalletTransfer[]
144+
{
145+
new WalletTransfer
146+
{
147+
Message = new InternalMessage(new InternalMessageOptions
148+
{
149+
Info = new IntMsgInfo(new IntMsgInfoOptions
150+
{
151+
Dest = jettonWallet,
152+
Value = new Coins(0.3)
153+
}),
154+
Body = jettonTransfer
155+
}),
156+
Mode = 1
157+
}
158+
}, seqno.Value).Sign(mnemonic.Keys.PrivateKey);
159+
160+
// send signed message
161+
await tonClient.SendBoc(msg.Cell);
162+
```

0 commit comments

Comments
 (0)
Please sign in to comment.