From dc92366c203cae8c020ae8e8c9facd5645fe839c Mon Sep 17 00:00:00 2001 From: 0xFirekeeper <0xFirekeeper@gmail.com> Date: Thu, 13 Jun 2024 11:15:36 +0300 Subject: [PATCH 1/3] Native transfer extension --- .../Thirdweb.WalletExtension.Tests.cs | 25 +++++++++++- .../ThirdwebWalletExtensions.cs | 38 +++++++++++++++++++ 2 files changed, 62 insertions(+), 1 deletion(-) diff --git a/Thirdweb.Tests/Thirdweb.WalletExtension.Tests.cs b/Thirdweb.Tests/Thirdweb.WalletExtension.Tests.cs index 449f82a..f551578 100644 --- a/Thirdweb.Tests/Thirdweb.WalletExtension.Tests.cs +++ b/Thirdweb.Tests/Thirdweb.WalletExtension.Tests.cs @@ -13,7 +13,8 @@ private async Task GetWallet() { var client = ThirdwebClient.Create(secretKey: _secretKey); var privateKeyWallet = await PrivateKeyWallet.Create(client, _testPrivateKey); - return privateKeyWallet; + var smartAccount = await SmartWallet.Create(client, personalWallet: privateKeyWallet, factoryAddress: "0xbf1C9aA4B1A085f7DA890a44E82B0A1289A40052", gasless: true, chainId: 421614); + return smartAccount; } [Fact] @@ -21,15 +22,26 @@ public async Task NullChecks() { var client = ThirdwebClient.Create(secretKey: _secretKey); var wallet = await GetWallet(); + _ = await Assert.ThrowsAsync(async () => await wallet.GetBalance(null, _chainId)); _ = await Assert.ThrowsAsync(async () => await wallet.GetBalance(client, BigInteger.Zero)); _ = await Assert.ThrowsAsync(async () => await wallet.GetBalance(client, -1)); + _ = await Assert.ThrowsAsync(async () => await wallet.Transfer(null, BigInteger.Zero, null, BigInteger.Zero)); + _ = await Assert.ThrowsAsync(async () => await wallet.Transfer(client, BigInteger.Zero, null, BigInteger.Zero)); + _ = await Assert.ThrowsAsync(async () => await wallet.Transfer(client, -1, string.Empty, BigInteger.Zero)); + _ = await Assert.ThrowsAsync(async () => await wallet.Transfer(client, _chainId, null, BigInteger.Zero)); + _ = await Assert.ThrowsAsync(async () => await wallet.Transfer(client, _chainId, string.Empty, BigInteger.Zero)); + _ = await Assert.ThrowsAsync(async () => await wallet.Transfer(client, _chainId, Constants.ADDRESS_ZERO, -1)); + client = null; + _ = await Assert.ThrowsAsync(async () => await wallet.GetBalance(client, _chainId)); + _ = await Assert.ThrowsAsync(async () => await wallet.Transfer(client, BigInteger.Zero, null, BigInteger.Zero)); wallet = null; _ = await Assert.ThrowsAsync(async () => await wallet.GetBalance(client, _chainId)); + _ = await Assert.ThrowsAsync(async () => await wallet.Transfer(client, BigInteger.Zero, null, BigInteger.Zero)); } [Fact] @@ -40,5 +52,16 @@ public async Task GetBalance() var balance = await wallet.GetBalance(client, _chainId); Assert.True(balance >= 0); } + + [Fact] + public async Task Transfer() + { + var client = ThirdwebClient.Create(secretKey: _secretKey); + var wallet = await GetWallet(); + var toAddress = await wallet.GetAddress(); + var receipt = await wallet.Transfer(client, _chainId, toAddress, BigInteger.Zero); + Assert.NotNull(receipt); + Assert.True(receipt.TransactionHash.Length == 66); + } } } diff --git a/Thirdweb/Thirdweb.Wallets/ThirdwebWalletExtensions.cs b/Thirdweb/Thirdweb.Wallets/ThirdwebWalletExtensions.cs index 59ddaee..f7ba3c2 100644 --- a/Thirdweb/Thirdweb.Wallets/ThirdwebWalletExtensions.cs +++ b/Thirdweb/Thirdweb.Wallets/ThirdwebWalletExtensions.cs @@ -1,5 +1,6 @@ using System.Numerics; using Nethereum.Hex.HexTypes; +using Nethereum.RPC.Eth.DTOs; namespace Thirdweb { @@ -27,6 +28,43 @@ public static async Task GetBalance(this IThirdwebWallet wallet, Thi return new HexBigInteger(balanceHex).Value; } + public static async Task Transfer(this IThirdwebWallet wallet, ThirdwebClient client, BigInteger chainId, string toAddress, BigInteger weiAmount) + { + if (wallet == null) + { + throw new ArgumentNullException(nameof(wallet)); + } + + if (client == null) + { + throw new ArgumentNullException(nameof(client)); + } + + if (string.IsNullOrEmpty(toAddress)) + { + throw new ArgumentException(nameof(toAddress), "Recipient address cannot be null or empty."); + } + + if (weiAmount < 0) + { + throw new ArgumentOutOfRangeException(nameof(weiAmount), "Amount must be 0 or greater."); + } + + if (chainId <= 0) + { + throw new ArgumentOutOfRangeException(nameof(chainId), "Chain ID must be greater than 0."); + } + + var txInput = new ThirdwebTransactionInput() + { + From = await wallet.GetAddress(), + To = toAddress, + Value = new HexBigInteger(weiAmount) + }; + var tx = await ThirdwebTransaction.Create(client, wallet, txInput, chainId); + return await ThirdwebTransaction.SendAndWaitForTransactionReceipt(tx); + } + // TODO: Tx Listener? } } From d0479156ad0138f480b46fc00905d3f742d28b63 Mon Sep 17 00:00:00 2001 From: 0xFirekeeper <0xFirekeeper@gmail.com> Date: Thu, 13 Jun 2024 11:24:03 +0300 Subject: [PATCH 2/3] cov --- Thirdweb.Tests/Thirdweb.WalletExtension.Tests.cs | 4 ++-- Thirdweb/Thirdweb.Wallets/ThirdwebWalletExtensions.cs | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Thirdweb.Tests/Thirdweb.WalletExtension.Tests.cs b/Thirdweb.Tests/Thirdweb.WalletExtension.Tests.cs index f551578..674d55f 100644 --- a/Thirdweb.Tests/Thirdweb.WalletExtension.Tests.cs +++ b/Thirdweb.Tests/Thirdweb.WalletExtension.Tests.cs @@ -28,8 +28,8 @@ public async Task NullChecks() _ = await Assert.ThrowsAsync(async () => await wallet.GetBalance(client, -1)); _ = await Assert.ThrowsAsync(async () => await wallet.Transfer(null, BigInteger.Zero, null, BigInteger.Zero)); - _ = await Assert.ThrowsAsync(async () => await wallet.Transfer(client, BigInteger.Zero, null, BigInteger.Zero)); - _ = await Assert.ThrowsAsync(async () => await wallet.Transfer(client, -1, string.Empty, BigInteger.Zero)); + _ = await Assert.ThrowsAsync(async () => await wallet.Transfer(client, BigInteger.Zero, null, BigInteger.Zero)); + _ = await Assert.ThrowsAsync(async () => await wallet.Transfer(client, -1, string.Empty, BigInteger.Zero)); _ = await Assert.ThrowsAsync(async () => await wallet.Transfer(client, _chainId, null, BigInteger.Zero)); _ = await Assert.ThrowsAsync(async () => await wallet.Transfer(client, _chainId, string.Empty, BigInteger.Zero)); _ = await Assert.ThrowsAsync(async () => await wallet.Transfer(client, _chainId, Constants.ADDRESS_ZERO, -1)); diff --git a/Thirdweb/Thirdweb.Wallets/ThirdwebWalletExtensions.cs b/Thirdweb/Thirdweb.Wallets/ThirdwebWalletExtensions.cs index f7ba3c2..bc69dab 100644 --- a/Thirdweb/Thirdweb.Wallets/ThirdwebWalletExtensions.cs +++ b/Thirdweb/Thirdweb.Wallets/ThirdwebWalletExtensions.cs @@ -40,6 +40,11 @@ public static async Task Transfer(this IThirdwebWallet walle throw new ArgumentNullException(nameof(client)); } + if (chainId <= 0) + { + throw new ArgumentOutOfRangeException(nameof(chainId), "Chain ID must be greater than 0."); + } + if (string.IsNullOrEmpty(toAddress)) { throw new ArgumentException(nameof(toAddress), "Recipient address cannot be null or empty."); @@ -50,11 +55,6 @@ public static async Task Transfer(this IThirdwebWallet walle throw new ArgumentOutOfRangeException(nameof(weiAmount), "Amount must be 0 or greater."); } - if (chainId <= 0) - { - throw new ArgumentOutOfRangeException(nameof(chainId), "Chain ID must be greater than 0."); - } - var txInput = new ThirdwebTransactionInput() { From = await wallet.GetAddress(), From 24805799ab0ea0566e8384b5aa1eba3b22fa92b5 Mon Sep 17 00:00:00 2001 From: 0xFirekeeper <0xFirekeeper@gmail.com> Date: Thu, 13 Jun 2024 11:24:38 +0300 Subject: [PATCH 3/3] sw auth should personal sign itself for wrap --- .../SmartWallet/SmartWallet.cs | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/Thirdweb/Thirdweb.Wallets/SmartWallet/SmartWallet.cs b/Thirdweb/Thirdweb.Wallets/SmartWallet/SmartWallet.cs index a63aee6..fd829e3 100644 --- a/Thirdweb/Thirdweb.Wallets/SmartWallet/SmartWallet.cs +++ b/Thirdweb/Thirdweb.Wallets/SmartWallet/SmartWallet.cs @@ -582,7 +582,30 @@ public async Task Authenticate( IThirdwebHttpClient httpClientOverride = null ) { - return await _personalAccount.Authenticate(domain, chainId, authPayloadPath, authLoginPath, httpClientOverride); + var payloadURL = domain + authPayloadPath; + var loginURL = domain + authLoginPath; + + var payloadBodyRaw = new { address = await GetAddress(), chainId = chainId.ToString() }; + var payloadBody = JsonConvert.SerializeObject(payloadBodyRaw); + + using var httpClient = httpClientOverride ?? _client.HttpClient; + + var payloadContent = new StringContent(payloadBody, Encoding.UTF8, "application/json"); + var payloadResponse = await httpClient.PostAsync(payloadURL, payloadContent); + _ = payloadResponse.EnsureSuccessStatusCode(); + var payloadString = await payloadResponse.Content.ReadAsStringAsync(); + + var loginBodyRaw = JsonConvert.DeserializeObject(payloadString); + var payloadToSign = Utils.GenerateSIWE(loginBodyRaw.payload); + + loginBodyRaw.signature = await PersonalSign(payloadToSign); + var loginBody = JsonConvert.SerializeObject(new { payload = loginBodyRaw }); + + var loginContent = new StringContent(loginBody, Encoding.UTF8, "application/json"); + var loginResponse = await httpClient.PostAsync(loginURL, loginContent); + _ = loginResponse.EnsureSuccessStatusCode(); + var responseString = await loginResponse.Content.ReadAsStringAsync(); + return responseString; } } }