Skip to content

# Sequence diagram for ChainSessionManager cached signed call reuse on relay #266

@Dargon789

Description

@Dargon789

Reviewer's Guide

Adds new userdata service RPC methods and types, introduces caching of recently signed calls in the dapp ChainSessionManager to avoid redundant signing, and bumps multiple packages to 3.0.0-beta.12 with corresponding changelog and changeset updates for a beta release with dapp connector fixes.

Sequence diagram for ChainSessionManager cached signed call reuse on relay

sequenceDiagram
  actor Dapp
  participant ChainSessionManager
  participant Relayer

  Dapp->>ChainSessionManager: sendTransaction(transactions)
  ChainSessionManager->>ChainSessionManager: build callsToSend from transactions
  ChainSessionManager->>ChainSessionManager: cached = _getCachedSignedCall(callsToSend)
  alt cache hit and not expired
    ChainSessionManager-->>ChainSessionManager: use cached.signedCall
  else cache miss or expired or fingerprint mismatch
    ChainSessionManager->>ChainSessionManager: signedCalls = _buildAndSignCalls(callsToSend)
    ChainSessionManager->>ChainSessionManager: fingerprint = _fingerprintCalls(callsToSend)
    ChainSessionManager->>ChainSessionManager: lastSignedCallCache = { fingerprint, signedCall, createdAtMs }
  end
  ChainSessionManager->>Relayer: relay(signedCalls.to, signedCalls.data, chainId)
  Relayer-->>ChainSessionManager: hash
  ChainSessionManager->>Relayer: waitForTransactionReceipt(hash.opHash, chainId)
  Relayer-->>ChainSessionManager: status
  ChainSessionManager-->>Dapp: transaction result based on status
Loading

ER diagram for wallet userdata entities introduced in userdata service

erDiagram
  Wallet {
    string address PK
    number ecosystem
    string updatedAt
    string createdAt
  }

  WalletPreferences {
    string walletAddress FK
    boolean manualSigning
    boolean hideUnlistedTokens
    boolean includeTestnets
    string currency
  }

  WalletSigner {
    string walletAddress FK
    string signerAddress
    string kind
    string email
    string updatedAt
    string createdAt
  }

  Session {
    string walletAddress FK
    string sessionAddress
    string appUrl
    string updatedAt
    string createdAt
  }

  Contact {
    string walletAddress FK
    string contactAddress
    string nickname
    string updatedAt
    string createdAt
  }

  WatchedWallet {
    string walletAddress FK
    string watchedAddress
    string nickname
    string updatedAt
    string createdAt
  }

  DiscoverFavorite {
    string walletAddress FK
    int id
    string dappId
    string createdAt
  }

  DiscoverHistory {
    string walletAddress FK
    int id
    string dappId
    string accessedAt
  }

  TokenFavorite {
    string walletAddress FK
    int id
    string chainId
    string contractAddress
    string tokenId
    string createdAt
  }

  Wallet ||--|| WalletPreferences : has
  Wallet ||--o{ WalletSigner : has
  Wallet ||--o{ Session : has
  Wallet ||--o{ Contact : has
  Wallet ||--o{ WatchedWallet : has
  Wallet ||--o{ DiscoverFavorite : has
  Wallet ||--o{ DiscoverHistory : has
  Wallet ||--o{ TokenFavorite : has
Loading

Class diagram for new userdata service RPC types and client

classDiagram
  class UserDataClient {
    <<interface>>
    +getCapabilities(headers, signal) GetCapabilitiesResponse
    +getAccessToken(req, headers, signal) GetAccessTokenResponse
    +getIdentityToken(req, headers, signal) GetIdentityTokenResponse
    +getWalletPreferences(req, headers, signal) GetWalletPreferencesResponse
    +putWalletPreferences(req, headers, signal) PutWalletPreferencesResponse
    +listWalletSigners(req, headers, signal) ListWalletSignersResponse
    +putWalletSigner(req, headers, signal) PutWalletSignerResponse
    +deleteWalletSigner(req, headers, signal) DeleteWalletSignerResponse
    +listSessions(req, headers, signal) ListSessionsResponse
    +putSession(req, headers, signal) PutSessionResponse
    +deleteSession(req, headers, signal) DeleteSessionResponse
    +listContacts(req, headers, signal) ListContactsResponse
    +putContact(req, headers, signal) PutContactResponse
    +deleteContact(req, headers, signal) DeleteContactResponse
    +listWatchedWallets(req, headers, signal) ListWatchedWalletsResponse
    +putWatchedWallet(req, headers, signal) PutWatchedWalletResponse
    +deleteWatchedWallet(req, headers, signal) DeleteWatchedWalletResponse
    +listDiscoverFavorites(req, headers, signal) ListDiscoverFavoritesResponse
    +putDiscoverFavorite(req, headers, signal) PutDiscoverFavoriteResponse
    +deleteDiscoverFavorite(req, headers, signal) DeleteDiscoverFavoriteResponse
    +listDiscoverHistory(req, headers, signal) ListDiscoverHistoryResponse
    +putDiscoverHistory(req, headers, signal) PutDiscoverHistoryResponse
    +deleteDiscoverHistory(req, headers, signal) DeleteDiscoverHistoryResponse
    +listTokenFavorites(req, headers, signal) ListTokenFavoritesResponse
    +putTokenFavorite(req, headers, signal) PutTokenFavoriteResponse
    +deleteTokenFavorite(req, headers, signal) DeleteTokenFavoriteResponse
  }

  class UserData {
    -fetch
    -url
    +getCapabilities(headers, signal) GetCapabilitiesResponse
    +getAccessToken(req, headers, signal) GetAccessTokenResponse
    +getIdentityToken(req, headers, signal) GetIdentityTokenResponse
    +getWalletPreferences(req, headers, signal) GetWalletPreferencesResponse
    +putWalletPreferences(req, headers, signal) PutWalletPreferencesResponse
    +listWalletSigners(req, headers, signal) ListWalletSignersResponse
    +putWalletSigner(req, headers, signal) PutWalletSignerResponse
    +deleteWalletSigner(req, headers, signal) DeleteWalletSignerResponse
    +listSessions(req, headers, signal) ListSessionsResponse
    +putSession(req, headers, signal) PutSessionResponse
    +deleteSession(req, headers, signal) DeleteSessionResponse
    +listContacts(req, headers, signal) ListContactsResponse
    +putContact(req, headers, signal) PutContactResponse
    +deleteContact(req, headers, signal) DeleteContactResponse
    +listWatchedWallets(req, headers, signal) ListWatchedWalletsResponse
    +putWatchedWallet(req, headers, signal) PutWatchedWalletResponse
    +deleteWatchedWallet(req, headers, signal) DeleteWatchedWalletResponse
    +listDiscoverFavorites(req, headers, signal) ListDiscoverFavoritesResponse
    +putDiscoverFavorite(req, headers, signal) PutDiscoverFavoriteResponse
    +deleteDiscoverFavorite(req, headers, signal) DeleteDiscoverFavoriteResponse
    +listDiscoverHistory(req, headers, signal) ListDiscoverHistoryResponse
    +putDiscoverHistory(req, headers, signal) PutDiscoverHistoryResponse
    +deleteDiscoverHistory(req, headers, signal) DeleteDiscoverHistoryResponse
    +listTokenFavorites(req, headers, signal) ListTokenFavoritesResponse
    +putTokenFavorite(req, headers, signal) PutTokenFavoriteResponse
    +deleteTokenFavorite(req, headers, signal) DeleteTokenFavoriteResponse
  }

  UserDataClient <|.. UserData

  class Wallet {
    +string address
    +number ecosystem
    +WalletPreferences preferences
    +string updatedAt
    +string createdAt
  }

  class WalletPreferences {
    +boolean manualSigning
    +boolean hideUnlistedTokens
    +boolean includeTestnets
    +string currency
  }

  class WalletSigner {
    +string walletAddress
    +string signerAddress
    +string kind
    +string email
    +string updatedAt
    +string createdAt
  }

  class WalletSignerProps {
    +string address
    +string kind
    +string email
  }

  class Session {
    +string walletAddress
    +string sessionAddress
    +string appUrl
    +string updatedAt
    +string createdAt
  }

  class SessionProps {
    +string address
    +string appUrl
  }

  class Contact {
    +string walletAddress
    +string contactAddress
    +string nickname
    +string updatedAt
    +string createdAt
  }

  class ContactProps {
    +string address
    +string nickname
  }

  class WatchedWallet {
    +string walletAddress
    +string watchedAddress
    +string nickname
    +string updatedAt
    +string createdAt
  }

  class WatchedWalletProps {
    +string watchedAddress
    +string nickname
  }

  class DiscoverFavorite {
    +string walletAddress
    +number id
    +string dappId
    +string createdAt
  }

  class DiscoverHistory {
    +string walletAddress
    +number id
    +string dappId
    +string accessedAt
  }

  class DiscoverProps {
    +string dappId
  }

  class TokenFavorite {
    +string walletAddress
    +number id
    +string chainId
    +string contractAddress
    +string tokenId
    +string createdAt
  }

  class TokenFavoriteProps {
    +string chainId
    +string contractAddress
    +string tokenId
  }

  class GetWalletPreferencesRequest {
    +string wallet
  }

  class GetWalletPreferencesResponse {
    +WalletPreferences preferences
  }

  class PutWalletPreferencesRequest {
    +string wallet
    +WalletPreferences preferences
  }

  class PutWalletPreferencesResponse {
  }

  class ListWalletSignersRequest {
    +string wallet
    +number pageSize
    +string cursor
  }

  class ListWalletSignersResponse {
    +WalletSigner[] signers
    +string nextCursor
  }

  class PutWalletSignerRequest {
    +string wallet
    +WalletSignerProps signer
  }

  class PutWalletSignerResponse {
    +WalletSigner signer
  }

  class DeleteWalletSignerRequest {
    +string wallet
    +string signer
  }

  class DeleteWalletSignerResponse {
  }

  class ListSessionsRequest {
    +string wallet
    +number pageSize
    +string cursor
  }

  class ListSessionsResponse {
    +Session[] sessions
    +string nextCursor
  }

  class PutSessionRequest {
    +string wallet
    +SessionProps session
  }

  class PutSessionResponse {
    +Session session
  }

  class DeleteSessionRequest {
    +string wallet
    +string session
  }

  class DeleteSessionResponse {
  }

  class ListContactsRequest {
    +string wallet
    +number pageSize
    +string cursor
  }

  class ListContactsResponse {
    +Contact[] contacts
    +string nextCursor
  }

  class PutContactRequest {
    +string wallet
    +ContactProps contact
  }

  class PutContactResponse {
    +Contact contact
  }

  class DeleteContactRequest {
    +string wallet
    +string contact
  }

  class DeleteContactResponse {
  }

  class ListWatchedWalletsRequest {
    +string wallet
    +number pageSize
    +string cursor
  }

  class ListWatchedWalletsResponse {
    +WatchedWallet[] watchedWallets
    +string nextCursor
  }

  class PutWatchedWalletRequest {
    +string wallet
    +WatchedWalletProps watchedWallet
  }

  class PutWatchedWalletResponse {
    +WatchedWallet watchedWallet
  }

  class DeleteWatchedWalletRequest {
    +string wallet
    +string watchedWallet
  }

  class DeleteWatchedWalletResponse {
  }

  class ListDiscoverFavoritesRequest {
    +string wallet
    +number pageSize
    +string cursor
  }

  class ListDiscoverFavoritesResponse {
    +DiscoverFavorite[] favorites
    +string nextCursor
  }

  class PutDiscoverFavoriteRequest {
    +string wallet
    +DiscoverProps favorite
  }

  class PutDiscoverFavoriteResponse {
    +DiscoverFavorite favorite
  }

  class DeleteDiscoverFavoriteRequest {
    +string wallet
    +number id
  }

  class DeleteDiscoverFavoriteResponse {
  }

  class ListDiscoverHistoryRequest {
    +string wallet
    +number pageSize
    +string cursor
  }

  class ListDiscoverHistoryResponse {
    +DiscoverHistory[] history
    +string nextCursor
  }

  class PutDiscoverHistoryRequest {
    +string wallet
    +DiscoverProps history
  }

  class PutDiscoverHistoryResponse {
    +DiscoverHistory history
  }

  class DeleteDiscoverHistoryRequest {
    +string wallet
    +number id
  }

  class DeleteDiscoverHistoryResponse {
  }

  class ListTokenFavoritesRequest {
    +string wallet
    +number pageSize
    +string cursor
  }

  class ListTokenFavoritesResponse {
    +TokenFavorite[] favorites
    +string nextCursor
  }

  class PutTokenFavoriteRequest {
    +string wallet
    +TokenFavoriteProps favorite
  }

  class PutTokenFavoriteResponse {
    +TokenFavorite favorite
  }

  class DeleteTokenFavoriteRequest {
    +string wallet
    +number id
  }

  class DeleteTokenFavoriteResponse {
  }

  UserData --> GetWalletPreferencesRequest
  UserData --> GetWalletPreferencesResponse
  UserData --> PutWalletPreferencesRequest
  UserData --> PutWalletPreferencesResponse
  UserData --> ListWalletSignersRequest
  UserData --> ListWalletSignersResponse
  UserData --> PutWalletSignerRequest
  UserData --> PutWalletSignerResponse
  UserData --> DeleteWalletSignerRequest
  UserData --> DeleteWalletSignerResponse
  UserData --> ListSessionsRequest
  UserData --> ListSessionsResponse
  UserData --> PutSessionRequest
  UserData --> PutSessionResponse
  UserData --> DeleteSessionRequest
  UserData --> DeleteSessionResponse
  UserData --> ListContactsRequest
  UserData --> ListContactsResponse
  UserData --> PutContactRequest
  UserData --> PutContactResponse
  UserData --> DeleteContactRequest
  UserData --> DeleteContactResponse
  UserData --> ListWatchedWalletsRequest
  UserData --> ListWatchedWalletsResponse
  UserData --> PutWatchedWalletRequest
  UserData --> PutWatchedWalletResponse
  UserData --> DeleteWatchedWalletRequest
  UserData --> DeleteWatchedWalletResponse
  UserData --> ListDiscoverFavoritesRequest
  UserData --> ListDiscoverFavoritesResponse
  UserData --> PutDiscoverFavoriteRequest
  UserData --> PutDiscoverFavoriteResponse
  UserData --> DeleteDiscoverFavoriteRequest
  UserData --> DeleteDiscoverFavoriteResponse
  UserData --> ListDiscoverHistoryRequest
  UserData --> ListDiscoverHistoryResponse
  UserData --> PutDiscoverHistoryRequest
  UserData --> PutDiscoverHistoryResponse
  UserData --> DeleteDiscoverHistoryRequest
  UserData --> DeleteDiscoverHistoryResponse
  UserData --> ListTokenFavoritesRequest
  UserData --> ListTokenFavoritesResponse
  UserData --> PutTokenFavoriteRequest
  UserData --> PutTokenFavoriteResponse
  UserData --> DeleteTokenFavoriteRequest
  UserData --> DeleteTokenFavoriteResponse

  Wallet --> WalletPreferences
Loading

File-Level Changes

Change Details Files
Expand userdata WebRPC schema, client interface, and TypeScript types to support wallet preferences, signers, sessions, contacts, watched wallets, discover data, and token favorites.
  • Update generated schema metadata hash to match new RIDL schema
  • Extend UserDataClient with CRUD-style methods for wallet preferences, wallet signers, sessions, contacts, watched wallets, discover favorites/history, and token favorites
  • Introduce new schema types and props (Version, RuntimeStatus, WalletPreferences, WalletSigner, Contact, WatchedWallet, DiscoverFavorite/History, TokenFavorite, and related *Props interfaces)
  • Define request/response interfaces for the new RPC methods
  • Add corresponding UserData class methods that encode requests, call fetch with appropriate method names, and decode responses
packages/services/userdata/src/userdata.gen.ts
Optimize dapp-client transaction handling by caching the last signed call and reusing it when identical calls are sent shortly after, while simplifying permission error handling.
  • Add lastSignedCallCache state to ChainSessionManager to store fingerprinted calls, signed payload, and creation timestamp
  • Remove special-case retry logic for expired signer errors in hasPermission, now treating signer resolution failures as permission failures
  • Cache signed calls in getTransactionsFeeOptions using a call fingerprint, with a short TTL, before querying relayer fee options
  • Reuse cached signed call in sendTransact when the call set matches the cached fingerprint, otherwise rebuild and sign
  • Implement _getCachedSignedCall with TTL-based invalidation and fingerprint equality check
  • Implement _fingerprintCalls to JSON-stringify normalized call fields (to, value, data, gasLimit, delegateCall, onlyFallback, behaviorOnError) with error handling
packages/wallet/dapp-client/src/ChainSessionManager.ts
Publish 3.0.0-beta.12 across core, wallet, services, and utils packages with notes about dapp connector fixes and dependency bumps.
  • Add 3.0.0-beta.11 and 3.0.0-beta.12 entries to multiple package CHANGELOGs describing beta and dapp connector fixes
  • Bump package.json versions for services, wallet, and abi-related packages from 3.0.0-beta.10 to 3.0.0-beta.12
  • Add new changeset files describing patch releases for all affected packages (3.0.0 beta and beta.12 with dapp connector fixes)
packages/wallet/wdk/CHANGELOG.md
packages/wallet/dapp-client/CHANGELOG.md
packages/wallet/core/CHANGELOG.md
packages/services/relayer/CHANGELOG.md
packages/services/api/CHANGELOG.md
packages/services/builder/CHANGELOG.md
packages/services/guard/CHANGELOG.md
packages/services/identity-instrument/CHANGELOG.md
packages/services/indexer/CHANGELOG.md
packages/services/marketplace/CHANGELOG.md
packages/services/metadata/CHANGELOG.md
packages/services/userdata/CHANGELOG.md
packages/utils/abi/CHANGELOG.md
packages/wallet/primitives/CHANGELOG.md
packages/services/api/package.json
packages/services/builder/package.json
packages/services/guard/package.json
packages/services/identity-instrument/package.json
packages/services/indexer/package.json
packages/services/marketplace/package.json
packages/services/metadata/package.json
packages/services/relayer/package.json
packages/services/userdata/package.json
packages/utils/abi/package.json
packages/wallet/core/package.json
packages/wallet/dapp-client/package.json
packages/wallet/primitives/package.json
packages/wallet/wdk/package.json
.changeset/bright-pots-hope.md
.changeset/free-tips-switch.md

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Originally posted by @sourcery-ai[bot] in #265 (comment)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    Status

    Done

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions