diff --git a/benchmarks/ink-rpc.yml b/benchmarks/ink-rpc.yml index 6f93117d..38f6a737 100644 --- a/benchmarks/ink-rpc.yml +++ b/benchmarks/ink-rpc.yml @@ -35,7 +35,7 @@ methodology: - "Latency: client-side round-trip delta in milliseconds, exposed as both a gauge and a histogram (buckets 50 ms → 10 s), so percentiles are computed via Prometheus `quantile_over_time` over the last 24 hours." - "Call-result classification: `ok` (HTTP 200 + non-empty result with a parsable hex block number), `http_err`, `jsonrpc_err` (HTTP 200 carrying an error body), `stale` (more than 25 blocks behind the cross-provider tip), `timeout`. Latency without reliability is a misleading ranking signal." - "This page is part of the per-chain RPC cluster derived from the cross-chain [rpc-capabilities](https://openchainbench.com/benchmarks/rpc-capabilities) benchmark; the identical harness, cadence and exclusion rules apply on every chain." - - "Chain scope: every query on this page is pinned to `chain=\"ink\"`. Provider coverage at launch: 4 no-key endpoints (Ink (Gelato), Ink (QuickNode), dRPC, Tenderly Gateway)." + - "Chain scope: every query on this page is pinned to `chain=\"ink\"`. Provider coverage at launch: 4 no-key endpoints (Gelato, QuickNode, dRPC, Tenderly Gateway)." findings: - "{{best_name}} currently leads free Ink RPC at {{best_p50}} (`eth_getBlockByNumber(\"latest\", false)` p50, 24h) across 4 measured providers." @@ -44,7 +44,7 @@ faq: - q: "What is the fastest free Ink RPC right now?" a: "{{best_name}} currently leads at {{best_p50}} (`eth_getBlockByNumber(\"latest\", false)` p50 over the last 24h), measured against 4 no-key providers probed every 60 seconds from us-east, eu-west and Singapore. The leaderboard re-sorts continuously against fresh Prometheus samples, so the answer on this page is the answer right now, not a quarterly snapshot. Use the region tabs to see the leader from the origin closest to your deployment." - q: "Which Ink RPCs work without an API key?" - a: "5 endpoints sustain continuous keyless probing at launch: Ink (Gelato), Ink (QuickNode), dRPC, Tenderly Gateway. Every listed endpoint was live-verified with a `eth_getBlockByNumber(\"latest\", false)` POST returning a parsable response before inclusion. See the methodology section for the specific endpoints that were audited and excluded." + a: "5 endpoints sustain continuous keyless probing at launch: Gelato, QuickNode, dRPC, Tenderly Gateway. Every listed endpoint was live-verified with a `eth_getBlockByNumber(\"latest\", false)` POST returning a parsable response before inclusion. See the methodology section for the specific endpoints that were audited and excluded." - q: "Does the fastest Ink RPC change by region?" a: "Often. Public infra concentrates in specific regions; a gateway that wins from Amsterdam can lose from Singapore by multiples. The region tabs at the top of the page re-scope every number to a single origin; pick the one closest to where your requests originate." - q: "How is Ink RPC latency measured here, technically?" @@ -67,9 +67,9 @@ dimensions: providers: - slug: ink-official - name: Ink (Gelato) + name: Gelato tag: Kraken's Ink Chain official public RPC, Gelato-backed - formula: "50th percentile over 24h of client-side round-trip latency (ms) for a single `eth_getBlockByNumber(\"latest\", false)` POST sent every 60s from 3 regions (us-east + eu-west + sgp) to Ink (Gelato)'s no-key ink RPC endpoint." + formula: "50th percentile over 24h of client-side round-trip latency (ms) for a single `eth_getBlockByNumber(\"latest\", false)` POST sent every 60s from 3 regions (us-east + eu-west + sgp) to Gelato's no-key ink RPC endpoint." queries: p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="ink-official", chain="ink"}) p90: avg(ocb:rpc_latency_milliseconds:p90_24h{provider="ink-official", chain="ink"}) @@ -90,9 +90,9 @@ providers: series: avg_over_time(rpc_latency_milliseconds{provider="ink-official", chain="ink", region="sgp"}[1h]) - slug: ink-quicknode - name: Ink (QuickNode) + name: QuickNode tag: Kraken's Ink Chain second official public RPC, QuickNode-backed - formula: "50th percentile over 24h of client-side round-trip latency (ms) for a single `eth_getBlockByNumber(\"latest\", false)` POST sent every 60s from 3 regions (us-east + eu-west + sgp) to Ink (QuickNode)'s no-key ink RPC endpoint." + formula: "50th percentile over 24h of client-side round-trip latency (ms) for a single `eth_getBlockByNumber(\"latest\", false)` POST sent every 60s from 3 regions (us-east + eu-west + sgp) to QuickNode's no-key ink RPC endpoint." queries: p50: avg(ocb:rpc_latency_milliseconds:p50_24h{provider="ink-quicknode", chain="ink"}) p90: avg(ocb:rpc_latency_milliseconds:p90_24h{provider="ink-quicknode", chain="ink"}) diff --git a/benchmarks/rwa-yield-accuracy.yml b/benchmarks/rwa-yield-accuracy.yml index 7266b720..5e94e365 100644 --- a/benchmarks/rwa-yield-accuracy.yml +++ b/benchmarks/rwa-yield-accuracy.yml @@ -147,3 +147,17 @@ providers: success: rwa_yield_probe_ok{token="ousg"} sample_size: rwa_yield_aum_usd{token="ousg"} series: rwa_yield_delivered_bps_30d{token="ousg"} + + - slug: syrup-usdc + name: SyrupUSDC + tag: "Maple, Ethereum, ERC-4626 NAV" + formula: "ERC-4626 convertToAssets(1 share) delta over 30d, annualized. SyrupUSDC is Maple's yield-bearing USDC vault backed by institutional loans, not T-bills. Compared to the pool's base APY on DefiLlama (excludes SYRUP token incentives)." + queries: + p50: rwa_yield_deviation_bps_30d{token="syrup-usdc"} + p90: rwa_yield_deviation_bps_7d{token="syrup-usdc"} + p99: rwa_yield_deviation_bps_lifetime{token="syrup-usdc"} + mean: rwa_yield_delivered_bps_30d{token="syrup-usdc"} + success: rwa_yield_probe_ok{token="syrup-usdc"} + sample_size: rwa_yield_aum_usd{token="syrup-usdc"} + series: rwa_yield_delivered_bps_30d{token="syrup-usdc"} + diff --git a/harnesses/rpc-capabilities/cmd/script/config.go b/harnesses/rpc-capabilities/cmd/script/config.go index a09adca2..da545732 100644 --- a/harnesses/rpc-capabilities/cmd/script/config.go +++ b/harnesses/rpc-capabilities/cmd/script/config.go @@ -576,8 +576,8 @@ func chains() []Chain { Slug: "ink", Name: "Ink", Providers: []Provider{ - {Slug: "ink-official", Name: "Ink (Gelato)", URL: envDefault("RPC_URL_INK_OFFICIAL", "https://rpc-gel.inkonchain.com")}, - {Slug: "ink-quicknode", Name: "Ink (QuickNode)", URL: envDefault("RPC_URL_INK_QUICKNODE", "https://rpc-qnd.inkonchain.com")}, + {Slug: "ink-official", Name: "Gelato", URL: envDefault("RPC_URL_INK_OFFICIAL", "https://rpc-gel.inkonchain.com")}, + {Slug: "ink-quicknode", Name: "QuickNode", URL: envDefault("RPC_URL_INK_QUICKNODE", "https://rpc-qnd.inkonchain.com")}, {Slug: "drpc", Name: "dRPC", URL: envDefault("RPC_URL_INK_DRPC", "https://ink.drpc.org")}, {Slug: "tenderly", Name: "Tenderly Gateway", URL: envDefault("RPC_URL_INK_TENDERLY", "https://ink.gateway.tenderly.co")}, }, diff --git a/harnesses/rwa-yield-accuracy/cmd/script/main.go b/harnesses/rwa-yield-accuracy/cmd/script/main.go index dd7381b6..c2ef8450 100644 --- a/harnesses/rwa-yield-accuracy/cmd/script/main.go +++ b/harnesses/rwa-yield-accuracy/cmd/script/main.go @@ -18,26 +18,19 @@ import ( // this bench is: (1) implement IssuerProbe, (2) add its slug to // promised-yields.yml, (3) append here. // -// Active cohort: USDY (rebase, totalSupply growth), USTB (NAV via -// Chainlink feed), OUSG (NAV via OndoOracle). All three are fully -// on-chain measurable — no off-chain HTTP dependency, no treasury -// wallet assumption. +// Active cohort: +// - USDY (Ondo, rebase) +// - USTB (Superstate, Chainlink NAV feed) +// - OUSG (Ondo, OndoOracle IPriceOracle) +// - SyrupUSDC (Maple, ERC-4626 convertToAssets) // -// BUIDL and BENJI stay dormant: -// - BUIDL: distributor 0x5072Ed40EBa6bE38C2370cAD1Cb1df0202924e53 -// was identified but calls bulkIssuance (mints more BUIDL, not -// USDC transfers). The current dividend.go model that scans USDC -// Transfer events doesn't fit; needs a rebase-style measurement -// PLUS a way to separate yield mints from new subscriptions. -// - BENJI: Ethereum wrapper (0x3DDc...50dc9) has 3 holders, ~$48M, -// no on-chain sharePrice function. NAV is $1.00 by design; yield -// is only knowable via Franklin's off-chain fund page. Reached -// out to digitalassets@franklintempleton.com; unblock once they -// confirm an API endpoint or Chainlink feed. +// BUIDL and BENJI dormant: distributor mints conflate yield with +// subscriptions (BUIDL), Ethereum wrapper lacks on-chain NAV (BENJI). var probes = []IssuerProbe{ NewUSDYProbe(), NewUSTBProbe(), NewOUSGProbe(), + NewSyrupUSDCProbe(), // NewBUIDLProbe(), // TODO: split bulkIssuance mints from subscriptions // NewBENJIProbe(), // TODO: waiting Franklin NAV endpoint confirmation } diff --git a/harnesses/rwa-yield-accuracy/cmd/script/syrupusdc.go b/harnesses/rwa-yield-accuracy/cmd/script/syrupusdc.go new file mode 100644 index 00000000..8d272316 --- /dev/null +++ b/harnesses/rwa-yield-accuracy/cmd/script/syrupusdc.go @@ -0,0 +1,138 @@ +package main + +import ( + "context" + "fmt" + "math/big" + "time" + + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/ethereum/go-ethereum/ethclient" +) + +// SyrupUSDC is Maple Finance's yield-bearing USDC token — an ERC-4626 +// vault whose underlying position is a book of over-collateralized +// (and, more recently, undercollateralized) institutional USDC loans. +// Share price grows daily as pool interest accrues; no rebase, no +// dividend transfers. +// +// Contract (Ethereum mainnet): +// 0x80ac24aA929eaF5013f6436cdA2a7ba190f5Cc0b (6 decimals, asset=USDC) +// +// Yield model: NAV appreciation via ERC-4626 convertToAssets(). +// Passing 1e6 (one share in 6 decimals) returns the current USDC value +// of one share directly. Called at a historical block, we get the NAV +// that was current at that block — same clean methodology as OUSG and +// USTB, no off-chain data needed. +// +// Scope note: SyrupUSDC is NOT a T-bill wrapper — it's collateralized +// pool lending, so deviation from advertised APY reflects both loan- +// book performance AND yield disclosure honesty. Included in bench 089 +// under the broader framing "on-chain yield-bearing stables promising +// an APY," not the narrow "tokenized T-bill" reading. +// +// Advertised APY tracked here is the pool's BASE APY (yield generated +// by the loan book), not the boosted APY including SYRUP token +// incentives. This matches what we can measure on-chain: convertToAssets +// only reflects underlying pool yield, not off-chain reward +// distributions. + +const ( + syrupUSDCContractEthereum = "0x80ac24aA929eaF5013f6436cdA2a7ba190f5Cc0b" + // One share in the token's 6-decimal base units. Passing this to + // convertToAssets returns USDC-per-share directly. + syrupUSDCOneShareRaw = uint64(1_000_000) +) + +var erc4626ConvertToAssetsSelector []byte + +func init() { + erc4626ConvertToAssetsSelector = crypto.Keccak256([]byte("convertToAssets(uint256)"))[:4] +} + +type syrupUSDCProbe struct { + contract common.Address +} + +func NewSyrupUSDCProbe() IssuerProbe { + return &syrupUSDCProbe{ + contract: common.HexToAddress(syrupUSDCContractEthereum), + } +} + +func (p *syrupUSDCProbe) Slug() string { return "syrup-usdc" } +func (p *syrupUSDCProbe) Issuer() string { return "maple" } +func (p *syrupUSDCProbe) Chain() string { return "ethereum" } + +func (p *syrupUSDCProbe) Measure(ctx context.Context, rpc *ethclient.Client) (*Measurement, error) { + now := time.Now().UTC() + + latest, err := rpc.BlockNumber(ctx) + if err != nil { + return nil, fmt.Errorf("latest block: %w", err) + } + block30dAgo := blockOffsetBySeconds(latest, int64(Window30d.Seconds())) + block7dAgo := blockOffsetBySeconds(latest, int64(Window7d.Seconds())) + + // USDC-per-share at each snapshot. 6-decimal in, 6-decimal out; the + // returned float64 is already scaled to USD. + navNow, err := p.readSharePriceUSD(ctx, rpc, nil) + if err != nil { + return nil, fmt.Errorf("nav now: %w", err) + } + nav30d, err := p.readSharePriceUSD(ctx, rpc, block30dAgo) + if err != nil { + return nil, fmt.Errorf("nav 30d: %w", err) + } + nav7d, err := p.readSharePriceUSD(ctx, rpc, block7dAgo) + if err != nil { + return nil, fmt.Errorf("nav 7d: %w", err) + } + + yield30dBps := annualizedYieldBpsFromNAV(navNow, nav30d, 30.0) + yield7dBps := annualizedYieldBpsFromNAV(navNow, nav7d, 7.0) + + supply, err := readERC20TotalSupply(ctx, rpc, p.contract, nil) + if err != nil { + supply = 0 + } + supplyUnits := supply / 1e6 + + return &Measurement{ + Token: p.Slug(), + Issuer: p.Issuer(), + Chain: p.Chain(), + DeliveredBps30d: yield30dBps, + DeliveredBps7d: yield7dBps, + DeliveredBpsLifetime: 0, + TotalSupplyUnits: supplyUnits, + AUMUSD: supplyUnits * navNow, + NewDistributionsUSD: 0, + MeasuredAt: now, + }, nil +} + +// readSharePriceUSD returns USDC-per-share as a float. Calls +// convertToAssets(1 share) which for a 6-decimal vault yields a +// 6-decimal USDC amount that we scale to USD (divide by 1e6). +func (p *syrupUSDCProbe) readSharePriceUSD(ctx context.Context, rpc *ethclient.Client, blockNumber *big.Int) (float64, error) { + // ABI-encoded call: selector + uint256(1_000_000) left-padded. + amount := new(big.Int).SetUint64(syrupUSDCOneShareRaw) + data := make([]byte, 0, 4+32) + data = append(data, erc4626ConvertToAssetsSelector...) + data = append(data, common.LeftPadBytes(amount.Bytes(), 32)...) + + msg := ethereum.CallMsg{To: &p.contract, Data: data} + result, err := rpc.CallContract(ctx, msg, blockNumber) + if err != nil { + return 0, err + } + if len(result) < 32 { + return 0, fmt.Errorf("convertToAssets: short response") + } + assets := new(big.Int).SetBytes(result[0:32]) + f, _ := new(big.Float).SetInt(assets).Float64() + return f / 1e6, nil +} diff --git a/harnesses/rwa-yield-accuracy/promised-yields.yml b/harnesses/rwa-yield-accuracy/promised-yields.yml index ed4267da..1502f927 100644 --- a/harnesses/rwa-yield-accuracy/promised-yields.yml +++ b/harnesses/rwa-yield-accuracy/promised-yields.yml @@ -54,3 +54,9 @@ issuers: source: "https://ondo.finance/ousg" source_date: 2026-07-22 notes: "Ondo OUSG NAV-appreciation yield estimate (probe inactive; NAV endpoint TBD)." + + - token: syrup-usdc + promised_apy_bps: 482 + source: "https://yields.llama.fi/pools" + source_date: 2026-07-24 + notes: "Maple SyrupUSDC base APY (loan-book yield only, excludes SYRUP token incentives). Sourced from DefiLlama filter {project:maple, symbol:USDC, chain:Ethereum}. Delivered yield measured via ERC-4626 convertToAssets rate delta - matches the base disclosure, not the boosted APY." diff --git a/public/logos/gelato.svg b/public/logos/gelato.svg new file mode 100644 index 00000000..b974ede8 --- /dev/null +++ b/public/logos/gelato.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/src/data/provider-registry.ts b/src/data/provider-registry.ts index 1c475fed..4d2b4231 100644 --- a/src/data/provider-registry.ts +++ b/src/data/provider-registry.ts @@ -711,6 +711,12 @@ export const PROVIDER_REGISTRY: Record = { "ThirdWeb operates a keyless public RPC gateway at `.rpc.thirdweb.com` covering 2000+ EVM chains via chain-id routing. Free tier is rate-limited per IP with no signup; paid tiers unlock higher throughput and dedicated endpoints. Used keyless across 13 chains on the RPC latency cluster (arbitrum, ethereum, polygon, optimism, base, bnb, avalanche, linea, worldchain, kaia, ink, opbnb + more).", twitter: "@thirdweb", }, + gelato: { + url: "https://gelato.network", + description: + "Gelato Network runs a rollup-as-a-service platform and operates RPC infrastructure for OP Stack + Arbitrum Orbit chains it hosts. On the RPC latency cluster it appears as the Kraken Ink chain's Gelato-backed official endpoint (`rpc-gel.inkonchain.com`), running active-active alongside the QuickNode-backed sibling.", + twitter: "@gelatonetwork", + }, infura: { url: "https://www.infura.io", description: diff --git a/src/lib/logo-manifest.ts b/src/lib/logo-manifest.ts index e7d3ac9f..6c87905f 100644 --- a/src/lib/logo-manifest.ts +++ b/src/lib/logo-manifest.ts @@ -107,6 +107,7 @@ const RAW: Record = { chainstack: "/logos/chainstack.svg", drpc: "/logos/drpc.webp", thirdweb: "/logos/thirdweb.png", + gelato: "/logos/gelato.svg", "1rpc": "/logos/1rpc.svg", cloudflare: "/logos/cloudflare.svg", parity: "/logos/parity.png", @@ -408,8 +409,8 @@ const ALIASES: Record = { "injective-official": "injective", "worldchain-official": "world-chain", "kaia-official": "kaia", - "ink-official": "ink", - "ink-quicknode": "ink", + "ink-official": "gelato", + "ink-quicknode": "quicknode", "opbnb-official": "opbnb", hood: "robinhood", coin: "coinbase",