From 49de018e8cbaf19290bd5b799023d98473f990cc Mon Sep 17 00:00:00 2001 From: 0xFirekeeper <43042585+0xFirekeeper@users.noreply.github.com> Date: Mon, 12 May 2025 23:07:50 +0000 Subject: [PATCH] [Portal] .NET `ThirdwebBridge` Onramping & `InAppWallet` 7702 Account Abstraction (#7017) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes TOOL-4446 --- ## PR-Codex overview This PR focuses on updating documentation and examples related to `SignAuthorization` and `Native Account Abstraction`, enhancing clarity and functionality descriptions, and introducing new features for integrating with Ethereum's EIP-7702 and EIP-4337 standards. ### Detailed summary - Updated `name` in `sidebar.tsx` from "SignAuthorization (Experimental)" to "SignAuthorization". - Revised documentation in `page.mdx` files to clarify `Native Account Abstraction`. - Added examples for creating and using `InAppWallet` with EIP-7702. - Introduced details on `ExecutionMode` options for account abstraction. - Enhanced `Onramping with Fiat` section with new code examples. - Updated `SignAuthorization` section to reflect low-level API usage and parameters. > ✨ Ask PR-Codex anything about this PR by commenting with `/codex {your question}` --- apps/portal/src/app/dotnet/sidebar.tsx | 2 +- .../universal-bridge/quickstart/page.mdx | 40 ++++++++ .../actions/signauthorization/page.mdx | 95 ++++--------------- .../providers/account-abstraction/page.mdx | 44 ++++++++- .../v5/wallets/account-abstraction/page.mdx | 26 ++++- 5 files changed, 127 insertions(+), 80 deletions(-) diff --git a/apps/portal/src/app/dotnet/sidebar.tsx b/apps/portal/src/app/dotnet/sidebar.tsx index b232b879f84..462f388b136 100644 --- a/apps/portal/src/app/dotnet/sidebar.tsx +++ b/apps/portal/src/app/dotnet/sidebar.tsx @@ -81,7 +81,7 @@ const walletActions: SidebarLink = (() => { href: `${parentSlug}/switchnetwork`, }, { - name: "SignAuthorization (Experimental)", + name: "SignAuthorization", href: `${parentSlug}/signauthorization`, }, ], diff --git a/apps/portal/src/app/dotnet/universal-bridge/quickstart/page.mdx b/apps/portal/src/app/dotnet/universal-bridge/quickstart/page.mdx index 53f3df0b0d5..cbad5f6fce5 100644 --- a/apps/portal/src/app/dotnet/universal-bridge/quickstart/page.mdx +++ b/apps/portal/src/app/dotnet/universal-bridge/quickstart/page.mdx @@ -17,6 +17,7 @@ The design is akin to letting us know what your intent is. - Buy: "I want to buy x USDC on y Chain using z Token" - Sell: "I want to sell x USDC on y Chain for z Token" - Transfer: "Just transfer all my money to vitalik" +- Onramp: "I want to buy x USDC on y Chain by paying with card" We will return the transactions needed to achieve whatever you desire. You may then handle execution yourself or use our extensions. @@ -127,4 +128,43 @@ Console.WriteLine($"Sell hashes: {JsonConvert.SerializeObject(sellHashes, Format var transferResult = await bridge.Execute(myWallet, preparedTransfer); var transferHashes = transferResult.Select(receipt => receipt.TransactionHash).ToList(); Console.WriteLine($"Transfer hashes: {JsonConvert.SerializeObject(transferHashes, Formatting.Indented)}"); +``` + +## Onramping with Fiat +The onramp flow will return a link for you to display/open as you please. You may poll the status of that onramp by its ID. +In some cases, you may receive an additional set of onchain steps required to get to your destination token post on-ramp, in such cases, you may use our extension `IsSwapRequiredPostOnramp` to check, and if a swap is indeed required, you may use our `Execute` extensions to execute the transactions, or manually execute them by going through each `Step`. + +```csharp +// Onramp - Get a quote for buying crypto with Fiat +var preparedOnramp = await bridge.Onramp_Prepare( + onramp: OnrampProvider.Coinbase, + chainId: 8453, + tokenAddress: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913", // USDC on Base + amount: "10000000", + receiver: await myWallet.GetAddress() +); +Console.WriteLine($"Onramp link: {preparedOnramp.Link}"); +Console.WriteLine($"Full onramp quote and steps data: {JsonConvert.SerializeObject(preparedOnramp, Formatting.Indented)}"); + +while (true) +{ + var onrampStatus = await bridge.Onramp_Status(id: preparedOnramp.Id); + Console.WriteLine($"Full Onramp Status: {JsonConvert.SerializeObject(onrampStatus, Formatting.Indented)}"); + if (onrampStatus.StatusType is StatusType.COMPLETED or StatusType.FAILED) + { + break; + } + await ThirdwebTask.Delay(5000); +} + +if (preparedOnramp.IsSwapRequiredPostOnramp()) +{ + // Execute additional steps that are required post-onramp to get to your token, manually or via the Execute extension + var receipts = await bridge.Execute(myWallet, preparedOnramp); + Console.WriteLine($"Onramp receipts: {JsonConvert.SerializeObject(receipts, Formatting.Indented)}"); +} +else +{ + Console.WriteLine("No additional steps required post-onramp, you can use the tokens directly!"); +} ``` \ No newline at end of file diff --git a/apps/portal/src/app/dotnet/wallets/actions/signauthorization/page.mdx b/apps/portal/src/app/dotnet/wallets/actions/signauthorization/page.mdx index 3ade9a2c6d4..b046dc4ec9e 100644 --- a/apps/portal/src/app/dotnet/wallets/actions/signauthorization/page.mdx +++ b/apps/portal/src/app/dotnet/wallets/actions/signauthorization/page.mdx @@ -5,96 +5,37 @@ export const metadata = createMetadata({ description: "Sign an EIP-7702 Payload to Set Code to your EOA.", }); -# [EIP-7702](https://eips.ethereum.org/EIPS/eip-7702) Integration (Experimental) +# [EIP-7702](https://eips.ethereum.org/EIPS/eip-7702) Integration (Low-level API) Integrates `authorizationList` for any transactions. This EIP essentially allows you to set code to an EOA, unlocking a world of possibilities to enhance their functionality. - -The best way to understand it outside of reading the EIP is looking at the example below; to preface it: we sign an authorization using the wallet we want to set code to. Another wallet sends a transaction with said authorization passed in, essentially activating it. The authority wallet now has code set to it pointing to an (insecure) [Delegation](https://thirdweb.com/odyssey-911867/0x654F42b74885EE6803F403f077bc0409f1066c58) contract in this case, which allows any wallet to execute any call through it on behalf of the authority. In this example, we call the wallet executing both the authorization and the claim transaction afterwards, the exectuor. - An authority may execute its own authorization, the only difference is internal whereby the authorization nonce is incremented by 1. -```csharp -// Chain and contract addresses -var chainWith7702 = 911867; -var erc20ContractAddress = "0xAA462a5BE0fc5214507FDB4fB2474a7d5c69065b"; // Fake ERC20 -var delegationContractAddress = "0x654F42b74885EE6803F403f077bc0409f1066c58"; // BatchCallDelegation - -// Initialize contracts normally -var erc20Contract = await ThirdwebContract.Create(client: client, address: erc20ContractAddress, chain: chainWith7702); -var delegationContract = await ThirdwebContract.Create(client: client, address: delegationContractAddress, chain: chainWith7702); +You almost never need to use this API directly, but it's available for low-level access. Use higher level [Account Abstraction](/dotnet/wallets/providers/account-abstraction) APIs instead. -// Initialize a (to-be) 7702 EOA -var eoaWallet = await PrivateKeyWallet.Generate(client); -var eoaWalletAddress = await eoaWallet.GetAddress(); -Console.WriteLine($"EOA address: {eoaWalletAddress}"); +## Usage -// Initialize another wallet, the "executor" that will hit the eoa's (to-be) execute function -var executorWallet = await PrivateKeyWallet.Generate(client); -var executorWalletAddress = await executorWallet.GetAddress(); -Console.WriteLine($"Executor address: {executorWalletAddress}"); +```csharp +var authorization = await eoaWallet.SignAuthorization(chainId: chainWith7702Support, contractAddress: delegationContractAddress, willSelfExecute: false); +``` -// Fund the executor wallet -var fundingWallet = await PrivateKeyWallet.Create(client, privateKey); -var fundingHash = (await fundingWallet.Transfer(chainWith7702, executorWalletAddress, BigInteger.Parse("0.001".ToWei()))).TransactionHash; -Console.WriteLine($"Funded Executor Wallet: {fundingHash}"); +
-// Sign the authorization to make it point to the delegation contract -var authorization = await eoaWallet.SignAuthorization(chainId: chainWith7702, contractAddress: delegationContractAddress, willSelfExecute: false); -Console.WriteLine($"Authorization: {JsonConvert.SerializeObject(authorization, Formatting.Indented)}"); +### chainId (required) -// Execute the delegation -var tx = await ThirdwebTransaction.Create(executorWallet, new ThirdwebTransactionInput(chainId: chainWith7702, to: executorWalletAddress, authorization: authorization)); -var hash = (await ThirdwebTransaction.SendAndWaitForTransactionReceipt(tx)).TransactionHash; -Console.WriteLine($"Authorization execution transaction hash: {hash}"); +Your chain ID as a `BigInteger` value. -// Prove that code has been deployed to the eoa -var rpc = ThirdwebRPC.GetRpcInstance(client, chainWith7702); -var code = await rpc.SendRequestAsync("eth_getCode", eoaWalletAddress, "latest"); -Console.WriteLine($"EOA code: {code}"); +### contractAddress (required) +The address of the contract you want to authorize. Your EOA will essentially become a smart contract if this authorization is executed. -// Log erc20 balance of executor before the claim -var executorBalanceBefore = await erc20Contract.ERC20_BalanceOf(executorWalletAddress); -Console.WriteLine($"Executor balance before: {executorBalanceBefore}"); +### willSelfExecute (required) +Whether the authorization will be executed by the EOA itself or by a third-party. It will affect the nonce of the authorization. -// Prepare the claim call -var claimCallData = erc20Contract.CreateCallData( - "claim", - new object[] - { - executorWalletAddress, // receiver - 100, // quantity - Constants.NATIVE_TOKEN_ADDRESS, // currency - 0, // pricePerToken - new object[] { Array.Empty(), BigInteger.Zero, BigInteger.Zero, Constants.ADDRESS_ZERO }, // allowlistProof - Array.Empty() // data - } -); +
-// Embed the claim call in the execute call -var executeCallData = delegationContract.CreateCallData( - method: "execute", - parameters: new object[] - { - new List - { - new() - { - Data = claimCallData.HexToBytes(), - To = erc20ContractAddress, - Value = BigInteger.Zero - } - } - } -); +
-// Execute from the executor wallet targeting the eoa which is pointing to the delegation contract -var tx2 = await ThirdwebTransaction.Create(executorWallet, new ThirdwebTransactionInput(chainId: chainWith7702, to: eoaWalletAddress, data: executeCallData)); -var hash2 = (await ThirdwebTransaction.SendAndWaitForTransactionReceipt(tx2)).TransactionHash; -Console.WriteLine($"Token claim transaction hash: {hash2}"); +### string -// Log erc20 balance of executor after the claim -var executorBalanceAfter = await erc20Contract.ERC20_BalanceOf(executorWalletAddress); -Console.WriteLine($"Executor balance after: {executorBalanceAfter}"); -``` +The method returns an `EIP7702Authorization` object that can be passed to our `ThirdwebTransaction`/`ThirdwebTransactionInput` related APIs and broadcast as such. -_Note that for the time being this only works on 7702-enabled chains such as [Odyssey](https://thirdweb.com/odyssey-911867) and the feature has only been integrated with `PrivateKeyWallet`._ \ No newline at end of file +
diff --git a/apps/portal/src/app/dotnet/wallets/providers/account-abstraction/page.mdx b/apps/portal/src/app/dotnet/wallets/providers/account-abstraction/page.mdx index ea9422a91bd..523663afb7f 100644 --- a/apps/portal/src/app/dotnet/wallets/providers/account-abstraction/page.mdx +++ b/apps/portal/src/app/dotnet/wallets/providers/account-abstraction/page.mdx @@ -6,7 +6,49 @@ export const metadata = createMetadata({ "Instantiate a SmartWallet for enhanced user interactions with blockchain applications.", }); -# SmartWallet.Create +# Native Account Abstraction (via EIP-7702 Smart EOAs) + +With the recent Ethereum upgrade Pectra, EIP-7702 allows you to upgrade your EOA and get SmartWallet-like functionality with: +- Much cheaper gas costs, batching functionality +- No account separation - your wallet address does not change, not even on zksync chains (once they implement EIP-7702) +- Much faster execution, with the option of paying for gas yourself or having thirdweb manage gas sponsorship, similar to SmartWallet. + +The API is also drastically simplified! + +### ExecutionMode.EIP7702Sponsored +Upgrade to an EIP7702 smart account, unlocking all functionality of 4337 without the downsides, and thirdweb handles the execution and gas sponsorship for you! +```csharp +var smartEoa = await InAppWallet.Create( + client: thirdwebClient, + authProvider: AuthProvider.Google, + executionMode: ExecutionMode.EIP7702Sponsored +); +``` + +### ExecutionMode.EIP7702 +Upgrade to an EIP7702 smart account, unlocking all functionality of 4337 without the downsides, but sponsoring gas yourself. +```csharp +var smartEoa = await InAppWallet.Create( + client: thirdwebClient, + authProvider: AuthProvider.Google, + executionMode: ExecutionMode.EIP7702 +); +``` + +### ExecutionMode.EOA +"Normal" EOA Execution, no smart account functionality +```csharp +var basicEoa = await InAppWallet.Create( + client: thirdwebClient, + authProvider: AuthProvider.Google, + // does not need to be explicitly passed, is the default but we're showing it here + executionMode: ExecutionMode.EOA +); +``` + +_When using EIP-7702 execution modes - if not already delegated to a smart account - an EIP-7702 authorization will be signed and bundled with your first transaction, similar to how 4337 works with initcode, but without the large gas costs, slower execution and chain specific requirements._ + +# SmartWallet (via EIP-4337 Bundlers) Instantiate a `SmartWallet` to enable advanced blockchain interactions, including gasless transactions through account abstraction. This wallet type is especially useful for creating a user-friendly experience in decentralized applications. diff --git a/apps/portal/src/app/unity/v5/wallets/account-abstraction/page.mdx b/apps/portal/src/app/unity/v5/wallets/account-abstraction/page.mdx index 11deacfb376..7d8003268a6 100644 --- a/apps/portal/src/app/unity/v5/wallets/account-abstraction/page.mdx +++ b/apps/portal/src/app/unity/v5/wallets/account-abstraction/page.mdx @@ -7,7 +7,31 @@ export const metadata = createMetadata({ "Instantiate a SmartWallet to sign transactions and messages.", }); -# SmartWallet +# Native Account Abstraction (via EIP-7702 Smart EOAs) +Native Account Abstraction is a system that allows you to set code to an EOA, unlocking a world of possibilities to enhance their functionality. It is available since the Pectra upgrade on various chains. + +Enabling it is as simple as creating an `InAppWallet` and passing the `ExecutionMode.EIP7702Sponsored` flag during creation. + +```csharp +// Turn your boring EOAs into Smart EOAs! +var smartIaw = await ConnectWallet( + new WalletOptions( + provider: WalletProvider.InAppWallet, + chainId: 11155111, // Sepolia supports EIP-7702 + inAppWalletOptions: new InAppWalletOptions( + authprovider: AuthProvider.Google, + executionMode: ExecutionMode.EIP7702Sponsored // new! + ) + ) +); +ThirdwebDebug.Log("Connected to InAppWallet: " + await smartIaw.GetAddress()); + +// Execute a transaction as usual, execution is managed by thirdweb seamlessly! +var receipt = await smartIaw.Transfer(11155111, await smartIaw.GetAddress(), 0); +ThirdwebDebug.Log($"Transfer receipt: https://sepolia.etherscan.io/tx/{receipt.TransactionHash}"); +``` + +# SmartWallet (via EIP-4337 Bundlers) Instantiate or upgrade any other wallet to a `SmartWallet` to enable advanced blockchain interactions, including gasless transactions through Account Abstraction (ERC4337 as well as ZkSync Native AA).