Skip to content

Tags: commercelayer/rail0

Tags

v1.2.1

Toggle v1.2.1's commit message
v1.2.1 — Enumerate the token allowlist (acceptedTokens view)

Adds acceptedTokens(), a view returning the full token allowlist in
constructor order, complementing the existing single-token
isAcceptedToken(address). Additive, non-breaking to behavior, but the
VERSION/EIP-712 domain bump to 1.2.1 requires a redeploy to new addresses:
- Arc Testnet:  0x841608e9cB4D62607D3b1d2617aF67025C30C16e (USDC, EURC)
- Celo Sepolia: 0x58E1A21F6d34e9F9Ecc441B8079befd0ff892De2 (USDC, USDT)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

v1.2.0

Toggle v1.2.0's commit message
docs(release): v1.2.0 deployed addresses and version badge

Redeploy of RAIL0 v1.2.0 to the live testnets (new immutable addresses):
- Arc Testnet:  0x58E1A21F6d34e9F9Ecc441B8079befd0ff892De2 (USDC, EURC)
- Celo Sepolia: 0xd9b5Be76F99EC8AE583dc1385832B2E54D406792 (USDC, USDT)

Update the address tables in README and the site, and bump the nav version
badge v1.1.0 -> v1.2.0 on both pages.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

v1.1.0

Toggle v1.1.0's commit message
v1.1.0 — On-chain buyer dispute signal

v1.0.0

Toggle v1.0.0's commit message
RAIL0 v1.0.0

Peer-to-peer stablecoin payments with no intermediaries.
- No fees: feeBps/feeReceiver removed; merchant receives every captured token in full.
- Restricted submission: payee-only on authorize/charge/capture/void/refund;
  payer-or-payee on release.
- EIP-712 domain version bumped to 1.0.0 (invalidates prior signatures).

Deployed:
- Arc testnet:  0x4CCC4DdeBB8A63B9186936A8C0FA404910A4311b
- Celo Sepolia: 0x0e393A626EfC45EBd030EBB997CDa207013C4364

v0.9.0

Toggle v0.9.0's commit message
Remove dead code, bump to v0.9.0, redeploy to Arc and Celo Sepolia

Cleanup (no behavior change):
- Remove unused `error InvalidRefundNonce()` (declared, never thrown)
- Remove unused `_safeTransferFrom` helper (no callers since refund moved
  to EIP-3009 receiveWithAuthorization in PR #3)
- Bump VERSION 8 -> 9 so the new bytecode reports a distinct version from
  the already-deployed v0.8.0 contracts

Deployed:
- Arc testnet: 0x0e393A626EfC45EBd030EBB997CDa207013C4364
  (USDC + EURC; verified on Sourcify)
- Celo Sepolia: 0x7337ce441e831ef2904b7B2f33507d655a4381d0
  (USDC + USDT; verified on Blockscout)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

v0.8.0

Toggle v0.8.0's commit message
feat: refund via EIP-3009 receiveWithAuthorization — remove approve step

Replace the ERC-20 approve + transferFrom pattern in refund() with EIP-3009
receiveWithAuthorization, making the refund flow symmetric with authorize/charge.

Changes:
- RAIL0.sol VERSION 7 → 8
- interfaces/IERC20.sol: add receiveWithAuthorization to IEIP3009
- RAIL0.sol: add _REFUND_NONCE_PREFIX constant
- RAIL0.sol: refund() accepts (paymentId, p, amount, v, r, s) — payee signs
  TransferWithAuthorization off-chain; anyone can submit (same pattern as authorize)
- RAIL0.sol: refund nonce encodes current refundableAmount, making each partial
  refund nonce unique and replay-proof across the payment's lifetime
- RAIL0.sol: add refundNonce() view for off-chain nonce computation
- RAIL0.sol: remove _safeTransferFrom (no longer needed)
- test/RAIL0.t.sol: add receiveWithAuthorization to MockERC20
- test/RAIL0.t.sol: remove payee pre-approve from setUp
- test/RAIL0.t.sol: add _refund() helper (signs + submits EIP-3009 refund)
- test/RAIL0.t.sol: update all refund tests for new signature
- test/RAIL0.t.sol: add test_Refund_AnyoneCanSubmit, test_Refund_RevertsOnBadSignature,
  test_Refund_RevertsOnNonceReplay

Benefits:
- No separate ERC-20 approve transaction before refund
- Payee never needs to manage token allowances
- Refund can be submitted by anyone (relayer, merchant, watchdog)
- Indexer sees only PaymentRefunded — no ERC-20 Approval tracking needed
- All tokens in the RAIL0 allowlist already support EIP-3009

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

v0.7.0

Toggle v0.7.0's commit message
Unify amount/maxAmount in Payment struct, bump to v0.7.0

`amount` is the exact value the buyer commits to pay and signs via
EIP-3009 — `maxAmount` was a misnomer with no distinct semantic.
Replace `uint120 maxAmount` with `uint120 amount` in the Payment struct
and remove the redundant `uint256 amount` parameter from `authorize`
and `charge`. The config hash still commits to the amount, preserving
the anti-tampering guarantee via nonce derivation.

Also: VERSION changed from uint256 to string so _VERSION_HASH is
derived automatically via keccak256(bytes(VERSION)).

Breaking changes: new ABI for authorize/charge (no amount param),
new typehash, new domain separator version → requires fresh deployment.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

v0.6.0

Toggle v0.6.0's commit message
Drop validAfter/validBefore from authorize/charge, bump to v0.6.0

EIP-3009 window is now pinned by the contract (validAfter=0,
validBefore=p.authorizationExpiry), eliminating two redundant params.
Adds explicit block.timestamp >= authorizationExpiry check in
_validatePayment as defense in depth.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

v0.5.0

Toggle v0.5.0's commit message
v0.5.0 — drop preApprovalExpiry (vestigial after EIP-3009)

preApprovalExpiry was the Payment-level deadline for opening a payment.
It existed in v0.0/v0.1 to bound the time between the buyer's separate
approve/permit step and the actual authorize call.

In the v0.4.x EIP-3009 model, the buyer's signed validBefore field
already provides this deadline at the right layer (the token's domain).
Having both was redundant — only the tighter constraint ever mattered,
and they were both controlled by the buyer anyway.

Contract changes
- Payment struct: 9 fields → 8 (drop preApprovalExpiry).
- _PAYMENT_TYPEHASH: updated for new field list.
- _hash: drops the field from abi.encode.
- _validatePayment: drops 3 checks (preApproval==0, preApproval>auth,
  block.timestamp>=preApproval). Adds an explicit authorizationExpiry==0
  check that previously was caught implicitly.
- Drops PreApprovalExpired error.
- VERSION 4 → 5; EIP-712 domain version "4" → "5".

Tests
- _payment() helper drops the field.
- Removes RevertsAtPreApprovalExpiry tests (Authorize + Charge).
- Replaces RejectsZeroPreApprovalExpiry → RejectsZeroAuthorizationExpiry.
- Updates RejectsBadExpiriesOrder to test the remaining invariant
  (authorizationExpiry > refundExpiry).
- Updates AcceptsExpiriesEqual for the simplified constraint.
- 96 tests pass (98 → 96; net -2 from removing preApproval-specific
  tests).

README
- Lifecycle paragraph updated to two windows instead of three; mentions
  validBefore as the buyer's opening-window control.
- Payment struct table drops the preApprovalExpiry row.
- Errors table drops PreApprovalExpired.
- Examples PAYMENT env var and PAYMENT_TYPE updated for 8-field struct.
- Charge subsection: "before preApprovalExpiry" → "EIP-3009 sig still
  within its validBefore".

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

v0.4.0

Toggle v0.4.0's commit message
v0.4.0 — rename reclaim → release

reclaim was buyer-centric ("I am reclaiming MY funds") but the function
is now public (anyone can call it on the buyer's behalf, since funds
always go to p.payer regardless of submitter). release is generic,
matches card-rails vocabulary ("the bank releases the auth hold at
expiry"), and reads correctly regardless of who calls.

Renames
- function reclaim → function release
- event PaymentReclaimed → event PaymentReleased
- error NothingToReclaim → error NothingToRelease

Versioning
- VERSION 3 → 4
- EIP-712 domain version "3" → "4" (so signatures don't accidentally
  cross v0.3.0/v0.4.0 boundaries)

Tests
- All test_Reclaim_* renamed to test_Release_*; rail0.reclaim → rail0.release.
- 51 tests, all green.

README
- All function/event/error references updated.
- Lifecycle subsection title: "Reclaim" → "Release".
- Examples subsection updated.
- Bonus cleanup: security model said "All nine entrypoints (...,
  permitAnd*)" — stale from v0.2.0. Now correctly says "All six
  entrypoints" matching the current contract surface.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>