From 798f4a82a40fa27abcd2fa560ee1c4c18a2ccbcf Mon Sep 17 00:00:00 2001
From: 0xFirekeeper <0xFirekeeper@gmail.com>
Date: Sat, 30 Mar 2024 02:44:06 +0300
Subject: [PATCH] PrivateKeyWallet
---
Directory.Build.props | 2 +-
Directory.Packages.props | 3 +
Thirdweb.Console/Program.cs | 37 +++--
.../Thirdweb.Contracts/ThirdwebContract.cs | 36 +++++
Thirdweb/Thirdweb.RPC/ThirdwebRPC.cs | 9 +-
Thirdweb/Thirdweb.Wallets/IWallet.cs | 16 +++
.../EmbeddedWallet.cs | 7 +
.../PrivateKeyWallet.cs | 131 ++++++++++++++++++
Thirdweb/Thirdweb.Wallets/ThirdwebAccount.cs | 64 +++++++++
.../ThirdwebAccountOptions.cs | 18 +++
Thirdweb/Thirdweb.Wallets/WalletType.cs | 8 ++
Thirdweb/Thirdweb.csproj | 3 +
12 files changed, 323 insertions(+), 11 deletions(-)
create mode 100644 Thirdweb/Thirdweb.Wallets/IWallet.cs
create mode 100644 Thirdweb/Thirdweb.Wallets/Thirdweb.Wallets.EmbeddedWallet/EmbeddedWallet.cs
create mode 100644 Thirdweb/Thirdweb.Wallets/Thirdweb.Wallets.PrivateKeyWallet/PrivateKeyWallet.cs
create mode 100644 Thirdweb/Thirdweb.Wallets/ThirdwebAccount.cs
create mode 100644 Thirdweb/Thirdweb.Wallets/ThirdwebAccountOptions.cs
create mode 100644 Thirdweb/Thirdweb.Wallets/WalletType.cs
diff --git a/Directory.Build.props b/Directory.Build.props
index 5968e5f..e120acd 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -2,7 +2,7 @@
0.0.1
- netstandard2.0;netstandard2.1;net6.0;net7.0;
+ netstandard2.1;net6.0;net7.0;
latest
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 0da372c..5759a8f 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -13,5 +13,8 @@
+
+
+
\ No newline at end of file
diff --git a/Thirdweb.Console/Program.cs b/Thirdweb.Console/Program.cs
index d501da1..d937303 100644
--- a/Thirdweb.Console/Program.cs
+++ b/Thirdweb.Console/Program.cs
@@ -1,24 +1,45 @@
-using dotenv.net;
+using System.Numerics;
+using dotenv.net;
+using Nethereum.Hex.HexTypes;
using Newtonsoft.Json;
using Thirdweb;
DotEnv.Load();
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 rpc = ThirdwebRPC.GetRpcInstance(client, 1);
-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: "0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D", chain: 1, abi: "function name() view returns (string)");
+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}");
-Console.ReadLine();
+var accountOptions = new ThirdwebAccountOptions(client: client, type: WalletType.PrivateKey, privateKey: privateKey);
+var account = new ThirdwebAccount(accountOptions);
+Console.WriteLine($"Wallet address: {account.GetAddress()}");
+
+var message = "Hello, Thirdweb!";
+var signature = account.PersonalSign(message);
+Console.WriteLine($"Signed message: {signature}");
+
+var balanceBefore = await ThirdwebContract.ReadContract(contract, "balanceOf", account.GetAddress());
+Console.WriteLine($"Balance before mint: {balanceBefore}");
+
+var writeResult = await ThirdwebContract.WriteContract(account, contract, "mintTo", account.GetAddress(), 100);
+Console.WriteLine($"Contract write result: {writeResult}");
+
+var balanceAfter = await ThirdwebContract.ReadContract(contract, "balanceOf", account.GetAddress());
+Console.WriteLine($"Balance after mint: {balanceAfter}");
diff --git a/Thirdweb/Thirdweb.Contracts/ThirdwebContract.cs b/Thirdweb/Thirdweb.Contracts/ThirdwebContract.cs
index 5a3f857..aaa55a0 100644
--- a/Thirdweb/Thirdweb.Contracts/ThirdwebContract.cs
+++ b/Thirdweb/Thirdweb.Contracts/ThirdwebContract.cs
@@ -1,4 +1,6 @@
using System.Numerics;
+using Nethereum.Hex.HexTypes;
+using Nethereum.RPC.Eth.DTOs;
namespace Thirdweb
{
@@ -40,5 +42,39 @@ public static async Task ReadContract(ThirdwebContract contract, string me
var resultData = await rpc.SendRequestAsync("eth_call", new { to = contract.Address, data = data, }, "latest");
return function.DecodeTypeOutput(resultData);
}
+
+ public static async Task WriteContract(ThirdwebAccount account, ThirdwebContract contract, string method, params object[] parameters)
+ {
+ var rpc = ThirdwebRPC.GetRpcInstance(contract.Client, contract.Chain);
+
+ var service = new Nethereum.Contracts.Contract(null, contract.Abi, contract.Address);
+ var function = service.GetFunction(method);
+ var data = function.GetData(parameters);
+
+ var transaction = new TransactionInput
+ {
+ From = account.GetAddress(),
+ To = contract.Address,
+ Data = data,
+ };
+
+ // 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"));
+
+ string hash;
+ if (account.Options.Type == WalletType.PrivateKey)
+ {
+ var signedTx = account.SignTransaction(transaction, contract.Chain);
+ Console.WriteLine($"Signed transaction: {signedTx}");
+ hash = await rpc.SendRequestAsync("eth_sendRawTransaction", signedTx);
+ }
+ else
+ {
+ hash = await rpc.SendRequestAsync("eth_sendTransaction", transaction);
+ }
+ return hash;
+ }
}
}
diff --git a/Thirdweb/Thirdweb.RPC/ThirdwebRPC.cs b/Thirdweb/Thirdweb.RPC/ThirdwebRPC.cs
index b1c69e8..674b9ce 100644
--- a/Thirdweb/Thirdweb.RPC/ThirdwebRPC.cs
+++ b/Thirdweb/Thirdweb.RPC/ThirdwebRPC.cs
@@ -1,5 +1,10 @@
-using System.Numerics;
+using System;
+using System.Collections.Generic;
+using System.Net.Http;
+using System.Numerics;
using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
using Newtonsoft.Json;
namespace Thirdweb
@@ -112,7 +117,7 @@ private void SendBatchNow()
private async Task SendBatchAsync(List batch)
{
var batchJson = JsonConvert.SerializeObject(batch);
- Console.WriteLine($"Sending batch: {batchJson}");
+
var requestMessage = new HttpRequestMessage(HttpMethod.Post, _rpcUrl) { Content = new StringContent(batchJson, Encoding.UTF8, "application/json") };
if (!string.IsNullOrEmpty(_clientId))
requestMessage.Headers.Add("x-client-id", _clientId);
diff --git a/Thirdweb/Thirdweb.Wallets/IWallet.cs b/Thirdweb/Thirdweb.Wallets/IWallet.cs
new file mode 100644
index 0000000..963bf15
--- /dev/null
+++ b/Thirdweb/Thirdweb.Wallets/IWallet.cs
@@ -0,0 +1,16 @@
+using System.Numerics;
+using Nethereum.ABI.EIP712;
+using Nethereum.RPC.Eth.DTOs;
+
+namespace Thirdweb
+{
+ internal interface IWallet
+ {
+ string GetAddress();
+ string EthSign(string message);
+ string PersonalSign(string message);
+ string SignTypedDataV4(string json);
+ string SignTypedDataV4(T data, TypedData typedData);
+ string SignTransaction(TransactionInput transaction, BigInteger chainId);
+ }
+}
diff --git a/Thirdweb/Thirdweb.Wallets/Thirdweb.Wallets.EmbeddedWallet/EmbeddedWallet.cs b/Thirdweb/Thirdweb.Wallets/Thirdweb.Wallets.EmbeddedWallet/EmbeddedWallet.cs
new file mode 100644
index 0000000..3e1db48
--- /dev/null
+++ b/Thirdweb/Thirdweb.Wallets/Thirdweb.Wallets.EmbeddedWallet/EmbeddedWallet.cs
@@ -0,0 +1,7 @@
+using Thirdweb;
+
+internal class EmbeddedWallet : PrivateKeyWallet
+{
+ internal EmbeddedWallet(string privateKeyHex)
+ : base(privateKeyHex) { }
+}
diff --git a/Thirdweb/Thirdweb.Wallets/Thirdweb.Wallets.PrivateKeyWallet/PrivateKeyWallet.cs b/Thirdweb/Thirdweb.Wallets/Thirdweb.Wallets.PrivateKeyWallet/PrivateKeyWallet.cs
new file mode 100644
index 0000000..3fa977b
--- /dev/null
+++ b/Thirdweb/Thirdweb.Wallets/Thirdweb.Wallets.PrivateKeyWallet/PrivateKeyWallet.cs
@@ -0,0 +1,131 @@
+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;
+
+internal class PrivateKeyWallet : IWallet
+{
+ private readonly EthECKey _ecKey;
+
+ internal PrivateKeyWallet(string privateKeyHex)
+ {
+ if (string.IsNullOrEmpty(privateKeyHex))
+ {
+ throw new ArgumentNullException(nameof(privateKeyHex), "Private key cannot be null or empty.");
+ }
+
+ _ecKey = new EthECKey(privateKeyHex);
+ }
+
+ 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;
+ }
+}
diff --git a/Thirdweb/Thirdweb.Wallets/ThirdwebAccount.cs b/Thirdweb/Thirdweb.Wallets/ThirdwebAccount.cs
new file mode 100644
index 0000000..ce549a7
--- /dev/null
+++ b/Thirdweb/Thirdweb.Wallets/ThirdwebAccount.cs
@@ -0,0 +1,64 @@
+using System.Numerics;
+using Nethereum.ABI.EIP712;
+using Nethereum.RPC.Eth.DTOs;
+
+namespace Thirdweb
+{
+ public class ThirdwebAccount
+ {
+ internal ThirdwebAccountOptions Options { get; private set; }
+
+ private IWallet _wallet;
+
+ public ThirdwebAccount(ThirdwebAccountOptions options)
+ {
+ if (options.Client == null)
+ {
+ throw new ArgumentException("Client must be provided");
+ }
+
+ Options = options;
+
+ InitializeWallet();
+ }
+
+ private void InitializeWallet()
+ {
+ _wallet = Options.Type switch
+ {
+ WalletType.PrivateKey => new PrivateKeyWallet(Options.PrivateKey),
+ _ => throw new ArgumentException("Invalid wallet type"),
+ };
+ }
+
+ 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);
+ }
+ }
+}
diff --git a/Thirdweb/Thirdweb.Wallets/ThirdwebAccountOptions.cs b/Thirdweb/Thirdweb.Wallets/ThirdwebAccountOptions.cs
new file mode 100644
index 0000000..5ace94a
--- /dev/null
+++ b/Thirdweb/Thirdweb.Wallets/ThirdwebAccountOptions.cs
@@ -0,0 +1,18 @@
+using System.Numerics;
+
+namespace Thirdweb
+{
+ public class ThirdwebAccountOptions
+ {
+ internal ThirdwebClient Client { get; private set; }
+ internal WalletType Type { get; private set; }
+ internal string PrivateKey { get; private set; }
+
+ public ThirdwebAccountOptions(ThirdwebClient client, WalletType type, string privateKey)
+ {
+ Client = client;
+ Type = type;
+ PrivateKey = privateKey;
+ }
+ }
+}
diff --git a/Thirdweb/Thirdweb.Wallets/WalletType.cs b/Thirdweb/Thirdweb.Wallets/WalletType.cs
new file mode 100644
index 0000000..b84aabb
--- /dev/null
+++ b/Thirdweb/Thirdweb.Wallets/WalletType.cs
@@ -0,0 +1,8 @@
+namespace Thirdweb
+{
+ public enum WalletType
+ {
+ PrivateKey,
+ Embedded,
+ }
+}
diff --git a/Thirdweb/Thirdweb.csproj b/Thirdweb/Thirdweb.csproj
index 61acf63..cdb9025 100644
--- a/Thirdweb/Thirdweb.csproj
+++ b/Thirdweb/Thirdweb.csproj
@@ -30,6 +30,9 @@
+
+
+