中文 / English
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.
$ git clone https://github.com/DODOEX/dodo-bridge-aggregator.git
$ mkdir src/bridge/[bridgeName]
$ touch src/bridge/[bridgeName]/config.ts
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;
name
Bridge nameapiInterface
API interface config APIroute
Route API (required)url
URL address (required)method
api method: get/post/put (required)headers
Request header (optional)before
Call this function before execution (optional)after
Call this function after execution (optional)requestAfter
Call this function after making API request (required)requestMapping
API request parameter dataresponseMapping
Data returned as output from API
status
API to get cross-chain orders status (required)
...tokenList
API to get information on tokens supporting cross chain transactions (required)
...buildTransactionData
API to generate onchain transaction data (optional)
...createOrder
Create order (optional)
...health
Health check API (optional)
...
errorCodes
Errors status code
‘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
depositContract
Deposit contract addresstoAmount
Receiving amount, value in weifee
fees, including swap fees, receiving chain gas fees, cross chain fees, and other fees.swapFee
swap fees, value in USDdestinationGasFee
receiving chain gas fees, value in USDcrossChainFee
cross chain fees, value in USDotherFee
other fees, value in USD
otherPayOut
Additional 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
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
toHash
receiving chain hashstatusInfo
Statusstatus
Status 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
bridgeResponseResult
Responses returned from third party bridges
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
tokens
Token informationchainId
Chain IDaddress
Token addressname
Token namesymbol
Symbol of the token contractdecimals
Decimals of the token contractlogoImg
Link to token’s logo
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
data
Data to be sent onchainvalue
Value to be sent onchain (Hexadecimal)
After ‘BuildTransaction Data’ interface, call ‘CreateOrder’ to save hash
-
Data request
Use both ‘CrossChainParamsData’ and ‘interfaceParamsData’ returned from route interface
-
Responses
interfaceParamData
Data can be stored here for later use
Call ‘health’ to check services or route is available before calling ‘route’
-
Data request
Use ‘CrossChainParamsData’
-
Responses
if ‘isAvailable’ returns ‘false’, service is unavailable
isAvailable
If the service available for usedescription
description
Test configuration settings using method in src/index.ts
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,...})
// ...
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);
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.