Skip to content

SingleOwnerAccount::new does not verify that the signer controls the supplied account address #155

Description

@sainathr19

Summary

SingleOwnerAccount::new accepts a signer and an address without verifying that the signer is actually authorized for that account.

This is not a protocol vulnerability—the account contract still rejects invalid signatures during transaction validation—but it can lead to a client-side footgun where an application treats account.address() as an address it can control, even though the configured signer cannot authorize transactions for that account.

Current behavior

SingleOwnerAccount::new simply stores the provided fields:

pub const fn new(
    provider: P,
    signer: S,
    address: Felt,
    chain_id: Felt,
    encoding: ExecutionEncoding,
) -> Self {
    Self {
        provider,
        signer,
        address,
        chain_id,
        block_id: BlockId::Tag(BlockTag::PreConfirmed),
        encoding,
    }
}

No relationship between the signer and account address is validated.

Why this matters

A mismatched signer/address pair is only discovered when the first transaction is submitted and rejected by the account contract.

However, many applications expose account.address() as a receive address immediately after construction. If that address does not correspond to the supplied signer, users may receive funds into an account they cannot operate.

For example:

  1. Construct a SingleOwnerAccount with the wrong signer.
  2. Publish account.address() as the deposit address.
  3. Funds are sent to that address.
  4. The first transaction fails because the signer is not authorized.

At that point, the caller has no way to spend those funds unless they possess the actual signing key for that account.

Why not verify in new

Performing verification during construction would introduce several problems:

  • Counterfactual accounts cannot be verified before deployment.
  • new is currently synchronous, infallible, and const; verification would require asynchronous RPC calls or signing operations.
  • Interactive signers (e.g. hardware wallets) would require user confirmation during construction.

Because of these constraints, automatic verification inside new does not seem practical.

Possible approaches

Some possibilities that might improve safety without breaking existing behavior:

  • Add an opt-in verify_signer() helper that validates the signer against the deployed account (e.g. via is_valid_signature).
  • Provide a new_verified(...) -> Result<Self, _> constructor alongside the existing new.
  • Add explicit documentation to new and/or address() clarifying that the signer/address relationship is not validated.
  • Document that accounts derived through AccountFactory are not affected since the address is deterministically derived from the signing key.

Questions

  • Is this considered a client-side safety issue worth addressing?
  • If so, would an explicit verification API be preferable to additional documentation?
  • Are there other approaches that fit the library's design better?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions