Skip to content

Signer

To connect to XMTP, your app needs a signer, an object that identifies a user and can sign messages on their behalf. Under the hood, XMTP uses ECDSA signatures on the secp256k1 curve.

There are three ways to create a signer:

  • Key pair: Generate or provide a secp256k1 private key directly. No wallet or blockchain needed.
  • Externally Owned Account (EOA): Use an Ethereum wallet.
  • Smart Contract Wallet (SCW): Use an ERC-1271 compatible smart contract wallet.

All three produce the same signer interface, so the rest of your XMTP integration is identical regardless of which you choose.

Generate a secp256k1 key pair and sign messages directly.

Install @noble/curves and @noble/hashes:

const
const signer: {
type: "EOA";
getIdentifier: () => {
identifier: string;
identifierKind: IdentifierKind;
};
signMessage: (message: string) => Promise<Uint8Array<ArrayBufferLike>>;
}
signer
= {
type: "EOA"
type
: "EOA" as
type const = "EOA"
const
,
getIdentifier: () => {
identifier: string;
identifierKind: IdentifierKind;
}
getIdentifier
: () => ({
identifier: string
identifier
:
address: string
address
,
identifierKind: IdentifierKind
identifierKind
:
enum IdentifierKind
IdentifierKind
.
function (enum member) IdentifierKind.Ethereum = 0
Ethereum
,
}),
signMessage: (message: string) => Promise<Uint8Array<ArrayBufferLike>>
signMessage
: async (
message: string
message
: string) =>
signatureBytes: Uint8Array<ArrayBufferLike>
signatureBytes
,
};
PartBrowser, NodeKotlinSwift
Typetype: "EOA" | "SCW"val type: SignerTypevar type: SignerType
IdentifiergetIdentifier()publicIdentityidentity
SignsignMessage(message): Uint8Arraysign(message): SignedDatasign(_ message): SignedData
Chain ID, SCWgetChainId(): bigintchainId: Long?chainId: Int64?
Block numbergetBlockNumber?(): bigintblockNumber: Long?blockNumber: Int64?

An EOA has no chain ID. Do not use 0 as a sentinel. Kotlin and Swift allow a null chain ID, but an SCW signer with no chain ID fails when it signs.

Return raw signature bytes, not a hex string. Convert the value returned by a wallet library when needed.

Most Ethereum wallet providers add the ERC-191 prefix and hash the message. If you add it again, verification fails. Check the wallet provider behavior.

An SCW signer uses the same interface and adds its chain ID. If verification needs historical state, it can also return a block number. The backend operator must configure the RPC endpoint for that chain.

To add a different identity kind, see Extend the identity model.

ErrorCause
AssociationError.ChainIdMismatchThe supplied chain ID does not match the registered identity
NotFound.InboxIdForAddressThe identifier is not registered on this backend
SignatureError.InvalidThe bytes, prefix, signer type, or SCW verification result is invalid