Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.rhinestone.dev/llms.txt

Use this file to discover all available pages before exploring further.

The ECDSA signer uses the Ownable Validator module to authorise transactions with standard Ethereum key pairs. It supports both single-owner and multisig (n/m threshold) configurations on the same account.

Single owner

To create an account owned by a single ECDSA key:
import { RhinestoneSDK } from '@rhinestone/sdk'
import { privateKeyToAccount } from 'viem/accounts'

const signer = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`)

const rhinestone = new RhinestoneSDK({
  apiKey: process.env.RHINESTONE_API_KEY as string,
})

const account = await rhinestone.createAccount({
  owners: {
    type: 'ecdsa',
    accounts: [signer],
  },
})

Multisig

The Ownable Validator supports n/m multisig with multiple owners and a configurable signature threshold. This is useful for shared accounts, treasury management, or any scenario where unilateral control is a risk.

Setup

Pass multiple accounts and set a threshold. The threshold defines how many signatures are required to authorise a transaction.
const account = await rhinestone.createAccount({
  owners: {
    type: 'ecdsa',
    accounts: [signerA, signerB, signerC],
    threshold: 2, // 2-of-3
  },
})
By default, threshold is 1, meaning any single owner can sign.

Signing with a subset of owners

For m-of-n setups, specify which signers to use when sending a transaction:
const transaction = await account.prepareTransaction({
  chain,
  calls: [
    // …
  ],
  signers: {
    type: 'owner',
    kind: 'ecdsa',
    accounts: [signerA, signerB],
  },
})
Provide at least as many signers as the threshold requires, or the transaction will fail validation.

Add a signer

import { addOwner } from '@rhinestone/sdk/actions/ecdsa'

const transaction = await account.prepareTransaction({
  chain,
  calls: [addOwner(newSigner.address)],
})

Remove a signer

import { removeOwner } from '@rhinestone/sdk/actions/ecdsa'

const transaction = await account.prepareTransaction({
  chain,
  calls: [removeOwner(signerC.address)],
})

Change threshold

import { changeThreshold } from '@rhinestone/sdk/actions/ecdsa'

const transaction = await account.prepareTransaction({
  chain,
  calls: [changeThreshold(2)],
})

Get current owners

const owners = await account.getOwners(chain)
// → { accounts: ['0xaaa…', '0xbbb…', '0xccc…'], threshold: 2 }

Enable in a separate transaction

If you need to enable the ECDSA module on an existing account rather than at creation time:
import { enable as enableEcdsa } from '@rhinestone/sdk/actions/ecdsa'

const transaction = await account.prepareTransaction({
  chain,
  calls: [enableEcdsa([signer.address])],
  tokenRequests: [],
})

Validator versions

The SDK ships with two Ownable Validator implementations. The default is the latest version, which supports EIP-712 legible signing — wallet prompts show structured typed data instead of an opaque hash — but accepts only raw 65-byte ECDSA signatures. The legacy validator (0x2483da3a338895199e5e538530213157e931bf06) does not support EIP-712 legibility, but it accepts ERC-1271 contract signatures, so you can use a smart account (e.g., a Safe) as an owner. To use the legacy validator, pass its address as module:
const account = await rhinestone.createAccount({
  owners: {
    type: 'ecdsa',
    accounts: [smartAccountSigner],
    module: '0x2483da3a338895199e5e538530213157e931bf06',
  },
})