diff --git a/Thirdweb.Console/Program.cs b/Thirdweb.Console/Program.cs index ca8b262..31b4776 100644 --- a/Thirdweb.Console/Program.cs +++ b/Thirdweb.Console/Program.cs @@ -3,70 +3,78 @@ using Newtonsoft.Json; using Thirdweb; -DotEnv.Load(); +internal class Program +{ + private static async Task Main(string[] args) + { + DotEnv.Load(); -var secretKey = Environment.GetEnvironmentVariable("THIRDWEB_SECRET_KEY"); -var privateKey = Environment.GetEnvironmentVariable("PRIVATE_KEY"); + var secretKey = Environment.GetEnvironmentVariable("THIRDWEB_SECRET_KEY"); + var privateKey = Environment.GetEnvironmentVariable("PRIVATE_KEY"); -var clientOptions = new ThirdwebClientOptions(secretKey: secretKey, fetchTimeoutOptions: new TimeoutOptions(storage: 30000, rpc: 10000)); -var client = new ThirdwebClient(clientOptions); -Console.WriteLine($"Initialized ThirdwebClient: {JsonConvert.SerializeObject(clientOptions, Formatting.Indented)}"); + var clientOptions = new ThirdwebClientOptions(secretKey: secretKey, fetchTimeoutOptions: new TimeoutOptions(storage: 30000, rpc: 60000)); + var client = new ThirdwebClient(clientOptions); + Console.WriteLine($"Initialized ThirdwebClient: {JsonConvert.SerializeObject(clientOptions, Formatting.Indented)}"); -// var rpc = ThirdwebRPC.GetRpcInstance(client, 421614); -// var blockNumber = await rpc.SendRequestAsync("eth_blockNumber"); -// Console.WriteLine($"Block number: {blockNumber}"); + // var rpc = ThirdwebRPC.GetRpcInstance(client, 421614); + // var blockNumber = await rpc.SendRequestAsync("eth_blockNumber"); + // Console.WriteLine($"Block number: {blockNumber}"); -var contractOptions = new ThirdwebContractOptions( - client: client, - address: "0x81ebd23aA79bCcF5AaFb9c9c5B0Db4223c39102e", - chain: 421614, - abi: "[{\"type\": \"constructor\",\"name\": \"\",\"inputs\": [],\"outputs\": [],\"stateMutability\": \"nonpayable\"},{\"type\": \"event\",\"name\": \"Approval\",\"inputs\": [{\"type\": \"address\",\"name\": \"owner\",\"indexed\": true,\"internalType\": \"address\"},{\"type\": \"address\",\"name\": \"spender\",\"indexed\": true,\"internalType\": \"address\"},{\"type\": \"uint256\",\"name\": \"value\",\"indexed\": false,\"internalType\": \"uint256\"}],\"outputs\": [],\"anonymous\": false},{\"type\": \"event\",\"name\": \"DelegateChanged\",\"inputs\": [{\"type\": \"address\",\"name\": \"delegator\",\"indexed\": true,\"internalType\": \"address\"},{\"type\": \"address\",\"name\": \"fromDelegate\",\"indexed\": true,\"internalType\": \"address\"},{\"type\": \"address\",\"name\": \"toDelegate\",\"indexed\": true,\"internalType\": \"address\"}],\"outputs\": [],\"anonymous\": false},{\"type\": \"event\",\"name\": \"DelegateVotesChanged\",\"inputs\": [{\"type\": \"address\",\"name\": \"delegate\",\"indexed\": true,\"internalType\": \"address\"},{\"type\": \"uint256\",\"name\": \"previousBalance\",\"indexed\": false,\"internalType\": \"uint256\"},{\"type\": \"uint256\",\"name\": \"newBalance\",\"indexed\": false,\"internalType\": \"uint256\"}],\"outputs\": [],\"anonymous\": false},{\"type\": \"event\",\"name\": \"EIP712DomainChanged\",\"inputs\": [],\"outputs\": [],\"anonymous\": false},{\"type\": \"event\",\"name\": \"FlatPlatformFeeUpdated\",\"inputs\": [{\"type\": \"address\",\"name\": \"platformFeeRecipient\",\"indexed\": false,\"internalType\": \"address\"},{\"type\": \"uint256\",\"name\": \"flatFee\",\"indexed\": false,\"internalType\": \"uint256\"}],\"outputs\": [],\"anonymous\": false},{\"type\": \"event\",\"name\": \"Initialized\",\"inputs\": [{\"type\": \"uint8\",\"name\": \"version\",\"indexed\": false,\"internalType\": \"uint8\"}],\"outputs\": [],\"anonymous\": false},{\"type\": \"event\",\"name\": \"PlatformFeeInfoUpdated\",\"inputs\": [{\"type\": \"address\",\"name\": \"platformFeeRecipient\",\"indexed\": true,\"internalType\": \"address\"},{\"type\": \"uint256\",\"name\": \"platformFeeBps\",\"indexed\": false,\"internalType\": \"uint256\"}],\"outputs\": [],\"anonymous\": false},{\"type\": \"event\",\"name\": \"PlatformFeeTypeUpdated\",\"inputs\": [{\"type\": \"uint8\",\"name\": \"feeType\",\"indexed\": false,\"internalType\": \"enum IPlatformFee.PlatformFeeType\"}],\"outputs\": [],\"anonymous\": false},{\"type\": \"event\",\"name\": \"PrimarySaleRecipientUpdated\",\"inputs\": [{\"type\": \"address\",\"name\": \"recipient\",\"indexed\": true,\"internalType\": \"address\"}],\"outputs\": [],\"anonymous\": false},{\"type\": \"event\",\"name\": \"RoleAdminChanged\",\"inputs\": [{\"type\": \"bytes32\",\"name\": \"role\",\"indexed\": true,\"internalType\": \"bytes32\"},{\"type\": \"bytes32\",\"name\": \"previousAdminRole\",\"indexed\": true,\"internalType\": \"bytes32\"},{\"type\": \"bytes32\",\"name\": \"newAdminRole\",\"indexed\": true,\"internalType\": \"bytes32\"}],\"outputs\": [],\"anonymous\": false},{\"type\": \"event\",\"name\": \"RoleGranted\",\"inputs\": [{\"type\": \"bytes32\",\"name\": \"role\",\"indexed\": true,\"internalType\": \"bytes32\"},{\"type\": \"address\",\"name\": \"account\",\"indexed\": true,\"internalType\": \"address\"},{\"type\": \"address\",\"name\": \"sender\",\"indexed\": true,\"internalType\": \"address\"}],\"outputs\": [],\"anonymous\": false},{\"type\": \"event\",\"name\": \"RoleRevoked\",\"inputs\": [{\"type\": \"bytes32\",\"name\": \"role\",\"indexed\": true,\"internalType\": \"bytes32\"},{\"type\": \"address\",\"name\": \"account\",\"indexed\": true,\"internalType\": \"address\"},{\"type\": \"address\",\"name\": \"sender\",\"indexed\": true,\"internalType\": \"address\"}],\"outputs\": [],\"anonymous\": false},{\"type\": \"event\",\"name\": \"TokensMinted\",\"inputs\": [{\"type\": \"address\",\"name\": \"mintedTo\",\"indexed\": true,\"internalType\": \"address\"},{\"type\": \"uint256\",\"name\": \"quantityMinted\",\"indexed\": false,\"internalType\": \"uint256\"}],\"outputs\": [],\"anonymous\": false},{\"type\": \"event\",\"name\": \"TokensMintedWithSignature\",\"inputs\": [{\"type\": \"address\",\"name\": \"signer\",\"indexed\": true,\"internalType\": \"address\"},{\"type\": \"address\",\"name\": \"mintedTo\",\"indexed\": true,\"internalType\": \"address\"},{\"type\": \"tuple\",\"name\": \"mintRequest\",\"components\": [{\"type\": \"address\",\"name\": \"to\",\"internalType\": \"address\"},{\"type\": \"address\",\"name\": \"primarySaleRecipient\",\"internalType\": \"address\"},{\"type\": \"uint256\",\"name\": \"quantity\",\"internalType\": \"uint256\"},{\"type\": \"uint256\",\"name\": \"price\",\"internalType\": \"uint256\"},{\"type\": \"address\",\"name\": \"currency\",\"internalType\": \"address\"},{\"type\": \"uint128\",\"name\": \"validityStartTimestamp\",\"internalType\": \"uint128\"},{\"type\": \"uint128\",\"name\": \"validityEndTimestamp\",\"internalType\": \"uint128\"},{\"type\": \"bytes32\",\"name\": \"uid\",\"internalType\": \"bytes32\"}],\"indexed\": false,\"internalType\": \"struct ITokenERC20.MintRequest\"}],\"outputs\": [],\"anonymous\": false},{\"type\": \"event\",\"name\": \"Transfer\",\"inputs\": [{\"type\": \"address\",\"name\": \"from\",\"indexed\": true,\"internalType\": \"address\"},{\"type\": \"address\",\"name\": \"to\",\"indexed\": true,\"internalType\": \"address\"},{\"type\": \"uint256\",\"name\": \"value\",\"indexed\": false,\"internalType\": \"uint256\"}],\"outputs\": [],\"anonymous\": false},{\"type\": \"function\",\"name\": \"CLOCK_MODE\",\"inputs\": [],\"outputs\": [{\"type\": \"string\",\"name\": \"\",\"internalType\": \"string\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"DEFAULT_ADMIN_ROLE\",\"inputs\": [],\"outputs\": [{\"type\": \"bytes32\",\"name\": \"\",\"internalType\": \"bytes32\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"DOMAIN_SEPARATOR\",\"inputs\": [],\"outputs\": [{\"type\": \"bytes32\",\"name\": \"\",\"internalType\": \"bytes32\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"allowance\",\"inputs\": [{\"type\": \"address\",\"name\": \"owner\",\"internalType\": \"address\"},{\"type\": \"address\",\"name\": \"spender\",\"internalType\": \"address\"}],\"outputs\": [{\"type\": \"uint256\",\"name\": \"\",\"internalType\": \"uint256\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"approve\",\"inputs\": [{\"type\": \"address\",\"name\": \"spender\",\"internalType\": \"address\"},{\"type\": \"uint256\",\"name\": \"amount\",\"internalType\": \"uint256\"}],\"outputs\": [{\"type\": \"bool\",\"name\": \"\",\"internalType\": \"bool\"}],\"stateMutability\": \"nonpayable\"},{\"type\": \"function\",\"name\": \"balanceOf\",\"inputs\": [{\"type\": \"address\",\"name\": \"account\",\"internalType\": \"address\"}],\"outputs\": [{\"type\": \"uint256\",\"name\": \"\",\"internalType\": \"uint256\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"burn\",\"inputs\": [{\"type\": \"uint256\",\"name\": \"amount\",\"internalType\": \"uint256\"}],\"outputs\": [],\"stateMutability\": \"nonpayable\"},{\"type\": \"function\",\"name\": \"burnFrom\",\"inputs\": [{\"type\": \"address\",\"name\": \"account\",\"internalType\": \"address\"},{\"type\": \"uint256\",\"name\": \"amount\",\"internalType\": \"uint256\"}],\"outputs\": [],\"stateMutability\": \"nonpayable\"},{\"type\": \"function\",\"name\": \"checkpoints\",\"inputs\": [{\"type\": \"address\",\"name\": \"account\",\"internalType\": \"address\"},{\"type\": \"uint32\",\"name\": \"pos\",\"internalType\": \"uint32\"}],\"outputs\": [{\"type\": \"tuple\",\"name\": \"\",\"components\": [{\"type\": \"uint32\",\"name\": \"fromBlock\",\"internalType\": \"uint32\"},{\"type\": \"uint224\",\"name\": \"votes\",\"internalType\": \"uint224\"}],\"internalType\": \"struct ERC20VotesUpgradeable.Checkpoint\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"clock\",\"inputs\": [],\"outputs\": [{\"type\": \"uint48\",\"name\": \"\",\"internalType\": \"uint48\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"contractType\",\"inputs\": [],\"outputs\": [{\"type\": \"bytes32\",\"name\": \"\",\"internalType\": \"bytes32\"}],\"stateMutability\": \"pure\"},{\"type\": \"function\",\"name\": \"contractURI\",\"inputs\": [],\"outputs\": [{\"type\": \"string\",\"name\": \"\",\"internalType\": \"string\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"contractVersion\",\"inputs\": [],\"outputs\": [{\"type\": \"uint8\",\"name\": \"\",\"internalType\": \"uint8\"}],\"stateMutability\": \"pure\"},{\"type\": \"function\",\"name\": \"decimals\",\"inputs\": [],\"outputs\": [{\"type\": \"uint8\",\"name\": \"\",\"internalType\": \"uint8\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"decreaseAllowance\",\"inputs\": [{\"type\": \"address\",\"name\": \"spender\",\"internalType\": \"address\"},{\"type\": \"uint256\",\"name\": \"subtractedValue\",\"internalType\": \"uint256\"}],\"outputs\": [{\"type\": \"bool\",\"name\": \"\",\"internalType\": \"bool\"}],\"stateMutability\": \"nonpayable\"},{\"type\": \"function\",\"name\": \"delegate\",\"inputs\": [{\"type\": \"address\",\"name\": \"delegatee\",\"internalType\": \"address\"}],\"outputs\": [],\"stateMutability\": \"nonpayable\"},{\"type\": \"function\",\"name\": \"delegateBySig\",\"inputs\": [{\"type\": \"address\",\"name\": \"delegatee\",\"internalType\": \"address\"},{\"type\": \"uint256\",\"name\": \"nonce\",\"internalType\": \"uint256\"},{\"type\": \"uint256\",\"name\": \"expiry\",\"internalType\": \"uint256\"},{\"type\": \"uint8\",\"name\": \"v\",\"internalType\": \"uint8\"},{\"type\": \"bytes32\",\"name\": \"r\",\"internalType\": \"bytes32\"},{\"type\": \"bytes32\",\"name\": \"s\",\"internalType\": \"bytes32\"}],\"outputs\": [],\"stateMutability\": \"nonpayable\"},{\"type\": \"function\",\"name\": \"delegates\",\"inputs\": [{\"type\": \"address\",\"name\": \"account\",\"internalType\": \"address\"}],\"outputs\": [{\"type\": \"address\",\"name\": \"\",\"internalType\": \"address\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"eip712Domain\",\"inputs\": [],\"outputs\": [{\"type\": \"bytes1\",\"name\": \"fields\",\"internalType\": \"bytes1\"},{\"type\": \"string\",\"name\": \"name\",\"internalType\": \"string\"},{\"type\": \"string\",\"name\": \"version\",\"internalType\": \"string\"},{\"type\": \"uint256\",\"name\": \"chainId\",\"internalType\": \"uint256\"},{\"type\": \"address\",\"name\": \"verifyingContract\",\"internalType\": \"address\"},{\"type\": \"bytes32\",\"name\": \"salt\",\"internalType\": \"bytes32\"},{\"type\": \"uint256[]\",\"name\": \"extensions\",\"internalType\": \"uint256[]\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"getPastTotalSupply\",\"inputs\": [{\"type\": \"uint256\",\"name\": \"timepoint\",\"internalType\": \"uint256\"}],\"outputs\": [{\"type\": \"uint256\",\"name\": \"\",\"internalType\": \"uint256\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"getPastVotes\",\"inputs\": [{\"type\": \"address\",\"name\": \"account\",\"internalType\": \"address\"},{\"type\": \"uint256\",\"name\": \"timepoint\",\"internalType\": \"uint256\"}],\"outputs\": [{\"type\": \"uint256\",\"name\": \"\",\"internalType\": \"uint256\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"getPlatformFeeInfo\",\"inputs\": [],\"outputs\": [{\"type\": \"address\",\"name\": \"\",\"internalType\": \"address\"},{\"type\": \"uint16\",\"name\": \"\",\"internalType\": \"uint16\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"getRoleAdmin\",\"inputs\": [{\"type\": \"bytes32\",\"name\": \"role\",\"internalType\": \"bytes32\"}],\"outputs\": [{\"type\": \"bytes32\",\"name\": \"\",\"internalType\": \"bytes32\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"getRoleMember\",\"inputs\": [{\"type\": \"bytes32\",\"name\": \"role\",\"internalType\": \"bytes32\"},{\"type\": \"uint256\",\"name\": \"index\",\"internalType\": \"uint256\"}],\"outputs\": [{\"type\": \"address\",\"name\": \"\",\"internalType\": \"address\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"getRoleMemberCount\",\"inputs\": [{\"type\": \"bytes32\",\"name\": \"role\",\"internalType\": \"bytes32\"}],\"outputs\": [{\"type\": \"uint256\",\"name\": \"\",\"internalType\": \"uint256\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"getVotes\",\"inputs\": [{\"type\": \"address\",\"name\": \"account\",\"internalType\": \"address\"}],\"outputs\": [{\"type\": \"uint256\",\"name\": \"\",\"internalType\": \"uint256\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"grantRole\",\"inputs\": [{\"type\": \"bytes32\",\"name\": \"role\",\"internalType\": \"bytes32\"},{\"type\": \"address\",\"name\": \"account\",\"internalType\": \"address\"}],\"outputs\": [],\"stateMutability\": \"nonpayable\"},{\"type\": \"function\",\"name\": \"hasRole\",\"inputs\": [{\"type\": \"bytes32\",\"name\": \"role\",\"internalType\": \"bytes32\"},{\"type\": \"address\",\"name\": \"account\",\"internalType\": \"address\"}],\"outputs\": [{\"type\": \"bool\",\"name\": \"\",\"internalType\": \"bool\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"increaseAllowance\",\"inputs\": [{\"type\": \"address\",\"name\": \"spender\",\"internalType\": \"address\"},{\"type\": \"uint256\",\"name\": \"addedValue\",\"internalType\": \"uint256\"}],\"outputs\": [{\"type\": \"bool\",\"name\": \"\",\"internalType\": \"bool\"}],\"stateMutability\": \"nonpayable\"},{\"type\": \"function\",\"name\": \"initialize\",\"inputs\": [{\"type\": \"address\",\"name\": \"_defaultAdmin\",\"internalType\": \"address\"},{\"type\": \"string\",\"name\": \"_name\",\"internalType\": \"string\"},{\"type\": \"string\",\"name\": \"_symbol\",\"internalType\": \"string\"},{\"type\": \"string\",\"name\": \"_contractURI\",\"internalType\": \"string\"},{\"type\": \"address[]\",\"name\": \"_trustedForwarders\",\"internalType\": \"address[]\"},{\"type\": \"address\",\"name\": \"_primarySaleRecipient\",\"internalType\": \"address\"},{\"type\": \"address\",\"name\": \"_platformFeeRecipient\",\"internalType\": \"address\"},{\"type\": \"uint256\",\"name\": \"_platformFeeBps\",\"internalType\": \"uint256\"}],\"outputs\": [],\"stateMutability\": \"nonpayable\"},{\"type\": \"function\",\"name\": \"isTrustedForwarder\",\"inputs\": [{\"type\": \"address\",\"name\": \"forwarder\",\"internalType\": \"address\"}],\"outputs\": [{\"type\": \"bool\",\"name\": \"\",\"internalType\": \"bool\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"mintTo\",\"inputs\": [{\"type\": \"address\",\"name\": \"to\",\"internalType\": \"address\"},{\"type\": \"uint256\",\"name\": \"amount\",\"internalType\": \"uint256\"}],\"outputs\": [],\"stateMutability\": \"nonpayable\"},{\"type\": \"function\",\"name\": \"mintWithSignature\",\"inputs\": [{\"type\": \"tuple\",\"name\": \"_req\",\"components\": [{\"type\": \"address\",\"name\": \"to\",\"internalType\": \"address\"},{\"type\": \"address\",\"name\": \"primarySaleRecipient\",\"internalType\": \"address\"},{\"type\": \"uint256\",\"name\": \"quantity\",\"internalType\": \"uint256\"},{\"type\": \"uint256\",\"name\": \"price\",\"internalType\": \"uint256\"},{\"type\": \"address\",\"name\": \"currency\",\"internalType\": \"address\"},{\"type\": \"uint128\",\"name\": \"validityStartTimestamp\",\"internalType\": \"uint128\"},{\"type\": \"uint128\",\"name\": \"validityEndTimestamp\",\"internalType\": \"uint128\"},{\"type\": \"bytes32\",\"name\": \"uid\",\"internalType\": \"bytes32\"}],\"internalType\": \"struct ITokenERC20.MintRequest\"},{\"type\": \"bytes\",\"name\": \"_signature\",\"internalType\": \"bytes\"}],\"outputs\": [],\"stateMutability\": \"payable\"},{\"type\": \"function\",\"name\": \"multicall\",\"inputs\": [{\"type\": \"bytes[]\",\"name\": \"data\",\"internalType\": \"bytes[]\"}],\"outputs\": [{\"type\": \"bytes[]\",\"name\": \"results\",\"internalType\": \"bytes[]\"}],\"stateMutability\": \"nonpayable\"},{\"type\": \"function\",\"name\": \"name\",\"inputs\": [],\"outputs\": [{\"type\": \"string\",\"name\": \"\",\"internalType\": \"string\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"nonces\",\"inputs\": [{\"type\": \"address\",\"name\": \"owner\",\"internalType\": \"address\"}],\"outputs\": [{\"type\": \"uint256\",\"name\": \"\",\"internalType\": \"uint256\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"numCheckpoints\",\"inputs\": [{\"type\": \"address\",\"name\": \"account\",\"internalType\": \"address\"}],\"outputs\": [{\"type\": \"uint32\",\"name\": \"\",\"internalType\": \"uint32\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"permit\",\"inputs\": [{\"type\": \"address\",\"name\": \"owner\",\"internalType\": \"address\"},{\"type\": \"address\",\"name\": \"spender\",\"internalType\": \"address\"},{\"type\": \"uint256\",\"name\": \"value\",\"internalType\": \"uint256\"},{\"type\": \"uint256\",\"name\": \"deadline\",\"internalType\": \"uint256\"},{\"type\": \"uint8\",\"name\": \"v\",\"internalType\": \"uint8\"},{\"type\": \"bytes32\",\"name\": \"r\",\"internalType\": \"bytes32\"},{\"type\": \"bytes32\",\"name\": \"s\",\"internalType\": \"bytes32\"}],\"outputs\": [],\"stateMutability\": \"nonpayable\"},{\"type\": \"function\",\"name\": \"primarySaleRecipient\",\"inputs\": [],\"outputs\": [{\"type\": \"address\",\"name\": \"\",\"internalType\": \"address\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"renounceRole\",\"inputs\": [{\"type\": \"bytes32\",\"name\": \"role\",\"internalType\": \"bytes32\"},{\"type\": \"address\",\"name\": \"account\",\"internalType\": \"address\"}],\"outputs\": [],\"stateMutability\": \"nonpayable\"},{\"type\": \"function\",\"name\": \"revokeRole\",\"inputs\": [{\"type\": \"bytes32\",\"name\": \"role\",\"internalType\": \"bytes32\"},{\"type\": \"address\",\"name\": \"account\",\"internalType\": \"address\"}],\"outputs\": [],\"stateMutability\": \"nonpayable\"},{\"type\": \"function\",\"name\": \"setContractURI\",\"inputs\": [{\"type\": \"string\",\"name\": \"_uri\",\"internalType\": \"string\"}],\"outputs\": [],\"stateMutability\": \"nonpayable\"},{\"type\": \"function\",\"name\": \"setPlatformFeeInfo\",\"inputs\": [{\"type\": \"address\",\"name\": \"_platformFeeRecipient\",\"internalType\": \"address\"},{\"type\": \"uint256\",\"name\": \"_platformFeeBps\",\"internalType\": \"uint256\"}],\"outputs\": [],\"stateMutability\": \"nonpayable\"},{\"type\": \"function\",\"name\": \"setPrimarySaleRecipient\",\"inputs\": [{\"type\": \"address\",\"name\": \"_saleRecipient\",\"internalType\": \"address\"}],\"outputs\": [],\"stateMutability\": \"nonpayable\"},{\"type\": \"function\",\"name\": \"supportsInterface\",\"inputs\": [{\"type\": \"bytes4\",\"name\": \"interfaceId\",\"internalType\": \"bytes4\"}],\"outputs\": [{\"type\": \"bool\",\"name\": \"\",\"internalType\": \"bool\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"symbol\",\"inputs\": [],\"outputs\": [{\"type\": \"string\",\"name\": \"\",\"internalType\": \"string\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"totalSupply\",\"inputs\": [],\"outputs\": [{\"type\": \"uint256\",\"name\": \"\",\"internalType\": \"uint256\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"transfer\",\"inputs\": [{\"type\": \"address\",\"name\": \"to\",\"internalType\": \"address\"},{\"type\": \"uint256\",\"name\": \"amount\",\"internalType\": \"uint256\"}],\"outputs\": [{\"type\": \"bool\",\"name\": \"\",\"internalType\": \"bool\"}],\"stateMutability\": \"nonpayable\"},{\"type\": \"function\",\"name\": \"transferFrom\",\"inputs\": [{\"type\": \"address\",\"name\": \"from\",\"internalType\": \"address\"},{\"type\": \"address\",\"name\": \"to\",\"internalType\": \"address\"},{\"type\": \"uint256\",\"name\": \"amount\",\"internalType\": \"uint256\"}],\"outputs\": [{\"type\": \"bool\",\"name\": \"\",\"internalType\": \"bool\"}],\"stateMutability\": \"nonpayable\"},{\"type\": \"function\",\"name\": \"verify\",\"inputs\": [{\"type\": \"tuple\",\"name\": \"_req\",\"components\": [{\"type\": \"address\",\"name\": \"to\",\"internalType\": \"address\"},{\"type\": \"address\",\"name\": \"primarySaleRecipient\",\"internalType\": \"address\"},{\"type\": \"uint256\",\"name\": \"quantity\",\"internalType\": \"uint256\"},{\"type\": \"uint256\",\"name\": \"price\",\"internalType\": \"uint256\"},{\"type\": \"address\",\"name\": \"currency\",\"internalType\": \"address\"},{\"type\": \"uint128\",\"name\": \"validityStartTimestamp\",\"internalType\": \"uint128\"},{\"type\": \"uint128\",\"name\": \"validityEndTimestamp\",\"internalType\": \"uint128\"},{\"type\": \"bytes32\",\"name\": \"uid\",\"internalType\": \"bytes32\"}],\"internalType\": \"struct ITokenERC20.MintRequest\"},{\"type\": \"bytes\",\"name\": \"_signature\",\"internalType\": \"bytes\"}],\"outputs\": [{\"type\": \"bool\",\"name\": \"\",\"internalType\": \"bool\"},{\"type\": \"address\",\"name\": \"\",\"internalType\": \"address\"}],\"stateMutability\": \"view\"}]" -); -var contract = new ThirdwebContract(contractOptions); -var readResult = await ThirdwebContract.ReadContract(contract, "name"); -Console.WriteLine($"Contract read result: {readResult}"); + var contract = new ThirdwebContract( + client: client, + address: "0x81ebd23aA79bCcF5AaFb9c9c5B0Db4223c39102e", + chain: 421614, + abi: "[{\"type\": \"constructor\",\"name\": \"\",\"inputs\": [],\"outputs\": [],\"stateMutability\": \"nonpayable\"},{\"type\": \"event\",\"name\": \"Approval\",\"inputs\": [{\"type\": \"address\",\"name\": \"owner\",\"indexed\": true,\"internalType\": \"address\"},{\"type\": \"address\",\"name\": \"spender\",\"indexed\": true,\"internalType\": \"address\"},{\"type\": \"uint256\",\"name\": \"value\",\"indexed\": false,\"internalType\": \"uint256\"}],\"outputs\": [],\"anonymous\": false},{\"type\": \"event\",\"name\": \"DelegateChanged\",\"inputs\": [{\"type\": \"address\",\"name\": \"delegator\",\"indexed\": true,\"internalType\": \"address\"},{\"type\": \"address\",\"name\": \"fromDelegate\",\"indexed\": true,\"internalType\": \"address\"},{\"type\": \"address\",\"name\": \"toDelegate\",\"indexed\": true,\"internalType\": \"address\"}],\"outputs\": [],\"anonymous\": false},{\"type\": \"event\",\"name\": \"DelegateVotesChanged\",\"inputs\": [{\"type\": \"address\",\"name\": \"delegate\",\"indexed\": true,\"internalType\": \"address\"},{\"type\": \"uint256\",\"name\": \"previousBalance\",\"indexed\": false,\"internalType\": \"uint256\"},{\"type\": \"uint256\",\"name\": \"newBalance\",\"indexed\": false,\"internalType\": \"uint256\"}],\"outputs\": [],\"anonymous\": false},{\"type\": \"event\",\"name\": \"EIP712DomainChanged\",\"inputs\": [],\"outputs\": [],\"anonymous\": false},{\"type\": \"event\",\"name\": \"FlatPlatformFeeUpdated\",\"inputs\": [{\"type\": \"address\",\"name\": \"platformFeeRecipient\",\"indexed\": false,\"internalType\": \"address\"},{\"type\": \"uint256\",\"name\": \"flatFee\",\"indexed\": false,\"internalType\": \"uint256\"}],\"outputs\": [],\"anonymous\": false},{\"type\": \"event\",\"name\": \"Initialized\",\"inputs\": [{\"type\": \"uint8\",\"name\": \"version\",\"indexed\": false,\"internalType\": \"uint8\"}],\"outputs\": [],\"anonymous\": false},{\"type\": \"event\",\"name\": \"PlatformFeeInfoUpdated\",\"inputs\": [{\"type\": \"address\",\"name\": \"platformFeeRecipient\",\"indexed\": true,\"internalType\": \"address\"},{\"type\": \"uint256\",\"name\": \"platformFeeBps\",\"indexed\": false,\"internalType\": \"uint256\"}],\"outputs\": [],\"anonymous\": false},{\"type\": \"event\",\"name\": \"PlatformFeeTypeUpdated\",\"inputs\": [{\"type\": \"uint8\",\"name\": \"feeType\",\"indexed\": false,\"internalType\": \"enum IPlatformFee.PlatformFeeType\"}],\"outputs\": [],\"anonymous\": false},{\"type\": \"event\",\"name\": \"PrimarySaleRecipientUpdated\",\"inputs\": [{\"type\": \"address\",\"name\": \"recipient\",\"indexed\": true,\"internalType\": \"address\"}],\"outputs\": [],\"anonymous\": false},{\"type\": \"event\",\"name\": \"RoleAdminChanged\",\"inputs\": [{\"type\": \"bytes32\",\"name\": \"role\",\"indexed\": true,\"internalType\": \"bytes32\"},{\"type\": \"bytes32\",\"name\": \"previousAdminRole\",\"indexed\": true,\"internalType\": \"bytes32\"},{\"type\": \"bytes32\",\"name\": \"newAdminRole\",\"indexed\": true,\"internalType\": \"bytes32\"}],\"outputs\": [],\"anonymous\": false},{\"type\": \"event\",\"name\": \"RoleGranted\",\"inputs\": [{\"type\": \"bytes32\",\"name\": \"role\",\"indexed\": true,\"internalType\": \"bytes32\"},{\"type\": \"address\",\"name\": \"account\",\"indexed\": true,\"internalType\": \"address\"},{\"type\": \"address\",\"name\": \"sender\",\"indexed\": true,\"internalType\": \"address\"}],\"outputs\": [],\"anonymous\": false},{\"type\": \"event\",\"name\": \"RoleRevoked\",\"inputs\": [{\"type\": \"bytes32\",\"name\": \"role\",\"indexed\": true,\"internalType\": \"bytes32\"},{\"type\": \"address\",\"name\": \"account\",\"indexed\": true,\"internalType\": \"address\"},{\"type\": \"address\",\"name\": \"sender\",\"indexed\": true,\"internalType\": \"address\"}],\"outputs\": [],\"anonymous\": false},{\"type\": \"event\",\"name\": \"TokensMinted\",\"inputs\": [{\"type\": \"address\",\"name\": \"mintedTo\",\"indexed\": true,\"internalType\": \"address\"},{\"type\": \"uint256\",\"name\": \"quantityMinted\",\"indexed\": false,\"internalType\": \"uint256\"}],\"outputs\": [],\"anonymous\": false},{\"type\": \"event\",\"name\": \"TokensMintedWithSignature\",\"inputs\": [{\"type\": \"address\",\"name\": \"signer\",\"indexed\": true,\"internalType\": \"address\"},{\"type\": \"address\",\"name\": \"mintedTo\",\"indexed\": true,\"internalType\": \"address\"},{\"type\": \"tuple\",\"name\": \"mintRequest\",\"components\": [{\"type\": \"address\",\"name\": \"to\",\"internalType\": \"address\"},{\"type\": \"address\",\"name\": \"primarySaleRecipient\",\"internalType\": \"address\"},{\"type\": \"uint256\",\"name\": \"quantity\",\"internalType\": \"uint256\"},{\"type\": \"uint256\",\"name\": \"price\",\"internalType\": \"uint256\"},{\"type\": \"address\",\"name\": \"currency\",\"internalType\": \"address\"},{\"type\": \"uint128\",\"name\": \"validityStartTimestamp\",\"internalType\": \"uint128\"},{\"type\": \"uint128\",\"name\": \"validityEndTimestamp\",\"internalType\": \"uint128\"},{\"type\": \"bytes32\",\"name\": \"uid\",\"internalType\": \"bytes32\"}],\"indexed\": false,\"internalType\": \"struct ITokenERC20.MintRequest\"}],\"outputs\": [],\"anonymous\": false},{\"type\": \"event\",\"name\": \"Transfer\",\"inputs\": [{\"type\": \"address\",\"name\": \"from\",\"indexed\": true,\"internalType\": \"address\"},{\"type\": \"address\",\"name\": \"to\",\"indexed\": true,\"internalType\": \"address\"},{\"type\": \"uint256\",\"name\": \"value\",\"indexed\": false,\"internalType\": \"uint256\"}],\"outputs\": [],\"anonymous\": false},{\"type\": \"function\",\"name\": \"CLOCK_MODE\",\"inputs\": [],\"outputs\": [{\"type\": \"string\",\"name\": \"\",\"internalType\": \"string\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"DEFAULT_ADMIN_ROLE\",\"inputs\": [],\"outputs\": [{\"type\": \"bytes32\",\"name\": \"\",\"internalType\": \"bytes32\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"DOMAIN_SEPARATOR\",\"inputs\": [],\"outputs\": [{\"type\": \"bytes32\",\"name\": \"\",\"internalType\": \"bytes32\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"allowance\",\"inputs\": [{\"type\": \"address\",\"name\": \"owner\",\"internalType\": \"address\"},{\"type\": \"address\",\"name\": \"spender\",\"internalType\": \"address\"}],\"outputs\": [{\"type\": \"uint256\",\"name\": \"\",\"internalType\": \"uint256\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"approve\",\"inputs\": [{\"type\": \"address\",\"name\": \"spender\",\"internalType\": \"address\"},{\"type\": \"uint256\",\"name\": \"amount\",\"internalType\": \"uint256\"}],\"outputs\": [{\"type\": \"bool\",\"name\": \"\",\"internalType\": \"bool\"}],\"stateMutability\": \"nonpayable\"},{\"type\": \"function\",\"name\": \"balanceOf\",\"inputs\": [{\"type\": \"address\",\"name\": \"account\",\"internalType\": \"address\"}],\"outputs\": [{\"type\": \"uint256\",\"name\": \"\",\"internalType\": \"uint256\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"burn\",\"inputs\": [{\"type\": \"uint256\",\"name\": \"amount\",\"internalType\": \"uint256\"}],\"outputs\": [],\"stateMutability\": \"nonpayable\"},{\"type\": \"function\",\"name\": \"burnFrom\",\"inputs\": [{\"type\": \"address\",\"name\": \"account\",\"internalType\": \"address\"},{\"type\": \"uint256\",\"name\": \"amount\",\"internalType\": \"uint256\"}],\"outputs\": [],\"stateMutability\": \"nonpayable\"},{\"type\": \"function\",\"name\": \"checkpoints\",\"inputs\": [{\"type\": \"address\",\"name\": \"account\",\"internalType\": \"address\"},{\"type\": \"uint32\",\"name\": \"pos\",\"internalType\": \"uint32\"}],\"outputs\": [{\"type\": \"tuple\",\"name\": \"\",\"components\": [{\"type\": \"uint32\",\"name\": \"fromBlock\",\"internalType\": \"uint32\"},{\"type\": \"uint224\",\"name\": \"votes\",\"internalType\": \"uint224\"}],\"internalType\": \"struct ERC20VotesUpgradeable.Checkpoint\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"clock\",\"inputs\": [],\"outputs\": [{\"type\": \"uint48\",\"name\": \"\",\"internalType\": \"uint48\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"contractType\",\"inputs\": [],\"outputs\": [{\"type\": \"bytes32\",\"name\": \"\",\"internalType\": \"bytes32\"}],\"stateMutability\": \"pure\"},{\"type\": \"function\",\"name\": \"contractURI\",\"inputs\": [],\"outputs\": [{\"type\": \"string\",\"name\": \"\",\"internalType\": \"string\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"contractVersion\",\"inputs\": [],\"outputs\": [{\"type\": \"uint8\",\"name\": \"\",\"internalType\": \"uint8\"}],\"stateMutability\": \"pure\"},{\"type\": \"function\",\"name\": \"decimals\",\"inputs\": [],\"outputs\": [{\"type\": \"uint8\",\"name\": \"\",\"internalType\": \"uint8\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"decreaseAllowance\",\"inputs\": [{\"type\": \"address\",\"name\": \"spender\",\"internalType\": \"address\"},{\"type\": \"uint256\",\"name\": \"subtractedValue\",\"internalType\": \"uint256\"}],\"outputs\": [{\"type\": \"bool\",\"name\": \"\",\"internalType\": \"bool\"}],\"stateMutability\": \"nonpayable\"},{\"type\": \"function\",\"name\": \"delegate\",\"inputs\": [{\"type\": \"address\",\"name\": \"delegatee\",\"internalType\": \"address\"}],\"outputs\": [],\"stateMutability\": \"nonpayable\"},{\"type\": \"function\",\"name\": \"delegateBySig\",\"inputs\": [{\"type\": \"address\",\"name\": \"delegatee\",\"internalType\": \"address\"},{\"type\": \"uint256\",\"name\": \"nonce\",\"internalType\": \"uint256\"},{\"type\": \"uint256\",\"name\": \"expiry\",\"internalType\": \"uint256\"},{\"type\": \"uint8\",\"name\": \"v\",\"internalType\": \"uint8\"},{\"type\": \"bytes32\",\"name\": \"r\",\"internalType\": \"bytes32\"},{\"type\": \"bytes32\",\"name\": \"s\",\"internalType\": \"bytes32\"}],\"outputs\": [],\"stateMutability\": \"nonpayable\"},{\"type\": \"function\",\"name\": \"delegates\",\"inputs\": [{\"type\": \"address\",\"name\": \"account\",\"internalType\": \"address\"}],\"outputs\": [{\"type\": \"address\",\"name\": \"\",\"internalType\": \"address\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"eip712Domain\",\"inputs\": [],\"outputs\": [{\"type\": \"bytes1\",\"name\": \"fields\",\"internalType\": \"bytes1\"},{\"type\": \"string\",\"name\": \"name\",\"internalType\": \"string\"},{\"type\": \"string\",\"name\": \"version\",\"internalType\": \"string\"},{\"type\": \"uint256\",\"name\": \"chainId\",\"internalType\": \"uint256\"},{\"type\": \"address\",\"name\": \"verifyingContract\",\"internalType\": \"address\"},{\"type\": \"bytes32\",\"name\": \"salt\",\"internalType\": \"bytes32\"},{\"type\": \"uint256[]\",\"name\": \"extensions\",\"internalType\": \"uint256[]\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"getPastTotalSupply\",\"inputs\": [{\"type\": \"uint256\",\"name\": \"timepoint\",\"internalType\": \"uint256\"}],\"outputs\": [{\"type\": \"uint256\",\"name\": \"\",\"internalType\": \"uint256\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"getPastVotes\",\"inputs\": [{\"type\": \"address\",\"name\": \"account\",\"internalType\": \"address\"},{\"type\": \"uint256\",\"name\": \"timepoint\",\"internalType\": \"uint256\"}],\"outputs\": [{\"type\": \"uint256\",\"name\": \"\",\"internalType\": \"uint256\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"getPlatformFeeInfo\",\"inputs\": [],\"outputs\": [{\"type\": \"address\",\"name\": \"\",\"internalType\": \"address\"},{\"type\": \"uint16\",\"name\": \"\",\"internalType\": \"uint16\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"getRoleAdmin\",\"inputs\": [{\"type\": \"bytes32\",\"name\": \"role\",\"internalType\": \"bytes32\"}],\"outputs\": [{\"type\": \"bytes32\",\"name\": \"\",\"internalType\": \"bytes32\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"getRoleMember\",\"inputs\": [{\"type\": \"bytes32\",\"name\": \"role\",\"internalType\": \"bytes32\"},{\"type\": \"uint256\",\"name\": \"index\",\"internalType\": \"uint256\"}],\"outputs\": [{\"type\": \"address\",\"name\": \"\",\"internalType\": \"address\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"getRoleMemberCount\",\"inputs\": [{\"type\": \"bytes32\",\"name\": \"role\",\"internalType\": \"bytes32\"}],\"outputs\": [{\"type\": \"uint256\",\"name\": \"\",\"internalType\": \"uint256\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"getVotes\",\"inputs\": [{\"type\": \"address\",\"name\": \"account\",\"internalType\": \"address\"}],\"outputs\": [{\"type\": \"uint256\",\"name\": \"\",\"internalType\": \"uint256\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"grantRole\",\"inputs\": [{\"type\": \"bytes32\",\"name\": \"role\",\"internalType\": \"bytes32\"},{\"type\": \"address\",\"name\": \"account\",\"internalType\": \"address\"}],\"outputs\": [],\"stateMutability\": \"nonpayable\"},{\"type\": \"function\",\"name\": \"hasRole\",\"inputs\": [{\"type\": \"bytes32\",\"name\": \"role\",\"internalType\": \"bytes32\"},{\"type\": \"address\",\"name\": \"account\",\"internalType\": \"address\"}],\"outputs\": [{\"type\": \"bool\",\"name\": \"\",\"internalType\": \"bool\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"increaseAllowance\",\"inputs\": [{\"type\": \"address\",\"name\": \"spender\",\"internalType\": \"address\"},{\"type\": \"uint256\",\"name\": \"addedValue\",\"internalType\": \"uint256\"}],\"outputs\": [{\"type\": \"bool\",\"name\": \"\",\"internalType\": \"bool\"}],\"stateMutability\": \"nonpayable\"},{\"type\": \"function\",\"name\": \"initialize\",\"inputs\": [{\"type\": \"address\",\"name\": \"_defaultAdmin\",\"internalType\": \"address\"},{\"type\": \"string\",\"name\": \"_name\",\"internalType\": \"string\"},{\"type\": \"string\",\"name\": \"_symbol\",\"internalType\": \"string\"},{\"type\": \"string\",\"name\": \"_contractURI\",\"internalType\": \"string\"},{\"type\": \"address[]\",\"name\": \"_trustedForwarders\",\"internalType\": \"address[]\"},{\"type\": \"address\",\"name\": \"_primarySaleRecipient\",\"internalType\": \"address\"},{\"type\": \"address\",\"name\": \"_platformFeeRecipient\",\"internalType\": \"address\"},{\"type\": \"uint256\",\"name\": \"_platformFeeBps\",\"internalType\": \"uint256\"}],\"outputs\": [],\"stateMutability\": \"nonpayable\"},{\"type\": \"function\",\"name\": \"isTrustedForwarder\",\"inputs\": [{\"type\": \"address\",\"name\": \"forwarder\",\"internalType\": \"address\"}],\"outputs\": [{\"type\": \"bool\",\"name\": \"\",\"internalType\": \"bool\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"mintTo\",\"inputs\": [{\"type\": \"address\",\"name\": \"to\",\"internalType\": \"address\"},{\"type\": \"uint256\",\"name\": \"amount\",\"internalType\": \"uint256\"}],\"outputs\": [],\"stateMutability\": \"nonpayable\"},{\"type\": \"function\",\"name\": \"mintWithSignature\",\"inputs\": [{\"type\": \"tuple\",\"name\": \"_req\",\"components\": [{\"type\": \"address\",\"name\": \"to\",\"internalType\": \"address\"},{\"type\": \"address\",\"name\": \"primarySaleRecipient\",\"internalType\": \"address\"},{\"type\": \"uint256\",\"name\": \"quantity\",\"internalType\": \"uint256\"},{\"type\": \"uint256\",\"name\": \"price\",\"internalType\": \"uint256\"},{\"type\": \"address\",\"name\": \"currency\",\"internalType\": \"address\"},{\"type\": \"uint128\",\"name\": \"validityStartTimestamp\",\"internalType\": \"uint128\"},{\"type\": \"uint128\",\"name\": \"validityEndTimestamp\",\"internalType\": \"uint128\"},{\"type\": \"bytes32\",\"name\": \"uid\",\"internalType\": \"bytes32\"}],\"internalType\": \"struct ITokenERC20.MintRequest\"},{\"type\": \"bytes\",\"name\": \"_signature\",\"internalType\": \"bytes\"}],\"outputs\": [],\"stateMutability\": \"payable\"},{\"type\": \"function\",\"name\": \"multicall\",\"inputs\": [{\"type\": \"bytes[]\",\"name\": \"data\",\"internalType\": \"bytes[]\"}],\"outputs\": [{\"type\": \"bytes[]\",\"name\": \"results\",\"internalType\": \"bytes[]\"}],\"stateMutability\": \"nonpayable\"},{\"type\": \"function\",\"name\": \"name\",\"inputs\": [],\"outputs\": [{\"type\": \"string\",\"name\": \"\",\"internalType\": \"string\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"nonces\",\"inputs\": [{\"type\": \"address\",\"name\": \"owner\",\"internalType\": \"address\"}],\"outputs\": [{\"type\": \"uint256\",\"name\": \"\",\"internalType\": \"uint256\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"numCheckpoints\",\"inputs\": [{\"type\": \"address\",\"name\": \"account\",\"internalType\": \"address\"}],\"outputs\": [{\"type\": \"uint32\",\"name\": \"\",\"internalType\": \"uint32\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"permit\",\"inputs\": [{\"type\": \"address\",\"name\": \"owner\",\"internalType\": \"address\"},{\"type\": \"address\",\"name\": \"spender\",\"internalType\": \"address\"},{\"type\": \"uint256\",\"name\": \"value\",\"internalType\": \"uint256\"},{\"type\": \"uint256\",\"name\": \"deadline\",\"internalType\": \"uint256\"},{\"type\": \"uint8\",\"name\": \"v\",\"internalType\": \"uint8\"},{\"type\": \"bytes32\",\"name\": \"r\",\"internalType\": \"bytes32\"},{\"type\": \"bytes32\",\"name\": \"s\",\"internalType\": \"bytes32\"}],\"outputs\": [],\"stateMutability\": \"nonpayable\"},{\"type\": \"function\",\"name\": \"primarySaleRecipient\",\"inputs\": [],\"outputs\": [{\"type\": \"address\",\"name\": \"\",\"internalType\": \"address\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"renounceRole\",\"inputs\": [{\"type\": \"bytes32\",\"name\": \"role\",\"internalType\": \"bytes32\"},{\"type\": \"address\",\"name\": \"account\",\"internalType\": \"address\"}],\"outputs\": [],\"stateMutability\": \"nonpayable\"},{\"type\": \"function\",\"name\": \"revokeRole\",\"inputs\": [{\"type\": \"bytes32\",\"name\": \"role\",\"internalType\": \"bytes32\"},{\"type\": \"address\",\"name\": \"account\",\"internalType\": \"address\"}],\"outputs\": [],\"stateMutability\": \"nonpayable\"},{\"type\": \"function\",\"name\": \"setContractURI\",\"inputs\": [{\"type\": \"string\",\"name\": \"_uri\",\"internalType\": \"string\"}],\"outputs\": [],\"stateMutability\": \"nonpayable\"},{\"type\": \"function\",\"name\": \"setPlatformFeeInfo\",\"inputs\": [{\"type\": \"address\",\"name\": \"_platformFeeRecipient\",\"internalType\": \"address\"},{\"type\": \"uint256\",\"name\": \"_platformFeeBps\",\"internalType\": \"uint256\"}],\"outputs\": [],\"stateMutability\": \"nonpayable\"},{\"type\": \"function\",\"name\": \"setPrimarySaleRecipient\",\"inputs\": [{\"type\": \"address\",\"name\": \"_saleRecipient\",\"internalType\": \"address\"}],\"outputs\": [],\"stateMutability\": \"nonpayable\"},{\"type\": \"function\",\"name\": \"supportsInterface\",\"inputs\": [{\"type\": \"bytes4\",\"name\": \"interfaceId\",\"internalType\": \"bytes4\"}],\"outputs\": [{\"type\": \"bool\",\"name\": \"\",\"internalType\": \"bool\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"symbol\",\"inputs\": [],\"outputs\": [{\"type\": \"string\",\"name\": \"\",\"internalType\": \"string\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"totalSupply\",\"inputs\": [],\"outputs\": [{\"type\": \"uint256\",\"name\": \"\",\"internalType\": \"uint256\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"transfer\",\"inputs\": [{\"type\": \"address\",\"name\": \"to\",\"internalType\": \"address\"},{\"type\": \"uint256\",\"name\": \"amount\",\"internalType\": \"uint256\"}],\"outputs\": [{\"type\": \"bool\",\"name\": \"\",\"internalType\": \"bool\"}],\"stateMutability\": \"nonpayable\"},{\"type\": \"function\",\"name\": \"transferFrom\",\"inputs\": [{\"type\": \"address\",\"name\": \"from\",\"internalType\": \"address\"},{\"type\": \"address\",\"name\": \"to\",\"internalType\": \"address\"},{\"type\": \"uint256\",\"name\": \"amount\",\"internalType\": \"uint256\"}],\"outputs\": [{\"type\": \"bool\",\"name\": \"\",\"internalType\": \"bool\"}],\"stateMutability\": \"nonpayable\"},{\"type\": \"function\",\"name\": \"verify\",\"inputs\": [{\"type\": \"tuple\",\"name\": \"_req\",\"components\": [{\"type\": \"address\",\"name\": \"to\",\"internalType\": \"address\"},{\"type\": \"address\",\"name\": \"primarySaleRecipient\",\"internalType\": \"address\"},{\"type\": \"uint256\",\"name\": \"quantity\",\"internalType\": \"uint256\"},{\"type\": \"uint256\",\"name\": \"price\",\"internalType\": \"uint256\"},{\"type\": \"address\",\"name\": \"currency\",\"internalType\": \"address\"},{\"type\": \"uint128\",\"name\": \"validityStartTimestamp\",\"internalType\": \"uint128\"},{\"type\": \"uint128\",\"name\": \"validityEndTimestamp\",\"internalType\": \"uint128\"},{\"type\": \"bytes32\",\"name\": \"uid\",\"internalType\": \"bytes32\"}],\"internalType\": \"struct ITokenERC20.MintRequest\"},{\"type\": \"bytes\",\"name\": \"_signature\",\"internalType\": \"bytes\"}],\"outputs\": [{\"type\": \"bool\",\"name\": \"\",\"internalType\": \"bool\"},{\"type\": \"address\",\"name\": \"\",\"internalType\": \"address\"}],\"stateMutability\": \"view\"}]" + ); + var readResult = await ThirdwebContract.ReadContract(contract, "name"); + Console.WriteLine($"Contract read result: {readResult}"); -var accountOptions = new ThirdwebAccountOptions(client: client, type: WalletType.Embedded, email: "firekeeper@thirdweb.com"); -var account = new ThirdwebAccount(accountOptions); -await account.Initialize(); -var isConnected = account.IsConnected(); -await account.Disconnect(); // Force email + var privateKeyAccount = new PrivateKeyAccount(client, privateKey); + var embeddedAccount = new EmbeddedAccount(client, "firekeeper@thirdweb.com"); + var smartAccount = new SmartAccount(client, privateKeyAccount, "0xbf1C9aA4B1A085f7DA890a44E82B0A1289A40052", true, 421614); -if (!account.IsConnected()) -{ - if (account.Wallet is not Embedded embedded) - { - throw new Exception("Account is not connected and is not an embedded wallet."); - } - await embedded.SendOTP(); - var otp = Console.ReadLine(); - (var address, var canRetry) = await embedded.SubmitOTP(otp); - if (canRetry) - { - Console.WriteLine("OTP failed. Please try again."); - var otp2 = Console.ReadLine(); - (address, _) = await embedded.SubmitOTP(otp2); - } - if (string.IsNullOrEmpty(address)) - { - throw new Exception("Unknown Error, Null Address"); - } - else - { - Console.WriteLine($"Successfully Logged In! Address: {address}"); - } -} -Console.WriteLine($"Wallet address: {account.GetAddress()}"); + var accounts = new List { privateKeyAccount, embeddedAccount, smartAccount }; + + foreach (var account in accounts) + { + await account.Connect(); + } -var message = "Hello, Thirdweb!"; -var signature = account.PersonalSign(message); -Console.WriteLine($"Signed message: {signature}"); + if (!await embeddedAccount.IsConnected()) + { + await embeddedAccount.SendOTP(); + Console.WriteLine("Please submit the OTP."); + var otp = Console.ReadLine(); + (var embeddedAccountAddress, var canRetry) = await embeddedAccount.SubmitOTP(otp); + if (embeddedAccountAddress == null && canRetry) + { + Console.WriteLine("Please submit the OTP again."); + otp = Console.ReadLine(); + _ = await embeddedAccount.SubmitOTP(otp); + } + if (embeddedAccountAddress == null) + { + Console.WriteLine("OTP login failed. Please try again."); + return; + } + } -var balanceBefore = await ThirdwebContract.ReadContract(contract, "balanceOf", account.GetAddress()); -Console.WriteLine($"Balance before mint: {balanceBefore}"); + var thirdwebWallet = new ThirdwebWallet(); + await thirdwebWallet.Initialize(accounts); + thirdwebWallet.SetActive(await smartAccount.GetAddress()); + Console.WriteLine($"Active account: {await thirdwebWallet.GetAddress()}"); -var writeResult = await ThirdwebContract.WriteContract(account, contract, "mintTo", account.GetAddress(), 100); -Console.WriteLine($"Contract write result: {writeResult}"); + var message = "Hello, Thirdweb!"; + var signature = await thirdwebWallet.PersonalSign(message); + Console.WriteLine($"Signed message: {signature}"); -var balanceAfter = await ThirdwebContract.ReadContract(contract, "balanceOf", account.GetAddress()); -Console.WriteLine($"Balance after mint: {balanceAfter}"); + var balanceBefore = await ThirdwebContract.ReadContract(contract, "balanceOf", await thirdwebWallet.GetAddress()); + Console.WriteLine($"Balance before mint: {balanceBefore}"); + + var writeResult = await ThirdwebContract.WriteContract(thirdwebWallet, contract, "mintTo", 0, await thirdwebWallet.GetAddress(), 100); + Console.WriteLine($"Contract write result: {writeResult}"); + + var balanceAfter = await ThirdwebContract.ReadContract(contract, "balanceOf", await thirdwebWallet.GetAddress()); + Console.WriteLine($"Balance after mint: {balanceAfter}"); + } +} diff --git a/Thirdweb.Tests/ClientTests.cs b/Thirdweb.Tests/ClientTests.cs index c3fadb0..e288976 100644 --- a/Thirdweb.Tests/ClientTests.cs +++ b/Thirdweb.Tests/ClientTests.cs @@ -94,7 +94,7 @@ public void NoTimeoutOptions() { var client = new ThirdwebClient(new ThirdwebClientOptions(secretKey: _secretKey)); Assert.NotNull(client.FetchTimeoutOptions); - Assert.Equal(Constants.DefaultFetchTimeout, client.FetchTimeoutOptions.GetTimeout(TimeoutType.Storage)); - Assert.Equal(Constants.DefaultFetchTimeout, client.FetchTimeoutOptions.GetTimeout(TimeoutType.Rpc)); + Assert.Equal(Constants.DEFAULT_FETCH_TIMEOUT, client.FetchTimeoutOptions.GetTimeout(TimeoutType.Storage)); + Assert.Equal(Constants.DEFAULT_FETCH_TIMEOUT, client.FetchTimeoutOptions.GetTimeout(TimeoutType.Rpc)); } } diff --git a/Thirdweb/Thirdweb.Client/ITimeoutOptions.cs b/Thirdweb/Thirdweb.Client/ITimeoutOptions.cs index 297baaa..2e2d0b6 100644 --- a/Thirdweb/Thirdweb.Client/ITimeoutOptions.cs +++ b/Thirdweb/Thirdweb.Client/ITimeoutOptions.cs @@ -2,6 +2,6 @@ { public interface ITimeoutOptions { - int GetTimeout(TimeoutType type, int fallback = Constants.DefaultFetchTimeout); + int GetTimeout(TimeoutType type, int fallback = Constants.DEFAULT_FETCH_TIMEOUT); } } diff --git a/Thirdweb/Thirdweb.Client/TimeoutOptions.cs b/Thirdweb/Thirdweb.Client/TimeoutOptions.cs index 70b43ca..7f541c5 100644 --- a/Thirdweb/Thirdweb.Client/TimeoutOptions.cs +++ b/Thirdweb/Thirdweb.Client/TimeoutOptions.cs @@ -13,7 +13,7 @@ public TimeoutOptions(int? storage = null, int? rpc = null, int? other = null) Other = other; } - public int GetTimeout(TimeoutType type, int fallback = Constants.DefaultFetchTimeout) + public int GetTimeout(TimeoutType type, int fallback = Constants.DEFAULT_FETCH_TIMEOUT) { return type switch { diff --git a/Thirdweb/Thirdweb.Contracts/ThirdwebContract.cs b/Thirdweb/Thirdweb.Contracts/ThirdwebContract.cs index de4963b..56c99f6 100644 --- a/Thirdweb/Thirdweb.Contracts/ThirdwebContract.cs +++ b/Thirdweb/Thirdweb.Contracts/ThirdwebContract.cs @@ -11,24 +11,32 @@ public class ThirdwebContract internal BigInteger Chain { get; private set; } internal string Abi { get; private set; } - public ThirdwebContract(ThirdwebContractOptions options) + public ThirdwebContract(ThirdwebClient client, string address, BigInteger chain, string abi) { - if (options.Client == null) + if (client == null) + { throw new ArgumentException("Client must be provided"); + } - if (string.IsNullOrEmpty(options.Address)) + if (string.IsNullOrEmpty(address)) + { throw new ArgumentException("Address must be provided"); + } - if (options.Chain == 0) + if (chain == 0) + { throw new ArgumentException("Chain must be provided"); + } - if (string.IsNullOrEmpty(options.Abi)) + if (string.IsNullOrEmpty(abi)) + { throw new ArgumentException("Abi must be provided"); + } - Client = options.Client; - Address = options.Address; - Chain = options.Chain; - Abi = options.Abi; + Client = client; + Address = address; + Chain = chain; + Abi = abi; } public static async Task ReadContract(ThirdwebContract contract, string method, params object[] parameters) @@ -43,7 +51,7 @@ public static async Task ReadContract(ThirdwebContract contract, string me return function.DecodeTypeOutput(resultData); } - public static async Task WriteContract(ThirdwebAccount account, ThirdwebContract contract, string method, params object[] parameters) + public static async Task WriteContract(ThirdwebWallet wallet, ThirdwebContract contract, string method, BigInteger weiValue, params object[] parameters) { var rpc = ThirdwebRPC.GetRpcInstance(contract.Client, contract.Chain); @@ -53,7 +61,7 @@ public static async Task WriteContract(ThirdwebAccount account, Thirdweb var transaction = new TransactionInput { - From = account.GetAddress(), + From = await wallet.GetAddress(), To = contract.Address, Data = data, }; @@ -61,18 +69,24 @@ public static async Task WriteContract(ThirdwebAccount account, Thirdweb // TODO: Implement 1559 transaction.Gas = new HexBigInteger(await rpc.SendRequestAsync("eth_estimateGas", transaction)); transaction.GasPrice = new HexBigInteger(await rpc.SendRequestAsync("eth_gasPrice")); - transaction.Nonce = new HexBigInteger(await rpc.SendRequestAsync("eth_getTransactionCount", account.GetAddress(), "latest")); + transaction.Value = new HexBigInteger(weiValue); string hash; - if (account.Options.Type == WalletType.PrivateKey || account.Options.Type == WalletType.Embedded) + if (wallet.ActiveAccount.AccountType is ThirdwebAccountType.PrivateKeyAccount) { - var signedTx = account.SignTransaction(transaction, contract.Chain); + transaction.Nonce = new HexBigInteger(await rpc.SendRequestAsync("eth_getTransactionCount", wallet.GetAddress(), "latest")); + var signedTx = wallet.SignTransaction(transaction, contract.Chain); Console.WriteLine($"Signed transaction: {signedTx}"); hash = await rpc.SendRequestAsync("eth_sendRawTransaction", signedTx); } + else if (wallet.ActiveAccount.AccountType is ThirdwebAccountType.SmartAccount) + { + var smartAccount = wallet.ActiveAccount as SmartAccount; + hash = await smartAccount.SendTransaction(transaction); + } else { - hash = await rpc.SendRequestAsync("eth_sendTransaction", transaction); + throw new NotImplementedException("Account type not supported"); } return hash; } diff --git a/Thirdweb/Thirdweb.Contracts/ThirdwebContractOptions.cs b/Thirdweb/Thirdweb.Contracts/ThirdwebContractOptions.cs deleted file mode 100644 index 6d5d6d4..0000000 --- a/Thirdweb/Thirdweb.Contracts/ThirdwebContractOptions.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System.Numerics; - -namespace Thirdweb -{ - public class ThirdwebContractOptions - { - internal ThirdwebClient Client { get; private set; } - internal string Address { get; private set; } - internal BigInteger Chain { get; private set; } - internal string Abi { get; private set; } - - public ThirdwebContractOptions(ThirdwebClient client, string address, BigInteger chain, string abi) - { - Client = client; - Address = address; - Chain = chain; - Abi = abi; - } - } -} diff --git a/Thirdweb/Thirdweb.RPC/ThirdwebRPC.cs b/Thirdweb/Thirdweb.RPC/ThirdwebRPC.cs index 674b9ce..4dcf7b4 100644 --- a/Thirdweb/Thirdweb.RPC/ThirdwebRPC.cs +++ b/Thirdweb/Thirdweb.RPC/ThirdwebRPC.cs @@ -80,7 +80,7 @@ static ThirdwebRPC() _httpClient.DefaultRequestHeaders.Add("x-sdk-name", "Thirdweb.NET"); _httpClient.DefaultRequestHeaders.Add("x-sdk-os", System.Runtime.InteropServices.RuntimeInformation.OSDescription); _httpClient.DefaultRequestHeaders.Add("x-sdk-platform", "dotnet"); - _httpClient.DefaultRequestHeaders.Add("x-sdk-version", Constants.Version); + _httpClient.DefaultRequestHeaders.Add("x-sdk-version", Constants.VERSION); } private ThirdwebRPC(ThirdwebClient client, BigInteger chainId) diff --git a/Thirdweb/Thirdweb.Utils/Constants.cs b/Thirdweb/Thirdweb.Utils/Constants.cs index 278920e..0a621d8 100644 --- a/Thirdweb/Thirdweb.Utils/Constants.cs +++ b/Thirdweb/Thirdweb.Utils/Constants.cs @@ -2,7 +2,11 @@ { public static class Constants { - internal const string Version = "0.0.1"; - internal const int DefaultFetchTimeout = 60000; + internal const string VERSION = "0.0.1"; + internal const int DEFAULT_FETCH_TIMEOUT = 60000; + internal const string DEFAULT_ENTRYPOINT_ADDRESS = "0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789"; // v0.6 + internal const string DUMMY_SIG = "0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c"; + internal const string DUMMY_PAYMASTER_AND_DATA_HEX = + "0x0101010101010101010101010101010101010101000000000000000000000000000000000000000000000000000001010101010100000000000000000000000000000000000000000000000000000000000000000101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101"; } } diff --git a/Thirdweb/Thirdweb.Utils/Utils.cs b/Thirdweb/Thirdweb.Utils/Utils.cs index 480f664..c69f262 100644 --- a/Thirdweb/Thirdweb.Utils/Utils.cs +++ b/Thirdweb/Thirdweb.Utils/Utils.cs @@ -11,5 +11,17 @@ public static string ComputeClientIdFromSecretKey(string secretKey) var hash = sha256.ComputeHash(Encoding.UTF8.GetBytes(secretKey)); return BitConverter.ToString(hash).Replace("-", "").ToLower().Substring(0, 32); } + + public static string HexConcat(params string[] hexStrings) + { + var hex = new StringBuilder("0x"); + + foreach (var hexStr in hexStrings) + { + _ = hex.Append(hexStr[2..]); + } + + return hex.ToString(); + } } } diff --git a/Thirdweb/Thirdweb.Wallets/EmbeddedAccount/EmbeddedAccount.cs b/Thirdweb/Thirdweb.Wallets/EmbeddedAccount/EmbeddedAccount.cs new file mode 100644 index 0000000..c7753fa --- /dev/null +++ b/Thirdweb/Thirdweb.Wallets/EmbeddedAccount/EmbeddedAccount.cs @@ -0,0 +1,234 @@ +using System.Numerics; +using System.Text; +using Nethereum.ABI.EIP712; +using Nethereum.Hex.HexConvertors.Extensions; +using Nethereum.Hex.HexTypes; +using Nethereum.Model; +using Nethereum.RPC.Eth.DTOs; +using Nethereum.RPC.Eth.Mappers; +using Nethereum.Signer; +using Nethereum.Signer.EIP712; +using Thirdweb.EWS; + +namespace Thirdweb +{ + public class EmbeddedAccount : IThirdwebAccount + { + public ThirdwebAccountType AccountType => ThirdwebAccountType.PrivateKeyAccount; + + private ThirdwebClient _client; + private EmbeddedWallet _embeddedWallet; + private User _user; + private EthECKey _ecKey; + private string _email; + + public EmbeddedAccount(ThirdwebClient client, string email) + { + if (string.IsNullOrEmpty(email)) + { + throw new ArgumentException("Email must be provided to use Embedded Wallets."); + } + + _embeddedWallet = new EmbeddedWallet(client); + _email = email; + _client = client; + } + + public async Task Connect() + { + try + { + _user = await _embeddedWallet.GetUserAsync(_email, "EmailOTP"); + _ecKey = new EthECKey(_user.Account.PrivateKey); + } + catch + { + Console.WriteLine("User not found. Please call EmbeddedAccount.SendOTP() to initialize the login process."); + _user = null; + _ecKey = null; + } + } + + #region Email OTP Flow + + public async Task SendOTP() + { + if (string.IsNullOrEmpty(_email)) + { + throw new Exception("Email is required for OTP login"); + } + + try + { + (bool isNewUser, bool isNewDevice, bool needsRecoveryCode) = await _embeddedWallet.SendOtpEmailAsync(_email); + Console.WriteLine("OTP sent to email. Please call EmbeddedAccount.SubmitOTP to login."); + } + catch (Exception e) + { + throw new Exception("Failed to send OTP email", e); + } + } + + public async Task<(string, bool)> SubmitOTP(string otp) + { + var res = await _embeddedWallet.VerifyOtpAsync(_email, otp, null); + if (res.User == null) + { + var canRetry = res.CanRetry; + if (canRetry) + { + Console.WriteLine("Invalid OTP. Please try again."); + } + else + { + Console.WriteLine("Invalid OTP. Please request a new OTP."); + } + return (null, canRetry); + } + else + { + _user = res.User; + _ecKey = new EthECKey(_user.Account.PrivateKey); + return (await GetAddress(), false); + } + } + + #endregion + + public Task GetAddress() + { + return Task.FromResult(_ecKey.GetPublicAddress()); + } + + public Task EthSign(string message) + { + if (message == null) + { + throw new ArgumentNullException(nameof(message), "Message to sign cannot be null."); + } + + var signer = new MessageSigner(); + var signature = signer.Sign(Encoding.UTF8.GetBytes(message), _ecKey); + return Task.FromResult(signature); + } + + public Task PersonalSign(byte[] rawMessage) + { + if (rawMessage == null) + { + throw new ArgumentNullException(nameof(rawMessage), "Message to sign cannot be null."); + } + + var signer = new EthereumMessageSigner(); + var signature = signer.Sign(rawMessage, _ecKey); + return Task.FromResult(signature); + } + + public Task PersonalSign(string message) + { + if (message == null) + { + throw new ArgumentNullException(nameof(message), "Message to sign cannot be null."); + } + + var signer = new EthereumMessageSigner(); + var signature = signer.EncodeUTF8AndSign(message, _ecKey); + return Task.FromResult(signature); + } + + public Task SignTypedDataV4(string json) + { + if (json == null) + { + throw new ArgumentNullException(nameof(json), "Json to sign cannot be null."); + } + + var signer = new Eip712TypedDataSigner(); + var signature = signer.SignTypedDataV4(json, _ecKey); + return Task.FromResult(signature); + } + + public Task SignTypedDataV4(T data, TypedData typedData) + { + if (data == null) + { + throw new ArgumentNullException(nameof(data), "Data to sign cannot be null."); + } + + var signer = new Eip712TypedDataSigner(); + var signature = signer.SignTypedDataV4(data, typedData, _ecKey); + return Task.FromResult(signature); + } + + public async Task SignTransaction(TransactionInput transaction, BigInteger chainId) + { + if (transaction == null) + { + throw new ArgumentNullException(nameof(transaction)); + } + + if (string.IsNullOrWhiteSpace(transaction.From)) + { + transaction.From = await GetAddress(); + } + else if (transaction.From != await GetAddress()) + { + throw new Exception("Transaction 'From' address does not match the wallet address"); + } + + var nonce = transaction.Nonce ?? throw new ArgumentNullException(nameof(transaction), "Transaction nonce has not been set"); + + var gasLimit = transaction.Gas; + var value = transaction.Value ?? new HexBigInteger(0); + + string signedTransaction; + if (transaction.Type != null && transaction.Type.Value == TransactionType.EIP1559.AsByte()) + { + var maxPriorityFeePerGas = transaction.MaxPriorityFeePerGas.Value; + var maxFeePerGas = transaction.MaxFeePerGas.Value; + var transaction1559 = new Transaction1559( + chainId, + nonce, + maxPriorityFeePerGas, + maxFeePerGas, + gasLimit, + transaction.To, + value, + transaction.Data, + transaction.AccessList.ToSignerAccessListItemArray() + ); + + var signer = new Transaction1559Signer(); + signer.SignTransaction(_ecKey, transaction1559); + signedTransaction = transaction1559.GetRLPEncoded().ToHex(); + } + else + { + var gasPrice = transaction.GasPrice; + var legacySigner = new LegacyTransactionSigner(); + signedTransaction = legacySigner.SignTransaction(_ecKey.GetPrivateKey(), chainId, transaction.To, value.Value, nonce, gasPrice.Value, gasLimit.Value, transaction.Data); + } + + return "0x" + signedTransaction; + } + + public Task IsConnected() + { + return Task.FromResult(_ecKey != null); + } + + public async Task Disconnect() + { + try + { + await _embeddedWallet.SignOutAsync(); + } + catch + { + Console.WriteLine("Failed to sign out user. Proceeding anyway."); + } + _user = null; + _ecKey = null; + } + } +} diff --git a/Thirdweb/Thirdweb.Wallets/Thirdweb.Wallets.Embedded/EmbeddedWallet.Authentication/AWS.cs b/Thirdweb/Thirdweb.Wallets/EmbeddedAccount/EmbeddedWallet.Authentication/AWS.cs similarity index 100% rename from Thirdweb/Thirdweb.Wallets/Thirdweb.Wallets.Embedded/EmbeddedWallet.Authentication/AWS.cs rename to Thirdweb/Thirdweb.Wallets/EmbeddedAccount/EmbeddedWallet.Authentication/AWS.cs diff --git a/Thirdweb/Thirdweb.Wallets/Thirdweb.Wallets.Embedded/EmbeddedWallet.Authentication/Server.Types.cs b/Thirdweb/Thirdweb.Wallets/EmbeddedAccount/EmbeddedWallet.Authentication/Server.Types.cs similarity index 100% rename from Thirdweb/Thirdweb.Wallets/Thirdweb.Wallets.Embedded/EmbeddedWallet.Authentication/Server.Types.cs rename to Thirdweb/Thirdweb.Wallets/EmbeddedAccount/EmbeddedWallet.Authentication/Server.Types.cs diff --git a/Thirdweb/Thirdweb.Wallets/Thirdweb.Wallets.Embedded/EmbeddedWallet.Authentication/Server.cs b/Thirdweb/Thirdweb.Wallets/EmbeddedAccount/EmbeddedWallet.Authentication/Server.cs similarity index 100% rename from Thirdweb/Thirdweb.Wallets/Thirdweb.Wallets.Embedded/EmbeddedWallet.Authentication/Server.cs rename to Thirdweb/Thirdweb.Wallets/EmbeddedAccount/EmbeddedWallet.Authentication/Server.cs diff --git a/Thirdweb/Thirdweb.Wallets/Thirdweb.Wallets.Embedded/EmbeddedWallet.Encryption/EmbeddedWallet.Cryptography.cs b/Thirdweb/Thirdweb.Wallets/EmbeddedAccount/EmbeddedWallet.Encryption/EmbeddedWallet.Cryptography.cs similarity index 100% rename from Thirdweb/Thirdweb.Wallets/Thirdweb.Wallets.Embedded/EmbeddedWallet.Encryption/EmbeddedWallet.Cryptography.cs rename to Thirdweb/Thirdweb.Wallets/EmbeddedAccount/EmbeddedWallet.Encryption/EmbeddedWallet.Cryptography.cs diff --git a/Thirdweb/Thirdweb.Wallets/Thirdweb.Wallets.Embedded/EmbeddedWallet.Encryption/IvGenerator.cs b/Thirdweb/Thirdweb.Wallets/EmbeddedAccount/EmbeddedWallet.Encryption/IvGenerator.cs similarity index 100% rename from Thirdweb/Thirdweb.Wallets/Thirdweb.Wallets.Embedded/EmbeddedWallet.Encryption/IvGenerator.cs rename to Thirdweb/Thirdweb.Wallets/EmbeddedAccount/EmbeddedWallet.Encryption/IvGenerator.cs diff --git a/Thirdweb/Thirdweb.Wallets/Thirdweb.Wallets.Embedded/EmbeddedWallet.Encryption/Secrets.cs b/Thirdweb/Thirdweb.Wallets/EmbeddedAccount/EmbeddedWallet.Encryption/Secrets.cs similarity index 100% rename from Thirdweb/Thirdweb.Wallets/Thirdweb.Wallets.Embedded/EmbeddedWallet.Encryption/Secrets.cs rename to Thirdweb/Thirdweb.Wallets/EmbeddedAccount/EmbeddedWallet.Encryption/Secrets.cs diff --git a/Thirdweb/Thirdweb.Wallets/Thirdweb.Wallets.Embedded/EmbeddedWallet.Exceptions/VerificationException.cs b/Thirdweb/Thirdweb.Wallets/EmbeddedAccount/EmbeddedWallet.Exceptions/VerificationException.cs similarity index 100% rename from Thirdweb/Thirdweb.Wallets/Thirdweb.Wallets.Embedded/EmbeddedWallet.Exceptions/VerificationException.cs rename to Thirdweb/Thirdweb.Wallets/EmbeddedAccount/EmbeddedWallet.Exceptions/VerificationException.cs diff --git a/Thirdweb/Thirdweb.Wallets/Thirdweb.Wallets.Embedded/EmbeddedWallet.Models/User.cs b/Thirdweb/Thirdweb.Wallets/EmbeddedAccount/EmbeddedWallet.Models/User.cs similarity index 100% rename from Thirdweb/Thirdweb.Wallets/Thirdweb.Wallets.Embedded/EmbeddedWallet.Models/User.cs rename to Thirdweb/Thirdweb.Wallets/EmbeddedAccount/EmbeddedWallet.Models/User.cs diff --git a/Thirdweb/Thirdweb.Wallets/Thirdweb.Wallets.Embedded/EmbeddedWallet.Models/UserStatus.cs b/Thirdweb/Thirdweb.Wallets/EmbeddedAccount/EmbeddedWallet.Models/UserStatus.cs similarity index 100% rename from Thirdweb/Thirdweb.Wallets/Thirdweb.Wallets.Embedded/EmbeddedWallet.Models/UserStatus.cs rename to Thirdweb/Thirdweb.Wallets/EmbeddedAccount/EmbeddedWallet.Models/UserStatus.cs diff --git a/Thirdweb/Thirdweb.Wallets/Thirdweb.Wallets.Embedded/EmbeddedWallet.Storage/LocalStorage.Types.cs b/Thirdweb/Thirdweb.Wallets/EmbeddedAccount/EmbeddedWallet.Storage/LocalStorage.Types.cs similarity index 100% rename from Thirdweb/Thirdweb.Wallets/Thirdweb.Wallets.Embedded/EmbeddedWallet.Storage/LocalStorage.Types.cs rename to Thirdweb/Thirdweb.Wallets/EmbeddedAccount/EmbeddedWallet.Storage/LocalStorage.Types.cs diff --git a/Thirdweb/Thirdweb.Wallets/Thirdweb.Wallets.Embedded/EmbeddedWallet.Storage/LocalStorage.cs b/Thirdweb/Thirdweb.Wallets/EmbeddedAccount/EmbeddedWallet.Storage/LocalStorage.cs similarity index 97% rename from Thirdweb/Thirdweb.Wallets/Thirdweb.Wallets.Embedded/EmbeddedWallet.Storage/LocalStorage.cs rename to Thirdweb/Thirdweb.Wallets/EmbeddedAccount/EmbeddedWallet.Storage/LocalStorage.cs index 03fee78..2d60736 100644 --- a/Thirdweb/Thirdweb.Wallets/Thirdweb.Wallets.Embedded/EmbeddedWallet.Storage/LocalStorage.cs +++ b/Thirdweb/Thirdweb.Wallets/EmbeddedAccount/EmbeddedWallet.Storage/LocalStorage.cs @@ -34,7 +34,7 @@ internal LocalStorage(string clientId) directory = Application.persistentDataPath; #else directory = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); - Console.WriteLine($"Embedded Wallet Storage: Using '{directory}'"); + // Console.WriteLine($"Embedded Wallet Storage: Using '{directory}'"); #endif directory = Path.Combine(directory, "EWS"); Directory.CreateDirectory(directory); diff --git a/Thirdweb/Thirdweb.Wallets/Thirdweb.Wallets.Embedded/EmbeddedWallet/EmbeddedWallet.AuthEndpoint.cs b/Thirdweb/Thirdweb.Wallets/EmbeddedAccount/EmbeddedWallet/EmbeddedWallet.AuthEndpoint.cs similarity index 100% rename from Thirdweb/Thirdweb.Wallets/Thirdweb.Wallets.Embedded/EmbeddedWallet/EmbeddedWallet.AuthEndpoint.cs rename to Thirdweb/Thirdweb.Wallets/EmbeddedAccount/EmbeddedWallet/EmbeddedWallet.AuthEndpoint.cs diff --git a/Thirdweb/Thirdweb.Wallets/Thirdweb.Wallets.Embedded/EmbeddedWallet/EmbeddedWallet.EmailOTP.cs b/Thirdweb/Thirdweb.Wallets/EmbeddedAccount/EmbeddedWallet/EmbeddedWallet.EmailOTP.cs similarity index 100% rename from Thirdweb/Thirdweb.Wallets/Thirdweb.Wallets.Embedded/EmbeddedWallet/EmbeddedWallet.EmailOTP.cs rename to Thirdweb/Thirdweb.Wallets/EmbeddedAccount/EmbeddedWallet/EmbeddedWallet.EmailOTP.cs diff --git a/Thirdweb/Thirdweb.Wallets/Thirdweb.Wallets.Embedded/EmbeddedWallet/EmbeddedWallet.JWT.cs b/Thirdweb/Thirdweb.Wallets/EmbeddedAccount/EmbeddedWallet/EmbeddedWallet.JWT.cs similarity index 100% rename from Thirdweb/Thirdweb.Wallets/Thirdweb.Wallets.Embedded/EmbeddedWallet/EmbeddedWallet.JWT.cs rename to Thirdweb/Thirdweb.Wallets/EmbeddedAccount/EmbeddedWallet/EmbeddedWallet.JWT.cs diff --git a/Thirdweb/Thirdweb.Wallets/Thirdweb.Wallets.Embedded/EmbeddedWallet/EmbeddedWallet.Misc.cs b/Thirdweb/Thirdweb.Wallets/EmbeddedAccount/EmbeddedWallet/EmbeddedWallet.Misc.cs similarity index 100% rename from Thirdweb/Thirdweb.Wallets/Thirdweb.Wallets.Embedded/EmbeddedWallet/EmbeddedWallet.Misc.cs rename to Thirdweb/Thirdweb.Wallets/EmbeddedAccount/EmbeddedWallet/EmbeddedWallet.Misc.cs diff --git a/Thirdweb/Thirdweb.Wallets/Thirdweb.Wallets.Embedded/EmbeddedWallet/EmbeddedWallet.OAuth.cs b/Thirdweb/Thirdweb.Wallets/EmbeddedAccount/EmbeddedWallet/EmbeddedWallet.OAuth.cs similarity index 100% rename from Thirdweb/Thirdweb.Wallets/Thirdweb.Wallets.Embedded/EmbeddedWallet/EmbeddedWallet.OAuth.cs rename to Thirdweb/Thirdweb.Wallets/EmbeddedAccount/EmbeddedWallet/EmbeddedWallet.OAuth.cs diff --git a/Thirdweb/Thirdweb.Wallets/Thirdweb.Wallets.Embedded/EmbeddedWallet/EmbeddedWallet.PhoneOTP.cs b/Thirdweb/Thirdweb.Wallets/EmbeddedAccount/EmbeddedWallet/EmbeddedWallet.PhoneOTP.cs similarity index 100% rename from Thirdweb/Thirdweb.Wallets/Thirdweb.Wallets.Embedded/EmbeddedWallet/EmbeddedWallet.PhoneOTP.cs rename to Thirdweb/Thirdweb.Wallets/EmbeddedAccount/EmbeddedWallet/EmbeddedWallet.PhoneOTP.cs diff --git a/Thirdweb/Thirdweb.Wallets/Thirdweb.Wallets.Embedded/EmbeddedWallet/EmbeddedWallet.cs b/Thirdweb/Thirdweb.Wallets/EmbeddedAccount/EmbeddedWallet/EmbeddedWallet.cs similarity index 94% rename from Thirdweb/Thirdweb.Wallets/Thirdweb.Wallets.Embedded/EmbeddedWallet/EmbeddedWallet.cs rename to Thirdweb/Thirdweb.Wallets/EmbeddedAccount/EmbeddedWallet/EmbeddedWallet.cs index 918bc60..e14ff66 100644 --- a/Thirdweb/Thirdweb.Wallets/Thirdweb.Wallets.Embedded/EmbeddedWallet/EmbeddedWallet.cs +++ b/Thirdweb/Thirdweb.Wallets/EmbeddedAccount/EmbeddedWallet/EmbeddedWallet.cs @@ -18,7 +18,7 @@ internal partial class EmbeddedWallet public EmbeddedWallet(ThirdwebClient client) { localStorage = new LocalStorage(client.ClientId); - server = new Server(client.ClientId, client.BundleId, "dotnet", Constants.Version, client.SecretKey); + server = new Server(client.ClientId, client.BundleId, "dotnet", Constants.VERSION, client.SecretKey); ivGenerator = new IvGenerator(); } } diff --git a/Thirdweb/Thirdweb.Wallets/IThirdwebAccount.cs b/Thirdweb/Thirdweb.Wallets/IThirdwebAccount.cs new file mode 100644 index 0000000..86ede99 --- /dev/null +++ b/Thirdweb/Thirdweb.Wallets/IThirdwebAccount.cs @@ -0,0 +1,27 @@ +using System.Numerics; +using Nethereum.ABI.EIP712; +using Nethereum.RPC.Eth.DTOs; + +namespace Thirdweb +{ + public interface IThirdwebAccount + { + public ThirdwebAccountType AccountType { get; } + public Task Connect(); + public Task GetAddress(); + public Task EthSign(string message); + public Task PersonalSign(byte[] rawMessage); + public Task PersonalSign(string message); + public Task SignTypedDataV4(string json); + public Task SignTypedDataV4(T data, TypedData typedData); + public Task SignTransaction(TransactionInput transaction, BigInteger chainId); + public Task IsConnected(); + public Task Disconnect(); + } + + public enum ThirdwebAccountType + { + PrivateKeyAccount, + SmartAccount + } +} diff --git a/Thirdweb/Thirdweb.Wallets/IWallet.cs b/Thirdweb/Thirdweb.Wallets/IWallet.cs deleted file mode 100644 index 4fb8086..0000000 --- a/Thirdweb/Thirdweb.Wallets/IWallet.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System.Numerics; -using Nethereum.ABI.EIP712; -using Nethereum.RPC.Eth.DTOs; - -namespace Thirdweb -{ - public interface IWallet - { - internal Task Initialize(); - internal string GetAddress(); - internal string EthSign(string message); - internal string PersonalSign(string message); - internal string SignTypedDataV4(string json); - internal string SignTypedDataV4(T data, TypedData typedData); - internal string SignTransaction(TransactionInput transaction, BigInteger chainId); - internal bool IsConnected(); - internal Task Disconnect(); - } -} diff --git a/Thirdweb/Thirdweb.Wallets/PrivateKeyAccount/PrivateKeyAccount.cs b/Thirdweb/Thirdweb.Wallets/PrivateKeyAccount/PrivateKeyAccount.cs new file mode 100644 index 0000000..0947237 --- /dev/null +++ b/Thirdweb/Thirdweb.Wallets/PrivateKeyAccount/PrivateKeyAccount.cs @@ -0,0 +1,166 @@ +using System.Numerics; +using System.Text; +using Nethereum.ABI.EIP712; +using Nethereum.Hex.HexConvertors.Extensions; +using Nethereum.Hex.HexTypes; +using Nethereum.Model; +using Nethereum.RPC.Eth.DTOs; +using Nethereum.RPC.Eth.Mappers; +using Nethereum.Signer; +using Nethereum.Signer.EIP712; + +namespace Thirdweb +{ + public class PrivateKeyAccount : IThirdwebAccount + { + public ThirdwebAccountType AccountType => ThirdwebAccountType.SmartAccount; + + private ThirdwebClient _client; + private EthECKey _ecKey; + + public PrivateKeyAccount(ThirdwebClient client, string privateKeyHex) + { + if (string.IsNullOrEmpty(privateKeyHex)) + { + throw new ArgumentNullException(nameof(privateKeyHex), "Private key cannot be null or empty."); + } + + _client = client; + _ecKey = new EthECKey(privateKeyHex); + } + + public Task Connect() + { + // No initialization required for private key wallets + return Task.CompletedTask; + } + + public Task GetAddress() + { + return Task.FromResult(_ecKey.GetPublicAddress()); + } + + public Task EthSign(string message) + { + if (message == null) + { + throw new ArgumentNullException(nameof(message), "Message to sign cannot be null."); + } + + var signer = new MessageSigner(); + var signature = signer.Sign(Encoding.UTF8.GetBytes(message), _ecKey); + return Task.FromResult(signature); + } + + public Task PersonalSign(byte[] rawMessage) + { + if (rawMessage == null) + { + throw new ArgumentNullException(nameof(rawMessage), "Message to sign cannot be null."); + } + + var signer = new EthereumMessageSigner(); + var signature = signer.Sign(rawMessage, _ecKey); + return Task.FromResult(signature); + } + + public Task PersonalSign(string message) + { + if (message == null) + { + throw new ArgumentNullException(nameof(message), "Message to sign cannot be null."); + } + + var signer = new EthereumMessageSigner(); + var signature = signer.EncodeUTF8AndSign(message, _ecKey); + return Task.FromResult(signature); + } + + public Task SignTypedDataV4(string json) + { + if (json == null) + { + throw new ArgumentNullException(nameof(json), "Json to sign cannot be null."); + } + + var signer = new Eip712TypedDataSigner(); + var signature = signer.SignTypedDataV4(json, _ecKey); + return Task.FromResult(signature); + } + + public Task SignTypedDataV4(T data, TypedData typedData) + { + if (data == null) + { + throw new ArgumentNullException(nameof(data), "Data to sign cannot be null."); + } + + var signer = new Eip712TypedDataSigner(); + var signature = signer.SignTypedDataV4(data, typedData, _ecKey); + return Task.FromResult(signature); + } + + public async Task SignTransaction(TransactionInput transaction, BigInteger chainId) + { + if (transaction == null) + { + throw new ArgumentNullException(nameof(transaction)); + } + + if (string.IsNullOrWhiteSpace(transaction.From)) + { + transaction.From = await GetAddress(); + } + else if (transaction.From != await GetAddress()) + { + throw new Exception("Transaction 'From' address does not match the wallet address"); + } + + var nonce = transaction.Nonce ?? throw new ArgumentNullException(nameof(transaction), "Transaction nonce has not been set"); + + var gasLimit = transaction.Gas; + var value = transaction.Value ?? new HexBigInteger(0); + + string signedTransaction; + if (transaction.Type != null && transaction.Type.Value == TransactionType.EIP1559.AsByte()) + { + var maxPriorityFeePerGas = transaction.MaxPriorityFeePerGas.Value; + var maxFeePerGas = transaction.MaxFeePerGas.Value; + var transaction1559 = new Transaction1559( + chainId, + nonce, + maxPriorityFeePerGas, + maxFeePerGas, + gasLimit, + transaction.To, + value, + transaction.Data, + transaction.AccessList.ToSignerAccessListItemArray() + ); + + var signer = new Transaction1559Signer(); + signer.SignTransaction(_ecKey, transaction1559); + signedTransaction = transaction1559.GetRLPEncoded().ToHex(); + } + else + { + var gasPrice = transaction.GasPrice; + var legacySigner = new LegacyTransactionSigner(); + signedTransaction = legacySigner.SignTransaction(_ecKey.GetPrivateKey(), chainId, transaction.To, value.Value, nonce, gasPrice.Value, gasLimit.Value, transaction.Data); + } + + return "0x" + signedTransaction; + } + + public Task IsConnected() + { + return Task.FromResult(_ecKey != null); + } + + public Task Disconnect() + { + _ecKey = null; + return Task.CompletedTask; + } + } +} diff --git a/Thirdweb/Thirdweb.Wallets/SmartAccount/SmartAccount.cs b/Thirdweb/Thirdweb.Wallets/SmartAccount/SmartAccount.cs new file mode 100644 index 0000000..21e2cbd --- /dev/null +++ b/Thirdweb/Thirdweb.Wallets/SmartAccount/SmartAccount.cs @@ -0,0 +1,275 @@ +using System.Numerics; +using System.Security.Cryptography; +using Nethereum.ABI.EIP712; +using Nethereum.Contracts; +using Nethereum.Hex.HexConvertors.Extensions; +using Nethereum.Hex.HexTypes; +using Nethereum.JsonRpc.Client.RpcMessages; +using Nethereum.RPC.Eth.DTOs; +using Nethereum.Signer; +using Newtonsoft.Json; +using Thirdweb.AccountAbstraction; + +namespace Thirdweb +{ + public class SmartAccount : IThirdwebAccount + { + public ThirdwebAccountType AccountType => ThirdwebAccountType.SmartAccount; + + private ThirdwebClient _client; + private IThirdwebAccount _personalAccount; + private string _factoryAddress; + private bool _gasless; + private ThirdwebContract _factoryContract; + private ThirdwebContract _accountContract; + private ThirdwebContract _entryPointContract; + private BigInteger _chainId; + private string _bundlerUrl; + private string _paymasterUrl; + private string _entryPoint; + + public SmartAccount( + ThirdwebClient client, + IThirdwebAccount personalAccount, + string factoryAddress, + bool gasless, + BigInteger chainId, + string entryPoint = null, + string bundlerUrl = null, + string paymasterUrl = null + ) + { + _client = client; + _personalAccount = personalAccount; + _factoryAddress = factoryAddress; + _gasless = gasless; + _chainId = chainId; + _entryPoint ??= $"0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789"; // v0.6.0 + _bundlerUrl ??= $"https://{chainId}.bundler.thirdweb.com"; + _paymasterUrl ??= $"https://{chainId}.bundler.thirdweb.com"; + } + + public async Task Connect() + { + if (!await _personalAccount.IsConnected()) + { + throw new Exception("SmartAccount.Connect: Personal account must be connected."); + } + + _entryPointContract = new ThirdwebContract( + _client, + _entryPoint, + _chainId, + "[{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"preOpGas\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"paid\",\"type\":\"uint256\"},{\"internalType\":\"uint48\",\"name\":\"validAfter\",\"type\":\"uint48\"},{\"internalType\":\"uint48\",\"name\":\"validUntil\",\"type\":\"uint48\"},{\"internalType\":\"bool\",\"name\":\"targetSuccess\",\"type\":\"bool\"},{\"internalType\":\"bytes\",\"name\":\"targetResult\",\"type\":\"bytes\"}],\"name\":\"ExecutionResult\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"opIndex\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"reason\",\"type\":\"string\"}],\"name\":\"FailedOp\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"SenderAddressResult\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"aggregator\",\"type\":\"address\"}],\"name\":\"SignatureValidationFailed\",\"type\":\"error\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"preOpGas\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"prefund\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"sigFailed\",\"type\":\"bool\"},{\"internalType\":\"uint48\",\"name\":\"validAfter\",\"type\":\"uint48\"},{\"internalType\":\"uint48\",\"name\":\"validUntil\",\"type\":\"uint48\"},{\"internalType\":\"bytes\",\"name\":\"paymasterContext\",\"type\":\"bytes\"}],\"internalType\":\"struct IEntryPoint.ReturnInfo\",\"name\":\"returnInfo\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"stake\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"unstakeDelaySec\",\"type\":\"uint256\"}],\"internalType\":\"struct IStakeManager.StakeInfo\",\"name\":\"senderInfo\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"stake\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"unstakeDelaySec\",\"type\":\"uint256\"}],\"internalType\":\"struct IStakeManager.StakeInfo\",\"name\":\"factoryInfo\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"stake\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"unstakeDelaySec\",\"type\":\"uint256\"}],\"internalType\":\"struct IStakeManager.StakeInfo\",\"name\":\"paymasterInfo\",\"type\":\"tuple\"}],\"name\":\"ValidationResult\",\"type\":\"error\"},{\"inputs\":[{\"components\":[{\"internalType\":\"uint256\",\"name\":\"preOpGas\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"prefund\",\"type\":\"uint256\"},{\"internalType\":\"bool\",\"name\":\"sigFailed\",\"type\":\"bool\"},{\"internalType\":\"uint48\",\"name\":\"validAfter\",\"type\":\"uint48\"},{\"internalType\":\"uint48\",\"name\":\"validUntil\",\"type\":\"uint48\"},{\"internalType\":\"bytes\",\"name\":\"paymasterContext\",\"type\":\"bytes\"}],\"internalType\":\"struct IEntryPoint.ReturnInfo\",\"name\":\"returnInfo\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"stake\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"unstakeDelaySec\",\"type\":\"uint256\"}],\"internalType\":\"struct IStakeManager.StakeInfo\",\"name\":\"senderInfo\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"stake\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"unstakeDelaySec\",\"type\":\"uint256\"}],\"internalType\":\"struct IStakeManager.StakeInfo\",\"name\":\"factoryInfo\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"stake\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"unstakeDelaySec\",\"type\":\"uint256\"}],\"internalType\":\"struct IStakeManager.StakeInfo\",\"name\":\"paymasterInfo\",\"type\":\"tuple\"},{\"components\":[{\"internalType\":\"address\",\"name\":\"aggregator\",\"type\":\"address\"},{\"components\":[{\"internalType\":\"uint256\",\"name\":\"stake\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"unstakeDelaySec\",\"type\":\"uint256\"}],\"internalType\":\"struct IStakeManager.StakeInfo\",\"name\":\"stakeInfo\",\"type\":\"tuple\"}],\"internalType\":\"struct IEntryPoint.AggregatorStakeInfo\",\"name\":\"aggregatorInfo\",\"type\":\"tuple\"}],\"name\":\"ValidationResultWithAggregation\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"userOpHash\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"factory\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"paymaster\",\"type\":\"address\"}],\"name\":\"AccountDeployed\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[],\"name\":\"BeforeExecution\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"totalDeposit\",\"type\":\"uint256\"}],\"name\":\"Deposited\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"aggregator\",\"type\":\"address\"}],\"name\":\"SignatureAggregatorChanged\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"totalStaked\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"unstakeDelaySec\",\"type\":\"uint256\"}],\"name\":\"StakeLocked\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"withdrawTime\",\"type\":\"uint256\"}],\"name\":\"StakeUnlocked\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"withdrawAddress\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"StakeWithdrawn\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"userOpHash\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"paymaster\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"actualGasCost\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"actualGasUsed\",\"type\":\"uint256\"}],\"name\":\"UserOperationEvent\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"bytes32\",\"name\":\"userOpHash\",\"type\":\"bytes32\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"revertReason\",\"type\":\"bytes\"}],\"name\":\"UserOperationRevertReason\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"address\",\"name\":\"withdrawAddress\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"Withdrawn\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"SIG_VALIDATION_FAILED\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"initCode\",\"type\":\"bytes\"},{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"paymasterAndData\",\"type\":\"bytes\"}],\"name\":\"_validateSenderAndPaymaster\",\"outputs\":[],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint32\",\"name\":\"unstakeDelaySec\",\"type\":\"uint32\"}],\"name\":\"addStake\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"balanceOf\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"depositTo\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"deposits\",\"outputs\":[{\"internalType\":\"uint112\",\"name\":\"deposit\",\"type\":\"uint112\"},{\"internalType\":\"bool\",\"name\":\"staked\",\"type\":\"bool\"},{\"internalType\":\"uint112\",\"name\":\"stake\",\"type\":\"uint112\"},{\"internalType\":\"uint32\",\"name\":\"unstakeDelaySec\",\"type\":\"uint32\"},{\"internalType\":\"uint48\",\"name\":\"withdrawTime\",\"type\":\"uint48\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"getDepositInfo\",\"outputs\":[{\"components\":[{\"internalType\":\"uint112\",\"name\":\"deposit\",\"type\":\"uint112\"},{\"internalType\":\"bool\",\"name\":\"staked\",\"type\":\"bool\"},{\"internalType\":\"uint112\",\"name\":\"stake\",\"type\":\"uint112\"},{\"internalType\":\"uint32\",\"name\":\"unstakeDelaySec\",\"type\":\"uint32\"},{\"internalType\":\"uint48\",\"name\":\"withdrawTime\",\"type\":\"uint48\"}],\"internalType\":\"struct IStakeManager.DepositInfo\",\"name\":\"info\",\"type\":\"tuple\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"uint192\",\"name\":\"key\",\"type\":\"uint192\"}],\"name\":\"getNonce\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"initCode\",\"type\":\"bytes\"}],\"name\":\"getSenderAddress\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"initCode\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"callData\",\"type\":\"bytes\"},{\"internalType\":\"uint256\",\"name\":\"callGasLimit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"verificationGasLimit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"preVerificationGas\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxFeePerGas\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxPriorityFeePerGas\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"paymasterAndData\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"signature\",\"type\":\"bytes\"}],\"internalType\":\"struct UserOperation\",\"name\":\"userOp\",\"type\":\"tuple\"}],\"name\":\"getUserOpHash\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"initCode\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"callData\",\"type\":\"bytes\"},{\"internalType\":\"uint256\",\"name\":\"callGasLimit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"verificationGasLimit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"preVerificationGas\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxFeePerGas\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxPriorityFeePerGas\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"paymasterAndData\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"signature\",\"type\":\"bytes\"}],\"internalType\":\"struct UserOperation[]\",\"name\":\"userOps\",\"type\":\"tuple[]\"},{\"internalType\":\"contract IAggregator\",\"name\":\"aggregator\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"signature\",\"type\":\"bytes\"}],\"internalType\":\"struct IEntryPoint.UserOpsPerAggregator[]\",\"name\":\"opsPerAggregator\",\"type\":\"tuple[]\"},{\"internalType\":\"address payable\",\"name\":\"beneficiary\",\"type\":\"address\"}],\"name\":\"handleAggregatedOps\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"initCode\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"callData\",\"type\":\"bytes\"},{\"internalType\":\"uint256\",\"name\":\"callGasLimit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"verificationGasLimit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"preVerificationGas\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxFeePerGas\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxPriorityFeePerGas\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"paymasterAndData\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"signature\",\"type\":\"bytes\"}],\"internalType\":\"struct UserOperation[]\",\"name\":\"ops\",\"type\":\"tuple[]\"},{\"internalType\":\"address payable\",\"name\":\"beneficiary\",\"type\":\"address\"}],\"name\":\"handleOps\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint192\",\"name\":\"key\",\"type\":\"uint192\"}],\"name\":\"incrementNonce\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"callData\",\"type\":\"bytes\"},{\"components\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"callGasLimit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"verificationGasLimit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"preVerificationGas\",\"type\":\"uint256\"},{\"internalType\":\"address\",\"name\":\"paymaster\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"maxFeePerGas\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxPriorityFeePerGas\",\"type\":\"uint256\"}],\"internalType\":\"struct EntryPoint.MemoryUserOp\",\"name\":\"mUserOp\",\"type\":\"tuple\"},{\"internalType\":\"bytes32\",\"name\":\"userOpHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint256\",\"name\":\"prefund\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"contextOffset\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"preOpGas\",\"type\":\"uint256\"}],\"internalType\":\"struct EntryPoint.UserOpInfo\",\"name\":\"opInfo\",\"type\":\"tuple\"},{\"internalType\":\"bytes\",\"name\":\"context\",\"type\":\"bytes\"}],\"name\":\"innerHandleOp\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"actualGasCost\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"uint192\",\"name\":\"\",\"type\":\"uint192\"}],\"name\":\"nonceSequenceNumber\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"initCode\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"callData\",\"type\":\"bytes\"},{\"internalType\":\"uint256\",\"name\":\"callGasLimit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"verificationGasLimit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"preVerificationGas\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxFeePerGas\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxPriorityFeePerGas\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"paymasterAndData\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"signature\",\"type\":\"bytes\"}],\"internalType\":\"struct UserOperation\",\"name\":\"op\",\"type\":\"tuple\"},{\"internalType\":\"address\",\"name\":\"target\",\"type\":\"address\"},{\"internalType\":\"bytes\",\"name\":\"targetCallData\",\"type\":\"bytes\"}],\"name\":\"simulateHandleOp\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"nonce\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"initCode\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"callData\",\"type\":\"bytes\"},{\"internalType\":\"uint256\",\"name\":\"callGasLimit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"verificationGasLimit\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"preVerificationGas\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxFeePerGas\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"maxPriorityFeePerGas\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"paymasterAndData\",\"type\":\"bytes\"},{\"internalType\":\"bytes\",\"name\":\"signature\",\"type\":\"bytes\"}],\"internalType\":\"struct UserOperation\",\"name\":\"userOp\",\"type\":\"tuple\"}],\"name\":\"simulateValidation\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"unlockStake\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address payable\",\"name\":\"withdrawAddress\",\"type\":\"address\"}],\"name\":\"withdrawStake\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address payable\",\"name\":\"withdrawAddress\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"withdrawAmount\",\"type\":\"uint256\"}],\"name\":\"withdrawTo\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"stateMutability\":\"payable\",\"type\":\"receive\"}]" + ); + _factoryContract = new ThirdwebContract( + _client, + _factoryAddress, + _chainId, + "[{\"type\": \"constructor\",\"name\": \"\",\"inputs\": [{\"type\": \"address\",\"name\": \"_defaultAdmin\",\"internalType\": \"address\"},{\"type\": \"address\",\"name\": \"_entrypoint\",\"internalType\": \"contract IEntryPoint\"},{\"type\": \"tuple[]\",\"name\": \"_defaultExtensions\",\"components\": [{\"type\": \"tuple\",\"name\": \"metadata\",\"components\": [{\"internalType\": \"string\",\"name\": \"name\",\"type\": \"string\"},{\"internalType\": \"string\",\"name\": \"metadataURI\",\"type\": \"string\"},{\"internalType\": \"address\",\"name\": \"implementation\",\"type\": \"address\"}],\"internalType\": \"struct IExtension.ExtensionMetadata\"},{\"type\": \"tuple[]\",\"name\": \"functions\",\"components\": [{\"internalType\": \"bytes4\",\"name\": \"functionSelector\",\"type\": \"bytes4\"},{\"internalType\": \"string\",\"name\": \"functionSignature\",\"type\": \"string\"}],\"internalType\": \"struct IExtension.ExtensionFunction[]\"}],\"internalType\": \"struct IExtension.Extension[]\"}],\"outputs\": [],\"stateMutability\": \"nonpayable\"},{\"type\": \"error\",\"name\": \"InvalidCodeAtRange\",\"inputs\": [{\"type\": \"uint256\",\"name\": \"_size\",\"internalType\": \"uint256\"},{\"type\": \"uint256\",\"name\": \"_start\",\"internalType\": \"uint256\"},{\"type\": \"uint256\",\"name\": \"_end\",\"internalType\": \"uint256\"}],\"outputs\": []},{\"type\": \"error\",\"name\": \"WriteError\",\"inputs\": [],\"outputs\": []},{\"type\": \"event\",\"name\": \"AccountCreated\",\"inputs\": [{\"type\": \"address\",\"name\": \"account\",\"indexed\": true,\"internalType\": \"address\"},{\"type\": \"address\",\"name\": \"accountAdmin\",\"indexed\": true,\"internalType\": \"address\"}],\"outputs\": [],\"anonymous\": false},{\"type\": \"event\",\"name\": \"ContractURIUpdated\",\"inputs\": [{\"type\": \"string\",\"name\": \"prevURI\",\"indexed\": false,\"internalType\": \"string\"},{\"type\": \"string\",\"name\": \"newURI\",\"indexed\": false,\"internalType\": \"string\"}],\"outputs\": [],\"anonymous\": false},{\"type\": \"event\",\"name\": \"ExtensionAdded\",\"inputs\": [{\"type\": \"string\",\"name\": \"name\",\"indexed\": true,\"internalType\": \"string\"},{\"type\": \"address\",\"name\": \"implementation\",\"indexed\": true,\"internalType\": \"address\"},{\"type\": \"tuple\",\"name\": \"extension\",\"components\": [{\"type\": \"tuple\",\"name\": \"metadata\",\"components\": [{\"internalType\": \"string\",\"name\": \"name\",\"type\": \"string\"},{\"internalType\": \"string\",\"name\": \"metadataURI\",\"type\": \"string\"},{\"internalType\": \"address\",\"name\": \"implementation\",\"type\": \"address\"}],\"internalType\": \"struct IExtension.ExtensionMetadata\"},{\"type\": \"tuple[]\",\"name\": \"functions\",\"components\": [{\"internalType\": \"bytes4\",\"name\": \"functionSelector\",\"type\": \"bytes4\"},{\"internalType\": \"string\",\"name\": \"functionSignature\",\"type\": \"string\"}],\"internalType\": \"struct IExtension.ExtensionFunction[]\"}],\"indexed\": false,\"internalType\": \"struct IExtension.Extension\"}],\"outputs\": [],\"anonymous\": false},{\"type\": \"event\",\"name\": \"ExtensionRemoved\",\"inputs\": [{\"type\": \"string\",\"name\": \"name\",\"indexed\": true,\"internalType\": \"string\"},{\"type\": \"tuple\",\"name\": \"extension\",\"components\": [{\"type\": \"tuple\",\"name\": \"metadata\",\"components\": [{\"internalType\": \"string\",\"name\": \"name\",\"type\": \"string\"},{\"internalType\": \"string\",\"name\": \"metadataURI\",\"type\": \"string\"},{\"internalType\": \"address\",\"name\": \"implementation\",\"type\": \"address\"}],\"internalType\": \"struct IExtension.ExtensionMetadata\"},{\"type\": \"tuple[]\",\"name\": \"functions\",\"components\": [{\"internalType\": \"bytes4\",\"name\": \"functionSelector\",\"type\": \"bytes4\"},{\"internalType\": \"string\",\"name\": \"functionSignature\",\"type\": \"string\"}],\"internalType\": \"struct IExtension.ExtensionFunction[]\"}],\"indexed\": false,\"internalType\": \"struct IExtension.Extension\"}],\"outputs\": [],\"anonymous\": false},{\"type\": \"event\",\"name\": \"ExtensionReplaced\",\"inputs\": [{\"type\": \"string\",\"name\": \"name\",\"indexed\": true,\"internalType\": \"string\"},{\"type\": \"address\",\"name\": \"implementation\",\"indexed\": true,\"internalType\": \"address\"},{\"type\": \"tuple\",\"name\": \"extension\",\"components\": [{\"type\": \"tuple\",\"name\": \"metadata\",\"components\": [{\"internalType\": \"string\",\"name\": \"name\",\"type\": \"string\"},{\"internalType\": \"string\",\"name\": \"metadataURI\",\"type\": \"string\"},{\"internalType\": \"address\",\"name\": \"implementation\",\"type\": \"address\"}],\"internalType\": \"struct IExtension.ExtensionMetadata\"},{\"type\": \"tuple[]\",\"name\": \"functions\",\"components\": [{\"internalType\": \"bytes4\",\"name\": \"functionSelector\",\"type\": \"bytes4\"},{\"internalType\": \"string\",\"name\": \"functionSignature\",\"type\": \"string\"}],\"internalType\": \"struct IExtension.ExtensionFunction[]\"}],\"indexed\": false,\"internalType\": \"struct IExtension.Extension\"}],\"outputs\": [],\"anonymous\": false},{\"type\": \"event\",\"name\": \"FunctionDisabled\",\"inputs\": [{\"type\": \"string\",\"name\": \"name\",\"indexed\": true,\"internalType\": \"string\"},{\"type\": \"bytes4\",\"name\": \"functionSelector\",\"indexed\": true,\"internalType\": \"bytes4\"},{\"type\": \"tuple\",\"name\": \"extMetadata\",\"components\": [{\"type\": \"string\",\"name\": \"name\",\"internalType\": \"string\"},{\"type\": \"string\",\"name\": \"metadataURI\",\"internalType\": \"string\"},{\"type\": \"address\",\"name\": \"implementation\",\"internalType\": \"address\"}],\"indexed\": false,\"internalType\": \"struct IExtension.ExtensionMetadata\"}],\"outputs\": [],\"anonymous\": false},{\"type\": \"event\",\"name\": \"FunctionEnabled\",\"inputs\": [{\"type\": \"string\",\"name\": \"name\",\"indexed\": true,\"internalType\": \"string\"},{\"type\": \"bytes4\",\"name\": \"functionSelector\",\"indexed\": true,\"internalType\": \"bytes4\"},{\"type\": \"tuple\",\"name\": \"extFunction\",\"components\": [{\"type\": \"bytes4\",\"name\": \"functionSelector\",\"internalType\": \"bytes4\"},{\"type\": \"string\",\"name\": \"functionSignature\",\"internalType\": \"string\"}],\"indexed\": false,\"internalType\": \"struct IExtension.ExtensionFunction\"},{\"type\": \"tuple\",\"name\": \"extMetadata\",\"components\": [{\"type\": \"string\",\"name\": \"name\",\"internalType\": \"string\"},{\"type\": \"string\",\"name\": \"metadataURI\",\"internalType\": \"string\"},{\"type\": \"address\",\"name\": \"implementation\",\"internalType\": \"address\"}],\"indexed\": false,\"internalType\": \"struct IExtension.ExtensionMetadata\"}],\"outputs\": [],\"anonymous\": false},{\"type\": \"event\",\"name\": \"RoleAdminChanged\",\"inputs\": [{\"type\": \"bytes32\",\"name\": \"role\",\"indexed\": true,\"internalType\": \"bytes32\"},{\"type\": \"bytes32\",\"name\": \"previousAdminRole\",\"indexed\": true,\"internalType\": \"bytes32\"},{\"type\": \"bytes32\",\"name\": \"newAdminRole\",\"indexed\": true,\"internalType\": \"bytes32\"}],\"outputs\": [],\"anonymous\": false},{\"type\": \"event\",\"name\": \"RoleGranted\",\"inputs\": [{\"type\": \"bytes32\",\"name\": \"role\",\"indexed\": true,\"internalType\": \"bytes32\"},{\"type\": \"address\",\"name\": \"account\",\"indexed\": true,\"internalType\": \"address\"},{\"type\": \"address\",\"name\": \"sender\",\"indexed\": true,\"internalType\": \"address\"}],\"outputs\": [],\"anonymous\": false},{\"type\": \"event\",\"name\": \"RoleRevoked\",\"inputs\": [{\"type\": \"bytes32\",\"name\": \"role\",\"indexed\": true,\"internalType\": \"bytes32\"},{\"type\": \"address\",\"name\": \"account\",\"indexed\": true,\"internalType\": \"address\"},{\"type\": \"address\",\"name\": \"sender\",\"indexed\": true,\"internalType\": \"address\"}],\"outputs\": [],\"anonymous\": false},{\"type\": \"event\",\"name\": \"SignerAdded\",\"inputs\": [{\"type\": \"address\",\"name\": \"account\",\"indexed\": true,\"internalType\": \"address\"},{\"type\": \"address\",\"name\": \"signer\",\"indexed\": true,\"internalType\": \"address\"}],\"outputs\": [],\"anonymous\": false},{\"type\": \"event\",\"name\": \"SignerRemoved\",\"inputs\": [{\"type\": \"address\",\"name\": \"account\",\"indexed\": true,\"internalType\": \"address\"},{\"type\": \"address\",\"name\": \"signer\",\"indexed\": true,\"internalType\": \"address\"}],\"outputs\": [],\"anonymous\": false},{\"type\": \"fallback\",\"name\": \"\",\"inputs\": [],\"outputs\": [],\"stateMutability\": \"payable\"},{\"type\": \"function\",\"name\": \"DEFAULT_ADMIN_ROLE\",\"inputs\": [],\"outputs\": [{\"type\": \"bytes32\",\"name\": \"\",\"internalType\": \"bytes32\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"_disableFunctionInExtension\",\"inputs\": [{\"type\": \"string\",\"name\": \"_extensionName\",\"internalType\": \"string\"},{\"type\": \"bytes4\",\"name\": \"_functionSelector\",\"internalType\": \"bytes4\"}],\"outputs\": [],\"stateMutability\": \"nonpayable\"},{\"type\": \"function\",\"name\": \"accountImplementation\",\"inputs\": [],\"outputs\": [{\"type\": \"address\",\"name\": \"\",\"internalType\": \"address\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"addExtension\",\"inputs\": [{\"type\": \"tuple\",\"name\": \"_extension\",\"components\": [{\"type\": \"tuple\",\"name\": \"metadata\",\"components\": [{\"internalType\": \"string\",\"name\": \"name\",\"type\": \"string\"},{\"internalType\": \"string\",\"name\": \"metadataURI\",\"type\": \"string\"},{\"internalType\": \"address\",\"name\": \"implementation\",\"type\": \"address\"}],\"internalType\": \"struct IExtension.ExtensionMetadata\"},{\"type\": \"tuple[]\",\"name\": \"functions\",\"components\": [{\"internalType\": \"bytes4\",\"name\": \"functionSelector\",\"type\": \"bytes4\"},{\"internalType\": \"string\",\"name\": \"functionSignature\",\"type\": \"string\"}],\"internalType\": \"struct IExtension.ExtensionFunction[]\"}],\"internalType\": \"struct IExtension.Extension\"}],\"outputs\": [],\"stateMutability\": \"nonpayable\"},{\"type\": \"function\",\"name\": \"contractURI\",\"inputs\": [],\"outputs\": [{\"type\": \"string\",\"name\": \"\",\"internalType\": \"string\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"createAccount\",\"inputs\": [{\"type\": \"address\",\"name\": \"_admin\",\"internalType\": \"address\"},{\"type\": \"bytes\",\"name\": \"_data\",\"internalType\": \"bytes\"}],\"outputs\": [{\"type\": \"address\",\"name\": \"\",\"internalType\": \"address\"}],\"stateMutability\": \"nonpayable\"},{\"type\": \"function\",\"name\": \"defaultExtensions\",\"inputs\": [],\"outputs\": [{\"type\": \"address\",\"name\": \"\",\"internalType\": \"address\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"disableFunctionInExtension\",\"inputs\": [{\"type\": \"string\",\"name\": \"_extensionName\",\"internalType\": \"string\"},{\"type\": \"bytes4\",\"name\": \"_functionSelector\",\"internalType\": \"bytes4\"}],\"outputs\": [],\"stateMutability\": \"nonpayable\"},{\"type\": \"function\",\"name\": \"enableFunctionInExtension\",\"inputs\": [{\"type\": \"string\",\"name\": \"_extensionName\",\"internalType\": \"string\"},{\"type\": \"tuple\",\"name\": \"_function\",\"components\": [{\"type\": \"bytes4\",\"name\": \"functionSelector\",\"internalType\": \"bytes4\"},{\"type\": \"string\",\"name\": \"functionSignature\",\"internalType\": \"string\"}],\"internalType\": \"struct IExtension.ExtensionFunction\"}],\"outputs\": [],\"stateMutability\": \"nonpayable\"},{\"type\": \"function\",\"name\": \"entrypoint\",\"inputs\": [],\"outputs\": [{\"type\": \"address\",\"name\": \"\",\"internalType\": \"address\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"getAccounts\",\"inputs\": [{\"type\": \"uint256\",\"name\": \"_start\",\"internalType\": \"uint256\"},{\"type\": \"uint256\",\"name\": \"_end\",\"internalType\": \"uint256\"}],\"outputs\": [{\"type\": \"address[]\",\"name\": \"accounts\",\"internalType\": \"address[]\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"getAccountsOfSigner\",\"inputs\": [{\"type\": \"address\",\"name\": \"signer\",\"internalType\": \"address\"}],\"outputs\": [{\"type\": \"address[]\",\"name\": \"accounts\",\"internalType\": \"address[]\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"getAddress\",\"inputs\": [{\"type\": \"address\",\"name\": \"_adminSigner\",\"internalType\": \"address\"},{\"type\": \"bytes\",\"name\": \"_data\",\"internalType\": \"bytes\"}],\"outputs\": [{\"type\": \"address\",\"name\": \"\",\"internalType\": \"address\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"getAllAccounts\",\"inputs\": [],\"outputs\": [{\"type\": \"address[]\",\"name\": \"\",\"internalType\": \"address[]\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"getAllExtensions\",\"inputs\": [],\"outputs\": [{\"type\": \"tuple[]\",\"name\": \"allExtensions\",\"components\": [{\"type\": \"tuple\",\"name\": \"metadata\",\"components\": [{\"internalType\": \"string\",\"name\": \"name\",\"type\": \"string\"},{\"internalType\": \"string\",\"name\": \"metadataURI\",\"type\": \"string\"},{\"internalType\": \"address\",\"name\": \"implementation\",\"type\": \"address\"}],\"internalType\": \"struct IExtension.ExtensionMetadata\"},{\"type\": \"tuple[]\",\"name\": \"functions\",\"components\": [{\"internalType\": \"bytes4\",\"name\": \"functionSelector\",\"type\": \"bytes4\"},{\"internalType\": \"string\",\"name\": \"functionSignature\",\"type\": \"string\"}],\"internalType\": \"struct IExtension.ExtensionFunction[]\"}],\"internalType\": \"struct IExtension.Extension[]\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"getExtension\",\"inputs\": [{\"type\": \"string\",\"name\": \"extensionName\",\"internalType\": \"string\"}],\"outputs\": [{\"type\": \"tuple\",\"name\": \"\",\"components\": [{\"type\": \"tuple\",\"name\": \"metadata\",\"components\": [{\"internalType\": \"string\",\"name\": \"name\",\"type\": \"string\"},{\"internalType\": \"string\",\"name\": \"metadataURI\",\"type\": \"string\"},{\"internalType\": \"address\",\"name\": \"implementation\",\"type\": \"address\"}],\"internalType\": \"struct IExtension.ExtensionMetadata\"},{\"type\": \"tuple[]\",\"name\": \"functions\",\"components\": [{\"internalType\": \"bytes4\",\"name\": \"functionSelector\",\"type\": \"bytes4\"},{\"internalType\": \"string\",\"name\": \"functionSignature\",\"type\": \"string\"}],\"internalType\": \"struct IExtension.ExtensionFunction[]\"}],\"internalType\": \"struct IExtension.Extension\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"getImplementationForFunction\",\"inputs\": [{\"type\": \"bytes4\",\"name\": \"_functionSelector\",\"internalType\": \"bytes4\"}],\"outputs\": [{\"type\": \"address\",\"name\": \"\",\"internalType\": \"address\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"getMetadataForFunction\",\"inputs\": [{\"type\": \"bytes4\",\"name\": \"functionSelector\",\"internalType\": \"bytes4\"}],\"outputs\": [{\"type\": \"tuple\",\"name\": \"\",\"components\": [{\"type\": \"string\",\"name\": \"name\",\"internalType\": \"string\"},{\"type\": \"string\",\"name\": \"metadataURI\",\"internalType\": \"string\"},{\"type\": \"address\",\"name\": \"implementation\",\"internalType\": \"address\"}],\"internalType\": \"struct IExtension.ExtensionMetadata\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"getRoleAdmin\",\"inputs\": [{\"type\": \"bytes32\",\"name\": \"role\",\"internalType\": \"bytes32\"}],\"outputs\": [{\"type\": \"bytes32\",\"name\": \"\",\"internalType\": \"bytes32\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"getRoleMember\",\"inputs\": [{\"type\": \"bytes32\",\"name\": \"role\",\"internalType\": \"bytes32\"},{\"type\": \"uint256\",\"name\": \"index\",\"internalType\": \"uint256\"}],\"outputs\": [{\"type\": \"address\",\"name\": \"member\",\"internalType\": \"address\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"getRoleMemberCount\",\"inputs\": [{\"type\": \"bytes32\",\"name\": \"role\",\"internalType\": \"bytes32\"}],\"outputs\": [{\"type\": \"uint256\",\"name\": \"count\",\"internalType\": \"uint256\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"grantRole\",\"inputs\": [{\"type\": \"bytes32\",\"name\": \"role\",\"internalType\": \"bytes32\"},{\"type\": \"address\",\"name\": \"account\",\"internalType\": \"address\"}],\"outputs\": [],\"stateMutability\": \"nonpayable\"},{\"type\": \"function\",\"name\": \"hasRole\",\"inputs\": [{\"type\": \"bytes32\",\"name\": \"role\",\"internalType\": \"bytes32\"},{\"type\": \"address\",\"name\": \"account\",\"internalType\": \"address\"}],\"outputs\": [{\"type\": \"bool\",\"name\": \"\",\"internalType\": \"bool\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"hasRoleWithSwitch\",\"inputs\": [{\"type\": \"bytes32\",\"name\": \"role\",\"internalType\": \"bytes32\"},{\"type\": \"address\",\"name\": \"account\",\"internalType\": \"address\"}],\"outputs\": [{\"type\": \"bool\",\"name\": \"\",\"internalType\": \"bool\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"isRegistered\",\"inputs\": [{\"type\": \"address\",\"name\": \"_account\",\"internalType\": \"address\"}],\"outputs\": [{\"type\": \"bool\",\"name\": \"\",\"internalType\": \"bool\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"multicall\",\"inputs\": [{\"type\": \"bytes[]\",\"name\": \"data\",\"internalType\": \"bytes[]\"}],\"outputs\": [{\"type\": \"bytes[]\",\"name\": \"results\",\"internalType\": \"bytes[]\"}],\"stateMutability\": \"nonpayable\"},{\"type\": \"function\",\"name\": \"onRegister\",\"inputs\": [{\"type\": \"bytes32\",\"name\": \"_salt\",\"internalType\": \"bytes32\"}],\"outputs\": [],\"stateMutability\": \"nonpayable\"},{\"type\": \"function\",\"name\": \"onSignerAdded\",\"inputs\": [{\"type\": \"address\",\"name\": \"_signer\",\"internalType\": \"address\"},{\"type\": \"bytes32\",\"name\": \"_salt\",\"internalType\": \"bytes32\"}],\"outputs\": [],\"stateMutability\": \"nonpayable\"},{\"type\": \"function\",\"name\": \"onSignerRemoved\",\"inputs\": [{\"type\": \"address\",\"name\": \"_signer\",\"internalType\": \"address\"},{\"type\": \"bytes32\",\"name\": \"_salt\",\"internalType\": \"bytes32\"}],\"outputs\": [],\"stateMutability\": \"nonpayable\"},{\"type\": \"function\",\"name\": \"removeExtension\",\"inputs\": [{\"type\": \"string\",\"name\": \"_extensionName\",\"internalType\": \"string\"}],\"outputs\": [],\"stateMutability\": \"nonpayable\"},{\"type\": \"function\",\"name\": \"renounceRole\",\"inputs\": [{\"type\": \"bytes32\",\"name\": \"role\",\"internalType\": \"bytes32\"},{\"type\": \"address\",\"name\": \"account\",\"internalType\": \"address\"}],\"outputs\": [],\"stateMutability\": \"nonpayable\"},{\"type\": \"function\",\"name\": \"replaceExtension\",\"inputs\": [{\"type\": \"tuple\",\"name\": \"_extension\",\"components\": [{\"type\": \"tuple\",\"name\": \"metadata\",\"components\": [{\"internalType\": \"string\",\"name\": \"name\",\"type\": \"string\"},{\"internalType\": \"string\",\"name\": \"metadataURI\",\"type\": \"string\"},{\"internalType\": \"address\",\"name\": \"implementation\",\"type\": \"address\"}],\"internalType\": \"struct IExtension.ExtensionMetadata\"},{\"type\": \"tuple[]\",\"name\": \"functions\",\"components\": [{\"internalType\": \"bytes4\",\"name\": \"functionSelector\",\"type\": \"bytes4\"},{\"internalType\": \"string\",\"name\": \"functionSignature\",\"type\": \"string\"}],\"internalType\": \"struct IExtension.ExtensionFunction[]\"}],\"internalType\": \"struct IExtension.Extension\"}],\"outputs\": [],\"stateMutability\": \"nonpayable\"},{\"type\": \"function\",\"name\": \"revokeRole\",\"inputs\": [{\"type\": \"bytes32\",\"name\": \"role\",\"internalType\": \"bytes32\"},{\"type\": \"address\",\"name\": \"account\",\"internalType\": \"address\"}],\"outputs\": [],\"stateMutability\": \"nonpayable\"},{\"type\": \"function\",\"name\": \"setContractURI\",\"inputs\": [{\"type\": \"string\",\"name\": \"_uri\",\"internalType\": \"string\"}],\"outputs\": [],\"stateMutability\": \"nonpayable\"},{\"type\": \"function\",\"name\": \"totalAccounts\",\"inputs\": [],\"outputs\": [{\"type\": \"uint256\",\"name\": \"\",\"internalType\": \"uint256\"}],\"stateMutability\": \"view\"}]" + ); + var accountAddress = await ThirdwebContract.ReadContract(_factoryContract, "getAddress", await _personalAccount.GetAddress(), new byte[0]); + _accountContract = new ThirdwebContract( + _client, + accountAddress, + _chainId, + "[{\"type\": \"constructor\",\"name\": \"\",\"inputs\": [{\"type\": \"address\",\"name\": \"_defaultAdmin\",\"internalType\": \"address\"},{\"type\": \"address\",\"name\": \"_entrypoint\",\"internalType\": \"contract IEntryPoint\"},{\"type\": \"tuple[]\",\"name\": \"_defaultExtensions\",\"components\": [{\"type\": \"tuple\",\"name\": \"metadata\",\"components\": [{\"internalType\": \"string\",\"name\": \"name\",\"type\": \"string\"},{\"internalType\": \"string\",\"name\": \"metadataURI\",\"type\": \"string\"},{\"internalType\": \"address\",\"name\": \"implementation\",\"type\": \"address\"}],\"internalType\": \"struct IExtension.ExtensionMetadata\"},{\"type\": \"tuple[]\",\"name\": \"functions\",\"components\": [{\"internalType\": \"bytes4\",\"name\": \"functionSelector\",\"type\": \"bytes4\"},{\"internalType\": \"string\",\"name\": \"functionSignature\",\"type\": \"string\"}],\"internalType\": \"struct IExtension.ExtensionFunction[]\"}],\"internalType\": \"struct IExtension.Extension[]\"}],\"outputs\": [],\"stateMutability\": \"nonpayable\"},{\"type\": \"error\",\"name\": \"InvalidCodeAtRange\",\"inputs\": [{\"type\": \"uint256\",\"name\": \"_size\",\"internalType\": \"uint256\"},{\"type\": \"uint256\",\"name\": \"_start\",\"internalType\": \"uint256\"},{\"type\": \"uint256\",\"name\": \"_end\",\"internalType\": \"uint256\"}],\"outputs\": []},{\"type\": \"error\",\"name\": \"WriteError\",\"inputs\": [],\"outputs\": []},{\"type\": \"event\",\"name\": \"AccountCreated\",\"inputs\": [{\"type\": \"address\",\"name\": \"account\",\"indexed\": true,\"internalType\": \"address\"},{\"type\": \"address\",\"name\": \"accountAdmin\",\"indexed\": true,\"internalType\": \"address\"}],\"outputs\": [],\"anonymous\": false},{\"type\": \"event\",\"name\": \"ContractURIUpdated\",\"inputs\": [{\"type\": \"string\",\"name\": \"prevURI\",\"indexed\": false,\"internalType\": \"string\"},{\"type\": \"string\",\"name\": \"newURI\",\"indexed\": false,\"internalType\": \"string\"}],\"outputs\": [],\"anonymous\": false},{\"type\": \"event\",\"name\": \"ExtensionAdded\",\"inputs\": [{\"type\": \"string\",\"name\": \"name\",\"indexed\": true,\"internalType\": \"string\"},{\"type\": \"address\",\"name\": \"implementation\",\"indexed\": true,\"internalType\": \"address\"},{\"type\": \"tuple\",\"name\": \"extension\",\"components\": [{\"type\": \"tuple\",\"name\": \"metadata\",\"components\": [{\"internalType\": \"string\",\"name\": \"name\",\"type\": \"string\"},{\"internalType\": \"string\",\"name\": \"metadataURI\",\"type\": \"string\"},{\"internalType\": \"address\",\"name\": \"implementation\",\"type\": \"address\"}],\"internalType\": \"struct IExtension.ExtensionMetadata\"},{\"type\": \"tuple[]\",\"name\": \"functions\",\"components\": [{\"internalType\": \"bytes4\",\"name\": \"functionSelector\",\"type\": \"bytes4\"},{\"internalType\": \"string\",\"name\": \"functionSignature\",\"type\": \"string\"}],\"internalType\": \"struct IExtension.ExtensionFunction[]\"}],\"indexed\": false,\"internalType\": \"struct IExtension.Extension\"}],\"outputs\": [],\"anonymous\": false},{\"type\": \"event\",\"name\": \"ExtensionRemoved\",\"inputs\": [{\"type\": \"string\",\"name\": \"name\",\"indexed\": true,\"internalType\": \"string\"},{\"type\": \"tuple\",\"name\": \"extension\",\"components\": [{\"type\": \"tuple\",\"name\": \"metadata\",\"components\": [{\"internalType\": \"string\",\"name\": \"name\",\"type\": \"string\"},{\"internalType\": \"string\",\"name\": \"metadataURI\",\"type\": \"string\"},{\"internalType\": \"address\",\"name\": \"implementation\",\"type\": \"address\"}],\"internalType\": \"struct IExtension.ExtensionMetadata\"},{\"type\": \"tuple[]\",\"name\": \"functions\",\"components\": [{\"internalType\": \"bytes4\",\"name\": \"functionSelector\",\"type\": \"bytes4\"},{\"internalType\": \"string\",\"name\": \"functionSignature\",\"type\": \"string\"}],\"internalType\": \"struct IExtension.ExtensionFunction[]\"}],\"indexed\": false,\"internalType\": \"struct IExtension.Extension\"}],\"outputs\": [],\"anonymous\": false},{\"type\": \"event\",\"name\": \"ExtensionReplaced\",\"inputs\": [{\"type\": \"string\",\"name\": \"name\",\"indexed\": true,\"internalType\": \"string\"},{\"type\": \"address\",\"name\": \"implementation\",\"indexed\": true,\"internalType\": \"address\"},{\"type\": \"tuple\",\"name\": \"extension\",\"components\": [{\"type\": \"tuple\",\"name\": \"metadata\",\"components\": [{\"internalType\": \"string\",\"name\": \"name\",\"type\": \"string\"},{\"internalType\": \"string\",\"name\": \"metadataURI\",\"type\": \"string\"},{\"internalType\": \"address\",\"name\": \"implementation\",\"type\": \"address\"}],\"internalType\": \"struct IExtension.ExtensionMetadata\"},{\"type\": \"tuple[]\",\"name\": \"functions\",\"components\": [{\"internalType\": \"bytes4\",\"name\": \"functionSelector\",\"type\": \"bytes4\"},{\"internalType\": \"string\",\"name\": \"functionSignature\",\"type\": \"string\"}],\"internalType\": \"struct IExtension.ExtensionFunction[]\"}],\"indexed\": false,\"internalType\": \"struct IExtension.Extension\"}],\"outputs\": [],\"anonymous\": false},{\"type\": \"event\",\"name\": \"FunctionDisabled\",\"inputs\": [{\"type\": \"string\",\"name\": \"name\",\"indexed\": true,\"internalType\": \"string\"},{\"type\": \"bytes4\",\"name\": \"functionSelector\",\"indexed\": true,\"internalType\": \"bytes4\"},{\"type\": \"tuple\",\"name\": \"extMetadata\",\"components\": [{\"type\": \"string\",\"name\": \"name\",\"internalType\": \"string\"},{\"type\": \"string\",\"name\": \"metadataURI\",\"internalType\": \"string\"},{\"type\": \"address\",\"name\": \"implementation\",\"internalType\": \"address\"}],\"indexed\": false,\"internalType\": \"struct IExtension.ExtensionMetadata\"}],\"outputs\": [],\"anonymous\": false},{\"type\": \"event\",\"name\": \"FunctionEnabled\",\"inputs\": [{\"type\": \"string\",\"name\": \"name\",\"indexed\": true,\"internalType\": \"string\"},{\"type\": \"bytes4\",\"name\": \"functionSelector\",\"indexed\": true,\"internalType\": \"bytes4\"},{\"type\": \"tuple\",\"name\": \"extFunction\",\"components\": [{\"type\": \"bytes4\",\"name\": \"functionSelector\",\"internalType\": \"bytes4\"},{\"type\": \"string\",\"name\": \"functionSignature\",\"internalType\": \"string\"}],\"indexed\": false,\"internalType\": \"struct IExtension.ExtensionFunction\"},{\"type\": \"tuple\",\"name\": \"extMetadata\",\"components\": [{\"type\": \"string\",\"name\": \"name\",\"internalType\": \"string\"},{\"type\": \"string\",\"name\": \"metadataURI\",\"internalType\": \"string\"},{\"type\": \"address\",\"name\": \"implementation\",\"internalType\": \"address\"}],\"indexed\": false,\"internalType\": \"struct IExtension.ExtensionMetadata\"}],\"outputs\": [],\"anonymous\": false},{\"type\": \"event\",\"name\": \"RoleAdminChanged\",\"inputs\": [{\"type\": \"bytes32\",\"name\": \"role\",\"indexed\": true,\"internalType\": \"bytes32\"},{\"type\": \"bytes32\",\"name\": \"previousAdminRole\",\"indexed\": true,\"internalType\": \"bytes32\"},{\"type\": \"bytes32\",\"name\": \"newAdminRole\",\"indexed\": true,\"internalType\": \"bytes32\"}],\"outputs\": [],\"anonymous\": false},{\"type\": \"event\",\"name\": \"RoleGranted\",\"inputs\": [{\"type\": \"bytes32\",\"name\": \"role\",\"indexed\": true,\"internalType\": \"bytes32\"},{\"type\": \"address\",\"name\": \"account\",\"indexed\": true,\"internalType\": \"address\"},{\"type\": \"address\",\"name\": \"sender\",\"indexed\": true,\"internalType\": \"address\"}],\"outputs\": [],\"anonymous\": false},{\"type\": \"event\",\"name\": \"RoleRevoked\",\"inputs\": [{\"type\": \"bytes32\",\"name\": \"role\",\"indexed\": true,\"internalType\": \"bytes32\"},{\"type\": \"address\",\"name\": \"account\",\"indexed\": true,\"internalType\": \"address\"},{\"type\": \"address\",\"name\": \"sender\",\"indexed\": true,\"internalType\": \"address\"}],\"outputs\": [],\"anonymous\": false},{\"type\": \"event\",\"name\": \"SignerAdded\",\"inputs\": [{\"type\": \"address\",\"name\": \"account\",\"indexed\": true,\"internalType\": \"address\"},{\"type\": \"address\",\"name\": \"signer\",\"indexed\": true,\"internalType\": \"address\"}],\"outputs\": [],\"anonymous\": false},{\"type\": \"event\",\"name\": \"SignerRemoved\",\"inputs\": [{\"type\": \"address\",\"name\": \"account\",\"indexed\": true,\"internalType\": \"address\"},{\"type\": \"address\",\"name\": \"signer\",\"indexed\": true,\"internalType\": \"address\"}],\"outputs\": [],\"anonymous\": false},{\"type\": \"fallback\",\"name\": \"\",\"inputs\": [],\"outputs\": [],\"stateMutability\": \"payable\"},{\"type\": \"function\",\"name\": \"DEFAULT_ADMIN_ROLE\",\"inputs\": [],\"outputs\": [{\"type\": \"bytes32\",\"name\": \"\",\"internalType\": \"bytes32\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"_disableFunctionInExtension\",\"inputs\": [{\"type\": \"string\",\"name\": \"_extensionName\",\"internalType\": \"string\"},{\"type\": \"bytes4\",\"name\": \"_functionSelector\",\"internalType\": \"bytes4\"}],\"outputs\": [],\"stateMutability\": \"nonpayable\"},{\"type\": \"function\",\"name\": \"accountImplementation\",\"inputs\": [],\"outputs\": [{\"type\": \"address\",\"name\": \"\",\"internalType\": \"address\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"addExtension\",\"inputs\": [{\"type\": \"tuple\",\"name\": \"_extension\",\"components\": [{\"type\": \"tuple\",\"name\": \"metadata\",\"components\": [{\"internalType\": \"string\",\"name\": \"name\",\"type\": \"string\"},{\"internalType\": \"string\",\"name\": \"metadataURI\",\"type\": \"string\"},{\"internalType\": \"address\",\"name\": \"implementation\",\"type\": \"address\"}],\"internalType\": \"struct IExtension.ExtensionMetadata\"},{\"type\": \"tuple[]\",\"name\": \"functions\",\"components\": [{\"internalType\": \"bytes4\",\"name\": \"functionSelector\",\"type\": \"bytes4\"},{\"internalType\": \"string\",\"name\": \"functionSignature\",\"type\": \"string\"}],\"internalType\": \"struct IExtension.ExtensionFunction[]\"}],\"internalType\": \"struct IExtension.Extension\"}],\"outputs\": [],\"stateMutability\": \"nonpayable\"},{\"type\": \"function\",\"name\": \"contractURI\",\"inputs\": [],\"outputs\": [{\"type\": \"string\",\"name\": \"\",\"internalType\": \"string\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"createAccount\",\"inputs\": [{\"type\": \"address\",\"name\": \"_admin\",\"internalType\": \"address\"},{\"type\": \"bytes\",\"name\": \"_data\",\"internalType\": \"bytes\"}],\"outputs\": [{\"type\": \"address\",\"name\": \"\",\"internalType\": \"address\"}],\"stateMutability\": \"nonpayable\"},{\"type\": \"function\",\"name\": \"defaultExtensions\",\"inputs\": [],\"outputs\": [{\"type\": \"address\",\"name\": \"\",\"internalType\": \"address\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"disableFunctionInExtension\",\"inputs\": [{\"type\": \"string\",\"name\": \"_extensionName\",\"internalType\": \"string\"},{\"type\": \"bytes4\",\"name\": \"_functionSelector\",\"internalType\": \"bytes4\"}],\"outputs\": [],\"stateMutability\": \"nonpayable\"},{\"type\": \"function\",\"name\": \"enableFunctionInExtension\",\"inputs\": [{\"type\": \"string\",\"name\": \"_extensionName\",\"internalType\": \"string\"},{\"type\": \"tuple\",\"name\": \"_function\",\"components\": [{\"type\": \"bytes4\",\"name\": \"functionSelector\",\"internalType\": \"bytes4\"},{\"type\": \"string\",\"name\": \"functionSignature\",\"internalType\": \"string\"}],\"internalType\": \"struct IExtension.ExtensionFunction\"}],\"outputs\": [],\"stateMutability\": \"nonpayable\"},{\"type\": \"function\",\"name\": \"entrypoint\",\"inputs\": [],\"outputs\": [{\"type\": \"address\",\"name\": \"\",\"internalType\": \"address\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"getAccounts\",\"inputs\": [{\"type\": \"uint256\",\"name\": \"_start\",\"internalType\": \"uint256\"},{\"type\": \"uint256\",\"name\": \"_end\",\"internalType\": \"uint256\"}],\"outputs\": [{\"type\": \"address[]\",\"name\": \"accounts\",\"internalType\": \"address[]\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"getAccountsOfSigner\",\"inputs\": [{\"type\": \"address\",\"name\": \"signer\",\"internalType\": \"address\"}],\"outputs\": [{\"type\": \"address[]\",\"name\": \"accounts\",\"internalType\": \"address[]\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"getAddress\",\"inputs\": [{\"type\": \"address\",\"name\": \"_adminSigner\",\"internalType\": \"address\"},{\"type\": \"bytes\",\"name\": \"_data\",\"internalType\": \"bytes\"}],\"outputs\": [{\"type\": \"address\",\"name\": \"\",\"internalType\": \"address\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"getAllAccounts\",\"inputs\": [],\"outputs\": [{\"type\": \"address[]\",\"name\": \"\",\"internalType\": \"address[]\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"getAllExtensions\",\"inputs\": [],\"outputs\": [{\"type\": \"tuple[]\",\"name\": \"allExtensions\",\"components\": [{\"type\": \"tuple\",\"name\": \"metadata\",\"components\": [{\"internalType\": \"string\",\"name\": \"name\",\"type\": \"string\"},{\"internalType\": \"string\",\"name\": \"metadataURI\",\"type\": \"string\"},{\"internalType\": \"address\",\"name\": \"implementation\",\"type\": \"address\"}],\"internalType\": \"struct IExtension.ExtensionMetadata\"},{\"type\": \"tuple[]\",\"name\": \"functions\",\"components\": [{\"internalType\": \"bytes4\",\"name\": \"functionSelector\",\"type\": \"bytes4\"},{\"internalType\": \"string\",\"name\": \"functionSignature\",\"type\": \"string\"}],\"internalType\": \"struct IExtension.ExtensionFunction[]\"}],\"internalType\": \"struct IExtension.Extension[]\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"getExtension\",\"inputs\": [{\"type\": \"string\",\"name\": \"extensionName\",\"internalType\": \"string\"}],\"outputs\": [{\"type\": \"tuple\",\"name\": \"\",\"components\": [{\"type\": \"tuple\",\"name\": \"metadata\",\"components\": [{\"internalType\": \"string\",\"name\": \"name\",\"type\": \"string\"},{\"internalType\": \"string\",\"name\": \"metadataURI\",\"type\": \"string\"},{\"internalType\": \"address\",\"name\": \"implementation\",\"type\": \"address\"}],\"internalType\": \"struct IExtension.ExtensionMetadata\"},{\"type\": \"tuple[]\",\"name\": \"functions\",\"components\": [{\"internalType\": \"bytes4\",\"name\": \"functionSelector\",\"type\": \"bytes4\"},{\"internalType\": \"string\",\"name\": \"functionSignature\",\"type\": \"string\"}],\"internalType\": \"struct IExtension.ExtensionFunction[]\"}],\"internalType\": \"struct IExtension.Extension\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"getImplementationForFunction\",\"inputs\": [{\"type\": \"bytes4\",\"name\": \"_functionSelector\",\"internalType\": \"bytes4\"}],\"outputs\": [{\"type\": \"address\",\"name\": \"\",\"internalType\": \"address\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"getMetadataForFunction\",\"inputs\": [{\"type\": \"bytes4\",\"name\": \"functionSelector\",\"internalType\": \"bytes4\"}],\"outputs\": [{\"type\": \"tuple\",\"name\": \"\",\"components\": [{\"type\": \"string\",\"name\": \"name\",\"internalType\": \"string\"},{\"type\": \"string\",\"name\": \"metadataURI\",\"internalType\": \"string\"},{\"type\": \"address\",\"name\": \"implementation\",\"internalType\": \"address\"}],\"internalType\": \"struct IExtension.ExtensionMetadata\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"getRoleAdmin\",\"inputs\": [{\"type\": \"bytes32\",\"name\": \"role\",\"internalType\": \"bytes32\"}],\"outputs\": [{\"type\": \"bytes32\",\"name\": \"\",\"internalType\": \"bytes32\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"getRoleMember\",\"inputs\": [{\"type\": \"bytes32\",\"name\": \"role\",\"internalType\": \"bytes32\"},{\"type\": \"uint256\",\"name\": \"index\",\"internalType\": \"uint256\"}],\"outputs\": [{\"type\": \"address\",\"name\": \"member\",\"internalType\": \"address\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"getRoleMemberCount\",\"inputs\": [{\"type\": \"bytes32\",\"name\": \"role\",\"internalType\": \"bytes32\"}],\"outputs\": [{\"type\": \"uint256\",\"name\": \"count\",\"internalType\": \"uint256\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"grantRole\",\"inputs\": [{\"type\": \"bytes32\",\"name\": \"role\",\"internalType\": \"bytes32\"},{\"type\": \"address\",\"name\": \"account\",\"internalType\": \"address\"}],\"outputs\": [],\"stateMutability\": \"nonpayable\"},{\"type\": \"function\",\"name\": \"hasRole\",\"inputs\": [{\"type\": \"bytes32\",\"name\": \"role\",\"internalType\": \"bytes32\"},{\"type\": \"address\",\"name\": \"account\",\"internalType\": \"address\"}],\"outputs\": [{\"type\": \"bool\",\"name\": \"\",\"internalType\": \"bool\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"hasRoleWithSwitch\",\"inputs\": [{\"type\": \"bytes32\",\"name\": \"role\",\"internalType\": \"bytes32\"},{\"type\": \"address\",\"name\": \"account\",\"internalType\": \"address\"}],\"outputs\": [{\"type\": \"bool\",\"name\": \"\",\"internalType\": \"bool\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"isRegistered\",\"inputs\": [{\"type\": \"address\",\"name\": \"_account\",\"internalType\": \"address\"}],\"outputs\": [{\"type\": \"bool\",\"name\": \"\",\"internalType\": \"bool\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"multicall\",\"inputs\": [{\"type\": \"bytes[]\",\"name\": \"data\",\"internalType\": \"bytes[]\"}],\"outputs\": [{\"type\": \"bytes[]\",\"name\": \"results\",\"internalType\": \"bytes[]\"}],\"stateMutability\": \"nonpayable\"},{\"type\": \"function\",\"name\": \"onRegister\",\"inputs\": [{\"type\": \"bytes32\",\"name\": \"_salt\",\"internalType\": \"bytes32\"}],\"outputs\": [],\"stateMutability\": \"nonpayable\"},{\"type\": \"function\",\"name\": \"onSignerAdded\",\"inputs\": [{\"type\": \"address\",\"name\": \"_signer\",\"internalType\": \"address\"},{\"type\": \"bytes32\",\"name\": \"_salt\",\"internalType\": \"bytes32\"}],\"outputs\": [],\"stateMutability\": \"nonpayable\"},{\"type\": \"function\",\"name\": \"onSignerRemoved\",\"inputs\": [{\"type\": \"address\",\"name\": \"_signer\",\"internalType\": \"address\"},{\"type\": \"bytes32\",\"name\": \"_salt\",\"internalType\": \"bytes32\"}],\"outputs\": [],\"stateMutability\": \"nonpayable\"},{\"type\": \"function\",\"name\": \"removeExtension\",\"inputs\": [{\"type\": \"string\",\"name\": \"_extensionName\",\"internalType\": \"string\"}],\"outputs\": [],\"stateMutability\": \"nonpayable\"},{\"type\": \"function\",\"name\": \"renounceRole\",\"inputs\": [{\"type\": \"bytes32\",\"name\": \"role\",\"internalType\": \"bytes32\"},{\"type\": \"address\",\"name\": \"account\",\"internalType\": \"address\"}],\"outputs\": [],\"stateMutability\": \"nonpayable\"},{\"type\": \"function\",\"name\": \"replaceExtension\",\"inputs\": [{\"type\": \"tuple\",\"name\": \"_extension\",\"components\": [{\"type\": \"tuple\",\"name\": \"metadata\",\"components\": [{\"internalType\": \"string\",\"name\": \"name\",\"type\": \"string\"},{\"internalType\": \"string\",\"name\": \"metadataURI\",\"type\": \"string\"},{\"internalType\": \"address\",\"name\": \"implementation\",\"type\": \"address\"}],\"internalType\": \"struct IExtension.ExtensionMetadata\"},{\"type\": \"tuple[]\",\"name\": \"functions\",\"components\": [{\"internalType\": \"bytes4\",\"name\": \"functionSelector\",\"type\": \"bytes4\"},{\"internalType\": \"string\",\"name\": \"functionSignature\",\"type\": \"string\"}],\"internalType\": \"struct IExtension.ExtensionFunction[]\"}],\"internalType\": \"struct IExtension.Extension\"}],\"outputs\": [],\"stateMutability\": \"nonpayable\"},{\"type\": \"function\",\"name\": \"revokeRole\",\"inputs\": [{\"type\": \"bytes32\",\"name\": \"role\",\"internalType\": \"bytes32\"},{\"type\": \"address\",\"name\": \"account\",\"internalType\": \"address\"}],\"outputs\": [],\"stateMutability\": \"nonpayable\"},{\"type\": \"function\",\"name\": \"setContractURI\",\"inputs\": [{\"type\": \"string\",\"name\": \"_uri\",\"internalType\": \"string\"}],\"outputs\": [],\"stateMutability\": \"nonpayable\"},{\"type\": \"function\",\"name\": \"totalAccounts\",\"inputs\": [],\"outputs\": [{\"type\": \"uint256\",\"name\": \"\",\"internalType\": \"uint256\"}],\"stateMutability\": \"view\"}]" + ); + } + + public async Task IsDeployed() + { + var code = await ThirdwebRPC.GetRpcInstance(_client, _chainId).SendRequestAsync("eth_getCode", _accountContract.Address, "latest"); + return code != "0x"; + } + + public async Task SendTransaction(TransactionInput transaction) + { + var signedOp = await SignUserOp(transaction); + return await SendUserOp(signedOp); + } + + private async Task GetInitCode() + { + if (await IsDeployed()) + { + return new byte[0]; + } + + var rpc = ThirdwebRPC.GetRpcInstance(_factoryContract.Client, _factoryContract.Chain); + var service = new Contract(null, _factoryContract.Abi, _factoryContract.Address); + var function = service.GetFunction("createAccount"); + var data = function.GetData(await _personalAccount.GetAddress(), new byte[0]); + data = Utils.HexConcat(_factoryAddress, data); + return data.HexToByteArray(); + } + + private async Task SignUserOp(TransactionInput transactionInput, int? requestId = null) + { + requestId ??= 1; + + // Create the user operation and its safe (hexified) version + + var executeFn = new ExecuteFunction + { + Target = transactionInput.To, + Value = transactionInput.Value.Value, + Calldata = transactionInput.Data.HexToByteArray(), + FromAddress = await GetAddress(), + }; + var executeInput = executeFn.CreateTransactionInput(await GetAddress()); + + var fees = await BundlerClient.ThirdwebGetUserOperationGasPrice(_client, _bundlerUrl, requestId); + var maxFee = new HexBigInteger(fees.maxFeePerGas).Value; + var maxPriorityFee = new HexBigInteger(fees.maxPriorityFeePerGas).Value; + + var partialUserOp = new UserOperation() + { + Sender = _accountContract.Address, + Nonce = await GetNonce(), + InitCode = await GetInitCode(), + CallData = executeInput.Data.HexToByteArray(), + CallGasLimit = 0, + VerificationGasLimit = 0, + PreVerificationGas = 0, + MaxFeePerGas = maxFee, + MaxPriorityFeePerGas = maxPriorityFee, + PaymasterAndData = new byte[] { }, + Signature = Constants.DUMMY_SIG.HexToByteArray(), + }; + + // Update paymaster data if any + + partialUserOp.PaymasterAndData = await GetPaymasterAndData(requestId, EncodeUserOperation(partialUserOp)); + + // Estimate gas + + var gasEstimates = await BundlerClient.EthEstimateUserOperationGas(_client, _bundlerUrl, requestId, EncodeUserOperation(partialUserOp), _entryPoint); + partialUserOp.CallGasLimit = 50000 + new HexBigInteger(gasEstimates.CallGasLimit).Value; + partialUserOp.VerificationGasLimit = new HexBigInteger(gasEstimates.VerificationGas).Value; + partialUserOp.PreVerificationGas = new HexBigInteger(gasEstimates.PreVerificationGas).Value; + + // Update paymaster data if any + + partialUserOp.PaymasterAndData = await GetPaymasterAndData(requestId, EncodeUserOperation(partialUserOp)); + + // Hash, sign and encode the user operation + + partialUserOp.Signature = await HashAndSignUserOp(partialUserOp, _entryPointContract); + + return partialUserOp; + } + + private async Task SendUserOp(UserOperation userOperation, int? requestId = null) + { + requestId ??= 1; + + // Send the user operation + + var userOpHash = await BundlerClient.EthSendUserOperation(_client, _bundlerUrl, requestId, EncodeUserOperation(userOperation), _entryPoint); + + // Wait for the transaction to be mined + + string txHash = null; + while (txHash == null) + { + var userOpReceipt = await BundlerClient.EthGetUserOperationReceipt(_client, _bundlerUrl, requestId, userOpHash); + txHash = userOpReceipt?.receipt?.TransactionHash; + await Task.Delay(1000); + } + return txHash; + } + + private async Task GetNonce() + { + var randomBytes = new byte[24]; + RandomNumberGenerator.Fill(randomBytes); + BigInteger randomInt192 = new(randomBytes); + randomInt192 = BigInteger.Abs(randomInt192) % (BigInteger.One << 192); + return await ThirdwebContract.ReadContract(_entryPointContract, "getNonce", await GetAddress(), randomInt192); + } + + private async Task GetPaymasterAndData(object requestId, UserOperationHexified userOp) + { + if (_gasless) + { + var paymasterAndData = await BundlerClient.PMSponsorUserOperation(_client, _paymasterUrl, requestId, userOp, _entryPoint); + return paymasterAndData.paymasterAndData.HexToByteArray(); + } + else + { + return new byte[] { }; + } + } + + private async Task HashAndSignUserOp(UserOperation userOp, ThirdwebContract entryPointContract) + { + var userOpHash = await ThirdwebContract.ReadContract(entryPointContract, "getUserOpHash", userOp); + var sig = await _personalAccount.PersonalSign(userOpHash); + return sig.HexToByteArray(); + } + + private UserOperationHexified EncodeUserOperation(UserOperation userOperation) + { + return new UserOperationHexified() + { + sender = userOperation.Sender, + nonce = userOperation.Nonce.ToHexBigInteger().HexValue, + initCode = userOperation.InitCode.ToHex(true), + callData = userOperation.CallData.ToHex(true), + callGasLimit = userOperation.CallGasLimit.ToHexBigInteger().HexValue, + verificationGasLimit = userOperation.VerificationGasLimit.ToHexBigInteger().HexValue, + preVerificationGas = userOperation.PreVerificationGas.ToHexBigInteger().HexValue, + maxFeePerGas = userOperation.MaxFeePerGas.ToHexBigInteger().HexValue, + maxPriorityFeePerGas = userOperation.MaxPriorityFeePerGas.ToHexBigInteger().HexValue, + paymasterAndData = userOperation.PaymasterAndData.ToHex(true), + signature = userOperation.Signature.ToHex(true) + }; + } + + public Task GetAddress() + { + return Task.FromResult(_accountContract.Address); + } + + public Task EthSign(string message) + { + return _personalAccount.EthSign(message); + } + + public Task PersonalSign(byte[] rawMessage) + { + return _personalAccount.PersonalSign(rawMessage); + } + + public Task PersonalSign(string message) + { + return _personalAccount.PersonalSign(message); + } + + public Task SignTypedDataV4(string json) + { + return _personalAccount.SignTypedDataV4(json); + } + + public Task SignTypedDataV4(T data, TypedData typedData) + { + throw new NotImplementedException(); + } + + public Task SignTransaction(TransactionInput transaction, BigInteger chainId) + { + return _personalAccount.SignTransaction(transaction, chainId); + } + + public Task IsConnected() + { + return Task.FromResult(_accountContract != null); + } + + public Task Disconnect() + { + return _personalAccount.Disconnect(); + } + } +} diff --git a/Thirdweb/Thirdweb.Wallets/SmartAccount/Thirdweb.AccountAbstraction/AATypes.cs b/Thirdweb/Thirdweb.Wallets/SmartAccount/Thirdweb.AccountAbstraction/AATypes.cs new file mode 100644 index 0000000..fe0bd2e --- /dev/null +++ b/Thirdweb/Thirdweb.Wallets/SmartAccount/Thirdweb.AccountAbstraction/AATypes.cs @@ -0,0 +1,117 @@ +using System.Numerics; +using Nethereum.ABI.FunctionEncoding.Attributes; +using Nethereum.Contracts; +using Nethereum.RPC.Eth.DTOs; + +namespace Thirdweb.AccountAbstraction +{ + public class UserOperation + { + [Parameter("address", "sender", 1)] + public virtual string Sender { get; set; } + + [Parameter("uint256", "nonce", 2)] + public virtual BigInteger Nonce { get; set; } + + [Parameter("bytes", "initCode", 3)] + public virtual byte[] InitCode { get; set; } + + [Parameter("bytes", "callData", 4)] + public virtual byte[] CallData { get; set; } + + [Parameter("uint256", "callGasLimit", 5)] + public virtual BigInteger CallGasLimit { get; set; } + + [Parameter("uint256", "verificationGasLimit", 6)] + public virtual BigInteger VerificationGasLimit { get; set; } + + [Parameter("uint256", "preVerificationGas", 7)] + public virtual BigInteger PreVerificationGas { get; set; } + + [Parameter("uint256", "maxFeePerGas", 8)] + public virtual BigInteger MaxFeePerGas { get; set; } + + [Parameter("uint256", "maxPriorityFeePerGas", 9)] + public virtual BigInteger MaxPriorityFeePerGas { get; set; } + + [Parameter("bytes", "paymasterAndData", 10)] + public virtual byte[] PaymasterAndData { get; set; } + + [Parameter("bytes", "signature", 11)] + public virtual byte[] Signature { get; set; } + } + + public class UserOperationHexified + { + public string sender { get; set; } + public string nonce { get; set; } + public string initCode { get; set; } + public string callData { get; set; } + public string callGasLimit { get; set; } + public string verificationGasLimit { get; set; } + public string preVerificationGas { get; set; } + public string maxFeePerGas { get; set; } + public string maxPriorityFeePerGas { get; set; } + public string paymasterAndData { get; set; } + public string signature { get; set; } + } + + [Function("execute")] + public class ExecuteFunction : FunctionMessage + { + [Parameter("address", "_target", 1)] + public virtual string Target { get; set; } + + [Parameter("uint256", "_value", 2)] + public virtual BigInteger Value { get; set; } + + [Parameter("bytes", "_calldata", 3)] + public virtual byte[] Calldata { get; set; } + } + + [Function("createAccount", "address")] + public class CreateAccountFunction : FunctionMessage + { + [Parameter("address", "_admin", 1)] + public virtual string Admin { get; set; } + + [Parameter("bytes", "_data", 2)] + public virtual byte[] Data { get; set; } + } + + public class EthEstimateUserOperationGasResponse + { + public string PreVerificationGas { get; set; } + public string VerificationGas { get; set; } + public string CallGasLimit { get; set; } + } + + public class EthGetUserOperationByHashResponse + { + public string entryPoint { get; set; } + public string transactionHash { get; set; } + public string blockHash { get; set; } + public string blockNumber { get; set; } + } + + public class EthGetUserOperationReceiptResponse + { + public TransactionReceipt receipt { get; set; } + } + + public class EntryPointWrapper + { + public string entryPoint { get; set; } + } + + public class PMSponsorOperationResponse + { + public string paymasterAndData { get; set; } + } + + public class ThirdwebGetUserOperationGasPriceResponse + { + public string maxFeePerGas { get; set; } + public string maxPriorityFeePerGas { get; set; } + } +} diff --git a/Thirdweb/Thirdweb.Wallets/SmartAccount/Thirdweb.AccountAbstraction/BundlerClient.cs b/Thirdweb/Thirdweb.Wallets/SmartAccount/Thirdweb.AccountAbstraction/BundlerClient.cs new file mode 100644 index 0000000..30615c5 --- /dev/null +++ b/Thirdweb/Thirdweb.Wallets/SmartAccount/Thirdweb.AccountAbstraction/BundlerClient.cs @@ -0,0 +1,115 @@ +using System; +using System.Net.Http; +using System.Threading.Tasks; +using Nethereum.JsonRpc.Client.RpcMessages; +using Newtonsoft.Json; + +namespace Thirdweb.AccountAbstraction +{ + public static class BundlerClient + { + // Bundler requests + + public static async Task EthGetUserOperationByHash(ThirdwebClient client, string bundlerUrl, object requestId, string userOpHash) + { + var response = await BundlerRequest(client, bundlerUrl, requestId, "eth_getUserOperationByHash", userOpHash); + return JsonConvert.DeserializeObject(response.Result.ToString()); + } + + public static async Task EthGetUserOperationReceipt(ThirdwebClient client, string bundlerUrl, object requestId, string userOpHash) + { + var response = await BundlerRequest(client, bundlerUrl, requestId, "eth_getUserOperationReceipt", userOpHash); + return JsonConvert.DeserializeObject(response.Result.ToString()); + } + + public static async Task EthSendUserOperation(ThirdwebClient client, string bundlerUrl, object requestId, UserOperationHexified userOp, string entryPoint) + { + var response = await BundlerRequest(client, bundlerUrl, requestId, "eth_sendUserOperation", userOp, entryPoint); + return response.Result.ToString(); + } + + public static async Task EthEstimateUserOperationGas( + ThirdwebClient client, + string bundlerUrl, + object requestId, + UserOperationHexified userOp, + string entryPoint + ) + { + var response = await BundlerRequest(client, bundlerUrl, requestId, "eth_estimateUserOperationGas", userOp, entryPoint); + return JsonConvert.DeserializeObject(response.Result.ToString()); + } + + public static async Task ThirdwebGetUserOperationGasPrice(ThirdwebClient client, string bundlerUrl, object requestId) + { + var response = await BundlerRequest(client, bundlerUrl, requestId, "thirdweb_getUserOperationGasPrice"); + return JsonConvert.DeserializeObject(response.Result.ToString()); + } + + // Paymaster requests + + public static async Task PMSponsorUserOperation(ThirdwebClient client, string paymasterUrl, object requestId, UserOperationHexified userOp, string entryPoint) + { + var response = await BundlerRequest(client, paymasterUrl, requestId, "pm_sponsorUserOperation", userOp, new EntryPointWrapper() { entryPoint = entryPoint }); + try + { + return JsonConvert.DeserializeObject(response.Result.ToString()); + } + catch + { + return new PMSponsorOperationResponse() { paymasterAndData = response.Result.ToString() }; + } + } + + // Request + + private static async Task BundlerRequest(ThirdwebClient client, string url, object requestId, string method, params object[] args) + { + using var httpClient = new HttpClient(); +#if DEBUG + Console.WriteLine($"Bundler Request: {method}({JsonConvert.SerializeObject(args)}"); +#endif + var requestMessage = new RpcRequestMessage(requestId, method, args); + var requestMessageJson = JsonConvert.SerializeObject(requestMessage); + + var httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, url) { Content = new StringContent(requestMessageJson, System.Text.Encoding.UTF8, "application/json") }; + if (new Uri(url).Host.EndsWith(".thirdweb.com")) + { + httpRequestMessage.Headers.Add("x-sdk-name", "Thirdweb.NET"); + httpRequestMessage.Headers.Add("x-sdk-os", System.Runtime.InteropServices.RuntimeInformation.OSDescription); + httpRequestMessage.Headers.Add("x-sdk-platform", "dotnet"); + httpRequestMessage.Headers.Add("x-sdk-version", Constants.VERSION); + if (!string.IsNullOrEmpty(client.ClientId)) + { + httpRequestMessage.Headers.Add("x-client-id", client.ClientId); + } + + if (!string.IsNullOrEmpty(client.SecretKey)) + { + httpRequestMessage.Headers.Add("x-secret-key", client.SecretKey); + } + + if (!string.IsNullOrEmpty(client.BundleId)) + { + httpRequestMessage.Headers.Add("x-bundle-id", client.BundleId); + } + } + + var httpResponse = await httpClient.SendAsync(httpRequestMessage); + + if (!httpResponse.IsSuccessStatusCode) + { + throw new Exception($"Bundler Request Failed. Error: {httpResponse.StatusCode} - {httpResponse.ReasonPhrase} - {await httpResponse.Content.ReadAsStringAsync()}"); + } + + var httpResponseJson = await httpResponse.Content.ReadAsStringAsync(); + +#if DEBUG + Console.WriteLine($"Bundler Response: {httpResponseJson}"); +#endif + + var response = JsonConvert.DeserializeObject(httpResponseJson); + return response.Error != null ? throw new Exception($"Bundler Request Failed. Error: {response.Error.Code} - {response.Error.Message} - {response.Error.Data}") : response; + } + } +} diff --git a/Thirdweb/Thirdweb.Wallets/Thirdweb.Wallets.Embedded/Embedded.cs b/Thirdweb/Thirdweb.Wallets/Thirdweb.Wallets.Embedded/Embedded.cs deleted file mode 100644 index 7192bdc..0000000 --- a/Thirdweb/Thirdweb.Wallets/Thirdweb.Wallets.Embedded/Embedded.cs +++ /dev/null @@ -1,209 +0,0 @@ -using System.Numerics; -using System.Text; -using Nethereum.ABI.EIP712; -using Nethereum.Hex.HexConvertors.Extensions; -using Nethereum.Hex.HexTypes; -using Nethereum.Model; -using Nethereum.RPC.Eth.DTOs; -using Nethereum.RPC.Eth.Mappers; -using Nethereum.Signer; -using Nethereum.Signer.EIP712; -using Thirdweb; -using Thirdweb.EWS; - -public class Embedded : IWallet -{ - EmbeddedWallet _embeddedWallet; - User _user; - EthECKey _ecKey; - string _email; - - internal Embedded(ThirdwebClient client, string email) - { - if (string.IsNullOrEmpty(email)) - { - throw new ArgumentException("Email must be provided to use Embedded Wallets."); - } - - _embeddedWallet = new EmbeddedWallet(client); - _email = email; - } - - public async Task Initialize() - { - try - { - _user = await _embeddedWallet.GetUserAsync(_email, "EmailOTP"); - _ecKey = new EthECKey(_user.Account.PrivateKey); - } - catch - { - Console.WriteLine("User not found. Please call Embedded.LoginWithOTP() to create a new user."); - _user = null; - _ecKey = null; - } - } - - #region Email OTP Flow - - public async Task SendOTP() - { - if (string.IsNullOrEmpty(_email)) - { - throw new Exception("Email is required for OTP login"); - } - - try - { - (bool isNewUser, bool isNewDevice, bool needsRecoveryCode) = await _embeddedWallet.SendOtpEmailAsync(_email); - Console.WriteLine("OTP sent to email. Please call SubmitOTP to login."); - } - catch (Exception e) - { - throw new Exception("Failed to send OTP email", e); - } - } - - public async Task<(string, bool)> SubmitOTP(string otp) - { - var res = await _embeddedWallet.VerifyOtpAsync(_email, otp, null); - if (res.User == null) - { - bool canRetry = res.CanRetry; - if (canRetry) - { - Console.WriteLine("Invalid OTP. Please try again."); - } - else - { - Console.WriteLine("Invalid OTP. Please request a new OTP."); - } - return (null, canRetry); - } - else - { - _user = res.User; - _ecKey = new EthECKey(_user.Account.PrivateKey); - return (GetAddress(), false); - } - } - - #endregion - - public string GetAddress() - { - return _ecKey.GetPublicAddress(); - } - - public string EthSign(string message) - { - if (message == null) - { - throw new ArgumentNullException(nameof(message), "Message to sign cannot be null."); - } - - var signer = new MessageSigner(); - var signature = signer.Sign(Encoding.UTF8.GetBytes(message), _ecKey); - return signature; - } - - public string PersonalSign(string message) - { - if (message == null) - { - throw new ArgumentNullException(nameof(message), "Message to sign cannot be null."); - } - - var signer = new EthereumMessageSigner(); - var signature = signer.EncodeUTF8AndSign(message, _ecKey); - return signature; - } - - public string SignTypedDataV4(string json) - { - if (json == null) - { - throw new ArgumentNullException(nameof(json), "Json to sign cannot be null."); - } - - var signer = new Eip712TypedDataSigner(); - var signature = signer.SignTypedDataV4(json, _ecKey); - return signature; - } - - public string SignTypedDataV4(T data, TypedData typedData) - { - if (data == null) - { - throw new ArgumentNullException(nameof(data), "Data to sign cannot be null."); - } - - var signer = new Eip712TypedDataSigner(); - var signature = signer.SignTypedDataV4(data, typedData, _ecKey); - return signature; - } - - public string SignTransaction(TransactionInput transaction, BigInteger chainId) - { - if (transaction == null) - { - throw new ArgumentNullException(nameof(transaction)); - } - - if (string.IsNullOrWhiteSpace(transaction.From)) - { - transaction.From = GetAddress(); - } - else if (transaction.From != GetAddress()) - { - throw new Exception("Transaction 'From' address does not match the wallet address"); - } - - var nonce = transaction.Nonce ?? throw new ArgumentNullException(nameof(transaction), "Transaction nonce has not been set"); - - var gasLimit = transaction.Gas; - var value = transaction.Value ?? new HexBigInteger(0); - - string signedTransaction; - if (transaction.Type != null && transaction.Type.Value == TransactionType.EIP1559.AsByte()) - { - var maxPriorityFeePerGas = transaction.MaxPriorityFeePerGas.Value; - var maxFeePerGas = transaction.MaxFeePerGas.Value; - var transaction1559 = new Transaction1559( - chainId, - nonce, - maxPriorityFeePerGas, - maxFeePerGas, - gasLimit, - transaction.To, - value, - transaction.Data, - transaction.AccessList.ToSignerAccessListItemArray() - ); - - var signer = new Transaction1559Signer(); - signer.SignTransaction(_ecKey, transaction1559); - signedTransaction = transaction1559.GetRLPEncoded().ToHex(); - } - else - { - var gasPrice = transaction.GasPrice; - var legacySigner = new LegacyTransactionSigner(); - signedTransaction = legacySigner.SignTransaction(_ecKey.GetPrivateKey(), chainId, transaction.To, value.Value, nonce, gasPrice.Value, gasLimit.Value, transaction.Data); - } - - return "0x" + signedTransaction; - } - - public bool IsConnected() - { - return _ecKey != null; - } - - public async Task Disconnect() - { - await _embeddedWallet.SignOutAsync(); - _user = null; - _ecKey = null; - } -} diff --git a/Thirdweb/Thirdweb.Wallets/Thirdweb.Wallets.PrivateKey/PrivateKey.cs b/Thirdweb/Thirdweb.Wallets/Thirdweb.Wallets.PrivateKey/PrivateKey.cs deleted file mode 100644 index 1e24421..0000000 --- a/Thirdweb/Thirdweb.Wallets/Thirdweb.Wallets.PrivateKey/PrivateKey.cs +++ /dev/null @@ -1,148 +0,0 @@ -using System.Numerics; -using System.Text; -using Nethereum.ABI.EIP712; -using Nethereum.Hex.HexConvertors.Extensions; -using Nethereum.Hex.HexTypes; -using Nethereum.Model; -using Nethereum.RPC.Eth.DTOs; -using Nethereum.RPC.Eth.Mappers; -using Nethereum.Signer; -using Nethereum.Signer.EIP712; -using Thirdweb; - -public class PrivateKey : IWallet -{ - private EthECKey _ecKey; - - internal PrivateKey(string privateKeyHex) - { - if (string.IsNullOrEmpty(privateKeyHex)) - { - throw new ArgumentNullException(nameof(privateKeyHex), "Private key cannot be null or empty."); - } - - _ecKey = new EthECKey(privateKeyHex); - } - - public Task Initialize() - { - // No initialization required for private key wallets - return Task.CompletedTask; - } - - public string GetAddress() - { - return _ecKey.GetPublicAddress(); - } - - public string EthSign(string message) - { - if (message == null) - { - throw new ArgumentNullException(nameof(message), "Message to sign cannot be null."); - } - - var signer = new MessageSigner(); - var signature = signer.Sign(Encoding.UTF8.GetBytes(message), _ecKey); - return signature; - } - - public string PersonalSign(string message) - { - if (message == null) - { - throw new ArgumentNullException(nameof(message), "Message to sign cannot be null."); - } - - var signer = new EthereumMessageSigner(); - var signature = signer.EncodeUTF8AndSign(message, _ecKey); - return signature; - } - - public string SignTypedDataV4(string json) - { - if (json == null) - { - throw new ArgumentNullException(nameof(json), "Json to sign cannot be null."); - } - - var signer = new Eip712TypedDataSigner(); - var signature = signer.SignTypedDataV4(json, _ecKey); - return signature; - } - - public string SignTypedDataV4(T data, TypedData typedData) - { - if (data == null) - { - throw new ArgumentNullException(nameof(data), "Data to sign cannot be null."); - } - - var signer = new Eip712TypedDataSigner(); - var signature = signer.SignTypedDataV4(data, typedData, _ecKey); - return signature; - } - - public string SignTransaction(TransactionInput transaction, BigInteger chainId) - { - if (transaction == null) - { - throw new ArgumentNullException(nameof(transaction)); - } - - if (string.IsNullOrWhiteSpace(transaction.From)) - { - transaction.From = GetAddress(); - } - else if (transaction.From != GetAddress()) - { - throw new Exception("Transaction 'From' address does not match the wallet address"); - } - - var nonce = transaction.Nonce ?? throw new ArgumentNullException(nameof(transaction), "Transaction nonce has not been set"); - - var gasLimit = transaction.Gas; - var value = transaction.Value ?? new HexBigInteger(0); - - string signedTransaction; - if (transaction.Type != null && transaction.Type.Value == TransactionType.EIP1559.AsByte()) - { - var maxPriorityFeePerGas = transaction.MaxPriorityFeePerGas.Value; - var maxFeePerGas = transaction.MaxFeePerGas.Value; - var transaction1559 = new Transaction1559( - chainId, - nonce, - maxPriorityFeePerGas, - maxFeePerGas, - gasLimit, - transaction.To, - value, - transaction.Data, - transaction.AccessList.ToSignerAccessListItemArray() - ); - - var signer = new Transaction1559Signer(); - signer.SignTransaction(_ecKey, transaction1559); - signedTransaction = transaction1559.GetRLPEncoded().ToHex(); - } - else - { - var gasPrice = transaction.GasPrice; - var legacySigner = new LegacyTransactionSigner(); - signedTransaction = legacySigner.SignTransaction(_ecKey.GetPrivateKey(), chainId, transaction.To, value.Value, nonce, gasPrice.Value, gasLimit.Value, transaction.Data); - } - - return "0x" + signedTransaction; - } - - public bool IsConnected() - { - return _ecKey != null; - } - - public Task Disconnect() - { - _ecKey = null; - return Task.CompletedTask; - } -} diff --git a/Thirdweb/Thirdweb.Wallets/ThirdwebAccount.cs b/Thirdweb/Thirdweb.Wallets/ThirdwebAccount.cs deleted file mode 100644 index e4110e0..0000000 --- a/Thirdweb/Thirdweb.Wallets/ThirdwebAccount.cs +++ /dev/null @@ -1,75 +0,0 @@ -using System.Numerics; -using Nethereum.ABI.EIP712; -using Nethereum.RPC.Eth.DTOs; - -namespace Thirdweb -{ - public class ThirdwebAccount - { - internal ThirdwebAccountOptions Options { get; private set; } - - public IWallet Wallet { get; private set; } - - public ThirdwebAccount(ThirdwebAccountOptions options) - { - if (options.Client == null) - { - throw new ArgumentException("Client must be provided"); - } - - Options = options; - - Wallet = Options.Type switch - { - WalletType.PrivateKey => new PrivateKey(Options.PrivateKey), - WalletType.Embedded => new Embedded(Options.Client, Options.Email), - _ => throw new ArgumentException("Invalid wallet type"), - }; - } - - public async Task Initialize() - { - await Wallet.Initialize(); - } - - public string GetAddress() - { - return Wallet.GetAddress(); - } - - public string EthSign(string message) - { - return Wallet.EthSign(message); - } - - public string PersonalSign(string message) - { - return Wallet.PersonalSign(message); - } - - public string SignTypedDataV4(string json) - { - return Wallet.SignTypedDataV4(json); - } - - public string SignTypedDataV4(T data, TypedData typedData) - { - return Wallet.SignTypedDataV4(data, typedData); - } - - public string SignTransaction(TransactionInput transaction, BigInteger chainId) - { - return Wallet.SignTransaction(transaction, chainId); - } - - public bool IsConnected() - { - return Wallet.IsConnected(); - } - - public async Task Disconnect() - { - await Wallet.Disconnect(); - } - } -} diff --git a/Thirdweb/Thirdweb.Wallets/ThirdwebAccountOptions.cs b/Thirdweb/Thirdweb.Wallets/ThirdwebAccountOptions.cs deleted file mode 100644 index e09c9d7..0000000 --- a/Thirdweb/Thirdweb.Wallets/ThirdwebAccountOptions.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System.Numerics; - -namespace Thirdweb -{ - public class ThirdwebAccountOptions - { - internal ThirdwebClient Client { get; private set; } - internal WalletType Type { get; private set; } - internal string Email { get; private set; } - internal string PrivateKey { get; private set; } - - public ThirdwebAccountOptions(ThirdwebClient client, WalletType type, string email = null, string privateKey = null) - { - Client = client; - Type = type; - Email = email; - PrivateKey = privateKey; - } - } -} diff --git a/Thirdweb/Thirdweb.Wallets/ThirdwebWallet.cs b/Thirdweb/Thirdweb.Wallets/ThirdwebWallet.cs new file mode 100644 index 0000000..460d86b --- /dev/null +++ b/Thirdweb/Thirdweb.Wallets/ThirdwebWallet.cs @@ -0,0 +1,96 @@ +using System.Numerics; +using Nethereum.ABI.EIP712; +using Nethereum.RPC.Eth.DTOs; + +namespace Thirdweb +{ + public class ThirdwebWallet + { + public Dictionary Accounts { get; } + public IThirdwebAccount ActiveAccount { get; private set; } + + public ThirdwebWallet() + { + Accounts = new Dictionary(); + ActiveAccount = null; + } + + public async Task Initialize(List accounts) + { + if (accounts.Count == 0) + { + throw new ArgumentException("At least one account must be provided."); + } + + for (var i = 0; i < accounts.Count; i++) + { + if (!await accounts[i].IsConnected()) + { + throw new InvalidOperationException($"Account at index {i} is not connected."); + } + } + + foreach (var account in accounts) + { + Accounts.Add(await account.GetAddress(), account); + } + + SetActive(Accounts.Keys.First()); + } + + public void SetActive(string address) + { + if (!Accounts.ContainsKey(address)) + { + throw new ArgumentException($"Account with address {address} not found."); + } + + ActiveAccount = Accounts[address]; + } + + public async Task GetAddress() + { + return await ActiveAccount.GetAddress(); + } + + public async Task EthSign(string message) + { + return await ActiveAccount.EthSign(message); + } + + public async Task PersonalSign(string message) + { + return await ActiveAccount.PersonalSign(message); + } + + public async Task SignTypedDataV4(string json) + { + return await ActiveAccount.SignTypedDataV4(json); + } + + public async Task SignTypedDataV4(T data, TypedData typedData) + { + return await ActiveAccount.SignTypedDataV4(data, typedData); + } + + public async Task SignTransaction(TransactionInput transaction, BigInteger chainId) + { + return await ActiveAccount.SignTransaction(transaction, chainId); + } + + public async Task IsConnected() + { + return await ActiveAccount.IsConnected(); + } + + public async Task Disconnect() + { + foreach (var account in Accounts.Values) + { + await account.Disconnect(); + } + + ActiveAccount = null; + } + } +} diff --git a/Thirdweb/Thirdweb.Wallets/WalletType.cs b/Thirdweb/Thirdweb.Wallets/WalletType.cs deleted file mode 100644 index b84aabb..0000000 --- a/Thirdweb/Thirdweb.Wallets/WalletType.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Thirdweb -{ - public enum WalletType - { - PrivateKey, - Embedded, - } -}