Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 77 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
name: CI

# Run only on pull requests, and only when code-relevant files change.
# Doc-only changes (README, docs/, gas_logs/, comments) skip CI.
on:
pull_request:
paths:
# Solidity sources, tests, deploy scripts.
- 'src/**'
- 'test/**'
# Gas-report baselines are generated artifacts — exclude from CI triggers
# so a baseline refresh alone does not run the full suite.
- '!test/gas_logs/**'
- 'script/**'
# Submodule SHA bumps (forge-std, openzeppelin) record under lib/.
- 'lib/**'
# Build / dependency / import config.
- 'foundry.toml'
- 'remappings.txt'
- '.gitmodules'
# The workflow itself — catches changes to CI definitions.
- '.github/workflows/**'

# Cancel an in-flight run if the PR is updated again.
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true

jobs:
build-and-lint:
name: Build & lint
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: recursive

- name: Install Foundry
uses: foundry-rs/foundry-toolchain@v1
with:
# Pin to a specific Foundry release so CI is reproducible. Bump this
# alongside any local `foundryup` upgrade, then re-run `forge fmt` to
# absorb any new formatter rules.
version: v1.5.1

- name: Show versions
run: forge --version

- name: Format check
run: forge fmt --check

- name: Lint
run: forge lint

- name: Build
run: forge build --sizes

test:
name: Test
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: recursive

- name: Install Foundry
uses: foundry-rs/foundry-toolchain@v1
with:
# Pin to a specific Foundry release so CI is reproducible. Bump this
# alongside any local `foundryup` upgrade, then re-run `forge fmt` to
# absorb any new formatter rules.
version: v1.5.1

- name: Run tests
run: forge test -vv
16 changes: 15 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,18 @@ broadcast/
# Local environment file
.env

deployments/
deployments/

# Local-machine MCP server config (contains absolute paths)
.mcp.json

# AI assistant local docs — kept out of the public repo
AGENTS.md
CLAUDE.md
.claude

# Coverage output (forge coverage --report lcov)
lcov.info
coverage/

local
133 changes: 132 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,132 @@
# Celer AgentPay state channel contracts
# Celer AgentPay state-channel contracts

Smart contracts for **Celer AgentPay** — a generalized state-channel payment network
that enables real-time, trust-free micropayments between AI agents, decentralized
services, and human users.

This repo contains the on-chain L1 contract suite. Almost all activity happens
off-chain via co-signed simplex states; the chain is touched only for deposits,
withdrawals, settlement, and dispute resolution. For the full architecture — design
principles, data structures, off-chain protocols, app channels — see the canonical
docs:

- 📖 [agentpay-docs.celer.network](https://agentpay-docs.celer.network/)

A condensed in-repo orientation lives at [`docs/architecture-summary.md`](docs/architecture-summary.md).

---

## Quick Start

```bash
# Install submodule dependencies (forge-std, openzeppelin)
git submodule update --init --recursive

# Build
forge build

# Test
forge test

# Format / lint
forge fmt
forge lint
```

Verified with `forge 1.3.5-stable`. See [foundry.toml](foundry.toml) for compiler /
optimizer settings.

---

## Components

Seven top-level contracts in [`src/`](src/), grouped by role:

### Asset custody (permanent, audited, not versioned)

- **[CelerWallet](src/CelerWallet.sol)** — Multi-owner / multi-token wallet shared by
the whole network; the only contract that holds funds. Operated by a `CelerLedger`.
- **[PayRegistry](src/PayRegistry.sol)** — Append-only global record of resolved
conditional-payment results, indexed by `payId = keccak256(payHash, setterAddress)`.
- **[VirtContractResolver](src/VirtContractResolver.sol)** — On-demand deployer for
virtual (off-chain) contracts when disputes need them on-chain.
- **[EthPool](src/EthPool.sol)** — ERC-20-shaped wrapper for native ETH; enables
single-tx channel opening via a uniform `transferFrom` flow.

### Channel & payment logic (versioned, peer-controlled migration)

- **[CelerLedger](src/CelerLedger.sol)** — Channel state machine and primary user entry
point; operator of `CelerWallet`. Logic split across libraries in
[`src/lib/ledgerlib/`](src/lib/ledgerlib/).
- **[PayResolver](src/PayResolver.sol)** — On-chain resolution of conditional payments;
evaluates conditions or vouched results and writes outcomes to `PayRegistry`.

### Networking

- **[RouterRegistry](src/RouterRegistry.sol)** — Optional registry where relay-router
operators advertise themselves.

For per-contract APIs, constructor args, events, and storage, see
[`docs/contracts.md`](docs/contracts.md).

---

## Structure

```
src/
├── CelerLedger.sol # channel state machine; primary entry point (versioned)
├── CelerLedgerMock.sol # test-only ledger variant
├── CelerWallet.sol # multi-owner asset custodian (permanent)
├── EthPool.sol # ERC20-like wrapper for native ETH
├── PayRegistry.sol # global resolved-payment registry (permanent)
├── PayResolver.sol # conditional-pay resolution (versioned)
├── RouterRegistry.sol # optional relay-router registry
├── VirtContractResolver.sol # on-demand virtual-contract deployer
├── helper/ # test fixtures (mocks + sample ERC20) — NOT production
└── lib/
├── data/ # auto-generated protobuf decoders + .proto sources
├── interface/ # I*.sol interfaces
└── ledgerlib/ # CelerLedger logic split into libraries

test/ # Foundry tests
script/ # Forge deploy scripts (TBD)
lib/ # git submodules: forge-std, openzeppelin
```

`broadcast/`, `cache/`, `out/`, `deployments/`, `.env`, `.mcp.json`, `.vscode/` are
gitignored.

---

## Documentation

| Document | Purpose |
|---|---|
| [docs/architecture-summary.md](docs/architecture-summary.md) | One-page on-chain orientation; links into the full architecture docs. |
| [docs/contracts.md](docs/contracts.md) | Per-contract API reference (constructor, functions, events, storage). |

---

## Dependencies

| Package | Version | Purpose |
|---|---|---|
| [forge-std](https://github.com/foundry-rs/forge-std) | latest | Testing & scripting |
| [openzeppelin-contracts](https://github.com/OpenZeppelin/openzeppelin-contracts) | release-v5.3 | Access control, ERC-20, ECDSA, Pausable |

Pinned via git submodules; see [.gitmodules](.gitmodules).

---

## Toolchain

- [Foundry](https://book.getfoundry.sh) (`forge`, `cast`, `anvil`)
- Solidity `^0.8.20` (see `pragma` in [src/](src/))
- Optimizer enabled, `runs = 200` ([foundry.toml](foundry.toml))

---

## License

MIT — see [LICENSE](LICENSE).
147 changes: 147 additions & 0 deletions docs/architecture-summary.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
# Architecture Summary

A one-page orientation for the on-chain layer of **Celer AgentPay**. For the full
specification — design principles, off-chain protocols, app channels, message flows —
see the canonical architecture docs:

> 📖 **Full architecture:** [agentpay-docs.celer.network](https://agentpay-docs.celer.network/)

This page is a quick map. **It does not duplicate the architecture docs** — it gives you
just enough context to read the Solidity in this repo without bouncing back and forth.

---

## On-chain layer in two paragraphs

AgentPay is a generalized state-channel payment network. The on-chain contracts in this
repo bind two off-chain primitives — the **duplex payment channel** and the
**conditional payment** — to a minimal, verifiable on-chain footprint. Almost all
activity happens off-chain: peers exchange co-signed simplex states and forward
conditional payments through routed paths. The blockchain is only touched for deposits,
withdrawals, settlement, dispute resolution, and (rarely) deploying virtual contracts.

The seven contracts split cleanly into two roles. **Asset custody** lives in
permanent, audited contracts that change rarely or never (`CelerWallet`, `PayRegistry`,
`VirtContractResolver`, `EthPool`). **Channel and payment logic** lives in *versioned*
contracts (`CelerLedger`, `PayResolver`) that peers can cooperatively migrate between
without disturbing the assets — see [Decentralized Versioning][versioning] in the full
docs. `RouterRegistry` is an optional advertisement registry for relay nodes.

[versioning]: https://agentpay-docs.celer.network/agentpay-architecture/on-chain-contracts/decentralized-versioning

---

## Five design principles (one-line each)

These shape every choice in the codebase. Read the full text in
[system-overview.md][system-overview] when context matters.

1. **Minimize on-chain footprint.** Touch the chain only for deposits / withdrawals /
disputes; keep storage compact.
2. **Minimize relay-node on-chain interaction.** Disputes are between source and
destination; relays never write to chain.
3. **Minimize on-chain view calls.** Cache locally, exchange verified state directly.
4. **Minimize off-chain communication overhead.** Few round-trips, lightweight encoding.
5. **Decouple payment channels from app channels.** Conditions expose a uniform
`isFinalized` / `getOutcome` interface; payment logic is independent of app logic.

A sixth, structural principle: **decentralized, peer-controlled versioning** — instead
of admin-controlled proxy upgrades, channel peers cooperatively migrate to new
`CelerLedger` / `PayResolver` versions. This eliminates trusted upgrade controllers and
keeps asset custody (`CelerWallet`) immutable.

[system-overview]: https://agentpay-docs.celer.network/agentpay-architecture/system-overview

---

## Channel state machine

The status of a payment channel inside `CelerLedger` (see
[`LedgerStruct.ChannelStatus`](../src/lib/ledgerlib/LedgerStruct.sol)):

```
openChannel
Uninitialized ─────────────▶ Operable
│ ▲
intendSettle │ │ migrateChannelFrom
▼ │ (re-activates)
Settling
confirmSettle │
(after window) ▼
Closed

Operable / Settling ──── migrateChannelFrom ───▶ Migrated
(on the OLD ledger)
```

- **Uninitialized** — channel does not yet exist in this `CelerLedger` instance.
- **Operable** — active; deposits, withdrawals, snapshots, off-chain pay forwarding.
- **Settling** — `intendSettle` opened a challenge window; counterparty can submit
newer simplex states.
- **Closed** — terminal; balances paid out, channel finalized.
- **Migrated** — terminal *on the old ledger*; the channel continues life on a new
`CelerLedger` version. Migration outranks `intendSettle`: peers can migrate even
while `Settling`, returning the channel to `Operable` on the new ledger.

For the full state-transition rules, see
[channel-operations.md][channel-operations].

[channel-operations]: https://agentpay-docs.celer.network/agentpay-architecture/on-chain-contracts/channel-operations

---

## Contract map (1-line each)

| Contract | Role | Versioned? |
|---|---|---|
| [`CelerWallet`](../src/CelerWallet.sol) | Multi-owner / multi-token asset custodian. One global instance. | No (permanent) |
| [`CelerLedger`](../src/CelerLedger.sol) | Channel state machine + primary user entry point. Operator of `CelerWallet`. | **Yes** |
| [`PayResolver`](../src/PayResolver.sol) | On-chain conditional-pay resolution; writes results to `PayRegistry`. | **Yes** (chosen per-payment) |
| [`PayRegistry`](../src/PayRegistry.sol) | Global `payId → (amount, deadline)` map; immutable, public reference. | No (permanent) |
| [`VirtContractResolver`](../src/VirtContractResolver.sol) | On-demand deployment of virtual contracts during disputes. | No (permanent) |
| [`EthPool`](../src/EthPool.sol) | ERC20-like wrapper for native ETH; enables single-tx channel opening. | No |
| [`RouterRegistry`](../src/RouterRegistry.sol) | Optional registry for relay-router self-advertisement. | No |

For per-contract APIs (constructor args, external functions, events, storage), see
[`contracts.md`](contracts.md).

---

## Key invariants (worth flagging in any change)

These come straight from the architecture docs. Tests should encode them; PRs should
not violate them.

- A simplex state is valid only if **co-signed by both peers** and has the highest
`seq_num`.
- `payID = keccak256(payHash, setterAddress)` — binds a payment result to its
designated resolver. `payHash = keccak256(serializedConditionalPay)`.
- During the challenge window of `resolvePayment*`, a result may be **raised** but
never lowered. This protects relay nodes from collusive source/dest pairs.
- Migration outranks `intendSettle`. Cooperative migration always wins over a unilateral
settle in flight.
- `CelerWallet` has exactly one **operator** (a `CelerLedger` instance). Operatorship
transfer is the migration pivot; only the current operator (or all owners
cooperatively, via `proposeNewOperator`) can transfer it.

---

## Where to read more

| Topic | Source |
|---|---|
| Design principles | [system-overview.md][system-overview] |
| Core data structures (protobuf) | [core-data-structures.md][data-structures] |
| Per-contract responsibilities & relationships | [contracts-architecture.md][contracts-arch] |
| Channel operations (open / deposit / withdraw / settle) | [channel-operations.md][channel-operations] |
| Decentralized versioning (migration) | [decentralized-versioning.md][versioning] |
| App contracts and condition interface | [app-contracts-and-protocols.md][app-protocol] |

[data-structures]: https://agentpay-docs.celer.network/agentpay-architecture/on-chain-contracts/core-data-structures
[contracts-arch]: https://agentpay-docs.celer.network/agentpay-architecture/on-chain-contracts/contracts-architecture
[app-protocol]: https://agentpay-docs.celer.network/agentpay-architecture/app-contracts-and-protocols

---

**See also:** [`contracts.md`](contracts.md) · [`README.md`](../README.md)
Loading
Loading