> ## 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.

# ECDSA signer

> Use an EOA as the owner of a smart account. Supports single key or n/m multisig.

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:

```ts theme={null}
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.

```ts theme={null}
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:

```ts theme={null}
const result = await account.sendTransaction({
  chain,
  calls: [
    // …
  ],
  signers: {
    type: 'owner',
    kind: 'ecdsa',
    accounts: [signerA, signerB],
  },
})
```

<Note>Provide at least as many signers as the threshold requires, or the transaction will fail validation.</Note>

### Add a signer

```ts theme={null}
import { addOwner } from '@rhinestone/sdk/actions/ecdsa'

await account.sendTransaction({
  chain,
  calls: [addOwner(newSigner.address)],
})
```

### Remove a signer

```ts theme={null}
import { removeOwner } from '@rhinestone/sdk/actions/ecdsa'

await account.sendTransaction({
  chain,
  calls: [removeOwner(signerC.address)],
})
```

### Change threshold

```ts theme={null}
import { changeThreshold } from '@rhinestone/sdk/actions/ecdsa'

await account.sendTransaction({
  chain,
  calls: [changeThreshold(2)],
})
```

### Get current owners

```ts theme={null}
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:

```ts theme={null}
import { enable as enableEcdsa } from '@rhinestone/sdk/actions/ecdsa'

await account.sendTransaction({
  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`:

```ts {5} theme={null}
const account = await rhinestone.createAccount({
  owners: {
    type: 'ecdsa',
    accounts: [smartAccountSigner],
    module: '0x2483da3a338895199e5e538530213157e931bf06',
  },
})
```
