dodo-bridge-aggregator v1.0.7
中文 / English
Table of Contents
TL;DR
This repository contains codes for cross-chain bridges to submit a config file to DODO so as to get your liquidity aggregated by DODO. The config file will include info on API Config and data conversion. Upon completion of config, you could submit via ‘Submit Mergerd Config’ to DODO.
Clone codes
$ git clone https://github.com/DODOEX/dodo-bridge-aggregator.gitConfiguration
1. Add config file
$ mkdir src/bridge/[bridgeName]
$ touch src/bridge/[bridgeName]/config.ts2. Config
a. Prepare config data
Copy the following template to 【src/bridge/bridgeName/config.ts】, and then modify accordingly.
There are four functions in the template, including route, buildTransactionData, status, and tokenList. Modify settings to get corresponding responses.
For other parameters, refer to data in 【src/bridge/swft/config.ts, src/bridge/squid/config.ts】.
import { CrossChainBusinessException } from "./../../exception/index";
import {
CrossChainConfig,
CrossChainParamsData,
StatusInfo,
} from "../../types";
import BigNumber from "bignumber.js";
const serverHost = "http://127.0.0.1:8080";
const errorCodes = {
ERROR: { code: "ERROR", message: "unknown error" },
SERVICE_UNAVAILABLE: {
code: "SERVICE_UNAVAILABLE",
message: "service unavailable",
},
// other error code
};
const bridgeNameConfig: CrossChainConfig = {
name: "bridge_name", // bridge's name
// API configuration
apiInterface: {
// Route (required)
route: {
url: `${serverHost}/api/route`, // API Request URL
method: "get", // API method ( get/post/put)
// call this function before execution
before: async (params: CrossChainParamsData) => {
const fromAmountUSD = new BigNumber(params.fromAmount)
.div(10 ** params.fromTokenDecimals)
.times(params.fromTokenPrice);
return { fromAmountUSD }; // The data returned here is available in the "format" function in 'requestMapping' and 'responseMapping'
},
// call this function after execution
after: (err, res) => {
// need to check if any errors
if (err) throw new CrossChainBusinessException(errorCodes.ERROR);
return res;
},
// call this function after making API request
requestAfter: (res) => {
// check if API request returns a valid or expected response; throw an exception or error if the respons is not normal or expected
if (res.code !== 200)
throw new CrossChainBusinessException(errorCodes.ERROR);
return res.data;
},
// Interface request parameter data is put into "requestMapping", and field values are mapped from "CrossChainParamsData" parameters
requestMapping: {
fromChain: "fromChainId",
fromAmount: {
format: (_, { beforeResult }) => beforeResult.fromAmountUSD,
},
toChain: { field: "toChain", type: "string" },
// other params
},
// The interface return data is put into "responseMapping", and the field values are mapped from "interface response data
responseMapping: {
depositContract: "data.contractAddress", // deposit contract address
toAmount: "data.amountOutMin", // amount reaching the receiving chain, unit in gwei
fee: {
swapFee: "0", // swap fee (USD)
destinationGasFee: {
// receiving chain gas fee (USD)
format: (route, { crossChainParamsData }) => {
return new BigNumber(route.data.fee)
.div(10 ** crossChainParamsData.toTokenDecimals)
.times(crossChainParamsData.toTokenPrice)
.toString(10);
},
},
crossChainFee: "0", // cross chain fee (USD)
otherFee: "0", // other fee (USD)
},
// if 'route' returns 'transactionData', the 'buildTransactionData' is not needed
// transactionData: {
// data: 'data.transactionData',
// value: 'data.value',
// },
// Additional fees deducted from wallet when initiating an onchain transaction (e.g. cross chain bridges may require an upfront fees paid in the sending chain's native token, defined as "otherPayOut")(USD)
otherPayOut: "0",
// "Route" API may return data needed for subsequent actionss, and this data can be stored in "interfaceParamData" section for later use
interfaceParamData: {
routeId: "data.routeId",
},
},
},
// Get Status Interface (required)
status: {
url: `${serverHost}/api/status`,
method: "get",
requestAfter: (res) => {
if (res.code !== 200)
throw new CrossChainBusinessException(errorCodes.ERROR);
return res.data;
},
requestMapping: {
hash: "fromHash",
// other params
},
responseMapping: {
toHash: "toHash",
statusInfo: {
format: (resResult: any): StatusInfo => {
const data: StatusInfo = {
status: "PENDING",
bridgeResponseResult: resResult,
};
if (resResult.status === "success") {
data.status = "DONE";
} else if (
resResult.status === "timeout" ||
resResult.sourceTxStatus === "error"
) {
data.status = "FAILED";
data.message = `failed: ${resResult.message}`;
}
return data;
},
},
},
},
// get information on tokens supporting cross chain transactions (required)
tokenList: {
url: `${serverHost}/api/tokens`,
method: "get",
requestAfter: (res) => {
if (res.code !== 200)
throw new CrossChainBusinessException(errorCodes.ERROR);
return res.data;
},
responseMapping: {
tokens: {
format: (tokenResult) => {
return tokenResult.tokens.map((item: any) => ({
chainId: Number(item.chainId),
address: item.address,
name: item.name,
symbol: item.symbol,
decimals: item.decimals,
logoImg: item.logoImg,
}));
},
},
},
},
buildTransactionData: {
// API to generate onchain transaction data. If "Route" API doesn't return "transactionData", need to provide this API (optional)
url: `${serverHost}/api/buildTransactionData`,
method: "post",
headers: { "Content-Type": "application/json" },
requestAfter: (res) => {
if (res.code !== 200)
throw new CrossChainBusinessException(errorCodes.ERROR);
return res.data;
},
requestMapping: {
// routeId: 'routeId', // use "route" API to return "interfaceParadData" data.
routeId: {
format: (_, { interfaceParamData }) => interfaceParamData.routeId,
},
// other params
},
responseMapping: {
data: "data.data",
value: "data.value",
},
},
createOrder: {
// Create Order (optional)
url: `${serverHost}/api/createOrder`,
method: "post",
requestAfter: (res) => {
if (res.code !== 200)
throw new CrossChainBusinessException(errorCodes.ERROR);
return res.data;
},
requestMapping: {
routeId: "routeId",
// other params
},
responseMapping: {
interfaceParamData: {
orderId: "orderId",
},
},
},
// Healthcheck API (optional)
health: {
url: `${serverHost}/api/health`,
method: "get",
requestAfter: (res) => {
if (res.code !== 0)
throw new CrossChainBusinessException(errorCodes.SERVICE_UNAVAILABLE);
return { isAvailable: res.isAvailable, description: res.description };
},
responseMapping: {
isAvailable: "isAvailable",
description: "description",
},
},
},
errorCodes, // errors status code
};
export default bridgeNameConfig;b. Config items
nameBridge nameapiInterfaceAPI interface config APIrouteRoute API (required)urlURL address (required)methodapi method: get/post/put (required)headersRequest header (optional)beforeCall this function before execution (optional)afterCall this function after execution (optional)requestAfterCall this function after making API request (required)requestMappingAPI request parameter dataresponseMappingData returned as output from API
statusAPI to get cross-chain orders status (required)
...tokenListAPI to get information on tokens supporting cross chain transactions (required)
...buildTransactionDataAPI to generate onchain transaction data (optional)
...createOrderCreate order (optional)
...healthHealth check API (optional)
...
errorCodesErrors status code
i. route
‘Route’ is used to enquire to get responses such as pricing.
- Data request
Data under default settings for cross-chain requests can be found in ‘CrossChainParamsData’ for configuration. You only need to map these data via ‘requestMapping’.
type CrossChainParamsData = {
fromChainId: number, // sending chain
fromAmount: string, // sending amount
fromTokenAddress: string, // sending token address
fromTokenDecimals: number, // sending token decimals
fromTokenPrice: string, // sending token price
fromPlatformTokenPrice: string,
toChainId: number, // receiving chain
toTokenAddress: string, // receiving token address
toTokenDecimals: number, // receiving token decimals
toTokenPrice: string, // receiving token price
fromAddress: string, // sending user address
toAddress: string, // receiving user address
slippage: number, // slippage
fromHash?: string, // sending chain hash
};- Responses
You need to map the responses in ‘responseMapping’ to return the required fields back to DODO
depositContractDeposit contract addresstoAmountReceiving amount, value in weifeefees, including swap fees, receiving chain gas fees, cross chain fees, and other fees.swapFeeswap fees, value in USDdestinationGasFeereceiving chain gas fees, value in USDcrossChainFeecross chain fees, value in USDotherFeeother fees, value in USD
otherPayOutAdditional fees deducted from wallet when initiating an onchain transaction (e.g. cross chain bridges may require upfront fees paid in sending chain's native token, defined as "otherPayOut")interfaceParamData"Route" API may return data needed for subsequent actions, and those data can be stored in "interfaceParamData" section for later use
ii. status
To get cross chain orders status
Data request
Use both ‘CrossChainParamsData’ and ‘interfaceParamsData’ returned from route interface
Responses
You need to map the responses in ‘responseMapping’ to return the required fields back to DODO
toHashreceiving chain hashstatusInfoStatusstatusStatus type- PENDING Transaction pending
- DONE Transaction completed
- FAILED Transaction failed
- TRANSFER_REFUNDED Transaction refunded
- INVALID Data invalid
- NOT_FOUND Transaction hash couldn’t be found NOT FOUND
bridgeResponseResultResponses returned from third party bridges
iii. tokenList
Need responses from third party bridges on tokens supported or available to transact
Data request
Use ‘CrossChainParamsData’
Responses
You need to map the responses in ‘responseMapping’ to return the required fields back to DODO
tokensToken informationchainIdChain IDaddressToken addressnameToken namesymbolSymbol of the token contractdecimalsDecimals of the token contractlogoImgLink to token’s logo
iv. buildTransactionData
After getting ‘route’, use ‘buildTransactionData’ to get inputs for sending requests
Data request
Use both ‘CrossChainParamsData’ and ‘interfaceParamsData’ returned from route interface
Responses
Return data and value when sending inputs onto chains
dataData to be sent onchainvalueValue to be sent onchain (Hexadecimal)
v. createOrder
After ‘BuildTransaction Data’ interface, call ‘CreateOrder’ to save hash
Data request
Use both ‘CrossChainParamsData’ and ‘interfaceParamsData’ returned from route interface
Responses
interfaceParamDataData can be stored here for later use
vi. health
Call ‘health’ to check services or route is available before calling ‘route’
Data request
Use ‘CrossChainParamsData’
Responses
if ‘isAvailable’ returns ‘false’, service is unavailable
isAvailableIf the service available for usedescriptiondescription
3. Test config
Test configuration settings using method in src/index.ts
1. Test ‘route’ and ’buildTransactionData‘
import {
buildTransactionData,
CrossChainParamsData,
getBridgeConfig,
getRoute,
} from "../src/index";
const crossChainParamsData: CrossChainParamsData = {
fromChainId: 56,
fromAmount: "20000000000000000000",
fromTokenAddress: "0x55d398326f99059ff775485246999027b3197955",
fromTokenDecimals: 18,
fromTokenPrice: "1",
fromPlatformTokenPrice: "300",
toChainId: 137,
toTokenAddress: "0xc2132d05d31c914a87c6611c10748aeb04b58e8f",
toTokenDecimals: 6,
toTokenPrice: "1",
fromAddress: "0xd8C446197cA9eE5b6cFC212460C9C5b621a5e1F2",
toAddress: "0xd8C446197cA9eE5b6cFC212460C9C5b621a5e1F2",
slippage: 0.01,
};
const bridgeName = "swft_test";
const swftConfig = await getBridgeConfig(bridgeName);
const routeResult = await getRoute(bridgeName, crossChainParamsData);
console.log("routeResult:", routeResult);
let transactionInfo;
if (swftConfig.apiInterface.buildTransactionData) {
transactionInfo = await buildTransactionData(
bridgeName,
crossChainParamsData,
routeResult?.interfaceParamData
);
} else if (routeResult && !routeResult.transactionData) {
transactionInfo = routeResult.transactionData;
}
console.log("transactionInfo:", transactionInfo);
// Get hash of the onchain transaction after sending data inputs
// wallet.sendTransaction({data:transactionInfo.data,value:transactionInfo.value,...})
// ...2. Test ‘createOrder‘, ’status‘, ’tokenList‘, ’health’
import { createOrder, getStatus, getTokenList, health } from "../src/index";
const createOrderResult = await createOrder(
bridgeName,
crossChainParamsData,
routeResult?.interfaceParamData
);
const statusResult = await getStatus(
bridgeName,
crossChainParamsData,
routeResult?.interfaceParamData
);
const tokenListResult = await getTokenList(bridgeName, crossChainParamsData);
const healthResult = await health(bridgeName, crossChainParamsData);4. Submit merged config
Upon completion of configuration, you can submit a merge request with correct configuration information to DODO via https://github.com/DODOEX/dodo-bridge-aggregator/pulls
We will verify your request and proceed to merge, and test in the test environment if data is submitted correctly.