{"id":25817478,"url":"https://github.com/dodoex/dodo-bridge-aggregator","last_synced_at":"2025-07-15T16:33:49.386Z","repository":{"id":175457288,"uuid":"622741723","full_name":"DODOEX/dodo-bridge-aggregator","owner":"DODOEX","description":null,"archived":false,"fork":false,"pushed_at":"2024-10-14T07:14:04.000Z","size":93,"stargazers_count":0,"open_issues_count":1,"forks_count":7,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-07-04T17:48:56.817Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/DODOEX.png","metadata":{"files":{"readme":"README-ZH.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-04-03T00:59:30.000Z","updated_at":"2024-10-24T01:15:48.000Z","dependencies_parsed_at":null,"dependency_job_id":"feda58da-a3be-4168-ad15-28e0daa89ee2","html_url":"https://github.com/DODOEX/dodo-bridge-aggregator","commit_stats":null,"previous_names":["dodoex/dodo-bridge-aggregator"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/DODOEX/dodo-bridge-aggregator","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DODOEX%2Fdodo-bridge-aggregator","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DODOEX%2Fdodo-bridge-aggregator/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DODOEX%2Fdodo-bridge-aggregator/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DODOEX%2Fdodo-bridge-aggregator/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/DODOEX","download_url":"https://codeload.github.com/DODOEX/dodo-bridge-aggregator/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DODOEX%2Fdodo-bridge-aggregator/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":265445338,"owners_count":23766448,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-02-28T06:36:52.901Z","updated_at":"2025-07-15T16:33:49.362Z","avatar_url":"https://github.com/DODOEX.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"中文 / [English](https://github.com/DODOEX/dodo-bridge-aggregator#table-of-contents)\n\n# 目录\n\n- [介绍](https://github.com/DODOEX/dodo-bridge-aggregator/blob/main/README-ZH.md#%E4%BB%8B%E7%BB%8D)\n- [克隆代码](https://github.com/DODOEX/dodo-bridge-aggregator/blob/main/README-ZH.md#%E5%85%8B%E9%9A%86%E4%BB%A3%E7%A0%81)\n- [配置](https://github.com/DODOEX/dodo-bridge-aggregator/blob/main/README-ZH.md#%E9%85%8D%E7%BD%AE)\n  - [1. 创建配置文件](https://github.com/DODOEX/dodo-bridge-aggregator/blob/main/README-ZH.md#1-%E5%88%9B%E5%BB%BA%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6)\n  - [2. 添加配置](https://github.com/DODOEX/dodo-bridge-aggregator/blob/main/README-ZH.md#2-%E6%B7%BB%E5%8A%A0%E9%85%8D%E7%BD%AE)\n    - [a. 配置数据](https://github.com/DODOEX/dodo-bridge-aggregator/blob/main/README-ZH.md#a-%E9%85%8D%E7%BD%AE%E6%95%B0%E6%8D%AE)\n    - [b. 接口说明](https://github.com/DODOEX/dodo-bridge-aggregator/blob/main/README-ZH.md#b-%E6%8E%A5%E5%8F%A3%E8%AF%B4%E6%98%8E)\n      - [i. route](https://github.com/DODOEX/dodo-bridge-aggregator/blob/main/README-ZH.md#i-route)\n      - [ii. status](https://github.com/DODOEX/dodo-bridge-aggregator/blob/main/README-ZH.md#ii-status)\n      - [iii. tokenList](https://github.com/DODOEX/dodo-bridge-aggregator/blob/main/README-ZH.md#iii-tokenlist)\n      - [iv. buildTransactionData](https://github.com/DODOEX/dodo-bridge-aggregator/blob/main/README-ZH.md#iv-buildtransactiondata)\n      - [v. createOrder](https://github.com/DODOEX/dodo-bridge-aggregator/blob/main/README-ZH.md#v-createorder)\n      - [vi. health](https://github.com/DODOEX/dodo-bridge-aggregator/blob/main/README-ZH.md#vi-health)\n  - [3. 测试配置](https://github.com/DODOEX/dodo-bridge-aggregator/blob/main/README-ZH.md#3-%E6%B5%8B%E8%AF%95%E9%85%8D%E7%BD%AE)\n  - [4. 提交合并配置](https://github.com/DODOEX/dodo-bridge-aggregator/blob/main/README-ZH.md#4-%E6%8F%90%E4%BA%A4%E5%90%88%E5%B9%B6%E9%85%8D%E7%BD%AE)\n\n# 介绍\n\n该项目旨在聚合第三方跨链桥。跨链桥只需提供一个配置文件，该配置文件涵盖接口配置和数据转换。每个接口将返回相应的数据，以实现聚合。一旦配置完成，即可在“[提交合并配置](https://github.com/DODOEX/dodo-bridge-aggregator/blob/main/README-ZH.md#4-%E6%8F%90%E4%BA%A4%E5%90%88%E5%B9%B6%E9%85%8D%E7%BD%AE)”中提交合并。\n\n# 克隆代码\n\n```bash\n$ git clone https://github.com/DODOEX/dodo-bridge-aggregator.git\n```\n\n# 配置\n\n## 1. 创建配置文件\n\n```bash\n$ mkdir src/bridge/[bridgeName]\n$ touch src/bridge/[bridgeName]/config.ts\n```\n\n## 2. 添加配置\n\n### a. 配置数据\n\n\u003e 复制下面的示例配置内容到 【src/bridge/[bridgeName]/config.ts】 文件中，然后修改配置中的数据。  \n\u003e 在示例配置中主要有 4 个接口配置，分别是 route 、buildTransactionData、status、tokenList 等接口配置。所以需要修改示例配置中的这 4 个接口并且返回相应的数据。  \n\u003e 其他可以参考 【src/bridge/swft/config.ts、src/bridge/squid/config.ts】测试配置数据\n\n```js\nimport { CrossChainBusinessException } from \"./../../exception/index\";\nimport {\n  CrossChainConfig,\n  CrossChainParamsData,\n  StatusInfo,\n} from \"../../types\";\nimport BigNumber from \"bignumber.js\";\n\nconst serverHost = \"http://127.0.0.1:8080\";\nconst errorCodes = {\n  ERROR: { code: \"ERROR\", message: \"unknown error\" },\n  SERVICE_UNAVAILABLE: {\n    code: \"SERVICE_UNAVAILABLE\",\n    message: \"service unavailable\",\n  },\n  // other error code\n};\nconst bridgeNameConfig: CrossChainConfig = {\n  name: \"bridge_name\", // 桥名称\n  //  API 接口配置\n  apiInterface: {\n    // 路由接口(必填)\n    route: {\n      url: `${serverHost}/api/route`, // 接口请求地址\n      method: \"get\", // 接口类型 get/post/put\n      // 开始执行之前调用该函数\n      before: async (params: CrossChainParamsData) =\u003e {\n        const fromAmountUSD = new BigNumber(params.fromAmount)\n          .div(10 ** params.fromTokenDecimals)\n          .times(params.fromTokenPrice);\n        return { fromAmountUSD }; //  这里返回的数据在‘requestMapping’和‘responseMapping’ 中的 \"format\" 函数中可以使用\n      },\n      // 执行完成之后调用该函数\n      after: (err, res) =\u003e {\n        // 需要处理下error\n        if (err) throw new CrossChainBusinessException(errorCodes.ERROR);\n        return res;\n      },\n      // 请求接口之后调用该函数\n      requestAfter: (res) =\u003e {\n        // 检查是否返回正常结果数据\n        if (res.code !== 200)\n          throw new CrossChainBusinessException(errorCodes.ERROR);\n        return res.data;\n      },\n      // 接口请求参数数据放到“requestMapping”中，字段值会从“CrossChainParamsData”参数中做映射处理\n      requestMapping: {\n        fromChain: \"fromChainId\",\n        fromAmount: {\n          format: (_, { beforeResult }) =\u003e beforeResult.fromAmountUSD,\n        },\n        toChain: { field: \"toChain\", type: \"string\" },\n        // other params\n      },\n      // 接口返回数据放到“responseMapping”中，字段值会从“接口响应数据”做映射\n      responseMapping: {\n        depositContract: \"data.contractAddress\", // 存款合约地址\n        toAmount: \"data.amountOutMin\", // 目标链到账金额，单位（wei）\n        fee: {\n          swapFee: \"0\", // swap fee ，单位(USD)\n          destinationGasFee: {\n            //目标链 gas fee ，单位(USD)\n            format: (route, { crossChainParamsData }) =\u003e {\n              return new BigNumber(route.data.fee)\n                .div(10 ** crossChainParamsData.toTokenDecimals)\n                .times(crossChainParamsData.toTokenPrice)\n                .toString(10);\n            },\n          },\n          crossChainFee: \"0\", // 跨链 fee ，单位(USD)\n          otherFee: \"0\", // 其它 fee ，单位(USD)\n        },\n        // 如果在 route 接口中返回了 transactionData 数据，则可以不用在提供 buildTransactionData 接口\n        // transactionData: {\n        //     data: 'data.transactionData',\n        //     value: 'data.value',\n        // },\n        // 发起交易上链时额外从钱包扣除的费用（比如跨链平台将会使用“来源链平台币”提前预支一笔费用，该笔费用定义为“otherPayOut”, 单位USD)\n        otherPayOut: \"0\",\n        // 如果需要保存路由接口中一些数据以便后续几个接口使用可以放到 'interfaceParamData' 中\n        interfaceParamData: {\n          routeId: \"data.routeId\",\n        },\n      },\n    },\n    // 获取状态接口 (必填)\n    status: {\n      url: `${serverHost}/api/status`,\n      method: \"get\",\n      requestAfter: (res) =\u003e {\n        if (res.code !== 200)\n          throw new CrossChainBusinessException(errorCodes.ERROR);\n        return res.data;\n      },\n      requestMapping: {\n        hash: \"fromHash\",\n        // other params\n      },\n      responseMapping: {\n        toHash: \"toHash\",\n        statusInfo: {\n          format: (resResult: any): StatusInfo =\u003e {\n            const data: StatusInfo = {\n              status: \"PENDING\",\n              bridgeResponseResult: resResult,\n            };\n            if (resResult.status === \"success\") {\n              data.status = \"DONE\";\n            } else if (\n              resResult.status === \"timeout\" ||\n              resResult.sourceTxStatus === \"error\"\n            ) {\n              data.status = \"FAILED\";\n              data.message = `failed: ${resResult.message}`;\n            }\n            return data;\n          },\n        },\n      },\n    },\n    // 获取支持跨链交易的token信息 (必填)\n    tokenList: {\n      url: `${serverHost}/api/tokens`,\n      method: \"get\",\n      requestAfter: (res) =\u003e {\n        if (res.code !== 200)\n          throw new CrossChainBusinessException(errorCodes.ERROR);\n        return res.data;\n      },\n      responseMapping: {\n        tokens: {\n          format: (tokenResult) =\u003e {\n            return tokenResult.tokens.map((item: any) =\u003e ({\n              chainId: Number(item.chainId),\n              address: item.address,\n              name: item.name,\n              symbol: item.symbol,\n              decimals: item.decimals,\n              logoImg: item.logoImg,\n            }));\n          },\n        },\n      },\n    },\n    buildTransactionData: {\n      // 生成上链事物数据接口， 如果在 route 接口中没有返回 transactionData，则需要提供该接口 (非必填)\n      url: `${serverHost}/api/buildTransactionData`,\n      method: \"post\",\n      headers: { \"Content-Type\": \"application/json\" },\n      requestAfter: (res) =\u003e {\n        if (res.code !== 200)\n          throw new CrossChainBusinessException(errorCodes.ERROR);\n        return res.data;\n      },\n      requestMapping: {\n        // routeId: 'routeId', // 这里的routeId 使用的是 ‘route’ 接口返回结果中 interfaceParamData 的数据\n        routeId: {\n          format: (_, { interfaceParamData }) =\u003e interfaceParamData.routeId,\n        },\n        // other params\n      },\n      responseMapping: {\n        data: \"data.data\",\n        value: \"data.value\",\n      },\n    },\n    createOrder: {\n      // 创建订单 (非必填)\n      url: `${serverHost}/api/createOrder`,\n      method: \"post\",\n      requestAfter: (res) =\u003e {\n        if (res.code !== 200)\n          throw new CrossChainBusinessException(errorCodes.ERROR);\n        return res.data;\n      },\n      requestMapping: {\n        routeId: \"routeId\",\n        // other params\n      },\n      responseMapping: {\n        interfaceParamData: {\n          orderId: \"orderId\",\n        },\n      },\n    },\n    // 健康检查接口 (非必填)\n    health: {\n      url: `${serverHost}/api/health`,\n      method: \"get\",\n      requestAfter: (res) =\u003e {\n        if (res.code !== 0)\n          throw new CrossChainBusinessException(errorCodes.SERVICE_UNAVAILABLE);\n        return { isAvailable: res.isAvailable, description: res.description };\n      },\n      responseMapping: {\n        isAvailable: \"isAvailable\",\n        description: \"description\",\n      },\n    },\n  },\n  errorCodes, // 错误状态信息\n};\n\nexport default bridgeNameConfig;\n```\n\n### b. 接口说明\n\n- `name` 桥名称\n- `apiInterface` API 接口配置\n  - `route` 路由接口(必填)\n    - `url` 接口地址(必填)\n    - `method` 接口类型 get/post/put (必填)\n    - `headers` 请求头(非必填)\n    - `before` 开始执行之前调用该函数(非必填)\n    - `after` 执行完成之后调用该函数(非必填)\n    - `requestAfter` 请求接口之后调用该函数(必填)\n    - `requestMapping` 接口请求参数数据\n    - `responseMapping` 接口响应结果数据\n  - `status` 获取跨链订单状态接口(必填)  \n    ...\n  - `tokenList` 支持跨链代币列表接口 (必填)  \n    ...\n  - `buildTransactionData` 生成上链事物数据接口(非必填)  \n    ...\n  - `createOrder` 创建订单(非必填)  \n    ...\n  - `health` 健康检查接口 (非必填)  \n    ...\n- `errorCodes` 错误状态信息\n\n## i. route\n\n用于查询路由，返回该路由的报价等信息\n\n1. 请求数据\n   \u003e 我们会提供 【CrossChainParamsData】 跨链请求的基本参数数据给到接口配置中，只需要在 【requestMapping】 中配置请求参数数据做映射处理既可以完成\n\n```js\ntype CrossChainParamsData = {\n  fromChainId: number, // 来源链\n  fromAmount: string, // 来源金额\n  fromTokenAddress: string, // 来源 token 地址\n  fromTokenDecimals: number, // 来源 token decimals\n  fromTokenPrice: string, // 来源 token price\n  fromPlatformTokenPrice: string, // 来源 平台币 price\n  toChainId: number, // 目标链\n  toTokenAddress: string, // 目标token 地址\n  toTokenDecimals: number, // 目标token decimals\n  toTokenPrice: string, // 目标token price\n  fromAddress: string, // 来源用户地址\n  toAddress: string, // 目标用户地址\n  slippage: number, // 滑点\n  fromHash?: string, // 来源链交易hash\n};\n```\n\n2. 响应数据\n   \u003e 这里需要将接口返回数据再次在【responseMapping】中做映射处理，返回 DODO 需要的字段数据信息\n\n- `depositContract` 存款合约地址\n- `toAmount` 目标链到账金额，单位（wei）\n- `fee` 包括 swap fee、目标链 gas fee、跨链 fee、其它 fee 四种费用，不包括来源链的 gas fee\n  - `swapFee` swap fee （单位 USD）\n  - `destinationGasFee` 目标链 gas fee （单位 USD）\n  - `crossChainFee` 跨链 fee （单位 USD）\n  - `otherFee` 其它 fee （单位 USD）\n- `otherPayOut` 发起交易上链时额外从钱包扣除的费用（比如跨链平台将会使用“来源链平台币”提前预支一笔费用，该笔费用定义为“otherPayOut”, 单位 USD)\n- `interfaceParamData` 如果需要保存路由接口中一些数据以便后续几个接口使用则可以放到 'interfaceParamData' 中\n\n## ii. status\n\n获取跨链订单交易的状态\n\n1. 请求数据\n\n   \u003e 【CrossChainParamsData】数据 和 route 接口返回的 【interfaceParamData】的数据\n\n2. 响应数据\n   \u003e 这里需要将接口返回数据再次在【responseMapping】中做映射处理，返回 DODO 需要的字段数据信息\n\n- `toHash` 目标链 hash\n- `statusInfo` 状态信息\n  - `status` 状态\n    - PENDING 处理中状态 （交易处于处理中时的状态）\n    - DONE 完成状态 （交易成功）\n    - FAILED 失败状态 （交易失败）\n    - TRANSFER_REFUNDED 退款成功状态\n    - INVALID 无效数据状态 （数据不正确时的状态）\n    - NOT_FOUND NOT FOUND 状态 （交易 hash 不存在时的状态）\n  - `bridgeResponseResult` 源第三方桥返回的相应数据\n\n## iii. tokenList\n\n需要返回第三方跨链桥支持哪些 token 交易\n\n1. 请求数据\n\n   \u003e 【CrossChainParamsData】数据\n\n2. 响应数据\n   \u003e 这里需要将接口返回数据再次在【responseMapping】中做映射处理，返回 DODO 需要的字段数据信息\n\n- `tokens` 代币信息\n  - `chainId` 链 Id\n  - `address` 代币地址\n  - `name` 代币名称\n  - `symbol` 代币合约上的 symbol\n  - `decimals`代币合约上的 decimals\n  - `logoImg` 代币 logo 图标链接地址\n\n## iv. buildTransactionData\n\n在 route 接口后会调用该接口拿到发送事物的数据\n\n1. 请求数据\n\n   \u003e 【CrossChainParamsData】数据 和 route 接口返回的 【interfaceParamData】的数据\n\n2. 响应数据\n   \u003e 这里需要返回发送事物上链时的 data 和 value 数据\n\n- `data` 上链 data 数据\n- `value` 上链 value 数据 （需要返回 16 进制）\n\n## v. createOrder\n\n在 buildTransactionData 接口后调用创建订单接口保存 hash 等数据\n\n1. 请求数据\n\n   \u003e 【CrossChainParamsData】数据 和 route 接口返回的 【interfaceParamData】的数据\n\n2. 响应数据\n\n- `interfaceParamData` 如果需要保存一些数据可以放到这里\n\n## vi. health\n\n在调用 route 接口前会调用 health 检查服务或路由是否可用\n\n1. 请求数据\n\n   \u003e 【CrossChainParamsData】数据\n\n2. 响应数据\n   \u003e 如果【isAvailable】为 false 代表服务不可用\n\n- `isAvailable` 服务是否可用\n- `description`描述\n\n## 3. 测试配置\n\n调用 src/index.ts 中的方法测试配置\n\n### 1. route 和 buildTransactionData 接口配置测试\n\n```js\nimport {\n  buildTransactionData,\n  CrossChainParamsData,\n  getBridgeConfig,\n  getRoute,\n} from \"../src/index\";\n\nconst crossChainParamsData: CrossChainParamsData = {\n  fromChainId: 56,\n  fromAmount: \"20000000000000000000\",\n  fromTokenAddress: \"0x55d398326f99059ff775485246999027b3197955\",\n  fromTokenDecimals: 18,\n  fromTokenPrice: \"1\",\n  fromPlatformTokenPrice: \"300\",\n  toChainId: 137,\n  toTokenAddress: \"0xc2132d05d31c914a87c6611c10748aeb04b58e8f\",\n  toTokenDecimals: 6,\n  toTokenPrice: \"1\",\n  fromAddress: \"0xd8C446197cA9eE5b6cFC212460C9C5b621a5e1F2\",\n  toAddress: \"0xd8C446197cA9eE5b6cFC212460C9C5b621a5e1F2\",\n  slippage: 0.01,\n};\nconst bridgeName = \"swft_test\";\nconst swftConfig = await getBridgeConfig(bridgeName);\n\nconst routeResult = await getRoute(bridgeName, crossChainParamsData);\nconsole.log(\"routeResult:\", routeResult);\nlet transactionInfo;\nif (swftConfig.apiInterface.buildTransactionData) {\n  transactionInfo = await buildTransactionData(\n    bridgeName,\n    crossChainParamsData,\n    routeResult?.interfaceParamData\n  );\n} else if (routeResult \u0026\u0026 !routeResult.transactionData) {\n  transactionInfo = routeResult.transactionData;\n}\nconsole.log(\"transactionInfo:\", transactionInfo);\n// 拿到发送事物数据后进行上链交易得到 hash\n// wallet.sendTransaction({data:transactionInfo.data,value:transactionInfo.value,...})\n// ...\n```\n\n### 2. createOrder、status、tokenList、health 接口配置测试\n\n```js\nimport { createOrder, getStatus, getTokenList, health } from \"../src/index\";\n\nconst createOrderResult = await createOrder(\n  bridgeName,\n  crossChainParamsData,\n  routeResult?.interfaceParamData\n);\n\nconst statusResult = await getStatus(\n  bridgeName,\n  crossChainParamsData,\n  routeResult?.interfaceParamData\n);\n\nconst tokenListResult = await getTokenList(bridgeName, crossChainParamsData);\n\nconst healthResult = await health(bridgeName, crossChainParamsData);\n```\n\n## 4. 提交合并配置\n\n在完成配置测试后，您可以提交合并请求给我们。请确保请求包含正确的配置，并在 [https://github.com/DODOEX/dodo-bridge-aggregator/pulls](https://github.com/DODOEX/dodo-bridge-aggregator/pulls) 页面中提交。一旦我们收到您的请求，我们会进行检查，确保配置正确无误。如果一切顺利，我们会进行合并，并在测试环境中进行测试。\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdodoex%2Fdodo-bridge-aggregator","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdodoex%2Fdodo-bridge-aggregator","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdodoex%2Fdodo-bridge-aggregator/lists"}