Skip to content

Commit

Permalink
Working example
Browse files Browse the repository at this point in the history
  • Loading branch information
0xFirekeeper committed May 29, 2024
1 parent 87e5298 commit 147d791
Show file tree
Hide file tree
Showing 5 changed files with 43 additions and 62 deletions.
62 changes: 32 additions & 30 deletions Thirdweb.Console/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
var privateKeyWallet = await PrivateKeyWallet.Create(client: client, privateKeyHex: privateKey);

// var inAppWallet = await InAppWallet.Create(client: client, email: "[email protected]"); // or email: null, phoneNumber: "+1234567890"
var inAppWallet = await InAppWallet.Create(client: client, authprovider: AuthProvider.Google); // or email: null, phoneNumber: "+1234567890"
// var inAppWallet = await InAppWallet.Create(client: client, authprovider: AuthProvider.Google); // or email: null, phoneNumber: "+1234567890"

// Reset InAppWallet (optional step for testing login flow)
// if (await inAppWallet.IsConnected())
Expand All @@ -34,35 +34,35 @@
// }

// Relog if InAppWallet not logged in
if (!await inAppWallet.IsConnected())
{
var address = await inAppWallet.LoginWithOauth(
isMobile: false,
(url) =>
{
var psi = new ProcessStartInfo { FileName = url, UseShellExecute = true };
_ = Process.Start(psi);
},
"thirdweb://",
new InAppWalletBrowser()
);
Console.WriteLine($"InAppWallet address: {address}");
// await inAppWallet.SendOTP();
// Console.WriteLine("Please submit the OTP.");
// var otp = Console.ReadLine();
// (var inAppWalletAddress, var canRetry) = await inAppWallet.SubmitOTP(otp);
// if (inAppWalletAddress == null && canRetry)
// {
// Console.WriteLine("Please submit the OTP again.");
// otp = Console.ReadLine();
// (inAppWalletAddress, _) = await inAppWallet.SubmitOTP(otp);
// }
// if (inAppWalletAddress == null)
// {
// Console.WriteLine("OTP login failed. Please try again.");
// return;
// }
}
// if (!await inAppWallet.IsConnected())
// {
// var address = await inAppWallet.LoginWithOauth(
// isMobile: false,
// (url) =>
// {
// var psi = new ProcessStartInfo { FileName = url, UseShellExecute = true };
// _ = Process.Start(psi);
// },
// "thirdweb://",
// new InAppWalletBrowser()
// );
// Console.WriteLine($"InAppWallet address: {address}");
// await inAppWallet.SendOTP();
// Console.WriteLine("Please submit the OTP.");
// var otp = Console.ReadLine();
// (var inAppWalletAddress, var canRetry) = await inAppWallet.SubmitOTP(otp);
// if (inAppWalletAddress == null && canRetry)
// {
// Console.WriteLine("Please submit the OTP again.");
// otp = Console.ReadLine();
// (inAppWalletAddress, _) = await inAppWallet.SubmitOTP(otp);
// }
// if (inAppWalletAddress == null)
// {
// Console.WriteLine("OTP login failed. Please try again.");
// return;
// }
// }

// Prepare a transaction directly, or with Contract.Prepare
// var tx = await ThirdwebTransaction.Create(
Expand Down Expand Up @@ -92,13 +92,15 @@
// Console.WriteLine($"Transaction hash: {txHash}");

var zkSmartWallet = await SmartWallet.Create(client: client, personalWallet: privateKeyWallet, chainId: 300, gasless: true);
Console.WriteLine($"Smart wallet address: {await zkSmartWallet.GetAddress()}");
var zkSyncSignatureBasedAaTxHash = await zkSmartWallet.SendTransaction(
new ThirdwebTransactionInput()
{
From = await zkSmartWallet.GetAddress(),
To = await zkSmartWallet.GetAddress(),
Value = new HexBigInteger(BigInteger.Zero),
Data = "0x",
Gas = new HexBigInteger(25000000)
}
);
Console.WriteLine($"Transaction hash: {zkSyncSignatureBasedAaTxHash}");
Expand Down
1 change: 0 additions & 1 deletion Thirdweb/Thirdweb.Utils/Constants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,5 @@ public static class Constants
"0x0101010101010101010101010101010101010101000000000000000000000000000000000000000000000000000001010101010100000000000000000000000000000000000000000000000000000000000000000101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101";
internal const string FALLBACK_IPFS_GATEWAY = "https://ipfs.io/ipfs/";
internal const string PIN_URI = "https://storage.thirdweb.com/ipfs/upload";
internal const string ZKSYNC_SIGNATUREBASED_PAYMASTER = "0xE74eA4e1785F74016e0076754B5ca6d8ADC10934";
}
}
30 changes: 8 additions & 22 deletions Thirdweb/Thirdweb.Wallets/SmartWallet/SmartWallet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ public class SmartWallet : IThirdwebWallet
protected BigInteger _chainId;
protected string _bundlerUrl;
protected string _paymasterUrl;
protected ThirdwebContract _zkSyncPaymaster;
protected bool IsZkSync => _chainId == 324 || _chainId == 300;

protected SmartWallet(
Expand All @@ -36,8 +35,7 @@ protected SmartWallet(
string paymasterUrl,
ThirdwebContract entryPointContract,
ThirdwebContract factoryContract,
ThirdwebContract accountContract,
ThirdwebContract zkSyncPaymaster
ThirdwebContract accountContract
)
{
_client = client;
Expand All @@ -49,7 +47,6 @@ ThirdwebContract zkSyncPaymaster
_entryPointContract = entryPointContract;
_factoryContract = factoryContract;
_accountContract = accountContract;
_zkSyncPaymaster = zkSyncPaymaster;
}

public static async Task<SmartWallet> Create(
Expand All @@ -76,18 +73,8 @@ public static async Task<SmartWallet> Create(
ThirdwebContract entryPointContract = null;
ThirdwebContract factoryContract = null;
ThirdwebContract accountContract = null;
ThirdwebContract zkSyncPaymasterContract = null;

if (chainId == 324 || chainId == 300)
{
zkSyncPaymasterContract = await ThirdwebContract.Create(
client,
Constants.ZKSYNC_SIGNATUREBASED_PAYMASTER,
chainId,
"[{\"type\": \"constructor\",\"name\": \"\",\"inputs\": [{\"type\": \"address\",\"name\": \"_signer\",\"internalType\": \"address\"}],\"outputs\": [],\"stateMutability\": \"nonpayable\"},{\"type\": \"error\",\"name\": \"InvalidShortString\",\"inputs\": [],\"outputs\": []},{\"type\": \"error\",\"name\": \"StringTooLong\",\"inputs\": [{\"type\": \"string\",\"name\": \"str\",\"internalType\": \"string\"}],\"outputs\": []},{\"type\": \"event\",\"name\": \"EIP712DomainChanged\",\"inputs\": [],\"outputs\": [],\"anonymous\": false},{\"type\": \"event\",\"name\": \"OwnershipTransferred\",\"inputs\": [{\"type\": \"address\",\"name\": \"previousOwner\",\"indexed\": true,\"internalType\": \"address\"},{\"type\": \"address\",\"name\": \"newOwner\",\"indexed\": true,\"internalType\": \"address\"}],\"outputs\": [],\"anonymous\": false},{\"type\": \"function\",\"name\": \"SIGNATURE_TYPEHASH\",\"inputs\": [],\"outputs\": [{\"type\": \"bytes32\",\"name\": \"\",\"internalType\": \"bytes32\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"cancelNonce\",\"inputs\": [{\"type\": \"address\",\"name\": \"_userAddress\",\"internalType\": \"address\"}],\"outputs\": [],\"stateMutability\": \"nonpayable\"},{\"type\": \"function\",\"name\": \"changeSigner\",\"inputs\": [{\"type\": \"address\",\"name\": \"_signer\",\"internalType\": \"address\"}],\"outputs\": [],\"stateMutability\": \"nonpayable\"},{\"type\": \"function\",\"name\": \"domainSeparator\",\"inputs\": [],\"outputs\": [{\"type\": \"bytes32\",\"name\": \"\",\"internalType\": \"bytes32\"}],\"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\": \"nonces\",\"inputs\": [{\"type\": \"address\",\"name\": \"\",\"internalType\": \"address\"}],\"outputs\": [{\"type\": \"uint256\",\"name\": \"\",\"internalType\": \"uint256\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"owner\",\"inputs\": [],\"outputs\": [{\"type\": \"address\",\"name\": \"\",\"internalType\": \"address\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"postTransaction\",\"inputs\": [{\"type\": \"bytes\",\"name\": \"_context\",\"internalType\": \"bytes\"},{\"type\": \"tuple\",\"name\": \"_transaction\",\"components\": [{\"type\": \"uint256\",\"name\": \"txType\",\"internalType\": \"uint256\"},{\"type\": \"uint256\",\"name\": \"from\",\"internalType\": \"uint256\"},{\"type\": \"uint256\",\"name\": \"to\",\"internalType\": \"uint256\"},{\"type\": \"uint256\",\"name\": \"gasLimit\",\"internalType\": \"uint256\"},{\"type\": \"uint256\",\"name\": \"gasPerPubdataByteLimit\",\"internalType\": \"uint256\"},{\"type\": \"uint256\",\"name\": \"maxFeePerGas\",\"internalType\": \"uint256\"},{\"type\": \"uint256\",\"name\": \"maxPriorityFeePerGas\",\"internalType\": \"uint256\"},{\"type\": \"uint256\",\"name\": \"paymaster\",\"internalType\": \"uint256\"},{\"type\": \"uint256\",\"name\": \"nonce\",\"internalType\": \"uint256\"},{\"type\": \"uint256\",\"name\": \"value\",\"internalType\": \"uint256\"},{\"type\": \"uint256[4]\",\"name\": \"reserved\",\"internalType\": \"uint256[4]\"},{\"type\": \"bytes\",\"name\": \"data\",\"internalType\": \"bytes\"},{\"type\": \"bytes\",\"name\": \"signature\",\"internalType\": \"bytes\"},{\"type\": \"bytes32[]\",\"name\": \"factoryDeps\",\"internalType\": \"bytes32[]\"},{\"type\": \"bytes\",\"name\": \"paymasterInput\",\"internalType\": \"bytes\"},{\"type\": \"bytes\",\"name\": \"reservedDynamic\",\"internalType\": \"bytes\"}],\"internalType\": \"struct Transaction\"},{\"type\": \"bytes32\",\"name\": \"\",\"internalType\": \"bytes32\"},{\"type\": \"bytes32\",\"name\": \"\",\"internalType\": \"bytes32\"},{\"type\": \"uint8\",\"name\": \"_txResult\",\"internalType\": \"enum ExecutionResult\"},{\"type\": \"uint256\",\"name\": \"_maxRefundedGas\",\"internalType\": \"uint256\"}],\"outputs\": [],\"stateMutability\": \"payable\"},{\"type\": \"function\",\"name\": \"renounceOwnership\",\"inputs\": [],\"outputs\": [],\"stateMutability\": \"nonpayable\"},{\"type\": \"function\",\"name\": \"signer\",\"inputs\": [],\"outputs\": [{\"type\": \"address\",\"name\": \"\",\"internalType\": \"address\"}],\"stateMutability\": \"view\"},{\"type\": \"function\",\"name\": \"transferOwnership\",\"inputs\": [{\"type\": \"address\",\"name\": \"newOwner\",\"internalType\": \"address\"}],\"outputs\": [],\"stateMutability\": \"nonpayable\"},{\"type\": \"function\",\"name\": \"validateAndPayForPaymasterTransaction\",\"inputs\": [{\"type\": \"bytes32\",\"name\": \"\",\"internalType\": \"bytes32\"},{\"type\": \"bytes32\",\"name\": \"\",\"internalType\": \"bytes32\"},{\"type\": \"tuple\",\"name\": \"_transaction\",\"components\": [{\"type\": \"uint256\",\"name\": \"txType\",\"internalType\": \"uint256\"},{\"type\": \"uint256\",\"name\": \"from\",\"internalType\": \"uint256\"},{\"type\": \"uint256\",\"name\": \"to\",\"internalType\": \"uint256\"},{\"type\": \"uint256\",\"name\": \"gasLimit\",\"internalType\": \"uint256\"},{\"type\": \"uint256\",\"name\": \"gasPerPubdataByteLimit\",\"internalType\": \"uint256\"},{\"type\": \"uint256\",\"name\": \"maxFeePerGas\",\"internalType\": \"uint256\"},{\"type\": \"uint256\",\"name\": \"maxPriorityFeePerGas\",\"internalType\": \"uint256\"},{\"type\": \"uint256\",\"name\": \"paymaster\",\"internalType\": \"uint256\"},{\"type\": \"uint256\",\"name\": \"nonce\",\"internalType\": \"uint256\"},{\"type\": \"uint256\",\"name\": \"value\",\"internalType\": \"uint256\"},{\"type\": \"uint256[4]\",\"name\": \"reserved\",\"internalType\": \"uint256[4]\"},{\"type\": \"bytes\",\"name\": \"data\",\"internalType\": \"bytes\"},{\"type\": \"bytes\",\"name\": \"signature\",\"internalType\": \"bytes\"},{\"type\": \"bytes32[]\",\"name\": \"factoryDeps\",\"internalType\": \"bytes32[]\"},{\"type\": \"bytes\",\"name\": \"paymasterInput\",\"internalType\": \"bytes\"},{\"type\": \"bytes\",\"name\": \"reservedDynamic\",\"internalType\": \"bytes\"}],\"internalType\": \"struct Transaction\"}],\"outputs\": [{\"type\": \"bytes4\",\"name\": \"magic\",\"internalType\": \"bytes4\"},{\"type\": \"bytes\",\"name\": \"context\",\"internalType\": \"bytes\"}],\"stateMutability\": \"payable\"},{\"type\": \"function\",\"name\": \"withdraw\",\"inputs\": [{\"type\": \"address\",\"name\": \"_to\",\"internalType\": \"address\"}],\"outputs\": [],\"stateMutability\": \"nonpayable\"},{\"type\": \"receive\",\"name\": \"\",\"inputs\": [],\"outputs\": [],\"stateMutability\": \"payable\"}]"
);
}
else
if (chainId != 324 && chainId != 300)
{
entryPointContract = await ThirdwebContract.Create(
client,
Expand All @@ -110,7 +97,7 @@ public static async Task<SmartWallet> Create(
);
}

return new SmartWallet(client, personalWallet, gasless, chainId, bundlerUrl, paymasterUrl, entryPointContract, factoryContract, accountContract, zkSyncPaymasterContract);
return new SmartWallet(client, personalWallet, gasless, chainId, bundlerUrl, paymasterUrl, entryPointContract, factoryContract, accountContract);
}

public async Task<bool> IsDeployed()
Expand All @@ -136,9 +123,8 @@ public async Task<string> SendTransaction(ThirdwebTransactionInput transactionIn
var transaction = await ThirdwebTransaction.Create(_client, _personalAccount, transactionInput, _chainId);
if (_gasless)
{
var paymaster = _zkSyncPaymaster.Address;
var paymasterInput = await GetPaymasterInput(transactionInput);
transaction = transaction.SetZkSyncOptions(new ZkSyncOptions(paymaster: paymaster, paymasterInput: Utils.BytesToHex(paymasterInput)));
(var paymaster, var paymasterInput) = await GetPaymasterInput(transactionInput);
transaction = transaction.SetZkSyncOptions(new ZkSyncOptions(paymaster: paymaster, paymasterInput: paymasterInput));
}
return await ThirdwebTransaction.Send(transaction);
}
Expand Down Expand Up @@ -247,16 +233,16 @@ private async Task<BigInteger> GetNonce()
return await ThirdwebContract.Read<BigInteger>(_entryPointContract, "getNonce", await GetAddress(), randomInt192);
}

private async Task<byte[]> GetPaymasterInput(ThirdwebTransactionInput transactionInput)
private async Task<(string, string)> GetPaymasterInput(ThirdwebTransactionInput transactionInput)
{
if (_gasless)
{
var paymasterInput = await BundlerClient.PMSponsorTransaction(_client, _paymasterUrl, 1, transactionInput);
return paymasterInput.paymasterInput.HexToByteArray();
return (paymasterInput.paymaster, paymasterInput.paymasterInput);
}
else
{
return new byte[] { };
return (null, null);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ public class PMSponsorOperationResponse

public class PMSponsorTransactionResponse
{
public string paymaster { get; set; }
public string paymasterInput { get; set; }
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,15 +57,8 @@ public static async Task<PMSponsorOperationResponse> PMSponsorUserOperation(Thir

public static async Task<PMSponsorTransactionResponse> PMSponsorTransaction(ThirdwebClient client, string paymasterUrl, object requestId, ThirdwebTransactionInput txInput)
{
var response = await BundlerRequest(client, paymasterUrl, requestId, "pm_sponsorTransaction", txInput);
try
{
return JsonConvert.DeserializeObject<PMSponsorTransactionResponse>(response.Result.ToString());
}
catch
{
return new PMSponsorTransactionResponse() { paymasterInput = response.Result.ToString() };
}
var response = await BundlerRequest(client, "http://127.0.0.1:8787?chain=300", requestId, "pm_sponsorTransaction", txInput);
return JsonConvert.DeserializeObject<PMSponsorTransactionResponse>(response.Result.ToString());
}

// Request
Expand Down

0 comments on commit 147d791

Please sign in to comment.