Smart contracts

The Solidity project lives in contracts/ (Hardhat + TypeScript). Two contracts:

  • AXONEscrow.sol — milestone escrow.
  • MockUSDC.sol — a testnet ERC‑20 standing in for USDC (mintable by the treasury).

AXONEscrow — lifecycle

stateDiagram-v2
    [*] --> Draft: createAgreement
    Draft --> Active: acceptAgreement (counterparty)
    Draft --> Cancelled: cancelAgreement (initiator, unfunded)
    Active --> Cancelled: cancelAgreement (initiator, unfunded)
    Active --> Funded: fundMilestone (initiator)
    Funded --> Funded: markMilestoneComplete / releaseMilestone
    Funded --> Disputed: raiseDispute (either party)
    Disputed --> Completed: acceptResolution
    Funded --> Completed: all milestones released

Key functions

Function Caller Effect
createAgreement(counterparty, offChainId, titles[], amounts[]) initiator Creates a draft agreement; returns its id.
acceptAgreement(id) counterparty Draft → Active (enables funding).
fundMilestone(id, index) initiator Pulls USDC into escrow; snapshots feeBpsAtFunding.
markMilestoneComplete(id, index) counterparty Starts the 5‑day permissionless‑release timeout.
releaseMilestone(id, index) initiator any time, or anyone after 5 days Pays the counterparty net (fee deducted at the snapshotted rate).
raiseDispute(id) either Freezes the agreement (no fund/mark/release while disputed).
proposeResolution(id, recipientBps) either Proposes a split of the funded pot (latest overwrites).
acceptResolution(id) other party Settles the pot per the split, fee on the snapshotted rate.
cancelAgreement(id) initiator Scraps an unfunded agreement (Draft/Active only).
setFee(bps) / setFeeRecipient(addr) owner Update the live fee for future fundings (zero‑addr guard on recipient).

Events

All events carry offChainId indexed, so they're filterable on BaseScan and joinable to off‑chain rows:

AgreementCreated, AgreementAccepted, MilestoneFunded, MilestoneMarkedComplete, MilestoneReleased(…, viaTimeout, …), DisputeRaised, DisputeResolutionProposed, DisputeResolved, AgreementCancelled.

Why the fee snapshot matters

The owner can change the platform fee, but only for milestones funded after the change. Each milestone records the fee in force at fundMilestone time and uses that rate at release or dispute settlement — so funds already in escrow can never be charged a higher fee retroactively. This is a trust guarantee for counterparties.

Build, test, deploy

cd contracts
pnpm install
npx hardhat compile          # build artifacts
npx hardhat test             # run the contract test suite
# deploy (Base Sepolia) — uses a fresh key; supports FEE_RECIPIENT / OWNER_AFTER_DEPLOY
npx hardhat run scripts/deploy.ts --network baseSepolia

After deploy, set the new address in the backend env (ESCROW_CONTRACT_ADDRESS / the blockchain config) and rebuild the services that touch escrow (agreements, funding, settlements, reconciliation). The full procedure is in docs/CONTRACT_REDEPLOY_RUNBOOK.md.

Verification: see docs/BASESCAN_VERIFY_AND_OFFCHAINID_EVENTS.md for verifying the contract on BaseScan and reading the indexed offChainId events.

results matching ""

    No results matching ""