diff --git a/Assets/SequenceSDK/Marketplace/CurrencySwapTests.cs b/Assets/SequenceSDK/Marketplace/CurrencySwapTests.cs index 1e088f23..30a7b645 100644 --- a/Assets/SequenceSDK/Marketplace/CurrencySwapTests.cs +++ b/Assets/SequenceSDK/Marketplace/CurrencySwapTests.cs @@ -120,5 +120,92 @@ public async Task GetSwapQuoteTest_FailedToFetchPrice() Assert.IsTrue(e.Message.Contains("Error fetching swap quote")); } } + + [Test] + public async Task TestGetSupportedChains() + { + CurrencySwap currencySwap = new CurrencySwap(_chain); + + try + { + Chain[] supportedChains = await currencySwap.GetSupportedChains(); + Assert.IsNotNull(supportedChains); + Assert.Greater(supportedChains.Length, 0); + } + catch (Exception e) + { + Assert.Fail($"Exception encountered while fetching supported chains: {e.Message}"); + } + } + + [Test] + public async Task TestGetSupportedTokens() + { + CurrencySwap currencySwap = new CurrencySwap(_chain); + + try + { + Token[] supportedTokens = await currencySwap.GetSupportedTokens(new[] { _chain }); + Assert.IsNotNull(supportedTokens); + Assert.Greater(supportedTokens.Length, 0); + } + catch (Exception e) + { + Assert.Fail($"Exception encountered while fetching supported tokens: {e.Message}"); + } + } + + [Test] + public async Task TestGetSupportedTokens_EmptyChains() + { + CurrencySwap currencySwap = new CurrencySwap(_chain); + + try + { + Token[] supportedTokens = await currencySwap.GetSupportedTokens(new Chain[0]); + Assert.Fail("Expected exception but none was thrown"); + } + catch (Exception e) + { + Assert.IsTrue(e.Message.Contains("Error fetching supported tokens:")); + } + } + + [Test] + public async Task TestGetSupportedTokens_NullChains() + { + CurrencySwap currencySwap = new CurrencySwap(_chain); + + try + { + Token[] supportedTokens = await currencySwap.GetSupportedTokens(null); + Assert.Fail("Expected exception but none was thrown"); + } + catch (Exception e) + { + Assert.IsTrue(e.Message.Contains("Error fetching supported tokens:")); + } + } + + [Test] + public async Task TestGetSupportedTokens_AllSupportedChains() + { + CurrencySwap currencySwap = new CurrencySwap(_chain); + + try + { + Chain[] supportedChains = await currencySwap.GetSupportedChains(); + Assert.IsNotNull(supportedChains); + Assert.Greater(supportedChains.Length, 0); + + Token[] supportedTokens = await currencySwap.GetSupportedTokens(supportedChains); + Assert.IsNotNull(supportedTokens); + Assert.Greater(supportedTokens.Length, 0); + } + catch (Exception e) + { + Assert.Fail($"Exception encountered while fetching supported tokens: {e.Message}"); + } + } } } \ No newline at end of file diff --git a/Assets/SequenceSDK/Marketplace/Mocks/MockSwapThatGivesPricesInOrder.cs b/Assets/SequenceSDK/Marketplace/Mocks/MockSwapThatGivesPricesInOrder.cs index 5f17a70e..ab61fced 100644 --- a/Assets/SequenceSDK/Marketplace/Mocks/MockSwapThatGivesPricesInOrder.cs +++ b/Assets/SequenceSDK/Marketplace/Mocks/MockSwapThatGivesPricesInOrder.cs @@ -48,5 +48,15 @@ public Task GetSwapQuote(Address userWallet, Address buyCurrency, Add { throw new NotImplementedException(); } + + public Task GetSupportedChains() + { + throw new NotImplementedException(); + } + + public Task GetSupportedTokens(Chain[] chains) + { + throw new NotImplementedException(); + } } } \ No newline at end of file diff --git a/Packages/Sequence-Unity/Sequence/SequenceSDK/Ethereum/Token.cs b/Packages/Sequence-Unity/Sequence/SequenceSDK/Ethereum/Token.cs new file mode 100644 index 00000000..dc153259 --- /dev/null +++ b/Packages/Sequence-Unity/Sequence/SequenceSDK/Ethereum/Token.cs @@ -0,0 +1,63 @@ +using System; +using System.Numerics; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using UnityEngine.Scripting; + +namespace Sequence +{ + [Serializable] + [JsonConverter(typeof(TokenConverter))] + public class Token + { + public Chain Chain; + public Address Contract; + public string TokenId; + + public Token(Chain chain, Address contract, string tokenId = null) + { + Chain = chain; + Contract = contract; + TokenId = tokenId; + } + + [Preserve] + [JsonConstructor] + public Token(BigInteger chainId, string contractAddress, string tokenId = null) + { + Chain = ChainDictionaries.ChainById[chainId.ToString()]; + Contract = new Address(contractAddress); + TokenId = tokenId; + } + } + + internal class TokenConverter : JsonConverter + { + public override void WriteJson(JsonWriter writer, Token value, JsonSerializer serializer) + { + var jsonObject = new JObject + { + ["chainId"] = ulong.Parse(ChainDictionaries.ChainIdOf[value.Chain]), + ["contractAddress"] = value.Contract.ToString(), + }; + if (value.TokenId != null) + { + jsonObject["tokenId"] = value.TokenId; + } + + jsonObject.WriteTo(writer); + } + + public override Token ReadJson(JsonReader reader, Type objectType, Token existingValue, + bool hasExistingValue, JsonSerializer serializer) + { + var jsonObject = JObject.Load(reader); + + BigInteger chainId = jsonObject["chainId"]?.Value() ?? 0; + string contractAddress = jsonObject["contractAddress"]?.Value(); + string tokenId = jsonObject["tokenId"]?.Value(); + + return new Token(chainId, contractAddress, tokenId); + } + } +} \ No newline at end of file diff --git a/Packages/Sequence-Unity/Sequence/SequenceSDK/Ethereum/Token.cs.meta b/Packages/Sequence-Unity/Sequence/SequenceSDK/Ethereum/Token.cs.meta new file mode 100644 index 00000000..ea934246 --- /dev/null +++ b/Packages/Sequence-Unity/Sequence/SequenceSDK/Ethereum/Token.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 4f7a9f677198422da3bd8cab76eaa17f +timeCreated: 1745248786 \ No newline at end of file diff --git a/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/DataTypes/GetTokenPricesArgs.cs b/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/DataTypes/GetTokenPricesArgs.cs index ff307407..3afa855f 100644 --- a/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/DataTypes/GetTokenPricesArgs.cs +++ b/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/DataTypes/GetTokenPricesArgs.cs @@ -6,9 +6,9 @@ namespace Sequence [Serializable] internal class GetTokenPricesArgs { - public PriceFeed.Token[] tokens; + public Token[] tokens; - public GetTokenPricesArgs(PriceFeed.Token[] tokens) + public GetTokenPricesArgs(Token[] tokens) { this.tokens = tokens; } diff --git a/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/DataTypes/TokenPrice.cs b/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/DataTypes/TokenPrice.cs index 9dbb2781..a1c5c76c 100644 --- a/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/DataTypes/TokenPrice.cs +++ b/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/DataTypes/TokenPrice.cs @@ -6,7 +6,7 @@ namespace Sequence [Serializable] public class TokenPrice { - public PriceFeed.Token token; + public Token token; public Price price; public Price price24hChange; public Price floorPrice; @@ -15,7 +15,7 @@ public class TokenPrice public DateTime updatedAt; [Preserve] - public TokenPrice(PriceFeed.Token token, Price price, Price price24hChange, Price floorPrice, Price buyPrice, Price sellPrice, DateTime updatedAt) + public TokenPrice(Token token, Price price, Price price24hChange, Price floorPrice, Price buyPrice, Price sellPrice, DateTime updatedAt) { this.token = token; this.price = price; diff --git a/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/PriceFeed.cs b/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/PriceFeed.cs index b4d7fdbc..1e78f454 100644 --- a/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/PriceFeed.cs +++ b/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/PriceFeed.cs @@ -1,58 +1,22 @@ using System; using System.Numerics; using System.Threading.Tasks; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; using Sequence.Config; using Sequence.Utils; -using UnityEngine.Scripting; namespace Sequence { public class PriceFeed { - [Serializable] - [JsonConverter(typeof(TokenConverter))] - public class Token + [Obsolete("Use the Token class in the Sequence namespace instead.")] + public class Token : Sequence.Token { - public Chain Chain; - public Address Contract; - - public Token(Chain chain, Address contract) - { - Chain = chain; - Contract = contract; - } - - [Preserve] - [JsonConstructor] - public Token(BigInteger chainId, string contractAddress) + public Token(Chain chain, Address contract, string tokenId = null) : base(chain, contract, tokenId) { - Chain = ChainDictionaries.ChainById[chainId.ToString()]; - Contract = new Address(contractAddress); } - } - private class TokenConverter : JsonConverter - { - public override void WriteJson(JsonWriter writer, Token value, JsonSerializer serializer) + public Token(BigInteger chainId, string contractAddress, string tokenId = null) : base(chainId, contractAddress, tokenId) { - var jsonObject = new JObject - { - ["chainId"] = ulong.Parse(ChainDictionaries.ChainIdOf[value.Chain]), - ["contractAddress"] = value.Contract.ToString(), - }; - jsonObject.WriteTo(writer); - } - - public override Token ReadJson(JsonReader reader, Type objectType, Token existingValue, bool hasExistingValue, JsonSerializer serializer) - { - var jsonObject = JObject.Load(reader); - - BigInteger chainId = jsonObject["chainId"]?.Value() ?? 0; - string contractAddress = jsonObject["contractAddress"]?.Value(); - - return new Token(chainId, contractAddress); } } diff --git a/Packages/Sequence-Unity/Sequence/SequenceSDK/Marketplace/CurrencySwap.cs b/Packages/Sequence-Unity/Sequence/SequenceSDK/Marketplace/CurrencySwap.cs index 0bef96f3..a58bc46a 100644 --- a/Packages/Sequence-Unity/Sequence/SequenceSDK/Marketplace/CurrencySwap.cs +++ b/Packages/Sequence-Unity/Sequence/SequenceSDK/Marketplace/CurrencySwap.cs @@ -165,5 +165,37 @@ private async Task AssertWeHaveSufficientBalance(Address userWallet, Address buy $"Insufficient balance of {sellCurrency} to buy {buyAmount} of {buyCurrency}, have {have}, need {required}"); } } + + public async Task GetSupportedChains() + { + string url = BaseUrl.AppendTrailingSlashIfNeeded() + "GetLifiChains"; + try + { + LifiSupportedChainsResponse response = + await _client.SendRequest(url, null); + return response.GetChains(); + } + catch (Exception e) + { + string error = $"Error fetching supported chains: {e.Message}"; + throw new Exception(error); + } + } + + public async Task GetSupportedTokens(Chain[] chains) + { + string url = BaseUrl.AppendTrailingSlashIfNeeded() + "GetLifiTokens"; + try + { + GetLifiTokensResponse response = + await _client.SendRequest(url, new GetLifiTokensRequest(chains)); + return response.tokens; + } + catch (Exception e) + { + string error = $"Error fetching supported tokens: {e.Message}"; + throw new Exception(error); + } + } } } \ No newline at end of file diff --git a/Packages/Sequence-Unity/Sequence/SequenceSDK/Marketplace/DataTypes/GetLifiTokensRequest.cs b/Packages/Sequence-Unity/Sequence/SequenceSDK/Marketplace/DataTypes/GetLifiTokensRequest.cs new file mode 100644 index 00000000..227f5a06 --- /dev/null +++ b/Packages/Sequence-Unity/Sequence/SequenceSDK/Marketplace/DataTypes/GetLifiTokensRequest.cs @@ -0,0 +1,30 @@ +using System; +using System.Numerics; +using Newtonsoft.Json; +using UnityEngine.Scripting; + +namespace Sequence.Marketplace +{ + [Serializable] + public class GetLifiTokensRequest + { + public BigInteger[] chainIds; + + [JsonConstructor] + [Preserve] + public GetLifiTokensRequest(BigInteger[] chainIds) + { + this.chainIds = chainIds; + } + + public GetLifiTokensRequest(Chain[] chains) + { + int length = chains.Length; + chainIds = new BigInteger[length]; + for (int i = 0; i < length; i++) + { + chainIds[i] = BigInteger.Parse(ChainDictionaries.ChainIdOf[chains[i]]); + } + } + } +} \ No newline at end of file diff --git a/Packages/Sequence-Unity/Sequence/SequenceSDK/Marketplace/DataTypes/GetLifiTokensRequest.cs.meta b/Packages/Sequence-Unity/Sequence/SequenceSDK/Marketplace/DataTypes/GetLifiTokensRequest.cs.meta new file mode 100644 index 00000000..36c5e19f --- /dev/null +++ b/Packages/Sequence-Unity/Sequence/SequenceSDK/Marketplace/DataTypes/GetLifiTokensRequest.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 2c0c2de0e60345189236b54875874335 +timeCreated: 1745249518 \ No newline at end of file diff --git a/Packages/Sequence-Unity/Sequence/SequenceSDK/Marketplace/DataTypes/GetLifiTokensResponse.cs b/Packages/Sequence-Unity/Sequence/SequenceSDK/Marketplace/DataTypes/GetLifiTokensResponse.cs new file mode 100644 index 00000000..f47631aa --- /dev/null +++ b/Packages/Sequence-Unity/Sequence/SequenceSDK/Marketplace/DataTypes/GetLifiTokensResponse.cs @@ -0,0 +1,17 @@ +using System; +using UnityEngine.Scripting; + +namespace Sequence.Marketplace +{ + [Serializable] + public class GetLifiTokensResponse + { + public Token[] tokens; + + [Preserve] + public GetLifiTokensResponse(Token[] tokens) + { + this.tokens = tokens; + } + } +} \ No newline at end of file diff --git a/Packages/Sequence-Unity/Sequence/SequenceSDK/Marketplace/DataTypes/GetLifiTokensResponse.cs.meta b/Packages/Sequence-Unity/Sequence/SequenceSDK/Marketplace/DataTypes/GetLifiTokensResponse.cs.meta new file mode 100644 index 00000000..6dec35fe --- /dev/null +++ b/Packages/Sequence-Unity/Sequence/SequenceSDK/Marketplace/DataTypes/GetLifiTokensResponse.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: a1bffa4ea37e4ec4b0a1e0d9706f4f62 +timeCreated: 1745249557 \ No newline at end of file diff --git a/Packages/Sequence-Unity/Sequence/SequenceSDK/Marketplace/DataTypes/LifiSupportedChainsResponse.cs b/Packages/Sequence-Unity/Sequence/SequenceSDK/Marketplace/DataTypes/LifiSupportedChainsResponse.cs new file mode 100644 index 00000000..28b1c804 --- /dev/null +++ b/Packages/Sequence-Unity/Sequence/SequenceSDK/Marketplace/DataTypes/LifiSupportedChainsResponse.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Numerics; +using UnityEngine.Scripting; + +namespace Sequence.Marketplace +{ + [Serializable] + public class LifiSupportedChainsResponse + { + public BigInteger[] chains; + + [Preserve] + public LifiSupportedChainsResponse(BigInteger[] chains) + { + this.chains = chains; + } + + public Chain[] GetChains() + { + int length = chains.Length; + List result = new List(); + for (int i = 0; i < length; i++) + { + string chainId = chains[i].ToString(); + if (ChainDictionaries.ChainById.TryGetValue(chainId, out Chain chain)) // There may be chains that aren't supported by Sequence + { + result.Add(chain); + } + } + return result.ToArray(); + } + } +} \ No newline at end of file diff --git a/Packages/Sequence-Unity/Sequence/SequenceSDK/Marketplace/DataTypes/LifiSupportedChainsResponse.cs.meta b/Packages/Sequence-Unity/Sequence/SequenceSDK/Marketplace/DataTypes/LifiSupportedChainsResponse.cs.meta new file mode 100644 index 00000000..03166fb8 --- /dev/null +++ b/Packages/Sequence-Unity/Sequence/SequenceSDK/Marketplace/DataTypes/LifiSupportedChainsResponse.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 7317e5bccdf84916b9657202923b4a4d +timeCreated: 1745249460 \ No newline at end of file diff --git a/Packages/Sequence-Unity/Sequence/SequenceSDK/Marketplace/ISwap.cs b/Packages/Sequence-Unity/Sequence/SequenceSDK/Marketplace/ISwap.cs index 647f72c0..c21ff2f3 100644 --- a/Packages/Sequence-Unity/Sequence/SequenceSDK/Marketplace/ISwap.cs +++ b/Packages/Sequence-Unity/Sequence/SequenceSDK/Marketplace/ISwap.cs @@ -48,5 +48,18 @@ public interface ISwap /// public Task GetSwapQuote(Address userWallet, Address buyCurrency, Address sellCurrency, string buyAmount, bool includeApprove, uint slippagePercentage = DefaultSlippagePercentage); + + /// + /// Get a Chain[] of all supported chains for the swap provider + /// + /// + public Task GetSupportedChains(); + + /// + /// Get the supported tokens by the swap provider for a given set of chains + /// + /// + /// + public Task GetSupportedTokens(Chain[] chains); } } \ No newline at end of file diff --git a/Packages/Sequence-Unity/package.json b/Packages/Sequence-Unity/package.json index a813ab1f..3dbf8cb3 100644 --- a/Packages/Sequence-Unity/package.json +++ b/Packages/Sequence-Unity/package.json @@ -1,6 +1,6 @@ { "name": "xyz.0xsequence.waas-unity", - "version": "4.0.5", + "version": "4.1.0", "displayName": "Sequence Embedded Wallet SDK", "description": "A Unity SDK for Sequence APIs", "unity": "2021.3",