Skip to content

Commit 4ac5832

Browse files
Fix/lifi swaps (#298)
* Add implementation of GetLifiChains and GetLifiTokens endpoints - these are useful to check which chains and tokens per chain are enabled by the new swap service provider, Lifi. Additionally, refactored PriceFeed.Token to a more generic class so it can be reused. * Fix get support chains implementation. Fix typo in test * Base integration of new Lifi swap routes * Basic integration of new Lifi swap quote * Update existing swap methods to use the lifi routes instead * Increment package version * Fix intellisence summary * Handle null Token.Price case in json
1 parent 20e97cb commit 4ac5832

31 files changed

+841
-102
lines changed

Assets/SequenceSDK/Marketplace/CurrencySwapTests.cs

Lines changed: 143 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,11 @@ public async Task GetSwapPriceTest()
1717
CurrencySwap currencySwap = new CurrencySwap(_chain);
1818
string amount = "1000";
1919

20-
SwapPrice swapPrice = await currencySwap.GetSwapPrice(new Address(USDC), new Address(USDCe), amount);
20+
SwapPrice swapPrice = await currencySwap.GetSwapPrice(new Address("0xe8db071f698aBA1d60babaE8e08F5cBc28782108"), new Address(USDC), new Address(USDCe), amount);
2121

2222
Assert.IsNotNull(swapPrice);
2323
Assert.AreEqual(USDCe.ToLower(), swapPrice.currencyAddress.Value.ToLower());
2424
Assert.IsFalse(string.IsNullOrWhiteSpace(swapPrice.price));
25-
Assert.IsFalse(string.IsNullOrWhiteSpace(swapPrice.maxPrice));
26-
Assert.IsFalse(string.IsNullOrWhiteSpace(swapPrice.transactionValue));
27-
Assert.GreaterOrEqual(BigInteger.Parse(swapPrice.transactionValue), BigInteger.Zero);
2825
}
2926

3027
[Test]
@@ -41,9 +38,7 @@ public async Task GetSwapPricesTest()
4138
{
4239
Assert.IsNotNull(swapPrice);
4340
Assert.IsFalse(string.IsNullOrWhiteSpace(swapPrice.price));
44-
Assert.IsFalse(string.IsNullOrWhiteSpace(swapPrice.maxPrice));
45-
Assert.IsFalse(string.IsNullOrWhiteSpace(swapPrice.transactionValue));
46-
Assert.GreaterOrEqual(BigInteger.Parse(swapPrice.transactionValue), BigInteger.Zero);
41+
Assert.NotNull(swapPrice.currencyAddress);
4742
}
4843
}
4944

@@ -81,6 +76,8 @@ public async Task GetSwapQuoteTest()
8176
Assert.IsFalse(string.IsNullOrWhiteSpace(swapQuote.transactionValue));
8277
Assert.GreaterOrEqual(BigInteger.Parse(swapQuote.transactionValue), BigInteger.Zero);
8378
Assert.IsFalse(string.IsNullOrWhiteSpace(swapQuote.approveData));
79+
Assert.IsFalse(string.IsNullOrWhiteSpace(swapQuote.amount));
80+
Assert.IsFalse(string.IsNullOrWhiteSpace(swapQuote.amountMin));
8481
}
8582

8683
[Test]
@@ -98,7 +95,7 @@ public async Task GetSwapQuoteTest_InsufficientBalance()
9895
}
9996
catch (Exception e)
10097
{
101-
Assert.IsTrue(e.Message.Contains("Insufficient balance"));
98+
Assert.IsTrue(e.Message.Contains("not enough balance for swap"));
10299
}
103100
}
104101

@@ -120,5 +117,143 @@ public async Task GetSwapQuoteTest_FailedToFetchPrice()
120117
Assert.IsTrue(e.Message.Contains("Error fetching swap quote"));
121118
}
122119
}
120+
121+
[Test]
122+
public async Task TestGetSupportedChains()
123+
{
124+
CurrencySwap currencySwap = new CurrencySwap(_chain);
125+
126+
try
127+
{
128+
Chain[] supportedChains = await currencySwap.GetSupportedChains();
129+
Assert.IsNotNull(supportedChains);
130+
Assert.Greater(supportedChains.Length, 0);
131+
}
132+
catch (Exception e)
133+
{
134+
Assert.Fail($"Exception encountered while fetching supported chains: {e.Message}");
135+
}
136+
}
137+
138+
[Test]
139+
public async Task TestGetSupportedTokens()
140+
{
141+
CurrencySwap currencySwap = new CurrencySwap(_chain);
142+
143+
try
144+
{
145+
Token[] supportedTokens = await currencySwap.GetSupportedTokens(new[] { _chain });
146+
Assert.IsNotNull(supportedTokens);
147+
Assert.Greater(supportedTokens.Length, 0);
148+
}
149+
catch (Exception e)
150+
{
151+
Assert.Fail($"Exception encountered while fetching supported tokens: {e.Message}");
152+
}
153+
}
154+
155+
[Test]
156+
public async Task TestGetSupportedTokens_EmptyChains()
157+
{
158+
CurrencySwap currencySwap = new CurrencySwap(_chain);
159+
160+
try
161+
{
162+
Token[] supportedTokens = await currencySwap.GetSupportedTokens(new Chain[0]);
163+
Assert.Fail("Expected exception but none was thrown");
164+
}
165+
catch (Exception e)
166+
{
167+
Assert.IsTrue(e.Message.Contains("Error fetching supported tokens:"));
168+
}
169+
}
170+
171+
[Test]
172+
public async Task TestGetSupportedTokens_NullChains()
173+
{
174+
CurrencySwap currencySwap = new CurrencySwap(_chain);
175+
176+
try
177+
{
178+
Token[] supportedTokens = await currencySwap.GetSupportedTokens(null);
179+
Assert.Fail("Expected exception but none was thrown");
180+
}
181+
catch (Exception e)
182+
{
183+
Assert.IsTrue(e.Message.Contains("Error fetching supported tokens:"));
184+
}
185+
}
186+
187+
[Test]
188+
public async Task TestGetSupportedTokens_AllSupportedChains()
189+
{
190+
CurrencySwap currencySwap = new CurrencySwap(_chain);
191+
192+
try
193+
{
194+
Chain[] supportedChains = await currencySwap.GetSupportedChains();
195+
Assert.IsNotNull(supportedChains);
196+
Assert.Greater(supportedChains.Length, 0);
197+
198+
Token[] supportedTokens = await currencySwap.GetSupportedTokens(supportedChains);
199+
Assert.IsNotNull(supportedTokens);
200+
Assert.Greater(supportedTokens.Length, 0);
201+
}
202+
catch (Exception e)
203+
{
204+
Assert.Fail($"Exception encountered while fetching supported tokens: {e.Message}");
205+
}
206+
}
207+
208+
[Test]
209+
public async Task TestGetLifiSwapRoutes()
210+
{
211+
CurrencySwap currencySwap = new CurrencySwap(_chain);
212+
try
213+
{
214+
LifiSwapRoute[] swapRoutes = await currencySwap.GetLifiSwapRoutes(
215+
new Address("0xe8db071f698aBA1d60babaE8e08F5cBc28782108"), new Address(USDC), "1000");
216+
Assert.NotNull(swapRoutes);
217+
Assert.Greater(swapRoutes.Length, 0);
218+
}
219+
catch (Exception e)
220+
{
221+
Assert.Fail($"Exception encountered while fetching Lifi swap routes: {e.Message}");
222+
}
223+
}
224+
225+
[Test]
226+
public async Task TestGetLifiSwapQuote()
227+
{
228+
CurrencySwap currencySwap = new CurrencySwap(_chain);
229+
try
230+
{
231+
LifiSwapQuote swapQuote = await currencySwap.GetLifiSwapQuote(
232+
new Address("0xe8db071f698aBA1d60babaE8e08F5cBc28782108"), new Address(USDC), new Address(USDCe),
233+
"1000");
234+
Assert.NotNull(swapQuote);
235+
Assert.AreEqual(USDCe.ToLower(), swapQuote.currencyAddress.Value.ToLower());
236+
}
237+
catch (Exception e)
238+
{
239+
Assert.Fail($"Exception encountered while fetching Lifi swap quote: {e.Message}");
240+
}
241+
}
242+
243+
[Test]
244+
public async Task TestGetLifiSwapQuote_NoAmounts()
245+
{
246+
CurrencySwap currencySwap = new CurrencySwap(_chain);
247+
try
248+
{
249+
LifiSwapQuote swapQuote = await currencySwap.GetLifiSwapQuote(
250+
new Address("0xe8db071f698aBA1d60babaE8e08F5cBc28782108"), new Address(USDC), new Address(USDCe));
251+
Assert.Fail("Expected exception but none was thrown");
252+
}
253+
catch (Exception e)
254+
{
255+
Assert.IsTrue(e.Message.Contains("Error fetching Lifi swap quote:"));
256+
}
257+
}
123258
}
124259
}

Assets/SequenceSDK/Marketplace/Mocks/MockSwapThatGivesPricesInOrder.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@ public MockSwapThatGivesPricesInOrder(ulong[] maxPrices)
2020
public event Action<SwapPrice> OnSwapPriceReturn;
2121
public event Action<string> OnSwapPriceError;
2222

23+
public Task<SwapPrice> GetSwapPrice(Address userWallet, Address buyCurrency, Address sellCurrency, string buyAmount)
24+
{
25+
throw new NotImplementedException();
26+
}
27+
2328
public async Task<SwapPrice> GetSwapPrice(Address buyCurrency, Address sellCurrency, string buyAmount,
2429
uint slippagePercent = ISwap.DefaultSlippagePercentage)
2530
{
@@ -48,5 +53,20 @@ public Task<SwapQuote> GetSwapQuote(Address userWallet, Address buyCurrency, Add
4853
{
4954
throw new NotImplementedException();
5055
}
56+
57+
public Task<Chain[]> GetSupportedChains()
58+
{
59+
throw new NotImplementedException();
60+
}
61+
62+
public Task<Token[]> GetSupportedTokens(Chain[] chains)
63+
{
64+
throw new NotImplementedException();
65+
}
66+
67+
public Task<LifiSwapRoute[]> GetLifiSwapRoutes(Address userWallet, Address buyCurrency, string buyAmount)
68+
{
69+
throw new NotImplementedException();
70+
}
5171
}
5272
}
Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
using System;
2+
using System.Numerics;
3+
using Newtonsoft.Json;
4+
using Newtonsoft.Json.Linq;
5+
using UnityEngine.Scripting;
6+
7+
namespace Sequence
8+
{
9+
[Serializable]
10+
[JsonConverter(typeof(TokenConverter))]
11+
public class Token
12+
{
13+
public Chain Chain;
14+
public Address Contract;
15+
public string TokenId;
16+
public string Symbol;
17+
public string Name;
18+
public BigInteger Decimals;
19+
public BigInteger Price;
20+
public decimal PriceUsd;
21+
public string LogoUri;
22+
23+
public Token(Chain chain, Address contract, string tokenId = null, string symbol = null, string name = null,
24+
BigInteger decimals = default, decimal priceUsd = default, string logoUri = null, BigInteger price = default)
25+
{
26+
Chain = chain;
27+
Contract = contract;
28+
TokenId = tokenId;
29+
Symbol = symbol;
30+
Name = name;
31+
Decimals = decimals;
32+
PriceUsd = priceUsd;
33+
LogoUri = logoUri;
34+
Price = price;
35+
}
36+
37+
[Preserve]
38+
[JsonConstructor]
39+
public Token(BigInteger chainId, string contractAddress, string tokenId = null, string symbol = null,
40+
string name = null, BigInteger decimals = default, decimal priceUsd = default, string logoUri = null, BigInteger price = default)
41+
{
42+
Chain = ChainDictionaries.ChainById[chainId.ToString()];
43+
if (!string.IsNullOrWhiteSpace(contractAddress))
44+
{
45+
Contract = new Address(contractAddress);
46+
}
47+
TokenId = tokenId;
48+
Symbol = symbol;
49+
Name = name;
50+
Decimals = decimals;
51+
PriceUsd = priceUsd;
52+
LogoUri = logoUri;
53+
Price = price;
54+
}
55+
}
56+
57+
internal class TokenConverter : JsonConverter<Token>
58+
{
59+
public override void WriteJson(JsonWriter writer, Token value, JsonSerializer serializer)
60+
{
61+
var jsonObject = new JObject
62+
{
63+
["chainId"] = ulong.Parse(ChainDictionaries.ChainIdOf[value.Chain]),
64+
["contractAddress"] = value.Contract.ToString(),
65+
};
66+
if (value.TokenId != null)
67+
{
68+
jsonObject["tokenId"] = value.TokenId;
69+
}
70+
if (value.Symbol != null)
71+
{
72+
jsonObject["symbol"] = value.Symbol;
73+
}
74+
if (value.Name != null)
75+
{
76+
jsonObject["name"] = value.Name;
77+
}
78+
if (value.Decimals != default)
79+
{
80+
jsonObject["decimals"] = value.Decimals.ToString();
81+
}
82+
if (value.PriceUsd != default)
83+
{
84+
jsonObject["priceUsd"] = value.PriceUsd;
85+
}
86+
if (value.LogoUri != null)
87+
{
88+
jsonObject["logoUri"] = value.LogoUri;
89+
}
90+
if (value.Price != default)
91+
{
92+
jsonObject["price"] = value.Price.ToString();
93+
}
94+
95+
jsonObject.WriteTo(writer);
96+
}
97+
98+
public override Token ReadJson(JsonReader reader, Type objectType, Token existingValue,
99+
bool hasExistingValue, JsonSerializer serializer)
100+
{
101+
var jsonObject = JObject.Load(reader);
102+
103+
BigInteger chainId = jsonObject["chainId"]?.Value<ulong>() ?? 0;
104+
string contractAddress = jsonObject["contractAddress"]?.Value<string>();
105+
if (string.IsNullOrWhiteSpace(contractAddress))
106+
{
107+
contractAddress = jsonObject["address"]?.Value<string>();
108+
}
109+
string tokenId = jsonObject["tokenId"]?.Value<string>();
110+
string symbol = jsonObject["symbol"]?.Value<string>();
111+
string name = jsonObject["name"]?.Value<string>();
112+
BigInteger decimals = (jsonObject["decimals"]?.Value<ulong>() ?? 18);
113+
decimal priceUsd = jsonObject["priceUsd"]?.Value<decimal>() ?? default;
114+
string logoUri = jsonObject["logoUri"]?.Value<string>();
115+
BigInteger price = 0;
116+
JToken priceToken = jsonObject["price"];
117+
if (priceToken != null && priceToken.Type != JTokenType.Null)
118+
{
119+
string priceStr = priceToken.Value<string>();
120+
if (!string.IsNullOrWhiteSpace(priceStr) && BigInteger.TryParse(priceStr, out var parsedPrice))
121+
{
122+
price = parsedPrice;
123+
}
124+
}
125+
126+
127+
return new Token(chainId, contractAddress, tokenId, symbol, name, decimals, priceUsd, logoUri, price);
128+
}
129+
}
130+
131+
public static class TokenExtensions
132+
{
133+
public static bool ContainsToken(this Token[] tokens, Token token)
134+
{
135+
if (tokens == null || token == null)
136+
{
137+
return false;
138+
}
139+
foreach (var t in tokens)
140+
{
141+
if (t.Chain == token.Chain && t.Contract.Equals(token.Contract) && t.TokenId == token.TokenId)
142+
{
143+
return true;
144+
}
145+
}
146+
147+
return false;
148+
}
149+
150+
public static bool ContainsToken(this Token[] tokens, Address token)
151+
{
152+
if (tokens == null || token == null)
153+
{
154+
return false;
155+
}
156+
foreach (var t in tokens)
157+
{
158+
if (t.Contract.Equals(token))
159+
{
160+
return true;
161+
}
162+
}
163+
164+
return false;
165+
}
166+
}
167+
}

Packages/Sequence-Unity/Sequence/SequenceSDK/Ethereum/Token.cs.meta

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)