diff --git a/Assets/SequenceSDK/Indexer/Tests/ChainIndexerTests.cs b/Assets/SequenceSDK/Indexer/Tests/ChainIndexerTests.cs index 91069ded9..1c05c4bfb 100644 --- a/Assets/SequenceSDK/Indexer/Tests/ChainIndexerTests.cs +++ b/Assets/SequenceSDK/Indexer/Tests/ChainIndexerTests.cs @@ -5,9 +5,14 @@ using System.Threading.Tasks; using NUnit.Framework; using Sequence; +using Sequence.Contracts; +using Sequence.EmbeddedWallet; +using Sequence.EmbeddedWallet.Tests; using Sequence.Utils; using UnityEngine; using UnityEngine.TestTools; +using EOAWallet = Sequence.Wallet.EOAWallet; +using Transaction = System.Transactions.Transaction; namespace Sequence.Indexer.Tests { @@ -299,5 +304,217 @@ public async Task TestRequestDecodingErrorHandling(bool logError) Assert.IsTrue(errorEventFired); } + + [TestCase] + public void TestSubscribeReceipts() + { + var indexer = new ChainIndexer(Chain.TestnetArbitrumSepolia); + var streamOptions = new WebRPCStreamOptions( + OnSubscribeReceiptsMessageReceived, + OnWebRPCErrorReceived); + + var filter = new TransactionFilter + { + contractAddress = "0x4ab3b16e9d3328f6d8025e71cefc64305ae4fe9c" + }; + + indexer.SubscribeReceipts(new SubscribeReceiptsArgs(filter), streamOptions); + } + + [Test] + public async Task TestReceiptSubscriptionReceived() + { + Chain chain = Chain.ArbitrumNova; + bool receiptReceived = false; + IIndexer indexer = new ChainIndexer(chain); + ERC1155 universallyMintable = new ERC1155("0x0ee3af1874789245467e7482f042ced9c5171073"); + var streamOptions = new WebRPCStreamOptions( + (@return => + { + Assert.IsNotNull(@return); + Assert.IsNotNull(@return.receipt); + receiptReceived = true; + }), + OnWebRPCErrorReceived); + + + var filter = new TransactionFilter() + { + contractAddress = universallyMintable.Contract.GetAddress() + }; + indexer.SubscribeReceipts(new SubscribeReceiptsArgs(filter), streamOptions); + + await DoMintTransaction(chain); + + while (!receiptReceived) + { + await Task.Yield(); + } + } + + private async Task DoMintTransaction(Chain chain) + { + var tcs = new TaskCompletionSource(); + EndToEndTestHarness testHarness = new EndToEndTestHarness(); + EOAWallet toWallet = new EOAWallet(); + string toAddress = toWallet.GetAddress().Value; + + testHarness.Login(async wallet => + { + try + { + ERC1155 erc1155 = new ERC1155("0x0ee3af1874789245467e7482f042ced9c5171073"); + + TransactionReturn transactionReturn = await wallet.SendTransaction(chain, + new EmbeddedWallet.Transaction[] + { + new RawTransaction(erc1155.Mint(toAddress, 1, 1)), + }); + Assert.IsNotNull(transactionReturn); + Assert.IsTrue(transactionReturn is SuccessfulTransactionReturn); + + tcs.TrySetResult(true); + } + catch (System.Exception e) + { + tcs.TrySetException(e); + } + }, (error, method, email, methods) => + { + tcs.TrySetException(new Exception(error)); + }); + + await tcs.Task; + } + + [TestCase] + public void TestSubscribeEvents() + { + var indexer = new ChainIndexer(Chain.TestnetArbitrumSepolia); + var streamOptions = new WebRPCStreamOptions( + OnSubscribeEventsMessageReceived, + OnWebRPCErrorReceived); + + var eventFilter = new EventFilter + { + accounts = Array.Empty(), + contractAddresses = new[] {"0x4ab3b16e9d3328f6d8025e71cefc64305ae4fe9c"}, + tokenIDs = new[] {"0"}, + events = new[] {"Transfer(address from, address to, uint256 value)"} + }; + + indexer.SubscribeEvents(new SubscribeEventsArgs(eventFilter), streamOptions); + } + + [Test] + public async Task TestEventSubscriptionReceived() + { + Chain chain = Chain.ArbitrumNova; + bool eventReceived = false; + IIndexer indexer = new ChainIndexer(chain); + ERC1155 universallyMintable = new ERC1155("0x0ee3af1874789245467e7482f042ced9c5171073"); + var streamOptions = new WebRPCStreamOptions( + (@return => + { + Assert.IsNotNull(@return); + Assert.IsNotNull(@return.log); + Assert.AreEqual(universallyMintable.Contract.GetAddress().Value, @return.log.contractAddress); + eventReceived = true; + }), + OnWebRPCErrorReceived); + + + var eventFilter = new EventFilter + { + accounts = Array.Empty(), + contractAddresses = new[] {universallyMintable.Contract.GetAddress().Value}, + tokenIDs = new[] {"1"}, + events = new[] {"TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value)"} + }; + + indexer.SubscribeEvents(new SubscribeEventsArgs(eventFilter), streamOptions); + + await DoMintTransaction(chain); + + while (!eventReceived) + { + await Task.Yield(); + } + } + + [TestCase] + public void TestSubscribeBalanceUpdates() + { + var indexer = new ChainIndexer(Chain.TestnetArbitrumSepolia); + var streamOptions = new WebRPCStreamOptions( + OnSubscribeEventsMessageReceived, + OnWebRPCErrorReceived); + + var contractAddress = "0x4ab3b16e9d3328f6d8025e71cefc64305ae4fe9c"; + indexer.SubscribeBalanceUpdates(new SubscribeBalanceUpdatesArgs(contractAddress), streamOptions); + } + + [Test] + public async Task TestBalanceUpdateSubscriptionReceived() + { + Chain chain = Chain.ArbitrumNova; + bool eventReceived = false; + IIndexer indexer = new ChainIndexer(chain); + var streamOptions = new WebRPCStreamOptions( + (@return => + { + Assert.IsNotNull(@return); + Assert.IsNotNull(@return.balance); + if (!@return.balance.accountAddress.IsZeroAddress()) + { + Assert.AreEqual(BigInteger.One, @return.balance.balance); + } + eventReceived = true; + }), + OnWebRPCErrorReceived); + + ERC1155 universallyMintable = new ERC1155("0x0ee3af1874789245467e7482f042ced9c5171073"); + + indexer.SubscribeBalanceUpdates(new SubscribeBalanceUpdatesArgs(universallyMintable.Contract.GetAddress()), streamOptions); + + await DoMintTransaction(chain); + + while (!eventReceived) + { + await Task.Yield(); + } + } + + [TestCase] + public void TestAbortStreams() + { + new ChainIndexer(Chain.TestnetArbitrumSepolia).AbortStreams(); + } + + [TearDown] + public void UnsubscribeFromEvents() + { + new ChainIndexer(Chain.ArbitrumNova).AbortStreams(); + } + + private void OnSubscribeReceiptsMessageReceived(SubscribeReceiptsReturn @event) + { + Debug.Log($"Receipt Event Received - hash: {@event.receipt.txnHash}"); + } + + private void OnSubscribeEventsMessageReceived(SubscribeEventsReturn @event) + { + Debug.Log($"Contract Event Received - {@event.log.type} {@event.log.contractType}: {@event.log.txnHash}"); + } + + private void OnSubscribeEventsMessageReceived(SubscribeBalanceUpdatesReturn @event) + { + Debug.Log($"Balance Update Received - {@event.balance.accountAddress} owns {@event.balance.balance}"); + } + + private void OnWebRPCErrorReceived(WebRPCError error) + { + Debug.LogError($"OnWebRPCErrorReceived: {error.msg}"); + } } } \ No newline at end of file diff --git a/Assets/SequenceSDK/Indexer/Tests/MockHttpHandler.cs b/Assets/SequenceSDK/Indexer/Tests/MockHttpHandler.cs index 27799234c..de11a112f 100644 --- a/Assets/SequenceSDK/Indexer/Tests/MockHttpHandler.cs +++ b/Assets/SequenceSDK/Indexer/Tests/MockHttpHandler.cs @@ -26,5 +26,15 @@ public Task HttpPost(string chainID, string endPoint, object args, int r } return Task.FromResult(_response); } + + public void HttpStream(string chainID, string endPoint, object args, WebRPCStreamOptions options, int retries = 0) + { + throw new NotImplementedException(); + } + + public void AbortStreams() + { + throw new NotImplementedException(); + } } } \ No newline at end of file diff --git a/Assets/SequenceSDK/Indexer/Tests/SequenceIndexerTests.asmdef b/Assets/SequenceSDK/Indexer/Tests/SequenceIndexerTests.asmdef index f7c1efa2a..a434fd936 100644 --- a/Assets/SequenceSDK/Indexer/Tests/SequenceIndexerTests.asmdef +++ b/Assets/SequenceSDK/Indexer/Tests/SequenceIndexerTests.asmdef @@ -7,7 +7,10 @@ "GUID:0acc523941302664db1f4e527237feb3", "GUID:a35e3a53d4439435f8b36ed2c6158cd8", "GUID:b4f9c0f8f363f439b9e337f79050f189", - "GUID:403077141e1554429a890cbc129df651" + "GUID:403077141e1554429a890cbc129df651", + "GUID:040286810a82b46ed9acd6d70bfbbfd4", + "SequenceEmbeddedWallet", + "SequenceAuthentication" ], "includePlatforms": [ "Editor" diff --git a/Packages/Sequence-Unity/Sequence/SequenceFrontend/Scripts/UI/SequenceSampleUI.cs b/Packages/Sequence-Unity/Sequence/SequenceFrontend/Scripts/UI/SequenceSampleUI.cs index 62ef082df..c87cc1a33 100644 --- a/Packages/Sequence-Unity/Sequence/SequenceFrontend/Scripts/UI/SequenceSampleUI.cs +++ b/Packages/Sequence-Unity/Sequence/SequenceFrontend/Scripts/UI/SequenceSampleUI.cs @@ -1,3 +1,4 @@ +using System; using Sequence.EmbeddedWallet; using UnityEngine; diff --git a/Packages/Sequence-Unity/Sequence/SequenceSDK/Ethereum/Address/Address.cs b/Packages/Sequence-Unity/Sequence/SequenceSDK/Ethereum/Address/Address.cs index 3c58abb74..82f2b9dc6 100644 --- a/Packages/Sequence-Unity/Sequence/SequenceSDK/Ethereum/Address/Address.cs +++ b/Packages/Sequence-Unity/Sequence/SequenceSDK/Ethereum/Address/Address.cs @@ -82,4 +82,19 @@ public override bool CanConvert(Type objectType) return true; } } + + public static class AddressExtensions + { + public static bool IsZeroAddress(this Address address) + { + return IsZeroAddress(address.Value); + } + + public static bool IsZeroAddress(this string address) + { + string toCheck = address.WithoutHexPrefix(); + toCheck = toCheck.Replace("0", ""); + return toCheck.Length == 0; + } + } } \ No newline at end of file diff --git a/Packages/Sequence-Unity/Sequence/SequenceSDK/Ethereum/Provider/DataTypes/TransactionReceipt.cs b/Packages/Sequence-Unity/Sequence/SequenceSDK/Ethereum/Provider/DataTypes/TransactionReceipt.cs index 08d757668..08832b9c7 100644 --- a/Packages/Sequence-Unity/Sequence/SequenceSDK/Ethereum/Provider/DataTypes/TransactionReceipt.cs +++ b/Packages/Sequence-Unity/Sequence/SequenceSDK/Ethereum/Provider/DataTypes/TransactionReceipt.cs @@ -6,6 +6,7 @@ namespace Sequence [System.Serializable] public class TransactionReceipt { + public string txnHash; public string transactionHash; public string transactionIndex; public string blockHash; @@ -22,6 +23,4 @@ public class TransactionReceipt public string root; public string status; } - - } diff --git a/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/ChainIndexer.cs b/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/ChainIndexer.cs index 90833e09b..844c9b0ba 100644 --- a/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/ChainIndexer.cs +++ b/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/ChainIndexer.cs @@ -148,5 +148,25 @@ public Task GetTransactionHistory(GetTransactionHis { return Indexer.GetTransactionHistory(ChainId, args, 0, _customHttpHandler, this); } + + public void SubscribeReceipts(SubscribeReceiptsArgs args, WebRPCStreamOptions options) + { + Indexer.SubscribeReceipts(ChainId, args, options, this); + } + + public void SubscribeEvents(SubscribeEventsArgs args, WebRPCStreamOptions options) + { + Indexer.SubscribeEvents(ChainId, args, options, this); + } + + public void SubscribeBalanceUpdates(SubscribeBalanceUpdatesArgs args, WebRPCStreamOptions options) + { + Indexer.SubscribeBalanceUpdates(ChainId, args, options, this); + } + + public void AbortStreams() + { + Indexer.AbortStreams(this); + } } } \ No newline at end of file diff --git a/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/DataTypes/EventDecoded.cs b/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/DataTypes/EventDecoded.cs new file mode 100644 index 000000000..c321a7f30 --- /dev/null +++ b/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/DataTypes/EventDecoded.cs @@ -0,0 +1,12 @@ +namespace Sequence +{ + [System.Serializable] + public class EventDecoded + { + public string topicHash; + public string eventSig; + public string[] types; + public string[] names; + public string[] values; + } +} \ No newline at end of file diff --git a/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/DataTypes/EventDecoded.cs.meta b/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/DataTypes/EventDecoded.cs.meta new file mode 100644 index 000000000..a6a4f5744 --- /dev/null +++ b/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/DataTypes/EventDecoded.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 54a3a34fc2c64f73b619ef8e7d85158e +timeCreated: 1739540532 \ No newline at end of file diff --git a/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/DataTypes/EventFilter.cs b/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/DataTypes/EventFilter.cs new file mode 100644 index 000000000..4e8ddb075 --- /dev/null +++ b/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/DataTypes/EventFilter.cs @@ -0,0 +1,11 @@ +namespace Sequence +{ + [System.Serializable] + public class EventFilter + { + public string[] events; + public string[] contractAddresses; + public string[] accounts; + public string[] tokenIDs; + } +} \ No newline at end of file diff --git a/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/DataTypes/EventFilter.cs.meta b/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/DataTypes/EventFilter.cs.meta new file mode 100644 index 000000000..e53010796 --- /dev/null +++ b/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/DataTypes/EventFilter.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: d0ae9c58dff243c9b734ebad0df6e7f4 +timeCreated: 1739521000 \ No newline at end of file diff --git a/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/DataTypes/EventLog.cs b/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/DataTypes/EventLog.cs index 4b050c72b..d4aec35ba 100644 --- a/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/DataTypes/EventLog.cs +++ b/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/DataTypes/EventLog.cs @@ -17,5 +17,6 @@ public class EventLog public EventLogDataType logDataType; public string ts; public string logData; + public EventDecoded @event; } } \ No newline at end of file diff --git a/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/DataTypes/SubscribeBalanceUpdatesArgs.cs b/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/DataTypes/SubscribeBalanceUpdatesArgs.cs new file mode 100644 index 000000000..4ff9a3c79 --- /dev/null +++ b/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/DataTypes/SubscribeBalanceUpdatesArgs.cs @@ -0,0 +1,13 @@ +namespace Sequence +{ + [System.Serializable] + public class SubscribeBalanceUpdatesArgs + { + public string contractAddress; + + public SubscribeBalanceUpdatesArgs(string contractAddress) + { + this.contractAddress = contractAddress; + } + } +} \ No newline at end of file diff --git a/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/DataTypes/SubscribeBalanceUpdatesArgs.cs.meta b/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/DataTypes/SubscribeBalanceUpdatesArgs.cs.meta new file mode 100644 index 000000000..8d5a2091a --- /dev/null +++ b/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/DataTypes/SubscribeBalanceUpdatesArgs.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: f1132eb6ed56492882726a81a4dd9563 +timeCreated: 1739521133 \ No newline at end of file diff --git a/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/DataTypes/SubscribeBalanceUpdatesReturn.cs b/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/DataTypes/SubscribeBalanceUpdatesReturn.cs new file mode 100644 index 000000000..aa81aa96e --- /dev/null +++ b/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/DataTypes/SubscribeBalanceUpdatesReturn.cs @@ -0,0 +1,8 @@ +namespace Sequence +{ + [System.Serializable] + public class SubscribeBalanceUpdatesReturn + { + public TokenBalance balance; + } +} \ No newline at end of file diff --git a/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/DataTypes/SubscribeBalanceUpdatesReturn.cs.meta b/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/DataTypes/SubscribeBalanceUpdatesReturn.cs.meta new file mode 100644 index 000000000..a47780145 --- /dev/null +++ b/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/DataTypes/SubscribeBalanceUpdatesReturn.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: d9f796ff4af64caba1a2f177482ef754 +timeCreated: 1739523574 \ No newline at end of file diff --git a/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/DataTypes/SubscribeEventsArgs.cs b/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/DataTypes/SubscribeEventsArgs.cs new file mode 100644 index 000000000..19cf401c7 --- /dev/null +++ b/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/DataTypes/SubscribeEventsArgs.cs @@ -0,0 +1,13 @@ +namespace Sequence +{ + [System.Serializable] + public class SubscribeEventsArgs + { + public EventFilter filter; + + public SubscribeEventsArgs(EventFilter filter) + { + this.filter = filter; + } + } +} \ No newline at end of file diff --git a/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/DataTypes/SubscribeEventsArgs.cs.meta b/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/DataTypes/SubscribeEventsArgs.cs.meta new file mode 100644 index 000000000..8ccc0fc03 --- /dev/null +++ b/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/DataTypes/SubscribeEventsArgs.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 6f26c5e9d9854c6a930b8f5a77127787 +timeCreated: 1739521203 \ No newline at end of file diff --git a/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/DataTypes/SubscribeEventsReturn.cs b/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/DataTypes/SubscribeEventsReturn.cs new file mode 100644 index 000000000..c927e1a6f --- /dev/null +++ b/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/DataTypes/SubscribeEventsReturn.cs @@ -0,0 +1,8 @@ +namespace Sequence +{ + [System.Serializable] + public class SubscribeEventsReturn + { + public EventLog log; + } +} \ No newline at end of file diff --git a/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/DataTypes/SubscribeEventsReturn.cs.meta b/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/DataTypes/SubscribeEventsReturn.cs.meta new file mode 100644 index 000000000..a562845a2 --- /dev/null +++ b/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/DataTypes/SubscribeEventsReturn.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 6a2c967a0b6f430bb2b93fe2577fbc21 +timeCreated: 1739523529 \ No newline at end of file diff --git a/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/DataTypes/SubscribeReceiptsArgs.cs b/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/DataTypes/SubscribeReceiptsArgs.cs new file mode 100644 index 000000000..023af78de --- /dev/null +++ b/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/DataTypes/SubscribeReceiptsArgs.cs @@ -0,0 +1,13 @@ +namespace Sequence +{ + [System.Serializable] + public class SubscribeReceiptsArgs + { + public TransactionFilter filter; + + public SubscribeReceiptsArgs(TransactionFilter filter) + { + this.filter = filter; + } + } +} \ No newline at end of file diff --git a/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/DataTypes/SubscribeReceiptsArgs.cs.meta b/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/DataTypes/SubscribeReceiptsArgs.cs.meta new file mode 100644 index 000000000..751784d25 --- /dev/null +++ b/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/DataTypes/SubscribeReceiptsArgs.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: cd83b02ca83d49e59536e0916b7627d0 +timeCreated: 1739521261 \ No newline at end of file diff --git a/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/DataTypes/SubscribeReceiptsReturn.cs b/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/DataTypes/SubscribeReceiptsReturn.cs new file mode 100644 index 000000000..3a8ace886 --- /dev/null +++ b/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/DataTypes/SubscribeReceiptsReturn.cs @@ -0,0 +1,8 @@ +namespace Sequence +{ + [System.Serializable] + public class SubscribeReceiptsReturn + { + public TransactionReceipt receipt; + } +} \ No newline at end of file diff --git a/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/DataTypes/SubscribeReceiptsReturn.cs.meta b/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/DataTypes/SubscribeReceiptsReturn.cs.meta new file mode 100644 index 000000000..e5499c15a --- /dev/null +++ b/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/DataTypes/SubscribeReceiptsReturn.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: b95cc9bfddb648a48bfa08a95608a1ef +timeCreated: 1739523428 \ No newline at end of file diff --git a/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/DataTypes/TransactionFilter.cs b/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/DataTypes/TransactionFilter.cs new file mode 100644 index 000000000..5c1e7a9b4 --- /dev/null +++ b/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/DataTypes/TransactionFilter.cs @@ -0,0 +1,12 @@ +namespace Sequence +{ + [System.Serializable] + public class TransactionFilter + { + public string txnHash; + public string from; + public string to; + public string contractAddress; + public string @event; + } +} \ No newline at end of file diff --git a/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/DataTypes/TransactionFilter.cs.meta b/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/DataTypes/TransactionFilter.cs.meta new file mode 100644 index 000000000..881c7f0f2 --- /dev/null +++ b/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/DataTypes/TransactionFilter.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 50810c603001415092f746d50330144a +timeCreated: 1739520881 \ No newline at end of file diff --git a/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/DataTypes/WebRPCStreamOptions.cs b/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/DataTypes/WebRPCStreamOptions.cs new file mode 100644 index 000000000..a9470280b --- /dev/null +++ b/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/DataTypes/WebRPCStreamOptions.cs @@ -0,0 +1,16 @@ +using System; + +namespace Sequence +{ + public class WebRPCStreamOptions + { + public Action onMessage; + public Action onError; + + public WebRPCStreamOptions(Action onMessage, Action onError) + { + this.onMessage = onMessage; + this.onError = onError; + } + } +} \ No newline at end of file diff --git a/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/DataTypes/WebRPCStreamOptions.cs.meta b/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/DataTypes/WebRPCStreamOptions.cs.meta new file mode 100644 index 000000000..caf9789f8 --- /dev/null +++ b/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/DataTypes/WebRPCStreamOptions.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bd39d5ed33c649d08e29d33a47e0e13b +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/DownloadHandlerStream.cs b/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/DownloadHandlerStream.cs new file mode 100644 index 000000000..8e242dbd2 --- /dev/null +++ b/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/DownloadHandlerStream.cs @@ -0,0 +1,55 @@ +using System; +using System.Text; +using Newtonsoft.Json; +using UnityEngine; +using UnityEngine.Networking; + +namespace Sequence +{ + public class DownloadHandlerStream : DownloadHandlerScript + { + private readonly WebRPCStreamOptions _options; + + public DownloadHandlerStream(WebRPCStreamOptions options) + { + _options = options; + } + + protected override bool ReceiveData(byte[] data, int dataLength) + { + CompleteContent(); + + // Stream returns a ping of size 1 + if (dataLength <= 1) + return true; + + try + { + // One event can include multiple JSON objects, seperated by a line break + var input = Encoding.UTF8.GetString(data); + var jsonParts = input.Split(Environment.NewLine.ToCharArray()); + + foreach (var part in jsonParts) + { + if (!part.StartsWith('{')) + continue; + + var streamData = JsonConvert.DeserializeObject(part); + _options.onMessage?.Invoke(streamData); + } + + return true; + } + catch (Exception e) + { + _options.onError?.Invoke(new WebRPCError + { + msg = e.Message + }); + + Debug.LogException(e); + return false; + } + } + } +} \ No newline at end of file diff --git a/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/DownloadHandlerStream.cs.meta b/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/DownloadHandlerStream.cs.meta new file mode 100644 index 000000000..d253d8421 --- /dev/null +++ b/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/DownloadHandlerStream.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: a894555997aa4908a1c7b761f2c025d2 +timeCreated: 1739529322 \ No newline at end of file diff --git a/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/HttpHandler.cs b/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/HttpHandler.cs index bb2b03305..acca96977 100644 --- a/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/HttpHandler.cs +++ b/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/HttpHandler.cs @@ -1,10 +1,10 @@ using System; +using System.Collections.Generic; using System.IO; using System.Net; using System.Net.Http; using System.Threading.Tasks; using Newtonsoft.Json; -using Sequence.Config; using Sequence.Utils; using UnityEngine; using UnityEngine.Networking; @@ -13,6 +13,8 @@ namespace Sequence { public class HttpHandler : IHttpHandler { + private static List _streams = new(); + private string _builderApiKey; private IIndexer _caller; @@ -36,9 +38,11 @@ public async Task HttpPost(string chainID, string endPoint, object args, req.SetRequestHeader("X-Access-Key", _builderApiKey); req.method = UnityWebRequest.kHttpVerbPOST; req.timeout = 10; // Request will timeout after 10 seconds - + string curlRequest = $"curl -X POST -H \"Content-Type: application/json\" -H \"Accept: application/json\" -H \"X-Access-Key: {req.GetRequestHeader("X-Access-Key")}\" -d '{requestJson}' {Url(chainID, endPoint)}"; + + Debug.Log($"{curlRequest}"); try { await req.SendWebRequest(); @@ -51,6 +55,7 @@ public async Task HttpPost(string chainID, string endPoint, object args, } string returnText = req.downloadHandler.text; + Debug.Log(returnText); req.Dispose(); return returnText; } @@ -104,6 +109,30 @@ public async Task HttpPost(string chainID, string endPoint, object args, return ""; } + public async void HttpStream(string chainID, string endPoint, object args, WebRPCStreamOptions options, int retries = 0) + { + var requestJson = JsonConvert.SerializeObject(args, serializerSettings); + using var req = UnityWebRequest.Put(Url(chainID, endPoint), requestJson); + req.SetRequestHeader("Content-Type", "application/json"); + req.SetRequestHeader("X-Access-Key", _builderApiKey); + req.downloadHandler = new DownloadHandlerStream(options); + req.method = UnityWebRequest.kHttpVerbPOST; + + _streams.Add(req); + await req.SendWebRequest(); + + if (_streams.Contains(req)) + _streams.Remove(req); + } + + public void AbortStreams() + { + foreach (var stream in _streams) + stream.Abort(); + + _streams.Clear(); + } + private async Task RetryHttpPost(string chainID, string endPoint, object args, float waitInSeconds, int retries) { await AsyncExtensions.DelayTask(waitInSeconds); @@ -132,4 +161,4 @@ private string HostName(string chainID) return $"https://{indexerName}-indexer.sequence.app"; } } -} \ No newline at end of file +} diff --git a/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/IHttpHandler.cs b/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/IHttpHandler.cs index 2ff5c8f7f..b718b0e3d 100644 --- a/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/IHttpHandler.cs +++ b/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/IHttpHandler.cs @@ -9,5 +9,7 @@ public interface IHttpHandler /// /// public Task HttpPost(string chainID, string endPoint, object args, int retries = 0); + public void HttpStream(string chainID, string endPoint, object args, WebRPCStreamOptions options, int retries = 0); + public void AbortStreams(); } } \ No newline at end of file diff --git a/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/IIndexer.cs b/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/IIndexer.cs index abfd88bb5..7f9477189 100644 --- a/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/IIndexer.cs +++ b/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/IIndexer.cs @@ -93,5 +93,31 @@ public Task> GetTokenBalancesOrganizedInDic /// /// If the network request fails public Task GetTransactionHistory(GetTransactionHistoryArgs args); + + /// + /// Subscribe to receipt events. + /// + /// Specify the account or event you want to receive events from by defining a filter. + /// Actions triggered whenever a message or error event is received. + public void SubscribeReceipts(SubscribeReceiptsArgs args, WebRPCStreamOptions options); + + /// + /// Subscribe to smart contract events. + /// + /// Specify the accounts or contracts you want to receive events from by defining a filter. + /// Actions triggered whenever a message or error event is received. + public void SubscribeEvents(SubscribeEventsArgs args, WebRPCStreamOptions options); + + /// + /// Subscribe to balance update events for a given contract address. + /// + /// Define the contract address you want to receive events from. + /// Actions triggered whenever a message or error event is received. + public void SubscribeBalanceUpdates(SubscribeBalanceUpdatesArgs args, WebRPCStreamOptions options); + + /// + /// Aborts all running streams. + /// + public void AbortStreams(); } } \ No newline at end of file diff --git a/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/Indexer.cs b/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/Indexer.cs index 668ff6610..1a58302fa 100644 --- a/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/Indexer.cs +++ b/Packages/Sequence-Unity/Sequence/SequenceSDK/Indexer/Indexer.cs @@ -41,6 +41,7 @@ public enum EventLogType public enum EventLogDataType { UNKNOWN, + EVENT, TOKEN_TRANSFER, SEQUENCE_TXN, NATIVE_TOKEN_TRANSFER @@ -321,6 +322,38 @@ public static async Task GetTransactionHistory(stri var responseBody = await HttpPost(chainID, "GetTransactionHistory", args, retries, httpHandler, caller); return BuildResponse(responseBody); } + + /// + /// Subscribe to receipt events. + /// + public static void SubscribeReceipts(string chainID, SubscribeReceiptsArgs args, WebRPCStreamOptions options, IIndexer caller) + { + new HttpHandler(_builderApiKey, caller).HttpStream(chainID, "SubscribeReceipts", args, options); + } + + /// + /// Subscribe to smart contract events. + /// + public static void SubscribeEvents(string chainID, SubscribeEventsArgs args, WebRPCStreamOptions options, IIndexer caller) + { + new HttpHandler(_builderApiKey, caller).HttpStream(chainID, "SubscribeEvents", args, options); + } + + /// + /// Subscribe to balance update events for a given contract address. + /// + public static void SubscribeBalanceUpdates(string chainID, SubscribeBalanceUpdatesArgs args, WebRPCStreamOptions options, IIndexer caller) + { + new HttpHandler(_builderApiKey, caller).HttpStream(chainID, "SubscribeBalanceUpdates", args, options); + } + + /// + /// Subscribe to balance update events for a given contract address. + /// + public static void AbortStreams(IIndexer caller) + { + new HttpHandler(_builderApiKey, caller).AbortStreams(); + } [Obsolete] private static async Task HttpPost(BigInteger chainID, string endPoint, object args, int retries = 0) diff --git a/Packages/Sequence-Unity/package.json b/Packages/Sequence-Unity/package.json index 184b694ab..1f18690d3 100644 --- a/Packages/Sequence-Unity/package.json +++ b/Packages/Sequence-Unity/package.json @@ -1,6 +1,6 @@ { "name": "xyz.0xsequence.waas-unity", - "version": "3.18.1", + "version": "3.19.0", "displayName": "Sequence Embedded Wallet SDK", "description": "A Unity SDK for the Sequence WaaS API", "unity": "2021.3",