import type { Hex } from 'ox' import * as core_Provider from '../Provider.js' import type * as Log from '../Log.js' /** Exchange quote provider called after the native DEX cannot route a swap. */ export type Provider = core_Provider.Provider & { /** Converts a signed continuation into final executable calls. */ finalizeQuote?( this: void, input: FinalizeQuoteInput, signal: AbortSignal, ): Promise /** Returns the approvals, signature, or executable calls needed to continue. */ quote(this: void, input: QuoteInput, signal: AbortSignal): Promise } /** Provider-neutral unsigned transaction call. */ export type Call = { /** ABI-encoded call data. */ data: Hex.Hex /** Contract receiving the call. */ to: `0x${string}` /** Native token value as a JSON-RPC quantity. */ value: Hex.Hex } /** Input used to finish a provider quote after signing. */ export type FinalizeQuoteInput = { /** Wallet signing and executing the swap. */ account: `0x${string}` /** Tempo chain id. */ chainId: number /** Opaque provider state returned by `quote`. */ continuation: string /** EIP-712 signature requested by the quote result. */ signature: Hex.Hex } /** Final executable calls returned after a signature. */ export type FinalizeQuoteResult = { /** Calls to execute in order. */ calls: readonly Call[] /** Quote constraints that the final calls must satisfy. */ execution: Execution } /** Constraints used to validate executable provider calls. */ export type Execution = QuoteAmounts & { /** Token received by the swap. */ destinationToken: `0x${string}` /** Side whose requested amount remains exact. */ mode: 'exactDestination' | 'exactSource' /** Token spent by the swap. */ sourceToken: `0x${string}` } /** Token movement observed while simulating provider calls. */ export type Transfer = { /** Amount moved in base units. */ amount: bigint /** Account sending the tokens. */ from: `0x${string}` /** Account receiving the tokens. */ to: `0x${string}` /** Token contract that emitted the transfer. */ token: `0x${string}` } /** Provider-neutral quote input. */ export type QuoteInput = { /** Wallet receiving and executing the swap. */ account: `0x${string}` /** Exact source or destination amount in base units. */ amount: string /** Tempo chain id. */ chainId: number /** Token received by the swap. */ destinationToken: `0x${string}` /** Side whose requested amount remains exact. */ mode: 'exactDestination' | 'exactSource' /** Maximum execution slippage in basis points. */ slippageBps: number /** Token spent by the swap. */ sourceToken: `0x${string}` } /** Quote with the approvals, signature, or transaction needed for its next step. */ export type QuoteResult = | (QuoteAmounts & { /** Calls that must confirm before requesting a fresh quote. */ approval: { calls: readonly Call[] } /** Onchain approval is required before this swap can be quoted for execution. */ status: 'approvalRequired' }) | (QuoteAmounts & { /** Quote is ready for execution. */ status: 'ready' /** Ordered calls required to execute the quote. */ transaction: { calls: readonly Call[] } }) | (QuoteAmounts & { /** Opaque provider state passed to `finalizeQuote`. */ continuation: string /** Quote needs a wallet signature before final calldata can be built. */ status: 'signatureRequired' /** EIP-712 payload the wallet must sign. */ typedData: TypedData }) /** Defines an exchange provider while preserving its concrete identity. */ export function from(options: from.Options): Provider { if (options.id !== options.id.trim().toLowerCase() || options.id.length === 0) throw new ProviderConfigurationError('Provider ids must be lowercase and trimmed.') return core_Provider.from({ ...options, type: 'exchange' }) } export declare namespace from { /** Exchange provider definition. */ type Options = Omit, 'type'> } /** Returns whether a registered provider implements exchange quotes. */ export function is(provider: core_Provider.Provider): provider is Provider { return provider.type === 'exchange' && 'quote' in provider && typeof provider.quote === 'function' } /** Returns bounded exchange-provider failure details for structured request logs. */ export function failure(cause: unknown, options: failure.Options): Log.ProviderFailure | undefined { const { chainId, operation, provider } = options if (cause instanceof ProviderResponseError) return { chainId, failure: cause.status === 429 ? 'rate_limit' : 'http', id: provider.id, operation: cause.operation, status: cause.status, } if (cause instanceof ProviderNetworkError) return { chainId, failure: 'network', id: provider.id, operation: cause.operation } if (cause instanceof ProviderPayloadError) return { chainId, ...(cause.code ? { code: cause.code } : {}), failure: 'payload', id: provider.id, operation, } return undefined } export declare namespace failure { /** Exchange-provider context retained when a quote operation fails. */ type Options = { /** Chain selected by the quote request. */ chainId: number /** Provider operation used when the error has no narrower operation. */ operation: string /** Exchange provider that handled the request. */ provider: Provider } } /** Validates simulated token movements against an executable quote. */ export function validateExecution(options: validateExecution.Options): void { const account = options.account.toLowerCase() const destinationToken = options.execution.destinationToken.toLowerCase() const sourceToken = options.execution.sourceToken.toLowerCase() let destinationReceived = 0n let sourceIncoming = 0n let sourceOutgoing = 0n for (const transfer of options.transfers) { const token = transfer.token.toLowerCase() if (transfer.from.toLowerCase() === account) { if (token !== sourceToken) throw new ProviderPayloadError() sourceOutgoing += transfer.amount } if (transfer.to.toLowerCase() !== account) continue if (token === sourceToken) sourceIncoming += transfer.amount if (token === destinationToken) destinationReceived += transfer.amount } const sourceSpent = sourceOutgoing - sourceIncoming if (sourceSpent <= 0n) throw new ProviderPayloadError() if (options.execution.mode === 'exactSource') { if ( sourceSpent !== BigInt(options.execution.sourceAmount) || options.execution.minimumDestinationAmount === undefined || destinationReceived < BigInt(options.execution.minimumDestinationAmount) ) throw new ProviderPayloadError() return } if ( options.execution.maximumSourceAmount === undefined || sourceSpent > BigInt(options.execution.maximumSourceAmount) || destinationReceived < BigInt(options.execution.destinationAmount) ) throw new ProviderPayloadError() } export declare namespace validateExecution { /** Simulated transfers and quote constraints to validate. */ type Options = { /** Wallet executing the provider calls. */ account: `0x${string}` /** Quote constraints bound to the executable calls. */ execution: Execution /** Token transfers emitted by the simulated calls. */ transfers: readonly Transfer[] } } /** Amount bounds returned by an exchange provider. */ export type QuoteAmounts = { /** Expected destination amount in base units. */ destinationAmount: string /** Estimated execution gas units. */ gasUnits: string /** Maximum source amount for an exact-destination quote. */ maximumSourceAmount?: string | undefined /** Minimum destination amount for an exact-source quote. */ minimumDestinationAmount?: string | undefined /** Expected source amount in base units. */ sourceAmount: string } /** Provider-neutral EIP-712 payload accepted by Privy and common wallet clients. */ export type TypedData = { /** EIP-712 signing domain. */ domain: Record /** EIP-712 message values. */ message: Record /** Root EIP-712 type. */ primaryType: string /** EIP-712 type definitions. */ types: Record } /** Raised when a provider has no executable route for the requested swap. */ export class RouteUnavailableError extends Error { override name = 'ExchangeProvider.RouteUnavailableError' } /** Raised when a continuation cannot be decoded or validated. */ export class ProviderContinuationError extends Error { override name = 'ExchangeProvider.ProviderContinuationError' } /** Raised when required provider configuration is invalid. */ export class ProviderConfigurationError extends Error { override name = 'ExchangeProvider.ProviderConfigurationError' } /** Raised when an upstream provider request fails before receiving a response. */ export class ProviderNetworkError extends Error { override name = 'ExchangeProvider.ProviderNetworkError' /** Upstream operation that failed. */ operation: string constructor(options: ProviderNetworkError.Options) { super(`Provider ${options.operation} request failed`, { cause: options.cause }) this.operation = options.operation } } export declare namespace ProviderNetworkError { /** Safe request details retained for operational reporting. */ type Options = { /** Original network error. */ cause: TypeError /** Stable upstream operation identifier. */ operation: string } } /** Raised when an upstream provider returns an invalid payload. */ export class ProviderPayloadError extends Error { override name = 'ExchangeProvider.ProviderPayloadError' /** Stable validation code safe for operational reporting. */ code?: string | undefined constructor(options: ProviderPayloadError.Options = {}) { super(options.code ? `Provider payload failed ${options.code}` : undefined) this.code = options.code } } export declare namespace ProviderPayloadError { /** Bounded invalid-payload details. */ type Options = { /** Stable validation code safe for operational reporting. */ code?: string | undefined } } /** Raised when an upstream provider request fails. */ export class ProviderResponseError extends Error { override name = 'ExchangeProvider.ProviderResponseError' /** Upstream operation that returned the response. */ operation: string /** Upstream HTTP status. */ status: number constructor(options: ProviderResponseError.Options) { super(`Provider ${options.operation} request failed with status ${options.status}`) this.operation = options.operation this.status = options.status } } export declare namespace ProviderResponseError { /** Safe upstream response details retained for operational reporting. */ type Options = { /** Stable upstream operation identifier. */ operation: string /** Upstream HTTP status. */ status: number } }