├── .123trigger ├── .eslintignore ├── .eslintrc.js ├── .github └── workflows │ ├── pr-any.yml │ └── push-master.yml ├── .gitignore ├── .yarn ├── plugins │ └── @yarnpkg │ │ └── plugin-version.cjs ├── releases │ └── yarn-3.2.1.cjs └── versions │ ├── 04cf466a.yml │ ├── 08ccb2ec.yml │ ├── 0a2ac701.yml │ ├── 0c499e88.yml │ ├── 0d43bc64.yml │ ├── 1019e716.yml │ ├── 10385d15.yml │ ├── 12d1b9ba.yml │ ├── 1459d9b3.yml │ ├── 14bbaba3.yml │ ├── 14bbeb63.yml │ ├── 1bf8c891.yml │ ├── 1bf9c911.yml │ ├── 2162cbca.yml │ ├── 22d1b195.yml │ ├── 23081070.yml │ ├── 25cb1c87.yml │ ├── 273a4b27.yml │ ├── 27abe91d.yml │ ├── 27e8d20b.yml │ ├── 2df377cf.yml │ ├── 2feafc9f.yml │ ├── 3078f25e.yml │ ├── 30843f4f.yml │ ├── 32dbc9ab.yml │ ├── 34d6c8f5.yml │ ├── 34df271f.yml │ ├── 369c362c.yml │ ├── 3a0972d6.yml │ ├── 3d711261.yml │ ├── 3ed5fcf2.yml │ ├── 408dad19.yml │ ├── 42a80d7e.yml │ ├── 437e0a6b.yml │ ├── 44b861fd.yml │ ├── 460b74e3.yml │ ├── 462e23de.yml │ ├── 47d1f9a3.yml │ ├── 4c1e3884.yml │ ├── 4c466931.yml │ ├── 51957684.yml │ ├── 528cac28.yml │ ├── 531c1f4d.yml │ ├── 546b0a32.yml │ ├── 56eae19f.yml │ ├── 57355b41.yml │ ├── 5b9db4ed.yml │ ├── 5dac03e1.yml │ ├── 5dec67a3.yml │ ├── 5e8da6ae.yml │ ├── 5eca6b8f.yml │ ├── 5f20d9de.yml │ ├── 5ff6be1d.yml │ ├── 602856ab.yml │ ├── 615143bb.yml │ ├── 62369a31.yml │ ├── 62c4154c.yml │ ├── 62ec724a.yml │ ├── 64b9f440.yml │ ├── 64d10e83.yml │ ├── 64fddd6c.yml │ ├── 6732cf79.yml │ ├── 67c20716.yml │ ├── 6bb91965.yml │ ├── 6f5e08d9.yml │ ├── 704a6a6b.yml │ ├── 7062f961.yml │ ├── 739ec2e2.yml │ ├── 73f12024.yml │ ├── 7865f8b0.yml │ ├── 79467524.yml │ ├── 85b348b8.yml │ ├── 87192e5e.yml │ ├── 87e740ed.yml │ ├── 8b93d974.yml │ ├── 8e27b782.yml │ ├── 905c7860.yml │ ├── 90828de7.yml │ ├── 9105fa32.yml │ ├── 96de212a.yml │ ├── 972d6932.yml │ ├── 98a90bbc.yml │ ├── 98f4004d.yml │ ├── 99b05e3d.yml │ ├── 9a46bac2.yml │ ├── 9bb271a5.yml │ ├── 9d040926.yml │ ├── 9f7831c9.yml │ ├── a065aaed.yml │ ├── a2253144.yml │ ├── ac279d2a.yml │ ├── af2e0b52.yml │ ├── b15b8b60.yml │ ├── b1b80ffb.yml │ ├── b2043787.yml │ ├── b2702525.yml │ ├── b5a3c7a9.yml │ ├── b5f888a5.yml │ ├── bb8c0845.yml │ ├── bba5e161.yml │ ├── bbcf8c73.yml │ ├── bc47a857.yml │ ├── bd19d71f.yml │ ├── c06d2d5a.yml │ ├── c3b235fb.yml │ ├── c3e164ad.yml │ ├── c8aad0e7.yml │ ├── c92a6790.yml │ ├── c9460329.yml │ ├── ca7cf569.yml │ ├── caac6414.yml │ ├── d0078662.yml │ ├── d12107ff.yml │ ├── d19008b4.yml │ ├── d1d03f39.yml │ ├── d42ebc5e.yml │ ├── d7cd1fa2.yml │ ├── d9ddd5b3.yml │ ├── da7406f7.yml │ ├── dbfef434.yml │ ├── dcee1b8d.yml │ ├── df5c8691.yml │ ├── e185dfc7.yml │ ├── e5068824.yml │ ├── e5466735.yml │ ├── e571a584.yml │ ├── e5ca4724.yml │ ├── e6416b1c.yml │ ├── e6893c9b.yml │ ├── ea1abecd.yml │ ├── edef6c72.yml │ ├── efc428ec.yml │ ├── f1871fbc.yml │ ├── f29a3d1c.yml │ ├── f3178094.yml │ ├── f32d2691.yml │ ├── f3ebc6d0.yml │ ├── f5d27cb7.yml │ ├── f6dc137f.yml │ ├── f7fad8f7.yml │ ├── f89686c5.yml │ ├── f98157fd.yml │ ├── fafd35b2.yml │ ├── fb2d72c8.yml │ └── fe01a1db.yml ├── .yarnrc ├── .yarnrc.yml ├── CHANGELOG.md ├── CONTRIBUTORS ├── LICENSE ├── README.md ├── babel.config.js ├── docs └── support-bridges.md ├── package.json ├── scripts ├── generate-bridge-document.ts └── traverse-dir.ts ├── src ├── adapters │ ├── acala │ │ ├── acala-configs.ts │ │ ├── acala.spec.ts │ │ ├── acala.ts │ │ ├── index.ts │ │ └── karura-configs.ts │ ├── assethub.ts │ ├── astar.spec.ts │ ├── astar.ts │ ├── basilisk.spec.ts │ ├── bifrost.spec.ts │ ├── bifrost.ts │ ├── centrifuge.spec.ts │ ├── centrifuge.ts │ ├── crust.spec.ts │ ├── crust.ts │ ├── darwinia.spec.ts │ ├── darwinia.ts │ ├── hydradx.spec.ts │ ├── hydradx.ts │ ├── index.ts │ ├── integritee.spec.ts │ ├── integritee.ts │ ├── interlay.spec.ts │ ├── interlay.ts │ ├── listen.ts │ ├── manta.ts │ ├── moonbeam.spec.ts │ ├── moonbeam.ts │ ├── oak.spec.ts │ ├── oak.ts │ ├── phala.ts │ ├── polkadot.spec.ts │ ├── polkadot.ts │ ├── robonomics.spec.ts │ ├── robonomics.ts │ ├── shiden.spec.ts │ ├── subsocial.ts │ ├── tinkernet.ts │ ├── unique.ts │ └── zeitgeist.ts ├── api-provider.spec.ts ├── api-provider.ts ├── balance-adapter.ts ├── base-chain-adapter.ts ├── bridge.spec.ts ├── bridge.ts ├── configs │ ├── chains │ │ ├── index.ts │ │ ├── kusama-chains.ts │ │ └── polkadot-chains.ts │ ├── common.ts │ └── index.ts ├── cross-chain-router.spec.ts ├── cross-chain-router.ts ├── errors.ts ├── index.ts ├── support-bridges.ts ├── types.ts └── utils │ ├── check-message-version.ts │ ├── create-route-configs.ts │ ├── destination-utils.ts │ ├── fetch-config-from-api-or-local.ts │ ├── get-xcm-delivery-fee.ts │ ├── index.ts │ ├── is-chain-equal.ts │ ├── polkadot-xcm-params.ts │ ├── unit-test.ts │ ├── validate-address.ts │ └── xtokens-params.ts ├── tsconfig.build.json ├── tsconfig.json └── yarn.lock /.123trigger: -------------------------------------------------------------------------------- 1 | 2 | 0.0.4 3 | 0.1.2 4 | 0.1.5 5 | 0.1.6 6 | 0.1.8 -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | .eslintrc.js 2 | babel.config.js 3 | jest.config.js 4 | 5 | scripts/** 6 | build/** 7 | /node_modules/** 8 | src/**/*.spec.ts 9 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | 2 | // Copyright 2017-2021 @polkadot/apps authors & contributors 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | const base = require('@open-web3/dev-config/config/eslint.cjs'); 6 | 7 | // add override for any (a metric ton of them, initial conversion) 8 | module.exports = { 9 | ...base, 10 | ignorePatterns: [ 11 | '.eslintrc.js', 12 | '.github/**', 13 | '.vscode/**', 14 | '.yarn/**', 15 | '**/build/*', 16 | '**/coverage/*', 17 | '**/node_modules/*', 18 | 'src/support-bridges.ts' 19 | ], 20 | plugins: [ 21 | ...base.plugins, 22 | "unused-imports" 23 | ], 24 | parserOptions: { 25 | ...base.parserOptions, 26 | project: [ 27 | './tsconfig.json' 28 | ] 29 | }, 30 | rules: { 31 | ...base.rules, 32 | '@typescript-eslint/no-explicit-any': 'off', 33 | 'react/prop-types': 'off', 34 | 'header/header': 'off', 35 | // TODO This is a new error on latest eslint version, if possible fix all 223 places where 'any' is used as function argument 36 | '@typescript-eslint/no-unsafe-argument': 'off', 37 | '@typescript-eslint/no-unsafe-assignment': 'off', 38 | '@typescript-eslint/no-unsafe-member-access': 'off', 39 | '@typescript-eslint/no-unsafe-call': 'off', 40 | '@typescript-eslint/restrict-plus-operands': 'off', 41 | '@typescript-eslint/no-unsafe-return': 'off', 42 | '@typescript-eslint/restrict-template-expressions': 'off', 43 | '@typescript-eslint/no-floating-promises': 'off', 44 | '@typescript-eslint/ban-ts-comment': 'off', 45 | '@typescript-eslint/explicit-module-boundary-types': 'off', 46 | 'react/jsx-no-bind': 'off', 47 | 'react/jsx-max-props-per-line': 'off', 48 | '@typescript-eslint/unbound-method': 'off', 49 | 'react/jsx-closing-bracket-location': 'off', 50 | 'sort-keys': 'off', 51 | 'react/jsx-no-bind': 'off', 52 | '@typescript-eslint/no-unused-vars': 'off', 53 | "unused-imports/no-unused-imports": "error", 54 | "unused-imports/no-unused-vars": [ 55 | "error", 56 | { "vars": "all", "varsIgnorePattern": "^_", "args": "after-used", "argsIgnorePattern": "^_" } 57 | ] 58 | } 59 | }; 60 | -------------------------------------------------------------------------------- /.github/workflows/pr-any.yml: -------------------------------------------------------------------------------- 1 | 2 | name: PR 3 | on: [pull_request] 4 | 5 | jobs: 6 | pr: 7 | strategy: 8 | matrix: 9 | step: ['lint', 'test'] 10 | name: ${{ matrix.step }} 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v2 14 | - name: ${{ matrix.step }} 15 | run: | 16 | yarn install --immutable | grep -v 'YN0013' 17 | yarn ${{ matrix.step }} 18 | 19 | -------------------------------------------------------------------------------- /.github/workflows/push-master.yml: -------------------------------------------------------------------------------- 1 | name: Master 2 | on: 3 | push: 4 | branches: 5 | - master 6 | 7 | jobs: 8 | master: 9 | strategy: 10 | matrix: 11 | step: ['build:release'] 12 | name: ${{ matrix.step }} 13 | if: "! startsWith(github.event.head_commit.message, '[CI Skip]')" 14 | runs-on: ubuntu-latest 15 | steps: 16 | - uses: actions/checkout@v2 17 | with: 18 | token: ${{ secrets.GH_PAT }} 19 | - name: ${{ matrix.step }} 20 | env: 21 | CC_TEST_REPORTER_ID: ${{ secrets.CC_TEST_REPORTER_ID }} 22 | GH_PAT: ${{ secrets.GH_PAT }} 23 | GH_RELEASE_GITHUB_API_TOKEN: ${{ secrets.GH_PAT }} 24 | NPM_TOKEN: ${{ secrets.NPM_TOKEN }} 25 | run: | 26 | yarn install --immutable | grep -v 'YN0013' 27 | yarn ${{ matrix.step }} 28 | 29 | dummy: 30 | runs-on: ubuntu-latest 31 | steps: 32 | - name: dummy 33 | run: | 34 | echo "Dummy skip step" -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # yarn 2 | .yarn/* 3 | !.yarn/patches 4 | !.yarn/plugins 5 | !.yarn/releases 6 | !.yarn/sdks 7 | !.yarn/versions 8 | 9 | # Logs 10 | logs 11 | *.log 12 | npm-debug.log* 13 | yarn-debug.log* 14 | yarn-error.log* 15 | lerna-debug.log* 16 | 17 | # Diagnostic reports (https://nodejs.org/api/report.html) 18 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 19 | 20 | # Runtime data 21 | pids 22 | *.pid 23 | *.seed 24 | *.pid.lock 25 | 26 | # Directory for instrumented libs generated by jscoverage/JSCover 27 | lib-cov 28 | 29 | # Coverage directory used by tools like istanbul 30 | coverage 31 | *.lcov 32 | 33 | # nyc test coverage 34 | .nyc_output 35 | 36 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 37 | .grunt 38 | 39 | # Bower dependency directory (https://bower.io/) 40 | bower_components 41 | 42 | # node-waf configuration 43 | .lock-wscript 44 | 45 | # Compiled binary addons (https://nodejs.org/api/addons.html) 46 | build/ 47 | 48 | # Dependency directories 49 | node_modules/ 50 | jspm_packages/ 51 | 52 | # TypeScript v1 declaration files 53 | typings/ 54 | 55 | # TypeScript cache 56 | *.tsbuildinfo 57 | 58 | # Optional npm cache directory 59 | .npm 60 | 61 | # Optional eslint cache 62 | .eslintcache 63 | 64 | # Microbundle cache 65 | .rpt2_cache/ 66 | .rts2_cache_cjs/ 67 | .rts2_cache_es/ 68 | .rts2_cache_umd/ 69 | 70 | # Optional REPL history 71 | .node_repl_history 72 | 73 | # Output of 'npm pack' 74 | *.tgz 75 | 76 | # Yarn Integrity file 77 | .yarn-integrity 78 | 79 | # dotenv environment variables file 80 | .env 81 | .env.test 82 | 83 | # parcel-bundler cache (https://parceljs.org/) 84 | .cache 85 | 86 | # Next.js build output 87 | .next 88 | 89 | # Nuxt.js build / generate output 90 | .nuxt 91 | dist 92 | 93 | # Gatsby files 94 | .cache/ 95 | # Comment in the public line in if your project uses Gatsby and *not* Next.js 96 | # https://nextjs.org/blog/next-9-1#public-directory-support 97 | # public 98 | 99 | # vuepress build output 100 | .vuepress/dist 101 | 102 | # Serverless directories 103 | .serverless/ 104 | 105 | # FuseBox cache 106 | .fusebox/ 107 | 108 | # DynamoDB Local files 109 | .dynamodb/ 110 | 111 | # TernJS port file 112 | .tern-port 113 | 114 | # VSCode 115 | .vscode -------------------------------------------------------------------------------- /.yarn/versions/04cf466a.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/04cf466a.yml -------------------------------------------------------------------------------- /.yarn/versions/08ccb2ec.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/08ccb2ec.yml -------------------------------------------------------------------------------- /.yarn/versions/0a2ac701.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/0a2ac701.yml -------------------------------------------------------------------------------- /.yarn/versions/0c499e88.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/0c499e88.yml -------------------------------------------------------------------------------- /.yarn/versions/0d43bc64.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/0d43bc64.yml -------------------------------------------------------------------------------- /.yarn/versions/1019e716.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/1019e716.yml -------------------------------------------------------------------------------- /.yarn/versions/10385d15.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/10385d15.yml -------------------------------------------------------------------------------- /.yarn/versions/12d1b9ba.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/12d1b9ba.yml -------------------------------------------------------------------------------- /.yarn/versions/1459d9b3.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/1459d9b3.yml -------------------------------------------------------------------------------- /.yarn/versions/14bbaba3.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/14bbaba3.yml -------------------------------------------------------------------------------- /.yarn/versions/14bbeb63.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/14bbeb63.yml -------------------------------------------------------------------------------- /.yarn/versions/1bf8c891.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/1bf8c891.yml -------------------------------------------------------------------------------- /.yarn/versions/1bf9c911.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/1bf9c911.yml -------------------------------------------------------------------------------- /.yarn/versions/2162cbca.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/2162cbca.yml -------------------------------------------------------------------------------- /.yarn/versions/22d1b195.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/22d1b195.yml -------------------------------------------------------------------------------- /.yarn/versions/23081070.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/23081070.yml -------------------------------------------------------------------------------- /.yarn/versions/25cb1c87.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/25cb1c87.yml -------------------------------------------------------------------------------- /.yarn/versions/273a4b27.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/273a4b27.yml -------------------------------------------------------------------------------- /.yarn/versions/27abe91d.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/27abe91d.yml -------------------------------------------------------------------------------- /.yarn/versions/27e8d20b.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/27e8d20b.yml -------------------------------------------------------------------------------- /.yarn/versions/2df377cf.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/2df377cf.yml -------------------------------------------------------------------------------- /.yarn/versions/2feafc9f.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/2feafc9f.yml -------------------------------------------------------------------------------- /.yarn/versions/3078f25e.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/3078f25e.yml -------------------------------------------------------------------------------- /.yarn/versions/30843f4f.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/30843f4f.yml -------------------------------------------------------------------------------- /.yarn/versions/32dbc9ab.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/32dbc9ab.yml -------------------------------------------------------------------------------- /.yarn/versions/34d6c8f5.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/34d6c8f5.yml -------------------------------------------------------------------------------- /.yarn/versions/34df271f.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/34df271f.yml -------------------------------------------------------------------------------- /.yarn/versions/369c362c.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/369c362c.yml -------------------------------------------------------------------------------- /.yarn/versions/3a0972d6.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/3a0972d6.yml -------------------------------------------------------------------------------- /.yarn/versions/3d711261.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/3d711261.yml -------------------------------------------------------------------------------- /.yarn/versions/3ed5fcf2.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/3ed5fcf2.yml -------------------------------------------------------------------------------- /.yarn/versions/408dad19.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/408dad19.yml -------------------------------------------------------------------------------- /.yarn/versions/42a80d7e.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/42a80d7e.yml -------------------------------------------------------------------------------- /.yarn/versions/437e0a6b.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/437e0a6b.yml -------------------------------------------------------------------------------- /.yarn/versions/44b861fd.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/44b861fd.yml -------------------------------------------------------------------------------- /.yarn/versions/460b74e3.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/460b74e3.yml -------------------------------------------------------------------------------- /.yarn/versions/462e23de.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/462e23de.yml -------------------------------------------------------------------------------- /.yarn/versions/47d1f9a3.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/47d1f9a3.yml -------------------------------------------------------------------------------- /.yarn/versions/4c1e3884.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/4c1e3884.yml -------------------------------------------------------------------------------- /.yarn/versions/4c466931.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/4c466931.yml -------------------------------------------------------------------------------- /.yarn/versions/51957684.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/51957684.yml -------------------------------------------------------------------------------- /.yarn/versions/528cac28.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/528cac28.yml -------------------------------------------------------------------------------- /.yarn/versions/531c1f4d.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/531c1f4d.yml -------------------------------------------------------------------------------- /.yarn/versions/546b0a32.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/546b0a32.yml -------------------------------------------------------------------------------- /.yarn/versions/56eae19f.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/56eae19f.yml -------------------------------------------------------------------------------- /.yarn/versions/57355b41.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/57355b41.yml -------------------------------------------------------------------------------- /.yarn/versions/5b9db4ed.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/5b9db4ed.yml -------------------------------------------------------------------------------- /.yarn/versions/5dac03e1.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/5dac03e1.yml -------------------------------------------------------------------------------- /.yarn/versions/5dec67a3.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/5dec67a3.yml -------------------------------------------------------------------------------- /.yarn/versions/5e8da6ae.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/5e8da6ae.yml -------------------------------------------------------------------------------- /.yarn/versions/5eca6b8f.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/5eca6b8f.yml -------------------------------------------------------------------------------- /.yarn/versions/5f20d9de.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/5f20d9de.yml -------------------------------------------------------------------------------- /.yarn/versions/5ff6be1d.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/5ff6be1d.yml -------------------------------------------------------------------------------- /.yarn/versions/602856ab.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/602856ab.yml -------------------------------------------------------------------------------- /.yarn/versions/615143bb.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/615143bb.yml -------------------------------------------------------------------------------- /.yarn/versions/62369a31.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/62369a31.yml -------------------------------------------------------------------------------- /.yarn/versions/62c4154c.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/62c4154c.yml -------------------------------------------------------------------------------- /.yarn/versions/62ec724a.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/62ec724a.yml -------------------------------------------------------------------------------- /.yarn/versions/64b9f440.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/64b9f440.yml -------------------------------------------------------------------------------- /.yarn/versions/64d10e83.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/64d10e83.yml -------------------------------------------------------------------------------- /.yarn/versions/64fddd6c.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/64fddd6c.yml -------------------------------------------------------------------------------- /.yarn/versions/6732cf79.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/6732cf79.yml -------------------------------------------------------------------------------- /.yarn/versions/67c20716.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/67c20716.yml -------------------------------------------------------------------------------- /.yarn/versions/6bb91965.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/6bb91965.yml -------------------------------------------------------------------------------- /.yarn/versions/6f5e08d9.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/6f5e08d9.yml -------------------------------------------------------------------------------- /.yarn/versions/704a6a6b.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/704a6a6b.yml -------------------------------------------------------------------------------- /.yarn/versions/7062f961.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/7062f961.yml -------------------------------------------------------------------------------- /.yarn/versions/739ec2e2.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/739ec2e2.yml -------------------------------------------------------------------------------- /.yarn/versions/73f12024.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/73f12024.yml -------------------------------------------------------------------------------- /.yarn/versions/7865f8b0.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/7865f8b0.yml -------------------------------------------------------------------------------- /.yarn/versions/79467524.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/79467524.yml -------------------------------------------------------------------------------- /.yarn/versions/85b348b8.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/85b348b8.yml -------------------------------------------------------------------------------- /.yarn/versions/87192e5e.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/87192e5e.yml -------------------------------------------------------------------------------- /.yarn/versions/87e740ed.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/87e740ed.yml -------------------------------------------------------------------------------- /.yarn/versions/8b93d974.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/8b93d974.yml -------------------------------------------------------------------------------- /.yarn/versions/8e27b782.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/8e27b782.yml -------------------------------------------------------------------------------- /.yarn/versions/905c7860.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/905c7860.yml -------------------------------------------------------------------------------- /.yarn/versions/90828de7.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/90828de7.yml -------------------------------------------------------------------------------- /.yarn/versions/9105fa32.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/9105fa32.yml -------------------------------------------------------------------------------- /.yarn/versions/96de212a.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/96de212a.yml -------------------------------------------------------------------------------- /.yarn/versions/972d6932.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/972d6932.yml -------------------------------------------------------------------------------- /.yarn/versions/98a90bbc.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/98a90bbc.yml -------------------------------------------------------------------------------- /.yarn/versions/98f4004d.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/98f4004d.yml -------------------------------------------------------------------------------- /.yarn/versions/99b05e3d.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/99b05e3d.yml -------------------------------------------------------------------------------- /.yarn/versions/9a46bac2.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/9a46bac2.yml -------------------------------------------------------------------------------- /.yarn/versions/9bb271a5.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/9bb271a5.yml -------------------------------------------------------------------------------- /.yarn/versions/9d040926.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/9d040926.yml -------------------------------------------------------------------------------- /.yarn/versions/9f7831c9.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/9f7831c9.yml -------------------------------------------------------------------------------- /.yarn/versions/a065aaed.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/a065aaed.yml -------------------------------------------------------------------------------- /.yarn/versions/a2253144.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/a2253144.yml -------------------------------------------------------------------------------- /.yarn/versions/ac279d2a.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/ac279d2a.yml -------------------------------------------------------------------------------- /.yarn/versions/af2e0b52.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/af2e0b52.yml -------------------------------------------------------------------------------- /.yarn/versions/b15b8b60.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/b15b8b60.yml -------------------------------------------------------------------------------- /.yarn/versions/b1b80ffb.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/b1b80ffb.yml -------------------------------------------------------------------------------- /.yarn/versions/b2043787.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/b2043787.yml -------------------------------------------------------------------------------- /.yarn/versions/b2702525.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/b2702525.yml -------------------------------------------------------------------------------- /.yarn/versions/b5a3c7a9.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/b5a3c7a9.yml -------------------------------------------------------------------------------- /.yarn/versions/b5f888a5.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/b5f888a5.yml -------------------------------------------------------------------------------- /.yarn/versions/bb8c0845.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/bb8c0845.yml -------------------------------------------------------------------------------- /.yarn/versions/bba5e161.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/bba5e161.yml -------------------------------------------------------------------------------- /.yarn/versions/bbcf8c73.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/bbcf8c73.yml -------------------------------------------------------------------------------- /.yarn/versions/bc47a857.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/bc47a857.yml -------------------------------------------------------------------------------- /.yarn/versions/bd19d71f.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/bd19d71f.yml -------------------------------------------------------------------------------- /.yarn/versions/c06d2d5a.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/c06d2d5a.yml -------------------------------------------------------------------------------- /.yarn/versions/c3b235fb.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/c3b235fb.yml -------------------------------------------------------------------------------- /.yarn/versions/c3e164ad.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/c3e164ad.yml -------------------------------------------------------------------------------- /.yarn/versions/c8aad0e7.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/c8aad0e7.yml -------------------------------------------------------------------------------- /.yarn/versions/c92a6790.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/c92a6790.yml -------------------------------------------------------------------------------- /.yarn/versions/c9460329.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/c9460329.yml -------------------------------------------------------------------------------- /.yarn/versions/ca7cf569.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/ca7cf569.yml -------------------------------------------------------------------------------- /.yarn/versions/caac6414.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/caac6414.yml -------------------------------------------------------------------------------- /.yarn/versions/d0078662.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/d0078662.yml -------------------------------------------------------------------------------- /.yarn/versions/d12107ff.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/d12107ff.yml -------------------------------------------------------------------------------- /.yarn/versions/d19008b4.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/d19008b4.yml -------------------------------------------------------------------------------- /.yarn/versions/d1d03f39.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/d1d03f39.yml -------------------------------------------------------------------------------- /.yarn/versions/d42ebc5e.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/d42ebc5e.yml -------------------------------------------------------------------------------- /.yarn/versions/d7cd1fa2.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/d7cd1fa2.yml -------------------------------------------------------------------------------- /.yarn/versions/d9ddd5b3.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/d9ddd5b3.yml -------------------------------------------------------------------------------- /.yarn/versions/da7406f7.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/da7406f7.yml -------------------------------------------------------------------------------- /.yarn/versions/dbfef434.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/dbfef434.yml -------------------------------------------------------------------------------- /.yarn/versions/dcee1b8d.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/dcee1b8d.yml -------------------------------------------------------------------------------- /.yarn/versions/df5c8691.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/df5c8691.yml -------------------------------------------------------------------------------- /.yarn/versions/e185dfc7.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/e185dfc7.yml -------------------------------------------------------------------------------- /.yarn/versions/e5068824.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/e5068824.yml -------------------------------------------------------------------------------- /.yarn/versions/e5466735.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/e5466735.yml -------------------------------------------------------------------------------- /.yarn/versions/e571a584.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/e571a584.yml -------------------------------------------------------------------------------- /.yarn/versions/e5ca4724.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/e5ca4724.yml -------------------------------------------------------------------------------- /.yarn/versions/e6416b1c.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/e6416b1c.yml -------------------------------------------------------------------------------- /.yarn/versions/e6893c9b.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/e6893c9b.yml -------------------------------------------------------------------------------- /.yarn/versions/ea1abecd.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/ea1abecd.yml -------------------------------------------------------------------------------- /.yarn/versions/edef6c72.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/edef6c72.yml -------------------------------------------------------------------------------- /.yarn/versions/efc428ec.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/efc428ec.yml -------------------------------------------------------------------------------- /.yarn/versions/f1871fbc.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/f1871fbc.yml -------------------------------------------------------------------------------- /.yarn/versions/f29a3d1c.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/f29a3d1c.yml -------------------------------------------------------------------------------- /.yarn/versions/f3178094.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/f3178094.yml -------------------------------------------------------------------------------- /.yarn/versions/f32d2691.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/f32d2691.yml -------------------------------------------------------------------------------- /.yarn/versions/f3ebc6d0.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/f3ebc6d0.yml -------------------------------------------------------------------------------- /.yarn/versions/f5d27cb7.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/f5d27cb7.yml -------------------------------------------------------------------------------- /.yarn/versions/f6dc137f.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/f6dc137f.yml -------------------------------------------------------------------------------- /.yarn/versions/f7fad8f7.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/f7fad8f7.yml -------------------------------------------------------------------------------- /.yarn/versions/f89686c5.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/f89686c5.yml -------------------------------------------------------------------------------- /.yarn/versions/f98157fd.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/f98157fd.yml -------------------------------------------------------------------------------- /.yarn/versions/fafd35b2.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/fafd35b2.yml -------------------------------------------------------------------------------- /.yarn/versions/fb2d72c8.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/fb2d72c8.yml -------------------------------------------------------------------------------- /.yarn/versions/fe01a1db.yml: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/.yarn/versions/fe01a1db.yml -------------------------------------------------------------------------------- /.yarnrc: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | yarn-path ".yarn/releases/yarn-1.22.19.cjs" 6 | -------------------------------------------------------------------------------- /.yarnrc.yml: -------------------------------------------------------------------------------- 1 | nodeLinker: node-modules 2 | 3 | plugins: 4 | - path: .yarn/plugins/@yarnpkg/plugin-version.cjs 5 | spec: "@yarnpkg/plugin-version" 6 | 7 | yarnPath: .yarn/releases/yarn-3.2.1.cjs 8 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # [0.0.6-1] - 20230314 2 | 1. support some promise type api to query informations 3 | 2. sorted some types, method name 4 | 3. change `setApi` method name to `init` and support custom parameters 5 | 6 | # [0.0.3-1] - 20220809 7 | 1. acala <--> astar support. 8 | 2. bump acala.js 4.1.6-23. 9 | 10 | # [0.0.3] - 20220801 11 | 1. initial release. 12 | 2. 20+ parachains supported. -------------------------------------------------------------------------------- /CONTRIBUTORS: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/polkawallet-io/bridge/c6326a33cfd030c1895112348bf85e25bb09db38/CONTRIBUTORS -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = require('@open-web3/dev-config/config/babel-config-cjs.cjs'); 2 | -------------------------------------------------------------------------------- /docs/support-bridges.md: -------------------------------------------------------------------------------- 1 | 2 | # Support Bridges 3 | 4 | ## Bridges 5 | 6 | | From | To | Token | 7 | | --- | --- | --- | 8 | | acala | polkadot | DOT | 9 | | acala | moonbeam | GLMR | 10 | | acala | moonbeam | ACA | 11 | | acala | moonbeam | AUSD | 12 | | acala | moonbeam | DOT | 13 | | acala | moonbeam | LDOT | 14 | | acala | astar | ASTR | 15 | | acala | astar | ACA | 16 | | acala | astar | AUSD | 17 | | acala | astar | LDOT | 18 | | acala | interlay | INTR | 19 | | acala | interlay | IBTC | 20 | | acala | hydradx | DAI | 21 | | acala | hydradx | DOT | 22 | | acala | hydradx | WETH | 23 | | acala | hydradx | WBTC | 24 | | acala | hydradx | LDOT | 25 | | acala | hydradx | ACA | 26 | | acala | unique | UNQ | 27 | | acala | assetHubPolkadot | USDT | 28 | | acala | assetHubPolkadot | USDC | 29 | | acala | assetHubPolkadot | PINK | 30 | | acala | assetHubPolkadot | LOVA | 31 | | acala | assetHubPolkadot | LOTY | 32 | | acala | assetHubPolkadot | DAMN | 33 | | karura | kusama | KSM | 34 | | karura | assetHubKusama | RMRK | 35 | | karura | assetHubKusama | ARIS | 36 | | karura | assetHubKusama | USDT | 37 | | karura | shiden | SDN | 38 | | karura | shiden | KUSD | 39 | | karura | bifrost | BNC | 40 | | karura | bifrost | KSM | 41 | | karura | bifrost | KAR | 42 | | karura | bifrost | KUSD | 43 | | karura | bifrost | VSKSM | 44 | | karura | altair | AIR | 45 | | karura | altair | KUSD | 46 | | karura | shadow | CSM | 47 | | karura | shadow | KAR | 48 | | karura | shadow | KUSD | 49 | | karura | crab | CRAB | 50 | | karura | integritee | TEER | 51 | | karura | kintsugi | KINT | 52 | | karura | kintsugi | KBTC | 53 | | karura | kintsugi | LKSM | 54 | | karura | khala | PHA | 55 | | karura | khala | KUSD | 56 | | karura | khala | KAR | 57 | | karura | calamari | KSM | 58 | | karura | calamari | KMA | 59 | | karura | calamari | KUSD | 60 | | karura | calamari | KAR | 61 | | karura | calamari | LKSM | 62 | | karura | moonriver | MOVR | 63 | | karura | moonriver | KSM | 64 | | karura | moonriver | KAR | 65 | | karura | moonriver | KUSD | 66 | | karura | turing | TUR | 67 | | karura | turing | KAR | 68 | | karura | turing | KUSD | 69 | | karura | turing | LKSM | 70 | | karura | basilisk | BSX | 71 | | karura | basilisk | KSM | 72 | | karura | basilisk | KUSD | 73 | | karura | basilisk | DAI | 74 | | karura | basilisk | USDCet | 75 | | karura | basilisk | WETH | 76 | | karura | basilisk | WBTC | 77 | | karura | listen | LT | 78 | | karura | listen | KAR | 79 | | karura | listen | KUSD | 80 | | karura | listen | LKSM | 81 | | karura | quartz | QTZ | 82 | | assetHubPolkadot | polkadot | DOT | 83 | | assetHubPolkadot | hydradx | USDT | 84 | | assetHubPolkadot | acala | USDT | 85 | | assetHubPolkadot | acala | USDC | 86 | | assetHubPolkadot | acala | LOVA | 87 | | assetHubPolkadot | acala | LOTY | 88 | | assetHubPolkadot | acala | DAMN | 89 | | assetHubPolkadot | acala | PINK | 90 | | assetHubPolkadot | astar | USDT | 91 | | assetHubPolkadot | interlay | USDT | 92 | | assetHubPolkadot | interlay | USDC | 93 | | assetHubPolkadot | moonbeam | USDT | 94 | | assetHubKusama | kusama | KSM | 95 | | assetHubKusama | karura | RMRK | 96 | | assetHubKusama | karura | ARIS | 97 | | assetHubKusama | karura | USDT | 98 | | assetHubKusama | kintsugi | USDT | 99 | | astar | acala | ASTR | 100 | | astar | acala | ACA | 101 | | astar | acala | AUSD | 102 | | astar | acala | LDOT | 103 | | astar | hydradx | ASTR | 104 | | astar | assetHubPolkadot | USDT | 105 | | astar | interlay | IBTC | 106 | | astar | interlay | INTR | 107 | | shiden | karura | SDN | 108 | | shiden | karura | KUSD | 109 | | bifrost | karura | BNC | 110 | | bifrost | karura | VSKSM | 111 | | bifrost | karura | KSM | 112 | | bifrost | karura | KAR | 113 | | bifrost | karura | KUSD | 114 | | bifrost | kintsugi | VKSM | 115 | | basilisk | kusama | KSM | 116 | | basilisk | karura | BSX | 117 | | basilisk | karura | aUSD | 118 | | basilisk | karura | KSM | 119 | | basilisk | karura | DAI | 120 | | basilisk | karura | USDCet | 121 | | basilisk | karura | WETH | 122 | | basilisk | karura | WBTC | 123 | | basilisk | assetHubKusama | USDT | 124 | | basilisk | tinkernet | TNKR | 125 | | basilisk | robonomics | XRT | 126 | | integritee | karura | TEER | 127 | | interlay | acala | INTR | 128 | | interlay | acala | IBTC | 129 | | interlay | astar | INTR | 130 | | interlay | astar | IBTC | 131 | | interlay | phala | PHA | 132 | | interlay | phala | INTR | 133 | | interlay | phala | IBTC | 134 | | interlay | polkadot | DOT | 135 | | interlay | assetHubPolkadot | USDC | 136 | | interlay | assetHubPolkadot | USDT | 137 | | interlay | hydradx | IBTC | 138 | | interlay | hydradx | INTR | 139 | | interlay | hydradx | HDX | 140 | | interlay | bifrostPolkadot | VDOT | 141 | | interlay | bifrostPolkadot | BNC | 142 | | kintsugi | karura | KINT | 143 | | kintsugi | karura | KBTC | 144 | | kintsugi | karura | LKSM | 145 | | kintsugi | kusama | KSM | 146 | | kintsugi | assetHubKusama | USDT | 147 | | kintsugi | bifrost | VKSM | 148 | | listen | karura | LT | 149 | | listen | karura | KAR | 150 | | listen | karura | KUSD | 151 | | listen | karura | LKSM | 152 | | calamari | karura | KMA | 153 | | calamari | karura | KUSD | 154 | | calamari | karura | KAR | 155 | | calamari | karura | LKSM | 156 | | calamari | karura | KSM | 157 | | moonbeam | acala | GLMR | 158 | | moonbeam | acala | xcACA | 159 | | moonbeam | acala | xcaUSD | 160 | | moonbeam | acala | xcDOT | 161 | | turing | karura | TUR | 162 | | turing | karura | KAR | 163 | | turing | karura | KUSD | 164 | | turing | karura | LKSM | 165 | | phala | interlay | PHA | 166 | | phala | interlay | IBTC | 167 | | phala | interlay | INTR | 168 | | khala | karura | PHA | 169 | | khala | karura | KUSD | 170 | | khala | karura | KAR | 171 | | polkadot | acala | DOT | 172 | | polkadot | hydradx | DOT | 173 | | polkadot | assetHubPolkadot | DOT | 174 | | polkadot | interlay | DOT | 175 | | kusama | karura | KSM | 176 | | kusama | basilisk | KSM | 177 | | kusama | assetHubKusama | KSM | 178 | | kusama | kintsugi | KSM | 179 | | subsocial | hydradx | SUB | 180 | | subsocial | moonbeam | SUB | 181 | | unique | acala | UNQ | 182 | | quartz | karura | QTZ | 183 | | zeitgeist | hydradx | ZTG | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@polkawallet/bridge", 3 | "version": "0.1.8", 4 | "description": "polkawallet bridge sdk", 5 | "main": "index.js", 6 | "types": "index.d.ts", 7 | "author": "Polkawallet developers ", 8 | "license": "Apache-2.0", 9 | "engines": { 10 | "yarn": "^1.10.1" 11 | }, 12 | "publishConfig": { 13 | "access": "public", 14 | "registry": "https://registry.npmjs.org" 15 | }, 16 | "repository": "https://github.com/polkawallet-io/bridge.git", 17 | "homepage": "https://github.com/polkawallet-io/bridge", 18 | "scripts": { 19 | "clean": "rm -rf build", 20 | "build": "tsc", 21 | "test": "jest --runInBand --detectOpenHandles --forceExit", 22 | "lint": "polkadot-dev-run-lint", 23 | "build:docs": "ts-node ./scripts/generate-bridge-document.ts", 24 | "build:release": "polkadot-ci-ghact-build" 25 | }, 26 | "peerDependencies": { 27 | "@acala-network/api": "^5", 28 | "@polkadot/api": "^14", 29 | "ethers": "^5" 30 | }, 31 | "resolutions": { 32 | "@acala-network/api": "^5.1.1", 33 | "@acala-network/sdk": "^4.1.9-13", 34 | "@acala-network/sdk-core": "^4.1.9-13", 35 | "@polkadot/api": "^14.0.1", 36 | "@polkadot/types": "^14.0.1" 37 | }, 38 | "dependencies": { 39 | "@acala-network/api": "^5", 40 | "@acala-network/sdk": "^4.1.9-13", 41 | "@acala-network/sdk-core": "^4.1.9-13", 42 | "@polkadot/api": "^14.0.1", 43 | "@polkadot/apps-config": "0.146.1", 44 | "@polkadot/keyring": "^13.2.3", 45 | "@polkadot/types": "^14.0.1", 46 | "@polkadot/util": "^13.2.3", 47 | "axios": "^0.27.2", 48 | "ethers": "^5", 49 | "lodash": "^4.17.20" 50 | }, 51 | "devDependencies": { 52 | "@acala-network/eth-providers": "^2.7.1", 53 | "@open-web3/dev-config": "^0.2.3", 54 | "@types/jest": "^28.1.1", 55 | "@types/lodash": "^4.14.161", 56 | "eslint-plugin-unused-imports": "^2.0.0", 57 | "jest": "^28.1.1", 58 | "ts-node": "^10.9.2", 59 | "typescript": "^4.7.4" 60 | }, 61 | "stableVersion": "0.1.8" 62 | } 63 | -------------------------------------------------------------------------------- /scripts/generate-bridge-document.ts: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import { traverseDir } from './traverse-dir'; 3 | import { flatten } from 'lodash'; 4 | import { writeFileSync, rmSync } from 'fs'; 5 | 6 | function template (from: string, to: string, token: string) { 7 | return `| ${from} | ${to} | ${token} |`; 8 | } 9 | 10 | function title () { 11 | return ` 12 | # Support Bridges 13 | 14 | ## Bridges 15 | 16 | | From | To | Token | 17 | | --- | --- | --- |` 18 | } 19 | 20 | function getSupportBridgesFiles () { 21 | const files: string[] = []; 22 | 23 | traverseDir( 24 | path.resolve(__dirname, '../src/adapters'), 25 | (path) => { 26 | return path.endsWith('ts') 27 | && !path.endsWith('configs.ts') 28 | && !path.endsWith('spec.ts'); 29 | }, 30 | (path) => { 31 | files.push(path); 32 | } 33 | ); 34 | 35 | return files; 36 | } 37 | 38 | function getRoutes (path: string) { 39 | const exports = require(path); 40 | 41 | const fileted = Object.entries(exports).filter(([k, v]) => { 42 | return k.endsWith('RouteConfigs'); 43 | }).map((i) => i[1]) as {from: string, to: string; token: string}[][]; 44 | 45 | return flatten(fileted); 46 | } 47 | 48 | function createSupportsaDocs (configs: {from: string, to: string; token: string}[][]) { 49 | const docs: string[] = []; 50 | 51 | docs.push(title()); 52 | 53 | configs.forEach((config) => { 54 | config.forEach((i) => { 55 | docs.push(template(i.from, i.to, i.token)); 56 | }); 57 | }); 58 | 59 | return docs.join('\n'); 60 | } 61 | 62 | // clean generated docs 63 | function clean () { 64 | const docs = [ 65 | path.resolve(__dirname, '../docs/support-bridges.md'), 66 | ]; 67 | 68 | docs.forEach((doc) => { 69 | rmSync(doc); 70 | }); 71 | } 72 | 73 | // generate support bridges file to SRC 74 | function writeSupportBridges (configs: {from: string, to: string; token: string}[][]) { 75 | writeFileSync(path.resolve(__dirname, '../src/support-bridges.ts'), 76 | ` 77 | // This file is generated by scripts/generate-bridge-document.ts 78 | export default ${JSON.stringify(configs, undefined, 2)} 79 | ` 80 | ) 81 | } 82 | 83 | 84 | function main () { 85 | clean(); 86 | 87 | console.log('generating support bridges docs...'); 88 | 89 | const files = getSupportBridgesFiles(); 90 | const configs = files.map(getRoutes).filter((i) => i.length > 0); 91 | const doc = createSupportsaDocs(configs); 92 | 93 | writeFileSync(path.resolve(__dirname, '../docs/support-bridges.md'), doc) 94 | writeSupportBridges(configs); 95 | 96 | console.log('done'); 97 | } 98 | 99 | main(); -------------------------------------------------------------------------------- /scripts/traverse-dir.ts: -------------------------------------------------------------------------------- 1 | import { readdirSync, statSync } from 'fs'; 2 | 3 | export function traverseDir ( 4 | dir: string, 5 | filter: (path: string) => boolean, 6 | callback: (path: string) => void 7 | ) { 8 | const files = readdirSync(dir); 9 | 10 | for (const file of files) { 11 | const path = `${dir}/${file}`; 12 | const stat = statSync(path); 13 | 14 | if (stat.isDirectory()) { 15 | traverseDir(path, filter, callback); 16 | } else if (filter(path)) { 17 | callback(path); 18 | } 19 | } 20 | } -------------------------------------------------------------------------------- /src/adapters/acala/acala-configs.ts: -------------------------------------------------------------------------------- 1 | import { createRouteConfigs } from "../../utils"; 2 | import { BasicToken, ExtendedToken } from "../../types"; 3 | 4 | export const acalaRouteConfigs = createRouteConfigs("acala", [ 5 | { 6 | to: "polkadot", 7 | token: "DOT", 8 | xcm: { 9 | fee: { token: "DOT", amount: "19978738" }, 10 | }, 11 | }, 12 | { 13 | to: "moonbeam", 14 | token: "GLMR", 15 | xcm: { 16 | fee: { token: "GLMR", amount: "8000000000000000" }, 17 | }, 18 | }, 19 | { 20 | to: "moonbeam", 21 | token: "ACA", 22 | xcm: { 23 | fee: { token: "ACA", amount: "24963428577" }, 24 | }, 25 | }, 26 | { 27 | to: "moonbeam", 28 | token: "AUSD", 29 | xcm: { 30 | fee: { token: "AUSD", amount: "2000000000" }, 31 | }, 32 | }, 33 | { 34 | to: "moonbeam", 35 | token: "DOT", 36 | xcm: { 37 | fee: { token: "DOT", amount: "447889166" }, 38 | }, 39 | }, 40 | { 41 | to: "moonbeam", 42 | token: "LDOT", 43 | xcm: { 44 | fee: { token: "LDOT", amount: "45977011" }, 45 | }, 46 | }, 47 | { 48 | to: "astar", 49 | token: "ASTR", 50 | xcm: { 51 | fee: { token: "ASTR", amount: "4006410300000000" }, 52 | }, 53 | }, 54 | { 55 | to: "astar", 56 | token: "ACA", 57 | xcm: { 58 | fee: { token: "ACA", amount: "1108000000" }, 59 | }, 60 | }, 61 | { 62 | to: "astar", 63 | token: "AUSD", 64 | xcm: { 65 | fee: { token: "AUSD", amount: "252800000" }, 66 | }, 67 | }, 68 | { 69 | to: "astar", 70 | token: "LDOT", 71 | xcm: { 72 | fee: { token: "LDOT", amount: "3692000" }, 73 | }, 74 | }, 75 | { 76 | to: "interlay", 77 | token: "INTR", 78 | xcm: { 79 | fee: { token: "INTR", amount: "20000000" }, 80 | }, 81 | }, 82 | { 83 | to: "interlay", 84 | token: "IBTC", 85 | xcm: { 86 | fee: { token: "IBTC", amount: "72" }, 87 | }, 88 | }, 89 | { 90 | to: "hydradx", 91 | token: "DAI", 92 | xcm: { 93 | fee: { token: "DAI", amount: "2926334210356268" }, 94 | }, 95 | }, 96 | { 97 | to: "hydradx", 98 | token: "DOT", 99 | xcm: { 100 | fee: { token: "DOT", amount: "491129243" }, 101 | }, 102 | }, 103 | { 104 | to: "hydradx", 105 | token: "WETH", 106 | xcm: { 107 | fee: { token: "WETH", amount: "956965470918" }, 108 | }, 109 | }, 110 | { 111 | to: "hydradx", 112 | token: "WBTC", 113 | xcm: { 114 | fee: { token: "WBTC", amount: "6" }, 115 | }, 116 | }, 117 | { 118 | to: "hydradx", 119 | token: "LDOT", 120 | xcm: { 121 | fee: { token: "LDOT", amount: "11516111" }, 122 | }, 123 | }, 124 | { 125 | to: "hydradx", 126 | token: "ACA", 127 | xcm: { 128 | fee: { token: "ACA", amount: "10429291793" }, 129 | }, 130 | }, 131 | { 132 | to: "unique", 133 | token: "UNQ", 134 | xcm: { 135 | fee: { token: "UNQ", amount: "" }, 136 | }, 137 | }, 138 | { 139 | to: "assetHubPolkadot", 140 | token: "USDT", 141 | xcm: { 142 | fee: { token: "DOT", amount: "160000000" }, 143 | }, 144 | }, 145 | { 146 | to: "assetHubPolkadot", 147 | token: "USDC", 148 | xcm: { 149 | fee: { token: "DOT", amount: "160000000" }, 150 | }, 151 | }, 152 | { 153 | to: "assetHubPolkadot", 154 | token: "PINK", 155 | xcm: { 156 | fee: { token: "DOT", amount: "160000000" }, 157 | }, 158 | }, 159 | { 160 | to: "assetHubPolkadot", 161 | token: "LOVA", 162 | xcm: { 163 | fee: { token: "DOT", amount: "160000000" }, 164 | }, 165 | }, 166 | { 167 | to: "assetHubPolkadot", 168 | token: "LOTY", 169 | xcm: { 170 | fee: { token: "DOT", amount: "160000000" }, 171 | }, 172 | }, 173 | { 174 | to: "assetHubPolkadot", 175 | token: "DAMN", 176 | xcm: { 177 | fee: { token: "DOT", amount: "160000000" }, 178 | }, 179 | }, 180 | ]); 181 | 182 | export const acalaTokensConfig: Record = { 183 | ACA: { 184 | name: "ACA", 185 | symbol: "ACA", 186 | decimals: 12, 187 | ed: "100000000000", 188 | toRaw: () => 189 | "0x0000000000000000000000000000000000000000000000000000000000000000", 190 | }, 191 | AUSD: { 192 | name: "AUSD", 193 | symbol: "AUSD", 194 | decimals: 12, 195 | ed: "100000000000", 196 | toRaw: () => 197 | "0x0001000000000000000000000000000000000000000000000000000000000000", 198 | }, 199 | LDOT: { 200 | name: "LDOT", 201 | symbol: "LDOT", 202 | decimals: 10, 203 | ed: "500000000", 204 | toRaw: () => 205 | "0x0003000000000000000000000000000000000000000000000000000000000000", 206 | }, 207 | INTR: { name: "INTR", symbol: "INTR", decimals: 10, ed: "1000000000" }, 208 | IBTC: { name: "IBTC", symbol: "IBTC", decimals: 8, ed: "100" }, 209 | GLMR: { 210 | name: "GLMR", 211 | symbol: "GLMR", 212 | decimals: 18, 213 | ed: "100000000000000000", 214 | toRaw: () => 215 | "0x0500000000000000000000000000000000000000000000000000000000000000", 216 | }, 217 | PARA: { name: "PARA", symbol: "PARA", decimals: 12, ed: "100000000000" }, 218 | ASTR: { 219 | name: "ASTR", 220 | symbol: "ASTR", 221 | decimals: 18, 222 | ed: "100000000000000000", 223 | }, 224 | DOT: { 225 | name: "DOT", 226 | symbol: "DOT", 227 | decimals: 10, 228 | ed: "100000000", 229 | toRaw: () => 230 | "0x0002000000000000000000000000000000000000000000000000000000000000", 231 | }, 232 | DAI: { 233 | name: "DAI", 234 | symbol: "DAI", 235 | decimals: 18, 236 | ed: "10000000000000000", 237 | }, 238 | WETH: { 239 | name: "WETH", 240 | symbol: "WETH", 241 | decimals: 18, 242 | ed: "5555555555555", 243 | }, 244 | WBTC: { 245 | name: "WBTC", 246 | symbol: "WBTC", 247 | decimals: 8, 248 | ed: "35", 249 | }, 250 | UNQ: { 251 | name: "Unique Network", 252 | symbol: "UNQ", 253 | decimals: 18, 254 | ed: "1250000000000000000", 255 | }, 256 | USDT: { 257 | name: "USDT", 258 | symbol: "USDT", 259 | decimals: 6, 260 | ed: "700000", 261 | }, 262 | USDC: { 263 | name: "USDC", 264 | symbol: "USDC", 265 | decimals: 6, 266 | ed: "10000", 267 | }, 268 | PINK: { 269 | name: "PINK", 270 | symbol: "PINK", 271 | decimals: 10, 272 | ed: "1000000000", 273 | }, 274 | LOVA: { 275 | name: "LOVA", 276 | symbol: "LOVA", 277 | decimals: 12, 278 | ed: "1", 279 | }, 280 | LOTY: { 281 | name: "LOTY", 282 | symbol: "LOTY", 283 | decimals: 12, 284 | ed: "100000000", 285 | }, 286 | DAMN: { 287 | name: "DAMN", 288 | symbol: "DAMN", 289 | decimals: 12, 290 | ed: "1", 291 | }, 292 | }; 293 | -------------------------------------------------------------------------------- /src/adapters/acala/acala.spec.ts: -------------------------------------------------------------------------------- 1 | import { FixedPointNumber } from "@acala-network/sdk-core"; 2 | import { Bridge } from "../../bridge"; 3 | import { KaruraAdapter } from "./acala"; 4 | import { KusamaAdapter } from "../polkadot"; 5 | import { MoonriverAdapter } from "../moonbeam"; 6 | import { AssetHubKusamaAdapter } from "../assethub"; 7 | import { SubmittableExtrinsic } from "@polkadot/api/types"; 8 | import { ISubmittableResult } from "@polkadot/types/types"; 9 | import { ApiPromise, WsProvider } from "@polkadot/api"; 10 | 11 | describe.skip("acala-adapter", () => { 12 | jest.setTimeout(50000); 13 | 14 | let bridge: Bridge; 15 | const address = "5GREeQcGHt7na341Py6Y6Grr38KUYRvVoiFSiDB52Gt7VZiN"; 16 | // the addressId is the address above in hex format 17 | const addressId = '0xc0997c4f2b3a83eb07ef74a867cf672a25a2a30cc61abc936dcc994df77ba84a' 18 | const moonbeamReceive = "0x46DBcbDe55be6cc4ce0C72C8d48BF61eb19D6be0"; 19 | 20 | const validateTx = ( 21 | tx: SubmittableExtrinsic<'rxjs', ISubmittableResult>, 22 | token: any, 23 | amount: string, 24 | destination: any, 25 | ) => { 26 | expect(tx.method.method).toEqual('transfer'); 27 | expect(tx.method.section).toEqual('xTokens'); 28 | expect(tx.args[0].toHuman()).toEqual(token); 29 | expect(tx.args[1].toString()).toEqual(amount); 30 | expect(tx.args[2].toHuman()).toEqual(destination); 31 | } 32 | 33 | 34 | beforeAll(async () => { 35 | const karura = new KaruraAdapter(); 36 | const kusama = new KusamaAdapter(); 37 | const moonriver = new MoonriverAdapter(); 38 | const assetHubKusama = new AssetHubKusamaAdapter(); 39 | 40 | const karuraApi = new ApiPromise({ provider: new WsProvider('wss://karura-rpc-1.aca-api.network') }); 41 | const kusmaApi = new ApiPromise({ provider: new WsProvider('wss://kusama-public-rpc.blockops.network/ws') }); 42 | const assetHubApi = new ApiPromise({ provider: new WsProvider('wss://statemine-rpc.dwellir.com') }); 43 | 44 | await karura.init(karuraApi); 45 | await kusama.init(kusmaApi); 46 | await assetHubKusama.init(assetHubApi); 47 | 48 | bridge = new Bridge({ 49 | adapters: [karura, kusama, moonriver, assetHubKusama], 50 | }); 51 | }); 52 | 53 | afterAll(async () => { 54 | for (const adapter of bridge.adapters) { 55 | const api = adapter.getApi(); 56 | 57 | if (api) { 58 | await api?.disconnect(); 59 | } 60 | } 61 | }); 62 | 63 | test('bridge sdk init should work', (done) => { 64 | expect(bridge).toBeDefined(); 65 | 66 | done(); 67 | }); 68 | 69 | test('transfer from karura to kusama should be ok', (done) => { 70 | const adapter = bridge.findAdapter('karura'); 71 | 72 | expect(adapter).toBeDefined(); 73 | 74 | const kusama = adapter.getToken('KSM'); 75 | const api = adapter.getApi(); 76 | 77 | // just for type check 78 | if (!api) return; 79 | 80 | const amount = new FixedPointNumber(1, kusama.decimals); 81 | const tx = adapter.createTx({ 82 | to: 'kusama', 83 | token: 'KSM', 84 | amount, 85 | address 86 | }); 87 | 88 | // DONT MODIFY THIS, THE OBJECT IS VALID, UNLESS YOU KNOW WHAT YOU ARE DOING 89 | const location = api.createType('XcmVersionedLocation', { 90 | V4: { 91 | parents: '1', 92 | interior: { X1: [{ AccountId32: { id: addressId, network: null } }] } 93 | } 94 | }); 95 | 96 | validateTx( 97 | tx as SubmittableExtrinsic<'rxjs', ISubmittableResult>, 98 | { Token: 'KSM' }, 99 | amount.toChainData(), 100 | location.toHuman() 101 | ); 102 | 103 | done(); 104 | }); 105 | 106 | test('tranfser from karura to moonbeam should be ok', (done) => { 107 | try { 108 | const adapter = bridge.findAdapter('karura'); 109 | 110 | expect(adapter).toBeDefined(); 111 | const movr = adapter.getToken('MOVR'); 112 | const api = adapter.getApi(); 113 | 114 | // just for type check 115 | if (!api) return; 116 | 117 | const amount = new FixedPointNumber(1, movr.decimals); 118 | const tx = adapter.createTx({ 119 | to: 'moonriver', 120 | token: 'MOVR', 121 | amount, 122 | address: moonbeamReceive 123 | }); 124 | 125 | // DONT MODIFY THIS, THE OBJECT IS VALID, UNLESS YOU KNOW WHAT YOU ARE DOING 126 | const location = api.createType('XcmVersionedLocation', { 127 | V3: { 128 | parents: "1", 129 | interior: { 130 | X2: [ 131 | { Parachain: "2023" }, 132 | { AccountKey20: { key: moonbeamReceive } } 133 | ] 134 | } 135 | } 136 | }) 137 | 138 | validateTx( 139 | tx as SubmittableExtrinsic<'rxjs', ISubmittableResult>, 140 | { ForeignAsset: "3" }, 141 | amount.toChainData(), 142 | location.toHuman() 143 | ); 144 | 145 | done(); 146 | 147 | } catch (e) { 148 | // ignore error 149 | } 150 | }); 151 | 152 | test('transfer from karura to asset hub should be ok', (done) => { 153 | try { 154 | 155 | const adapter = bridge.findAdapter('karura'); 156 | 157 | expect(adapter).toBeDefined(); 158 | 159 | const rmrk = adapter.getToken('RMRK'); 160 | const api = adapter.getApi(); 161 | 162 | // just for type check 163 | if (!api) return; 164 | 165 | const amount = new FixedPointNumber(1, rmrk.decimals); 166 | const tx = adapter.createTx({ 167 | to: 'assetHubKusama', 168 | token: 'RMRK', 169 | amount, 170 | address 171 | }); 172 | 173 | // DONT MODIFY THIS, THE OBJECT IS VALID, UNLESS YOU KNOW WHAT YOU ARE DOING 174 | const location = api.createType('XcmVersionedLocation', { 175 | V3: { 176 | parents: "1", 177 | interior: { 178 | X2: [ 179 | { Parachain: "1000" }, 180 | { AccountId32: { id: addressId } } 181 | ] 182 | } 183 | } 184 | }); 185 | 186 | // DONT MODIFY THIS, THE OBJECT IS VALID, UNLESS YOU KNOW WHAT YOU ARE DOING 187 | const assets = api.createType('XcmVersionedAsset', { 188 | V3: { 189 | fun: { 190 | Fungible: amount.toChainData(), 191 | }, 192 | id: { 193 | Concrete: { 194 | parents: 1, 195 | interior: { 196 | X3: [ 197 | { Parachain: "1000" }, 198 | { PalletInstance: 50 }, 199 | { GeneralIndex: 8 } 200 | ] 201 | } 202 | } 203 | } 204 | } 205 | }) 206 | 207 | expect(tx.method.method).toEqual('transferMultiasset'); 208 | expect(tx.method.section).toEqual('xTokens'); 209 | expect(tx.args[0].toHuman()).toEqual(assets.toHuman()); 210 | expect(tx.args[1].toHuman()).toEqual(location.toHuman()); 211 | 212 | done(); 213 | } catch (e) { 214 | // ignore error 215 | } 216 | }); 217 | }); 218 | -------------------------------------------------------------------------------- /src/adapters/acala/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./acala"; 2 | export * from "./acala-configs"; 3 | export * from "./karura-configs"; 4 | -------------------------------------------------------------------------------- /src/adapters/astar.spec.ts: -------------------------------------------------------------------------------- 1 | import { Bridge } from "../bridge"; 2 | import { AstarAdapter } from "./astar"; 3 | import { logFormatedRoute, formateRouteLogLine } from "../utils/unit-test"; 4 | import { ApiPromise, WsProvider } from "@polkadot/api"; 5 | import { FixedPointNumber } from "@acala-network/sdk-core"; 6 | 7 | describe.skip("astar adapter should work", () => { 8 | jest.setTimeout(300000); 9 | 10 | const address = "5GREeQcGHt7na341Py6Y6Grr38KUYRvVoiFSiDB52Gt7VZiN"; 11 | let bridge: Bridge; 12 | const outputSummary: string[] = []; 13 | 14 | beforeAll(async () => { 15 | const astar = new AstarAdapter(); 16 | 17 | const astarApi = new ApiPromise({ provider: new WsProvider("wss://rpc.astar.network") }); 18 | 19 | await astar.init(astarApi); 20 | 21 | bridge = new Bridge({ 22 | adapters: [astar], 23 | }); 24 | }); 25 | 26 | afterAll(async () => { 27 | for (const adapter of bridge.adapters) { 28 | const api = adapter.getApi(); 29 | 30 | if (api) { 31 | await api.disconnect(); 32 | } 33 | } 34 | 35 | await new Promise((resolve) => setTimeout(() => resolve(undefined), 5000)); 36 | 37 | logFormatedRoute("Astar summary:\n", outputSummary); 38 | }); 39 | 40 | test("bridge sdk init should work", (done) => { 41 | expect(bridge).toBeDefined(); 42 | 43 | done(); 44 | }); 45 | 46 | test("transfer tokens out of astar should work", (done) => { 47 | try { 48 | const adapter = bridge.findAdapter("astar"); 49 | expect(adapter).toBeDefined(); 50 | 51 | if (!adapter) return; 52 | 53 | const allRoutes = bridge.router.getAvailableRouters(); 54 | allRoutes.forEach((e) => { 55 | const token = adapter.getToken(e.token); 56 | 57 | const tx = adapter.createTx({ 58 | to: e.to.id, 59 | token: token.name, 60 | amount: new FixedPointNumber(1, token.decimals), 61 | address, 62 | }); 63 | 64 | expect(tx).toBeDefined(); 65 | 66 | const logRoute = formateRouteLogLine(e.token, e.from.display, e.to.display, "createTx"); 67 | logFormatedRoute("", [logRoute]); 68 | outputSummary.push(logRoute); 69 | }); 70 | 71 | done(); 72 | } catch (e) { 73 | // ignore error 74 | } 75 | }); 76 | }); 77 | -------------------------------------------------------------------------------- /src/adapters/basilisk.spec.ts: -------------------------------------------------------------------------------- 1 | import { Bridge } from "../bridge"; 2 | import { ApiPromise, WsProvider } from "@polkadot/api"; 3 | import { FixedPointNumber } from "@acala-network/sdk-core"; 4 | import { BasiliskAdapter } from "./hydradx"; 5 | import { formateRouteLogLine, logFormatedRoute } from "../utils/unit-test"; 6 | 7 | describe.skip("basilisk adapter should work", () => { 8 | jest.setTimeout(300000); 9 | 10 | const address = "5GREeQcGHt7na341Py6Y6Grr38KUYRvVoiFSiDB52Gt7VZiN"; 11 | let bridge: Bridge; 12 | const outputSummary: string[] = []; 13 | 14 | beforeAll(async () => { 15 | const basilisk = new BasiliskAdapter(); 16 | 17 | const basiliskApi = new ApiPromise({ provider: new WsProvider("wss://rpc.basilisk.cloud") }); 18 | 19 | await basilisk.init(basiliskApi); 20 | 21 | bridge = new Bridge({ adapters: [basilisk] }); 22 | }); 23 | 24 | afterAll(async () => { 25 | for (const adapter of bridge.adapters) { 26 | const api = adapter.getApi(); 27 | 28 | if (api) { 29 | await api.disconnect(); 30 | } 31 | } 32 | 33 | await new Promise((resolve) => setTimeout(() => resolve(undefined), 5000)); 34 | 35 | logFormatedRoute("Basilisk summary:\n", outputSummary); 36 | }); 37 | 38 | test("bridge sdk init should work", (done) => { 39 | expect(bridge).toBeDefined(); 40 | 41 | done(); 42 | }); 43 | 44 | test("transfer tokens out of balsilisk should work", (done) => { 45 | try { 46 | const adapter = bridge.findAdapter("basilisk"); 47 | expect(adapter).toBeDefined(); 48 | 49 | if (!adapter) return; 50 | 51 | const allRoutes = bridge.router.getAvailableRouters(); 52 | allRoutes.forEach((e) => { 53 | const token = adapter.getToken(e.token); 54 | 55 | const tx = adapter.createTx({ 56 | to: e.to.id, 57 | token: token.name, 58 | amount: new FixedPointNumber(1, token.decimals), 59 | address, 60 | }); 61 | 62 | expect(tx).toBeDefined(); 63 | 64 | const logRoute = formateRouteLogLine(e.token, e.from.display, e.to.display, "createTx"); 65 | logFormatedRoute("", [logRoute]); 66 | outputSummary.push(logRoute); 67 | }); 68 | 69 | done(); 70 | } catch (e) { 71 | // ignore error 72 | } 73 | }); 74 | }); 75 | -------------------------------------------------------------------------------- /src/adapters/bifrost.spec.ts: -------------------------------------------------------------------------------- 1 | import { Bridge } from "../bridge"; 2 | import { FixedPointNumber } from "@acala-network/sdk-core"; 3 | import { formateRouteLogLine, logFormatedRoute } from "../utils/unit-test"; 4 | import { BifrostAdapter } from "./bifrost"; 5 | import { ApiProvider } from "../api-provider"; 6 | import { firstValueFrom } from "rxjs"; 7 | 8 | describe.skip("bifrost adapter should work", () => { 9 | jest.setTimeout(300000); 10 | 11 | const address = "5GREeQcGHt7na341Py6Y6Grr38KUYRvVoiFSiDB52Gt7VZiN"; 12 | let bridge: Bridge; 13 | const provider = new ApiProvider(); 14 | const outputSummary: string[] = []; 15 | 16 | beforeAll(async () => { 17 | const bifrost = new BifrostAdapter(); 18 | 19 | await firstValueFrom(provider.connectFromChain(["bifrost"])); 20 | 21 | await bifrost.init(provider.getApi("bifrost")); 22 | 23 | bridge = new Bridge({ adapters: [bifrost] }); 24 | }); 25 | 26 | afterAll(async () => { 27 | for (const adapter of bridge.adapters) { 28 | const api = adapter.getApi(); 29 | 30 | if (api) { 31 | await api.disconnect(); 32 | } 33 | } 34 | 35 | await new Promise((resolve) => setTimeout(() => resolve(undefined), 5000)); 36 | 37 | logFormatedRoute("bifrost summary:\n", outputSummary); 38 | }); 39 | 40 | test("bridge sdk init should work", (done) => { 41 | expect(bridge).toBeDefined(); 42 | 43 | done(); 44 | }); 45 | 46 | test("transfer tokens out of bifrost should work", (done) => { 47 | try { 48 | const adapter = bridge.findAdapter("bifrost"); 49 | expect(adapter).toBeDefined(); 50 | 51 | if (!adapter) return; 52 | 53 | const allRoutes = bridge.router.getAvailableRouters(); 54 | allRoutes.forEach((e) => { 55 | const token = adapter.getToken(e.token); 56 | 57 | const tx = adapter.createTx({ 58 | to: e.to.id, 59 | token: token.name, 60 | amount: new FixedPointNumber(1, token.decimals), 61 | address, 62 | }); 63 | 64 | expect(tx).toBeDefined(); 65 | 66 | const logRoute = formateRouteLogLine(e.token, e.from.display, e.to.display, "createTx"); 67 | logFormatedRoute("", [logRoute]); 68 | outputSummary.push(logRoute); 69 | }); 70 | 71 | done(); 72 | } catch (e) { 73 | // ignore error 74 | } 75 | }); 76 | }); 77 | -------------------------------------------------------------------------------- /src/adapters/centrifuge.spec.ts: -------------------------------------------------------------------------------- 1 | import { Bridge } from "../bridge"; 2 | import { FixedPointNumber } from "@acala-network/sdk-core"; 3 | import { formateRouteLogLine, logFormatedRoute } from "../utils/unit-test"; 4 | import { AltairAdapter, CentrifugeAdapter } from "./centrifuge"; 5 | import { ApiProvider } from "../api-provider"; 6 | import { firstValueFrom } from "rxjs"; 7 | import { ChainId } from "../configs"; 8 | 9 | // TODO: centrifuge API can not connect in test, need to be fixed 10 | describe.skip("centrifuge adapter should work", () => { 11 | jest.setTimeout(300000); 12 | 13 | const address = "5GREeQcGHt7na341Py6Y6Grr38KUYRvVoiFSiDB52Gt7VZiN"; 14 | let bridge: Bridge; 15 | const provider = new ApiProvider(); 16 | const outputSummary: string[] = []; 17 | 18 | beforeAll(async () => { 19 | const centri = new CentrifugeAdapter(); 20 | const alt = new AltairAdapter(); 21 | 22 | await firstValueFrom(provider.connectFromChain(["centrifuge", "altair"])); 23 | 24 | await centri.init(provider.getApi("centrifuge")); 25 | await alt.init(provider.getApi("altair")); 26 | 27 | bridge = new Bridge({ adapters: [centri, alt] }); 28 | }); 29 | 30 | afterAll(async () => { 31 | for (const adapter of bridge.adapters) { 32 | const api = adapter.getApi(); 33 | 34 | if (api) { 35 | await api.disconnect(); 36 | } 37 | } 38 | 39 | await new Promise((resolve) => setTimeout(() => resolve(undefined), 5000)); 40 | 41 | logFormatedRoute("centrifuge/altair summary:\n", outputSummary); 42 | }); 43 | 44 | test("bridge sdk init should work", (done) => { 45 | expect(bridge).toBeDefined(); 46 | 47 | done(); 48 | }); 49 | 50 | ["centrifuge", "altair"].forEach((chain) => { 51 | test(`transfer tokens out of ${chain} should work`, (done) => { 52 | try { 53 | const adapter = bridge.findAdapter(chain as ChainId); 54 | expect(adapter).toBeDefined(); 55 | 56 | if (!adapter) return; 57 | 58 | const allRoutes = bridge.router.getAvailableRouters(); 59 | allRoutes.forEach((e) => { 60 | const token = adapter.getToken(e.token); 61 | 62 | const tx = adapter.createTx({ 63 | to: e.to.id, 64 | token: token.name, 65 | amount: new FixedPointNumber(1, token.decimals), 66 | address, 67 | }); 68 | 69 | expect(tx).toBeDefined(); 70 | 71 | const logRoute = formateRouteLogLine(e.token, e.from.display, e.to.display, "createTx"); 72 | logFormatedRoute("", [logRoute]); 73 | outputSummary.push(logRoute); 74 | }); 75 | 76 | done(); 77 | } catch (e) { 78 | // ignore error 79 | } 80 | }); 81 | }); 82 | }); 83 | -------------------------------------------------------------------------------- /src/adapters/centrifuge.ts: -------------------------------------------------------------------------------- 1 | import { Storage } from "@acala-network/sdk/utils/storage"; 2 | import { AnyApi, FixedPointNumber as FN } from "@acala-network/sdk-core"; 3 | import { combineLatest, map, Observable } from "rxjs"; 4 | 5 | import { SubmittableExtrinsic } from "@polkadot/api/types"; 6 | import { DeriveBalancesAll } from "@polkadot/api-derive/balances/types"; 7 | import { ISubmittableResult } from "@polkadot/types/types"; 8 | 9 | import { BalanceAdapter, BalanceAdapterConfigs } from "../balance-adapter"; 10 | import { BaseCrossChainAdapter } from "../base-chain-adapter"; 11 | import { ChainId, chains } from "../configs"; 12 | import { ApiNotFound, InvalidAddress } from "../errors"; 13 | import { BalanceData, ExtendedToken, TransferParams } from "../types"; 14 | import { createRouteConfigs, validateAddress } from "../utils"; 15 | 16 | const DEST_WEIGHT = "Unlimited"; 17 | 18 | const centrifugeRouteConfigs = createRouteConfigs("centrifuge", [ 19 | { 20 | to: "hydradx", 21 | token: "CFG", 22 | xcm: { 23 | fee: { token: "CFG", amount: "6373834498834048" }, 24 | weightLimit: DEST_WEIGHT, 25 | }, 26 | }, 27 | ]); 28 | 29 | export const centrifugeTokensonfigs: Record = { 30 | CFG: { 31 | name: "CFG", 32 | symbol: "CFG", 33 | decimals: 18, 34 | ed: "1000000000000", 35 | toRaw: () => "Native", 36 | }, 37 | }; 38 | 39 | const altairRouteConfigs = createRouteConfigs("altair", [ 40 | { 41 | to: "karura", 42 | token: "AIR", 43 | xcm: { 44 | fee: { token: "AIR", amount: "8082400000000000" }, 45 | weightLimit: DEST_WEIGHT, 46 | }, 47 | }, 48 | { 49 | to: "karura", 50 | token: "KUSD", 51 | xcm: { 52 | fee: { token: "KUSD", amount: "3481902463" }, 53 | weightLimit: DEST_WEIGHT, 54 | }, 55 | }, 56 | ]); 57 | 58 | export const altairTokensConfig: Record = { 59 | AIR: { 60 | name: "AIR", 61 | symbol: "AIR", 62 | decimals: 18, 63 | ed: "1000000000000", 64 | toRaw: () => "Native", 65 | }, 66 | KUSD: { 67 | name: "KUSD", 68 | symbol: "KUSD", 69 | decimals: 12, 70 | ed: "100000000000", 71 | toRaw: () => "AUSD", 72 | }, 73 | }; 74 | 75 | // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types 76 | const createBalanceStorages = (api: AnyApi) => { 77 | return { 78 | balances: (address: string) => 79 | Storage.create({ 80 | api, 81 | path: "derive.balances.all", 82 | params: [address], 83 | }), 84 | assets: (address: string, token: string) => 85 | Storage.create({ 86 | api, 87 | path: "query.ormlTokens.accounts", 88 | params: [address, token], 89 | }), 90 | }; 91 | }; 92 | 93 | class CentrifugeBalanceAdapter extends BalanceAdapter { 94 | private storages: ReturnType; 95 | 96 | constructor({ api, chain, tokens }: BalanceAdapterConfigs) { 97 | super({ api, chain, tokens }); 98 | this.storages = createBalanceStorages(api); 99 | } 100 | 101 | public subscribeBalance( 102 | name: string, 103 | address: string 104 | ): Observable { 105 | if (!validateAddress(address)) throw new InvalidAddress(address); 106 | 107 | const storage = this.storages.balances(address); 108 | 109 | if (name === this.nativeToken) { 110 | return storage.observable.pipe( 111 | map((data) => ({ 112 | free: FN.fromInner(data.freeBalance.toString(), this.decimals), 113 | locked: FN.fromInner(data.lockedBalance.toString(), this.decimals), 114 | reserved: FN.fromInner( 115 | data.reservedBalance.toString(), 116 | this.decimals 117 | ), 118 | available: FN.fromInner( 119 | data.availableBalance.toString(), 120 | this.decimals 121 | ), 122 | })) 123 | ); 124 | } 125 | 126 | const token = this.getToken(name); 127 | 128 | return this.storages.assets(address, token.toRaw()).observable.pipe( 129 | map((balance) => { 130 | const amount = FN.fromInner( 131 | balance.free?.toString() || "0", 132 | token.decimals 133 | ); 134 | 135 | return { 136 | free: amount, 137 | locked: new FN(0), 138 | reserved: new FN(0), 139 | available: amount, 140 | }; 141 | }) 142 | ); 143 | } 144 | } 145 | 146 | class BaseCentrifugeAdapter extends BaseCrossChainAdapter { 147 | private balanceAdapter?: CentrifugeBalanceAdapter; 148 | 149 | public async init(api: AnyApi) { 150 | this.api = api; 151 | 152 | await api.isReady; 153 | 154 | this.balanceAdapter = new CentrifugeBalanceAdapter({ 155 | chain: this.chain.id as ChainId, 156 | api, 157 | tokens: this.tokens, 158 | }); 159 | } 160 | 161 | public subscribeTokenBalance( 162 | token: string, 163 | address: string 164 | ): Observable { 165 | if (!this.balanceAdapter) { 166 | throw new ApiNotFound(this.chain.id); 167 | } 168 | 169 | return this.balanceAdapter.subscribeBalance(token, address); 170 | } 171 | 172 | public subscribeMaxInput( 173 | token: string, 174 | address: string, 175 | to: ChainId 176 | ): Observable { 177 | if (!this.balanceAdapter) { 178 | throw new ApiNotFound(this.chain.id); 179 | } 180 | 181 | return combineLatest({ 182 | txFee: 183 | token === this.balanceAdapter?.nativeToken 184 | ? this.estimateTxFee({ 185 | amount: FN.ZERO, 186 | to, 187 | token, 188 | address, 189 | signer: address, 190 | }) 191 | : "0", 192 | balance: this.balanceAdapter 193 | .subscribeBalance(token, address) 194 | .pipe(map((i) => i.available)), 195 | }).pipe( 196 | map(({ balance, txFee }) => { 197 | const tokenMeta = this.balanceAdapter?.getToken(token); 198 | const feeFactor = 1.2; 199 | const fee = FN.fromInner(txFee, tokenMeta?.decimals).mul( 200 | new FN(feeFactor) 201 | ); 202 | 203 | // always minus ed 204 | return balance 205 | .minus(fee) 206 | .minus(FN.fromInner(tokenMeta?.ed || "0", tokenMeta?.decimals)); 207 | }) 208 | ); 209 | } 210 | 211 | public createTx( 212 | params: TransferParams 213 | ): 214 | | SubmittableExtrinsic<"promise", ISubmittableResult> 215 | | SubmittableExtrinsic<"rxjs", ISubmittableResult> { 216 | return this.createXTokensTx(params); 217 | } 218 | } 219 | 220 | export class AltairAdapter extends BaseCentrifugeAdapter { 221 | constructor() { 222 | super(chains.altair, altairRouteConfigs, altairTokensConfig); 223 | } 224 | } 225 | 226 | export class CentrifugeAdapter extends BaseCentrifugeAdapter { 227 | constructor() { 228 | super(chains.centrifuge, centrifugeRouteConfigs, centrifugeTokensonfigs); 229 | } 230 | } 231 | -------------------------------------------------------------------------------- /src/adapters/crust.spec.ts: -------------------------------------------------------------------------------- 1 | import { Bridge } from "../bridge"; 2 | import { FixedPointNumber } from "@acala-network/sdk-core"; 3 | import { formateRouteLogLine, logFormatedRoute } from "../utils/unit-test"; 4 | import { ApiProvider } from "../api-provider"; 5 | import { firstValueFrom } from "rxjs"; 6 | import { ShadowAdapter } from "./crust"; 7 | 8 | describe.skip("shadow adapter should work", () => { 9 | jest.setTimeout(300000); 10 | 11 | const address = "5GREeQcGHt7na341Py6Y6Grr38KUYRvVoiFSiDB52Gt7VZiN"; 12 | let bridge: Bridge; 13 | const provider = new ApiProvider(); 14 | const outputSummary: string[] = []; 15 | 16 | beforeAll(async () => { 17 | const shadow = new ShadowAdapter(); 18 | 19 | await firstValueFrom(provider.connectFromChain(["shadow"])); 20 | 21 | await shadow.init(provider.getApi("shadow")); 22 | 23 | bridge = new Bridge({ adapters: [shadow] }); 24 | }); 25 | 26 | afterAll(async () => { 27 | for (const adapter of bridge.adapters) { 28 | const api = adapter.getApi(); 29 | 30 | if (api) { 31 | await api.disconnect(); 32 | } 33 | } 34 | 35 | await new Promise((resolve) => setTimeout(() => resolve(undefined), 5000)); 36 | 37 | logFormatedRoute("shadow summary:\n", outputSummary); 38 | }); 39 | 40 | test("bridge sdk init should work", (done) => { 41 | expect(bridge).toBeDefined(); 42 | 43 | done(); 44 | }); 45 | 46 | test("transfer tokens out of shadow should work", (done) => { 47 | try { 48 | const adapter = bridge.findAdapter("shadow"); 49 | expect(adapter).toBeDefined(); 50 | 51 | if (!adapter) return; 52 | 53 | const allRoutes = bridge.router.getAvailableRouters(); 54 | allRoutes.forEach((e) => { 55 | const token = adapter.getToken(e.token); 56 | 57 | const tx = adapter.createTx({ 58 | to: e.to.id, 59 | token: token.name, 60 | amount: new FixedPointNumber(1, token.decimals), 61 | address, 62 | }); 63 | 64 | expect(tx).toBeDefined(); 65 | 66 | const logRoute = formateRouteLogLine(e.token, e.from.display, e.to.display, "createTx"); 67 | logFormatedRoute("", [logRoute]); 68 | outputSummary.push(logRoute); 69 | }); 70 | 71 | done(); 72 | } catch (e) { 73 | // ignore error 74 | } 75 | }); 76 | }); 77 | -------------------------------------------------------------------------------- /src/adapters/crust.ts: -------------------------------------------------------------------------------- 1 | import { Storage } from "@acala-network/sdk/utils/storage"; 2 | import { AnyApi, FixedPointNumber as FN } from "@acala-network/sdk-core"; 3 | import { combineLatest, map, Observable } from "rxjs"; 4 | 5 | import { SubmittableExtrinsic } from "@polkadot/api/types"; 6 | import { DeriveBalancesAll } from "@polkadot/api-derive/balances/types"; 7 | import { ISubmittableResult } from "@polkadot/types/types"; 8 | 9 | import { BalanceAdapter, BalanceAdapterConfigs } from "../balance-adapter"; 10 | import { BaseCrossChainAdapter } from "../base-chain-adapter"; 11 | import { ChainId, chains } from "../configs"; 12 | import { ApiNotFound, TokenNotFound } from "../errors"; 13 | import { BalanceData, ExtendedToken, TransferParams } from "../types"; 14 | import { createRouteConfigs } from "../utils"; 15 | 16 | const DEST_WEIGHT = "5000000000"; 17 | 18 | const shadowRouteConfigs = createRouteConfigs("shadow", [ 19 | { 20 | to: "karura", 21 | token: "CSM", 22 | xcm: { 23 | fee: { token: "CSM", amount: "64000000000" }, 24 | weightLimit: DEST_WEIGHT, 25 | }, 26 | }, 27 | { 28 | to: "karura", 29 | token: "KAR", 30 | xcm: { 31 | fee: { token: "KAR", amount: "9324000000" }, 32 | weightLimit: DEST_WEIGHT, 33 | }, 34 | }, 35 | { 36 | to: "karura", 37 | token: "KUSD", 38 | xcm: { 39 | fee: { token: "KUSD", amount: "5693632140" }, 40 | weightLimit: DEST_WEIGHT, 41 | }, 42 | }, 43 | ]); 44 | 45 | export const shadowTokensConfig: Record = { 46 | CSM: { 47 | name: "CSM", 48 | symbol: "CSM", 49 | decimals: 12, 50 | ed: "100000000000", 51 | // just for type checking 52 | toRaw: () => "SelfReserve", 53 | }, 54 | KAR: { 55 | name: "KAR", 56 | symbol: "KAR", 57 | decimals: 12, 58 | ed: "1", 59 | toRaw: () => ({ OtherReserve: "10810581592933651521121702237638664357" }), 60 | }, 61 | KUSD: { 62 | name: "KUSD", 63 | symbol: "KUSD", 64 | decimals: 12, 65 | ed: "1", 66 | toRaw: () => ({ OtherReserve: "214920334981412447805621250067209749032" }), 67 | }, 68 | }; 69 | 70 | // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types 71 | const createBalanceStorages = (api: AnyApi) => { 72 | return { 73 | balances: (address: string) => 74 | Storage.create({ 75 | api, 76 | path: "derive.balances.all", 77 | params: [address], 78 | }), 79 | assets: (tokenId: string, address: string) => 80 | Storage.create({ 81 | api, 82 | path: "query.assets.account", 83 | params: [tokenId, address], 84 | }), 85 | }; 86 | }; 87 | 88 | class CrustBalanceAdapter extends BalanceAdapter { 89 | private storages: ReturnType; 90 | 91 | constructor({ api, chain, tokens }: BalanceAdapterConfigs) { 92 | super({ api, chain, tokens }); 93 | this.storages = createBalanceStorages(api); 94 | } 95 | 96 | public subscribeBalance( 97 | token: string, 98 | address: string 99 | ): Observable { 100 | const storage = this.storages.balances(address); 101 | 102 | if (token === this.nativeToken) { 103 | return storage.observable.pipe( 104 | map((data) => ({ 105 | free: FN.fromInner(data.freeBalance.toString(), this.decimals), 106 | locked: FN.fromInner(data.lockedBalance.toString(), this.decimals), 107 | reserved: FN.fromInner( 108 | data.reservedBalance.toString(), 109 | this.decimals 110 | ), 111 | available: FN.fromInner( 112 | data.availableBalance.toString(), 113 | this.decimals 114 | ), 115 | })) 116 | ); 117 | } 118 | 119 | const tokenData: ExtendedToken = this.getToken(token); 120 | 121 | if (!tokenData) throw new TokenNotFound(token); 122 | 123 | return this.storages.assets(tokenData.toRaw(), address).observable.pipe( 124 | map((balance) => { 125 | const amount = FN.fromInner( 126 | balance.unwrapOrDefault()?.balance?.toString() || "0", 127 | this.getToken(token).decimals 128 | ); 129 | 130 | return { 131 | free: amount, 132 | locked: new FN(0), 133 | reserved: new FN(0), 134 | available: amount, 135 | }; 136 | }) 137 | ); 138 | } 139 | } 140 | 141 | class BaseCrustAdapter extends BaseCrossChainAdapter { 142 | private balanceAdapter?: CrustBalanceAdapter; 143 | 144 | public async init(api: AnyApi) { 145 | this.api = api; 146 | 147 | await api.isReady; 148 | 149 | this.balanceAdapter = new CrustBalanceAdapter({ 150 | chain: this.chain.id as ChainId, 151 | api, 152 | tokens: shadowTokensConfig, 153 | }); 154 | } 155 | 156 | public subscribeTokenBalance( 157 | token: string, 158 | address: string 159 | ): Observable { 160 | if (!this.balanceAdapter) { 161 | throw new ApiNotFound(this.chain.id); 162 | } 163 | 164 | return this.balanceAdapter.subscribeBalance(token, address); 165 | } 166 | 167 | public subscribeMaxInput( 168 | token: string, 169 | address: string, 170 | to: ChainId 171 | ): Observable { 172 | if (!this.balanceAdapter) { 173 | throw new ApiNotFound(this.chain.id); 174 | } 175 | 176 | return combineLatest({ 177 | txFee: 178 | token === this.balanceAdapter?.nativeToken 179 | ? this.estimateTxFee({ 180 | amount: FN.ZERO, 181 | to, 182 | token, 183 | address, 184 | signer: address, 185 | }) 186 | : "0", 187 | balance: this.balanceAdapter 188 | .subscribeBalance(token, address) 189 | .pipe(map((i) => i.available)), 190 | }).pipe( 191 | map(({ balance, txFee }) => { 192 | const tokenMeta = this.balanceAdapter?.getToken(token); 193 | const feeFactor = 1.2; 194 | const fee = FN.fromInner(txFee, tokenMeta?.decimals).mul( 195 | new FN(feeFactor) 196 | ); 197 | 198 | // always minus ed 199 | return balance 200 | .minus(fee) 201 | .minus(FN.fromInner(tokenMeta?.ed || "0", tokenMeta?.decimals)); 202 | }) 203 | ); 204 | } 205 | 206 | public createTx( 207 | params: TransferParams 208 | ): 209 | | SubmittableExtrinsic<"promise", ISubmittableResult> 210 | | SubmittableExtrinsic<"rxjs", ISubmittableResult> { 211 | return this.createXTokensTx(params); 212 | } 213 | } 214 | 215 | export class ShadowAdapter extends BaseCrustAdapter { 216 | constructor() { 217 | super(chains.shadow, shadowRouteConfigs, shadowTokensConfig); 218 | } 219 | } 220 | -------------------------------------------------------------------------------- /src/adapters/darwinia.spec.ts: -------------------------------------------------------------------------------- 1 | import { Bridge } from "../bridge"; 2 | import { FixedPointNumber } from "@acala-network/sdk-core"; 3 | import { formateRouteLogLine, logFormatedRoute } from "../utils/unit-test"; 4 | import { ApiProvider } from "../api-provider"; 5 | import { firstValueFrom } from "rxjs"; 6 | import { CrabAdapter } from "./darwinia"; 7 | 8 | describe("crab adapter should work", () => { 9 | jest.setTimeout(300000); 10 | 11 | const address = "5GREeQcGHt7na341Py6Y6Grr38KUYRvVoiFSiDB52Gt7VZiN"; 12 | let bridge: Bridge; 13 | const provider = new ApiProvider(); 14 | const outputSummary: string[] = []; 15 | 16 | beforeAll(async () => { 17 | const crab = new CrabAdapter(); 18 | 19 | await firstValueFrom(provider.connectFromChain(["crab"])); 20 | 21 | await crab.init(provider.getApi("crab")); 22 | 23 | console.log("init crab adapter done"); 24 | 25 | bridge = new Bridge({ adapters: [crab] }); 26 | }); 27 | 28 | afterAll(async () => { 29 | for (const adapter of bridge.adapters) { 30 | const api = adapter.getApi(); 31 | 32 | if (api) { 33 | await api.disconnect(); 34 | } 35 | } 36 | 37 | await new Promise((resolve) => setTimeout(() => resolve(undefined), 5000)); 38 | 39 | logFormatedRoute("crab summary:\n", outputSummary); 40 | }); 41 | 42 | test("bridge sdk init should work", (done) => { 43 | expect(bridge).toBeDefined(); 44 | 45 | done(); 46 | }); 47 | 48 | test("transfer tokens out of crab should work", (done) => { 49 | try { 50 | const adapter = bridge.findAdapter("crab"); 51 | expect(adapter).toBeDefined(); 52 | 53 | if (!adapter) return; 54 | 55 | const allRoutes = bridge.router.getAvailableRouters(); 56 | allRoutes.forEach((e) => { 57 | const token = adapter.getToken(e.token); 58 | 59 | const tx = adapter.createTx({ 60 | to: e.to.id, 61 | token: token.name, 62 | amount: new FixedPointNumber(1, token.decimals), 63 | address, 64 | }); 65 | 66 | expect(tx).toBeDefined(); 67 | 68 | const logRoute = formateRouteLogLine( 69 | e.token, 70 | e.from.display, 71 | e.to.display, 72 | "createTx" 73 | ); 74 | logFormatedRoute("", [logRoute]); 75 | outputSummary.push(logRoute); 76 | }); 77 | 78 | done(); 79 | } catch (e) { 80 | // ignore error 81 | } 82 | }); 83 | }); 84 | -------------------------------------------------------------------------------- /src/adapters/darwinia.ts: -------------------------------------------------------------------------------- 1 | import { Storage } from "@acala-network/sdk/utils/storage"; 2 | import { AnyApi, FixedPointNumber as FN } from "@acala-network/sdk-core"; 3 | import { combineLatest, map, Observable } from "rxjs"; 4 | 5 | import { SubmittableExtrinsic } from "@polkadot/api/types"; 6 | import { DeriveBalancesAll } from "@polkadot/api-derive/balances/types"; 7 | import { ISubmittableResult } from "@polkadot/types/types"; 8 | 9 | import { BalanceAdapter, BalanceAdapterConfigs } from "../balance-adapter"; 10 | import { BaseCrossChainAdapter } from "../base-chain-adapter"; 11 | import { ChainId, chains } from "../configs"; 12 | import { ApiNotFound, InvalidAddress, TokenNotFound } from "../errors"; 13 | import { BalanceData, BasicToken, TransferParams } from "../types"; 14 | import { 15 | createPolkadotXCMAccount, 16 | createPolkadotXCMAsset, 17 | createPolkadotXCMDest, 18 | createRouteConfigs, 19 | validateAddress, 20 | getDestAccountInfo, 21 | } from "../utils"; 22 | 23 | const crabRouteConfigs = createRouteConfigs("crab", [ 24 | { 25 | to: "karura", 26 | token: "CRAB", 27 | xcm: { 28 | fee: { token: "CRAB", amount: "64000000000000000" }, 29 | weightLimit: "Unlimited", 30 | }, 31 | }, 32 | ]); 33 | 34 | export const crabTokensConfig: Record = { 35 | CRAB: { name: "CRAB", symbol: "CRAB", decimals: 18, ed: "0" }, 36 | }; 37 | 38 | // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types 39 | const createBalanceStorages = (api: AnyApi) => { 40 | return { 41 | balances: (address: string) => 42 | Storage.create({ 43 | api, 44 | path: "derive.balances.all", 45 | params: [address], 46 | }), 47 | }; 48 | }; 49 | 50 | class DarwiniaBalanceAdapter extends BalanceAdapter { 51 | private storages: ReturnType; 52 | 53 | constructor({ api, chain, tokens }: BalanceAdapterConfigs) { 54 | super({ api, chain, tokens }); 55 | this.storages = createBalanceStorages(api); 56 | } 57 | 58 | public subscribeBalance( 59 | token: string, 60 | address: string 61 | ): Observable { 62 | if (!validateAddress(address)) throw new InvalidAddress(address); 63 | 64 | const storage = this.storages.balances(address); 65 | 66 | if (token !== this.nativeToken) { 67 | throw new TokenNotFound(token); 68 | } 69 | 70 | return storage.observable.pipe( 71 | map((data) => ({ 72 | free: FN.fromInner(data.freeBalance.toString(), this.decimals), 73 | locked: FN.fromInner(data.lockedBalance.toString(), this.decimals), 74 | reserved: FN.fromInner(data.reservedBalance.toString(), this.decimals), 75 | available: FN.fromInner( 76 | data.availableBalance.toString(), 77 | this.decimals 78 | ), 79 | })) 80 | ); 81 | } 82 | } 83 | 84 | class BaseDarwiniaAdapter extends BaseCrossChainAdapter { 85 | private balanceAdapter?: DarwiniaBalanceAdapter; 86 | 87 | public async init(api: AnyApi) { 88 | this.api = api; 89 | 90 | await api.isReady; 91 | 92 | this.balanceAdapter = new DarwiniaBalanceAdapter({ 93 | chain: this.chain.id as ChainId, 94 | api, 95 | tokens: crabTokensConfig, 96 | }); 97 | } 98 | 99 | public subscribeTokenBalance( 100 | token: string, 101 | address: string 102 | ): Observable { 103 | if (!this.balanceAdapter) { 104 | throw new ApiNotFound(this.chain.id); 105 | } 106 | 107 | return this.balanceAdapter.subscribeBalance(token, address); 108 | } 109 | 110 | public subscribeMaxInput( 111 | token: string, 112 | address: string, 113 | to: ChainId 114 | ): Observable { 115 | if (!this.balanceAdapter) { 116 | throw new ApiNotFound(this.chain.id); 117 | } 118 | 119 | return combineLatest({ 120 | txFee: 121 | token === this.balanceAdapter?.nativeToken 122 | ? this.estimateTxFee({ 123 | amount: FN.ONE, 124 | to, 125 | token, 126 | address, 127 | signer: address, 128 | }) 129 | : "0", 130 | balance: this.balanceAdapter 131 | .subscribeBalance(token, address) 132 | .pipe(map((i) => i.available)), 133 | }).pipe( 134 | map(({ balance, txFee }) => { 135 | const tokenMeta = this.balanceAdapter?.getToken(token); 136 | const feeFactor = 1.2; 137 | const fee = FN.fromInner(txFee, tokenMeta?.decimals).mul( 138 | new FN(feeFactor) 139 | ); 140 | 141 | // always minus ed 142 | return balance 143 | .minus(fee) 144 | .minus(FN.fromInner(tokenMeta?.ed || "0", tokenMeta?.decimals)); 145 | }) 146 | ); 147 | } 148 | 149 | public createTx( 150 | params: TransferParams 151 | ): 152 | | SubmittableExtrinsic<"promise", ISubmittableResult> 153 | | SubmittableExtrinsic<"rxjs", ISubmittableResult> { 154 | if (!this.api) throw new ApiNotFound(this.chain.id); 155 | 156 | const { address, amount, to, token } = params; 157 | 158 | const { accountId, accountType, addrType } = getDestAccountInfo( 159 | address, 160 | token, 161 | this.api, 162 | to 163 | ); 164 | 165 | if (!validateAddress(address, addrType)) throw new InvalidAddress(address); 166 | 167 | const toChain = chains[to]; 168 | 169 | // only support native token 170 | if (token !== this.balanceAdapter?.nativeToken) { 171 | throw new TokenNotFound(token); 172 | } 173 | 174 | const parachainId = toChain.paraChainId; 175 | const rawAmount = amount.toChainData(); 176 | 177 | return this.api?.tx.polkadotXcm.limitedReserveTransferAssets( 178 | createPolkadotXCMDest(this.api, parachainId, 1, "V4"), 179 | createPolkadotXCMAccount(this.api, accountId, accountType, "V4"), 180 | createPolkadotXCMAsset(this.api, rawAmount, "NATIVE", "V4"), 181 | 0, 182 | this.getDestWeight(token, to)?.toString() as any 183 | ); 184 | } 185 | } 186 | 187 | export class CrabAdapter extends BaseDarwiniaAdapter { 188 | constructor() { 189 | super(chains.crab, crabRouteConfigs, crabTokensConfig); 190 | } 191 | } 192 | -------------------------------------------------------------------------------- /src/adapters/hydradx.spec.ts: -------------------------------------------------------------------------------- 1 | import { Bridge } from "../bridge"; 2 | import { ApiPromise, WsProvider } from "@polkadot/api"; 3 | import { FixedPointNumber } from "@acala-network/sdk-core"; 4 | import { HydraDxAdapter } from "./hydradx"; 5 | import { formateRouteLogLine, logFormatedRoute } from "../utils/unit-test"; 6 | 7 | describe.skip("hydradx adapter should work", () => { 8 | jest.setTimeout(300000); 9 | 10 | const address = "5GREeQcGHt7na341Py6Y6Grr38KUYRvVoiFSiDB52Gt7VZiN"; 11 | let bridge: Bridge; 12 | const outputSummary: string[] = []; 13 | 14 | beforeAll(async () => { 15 | const hydradx = new HydraDxAdapter(); 16 | 17 | const hydradxApi = new ApiPromise({ provider: new WsProvider("wss://hydradx.api.onfinality.io/public-ws") }); 18 | 19 | await hydradx.init(hydradxApi); 20 | 21 | bridge = new Bridge({ adapters: [hydradx] }); 22 | }); 23 | 24 | afterAll(async () => { 25 | for (const adapter of bridge.adapters) { 26 | const api = adapter.getApi(); 27 | 28 | if (api) { 29 | await api.disconnect(); 30 | } 31 | } 32 | }); 33 | 34 | test("bridge sdk init should work", (done) => { 35 | expect(bridge).toBeDefined(); 36 | 37 | done(); 38 | }); 39 | 40 | test("transfer tokens out of hydradx should work", (done) => { 41 | try { 42 | const adapter = bridge.findAdapter("hydradx"); 43 | expect(adapter).toBeDefined(); 44 | 45 | if (!adapter) return; 46 | 47 | const allRoutes = bridge.router.getAvailableRouters(); 48 | allRoutes.forEach((e) => { 49 | const token = adapter.getToken(e.token); 50 | 51 | const tx = adapter.createTx({ 52 | to: e.to.id, 53 | token: token.name, 54 | amount: new FixedPointNumber(1, token.decimals), 55 | address, 56 | }); 57 | 58 | expect(tx).toBeDefined(); 59 | 60 | const logRoute = formateRouteLogLine(e.token, e.from.display, e.to.display, "createTx"); 61 | logFormatedRoute("", [logRoute]); 62 | outputSummary.push(logRoute); 63 | }); 64 | 65 | done(); 66 | } catch (e) { 67 | // ignore error 68 | } 69 | }); 70 | }); 71 | -------------------------------------------------------------------------------- /src/adapters/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./"; 2 | -------------------------------------------------------------------------------- /src/adapters/integritee.spec.ts: -------------------------------------------------------------------------------- 1 | import { Bridge } from "../bridge"; 2 | import { FixedPointNumber } from "@acala-network/sdk-core"; 3 | import { formateRouteLogLine, logFormatedRoute } from "../utils/unit-test"; 4 | import { ApiProvider } from "../api-provider"; 5 | import { firstValueFrom } from "rxjs"; 6 | import { IntegriteeAdapter } from "./integritee"; 7 | 8 | describe.skip("integritee adapter should work", () => { 9 | jest.setTimeout(300000); 10 | 11 | const address = "5GREeQcGHt7na341Py6Y6Grr38KUYRvVoiFSiDB52Gt7VZiN"; 12 | let bridge: Bridge; 13 | const provider = new ApiProvider(); 14 | const outputSummary: string[] = []; 15 | 16 | beforeAll(async () => { 17 | const integritee = new IntegriteeAdapter(); 18 | 19 | await firstValueFrom(provider.connectFromChain(["integritee"])); 20 | 21 | await integritee.init(provider.getApi("integritee")); 22 | 23 | bridge = new Bridge({ adapters: [integritee] }); 24 | }); 25 | 26 | afterAll(async () => { 27 | for (const adapter of bridge.adapters) { 28 | const api = adapter.getApi(); 29 | 30 | if (api) { 31 | await api.disconnect(); 32 | } 33 | } 34 | 35 | await new Promise((resolve) => setTimeout(() => resolve(undefined), 5000)); 36 | 37 | logFormatedRoute("integritee summary:\n", outputSummary); 38 | }); 39 | 40 | test("bridge sdk init should work", (done) => { 41 | expect(bridge).toBeDefined(); 42 | 43 | done(); 44 | }); 45 | 46 | test("transfer tokens out of integritee should work", (done) => { 47 | try { 48 | const adapter = bridge.findAdapter("integritee"); 49 | expect(adapter).toBeDefined(); 50 | 51 | if (!adapter) return; 52 | 53 | const allRoutes = bridge.router.getAvailableRouters(); 54 | allRoutes.forEach((e) => { 55 | const token = adapter.getToken(e.token); 56 | 57 | const tx = adapter.createTx({ 58 | to: e.to.id, 59 | token: token.name, 60 | amount: new FixedPointNumber(1, token.decimals), 61 | address, 62 | }); 63 | 64 | expect(tx).toBeDefined(); 65 | 66 | const logRoute = formateRouteLogLine(e.token, e.from.display, e.to.display, "createTx"); 67 | logFormatedRoute("", [logRoute]); 68 | outputSummary.push(logRoute); 69 | }); 70 | 71 | done(); 72 | } catch (e) { 73 | // ignore error 74 | } 75 | }); 76 | }); 77 | -------------------------------------------------------------------------------- /src/adapters/integritee.ts: -------------------------------------------------------------------------------- 1 | import { Storage } from "@acala-network/sdk/utils/storage"; 2 | import { AnyApi, FixedPointNumber as FN } from "@acala-network/sdk-core"; 3 | import { combineLatest, map, Observable } from "rxjs"; 4 | 5 | import { SubmittableExtrinsic } from "@polkadot/api/types"; 6 | import { DeriveBalancesAll } from "@polkadot/api-derive/balances/types"; 7 | import { ISubmittableResult } from "@polkadot/types/types"; 8 | 9 | import { BalanceAdapter, BalanceAdapterConfigs } from "../balance-adapter"; 10 | import { BaseCrossChainAdapter } from "../base-chain-adapter"; 11 | import { ChainId, chains } from "../configs"; 12 | import { ApiNotFound, TokenNotFound } from "../errors"; 13 | import { BalanceData, ExtendedToken, TransferParams } from "../types"; 14 | import { createRouteConfigs } from "../utils"; 15 | 16 | export const integriteeRouteConfigs = createRouteConfigs("integritee", [ 17 | { 18 | to: "karura", 19 | token: "TEER", 20 | xcm: { 21 | fee: { token: "TEER", amount: "6400000000" }, 22 | weightLimit: "5000000000", 23 | }, 24 | }, 25 | ]); 26 | 27 | export const integriteeTokensConfig: Record = { 28 | TEER: { 29 | name: "TEER", 30 | symbol: "TEER", 31 | decimals: 12, 32 | ed: "100000000000", 33 | toRaw: () => "TEER", 34 | }, 35 | }; 36 | 37 | // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types 38 | const createBalanceStorages = (api: AnyApi) => { 39 | return { 40 | balances: (address: string) => 41 | Storage.create({ 42 | api, 43 | path: "derive.balances.all", 44 | params: [address], 45 | }), 46 | }; 47 | }; 48 | 49 | class IntegriteeBalanceAdapter extends BalanceAdapter { 50 | private storages: ReturnType; 51 | 52 | constructor({ api, chain, tokens }: BalanceAdapterConfigs) { 53 | super({ api, chain, tokens }); 54 | this.storages = createBalanceStorages(api); 55 | } 56 | 57 | public subscribeBalance( 58 | token: string, 59 | address: string 60 | ): Observable { 61 | const storage = this.storages.balances(address); 62 | 63 | if (token !== this.nativeToken) { 64 | throw new TokenNotFound(token); 65 | } 66 | 67 | return storage.observable.pipe( 68 | map((data) => ({ 69 | free: FN.fromInner(data.freeBalance.toString(), this.decimals), 70 | locked: FN.fromInner(data.lockedBalance.toString(), this.decimals), 71 | reserved: FN.fromInner(data.reservedBalance.toString(), this.decimals), 72 | available: FN.fromInner( 73 | data.availableBalance.toString(), 74 | this.decimals 75 | ), 76 | })) 77 | ); 78 | } 79 | } 80 | 81 | class BaseIntegriteeAdapter extends BaseCrossChainAdapter { 82 | private balanceAdapter?: IntegriteeBalanceAdapter; 83 | 84 | public async init(api: AnyApi) { 85 | this.api = api; 86 | 87 | await api.isReady; 88 | 89 | this.balanceAdapter = new IntegriteeBalanceAdapter({ 90 | chain: this.chain.id as ChainId, 91 | api, 92 | tokens: integriteeTokensConfig, 93 | }); 94 | } 95 | 96 | public subscribeTokenBalance( 97 | token: string, 98 | address: string 99 | ): Observable { 100 | if (!this.balanceAdapter) { 101 | throw new ApiNotFound(this.chain.id); 102 | } 103 | 104 | return this.balanceAdapter.subscribeBalance(token, address); 105 | } 106 | 107 | public subscribeMaxInput( 108 | token: string, 109 | address: string, 110 | to: ChainId 111 | ): Observable { 112 | if (!this.balanceAdapter) { 113 | throw new ApiNotFound(this.chain.id); 114 | } 115 | 116 | return combineLatest({ 117 | txFee: 118 | token === this.balanceAdapter?.nativeToken 119 | ? this.estimateTxFee({ 120 | amount: FN.ZERO, 121 | to, 122 | token, 123 | address, 124 | signer: address, 125 | }) 126 | : "0", 127 | balance: this.balanceAdapter 128 | .subscribeBalance(token, address) 129 | .pipe(map((i) => i.available)), 130 | }).pipe( 131 | map(({ balance, txFee }) => { 132 | const tokenMeta = this.balanceAdapter?.getToken(token); 133 | const feeFactor = 1.2; 134 | const fee = FN.fromInner(txFee, tokenMeta?.decimals).mul( 135 | new FN(feeFactor) 136 | ); 137 | 138 | // always minus ed 139 | return balance 140 | .minus(fee) 141 | .minus(FN.fromInner(tokenMeta?.ed || "0", tokenMeta?.decimals)); 142 | }) 143 | ); 144 | } 145 | 146 | public createTx( 147 | params: TransferParams 148 | ): 149 | | SubmittableExtrinsic<"promise", ISubmittableResult> 150 | | SubmittableExtrinsic<"rxjs", ISubmittableResult> { 151 | return this.createXTokensTx(params); 152 | } 153 | } 154 | 155 | export class IntegriteeAdapter extends BaseIntegriteeAdapter { 156 | constructor() { 157 | super(chains.integritee, integriteeRouteConfigs, integriteeTokensConfig); 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /src/adapters/moonbeam.spec.ts: -------------------------------------------------------------------------------- 1 | import { Bridge } from "../bridge"; 2 | import { logFormatedRoute, formateRouteLogLine } from "../utils/unit-test"; 3 | import { FixedPointNumber } from "@acala-network/sdk-core"; 4 | import { ApiPromise, WsProvider } from "@polkadot/api"; 5 | import { MoonbeamAdapter } from "./moonbeam"; 6 | import { AcalaAdapter } from "./acala/acala"; 7 | 8 | describe.skip("moonbeam adapter should work", () => { 9 | jest.setTimeout(300000); 10 | 11 | let bridge: Bridge; 12 | const outputSummary: string[] = []; 13 | 14 | beforeAll(async () => { 15 | const moonbeam = new MoonbeamAdapter(); 16 | 17 | const moonbeamApi = new ApiPromise({ provider: new WsProvider("wss://moonbeam-rpc.dwellir.com") }); 18 | 19 | await moonbeam.init(moonbeamApi); 20 | 21 | const acala = new AcalaAdapter(); 22 | 23 | const acalaApi = new ApiPromise({ provider: new WsProvider("wss://acala-rpc.dwellir.com") }); 24 | 25 | await acala.init(acalaApi); 26 | 27 | bridge = new Bridge({ 28 | adapters: [moonbeam, acala], 29 | }); 30 | }); 31 | 32 | afterAll(async () => { 33 | for (const adapter of bridge.adapters) { 34 | const api = adapter.getApi(); 35 | 36 | if (api) { 37 | await api.disconnect(); 38 | } 39 | } 40 | 41 | await new Promise((resolve) => setTimeout(() => resolve(undefined), 5000)); 42 | logFormatedRoute("Moonbeam summary:\n", outputSummary || []); 43 | }); 44 | 45 | test("bridge sdk init should work", (done) => { 46 | expect(bridge).toBeDefined(); 47 | 48 | done(); 49 | }); 50 | 51 | test("transfer tokens from moonbeam should work", (done) => { 52 | try { 53 | const adapter = bridge.findAdapter("moonbeam"); 54 | expect(adapter).toBeDefined(); 55 | 56 | if (!adapter) return; 57 | 58 | const allRoutes = bridge.router.getAvailableRouters(); 59 | allRoutes.forEach((e) => { 60 | const token = adapter.getToken(e.token); 61 | 62 | const tx = adapter.createTx({ 63 | to: e.to.id, 64 | token: token.name, 65 | amount: new FixedPointNumber(1, token.decimals), 66 | address: "5GREeQcGHt7na341Py6Y6Grr38KUYRvVoiFSiDB52Gt7VZiN", 67 | }); 68 | 69 | expect(tx).toBeDefined(); 70 | 71 | const logRoute = formateRouteLogLine(e.token, e.from.display, e.to.display, "createTx"); 72 | logFormatedRoute("", [logRoute]); 73 | outputSummary.push(logRoute); 74 | }); 75 | 76 | done(); 77 | } catch (e) { 78 | // ignore error 79 | console.log(e); 80 | } 81 | }); 82 | }); 83 | -------------------------------------------------------------------------------- /src/adapters/oak.spec.ts: -------------------------------------------------------------------------------- 1 | import { Bridge } from "../bridge"; 2 | import { TuringAdapter } from "./oak"; 3 | import { logFormatedRoute, formateRouteLogLine } from "../utils/unit-test"; 4 | import { FixedPointNumber } from "@acala-network/sdk-core"; 5 | import { ApiProvider } from "../api-provider"; 6 | import { ApiPromise, WsProvider } from "@polkadot/api"; 7 | 8 | // TODO: turing API can not connect in test, need to be fixed 9 | describe.skip("oak adapter should work", () => { 10 | jest.setTimeout(300000); 11 | 12 | const address = "5GREeQcGHt7na341Py6Y6Grr38KUYRvVoiFSiDB52Gt7VZiN"; 13 | const provider = new ApiProvider(); 14 | let bridge: Bridge; 15 | const outputSummary: string[] = []; 16 | 17 | beforeAll(async () => { 18 | const turing = new TuringAdapter(); 19 | 20 | const turingApi = new ApiPromise({ provider: new WsProvider("wss://turing-rpc.dwellir.com") }); 21 | 22 | await turing.init(turingApi); 23 | 24 | bridge = new Bridge({ 25 | adapters: [turing], 26 | }); 27 | }); 28 | 29 | afterAll(async () => { 30 | for (const adapter of bridge.adapters) { 31 | const api = adapter.getApi(); 32 | 33 | if (api) { 34 | await api.disconnect(); 35 | } 36 | } 37 | 38 | await new Promise((resolve) => setTimeout(() => resolve(undefined), 5000)); 39 | logFormatedRoute("Turing summary:\n", outputSummary); 40 | }); 41 | 42 | test("bridge sdk init should work", (done) => { 43 | expect(bridge).toBeDefined(); 44 | 45 | done(); 46 | }); 47 | 48 | test("transfer tokens from turing should work", (done) => { 49 | try { 50 | const adapter = bridge.findAdapter("turing"); 51 | expect(adapter).toBeDefined(); 52 | 53 | if (!adapter) return; 54 | 55 | const allRoutes = bridge.router.getAvailableRouters(); 56 | allRoutes.forEach((e) => { 57 | const token = adapter.getToken(e.token); 58 | 59 | const tx = adapter.createTx({ 60 | to: e.to.id, 61 | token: token.name, 62 | amount: new FixedPointNumber(1, token.decimals), 63 | address, 64 | }); 65 | 66 | expect(tx).toBeDefined(); 67 | 68 | const logRoute = formateRouteLogLine(e.token, e.from.display, e.to.display, "createTx"); 69 | logFormatedRoute("", [logRoute]); 70 | outputSummary.push(logRoute); 71 | }); 72 | 73 | done(); 74 | } catch (e) { 75 | // ignore error 76 | } 77 | }); 78 | }); 79 | -------------------------------------------------------------------------------- /src/adapters/polkadot.spec.ts: -------------------------------------------------------------------------------- 1 | import { FixedPointNumber } from "@acala-network/sdk-core"; 2 | import { firstValueFrom } from "rxjs"; 3 | 4 | import { ApiProvider } from "../api-provider"; 5 | import { chains, ChainId } from "../configs"; 6 | import { Bridge } from "../bridge"; 7 | import { KusamaAdapter, PolkadotAdapter } from "./polkadot"; 8 | import { BasiliskAdapter, HydraDxAdapter } from "./hydradx"; 9 | import { AssetHubKusamaAdapter, AssetHubPolkadotAdapter } from "./assethub"; 10 | import { AcalaAdapter, KaruraAdapter } from "./acala/acala"; 11 | import { BaseCrossChainAdapter } from "../base-chain-adapter"; 12 | import { logFormatedRoute, formateRouteLogLine } from "../utils/unit-test"; 13 | 14 | describe.skip("polkadot-adapter should work", () => { 15 | jest.setTimeout(300000); 16 | 17 | const testAccount = "5GREeQcGHt7na341Py6Y6Grr38KUYRvVoiFSiDB52Gt7VZiN"; 18 | const adapters: Record = { 19 | kusama: new KusamaAdapter(), 20 | karura: new KaruraAdapter(), 21 | basilisk: new BasiliskAdapter(), 22 | assetHubKusama: new AssetHubKusamaAdapter(), 23 | polkadot: new PolkadotAdapter(), 24 | acala: new AcalaAdapter(), 25 | hydradx: new HydraDxAdapter(), 26 | assetHubPolkadot: new AssetHubPolkadotAdapter(), 27 | }; 28 | const provider = new ApiProvider(); 29 | let bridge: Bridge; 30 | const outputSummary: string[] = []; 31 | 32 | afterAll(async () => { 33 | for (const adapter of bridge.adapters) { 34 | const api = adapter.getApi(); 35 | 36 | if (api) { 37 | await api.disconnect(); 38 | } 39 | } 40 | 41 | await new Promise((resolve) => setTimeout(() => resolve(undefined), 5000)); 42 | 43 | logFormatedRoute("Polkadot/Kusama summary:\n", outputSummary); 44 | }); 45 | 46 | async function connect(chains: ChainId[]) { 47 | return firstValueFrom(provider.connectFromChain(chains, undefined)); 48 | } 49 | 50 | const fromChains = ["kusama", "polkadot"] as ChainId[]; 51 | 52 | test("connect kusama/polkadot should work", async () => { 53 | try { 54 | await connect(fromChains); 55 | 56 | await adapters.kusama.init(provider.getApi(fromChains[0])); 57 | await adapters.polkadot.init(provider.getApi(fromChains[1])); 58 | 59 | bridge = new Bridge({ 60 | adapters: Object.values(adapters), 61 | }); 62 | 63 | expect( 64 | bridge.router.getDestinationChains({ 65 | from: chains.kusama, 66 | token: "KSM", 67 | }).length 68 | ).toBeGreaterThanOrEqual(1); 69 | } catch (err) { 70 | // ignore node disconnected error 71 | } 72 | }); 73 | 74 | fromChains.forEach((fromChain) => { 75 | test(`connect ${fromChain} to do xcm`, async () => { 76 | const adapter = bridge.findAdapter(fromChain); 77 | expect(adapter).toBeDefined(); 78 | 79 | const testRoute = async (e) => { 80 | // TODO: add DOT to hydradx tokensConfig to fix this 81 | if (e.token === "DOT" && e.to === "hydradx") return; 82 | 83 | const balance = await firstValueFrom( 84 | adapter.subscribeTokenBalance(e.token, testAccount) 85 | ); 86 | 87 | const balanceLog = formateRouteLogLine( 88 | e.token, 89 | e.from, 90 | e.to, 91 | "balance" 92 | ); 93 | logFormatedRoute("", [balanceLog]); 94 | outputSummary.push(balanceLog); 95 | expect(balance.available.toNumber()).toBeGreaterThanOrEqual(0); 96 | expect(balance.free.toNumber()).toBeGreaterThanOrEqual( 97 | balance.available.toNumber() 98 | ); 99 | expect(balance.free.toNumber()).toEqual( 100 | balance.locked.add(balance.available).toNumber() 101 | ); 102 | 103 | const inputConfig = await firstValueFrom( 104 | adapter.subscribeInputConfig({ 105 | to: e.to, 106 | token: e.token, 107 | address: testAccount, 108 | signer: testAccount, 109 | }) 110 | ); 111 | 112 | const inputConfigLog = formateRouteLogLine( 113 | e.token, 114 | e.from, 115 | e.to, 116 | "inputConfig" 117 | ); 118 | logFormatedRoute("", [inputConfigLog]); 119 | outputSummary.push(inputConfigLog); 120 | expect(inputConfig.minInput.toNumber()).toBeGreaterThan(0); 121 | expect(inputConfig.maxInput.toNumber()).toBeLessThanOrEqual( 122 | balance.available.toNumber() 123 | ); 124 | 125 | const destFee = adapter.getCrossChainFee(e.token, e.to); 126 | 127 | const destFeeLog = formateRouteLogLine( 128 | e.token, 129 | e.from, 130 | e.to, 131 | "destFee" 132 | ); 133 | logFormatedRoute("", [destFeeLog]); 134 | outputSummary.push(destFeeLog); 135 | expect(destFee.balance.toNumber()).toBeGreaterThan(0); 136 | 137 | const token = adapter.getToken(e.token); 138 | const tx = adapter.createTx({ 139 | amount: new FixedPointNumber(0.01, token.decimals), 140 | to: e.to, 141 | token: e.token, 142 | address: testAccount, 143 | }); 144 | 145 | expect(tx).toBeDefined(); 146 | expect(tx.method.section).toEqual("xcmPallet"); 147 | if (e.to === "assetHubKusama" || e.to === "assetHubPolkadot") { 148 | expect(tx.method.method).toEqual("limitedTeleportAssets"); 149 | } else { 150 | expect(tx.method.method).toEqual("limitedReserveTransferAssets"); 151 | } 152 | 153 | const createTxLog = formateRouteLogLine( 154 | e.token, 155 | e.from, 156 | e.to, 157 | "createTx" 158 | ); 159 | logFormatedRoute("", [createTxLog]); 160 | outputSummary.push(createTxLog); 161 | }; 162 | 163 | const allRoutes = adapter.getRouters(); 164 | await Promise.all(allRoutes.map((e) => testRoute(e))); 165 | }); 166 | }); 167 | }); 168 | -------------------------------------------------------------------------------- /src/adapters/robonomics.spec.ts: -------------------------------------------------------------------------------- 1 | import { Bridge } from "../bridge"; 2 | import { FixedPointNumber } from "@acala-network/sdk-core"; 3 | import { formateRouteLogLine, logFormatedRoute } from "../utils/unit-test"; 4 | import { ApiProvider } from "../api-provider"; 5 | import { firstValueFrom } from "rxjs"; 6 | import { RobonomicsAdapter } from "./robonomics"; 7 | 8 | describe.skip("robonomics adapter should work", () => { 9 | jest.setTimeout(300000); 10 | 11 | const address = "5GREeQcGHt7na341Py6Y6Grr38KUYRvVoiFSiDB52Gt7VZiN"; 12 | let bridge: Bridge; 13 | const provider = new ApiProvider(); 14 | const outputSummary: string[] = []; 15 | 16 | beforeAll(async () => { 17 | const robo = new RobonomicsAdapter(); 18 | 19 | await firstValueFrom(provider.connectFromChain(["robonomics"])); 20 | 21 | await robo.init(provider.getApi("robonomics")); 22 | 23 | bridge = new Bridge({ adapters: [robo] }); 24 | }); 25 | 26 | afterAll(async () => { 27 | for (const adapter of bridge.adapters) { 28 | const api = adapter.getApi(); 29 | 30 | if (api) { 31 | await api.disconnect(); 32 | } 33 | } 34 | 35 | await new Promise((resolve) => setTimeout(() => resolve(undefined), 5000)); 36 | 37 | logFormatedRoute("robonomics summary:\n", outputSummary); 38 | }); 39 | 40 | test("bridge sdk init should work", (done) => { 41 | expect(bridge).toBeDefined(); 42 | 43 | done(); 44 | }); 45 | 46 | test("transfer tokens out of robonomics should work", (done) => { 47 | try { 48 | const adapter = bridge.findAdapter("robonomics"); 49 | expect(adapter).toBeDefined(); 50 | 51 | if (!adapter) return; 52 | 53 | const allRoutes = bridge.router.getAvailableRouters(); 54 | allRoutes.forEach((e) => { 55 | const token = adapter.getToken(e.token); 56 | 57 | const tx = adapter.createTx({ 58 | to: e.to.id, 59 | token: token.name, 60 | amount: new FixedPointNumber(1, token.decimals), 61 | address, 62 | }); 63 | 64 | expect(tx).toBeDefined(); 65 | 66 | const logRoute = formateRouteLogLine(e.token, e.from.display, e.to.display, "createTx"); 67 | logFormatedRoute("", [logRoute]); 68 | outputSummary.push(logRoute); 69 | }); 70 | 71 | done(); 72 | } catch (e) { 73 | // ignore error 74 | } 75 | }); 76 | }); 77 | -------------------------------------------------------------------------------- /src/adapters/robonomics.ts: -------------------------------------------------------------------------------- 1 | import { Storage } from "@acala-network/sdk/utils/storage"; 2 | import { AnyApi, FixedPointNumber as FN } from "@acala-network/sdk-core"; 3 | import { combineLatest, map, Observable } from "rxjs"; 4 | 5 | import { SubmittableExtrinsic } from "@polkadot/api/types"; 6 | import { DeriveBalancesAll } from "@polkadot/api-derive/balances/types"; 7 | import { ISubmittableResult } from "@polkadot/types/types"; 8 | 9 | import { BalanceAdapter, BalanceAdapterConfigs } from "../balance-adapter"; 10 | import { BaseCrossChainAdapter } from "../base-chain-adapter"; 11 | import { ChainId, chains } from "../configs"; 12 | import { ApiNotFound, InvalidAddress, TokenNotFound } from "../errors"; 13 | import { BalanceData, BasicToken, TransferParams } from "../types"; 14 | import { 15 | createRouteConfigs, 16 | getDestAccountInfo, 17 | validateAddress, 18 | } from "../utils"; 19 | 20 | const DEST_WEIGHT = "5000000000"; 21 | 22 | export const robonomicsRoutersConfig = createRouteConfigs("robonomics", [ 23 | { 24 | to: "basilisk", 25 | token: "XRT", 26 | xcm: { 27 | fee: { token: "XRT", amount: "447703" }, 28 | weightLimit: DEST_WEIGHT, 29 | }, 30 | }, 31 | ]); 32 | 33 | const robonomicsTokensConfig: Record = { 34 | XRT: { name: "XRT", symbol: "XRT", decimals: 9, ed: "1000" }, 35 | }; 36 | 37 | // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types 38 | const createBalanceStorages = (api: AnyApi) => { 39 | return { 40 | balances: (address: string) => 41 | Storage.create({ 42 | api, 43 | path: "derive.balances.all", 44 | params: [address], 45 | }), 46 | }; 47 | }; 48 | 49 | class RobonomicsBalanceAdapter extends BalanceAdapter { 50 | private storages: ReturnType; 51 | 52 | constructor({ api, chain, tokens }: BalanceAdapterConfigs) { 53 | super({ api, chain, tokens }); 54 | this.storages = createBalanceStorages(api); 55 | } 56 | 57 | public subscribeBalance( 58 | token: string, 59 | address: string 60 | ): Observable { 61 | const storage = this.storages.balances(address); 62 | 63 | if (token !== this.nativeToken) { 64 | throw new TokenNotFound(token); 65 | } 66 | 67 | return storage.observable.pipe( 68 | map((data) => ({ 69 | free: FN.fromInner(data.freeBalance.toString(), this.decimals), 70 | locked: FN.fromInner(data.lockedBalance.toString(), this.decimals), 71 | reserved: FN.fromInner(data.reservedBalance.toString(), this.decimals), 72 | available: FN.fromInner( 73 | data.availableBalance.toString(), 74 | this.decimals 75 | ), 76 | })) 77 | ); 78 | } 79 | } 80 | 81 | class RobonomicsBaseAdapter extends BaseCrossChainAdapter { 82 | private balanceAdapter?: RobonomicsBalanceAdapter; 83 | 84 | public async init(api: AnyApi) { 85 | this.api = api; 86 | 87 | await api.isReady; 88 | 89 | const chain = this.chain.id as ChainId; 90 | 91 | this.balanceAdapter = new RobonomicsBalanceAdapter({ 92 | chain, 93 | api, 94 | tokens: robonomicsTokensConfig, 95 | }); 96 | } 97 | 98 | public subscribeTokenBalance( 99 | token: string, 100 | address: string 101 | ): Observable { 102 | if (!this.balanceAdapter) { 103 | throw new ApiNotFound(this.chain.id); 104 | } 105 | 106 | return this.balanceAdapter.subscribeBalance(token, address); 107 | } 108 | 109 | public subscribeMaxInput( 110 | token: string, 111 | address: string, 112 | to: ChainId 113 | ): Observable { 114 | if (!this.balanceAdapter) { 115 | throw new ApiNotFound(this.chain.id); 116 | } 117 | 118 | return combineLatest({ 119 | txFee: this.estimateTxFee({ 120 | amount: FN.ZERO, 121 | to, 122 | token, 123 | address, 124 | signer: address, 125 | }), 126 | balance: this.balanceAdapter 127 | .subscribeBalance(token, address) 128 | .pipe(map((i) => i.available)), 129 | }).pipe( 130 | map(({ balance, txFee }) => { 131 | const tokenMeta = this.balanceAdapter?.getToken(token); 132 | const feeFactor = 1.2; 133 | const fee = FN.fromInner(txFee, tokenMeta?.decimals).mul( 134 | new FN(feeFactor) 135 | ); 136 | 137 | // always minus ed 138 | return balance 139 | .minus(fee) 140 | .minus(FN.fromInner(tokenMeta?.ed || "0", tokenMeta?.decimals)); 141 | }) 142 | ); 143 | } 144 | 145 | public createTx( 146 | params: TransferParams 147 | ): 148 | | SubmittableExtrinsic<"promise", ISubmittableResult> 149 | | SubmittableExtrinsic<"rxjs", ISubmittableResult> { 150 | if (this.api === undefined) { 151 | throw new ApiNotFound(this.chain.id); 152 | } 153 | 154 | const { address, amount, to, token } = params; 155 | 156 | const { accountId, accountType, addrType } = getDestAccountInfo( 157 | address, 158 | token, 159 | this.api, 160 | to 161 | ); 162 | if (!validateAddress(address, addrType)) throw new InvalidAddress(address); 163 | 164 | const toChain = chains[to]; 165 | 166 | const dst = { 167 | interior: { X1: { ParaChain: toChain.paraChainId } }, 168 | parents: 1, 169 | }; 170 | const acc = { 171 | interior: { 172 | X1: { 173 | [accountType]: { 174 | [accountType === "AccountId32" ? "id" : "key"]: accountId, 175 | network: "Any", 176 | }, 177 | }, 178 | }, 179 | parents: 0, 180 | }; 181 | const ass = [ 182 | { 183 | fun: { Fungible: amount.toChainData() }, 184 | id: { Concrete: { interior: "Here", parents: 0 } }, 185 | }, 186 | ]; 187 | 188 | return this.api?.tx.polkadotXcm.limitedReserveTransferAssets( 189 | { V1: dst }, 190 | { V1: acc }, 191 | { V1: ass }, 192 | 0, 193 | { Limited: "400000000" } 194 | ); 195 | } 196 | } 197 | 198 | export class RobonomicsAdapter extends RobonomicsBaseAdapter { 199 | constructor() { 200 | super(chains.robonomics, robonomicsRoutersConfig, robonomicsTokensConfig); 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /src/adapters/shiden.spec.ts: -------------------------------------------------------------------------------- 1 | import { Bridge } from "../bridge"; 2 | import { ShidenAdapter } from "./astar"; 3 | import { logFormatedRoute, formateRouteLogLine } from "../utils/unit-test"; 4 | import { ApiPromise, WsProvider } from "@polkadot/api"; 5 | import { FixedPointNumber } from "@acala-network/sdk-core"; 6 | 7 | describe.skip("shiden adapter should work", () => { 8 | jest.setTimeout(300000); 9 | 10 | const address = "5GREeQcGHt7na341Py6Y6Grr38KUYRvVoiFSiDB52Gt7VZiN"; 11 | let bridge: Bridge; 12 | const outputSummary: string[] = []; 13 | 14 | beforeAll(async () => { 15 | const shiden = new ShidenAdapter(); 16 | 17 | const shidenApi = new ApiPromise({ provider: new WsProvider("wss://shiden-rpc.dwellir.com") }); 18 | 19 | await shiden.init(shidenApi); 20 | 21 | bridge = new Bridge({ 22 | adapters: [shiden], 23 | }); 24 | }); 25 | 26 | afterAll(async () => { 27 | for (const adapter of bridge.adapters) { 28 | const api = adapter.getApi(); 29 | 30 | if (api) { 31 | await api.disconnect(); 32 | } 33 | } 34 | 35 | await new Promise((resolve) => setTimeout(() => resolve(undefined), 5000)); 36 | logFormatedRoute("Shiden summary:\n", outputSummary); 37 | }); 38 | 39 | test("bridge sdk init should work", (done) => { 40 | expect(bridge).toBeDefined(); 41 | 42 | done(); 43 | }); 44 | 45 | test("transfer tokens from shiden should work", (done) => { 46 | try { 47 | const adapter = bridge.findAdapter("shiden"); 48 | expect(adapter).toBeDefined(); 49 | 50 | if (!adapter) return; 51 | 52 | const allRoutes = bridge.router.getAvailableRouters(); 53 | allRoutes.forEach((e) => { 54 | const token = adapter.getToken(e.token); 55 | 56 | const tx = adapter.createTx({ 57 | to: e.to.id, 58 | token: token.name, 59 | amount: new FixedPointNumber(1, token.decimals), 60 | address, 61 | }); 62 | 63 | expect(tx).toBeDefined(); 64 | 65 | const logRoute = formateRouteLogLine(e.token, e.from.display, e.to.display, "createTx"); 66 | logFormatedRoute("", [logRoute]); 67 | outputSummary.push(logRoute); 68 | }); 69 | 70 | done(); 71 | } catch (e) { 72 | // ignore error 73 | } 74 | }); 75 | }); 76 | -------------------------------------------------------------------------------- /src/adapters/subsocial.ts: -------------------------------------------------------------------------------- 1 | import { Storage } from "@acala-network/sdk/utils/storage"; 2 | import { AnyApi, FixedPointNumber as FN } from "@acala-network/sdk-core"; 3 | import { combineLatest, map, Observable } from "rxjs"; 4 | 5 | import { SubmittableExtrinsic } from "@polkadot/api/types"; 6 | import { DeriveBalancesAll } from "@polkadot/api-derive/balances/types"; 7 | import { ISubmittableResult } from "@polkadot/types/types"; 8 | 9 | import { BalanceAdapter, BalanceAdapterConfigs } from "../balance-adapter"; 10 | import { BaseCrossChainAdapter } from "../base-chain-adapter"; 11 | import { ChainId, chains } from "../configs"; 12 | import { ApiNotFound, InvalidAddress, TokenNotFound } from "../errors"; 13 | import { BalanceData, BasicToken, TransferParams } from "../types"; 14 | import { 15 | createPolkadotXCMAccount, 16 | createPolkadotXCMAsset, 17 | createPolkadotXCMDest, 18 | createRouteConfigs, 19 | getDestAccountType, 20 | getValidDestAddrType, 21 | validateAddress, 22 | } from "../utils"; 23 | 24 | export const subsocialRouteConfigs = createRouteConfigs("subsocial" as any, [ 25 | { 26 | to: "hydradx", 27 | token: "SUB", 28 | xcm: { 29 | fee: { token: "SUB", amount: "65000000" }, 30 | }, 31 | }, 32 | { 33 | to: "moonbeam", 34 | token: "SUB", 35 | xcm: { 36 | fee: { token: "SUB", amount: "65000000" }, 37 | }, 38 | }, 39 | ]); 40 | 41 | export const subsocialTokensConfig: Record = { 42 | SUB: { name: "SUB", symbol: "SUB", decimals: 10, ed: "100000000" }, 43 | }; 44 | 45 | // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types 46 | const createBalanceStorages = (api: AnyApi) => { 47 | return { 48 | balances: (address: string) => 49 | Storage.create({ 50 | api, 51 | path: "derive.balances.all", 52 | params: [address], 53 | }), 54 | }; 55 | }; 56 | 57 | class SubsocialBalanceAdapter extends BalanceAdapter { 58 | private storages: ReturnType; 59 | 60 | constructor({ api, chain, tokens }: BalanceAdapterConfigs) { 61 | super({ api, chain, tokens }); 62 | this.storages = createBalanceStorages(api); 63 | } 64 | 65 | public subscribeBalance( 66 | token: string, 67 | address: string 68 | ): Observable { 69 | const storage = this.storages.balances(address); 70 | 71 | if (token !== this.nativeToken) { 72 | throw new TokenNotFound(token); 73 | } 74 | 75 | return storage.observable.pipe( 76 | map((data) => ({ 77 | free: FN.fromInner(data.freeBalance.toString(), this.decimals), 78 | locked: FN.fromInner(data.lockedBalance.toString(), this.decimals), 79 | reserved: FN.fromInner(data.reservedBalance.toString(), this.decimals), 80 | available: FN.fromInner( 81 | data.availableBalance.toString(), 82 | this.decimals 83 | ), 84 | })) 85 | ); 86 | } 87 | } 88 | 89 | class SubsocialBaseAdapter extends BaseCrossChainAdapter { 90 | private balanceAdapter?: SubsocialBalanceAdapter; 91 | 92 | public async init(api: AnyApi) { 93 | this.api = api; 94 | 95 | await api.isReady; 96 | 97 | const chain = this.chain.id as ChainId; 98 | 99 | this.balanceAdapter = new SubsocialBalanceAdapter({ 100 | chain, 101 | api, 102 | tokens: subsocialTokensConfig, 103 | }); 104 | } 105 | 106 | public subscribeTokenBalance( 107 | token: string, 108 | address: string 109 | ): Observable { 110 | if (!this.balanceAdapter) { 111 | throw new ApiNotFound(this.chain.id); 112 | } 113 | 114 | return this.balanceAdapter.subscribeBalance(token, address); 115 | } 116 | 117 | public subscribeMaxInput( 118 | token: string, 119 | address: string, 120 | to: ChainId 121 | ): Observable { 122 | if (!this.balanceAdapter) { 123 | throw new ApiNotFound(this.chain.id); 124 | } 125 | 126 | return combineLatest({ 127 | txFee: this.estimateTxFee({ 128 | amount: FN.ZERO, 129 | to, 130 | token, 131 | address, 132 | signer: address, 133 | }), 134 | balance: this.balanceAdapter 135 | .subscribeBalance(token, address) 136 | .pipe(map((i) => i.available)), 137 | }).pipe( 138 | map(({ balance, txFee }) => { 139 | const tokenMeta = this.balanceAdapter?.getToken(token); 140 | const feeFactor = 1.2; 141 | const fee = FN.fromInner(txFee, tokenMeta?.decimals).mul( 142 | new FN(feeFactor) 143 | ); 144 | 145 | // always minus ed 146 | return balance 147 | .minus(fee) 148 | .minus(FN.fromInner(tokenMeta?.ed || "0", tokenMeta?.decimals)); 149 | }) 150 | ); 151 | } 152 | 153 | public createTx( 154 | params: TransferParams 155 | ): 156 | | SubmittableExtrinsic<"promise", ISubmittableResult> 157 | | SubmittableExtrinsic<"rxjs", ISubmittableResult> { 158 | if (!this.api) throw new ApiNotFound(this.chain.id); 159 | 160 | const { address, amount, to, token } = params; 161 | 162 | const addrType = getValidDestAddrType(address, token, to); 163 | 164 | const accountId = 165 | addrType === "ethereum" 166 | ? address 167 | : this.api.createType("AccountId32", address).toHex(); 168 | 169 | const accountType = getDestAccountType(address, token, to); 170 | 171 | if (!validateAddress(address, addrType)) throw new InvalidAddress(address); 172 | 173 | const toChain = chains[to]; 174 | 175 | if (token !== this.balanceAdapter?.nativeToken) { 176 | throw new TokenNotFound(token); 177 | } 178 | 179 | // const accountId = this.api?.createType('AccountId32', address).toHex() 180 | const paraChainId = toChain.paraChainId; 181 | const rawAmount = amount.toChainData(); 182 | 183 | return this.api?.tx.polkadotXcm.limitedReserveTransferAssets( 184 | createPolkadotXCMDest(this.api, paraChainId), 185 | createPolkadotXCMAccount(this.api, accountId, accountType), 186 | createPolkadotXCMAsset(this.api, rawAmount, "NATIVE"), 187 | 0, 188 | this.getDestWeight(token, to)?.toString() as any 189 | ); 190 | } 191 | } 192 | 193 | export class SubsocialAdapter extends SubsocialBaseAdapter { 194 | constructor() { 195 | super(chains.subsocial, subsocialRouteConfigs, subsocialTokensConfig); 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /src/adapters/tinkernet.ts: -------------------------------------------------------------------------------- 1 | import { Storage } from "@acala-network/sdk/utils/storage"; 2 | import { AnyApi, FixedPointNumber as FN } from "@acala-network/sdk-core"; 3 | import { combineLatest, map, Observable } from "rxjs"; 4 | 5 | import { SubmittableExtrinsic } from "@polkadot/api/types"; 6 | import { DeriveBalancesAll } from "@polkadot/api-derive/balances/types"; 7 | import { ISubmittableResult } from "@polkadot/types/types"; 8 | 9 | import { BalanceAdapter, BalanceAdapterConfigs } from "../balance-adapter"; 10 | import { BaseCrossChainAdapter } from "../base-chain-adapter"; 11 | import { ChainId, chains } from "../configs"; 12 | import { ApiNotFound, InvalidAddress, TokenNotFound } from "../errors"; 13 | import { BalanceData, BasicToken, TransferParams } from "../types"; 14 | import { 15 | createRouteConfigs, 16 | validateAddress, 17 | getDestAccountInfo, 18 | } from "../utils"; 19 | 20 | const DEST_WEIGHT = "5000000000"; 21 | 22 | export const tinkernetRoutersConfig = createRouteConfigs("robonomics", [ 23 | { 24 | to: "basilisk", 25 | token: "TNKR", 26 | xcm: { 27 | fee: { token: "TNKR", amount: "13554386430" }, 28 | weightLimit: DEST_WEIGHT, 29 | }, 30 | }, 31 | ]); 32 | 33 | const tinkernetTokensConfig: Record = { 34 | TNKR: { name: "TNKR", symbol: "TNKR", decimals: 12, ed: "1000000000" }, 35 | }; 36 | 37 | // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types 38 | const createBalanceStorages = (api: AnyApi) => { 39 | return { 40 | balances: (address: string) => 41 | Storage.create({ 42 | api, 43 | path: "derive.balances.all", 44 | params: [address], 45 | }), 46 | }; 47 | }; 48 | 49 | class TinkernetBalanceAdapter extends BalanceAdapter { 50 | private storages: ReturnType; 51 | 52 | constructor({ api, chain, tokens }: BalanceAdapterConfigs) { 53 | super({ api, chain, tokens }); 54 | this.storages = createBalanceStorages(api); 55 | } 56 | 57 | public subscribeBalance( 58 | token: string, 59 | address: string 60 | ): Observable { 61 | const storage = this.storages.balances(address); 62 | 63 | if (token !== this.nativeToken) { 64 | throw new TokenNotFound(token); 65 | } 66 | 67 | return storage.observable.pipe( 68 | map((data) => ({ 69 | free: FN.fromInner(data.freeBalance.toString(), this.decimals), 70 | locked: FN.fromInner(data.lockedBalance.toString(), this.decimals), 71 | reserved: FN.fromInner(data.reservedBalance.toString(), this.decimals), 72 | available: FN.fromInner( 73 | data.availableBalance.toString(), 74 | this.decimals 75 | ), 76 | })) 77 | ); 78 | } 79 | } 80 | 81 | class TinkernetBaseAdapter extends BaseCrossChainAdapter { 82 | private balanceAdapter?: TinkernetBalanceAdapter; 83 | 84 | public async init(api: AnyApi) { 85 | this.api = api; 86 | 87 | await api.isReady; 88 | 89 | const chain = this.chain.id as ChainId; 90 | 91 | this.balanceAdapter = new TinkernetBalanceAdapter({ 92 | chain, 93 | api, 94 | tokens: tinkernetTokensConfig, 95 | }); 96 | } 97 | 98 | public subscribeTokenBalance( 99 | token: string, 100 | address: string 101 | ): Observable { 102 | if (!this.balanceAdapter) { 103 | throw new ApiNotFound(this.chain.id); 104 | } 105 | 106 | return this.balanceAdapter.subscribeBalance(token, address); 107 | } 108 | 109 | public subscribeMaxInput( 110 | token: string, 111 | address: string, 112 | to: ChainId 113 | ): Observable { 114 | if (!this.balanceAdapter) { 115 | throw new ApiNotFound(this.chain.id); 116 | } 117 | 118 | return combineLatest({ 119 | txFee: this.estimateTxFee({ 120 | amount: FN.ZERO, 121 | to, 122 | token, 123 | address, 124 | signer: address, 125 | }), 126 | balance: this.balanceAdapter 127 | .subscribeBalance(token, address) 128 | .pipe(map((i) => i.available)), 129 | }).pipe( 130 | map(({ balance, txFee }) => { 131 | const tokenMeta = this.balanceAdapter?.getToken(token); 132 | const feeFactor = 1.2; 133 | const fee = FN.fromInner(txFee, tokenMeta?.decimals).mul( 134 | new FN(feeFactor) 135 | ); 136 | 137 | // always minus ed 138 | return balance 139 | .minus(fee) 140 | .minus(FN.fromInner(tokenMeta?.ed || "0", tokenMeta?.decimals)); 141 | }) 142 | ); 143 | } 144 | 145 | public createTx( 146 | params: TransferParams 147 | ): 148 | | SubmittableExtrinsic<"promise", ISubmittableResult> 149 | | SubmittableExtrinsic<"rxjs", ISubmittableResult> { 150 | if (this.api === undefined) { 151 | throw new ApiNotFound(this.chain.id); 152 | } 153 | 154 | const { address, amount, to, token } = params; 155 | 156 | const { accountId, accountType, addrType } = getDestAccountInfo( 157 | address, 158 | token, 159 | this.api, 160 | to 161 | ); 162 | 163 | if (!validateAddress(address, addrType)) throw new InvalidAddress(address); 164 | 165 | const toChain = chains[to]; 166 | 167 | return this.api.tx.xTokens.transfer( 168 | "0", 169 | amount.toChainData(), 170 | { 171 | V1: { 172 | parents: 1, 173 | interior: { 174 | X2: [ 175 | { Parachain: toChain.paraChainId }, 176 | { 177 | [accountType]: { 178 | [accountType === "AccountId32" ? "id" : "key"]: accountId, 179 | network: "Any", 180 | }, 181 | }, 182 | ], 183 | }, 184 | }, 185 | }, 186 | "Unlimited" 187 | ); 188 | } 189 | } 190 | 191 | export class TinkernetAdapter extends TinkernetBaseAdapter { 192 | constructor() { 193 | super(chains.tinkernet, tinkernetRoutersConfig, tinkernetTokensConfig); 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /src/adapters/unique.ts: -------------------------------------------------------------------------------- 1 | import { Storage } from "@acala-network/sdk/utils/storage"; 2 | import { AnyApi, FixedPointNumber as FN } from "@acala-network/sdk-core"; 3 | import { combineLatest, map, Observable } from "rxjs"; 4 | 5 | import { SubmittableExtrinsic } from "@polkadot/api/types"; 6 | import { DeriveBalancesAll } from "@polkadot/api-derive/balances/types"; 7 | import { ISubmittableResult } from "@polkadot/types/types"; 8 | 9 | import { BalanceAdapter, BalanceAdapterConfigs } from "../balance-adapter"; 10 | import { BaseCrossChainAdapter } from "../base-chain-adapter"; 11 | import { ChainId, chains } from "../configs"; 12 | import { ApiNotFound, InvalidAddress, TokenNotFound } from "../errors"; 13 | import { BalanceData, BasicToken, TransferParams } from "../types"; 14 | import { 15 | createPolkadotXCMAccount, 16 | createPolkadotXCMAsset, 17 | createPolkadotXCMDest, 18 | createRouteConfigs, 19 | getDestAccountInfo, 20 | validateAddress, 21 | } from "../utils"; 22 | 23 | export const uniqueRouteConfigs = createRouteConfigs("unique", [ 24 | { 25 | to: "acala", 26 | token: "UNQ", 27 | xcm: { 28 | fee: { token: "UNQ", amount: "101030000000000000" }, 29 | weightLimit: "Unlimited", 30 | }, 31 | }, 32 | ]); 33 | 34 | export const uniqueTokensConfig: Record = { 35 | UNQ: { name: "UNQ", symbol: "UNQ", decimals: 18, ed: "0" }, 36 | }; 37 | 38 | export const quartzRouteConfigs = createRouteConfigs("quartz", [ 39 | { 40 | to: "karura", 41 | token: "QTZ", 42 | xcm: { 43 | fee: { token: "QTZ", amount: "64000000000000000" }, 44 | weightLimit: "Unlimited", 45 | }, 46 | }, 47 | ]); 48 | 49 | export const quartzTokensConfig: Record = { 50 | QTZ: { name: "QTZ", symbol: "QTZ", decimals: 18, ed: "1000000000000000000" }, 51 | }; 52 | 53 | // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types 54 | const createBalanceStorages = (api: AnyApi) => { 55 | return { 56 | balances: (address: string) => 57 | Storage.create({ 58 | api, 59 | path: "derive.balances.all", 60 | params: [address], 61 | }), 62 | }; 63 | }; 64 | 65 | class UniqueBalanceAdapter extends BalanceAdapter { 66 | private storages: ReturnType; 67 | 68 | constructor({ api, chain, tokens }: BalanceAdapterConfigs) { 69 | super({ api, chain, tokens }); 70 | this.storages = createBalanceStorages(api); 71 | } 72 | 73 | public subscribeBalance( 74 | token: string, 75 | address: string 76 | ): Observable { 77 | const storage = this.storages.balances(address); 78 | 79 | if (token !== this.nativeToken) { 80 | throw new TokenNotFound(token); 81 | } 82 | 83 | return storage.observable.pipe( 84 | map((data) => ({ 85 | free: FN.fromInner(data.freeBalance.toString(), this.decimals), 86 | locked: FN.fromInner(data.lockedBalance.toString(), this.decimals), 87 | reserved: FN.fromInner(data.reservedBalance.toString(), this.decimals), 88 | available: FN.fromInner( 89 | data.availableBalance.toString(), 90 | this.decimals 91 | ), 92 | })) 93 | ); 94 | } 95 | } 96 | 97 | class BaseUniqueAdapter extends BaseCrossChainAdapter { 98 | private balanceAdapter?: UniqueBalanceAdapter; 99 | 100 | public async init(api: AnyApi) { 101 | this.api = api; 102 | 103 | await api.isReady; 104 | 105 | this.balanceAdapter = new UniqueBalanceAdapter({ 106 | chain: this.chain.id as ChainId, 107 | api, 108 | tokens: this.tokens, 109 | }); 110 | } 111 | 112 | public subscribeTokenBalance( 113 | token: string, 114 | address: string 115 | ): Observable { 116 | if (!this.balanceAdapter) { 117 | throw new ApiNotFound(this.chain.id); 118 | } 119 | 120 | return this.balanceAdapter.subscribeBalance(token, address); 121 | } 122 | 123 | public subscribeMaxInput( 124 | token: string, 125 | address: string, 126 | to: ChainId 127 | ): Observable { 128 | if (!this.balanceAdapter) { 129 | throw new ApiNotFound(this.chain.id); 130 | } 131 | 132 | return combineLatest({ 133 | txFee: 134 | token === this.balanceAdapter?.nativeToken 135 | ? this.estimateTxFee({ 136 | amount: FN.ZERO, 137 | to, 138 | token, 139 | address, 140 | signer: address, 141 | }) 142 | : "0", 143 | balance: this.balanceAdapter 144 | .subscribeBalance(token, address) 145 | .pipe(map((i) => i.available)), 146 | }).pipe( 147 | map(({ balance, txFee }) => { 148 | const tokenMeta = this.balanceAdapter?.getToken(token); 149 | const feeFactor = 1.2; 150 | const fee = FN.fromInner(txFee, tokenMeta?.decimals).mul( 151 | new FN(feeFactor) 152 | ); 153 | 154 | // always minus ed 155 | return balance 156 | .minus(fee) 157 | .minus(FN.fromInner(tokenMeta?.ed || "0", tokenMeta?.decimals)); 158 | }) 159 | ); 160 | } 161 | 162 | public createTx( 163 | params: TransferParams 164 | ): 165 | | SubmittableExtrinsic<"promise", ISubmittableResult> 166 | | SubmittableExtrinsic<"rxjs", ISubmittableResult> { 167 | if (!this.api) throw new ApiNotFound(this.chain.id); 168 | 169 | const { address, amount, to, token } = params; 170 | 171 | const { accountId, accountType, addrType } = getDestAccountInfo( 172 | address, 173 | token, 174 | this.api, 175 | to 176 | ); 177 | 178 | if (!validateAddress(address, addrType)) throw new InvalidAddress(address); 179 | 180 | const toChain = chains[to]; 181 | 182 | if (token !== this.balanceAdapter?.nativeToken) { 183 | throw new TokenNotFound(token); 184 | } 185 | 186 | const paraChainId = toChain.paraChainId; 187 | const rawAmount = amount.toChainData(); 188 | 189 | return this.api?.tx.polkadotXcm.limitedReserveTransferAssets( 190 | createPolkadotXCMDest(this.api, paraChainId), 191 | createPolkadotXCMAccount(this.api, accountId, accountType), 192 | createPolkadotXCMAsset(this.api, rawAmount, "NATIVE"), 193 | 0, 194 | this.getDestWeight(token, to)?.toString() as any 195 | ); 196 | } 197 | } 198 | 199 | export class QuartzAdapter extends BaseUniqueAdapter { 200 | constructor() { 201 | super(chains.quartz, quartzRouteConfigs, quartzTokensConfig); 202 | } 203 | } 204 | 205 | export class UniqueAdapter extends BaseUniqueAdapter { 206 | constructor() { 207 | super(chains.unique, uniqueRouteConfigs, uniqueTokensConfig); 208 | } 209 | } 210 | -------------------------------------------------------------------------------- /src/adapters/zeitgeist.ts: -------------------------------------------------------------------------------- 1 | import { Storage } from "@acala-network/sdk/utils/storage"; 2 | import { AnyApi, FixedPointNumber as FN } from "@acala-network/sdk-core"; 3 | import { combineLatest, map, Observable } from "rxjs"; 4 | 5 | import { SubmittableExtrinsic } from "@polkadot/api/types"; 6 | import { DeriveBalancesAll } from "@polkadot/api-derive/balances/types"; 7 | import { ISubmittableResult } from "@polkadot/types/types"; 8 | 9 | import { BalanceAdapter, BalanceAdapterConfigs } from "../balance-adapter"; 10 | import { BaseCrossChainAdapter } from "../base-chain-adapter"; 11 | import { ChainId, chains } from "../configs"; 12 | import { ApiNotFound, InvalidAddress, TokenNotFound } from "../errors"; 13 | import { BalanceData, BasicToken, TransferParams } from "../types"; 14 | import { 15 | createRouteConfigs, 16 | getDestAccountInfo, 17 | validateAddress, 18 | } from "../utils"; 19 | 20 | const DEST_WEIGHT = "5000000000"; 21 | 22 | export const zeitgeistRouteConfigs = createRouteConfigs("zeitgeist", [ 23 | { 24 | to: "hydradx", 25 | token: "ZTG", 26 | xcm: { 27 | fee: { token: "ZTG", amount: "225000000" }, 28 | weightLimit: DEST_WEIGHT, 29 | }, 30 | }, 31 | ]); 32 | 33 | const zeitgeistTokensConfig: Record = { 34 | ZTG: { name: "ZTG", symbol: "ZTG", decimals: 10, ed: "50000000" }, 35 | }; 36 | 37 | // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types 38 | const createBalanceStorages = (api: AnyApi) => { 39 | return { 40 | balances: (address: string) => 41 | Storage.create({ 42 | api, 43 | path: "derive.balances.all", 44 | params: [address], 45 | }), 46 | }; 47 | }; 48 | 49 | class ZeitgeistBalanceAdapter extends BalanceAdapter { 50 | private storages: ReturnType; 51 | 52 | constructor({ api, chain, tokens }: BalanceAdapterConfigs) { 53 | super({ api, chain, tokens }); 54 | this.storages = createBalanceStorages(api); 55 | } 56 | 57 | public subscribeBalance( 58 | token: string, 59 | address: string 60 | ): Observable { 61 | const storage = this.storages.balances(address); 62 | 63 | if (token !== this.nativeToken) { 64 | throw new TokenNotFound(token); 65 | } 66 | 67 | return storage.observable.pipe( 68 | map((data) => ({ 69 | free: FN.fromInner(data.freeBalance.toString(), this.decimals), 70 | locked: FN.fromInner(data.lockedBalance.toString(), this.decimals), 71 | reserved: FN.fromInner(data.reservedBalance.toString(), this.decimals), 72 | available: FN.fromInner( 73 | data.availableBalance.toString(), 74 | this.decimals 75 | ), 76 | })) 77 | ); 78 | } 79 | } 80 | 81 | class ZeitgeistBaseAdapter extends BaseCrossChainAdapter { 82 | private balanceAdapter?: ZeitgeistBalanceAdapter; 83 | 84 | public async init(api: AnyApi) { 85 | this.api = api; 86 | 87 | await api.isReady; 88 | 89 | const chain = this.chain.id as ChainId; 90 | 91 | this.balanceAdapter = new ZeitgeistBalanceAdapter({ 92 | chain, 93 | api, 94 | tokens: zeitgeistTokensConfig, 95 | }); 96 | } 97 | 98 | public subscribeTokenBalance( 99 | token: string, 100 | address: string 101 | ): Observable { 102 | if (!this.balanceAdapter) { 103 | throw new ApiNotFound(this.chain.id); 104 | } 105 | 106 | return this.balanceAdapter.subscribeBalance(token, address); 107 | } 108 | 109 | public subscribeMaxInput( 110 | token: string, 111 | address: string, 112 | to: ChainId 113 | ): Observable { 114 | if (!this.balanceAdapter) { 115 | throw new ApiNotFound(this.chain.id); 116 | } 117 | 118 | return combineLatest({ 119 | txFee: this.estimateTxFee({ 120 | amount: FN.ZERO, 121 | to, 122 | token, 123 | address, 124 | signer: address, 125 | }), 126 | balance: this.balanceAdapter 127 | .subscribeBalance(token, address) 128 | .pipe(map((i) => i.available)), 129 | }).pipe( 130 | map(({ balance, txFee }) => { 131 | const tokenMeta = this.balanceAdapter?.getToken(token); 132 | const feeFactor = 1.2; 133 | const fee = FN.fromInner(txFee, tokenMeta?.decimals).mul( 134 | new FN(feeFactor) 135 | ); 136 | 137 | // always minus ed 138 | return balance 139 | .minus(fee) 140 | .minus(FN.fromInner(tokenMeta?.ed || "0", tokenMeta?.decimals)); 141 | }) 142 | ); 143 | } 144 | 145 | public createTx( 146 | params: TransferParams 147 | ): 148 | | SubmittableExtrinsic<"promise", ISubmittableResult> 149 | | SubmittableExtrinsic<"rxjs", ISubmittableResult> { 150 | if (this.api === undefined) { 151 | throw new ApiNotFound(this.chain.id); 152 | } 153 | 154 | const { address, amount, to, token } = params; 155 | 156 | const { accountId, accountType, addrType } = getDestAccountInfo( 157 | address, 158 | token, 159 | this.api, 160 | to 161 | ); 162 | 163 | if (!validateAddress(address, addrType)) throw new InvalidAddress(address); 164 | 165 | const toChain = chains[to]; 166 | 167 | return this.api.tx.xTokens.transfer( 168 | "Ztg", 169 | amount.toChainData(), 170 | { 171 | V1: { 172 | parents: 1, 173 | interior: { 174 | X2: [ 175 | { Parachain: toChain.paraChainId }, 176 | { 177 | [accountType]: { 178 | [accountType === "AccountId32" ? "id" : "key"]: accountId, 179 | network: "Any", 180 | }, 181 | }, 182 | ], 183 | }, 184 | }, 185 | }, 186 | DEST_WEIGHT 187 | ); 188 | } 189 | } 190 | 191 | export class ZeitgeistAdapter extends ZeitgeistBaseAdapter { 192 | constructor() { 193 | super(chains.zeitgeist, zeitgeistRouteConfigs, zeitgeistTokensConfig); 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /src/api-provider.spec.ts: -------------------------------------------------------------------------------- 1 | import { firstValueFrom } from "rxjs"; 2 | 3 | import { ApiProvider } from "./api-provider"; 4 | import { ChainId } from "./configs"; 5 | 6 | describe("api-provider", () => { 7 | jest.setTimeout(300000); 8 | 9 | const provider = new ApiProvider(); 10 | 11 | test("connectFromChain should be ok", async () => { 12 | const chains: ChainId[] = ["kusama", "karura", "polkadot", "acala"]; 13 | 14 | expect(provider.getApi(chains[0])).toEqual(undefined); 15 | expect(provider.getApi(chains[1])).toEqual(undefined); 16 | 17 | const res = await firstValueFrom( 18 | provider.connectFromChain(chains, { 19 | karura: ["wss://karura-rpc.dwellir.com", "wss://karura.polkawallet.io"], 20 | }) 21 | ); 22 | 23 | expect(res.length).toEqual(chains.length); 24 | 25 | expect(res[0]).toEqual(chains[0]); 26 | expect(res[1]).toEqual(chains[1]); 27 | expect(res[2]).toEqual(chains[2]); 28 | expect(res[3]).toEqual(chains[3]); 29 | 30 | expect(provider.getApi(chains[0])).toBeDefined(); 31 | expect(provider.getApi(chains[1])).toBeDefined(); 32 | 33 | expect((await firstValueFrom(provider.getApi(chains[2]).rpc.system.chain())).toLowerCase()).toEqual(chains[2]); 34 | expect((await firstValueFrom(provider.getApi(chains[3]).rpc.system.chain())).toLowerCase()).toEqual(chains[3]); 35 | 36 | setTimeout(async () => { 37 | expect((await provider.getApiPromise(chains[0]).rpc.system.chain()).toLowerCase()).toEqual(chains[0]); 38 | expect((await provider.getApiPromise(chains[1]).rpc.system.chain()).toLowerCase()).toEqual(chains[1]); 39 | }, 1000); 40 | }); 41 | }); 42 | -------------------------------------------------------------------------------- /src/api-provider.ts: -------------------------------------------------------------------------------- 1 | import { options } from "@acala-network/api"; 2 | import { combineLatest, map, Observable, race } from "rxjs"; 3 | 4 | import { ApiPromise, ApiRx, WsProvider } from "@polkadot/api"; 5 | import { 6 | prodParasKusama, 7 | prodParasKusamaCommon, 8 | prodParasPolkadot, 9 | prodParasPolkadotCommon, 10 | prodRelayKusama, 11 | prodRelayPolkadot, 12 | } from "@polkadot/apps-config/endpoints"; 13 | 14 | import { isChainEqual } from "./utils/is-chain-equal"; 15 | import { ChainId, chains } from "./configs"; 16 | import { polkadotChains } from "./configs/chains/polkadot-chains"; 17 | import { kusamaChains } from "./configs/chains/kusama-chains"; 18 | 19 | const kusamaChainIds = new Set(Object.keys(kusamaChains)); 20 | const polkadotChainIds = new Set(Object.keys(polkadotChains)); 21 | 22 | export class ApiProvider { 23 | protected apis: Record = {}; 24 | protected promiseApis: Record = {}; 25 | 26 | public getApi(ChainId: string) { 27 | return this.apis[ChainId]; 28 | } 29 | 30 | public getApiPromise(ChainId: string) { 31 | return this.promiseApis[ChainId]; 32 | } 33 | 34 | public connectFromChain( 35 | ChainId: ChainId[], 36 | nodeList?: Partial> 37 | ) { 38 | return combineLatest( 39 | ChainId.map((chain) => { 40 | let nodes = (nodeList || {})[chain]; 41 | 42 | if (!nodes) { 43 | if (isChainEqual(chain, "kusama")) { 44 | nodes = Object.values(prodRelayKusama.providers).filter((e) => 45 | e.startsWith("wss://") 46 | ); 47 | } else if (chain === "polkadot") { 48 | nodes = Object.values(prodRelayPolkadot.providers).filter((e) => 49 | e.startsWith("wss://") 50 | ); 51 | } else if (chain === "assetHubKusama") { 52 | nodes = Object.values( 53 | prodParasKusamaCommon.find((e) => e.info === chain)?.providers || 54 | {} 55 | ).filter((e) => e.startsWith("wss://")); 56 | } else { 57 | nodes = Object.values( 58 | [...prodParasKusama, ...prodParasPolkadot].find( 59 | (e) => e.info === chain 60 | )?.providers || {} 61 | ).filter((e) => e.startsWith("wss://")); 62 | } 63 | } 64 | 65 | if (!nodes?.length) { 66 | const paraId = chains[chain].paraChainId; 67 | 68 | const haystack = []; 69 | if (kusamaChainIds.has(chain)) { 70 | haystack.push(...prodParasKusamaCommon); 71 | haystack.push(...prodParasKusama); 72 | } else if (polkadotChainIds.has(chain)) { 73 | haystack.push(...prodParasPolkadotCommon); 74 | haystack.push(...prodParasPolkadot); 75 | } 76 | 77 | // find by parachain id 78 | nodes = Object.values( 79 | haystack.find((e) => e.paraId === paraId)?.providers || {} 80 | ).filter((e) => e.startsWith("wss://")); 81 | } 82 | 83 | if (nodes.length > 1) { 84 | return race(nodes.map((node) => this.connect([node], chain))); 85 | } 86 | 87 | return this.connect(nodes, chain); 88 | }) 89 | ); 90 | } 91 | 92 | public connect( 93 | nodes: string[], 94 | ChainId: ChainId 95 | ): Observable { 96 | if (this.apis[ChainId]) { 97 | this.apis[ChainId].disconnect(); 98 | delete this.apis[ChainId]; 99 | } 100 | 101 | if (this.promiseApis[ChainId]) { 102 | this.promiseApis[ChainId].disconnect(); 103 | delete this.promiseApis[ChainId]; 104 | } 105 | 106 | const wsProvider = new WsProvider(nodes); 107 | 108 | const isAcala = ChainId === "acala" || ChainId === "karura"; 109 | const option = isAcala 110 | ? options({ 111 | provider: wsProvider, 112 | }) 113 | : { 114 | provider: wsProvider, 115 | }; 116 | const promiseApi = ApiPromise.create(option); 117 | 118 | return ApiRx.create(option).pipe( 119 | map((api) => { 120 | // connect success 121 | if (api) { 122 | if (!this.apis[ChainId]) { 123 | this.apis[ChainId] = api; 124 | } else { 125 | api.disconnect(); 126 | } 127 | 128 | promiseApi.then((res) => { 129 | if (!this.promiseApis[ChainId]) { 130 | this.promiseApis[ChainId] = res; 131 | } else { 132 | res.disconnect(); 133 | } 134 | }); 135 | 136 | return ChainId; 137 | } 138 | 139 | return null; 140 | }) 141 | ); 142 | } 143 | 144 | public disconnect(ChainId: ChainId) { 145 | if (this.apis[ChainId]) { 146 | this.apis[ChainId].disconnect(); 147 | delete this.apis[ChainId]; 148 | } 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /src/balance-adapter.ts: -------------------------------------------------------------------------------- 1 | import { AnyApi } from "@acala-network/sdk-core"; 2 | 3 | import { Observable } from "@polkadot/types/types"; 4 | 5 | import { ChainId } from "./configs"; 6 | import { TokenNotFound } from "./errors"; 7 | import { BalanceData, BasicToken, FN } from "./types"; 8 | 9 | export interface BalanceAdapterConfigs { 10 | chain: ChainId; 11 | api: AnyApi; 12 | tokens: Record; 13 | } 14 | 15 | export abstract class BalanceAdapter { 16 | readonly chain: ChainId; 17 | readonly decimals: number; 18 | readonly ed: FN; 19 | readonly nativeToken: string; 20 | readonly tokens: Record; 21 | 22 | constructor({ api, chain, tokens }: BalanceAdapterConfigs) { 23 | this.chain = chain; 24 | this.decimals = api.registry.chainDecimals[0]; 25 | this.ed = FN.fromInner( 26 | api.consts.balances?.existentialDeposit?.toString() || "0", 27 | this.decimals 28 | ); 29 | this.nativeToken = api.registry.chainTokens[0]; 30 | this.tokens = tokens; 31 | } 32 | 33 | public getToken(token: string): R { 34 | const tokenConfig = this.tokens[token]; 35 | 36 | if (!tokenConfig) throw new TokenNotFound(token, this.chain); 37 | 38 | return tokenConfig as R; 39 | } 40 | 41 | public abstract subscribeBalance( 42 | token: string, 43 | address: string 44 | ): Observable; 45 | } 46 | -------------------------------------------------------------------------------- /src/bridge.spec.ts: -------------------------------------------------------------------------------- 1 | import { ApiProvider } from "./api-provider"; 2 | import { BaseCrossChainAdapter } from "./base-chain-adapter"; 3 | import { PolkadotAdapter } from "./adapters/polkadot"; 4 | import { ChainId } from "./configs"; 5 | import { Bridge } from "./bridge"; 6 | import { AcalaAdapter } from "./adapters/acala"; 7 | 8 | describe("Bridge sdk usage", () => { 9 | jest.setTimeout(300000); 10 | 11 | const provider = new ApiProvider(); 12 | 13 | const availableAdapters: Record = { 14 | polkadot: new PolkadotAdapter(), 15 | // kusama: new KusamaAdapter(), 16 | acala: new AcalaAdapter(), 17 | // karura: new KaruraAdapter(), 18 | // assetHubKusama: new AssetHubKusamaAdapter(), 19 | // altair: new AltairAdapter(), 20 | // shiden: new ShidenAdapter(), 21 | // bifrost: new BifrostAdapter(), 22 | // calamari: new CalamariAdapter(), 23 | // shadow: new ShadowAdapter(), 24 | // crab: new CrabAdapter(), 25 | // integritee: new IntegriteeAdapter(), 26 | // quartz: new QuartzAdapter(), 27 | }; 28 | 29 | const bridge = new Bridge({ 30 | adapters: Object.values(availableAdapters), 31 | }); 32 | 33 | test("1. bridge init should be ok", async () => { 34 | expect(bridge.router.getRouters().length).toBeGreaterThanOrEqual(Object.keys(availableAdapters).length); 35 | expect(bridge.router.getDestinationChains({ from: "acala" }).length).toBeGreaterThanOrEqual(0); 36 | expect(bridge.router.getAvailableTokens({ from: "acala", to: "polkadot" }).length).toBeGreaterThanOrEqual(0); 37 | }); 38 | 39 | test("2. find adapter should be ok", async () => { 40 | const chains = Object.keys(availableAdapters) as ChainId[]; 41 | 42 | expect(bridge.findAdapter(chains[0])).toEqual(availableAdapters[chains[0]]); 43 | expect(bridge.findAdapter(chains[1])).toEqual(availableAdapters[chains[1]]); 44 | }); 45 | }); 46 | -------------------------------------------------------------------------------- /src/bridge.ts: -------------------------------------------------------------------------------- 1 | import { BaseSDK } from "@acala-network/sdk"; 2 | import { BehaviorSubject, filter, firstValueFrom } from "rxjs"; 3 | 4 | import { ChainId } from "./configs/index"; 5 | import { BaseCrossChainAdapter } from "./base-chain-adapter"; 6 | import { BridgeRouterManager } from "./cross-chain-router"; 7 | import { NoCrossChainAdapterFound } from "./errors"; 8 | import { BridgeConfigs, Chain } from "./types"; 9 | 10 | export class Bridge implements BaseSDK { 11 | readonly router: BridgeRouterManager; 12 | readonly adapters: BaseCrossChainAdapter[]; 13 | 14 | public consts!: { 15 | runtimeChain: string; 16 | }; 17 | 18 | public isReady$: BehaviorSubject; 19 | 20 | constructor({ adapters, disabledRouters }: BridgeConfigs) { 21 | this.isReady$ = new BehaviorSubject(false); 22 | this.adapters = adapters; 23 | this.router = new BridgeRouterManager({ adapters, disabledRouters }); 24 | this.init(); 25 | } 26 | 27 | public init(): void { 28 | this.adapters.forEach((i) => this.router.addRouters(i.getRouters())); 29 | this.adapters.forEach((i) => i.injectFindAdapter(this.findAdapter)); 30 | 31 | this.router.init().then(() => this.isReady$.next(true)); 32 | } 33 | 34 | public get isReady(): Promise { 35 | return firstValueFrom( 36 | this.isReady$.asObservable().pipe(filter((i) => i === true)) 37 | ); 38 | } 39 | 40 | public findAdapter = (chain: ChainId | Chain): BaseCrossChainAdapter => { 41 | const result = this.router.findAdapterByName(chain); 42 | 43 | if (!result) { 44 | throw new NoCrossChainAdapterFound(JSON.stringify(chain)); 45 | } 46 | 47 | return result; 48 | }; 49 | } 50 | -------------------------------------------------------------------------------- /src/configs/chains/index.ts: -------------------------------------------------------------------------------- 1 | import { Chain } from "../../types"; 2 | import { kusamaChains } from "./kusama-chains"; 3 | import { polkadotChains } from "./polkadot-chains"; 4 | 5 | export const rawChains = { 6 | ...kusamaChains, 7 | ...polkadotChains, 8 | }; 9 | 10 | export type ChainId = keyof typeof rawChains; 11 | 12 | export const chains = Object.fromEntries( 13 | Object.entries(rawChains).map(([id, data]) => { 14 | return [id, { id, ...data }]; 15 | }) 16 | ) as { [k in ChainId]: Chain }; 17 | -------------------------------------------------------------------------------- /src/configs/chains/kusama-chains.ts: -------------------------------------------------------------------------------- 1 | export const kusamaChains = { 2 | kusama: { 3 | display: "Kusama", 4 | type: "substrate", 5 | icon: "https://resources.acala.network/_next/image?url=%2Fnetworks%2Fkusama.png&w=96&q=75", 6 | paraChainId: -1, 7 | ss58Prefix: 2, 8 | }, 9 | assetHubKusama: { 10 | display: "Asset Hub Kusama", 11 | type: "substrate", 12 | icon: "https://resources.acala.network/_next/image?url=%2Fnetworks%2Fstatemine.png&w=96&q=75", 13 | paraChainId: 1000, 14 | ss58Prefix: 2, 15 | }, 16 | karura: { 17 | display: "Karura", 18 | type: "substrate", 19 | icon: "https://resources.acala.network/_next/image?url=%2Fnetworks%2Fkarura.png&w=96&q=75", 20 | paraChainId: 2000, 21 | ss58Prefix: 8, 22 | }, 23 | quartz: { 24 | display: "Quartz", 25 | type: "substrate", 26 | icon: "https://resources.acala.network/_next/image?url=%2Fnetworks%2Fquartz.png&w=96&q=75", 27 | paraChainId: 2095, 28 | ss58Prefix: 255, 29 | }, 30 | bifrost: { 31 | display: "Bifrost", 32 | type: "substrate", 33 | icon: "https://resources.acala.network/_next/image?url=%2Fnetworks%2Fbifrost.png&w=96&q=75", 34 | paraChainId: 2001, 35 | ss58Prefix: 6, 36 | }, 37 | khala: { 38 | display: "Khala", 39 | type: "substrate", 40 | icon: "https://resources.acala.network/_next/image?url=%2Fnetworks%2Fkhala.png&w=96&q=75", 41 | paraChainId: 2004, 42 | ss58Prefix: 30, 43 | }, 44 | kintsugi: { 45 | display: "Kintsugi", 46 | type: "substrate", 47 | icon: "https://resources.acala.network/_next/image?url=%2Fnetworks%2Fkintsugi.png&w=96&q=75", 48 | paraChainId: 2092, 49 | ss58Prefix: 2092, 50 | }, 51 | moonriver: { 52 | display: "Moonriver", 53 | type: "ethereum", 54 | icon: "https://resources.acala.network/_next/image?url=%2Fnetworks%2Fmoonriver.png&w=96&q=75", 55 | paraChainId: 2023, 56 | ss58Prefix: 1285, 57 | }, 58 | shadow: { 59 | display: "Crust Shadow", 60 | type: "substrate", 61 | icon: "https://resources.acala.network/_next/image?url=%2Fnetworks%2Fcrust%20shadow.png&w=96&q=75", 62 | paraChainId: 2012, 63 | ss58Prefix: 66, 64 | }, 65 | calamari: { 66 | display: "Calamari", 67 | type: "substrate", 68 | icon: "https://resources.acala.network/_next/image?url=%2Fnetworks%2Fcalamari.png&w=96&q=75", 69 | paraChainId: 2084, 70 | ss58Prefix: 78, 71 | }, 72 | integritee: { 73 | display: "Integritee", 74 | type: "substrate", 75 | icon: "https://resources.acala.network/_next/image?url=%2Fnetworks%2Fintegritee.png&w=96&q=75", 76 | paraChainId: 2015, 77 | ss58Prefix: 13, 78 | }, 79 | altair: { 80 | display: "Altair", 81 | type: "substrate", 82 | icon: "https://resources.acala.network/_next/image?url=%2Fnetworks%2Faltair.png&w=96&q=75", 83 | paraChainId: 2088, 84 | ss58Prefix: 136, 85 | }, 86 | crab: { 87 | display: "Darwinia Crab", 88 | type: "substrate", 89 | icon: "https://resources.acala.network/_next/image?url=%2Fnetworks%2Fcrab.png&w=96&q=75", 90 | paraChainId: 2105, 91 | ss58Prefix: 42, 92 | }, 93 | turing: { 94 | display: "Turing", 95 | type: "substrate", 96 | icon: "https://resources.acala.network/_next/image?url=%2Fnetworks%2Fturing.png&w=96&q=75", 97 | paraChainId: 2114, 98 | ss58Prefix: 51, 99 | }, 100 | shiden: { 101 | display: "Shiden", 102 | type: "substrate", 103 | icon: "https://resources.acala.network/_next/image?url=%2Fnetworks%2Fshiden.png&w=96&q=75", 104 | paraChainId: 2007, 105 | ss58Prefix: 5, 106 | }, 107 | basilisk: { 108 | display: "Basilisk", 109 | type: "substrate", 110 | icon: "https://resources.acala.network/_next/image?url=%2Fnetworks%2Fbasilisk.png&w=96&q=75", 111 | paraChainId: 2090, 112 | ss58Prefix: 10041, 113 | }, 114 | listen: { 115 | display: "Listen", 116 | type: "substrate", 117 | icon: "https://resources.acala.network/_next/image?url=%2Fnetworks%2Flisten.png&w=96&q=75", 118 | paraChainId: 2118, 119 | ss58Prefix: 42, 120 | }, 121 | robonomics: { 122 | id: "robonomics", 123 | display: "Robonomics", 124 | type: "substrate", 125 | icon: "https://resources.acala.network/_next/image?url=%2Fnetworks%2Frobonomics.png&w=96&q=75", 126 | paraChainId: 2048, 127 | ss58Prefix: 32, 128 | }, 129 | tinkernet: { 130 | id: "tinkernet", 131 | display: "Tinkernet", 132 | type: "substrate", 133 | icon: "", 134 | paraChainId: 2125, 135 | ss58Prefix: 117, 136 | }, 137 | }; 138 | -------------------------------------------------------------------------------- /src/configs/chains/polkadot-chains.ts: -------------------------------------------------------------------------------- 1 | export const polkadotChains = { 2 | polkadot: { 3 | display: "Polkadot", 4 | type: "substrate", 5 | icon: "https://resources.acala.network/_next/image?url=%2Fnetworks%2Fpolkadot.png&w=96&q=75", 6 | paraChainId: -1, 7 | ss58Prefix: 0, 8 | }, 9 | assetHubPolkadot: { 10 | display: "Asset Hub Polkadot", 11 | type: "substrate", 12 | icon: "https://resources.acala.network/_next/image?url=%2Fnetworks%2Fstatemine.png&w=96&q=75", 13 | paraChainId: 1000, 14 | ss58Prefix: 0, 15 | }, 16 | acala: { 17 | display: "Acala", 18 | type: "substrate", 19 | icon: "https://resources.acala.network/_next/image?url=%2Fnetworks%2Facala.png&w=96&q=75", 20 | paraChainId: 2000, 21 | ss58Prefix: 10, 22 | }, 23 | phala: { 24 | display: "Phala", 25 | type: "substrate", 26 | icon: "https://resources.acala.network/_next/image?url=%2Fnetworks%2Fphala.png&w=96&q=75", 27 | paraChainId: 2035, 28 | ss58Prefix: 30, 29 | }, 30 | moonbeam: { 31 | display: "Moonbeam", 32 | type: "ethereum", 33 | icon: "https://resources.acala.network/_next/image?url=%2Fnetworks%2Fmoonbeam.png&w=96&q=75", 34 | paraChainId: 2004, 35 | ss58Prefix: 1284, 36 | }, 37 | interlay: { 38 | display: "Interlay", 39 | type: "substrate", 40 | icon: "https://resources.acala.network/_next/image?url=%2Fnetworks%2Finterlay.png&w=96&q=75", 41 | paraChainId: 2032, 42 | ss58Prefix: 2032, 43 | }, 44 | astar: { 45 | display: "Astar", 46 | type: "substrate", 47 | icon: "https://resources.acala.network/_next/image?url=%2Fnetworks%2Fastar.png&w=96&q=75", 48 | paraChainId: 2006, 49 | ss58Prefix: 5, 50 | }, 51 | hydradx: { 52 | display: "HydraDX", 53 | type: "substrate", 54 | icon: "https://resources.acala.network/_next/image?url=%2Fnetworks%2Fhydra.png&w=96&q=75", 55 | paraChainId: 2034, 56 | ss58Prefix: 63, 57 | }, 58 | zeitgeist: { 59 | display: "Zeitgeist", 60 | type: "substrate", 61 | icon: "https://resources.acala.network/_next/image?url=%2Fnetworks%2Fzeitgeist.png&w=96&q=75", 62 | paraChainId: 2092, 63 | ss58Prefix: 73, 64 | }, 65 | unique: { 66 | display: "Unique Network", 67 | type: "substrate", 68 | icon: "https://resources.acala.network/_next/image?url=%2Fnetworks%2Funique.png&w=96&q=75", 69 | paraChainId: 2037, 70 | ss58Prefix: 7391, 71 | }, 72 | centrifuge: { 73 | display: "Centrifuge", 74 | type: "substrate", 75 | icon: "https://resources.acala.network/_next/image?url=%2Fnetworks%2Fcentrifuge.png&w=96&q=75", 76 | paraChainId: 2031, 77 | ss58Prefix: 36, 78 | }, 79 | subsocial: { 80 | id: "subsocial", 81 | display: "Subsocial", 82 | type: "substrate", 83 | icon: "https://sub.id/images/subsocial.svg", 84 | paraChainId: 2101, 85 | ss58Prefix: 28, 86 | }, 87 | bifrostPolkadot: { 88 | display: "Bifrost", 89 | type: "substrate", 90 | icon: "https://resources.acala.network/_next/image?url=%2Fnetworks%2Fbifrost.png&w=96&q=75", 91 | paraChainId: 2030, 92 | ss58Prefix: 6, 93 | }, 94 | }; 95 | -------------------------------------------------------------------------------- /src/configs/common.ts: -------------------------------------------------------------------------------- 1 | export const DEV_ROUTER_DISABLED = 2 | "https://api.polkawallet.io/devConfiguration/config/bridge.json"; 3 | -------------------------------------------------------------------------------- /src/configs/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./chains"; 2 | -------------------------------------------------------------------------------- /src/cross-chain-router.spec.ts: -------------------------------------------------------------------------------- 1 | import { isChainEqual } from "./utils/is-chain-equal"; 2 | import { chains } from "./configs"; 3 | import { BridgeRouterManager } from "./cross-chain-router"; 4 | import { RouteConfigs } from "./types"; 5 | 6 | describe("cross-chain-router-manager", () => { 7 | let manager: BridgeRouterManager; 8 | 9 | jest.setTimeout(300000); 10 | 11 | const initSDK = async () => { 12 | if (manager) { 13 | return manager; 14 | } 15 | 16 | manager = new BridgeRouterManager(); 17 | 18 | await manager.addRouters( 19 | [ 20 | { from: chains.karura.id, to: chains.kusama.id, token: "KSM" }, 21 | { from: chains.karura.id, to: chains.khala.id, token: "KSM" }, 22 | { from: chains.karura.id, to: chains.khala.id, token: "AUSD" }, 23 | { from: chains.karura.id, to: chains.khala.id, token: "LKSM" }, 24 | { from: chains.khala.id, to: chains.karura.id, token: "KSM" }, 25 | { from: chains.khala.id, to: chains.karura.id, token: "AUSD" }, 26 | { from: chains.khala.id, to: chains.karura.id, token: "LKSM" }, 27 | { from: chains.kusama.id, to: chains.karura.id, token: "KSM" }, 28 | { from: chains.assetHubKusama.id, to: chains.karura.id, token: "RMRK" }, 29 | ] as RouteConfigs[], 30 | false 31 | ); 32 | 33 | return manager; 34 | }; 35 | 36 | test("isChainEqual should be ok", () => { 37 | expect(isChainEqual(chains.karura, chains.karura)).toBe(true); 38 | expect(isChainEqual(chains.karura, "karura")).toBe(true); 39 | expect(isChainEqual(chains.karura, "kusama")).toBe(false); 40 | expect(isChainEqual("karura", chains.karura)).toBe(true); 41 | expect(isChainEqual("kusama", chains.karura)).toBe(false); 42 | }); 43 | 44 | test("getRouter should be ok", async () => { 45 | const r1 = manager.getRouters({ from: "karura" }); 46 | // const r2 = manager.getRouters({ from: "khala" }); 47 | const r3 = manager.getRouters({ from: "karura", to: "khala" }); 48 | const r4 = manager.getRouters({ from: "karura", to: "khala", token: "AUSD" }); 49 | const r5 = manager.getRouters({ to: "karura" }); 50 | const r6 = manager.getRouters({ to: "karura", token: "AUSD" }); 51 | const r7 = manager.getRouters({ token: "AUSD" }); 52 | const r8 = manager.getRouters({ token: "RMRK" }); 53 | const r9 = manager.getRouters(); 54 | 55 | expect(r1.length).toEqual(4); 56 | // expect(r2.length).toEqual(3); 57 | expect(r3.length).toEqual(3); 58 | expect(r4.length).toEqual(1); 59 | expect(r5.length).toEqual(5); 60 | expect(r6.length).toEqual(1); 61 | expect(r7.length).toEqual(2); 62 | expect(r8.length).toEqual(1); 63 | expect(r9.length).toEqual(9); 64 | }); 65 | 66 | test("get* should be ok", async () => { 67 | const r1 = manager.getDestinationChains({ from: "karura" }); 68 | const r2 = manager.getFromChains({ to: "karura" }); 69 | 70 | expect(r1.length).toEqual(2); 71 | expect(r1[0].display).toEqual("Kusama"); 72 | expect(r1[1].display).toEqual("Khala"); 73 | expect(r2.length).toEqual(3); 74 | expect(r2[0].display).toEqual("Khala"); 75 | expect(r2[1].display).toEqual("Kusama"); 76 | expect(r2[2].display).toEqual("Asset Hub Kusama"); 77 | }); 78 | 79 | test("filter by disabled routers should be ok", async () => { 80 | const all = manager.getRouters(); 81 | // return all routers if no disabled routers 82 | manager.disabledRouters = []; 83 | const r1 = manager.getAvailableRouters(); 84 | expect(r1.length).toEqual(all.length); 85 | 86 | // filter out 1 router if 1 router is disabled 87 | manager.disabledRouters = [{ from: "karura", to: "khala", token: "AUSD" }]; 88 | const r2 = manager.getAvailableRouters(); 89 | expect(r2.length).toEqual(all.length - 1); 90 | 91 | // filter out 3 routers if 3 karura -> khala routers are disabled 92 | manager.disabledRouters = [{ from: "karura", to: "khala" }]; 93 | const r3 = manager.getAvailableRouters(); 94 | expect(r3.length).toEqual(all.length - 3); 95 | 96 | // filter out 4 karura routers if 4 karura routers are disabled 97 | manager.disabledRouters = [{ from: "karura" }]; 98 | const r4 = manager.getAvailableRouters(); 99 | expect(r4.length).toEqual(all.length - 4); 100 | }); 101 | 102 | beforeAll(async () => { 103 | try { 104 | await initSDK(); 105 | } catch (err) { 106 | // ignore node disconnect issue 107 | } 108 | }); 109 | }); 110 | -------------------------------------------------------------------------------- /src/cross-chain-router.ts: -------------------------------------------------------------------------------- 1 | import { isEmpty, overEvery, uniqWith } from "lodash"; 2 | 3 | import { isChainEqual } from "./utils/is-chain-equal"; 4 | import { BaseCrossChainAdapter } from "./base-chain-adapter"; 5 | import { ChainId, chains } from "./configs"; 6 | import { Chain, CrossChainRouter, RouteConfigs, RouterFilter } from "./types"; 7 | import { fetchConfigFromApiOrLocal } from "./utils"; 8 | 9 | interface BridgeRouterManagerConfigs { 10 | adapters: BaseCrossChainAdapter[]; 11 | disabledRouters?: RouterFilter[] | string; 12 | } 13 | 14 | export class BridgeRouterManager { 15 | private routers: CrossChainRouter[]; 16 | private adapters: BaseCrossChainAdapter[]; 17 | public disabledRouters: RouterFilter[] = []; 18 | private configs?: BridgeRouterManagerConfigs; 19 | 20 | constructor(configs?: BridgeRouterManagerConfigs) { 21 | this.routers = []; 22 | this.adapters = configs?.adapters || []; 23 | this.configs = configs; 24 | } 25 | 26 | public async init() { 27 | if (this.configs?.disabledRouters) { 28 | try { 29 | const disabledRouters = await fetchConfigFromApiOrLocal( 30 | this.configs.disabledRouters, 31 | (i: any) => i.disabledRoute 32 | ); 33 | this.disabledRouters = disabledRouters; 34 | } catch (e) { 35 | return false; 36 | } 37 | } 38 | 39 | return true; 40 | } 41 | 42 | public findAdapterByName( 43 | chain: ChainId | Chain 44 | ): BaseCrossChainAdapter | undefined { 45 | return this.adapters.find((i) => isChainEqual(chain, i.chain)); 46 | } 47 | 48 | public addRouter(router: RouteConfigs, checkAdapter = true) { 49 | const { token, xcm } = router; 50 | const from = 51 | typeof router.from === "string" ? chains[router.from] : router.from; 52 | const to = typeof router.to === "string" ? chains[router.to] : router.to; 53 | 54 | // push routers if from adapter is set 55 | if (!checkAdapter || (this.findAdapterByName(from) && checkAdapter)) { 56 | this.routers.push({ from, to, token, xcm }); 57 | } 58 | } 59 | 60 | public addRouters(routers: RouteConfigs[], checkAdapter = true) { 61 | routers.map((i) => this.addRouter(i, checkAdapter)); 62 | } 63 | 64 | public getRouters(params?: RouterFilter): CrossChainRouter[] { 65 | // return all router configs when params is empty 66 | if (!params || isEmpty(params)) { 67 | return this.routers; 68 | } 69 | 70 | const compares = overEvery( 71 | Object.entries(params) 72 | .map(([k, v]): undefined | ((i: CrossChainRouter) => boolean) => { 73 | switch (k) { 74 | case "from": 75 | return (i: CrossChainRouter) => isChainEqual(i.from, v); 76 | case "to": 77 | return (i: CrossChainRouter) => isChainEqual(i.to, v); 78 | 79 | case "token": { 80 | return (i: CrossChainRouter) => i.token === v; 81 | } 82 | } 83 | 84 | return undefined; 85 | }) 86 | .filter((i) => !!i) as ((i: CrossChainRouter) => boolean)[] 87 | ); 88 | 89 | return this.routers.filter((i) => compares(i)); 90 | } 91 | 92 | // filter routers with disabled list from config 93 | public getAvailableRouters(): CrossChainRouter[] { 94 | return this.routers.filter( 95 | (e) => 96 | !this.disabledRouters.find( 97 | (i) => 98 | (!i.from || i.from === e.from.id) && 99 | (!i.to || i.to === e.to.id) && 100 | (!i.token || i.token === e.token) 101 | ) 102 | ); 103 | } 104 | 105 | public getDestinationChains(params: Omit): Chain[] { 106 | if (isEmpty(params)) { 107 | return []; 108 | } 109 | 110 | return uniqWith( 111 | this.getRouters(params).map((i) => i.to), 112 | (i, j) => isChainEqual(i, j) 113 | ); 114 | } 115 | 116 | public getFromChains(params: Omit): Chain[] { 117 | if (isEmpty(params)) { 118 | return []; 119 | } 120 | 121 | return uniqWith( 122 | this.getRouters(params).map((i) => i.from), 123 | (i, j) => isChainEqual(i, j) 124 | ); 125 | } 126 | 127 | public getAvailableTokens(params: Omit): string[] { 128 | if (isEmpty(params)) { 129 | return []; 130 | } 131 | 132 | return uniqWith( 133 | this.getRouters(params).map((i) => i.token), 134 | (i, j) => i === j 135 | ); 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /src/errors.ts: -------------------------------------------------------------------------------- 1 | export class RouterConfigNotFound extends Error { 2 | constructor(token: string, destChain: string, network: string) { 3 | super(); 4 | 5 | this.message = `can't find ${token} to ${destChain} router in ${network} network`; 6 | this.name = "RouterConfigNotFound"; 7 | } 8 | } 9 | 10 | export class AdapterNotFound extends Error { 11 | constructor(network: string) { 12 | super(); 13 | 14 | this.message = `${network} adapter not find`; 15 | this.name = "AdapterNotFound"; 16 | } 17 | } 18 | 19 | export class ApiNotFound extends Error { 20 | constructor(network: string) { 21 | super(); 22 | 23 | this.message = `Api not set for ${network} adapter`; 24 | this.name = "ApiNotFound"; 25 | } 26 | } 27 | 28 | export class TokenNotFound extends Error { 29 | constructor(token: string, network?: string) { 30 | super(); 31 | 32 | if (network) { 33 | this.message = `can't find ${token} in ${network}`; 34 | } else { 35 | this.message = `can't find ${token} in current network`; 36 | } 37 | 38 | this.name = "TokenNotFound"; 39 | } 40 | } 41 | 42 | export class NoCrossChainAdapterFound extends Error { 43 | constructor(name: string) { 44 | super(); 45 | 46 | this.message = `Can't find ${name} adapter, please registed it first before use.`; 47 | this.name = "NoCrossChainAdapterFound"; 48 | } 49 | } 50 | 51 | export class DestinationWeightNotFound extends Error { 52 | constructor(source: string, destination: string, token: string) { 53 | super(); 54 | 55 | this.message = `Can't find ${source} addapter's destination weight for ${destination} destination sending ${token} token.`; 56 | this.name = "DestinationWeightNotFound"; 57 | } 58 | } 59 | 60 | export class InvalidAddress extends Error { 61 | constructor(address: string) { 62 | super(); 63 | 64 | this.message = `Address ${address} is invalid.`; 65 | this.name = "InvalidAddress"; 66 | } 67 | } 68 | 69 | export class ModuleNotFound extends Error { 70 | constructor(module: string) { 71 | super(); 72 | 73 | this.message = `${module} module not found`; 74 | this.name = "ModuleNotFound"; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./bridge"; 2 | export * from "./cross-chain-router"; 3 | export * from "./api-provider"; 4 | export * from "./adapters"; 5 | export * from "./configs/index"; 6 | export * from "./types"; 7 | -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | import { FixedPointNumber } from "@acala-network/sdk-core"; 2 | 3 | import { BaseCrossChainAdapter } from "./base-chain-adapter"; 4 | import { ChainId } from "./configs"; 5 | 6 | export { FixedPointNumber as FN } from "@acala-network/sdk-core"; 7 | 8 | export type ChainType = "substrate" | "ethereum"; 9 | 10 | export interface Chain { 11 | // unique chain id 12 | readonly id: ChainId; 13 | // chain name for display 14 | readonly display: string; 15 | // chain is `substract` or `ethereum` like 16 | readonly type: ChainType; 17 | // chain icon resource path 18 | readonly icon: string; 19 | // set id to -1 when the chain is para chain 20 | readonly paraChainId: number; 21 | // the chain SS58 Prefix 22 | readonly ss58Prefix: number; 23 | } 24 | 25 | export interface BasicToken { 26 | name: string; 27 | symbol: string; 28 | decimals: number; 29 | ed: string; 30 | } 31 | 32 | export interface ExtendedToken extends BasicToken { 33 | toRaw: () => any; 34 | } 35 | 36 | export interface RouteConfigs { 37 | // from chain name 38 | from: ChainId; 39 | // to chain name 40 | to: ChainId; 41 | // token name 42 | token: string; 43 | // xcm config 44 | xcm?: XCMTransferConfigs; 45 | } 46 | 47 | export interface CrossChainRouter { 48 | // from chain name 49 | from: Chain; 50 | // to chain name 51 | to: Chain; 52 | // token name 53 | token: string; 54 | // xcm config 55 | xcm?: XCMTransferConfigs; 56 | } 57 | 58 | export interface XCMTransferConfigs { 59 | // XCM transfer weight limit 60 | weightLimit?: string | "Unlimited" | "Limited"; 61 | // XCM transfer fee charged by `to chain` 62 | fee: { 63 | token: string; 64 | amount: string; 65 | }; 66 | // XCM delivery fee charged by `from chain` 67 | deliveryFee?: { 68 | token: string; 69 | amount: string; 70 | }; 71 | } 72 | 73 | export interface NetworkProps { 74 | ss58Format: number; 75 | tokenDecimals: number[]; 76 | tokenSymbol: string[]; 77 | } 78 | 79 | export interface TransferParams { 80 | address: string; 81 | amount: FixedPointNumber; 82 | to: ChainId; 83 | token: string; 84 | } 85 | 86 | export interface TransferParamsWithSigner extends TransferParams { 87 | signer: string; 88 | } 89 | 90 | export interface InputConfig { 91 | minInput: FixedPointNumber; 92 | maxInput: FixedPointNumber; 93 | ss58Prefix: number; 94 | destFee: TokenBalance; 95 | estimateFee: TokenBalance; 96 | xcmFee: TokenBalance | null; 97 | } 98 | 99 | export interface RouterFilter { 100 | from?: Chain | ChainId; 101 | to?: Chain | ChainId; 102 | token?: string; 103 | } 104 | 105 | export interface BridgeConfigs { 106 | adapters: BaseCrossChainAdapter[]; 107 | disabledRouters?: RouterFilter[] | string; 108 | } 109 | 110 | export interface BalanceChangeConfig { 111 | token: string; 112 | address: string; 113 | amount: FixedPointNumber; 114 | tolerance?: number; 115 | timeout?: number; 116 | } 117 | 118 | export enum BalanceChangeStatue { 119 | "CHECKING", 120 | "SUCCESS", 121 | "TIMEOUT", 122 | "UNKNOWN_ERROR", 123 | } 124 | 125 | export interface TokenBalance { 126 | token: string; 127 | balance: T; 128 | } 129 | 130 | export interface BalanceData { 131 | free: FixedPointNumber; 132 | locked: FixedPointNumber; 133 | reserved: FixedPointNumber; 134 | available: FixedPointNumber; 135 | } 136 | -------------------------------------------------------------------------------- /src/utils/check-message-version.ts: -------------------------------------------------------------------------------- 1 | import { AnyApi } from "@acala-network/sdk-core"; 2 | 3 | export type XCMVersion = "V1" | "V3" | "V4"; 4 | 5 | export function checkMessageVersion(api: AnyApi) { 6 | let version: XCMVersion = "V1"; 7 | 8 | try { 9 | const keys = (api?.createType("XcmVersionedMultiLocation") as any) 10 | .defKeys as string[]; 11 | 12 | if (keys.includes("V3")) { 13 | version = "V3"; 14 | } 15 | } catch (e) { 16 | // ignore 17 | } 18 | 19 | try { 20 | const keys = (api?.createType("StagingXcmVersionedMultiLocation") as any) 21 | .defKeys as string[]; 22 | 23 | if (keys.includes("V3")) { 24 | version = "V3"; 25 | } 26 | } catch (e) { 27 | // ignore 28 | } 29 | 30 | try { 31 | api?.createType("XcmVersionedLocation") as any; 32 | 33 | version = "V4"; 34 | } catch (e) { 35 | // ignore 36 | } 37 | 38 | return version; 39 | } 40 | -------------------------------------------------------------------------------- /src/utils/create-route-configs.ts: -------------------------------------------------------------------------------- 1 | import { ChainId } from "../configs"; 2 | import { RouteConfigs } from "../types"; 3 | 4 | export function createRouteConfigs( 5 | from: ChainId, 6 | routes: Omit[] 7 | ) { 8 | return routes.map((route) => ({ ...route, from })); 9 | } 10 | -------------------------------------------------------------------------------- /src/utils/destination-utils.ts: -------------------------------------------------------------------------------- 1 | import { AnyApi } from "@acala-network/sdk-core"; 2 | 3 | import { getValidDestAddrType, AddressType } from "./validate-address"; 4 | 5 | export const getDestAccountType = ( 6 | address: string, 7 | token: string, 8 | to?: string 9 | ): "AccountKey20" | "AccountId32" => { 10 | const addrType = getValidDestAddrType(address, token, to); 11 | if (addrType === "ethereum") { 12 | return "AccountKey20"; 13 | } else { 14 | return "AccountId32"; 15 | } 16 | }; 17 | 18 | export const getDestAccountId = ( 19 | address: string, 20 | api: AnyApi, 21 | token: string, 22 | to?: string 23 | ): `0x${string}` => { 24 | const addrType = getValidDestAddrType(address, token, to); 25 | if (addrType === "ethereum") { 26 | return api.createType("AccountId20", address).toHex(); 27 | } else { 28 | return api.createType("AccountId32", address).toHex(); 29 | } 30 | }; 31 | 32 | export const getDestAccountInfo = ( 33 | address: string, 34 | token: string, 35 | api: AnyApi, 36 | to?: string 37 | ): { 38 | addrType: AddressType; 39 | accountId: `0x${string}`; 40 | accountType: "AccountKey20" | "AccountId32"; 41 | } => { 42 | return { 43 | addrType: getValidDestAddrType(address, token, to), 44 | accountId: getDestAccountId(address, api, token, to), 45 | accountType: getDestAccountType(address, token, to), 46 | }; 47 | }; 48 | -------------------------------------------------------------------------------- /src/utils/fetch-config-from-api-or-local.ts: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | 3 | export async function fetchConfigFromApiOrLocal( 4 | config: string | T, 5 | getter?: (i: any) => T 6 | ): Promise { 7 | if (typeof config === "string" && config.startsWith("http")) { 8 | const data = await axios.get(config); 9 | 10 | return getter ? getter(data.data) : (data.data as unknown as T); 11 | } else { 12 | return config as T; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/utils/get-xcm-delivery-fee.ts: -------------------------------------------------------------------------------- 1 | import { AnyApi, FixedPointNumber } from "@acala-network/sdk-core"; 2 | import { XcmVersionedXcm } from "@polkadot/types/lookup"; 3 | import { Codec, Observable } from "@polkadot/types/types"; 4 | import { firstValueFrom } from "rxjs"; 5 | 6 | import { ChainId, chains } from "../configs"; 7 | 8 | // refer to https://github.com/albertov19/xcmTools/blob/main/calculateKusamaDeliveryFees.ts 9 | // delivery_fee_factor * (base_fee + encoded_msg_len * per_byte_fee) 10 | 11 | /** 12 | * Only for polkadot and kusama dmp for now 13 | * 14 | * FIXME: This is not accurate, need to get the real xcm message from client. Currently use the hardcoded value in each route config 15 | */ 16 | export async function getPolkadotXcmDeliveryFee( 17 | from: ChainId, 18 | to: ChainId, 19 | fromApi?: AnyApi 20 | ) { 21 | if (from !== "polkadot" && from !== "kusama") { 22 | throw new Error("from chain must be polkadot or kusama"); 23 | } 24 | if (!fromApi) return FixedPointNumber.ZERO; 25 | const decimal = fromApi?.registry.chainDecimals[0]; 26 | const paraID = chains[to].paraChainId; 27 | 28 | // https://github.com/polkadot-fellows/runtimes/blob/16635f6abda9c5fe4b62231d632ab6f6695b48f7/system-parachains/constants/src/polkadot.rs#L60C29-L60C43 29 | // https://github.com/polkadot-fellows/runtimes/blob/16635f6abda9c5fe4b62231d632ab6f6695b48f7/system-parachains/constants/src/kusama.rs#L38 30 | const UNITS = 31 | from === "polkadot" ? BigInt(10000000000) : BigInt(1000000000000); 32 | const QUID = UNITS / BigInt(30); 33 | const CENTS = QUID / BigInt(100); 34 | // const GRAND = QUID / BigInt(1000); 35 | const MILLICENTS = CENTS / BigInt(1000); 36 | 37 | const byteFee = BigInt(10) * MILLICENTS; 38 | const baseDeliveryFee = BigInt(3) * CENTS; 39 | 40 | const exampleXcm: XcmVersionedXcm = fromApi?.createType( 41 | "XcmVersionedXcm", 42 | exampleXcmMessage 43 | ) as any; 44 | const xcmBytes = exampleXcm.toU8a(); 45 | 46 | const deliveryFeeFactor: Codec = await (fromApi?.type === "rxjs" 47 | ? firstValueFrom( 48 | fromApi?.query.dmp.deliveryFeeFactor( 49 | BigInt(paraID) 50 | ) as Observable 51 | ) 52 | : (fromApi?.query.dmp.deliveryFeeFactor(BigInt(paraID)) as Promise)); 53 | 54 | const convDeliveryFeeFactor = 55 | BigInt(deliveryFeeFactor.toString()) / BigInt(10 ** 18); 56 | 57 | const fee = 58 | convDeliveryFeeFactor * 59 | (baseDeliveryFee + BigInt(xcmBytes.length) * byteFee); 60 | 61 | return FixedPointNumber.fromInner(fee.toString(), decimal).mul( 62 | new FixedPointNumber(1.2) // add some buffer 63 | ); 64 | } 65 | 66 | const exampleXcmMessage = { 67 | V3: [ 68 | { 69 | WithdrawAsset: [ 70 | { 71 | assets: [ 72 | { 73 | ConcreteFungible: { 74 | id: "Here", 75 | amount: "20000000000000", 76 | }, 77 | }, 78 | ], 79 | effects: [ 80 | { 81 | BuyExecution: { 82 | fees: "0", 83 | weight_limit: { 84 | Unlimited: null, 85 | }, 86 | }, 87 | }, 88 | { 89 | DepositAsset: { 90 | assets: "All", 91 | dest: { 92 | X1: { 93 | Parachain: "2000", 94 | }, 95 | }, 96 | }, 97 | }, 98 | { 99 | DepositReserveAsset: { 100 | assets: "All", 101 | dest: { 102 | X1: { 103 | AccountId32: { 104 | network: "Any", 105 | id: "0x76c7e05b8ee00557f8f7e11f283aacd3bbee59c1d9fd588ebbe1b6c435fe504c", 106 | }, 107 | }, 108 | }, 109 | effects: [], 110 | }, 111 | }, 112 | ], 113 | }, 114 | ], 115 | }, 116 | ], 117 | }; 118 | -------------------------------------------------------------------------------- /src/utils/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./fetch-config-from-api-or-local"; 2 | export * from "./is-chain-equal"; 3 | export * from "./validate-address"; 4 | export * from "./xtokens-params"; 5 | export * from "./polkadot-xcm-params"; 6 | export * from "./create-route-configs"; 7 | export * from "./destination-utils"; 8 | export * from "./get-xcm-delivery-fee"; 9 | -------------------------------------------------------------------------------- /src/utils/is-chain-equal.ts: -------------------------------------------------------------------------------- 1 | import { ChainId } from "../configs"; 2 | import { Chain } from "../types"; 3 | 4 | export function isChainEqual( 5 | c1: Chain | ChainId, 6 | c2: Chain | ChainId 7 | ): boolean { 8 | const c1Name = typeof c1 === "string" ? c1 : c1.id; 9 | const c2Name = typeof c2 === "string" ? c2 : c2.id; 10 | 11 | return c1Name.toLowerCase() === c2Name.toLowerCase(); 12 | } 13 | -------------------------------------------------------------------------------- /src/utils/polkadot-xcm-params.ts: -------------------------------------------------------------------------------- 1 | import { AnyApi } from "@acala-network/sdk-core"; 2 | import { checkMessageVersion } from "./check-message-version"; 3 | 4 | export type XCMType = "V0" | "V1" | "V2" | "V3" | "V4"; 5 | 6 | export function createPolkadotXCMDest( 7 | api: AnyApi, 8 | parachainId: number, 9 | parents = 1, 10 | targetVersion: XCMType | undefined = undefined 11 | ): any { 12 | const version = targetVersion ?? checkMessageVersion(api); 13 | 14 | if (version === "V4") { 15 | return { 16 | V4: { 17 | parents, 18 | interior: { X1: [{ Parachain: parachainId }] }, 19 | }, 20 | }; 21 | } 22 | 23 | return { 24 | [version]: { 25 | parents, 26 | interior: { X1: { Parachain: parachainId } }, 27 | }, 28 | }; 29 | } 30 | 31 | export function createPolkadotXCMAccount( 32 | api: AnyApi, 33 | accountId: string, 34 | accountType = "AccountId32", 35 | targetVersion: XCMType | undefined = undefined 36 | ): any { 37 | const version = targetVersion ?? checkMessageVersion(api); 38 | 39 | if (version === "V4") { 40 | return { 41 | V4: { 42 | parents: 0, 43 | interior: { 44 | X1: [ 45 | { 46 | [accountType]: { 47 | [accountType === "AccountId32" ? "id" : "key"]: accountId, 48 | network: undefined, 49 | }, 50 | }, 51 | ], 52 | }, 53 | }, 54 | }; 55 | } 56 | 57 | return { 58 | [version]: { 59 | parents: 0, 60 | interior: { 61 | X1: { 62 | [accountType]: { 63 | [accountType === "AccountId32" ? "id" : "key"]: accountId, 64 | network: version === "V1" ? "Any" : undefined, 65 | }, 66 | }, 67 | }, 68 | }, 69 | }; 70 | } 71 | 72 | export function createPolkadotXCMAsset( 73 | api: AnyApi, 74 | amount: string, 75 | position: "NATIVE" | any[], 76 | targetVersion: XCMType | undefined = undefined 77 | ): any { 78 | const version = targetVersion ?? checkMessageVersion(api); 79 | let tokenPosition: any = 80 | position === "NATIVE" 81 | ? { 82 | id: { Concrete: { parents: 0, interior: "Here" } }, 83 | } 84 | : { 85 | id: { 86 | Concrete: { 87 | parents: 1, 88 | interior: { 89 | X2: position, 90 | }, 91 | }, 92 | }, 93 | }; 94 | 95 | if (version === "V4") { 96 | tokenPosition = 97 | position === "NATIVE" 98 | ? { 99 | id: { 100 | parents: 0, 101 | interior: "Here", 102 | }, 103 | } 104 | : { 105 | id: { 106 | paraents: 1, 107 | interior: { 108 | X2: position, 109 | }, 110 | }, 111 | }; 112 | } 113 | 114 | return { 115 | [version]: [ 116 | { 117 | ...tokenPosition, 118 | fun: { Fungible: amount }, 119 | }, 120 | ], 121 | }; 122 | } 123 | -------------------------------------------------------------------------------- /src/utils/unit-test.ts: -------------------------------------------------------------------------------- 1 | export function formateRouteLogLine( 2 | token: string, 3 | from: string, 4 | to: string, 5 | method: string 6 | ) { 7 | return `| ${token.padEnd(8, " ")} | ${from.padEnd(10, " ")} -> ${to.padEnd( 8 | 10, 9 | " " 10 | )} | ${method.padEnd(12, " ")} ✅ |`; 11 | } 12 | 13 | export function logFormatedRoute(prefix: string, content: string[]) { 14 | const divider = " ".padEnd(content[0].length, "-"); 15 | console.log(`${prefix}${divider}\n${content.join("\n")}\n${divider}\n`); 16 | } 17 | -------------------------------------------------------------------------------- /src/utils/validate-address.ts: -------------------------------------------------------------------------------- 1 | import { encodeAddress, decodeAddress } from "@polkadot/keyring"; 2 | import * as ethers from "ethers"; 3 | 4 | export type AddressType = "substract" | "ethereum"; 5 | 6 | const SUPPORTED_TOKEN_ERC20: { 7 | [x in string]?: { [y: string]: string }; 8 | } = { 9 | karura: { 10 | TAI: "0x0000000000000000000100000000000000000084", 11 | tKSM: "0x0000000000000000000300000000000000000000", 12 | KAR: "0x0000000000000000000100000000000000000080", 13 | LKSM: "0x0000000000000000000100000000000000000083", 14 | aUSD: "0x0000000000000000000100000000000000000081", 15 | aSEED: "0x0000000000000000000100000000000000000081", 16 | KSM: "0x0000000000000000000100000000000000000082", 17 | USDCet: "0x1f3a10587a20114ea25ba1b388ee2dd4a337ce27", 18 | }, 19 | acala: { 20 | tDOT: "0x0000000000000000000300000000000000000000", 21 | aUSD: "0x0000000000000000000100000000000000000001", 22 | aSEED: "0x0000000000000000000100000000000000000001", 23 | ACA: "0x0000000000000000000100000000000000000000", 24 | DOT: "0x0000000000000000000100000000000000000002", 25 | LDOT: "0x0000000000000000000100000000000000000003", 26 | USDCet: "0x07DF96D1341A7d16Ba1AD431E2c847d978BC2bCe", 27 | }, 28 | mandala: {}, 29 | }; 30 | 31 | export const isAcalaSupportedErc20 = (toChain: string, tokenName: string) => { 32 | const supportedTokens = Object.keys(SUPPORTED_TOKEN_ERC20[toChain] || {}).map( 33 | (token) => token.toUpperCase() 34 | ); 35 | return supportedTokens.includes(tokenName.toUpperCase()); 36 | }; 37 | 38 | /** 39 | * Get the valid destination address type for the given combination of 40 | * address, token name and destination chain name. 41 | * 42 | * Return "ethereum" or "substract", 43 | * only acala or karura supports ethereum address type as destination. 44 | * 45 | * @param address account address 46 | * @param token token name in string 47 | * @param to destination chain name 48 | */ 49 | export const getValidDestAddrType = ( 50 | address: string, 51 | token: string, 52 | to?: string 53 | ): AddressType => { 54 | if ( 55 | (to === "acala" || to === "karura") && 56 | address.startsWith("0x") && 57 | isAcalaSupportedErc20(to, token) 58 | ) { 59 | return "ethereum"; 60 | } 61 | return "substract"; 62 | }; 63 | 64 | function validateEthereumAddress(address: string) { 65 | return ethers.utils.isAddress(address); 66 | } 67 | 68 | function validateSubstrateAddress(address: string) { 69 | try { 70 | encodeAddress(decodeAddress(address)); 71 | 72 | return true; 73 | } catch (e) { 74 | // ignore error 75 | } 76 | 77 | return false; 78 | } 79 | 80 | export function validateAddress( 81 | address: string, 82 | type: AddressType = "substract" 83 | ) { 84 | if (type === "ethereum") return validateEthereumAddress(address); 85 | 86 | return validateSubstrateAddress(address); 87 | } 88 | -------------------------------------------------------------------------------- /src/utils/xtokens-params.ts: -------------------------------------------------------------------------------- 1 | import { AnyApi } from "@acala-network/sdk-core"; 2 | import { XCMVersion, checkMessageVersion } from "./check-message-version"; 3 | 4 | interface DestConfigs { 5 | useAccountKey20?: boolean; 6 | isToRelayChain?: boolean; 7 | } 8 | 9 | function createToRelayChainDestParam(version: XCMVersion, accountId: string) { 10 | if (version === "V1") { 11 | return { 12 | V1: { 13 | parents: 1, 14 | interior: { X1: { AccountId32: { id: accountId, network: "Any" } } }, 15 | }, 16 | }; 17 | } 18 | 19 | if (version === "V4") { 20 | return { 21 | [version]: { 22 | parents: 1, 23 | interior: { X1: [{ AccountId32: { id: accountId } }] }, 24 | }, 25 | }; 26 | } 27 | 28 | return { 29 | [version]: { 30 | parents: 1, 31 | interior: { X1: { AccountId32: { id: accountId } } }, 32 | }, 33 | }; 34 | } 35 | 36 | export function createXTokensDestParam( 37 | api: AnyApi, 38 | paraChainId: number, 39 | accountId: string, 40 | configs?: DestConfigs 41 | ) { 42 | const version = checkMessageVersion(api); 43 | const { useAccountKey20, isToRelayChain } = configs || {}; 44 | 45 | const accountKeyName = useAccountKey20 ? "AccountKey20" : "AccountId32"; 46 | 47 | if (isToRelayChain) { 48 | return createToRelayChainDestParam(version, accountId); 49 | } 50 | 51 | if (version === "V1") { 52 | return { 53 | V1: { 54 | parents: 1, 55 | interior: { 56 | X2: [ 57 | { Parachain: paraChainId }, 58 | { 59 | [accountKeyName]: { 60 | [useAccountKey20 ? "key" : "id"]: accountId, 61 | network: "Any", 62 | }, 63 | }, 64 | ], 65 | }, 66 | }, 67 | }; 68 | } 69 | 70 | // for message version v3 & v4 71 | return { 72 | [version]: { 73 | parents: 1, 74 | interior: { 75 | X2: [ 76 | { Parachain: paraChainId }, 77 | { 78 | [accountKeyName]: { 79 | [useAccountKey20 ? "key" : "id"]: accountId, 80 | }, 81 | }, 82 | ], 83 | }, 84 | }, 85 | }; 86 | } 87 | 88 | export function createXTokensAssetsParam( 89 | api: AnyApi, 90 | paraChainId: number, 91 | assetId: any, 92 | amount: string 93 | ) { 94 | const version = checkMessageVersion(api); 95 | 96 | if (version === "V1") { 97 | return { 98 | V1: { 99 | fun: { 100 | Fungible: amount, 101 | }, 102 | id: { 103 | Concrete: { 104 | parents: 1, 105 | interior: { 106 | X3: [ 107 | { Parachain: paraChainId }, 108 | { PalletInstance: 50 }, 109 | { GeneralIndex: assetId }, 110 | ], 111 | }, 112 | }, 113 | }, 114 | }, 115 | }; 116 | } 117 | 118 | if (version === "V4") { 119 | return { 120 | V4: { 121 | fun: { 122 | Fungible: amount, 123 | }, 124 | id: { 125 | parents: 1, 126 | interior: { 127 | X3: [ 128 | { Parachain: paraChainId }, 129 | { PalletInstance: 50 }, 130 | { GeneralIndex: assetId }, 131 | ], 132 | }, 133 | }, 134 | }, 135 | }; 136 | } 137 | 138 | return { 139 | [version]: { 140 | fun: { 141 | Fungible: amount, 142 | }, 143 | id: { 144 | Concrete: { 145 | parents: 1, 146 | interior: { 147 | X3: [ 148 | { Parachain: paraChainId }, 149 | { PalletInstance: 50 }, 150 | { GeneralIndex: assetId }, 151 | ], 152 | }, 153 | }, 154 | }, 155 | }, 156 | }; 157 | } 158 | 159 | export function createXTokensWeight(api: AnyApi, weight: string) { 160 | const tx = api.tx.xTokens?.transfer || api.tx.ormlXTokens?.transfer; 161 | 162 | if (!tx) throw new Error("xTokens.transfer tx is not found"); 163 | 164 | const weightType = tx.meta.args[3].type.toString(); 165 | const isUnlimited = 166 | weightType === "XcmV3WeightLimit" || weightType === "XcmV2WeightLimit"; 167 | 168 | return isUnlimited ? "Unlimited" : weight; 169 | } 170 | -------------------------------------------------------------------------------- /tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@open-web3/dev-config/config/tsconfig.json", 3 | "exclude": ["build/**/*", "**/build/**/*"], 4 | "compilerOptions": { 5 | "target": "es2019", 6 | "module": "CommonJS", 7 | "baseUrl": ".", 8 | "resolveJsonModule": true, 9 | "skipLibCheck": true, 10 | "emitDeclarationOnly": false, 11 | "outDir": "build", 12 | "typeRoots": ["./node_modules/@polkadot/ts", "./node_modules/@types"] 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@open-web3/dev-config/config/tsconfig.json", 3 | "exclude": ["build/**/*", "**/build/**/*", "scripts/**/*", "docs/**/*", "**/*.spec.ts"], 4 | "compilerOptions": { 5 | "target": "es2019", 6 | "module": "CommonJS", 7 | "baseUrl": ".", 8 | "resolveJsonModule": true, 9 | "skipLibCheck": true, 10 | "emitDeclarationOnly": false, 11 | "outDir": "build", 12 | "typeRoots": ["./node_modules/@polkadot/ts", "./node_modules/@types"] 13 | } 14 | } 15 | --------------------------------------------------------------------------------