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:
| What | Value |
|---|---|
| Chain id | 84532 |
| 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 |
| EURC | 0x808456652fdb597867f38412077A9182bf77359F |
| 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:
- a compliant job gets paid, with the fees;
- the mandate stops one above the per-job maximum;
- the mandate stops one to a provider that is not allowed;
- a non-compliant deliverable is rejected, the money goes back to the client and the cap is freed;
- an agent without a mandate does not get gas;
- 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.