Create a EOA or SCW signer
XMTP SDKs support message signing with 2 different types of Ethereum accounts: Externally Owned Accounts (EOAs) and Smart Contract Wallets (SCWs). All SDK clients accept a signer object (or instance), which provides a method for signing messages.
Create an Externally Owned Account signer
The EOA signer must have 3 properties: the account type, a function that returns the account identifier, and a function that signs messages.
import type { Signer, Identifier } from "@xmtp/browser-sdk";
const accountIdentifier: Identifier = {
identifier: "0x...", // Ethereum address as the identifier
identifierKind: "Ethereum", // Specifies the identity type
};
const signer: Signer = {
type: "EOA",
getIdentifier: () => accountIdentifier,
signMessage: async (message: string): Uint8Array => {
// typically, signing methods return a hex string
// this string must be converted to bytes and returned in this function
},
};
Create a Smart Contract Wallet signer
The SCW signer has the same 3 required properties as the EOA signer, but also requires a function that returns the chain ID of the blockchain being used and an optional function that returns the block number to verify signatures against. If a function is not provided to retrieve the block number, the latest block number will be used.
Here is a list of supported chain IDs:
- chain_rpc_1 = string
- chain_rpc_8453 = string
- chain_rpc_42161 = string
- chain_rpc_10 = string
- chain_rpc_137 = string
- chain_rpc_324 = string
- chain_rpc_59144 = string
- chain_rpc_480 = string
Need support for a different chain ID? Please post your request to the XMTP Community Forums.
The details of creating an SCW signer are highly dependent on the wallet provider and the library you're using to interact with it. Here are some general guidelines to consider:
-
Wallet provider integration: Different wallet providers (Safe, Argent, Rainbow, etc.) have different methods for signing messages. See the wallet provider documentation for more details.
-
Library selection: Choose a library that supports your wallet provider (e.g., viem, ethers.js, web3.js). Each library has its own API for interacting with wallets. See the library documentation for more details.
-
Add an Ethereum-specific prefix: Before signing, Ethereum requires a specific prefix to be added to the message. To learn more, see ERC-191: Signed Data Standard. Libraries and wallet providers might add the prefix for you, so make sure you don't add the prefix twice.
-
Hash the prefixed message with Keccak-256: The prefixed message is hashed using the Keccak-256 algorithm, which is Ethereum's standard hashing algorithm. This step creates a fixed-length representation of the message, ensuring consistency and security. Note that some wallet providers might handle this hashing internally.
-
Sign the replay-safe hash: The replay-safe hash is signed using the private key of the SCW. This generates a cryptographic signature that proves ownership of the wallet and ensures the integrity of the message.
-
Convert the signature to a Uint8Array: The resulting signature is converted to a
Uint8Array
format, which is required by the XMTP SDK for compatibility and further processing.
The code snippets below are examples only and will need to be adapted based on your specific wallet provider and library.
export const createSCWSigner = (
address: `0x${string}`,
walletClient: WalletClient,
chainId: bigint,
): Signer => {
return {
type: "SCW",
getIdentifier: () => ({
identifier: address.toLowerCase(),
identifierKind: "Ethereum",
}),
signMessage: async (message: string) => {
const signature = await walletClient.signMessage({
account: address,
message,
});
return toBytes(signature);
},
getChainId: () => {
return chainId;
},
};