feat: add use-refund-protocol skill#12
Conversation
Documents the Circle Refund Protocol — non-custodial escrow with arbiter-mediated dispute resolution for stablecoin payments on Arc. Covers: pay/withdraw lifecycle, lockup periods, refundByRecipient, refundByArbiter, earlyWithdrawByArbiter (EIP-712), arbiter fund management, debt settlement, updateRefundTo, and 6 antipatterns.
989b7f4 to
41a2134
Compare
| ``` | ||
|
|
||
| When the arbiter covers a refund from its own balance, the recipient incurs | ||
| a debt. The debt is automatically settled when the recipient next receives |
There was a problem hiding this comment.
This debt-settlement behavior is not what the contract does. pay() does not call _settleDebt(to); it transfers funds in, records the payment, increments balances[to], emits PaymentCreated, and increments nonce. Debt is only settled through settleDebt(recipient) or when withdraw() calls _settleDebt(msg.sender).
The repo’s own tests confirm this: after a later pay(), debts(receiver) is still 100 and balances(receiver) is 100 until settlement runs.
A safer wording would be: new payments increase the recipient’s balance, but existing debt remains until settleDebt(recipient) is called or the recipient attempts withdraw(). As written, builders may assume arbiter exposure is repaid automatically on new payments when it is not.
There was a problem hiding this comment.
Fixed — corrected the description to clarify that pay() does not settle debt automatically. Debt is only cleared via explicit settleDebt(recipient) or when the recipient calls withdraw().
| // Funds go to the refundTo address — recipient cannot block this | ||
| ``` | ||
|
|
||
| ### 6. Early withdrawal authorized by arbiter |
There was a problem hiding this comment.
This section should not present earlyWithdrawByArbiter() as ready-to-copy integration guidance in its current form. The current refund-protocol README has an active Security Notice for the early withdrawal function: it says the issue allows an arbiter to drain other users’ payments and that a fix is still in development.
There is also a concrete signature mismatch in this sample. signTypedData() produces canonical EIP-712 array encoding, but the contract verifies ecrecover(_hashEarlyWithdrawalInfo(...)), and _hashEarlyWithdrawalInfo() hashes paymentIDs and withdrawalAmounts with abi.encode(...) directly. I checked this with viem@2.44.4; the typed-data digest and the contract digest differ for the same inputs, so the signature produced by this snippet will not verify.
I’d either remove this integration section until the upstream contract is fixed, or clearly mark it unsafe and replace the signing flow with one that exactly matches hashEarlyWithdrawalInfo().
There was a problem hiding this comment.
Fixed — added a security warning about the active upstream issue and replaced signTypedData() with encodeAbiParameters + keccak256 + sign to match _hashEarlyWithdrawalInfo() exactly.
| functionName: "nonce", | ||
| }); | ||
|
|
||
| // Step 3: Pay — funds go into escrow |
There was a problem hiding this comment.
This flow is titled “Basic payment with lockup,” but the payment here does not actually use LOCKUP_SECONDS. The contract reads lockupSeconds[to] inside pay(), and that mapping defaults to 0 unless the arbiter already called setLockupSeconds(recipient, ...) before the payment.
Following this section in order creates an immediately withdrawable payment, while the log below still prints a 7-day lockup estimate. I’d move the setLockupSeconds step before pay(), or mark it as a prerequisite and read the actual releaseTimestamp from payments(paymentId) after the transaction.
There was a problem hiding this comment.
Fixed — moved setLockupSeconds before pay() in the flow and replaced the estimated timestamp log with a read from payments(paymentId).releaseTimestamp.
…nd security notice
What this PR adds
A new skill
use-refund-protocoldocumenting the Circle Refund Protocol — non-custodial escrow with arbiter-mediated dispute resolution for stablecoin payments on Arc.Motivation
The
circlefin/refund-protocolrepository andarc-escrowsample app exist but no skill documents the integration pattern. This skill fills that gap.What's included
SKILL.md(537 lines) covering all 10 contract functionsCloses #11
Submitted via Claude Code + WSL using the Circle MCP server.