You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Searched the circlefin org for any existing sample, reference pattern, or advisory covering invoice factoring / purchase-order financing on Arc (keywords: invoice, factoring, purchase order, accounts receivable, debtor, financier, working capital, trade finance — checked arc-node, arc-fintech, perimeter-protocol, arc-escrow, and org-wide). There isn't one. Given Arc's stablecoin-as-gas + CCTP + Gateway building blocks are a natural fit for receivables financing (a very common real-world commerce primitive), this seems like a documentation/reference-app gap worth flagging — similar in spirit to the PaymentEscrow advisory in #86.
We built an InvoiceFactoring contract as part of a stablecoin dApp on Arc Testnet and are sharing the design here as a possible starting point for an official reference pattern or sample app.
Design
Lifecycle:
Created → Funded → Delivered → Repaid
\→ Defaulted (permissionless, after dueDate)
Created → Cancelled (by sme, before funding)
Three roles per invoice: sme (raises it), financier (advances capital at a discount), debtor (owes the full face value).
Functions:
createInvoice(debtor, token, faceValue, advanceBps, feeBps, dueDate, description) — moves no funds; snapshots feeBps into the invoice struct at creation time (see note below).
fundInvoice(id) — financier's discounted advance is pulled and forwarded directly to the SME.
submitProofOfDelivery(id, proofHash) — SME posts a delivery proof hash once goods/services are delivered.
repayInvoice(id) — debtor repays the full face value; the contract atomically splits it into a financier payout (advance + fee) and an SME remainder.
markDefaulted(id) — permissionless after dueDate if unpaid.
cancelInvoice(id) — SME can cancel before funding.
Non-recourse by design — markDefaulted() is permissionless after dueDate but performs no on-chain fund recovery. This is standard real-world factoring risk allocation (the financier prices default risk into feeBps up front), not an oversight — worth stating explicitly in any reference doc so ecosystem developers don't assume otherwise.
Suggested action
Consider an official InvoiceFactoring/receivables-financing reference pattern or sample app, alongside the existing PaymentEscrow-style advisories and sample apps (arc-escrow, arc-nanopayments, arc-p2p-payments).
Summary
Searched the
circlefinorg for any existing sample, reference pattern, or advisory covering invoice factoring / purchase-order financing on Arc (keywords: invoice, factoring, purchase order, accounts receivable, debtor, financier, working capital, trade finance — checkedarc-node,arc-fintech,perimeter-protocol,arc-escrow, and org-wide). There isn't one. Given Arc's stablecoin-as-gas + CCTP + Gateway building blocks are a natural fit for receivables financing (a very common real-world commerce primitive), this seems like a documentation/reference-app gap worth flagging — similar in spirit to thePaymentEscrowadvisory in #86.We built an
InvoiceFactoringcontract as part of a stablecoin dApp on Arc Testnet and are sharing the design here as a possible starting point for an official reference pattern or sample app.Design
Lifecycle:
Three roles per invoice:
sme(raises it),financier(advances capital at a discount),debtor(owes the full face value).Functions:
createInvoice(debtor, token, faceValue, advanceBps, feeBps, dueDate, description)— moves no funds; snapshotsfeeBpsinto the invoice struct at creation time (see note below).fundInvoice(id)— financier's discounted advance is pulled and forwarded directly to the SME.submitProofOfDelivery(id, proofHash)— SME posts a delivery proof hash once goods/services are delivered.repayInvoice(id)— debtor repays the full face value; the contract atomically splits it into a financier payout (advance + fee) and an SME remainder.markDefaulted(id)— permissionless afterdueDateif unpaid.cancelInvoice(id)— SME can cancel before funding.Notable design decisions (informed by #86):
feeBpsis stored per-invoice in the struct atcreateInvoicetime rather than read from a mutable global rate, which avoids the "fee computed at release time" class of bug called out in security: developer advisory — 4 vulnerabilities found in common PaymentEscrow patterns when building on Arc Network #86 (Vuln feat: persistence meter, consensus tuning, TLS fix, spammer/arch docs #3).markDefaulted()is permissionless afterdueDatebut performs no on-chain fund recovery. This is standard real-world factoring risk allocation (the financier prices default risk intofeeBpsup front), not an oversight — worth stating explicitly in any reference doc so ecosystem developers don't assume otherwise.Suggested action
InvoiceFactoring/receivables-financing reference pattern or sample app, alongside the existingPaymentEscrow-style advisories and sample apps (arc-escrow,arc-nanopayments,arc-p2p-payments).PaymentEscrowpattern was in security: developer advisory — 4 vulnerabilities found in common PaymentEscrow patterns when building on Arc Network #86.Reference implementation
InvoiceFactoring.sol: https://github.com/osr21/arc-stablecoin-dapp/blob/main/contracts/src/InvoiceFactoring.solDocs: https://github.com/osr21/arc-stablecoin-dapp/blob/main/docs/contracts.md#invoicefactoring
Environment: Arc Testnet (chainId 5042002), Solidity 0.8.20