Tags: commercelayer/rail0
Tags
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>
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>
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
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>
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>
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>
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 — 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 — 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>
PreviousNext