Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 10 additions & 49 deletions benchmarks/fiat-onramp-cost.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@
slug: fiat-onramp-cost
number: "262"
title: Fiat on-ramp all-in cost, EUR to BTC premium over exchange mid
seo_title: "Fiat on-ramp cost 2026: MoonPay vs Transak vs Ramp, all-in premium over spot in bps"
seo_description: "{{best_name}} quotes the lowest all-in cost for 500 EUR of BTC by card at {{best_p50}} over the Kraken mid. Declared fee plus hidden spread for MoonPay, Transak and Ramp, quoted every 5 minutes from France."
seo_title: "Fiat on-ramp cost 2026: MoonPay vs Transak, all-in premium over spot in bps"
seo_description: "{{best_name}} quotes the lowest all-in cost for 500 EUR of BTC by card at {{best_p50}} over the Kraken mid. Declared fee plus hidden spread for MoonPay and Transak, quoted every 5 minutes from France."
subtitle: What a French retail buyer actually pays per bitcoin, in basis points over the Kraken EUR mid, once the declared fee, the network fee and the spread hidden in the provider's rate are folded into one number. 500 EUR by card, quoted every 5 minutes. Lower is better.
category: On-ramps
status: live
Expand All @@ -28,11 +28,12 @@ seo_intro: |
see where the cost actually lives. One persona: a retail buyer in
France paying EUR, 500 EUR by card into bitcoin, quoted every five
minutes from an EU vantage; SEPA, USDC and ETH cells are measured on
the same grid and published as their own series. Direct providers (MoonPay, Transak,
Ramp) and aggregators (Onramper, Meld) are two cohorts and are never
ranked against each other: an aggregator's row is the best member
ramp it returned, which is a different product from a single
provider's own price. Lower is better.
the same grid and published as their own series. MoonPay and Transak
are quoted today; Ramp, and the aggregators Onramper and Meld, join
the table as their API keys are granted. Direct providers and
aggregators are two cohorts and are never ranked against each other:
an aggregator's row is the best member ramp it returned, which is a
different product from a single provider's own price. Lower is better.

abstract: |
The harness quotes each provider on a fixed grid every five minutes:
Expand All @@ -59,18 +60,17 @@ methodology:
- "Metric. all_in_premium_bps = (fiat_in / crypto_out minus spot) / spot times 10 000. fiat_in is the total the buyer pays including every fee the provider adds; crypto_out is the amount the provider says will be delivered. Positive means the buyer pays above the exchange mid. A value near zero means the provider's rate plus fees matched the mid."
- "Spot reference. Kraken public Ticker, EUR pairs, mid of best bid and ask, fetched in the same cycle as the quotes (within two seconds). A second reference from Pyth Hermes (BTC/USD, ETH/USD, USDC/USD divided by EUR/USD) is recorded on every quote under spot_ref=pyth and the gap between the two is published as spot_reference_divergence_bps. Kraken is the headline because it has no FX leg."
- "Declared versus hidden. declared_fee_bps = (provider fee plus network fee plus partner fee) / fiat_in times 10 000 as the provider itemizes them. hidden_spread_bps = all_in minus declared, the part of the cost that lives in the rate rather than in the fee line."
- "Cohorts. Direct providers (MoonPay, Transak, Ramp; Mercuryo quoted for card only) are one cohort. Aggregators (Onramper, Meld) are another: one request returns one quote per member ramp, each member sample is labelled provider=member and via=aggregator, and the aggregator's row is the minimum across its members. The two cohorts are not ranked against each other."
- "Cohorts. Direct providers (MoonPay and Transak today; Ramp and Mercuryo once keyed) are one cohort. Aggregators (Onramper, Meld, once keyed) are another: one request returns one quote per member ramp, each member sample is labelled provider=member and via=aggregator, and the aggregator's row is the minimum across its members. The two cohorts are not ranked against each other."
- "Cadence. Every 5 minutes with a plus or minus 10 percent jitter, 12 cells per provider per cycle, at most 2 requests in flight per provider. Ramp documents 100 requests per minute per IP; the harness stays two orders of magnitude below that. A 4xx is never retried; a 5xx or timeout is retried once."
- "Failures. A provider that answers without a usable quote counts as success 0 on that cell and its gauges are deleted rather than frozen. A provider whose key is not configured is skipped, not failed. Errors are counted by reason: no_quote, http_4xx, http_5xx, rate_limited, timeout, network, parse."
- "Sample size. The harness increments a counter per usable quote; the table reads increase() over 24h so the count is quote events, not Prometheus scrapes of a gauge. Expected 288 per cell per day."
- "Reproducibility. Every sample carries the SHA-256 of the raw provider response in the harness log. The adapters are written against each provider's documented schema; the parameters not yet exercised with a live key are listed in harnesses/fiat-onramp-cost/CHECKLIST.md and stay marked UNVERIFIED until they are."
- "Not measured. Coinbase Onramp is excluded pending a read of the CDP terms on benchmarking. Apple Pay and Google Pay cells need a wallet session and are not quotable server-side. ATM operators publish no quote API. Banxa and Alchemy Pay reach the bench only through the aggregators in this version."
- "Not measured yet. Ramp, Onramper and Meld have adapters in the harness and enter the table when their keys are granted (requested 2026-09-14). Coinbase Onramp is excluded pending a read of the CDP terms on benchmarking. Apple Pay and Google Pay cells need a wallet session and are not quotable server-side. ATM operators publish no quote API. Banxa and Alchemy Pay reach the bench only through the aggregators in this version."

findings:
- "{{best_name}} currently quotes the lowest all-in premium on the headline cell at {{best_p50}} over the Kraken EUR mid (24h median, 500 EUR by card into BTC)."
- "{{name:moonpay}} sits at {{p50:moonpay}}. MoonPay prices by request IP, so the number is what an EU vantage sees, not a country parameter."
- "{{name:transak}} sits at {{p50:transak}}. Transak takes the country as a parameter and itemizes the network fee separately from its own fee."
- "{{name:ramp}} sits at {{p50:ramp}}. Ramp returns every payment method in one answer, so its card and SEPA cells come from the same quote."
- "Read the hidden spread next to the declared fee: a provider whose declared fee looks low can still sit high on the all-in number when its rate carries the margin."

source: https://github.com/ChainBench/OpenChainBench/tree/main/harnesses/fiat-onramp-cost
Expand Down Expand Up @@ -122,42 +122,3 @@ providers:
success: avg_over_time(onramp_quote_success{provider="transak",via="direct",asset="btc",network="bitcoin",payment_method="card",notional="500",fiat="EUR",country="FR",region="eu-west"}[24h])
sample_size: increase(onramp_quote_samples_total{provider="transak",via="direct",asset="btc",network="bitcoin",payment_method="card",notional="500",fiat="EUR",country="FR",region="eu-west"}[24h])
series: onramp_quote_all_in_premium_bps{provider="transak",via="direct",asset="btc",network="bitcoin",payment_method="card",notional="500",fiat="EUR",country="FR",region="eu-west",spot_ref="kraken_eur"}
- slug: ramp
name: Ramp
tag: Direct on-ramp, card and SEPA, quote returns every payment method at once
type: protocol
formula: "24h median of the all-in premium over Kraken EUR mid on the headline cell (BTC, card, 500 EUR, France), quoted directly through Ramp's own API."
queries:
p50: quantile_over_time(0.50, onramp_quote_all_in_premium_bps{provider="ramp",via="direct",asset="btc",network="bitcoin",payment_method="card",notional="500",fiat="EUR",country="FR",region="eu-west",spot_ref="kraken_eur"}[24h])
p90: quantile_over_time(0.90, onramp_quote_all_in_premium_bps{provider="ramp",via="direct",asset="btc",network="bitcoin",payment_method="card",notional="500",fiat="EUR",country="FR",region="eu-west",spot_ref="kraken_eur"}[24h])
p99: quantile_over_time(0.99, onramp_quote_all_in_premium_bps{provider="ramp",via="direct",asset="btc",network="bitcoin",payment_method="card",notional="500",fiat="EUR",country="FR",region="eu-west",spot_ref="kraken_eur"}[24h])
mean: avg_over_time(onramp_quote_all_in_premium_bps{provider="ramp",via="direct",asset="btc",network="bitcoin",payment_method="card",notional="500",fiat="EUR",country="FR",region="eu-west",spot_ref="kraken_eur"}[24h])
success: avg_over_time(onramp_quote_success{provider="ramp",via="direct",asset="btc",network="bitcoin",payment_method="card",notional="500",fiat="EUR",country="FR",region="eu-west"}[24h])
sample_size: increase(onramp_quote_samples_total{provider="ramp",via="direct",asset="btc",network="bitcoin",payment_method="card",notional="500",fiat="EUR",country="FR",region="eu-west"}[24h])
series: onramp_quote_all_in_premium_bps{provider="ramp",via="direct",asset="btc",network="bitcoin",payment_method="card",notional="500",fiat="EUR",country="FR",region="eu-west",spot_ref="kraken_eur"}
- slug: onramper
name: Onramper
tag: Aggregator, one call returns a quote per member ramp
type: aggregator
formula: "Best member ramp Onramper returns on the headline cell: 24h median of each member's all-in premium over Kraken EUR mid, then the minimum across members. Aggregator cohort, not ranked against direct rows."
queries:
p50: min(quantile_over_time(0.50, onramp_quote_all_in_premium_bps{via="onramper",cohort="aggregator",asset="btc",network="bitcoin",payment_method="card",notional="500",fiat="EUR",country="FR",region="eu-west",spot_ref="kraken_eur"}[24h]))
p90: min(quantile_over_time(0.90, onramp_quote_all_in_premium_bps{via="onramper",cohort="aggregator",asset="btc",network="bitcoin",payment_method="card",notional="500",fiat="EUR",country="FR",region="eu-west",spot_ref="kraken_eur"}[24h]))
p99: min(quantile_over_time(0.99, onramp_quote_all_in_premium_bps{via="onramper",cohort="aggregator",asset="btc",network="bitcoin",payment_method="card",notional="500",fiat="EUR",country="FR",region="eu-west",spot_ref="kraken_eur"}[24h]))
mean: min(avg_over_time(onramp_quote_all_in_premium_bps{via="onramper",cohort="aggregator",asset="btc",network="bitcoin",payment_method="card",notional="500",fiat="EUR",country="FR",region="eu-west",spot_ref="kraken_eur"}[24h]))
success: avg_over_time(onramp_quote_success{provider="onramper",via="onramper",asset="btc",network="bitcoin",payment_method="card",notional="500",fiat="EUR",country="FR",region="eu-west"}[24h])
sample_size: max(increase(onramp_quote_samples_total{via="onramper",cohort="aggregator",asset="btc",network="bitcoin",payment_method="card",notional="500",fiat="EUR",country="FR",region="eu-west"}[24h]))
series: min(onramp_quote_all_in_premium_bps{via="onramper",cohort="aggregator",asset="btc",network="bitcoin",payment_method="card",notional="500",fiat="EUR",country="FR",region="eu-west",spot_ref="kraken_eur"})
- slug: meld
name: Meld
tag: Aggregator, one call returns a quote per member ramp
type: aggregator
formula: "Best member ramp Meld returns on the headline cell: 24h median of each member's all-in premium over Kraken EUR mid, then the minimum across members. Aggregator cohort, not ranked against direct rows."
queries:
p50: min(quantile_over_time(0.50, onramp_quote_all_in_premium_bps{via="meld",cohort="aggregator",asset="btc",network="bitcoin",payment_method="card",notional="500",fiat="EUR",country="FR",region="eu-west",spot_ref="kraken_eur"}[24h]))
p90: min(quantile_over_time(0.90, onramp_quote_all_in_premium_bps{via="meld",cohort="aggregator",asset="btc",network="bitcoin",payment_method="card",notional="500",fiat="EUR",country="FR",region="eu-west",spot_ref="kraken_eur"}[24h]))
p99: min(quantile_over_time(0.99, onramp_quote_all_in_premium_bps{via="meld",cohort="aggregator",asset="btc",network="bitcoin",payment_method="card",notional="500",fiat="EUR",country="FR",region="eu-west",spot_ref="kraken_eur"}[24h]))
mean: min(avg_over_time(onramp_quote_all_in_premium_bps{via="meld",cohort="aggregator",asset="btc",network="bitcoin",payment_method="card",notional="500",fiat="EUR",country="FR",region="eu-west",spot_ref="kraken_eur"}[24h]))
success: avg_over_time(onramp_quote_success{provider="meld",via="meld",asset="btc",network="bitcoin",payment_method="card",notional="500",fiat="EUR",country="FR",region="eu-west"}[24h])
sample_size: max(increase(onramp_quote_samples_total{via="meld",cohort="aggregator",asset="btc",network="bitcoin",payment_method="card",notional="500",fiat="EUR",country="FR",region="eu-west"}[24h]))
series: min(onramp_quote_all_in_premium_bps{via="meld",cohort="aggregator",asset="btc",network="bitcoin",payment_method="card",notional="500",fiat="EUR",country="FR",region="eu-west",spot_ref="kraken_eur"})
Loading