Skip to content

Latest commit

 

History

9 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

moneta-cost-model

What a payment actually costs, and who pays it.

A zero-dependency model of the cost mechanics under a SeraPay-style payment. No network calls, no API key, no build step. Node 18 or newer.

node src/cli.js --invoice 230 --from USDC --to MYRT --swap-cost 4.30 --vs 0.03

  route              swap
  mode               pay_more
  invoice            230
  merchant receives  230
  payer sends        234.3
  cost               4.3  (1.8696%, borne by the payer)

  vs 3.00%       breakeven at 143.33, so at 230, this rail is cheaper

Why this exists

I build on Sera, and for a week I described the cost of a payment wrongly in public. Not by a little. I had the wrong party paying it.

The mechanics are not complicated, but they are spread across a fee page, a quote response and a comment in a payment handler, and if you read only the marketing you will get them backwards the same way I did. So this encodes them, with the source of each one written down, and a test suite that fails if the model drifts from what the documentation says.

The three things worth knowing

1. A same-currency payment never touches the order book.

If the payer already holds the currency the merchant keeps, there is no conversion. It is a plain token transfer, and it costs a normal network fee. Sera's fee documentation puts deposits, withdrawals and direct ERC-20 transfers in their own category: they "produce a normal Ethereum transaction... so you pay the standard network gas fee for that transaction. There is no separate Sera charge on top of it."

The swap cost is a property of a currency mismatch, not of the product. That single branch is routeFor() and it changes the answer more than anything else in this repo.

2. The conversion cost is flat, not a percentage.

A swap carries an execution cost that does not scale with size. It exists because the payer never has to hold ETH for gas. Sera's documentation is explicit that for swaps, "you do not need to hold ETH for gas, it is already included" in the quote. Somebody pays that gas and prices it in.

Flat is the word that matters. The same cost is a rounding error on a large payment and most of a small one, which decides who should use this rail far more than any headline percentage.

Note that Sera's fee documentation does not publish a fixed protocol fee percentage. If you have seen one quoted, check where it came from. Read the fee_breakdown on a real quote instead, which is what costFromQuote() is for.

3. Who pays it is a mode, and the default protects the merchant.

Sera's quote endpoint supports two cost application modes:

  • receive_less deducts the cost from the output. The merchant receives less.
  • pay_more adds it to the input. The payer sends more.

SeraPay's payment path requests pay_more, and the comment above it says a payment must preserve what the merchant receives, so the execution cost is added to the customer's maximum input rather than subtracted from the merchant's output.

So the merchant receives the full invoiced amount, and the cost lands on the payer. That is not a pricing promise that can be quietly reversed. It is a property of how the payment is constructed.

What follows from it

A flat cost on the payer behaves very differently from a percentage on the merchant, and worse in one specific way: a merchant fee is invisible to the buyer, while a surcharge is the last number they see before deciding whether to bother.

node src/cli.js --invoice 15 --from USDC --to MYRT --swap-cost 4.30 --day 60

  60 payments of 15 in a day, total 900
    converting each one    258  (28.6667%)
    converting once        4.3  (0.4778%)

That is the entire argument for settling in one currency and converting on a schedule rather than per sale, in two lines of output.

API

const { quotePayment, routeFor, breakeven, batched, costFromQuote, MODE, ROUTE } =
  require('./src/cost');

routeFor('USDC', 'USDC');        // 'direct_transfer'
routeFor('USDC', 'MYRT');        // 'swap'

quotePayment({
  invoice: 230,
  payerCurrency: 'USDC',
  merchantCurrency: 'MYRT',
  swapCost: 4.30,
  mode: MODE.PAY_MORE,
});
// { route:'swap', merchantReceives:230, payerSends:234.3,
//   cost:4.3, borneBy:'payer', costPct:1.8696, ... }

breakeven(1.00, 0.02);           // 50    the ticket where a flat $1 beats a 2% fee
batched([33.33, 33.33, ...]);    // per-sale vs once-a-day conversion
costFromQuote(quoteResponse);    // reads fee_breakdown.gas_cost_usd

Tests

node test/cost.test.js

Seventeen assertions, no framework. They cover routing, both cost modes, breakeven, batching, quote parsing and the input guards. The incidence tests are the ones that matter: they are the mistake I actually made.

Defaults, and how to replace them

--swap-cost defaults to 1.00 and --fee to 0.01. Both are illustrative.

Do not trust them for anything real. Ask the quote endpoint for a live quote, read fee_breakdown.gas_cost_usd off the response, and pass that in. Asking for a quote is a read: nothing is signed, nothing is submitted, no funds move.

Sources

Everything above came from primary sources, read in August 2026:

  • docs.sera.cx/fees for the split between swaps and transfers, and for gas being included in a swap quote
  • docs.sera.cx/contracts for the contract surface
  • sera-cx/sera-pay, the payment path, for pay_more and the comment on preserving the merchant amount
  • a live quote response, for the shape of fee_breakdown

If any of these change, the tests here should be the first thing that fails.

Running this against live rates

The defaults above are placeholders. Real numbers come from a live quote.

If you do not have a Sera account yet, this is my referral link: https://g.sera.cx/9Lf9dd8it4

It is a referral link, so it credits me if you sign up and trade. The plain documentation links are in Sources above if you would rather not use it.

Licence

MIT.

About

What a payment on Sera actually costs, and who pays it. Zero dependencies, no network calls.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages