Add escrow-backed transactions to a TypeScript app. Klescrow prepares unsigned transactions for escrow creation, deposits, releases, refunds, evidence, disputes, and appeals; your user's wallet still signs and broadcasts every transaction.
The SDK never holds private keys and never takes custody of funds.
Your app -> Klescrow SDK -> unsigned transaction -> user wallet -> blockchain
# Choose one integration. The SDK core has no Ethers or Viem dependency.
npm install @rakelabs/klescrow-sdk @rakelabs/ethers-adapter ethers
# or
npm install @rakelabs/klescrow-sdk @rakelabs/viem-adapter viemRequirements:
- Node.js 20+
- an application-supplied
RpcClientandAbiCodec - an optional ethers or viem integration package for creating those dependencies
Use this package when your product needs a buyer and seller to coordinate around locked funds:
- the buyer creates an escrow and locks ETH or ERC20 tokens,
- the seller performs the agreed work,
- both parties approve release or refund,
- either party can raise a Kleros dispute if they cannot agree,
- evidence and appeal transactions can be prepared from the same bound escrow handle.
Every write method returns a PreparedTx with a preview field. Show that preview before asking a user to sign.
import { BrowserProvider } from 'ethers';
import { Klescrow, KlescrowTxBuilder, ABI as KLESCROW_ABI } from '@rakelabs/klescrow-sdk';
import { createEthersAbiCodec, createEthersRpcClient } from '@rakelabs/ethers-adapter';
const provider = new BrowserProvider(window.ethereum);
await provider.send('eth_requestAccounts', []);
const signer = await provider.getSigner();
const rpcClient = createEthersRpcClient(provider);
const codec = createEthersAbiCodec(KLESCROW_ABI);
const buyerAddress = await signer.getAddress();
const klescrow = await Klescrow.fromRpc(rpcClient, { codec, walletAddress: buyerAddress });
const now = BigInt(Math.floor(Date.now() / 1000));
const { tx: createTx, escrowId } = await klescrow.factory.prepareCreateEthEscrow({
netAmount: 1_000_000_000_000_000_000n,
sellerAddress: '0xSELLER_ADDRESS',
obligationDeadlineUnixSec: now + 7n * 24n * 60n * 60n,
settlementDeadlineUnixSec: 0n,
termsHash: KlescrowTxBuilder.termsHashFromUri('https://example.com/orders/123/terms'),
});
console.log(createTx.preview);
const createResponse = await signer.sendTransaction({
to: createTx.to,
data: createTx.data,
value: BigInt(createTx.value),
});
await createResponse.wait();
const created = (await klescrow.factory.getLogsByParty('buyer', buyerAddress))
.find((event) => event.escrowId === escrowId);
if (!created) {
throw new Error('Escrow creation event was not found');
}
const escrow = klescrow.escrow(created.escrowAddress);
const { tx: depositTx } = await escrow.prepareDeposit();
await signer.sendTransaction({
to: depositTx.to,
data: depositTx.data,
value: BigInt(depositTx.value),
});Both parties express agreement by sending their own approval transaction from their own wallet.
const escrow = klescrow.escrow('0xESCROW_ADDRESS');
const approveTx = escrow.approvePayment();
console.log(approveTx.preview);
await signer.sendTransaction({
to: approveTx.to,
data: approveTx.data,
value: BigInt(approveTx.value),
});const refundTx = escrow.approveRefund();
await signer.sendTransaction({
to: refundTx.to,
data: refundTx.data,
value: BigInt(refundTx.value),
});prepareRaiseDispute() reads the current Kleros arbitration cost and includes it as the transaction value.
const { tx: disputeTx, arbFeeWei } = await escrow.prepareRaiseDispute();
console.log('Arbitration fee:', arbFeeWei.toString());
console.log(disputeTx.preview);
await signer.sendTransaction({
to: disputeTx.to,
data: disputeTx.data,
value: BigInt(disputeTx.value),
});Evidence is usually an ipfs://... URI produced by @rakelabs/evidence-publisher.
const evidenceTx = escrow.submitEvidence('ipfs://QmYourEvidenceDocument');
await signer.sendTransaction({
to: evidenceTx.to,
data: evidenceTx.data,
value: BigInt(evidenceTx.value),
});For ETH escrows, the SDK includes the required ETH value in the prepared transaction.
For ERC20 escrows, prepare the ERC20 creation flow with prepareCreateErc20Escrow(...), approve the token allowance as needed, then create and deposit through the escrow contract. See docs/erc20-escrow.md.
This README and the linked guides describe the unreleased 0.2.0 API until
that version is tagged. For 0.1.x usage, open the matching Git release tag.
| Document | Use it for |
|---|---|
| docs/reference.md | API reference, types, actions, events, and common mistakes |
| docs/erc20-escrow.md | ERC20 escrow setup and token approval flow |
| docs/disputes.md | Dispute, evidence, ruling, and appeal lifecycle |
| docs/advanced.md | Reader, transaction builder, multicall, and implementation selection |
| docs/migration-0.1-to-0.2.md | Migrate from provider-based initialization |
| docs/on-chain.md | Contract-level behavior and event model |
- Always show
tx.previewbefore requesting a signature. - Store the escrow contract address after creation; it is the canonical on-chain handle.
- Treat deadlines as Unix seconds.
- Check chain IDs and contract addresses before sending transactions.
- This software interacts with autonomous contracts. Users transact at their own risk.