Repository: ithacaxyz/porto
Pull request #272: docs(rpc-server): `wallet_prepareUpgradeAccount`
Author: login: onbjerg
Created: 2025-05-22T16:18:37Z | Merged: 2025-05-22T18:23:42Z



Diff:
diff --git a/apps/docs/pages/rpc-server/wallet_prepareCalls.md b/apps/docs/pages/rpc-server/wallet_prepareCalls.md
index 5deee130f..173ed4560 100644
--- a/apps/docs/pages/rpc-server/wallet_prepareCalls.md
+++ b/apps/docs/pages/rpc-server/wallet_prepareCalls.md
@@ -2,6 +2,10 @@
 
 Prepares a call bundle. The calls are simulated and a quote for executing the bundle by the server is provided. The quote lives for a certain amount of time, after which it expires.
 
+:::tip
+This method is intended to be used in conjunction with [`wallet_sendPreparedCalls`](/rpc-server/wallet_sendPreparedCalls).
+:::
+
 ## Preops
 
 Preops are calls that are executed prior to pre-verification of the signature of the bundle, and before any payment is made.
diff --git a/apps/docs/pages/rpc-server/wallet_prepareUpgradeAccount.md b/apps/docs/pages/rpc-server/wallet_prepareUpgradeAccount.md
new file mode 100644
index 000000000..e8152fb5a
--- /dev/null
+++ b/apps/docs/pages/rpc-server/wallet_prepareUpgradeAccount.md
@@ -0,0 +1,198 @@
+# `wallet_prepareUpgradeAccount`
+
+Prepares an account for upgrade by generating initialization data and an EIP-7702 authorization item.
+
+Upgrading an account is different from [creating an account](/rpc-server/wallet_prepareCreateAccount). Upgrading an account generates an EIP-7702 authorization for an existing EOA, whereas creating an account using the RPC server generates a new EOA using [PREP].
+
+The returned EIP-7702 authorization item must be signed by the EOA root key.
+
+:::tip
+This method is intended to be used in conjunction with [`wallet_upgradeAccount`](/rpc-server/wallet_upgradeAccount).
+:::
+
+## Keys
+
+There exists three different key roles:
+
+- **Admin keys** are capable of adding and modifying other keys, and capable of spending an unlimited amount of tokens and calling any contract and selector.
+- **Normal keys** can only call contracts as defined by the permissions set on it, and spend the amount of tokens afforded to it by permissions.
+- **Session keys** are like normal keys, except they also have an expiry.
+
+Setting permissions on an admin key does nothing.
+
+### Public key encoding
+
+The encoding of a key pair's public key depends on the key type:
+
+| Key Type         | Encoding Format       | Description                                                                    |
+| ---------------- | --------------------- | ------------------------------------------------------------------------------ |
+| secp256k1        | `abi.encode(address)` | Stores only the Ethereum address derived from the public key (truncated hash). |
+| webAuthn          | `abi.encode(x, y)`    | Stores both x and y coordinates of the public key on the elliptic curve.       |
+| secp256r1 (P256) | `abi.encode(x, y) `   | Stores both x and y coordinates for the secp256r1 curve.                       |
+
+## Signature encoding
+
+The signature is encoded as follows: `abi.encodePacked(bytes(innerSignature), bytes32(keyHash), bool(prehash))`, where the key hash is `keccak(bytes32(keyType, publicKey))`.
+
+The inner signature depends on the key type:
+
+| Key Type | Signature |
+| -------- | --------- |
+| secp256r1 (p256) | `(r, s)` |
+| webauthn | `(r, s)` |
+| secp256k1 | `(r, s)` or `(r, vs)` |
+
+## Request
+
+```ts
+type Request = {
+  method: 'wallet_prepareUpgradeAccount',
+  params: [{
+    address: `0x${string}`,
+    chainId: `0x${string}`,
+    capabilities: {
+      authorizeKeys: {
+        // See "Keys"
+        key: {
+          expiry?: number,
+          type: 'p256' | 'webauthnp256' | 'secp256k1',
+          role: 'admin' | 'normal' | 'session',
+          publicKey: `0x${string}`,
+        },
+        permissions: ({
+          type: 'call',
+          // See "Selectors"
+          selector: string,
+          to: `0x${string}`,
+        } | {
+          type: 'spend',
+          limit: number,
+          period: 'minute' | 'hour' | 'day' | 'week' | 'month' | 'year',
+          // defaults to the native token (address zero)
+          token?: `0x${string}`,
+        })[],
+      }[],
+      delegation: `0x${string}`,
+      // defaults to the EOA
+      feePayer?: `0x${string}`,
+      // defaults to the native token
+      feeToken?: `0x${string}`,
+    },
+  }],
+}
+```
+
+## Response
+
+```ts
+type Response = {
+  // The context returned depending on whether
+  // a preop or normal bundle was prepared.
+  //
+  // In the case of a preop, the preop itself
+  // is returned, otherwise a quote signed by the
+  // relay is returned.
+  context: {
+    quote: {
+      chainId: `0x${string}`,
+      op: {
+        eoa: `0x${string}`,
+        executionData: `0x${string}`,
+        nonce: `0x${string}`,
+        payer: `0x${string}`,
+        paymentToken: `0x${string}`,
+        prePaymentMaxAmount: `0x${string}`,
+        totalPaymentMaxAmount: `0x${string}`,
+        combinedGas: `0x${string}`,
+        encodedPreOps: `0x${string}`[],
+        initData: `0x${string}`,
+        prePaymentAmount: `0x${string}`,
+        totalPaymentAmount: `0x${string}`,
+        paymentRecipient: `0x${string}`,
+        signature: `0x${string}`,
+        paymentSignature: `0x${string}`,
+        supportedDelegationImplementation: `0x${string}`,
+      },
+      txGas: `0x${string}`,
+      nativeFeeEstimate: {
+        maxFeePerGas: number,
+        maxPriorityFeePerGas: number,
+      },
+      // UNIX timestamp the quote expires at.
+      ttl: number,
+      authorizationAddress?: `0x${string}`,
+      entrypoint: `0x${string}`,
+      // The RPC servers signature over the quote.
+      signature: {
+        y_parity: bool,
+        r: `0x${string}`,
+        s: `0x${string}`,
+      },
+      // The hash of the quote.
+      hash: `0x${string}`,
+    },
+  },
+  // the digest of the bundle that the user needs to sign
+  digest: `0x${string}`,
+  // EIP-712 typed data of the preop or bundle.
+  typedData: any,
+  // capabilities assigned to the account
+  capabilities: {
+    authorizeKeys: {
+      // key hash
+      hash: `0x${string}`,
+      // See "Keys"
+      key: {
+        expiry?: number,
+        type: 'p256' | 'webauthnp256' | 'secp256k1',
+        role: 'admin' | 'normal' | 'session',
+        publicKey: `0x${string}`,
+      },
+      permissions: ({
+        type: 'call',
+        // See "Selectors"
+        selector: string,
+        to: `0x${string}`,
+      } | {
+        type: 'spend',
+        limit: number,
+        period: 'minute' | 'hour' | 'day' | 'week' | 'month' | 'year',
+        // defaults to the native token (address zero)
+        token?: `0x${string}`,
+      })[],
+    }[],
+    // Key revocations.
+    revokeKeys: {
+      hash: `0x${string}`,
+      id?: `0x${string}`
+    }[],
+    // Simulated asset diffs, where the first element of the tuple is the recipient or sender.
+    assetDiff: [
+       `0x${string}`,
+       {
+         // Omitted if this is the native token.
+         address?: `0x${string}`,
+         decimals?: number,
+         direction: 'incoming' | 'outgoing',
+         name?: string,
+         symbol?: string,
+         type?: 'erc20' | 'erc721',
+         uri?: string,
+         // For ERC721, the asset ID. For ERC20 the value moved.
+         value: number,
+       }[]
+    ][],
+  },
+  // The key that will be used to sign the bundle. See "Keys".
+  //
+  // It can be omitted in the case of a preop, see "Preops".
+  key?: {
+    type: 'p256' | 'webauthnp256' | 'secp256k1',
+    publicKey: `0x${string}`,
+    // Whether the bundle digest will be prehashed by the key.
+    prehash: boolean,
+  },
+}
+```
+
+[PREP]: https://blog.biconomy.io/prep-deep-dive/
diff --git a/apps/docs/pages/rpc-server/wallet_sendPreparedCalls.md b/apps/docs/pages/rpc-server/wallet_sendPreparedCalls.md
index 598ea0b0f..79821b804 100644
--- a/apps/docs/pages/rpc-server/wallet_sendPreparedCalls.md
+++ b/apps/docs/pages/rpc-server/wallet_sendPreparedCalls.md
@@ -2,7 +2,9 @@
 
 Sends a prepared call bundle.
 
-See [`wallet_prepareCalls`](/rpc-server/wallet_prepareCalls).
+:::tip
+This method is intended to be used in conjunction with [`wallet_prepareCalls`](/rpc-server/wallet_prepareCalls).
+:::
 
 ## Fees
 
diff --git a/apps/docs/pages/rpc-server/wallet_upgradeAccount.md b/apps/docs/pages/rpc-server/wallet_upgradeAccount.md
new file mode 100644
index 000000000..4d910b31a
--- /dev/null
+++ b/apps/docs/pages/rpc-server/wallet_upgradeAccount.md
@@ -0,0 +1 @@
+# `wallet_upgradeAccount`
diff --git a/apps/docs/vocs.config.tsx b/apps/docs/vocs.config.tsx
index 44b90932b..2159073f9 100644
--- a/apps/docs/vocs.config.tsx
+++ b/apps/docs/vocs.config.tsx
@@ -67,9 +67,8 @@ export default defineConfig({
             text: 'wallet_prepareCalls',
           },
           {
-            disabled: true,
             link: '/rpc-server/wallet_prepareUpgradeAccount',
-            text: 'wallet_prepareUpgradeAccount 🚧',
+            text: 'wallet_prepareUpgradeAccount',
           },
           {
             link: '/rpc-server/wallet_sendPreparedCalls',


Reviews:
user: login: graphite-app[bot]
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-22T16:19:48Z
user: login: joshieDo
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-22T17:58:27Z
user: login: joshieDo
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-22T18:00:05Z
user: login: onbjerg
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-22T18:02:08Z
user: login: joshieDo
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-22T18:02:48Z
user: login: joshieDo
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-22T18:02:52Z
user: login: onbjerg
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-22T18:04:30Z

Review comments:
diff_hunk: @@ -0,0 +1,205 @@
+# `wallet_prepareUpgradeAccount`
+
+Prepares an account for upgrade by generating initialization data and an EIP-7702 authorization item.
+
+Upgrading an account is different from [creating an account](/rpc-server/wallet_prepareCreateAccount). Upgrading an account generates an EIP-7702 authorization for an existing EOA, whereas creating an account using the RPC server generates a new EOA using [PREP].
+
+The returned EIP-7702 authorization item must be signed by the EOA root key.
+
+:::tip
+This method is intended to be used in conjunction with [`wallet_upgradeAccount`](/rpc-server/wallet_upgradeAccount).
+:::
+
+## Keys
+
+There exists three different key roles:
+
+- **Admin keys** are capable of adding and modifying other keys, and capable of spending an unlimited amount of tokens and calling any contract and selector.
+- **Normal keys** can only call contracts as defined by the permissions set on it, and spend the amount of tokens afforded to it by permissions.
+- **Session keys** are like normal keys, except they also have an expiry.
+
+Setting permissions on an admin key does nothing.
+
+### Public key encoding
+
+The encoding of a key pair's public key depends on the key type:
+
+| Key Type         | Encoding Format       | Description                                                                    |
+| ---------------- | --------------------- | ------------------------------------------------------------------------------ |
+| secp256k1        | `abi.encode(address)` | Stores only the Ethereum address derived from the public key (truncated hash). |
+| webAuthn          | `abi.encode(x, y)`    | Stores both x and y coordinates of the public key on the elliptic curve.       |
+| secp256r1 (P256) | `abi.encode(x, y) `   | Stores both x and y coordinates for the secp256r1 curve.                       |
+
+## Signature encoding
+
+The signature is encoded as follows: `abi.encodePacked(bytes(innerSignature), bytes32(keyHash), bool(prehash))`, where the key hash is `keccak(bytes32(keyType, publicKey))`.
+
+The inner signature depends on the key type:
+
+| Key Type | Signature |
+| -------- | --------- |
+| secp256r1 (p256) | `(r, s)` |
+| webauthn | `(r, s)` |
+| secp256k1 | `(r, s)` or `(r, vs)` |
+
+## Request
+
+```ts
+type Request = {
+  method: 'wallet_prepareUpgradeAccount',
+  params: [{
+    address: `0x${string}`,
+    chainId: `0x${string}`,
+    capabilities: {
+      authorizeKeys: {
+        // See "Keys"
+        key: {
+          expiry?: number,
+          type: 'p256' | 'webauthnp256' | 'secp256k1',
+          role: 'admin' | 'normal' | 'session',
+          publicKey: `0x${string}`,
+        },
+        permissions: ({
+          type: 'call',
+          // See "Selectors"
+          selector: string,
+          to: `0x${string}`,
+        } | {
+          type: 'spend',
+          limit: number,
+          period: 'minute' | 'hour' | 'day' | 'week' | 'month' | 'year',
+          // defaults to the native token (address zero)
+          token?: `0x${string}`,
+        })[],
+      }[],
+      delegation: `0x${string}`,
+      // defaults to the EOA
+      feePayer?: `0x${string}`,
+      // defaults to the native token
+      feeToken?: `0x${string}`,
+    },
+  }],
+}
+```
+
+## Response
+
+```ts
+type Response = {
+  // The context returned depending on whether
+  // a preop or normal bundle was prepared.
+  //
+  // In the case of a preop, the preop itself
+  // is returned, otherwise a quote signed by the
+  // relay is returned.
+  context: {
+    quote: {
+      chainId: `0x${string}`,
+      op: {
+        eoa: `0x${string}`,
+        executionData: `0x${string}`,
+        nonce: `0x${string}`,
+        payer: `0x${string}`,
+        paymentToken: `0x${string}`,
+        prePaymentMaxAmount: `0x${string}`,
+        totalPaymentMaxAmount: `0x${string}`,
+        combinedGas: `0x${string}`,
+        encodedPreOps: `0x${string}`[],
+        initData: `0x${string}`,
+        prePaymentAmount: `0x${string}`,
+        totalPaymentAmount: `0x${string}`,
+        paymentRecipient: `0x${string}`,
+        signature: `0x${string}`,
+        paymentSignature: `0x${string}`,
+        supportedDelegationImplementation: `0x${string}`,
+      },
+      txGas: `0x${string}`,
+      nativeFeeEstimate: {
+        maxFeePerGas: number,
+        maxPriorityFeePerGas: number,
+      },
+      // UNIX timestamp the quote expires at.
+      ttl: number,
+      authorizationAddress?: `0x${string}`,
+      entrypoint: `0x${string}`,
+      // The RPC servers signature over the quote.
+      signature: {
+        y_parity: bool,
path: apps/docs/pages/rpc-server/wallet_prepareUpgradeAccount.md
user: login: graphite-app[bot]
body: The type `bool` should be changed to `boolean` to follow TypeScript's type system conventions. This appears in the signature property definition:

```ts
signature: {
  y_parity: bool,  // should be boolean
  r: `0x${string}`,
  s: `0x${string}`,
},
```
```suggestion
        y_parity: bool,
```
  

*Spotted by [Diamond](https://app.graphite.dev/diamond/?org=ithacaxyz&ref=ai-review-comment)*<i class='graphite__hidden'><br /><br />Is this helpful? React 👍 or 👎 to let us know.</i>
created_at: 2025-05-22T16:19:46Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,205 @@
+# `wallet_prepareUpgradeAccount`
+
+Prepares an account for upgrade by generating initialization data and an EIP-7702 authorization item.
+
+Upgrading an account is different from [creating an account](/rpc-server/wallet_prepareCreateAccount). Upgrading an account generates an EIP-7702 authorization for an existing EOA, whereas creating an account using the RPC server generates a new EOA using [PREP].
+
+The returned EIP-7702 authorization item must be signed by the EOA root key.
+
+:::tip
+This method is intended to be used in conjunction with [`wallet_upgradeAccount`](/rpc-server/wallet_upgradeAccount).
+:::
+
+## Keys
+
+There exists three different key roles:
+
+- **Admin keys** are capable of adding and modifying other keys, and capable of spending an unlimited amount of tokens and calling any contract and selector.
+- **Normal keys** can only call contracts as defined by the permissions set on it, and spend the amount of tokens afforded to it by permissions.
+- **Session keys** are like normal keys, except they also have an expiry.
+
+Setting permissions on an admin key does nothing.
+
+### Public key encoding
+
+The encoding of a key pair's public key depends on the key type:
+
+| Key Type         | Encoding Format       | Description                                                                    |
+| ---------------- | --------------------- | ------------------------------------------------------------------------------ |
+| secp256k1        | `abi.encode(address)` | Stores only the Ethereum address derived from the public key (truncated hash). |
+| webAuthn          | `abi.encode(x, y)`    | Stores both x and y coordinates of the public key on the elliptic curve.       |
+| secp256r1 (P256) | `abi.encode(x, y) `   | Stores both x and y coordinates for the secp256r1 curve.                       |
path: apps/docs/pages/rpc-server/wallet_prepareUpgradeAccount.md
user: login: graphite-app[bot]
body: There's an extra space in the code formatting for the secp256r1 row. Please remove the space before the closing backtick:

```diff
-| secp256r1 (P256) | `abi.encode(x, y) `   | Stores both x and y coordinates for the secp256r1 curve.                       |
+| secp256r1 (P256) | `abi.encode(x, y)`   | Stores both x and y coordinates for the secp256r1 curve.                       |
```
```suggestion
| secp256r1 (P256) | `abi.encode(x, y)`   | Stores both x and y coordinates for the secp256r1 curve.                       |
```
  

*Spotted by [Diamond](https://app.graphite.dev/diamond/?org=ithacaxyz&ref=ai-review-comment)*<i class='graphite__hidden'><br /><br />Is this helpful? React 👍 or 👎 to let us know.</i>
created_at: 2025-05-22T16:19:47Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,205 @@
+# `wallet_prepareUpgradeAccount`
+
+Prepares an account for upgrade by generating initialization data and an EIP-7702 authorization item.
+
+Upgrading an account is different from [creating an account](/rpc-server/wallet_prepareCreateAccount). Upgrading an account generates an EIP-7702 authorization for an existing EOA, whereas creating an account using the RPC server generates a new EOA using [PREP].
+
+The returned EIP-7702 authorization item must be signed by the EOA root key.
+
+:::tip
+This method is intended to be used in conjunction with [`wallet_upgradeAccount`](/rpc-server/wallet_upgradeAccount).
+:::
+
+## Keys
+
+There exists three different key roles:
+
+- **Admin keys** are capable of adding and modifying other keys, and capable of spending an unlimited amount of tokens and calling any contract and selector.
+- **Normal keys** can only call contracts as defined by the permissions set on it, and spend the amount of tokens afforded to it by permissions.
+- **Session keys** are like normal keys, except they also have an expiry.
+
+Setting permissions on an admin key does nothing.
path: apps/docs/pages/rpc-server/wallet_prepareUpgradeAccount.md
user: login: joshieDo
body: this actually returns error https://github.com/ithacaxyz/account/blob/de7168d87897b5627956545c12d57823631f5510/src/GuardedExecutor.sol#L370 
created_at: 2025-05-22T17:58:27Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,205 @@
+# `wallet_prepareUpgradeAccount`
+
+Prepares an account for upgrade by generating initialization data and an EIP-7702 authorization item.
+
+Upgrading an account is different from [creating an account](/rpc-server/wallet_prepareCreateAccount). Upgrading an account generates an EIP-7702 authorization for an existing EOA, whereas creating an account using the RPC server generates a new EOA using [PREP].
+
+The returned EIP-7702 authorization item must be signed by the EOA root key.
+
+:::tip
+This method is intended to be used in conjunction with [`wallet_upgradeAccount`](/rpc-server/wallet_upgradeAccount).
+:::
+
+## Keys
+
+There exists three different key roles:
+
+- **Admin keys** are capable of adding and modifying other keys, and capable of spending an unlimited amount of tokens and calling any contract and selector.
+- **Normal keys** can only call contracts as defined by the permissions set on it, and spend the amount of tokens afforded to it by permissions.
+- **Session keys** are like normal keys, except they also have an expiry.
+
+Setting permissions on an admin key does nothing.
+
+### Public key encoding
+
+The encoding of a key pair's public key depends on the key type:
+
+| Key Type         | Encoding Format       | Description                                                                    |
+| ---------------- | --------------------- | ------------------------------------------------------------------------------ |
+| secp256k1        | `abi.encode(address)` | Stores only the Ethereum address derived from the public key (truncated hash). |
+| webAuthn          | `abi.encode(x, y)`    | Stores both x and y coordinates of the public key on the elliptic curve.       |
+| secp256r1 (P256) | `abi.encode(x, y) `   | Stores both x and y coordinates for the secp256r1 curve.                       |
+
+## Signature encoding
+
+The signature is encoded as follows: `abi.encodePacked(bytes(innerSignature), bytes32(keyHash), bool(prehash))`, where the key hash is `keccak(bytes32(keyType, publicKey))`.
+
+The inner signature depends on the key type:
+
+| Key Type | Signature |
+| -------- | --------- |
+| secp256r1 (p256) | `(r, s)` |
+| webauthn | `(r, s)` |
+| secp256k1 | `(r, s)` or `(r, vs)` |
+
+## Request
+
+```ts
+type Request = {
+  method: 'wallet_prepareUpgradeAccount',
+  params: [{
+    address: `0x${string}`,
+    chainId: `0x${string}`,
+    capabilities: {
+      authorizeKeys: {
+        // See "Keys"
+        key: {
+          expiry?: number,
+          type: 'p256' | 'webauthnp256' | 'secp256k1',
+          role: 'admin' | 'normal' | 'session',
+          publicKey: `0x${string}`,
+        },
+        permissions: ({
+          type: 'call',
+          // See "Selectors"
+          selector: string,
+          to: `0x${string}`,
+        } | {
+          type: 'spend',
+          limit: number,
+          period: 'minute' | 'hour' | 'day' | 'week' | 'month' | 'year',
+          // defaults to the native token (address zero)
+          token?: `0x${string}`,
+        })[],
+      }[],
+      delegation: `0x${string}`,
+      // defaults to the EOA
+      feePayer?: `0x${string}`,
+      // defaults to the native token
+      feeToken?: `0x${string}`,
+    },
+  }],
+}
+```
+
+## Response
+
+```ts
+type Response = {
+  // The context returned depending on whether
+  // a preop or normal bundle was prepared.
path: apps/docs/pages/rpc-server/wallet_prepareUpgradeAccount.md
user: login: joshieDo
body: in this endpoint it should always be a normal bundle
created_at: 2025-05-22T18:00:05Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,205 @@
+# `wallet_prepareUpgradeAccount`
+
+Prepares an account for upgrade by generating initialization data and an EIP-7702 authorization item.
+
+Upgrading an account is different from [creating an account](/rpc-server/wallet_prepareCreateAccount). Upgrading an account generates an EIP-7702 authorization for an existing EOA, whereas creating an account using the RPC server generates a new EOA using [PREP].
+
+The returned EIP-7702 authorization item must be signed by the EOA root key.
+
+:::tip
+This method is intended to be used in conjunction with [`wallet_upgradeAccount`](/rpc-server/wallet_upgradeAccount).
+:::
+
+## Keys
+
+There exists three different key roles:
+
+- **Admin keys** are capable of adding and modifying other keys, and capable of spending an unlimited amount of tokens and calling any contract and selector.
+- **Normal keys** can only call contracts as defined by the permissions set on it, and spend the amount of tokens afforded to it by permissions.
+- **Session keys** are like normal keys, except they also have an expiry.
+
+Setting permissions on an admin key does nothing.
path: apps/docs/pages/rpc-server/wallet_prepareUpgradeAccount.md
user: login: onbjerg
body: will do this change in #274 since this is copy-pasted between multiple pages rn
created_at: 2025-05-22T18:02:08Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,205 @@
+# `wallet_prepareUpgradeAccount`
+
+Prepares an account for upgrade by generating initialization data and an EIP-7702 authorization item.
+
+Upgrading an account is different from [creating an account](/rpc-server/wallet_prepareCreateAccount). Upgrading an account generates an EIP-7702 authorization for an existing EOA, whereas creating an account using the RPC server generates a new EOA using [PREP].
+
+The returned EIP-7702 authorization item must be signed by the EOA root key.
+
+:::tip
+This method is intended to be used in conjunction with [`wallet_upgradeAccount`](/rpc-server/wallet_upgradeAccount).
+:::
+
+## Keys
+
+There exists three different key roles:
+
+- **Admin keys** are capable of adding and modifying other keys, and capable of spending an unlimited amount of tokens and calling any contract and selector.
+- **Normal keys** can only call contracts as defined by the permissions set on it, and spend the amount of tokens afforded to it by permissions.
+- **Session keys** are like normal keys, except they also have an expiry.
+
+Setting permissions on an admin key does nothing.
+
+### Public key encoding
+
+The encoding of a key pair's public key depends on the key type:
+
+| Key Type         | Encoding Format       | Description                                                                    |
+| ---------------- | --------------------- | ------------------------------------------------------------------------------ |
+| secp256k1        | `abi.encode(address)` | Stores only the Ethereum address derived from the public key (truncated hash). |
+| webAuthn          | `abi.encode(x, y)`    | Stores both x and y coordinates of the public key on the elliptic curve.       |
+| secp256r1 (P256) | `abi.encode(x, y) `   | Stores both x and y coordinates for the secp256r1 curve.                       |
+
+## Signature encoding
+
+The signature is encoded as follows: `abi.encodePacked(bytes(innerSignature), bytes32(keyHash), bool(prehash))`, where the key hash is `keccak(bytes32(keyType, publicKey))`.
+
+The inner signature depends on the key type:
+
+| Key Type | Signature |
+| -------- | --------- |
+| secp256r1 (p256) | `(r, s)` |
+| webauthn | `(r, s)` |
+| secp256k1 | `(r, s)` or `(r, vs)` |
+
+## Request
+
+```ts
+type Request = {
+  method: 'wallet_prepareUpgradeAccount',
+  params: [{
+    address: `0x${string}`,
+    chainId: `0x${string}`,
+    capabilities: {
+      authorizeKeys: {
+        // See "Keys"
+        key: {
+          expiry?: number,
+          type: 'p256' | 'webauthnp256' | 'secp256k1',
+          role: 'admin' | 'normal' | 'session',
+          publicKey: `0x${string}`,
+        },
+        permissions: ({
+          type: 'call',
+          // See "Selectors"
+          selector: string,
+          to: `0x${string}`,
+        } | {
+          type: 'spend',
+          limit: number,
+          period: 'minute' | 'hour' | 'day' | 'week' | 'month' | 'year',
+          // defaults to the native token (address zero)
+          token?: `0x${string}`,
+        })[],
+      }[],
+      delegation: `0x${string}`,
+      // defaults to the EOA
+      feePayer?: `0x${string}`,
+      // defaults to the native token
+      feeToken?: `0x${string}`,
+    },
+  }],
+}
+```
+
+## Response
+
+```ts
+type Response = {
+  // The context returned depending on whether
+  // a preop or normal bundle was prepared.
+  //
+  // In the case of a preop, the preop itself
+  // is returned, otherwise a quote signed by the
+  // relay is returned.
+  context: {
+    quote: {
+      chainId: `0x${string}`,
+      op: {
+        eoa: `0x${string}`,
+        executionData: `0x${string}`,
+        nonce: `0x${string}`,
+        payer: `0x${string}`,
+        paymentToken: `0x${string}`,
+        prePaymentMaxAmount: `0x${string}`,
+        totalPaymentMaxAmount: `0x${string}`,
+        combinedGas: `0x${string}`,
+        encodedPreOps: `0x${string}`[],
+        initData: `0x${string}`,
+        prePaymentAmount: `0x${string}`,
+        totalPaymentAmount: `0x${string}`,
+        paymentRecipient: `0x${string}`,
+        signature: `0x${string}`,
+        paymentSignature: `0x${string}`,
+        supportedDelegationImplementation: `0x${string}`,
+      },
+      txGas: `0x${string}`,
+      nativeFeeEstimate: {
+        maxFeePerGas: number,
+        maxPriorityFeePerGas: number,
+      },
+      // UNIX timestamp the quote expires at.
+      ttl: number,
+      authorizationAddress?: `0x${string}`,
+      entrypoint: `0x${string}`,
+      // The RPC servers signature over the quote.
+      signature: {
+        y_parity: bool,
+        r: `0x${string}`,
+        s: `0x${string}`,
+      },
+      // The hash of the quote.
+      hash: `0x${string}`,
+    },
+  } | {
+    preOp: {
+      eoa: `0x${string}`,
+      executionData: `0x${string}`,
+      nonce: `0x${string}`,
+      signature: `0x${string}`,
+    },
+  },
+  // the digest of the bundle that the user needs to sign
+  digest: `0x${string}`,
+  // EIP-712 typed data of the preop or bundle.
+  typedData: any,
+  // capabilities assigned to the account
+  capabilities: {
+    authorizeKeys: {
+      // key hash
+      hash: `0x${string}`,
+      // See "Keys"
+      key: {
+        expiry?: number,
+        type: 'p256' | 'webauthnp256' | 'secp256k1',
+        role: 'admin' | 'normal' | 'session',
+        publicKey: `0x${string}`,
+      },
+      permissions: ({
+        type: 'call',
+        // See "Selectors"
+        selector: string,
+        to: `0x${string}`,
+      } | {
+        type: 'spend',
+        limit: number,
+        period: 'minute' | 'hour' | 'day' | 'week' | 'month' | 'year',
+        // defaults to the native token (address zero)
+        token?: `0x${string}`,
+      })[],
+    }[],
+    // Key revocations.
+    revokeKeys: {
+      hash: `0x${string}`,
+      id?: `0x${string}`
+    }[],
+    // Simulated asset diffs, where the first element of the tuple is the recipient or sender.
+    assetDiff: [
+       `0x${string}`,
+       {
+         // Omitted if this is the native token.
+         address?: `0x${string}`,
+         decimals?: number,
+         direction: 'incoming' | 'outgoing',
+         name?: string,
+         symbol?: string,
+         type?: 'erc20' | 'erc721',
+         uri?: string,
+         // For ERC721, the asset ID. For ERC20 the value moved.
+         value: number,
+       }[]
+    ][],
+  },
+  // The key that will be used to sign the bundle. See "Keys".
+  //
+  // It can be omitted in the case of a preop, see "Preops".
+  key?: {
path: apps/docs/pages/rpc-server/wallet_prepareUpgradeAccount.md
user: login: joshieDo
body: hm, this will actually always be null in this endpoint
created_at: 2025-05-22T18:02:47Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,205 @@
+# `wallet_prepareUpgradeAccount`
+
+Prepares an account for upgrade by generating initialization data and an EIP-7702 authorization item.
+
+Upgrading an account is different from [creating an account](/rpc-server/wallet_prepareCreateAccount). Upgrading an account generates an EIP-7702 authorization for an existing EOA, whereas creating an account using the RPC server generates a new EOA using [PREP].
+
+The returned EIP-7702 authorization item must be signed by the EOA root key.
+
+:::tip
+This method is intended to be used in conjunction with [`wallet_upgradeAccount`](/rpc-server/wallet_upgradeAccount).
+:::
+
+## Keys
+
+There exists three different key roles:
+
+- **Admin keys** are capable of adding and modifying other keys, and capable of spending an unlimited amount of tokens and calling any contract and selector.
+- **Normal keys** can only call contracts as defined by the permissions set on it, and spend the amount of tokens afforded to it by permissions.
+- **Session keys** are like normal keys, except they also have an expiry.
+
+Setting permissions on an admin key does nothing.
+
+### Public key encoding
+
+The encoding of a key pair's public key depends on the key type:
+
+| Key Type         | Encoding Format       | Description                                                                    |
+| ---------------- | --------------------- | ------------------------------------------------------------------------------ |
+| secp256k1        | `abi.encode(address)` | Stores only the Ethereum address derived from the public key (truncated hash). |
+| webAuthn          | `abi.encode(x, y)`    | Stores both x and y coordinates of the public key on the elliptic curve.       |
+| secp256r1 (P256) | `abi.encode(x, y) `   | Stores both x and y coordinates for the secp256r1 curve.                       |
+
+## Signature encoding
+
+The signature is encoded as follows: `abi.encodePacked(bytes(innerSignature), bytes32(keyHash), bool(prehash))`, where the key hash is `keccak(bytes32(keyType, publicKey))`.
+
+The inner signature depends on the key type:
+
+| Key Type | Signature |
+| -------- | --------- |
+| secp256r1 (p256) | `(r, s)` |
+| webauthn | `(r, s)` |
+| secp256k1 | `(r, s)` or `(r, vs)` |
+
+## Request
+
+```ts
+type Request = {
+  method: 'wallet_prepareUpgradeAccount',
+  params: [{
+    address: `0x${string}`,
+    chainId: `0x${string}`,
+    capabilities: {
+      authorizeKeys: {
+        // See "Keys"
+        key: {
+          expiry?: number,
+          type: 'p256' | 'webauthnp256' | 'secp256k1',
+          role: 'admin' | 'normal' | 'session',
+          publicKey: `0x${string}`,
+        },
+        permissions: ({
+          type: 'call',
+          // See "Selectors"
+          selector: string,
+          to: `0x${string}`,
+        } | {
+          type: 'spend',
+          limit: number,
+          period: 'minute' | 'hour' | 'day' | 'week' | 'month' | 'year',
+          // defaults to the native token (address zero)
+          token?: `0x${string}`,
+        })[],
+      }[],
+      delegation: `0x${string}`,
+      // defaults to the EOA
+      feePayer?: `0x${string}`,
+      // defaults to the native token
+      feeToken?: `0x${string}`,
+    },
+  }],
+}
+```
+
+## Response
+
+```ts
+type Response = {
+  // The context returned depending on whether
+  // a preop or normal bundle was prepared.
+  //
+  // In the case of a preop, the preop itself
+  // is returned, otherwise a quote signed by the
+  // relay is returned.
+  context: {
+    quote: {
+      chainId: `0x${string}`,
+      op: {
+        eoa: `0x${string}`,
+        executionData: `0x${string}`,
+        nonce: `0x${string}`,
+        payer: `0x${string}`,
+        paymentToken: `0x${string}`,
+        prePaymentMaxAmount: `0x${string}`,
+        totalPaymentMaxAmount: `0x${string}`,
+        combinedGas: `0x${string}`,
+        encodedPreOps: `0x${string}`[],
+        initData: `0x${string}`,
+        prePaymentAmount: `0x${string}`,
+        totalPaymentAmount: `0x${string}`,
+        paymentRecipient: `0x${string}`,
+        signature: `0x${string}`,
+        paymentSignature: `0x${string}`,
+        supportedDelegationImplementation: `0x${string}`,
+      },
+      txGas: `0x${string}`,
+      nativeFeeEstimate: {
+        maxFeePerGas: number,
+        maxPriorityFeePerGas: number,
+      },
+      // UNIX timestamp the quote expires at.
+      ttl: number,
+      authorizationAddress?: `0x${string}`,
+      entrypoint: `0x${string}`,
+      // The RPC servers signature over the quote.
+      signature: {
+        y_parity: bool,
+        r: `0x${string}`,
+        s: `0x${string}`,
+      },
+      // The hash of the quote.
+      hash: `0x${string}`,
+    },
+  } | {
+    preOp: {
+      eoa: `0x${string}`,
+      executionData: `0x${string}`,
+      nonce: `0x${string}`,
+      signature: `0x${string}`,
+    },
+  },
+  // the digest of the bundle that the user needs to sign
+  digest: `0x${string}`,
+  // EIP-712 typed data of the preop or bundle.
+  typedData: any,
+  // capabilities assigned to the account
+  capabilities: {
+    authorizeKeys: {
+      // key hash
+      hash: `0x${string}`,
+      // See "Keys"
+      key: {
+        expiry?: number,
+        type: 'p256' | 'webauthnp256' | 'secp256k1',
+        role: 'admin' | 'normal' | 'session',
+        publicKey: `0x${string}`,
+      },
+      permissions: ({
+        type: 'call',
+        // See "Selectors"
+        selector: string,
+        to: `0x${string}`,
+      } | {
+        type: 'spend',
+        limit: number,
+        period: 'minute' | 'hour' | 'day' | 'week' | 'month' | 'year',
+        // defaults to the native token (address zero)
+        token?: `0x${string}`,
+      })[],
+    }[],
+    // Key revocations.
+    revokeKeys: {
+      hash: `0x${string}`,
+      id?: `0x${string}`
+    }[],
+    // Simulated asset diffs, where the first element of the tuple is the recipient or sender.
+    assetDiff: [
+       `0x${string}`,
+       {
+         // Omitted if this is the native token.
+         address?: `0x${string}`,
+         decimals?: number,
+         direction: 'incoming' | 'outgoing',
+         name?: string,
+         symbol?: string,
+         type?: 'erc20' | 'erc721',
+         uri?: string,
+         // For ERC721, the asset ID. For ERC20 the value moved.
+         value: number,
+       }[]
+    ][],
+  },
+  // The key that will be used to sign the bundle. See "Keys".
+  //
+  // It can be omitted in the case of a preop, see "Preops".
+  key?: {
path: apps/docs/pages/rpc-server/wallet_prepareUpgradeAccount.md
user: login: onbjerg
body: maybe we should rm then? if feasible
created_at: 2025-05-22T18:04:29Z
author_association: CONTRIBUTOR

PR comments:
user: login: changeset-bot[bot]
created_at: 2025-05-22T16:18:40Z
body: ###  ⚠️  No Changeset found

Latest commit: 3a89af1063961b80c850228bcd7591d91395901f

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. **If these changes should result in a version bump, you need to add a changeset.**

<details><summary>This PR includes no changesets</summary>

  When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

</details>

[Click here to learn what changesets are, and how to add one](https://github.com/changesets/changesets/blob/main/docs/adding-a-changeset.md).

[Click here if you're a maintainer who wants to add a changeset to this PR](https://github.com/ithacaxyz/porto/new/onbjerg/wallet-prepareupgradeaccount?filename=.changeset/old-pumpkins-travel.md&value=---%0A%22docs%22%3A%20patch%0A---%0A%0Adocs(rpc-server)%3A%20%60wallet_prepareUpgradeAccount%60%0A)


author_association: NONE
user: login: vercel[bot]
created_at: 2025-05-22T16:18:42Z
body: [vc]: #CHzX9ksEkmlxrcn4PLHKAc1vKS1s7T8yHyd0fXpPWkk=:eyJpc01vbm9yZXBvIjp0cnVlLCJ0eXBlIjoiZ2l0aHViIiwicHJvamVjdHMiOlt7Im5hbWUiOiJ3YWdtaS5wb3J0byIsInJvb3REaXJlY3RvcnkiOiJhcHBzL3dhZ21pIiwiaW5zcGVjdG9yVXJsIjoiaHR0cHM6Ly92ZXJjZWwuY29tL2l0aGFjYXh5ei93YWdtaS5wb3J0by80aXJ5clZoeTlXQlE3QkFaclZWTWo1OGgxemFqIiwicHJldmlld1VybCI6IndhZ21pcG9ydG8tZ2l0LW9uYmplcmctd2FsbGV0LXByZXBhcmV1cGdyYWRlYWNjb3VudC1pdGhhY2F4eXoudmVyY2VsLmFwcCIsIm5leHRDb21taXRTdGF0dXMiOiJERVBMT1lFRCIsImxpdmVGZWVkYmFjayI6eyJyZXNvbHZlZCI6MCwidW5yZXNvbHZlZCI6MCwidG90YWwiOjAsImxpbmsiOiJ3YWdtaXBvcnRvLWdpdC1vbmJqZXJnLXdhbGxldC1wcmVwYXJldXBncmFkZWFjY291bnQtaXRoYWNheHl6LnZlcmNlbC5hcHAifX0seyJuYW1lIjoiZGlhbG9nLnBvcnRvIiwicm9vdERpcmVjdG9yeSI6ImFwcHMvZGlhbG9nIiwiaW5zcGVjdG9yVXJsIjoiaHR0cHM6Ly92ZXJjZWwuY29tL2l0aGFjYXh5ei9kaWFsb2cucG9ydG8vcDVyUkxOOWJVd2dNVEJOb3pBNmFpRXVKcU05ZyIsInByZXZpZXdVcmwiOiJkaWFsb2dwb3J0by1naXQtb25iamVyZy13YWxsZXQtcHJlcGFyZXVwZ3JhZGVhY2NvdW50LWl0aGFjYXh5ei52ZXJjZWwuYXBwIiwibmV4dENvbW1pdFN0YXR1cyI6IkRFUExPWUVEIiwibGl2ZUZlZWRiYWNrIjp7InJlc29sdmVkIjowLCJ1bnJlc29sdmVkIjowLCJ0b3RhbCI6MCwibGluayI6ImRpYWxvZ3BvcnRvLWdpdC1vbmJqZXJnLXdhbGxldC1wcmVwYXJldXBncmFkZWFjY291bnQtaXRoYWNheHl6LnZlcmNlbC5hcHAifX0seyJuYW1lIjoibWFuYWdlLnBvcnRvIiwicm9vdERpcmVjdG9yeSI6ImFwcHMvbWFuYWdlciIsImluc3BlY3RvclVybCI6Imh0dHBzOi8vdmVyY2VsLmNvbS9pdGhhY2F4eXovbWFuYWdlLnBvcnRvLzJUSnp6dDlXTEFkc0dna3ZWR0V4a29ROXhhUXEiLCJwcmV2aWV3VXJsIjoibWFuYWdlcG9ydG8tZ2l0LW9uYmplcmctd2FsbGV0LXByZXBhcmV1cGdyYWRlYWNjb3VudC1pdGhhY2F4eXoudmVyY2VsLmFwcCIsIm5leHRDb21taXRTdGF0dXMiOiJERVBMT1lFRCIsImxpdmVGZWVkYmFjayI6eyJyZXNvbHZlZCI6MCwidW5yZXNvbHZlZCI6MCwidG90YWwiOjAsImxpbmsiOiJtYW5hZ2Vwb3J0by1naXQtb25iamVyZy13YWxsZXQtcHJlcGFyZXVwZ3JhZGVhY2NvdW50LWl0aGFjYXh5ei52ZXJjZWwuYXBwIn19LHsibmFtZSI6ImlkLnBvcnRvIiwicm9vdERpcmVjdG9yeSI6ImFwcHMvcHJveHkiLCJpbnNwZWN0b3JVcmwiOiJodHRwczovL3ZlcmNlbC5jb20vaXRoYWNheHl6L2lkLnBvcnRvLzU1cjJ0dkdGNURKUGYyUDJyOXI3VFp0SGhGeGsiLCJwcmV2aWV3VXJsIjoiaWRwb3J0by1naXQtb25iamVyZy13YWxsZXQtcHJlcGFyZXVwZ3JhZGVhY2NvdW50LWl0aGFjYXh5ei52ZXJjZWwuYXBwIiwibmV4dENvbW1pdFN0YXR1cyI6IkRFUExPWUVEIiwibGl2ZUZlZWRiYWNrIjp7InJlc29sdmVkIjowLCJ1bnJlc29sdmVkIjowLCJ0b3RhbCI6MCwibGluayI6ImlkcG9ydG8tZ2l0LW9uYmplcmctd2FsbGV0LXByZXBhcmV1cGdyYWRlYWNjb3VudC1pdGhhY2F4eXoudmVyY2VsLmFwcCJ9fSx7Im5hbWUiOiJwbGF5Z3JvdW5kLnBvcnRvIiwicm9vdERpcmVjdG9yeSI6ImFwcHMvcGxheWdyb3VuZCIsImluc3BlY3RvclVybCI6Imh0dHBzOi8vdmVyY2VsLmNvbS9pdGhhY2F4eXovcGxheWdyb3VuZC5wb3J0by80Q2JNYlViRjI0a2RtaTk0VFRuS3ZRemozYm1iIiwicHJldmlld1VybCI6InBsYXlncm91bmRwb3J0by1naXQtb25iamVyZy13YWxsZXQtcHJlcGFyZXVwZ3ItNWQ1MWVhLWl0aGFjYXh5ei52ZXJjZWwuYXBwIiwibmV4dENvbW1pdFN0YXR1cyI6IkRFUExPWUVEIiwibGl2ZUZlZWRiYWNrIjp7InJlc29sdmVkIjowLCJ1bnJlc29sdmVkIjowLCJ0b3RhbCI6MCwibGluayI6InBsYXlncm91bmRwb3J0by1naXQtb25iamVyZy13YWxsZXQtcHJlcGFyZXVwZ3ItNWQ1MWVhLWl0aGFjYXh5ei52ZXJjZWwuYXBwIn19LHsibmFtZSI6ImRvY3MucG9ydG8iLCJyb290RGlyZWN0b3J5IjoiYXBwcy9kb2NzIiwiaW5zcGVjdG9yVXJsIjoiaHR0cHM6Ly92ZXJjZWwuY29tL2l0aGFjYXh5ei9kb2NzLnBvcnRvL0Jrb3RMQmlmU0FhbWdIcFg1TlI1bjJpNnJCQmEiLCJwcmV2aWV3VXJsIjoiZG9jc3BvcnRvLWdpdC1vbmJqZXJnLXdhbGxldC1wcmVwYXJldXBncmFkZWFjY291bnQtaXRoYWNheHl6LnZlcmNlbC5hcHAiLCJuZXh0Q29tbWl0U3RhdHVzIjoiREVQTE9ZRUQiLCJsaXZlRmVlZGJhY2siOnsicmVzb2x2ZWQiOjAsInVucmVzb2x2ZWQiOjAsInRvdGFsIjowLCJsaW5rIjoiZG9jc3BvcnRvLWdpdC1vbmJqZXJnLXdhbGxldC1wcmVwYXJldXBncmFkZWFjY291bnQtaXRoYWNheHl6LnZlcmNlbC5hcHAifX1dfQ==
**The latest updates on your projects**. Learn more about [Vercel for Git ↗︎](https://vercel.link/github-learn-more)

| Name | Status | Preview | Comments | Updated (UTC) |
| :--- | :----- | :------ | :------- | :------ |
| **dialog.porto** | ✅ Ready ([Inspect](https://vercel.com/ithacaxyz/dialog.porto/p5rRLN9bUwgMTBNozA6aiEuJqM9g)) | [Visit Preview](https://dialogporto-git-onbjerg-wallet-prepareupgradeaccount-ithacaxyz.vercel.app) | 💬 [**Add feedback**](https://vercel.live/open-feedback/dialogporto-git-onbjerg-wallet-prepareupgradeaccount-ithacaxyz.vercel.app?via=pr-comment-feedback-link) | May 22, 2025 6:32pm |
| **docs.porto** | ✅ Ready ([Inspect](https://vercel.com/ithacaxyz/docs.porto/BkotLBifSAamgHpX5NR5n2i6rBBa)) | [Visit Preview](https://docsporto-git-onbjerg-wallet-prepareupgradeaccount-ithacaxyz.vercel.app) | 💬 [**Add feedback**](https://vercel.live/open-feedback/docsporto-git-onbjerg-wallet-prepareupgradeaccount-ithacaxyz.vercel.app?via=pr-comment-feedback-link) | May 22, 2025 6:32pm |
| **id.porto** | ✅ Ready ([Inspect](https://vercel.com/ithacaxyz/id.porto/55r2tvGF5DJPf2P2r9r7TZtHhFxk)) | [Visit Preview](https://idporto-git-onbjerg-wallet-prepareupgradeaccount-ithacaxyz.vercel.app) | 💬 [**Add feedback**](https://vercel.live/open-feedback/idporto-git-onbjerg-wallet-prepareupgradeaccount-ithacaxyz.vercel.app?via=pr-comment-feedback-link) | May 22, 2025 6:32pm |
| **manage.porto** | ✅ Ready ([Inspect](https://vercel.com/ithacaxyz/manage.porto/2TJzzt9WLAdsGgkvVGExkoQ9xaQq)) | [Visit Preview](https://manageporto-git-onbjerg-wallet-prepareupgradeaccount-ithacaxyz.vercel.app) | 💬 [**Add feedback**](https://vercel.live/open-feedback/manageporto-git-onbjerg-wallet-prepareupgradeaccount-ithacaxyz.vercel.app?via=pr-comment-feedback-link) | May 22, 2025 6:32pm |
| **playground.porto** | ✅ Ready ([Inspect](https://vercel.com/ithacaxyz/playground.porto/4CbMbUbF24kdmi94TTnKvQzj3bmb)) | [Visit Preview](https://playgroundporto-git-onbjerg-wallet-prepareupgr-5d51ea-ithacaxyz.vercel.app) | 💬 [**Add feedback**](https://vercel.live/open-feedback/playgroundporto-git-onbjerg-wallet-prepareupgr-5d51ea-ithacaxyz.vercel.app?via=pr-comment-feedback-link) | May 22, 2025 6:32pm |
| **wagmi.porto** | ✅ Ready ([Inspect](https://vercel.com/ithacaxyz/wagmi.porto/4iryrVhy9WBQ7BAZrVVMj58h1zaj)) | [Visit Preview](https://wagmiporto-git-onbjerg-wallet-prepareupgradeaccount-ithacaxyz.vercel.app) | 💬 [**Add feedback**](https://vercel.live/open-feedback/wagmiporto-git-onbjerg-wallet-prepareupgradeaccount-ithacaxyz.vercel.app?via=pr-comment-feedback-link) | May 22, 2025 6:32pm |







author_association: NONE