ETHGlobal Cannes 2026 · Best Smart Contracts on Arc with Advanced Stablecoin Logic
Programmable escrow + vesting + crosschain USDC settlement for DAO contributors.
Built on Arc Testnet · CCTP V2 · Circle Forwarding Service.
arc-escrow/
├── src/
│ └── ArcEscrow.sol ← Smart contract (tout est là)
├── test/
│ └── ArcEscrow.t.sol ← Tests Foundry (à décommenter et run)
├── script/
│ └── Deploy.s.sol ← Script de déploiement Foundry
├── frontend/
│ └── index.html ← Demo frontend (ouvre dans le browser, zero build)
├── foundry.toml ← Config Foundry
└── README.md
curl -L https://foundry.paradigm.xyz | bash
foundryupforge init arc-escrow
cd arc-escrow
# Copier les fichiers
cp ArcEscrow.sol src/
cp ArcEscrow.t.sol test/
cp Deploy.s.sol script/
# Build
forge build# Créer .env
echo "PRIVATE_KEY=0x..." > .env
echo "FEE_RECIPIENT=0x..." >> .env
source .env
# Déployer (mode local, sans CCTP)
forge script script/Deploy.s.sol \
--rpc-url https://rpc.testnet.arc.network \
--broadcast \
--private-key $PRIVATE_KEY \
-vvvvNote : Dans
Deploy.s.sol, décommenter les lignes ArcEscrow et choisir les adresses.
# Option A — juste ouvrir dans le browser
open frontend/index.html
# Option B — servir localement
npx serve frontend/
# → http://localhost:3000Coller l'adresse du contrat déployé dans le champ "Contract" du frontend.
Les tests sont dans ArcEscrow.t.sol (commentés, prêts à décommenter) :
# Décommenter les tests dans ArcEscrow.t.sol puis :
forge test -vvvv
# Test spécifique
forge test --match-test test_HappyPath_LocalRelease -vvvv
forge test --match-test test_AutoRelease_After72h -vvvv
forge test --match-test test_DisputeResolution_ArbiterSplit -vvvv| Asset/Contract | Address |
|---|---|
| USDC Arc Testnet | 0x3600000000000000000000000000000000000000 |
| USDC Ethereum Sepolia | 0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238 |
| USDC Base Sepolia | 0x036CbD53842c5426634e7929541eC2318f3dCF7e |
| TokenMessengerV2 (Sepolia) | 0x8fe6b999dc680ccfdd5bf7eb0974218be2542daa |
| Arc Testnet RPC | https://rpc.testnet.arc.network |
| Arc Testnet Chain ID | 2482601729 |
| CCTP Domain Arc | 26 |
| Faucet USDC | https://faucet.circle.com |
Client wallet:
1. createJob(contractor, arbiter=0x0, 100 USDC, "Audit contract", false, -)
2. fundJob(0) ← 100 USDC locked
Contractor wallet:
3. submitWork(0) ← démarre timer 72h
Client wallet:
4. approveWork(0) ← USDC released to contractor (- 0.5% fee)
1-3. Même que ci-dessus
4. openDispute(0) ← fonds gelés
5. resolveDispute(0, 7000) ← arbiter donne 70% au contractor
1-3. Même que ci-dessus
[Skip 72h → forge test ou warp]
4. claimAutoRelease(0) ← n'importe qui peut trigger
1. createJob(..., crosschain=true, destRecipient=0xARC_ADDRESS)
2. fundJob(0)
3. submitWork(0)
4. approveWork(0)
→ CCTP burn on source chain
→ Circle Forwarding Service relays attestation
→ USDC minted on Arc Testnet for contractor
Client wallet:
1. createStream(contractor, USDC, 1000 USDC, 30 days, false, -)
[Time passes...]
Contractor wallet:
2. claimStream(0) ← withdraw vested portion
Client wallet:
3. lockStream(0) ← dispute-lock, vesting paused
4. unlockStream(0) ← resume, end time extended
export RPC=https://rpc.testnet.arc.network
export ESCROW=0xYOUR_DEPLOYED_CONTRACT
export USDC=0x3600000000000000000000000000000000000000
export CLIENT_KEY=0x...
export CONTRACTOR_KEY=0x...
# Approve USDC
cast send $USDC "approve(address,uint256)" $ESCROW 100000000 \
--rpc-url $RPC --private-key $CLIENT_KEY
# Create + Fund Job
cast send $ESCROW \
"createJob(address,address,uint256,string,bool,address)" \
$CONTRACTOR 0x0000000000000000000000000000000000000000 \
100000000 "Build smart contract" false $CONTRACTOR \
--rpc-url $RPC --private-key $CLIENT_KEY
cast send $ESCROW "fundJob(uint256)" 0 \
--rpc-url $RPC --private-key $CLIENT_KEY
# Submit work (contractor)
cast send $ESCROW "submitWork(uint256)" 0 \
--rpc-url $RPC --private-key $CONTRACTOR_KEY
# Approve (client)
cast send $ESCROW "approveWork(uint256)" 0 \
--rpc-url $RPC --private-key $CLIENT_KEY
# Read job state
cast call $ESCROW "getJob(uint256)" 0 --rpc-url $RPC┌─────────────────────────────────────────────────────────┐
│ ArcEscrow.sol │
│ (Arc Testnet / Sepolia) │
│ │
│ ┌──────────────────┐ ┌──────────────────────────┐ │
│ │ Milestone Escrow│ │ Streaming Vesting │ │
│ │ │ │ │ │
│ │ CREATED │ │ createStream() │ │
│ │ → FUNDED │ │ → linear per-second │ │
│ │ → SUBMITTED │ │ → lockStream() pause │ │
│ │ → APPROVED ─────┤ │ → unlockStream() resume │ │
│ │ → DISPUTED │ │ → cancelStream() split │ │
│ │ → RESOLVED │ └──────────────────────────┘ │
│ └──────────────────┘ │
│ │ crosschain=true │
│ ↓ │
│ ┌──────────────────────────────────────────────────┐ │
│ │ _release() via CCTP V2 │ │
│ │ │ │
│ │ depositForBurnWithHook( │ │
│ │ amount, │ │
│ │ destinationDomain = 26 (Arc), │ │
│ │ mintRecipient = contractor bytes32, │ │
│ │ maxFee = 0.50 USDC, │ │
│ │ hookData = 0x636374702d... ← magic bytes │ │
│ │ ) │ │
│ └──────────────────────────────────────────────────┘ │
│ │ │
│ ↓ Circle Forwarding Service │
│ ┌──────────────────────────────────────────────────┐ │
│ │ [Arc Testnet] USDC minted to contractor │ │
│ │ No manual relay · No destination gas token │ │
│ └──────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
| Layer | Tech |
|---|---|
| Smart Contract | Solidity 0.8.20 |
| Dev Framework | Foundry (forge, cast, anvil) |
| Stablecoin | USDC (native Arc Testnet) |
| Crosschain | CCTP V2 · Circle Forwarding Service |
| Gas abstraction | Circle Paymaster (à intégrer next) |
| Frontend | Vanilla HTML + ethers.js 6 (zero build) |
| Wallet | MetaMask / any injected provider |
| Explorer | testnet.arcscan.app |