ITEN
Demo · Base Sepolia · test money

Test environment

There is a single demo, on Base Sepolia, with test EURC: no real money.

App https://scontratto-app.pages.dev
API https://scontratto-api.scontratto.workers.dev
Docs with live values https://scontratto-api.scontratto.workers.dev/docs
Chain Base Sepolia, chain id 84532

You are reading these docs from the demo itself. These are its parameters, read from the chain just now:

WhatValue
Chain id84532
Escrow (Scontracts)0x77f2b3ED3a2C2c5132c20ab2C2E8D8519F76c3FB
Attestor (Scontracts)0xEF17eCEE88edd059245db0F7ef7e76ab652997Ba
AgenticCommerce (agents, ERC-8183)0x837f52E246CAe9E438c911c9044f059dBccAc5d9
MandateHook (agents)0x7a0Ec0E757dC9c849C21630dD8Ce4A02592eF1EA
ERC-2771 forwarder (agents)0x0074C56350dA067D9147934a89d3dD18a9eB2b17
SchemaEval (agents)0xEF17eCEE88edd059245db0F7ef7e76ab652997Ba
GitHubEval (agents)0xaA63a746842ba0Af194b1Df35Db4A9b903B31b11
Facilitator (agents)0xEF17eCEE88edd059245db0F7ef7e76ab652997Ba
EURC0x808456652fdb597867f38412077A9182bf77359F
Platform fee (agents)100 bps
Evaluator fee (agents)50 bps

What you need

  • Test EURC, from the Circle faucet, choosing Base Sepolia. It goes to whoever pays in: the person who opens a Scontract, or the client agent's wallet.
  • Test ETH, only for Scontracts between people: whoever opens and pays in pays the gas for their three transactions. Agents do not need it, because the facilitator pays the gas.
  • The pilot token, for writes to the Scontracts API. Ask the team for it.

Limits

The demo is for trying things out, not for keeping them. It can be reset, and the contracts can change address with every new version. The facilitator sponsors at most 60 requests per hour per address and 300 in total.

Locally

Locally you can try the agent flow: a chain with anvil, the contracts, the Worker with its database, and two MCP agents. Scontracts between people need a real oracle, so for those you use the demo. You need Node 22 or newer and Foundry.

1. The chain

anvil --block-time 1

anvil prints ten accounts with their keys. They are public and only valid locally.

--block-time 1 produces one block per second, as on a real chain. Without it, anvil creates a block only when a transaction arrives, and waiting for a confirmation can hang until it hits its one-minute limit.

2. The contracts

cd packages/scontract-contracts
MINT_TO=0x976EA74026E726554dB657fA54763abd0C3a0aa9 PLATFORM_FEE_BP=100 EVALUATOR_FEE_BP=50 \
  forge script script/Deploy.s.sol:Deploy --rpc-url http://127.0.0.1:8545 \
  --private-key <anvil account 0 key> --broadcast
cat deployments/31337.json

Without EURC, the script creates a test EURC and gives 1000 to the addresses in MINT_TO. The one in the example is anvil account 6, which the demo uses as the client agent.

3. The Worker

Create a variables file outside the repository, for example /tmp/locale.vars, with two lines:

  • BASE_SEPOLIA_RPC_URL=http://127.0.0.1:8545;
  • ATTESTOR_PRIVATE_KEY, with the key of anvil account 2. Locally it acts as attestor, facilitator and SchemaEval. GitHubEval uses a key derived from this one. Optional: GITHUB_TOKEN, a read-only GitHub token, to stay clear of the 60 requests per hour limit.

Then, with the addresses from deployments/31337.json:

cd apps/api
npx wrangler d1 migrations apply scontratto --local
npx wrangler dev --port 8790 --env-file /tmp/locale.vars --var CHAIN_ID:31337 \
  --var ACP_ADDRESS:0x... --var MANDATE_HOOK_ADDRESS:0x... --var FORWARDER_ADDRESS:0x...

The docs are also at http://127.0.0.1:8790/docs, with the addresses of your local environment.

4. The demo

npm run build -w @scontract/mcp
SCONTRACT_API_URL=http://127.0.0.1:8790 node scripts/scontract-e2e.mjs

The demo has the owner sign a mandate, then has two agents work through MCP:

  1. a compliant job gets paid, with the fees;
  2. the mandate stops one above the per-job maximum;
  3. the mandate stops one to a provider that is not allowed;
  4. a non-compliant deliverable is rejected, the money goes back to the client and the cap is freed;
  5. an agent without a mandate does not get gas;
  6. the job's activity log contains every step.

It must end with 19 ok, 0 ko.

Letting a job expire

To try the automatic refund, move anvil's clock forward and run the agents' cron:

cast rpc evm_increaseTime 864000 && cast rpc evm_mine
curl -X POST "http://127.0.0.1:8790/cdn-cgi/local/explorer/api/local/scheduled?worker=scontratto-api" \
  -H 'content-type: application/json' -d '{"cron":"* * * * *"}'

Funded jobs that have expired move to Expired, the money goes back to the client and the mandate's cap is freed.

With CON_GITHUB=1 the demo adds a GitHubEval job: it commissions a real, already merged wevm/viem PR, submits it and checks that the provider gets paid. It needs network access to GitHub.

CON_GITHUB=1 SCONTRACT_API_URL=http://127.0.0.1:8790 node scripts/scontract-e2e.mjs

Tests

npm test
cd packages/contracts && forge test
cd packages/scontract-contracts && forge test

The first runs the TypeScript tests: the Scontract state machine, the oracle, the agent domain and the facilitator policy. The other two run the Solidity ones: the Scontracts escrow, and the agents' core and hook. In both cases TypeScript and Solidity share a test vector: the hash of the same mandate must come out identical on both sides.