Signing an Arbitrary Hash
This page demonstrates how to request and retrieve a signature for an arbitrary 32-byte hash using the ChainSignatures contract deployed on Sepolia.
Set Up Your Clients and Contract
Ensure your environment is configured (e.g., .env containing your PRIVATE_KEY).
import { chainAdapters, constants, contracts } from 'signet.js'
import { createPublicClient, createWalletClient, http } from 'viem'
import { privateKeyToAccount } from 'viem/accounts'
import { sepolia } from 'viem/chains'
import dotenv from 'dotenv'
dotenv.config()
const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`)
const publicClient = createPublicClient({ chain: sepolia, transport: http() })
const walletClient = createWalletClient({
account,
chain: sepolia,
transport: http(),
})
const evmChainSigContract = new contracts.evm.ChainSignatureContract({
publicClient,
walletClient,
contractAddress: constants.CONTRACT_ADDRESSES.ETHEREUM
.TESTNET_DEV as `0x${string}`,
})Request Signature for an Arbitrary Hash
Prepare your 32-byte payload (hash of your message) and choose an arbitrary path:
import { keccak256, stringToBytes, toBytes } from 'viem'
const message = 'dontcare'
const payload = Array.from(toBytes(keccak256(stringToBytes(message))))
const path = 'randomPath'
const rsvSignature = await evmChainSigContract.sign({
payload,
path,
key_version: 1,
})At this stage, the MPC network processes your signature request off-chain, subsequently calling the respond() method on-chain.
Retrieve and Validate the Signature
After the MPC responds, retrieve your signature components:
console.log('r:', rsvSignature.r)
console.log('s:', rsvSignature.s)
console.log('v:', rsvSignature.v)Verify the Signature
You can verify the signature to ensure it was correctly signed:
import { concat, padHex, recoverAddress } from 'viem'
const messageHash = keccak256(stringToBytes(message))
const signature = concat([
padHex(`0x${rsvSignature.r}`, { size: 32 }),
padHex(`0x${rsvSignature.s}`, { size: 32 }),
`0x${rsvSignature.v.toString(16)}`,
])
const recoveredAddress = await recoverAddress({
hash: messageHash,
signature,
})
const evm = new chainAdapters.evm.EVM({
publicClient,
contract: evmChainSigContract,
})
const keyVersion = 1
const { address: expectedAddress } = await evm.deriveAddressAndPublicKey(
account.address,
path,
keyVersion
)
console.log('Recovered address:', recoveredAddress)
console.log('Original address:', expectedAddress)
console.log(
'Signature is valid:',
recoveredAddress.toLowerCase() === expectedAddress.toLowerCase()
)Complete Code Example
For a complete working example, see the full implementation here.
This approach ensures secure, verifiable signatures for arbitrary payloads through the ChainSignatures smart contract.
