Build a Delta Order to Sign
Build a Delta Order to Sign
POST
https://api.paraswap.io/delta/orders/build
This section explains how to construct a Delta Order, which is essential for executing trades using the Velora Delta API. The /orders/build endpoint retrieves a Delta price and allows fallback to market prices.
A successfully built order includes a structured object ready for signing.
Common errors include validation failures, unsupported assets, and signature mismatches.
Use this guide to generate a Delta Order and prepare it for signing before submission.
Request Body Parameters
chainId*
number
Chain ID. (Mainnet - 1, Optimism - 10, BSC - 56, Polygon - 137, Fantom - 250, zkEVM - 1101, Base - 8453, Arbitrum - 42161, Avalanche - 43114, Gnosis - 100).
owner*
string
Order owner address.
bridge
DeltaBridge
Required only for cross-chain orders. See below for details
beneficiary
string
Order beneficiary. Default: zero address
, meaning owner address.
slippage
number
Slippage in base points (100
is 1%
). Default: 100
.
deadline
number
Order expiry time as UNIX timestamp. Default: 1 hour from order creation.
nonce
string
Arbitrary uint256
value to be used as order nonce
. Default: value generated with internal algos
. It is recommended to omit this param.
permit
string
Encoded permit to be used in order settlement. Supported permit types are described here. Default: 0x
.
partiallyFillable
boolean
If true
, order will be generated as partially fillable. Default: false.
partnerAddress
string
Address of the partner. Used to collect fees from the order. Default: zero address
.
partnerFeeBps
number
Partner fee percent in base points (100
is 1%
). Max value is 200
. Default: 0
.
partnerTakesSurplus
boolean
If true
, partner will collect 50%
of the order surplus instead of flat percent fee. Default: false
.
DeltaBridge
structure:
maxRelayerFee
string
Maximum allowed fee by bridge relayer. The amount is speicifed in srcToken
destinationChainId
number
Destination chain ID. (Mainnet - 1, Optimism - 10, BSC - 56, Polygon - 137, Fantom - 250, zkEVM - 1101, Base - 8453, Arbitrum - 42161, Avalanche - 43114, Gnosis - 100).
outputToken
string
Output token on the destination chain. List of supported output tokens can be obtained from /bridge-info
endpoint
multiCallHandler
string
The address of the Across MultiCallHandler
on the destination chain. Should be used when outputToken
is ETH
and beneficiary
is a smart contract, or when ouputToken
is WETH
and benfeficiary
is an EOA (see below for an example implementation). Otherwise should be set to zero address. The address can be obtained from /multicall-handlers
endpoint
{
"toSign": {
"domain": {
"name": "Portikus",
"version": "2.0.0",
"chainId": 8453,
"verifyingContract": "0x1b6c933c4a855c9f4ad1afbd05eb3f51dbb83cf8"
},
"types": {
"Order": [
{
"name": "owner",
"type": "address"
},
{
"name": "beneficiary",
"type": "address"
},
{
"name": "srcToken",
"type": "address"
},
{
"name": "destToken",
"type": "address"
},
{
"name": "srcAmount",
"type": "uint256"
},
{
"name": "destAmount",
"type": "uint256"
},
{
"name": "expectedDestAmount",
"type": "uint256"
},
{
"name": "deadline",
"type": "uint256"
},
{
"name": "nonce",
"type": "uint256"
},
{
"name": "partnerAndFee",
"type": "uint256"
},
{
"name": "permit",
"type": "bytes"
},
{
"name": "bridge",
"type": "Bridge"
}
],
"Bridge": [
{
"name": "maxRelayerFee",
"type": "uint256"
},
{
"name": "destinationChainId",
"type": "uint256"
},
{
"name": "outputToken",
"type": "address"
},
{
"name": "multiCallHandler",
"type": "address"
}
]
},
"value": {
"owner": "0x75c94990d2ad92d8da4e0a238d872d09ec16706e",
"beneficiary": "0xcef715485e33d25efd027e1281e945bef292844b",
"srcToken": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48",
"destToken": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
"srcAmount": "110000000",
"destAmount": "60182718830547894",
"expectedDestAmount": "60790625081361510",
"deadline": 1744733790,
"nonce": "1744730205674",
"permit": "0x",
"partnerAndFee": "0",
"bridge": {
"maxRelayerFee": "644565026335358",
"destinationChainId": 1,
"outputToken": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2",
"multiCallHandler": "0x924a9f036260ddd5808007e1aa95f08ed08aa569"
}
}
}
}
Most common error types
ValidationError
- validation for params failedUnsupportedChain
- the chain ID is not supported by Delta.UnsupportedToken
- the token is not supported by Delta.InvalidHmac
-hmac
check failed, meaningprice
object returned from/quote
endpoint was mutated.
Supported Permits
Permit(ERC-2612)
- expected to have 224 bytes length.Permit2
TransferFrom
format - expected length is 96 bytes (32 for permitNonce, 64 for compact signature), amount and deadline should be the same as inOrder.
Allowance
format - expected length is 192 bytes.
DAI Style Permit
- expected length is 256 bytes.0x01
- specialpermit
value that signifies existingPermit2
allowance.
Delta Contract should be specified as a spender is a permit.
Sign an Order
Once the Delta Order is built, it must be signed using the EIP-712 standard before submission. This section provides a working example using ethers.js and axios.
Example:
import { ethers, TypedDataEncoder } from "ethers"; // ethers V6
import axios from "axios";
const userWallet = new ethers.Wallet(ethers.id("alice"));
const userAddress = userWallet.address;
const chainId = 1;
// fetch price
const { data: quoteData } = await axios.get("https://api.paraswap.io/quote?mode=delta&...");
const price = quoteData.delta;
// prepare build order params
const buildOrderParams = {
price,
owner: userAddress,
chainId,
partnerAddress: "0x81037e7be71bce9591de0c54bb485ad3e048b8de",
partnerFeeBps: 100, // 1%
partnerTakesSurplus: false, // the default
};
// build the order to sign
const { data: buildOrderData } = await axios.post("https://api.paraswap.io/delta/orders/build", buildOrderParams);
const { domain, types, value: order } = buildOrderData.toSign;
// hash the order
const eip712Hash = TypedDataEncoder.hash(
domain,
types,
order,
);
// sign and compact
const signature = userWallet.signingKey.sign(typedHash).compactSerialized; // ERC-2098 representation
Sign an order (cross-chain)
The following snippet provides an example implementation of constructing the DeltaBridge
structure for the cross-chain swap
import { ethers, TypedDataEncoder } from "ethers"; // ethers V6
import axios from "axios";
const ETH_ADDRESS = "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee";
const WETH_ADDRESS = "0x4200000000000000000000000000000000000006";
const userWallet = new ethers.Wallet(ethers.id("alice"));
const userAddress = userWallet.address;
const chainId = 1;
const destChainId = 10;
type BeneficiaryType = "EOA" | "SmartContract";
const getMultiCallHandler = async (destTokenDestChain: string, beneficiaryType: BeneficiaryType) => {
const shouldReturnMulticall =
(beneficiaryType === "EOA" && destTokenDestChain === WETH_ADDRESS) ||
(beneficiaryType === "SmartContract" && destTokenDestChain === ETH_ADDRESS);
if (!shouldReturnMulticall) {
return ethers.ZeroAddress;
}
const { data: multiCallHandlers } = await axios.get('https://api.paraswap.io/delta/prices/multicall-handlers');
return multiCallHandlers[destChainId];
}
const getDeltaBridge = async (destTokenDestChain: string, bridgeFee: string) => {
const multiCallHandler = await getMulticallHandler(destTokenDestChain, 'EOA'); // a proper detection of the beneficiary type should be done here
return {
maxRelayerFee: bridgeFee,
destinationChainId: destChainId,
outputToken: destTokenDestChain,
multiCallHandler: multiCallHandler,
};
}
// fetch price
const { data: quoteData } = await axios.get(`https://api.paraswap.io/quote?mode=delta&chainId=${chainId}&destChainId=${destChainId}...`);
const price = quoteData.delta;
// construct delta bridge
const bridge = await getDeltaBridge(ETH_ADDRESS, price.bridgeFee);
// prepare build order params
const buildOrderParams = {
price,
owner: userAddress,
chainId,
partnerAddress: "0x81037e7be71bce9591de0c54bb485ad3e048b8de",
partnerFeeBps: 100, // 1%
partnerTakesSurplus: false, // the default,
bridge,
};
// build the order to sign
const { data: buildOrderData } = await axios.post("https://api.paraswap.io/delta/orders/build", buildOrderParams);
const { domain, types, value: order } = buildOrderData.toSign;
// hash the order
const eip712Hash = TypedDataEncoder.hash(
domain,
types,
order,
);
// sign and compact
const signature = userWallet.signingKey.sign(eip712Hash).compactSerialized; // ERC-2098 representation
Partner Fees
The fees are enabled by encoding partnerAndFee
param of the order, before its signed by a user.
The param includes three values:
partnerAddress
- on-chain address which will be able to collect the fees.partnerFeeBps
- flat fee percent which will be taken from the order. The value is in base points (100
is1%
), which the maximum allowed value of200
.partnerTakeSurplus
- a flag that, if set, allows the partner to collect50%
of thesurplus
as fees. It has no impact if passed together withpartnerFeeBps
, sincepartnerFeeBps
takes precedence.
These are encoded into a single uint256
value, which then is used as partnerAndFee
.
Last updated