Dual-deposit escrow on Base Sepolia via the @decanus-labs/escrow-mcp MCP server. Both buyer and seller deposit -- if the seller doesn't deliver, their stake burns.
bash
PRIVATE_KEY=0x... npx -y @decanus-labs/escrow-mcp
Or configure as an MCP server in config.yaml:
yaml
plugins:
entries:
escrow:
type: mcp
config:
command: npx
args: ["-y", "@decanus-labs/escrow-mcp"]
env:
PRIVATE_KEY: "0x..."
Optional env vars: RPC_URL (default: https://sepolia.base.org), CONTRACT_ADDRESS (default: v2 deployment).
create_escrowBuyer creates an escrow, depositing ETH as payment.
ParamTypeRequiredDescriptionsellerstringyesSeller's Ethereum addressarbiterstringyesArbiter's Ethereum address (dispute resolver)paymentAmountEthstringyesPayment in ETH (e.g. "0.01")durationSecondsnumberyesDeadline in seconds from now
accept_escrowSeller accepts by depositing a stake >= the payment amount.
ParamTypeRequiredDescriptionescrowIdnumberyesEscrow IDstakeAmountEthstringyesStake in ETH (must be >= payment)
deliver_workSeller submits delivery proof. Starts a 24h buyer review window.
ParamTypeRequiredDescriptionescrowIdnumberyesEscrow IDdeliveryHashstringyesbytes32 hex or plain string (auto-hashed via keccak256)
complete_escrowBuyer approves delivery. Releases payment + seller stake to seller.
ParamTypeRequiredDescriptionescrowIdnumberyesEscrow ID
dispute_escrowEither party raises a dispute. Arbiter must resolve.
ParamTypeRequiredDescriptionescrowIdnumberyesEscrow IDreasonstringnoHuman-readable reason (stored off-chain)
refund_expired_escrowAnyone triggers after deadline. Buyer gets payment back, seller stake burns.
ParamTypeRequiredDescriptionescrowIdnumberyesEscrow ID
get_escrowFetch escrow state. Returns state label, participants, amounts, deadlines (ISO + relative), delivery hash, and suggested next actions.
ParamTypeRequiredDescriptionescrowIdnumberyesEscrow ID
list_escrowsPaginated scan of recent escrows.
ParamTypeRequiredDescriptionlimitnumbernoMax results (default 10, max 50)statestringnoFilter: AWAITING_SELLER, FUNDED, DELIVERED, COMPLETED, DISPUTED, REFUNDED, BURNED``participantstringnoFilter by address (buyer, seller, or arbiter)
text
AWAITING_SELLER → FUNDED → DELIVERED → COMPLETED
↓ ↓
DISPUTED DISPUTED
↓
BURNED (after deadline)
text
1. create_escrow(seller=0x..., arbiter=0x..., paymentAmountEth="0.01", durationSeconds=86400)
2. Seller calls: accept_escrow(escrowId=0, stakeAmountEth="0.01")
3. Seller calls: deliver_work(escrowId=0, deliveryHash="ipfs://QmProof...")
4. Buyer calls: complete_escrow(escrowId=0)
→ Seller receives 0.02 ETH (payment + stake returned)
text
1. create_escrow(...) → accept_escrow(...)
2. Buyer or seller calls: dispute_escrow(escrowId=0, reason="Work incomplete")
→ State moves to DISPUTED, arbiter must resolve via contract
text
1. create_escrow(...) → accept_escrow(...)
2. Deadline passes with no delivery
3. Anyone calls: refund_expired_escrow(escrowId=0)
→ Buyer gets payment back, seller stake burned (locked in contract)
text
get_escrow(escrowId=0)
→ Returns state, deadlines, next valid actions
"0.01" not 0.01.deliveryHash field accepts either a raw bytes32 hex string or a plain string (which gets keccak256-hashed automatically).txHash and explorerUrl for verification.PRIVATE_KEY). Run separate instances for buyer and seller roles.