Skip to main content

Generate Key & Sign

This example walks through the complete flow of generating an MPC key share, deriving an EVM address from it, building a transaction, and signing it with the MPC node network — all without the private key ever leaving the nodes.

Workflow canvas​

GENERATE_KEY_SHARE (curve: SECP256k1, threshold: 2)
│ keyId, rootPublicKey
â–¼
GET_EVM_DERIVATION_PATH (addressIndex: 0)
│ derivationPath
â–¼
COMPUTE_PUBLIC_KEY (keyId, derivationPath)
│ publicKey
â–¼
COMPUTE_EVM_ADDRESS (publicKey)
│ address
â–¼
BUILD_EVM_TRANSACTION (from: address, to: recipient, value: ...)
│ unsignedTransaction, serializedHash
â–¼
SIGN_WITH_KEY_SHARE (keyId, derivationPath, messageHash: serializedHash)
│ signature
â–¼
SIGN_EVM_TRANSACTION (unsignedTransaction, signature)
│ signedTransaction
â–¼
BROADCAST_EVM_TRANSACTION
│ txHash

TypeScript example​

import { WorkspaceClient, ComponentModule } from 'caller-sdk';

const workspace = new WorkspaceClient({ apiKey: process.env.WR_API_KEY! });

// Step 1: Generate a new MPC key share
// IMPORTANT: Store keyId permanently — losing it means losing the key forever.
const key = await workspace.call(ComponentModule.GENERATE_KEY_SHARE, {
curve: 'SECP256k1',
threshold: 2,
}).promise();

const { keyId, rootPublicKey } = key;
console.log('keyId:', keyId); // e.g. "3f8a2b1c-..."
console.log('rootPublicKey:', rootPublicKey);

// Persist keyId to your database before proceeding
await db.saveKeyId(keyId);

// Step 2: Derive the BIP-44 path for account 0, address index 0
const pathResult = await workspace.call(ComponentModule.GET_EVM_DERIVATION_PATH, {
addressIndex: 0,
accountIndex: 0,
changeIndex: 0,
}).promise();

const { derivationPath } = pathResult;

// Step 3: Compute the public key at this derivation path
const pubKeyResult = await workspace.call(ComponentModule.COMPUTE_PUBLIC_KEY, {
keyId,
derivationPath,
}).promise();

const { publicKey } = pubKeyResult;
console.log('publicKey:', publicKey); // compressed hex, 33 bytes

// Step 4: Derive the EVM address from the public key
const addressResult = await workspace.call(ComponentModule.COMPUTE_EVM_ADDRESS, {
publicKey,
}).promise();

const { address } = addressResult;
console.log('EVM address:', address); // e.g. "0xAbCd..."

// Step 5: Build an EVM transaction (sending 0.01 ETH to a recipient)
const buildResult = await workspace.call(ComponentModule.BUILD_EVM_TRANSACTION, {
from: address,
to: '0xRecipientAddress...',
value: '0x2386F26FC10000', // 0.01 ETH in wei (hex)
data: null,
chainId: 1, // Ethereum mainnet
gasLimit: null, // auto-estimated
gasPrice: null, // auto-estimated
nonce: null, // auto-fetched
jsonRpcUrl: 'https://mainnet.infura.io/v3/YOUR_KEY',
}).promise();

const { unsignedTransaction, serializedHash } = buildResult;

// Step 6: Sign the transaction hash with the MPC key share
const signResult = await workspace.call(ComponentModule.SIGN_WITH_KEY_SHARE, {
keyId,
derivationPath,
messageHash: serializedHash,
}).promise();

const { signature } = signResult;

// Step 7: Apply the signature to the unsigned transaction
const signedResult = await workspace.call(ComponentModule.SIGN_EVM_TRANSACTION, {
unsignedTransaction,
signature,
}).promise();

const { signedTransaction } = signedResult;

// Step 8: Broadcast to the network
const broadcastResult = await workspace.call(ComponentModule.BROADCAST_EVM_TRANSACTION, {
signedTransaction,
jsonRpcUrl: 'https://mainnet.infura.io/v3/YOUR_KEY',
}).promise();

console.log('Transaction hash:', broadcastResult.txHash);

Notes​

  • The keyId returned by GENERATE_KEY_SHARE is the sole identifier for your distributed key shares. Persist it to durable storage (database, secrets manager) before doing anything else.
  • COMPUTE_PUBLIC_KEY can be called with any MPC server holding a share — it does not require the signing threshold.
  • SIGN_WITH_KEY_SHARE requires at least threshold servers listed in its servers config to participate in the signing ceremony.
  • The serializedHash from BUILD_EVM_TRANSACTION is the EIP-155 transaction hash — pass it directly as messageHash to SIGN_WITH_KEY_SHARE.