Integrate estimate

SkillCommerce & finance

Implement a ramp provider's estimateOnramp / estimateOfframp → PaymentRampEstimate in @sdp/payments. The cheapest live provider call, with no DB, counterparty, or KYC.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the Integrate estimate skill

What this skill tells your AI

The instructions your AI receives, as published by solana-foundation/solana-developer-platform in .agents/skills/integrate-estimate/SKILL.md and read by ahel’s review.

An estimate is a rate preview: "how much USDC for 100 EUR?" It hits the provider's live rate API and nothing else — no counterparty, no wallet, no DB. That makes it the first capability to build: if estimateOnramp works, your register-provider config reader and credentials are correct.

Choose the closest implementation in packages/sdp-payments/src/ramps/providers/ — GET- and POST-based estimate APIs, hosted-provider quote APIs, minor-unit conversion, and single-direction support are all represented.

Contract

Both methods are required on RampProvider (packages/sdp-payments/src/ramps/types.ts), even when one direction is unsupported:

estimateOnramp(ctx: RampRuntimeContext, input: RampEstimateOnrampInput): Promise<PaymentRampEstimate>
estimateOfframp(ctx: RampRuntimeContext, input: RampEstimateOfframpInput): Promise<PaymentRampEstimate>

Inputs (packages/sdp-payments/src/ramps/types.ts):

  • onramp: { assetRail: CryptoRailId, fiatCurrency: RampFiatCurrency, fiatAmount: string }
  • offramp: { assetRail: CryptoRailId, fiatCurrency: RampFiatCurrency, cryptoAmount: string }

Output PaymentRampEstimate (@sdp/types, packages/sdp-types/src/payments.ts):

{
  provider; direction: "onramp" | "offramp";
  fiatCurrency; assetRail; fiatAmount; cryptoAmount; exchangeRate;  // all strings
  fees: { currency; total; network?; provider? };
  minFiatAmount?; maxFiatAmount?; expiresAt?;
}

How to build it

ctx is { env, mode } — read your config with the mode-keyed reader from register-provider, then HTTP only. Convert the asset rail with getCryptoRailAssetLabel from @sdp/types/payment-rails; convert minor units with parseDecimalAmount / formatDecimalAmount from @sdp/solana/amount.

A common shape: GET the corridor's exchange rate once to learn decimals, again with the amount to get the quote, then map into PaymentRampEstimate.

Fail loud

A non-positive receiving amount is not a 0 estimate — it's a broken corridor. Throw, don't return zero:

if (rate.receivingAmount <= 0) {
  throw providerUnavailable("<Provider> returned a non-positive on-ramp receiving amount");
}

For an unsupported pair/direction, or a provider whose price exists only at hosted-quote time, throw estimateNotAvailable(...) from @sdp/payments/errors. The API fan-out maps that code to { status: "unsupported" }; it maps every other provider exception to { status: "error", error } for that provider, so one failed provider does not fail the whole fan-out.

Dispatch + route

The dashboard runtime routes are POST /v1/payments/ramps/{onramp|offramp}/estimate (apps/sdp-api/src/routes/payments/handlers/ramps.tsestimateAcrossProviders). They are availability-gated and metered. They are not currently part of the public OpenAPI surface, so do not advertise them as public endpoints unless the OpenAPI policy changes.

Variety

Estimate sourcing differs per upstream: corridor exchange-rate GETs (once for decimals, again with the amount), per-currency buy/sell quote GETs, or a POST quote flagged as estimate-only. Map whichever the upstream offers into PaymentRampEstimate.

Rules + verify

Shared rules live in integrate-ramp-provider. Hot here:

  • No fallbacks — non-positive/empty rate throws; never substitute a default amount or rate.
  • HTTP only; no DB, no counterparty lookups in estimate.
  • Strong typing — status/type maps are as const satisfies Record<…>; no any.
  • Verify with pnpm --filter @sdp/payments typecheck, lint, and test, plus focused API fan-out tests when orchestration changes. Unit-test provider mapping with mocked fetch and cover unsupported directions and missing credentials.

Signals

GitHub stars
53
Forks
23
Last commit
Sep 2026
Advanced
Catalog kind
skill
Gateway key
integrate-estimate
Source
github.com/solana-foundation/solana-developer-platform