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
81 changes: 0 additions & 81 deletions AGENTS.md

This file was deleted.

37 changes: 37 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# CLAUDE.md

Guidance for AI agents working in this repo. For protocol/architecture overview, see [README.md](README.md).

## What this repo deploys

The current, deployed contracts are the x402 Permit2 proxies and batch-settlement:

- `contracts/x402ExactPermit2Proxy.sol` — `exact` scheme
- `contracts/x402UptoPermit2Proxy.sol` — `upto` scheme
- `contracts/x402BatchSettlement.sol` — `batch-settlement` payment channels
- `contracts/periphery/` — deposit collectors (ERC-3009 / Permit2)

## Guardrails (read before changing contracts)

- **Do not touch `contracts/legacy/`.** It holds the old `PaymentPermit` design; it is retained for reference and is not deployed. Don't wire new code to it.
- **BSC is not deployed from this repo.** BSC contracts come from [`x402-foundation/x402`](https://github.com/x402-foundation/x402/tree/main/contracts/evm) via its CREATE2 scripts; this repo only records the addresses (see README). Do not add or edit BSC deploy logic here.
- **Preserve CREATE2 determinism.** These contracts are expected at identical addresses across chains. Keep constructor args identical on every chain (e.g. the canonical Permit2 address) — any change to initCode changes the deployed address.
- **TRON USDT transfers use `sun-contract-std`'s `SafeTransferLib`**, because TRON USDT does not return a bool. Use it for token transfers rather than a raw `transfer`.
- **Transient storage required.** Contracts use `ReentrancyGuardTransient` (EIP-1153); only deploy on chains that support transient storage.

## Build & test

```bash
npm run compile # hardhat compile (or: forge build)
npm test # hardhat test
npm run test-foundry # forge test -vvv
npm run coverage # hardhat coverage
```

Forge tests live in `test/` (`*.t.sol`, plus `*.fork.t.sol` fork tests and `*.gas.t.sol`).

## Deployment

- TRON deploy scripts: `deployTron/` (run via `npm run deploy` / `deploy-nile` / `deploy-shasta`).
- Networks are configured in `hardhat.config.ts` (`tron`, `nile`, `shasta`, `bscTestnet`).
- Recorded addresses live in `deployments/` and the README address tables — update both when deploying.
88 changes: 48 additions & 40 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,79 +19,89 @@ Smart contracts for the **x402** payment protocol on **TRON** and **BSC**. Enabl

## Features

- **EIP-712 typed permits** — Users sign payment details off-chain; a relayer or backend calls `permitTransferFrom` with the signature.
- **Gasless for the signer** — The submitter pays gas; the signer only needs a one-time `approve` of the PaymentPermit contract.
- **Optional fee** — Permit can include `feeTo` and `feeAmount` for protocol or facilitator fees.
- **Replay protection** — Nonce bitmap per owner; time window via `validAfter` / `validBefore`.
- **Permit2-based settlement** — Payers sign a Permit2 signature; a facilitator settles on-chain. No bespoke approval flow.
- **Witness-bound destination** — The payment destination is signed into a Permit2 witness, so a facilitator cannot redirect funds.
- **Gasless for the payer** — The facilitator pays gas; payers can approve Permit2 via an EIP-2612 `permit` in the same transaction.
- **Multiple schemes** — `exact` (fixed amount), `upto` (facilitator settles up to a cap), and `batch-settlement` (escrow-backed payment channels).

---

## Architecture

| Component | Role | File(s) |
|----------------|------------------------------|-------------------------|
| **PaymentPermit** | Entry point for permits and transfers | `contracts/PaymentPermit.sol` |
| **PermitHash** | EIP-712 struct hashes | `contracts/libraries/PermitHash.sol` |
| **EIP712** | Domain separator and typed data hashing | `contracts/EIP712.sol` |
| **IPaymentPermit** | Structs and interface for permits | `contracts/interface/IPaymentPermit.sol` |
| **x402BasePermit2Proxy** | Shared Permit2 witness-transfer logic | `contracts/x402BasePermit2Proxy.sol` |
| **x402ExactPermit2Proxy** | `exact` scheme — settles the full permitted amount | `contracts/x402ExactPermit2Proxy.sol` |
| **x402UptoPermit2Proxy** | `upto` scheme — facilitator settles up to the cap | `contracts/x402UptoPermit2Proxy.sol` |
| **x402BatchSettlement** | `batch-settlement` scheme — escrow-backed payment channels | `contracts/x402BatchSettlement.sol` |
| **DepositCollector** | Pulls funds into batch-settlement escrow (ERC-3009 / Permit2 variants) | `contracts/periphery/` |

Flow: **User signs** `PaymentPermitDetails` (payment, fee, validity, nonce) → **Relayer/backend** calls `permitTransferFrom(permit, transferDetails, owner, signature)` → Contract pulls tokens from `owner` to `payTo` (and optional `feeTo`) in one shot.
> The original `PaymentPermit` contract is retained under `contracts/legacy/` and is not part of the current deployments.

Flow (exact/upto): **Payer signs** a Permit2 permit + witness (destination, validity) → **Facilitator** calls `settle(...)` (or `settleWithPermit(...)` to bundle an EIP-2612 approval) → the proxy invokes Permit2's `permitWitnessTransferFrom` to pull tokens straight to the destination.

---

## Deployed Addresses

| Network | Chain / Environment | PaymentPermit Address |
|-----------|---------------------|------------------------|
| **TRON Mainnet** | Mainnet | [`TT8rEWbCoNX7vpEUauxb7rWJsTgs8vDLAn`](https://tronscan.org/#/contract/TT8rEWbCoNX7vpEUauxb7rWJsTgs8vDLAn) |
| **TRON Nile** | Testnet | [`TFxDcGvS7zfQrS1YzcCMp673ta2NHHzsiH`](https://nile.tronscan.org/#/contract/TFxDcGvS7zfQrS1YzcCMp673ta2NHHzsiH) |
| **TRON Shasta** | Testnet | [`TR2XninQ3jsvRRLGTifFyUHTBysffooUjt`](https://shasta.tronscan.org/#/contract/TR2XninQ3jsvRRLGTifFyUHTBysffooUjt) |
| Network | Chain / Environment | PaymentPermit Address | x402ExactPermit2Proxy | x402BatchSettlement | x402UptoPermit2Proxy |
|-----------|---------------------|------------------------|------------------------|------------------------|------------------------|
| **TRON Mainnet** | Mainnet | [`TT8rEWbCoNX7vpEUauxb7rWJsTgs8vDLAn`](https://tronscan.org/#/contract/TT8rEWbCoNX7vpEUauxb7rWJsTgs8vDLAn) | [`TN49yaJmZMZoEdDCqjB4uPzQLHvYkGw95m`](https://tronscan.org/#/contract/TN49yaJmZMZoEdDCqjB4uPzQLHvYkGw95m) | [`TW9yNhTySkEHYfjnGQU2u4NAsdb1tW4fbm`](https://tronscan.org/#/contract/TW9yNhTySkEHYfjnGQU2u4NAsdb1tW4fbm) | [`TBLeFPkfDiweBbYmAPqnakaFBPDt9p93sR`](https://tronscan.org/#/contract/TBLeFPkfDiweBbYmAPqnakaFBPDt9p93sR) |
| **TRON Nile** | Testnet | [`TFxDcGvS7zfQrS1YzcCMp673ta2NHHzsiH`](https://nile.tronscan.org/#/contract/TFxDcGvS7zfQrS1YzcCMp673ta2NHHzsiH) | [`TFGoaq2KjizijgjtkVxT7yjffW1A5T1j6F`](https://nile.tronscan.org/#/contract/TFGoaq2KjizijgjtkVxT7yjffW1A5T1j6F) | [`TWBwWHZWwH8TzrZnbxit1J645VGYY1K2fA`](https://nile.tronscan.org/#/contract/TWBwWHZWwH8TzrZnbxit1J645VGYY1K2fA) | [`TKvcqQ7S2bYyys5ZZNpjj9xGiPhiwzHq1K`](https://nile.tronscan.org/#/contract/TKvcqQ7S2bYyys5ZZNpjj9xGiPhiwzHq1K) |
| **TRON Shasta** | Testnet | [`TR2XninQ3jsvRRLGTifFyUHTBysffooUjt`](https://shasta.tronscan.org/#/contract/TR2XninQ3jsvRRLGTifFyUHTBysffooUjt) | — | — | — |
| **BSC Mainnet** | Mainnet | [`0x1825bB32db3443dEc2cc7508b2D818fc13EaD878`](https://bscscan.com/address/0x1825bB32db3443dEc2cc7508b2D818fc13EaD878) | [`0x402085c248EeA27D92E8b30b2C58ed07f9E20001`](https://bscscan.com/address/0x402085c248EeA27D92E8b30b2C58ed07f9E20001) | [`0x4020074e9dF2ce1deE5A9C1b5c3f541D02a10003`](https://bscscan.com/address/0x4020074e9dF2ce1deE5A9C1b5c3f541D02a10003) | [`0x4020A4f3b7b90ccA423B9fabCc0CE57C6C240002`](https://bscscan.com/address/0x4020A4f3b7b90ccA423B9fabCc0CE57C6C240002) |
| **BSC Testnet** | Testnet | [`0x1825bB32db3443dEc2cc7508b2D818fc13EaD878`](https://testnet.bscscan.com/address/0x1825bB32db3443dEc2cc7508b2D818fc13EaD878) | [`0x402085c248EeA27D92E8b30b2C58ed07f9E20001`](https://testnet.bscscan.com/address/0x402085c248EeA27D92E8b30b2C58ed07f9E20001) | [`0x4020074e9dF2ce1deE5A9C1b5c3f541D02a10003`](https://testnet.bscscan.com/address/0x4020074e9dF2ce1deE5A9C1b5c3f541D02a10003) | [`0x4020A4f3b7b90ccA423B9fabCc0CE57C6C240002`](https://testnet.bscscan.com/address/0x4020A4f3b7b90ccA423B9fabCc0CE57C6C240002) |

> BSC proxy/batch contracts use deterministic (CREATE2) addresses shared across EVM chains. Verified on-chain: `x402ExactPermit2Proxy`, `x402UptoPermit2Proxy`, `x402BatchSettlement`, and both deposit collectors are deployed on **both BSC Mainnet and Testnet** at identical CREATE2 addresses.

| Network | Chain / Environment | PaymentPermit Address |
|-----------|---------------------|------------------------|
| **BSC Mainnet** | Mainnet | [`0x1825bB32db3443dEc2cc7508b2D818fc13EaD878`](https://bscscan.com/address/0x1825bB32db3443dEc2cc7508b2D818fc13EaD878) |
| **BSC Testnet** | Testnet | [`0x1825bB32db3443dEc2cc7508b2D818fc13EaD878`](https://testnet.bscscan.com/address/0x1825bB32db3443dEc2cc7508b2D818fc13EaD878) |
> **BSC contracts are deployed from [x402-foundation/x402](https://github.com/x402-foundation/x402/tree/main/contracts/evm)** using its CREATE2 deterministic scripts (mined vanity salts, `cbor_metadata = false`). This repository does not deploy BSC contracts — the addresses above are recorded here for reference only.

### Deposit Collectors

| Network | Chain / Environment | ERC3009DepositCollector | Permit2DepositCollector |
|-----------|---------------------|--------------------------|--------------------------|
| **TRON Mainnet** | Mainnet | [`TTWA7aWMdx4jfcbp8XRAS2JAd2sUhyF9qj`](https://tronscan.org/#/contract/TTWA7aWMdx4jfcbp8XRAS2JAd2sUhyF9qj) | [`TAg5qqp1K9x5KeSTWnRa8LT79B5HUjzSHY`](https://tronscan.org/#/contract/TAg5qqp1K9x5KeSTWnRa8LT79B5HUjzSHY) |
| **TRON Nile** | Testnet | [`TJUQ3BQt4YFg8EeevjiUa5LbfSGz5BxzRW`](https://nile.tronscan.org/#/contract/TJUQ3BQt4YFg8EeevjiUa5LbfSGz5BxzRW) | [`TEp6bCqSEKAr99sCiqANC84RtRwx7xGbA4`](https://nile.tronscan.org/#/contract/TEp6bCqSEKAr99sCiqANC84RtRwx7xGbA4) |
| **BSC Mainnet** | Mainnet | [`0x4020806089470a89826cB9fB1f4059150b550004`](https://bscscan.com/address/0x4020806089470a89826cB9fB1f4059150b550004) | [`0x4020425FAf3B746C082C2f942b4E5159887B0005`](https://bscscan.com/address/0x4020425FAf3B746C082C2f942b4E5159887B0005) |
| **BSC Testnet** | Testnet | [`0x4020806089470a89826cB9fB1f4059150b550004`](https://testnet.bscscan.com/address/0x4020806089470a89826cB9fB1f4059150b550004) | [`0x4020425FAf3B746C082C2f942b4E5159887B0005`](https://testnet.bscscan.com/address/0x4020425FAf3B746C082C2f942b4E5159887B0005) |

---

## Project Layout

```
├── contracts/
│ ├── PaymentPermit.sol # Main permit & transfer logic
│ ├── EIP712.sol # EIP-712 domain and hashing
│ ├── interface/
│ │ ├── IPaymentPermit.sol # Permit structs and interface
│ │ └── IEIP712.sol
│ └── libraries/
│ └── PermitHash.sol # TypeHashes and struct hashes
├── deploy/ # Hardhat deploy scripts
│ ├── x402BasePermit2Proxy.sol # Shared Permit2 witness-transfer logic
│ ├── x402ExactPermit2Proxy.sol # exact scheme
│ ├── x402UptoPermit2Proxy.sol # upto scheme
│ ├── x402BatchSettlement.sol # batch-settlement payment channels
│ ├── periphery/ # Deposit collectors (ERC-3009 / Permit2)
│ ├── interfaces/ # ISignatureTransfer, IERC3009, IDepositCollector
│ └── legacy/ # Retained PaymentPermit (not deployed)
├── deploy/ # Hardhat deploy scripts (EVM)
├── deployTron/ # TRON deploy scripts
├── test/
│ ├── PaymentPermit.t.sol # Forge/Hardhat tests
│ └── MockERC20.sol
├── hardhat.config.ts
├── foundry.toml
└── AGENTS.md # Guidelines for AI/agent use of this repo
└── foundry.toml
```

---

## Integration

1. **Domain & types** — Use the same EIP-712 domain name `"PaymentPermit"` and the struct definitions from `IPaymentPermit.sol` and `PermitHash.sol` so that hashes match the contract. Domain separator uses `block.chainid` and contract address (see `EIP712.sol`).
2. **ChainId for signing** — When building EIP-712 typed data, use the chainId of the target network so the signature matches the contract. Wallet/TronLink must use the same chainId.
3. **Sign off-chain** — Build `PaymentPermitDetails` (meta, buyer, caller, payment, fee, delivery), hash with `PermitHash` and domain separator, then sign (e.g. 65-byte `r || s || v`).
4. **Submit on-chain** — Call `permitTransferFrom(permit, transferDetails, owner, signature)`. The `owner` must have approved the PaymentPermit contract for the `payToken` (and have sufficient balance for `amount` plus optional `feeAmount`).
1. **Approve Permit2** — The payer approves the canonical Permit2 contract for the token (once), or supplies an EIP-2612 `permit` so the facilitator can do it inline via `settleWithPermit(...)`.
2. **Sign permit + witness** — Build a Permit2 `PermitTransferFrom` with the proxy as the spender, and a witness binding the destination (`to`), validity (`validAfter`), and—for `upto`—the authorized `facilitator`. Use the target chainId. See each proxy's `WITNESS_TYPEHASH` / `WITNESS_TYPE_STRING`.
3. **Settle on-chain** — The facilitator calls `settle(...)` (or `settleWithPermit(...)`) on the relevant proxy. The proxy validates the witness and pulls tokens to the destination via Permit2.

For full struct and field definitions, see `contracts/interface/IPaymentPermit.sol`.
For batch-settlement (payment channels), see `contracts/x402BatchSettlement.sol` and the collectors in `contracts/periphery/`.

---

## Security

- **Access**: Only the signer’s signature authorizes transfers; no single admin can move user funds.
- **Replay**: Nonces and `validAfter`/`validBefore` limit replay across chains and time.
- **Access**: Only the payer’s signature authorizes transfers; the proxies hold no admin keys and cannot move user funds.
- **Destination binding**: The destination is signed into the Permit2 witness, so a facilitator cannot redirect funds.
- **Replay**: Permit2 nonces and the witness `validAfter` window bound replay; batch-settlement channels use cumulative claim amounts and a refund nonce.

We welcome responsible disclosure. Please report issues privately before public disclosure when possible.

Expand All @@ -106,8 +116,6 @@ We welcome responsible disclosure. Please report issues privately before public
## Contributing

1. Fork the repo and open a branch from `main`.
2. Follow existing style (Solidity ^0.8.20, existing patterns in `PaymentPermit.sol` and `PermitHash.sol`).
2. Follow existing style (Solidity ^0.8.20, existing patterns in the `x402*` proxy and settlement contracts).
3. Add or update tests for new behavior.
4. Open a PR with a clear description; maintainers will review.

For agent/AI usage of this codebase, see [AGENTS.md](AGENTS.md).
23 changes: 23 additions & 0 deletions contracts/interfaces/IDepositCollector.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

/// @title IDepositCollector
/// @notice Interface for pluggable deposit collectors used by x402BatchSettlement.
/// @dev Collectors handle the token transfer mechanics (ERC-3009, Permit2, etc.)
/// while `x402BatchSettlement` verifies actual token receipt via balance checks.
/// Collectors MUST transfer tokens to `msg.sender` (the calling `x402BatchSettlement`).
interface IDepositCollector {
/// @notice Pull tokens from payer to the calling `x402BatchSettlement` contract.
/// @param payer The address that owns the tokens being deposited
/// @param token The ERC-20 token address
/// @param amount The exact amount of tokens to transfer
/// @param channelId The channel identifier (used by Permit2 collectors for witness binding)
/// @param collectorData Opaque bytes containing collector-specific parameters (signatures, nonces, etc.)
function collect(
address payer,
address token,
uint256 amount,
bytes32 channelId,
bytes calldata collectorData
) external;
}
20 changes: 20 additions & 0 deletions contracts/interfaces/IERC3009.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

/**
* @title IERC3009
* @notice Minimal interface for EIP-3009 transferWithAuthorization / receiveWithAuthorization
* @dev Used by tokens like USDC that support gasless transfers via signed authorizations.
* See https://eips.ethereum.org/EIPS/eip-3009
*/
interface IERC3009 {
function receiveWithAuthorization(
address from,
address to,
uint256 value,
uint256 validAfter,
uint256 validBefore,
bytes32 nonce,
bytes memory signature
) external;
}
Loading
Loading