Skip to content

Conversation

0xFirekeeper
Copy link
Member

@0xFirekeeper 0xFirekeeper commented Aug 26, 2025

Replaces the hardcoded MINIMAL_ACCOUNT_7702 address with dynamic retrieval of the delegation contract address from the bundler. Updates EcosystemWallet and related utility methods to use the fetched delegation contract, improving flexibility and compatibility with different delegation contract deployments.


PR-Codex overview

This PR introduces a new class for handling delegation contract responses and modifies several methods to accommodate delegation contract addresses in various wallet operations.

Detailed summary

  • Added TwGetDelegationContractResponse class to handle delegation contract responses.
  • Updated TwGetDelegationContract method to return the new response type.
  • Modified IsDelegatedAccount method to accept a delegationContract parameter.
  • Updated constructors in InAppWallet and EcosystemWallet to include delegationContractAddress.
  • Adjusted calls to IsDelegatedAccount to use the new delegation contract address parameter throughout the codebase.

✨ Ask PR-Codex anything about this PR by commenting with /codex {your question}

Summary by CodeRabbit

  • New Features

    • Ecosystem Wallet and in-app wallets now support dynamic delegation contracts so delegated-account detection and authorization work correctly in normal and sponsored modes.
    • The backend can fetch the active delegation contract so wallets automatically use the correct target.
  • Refactor

    • Delegation checks and execution flows updated to use the fetched delegation contract instead of a fixed, hardcoded address.
  • Bug Fixes

    • Updated contract ABI data for delegation and multicall interactions for improved compatibility.

Replaces the hardcoded MINIMAL_ACCOUNT_7702 address with dynamic retrieval of the delegation contract address from the bundler. Updates EcosystemWallet and related utility methods to use the fetched delegation contract, improving flexibility and compatibility with different delegation contract deployments.
Copy link

coderabbitai bot commented Aug 26, 2025

Walkthrough

Removed the hardcoded MINIMAL_ACCOUNT_7702 constant and updated delegation detection to accept a delegation contract address. EcosystemWallet fetches and stores a delegation contract via a new bundler RPC, and that address is propagated through authorization, execution, and delegation checks. A DTO and bundler client method were added.

Changes

Cohort / File(s) Summary
Constants & Utils
Thirdweb/Thirdweb.Utils/Constants.cs, Thirdweb/Thirdweb.Utils/Utils.cs
Removed public MINIMAL_ACCOUNT_7702. Updated MINIMAL_ACCOUNT_7702_ABI and MULTICALL3_ABI. Utils.IsDelegatedAccount signature now accepts delegationContract and computes the comparison target from it.
EcosystemWallet & InAppWallet wiring
Thirdweb/Thirdweb.Wallets/InAppWallet/EcosystemWallet/EcosystemWallet.cs, Thirdweb/Thirdweb.Wallets/InAppWallet/InAppWallet.cs
Added DelegationContractAddress field and extended constructors to accept it. Creation flow calls BundlerClient.TwGetDelegationContract, stores the result, and passes the delegation address into EcosystemWallet and InAppWallet. Delegation checks, SignAuthorization, TwExecute, and Ensure7702 now use this address.
Bundler client & DTO
Thirdweb/Thirdweb.Wallets/SmartWallet/Thirdweb.AccountAbstraction/AATypes.cs, Thirdweb/Thirdweb.Wallets/SmartWallet/Thirdweb.AccountAbstraction/BundlerClient.cs
Added TwGetDelegationContractResponse DTO (DelegationContract property). Added BundlerClient.TwGetDelegationContract wrapper that calls "tw_getDelegationContract" and returns the DTO.
Console change
Thirdweb.Console/Program.cs
Replaced call to Utils.IsDelegatedAccount(...) with Utils.IsDeployed(...) using the same arguments; variable names and surrounding flow unchanged.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant CLI as Console/App
  participant BC as BundlerClient
  participant EW as EcosystemWallet
  participant RPC as RPC_Node

  CLI->>BC: TwGetDelegationContract(client, url, requestId)
  BC-->>CLI: TwGetDelegationContractResponse(delegationContract)
  CLI->>EW: new EcosystemWallet(..., executionMode, delegationContractAddress)

  rect rgba(230,245,255,0.6)
    note over EW: Delegation-aware code lookup
    EW->>RPC: eth_getCode(address)
    RPC-->>EW: bytecode
    EW->>EW: IsDelegatedAccount(..., delegationContractAddress)
  end

  rect rgba(235,255,235,0.6)
    note over EW: Authorization & execution with delegation contract
    EW->>EW: SignAuthorization(target=delegationContractAddress)
    EW->>BC: TwExecute(..., delegationContractAddress)
    BC-->>EW: Execute result
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Warning

Review ran into problems

🔥 Problems

Errors were encountered while retrieving linked issues.

Errors (1)
  • EIP-7702: Entity not found: Issue - Could not find referenced Issue.
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch firekeeper/dynamic-delegation

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore or @coderabbit ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Updated the InAppWallet constructor and related method to accept and pass delegationContractAddress, ensuring compatibility with changes in the base EcosystemWallet class.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
Thirdweb/Thirdweb.Wallets/InAppWallet/EcosystemWallet/EcosystemWallet.cs (2)

1299-1306: Avoid double eth_getCode and handle null delegation contract via lazy fetch.

Currently you:

  • Call IsDelegatedAccount once here, and again inside the TwExecute call.
  • Depend on DelegationContractAddress possibly being null.

Refactor to resolve the contract per chain once, reuse the result, and avoid the redundant RPC.

Apply this diff:

-        var userContract = await ThirdwebContract.Create(this.Client, userWalletAddress, transaction.ChainId, Constants.MINIMAL_ACCOUNT_7702_ABI);
-        var needsDelegation = !await Utils.IsDelegatedAccount(this.Client, transaction.ChainId, userWalletAddress, this.DelegationContractAddress);
-        EIP7702Authorization? authorization = needsDelegation
-            ? await this.SignAuthorization(transaction.ChainId, this.DelegationContractAddress, willSelfExecute: this.ExecutionMode != ExecutionMode.EIP7702Sponsored)
+        var userContract = await ThirdwebContract.Create(this.Client, userWalletAddress, transaction.ChainId, Constants.MINIMAL_ACCOUNT_7702_ABI).ConfigureAwait(false);
+        var delegationContract = this.DelegationContractAddress ?? await this.GetDelegationContractAddress(transaction.ChainId).ConfigureAwait(false);
+        var needsDelegation = !await Utils.IsDelegatedAccount(this.Client, transaction.ChainId, userWalletAddress, delegationContract).ConfigureAwait(false);
+        EIP7702Authorization? authorization = needsDelegation
+            ? await this.SignAuthorization(transaction.ChainId, delegationContract, willSelfExecute: this.ExecutionMode != ExecutionMode.EIP7702Sponsored).ConfigureAwait(false)
             : null;

1482-1489: Ensure delegation verification uses chain-aware address.

Ensure7702 should not rely on a possibly-null singleton address. Resolve per chain and reuse.

Apply this diff:

-        if (!await Utils.IsDelegatedAccount(this.Client, chainId, this.Address, this.DelegationContractAddress).ConfigureAwait(false))
+        {
+            var delegationContract = this.DelegationContractAddress ?? await this.GetDelegationContractAddress(chainId).ConfigureAwait(false);
+            if (!await Utils.IsDelegatedAccount(this.Client, chainId, this.Address, delegationContract).ConfigureAwait(false))
             {
                 if (ensureDelegated)
                 {
                     throw new InvalidOperationException("This operation requires a delegated account. Please ensure you have transacted at least once with the account to set up delegation.");
                 }
             }
-        }
+        }
🧹 Nitpick comments (5)
Thirdweb/Thirdweb.Wallets/SmartWallet/Thirdweb.AccountAbstraction/AATypes.cs (1)

243-247: DTO shape looks fine; suggest asserting presence and documenting units.

  • Consider marking the property as required to fail fast if the bundler changes shape or omits the field.
  • Minor: add an XML doc comment clarifying that the value is a 20-byte hex Ethereum address with 0x prefix.

Apply this diff to make the JSON contract explicit:

 public class TwGetDelegationContractResponse
 {
-    [JsonProperty("delegationContract")]
+    [JsonProperty("delegationContract", Required = Required.Always)]
     public string DelegationContract { get; set; }
 }
Thirdweb/Thirdweb.Wallets/InAppWallet/EcosystemWallet/EcosystemWallet.cs (4)

64-66: Make delegationContractAddress optional and normalize.

Constructor forces a delegation contract up-front, but you may not know the target chain at creation. Accept null and normalize if provided.

Apply this diff:

-        ExecutionMode executionMode,
-        string delegationContractAddress
+        ExecutionMode executionMode,
+        string delegationContractAddress
     )
@@
-        this.DelegationContractAddress = delegationContractAddress;
+        this.DelegationContractAddress = string.IsNullOrEmpty(delegationContractAddress) ? null : delegationContractAddress.ToChecksumAddress();

200-202: Pass-through constructor arg after lazy retrieval change.

Update to pass the local variable (currently null) instead of an early-fetched value.

Apply this diff:

-                executionMode,
-                delegationContractResponse.DelegationContract
+                executionMode,
+                delegationContractAddress

223-224: Same as above: pass the local (null) and resolve lazily later.

Apply this diff:

-                executionMode,
-                delegationContractResponse.DelegationContract
+                executionMode,
+                delegationContractAddress

1339-1347: Reuse needsDelegation instead of re-checking chain state.

You already computed needsDelegation. Re-checking IsDelegatedAccount here adds an unnecessary extra RPC.

Apply this diff:

-                    authorization: authorization != null && !await Utils.IsDelegatedAccount(this.Client, transaction.ChainId, userWalletAddress, this.DelegationContractAddress) ? authorization : null
+                    authorization: needsDelegation ? authorization : null
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 3d9b591 and 3dd8b5c.

📒 Files selected for processing (5)
  • Thirdweb/Thirdweb.Utils/Constants.cs (0 hunks)
  • Thirdweb/Thirdweb.Utils/Utils.cs (1 hunks)
  • Thirdweb/Thirdweb.Wallets/InAppWallet/EcosystemWallet/EcosystemWallet.cs (9 hunks)
  • Thirdweb/Thirdweb.Wallets/SmartWallet/Thirdweb.AccountAbstraction/AATypes.cs (1 hunks)
  • Thirdweb/Thirdweb.Wallets/SmartWallet/Thirdweb.AccountAbstraction/BundlerClient.cs (1 hunks)
💤 Files with no reviewable changes (1)
  • Thirdweb/Thirdweb.Utils/Constants.cs
🧰 Additional context used
🧬 Code graph analysis (2)
Thirdweb/Thirdweb.Wallets/SmartWallet/Thirdweb.AccountAbstraction/BundlerClient.cs (2)
Thirdweb/Thirdweb.Wallets/SmartWallet/Thirdweb.AccountAbstraction/AATypes.cs (1)
  • TwGetDelegationContractResponse (243-247)
Thirdweb/Thirdweb.Client/ThirdwebClient.cs (3)
  • ThirdwebClient (11-115)
  • ThirdwebClient (34-84)
  • ThirdwebClient (100-114)
Thirdweb/Thirdweb.Wallets/InAppWallet/EcosystemWallet/EcosystemWallet.cs (2)
Thirdweb/Thirdweb.Wallets/SmartWallet/Thirdweb.AccountAbstraction/BundlerClient.cs (1)
  • BundlerClient (6-147)
Thirdweb/Thirdweb.Utils/Utils.cs (1)
  • Utils (27-1341)
🔇 Additional comments (1)
Thirdweb/Thirdweb.Wallets/SmartWallet/Thirdweb.AccountAbstraction/BundlerClient.cs (1)

9-13: Confirm Bundler Response Shape for tw_getDelegationContract

Please verify in the Thirdweb Bundler documentation or sample responses whether the tw_getDelegationContract method can ever return:

  • A null result (in which case we should throw an error),
  • A plain JSON string (e.g. "0x123..."),
  • Or an object with a delegationContract field (e.g. { "delegationContract": "0x123..." }).

If it does support both string and object shapes, apply the following patch to Thirdweb/Thirdweb.Wallets/SmartWallet/Thirdweb.AccountAbstraction/BundlerClient.cs (method TwGetDelegationContract):

--- a/Thirdweb/Thirdweb.Wallets/SmartWallet/Thirdweb.AccountAbstraction/BundlerClient.cs
+++ b/Thirdweb/Thirdweb.Wallets/SmartWallet/Thirdweb.AccountAbstraction/BundlerClient.cs
@@ public static async Task<TwGetDelegationContractResponse> TwGetDelegationContract(ThirdwebClient client, string url, int requestId)
-        var response = await BundlerRequest(client, url, requestId, "tw_getDelegationContract").ConfigureAwait(false);
-        return JsonConvert.DeserializeObject<TwGetDelegationContractResponse>(response.Result.ToString());
+        var response = await BundlerRequest(client, url, requestId, "tw_getDelegationContract").ConfigureAwait(false);
+        if (response.Result == null)
+        {
+            throw new Exception("Bundler Request Failed. tw_getDelegationContract returned null result.");
+        }
+        // Handle both: result: "<addr>" or result: { delegationContract: "<addr>" }
+        if (response.Result is string addr)
+        {
+            return new TwGetDelegationContractResponse { DelegationContract = addr };
+        }
+        return JsonConvert.DeserializeObject<TwGetDelegationContractResponse>(response.Result.ToString());

• File: Thirdweb/Thirdweb.Wallets/SmartWallet/Thirdweb.AccountAbstraction/BundlerClient.cs
• Method: TwGetDelegationContract

Once you’ve confirmed the exact JSON shape, please update or remove the alternate-handling logic as needed.

Replaces the commented-out call to Utils.IsDelegated with Utils.IsDeployed for checking the deployment status of the account. This improves clarity and accuracy in the code comments.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (3)
Thirdweb.Console/Program.cs (1)

410-413: Rename variable/log to reflect IsDeployed semantics (not delegation).

You switched to Utils.IsDeployed but kept variable/logging names as “isDelegated,” which is misleading. Recommend renaming to “isDeployed” for clarity in this sample.

Apply:

-// var isDelegated = await Utils.IsDeployed(client, chain, smartEoaAddress);
-// Console.WriteLine($"Is delegated: {isDelegated}");
+// var isDeployed = await Utils.IsDeployed(client, chain, smartEoaAddress);
+// Console.WriteLine($"Is deployed: {isDeployed}");

Optional: If you actually want to assert delegation to a specific 7702 delegation contract (now dynamic), consider the explicit check:

-// var isDeployed = await Utils.IsDeployed(client, chain, smartEoaAddress);
+// var isDelegated = await Utils.IsDelegatedAccount(client, chain, smartEoaAddress, smartEoa.DelegationContractAddress);
+// Console.WriteLine($"Is delegated: {isDelegated}");
Thirdweb/Thirdweb.Wallets/InAppWallet/InAppWallet.cs (2)

23-27: Make delegationContractAddress optional and document null/empty handling.

Constructor signature change is internal, but making the new parameter optional reduces blast radius and clarifies intent. If base safely handles null, this keeps behavior stable for any un-updated callers.

-        ExecutionMode executionMode,
-        string delegationContractAddress
+        ExecutionMode executionMode,
+        string delegationContractAddress = null
     )
-        : base(null, null, client, embeddedWallet, httpClient, email, phoneNumber, authProvider, siweSigner, legacyEncryptionKey, walletSecret, executionMode, delegationContractAddress)
+        : base(
+            null,
+            null,
+            client,
+            embeddedWallet,
+            httpClient,
+            email,
+            phoneNumber,
+            authProvider,
+            siweSigner,
+            legacyEncryptionKey,
+            walletSecret,
+            executionMode,
+            delegationContractAddress
+        )

If null/empty is not acceptable by the base, add a guard here and fail fast with a descriptive exception.


59-61: Qualify static Create call for clarity and to avoid accidental self-calls.

The unqualified Create(...) relies on overload resolution to pick EcosystemWallet.Create (because of arg count). Qualifying improves readability and future-proofing if this class gains overloads.

-        var ecoWallet = await Create(client, null, null, email, phoneNumber, authProvider, storageDirectoryPath, siweSigner, legacyEncryptionKey, walletSecret, twAuthTokenOverride, executionMode);
+        var ecoWallet = await EcosystemWallet.Create(
+            client,
+            null, // ecosystemId
+            null, // domain
+            email,
+            phoneNumber,
+            authProvider,
+            storageDirectoryPath,
+            siweSigner,
+            legacyEncryptionKey,
+            walletSecret,
+            twAuthTokenOverride,
+            executionMode
+        );
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 3dd8b5c and 817deb4.

📒 Files selected for processing (2)
  • Thirdweb.Console/Program.cs (1 hunks)
  • Thirdweb/Thirdweb.Wallets/InAppWallet/InAppWallet.cs (2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build-test-cov
🔇 Additional comments (2)
Thirdweb/Thirdweb.Wallets/InAppWallet/InAppWallet.cs (2)

72-74: Good propagation of DelegationContractAddress.

Passing through ecoWallet.ExecutionMode and ecoWallet.DelegationContractAddress ensures the in-app wallet carries the dynamic delegation contract required for EIP-7702 flows.


23-27: All delegation changes verified and up to date.

  • Only InAppWallet instantiation is in Thirdweb/Thirdweb.Wallets/InAppWallet/InAppWallet.cs (lines 61–74) and it now passes ecoWallet.DelegationContractAddress.
  • Every Utils.IsDelegatedAccount call in Thirdweb/Thirdweb.Wallets/InAppWallet/EcosystemWallet/EcosystemWallet.cs (lines 1303, 1346, 1482) includes the new delegationContractAddress argument.
  • The EcosystemWallet constructor (lines 63–81) correctly assigns this.DelegationContractAddress before use.

No stale usages or missing parameters remain.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 817deb4 and 1674901.

📒 Files selected for processing (1)
  • Thirdweb/Thirdweb.Utils/Constants.cs (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build-test-cov

@0xFirekeeper 0xFirekeeper merged commit df77184 into main Aug 29, 2025
3 of 4 checks passed
@0xFirekeeper 0xFirekeeper deleted the firekeeper/dynamic-delegation branch August 29, 2025 17:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant