Skip to main content
Chain Signatures enable NEAR accounts and smart contracts to sign transactions for other blockchains, including Bitcoin, Ethereum, Solana, and more. This tutorial shows you how to control a NEAR account from another chain.

Overview

With Chain Signatures, you can:
  • Sign Bitcoin transactions from a NEAR account
  • Control Ethereum addresses from NEAR contracts
  • Create multi-chain applications with unified account management
  • Build cross-chain DeFi products

GitHub Repository

View the complete Chain Signatures example

How it works

1

Derive chain-specific addresses

NEAR accounts can derive addresses for other chains using a path-based system:
import { setupAdapter } from "@near-relay/client";

// Setup
const account = await setupAdapter({
  accountId: "your-account.testnet",
  privateKey: "ed25519:...",
  network: "testnet"
});

// Derive Ethereum address
const ethAddress = await account.deriveAddress("ethereum", "your-path");
console.log(`Your Ethereum address: ${ethAddress}`);

// Derive Bitcoin address
const btcAddress = await account.deriveAddress("bitcoin", "your-path");
console.log(`Your Bitcoin address: ${btcAddress}`);
The derivation path ensures deterministic address generation. The same path always produces the same address.
2

Create transaction for target chain

Build a transaction for the target blockchain:
import { ethers } from "ethers";

// Create Ethereum transaction
const tx = {
  to: "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
  value: ethers.parseEther("0.01"),
  gasLimit: 21000,
  gasPrice: await provider.getGasPrice(),
  nonce: await provider.getTransactionCount(ethAddress),
  chainId: 1,  // Ethereum mainnet
};

// Serialize transaction
const serializedTx = ethers.Transaction.from(tx).unsignedSerialized;
3

Request signature from NEAR

Use the NEAR account to sign the transaction:
// Request signature through Chain Signatures MPC
const signature = await account.signTransaction({
  transaction: serializedTx,
  path: "your-path",
  chain: "ethereum"
});

console.log(`Signature: ${signature}`);
4

Broadcast to target chain

Send the signed transaction to the target blockchain:
// Attach signature to transaction
const signedTx = ethers.Transaction.from({
  ...tx,
  signature: signature
}).serialized;

// Broadcast
const txHash = await provider.broadcastTransaction(signedTx);
console.log(`Transaction: ${txHash}`);

Supported chains

Ethereum

Full support for EVM chains including Ethereum, Polygon, Arbitrum, Optimism

Bitcoin

Native Bitcoin support for BTC transactions

Solana

Solana transaction signing support

Cosmos

Support for Cosmos SDK chains

Dogecoin

DOGE transaction signing

Ripple

XRP Ledger support

Example: Send ETH from NEAR

Complete example of sending ETH using a NEAR account:
import { setupAdapter } from "@near-relay/client";
import { ethers } from "ethers";

async function sendEth() {
  // Initialize NEAR adapter
  const account = await setupAdapter({
    accountId: "your-account.testnet",
    privateKey: process.env.NEAR_PRIVATE_KEY,
    network: "testnet"
  });
  
  // Derive Ethereum address
  const ethAddress = await account.deriveAddress("ethereum", "my-eth-account");
  console.log(`Sending from: ${ethAddress}`);
  
  // Setup Ethereum provider
  const provider = new ethers.JsonRpcProvider("https://eth.llamarpc.com");
  
  // Create transaction
  const tx = {
    to: "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
    value: ethers.parseEther("0.01"),
    gasLimit: 21000,
    gasPrice: await provider.getGasPrice(),
    nonce: await provider.getTransactionCount(ethAddress),
    chainId: 1,
  };
  
  // Sign with NEAR
  const serializedTx = ethers.Transaction.from(tx).unsignedSerialized;
  const signature = await account.signTransaction({
    transaction: serializedTx,
    path: "my-eth-account",
    chain: "ethereum"
  });
  
  // Broadcast
  const signedTx = ethers.Transaction.from({
    ...tx,
    signature
  }).serialized;
  
  const txHash = await provider.broadcastTransaction(signedTx);
  console.log(`Success! Transaction: ${txHash}`);
}

sendEth().catch(console.error);

Using in smart contracts

Smart contracts can also request signatures:
use near_sdk::{near, Promise, ext_contract};

#[ext_contract(ext_mpc)]
trait MpcContract {
    fn sign(&self, payload: Vec<u8>, path: String, key_version: u32) -> Promise;
}

#[near]
impl Contract {
    pub fn sign_eth_transaction(
        &self,
        transaction: Vec<u8>,
        path: String,
    ) -> Promise {
        ext_mpc::ext("v1.signer-prod.testnet".parse().unwrap())
            .with_static_gas(Gas::from_tgas(250))
            .sign(transaction, path, 0)
    }
}

Security considerations

  • Use unique paths for different use cases
  • Never share paths publicly if they control valuable assets
  • Consider using account-specific path prefixes
  • Document your path structure
  • NEAR private keys control all derived addresses
  • Use hardware wallets for mainnet accounts
  • Implement access controls for contract-based signing
  • Audit signature request flows
  • Always verify transaction details before signing
  • Check recipient addresses carefully
  • Validate amounts and gas prices
  • Test with small amounts first

Next steps

Multi-chain DAO

Build a DAO that governs multiple chains

Chain Signatures docs

Learn more about Chain Signatures

NEAR Intents

Use intents for cross-chain actions

Omnibridge

Bridge assets across chains

Build docs developers (and LLMs) love