# Settlement contract

> The on-chain endpoint where aa-native payments land. Replay-protected, allowlist-gated, UUPS-upgradeable.

The `SettlementContract` is the on-chain piece of NERO 402. Every `aa-native` payment ends in a call to its `settle(...)` function. The contract is a single shared deployment used by every merchant; merchants are arguments to the call, not separate contracts.

## Function signature

```solidity
function settle(
  address merchant,
  address token,
  uint256 amount,
  bytes32 requestHash
) external payable nonReentrant whenNotPaused;
```

The function:

1. Reverts on zero values, unallowed tokens, and reused `requestHash`.
2. Marks `isSettled[requestHash] = true` (replay protection).
3. Calls `IERC20(token).safeTransferFrom(msg.sender, merchant, amount)`.
4. Post-checks that the merchant's balance increased by exactly `amount`.
5. Emits `ReceiptLog(merchant, msg.sender, token, amount, requestHash)`.

`msg.sender` is the paying SCW. The SCW must have approved the settlement contract for at least `amount` of `token` before the call. The reference SDK encodes the approval and the settlement in one `executeBatch([approve, settle])` UserOp, so they are atomic.

## State

```solidity
mapping(bytes32 requestHash => bool settled) public isSettled;
mapping(address token => bool allowed)        public isTokenAllowed;
uint256[48] private __gap;
```

`isSettled` is the authoritative replay registry — once a `requestHash` is `true`, the contract refuses to settle it again. `isTokenAllowed` is an owner-managed allowlist of the tokens the contract will accept transfers for. The `__gap` reserves storage for future upgrades.

## Replay protection

The `requestHash` field is a `keccak256` of `(merchantAddress, chainId, httpMethod, canonicalEndpoint, timestampBucket, clientNonce)`. The merchant address is part of the hash, so a payload signed for merchant A produces a different hash from one a malicious party would need to settle against merchant B. Because `isSettled` is global, the same hash never settles twice regardless of which merchant the call names.

## Upgrade pattern

The contract uses the OpenZeppelin v5 UUPS proxy pattern. An `ERC1967Proxy` is deployed in front of an upgradeable implementation. The implementation calls `_disableInitializers()` in its constructor so it can never be driven directly. `_authorizeUpgrade` is owner-only and rejects the zero address.

The upgrade authorization path adds non-trivial audit surface; the contract is currently pre-audit and will undergo external review before the v1.0 release.

## Custom errors

```solidity
error UnexpectedNativeValue();   // settle() called with msg.value != 0
error ZeroAddress();              // merchant or token is 0x0
error ZeroAmount();               // amount is 0
error ZeroRequestHash();          // requestHash is bytes32(0)
error TokenNotAllowed(address);   // token is not on the allowlist
error AlreadySettled(bytes32);    // requestHash has already been settled
```

These are the only revert paths. A reverted UserOp surfaces in the facilitator's `/settle` response as `errorCode: "user_op_failed"` with the surfaced reason.

## Deployment addresses

- **Mainnet** (`eip155:1689`):
  proxy `0x5eCfc64f2339992668f555918674B604F97B412D`,
  implementation `0xb7f16185619c476ce3fd3fd9e8b6186e340802f6`.

- **Testnet** (`eip155:689`):
  proxy `0x925dbba44570683ac8da99be08bc5ece8cf5a8c6`.

The proxy address is the only address agents and merchants need to know. Implementations may be upgraded behind it.

## Verifying settlements externally

A merchant who needs strong assurance can independently verify the on-chain state of any settlement by reading the `ReceiptLog` event:

```solidity
event ReceiptLog(
  address indexed merchant,
  address indexed payer,
  address indexed token,
  uint256 amount,
  bytes32 requestHash
);
```

The reference Playground's live ledger panel does exactly this — it polls `getLogs` on the settlement contract independently of the facilitator's reports.

The contract source is [`contracts/src/SettlementContract.sol`](https://github.com/nerochain/nero-x402/blob/main/contracts/src/SettlementContract.sol). The complete ABI is on the [Contract reference page](/docs/reference/contract).
