@1delta/aggregators
v0.1.8
Published
Aggregators for 1Delta
Downloads
76
Readme
@1delta/aggregators
Unified wrapper around 17+ DEX aggregator APIs. Provides a single entry point
(fetchAggregatorTrade) that accepts a common set of parameters and returns a
normalized GenericTrade object, regardless of which underlying aggregator is
used.
Quick Start
import { fetchAggregatorTrade, TradeAggregator } from '@1delta/aggregators'
import { TradeType } from '@1delta/lib-utils'
const trade = await fetchAggregatorTrade(TradeAggregator.Odos, {
chainId: '1',
fromCurrency: { address: '0xA0b8...', decimals: 18, symbol: 'WETH' },
toCurrency: { address: '0xA0b8...', decimals: 6, symbol: 'USDC' },
swapAmount: '1000000000000000000', // 1 WETH in wei
caller: '0xYourAddress',
receiver: '0xYourAddress',
slippage: 0.3, // 0.3%
tradeType: TradeType.EXACT_INPUT,
})
// Get calldata for on-chain execution
const callInfo = await trade?.assemble?.()
// Re-quote with updated slippage
const refreshed = await trade?.refresh?.({ slippage: 0.5 })How It Works
Architecture
fetchAggregatorTrade(aggregator, input)
|
v
Fetcher Registry <-- Map<TradeAggregator, FetcherFn>
|
+---------+---------+
| | |
OneDelta Wrapper External (Odos, 1inch, Paraswap, ...)
| | |
v v v
GenericTrade (unified output)- Caller picks an aggregator from the
TradeAggregatorenum and passes unifiedAggregatorApiInput. - Router (
aggregator.ts) looks up the registered fetcher and dispatches the call. - Fetcher normalizes the input (token addresses, slippage format), calls
the aggregator API, and returns a
GenericTrade.
Fetcher Categories
| Category | Aggregator | Behavior |
| ------------ | --------------- | ----------------------------------------------------------------------------------- |
| Internal | OneDelta | Uses 1delta routing API. Accepts extended OneDeltaInput. |
| Wrapper | NativeWrapper | Synchronous wrap/unwrap of native token (ETH/MATIC/etc). No external API call. |
| External | All others | Calls a third-party aggregator API, returns quote + lazy assemble() for calldata. |
Unified Input
All fetchers accept AggregatorApiInput (from @1delta/lib-utils):
| Field | Type | Description |
| ---------------------- | ------------------ | ------------------------------------------------- |
| chainId | string | Target chain ID |
| fromCurrency | RawCurrency | Source token (address, decimals, symbol) |
| toCurrency | RawCurrency | Destination token |
| swapAmount | string | Amount in smallest unit (wei) |
| caller | string | Transaction sender |
| receiver | string | Token recipient |
| slippage | number | Slippage tolerance as percent (e.g. 0.3 = 0.3%) |
| tradeType | TradeType | EXACT_INPUT or EXACT_OUTPUT |
| marginParams? | MarginParameters | Flash loan configuration for margin trades |
| usePendleMintRedeem? | boolean | Enable Pendle PT/YT mint/redeem routing |
| simpleRoute? | boolean | Request simpler routing (Odos) |
Slippage Normalization
Slippage is always provided as percent (e.g. 0.3 = 0.3%). Callers should
never convert slippage themselves -- pass the raw percent value and
convertSlippageForAggregator() handles the rest.
Each aggregator API expects slippage in a different format. The conversion is
handled automatically inside every fetcher via
convertSlippageForAggregator(slippage, aggregator) (defined in
src/utils/misc.ts):
| Target format | Example (0.3% in) | Example (out) | Aggregators |
| ------------------------- | ----------------- | ------------- | ------------------------------------------- |
| Decimal | 0.3 | 0.003 | SushiSwap, Fly, Pendle, OogaBooga, Eisen |
| Basis points | 0.3 | 30 | Kyberswap, Paraswap, 0x, Enso, Uniswap |
| Percent (passthrough) | 0.3 | 0.3 | OpenOcean, Odos, 1inch |
When adding a new aggregator, check the API docs for the expected slippage
format and add a case to the switch in convertSlippageForAggregator(). If
the aggregator uses percent (the input format), no case is needed -- the
default branch passes the value through.
Unified Output
Every fetcher returns GenericTrade:
| Field | Type | Description |
| ------------------ | --------------------------------------- | ------------------------------------ |
| tradeType | TradeType | EXACT_INPUT or EXACT_OUTPUT |
| inputAmount | CurrencyAmount | Parsed input amount |
| outputAmount | CurrencyAmount | Parsed output amount |
| aggregator | TradeAggregator | Which aggregator produced this quote |
| target | string | Contract to send the transaction to |
| approvalTarget | string | Contract to approve tokens against |
| slippage | number | Applied slippage (percent) |
| flashLoanSource? | FlashLoanProvider | Flash loan provider (margin trades) |
| assemble? | () => Promise<GenericCallInfo> | Lazily builds calldata for execution |
| refresh? | (overrides?) => Promise<GenericTrade> | Re-fetches the quote |
| order? | Order | Bebop-specific signing order |
Two-Step Quote Flow
Most aggregators use a quote-then-assemble pattern:
- Quote -- fetcher calls the aggregator API and returns a
GenericTradewith price/amount info. - Assemble -- calling
trade.assemble()makes a second API call to get the actual transaction calldata (to,calldata,value).
This avoids building calldata until the user confirms the trade.
Supported Aggregators
| Aggregator | Enum Value | Chains |
| ------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| NativeWrapper | Wrapper | All |
| 1delta | 1delta | Polygon, Base, Arbitrum, Optimism, Mantle, Taiko, Core, Hemi, Moonbeam, Metis, Fuel |
| 1inch | 1inch | Ethereum, Polygon, BNB, Arbitrum, Optimism, Avalanche, Linea, Fantom, Kaia, Unichain |
| Paraswap | Paraswap | Ethereum, Polygon, BNB, Arbitrum, Optimism, Scroll, Sonic, Fantom, Gnosis |
| Odos | Odos | Ethereum, Polygon, BNB, Arbitrum, Optimism, Base, Avalanche, Mantle, Linea, Scroll, Sonic, Fantom, Mode, Unichain |
| Kyberswap | Kyberswap | Ethereum, Polygon, BNB, Arbitrum, Optimism, Base, Mantle, Linea, Scroll, Sonic, Fantom, Blast, Gnosis, Berachain, Metis, HyperEVM, Unichain, Plasma, Monad |
| Bebop | Bebop | Ethereum, Polygon, BNB, Arbitrum, Optimism, Base, Blast, Taiko, Mode, Berachain, HyperEVM, Unichain |
| Nordstern | Nordstern | Most chains |
| Fly (fly.trade) | Fly | Ethereum, Polygon, BNB, Arbitrum, Optimism, Base, Avalanche, Blast, Scroll, Sonic, Fantom, Manta, Berachain, Metis |
| 0x | 0x | Ethereum, Polygon, BNB, Arbitrum, Base, Mantle, Linea, Blast, Mode, Scroll, Plasma, Monad |
| SushiSwap | Sushiswap | Most chains |
| OpenOcean | OpenOcean | Most chains |
| Pendle | Pendle | Ethereum, BNB, Arbitrum, Optimism, Base, Mantle, Sonic, Berachain, HyperEVM, Plasma |
| Wowmax | Wowmax | (currently disabled) |
| Eisen | Eisen | BNB, Arbitrum, Base, Mantle, Linea, Taiko, Blast, Scroll, Morph, Hemi, Mode, Berachain, HyperEVM, Soneium, Katana, Zircuit, Abstract, Plume, Flow, Rootstock, Monad, Core |
| Enso | Enso | Ethereum, Polygon, Arbitrum, Optimism, Base, Avalanche, Linea, Sonic, Gnosis, Berachain, Soneium, Katana, HyperEVM, Unichain, World Chain |
| OogaBooga | OogaBooga | Berachain, HyperEVM, Monad |
| Uniswap | Uniswap | Ethereum, Polygon, BNB, Arbitrum, Base, Optimism, Avalanche, Blast, Linea, Unichain, Soneium, Monad, World Chain, Celo |
Use getAvailableAggregators(chainId) to get the list for a specific chain at
runtime.
Margin / Flash Loan Integration
When marginParams is set on the input, the fetcher adjusts the swap amount to
account for flash loan fees before quoting. This enables margin trading where
the swap is funded by a flash loan.
const trade = await fetchAggregatorTrade(TradeAggregator.Odos, {
...baseInput,
marginParams: {
flashSources: {
canUseMorpho: true,
canUseBalancerV3: false,
balancersAvailable: [],
aaveFee: 5n, // 5 bps
aaveFork: '0x...',
},
adjustForFlashFee: true,
maxInput: false,
},
})Flash loan provider priority: Morpho Blue > Balancer V3 > Balancer V2 > Aave fork.
If margin params are provided but no flash sources are available, the fetcher
returns undefined (no quote).
Proxy Configuration
Some aggregator HTTP origins are blocked from certain runtimes; those use a VPS relay. Everything else uses direct HTTP.
Per-aggregator routing is fixed in code:
src/proxy/config.ts exports AGGREGATOR_PROXY_MODES
(Record<TradeAggregator, ProxyMode>). Only Kyberswap uses Relay; all
other aggregators use None.
| Mode | Description |
| ------- | ------------------------------------------------------ |
| None | Direct fetch — no proxy |
| Relay | Route through the VPS relay (?url= + X-Relay-Auth) |
Setup
Set relay credentials once per process (required before any Relay fetcher runs, e.g. Kyberswap):
import { setProxyConfig } from '@1delta/aggregators'
setProxyConfig({
relay: {
baseUrl: process.env.RELAY_PROXY_URL!,
secret: process.env.RELAY_PROXY_SECRET!,
},
})How It Works
When a fetcher makes an HTTP call, it resolves its mode via
getAggregatorProxyMode() and passes it to proxyGet() / proxyPost() /
proxyNativeFetch():
- None — direct
axios/fetchto the original URL (passthrough) - Relay —
GET/POSTto{relay.baseUrl}?url={encodeURIComponent(originalUrl)}with anX-Relay-Authheader
Both GET and POST are supported by the relay. The request body (for POST) is forwarded as-is.
Environment Variables
For worker deployments, set these in wrangler.toml or your env:
| Variable | Description |
| -------------------- | ----------------------------------------- |
| RELAY_PROXY_URL | Base URL of the VPS relay proxy worker |
| RELAY_PROXY_SECRET | Auth secret for the X-Relay-Auth header |
Fetcher Registry
Fetchers are registered in a Map and can be replaced or extended at runtime:
import {
registerFetcher,
getFetcher,
getAllFetchers,
} from '@1delta/aggregators'
// Register a custom fetcher
registerFetcher('MyAggregator', myFetcherFn)
// Retrieve a fetcher
const fetcher = getFetcher('Odos')The default fetchers are registered via getAllAggregatorFetchers() in
fetcher/index.ts.
Adding a New Aggregator
- Create
src/fetcher/myAggregator.tsimplementingExternalAggregatorFetcher. - Add an entry to the
TradeAggregatorenum insrc/types/tradeAggregator.ts. - Add the new enum member to
AGGREGATOR_PROXY_MODESinsrc/proxy/config.ts(almost alwaysProxyMode.None; useRelayonly if the upstream blocks direct calls from your runtime). - Register it in the
fetchersmap insrc/fetcher/index.ts. - Add chain availability in
src/types/basics.ts. - Add the aggregator to
PENDLE_AGGREGATOR_CAPABILITIESinsrc/utils/pendle/index.ts(set toundefinedunless it supports Pendle tokens). - If the aggregator expects slippage in decimal or basis points (not
percent), add a
casetoconvertSlippageForAggregator()insrc/utils/misc.ts. See Slippage Normalization for the formats.
A typical fetcher follows this pattern:
export const fetchMyTrade = async (
input: AggregatorApiInput,
controller?: AbortController,
): Promise<GenericTrade | undefined> => {
// 1. Normalize token addresses (native → zero/wrapped)
// 2. Convert slippage via convertSlippageForAggregator()
// 3. Adjust amount for margin if marginParams present
// 4. Call aggregator quote API
// 5. Build GenericTrade with assemble() and refresh()
// 6. Return trade
}