Money
The 2% fee
The protocol fee, the rounding rule, and a worked example in exact numbers.
The arithmetic #
The fee is computed in integer nANM with basis points, and the rounding rule is fixed:
protocol_fee = floor(gross * 200 / 10000)
merchant_amount = gross - protocol_fee
The fee is floored, never rounded up. Flooring can only ever round in the merchant’s favour, and it makes the invariant
merchant_amount + protocol_fee == anm_amount
exact by construction rather than by a correction step. That invariant is also a CHECK constraint on the payment intents table, so an unbalanced payment cannot be stored even if application code is wrong.
A worked example, in exact numbers #
A 41825.25 ANM order — the same order used in the WooCommerce contract:
| nANM (integer, what the chain sees) | ANM (display) | |
|---|---|---|
| Gross paid by the customer | 41825250000000 | 41825.25 ANM |
| Protocol fee (2.00%) | 836505000000 | 836.505 ANM |
| Merchant amount (98%) | 40988745000000 | 40988.745 ANM |
Check it:
40988745000000 + 836505000000 = 41825250000000
Two more, to show both ends of the scale:
| Gross | Fee | Merchant keeps |
|---|---|---|
1 ANM (1000000000 nANM) | 0.02 ANM (20000000 nANM) | 0.98 ANM |
| 49 nANM (dust) | 0 nANM | 49 nANM |
A payment small enough that 2% floors to zero is charged nothing. We take nothing rather than over-collect on dust.
What the fee is charged on #
- Successful payments only. An expired, failed, underpaid-and-abandoned or never-paid intent costs nothing.
- The gross that actually arrived. On an overpayment the fee is computed on what was received, not on what was requested.
- Once. A reorg that un-settles a payment reverses the fee with contra-entries in the ledger; if the payment re-confirms, it is re-booked once.
- Not returned by a refund. Refunding a customer does not refund the protocol fee — the payment did settle. Model it the way you model a card fee on a refunded order. See Refunds.
The policy is captured per payment #
The fee rate and the treasury address are snapshotted when the intent is created and stored on it (protocol_fee_bps, treasury_address). Settlement uses that snapshot, never the live configuration. A future rate change therefore cannot retroactively re-price a payment that was already quoted to a customer.
The treasury address comes only from the server’s ANIMICA_TREASURY_ADDRESS configuration. It is never accepted from a request, never editable through a merchant API, and never taken from a client.
Where you see the numbers #
| Surface | Fields |
|---|---|
| PaymentIntent API | anm_amount, merchant_amount, protocol_fee, protocol_fee_bps |
| Webhook payload | gross_anm, protocol_fee_anm, merchant_anm |
| Balance API | settled, pending, protocol_fees_paid |
| WooCommerce order screen | Gross / fee (2%) / merchant / transaction / confirmations |
Every one of those is derived from the same integer split, so the dashboard, the plugin and your own reconciliation agree to the nANM.