diff --git a/Thirdweb.Tests/Thirdweb.Transactions/Thirdweb.ZkSmartWallet.Tests.cs b/Thirdweb.Tests/Thirdweb.Transactions/Thirdweb.ZkSmartWallet.Tests.cs index 0de2c2cc..3bc6452c 100644 --- a/Thirdweb.Tests/Thirdweb.Transactions/Thirdweb.ZkSmartWallet.Tests.cs +++ b/Thirdweb.Tests/Thirdweb.Transactions/Thirdweb.ZkSmartWallet.Tests.cs @@ -102,39 +102,39 @@ public async Task SendGaslessZkTx_Success() // Assert.True(hash.Length == 66); // } - [Fact(Timeout = 120000)] - public async Task SendGaslessZkTx_Abstract_Success() - { - var account = await this.GetSmartAccount(zkChainId: 11124); - var hash = await account.SendTransaction( - new ThirdwebTransactionInput(11124) - { - From = await account.GetAddress(), - To = await account.GetAddress(), - Value = new Nethereum.Hex.HexTypes.HexBigInteger(0), - Data = "0x" - } - ); - Assert.NotNull(hash); - Assert.True(hash.Length == 66); - } + // [Fact(Timeout = 120000)] + // public async Task SendGaslessZkTx_Abstract_Success() + // { + // var account = await this.GetSmartAccount(zkChainId: 11124); + // var hash = await account.SendTransaction( + // new ThirdwebTransactionInput(11124) + // { + // From = await account.GetAddress(), + // To = await account.GetAddress(), + // Value = new Nethereum.Hex.HexTypes.HexBigInteger(0), + // Data = "0x" + // } + // ); + // Assert.NotNull(hash); + // Assert.True(hash.Length == 66); + // } - [Fact(Timeout = 120000)] - public async Task SendGaslessZkTx_Creator_Success() - { - var account = await this.GetSmartAccount(zkChainId: 4654); - var hash = await account.SendTransaction( - new ThirdwebTransactionInput(4654) - { - From = await account.GetAddress(), - To = await account.GetAddress(), - Value = new Nethereum.Hex.HexTypes.HexBigInteger(0), - Data = "0x" - } - ); - Assert.NotNull(hash); - Assert.True(hash.Length == 66); - } + // [Fact(Timeout = 120000)] + // public async Task SendGaslessZkTx_Creator_Success() + // { + // var account = await this.GetSmartAccount(zkChainId: 4654); + // var hash = await account.SendTransaction( + // new ThirdwebTransactionInput(4654) + // { + // From = await account.GetAddress(), + // To = await account.GetAddress(), + // Value = new Nethereum.Hex.HexTypes.HexBigInteger(0), + // Data = "0x" + // } + // ); + // Assert.NotNull(hash); + // Assert.True(hash.Length == 66); + // } [Fact(Timeout = 120000)] public async Task ZkSync_Switch() diff --git a/Thirdweb.Tests/Thirdweb.Wallets/Thirdweb.Wallets.Tests.cs b/Thirdweb.Tests/Thirdweb.Wallets/Thirdweb.Wallets.Tests.cs index 8328a08d..517c11e9 100644 --- a/Thirdweb.Tests/Thirdweb.Wallets/Thirdweb.Wallets.Tests.cs +++ b/Thirdweb.Tests/Thirdweb.Wallets/Thirdweb.Wallets.Tests.cs @@ -10,7 +10,7 @@ public WalletTests(ITestOutputHelper output) private async Task GetSmartAccount() { var privateKeyAccount = await PrivateKeyWallet.Generate(this.Client); - var smartAccount = await SmartWallet.Create(personalWallet: privateKeyAccount, factoryAddress: "0xbf1C9aA4B1A085f7DA890a44E82B0A1289A40052", gasless: true, chainId: 421614); + var smartAccount = await SmartWallet.Create(personalWallet: privateKeyAccount, chainId: 421614); return smartAccount; } @@ -230,4 +230,21 @@ public async Task RecoverAddress_AllVariants_NullTests() ); #nullable restore } + + [Fact(Timeout = 120000)] + public async Task SwitchNetwork_Success() + { + var smartWallet = await this.GetSmartAccount(); + var wrappedSmartWallet = await SmartWallet.Create(personalWallet: smartWallet, chainId: 421614); + + Assert.Equal(421614, smartWallet.ActiveChainId); + Assert.Equal(421614, wrappedSmartWallet.ActiveChainId); + + await wrappedSmartWallet.SwitchNetwork(11155111); + + Assert.Equal(11155111, wrappedSmartWallet.ActiveChainId); + Assert.Equal(11155111, smartWallet.ActiveChainId); + + await (await PrivateKeyWallet.Generate(this.Client)).SwitchNetwork(11155111); + } } diff --git a/Thirdweb/Thirdweb.RPC/ThirdwebRPC.cs b/Thirdweb/Thirdweb.RPC/ThirdwebRPC.cs index 729edddd..2a49bb81 100644 --- a/Thirdweb/Thirdweb.RPC/ThirdwebRPC.cs +++ b/Thirdweb/Thirdweb.RPC/ThirdwebRPC.cs @@ -148,7 +148,7 @@ public async Task SendRequestAsync(string method, params o private ThirdwebRPC(ThirdwebClient client, BigInteger chainId) { this._httpClient = client.HttpClient; - this._rpcUrl = new Uri($"https://{chainId}.rpc.thirdweb.com/"); + this._rpcUrl = new Uri($"https://{chainId}.rpc.thirdweb.com/{client.ClientId}"); this._rpcTimeout = TimeSpan.FromMilliseconds(client.FetchTimeoutOptions.GetTimeout(TimeoutType.Rpc)); _ = this.StartBackgroundFlushAsync(); } diff --git a/Thirdweb/Thirdweb.Wallets/IThirdwebWallet.cs b/Thirdweb/Thirdweb.Wallets/IThirdwebWallet.cs index 54bd1554..83e2129e 100644 --- a/Thirdweb/Thirdweb.Wallets/IThirdwebWallet.cs +++ b/Thirdweb/Thirdweb.Wallets/IThirdwebWallet.cs @@ -176,6 +176,12 @@ Task> LinkAccount( /// Set to true if the wallet will also be the executor of the transaction, otherwise false. /// The signed authorization as an that can be used with . Task SignAuthorization(BigInteger chainId, string contractAddress, bool willSelfExecute); + + /// + /// Attempts to set the active network to the specified chain ID. + /// + /// The chain ID to switch to. + Task SwitchNetwork(BigInteger chainId); } /// diff --git a/Thirdweb/Thirdweb.Wallets/InAppWallet/EcosystemWallet/EcosystemWallet.cs b/Thirdweb/Thirdweb.Wallets/InAppWallet/EcosystemWallet/EcosystemWallet.cs index 8bdcb53f..d594e248 100644 --- a/Thirdweb/Thirdweb.Wallets/InAppWallet/EcosystemWallet/EcosystemWallet.cs +++ b/Thirdweb/Thirdweb.Wallets/InAppWallet/EcosystemWallet/EcosystemWallet.cs @@ -986,5 +986,10 @@ public Task SignAuthorization(BigInteger chainId, string c throw new NotImplementedException(); } + public Task SwitchNetwork(BigInteger chainId) + { + return Task.CompletedTask; + } + #endregion } diff --git a/Thirdweb/Thirdweb.Wallets/PrivateKeyWallet/PrivateKeyWallet.cs b/Thirdweb/Thirdweb.Wallets/PrivateKeyWallet/PrivateKeyWallet.cs index 481b92b0..3af321bd 100644 --- a/Thirdweb/Thirdweb.Wallets/PrivateKeyWallet/PrivateKeyWallet.cs +++ b/Thirdweb/Thirdweb.Wallets/PrivateKeyWallet/PrivateKeyWallet.cs @@ -469,5 +469,10 @@ public async Task SignAuthorization(BigInteger chainId, st return new EIP7702Authorization(chainId, contractAddress, nonce, authorizationSignature.V, authorizationSignature.R, authorizationSignature.S); } + public Task SwitchNetwork(BigInteger chainId) + { + return Task.CompletedTask; + } + #endregion } diff --git a/Thirdweb/Thirdweb.Wallets/SmartWallet/SmartWallet.cs b/Thirdweb/Thirdweb.Wallets/SmartWallet/SmartWallet.cs index 37215a4d..736bb8f4 100644 --- a/Thirdweb/Thirdweb.Wallets/SmartWallet/SmartWallet.cs +++ b/Thirdweb/Thirdweb.Wallets/SmartWallet/SmartWallet.cs @@ -27,11 +27,12 @@ public class SmartWallet : IThirdwebWallet public bool IsDeploying { get; private set; } + public BigInteger ActiveChainId { get; private set; } + private readonly IThirdwebWallet _personalAccount; private ThirdwebContract _factoryContract; private ThirdwebContract _accountContract; private ThirdwebContract _entryPointContract; - private BigInteger _chainId; private string _bundlerUrl; private string _paymasterUrl; private bool _isApproving; @@ -115,7 +116,7 @@ BigInteger erc20PaymasterStorageSlot this._personalAccount = personalAccount; this._gasless = gasless; - this._chainId = chainId; + this.ActiveChainId = chainId; this._bundlerUrl = bundlerUrl; this._paymasterUrl = paymasterUrl; this._entryPointContract = entryPointContract; @@ -242,51 +243,18 @@ public Task GetPersonalWallet() return Task.FromResult(this._personalAccount); } - /// - /// Attempts to set the active network to the specified chain ID. Requires related contracts to be deterministically deployed on the chain. - /// - /// The chain ID to switch to. - /// - public async Task SwitchNetwork(BigInteger chainId) - { - if (this._chainId == chainId) - { - return; - } - - if (this.UseERC20Paymaster) - { - throw new InvalidOperationException("You cannot switch networks when using an ERC20 paymaster yet."); - } - - this._bundlerUrl = this._bundlerUrl.Contains(".thirdweb.com") ? $"https://{chainId}.bundler.thirdweb.com/v2" : this._bundlerUrl; - this._paymasterUrl = this._paymasterUrl.Contains(".thirdweb.com") ? $"https://{chainId}.bundler.thirdweb.com/v2" : this._paymasterUrl; - - if (!await Utils.IsZkSync(this.Client, chainId).ConfigureAwait(false)) - { - this._entryPointContract = await ThirdwebContract.Create(this.Client, this._entryPointContract.Address, chainId, this._entryPointContract.Abi).ConfigureAwait(false); - this._factoryContract = await ThirdwebContract.Create(this.Client, this._factoryContract.Address, chainId, this._factoryContract.Abi).ConfigureAwait(false); - - var personalAddress = await this._personalAccount.GetAddress().ConfigureAwait(false); - var accountAddress = await ThirdwebContract.Read(this._factoryContract, "getAddress", personalAddress, Array.Empty()).ConfigureAwait(false); - this._accountContract = await ThirdwebContract.Create(this._personalAccount.Client, accountAddress, chainId, Constants.ACCOUNT_V06_ABI).ConfigureAwait(false); - } - - this._chainId = chainId; - } - /// /// Checks if the smart account is deployed on the current chain. A smart account is typically deployed when a personal message is signed or a transaction is sent. /// /// True if deployed, otherwise false. public async Task IsDeployed() { - if (await Utils.IsZkSync(this.Client, this._chainId).ConfigureAwait(false)) + if (await Utils.IsZkSync(this.Client, this.ActiveChainId).ConfigureAwait(false)) { return true; } - var code = await ThirdwebRPC.GetRpcInstance(this.Client, this._chainId).SendRequestAsync("eth_getCode", this._accountContract.Address, "latest").ConfigureAwait(false); + var code = await ThirdwebRPC.GetRpcInstance(this.Client, this.ActiveChainId).SendRequestAsync("eth_getCode", this._accountContract.Address, "latest").ConfigureAwait(false); return code != "0x"; } @@ -295,7 +263,7 @@ public async Task IsDeployed() /// public async Task ForceDeploy() { - if (await Utils.IsZkSync(this.Client, this._chainId).ConfigureAwait(false)) + if (await Utils.IsZkSync(this.Client, this.ActiveChainId).ConfigureAwait(false)) { return; } @@ -310,14 +278,14 @@ public async Task ForceDeploy() throw new InvalidOperationException("SmartAccount.ForceDeploy: Account is already deploying."); } - var input = new ThirdwebTransactionInput(this._chainId) + var input = new ThirdwebTransactionInput(this.ActiveChainId) { Data = "0x", To = this._accountContract.Address, Value = new HexBigInteger(0) }; var txHash = await this.SendTransaction(input).ConfigureAwait(false); - _ = await ThirdwebTransaction.WaitForTransactionReceipt(this.Client, this._chainId, txHash).ConfigureAwait(false); + _ = await ThirdwebTransaction.WaitForTransactionReceipt(this.Client, this.ActiveChainId, txHash).ConfigureAwait(false); } /// @@ -334,7 +302,7 @@ public async Task IsValidSignature(string message, string signature) if (isCounterFactual) { var erc6492Sig = new ABIEncode().DecodeEncodedComplexType(signature.HexToBytes().Take(signature.Length - 32).ToArray()); - var multicall3 = await ThirdwebContract.Create(this.Client, Constants.MULTICALL3_ADDRESS, this._chainId).ConfigureAwait(false); + var multicall3 = await ThirdwebContract.Create(this.Client, Constants.MULTICALL3_ADDRESS, this.ActiveChainId).ConfigureAwait(false); List result; try { @@ -402,7 +370,7 @@ public async Task IsValidSignature(string message, string signature) /// A list of admin addresses. public async Task> GetAllAdmins() { - if (await Utils.IsZkSync(this.Client, this._chainId).ConfigureAwait(false)) + if (await Utils.IsZkSync(this.Client, this.ActiveChainId).ConfigureAwait(false)) { throw new InvalidOperationException("Account Permissions are not supported in ZkSync"); } @@ -417,7 +385,7 @@ public async Task> GetAllAdmins() /// A list of . public async Task> GetAllActiveSigners() { - if (await Utils.IsZkSync(this.Client, this._chainId).ConfigureAwait(false)) + if (await Utils.IsZkSync(this.Client, this.ActiveChainId).ConfigureAwait(false)) { throw new InvalidOperationException("Account Permissions are not supported in ZkSync"); } @@ -446,7 +414,7 @@ public async Task CreateSessionKey( string reqValidityEndTimestamp ) { - if (await Utils.IsZkSync(this.Client, this._chainId).ConfigureAwait(false)) + if (await Utils.IsZkSync(this.Client, this.ActiveChainId).ConfigureAwait(false)) { throw new InvalidOperationException("Account Permissions are not supported in ZkSync"); } @@ -464,17 +432,19 @@ string reqValidityEndTimestamp Uid = Guid.NewGuid().ToByteArray() }; - var signature = await EIP712.GenerateSignature_SmartAccount("Account", "1", this._chainId, await this.GetAddress().ConfigureAwait(false), request, this._personalAccount).ConfigureAwait(false); + var signature = await EIP712 + .GenerateSignature_SmartAccount("Account", "1", this.ActiveChainId, await this.GetAddress().ConfigureAwait(false), request, this._personalAccount) + .ConfigureAwait(false); // Do it this way to avoid triggering an extra sig from estimation var data = new Contract(null, this._accountContract.Abi, this._accountContract.Address).GetFunction("setPermissionsForSigner").GetData(request, signature.HexToBytes()); - var txInput = new ThirdwebTransactionInput(this._chainId) + var txInput = new ThirdwebTransactionInput(this.ActiveChainId) { To = this._accountContract.Address, Value = new HexBigInteger(0), Data = data }; var txHash = await this.SendTransaction(txInput).ConfigureAwait(false); - return await ThirdwebTransaction.WaitForTransactionReceipt(this.Client, this._chainId, txHash).ConfigureAwait(false); + return await ThirdwebTransaction.WaitForTransactionReceipt(this.Client, this.ActiveChainId, txHash).ConfigureAwait(false); } /// @@ -484,7 +454,7 @@ string reqValidityEndTimestamp /// The transaction receipt. public async Task RevokeSessionKey(string signerAddress) { - return await Utils.IsZkSync(this.Client, this._chainId).ConfigureAwait(false) + return await Utils.IsZkSync(this.Client, this.ActiveChainId).ConfigureAwait(false) ? throw new InvalidOperationException("Account Permissions are not supported in ZkSync") : await this.CreateSessionKey(signerAddress, new List(), "0", "0", "0", "0", Utils.GetUnixTimeStampIn10Years().ToString()).ConfigureAwait(false); } @@ -496,7 +466,7 @@ public async Task RevokeSessionKey(string signerAddr /// The transaction receipt. public async Task AddAdmin(string admin) { - if (await Utils.IsZkSync(this.Client, this._chainId).ConfigureAwait(false)) + if (await Utils.IsZkSync(this.Client, this.ActiveChainId).ConfigureAwait(false)) { throw new InvalidOperationException("Account Permissions are not supported in ZkSync"); } @@ -514,16 +484,16 @@ public async Task AddAdmin(string admin) Uid = Guid.NewGuid().ToByteArray() }; - var signature = await EIP712.GenerateSignature_SmartAccount("Account", "1", this._chainId, await this.GetAddress(), request, this._personalAccount).ConfigureAwait(false); + var signature = await EIP712.GenerateSignature_SmartAccount("Account", "1", this.ActiveChainId, await this.GetAddress(), request, this._personalAccount).ConfigureAwait(false); var data = new Contract(null, this._accountContract.Abi, this._accountContract.Address).GetFunction("setPermissionsForSigner").GetData(request, signature.HexToBytes()); - var txInput = new ThirdwebTransactionInput(this._chainId) + var txInput = new ThirdwebTransactionInput(this.ActiveChainId) { To = this._accountContract.Address, Value = new HexBigInteger(0), Data = data }; var txHash = await this.SendTransaction(txInput).ConfigureAwait(false); - return await ThirdwebTransaction.WaitForTransactionReceipt(this.Client, this._chainId, txHash).ConfigureAwait(false); + return await ThirdwebTransaction.WaitForTransactionReceipt(this.Client, this.ActiveChainId, txHash).ConfigureAwait(false); } /// @@ -533,7 +503,7 @@ public async Task AddAdmin(string admin) /// The transaction receipt. public async Task RemoveAdmin(string admin) { - if (await Utils.IsZkSync(this.Client, this._chainId).ConfigureAwait(false)) + if (await Utils.IsZkSync(this.Client, this.ActiveChainId).ConfigureAwait(false)) { throw new InvalidOperationException("Account Permissions are not supported in ZkSync"); } @@ -551,16 +521,18 @@ public async Task RemoveAdmin(string admin) Uid = Guid.NewGuid().ToByteArray() }; - var signature = await EIP712.GenerateSignature_SmartAccount("Account", "1", this._chainId, await this.GetAddress().ConfigureAwait(false), request, this._personalAccount).ConfigureAwait(false); + var signature = await EIP712 + .GenerateSignature_SmartAccount("Account", "1", this.ActiveChainId, await this.GetAddress().ConfigureAwait(false), request, this._personalAccount) + .ConfigureAwait(false); var data = new Contract(null, this._accountContract.Abi, this._accountContract.Address).GetFunction("setPermissionsForSigner").GetData(request, signature.HexToBytes()); - var txInput = new ThirdwebTransactionInput(this._chainId) + var txInput = new ThirdwebTransactionInput(this.ActiveChainId) { To = this._accountContract.Address, Value = new HexBigInteger(0), Data = data }; var txHash = await this.SendTransaction(txInput).ConfigureAwait(false); - return await ThirdwebTransaction.WaitForTransactionReceipt(this.Client, this._chainId, txHash).ConfigureAwait(false); + return await ThirdwebTransaction.WaitForTransactionReceipt(this.Client, this.ActiveChainId, txHash).ConfigureAwait(false); } /// @@ -572,7 +544,7 @@ public async Task EstimateUserOperationGas(ThirdwebTransactionInput { await this.SwitchNetwork(transaction.ChainId.Value).ConfigureAwait(false); - if (await Utils.IsZkSync(this.Client, this._chainId).ConfigureAwait(false)) + if (await Utils.IsZkSync(this.Client, this.ActiveChainId).ConfigureAwait(false)) { throw new Exception("User Operations are not supported in ZkSync"); } @@ -623,7 +595,7 @@ private async Task SignUserOp(ThirdwebTransactionInput transactionInput, try { this._isApproving = true; - var tokenContract = await ThirdwebContract.Create(this.Client, this._erc20PaymasterToken, this._chainId).ConfigureAwait(false); + var tokenContract = await ThirdwebContract.Create(this.Client, this._erc20PaymasterToken, this.ActiveChainId).ConfigureAwait(false); var approvedAmount = await tokenContract.ERC20_Allowance(this._accountContract.Address, this._erc20PaymasterAddress).ConfigureAwait(false); if (approvedAmount == 0) { @@ -997,12 +969,12 @@ public async Task SendTransaction(ThirdwebTransactionInput transactionIn await this.SwitchNetwork(transactionInput.ChainId.Value).ConfigureAwait(false); var transaction = await ThirdwebTransaction - .Create(await Utils.IsZkSync(this.Client, this._chainId).ConfigureAwait(false) ? this._personalAccount : this, transactionInput) + .Create(await Utils.IsZkSync(this.Client, this.ActiveChainId).ConfigureAwait(false) ? this._personalAccount : this, transactionInput) .ConfigureAwait(false); transaction = await ThirdwebTransaction.Prepare(transaction).ConfigureAwait(false); transactionInput = transaction.Input; - if (await Utils.IsZkSync(this.Client, this._chainId).ConfigureAwait(false)) + if (await Utils.IsZkSync(this.Client, this.ActiveChainId).ConfigureAwait(false)) { if (this._gasless) { @@ -1023,7 +995,7 @@ public async Task SendTransaction(ThirdwebTransactionInput transactionIn data = Utils.BytesToHex(zkTx.Data), maxFeePerGas = zkTx.MaxFeePerGas.ToString(), maxPriorityFeePerGas = zkTx.MaxPriorityFeePerGas.ToString(), - chainId = this._chainId.ToString(), + chainId = this.ActiveChainId.ToString(), signedTransaction = zkTxSigned, paymaster } @@ -1046,12 +1018,12 @@ public async Task SendTransaction(ThirdwebTransactionInput transactionIn public async Task ExecuteTransaction(ThirdwebTransactionInput transactionInput) { var txHash = await this.SendTransaction(transactionInput).ConfigureAwait(false); - return await ThirdwebTransaction.WaitForTransactionReceipt(this.Client, this._chainId, txHash).ConfigureAwait(false); + return await ThirdwebTransaction.WaitForTransactionReceipt(this.Client, this.ActiveChainId, txHash).ConfigureAwait(false); } public async Task GetAddress() { - return await Utils.IsZkSync(this.Client, this._chainId).ConfigureAwait(false) + return await Utils.IsZkSync(this.Client, this.ActiveChainId).ConfigureAwait(false) ? await this._personalAccount.GetAddress().ConfigureAwait(false) : this._accountContract.Address.ToChecksumAddress(); } @@ -1083,7 +1055,7 @@ public Task PersonalSign(byte[] rawMessage) /// The signature. public async Task PersonalSign(string message) { - if (await Utils.IsZkSync(this.Client, this._chainId).ConfigureAwait(false)) + if (await Utils.IsZkSync(this.Client, this.ActiveChainId).ConfigureAwait(false)) { return await this._personalAccount.PersonalSign(message).ConfigureAwait(false); } @@ -1091,7 +1063,7 @@ public async Task PersonalSign(string message) var originalMsgHash = Encoding.UTF8.GetBytes(message).HashPrefixedMessage(); var sig = await EIP712 - .GenerateSignature_SmartAccount_AccountMessage("Account", "1", this._chainId, await this.GetAddress().ConfigureAwait(false), originalMsgHash, this._personalAccount) + .GenerateSignature_SmartAccount_AccountMessage("Account", "1", this.ActiveChainId, await this.GetAddress().ConfigureAwait(false), originalMsgHash, this._personalAccount) .ConfigureAwait(false); if (!await this.IsDeployed().ConfigureAwait(false)) @@ -1134,7 +1106,7 @@ public async Task SignTransaction(ThirdwebTransactionInput transaction) { await this.SwitchNetwork(transaction.ChainId.Value).ConfigureAwait(false); - if (await Utils.IsZkSync(this.Client, this._chainId).ConfigureAwait(false)) + if (await Utils.IsZkSync(this.Client, this.ActiveChainId).ConfigureAwait(false)) { throw new Exception("Offline Signing is not supported in ZkSync"); } @@ -1158,7 +1130,7 @@ public async Task SignTransaction(ThirdwebTransactionInput transaction) public async Task IsConnected() { - return await Utils.IsZkSync(this.Client, this._chainId).ConfigureAwait(false) ? await this._personalAccount.IsConnected().ConfigureAwait(false) : this._accountContract != null; + return await Utils.IsZkSync(this.Client, this.ActiveChainId).ConfigureAwait(false) ? await this._personalAccount.IsConnected().ConfigureAwait(false) : this._accountContract != null; } public Task Disconnect() @@ -1230,5 +1202,42 @@ public Task SignAuthorization(BigInteger chainId, string c return this._personalAccount.SignAuthorization(chainId, contractAddress, willSelfExecute); } + public async Task SwitchNetwork(BigInteger chainId) + { + if (this.ActiveChainId == chainId) + { + return; + } + + if (this.UseERC20Paymaster) + { + throw new InvalidOperationException("You cannot switch networks when using an ERC20 paymaster yet."); + } + + this._bundlerUrl = this._bundlerUrl.Contains(".thirdweb.com") ? $"https://{chainId}.bundler.thirdweb.com/v2" : this._bundlerUrl; + this._paymasterUrl = this._paymasterUrl.Contains(".thirdweb.com") ? $"https://{chainId}.bundler.thirdweb.com/v2" : this._paymasterUrl; + + if (!await Utils.IsZkSync(this.Client, chainId).ConfigureAwait(false)) + { + this._entryPointContract = await ThirdwebContract.Create(this.Client, this._entryPointContract.Address, chainId, this._entryPointContract.Abi).ConfigureAwait(false); + this._factoryContract = await ThirdwebContract.Create(this.Client, this._factoryContract.Address, chainId, this._factoryContract.Abi).ConfigureAwait(false); + + var personalAddress = await this._personalAccount.GetAddress().ConfigureAwait(false); + var accountAddress = await ThirdwebContract.Read(this._factoryContract, "getAddress", personalAddress, Array.Empty()).ConfigureAwait(false); + this._accountContract = await ThirdwebContract.Create(this._personalAccount.Client, accountAddress, chainId, Constants.ACCOUNT_V06_ABI).ConfigureAwait(false); + } + + this.ActiveChainId = chainId; + + try + { + await this._personalAccount.SwitchNetwork(chainId).ConfigureAwait(false); + } + catch + { + // Wallet likely still viable in Account Abstraction context + } + } + #endregion }