@velora-dex/sdk
Version:
622 lines (474 loc) • 21.3 kB
Markdown
<p align="center">
<a href="https://www.velora.xyz/">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://cdn.paraswap.io/brand/velora_banner_dark.svg">
<img width=350 src="https://cdn.paraswap.io/brand/velora_banner_light.svg">
</picture>
</a>
</p>
# SDK for the Velora API
Refer to the documentation of the Velora API: https://developers.velora.xyz/
## Features
**Versatility**: works with [web3](https://www.npmjs.com/package/web3) or [ethers](https://www.npmjs.com/package/ethers) without a direct dependency — or with [viem](https://viem.sh/) (also used internally for its utils)
**Canonical**: bring only the functions you actually need
**Lightweight**: 10KB Gzipped for the minimal variant
## What's changed in v10
v10 is a breaking update over the v9.x line. The key consumer-facing changes:
### Delta is now v2 — orders are built server-side
- **You no longer build the order locally.** Pass the quoted `route` + `side` (from `getDeltaPrice` / `quote.delta`) to `buildDeltaOrder` / `submitDeltaOrder`; the server returns the typed data to sign. You no longer pass local amounts.
- **One signer for every family.** `signDeltaOrder(builtOrder)` signs Standard, External, TWAP Sell and TWAP Buy orders. The old per-family signers **`signExternalDeltaOrder`** and **`signTWAPDeltaOrder`** are removed.
- **`getDeltaPrice` returns a route-based `DeltaPrice`** (`route` + `alternatives`). The old `BridgePrice` type is gone — cross-chain is handled in-route via `destChainId`.
- **`getBridgeInfo` → `getBridgeRoutes`** (now returns a flat `BridgeRoute[]`), plus a new `getBridgeProtocols`.
- **New `getAgentsList`** (`sdk.delta.getAgentsList`).
- **New read-only order types** surface through the read paths: `ProductiveOrder` (server-managed, no builder) and `FillableOrder` (a `partiallyFillable` Standard order — treat the same as `Order`).
- All Delta endpoints now use the `/v2/delta/...` prefix.
### Legacy limit orders removed — OTC orders remain
The deprecated legacy (non-Delta) limit-order methods are gone. The OTC order functionality that lived alongside them is kept, now exposed under its own `otcOrders` name:
- `sdk.limitOrders.*` → `sdk.otcOrders.*`
- the constructors are OTC-named: `constructBuildOTCOrder`, `constructSignOTCOrder`, `constructPostOTCOrder`, `constructCancelOTCOrder`, `constructGetOTCOrders`, `constructApproveTokenForOTCOrder`, `constructFillOTCOrder`, `constructSubmitOTCOrder`, etc.
### NFT orders removed
The deprecated NFT-orders feature is gone: `sdk.nftOrders.*` and all `construct*NFTOrder*` exports no longer exist.
## Installing Velora SDK
```bash
pnpm add @velora-dex/sdk
```
## Using Velora SDK
There are multiple ways to use Velora SDK, ranging from a simple construct-and-use approach to a fully composable _bring what you need_ approach which allows for advanced tree-shaking and minimizes bundle size.
You can see some examples in [/src/examples](src/examples) directory.
### Simple SDK
Can be created by providing `chainId` and either `axios` or `window.fetch` (or alternative `fetch` implementation), and an optional `version` (`'5'` or `'6.2'`) parameter that corresponds to the API version SDK will be making requests to. The resulting SDK will be able to use all methods that query the API.
```ts
import { constructSimpleSDK } from '@velora-dex/sdk';
import axios from 'axios';
// construct minimal SDK with fetcher only
const minSDK = constructSimpleSDK({ chainId: 1, axios });
// or
const minSDK = constructSimpleSDK({
chainId: 1,
fetch: window.fetch,
version: '5',
});
const ETH = '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee';
const DAI = '0x6B175474E89094C44Da98b954EedeAC495271d0F';
async function swapExample() {
// or any other signer/provider
const signer: JsonRpcSigner = ethers.Wallet.fromMnemonic('__your_mnemonic__');
const senderAddress = signer.address;
const priceRoute = await minSDK.swap.getRate({
srcToken: ETH,
destToken: DAI,
amount: srcAmount,
userAddress: senderAddress,
side: SwapSide.SELL,
});
const txParams = await minSDK.swap.buildTx({
srcToken,
destToken,
srcAmount,
destAmount,
priceRoute,
userAddress: senderAddress,
partner: referrer,
});
const transaction = {
...txParams,
gasPrice: '0x' + new BigNumber(txParams.gasPrice).toString(16),
gasLimit: '0x' + new BigNumber(5000000).toString(16),
value: '0x' + new BigNumber(txParams.value).toString(16),
};
const txr = await signer.sendTransaction(transaction);
}
```
If optional `providerOptions` is provided as the second parameter, then the resulting SDK will also be able to approve Tokens for swap, sign Orders, etc.
```ts
// with ethers@5
const providerOptionsEtherV5 = {
ethersProviderOrSigner: provider, // JsonRpcProvider
EthersContract: ethers.Contract,
account: senderAddress,
};
// with ethers@6
const providerOptionsEtherV6 = {
ethersV6ProviderOrSigner: provider, // JsonRpcProvider
EthersV6Contract: ethers.Contract,
account: senderAddress,
};
// or with viem (from wagmi or standalone)
const providerOptionsViem = {
viemClient, // made with createWalletClient()
account: senderAddress,
};
// or with web3.js
const providerOptionsWeb3 = {
web3, // new Web3(...) instance
account: senderAddress,
};
const sdk = constructSimpleSDK({ chainId: 1, axios }, providerOptionsEtherV5);
// approve token through sdk
const txHash = await sdk.approveToken(amountInWei, DAI);
// await tx somehow
await provider.waitForTransaction(txHash);
```
### Full SDK
```typescript
import {
constructFullSDK,
constructAxiosFetcher,
constructEthersContractCaller,
} from '@velora-dex/sdk';
const signer = ethers.Wallet.fromMnemonic('__your_mnemonic__'); // or any other signer/provider
const account = '__signer_address__';
const contractCaller = constructEthersContractCaller(
{
ethersProviderOrSigner: signer,
EthersContract: ethers.Contract,
},
account
); // alternatively constructViemContractCaller or constructWeb3ContractCaller
const fetcher = constructAxiosFetcher(axios); // alternatively constructFetchFetcher
const sdk = constructFullSDK({
chainId: 1,
fetcher,
contractCaller,
});
```
### Partial SDK
For bundle-size savvy developers, you can construct a lightweight version of the SDK and bring only the functions you need.
e.g. for only getting rates and allowances:
```typescript
import {
constructPartialSDK,
constructFetchFetcher,
constructGetRate,
constructGetBalances,
} from '@velora-dex/sdk';
const fetcher = constructFetchFetcher(window.fetch);
const sdk = constructPartialSDK(
{
chainId: 1,
fetcher,
},
constructGetRate,
constructGetBalances
);
const priceRoute = await sdk.getRate(params);
const allowance = await sdk.getAllowance(userAddress, tokenAddress);
```
---
### Basic usage
The easiest way to make a trade is to rely on the Quote method that communicates with the `/v2/quote` API endpoint. With `mode: 'all'` it returns Delta v2 pricing when possible, falling back to the current market price (use `mode: 'delta'` or `mode: 'market'` to request a single source).
```typescript
import axios from 'axios';
import { ethers } from 'ethersV5';
import { constructSimpleSDK } from '@velora-dex/sdk';
const ethersProvider = new ethers.providers.Web3Provider(window.ethereum);
const accounts = await ethersProvider.listAccounts();
const account = accounts[0]!;
const signer = ethersProvider.getSigner(account);
const simpleSDK = constructSimpleSDK(
{ chainId: 1, axios },
{
ethersProviderOrSigner: signer,
EthersContract: ethers.Contract,
account,
}
);
const amount = '1000000000000'; // wei
const Token1 = '0x1234...';
const Token2 = '0xabcde...';
const quote = await simpleSDK.quote.getQuote({
srcToken: Token1, // Native token (ETH) is only supported in mode: 'market'
destToken: Token2,
amount,
userAddress: account,
srcDecimals: 18,
destDecimals: 18,
mode: 'all', // Delta quote if possible, with fallback to Market price
side: 'SELL', // Delta mode only supports side: SELL currently
// partner: "..." // if available
});
if ('delta' in quote) {
// quote.delta is the v2 DeltaPrice (route-based)
const deltaPrice = quote.delta;
const DeltaContract = await simpleSDK.delta.getDeltaContract();
// or sign a Permit1 or Permit2 TransferFrom for DeltaContract
await simpleSDK.delta.approveTokenForDelta(amount, Token1);
// order building is server-side: pass the quoted route + side, no local amounts
const deltaAuction = await simpleSDK.delta.submitDeltaOrder({
route: deltaPrice.route, // or any deltaPrice.alternatives[i]
side: deltaPrice.side,
owner: account,
// beneficiary: anotherAccount, // if need to send the output destToken to another account
// permit: "0x1234...", // if signed a Permit1 or Permit2 TransferFrom for DeltaContract
slippage: 50, // 50 bps = 0.5%
});
// stop polling on any terminal status — COMPLETED already accounts for any dest-chain bridge
const SETTLED = ['COMPLETED', 'FAILED', 'CANCELLED', 'EXPIRED', 'REFUNDED'];
function startStatusCheck(auctionId: string) {
const intervalId = setInterval(async () => {
const auction = await simpleSDK.delta.getDeltaOrderById(auctionId);
if (SETTLED.includes(auction.status)) {
clearInterval(intervalId);
console.log(`Order settled: ${auction.status}`);
}
}, 3000);
setTimeout(() => clearInterval(intervalId), 60000 * 5); // stop after 5 minutes
}
startStatusCheck(deltaAuction.id);
} else {
console.log(
`Delta Quote failed: ${quote.fallbackReason.errorType} - ${quote.fallbackReason.details}`
);
const priceRoute = quote.market;
const TokenTransferProxy = await simpleSDK.swap.getSpender();
// or sign a Permit1 or Permit2 TransferFrom for TokenTransferProxy
const approveTxHash = simpleSDK.swap.approveToken(amount, Token1);
const txParams = await simpleSDK.swap.buildTx({
srcToken: Token1,
destToken: Token2,
srcAmount: amount,
slippage: 250, // 2.5%
priceRoute,
userAddress: account,
// partner: '...' // if available
});
const swapTx = await signer.sendTransaction(txParams);
}
```
### Delta Order handling
#### A more detailed overview of the Trade Flow, Delta Order variant.
**Velora Delta** is an intent-based protocol that enables a Velora user to make gasless swaps where multiple agents compete to execute the trade at the best price possible.
This way the user doesn't need to make a transaction themselve but only to sign a Delta Order.
(For **Crosschain Delta Orders** refer to a separate documentation page [DELTA.md](./docs/DELTA.md#crosschain-delta-orders) )
In v2 the Order is **built by the server** from the quoted route — you sign the returned typed data and post it. After getting **deltaPrice**, there are a few steps to sign the Order and wait for its execution.
### 1. Get deltaPrice
```ts
const amount = '1000000000000'; // wei
const Token1 = '0x1234...';
const Token2 = '0xabcde...';
// ... from the quote endpoint (mode: 'delta' or 'all')
const quote = await simpleSDK.quote.getQuote({
srcToken: Token1,
destToken: Token2,
amount,
userAddress: account,
srcDecimals: 18,
destDecimals: 18,
mode: 'delta',
side: 'SELL',
// partner: "..." // if available
});
const deltaPrice = quote.delta;
// ... or straight from the Delta price endpoint
const deltaPriceDirect = await simpleSDK.delta.getDeltaPrice({
srcToken: Token1,
destToken: Token2,
amount,
srcDecimals: 18,
destDecimals: 18,
userAddress: account,
// destChainId: 42161, // for cross-chain — bridge details land in deltaPrice.route.bridge
});
```
### 2. Approve srcToken for DeltaContract
```ts
const approveTxHash = await simpleSDK.delta.approveTokenForDelta(
amount,
Token1
);
```
Alternatively sign Permit (DAI or Permit1) or Permit2 TransferFrom with DeltaContract as the verifyingContract
```ts
const DeltaContract = await simpleSDK.delta.getDeltaContract();
// values depend on the Permit type and the srcToken
const signature = await signer._signTypedData(domain, types, message);
```
See more on accepted Permit variants in [Velora documentation](https://developers.velora.xyz/api/velora-api/velora-delta-api/build-a-delta-order-to-sign#supported-permits)
### 3. Build, sign and post a Delta Order
```ts
// build is server-side — pass the quoted route + side and your slippage
const built = await simpleSDK.delta.buildDeltaOrder({
route: deltaPrice.route, // or any deltaPrice.alternatives[i]
side: deltaPrice.side,
owner: account,
// beneficiary: anotherAccount, // if need to send the output destToken to another account
// permit: "0x1234...", // if signed a Permit1 or Permit2 TransferFrom for DeltaContract
slippage: 50, // 50 bps = 0.5%
// partner, partnerFeeBps — forwarded to the server
});
// one signer for every order family (Order / ExternalOrder / TWAPOrder / TWAPBuyOrder)
const signature = await simpleSDK.delta.signDeltaOrder(built);
const deltaAuction = await simpleSDK.delta.postDeltaOrder({
order: built.toSign.value,
signature,
// partner: "...",
// partiallyFillable: true, // allow the Order to be partially filled as opposed to fill-or-kill
});
```
#### 3.a.
As an option the `buildDeltaOrder + signDeltaOrder + postDeltaOrder` can be combined into one SDK call with the following code
```ts
const deltaAuction = await simpleSDK.delta.submitDeltaOrder({
route: deltaPrice.route,
side: deltaPrice.side,
owner: account,
// beneficiary: anotherAccount, // if need to send output destToken to another account
// permit: "0x1234...", // if signed a Permit1 or Permit2 TransferFrom for DeltaContract
// partiallyFillable: true, // allow the Order to be partially filled as opposed to fill-or-kill
slippage: 50,
});
```
This allows to simplify the flow at the expense of control over the Order signing.
#### 3.b adding partner fee
A portion of destToken is collected as a partner fee when `partner` (and optionally `partnerFeeBps`) is provided to `buildDeltaOrder` / `submitDeltaOrder`. In v2 these are forwarded to the server, which encodes them into `Order.partnerAndFee`. You can inspect the default partner-fee parameters with `getPartnerFee`:
```ts
const partnerFeeResponse = await simpleSDK.delta.getPartnerFee({ partner });
```
### 4. Wait for Delta Order execution
```ts
// stop polling on any terminal status — COMPLETED already accounts for any dest-chain bridge
const SETTLED = ['COMPLETED', 'FAILED', 'CANCELLED', 'EXPIRED', 'REFUNDED'];
function startStatusCheck(auctionId: string) {
const intervalId = setInterval(async () => {
const auction = await simpleSDK.delta.getDeltaOrderById(auctionId);
if (SETTLED.includes(auction.status)) {
clearInterval(intervalId); // stop interval once the order is settled
console.log(`Order settled: ${auction.status}`);
}
}, 3000);
// stop polling after 5 minutes
setTimeout(() => clearInterval(intervalId), 60000 * 5);
}
startStatusCheck(deltaAuction.id);
```
#### A more detailed example of Delta Order usage can be found in [examples/delta](./src/examples/delta.ts)
For more Delta protocol usage, and **Crosschain Delta Orders**, refer to [DELTA.md](./docs/DELTA.md)
For **External Delta Orders** (orders that delegate token handling to an external handler contract, enabling complex DeFi strategies like Aave collateral/debt swaps), refer to [EXTERNAL_ORDERS.md](./docs/EXTERNAL_ORDERS.md)
---
### Advanced Delta usage
#### TWAP order
A TWAP sell splits `totalSrcAmount` into `numSlices` equal slices executed `interval` seconds apart. Price is quoted for a single slice — the server multiplies amounts internally.
```ts
const perSliceAmount = (BigInt(totalSrcAmount) / BigInt(numSlices)).toString();
const slicePrice = await simpleSDK.delta.getDeltaPrice({
srcToken: Token1,
destToken: Token2,
amount: perSliceAmount,
srcDecimals: 18,
destDecimals: 18,
userAddress: account,
});
await simpleSDK.delta.approveTokenForDelta(totalSrcAmount, Token1);
const twapAuction = await simpleSDK.delta.submitTWAPDeltaOrder({
onChainOrderType: 'TWAPOrder', // or 'TWAPBuyOrder'
route: slicePrice.route,
owner: account,
totalSrcAmount,
numSlices,
interval: 300, // seconds (min 60)
slippage: 50,
});
```
#### Partial SDK
For bundle-savvy code, pull only the Delta constructors you need — they tree-shake cleanly.
```ts
import {
constructPartialSDK,
constructFetchFetcher,
constructGetDeltaPrice,
constructGetDeltaOrders,
constructGetBridgeRoutes,
} from '@velora-dex/sdk';
const sdk = constructPartialSDK(
{ chainId: 1, fetcher: constructFetchFetcher(fetch) },
constructGetDeltaPrice,
constructGetDeltaOrders,
constructGetBridgeRoutes
);
const price = await sdk.getDeltaPrice({
/* ... */
});
const orders = await sdk.getDeltaOrders({
userAddress: account,
page: 1,
limit: 50,
});
```
A complete example (standard, external handler, TWAP, and order listing) is in [examples/delta](./src/examples/delta.ts).
#### Productive Orders (read-only)
Alongside the four buildable families (`Order`, `ExternalOrder`, `TWAPOrder`, `TWAPBuyOrder`), the SDK surfaces a fifth `onChainOrderType` — **`ProductiveOrder`** — through the read endpoints (`sdk.delta.getDeltaOrderById`, `sdk.delta.getDeltaOrders`, etc.). Productive orders are produced and managed entirely by the server: there are deliberately **no `buildProductiveOrder`, `signProductiveOrder`, or `submitProductiveOrder` helpers**. The shape is wired through `OnChainOrderType` and `OnChainOrderMap` so that consumers iterating over an order list can type-narrow on `onChainOrderType === 'ProductiveOrder'` and read the order safely — productive orders carry no `OrderKind`, so the side is always `SELL`.
`OnChainOrderType` additionally includes **`'FillableOrder'`**, which maps to the same shape as a Standard `'Order'` (`DeltaAuctionOrder`). It is not a separate buildable family — it's the `onChainOrderType` the server reports for a `partiallyFillable` Standard order, so treat it the same as `'Order'` when narrowing.
#### Order helpers
`OrderHelpers` exposes `{ checks, getters }` for auctions returned by `sdk.delta.*` read/post methods (`status: DeltaOrderStatus`, `input`/`output`, flat `transactions`, explicit `side`):
```ts
import { OrderHelpers } from '@velora-dex/sdk';
const { checks, getters } = OrderHelpers;
if (checks.isProductiveAuction(auction)) {
// read-only, no SDK builder
} else if (
checks.isFillableAuction(auction) ||
checks.isDeltaAuction(auction)
) {
// treat FillableOrder the same as a standard Order
}
if (checks.isCompletedAuction(auction)) {
const { expected, executed } = getters.getAuctionAmounts(auction);
// `executed` — matches the API convention
}
const unified = getters.getUnifiedDeltaOrderData(auction);
// { srcChainId, destChainId, srcToken, destToken, swapSide, filledPercent, amounts, ... }
```
---
### Market Swap handling
#### A more detailed overview of the Trade Flow, Market variant.
Unlike the Delta Order, a Market swap requires the user themselves to submit a Swap transaction
### 1. Get Market priceRoute from /v2/quote
```ts
const amount = '1000000000000'; // wei
const Token1 = '0x1234...';
const Token2 = '0xabcde...';
const quote = await simpleSDK.quote.getQuote({
srcToken: Token1,
destToken: Token2,
amount,
userAddress: account,
srcDecimals: 18,
destDecimals: 18,
mode: 'market',
side: 'SELL', // or 'BUY'
// partner: "..." // if available
});
const priceRoute = quote.market;
```
### 2. Approve srcToken for TokenTransferProxy
```ts
const approveTxHash = simpleSDK.swap.approveToken(amount, DAI_TOKEN);
```
Alternatively sign Permit (DAI or Permit1) or Permit2 TransferFrom with TokenTransferProxy as the verifyingContract
```ts
const TokenTransferProxy = await simpleSDK.swap.getSpender();
// values depend on the Permit type and the srcToken
const signature = await signer._signTypedData(domain, types, message);
```
See more on accepted Permit variants in [Velora documentation](https://developers.velora.xyz/api/velora-api/velora-market-api/build-parameters-for-transaction)
### 3. Send Swap transaction
```ts
const txParams = await simpleSDK.swap.buildTx({
srcToken: Token1,
destToken: Token2,
srcAmount: amount,
slippage: 250, // 2.5%
// can pass `destAmount` (adjusted for slippage) instead of `slippage`
priceRoute,
userAddress: account,
// partner: '...' // if available
// receiver: '0x123ae...' // if need to send the output destToken to another account
});
const swapTxHash = await signer.sendTransaction(txParams);
```
#### See more details on `buildTx` parameters in [Velora documentation](https://developers.velora.xyz/api/velora-api/velora-market-api/build-parameters-for-transaction)
---
Refer to [SDK API documentation](docs/md/modules.md) for detailed documentation on the methods provided in this SDK.
## Tests
To run `pnpm test` it is necessary to provide `PROVIDER_URL=<mainnet_rpc_url>` environment variable.
If it is necessary to run tests against a different API endpoint, provide `API_URL=url_to_API` environment variable.