├── .gitattributes
├── .github
├── renovate.json
└── workflows
│ ├── publish_devel.yaml
│ ├── publish_mainline.yaml
│ └── tests.yaml
├── .gitignore
├── .mocharc.yml
├── .nvmrc
├── .yarnclean
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── docs
├── .nojekyll
├── assets
│ ├── highlight.css
│ ├── main.js
│ ├── search.js
│ └── style.css
├── classes
│ ├── AllowanceError.html
│ ├── BaseToken.html
│ ├── Bridge.SynapseBridge.html
│ ├── Networks.Network.html
│ ├── SwapPools.ETHSwapToken.html
│ ├── SwapPools.SwapToken.html
│ ├── TransactionError.html
│ ├── UnsupportedSwapErrors.UnsupportedSwapError.html
│ └── WrapperToken.html
├── enums
│ └── UnsupportedSwapErrors.UnsupportedSwapErrorKind.html
├── functions
│ ├── Bridge.bridgeSwapSupported.html
│ ├── Bridge.checkBridgeTransactionComplete.html
│ ├── Bridge.getRequiredConfirmationsForBridge.html
│ ├── L1BridgeZapContractInstance.html
│ ├── L2BridgeZapContractInstance.html
│ ├── Networks.fromChainId.html
│ ├── Networks.networkName.html
│ ├── Networks.networkSupportsToken.html
│ ├── Networks.supportedNetworks.html
│ ├── Slippages._applySlippage.html
│ ├── Slippages.addSlippage.html
│ ├── Slippages.formatSlippageToString.html
│ ├── Slippages.subtractSlippage.html
│ ├── SwapPools.ethSwapPoolForNetwork.html
│ ├── SwapPools.getAllSwappableTokensForNetwork.html
│ ├── SwapPools.stableswapPoolForNetwork.html
│ ├── SwapPools.swapGroupsForChain.html
│ ├── SwapPools.swapPoolTokenForTypeForChain.html
│ ├── SwapPools.swapPoolTokenFromLPTokenAddress.html
│ ├── SwapPools.swapPoolTokenFromSwapAddress.html
│ ├── SwapPools.swapPoolTokensForChainId.html
│ ├── SwapPools.tokensForChainBySwapGroup.html
│ ├── SynapseBridgeContractInstance.html
│ ├── TokenSwap.addLiquidity.html
│ ├── TokenSwap.bridgeSwapSupported.html
│ ├── TokenSwap.buildAddLiquidityTransaction.html
│ ├── TokenSwap.buildRemoveLiquidityOneTokenTransaction.html
│ ├── TokenSwap.buildRemoveLiquidityTransaction.html
│ ├── TokenSwap.buildSwapTokensTransaction.html
│ ├── TokenSwap.calculateAddLiquidity.html
│ ├── TokenSwap.calculateRemoveLiquidity.html
│ ├── TokenSwap.calculateRemoveLiquidityOneToken.html
│ ├── TokenSwap.calculateSwapRate.html
│ ├── TokenSwap.detailedTokenSwapMap-1.html
│ ├── TokenSwap.intermediateTokens.html
│ ├── TokenSwap.removeLiquidity.html
│ ├── TokenSwap.removeLiquidityOneToken.html
│ ├── TokenSwap.swapContract.html
│ ├── TokenSwap.swapSetup.html
│ ├── TokenSwap.swapSupported.html
│ ├── TokenSwap.swapTokens.html
│ ├── Tokens.approveTokenSpend.html
│ ├── Tokens.checkTokenAllowance.html
│ ├── Tokens.gasTokenForChain.html
│ ├── Tokens.gasTokenWrapper.html
│ ├── Tokens.isMintBurnToken.html
│ ├── Tokens.tokenFromSymbol.html
│ ├── UnsupportedSwapErrors.nonMatchingSwapTypes.html
│ ├── UnsupportedSwapErrors.tokenNotSupported.html
│ ├── UnsupportedSwapErrors.tokenNotSupportedNetFrom.html
│ ├── UnsupportedSwapErrors.tokenNotSupportedNetTo.html
│ ├── UnsupportedSwapErrors.unsupportedMultiJEWELMigration.html
│ ├── allNetworksSwapTokensMap.html
│ ├── chainSupportsEIP1559.html
│ ├── configureRPCEndpoints.html
│ ├── fetchChainGasPrices.html
│ ├── makeTransactionGasOverrides.html
│ ├── networkSwapTokensMap.html
│ ├── populateGasOptions.html
│ ├── supportedChainIds.html
│ ├── supportedNetworks.html
│ ├── useAddLiquidity.html
│ ├── useApproveBridgeSwap.html
│ ├── useApproveLPToken.html
│ ├── useApprovePoolToken.html
│ ├── useBridgeAllowance.html
│ ├── useBridgeSwapApproval.html
│ ├── useCalculateAddLiquidity.html
│ ├── useCalculateBridgeSwapOutput.html
│ ├── useCalculateRemoveLiquidity.html
│ ├── useCalculateRemoveLiquidityOneToken.html
│ ├── useCalculateSwapRate.html
│ ├── useChainETHSwapLPToken.html
│ ├── useChainStableswapLPToken.html
│ ├── useExecuteBridgeSwap.html
│ ├── useHarmonyAVAXLPToken.html
│ ├── useHarmonyJewelLPToken.html
│ ├── useLPTokenAllowance.html
│ ├── useLPTokenApproval.html
│ ├── useLPTokenNeedsApproval.html
│ ├── useNeedsBridgeSwapApproval.html
│ ├── usePoolTokenAllowance.html
│ ├── usePoolTokenApproval.html
│ ├── usePoolTokenNeedsApproval.html
│ ├── useRemoveLiquidity.html
│ ├── useRemoveLiquidityOneToken.html
│ └── useSwapTokens.html
├── index.html
├── interfaces
│ ├── Bridge.BridgeParams.html
│ ├── Bridge.BridgeTransactionCompleteParams.html
│ ├── Bridge.BridgeTransactionParams.html
│ ├── BridgeConfigV3Contract-1.html
│ ├── GasOptions.html
│ ├── L1BridgeZapContract.html
│ ├── L2BridgeZapContract.html
│ ├── RPCEndpointsConfig.html
│ ├── SwapPools.LPToken.html
│ ├── SwapPools.SwapPoolToken.html
│ ├── SynapseBridgeContract.html
│ ├── SynapseERC20Contract.html
│ ├── Token.html
│ ├── TokenSwap.AddLiquidityParams.html
│ ├── TokenSwap.AddRemoveLiquidityParams.html
│ ├── TokenSwap.BridgeSwapSupportedParams.html
│ ├── TokenSwap.RemoveLiquidityOneParams.html
│ ├── TokenSwap.RemoveLiquidityParams.html
│ ├── TokenSwap.SwapParams.html
│ ├── TokenSwap.SwapTokensParams.html
│ ├── Tokens.ApproveTokenParams.html
│ ├── Tokens.CheckTokenAllowanceParams.html
│ └── UnsupportedSwapErrors.UnsupportedSwapErrorArgs.html
├── modules.html
├── modules
│ ├── Bridge.html
│ ├── BridgeConfigV3Contract.html
│ ├── Networks.html
│ ├── Slippages.html
│ ├── SwapPools.html
│ ├── TokenSwap.html
│ ├── Tokens.html
│ └── UnsupportedSwapErrors.html
├── types
│ ├── Bridge.BridgeOutputEstimate.html
│ ├── Bridge.CanBridgeResult.html
│ ├── BridgeConfigV3Contract.PoolStruct.html
│ ├── BridgeConfigV3Contract.PoolStructOutput.html
│ ├── BridgeConfigV3Contract.TokenStruct.html
│ ├── BridgeConfigV3Contract.TokenStructOutput.html
│ ├── ChainGasPrices.html
│ ├── ChainId.html
│ ├── GenericZapBridgeContract.html
│ ├── NetworkSwappableTokensMap.html
│ ├── Slippages.Slippage.html
│ ├── SwapPools.PoolTokensAmountsMap.html
│ ├── SwapPools.SwapTypePoolTokens.html
│ ├── TokenSwap.DetailedTokenSwapMap.html
│ ├── TokenSwap.EstimatedSwapRate.html
│ ├── TokenSwap.IntermediateSwapTokens.html
│ └── TokenSwap.SwapSupportedResult.html
└── variables
│ ├── ChainId-1.html
│ ├── EIP1559Chains.html
│ ├── Networks.ARBITRUM.html
│ ├── Networks.AURORA.html
│ ├── Networks.AVALANCHE.html
│ ├── Networks.BOBA.html
│ ├── Networks.BSC.html
│ ├── Networks.CRONOS.html
│ ├── Networks.DFK.html
│ ├── Networks.ETH.html
│ ├── Networks.FANTOM.html
│ ├── Networks.HARMONY.html
│ ├── Networks.KLAYTN.html
│ ├── Networks.METIS.html
│ ├── Networks.MOONBEAM.html
│ ├── Networks.MOONRIVER.html
│ ├── Networks.OPTIMISM.html
│ ├── Networks.POLYGON.html
│ ├── Slippages.One.html
│ ├── Slippages.OneTenth.html
│ ├── Slippages.Quarter.html
│ ├── Slippages.TwoTenth.html
│ ├── SwapPools.ARBITRUM_ETH_SWAP_TOKEN.html
│ ├── SwapPools.ARBITRUM_POOL_SWAP_TOKEN.html
│ ├── SwapPools.AURORA_POOL_SWAP_TOKEN.html
│ ├── SwapPools.AVALANCHE_ETH_SWAP_TOKEN.html
│ ├── SwapPools.AVALANCHE_POOL_SWAP_TOKEN.html
│ ├── SwapPools.AllSwapPoolTokens.html
│ ├── SwapPools.BOBA_ETH_SWAP_TOKEN.html
│ ├── SwapPools.BOBA_POOL_SWAP_TOKEN.html
│ ├── SwapPools.BSC_POOL_SWAP_TOKEN.html
│ ├── SwapPools.CRONOS_POOL_SWAP_TOKEN.html
│ ├── SwapPools.ETH_POOL_SWAP_TOKEN.html
│ ├── SwapPools.FANTOM_ETH_SWAP_TOKEN.html
│ ├── SwapPools.FANTOM_POOL_SWAP_TOKEN.html
│ ├── SwapPools.HARMONY_AVAX_SWAP_TOKEN.html
│ ├── SwapPools.HARMONY_JEWEL_SWAP_TOKEN.html
│ ├── SwapPools.HARMONY_ONEETH_TOKEN.html
│ ├── SwapPools.HARMONY_POOL_SWAP_TOKEN.html
│ ├── SwapPools.METIS_ETH_SWAP_TOKEN.html
│ ├── SwapPools.METIS_POOL_SWAP_TOKEN.html
│ ├── SwapPools.OPTIMISM_ETH_SWAP_TOKEN.html
│ ├── SwapPools.OPTIMISM_POOL_SWAP_TOKEN.html
│ ├── SwapPools.POLYGON_POOL_SWAP_TOKEN.html
│ ├── SwapPools.bridgeSwappableMap.html
│ ├── Tokens.AVAX.html
│ ├── Tokens.AVWETH.html
│ ├── Tokens.AllTokens.html
│ ├── Tokens.BTCB.html
│ ├── Tokens.BUSD.html
│ ├── Tokens.ChainGasTokensMap.html
│ ├── Tokens.DAI.html
│ ├── Tokens.DFKTEARS.html
│ ├── Tokens.DFK_ETH.html
│ ├── Tokens.DFK_USDC.html
│ ├── Tokens.DOG.html
│ ├── Tokens.ETH.html
│ ├── Tokens.FRAX.html
│ ├── Tokens.FTM.html
│ ├── Tokens.FTM_ETH.html
│ ├── Tokens.GAS_JEWEL.html
│ ├── Tokens.GMX.html
│ ├── Tokens.GOHM.html
│ ├── Tokens.H20.html
│ ├── Tokens.HIGH.html
│ ├── Tokens.JEWEL.html
│ ├── Tokens.JUMP.html
│ ├── Tokens.KLAY.html
│ ├── Tokens.LINK.html
│ ├── Tokens.LUNA.html
│ ├── Tokens.MATIC.html
│ ├── Tokens.METIS_ETH.html
│ ├── Tokens.MOVR.html
│ ├── Tokens.MULTIJEWEL.html
│ ├── Tokens.MULTI_AVAX.html
│ ├── Tokens.NETH.html
│ ├── Tokens.NEWO.html
│ ├── Tokens.NFD.html
│ ├── Tokens.NUSD.html
│ ├── Tokens.ONE_ETH.html
│ ├── Tokens.SDT.html
│ ├── Tokens.SFI.html
│ ├── Tokens.SOLAR.html
│ ├── Tokens.SYN.html
│ ├── Tokens.SYN_AVAX.html
│ ├── Tokens.SYN_FRAX.html
│ ├── Tokens.SYN_JEWEL.html
│ ├── Tokens.USDB.html
│ ├── Tokens.USDC.html
│ ├── Tokens.USDT.html
│ ├── Tokens.UST.html
│ ├── Tokens.VSTA.html
│ ├── Tokens.WAVAX.html
│ ├── Tokens.WBTC.html
│ ├── Tokens.WETH.html
│ ├── Tokens.WETHBEAM.html
│ ├── Tokens.WETH_E.html
│ ├── Tokens.WFTM.html
│ ├── Tokens.WKLAY.html
│ ├── Tokens.WMATIC.html
│ ├── Tokens.WMOVR.html
│ ├── Tokens.XJEWEL.html
│ └── Tokens.mintBurnTokens.html
├── examples
└── frontend
│ ├── .gitignore
│ ├── README.md
│ ├── config-overrides.js
│ ├── package.json
│ ├── public
│ ├── favicon.ico
│ ├── index.html
│ ├── logo192.png
│ ├── logo512.png
│ ├── manifest.json
│ └── robots.txt
│ ├── src
│ ├── App.css
│ ├── App.test.tsx
│ ├── App.tsx
│ ├── components
│ │ ├── Button.tsx
│ │ ├── DarkRoundedItem.tsx
│ │ ├── DropdownMenu.tsx
│ │ └── Grid.tsx
│ ├── index.css
│ ├── index.tsx
│ ├── logo.svg
│ ├── pages
│ │ └── bridge
│ │ │ ├── components
│ │ │ ├── AddLiquidity.tsx
│ │ │ ├── BridgeSwap.tsx
│ │ │ ├── RemoveLiquidity.tsx
│ │ │ └── VariousComponents.tsx
│ │ │ ├── hooks
│ │ │ └── useActionButtonOnClick.tsx
│ │ │ └── index.tsx
│ ├── react-app-env.d.ts
│ ├── reportWebVitals.ts
│ ├── setupTests.ts
│ └── utils
│ │ ├── amounts.ts
│ │ ├── index.ts
│ │ ├── react_utils.ts
│ │ ├── transactions.ts
│ │ └── types.ts
│ ├── tailwind.config.js
│ ├── tsconfig.base.json
│ ├── tsconfig.json
│ └── yarn.lock
├── package.json
├── scripts
├── ERC20.json
├── fixup
├── gen_changelog
├── generate
├── make_release_branch
├── py_helpers.py
├── rebase
├── sdk-tests
└── utils
├── src
├── bridge
│ ├── bridge.ts
│ ├── bridgeconfig.ts
│ ├── bridgeutils.ts
│ ├── erc20.ts
│ ├── gasutils.ts
│ └── slippages.ts
├── common
│ ├── chainid.ts
│ ├── gasoptions.ts
│ ├── networks.ts
│ ├── synapse_contracts.ts
│ ├── types.ts
│ └── utils.ts
├── contracts.ts
├── entities.ts
├── explorer
│ ├── bridge_transactions
│ │ └── query.ts
│ ├── gqlutils.ts
│ └── index.ts
├── gasprice.ts
├── hooks
│ ├── bridge.ts
│ ├── errors.ts
│ ├── helpers.ts
│ ├── index.ts
│ ├── lptokens.ts
│ ├── signer.ts
│ ├── tokens.ts
│ └── types.ts
├── index.ts
├── internal
│ ├── gen
│ │ ├── AvaxJewelMigration.ts
│ │ ├── BridgeConfigV3.ts
│ │ ├── ERC20.ts
│ │ ├── L1BridgeZap.ts
│ │ ├── L2BridgeZap.ts
│ │ ├── SwapFlashLoan.ts
│ │ ├── SynapseBridge.ts
│ │ ├── SynapseERC20.ts
│ │ ├── common.ts
│ │ ├── factories
│ │ │ ├── AvaxJewelMigration__factory.ts
│ │ │ ├── BridgeConfigV3__factory.ts
│ │ │ ├── ERC20__factory.ts
│ │ │ ├── L1BridgeZap__factory.ts
│ │ │ ├── L2BridgeZap__factory.ts
│ │ │ ├── SwapFlashLoan__factory.ts
│ │ │ ├── SynapseBridge__factory.ts
│ │ │ ├── SynapseERC20__factory.ts
│ │ │ └── index.ts
│ │ └── index.ts
│ ├── minirpc.ts
│ ├── rpcconnector.ts
│ ├── rpcproviders.ts
│ ├── swaptype.ts
│ ├── types.ts
│ └── utils.ts
├── swappools.ts
├── token.ts
├── tokens.ts
└── tokenswap.ts
├── test
├── basic
│ ├── Basic-test.ts
│ ├── Env-test.ts
│ └── test_env.env
├── entities
│ └── entities-test.ts
├── erc20
│ └── ERC20-test.ts
├── explorer
│ └── explorer-test.ts
├── helpers
│ └── index.ts
├── swappools
│ └── SwapPools-test.ts
├── synapsebridge
│ ├── ContractWrapperFunctions-test.ts
│ ├── ProviderInteractions-test.ts
│ ├── Slippages-test.ts
│ ├── bridge_test_utils.ts
│ ├── buildBridgeTokenTransaction-test.ts
│ ├── checkSwapSupported-test.ts
│ └── getEstimatedBridgeOutput-test.ts
├── test_setup.ts
├── token
│ └── Token-test.ts
├── tokenswap
│ ├── SwapRate-test.ts
│ ├── TokenSwap-test.ts
│ ├── checkTokenAllowance-test.ts
│ └── liquidity-test.ts
└── tsconfig.json
├── tsconfig.build.json
├── tsconfig.json
└── yarn.lock
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Set the default behavior, in case people don't have core.autocrlf set.
2 | * text=auto
3 |
4 | docs/ linguist-vendored
--------------------------------------------------------------------------------
/.github/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "config:base"
4 | ],
5 | "ignorePaths": [
6 | "**/examples/**",
7 | "**/test/**",
8 | "**/scripts/**",
9 | "**/node_modules/**"
10 | ],
11 | "packageRules": [
12 | {
13 | "matchDepTypes": ["peerDependencies"],
14 | "matchManagers": ["npm"],
15 | "enabled": false
16 | },
17 | {
18 | "matchUpdateTypes": ["minor", "patch", "pin"],
19 | "matchDepTypes": ["devDependencies"],
20 | "groupName": "dev dependencies (minor/patch/pin)",
21 | "excludePackagePrefixes": ["dotenv", "react"],
22 | "matchManagers": ["npm"],
23 | "automerge": true
24 | },
25 | {
26 | "matchUpdateTypes": ["minor", "patch", "pin"],
27 | "matchDepTypes": ["dependencies"],
28 | "groupName": "dependencies (minor/patch/pin)",
29 | "excludePackagePrefixes": [],
30 | "matchManagers": ["npm"],
31 | "automerge": true
32 | },
33 | {
34 | "matchUpdateTypes": ["major"],
35 | "matchDepTypes": ["dependencies"],
36 | "matchManagers": ["npm"],
37 | "enabled": false
38 | },
39 | {
40 | "matchUpdateTypes": ["major"],
41 | "matchDepTypes": ["devDependencies"],
42 | "groupName": "dev dependencies (major)",
43 | "excludePackagePrefixes": ["dotenv"],
44 | "matchManagers": ["npm"],
45 | "automerge": false
46 | }
47 | ],
48 | "prConcurrentLimit": 3,
49 | "prHourlyLimit": 5,
50 | "baseBranches": ["dev"],
51 | "enabledManagers": ["npm"],
52 | "rebaseWhen": "behind-base-branch",
53 | "automergeType": "branch",
54 | "lockFileMaintenance": { "enabled": true }
55 | }
56 |
--------------------------------------------------------------------------------
/.github/workflows/publish_devel.yaml:
--------------------------------------------------------------------------------
1 | name: Publish (Dev/Beta)
2 |
3 | on:
4 | push:
5 | tags:
6 | - "v[01].[0-9]+.[0-9]+-dev.[0-9]+"
7 | - "v[01].[0-9]+.[0-9]+-beta.[0-9]+"
8 | - "v[01].[0-9]+.[0-9]+-alpha.[0-9]+"
9 |
10 | jobs:
11 | tagged_release:
12 | runs-on: ubuntu-latest
13 |
14 | steps:
15 | - uses: actions/checkout@v3
16 | with:
17 | fetch-depth: 0
18 |
19 | - name: Set git tag name in env
20 | id: git_tag
21 | run: echo ::set-output name=tag::${GITHUB_REF#refs/*/}
22 |
23 | - name: Set NPM tag name in env
24 | id: npm_tag
25 | run: |
26 | REF_NAME=${GITHUB_REF#refs/*/}
27 | ALPHA='alpha'
28 | BETA='beta'
29 | DEV='dev'
30 |
31 | case $REF_NAME in
32 | *"$ALPHA"*)
33 | echo ::set-output name=tag::${ALPHA}
34 | ;;
35 | *"$BETA"*)
36 | echo ::set-output name=tag::${BETA}
37 | ;;
38 | *"$DEV"*)
39 | echo ::set-output name=tag::${DEV}
40 | ;;
41 | esac
42 |
43 | - name: Set tarball filename in env
44 | id: tarball_file
45 | run: |
46 | TARBALL=synapseprotocol-sdk-${{ steps.git_tag.outputs.tag }}.tar.gz
47 | echo ::set-output name=filename::${TARBALL}
48 |
49 | - name: Get node version from nvmrc
50 | id: nvm_version
51 | run: echo ::set-output name=version::$(cat .nvmrc)
52 |
53 | - name: Import GPG key
54 | id: import_gpg
55 | uses: crazy-max/ghaction-import-gpg@v4
56 | with:
57 | gpg_private_key: ${{ secrets.RELEASEBOT_GPG_PRIVATEKEY }}
58 | passphrase: ${{ secrets.RELEASEBOT_GPG_PASSPHRASE }}
59 |
60 | - uses: actions/setup-node@v3
61 | with:
62 | cache: "yarn"
63 | scope: "@synapseprotocol"
64 | registry-url: "https://registry.npmjs.org"
65 | node-version-file: '.nvmrc'
66 |
67 | - id: cache_dir
68 | run: echo ::set-output name=dir::$(yarn cache dir)
69 |
70 | - uses: actions/cache@v3
71 | id: yarn_cache
72 | with:
73 | path: ${{ steps.cache_dir.outputs.dir }}
74 | key: ${{ runner.os }}-${{ steps.nvm_version.outputs.version }}-yarn-${{ hashFiles('**/yarn.lock') }}
75 | restore-keys: |
76 | ${{ runner.os }}-${{ steps.nvm_version.outputs.version }}-yarn-
77 |
78 | - run: yarn install --frozen-lockfile
79 | if: steps.yarn_cache.outputs.cache-hit != 'true'
80 |
81 | - run: yarn run build
82 |
83 | - name: Build dist tarball
84 | run: yarn pack --filename ${{ steps.tarball_file.outputs.filename }}
85 |
86 | - name: GPG sign dist tarball
87 | run: gpg --armor --detach-sign ${{ steps.tarball_file.outputs.filename }}
88 |
89 | - name: Create and publish GitHub release
90 | uses: marvinpinto/action-automatic-releases@v1.2.1
91 | with:
92 | repo_token: ${{ secrets.GITHUB_TOKEN }}
93 | prerelease: true
94 | files: |
95 | ${{ steps.tarball_file.outputs.filename }}
96 | ${{ steps.tarball_file.outputs.filename }}.asc
97 | LICENSE
98 | README.md
99 |
100 | - name: Publish to NPM
101 | run: yarn publish --non-interactive --tag ${{ steps.npm_tag.outputs.tag }}
102 | env:
103 | NODE_AUTH_TOKEN: ${{ secrets.NPM_SERVICE_ACCOUNT_TOKEN }}
--------------------------------------------------------------------------------
/.github/workflows/publish_mainline.yaml:
--------------------------------------------------------------------------------
1 | name: Publish (Mainline Release)
2 |
3 | on:
4 | push:
5 | tags:
6 | - "v[01].[0-9]+.[0-9]+"
7 | - "!v*-dev*"
8 | - "!v*-beta*"
9 | - "!v*-alpha*"
10 |
11 | jobs:
12 | tagged_release:
13 | runs-on: ubuntu-latest
14 |
15 | steps:
16 | - uses: actions/checkout@v3
17 | with:
18 | fetch-depth: 0
19 |
20 | - name: Set git tag name in env
21 | id: git_tag
22 | run: echo ::set-output name=tag::${GITHUB_REF#refs/*/}
23 |
24 | - name: Set tarball filename in env
25 | id: tarball_file
26 | run: |
27 | TARBALL=synapseprotocol-sdk-${{ steps.git_tag.outputs.tag }}.tar.gz
28 | echo ::set-output name=filename::${TARBALL}
29 |
30 | - name: Get node version from nvmrc
31 | id: nvm_version
32 | run: echo ::set-output name=version::$(cat .nvmrc)
33 |
34 | - name: Import GPG key
35 | id: import_gpg
36 | uses: crazy-max/ghaction-import-gpg@v4
37 | with:
38 | gpg_private_key: ${{ secrets.RELEASEBOT_GPG_PRIVATEKEY }}
39 | passphrase: ${{ secrets.RELEASEBOT_GPG_PASSPHRASE }}
40 |
41 | - uses: actions/setup-node@v3
42 | with:
43 | cache: "yarn"
44 | scope: "@synapseprotocol"
45 | registry-url: "https://registry.npmjs.org"
46 | node-version-file: '.nvmrc'
47 |
48 | - id: cache_dir
49 | run: echo ::set-output name=dir::$(yarn cache dir)
50 |
51 | - uses: actions/cache@v3
52 | id: yarn_cache
53 | with:
54 | path: ${{ steps.cache_dir.outputs.dir }}
55 | key: ${{ runner.os }}-${{ steps.nvm_version.outputs.version }}-yarn-${{ hashFiles('**/yarn.lock') }}
56 | restore-keys: |
57 | ${{ runner.os }}-${{ steps.nvm_version.outputs.version }}-yarn-
58 |
59 | - run: yarn install --frozen-lockfile
60 | if: steps.yarn_cache.outputs.cache-hit != 'true'
61 |
62 | - run: yarn run build
63 |
64 | - name: Build dist tarball
65 | run: yarn pack --filename ${{ steps.tarball_file.outputs.filename }}
66 |
67 | - name: GPG sign dist tarball
68 | run: gpg --armor --detach-sign ${{ steps.tarball_file.outputs.filename }}
69 |
70 | - name: Create and publish GitHub release
71 | uses: marvinpinto/action-automatic-releases@v1.2.1
72 | with:
73 | repo_token: ${{ secrets.GITHUB_TOKEN }}
74 | prerelease: false
75 | files: |
76 | ${{ steps.tarball_file.outputs.filename }}
77 | ${{ steps.tarball_file.outputs.filename }}.asc
78 | LICENSE
79 | README.md
80 |
81 | - name: Publish to NPM
82 | run: yarn publish --non-interactive --tag latest
83 | env:
84 | NODE_AUTH_TOKEN: ${{ secrets.NPM_SERVICE_ACCOUNT_TOKEN }}
--------------------------------------------------------------------------------
/.github/workflows/tests.yaml:
--------------------------------------------------------------------------------
1 | name: Tests
2 |
3 | on:
4 | pull_request:
5 | types:
6 | - opened
7 | - synchronize
8 | branches:
9 | - master
10 | - dev
11 | paths-ignore:
12 | - '**.md'
13 | - 'LICENSE'
14 | - '.nvmrc'
15 | - '.gitignore'
16 | - '.yarnclean'
17 | - 'scripts/**'
18 | - 'examples/**'
19 | - 'tsconfig.build.json'
20 | push:
21 | branches:
22 | - master
23 | - dev
24 | - 'renovate/*'
25 | paths-ignore:
26 | - '**.md'
27 | - 'LICENSE'
28 | - '.nvmrc'
29 | - '.gitignore'
30 | - '.yarnclean'
31 | - 'scripts/**'
32 | - 'examples/**'
33 | - 'tsconfig.build.json'
34 |
35 | jobs:
36 | prebuild:
37 | runs-on: ubuntu-latest
38 | steps:
39 | - uses: actions/checkout@v3
40 | with:
41 | fetch-depth: 2
42 |
43 | - if: github.event_name == 'push'
44 | id: push_commit_msg
45 | run: echo ::set-output name=push_msg::$(git log --format=%B -n 1 HEAD)
46 |
47 | - if: github.event_name == 'pull_request'
48 | id: pull_commit_msg
49 | run: echo ::set-output name=pull_msg::$(git log --format=%B -n 1 HEAD^2)
50 |
51 | outputs:
52 | commit_message: $( [ -z "${{ steps.pull_commit_msg.outputs.pull_msg }}" ] && echo "${{ steps.push_commit_msg.outputs.push_msg }}" || echo "${{ steps.pull_commit_msg.outputs.pull_msg }}" )
53 |
54 | test:
55 | strategy:
56 | matrix:
57 | node-version:
58 | - lts/gallium # most recent release of Node v16, since it's LTS
59 | - 17.0.1 # Node version used in .nvmrc
60 | platform:
61 | - ubuntu-latest
62 |
63 | needs:
64 | - prebuild
65 |
66 | if: ${{ !contains(needs.prebuild.outputs.commit_message, '[ci notest]') }}
67 |
68 | env:
69 | COVERAGE_DIR: ${{ github.workspace }}/coverage
70 |
71 | runs-on: ${{ matrix.platform }}
72 | steps:
73 | - uses: actions/checkout@v3
74 |
75 | - name: Set coveralls run flag
76 | id: coveralls_flag
77 | run: |
78 | FLAGVAL=node-${{ matrix.node-version }}
79 |
80 | if [[ ${{ github.event_name }} == 'pull_request' ]]; then
81 | FLAGVAL="${FLAGVAL}.pr.${{ github.event.pull_request.number }}"
82 | else
83 | FLAGVAL="${FLAGVAL}.${{ github.ref_name }}"
84 | fi
85 |
86 | FLAGVAL="${FLAGVAL}.${{ github.run_id }}/${{ github.run_attempt }}"
87 |
88 | echo ::set-output name=coveralls_flag::${FLAGVAL}
89 |
90 | - uses: actions/setup-node@v3
91 | with:
92 | node-version: ${{ matrix.node-version }}
93 | cache: "yarn"
94 |
95 | - id: cache_dir
96 | run: echo ::set-output name=dir::$(yarn cache dir)
97 |
98 | - uses: actions/cache@v3
99 | id: yarn_cache
100 | with:
101 | path: ${{ steps.cache_dir.outputs.dir }}
102 | key: ${{ runner.os }}-${{ matrix.node-version }}-yarn-${{ hashFiles('**/yarn.lock') }}
103 | restore-keys: |
104 | ${{ runner.os }}-${{ matrix.node-version }}-yarn-
105 |
106 | - run: yarn install
107 | if: steps.yarn_cache.outputs.cache-hit != 'true'
108 |
109 | - name: Run Tests (Node ${{ matrix.node-version }})
110 | run: yarn -s run coverage:test
111 | env:
112 | REPORTS_DIR: ${{ env.COVERAGE_DIR }}
113 | ETH_RPC_URI: ${{ secrets.ETH_RPC_URI }}
114 | POLYGON_RPC_URI: ${{ secrets.POLYGON_RPC_URI }}
115 | FANTOM_RPC_URI: ${{ secrets.FANTOM_RPC_URI }}
116 | METIS_RPC_URI: ${{ secrets.METIS_RPC_URI }}
117 | ARBITRUM_RPC_URI: ${{ secrets.ARBITRUM_RPC_URI }}
118 | CRONOS_RPC_URI: "https://mmf-rpc.xstaking.sg/"
119 | HARMONY_RPC_URI: "https://harmony-0-rpc.gateway.pokt.network"
120 | INFINITE_APPROVALS_PRIVKEY: ${{ secrets.INFINITE_APPROVALS_PRIVKEY }}
121 | INFINITE_APPROVALS_PRIVKEY_ADDRESS: ${{ secrets.INFINITE_APPROVALS_PRIVKEY_ADDRESS }}
122 | BRIDGE_INTERACTIONS_PRIVKEY: ${{ secrets.BRIDGE_INTERACTIONS_PRIVKEY }}
123 | BRIDGE_INTERACTIONS_PRIVKEY_ADDRESS: ${{ secrets.BRIDGE_INTERACTIONS_PRIVKEY_ADDRESS }}
124 |
125 | - name: Generate Coverage report
126 | run: yarn -s run coverage:report
127 | env:
128 | REPORTS_DIR: ${{ env.COVERAGE_DIR }}
129 |
130 | - name: Coveralls - Process coverage
131 | uses: coverallsapp/github-action@master
132 | with:
133 | github-token: ${{ secrets.GITHUB_TOKEN }}
134 | path-to-lcov: ${{ env.COVERAGE_DIR }}/lcov.info
135 | flag-name: ${{ steps.coveralls_flag.outputs.coveralls_flag }}
136 | parallel: true
137 |
138 | send-coverage:
139 | needs: test
140 | runs-on: ubuntu-latest
141 | steps:
142 | - name: Coveralls - Process all coverage results
143 | uses: coverallsapp/github-action@master
144 | with:
145 | github-token: ${{ secrets.GITHUB_TOKEN }}
146 | parallel-finished: true
147 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea/
2 | .nvim/
3 | node_modules/
4 |
5 | abigen/
6 | dist/
7 | artifacts/
8 |
9 | .env
10 | .coveralls.yml
11 |
12 | yarn-error.log
13 |
14 | keys/
15 |
16 | coverage/
--------------------------------------------------------------------------------
/.mocharc.yml:
--------------------------------------------------------------------------------
1 | node-option:
2 | - 'es-module-specifier-resolution=node'
3 | - 'loader=ts-node/esm'
4 |
5 | extensions:
6 | - ts
7 |
8 | require:
9 | - './test/test_setup.ts'
10 |
11 | recursive: true
12 | parallel: true
13 | jobs: 6
14 |
15 | spec:
16 | # sync/non-network-based tests
17 | - 'test/entities/*-test.ts'
18 | - 'test/basic/*-test.ts'
19 | - 'test/swappools/*-test.ts'
20 | - 'test/token/*-test.ts'
21 | - 'test/tokenswap/TokenSwap-test.ts'
22 | - 'test/synapsebridge/buildBridgeTokenTransaction-test.ts'
23 | - 'test/synapsebridge/checkSwapSupported-test.ts'
24 | - 'test/synapsebridge/Slippages-test.ts'
25 | # async/network-based tests
26 | - 'test/synapsebridge/getEstimatedBridgeOutput-test.ts'
27 | - 'test/synapsebridge/ProviderInteractions-test.ts'
28 | - 'test/synapsebridge/ContractWrapperFunctions-test.ts'
29 | - 'test/tokenswap/checkTokenAllowance-test.ts'
30 | - 'test/tokenswap/SwapRate-test.ts'
31 | - 'test/tokenswap/liquidity-test.ts'
32 | - 'test/erc20/*-test.ts'
33 | - 'test/explorer/explorer-test.ts'
34 |
--------------------------------------------------------------------------------
/.nvmrc:
--------------------------------------------------------------------------------
1 | v17.0.1
--------------------------------------------------------------------------------
/.yarnclean:
--------------------------------------------------------------------------------
1 | # test directories
2 | __tests__
3 | test
4 | tests
5 | powered-test
6 |
7 | # asset directories
8 | docs
9 | doc
10 | website
11 | images
12 | assets
13 |
14 | # examples
15 | example
16 | examples
17 |
18 | # code coverage directories
19 | coverage
20 | .nyc_output
21 |
22 | # build scripts
23 | Makefile
24 | Gulpfile.js
25 | Gruntfile.js
26 |
27 | # configs
28 | appveyor.yml
29 | circle.yml
30 | codeship-services.yml
31 | codeship-steps.yml
32 | wercker.yml
33 | .tern-project
34 | .gitattributes
35 | .editorconfig
36 | .*ignore
37 | .eslintrc
38 | .jshintrc
39 | .flowconfig
40 | .documentup.json
41 | .yarn-metadata.json
42 | .travis.yml
43 |
44 | # misc
45 | *.md
46 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to synapsecns/sdk
2 |
3 | We welcome issues and pull requests to this repo! If you have an idea which you think we should implement,
4 | just open up a new issue and make sure to tag it as a feature request.
5 |
6 | If you're opening a PR, please note that PRs against the [`master`](https://github.com/synapsecns/sdk/tree/master) branch
7 | will likely be ignored; instead, make your changes in a branch based off of [`dev`](https://github.com/synapsecns/sdk/tree/dev).
8 | This stipulation is due to how fast changes occur in the `dev` branch, and our PR workflow for merging `dev` into `master`
9 | by way of squash commits; that is to say, opening a PR against `master` will likely cause several merge conflicts
10 | when trying to merge it into `dev`. So please, just save all of us the pain of working out merge conflicts
11 | and make your changes against `dev`.
12 |
13 | ## PR formatting/etiquette
14 |
15 | When opening a pull request, please use a short but descriptive title. You can be as verbose and descriptive as necessary
16 | in the PR description, but try not to overthink it: if one sentence makes just as much sense as five, go with one.
17 |
18 | Please also make sure that, when your changes are ready for review, to rebase your branch against `dev` to ensure that
19 | your fork is up to date with any changes made to `dev` in the meantime.
20 |
21 | ## Tests
22 |
23 | PRs from external contributors will not be considered for merging unless all of the following criteria are met:
24 |
25 | 1. All existing tests pass
26 | 2. Test coverage of existing code is not decreased
27 | 3. New code -- especially significantly complex code -- has tests written for it, preferably a lot of them (both actual tests as well as test cases)
28 | 1. Minimum required test coverage percentage for new code is 95%
29 |
30 | If you need or want assistance from us in writing or improving tests, just let us know! We genuinely enjoy helping out contributors when we can.
31 |
32 | ## Development environment setup
33 |
34 | 1. Install [nvm](https://github.com/nvm-sh/nvm) (if not already installed on your system)
35 | 2. Use the correct NodeJS version: `nvm use`
36 | - If yarn is not already installed for the given NPM version: `npm install -g yarn`
37 | 3. Install dependencies: `yarn`
38 | 4. Checkout a new branch, and start developing! (`git checkout -b [insert branch name here]`)
39 |
40 | ### Tests
41 |
42 | `yarn test`
43 |
44 | ### Build
45 |
46 | `yarn build`
47 |
48 | Compiled and built files are output to `dist/`.
49 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | ISC License
2 |
3 | Copyright (c) 2021 Synapse CNS/Synapse Protocol
4 |
5 | Permission to use, copy, modify, and/or distribute this software for any
6 | purpose with or without fee is hereby granted, provided that the above
7 | copyright notice and this permission notice appear in all copies.
8 |
9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
10 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11 | AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
12 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13 | LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
14 | OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15 | PERFORMANCE OF THIS SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Repository Deprecated
2 |
3 | **Note:** This repository is no longer maintained and has been deprecated in favor of the new sdk. We recommend all users to transition to the new sdk for the latest updates, features, and support.
4 |
5 | ## New SDK
6 |
7 | You can find the npm package [here](https://www.npmjs.com/package/@synapsecns/sdk-router).
8 |
9 | ## Support
10 |
11 | For support with the new repository, please open an issue on the [SDK](https://github.com/synapsecns/sanguine/tree/master/packages/sdk-router).
12 |
13 | # synapseprotocol-sdk
14 |
15 | [](https://www.npmjs.com/package/@synapseprotocol/sdk)
16 |
17 | [](https://github.com/synapsecns/sdk/actions/workflows/tests.yaml)
18 | [](https://coveralls.io/github/synapsecns/sdk?branch=master)
19 | [](https://github.com/synapsecns/sdk/actions/workflows/tests.yaml)
20 | [](https://coveralls.io/github/synapsecns/sdk?branch=dev)
21 |
22 | Typescript SDK for the Synapse Protocol.
23 |
24 | ## Usage instructions
25 |
26 | See the [Docs](https://github.com/synapsecns/sdk/wiki).
27 |
28 | ## Looking to help?
29 |
30 | Read our guidelines for [contributing](https://github.com/synapsecns/sdk/blob/master/CONTRIBUTING.md).
31 |
--------------------------------------------------------------------------------
/docs/.nojekyll:
--------------------------------------------------------------------------------
1 | TypeDoc added this file to prevent GitHub Pages from using Jekyll. You can turn off this behavior by setting the `githubPages` option to false.
--------------------------------------------------------------------------------
/docs/assets/highlight.css:
--------------------------------------------------------------------------------
1 | :root {
2 | --light-code-background: #FFFFFF;
3 | --dark-code-background: #1E1E1E;
4 | }
5 |
6 | @media (prefers-color-scheme: light) { :root {
7 | --code-background: var(--light-code-background);
8 | } }
9 |
10 | @media (prefers-color-scheme: dark) { :root {
11 | --code-background: var(--dark-code-background);
12 | } }
13 |
14 | :root[data-theme='light'] {
15 | --code-background: var(--light-code-background);
16 | }
17 |
18 | :root[data-theme='dark'] {
19 | --code-background: var(--dark-code-background);
20 | }
21 |
22 | pre, code { background: var(--code-background); }
23 |
--------------------------------------------------------------------------------
/examples/frontend/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/examples/frontend/README.md:
--------------------------------------------------------------------------------
1 | # Getting Started with Create React App
2 |
3 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app).
4 |
5 | ## Available Scripts
6 |
7 | In the project directory, you can run:
8 |
9 | ### `yarn start`
10 |
11 | Runs the app in the development mode.\
12 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser.
13 |
14 | The page will reload if you make edits.\
15 | You will also see any lint errors in the console.
16 |
17 | ### `yarn test`
18 |
19 | Launches the test runner in the interactive watch mode.\
20 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information.
21 |
22 | ### `yarn build`
23 |
24 | Builds the app for production to the `build` folder.\
25 | It correctly bundles React in production mode and optimizes the build for the best performance.
26 |
27 | The build is minified and the filenames include the hashes.\
28 | Your app is ready to be deployed!
29 |
30 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information.
31 |
32 | ### `yarn eject`
33 |
34 | **Note: this is a one-way operation. Once you `eject`, you can’t go back!**
35 |
36 | If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project.
37 |
38 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.
39 |
40 | You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.
41 |
42 | ## Learn More
43 |
44 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started).
45 |
46 | To learn React, check out the [React documentation](https://reactjs.org/).
47 |
--------------------------------------------------------------------------------
/examples/frontend/config-overrides.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 |
3 | module.exports = function override(config) {
4 | config.resolve = {
5 | ...config.resolve,
6 | alias: {
7 | ...config.alias,
8 | "@utils": path.resolve(__dirname, 'src/utils'),
9 | "@hooks": path.resolve(__dirname, 'src/hooks'),
10 | "@components": path.resolve(__dirname, 'src/components'),
11 | "@pages": path.resolve(__dirname, 'src/pages'),
12 | },
13 | };
14 |
15 | return config;
16 | };
--------------------------------------------------------------------------------
/examples/frontend/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "frontend",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@headlessui/react": "^1.4.3",
7 | "@heroicons/react": "^1.0.5",
8 | "@synapseprotocol/sdk": "0.95.1-alpha.5",
9 | "@testing-library/jest-dom": "^5.14.1",
10 | "@testing-library/react": "^12.0.0",
11 | "@testing-library/user-event": "^13.2.1",
12 | "@types/jest": "^27.0.1",
13 | "@types/node": "^16.7.13",
14 | "@types/react": "^17.0.20",
15 | "@types/react-dom": "^17.0.9",
16 | "ethers": "^5.5.4",
17 | "lodash": "^4.17.21",
18 | "metamask-react": "^2.2.1",
19 | "react": "^18.0.0",
20 | "react-dom": "^18.0.0",
21 | "react-scripts": "5.0.0",
22 | "typescript": "^4.4.2",
23 | "web-vitals": "^2.1.0"
24 | },
25 | "resolutions": {
26 | "@synapseprotocol/sdk/react": "^18.0.0"
27 | },
28 | "scripts": {
29 | "start": "react-app-rewired start",
30 | "build": "react-app-rewired build",
31 | "test": "react-app-rewired test",
32 | "eject": "react-app-rewired eject"
33 | },
34 | "eslintConfig": {
35 | "extends": [
36 | "react-app",
37 | "react-app/jest"
38 | ]
39 | },
40 | "browserslist": {
41 | "production": [
42 | ">0.2%",
43 | "not dead",
44 | "not op_mini all"
45 | ],
46 | "development": [
47 | "last 1 chrome version",
48 | "last 1 firefox version",
49 | "last 1 safari version"
50 | ]
51 | },
52 | "devDependencies": {
53 | "autoprefixer": "^10.4.2",
54 | "postcss": "^8.4.6",
55 | "react-app-rewired": "^2.2.1",
56 | "tailwindcss": "^3.0.23"
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/examples/frontend/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/synapsecns/sdk/b7f0d8826a5556edce05cd2168dd043030882df5/examples/frontend/public/favicon.ico
--------------------------------------------------------------------------------
/examples/frontend/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 | React App
28 |
29 |
30 |
31 |
32 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/examples/frontend/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/synapsecns/sdk/b7f0d8826a5556edce05cd2168dd043030882df5/examples/frontend/public/logo192.png
--------------------------------------------------------------------------------
/examples/frontend/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/synapsecns/sdk/b7f0d8826a5556edce05cd2168dd043030882df5/examples/frontend/public/logo512.png
--------------------------------------------------------------------------------
/examples/frontend/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/examples/frontend/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/examples/frontend/src/App.css:
--------------------------------------------------------------------------------
1 | .App {
2 | text-align: center;
3 | }
4 |
5 | .App-logo {
6 | height: 40vmin;
7 | pointer-events: none;
8 | }
9 |
10 | @media (prefers-reduced-motion: no-preference) {
11 | .App-logo {
12 | animation: App-logo-spin infinite 20s linear;
13 | }
14 | }
15 |
16 | .App-header {
17 | background-color: #282c34;
18 | min-height: 100vh;
19 | display: flex;
20 | flex-direction: column;
21 | align-items: center;
22 | justify-content: center;
23 | font-size: calc(10px + 2vmin);
24 | color: white;
25 | }
26 |
27 | .App-link {
28 | color: #61dafb;
29 | }
30 |
31 | @keyframes App-logo-spin {
32 | from {
33 | transform: rotate(0deg);
34 | }
35 | to {
36 | transform: rotate(360deg);
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/examples/frontend/src/App.test.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { render, screen } from '@testing-library/react';
3 | import App from './App';
4 |
5 | test('renders learn react link', () => {
6 | render();
7 | const linkElement = screen.getByText(/learn react/i);
8 | expect(linkElement).toBeInTheDocument();
9 | });
10 |
--------------------------------------------------------------------------------
/examples/frontend/src/App.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import './App.css';
3 |
4 | import BridgePage from "@pages/bridge";
5 |
6 | function App() {
7 | return (
8 |
9 |
12 |
13 | );
14 | }
15 |
16 | export default App;
17 |
--------------------------------------------------------------------------------
/examples/frontend/src/components/Button.tsx:
--------------------------------------------------------------------------------
1 | import type {OnClickFunction} from "@utils";
2 |
3 | export interface ButtonProps {
4 | text: string,
5 | onClick?: OnClickFunction,
6 | disabled?: boolean,
7 | }
8 |
9 | export const emptyOnClick: OnClickFunction = () => {}
10 |
11 | export default function Button({text, onClick, disabled=false}: ButtonProps) {
12 | return (
13 |
14 |
21 |
22 | )
23 | }
--------------------------------------------------------------------------------
/examples/frontend/src/components/DarkRoundedItem.tsx:
--------------------------------------------------------------------------------
1 | import {classNames} from "@utils";
2 |
3 | const CLASS_NAME: string = classNames(
4 | "rounded-md border",
5 | "shadow-md",
6 | "dark:bg-gray-800 dark:border-gray-600",
7 | "h-28"
8 | )
9 |
10 | export default function DarkRoundedItem({children}) {
11 | return (
12 |
13 | {children}
14 |
15 | )
16 | }
--------------------------------------------------------------------------------
/examples/frontend/src/components/DropdownMenu.tsx:
--------------------------------------------------------------------------------
1 | import {
2 | Listbox,
3 | Transition,
4 | } from "@headlessui/react";
5 |
6 | import { SelectorIcon } from '@heroicons/react/solid';
7 |
8 | import {classNames} from "@utils";
9 |
10 | export interface DropdownItem {
11 | label: string,
12 | key: string,
13 | className?: string,
14 | link?: string,
15 | disabled: boolean,
16 | }
17 |
18 | interface DropdownMenuProps {
19 | selectedItem: any,
20 | setSelectedItem: any,
21 | title: string,
22 | items: DropdownItem[],
23 | }
24 |
25 | export default function DropdownMenu({title, selectedItem, setSelectedItem, items}: DropdownMenuProps) {
26 | return(
27 |
28 |
29 |
30 | {({open}) => (
31 | <>
32 | {title}
33 |
34 |
35 |
46 | {selectedItem?.label || ""}
47 |
48 |
49 |
50 |
51 |
52 |
64 |
65 | {items.map((item) => {
66 | const selected = item.key === selectedItem.key;
67 | return(
72 | {({ active }) => (
73 |
77 |
83 | {item.label}
84 |
85 |
86 | )}
87 |
88 | )})}
89 |
90 |
91 |
92 | >
93 | )}
94 |
95 |
96 |
97 | )
98 | }
--------------------------------------------------------------------------------
/examples/frontend/src/components/Grid.tsx:
--------------------------------------------------------------------------------
1 | import {classNames} from "@utils";
2 |
3 | interface GridProps {
4 | className?: string,
5 | rows?: number,
6 | cols?: number,
7 | gapX?: number,
8 | gapY?: number,
9 | children: JSX.Element | JSX.Element[],
10 | }
11 |
12 | export function Grid({
13 | className="",
14 | rows=6,
15 | cols=6,
16 | gapX=1,
17 | gapY=1,
18 | children
19 | }: GridProps) {
20 | return(
30 | {children}
31 |
)
32 | }
--------------------------------------------------------------------------------
/examples/frontend/src/index.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
4 |
5 | body {
6 | margin: 0;
7 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
8 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
9 | sans-serif;
10 | -webkit-font-smoothing: antialiased;
11 | -moz-osx-font-smoothing: grayscale;
12 | }
13 |
14 | code {
15 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
16 | monospace;
17 | }
18 |
--------------------------------------------------------------------------------
/examples/frontend/src/index.tsx:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {createRoot} from 'react-dom/client';
3 | import './index.css';
4 | import App from './App';
5 | import reportWebVitals from './reportWebVitals';
6 |
7 | import { MetaMaskProvider } from "metamask-react";
8 |
9 | createRoot(document.getElementById('root')).render(
10 |
11 |
12 |
13 |
14 | ,
15 | );
16 |
17 | // If you want to start measuring performance in your app, pass a function
18 | // to log results (for example: reportWebVitals(console.log))
19 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
20 | reportWebVitals();
21 |
--------------------------------------------------------------------------------
/examples/frontend/src/logo.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/examples/frontend/src/pages/bridge/components/AddLiquidity.tsx:
--------------------------------------------------------------------------------
1 | import {useConnectedMetaMask} from "metamask-react";
2 |
3 | import {
4 | type Token,
5 | Tokens,
6 | ChainId,
7 | SwapPools
8 | } from "@synapseprotocol/sdk";
9 |
10 | import {
11 | useAddLiquidity,
12 | usePoolTokenApproval,
13 | useCalculateAddLiquidity,
14 | useChainStableswapLPToken
15 | } from "@synapseprotocol/sdk";
16 |
17 | import {Grid} from "@components/Grid";
18 | import Button from "@components/Button";
19 |
20 | import {
21 | ApproveButton,
22 | ExecuteButton,
23 | RowBreak,
24 | ColBreak,
25 | DataRow,
26 | LOADING,
27 | LOADING_COLOR, NeedsApprovalCol
28 | } from "./VariousComponents";
29 |
30 | import {useEffect, useState} from "react";
31 | import {MetamaskStatus} from "@utils";
32 | import {formatEther} from "@ethersproject/units";
33 | import {
34 | BigNumber,
35 | type BigNumberish
36 | } from "@ethersproject/bignumber";
37 |
38 | interface BaseProps {
39 | ethereum: any;
40 | chainId: number;
41 | status: string;
42 | }
43 |
44 | interface AddLiquiditySingleTokenProps extends BaseProps {
45 | lpToken: SwapPools.SwapPoolToken;
46 | liquidityToken: Token;
47 | liquidityAmount: BigNumberish;
48 | }
49 |
50 |
51 | export default function AddLiquidity(props) {
52 | const {ethereum, chainId: cid, status} = useConnectedMetaMask();
53 | const chainId = status === MetamaskStatus.CONNECTED ? BigNumber.from(cid).toNumber() : ChainId.ETH;
54 |
55 | const [stableswapPool] = useChainStableswapLPToken(ethereum, chainId);
56 |
57 | const [liquidityAmountsMap, setLiquidityAmountsMap] = useState(null);
58 |
59 | const addLiquidityAmount = "55";
60 | const deadline = Math.round((new Date().getTime() / 1000) + 60 * 10);
61 |
62 | useEffect(() => {
63 | if (stableswapPool && !liquidityAmountsMap) {
64 | const amountsMap = stableswapPool.liquidityAmountsMap();
65 | Object.keys(amountsMap).forEach(k => amountsMap[k] = addLiquidityAmount)
66 | setLiquidityAmountsMap(amountsMap);
67 | }
68 | }, [stableswapPool])
69 |
70 | const [calculateAddLiquidity, addLiquidityEstimate] = useCalculateAddLiquidity({
71 | ethereum,
72 | chainId,
73 | lpToken: stableswapPool,
74 | amounts: liquidityAmountsMap
75 | });
76 |
77 | if (liquidityAmountsMap) {
78 | calculateAddLiquidity();
79 | }
80 |
81 | const [addLiquidity, addLiquidityTx] = useAddLiquidity({
82 | ethereum,
83 | chainId,
84 | deadline,
85 | lpToken: stableswapPool,
86 | amounts: liquidityAmountsMap,
87 | minToMint: addLiquidityEstimate
88 | });
89 |
90 | let baseProps: BaseProps;
91 |
92 | if (stableswapPool && liquidityAmountsMap) {
93 | baseProps = {
94 | ethereum,
95 | chainId,
96 | status
97 | };
98 |
99 | return (
100 |
101 |
102 |
103 | {/**/}
104 |
105 |
106 | {stableswapPool.poolTokens.map((liquidityToken) => {
107 | const tokenSymbol = liquidityToken.symbol;
108 | const liquidityAmount = liquidityAmountsMap[`${tokenSymbol}`];
109 |
110 | const componentProps: AddLiquiditySingleTokenProps = {
111 | ...baseProps,
112 | lpToken: stableswapPool,
113 | liquidityToken,
114 | liquidityAmount
115 | };
116 |
117 | return (
118 |
119 |
120 |
121 | )
122 | })}
123 |
124 |
125 | )
126 | }
127 |
128 |
129 | return (
130 | {LOADING}
131 | )
132 | }
133 |
134 | function AddLiquiditySingleToken(props: AddLiquiditySingleTokenProps) {
135 | const {
136 | ethereum,
137 | chainId,
138 | lpToken,
139 | liquidityToken,
140 | liquidityAmount
141 | } = props;
142 |
143 | const {
144 | needsApprove,
145 | allowance: swapPoolSpendAllowance,
146 | execApprove,
147 | approveTx,
148 | approveStatus
149 | } = usePoolTokenApproval({
150 | ethereum,
151 | chainId,
152 | lpToken,
153 | token: liquidityToken,
154 | amount: liquidityAmount
155 | });
156 |
157 | return (
158 |
159 |
160 | {/**/}
161 |
162 |
163 |
164 | )
165 | }
166 |
167 | function EstimatedLPTokenCol(args: {addLiquidityEstimate, lpToken}) {
168 | const {addLiquidityEstimate, lpToken} = args;
169 |
170 | const [formattedEstimate, setFormattedEstimate] = useState(LOADING);
171 | const [textColor, setTextColor] = useState(LOADING_COLOR);
172 |
173 | useEffect(() => {
174 | if (addLiquidityEstimate !== null && formattedEstimate === LOADING) {
175 | const amtEther = formatEther(addLiquidityEstimate);
176 |
177 | setFormattedEstimate(amtEther.toString());
178 | }
179 | }, [addLiquidityEstimate])
180 |
181 | if (!lpToken) {
182 | return (
183 |
184 | Estimated LP Tokens received
185 | {formattedEstimate}
186 |
187 | )
188 | }
189 |
190 | return (
191 |
192 | Estimated LP Tokens received
193 | {formattedEstimate} {lpToken.symbol}
194 |
195 | )
196 | }
--------------------------------------------------------------------------------
/examples/frontend/src/pages/bridge/components/BridgeSwap.tsx:
--------------------------------------------------------------------------------
1 | import {useConnectedMetaMask} from "metamask-react";
2 |
3 | import {
4 | Tokens,
5 | ChainId
6 | } from "@synapseprotocol/sdk";
7 |
8 | import {
9 | useExecuteBridgeSwap,
10 | useBridgeSwapApproval,
11 | useCalculateBridgeSwapOutput
12 | } from "@synapseprotocol/sdk";
13 |
14 | import {Grid} from "@components/Grid";
15 | import Button from "@components/Button";
16 |
17 | import {
18 | ColBreak,
19 | DataRow,
20 | NeedsApprovalCol,
21 | ApproveButton,
22 | LOADING,
23 | LOADING_COLOR
24 | } from "./VariousComponents";
25 |
26 | import {useEffect, useState} from "react";
27 | import {MetamaskStatus} from "@utils";
28 | import {BigNumber} from "@ethersproject/bignumber";
29 | import {formatEther} from "@ethersproject/units";
30 |
31 |
32 | export default function BridgeSwap(props) {
33 | const {ethereum, chainId: cid, status} = useConnectedMetaMask();
34 |
35 | const chainId = status === MetamaskStatus.CONNECTED ? BigNumber.from(cid).toNumber() : ChainId.ETH;
36 |
37 | const
38 | tokenFrom = Tokens.JEWEL,
39 | tokenTo = Tokens.JEWEL,
40 | amountFrom = tokenFrom.etherToWei("8", chainId),
41 | chainIdTo = ChainId.AVALANCHE;
42 |
43 | const [calculateBridgeSwap, bridgeSwapEstimate] = useCalculateBridgeSwapOutput({
44 | ethereum,
45 | chainId,
46 | tokenFrom,
47 | tokenTo,
48 | amountFrom,
49 | chainIdTo
50 | });
51 |
52 | const {
53 | needsApprove,
54 | allowance: bridgeSpendAllowance,
55 | execApprove,
56 | approveTx,
57 | approveStatus
58 | } = useBridgeSwapApproval({
59 | ethereum,
60 | chainId,
61 | token: tokenFrom,
62 | amount: amountFrom
63 | });
64 |
65 | const [executeBridgeSwap, bridgeSwapTx] = useExecuteBridgeSwap({
66 | ethereum,
67 | chainId,
68 | tokenFrom,
69 | tokenTo,
70 | amountFrom,
71 | amountTo: bridgeSwapEstimate?.amountToReceive ?? BigNumber.from(0),
72 | chainIdTo
73 | });
74 |
75 | if (!bridgeSwapEstimate) {
76 | calculateBridgeSwap();
77 | }
78 |
79 | return (
80 |
81 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 | )
100 | }
101 |
102 |
103 |
104 | function SwapHeader(args: {tokenFrom, tokenTo, chainId, chainIdTo}) {
105 | const headerStr: string = `${args.tokenFrom.symbol} (${args.chainId}) - ${args.tokenTo.symbol} (${args.chainIdTo})`
106 |
107 | return ({headerStr}
)
108 | }
109 |
110 | function EstimatedOutputCol(args: {tokenTo, bridgeSwapEstimate}) {
111 | const {tokenTo, bridgeSwapEstimate} = args;
112 |
113 | const [formattedEstimate, setFormattedEstimate] = useState(LOADING);
114 | const [textColor, setTextColor] = useState(LOADING_COLOR);
115 |
116 | useEffect(() => {
117 | if (bridgeSwapEstimate !== null && formattedEstimate === LOADING) {
118 | const amt = bridgeSwapEstimate.amountToReceive;
119 | const amtEther = formatEther(amt);
120 |
121 | setFormattedEstimate(amtEther.toString());
122 | }
123 | }, [bridgeSwapEstimate])
124 |
125 | return (
126 |
127 | Estimated output
128 | {formattedEstimate} {tokenTo.symbol}
129 |
130 | )
131 | }
132 |
133 | function BridgeFeeCol(args: {tokenFrom, bridgeSwapEstimate}) {
134 | const {tokenFrom, bridgeSwapEstimate} = args;
135 |
136 | const [formattedEstimate, setFormattedEstimate] = useState(LOADING);
137 | const [textColor, setTextColor] = useState(LOADING_COLOR);
138 |
139 | useEffect(() => {
140 | if (bridgeSwapEstimate !== null && formattedEstimate === LOADING) {
141 | const amt = bridgeSwapEstimate.bridgeFee;
142 | const amtEther = formatEther(amt);
143 |
144 | setFormattedEstimate(amtEther.toString());
145 | setTextColor("text-indigo-700")
146 | }
147 | }, [bridgeSwapEstimate])
148 |
149 | return (
150 |
151 | Bridge fee
152 | {formattedEstimate} {tokenFrom.symbol}
153 |
154 | )
155 | }
156 |
157 | function BridgeButton(args: {executeBridgeSwap}) {
158 | const {executeBridgeSwap} = args;
159 |
160 | return (
161 |
162 |
166 |
167 | )
168 | }
--------------------------------------------------------------------------------
/examples/frontend/src/pages/bridge/components/RemoveLiquidity.tsx:
--------------------------------------------------------------------------------
1 | import {useConnectedMetaMask} from "metamask-react";
2 |
3 | import {ChainId} from "@synapseprotocol/sdk";
4 |
5 | import {
6 | useRemoveLiquidity,
7 | useLPTokenApproval,
8 | useCalculateRemoveLiquidity,
9 | useChainStableswapLPToken
10 | } from "@synapseprotocol/sdk";
11 |
12 | import {Grid} from "@components/Grid";
13 |
14 | import {
15 | ApproveButton,
16 | ExecuteButton,
17 | ColBreak,
18 | DataRow,
19 | LOADING,
20 | LOADING_COLOR, NeedsApprovalCol
21 | } from "./VariousComponents";
22 |
23 | import {useState} from "react";
24 | import {MetamaskStatus} from "@utils";
25 | import {BigNumber} from "@ethersproject/bignumber";
26 |
27 | export default function RemoveLiquidity(props) {
28 | const {ethereum, chainId: cid, status} = useConnectedMetaMask();
29 |
30 | const chainId = status === MetamaskStatus.CONNECTED ? BigNumber.from(cid).toNumber() : ChainId.ETH;
31 |
32 | const [stableswapPool] = useChainStableswapLPToken(ethereum, chainId);
33 |
34 | const removeLiquidityAmount = "55";
35 |
36 | const deadline = Math.round((new Date().getTime() / 1000) + 60 * 10);
37 |
38 | const [calculateRemoveLiquidity, removeLiquidityEstimate] = useCalculateRemoveLiquidity({
39 | ethereum,
40 | chainId,
41 | lpToken: stableswapPool,
42 | amount: removeLiquidityAmount
43 | });
44 |
45 | if (stableswapPool && !removeLiquidityEstimate) {
46 | calculateRemoveLiquidity();
47 | }
48 |
49 | const {
50 | needsApprove,
51 | allowance: swapPoolSpendAllowance,
52 | execApprove,
53 | approveTx,
54 | approveStatus
55 | } = useLPTokenApproval({
56 | ethereum,
57 | chainId,
58 | lpToken: stableswapPool,
59 | amount: removeLiquidityAmount
60 | });
61 |
62 | const [removeLiquidity, removeLiquidityTx] = useRemoveLiquidity({
63 | ethereum,
64 | chainId,
65 | deadline,
66 | lpToken: stableswapPool,
67 | amount: removeLiquidityAmount,
68 | minAmounts: removeLiquidityEstimate
69 | });
70 |
71 | return (
72 |
73 |
74 |
75 |
76 | {stableswapPool && }
77 |
78 | {stableswapPool && }
79 |
80 |
81 |
82 |
83 | )
84 | }
85 |
86 | function EstimatedTokensCol(args: {removeLiquidityEstimate, lpToken}) {
87 | const {removeLiquidityEstimate, lpToken} = args;
88 |
89 | const [textColor, setTextColor] = useState(LOADING_COLOR);
90 |
91 | if (!lpToken || !removeLiquidityEstimate) {
92 | return (
93 |
94 | Estimated LP Tokens received
95 | {LOADING}
96 |
97 | )
98 | }
99 |
100 | return (
101 | removeLiquidityEstimate.map((amount, idx) => {
102 | const poolToken = lpToken.poolTokens[idx];
103 | const amtEther = poolToken.weiToEtherString(amount, lpToken.chainId);
104 | return (
105 |
106 | Estimated LP Tokens received
107 | {amtEther} {poolToken.symbol}
108 |
109 | )
110 | })
111 | )
112 | }
--------------------------------------------------------------------------------
/examples/frontend/src/pages/bridge/components/VariousComponents.tsx:
--------------------------------------------------------------------------------
1 | import {useEffect, useState} from "react";
2 | import Button from "@components/Button";
3 |
4 | export const LOADING = "Loading...";
5 | export const LOADING_COLOR = "text-sky-500";
6 |
7 | export const RowBreak = () => ()
8 | export const ColBreak = () => (
)
9 |
10 | export const DataRow = ({children, pb=8}) => (
{children}
)
11 | export const DataCol = ({children, pb=8}) => (
{children}
)
12 |
13 | export function NeedsApprovalCol(args: {token, needsApproval}) {
14 | const {token, needsApproval} = args;
15 |
16 | const [text, setText] = useState
(LOADING);
17 | const [textColor, setTextColor] = useState(LOADING_COLOR);
18 |
19 | useEffect(() => {
20 | if (needsApproval !== null && text === LOADING) {
21 | setText(`${needsApproval ? "Yes" : "No"}`);
22 | if (needsApproval) {
23 | setTextColor("text-amber-500");
24 | } else {
25 | setTextColor("text-emerald-600");
26 | }
27 | }
28 | }, [needsApproval])
29 |
30 | return (
31 |
32 | {token.symbol} approval required
33 | {text}
34 |
35 | )
36 | }
37 |
38 | export function ApproveButton(args: {execApprove, token, approveStatus}) {
39 | const {execApprove, token, approveStatus} = args;
40 |
41 | return (
42 |
43 |
48 |
49 | )
50 | }
51 |
52 | export function ExecuteButton(args: {text, execFn}) {
53 | const {text, execFn} = args;
54 |
55 | return (
56 |
57 |
61 |
62 | )
63 | }
--------------------------------------------------------------------------------
/examples/frontend/src/pages/bridge/hooks/useActionButtonOnClick.tsx:
--------------------------------------------------------------------------------
1 | import {ethers} from "ethers";
2 | import {SetStateFunction} from "@utils";
3 | import {ButtonProps} from "@components/Button";
4 |
5 |
6 | export function useActionButtonOnClick(
7 | setDisabled: SetStateFunction,
8 | setButtonProps: SetStateFunction
9 | ) {
10 | const fn = (executeTxnFn: () => Promise) => {
11 | return async () => {
12 | setButtonProps({
13 | text: "Waiting for transaction to send...",
14 | });
15 | setDisabled(true);
16 |
17 | try {
18 | const txn = await executeTxnFn();
19 |
20 | setButtonProps({
21 | text: "waiting for transaction to confirm..."
22 | });
23 | setDisabled(true);
24 |
25 | await txn.wait(1);
26 |
27 | setButtonProps({
28 | text: "Success!"
29 | });
30 | } catch (e) {
31 | const err = e instanceof Error ? e : new Error(e);
32 | console.error(err);
33 | setButtonProps({
34 | text: `Error: ${err.message}`,
35 | });
36 | setDisabled(true);
37 | }
38 |
39 | return
40 | }
41 | }
42 |
43 | return [fn] as const
44 | }
45 |
--------------------------------------------------------------------------------
/examples/frontend/src/pages/bridge/index.tsx:
--------------------------------------------------------------------------------
1 | import {useMetaMask, useConnectedMetaMask} from "metamask-react";
2 | import {MetamaskStatus} from "@utils";
3 |
4 | import BridgeSwap from "./components/BridgeSwap";
5 | import AddLiquiditySingleToken from "./components/AddLiquidity";
6 | import RemoveLiquidity from "./components/RemoveLiquidity";
7 |
8 | function BridgePageContent(props) {
9 | const {status} = useMetaMask();
10 |
11 | if (status !== MetamaskStatus.CONNECTED) {
12 | return (
13 |
14 | Loading...
15 |
16 | )
17 | }
18 |
19 | return(
20 |
21 | {/*
*/}
22 |
23 | {/*
*/}
24 |
25 | )
26 | }
27 |
28 | export default function BridgePage(props) {
29 | return (
30 |
31 | )
32 | }
--------------------------------------------------------------------------------
/examples/frontend/src/react-app-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
--------------------------------------------------------------------------------
/examples/frontend/src/reportWebVitals.ts:
--------------------------------------------------------------------------------
1 | import { ReportHandler } from 'web-vitals';
2 |
3 | const reportWebVitals = (onPerfEntry?: ReportHandler) => {
4 | if (onPerfEntry && onPerfEntry instanceof Function) {
5 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
6 | getCLS(onPerfEntry);
7 | getFID(onPerfEntry);
8 | getFCP(onPerfEntry);
9 | getLCP(onPerfEntry);
10 | getTTFB(onPerfEntry);
11 | });
12 | }
13 | };
14 |
15 | export default reportWebVitals;
16 |
--------------------------------------------------------------------------------
/examples/frontend/src/setupTests.ts:
--------------------------------------------------------------------------------
1 | // jest-dom adds custom jest matchers for asserting on DOM nodes.
2 | // allows you to do things like:
3 | // expect(element).toHaveTextContent(/react/i)
4 | // learn more: https://github.com/testing-library/jest-dom
5 | import '@testing-library/jest-dom';
6 |
--------------------------------------------------------------------------------
/examples/frontend/src/utils/amounts.ts:
--------------------------------------------------------------------------------
1 | import {BigNumber, BigNumberish} from "@ethersproject/bignumber";
2 |
3 | import {formatEther, parseUnits} from "@ethersproject/units";
4 |
5 | export function valueWei(ether: BigNumberish, decimals: number): BigNumber {
6 | return parseUnits(
7 | BigNumber.from(ether).toString(),
8 | decimals
9 | )
10 | }
11 |
12 | interface RoundingOptions {
13 | round: boolean,
14 | places?: number,
15 | }
16 |
17 | const PLACES: number = 6;
18 |
19 | export function valueEther(wei: BigNumberish, opts?: RoundingOptions): string {
20 | const weiVal = BigNumber.from(wei);
21 | let amtEther: string = formatEther(weiVal);
22 |
23 | opts = opts ?? { round: false, places: PLACES };
24 | const {round, places=PLACES} = opts;
25 |
26 | if (round) {
27 | let exp = 18-places;
28 | let pow = BigNumber.from(10).pow(exp);
29 |
30 | let remainder = weiVal.mod(pow);
31 | amtEther = formatEther(weiVal.sub(remainder));
32 | }
33 |
34 | return amtEther
35 | }
36 |
--------------------------------------------------------------------------------
/examples/frontend/src/utils/index.ts:
--------------------------------------------------------------------------------
1 | export * from "./react_utils";
2 |
3 | export * from "./amounts";
4 |
5 | export * from "./types";
6 |
7 | export * from "./transactions";
8 |
9 | export type {
10 | EventFunction,
11 | SetStateFunction,
12 | DeferredPopulatedTransaction,
13 | OnClickFunction,
14 | } from "./types";
15 |
16 | export const asError = (e: any): Error => e instanceof Error ? e : new Error(e)
17 |
18 | export const isNullOrUndefined = (value: any): boolean => typeof value === "undefined" || value === null
--------------------------------------------------------------------------------
/examples/frontend/src/utils/react_utils.ts:
--------------------------------------------------------------------------------
1 | export function classNames(...classes: string[]) {
2 | return classes.filter(Boolean).join(" ")
3 | }
4 |
5 |
--------------------------------------------------------------------------------
/examples/frontend/src/utils/transactions.ts:
--------------------------------------------------------------------------------
1 | import {DeferredPopulatedTransaction} from "./types";
2 | import {ethers} from "ethers";
3 |
4 | export function getSigner(ethereum: any) {
5 | return (new ethers.providers.Web3Provider(ethereum)).getSigner()
6 | }
7 |
8 | export interface SendTransactionResponse {
9 | transaction?: ethers.providers.TransactionResponse,
10 | error?: Error,
11 | }
12 |
13 | function catchTransactionError(e: any): SendTransactionResponse {
14 | let err = e instanceof Error ? e : new Error(e);
15 |
16 | return {error: err}
17 | }
18 |
19 | export function sendTransaction(
20 | transaction: DeferredPopulatedTransaction,
21 | ethereum: any
22 | ): Promise {
23 | return Promise.resolve(transaction)
24 | .then((txn): Promise =>
25 | getSigner(ethereum).sendTransaction(txn)
26 | .then((txnResponse) => ({transaction: txnResponse}))
27 | .catch(catchTransactionError)
28 | )
29 | .catch(catchTransactionError)
30 | }
--------------------------------------------------------------------------------
/examples/frontend/src/utils/types.ts:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | import type {MouseEventHandler} from "react";
4 | import {ethers} from "ethers";
5 |
6 | export type EventFunction = MouseEventHandler;
7 |
8 | export type SetStateFunction = React.Dispatch>
9 |
10 | export type OnClickFunction = (...args: any[]) => (void | any);
11 |
12 | export type DeferredPopulatedTransaction = ethers.PopulatedTransaction | Promise;
13 |
14 |
15 | export enum TransactionStatus {
16 | NOT_SENT,
17 | SENT,
18 | COMPLETE,
19 | ERROR,
20 | }
21 |
22 | export enum MetamaskStatus {
23 | INIT = "initializing",
24 | UNAVAILABLE = "unavailable",
25 | NOT_CONNECTED = "notConnected",
26 | CONNECTING = "connecting",
27 | CONNECTED = "connected"
28 | }
--------------------------------------------------------------------------------
/examples/frontend/tailwind.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | content: [
3 | "./src/**/*.{ts,tsx}"
4 | ],
5 | theme: {
6 | extend: {},
7 | },
8 | plugins: [],
9 | }
10 |
--------------------------------------------------------------------------------
/examples/frontend/tsconfig.base.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "es5",
4 | "lib": [
5 | "dom",
6 | "dom.iterable",
7 | "esnext"
8 | ],
9 | "baseUrl": ".",
10 | "paths": {
11 | "@utils": ["./src/utils"],
12 | "@utils/*": ["./src/utils/*"],
13 | "@hooks": ["./src/hooks"],
14 | "@hooks/*": ["./src/hooks/*"],
15 | "@components": ["./src/components"],
16 | "@components/*": ["./src/components/*"],
17 | "@pages": ["./src/pages"],
18 | "@pages/*": ["./src/pages/*"],
19 | },
20 | "allowJs": false,
21 | "skipLibCheck": true,
22 | "esModuleInterop": true,
23 | "allowSyntheticDefaultImports": true,
24 | "strict": false,
25 | "forceConsistentCasingInFileNames": true,
26 | "noFallthroughCasesInSwitch": true,
27 | "module": "ESNext",
28 | "moduleResolution": "node",
29 | "resolveJsonModule": true,
30 | "isolatedModules": true,
31 | "noEmit": true,
32 | "jsx": "react-jsx"
33 | },
34 | "include": [
35 | "src"
36 | ]
37 | }
38 |
--------------------------------------------------------------------------------
/examples/frontend/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "include": ["src"],
3 | "extends": "./tsconfig.base.json"
4 | }
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@synapseprotocol/sdk",
3 | "version": "0.95.1-alpha.68",
4 | "description": "Synapse Protocol's NodeJS SDK.",
5 | "repository": "github.com/synapsecns/sdk",
6 | "author": "synapsecns",
7 | "license": "ISC",
8 | "private": false,
9 | "publishConfig": {
10 | "access": "public",
11 | "registry": "https://registry.npmjs.org"
12 | },
13 | "files": [
14 | "/dist",
15 | "/LICENSE",
16 | "/README.md",
17 | "/CONTRIBUTING.md",
18 | "/tsconfig.json",
19 | "/package.json"
20 | ],
21 | "type": "module",
22 | "main": "dist/index.js",
23 | "types": "dist/index.d.ts",
24 | "scripts": {
25 | "prepare": "ts-patch install -s",
26 | "setup:dev": "yarn install --frozen-lockfile",
27 | "setup": "yarn && yarn -s run typegen",
28 | " -----------": "",
29 | "pretest": "yarn run react:add",
30 | "precoverage:test": "yarn run pretest",
31 | "test": "scripts/sdk-tests test",
32 | "coverage:test": "scripts/sdk-tests coverage test",
33 | "coverage:report": "scripts/sdk-tests coverage report",
34 | "posttest": "yarn run react:remove",
35 | " -----------": "",
36 | "prebuild": "yarn prepare && rm -rf ./dist",
37 | "build": "tsc --project tsconfig.build.json",
38 | "prepublishOnly": "yarn build",
39 | " -----------": "",
40 | "preabigen": "scripts/generate abigen prepare",
41 | "preabigen:compile": "scripts/generate abigen prepare",
42 | "abigen": "scripts/generate abigen download",
43 | "abigen:compile": "scripts/generate abigen compile",
44 | "typegen": "scripts/generate typegen",
45 | "posttypegen": "rm -rf ./abigen",
46 | " -----------": "",
47 | "fixup": "scripts/fixup",
48 | "react:add": "yarn add --frozen-lockfile --dev react@^17.0.0",
49 | "react:remove": "yarn remove --frozen-lockfile react --dev && yarn add --frozen-lockfile --peer react@^17.0.0",
50 | "docs": "typedoc src/index.ts"
51 | },
52 | "dependencies": {
53 | "@ethersproject/abi": "^5.5.0",
54 | "@ethersproject/abstract-signer": "^5.5.0",
55 | "@ethersproject/bignumber": "^5.5.0",
56 | "@ethersproject/bytes": "^5.5.0",
57 | "@ethersproject/constants": "^5.5.0",
58 | "@ethersproject/contracts": "^5.5.0",
59 | "@ethersproject/providers": "^5.5.0",
60 | "@ethersproject/transactions": "^5.5.0",
61 | "@ethersproject/units": "^5.5.0",
62 | "@ethersproject/wallet": "^5.5.0",
63 | "@ethersproject/web": "^5.5.1",
64 | "ethers": "^5.5.1",
65 | "lodash": "^4.17.21",
66 | "lodash-es": "^4.17.21",
67 | "node-fetch": "^3.2.6",
68 | "object-hash": "^3.0.0"
69 | },
70 | "devDependencies": {
71 | "@ethereum-waffle/chai": "3.4.4",
72 | "@typechain/ethers-v5": "10.1.0",
73 | "@types/chai-as-promised": "7.1.5",
74 | "@types/chai-things": "0.0.35",
75 | "@types/lodash": "4.14.185",
76 | "@types/lodash-es": "4.17.6",
77 | "@types/mocha": "9.1.1",
78 | "@types/node": "17.0.21",
79 | "@types/object-hash": "2.2.1",
80 | "@types/react": "18.0.21",
81 | "@zoltu/typescript-transformer-append-js-extension": "1.0.1",
82 | "c8": "7.12.0",
83 | "chai": "4.3.6",
84 | "chai-as-promised": "7.1.1",
85 | "chai-things": "0.2.0",
86 | "coveralls": "3.1.1",
87 | "dotenv": "16.0.0",
88 | "mocha": "10.0.0",
89 | "mocha-lcov-reporter": "1.3.0",
90 | "mocha-steps": "1.3.0",
91 | "ts-mocha": "10.0.0",
92 | "ts-node": "10.9.1",
93 | "ts-patch": "2.0.2",
94 | "typechain": "8.1.0",
95 | "typedoc": "^0.23.21",
96 | "typescript": "4.7.4",
97 | "typescript-transform-paths": "3.3.1"
98 | },
99 | "peerDependencies": {
100 | "react": "^17.0.0"
101 | }
102 | }
--------------------------------------------------------------------------------
/scripts/ERC20.json:
--------------------------------------------------------------------------------
1 | {
2 | "abi": [
3 | {
4 | "constant": true,
5 | "inputs": [],
6 | "name": "name",
7 | "outputs": [
8 | {
9 | "name": "",
10 | "type": "string"
11 | }
12 | ],
13 | "payable": false,
14 | "stateMutability": "view",
15 | "type": "function"
16 | },
17 | {
18 | "constant": false,
19 | "inputs": [
20 | {
21 | "name": "_spender",
22 | "type": "address"
23 | },
24 | {
25 | "name": "_value",
26 | "type": "uint256"
27 | }
28 | ],
29 | "name": "approve",
30 | "outputs": [
31 | {
32 | "name": "",
33 | "type": "bool"
34 | }
35 | ],
36 | "payable": false,
37 | "stateMutability": "nonpayable",
38 | "type": "function"
39 | },
40 | {
41 | "constant": true,
42 | "inputs": [],
43 | "name": "totalSupply",
44 | "outputs": [
45 | {
46 | "name": "",
47 | "type": "uint256"
48 | }
49 | ],
50 | "payable": false,
51 | "stateMutability": "view",
52 | "type": "function"
53 | },
54 | {
55 | "constant": false,
56 | "inputs": [
57 | {
58 | "name": "_from",
59 | "type": "address"
60 | },
61 | {
62 | "name": "_to",
63 | "type": "address"
64 | },
65 | {
66 | "name": "_value",
67 | "type": "uint256"
68 | }
69 | ],
70 | "name": "transferFrom",
71 | "outputs": [
72 | {
73 | "name": "",
74 | "type": "bool"
75 | }
76 | ],
77 | "payable": false,
78 | "stateMutability": "nonpayable",
79 | "type": "function"
80 | },
81 | {
82 | "constant": true,
83 | "inputs": [],
84 | "name": "decimals",
85 | "outputs": [
86 | {
87 | "name": "",
88 | "type": "uint8"
89 | }
90 | ],
91 | "payable": false,
92 | "stateMutability": "view",
93 | "type": "function"
94 | },
95 | {
96 | "constant": true,
97 | "inputs": [
98 | {
99 | "name": "_owner",
100 | "type": "address"
101 | }
102 | ],
103 | "name": "balanceOf",
104 | "outputs": [
105 | {
106 | "name": "balance",
107 | "type": "uint256"
108 | }
109 | ],
110 | "payable": false,
111 | "stateMutability": "view",
112 | "type": "function"
113 | },
114 | {
115 | "constant": true,
116 | "inputs": [],
117 | "name": "symbol",
118 | "outputs": [
119 | {
120 | "name": "",
121 | "type": "string"
122 | }
123 | ],
124 | "payable": false,
125 | "stateMutability": "view",
126 | "type": "function"
127 | },
128 | {
129 | "constant": false,
130 | "inputs": [
131 | {
132 | "name": "_to",
133 | "type": "address"
134 | },
135 | {
136 | "name": "_value",
137 | "type": "uint256"
138 | }
139 | ],
140 | "name": "transfer",
141 | "outputs": [
142 | {
143 | "name": "",
144 | "type": "bool"
145 | }
146 | ],
147 | "payable": false,
148 | "stateMutability": "nonpayable",
149 | "type": "function"
150 | },
151 | {
152 | "constant": true,
153 | "inputs": [
154 | {
155 | "name": "_owner",
156 | "type": "address"
157 | },
158 | {
159 | "name": "_spender",
160 | "type": "address"
161 | }
162 | ],
163 | "name": "allowance",
164 | "outputs": [
165 | {
166 | "name": "",
167 | "type": "uint256"
168 | }
169 | ],
170 | "payable": false,
171 | "stateMutability": "view",
172 | "type": "function"
173 | },
174 | {
175 | "payable": true,
176 | "stateMutability": "payable",
177 | "type": "fallback"
178 | },
179 | {
180 | "anonymous": false,
181 | "inputs": [
182 | {
183 | "indexed": true,
184 | "name": "owner",
185 | "type": "address"
186 | },
187 | {
188 | "indexed": true,
189 | "name": "spender",
190 | "type": "address"
191 | },
192 | {
193 | "indexed": false,
194 | "name": "value",
195 | "type": "uint256"
196 | }
197 | ],
198 | "name": "Approval",
199 | "type": "event"
200 | },
201 | {
202 | "anonymous": false,
203 | "inputs": [
204 | {
205 | "indexed": true,
206 | "name": "from",
207 | "type": "address"
208 | },
209 | {
210 | "indexed": true,
211 | "name": "to",
212 | "type": "address"
213 | },
214 | {
215 | "indexed": false,
216 | "name": "value",
217 | "type": "uint256"
218 | }
219 | ],
220 | "name": "Transfer",
221 | "type": "event"
222 | }
223 | ]
224 | }
--------------------------------------------------------------------------------
/scripts/fixup:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | source ./scripts/utils
4 |
5 | git commit -m "fixup commit"
6 |
7 | HEAD_COMMIT="$(git rev-parse --short HEAD)"
8 |
9 | _SEQ="sed -i -re 's/^pick ${HEAD_COMMIT} /fixup ${HEAD_COMMIT} /g'"
10 |
11 | GIT_SEQUENCE_EDITOR="$_SEQ" rebase_interactive 2
--------------------------------------------------------------------------------
/scripts/gen_changelog:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | source ./scripts/utils
4 |
5 | usage () {
6 | return_code=$1
7 |
8 | cat <<"HEREDOC"
9 | gen_changelog
10 |
11 | Description:
12 | Generate a CHANGELOG.md file from a pull request.
13 |
14 | Usage:
15 | gen_changelog [-h] [-c]
16 |
17 | Parameters:
18 | : Pull request number to generate a changelog from
19 | : Package version to add to the header of the changelog
20 |
21 | Options:
22 | -c|--commit : add and commit the generated changelog to the current git branch. Default: false
23 | -h|--help : show this usage text
24 | HEREDOC
25 |
26 | exit $return_code
27 | }
28 |
29 | make_changelog () {
30 | if [[ -z "${1-}" ]]; then
31 | echo -e "Parameter PULL_REQUEST must be passed.\n"
32 | echo -e "USAGE:\n"
33 | usage 1
34 | fi
35 |
36 | if [[ -z "${2-}" ]]; then
37 | echo -e "Parameter PACKAGE_VERSION must be passed.\n"
38 | echo -e "USAGE:\n"
39 | usage 1
40 | fi
41 |
42 | set +o noclobber
43 |
44 | PULL_REQUEST="$1"
45 | PACKAGE_VERSION="$2"
46 | DO_GIT_COMMIT="$3"
47 |
48 | REQUEST_ENDPOINT="https://api.github.com/repos/synapsecns/sdk/pulls/${PULL_REQUEST}"
49 |
50 | FILENAME="CHANGELOG.md"
51 |
52 | CHANGELOG_HEADER="# Changelog and Release notes (v${PACKAGE_VERSION})"
53 | BODY="$(curl_github "${REQUEST_ENDPOINT}" | jq -r '.body')"
54 |
55 | CHANGELOG_BODY=$(echo -e "${CHANGELOG_HEADER}\n\n${BODY}")
56 | echo "${CHANGELOG_BODY}" > $FILENAME
57 |
58 | sed -i '' -r 's#(([0-9a-f]{7})[0-9a-f]{33})#[\2](https://github.com/synapsecns/sdk/commits/\1)#g' $FILENAME
59 |
60 | if [[ $DO_GIT_COMMIT = "y" ]]; then
61 | git add $FILENAME
62 | git commit -m "Generate changelog for v${PACKAGE_VERSION}"
63 | fi
64 | }
65 |
66 | if [[ "$1" = "help" ]]; then usage 0; fi
67 |
68 | set -o errexit -o pipefail -o noclobber -o nounset
69 |
70 | # -allow a command to fail with !’s side effect on errexit
71 | # -use return value from ${PIPESTATUS[0]}, because ! hosed $?
72 | ! getopt --test > /dev/null
73 | if [[ ${PIPESTATUS[0]} -ne 4 ]]; then
74 | echo '`getopt --test` failed in this environment.'
75 | if [[ "$OSTYPE" == "darwin"* ]]; then
76 | echo 'try running `brew install gnu-getopt` and then putting `export PATH="/usr/local/opt/gnu-getopt/bin:$PATH"` into your shellrc file'
77 | fi
78 | exit 1
79 | fi
80 |
81 | OPTIONS=hc
82 | LONGOPTS=help,commit
83 |
84 | # -regarding ! and PIPESTATUS see above
85 | # -temporarily store output to be able to check for errors
86 | # -activate quoting/enhanced mode (e.g. by writing out “--options”)
87 | # -pass arguments only via -- "$@" to separate them correctly
88 | ! PARSED=$(getopt --options=$OPTIONS --longoptions=$LONGOPTS --name "$0" -- "$@")
89 | if [[ ${PIPESTATUS[0]} -ne 0 ]]; then
90 | # e.g. return value is 1
91 | # then getopt has complained about wrong arguments to stdout
92 | exit 2
93 | fi
94 | # read getopt’s output this way to handle the quoting right:
95 | eval set -- "$PARSED"
96 |
97 | gitcommit=n
98 | # now enjoy the options in order and nicely split until we see --
99 | while true; do
100 | case "$1" in
101 | -h|--help)
102 | usage 0
103 | ;;
104 | -c|--commit)
105 | gitcommit=y
106 | shift
107 | ;;
108 | --)
109 | shift
110 | break
111 | ;;
112 | esac
113 | done
114 |
115 | make_changelog "$@" "$gitcommit"
--------------------------------------------------------------------------------
/scripts/generate:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | source .env
4 | source ./scripts/utils
5 |
6 | CURRENT_DIR="${PWD}"
7 |
8 | COMBINED_OUTPUTS_DIR=$(pwd_path "abigen")
9 | TEMP_INPUT=$(pwd_path "temp_input.json")
10 | TEMP_OUTPUT=$(pwd_path "temp_output.json")
11 |
12 | PYTHON_PATH=$(which python3)
13 | PYTHON_HELPER_PATH=$(pwd_path ./scripts/py_helpers.py)
14 |
15 | SYNAPSE_BRIDGE="SynapseBridge"
16 | L1BRIDGEZAP="L1BridgeZap"
17 | L2BRIDGEZAP="L2BridgeZap"
18 | BRIDGECONFIG="BridgeConfigV3"
19 | SWAP_FLASH_LOAN="SwapFlashLoan"
20 | SYNAPSE_ERC20="SynapseERC20"
21 | AVAX_JEWEL_MIGRATION="AvaxJewelMigration"
22 |
23 | JQ_WANT='{abi: .abi, userdoc: .userdoc, devdoc: .devdoc}'
24 |
25 | RAW_GITHUB_URI="https://raw.githubusercontent.com/synapsecns/synapse-contracts"
26 |
27 | usage () {
28 | return_code=$1
29 |
30 | cat <<"HEREDOC"
31 | generate
32 |
33 | Description:
34 | Download or locally compile ABIs for synapsecns EVM Smart Contracts, or
35 | generate Typescript typings for the same.
36 |
37 | Usage:
38 | abigen:
39 | Pre-setup:
40 | generate [-h] abigen prepare
41 |
42 | Download from GitHub:
43 | generate [-h] abigen download [git_branch]
44 |
45 | Compile locally:
46 | generate [-h] abigen compile
47 |
48 | Generate typings:
49 | generate [-h] typegen
50 |
51 | Parameters:
52 | download:
53 | [git_branch] : [Optional] branch to download ABI definitions from. Defaults to master
54 |
55 | compile:
56 | : Path to directory containing the synapse-contracts repository
57 | : Path to solc binary, can be passed using something like $(which solc) or $(whence solc)
58 |
59 | Options:
60 | -h|--help : show this usage text
61 | HEREDOC
62 |
63 | exit $return_code
64 | }
65 |
66 | check_or_fallback () {
67 | [[ -z "$1" ]] && echo "$2" || echo "$1"
68 | }
69 |
70 | output_json () {
71 | DATA="$1"
72 | CONTRACT="$2"
73 | echo "${DATA}" | jq "${JQ_WANT}" > "${COMBINED_OUTPUTS_DIR}/${CONTRACT}.json"
74 | }
75 |
76 | PY_HELPER () {
77 | echo $($PYTHON_PATH "${PYTHON_HELPER_PATH}" "$@")
78 | }
79 |
80 | compile_abi () {
81 | BASE_CONTRACTS_PATH=$(realpath "$1")
82 | SOLC_PATH=$(realpath "$2")
83 | CONTRACT_PATH="$3"
84 | CONTRACT_NAME="$4"
85 |
86 | solc_input_json=$(PY_HELPER make_solc_input "${BASE_CONTRACTS_PATH}" "${CONTRACT_PATH}" "${CONTRACT_NAME}")
87 | cd "${BASE_CONTRACTS_PATH}"
88 | $SOLC_PATH --allow-paths="*," --no-color --standard-json $solc_input_json > "${TEMP_OUTPUT}"
89 |
90 | cd "${CURRENT_DIR}"
91 | output_json "$(PY_HELPER sol_output "${TEMP_OUTPUT}" "${CONTRACT_PATH}" "${CONTRACT_NAME}")" "${CONTRACT_NAME}"
92 |
93 | rm -rf "${TEMP_INPUT}" "${TEMP_OUTPUT}"
94 | }
95 |
96 | get_abi_from_gh () {
97 | BRANCH="$1"
98 | CHAIN="$2"
99 | CONTRACT="$3"
100 |
101 | REQUEST_ENDPOINT="${RAW_GITHUB_URI}/${BRANCH}/deployments/${CHAIN}/${CONTRACT}.json"
102 |
103 | output_json "$(curl_github "${REQUEST_ENDPOINT}")" "${CONTRACT}"
104 | }
105 |
106 | main_help () {
107 | usage $1
108 | }
109 |
110 | abigen_prepare () {
111 | mkdir -p ./abigen && cp ./scripts/ERC20.json ./abigen/ERC20.json
112 | }
113 |
114 | abigen_compile () {
115 | BRIDGE_PATH="contracts/bridge"
116 | WRAPPERS_PATH="${BRIDGE_PATH}/wrappers"
117 |
118 | compile_abi "$@" "${BRIDGE_PATH}/${SYNAPSE_BRIDGE}.sol" "${SYNAPSE_BRIDGE}"
119 | compile_abi "$@" "${WRAPPERS_PATH}/${L1BRIDGEZAP}.sol" "${L1BRIDGEZAP}"
120 | compile_abi "$@" "${WRAPPERS_PATH}/${L2BRIDGEZAP}.sol" "${L2BRIDGEZAP}"
121 | compile_abi "$@" "${BRIDGE_PATH}/${BRIDGECONFIG}.sol" "${BRIDGECONFIG}"
122 | compile_abi "$@" "contracts/amm/${SWAP_FLASH_LOAN}.sol" "${SWAP_FLASH_LOAN}"
123 | compile_abi "$@" "${BRIDGE_PATH}/${SYNAPSE_ERC20}.sol" "${SYNAPSE_ERC20}"
124 | compile_abi "$@" "${WRAPPERS_PATH}/${AVAX_JEWEL_MIGRATION}.sol" "${AVAX_JEWEL_MIGRATION}"
125 | }
126 |
127 | DFK="dfk"
128 | METIS="metis"
129 | CRONOS="cronos"
130 | MAINNET="mainnet"
131 | AVALANCHE="avalanche"
132 |
133 | abigen_download () {
134 | GIT_BRANCH=$(check_or_fallback "$1" "master")
135 |
136 | get_abi_from_gh "${GIT_BRANCH}" "${DFK}" "${SYNAPSE_BRIDGE}"
137 | get_abi_from_gh "${GIT_BRANCH}" "${DFK}" "${L1BRIDGEZAP}"
138 | get_abi_from_gh "${GIT_BRANCH}" "${CRONOS}" "${L2BRIDGEZAP}"
139 | get_abi_from_gh "${GIT_BRANCH}" "${MAINNET}" "${BRIDGECONFIG}"
140 | get_abi_from_gh "${GIT_BRANCH}" "${METIS}" "${SWAP_FLASH_LOAN}"
141 | get_abi_from_gh "${GIT_BRANCH}" "${MAINNET}" "${SYNAPSE_ERC20}"
142 | get_abi_from_gh "${GIT_BRANCH}" "${AVALANCHE}" "${AVAX_JEWEL_MIGRATION}"
143 | }
144 |
145 | main_typegen () {
146 | typechain --target ethers-v5 --out-dir src/internal/gen ./abigen/*.json
147 | }
148 |
149 | main_abigen () {
150 | command=$1
151 | case $command in
152 | "" | "-h" | "--help")
153 | main_help
154 | ;;
155 | *)
156 | shift
157 | abigen_${command} $@
158 | if [ $? = 127 ]; then
159 | echo "Error: '$command' is not a known command." >&2
160 | echo " Run 'generate --help' for usage." >&2
161 | exit 1
162 | fi
163 | ;;
164 | esac
165 | }
166 |
167 | command=$1
168 | case $command in
169 | "" | "-h" | "--help")
170 | main_help
171 | ;;
172 | *)
173 | shift
174 | main_${command} $@
175 | if [ $? = 127 ]; then
176 | echo "Error: '$command' is not a known command." >&2
177 | echo " Run 'generate --help' for usage." >&2
178 | exit 1
179 | fi
180 | ;;
181 | esac
--------------------------------------------------------------------------------
/scripts/make_release_branch:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | HEAD_BRANCH="$1"
4 | RELEASE_VERSION="$2"
5 |
6 | BRANCH_NAME="release-v${RELEASE_VERSION}"
7 |
8 | # first, checkout a new release branch
9 |
10 | git checkout "${HEAD_BRANCH}"
11 | git checkout -b "${BRANCH_NAME}"
12 |
13 | #nvm use
14 | #yarn version --new-version "$RELEASE_VERSION" --no-git-tag-version
15 |
16 | git push -u origin "${BRANCH_NAME}"
--------------------------------------------------------------------------------
/scripts/py_helpers.py:
--------------------------------------------------------------------------------
1 | import os
2 | import json
3 | import argparse
4 | from pprint import pprint
5 |
6 |
7 | BASE_PATH_REPLACE_KEY = "[BASE_PATH]"
8 |
9 | SOLC_TEMPLATE = {
10 | "language": "Solidity",
11 | "sources": {},
12 | "settings": {
13 | "remappings": [
14 | f"@openzeppelin/contracts/={BASE_PATH_REPLACE_KEY}/node_modules/@openzeppelin/contracts/",
15 | f"@openzeppelin/contracts-upgradeable/={BASE_PATH_REPLACE_KEY}/node_modules/@openzeppelin/contracts-upgradeable/"
16 | ],
17 | "optimizer": {
18 | "enabled": True,
19 | "runs": 200
20 | },
21 | "metadata": {
22 | "useLiteralContent": True
23 | },
24 | "outputSelection": {}
25 | }
26 | }
27 |
28 | DEFAULT_CONTRACT_OUTPUTS = ["abi", "devdoc", "userdoc"]
29 |
30 | SOLC_CONTRACTS_KEY = "contracts"
31 | SOLC_SOURCES_KEY = "sources"
32 | SOLC_SOURCES_URLS_KEY = "urls"
33 | SOLC_SETTINGS_KEY = "settings"
34 | SOLC_REMAPPINGS_KEY = "remappings"
35 | SOLC_OUTPUTS_KEY = "outputSelection"
36 |
37 | JSON_TEMPLATE_PATH = os.path.realpath("./scripts/solc-input.json")
38 | BUILT_JSON_PATH = os.path.realpath("./temp_input.json")
39 |
40 |
41 | def make_solc_input(args):
42 | base_path = args.base_path
43 | contract_path = args.contract_path
44 | contract_name = args.contract_name
45 |
46 | data = json.loads(json.dumps(SOLC_TEMPLATE))
47 |
48 | data[SOLC_SOURCES_KEY] = {
49 | f"{contract_path}": {
50 | SOLC_SOURCES_URLS_KEY: [
51 | os.path.realpath(base_path+"/"+contract_path)
52 | ]
53 | }
54 | }
55 |
56 | for i, remapping in enumerate(data[SOLC_SETTINGS_KEY][SOLC_REMAPPINGS_KEY]):
57 | data[SOLC_SETTINGS_KEY][SOLC_REMAPPINGS_KEY][i] = remapping.replace(BASE_PATH_REPLACE_KEY, base_path)
58 |
59 | data[SOLC_SETTINGS_KEY][SOLC_OUTPUTS_KEY] = {
60 | f"{contract_path}": {
61 | f"{contract_name}": DEFAULT_CONTRACT_OUTPUTS
62 | }
63 | }
64 |
65 | with open(BUILT_JSON_PATH, "w") as t:
66 | json.dump(data, t)
67 |
68 | print(BUILT_JSON_PATH)
69 |
70 |
71 | def parse_sol_output(args):
72 | with open(args.path, "r") as j:
73 | data = json.load(j)
74 |
75 | useful_data = data[SOLC_CONTRACTS_KEY][args.contract_path][args.contract_name]
76 |
77 | print(json.dumps(useful_data))
78 |
79 |
80 | parser = argparse.ArgumentParser(prog="py_helpers")
81 | subparsers = parser.add_subparsers()
82 |
83 | solc_parser = subparsers.add_parser("make_solc_input")
84 | solc_parser.add_argument("base_path", type=str)
85 | solc_parser.add_argument("contract_path", type=str)
86 | solc_parser.add_argument("contract_name", type=str)
87 | solc_parser.set_defaults(func=make_solc_input)
88 |
89 | sol_output_parser = subparsers.add_parser("sol_output")
90 | sol_output_parser.add_argument("path", type=str)
91 | sol_output_parser.add_argument("contract_path", type=str)
92 | sol_output_parser.add_argument("contract_name", type=str)
93 | sol_output_parser.set_defaults(func=parse_sol_output)
94 |
95 | if __name__ in '__main__':
96 | args = parser.parse_args()
97 |
98 | args.func(args)
--------------------------------------------------------------------------------
/scripts/rebase:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | source ./scripts/utils
4 |
5 | BRANCH=$([[ -n "$1" ]] && echo "$1" || echo "dev")
6 |
7 | rebase_branch "${BRANCH}"
8 |
--------------------------------------------------------------------------------
/scripts/sdk-tests:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | SRC_DIR="src"
4 | SRC_INCLUDES="${SRC_DIR}/*.ts"
5 | BRIDGE_INCLUDES="${SRC_DIR}/bridge/*.ts"
6 | COMMON_INCLUDES="${SRC_DIR}/common/*.ts"
7 | INTERNAL_INCLUDES="${SRC_DIR}/internal/*.ts"
8 |
9 | coverage_test () {
10 | c8 \
11 | --reports-dir $REPORTS_DIR \
12 | --include "${SRC_INCLUDES}" \
13 | --include "${BRIDGE_INCLUDES}" \
14 | --include "${COMMON_INCLUDES}" \
15 | --include "${INTERNAL_INCLUDES}" \
16 | ts-mocha -p test/tsconfig.json
17 | }
18 |
19 | coverage_report () {
20 | c8 report \
21 | --reports-dir $REPORTS_DIR \
22 | --include "${SRC_INCLUDES}" \
23 | --include "${BRIDGE_INCLUDES}" \
24 | --include "${COMMON_INCLUDES}" \
25 | --include "${INTERNAL_INCLUDES}" \
26 | --reporter=text-lcov > $REPORTS_DIR/lcov.info
27 | }
28 |
29 | main_test () {
30 | ts-mocha -p test/tsconfig.json
31 | }
32 |
33 | main_coverage () {
34 | command=$1
35 | case $command in
36 | *)
37 | shift
38 | coverage_${command} $@
39 | if [ $? = 127 ]; then
40 | echo "Error: coverage '$command' is not a known command." >&2
41 | echo " Allowed commands: test, report" >&2
42 | exit 1
43 | fi
44 | ;;
45 | esac
46 | }
47 |
48 | command=$1
49 | case $command in
50 | *)
51 | shift
52 | main_${command} $@
53 | if [ $? = 127 ]; then
54 | echo "Error: '$command' is not a known command." >&2
55 | echo " Allowed commands: test, coverage" >&2
56 | exit 1
57 | fi
58 | ;;
59 | esac
--------------------------------------------------------------------------------
/scripts/utils:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | source .env
4 |
5 | rebase_ () {
6 | git rebase --committer-date-is-author-date --ignore-whitespace "$@"
7 | }
8 |
9 | rebase_branch () {
10 | rebase_ "$1"
11 | }
12 |
13 | rebase_interactive () {
14 | rebase_ -i HEAD~$1
15 | }
16 |
17 | GITHUB_REQUEST_AUTH="${GITHUB_USERNAME}:${GITHUB_API_KEY}"
18 | GITHUB_REQUEST_HEADERS='Accept: application/vnd.github.v3.full+json'
19 |
20 | curl_github () {
21 | endpoint="$1"
22 |
23 | curl -s -H "${GITHUB_REQUEST_HEADERS}" -u "${GITHUB_REQUEST_AUTH}" "${endpoint}"
24 | }
25 |
26 | pwd_path () {
27 | echo "$(realpath "$PWD/$1")"
28 | }
--------------------------------------------------------------------------------
/src/bridge/bridgeconfig.ts:
--------------------------------------------------------------------------------
1 | import type {Token} from "@token";
2 | import {TokenSwap} from "@tokenswap";
3 |
4 | import {pow10} from "@common/utils";
5 |
6 | import type {BridgeConfigV3Contract} from "@contracts";
7 | import {BridgeConfigV3ContractInstance} from "@entities";
8 |
9 | import {
10 | BigNumber,
11 | type BigNumberish
12 | } from "@ethersproject/bignumber";
13 |
14 |
15 | export interface BridgeConfigSwapFeeParams {
16 | chainIdFrom: number;
17 | tokenFrom: Token;
18 | chainIdTo: number;
19 | amountFrom: BigNumberish;
20 | }
21 |
22 | export type CalculateSwapFeeResult = {
23 | amountFrom: BigNumber;
24 | bridgeFee: Promise;
25 | }
26 |
27 | export class BridgeConfig {
28 | private readonly instance: BridgeConfigV3Contract;
29 |
30 | constructor() {
31 | this.instance = BridgeConfigV3ContractInstance();
32 | }
33 |
34 | calculateSwapFee(args: BridgeConfigSwapFeeParams): CalculateSwapFeeResult {
35 | const {chainIdFrom, chainIdTo, tokenFrom, amountFrom: baseAmountFrom} = args;
36 |
37 | const {bridgeConfigIntermediateToken} = TokenSwap.intermediateTokens(chainIdTo, tokenFrom, chainIdFrom);
38 |
39 | const intermediateTokenAddress = bridgeConfigIntermediateToken.address(chainIdTo).toLowerCase();
40 |
41 | const
42 | multiplier: BigNumber = pow10(18 - tokenFrom.decimals(chainIdFrom)),
43 | amountFrom: BigNumber = BigNumber.from(baseAmountFrom).mul(multiplier);
44 |
45 | const bridgeFee = this.instance["calculateSwapFee(string,uint256,uint256)"](
46 | intermediateTokenAddress,
47 | chainIdTo,
48 | amountFrom
49 | );
50 |
51 | return {bridgeFee, amountFrom}
52 | }
53 | }
--------------------------------------------------------------------------------
/src/bridge/erc20.ts:
--------------------------------------------------------------------------------
1 | import {Signer} from "@ethersproject/abstract-signer";
2 | import type {TransactionResponse} from "@ethersproject/abstract-provider";
3 | import {
4 | BigNumber,
5 | type BigNumberish,
6 | } from "@ethersproject/bignumber";
7 | import type {
8 | PopulatedTransaction,
9 | ContractTransaction,
10 | } from "@ethersproject/contracts";
11 |
12 | import {
13 | ERC20Factory,
14 | ERC20Contract,
15 | } from "@contracts";
16 |
17 | import {rpcProviderForChain} from "@internal/rpcproviders";
18 |
19 | import {
20 | executePopulatedTransaction,
21 | rejectPromise
22 | } from "@common/utils";
23 |
24 | import type {SignerOrProvider} from "@common/types";
25 |
26 | import {GasUtils} from "./gasutils";
27 |
28 | export const MAX_APPROVAL_AMOUNT = BigNumber.from("0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff");
29 |
30 |
31 | export type ApproveArgs = {
32 | spender: string;
33 | amount?: BigNumberish;
34 | }
35 |
36 | export type TokenParams = {
37 | tokenAddress: string;
38 | chainId: number;
39 | }
40 |
41 | class ERC20 {
42 | readonly address: string;
43 | readonly chainId: number;
44 | private readonly instance: ERC20Contract;
45 |
46 | constructor(args: TokenParams) {
47 | this.address = args.tokenAddress;
48 | this.chainId = args.chainId;
49 |
50 | this.instance = ERC20Factory.connect(this.address, null);
51 | }
52 |
53 | private connectContract(provider?: SignerOrProvider): ERC20Contract {
54 | provider = provider ? provider : rpcProviderForChain(this.chainId);
55 |
56 | return this.instance.connect(provider)
57 | }
58 |
59 | async approve(args: ApproveArgs, signer: Signer,
60 | ): Promise {
61 | const
62 | contract = this.connectContract(signer),
63 | approveTxn = this._buildApproveTransaction(args, contract);
64 |
65 | return executePopulatedTransaction(approveTxn, signer)
66 | }
67 |
68 | async buildApproveTransaction(
69 | args: ApproveArgs,
70 | provider?: SignerOrProvider
71 | ): Promise {
72 | const contract = this.connectContract(provider);
73 | return this._buildApproveTransaction(args, contract)
74 | }
75 |
76 | private async _buildApproveTransaction(
77 | args: ApproveArgs,
78 | contract: ERC20Contract
79 | ): Promise {
80 | const {spender, amount=MAX_APPROVAL_AMOUNT} = args;
81 |
82 | return contract
83 | .populateTransaction
84 | .approve(spender, amount)
85 | .then(txn => {
86 | return GasUtils.populateGasParams(
87 | this.chainId,
88 | txn,
89 | "approve"
90 | )
91 | })
92 | .catch(rejectPromise)
93 | }
94 |
95 | async balanceOf(
96 | address: string
97 | ): Promise {
98 | return this.connectContract().balanceOf(address)
99 | }
100 |
101 | async allowanceOf(
102 | owner: string,
103 | spender: string
104 | ): Promise {
105 | return this.connectContract().allowance(owner, spender)
106 | }
107 | }
108 |
109 | export async function approve(
110 | approveArgs: ApproveArgs,
111 | tokenParams: TokenParams,
112 | signer: Signer
113 | ): Promise {
114 | return new ERC20(tokenParams).approve(approveArgs, signer)
115 | }
116 |
117 | export async function buildApproveTransaction(
118 | approveArgs: ApproveArgs,
119 | tokenParams: TokenParams
120 | ): Promise {
121 | return new ERC20(tokenParams).buildApproveTransaction(approveArgs)
122 | }
123 |
124 | export async function balanceOf(
125 | address: string,
126 | tokenParams: TokenParams
127 | ): Promise {
128 | return new ERC20(tokenParams).balanceOf(address)
129 | }
130 |
131 | export async function allowanceOf(
132 | owner: string,
133 | spender: string,
134 | tokenParams: TokenParams
135 | ): Promise {
136 | return new ERC20(tokenParams).allowanceOf(owner, spender)
137 | }
--------------------------------------------------------------------------------
/src/bridge/gasutils.ts:
--------------------------------------------------------------------------------
1 | import {ChainId, type ChainIdTypeMap} from "@chainid";
2 |
3 | import {GasOptions, populateGasOptions} from "@common/gasoptions";
4 |
5 | import {parseUnits} from "@ethersproject/units";
6 | import {BigNumber} from "@ethersproject/bignumber";
7 |
8 | import type {PopulatedTransaction} from "@ethersproject/contracts";
9 |
10 | export namespace GasUtils {
11 | type GasParams = {
12 | maxFeePerGas?: BigNumber;
13 | maxPriorityFee?: BigNumber;
14 | gasPrice?: BigNumber;
15 | bridgeGasLimit?: BigNumber;
16 | approveGasLimit?: BigNumber;
17 | }
18 |
19 | const makeGwei = (n: string): BigNumber => parseUnits(n, "gwei")
20 |
21 | const CHAIN_GAS_PARAMS: ChainIdTypeMap = {
22 | [ChainId.ETH]: {
23 | maxFeePerGas: makeGwei("80"),
24 | maxPriorityFee: makeGwei("1.5"),
25 | bridgeGasLimit: BigNumber.from(301000),
26 | approveGasLimit: BigNumber.from(75000),
27 | },
28 | [ChainId.OPTIMISM]: {
29 | bridgeGasLimit: BigNumber.from(250000),
30 | approveGasLimit: BigNumber.from(90000),
31 | },
32 | [ChainId.BSC]: {
33 | gasPrice: makeGwei("6"),
34 | bridgeGasLimit: BigNumber.from(260000),
35 | approveGasLimit: BigNumber.from(75000)
36 | },
37 | [ChainId.POLYGON]: {
38 | maxFeePerGas: makeGwei("32.01"),
39 | maxPriorityFee: makeGwei("32"),
40 | bridgeGasLimit: BigNumber.from(1000000),
41 | approveGasLimit: BigNumber.from(86000)
42 | },
43 | [ChainId.BOBA]: {
44 | gasPrice: makeGwei("10"),
45 | approveGasLimit: BigNumber.from(60000),
46 | },
47 | [ChainId.ARBITRUM]: {
48 | gasPrice: makeGwei("1.5"),
49 | bridgeGasLimit: BigNumber.from(1500000),
50 | },
51 | [ChainId.AVALANCHE]: {
52 | maxFeePerGas: makeGwei("2000"),
53 | maxPriorityFee: makeGwei("30"),
54 | bridgeGasLimit: BigNumber.from(700000),
55 | approveGasLimit: BigNumber.from(75000),
56 | },
57 | [ChainId.AURORA]: {
58 | gasPrice: makeGwei('1'),
59 | },
60 | [ChainId.HARMONY]: {
61 | gasPrice: makeGwei("115"),
62 | bridgeGasLimit: BigNumber.from(250000),
63 | approveGasLimit: BigNumber.from(75000)
64 | }
65 | }
66 |
67 | /* c8 ignore next 8 */
68 | export function approveLimit(chainId: number): BigNumber | null {
69 | if (chainId in CHAIN_GAS_PARAMS) {
70 | const gasParams = CHAIN_GAS_PARAMS[chainId];
71 | return gasParams.approveGasLimit ?? null
72 | }
73 |
74 | return null
75 | }
76 |
77 | /* c8 ignore next 8 */
78 | export function bridgeGasLimit(chainId: number): BigNumber | null {
79 | if (chainId in CHAIN_GAS_PARAMS) {
80 | const gasParams = CHAIN_GAS_PARAMS[chainId]
81 | return gasParams.bridgeGasLimit ?? null
82 | }
83 |
84 | return null
85 | }
86 |
87 | export const makeGasParams = (chainId: number): GasParams => CHAIN_GAS_PARAMS[chainId] ?? {};
88 |
89 | export const populateGasParams = (
90 | chainId: number,
91 | txn: PopulatedTransaction|Promise,
92 | gasLimitKind: "bridge" | "approve"
93 | ): Promise =>
94 | Promise.resolve(txn)
95 | .then((tx: PopulatedTransaction): PopulatedTransaction => {
96 | let {
97 | maxFeePerGas,
98 | maxPriorityFee,
99 | gasPrice,
100 | approveGasLimit,
101 | bridgeGasLimit
102 | } = makeGasParams(chainId);
103 |
104 | tx.chainId = chainId;
105 |
106 | let gasOpts: GasOptions = {};
107 |
108 | if (gasPrice) {
109 | gasOpts.gasPrice = gasPrice;
110 | } else if (maxFeePerGas) {
111 | gasOpts.maxFeePerGas = maxFeePerGas;
112 | if (maxPriorityFee) {
113 | gasOpts.maxPriorityFeePerGas = maxPriorityFee;
114 | }
115 | }
116 |
117 | switch (gasLimitKind) {
118 | case "bridge":
119 | if (bridgeGasLimit) gasOpts.gasLimit = bridgeGasLimit;
120 | break;
121 | case "approve":
122 | if (approveGasLimit) gasOpts.gasLimit = approveGasLimit;
123 | break;
124 | }
125 |
126 | return populateGasOptions(tx, gasOpts, chainId)
127 | })
128 | }
--------------------------------------------------------------------------------
/src/bridge/slippages.ts:
--------------------------------------------------------------------------------
1 | import {BigNumber} from "@ethersproject/bignumber";
2 | import {formatUnits} from "@ethersproject/units";
3 |
4 | export namespace Slippages {
5 | export type Slippage = string;
6 |
7 | export const
8 | One: Slippage = "ONE",
9 | OneTenth: Slippage = "ONE_TENTH",
10 | TwoTenth: Slippage = "TWO_TENTH",
11 | Quarter: Slippage = "QUARTER";
12 |
13 | export function _applySlippage(
14 | inputValue: BigNumber,
15 | slippageSelected: string|Slippage,
16 | add: boolean = false
17 | ): BigNumber {
18 | let numerator: number, denominator: number;
19 | switch (slippageSelected) {
20 | case Slippages.OneTenth:
21 | denominator = 1000;
22 | numerator = denominator + (add ? 1 : -1);
23 | break;
24 | case Slippages.TwoTenth:
25 | denominator = 500;
26 | numerator = denominator + (add ? 1 : -1);
27 | break;
28 | case Slippages.Quarter:
29 | denominator = 50;
30 | numerator = denominator + (add ? 1 : -1);
31 | break;
32 | default: // default to 1%
33 | denominator = 100;
34 | numerator = denominator + (add ? 1 : -1);
35 | break;
36 | }
37 |
38 | return inputValue.mul(numerator).div(denominator)
39 | }
40 |
41 | export function addSlippage(inputValue: BigNumber, slippageSelected: string|Slippage): BigNumber {
42 | return _applySlippage(inputValue, slippageSelected, true)
43 | }
44 |
45 | export function subtractSlippage(inputValue: BigNumber, slippageSelected: string|Slippage): BigNumber {
46 | return _applySlippage(inputValue, slippageSelected, false)
47 | }
48 |
49 | export function formatSlippageToString(slippageSelected: string|Slippage): string {
50 | switch (slippageSelected) {
51 | case Slippages.One:
52 | return formatUnits(BigNumber.from(100), 2)
53 | case Slippages.OneTenth:
54 | return formatUnits(BigNumber.from(100), 3)
55 | case Slippages.TwoTenth:
56 | return formatUnits(BigNumber.from(200), 3)
57 | case Slippages.Quarter:
58 | return formatUnits(BigNumber.from(2000), 3)
59 | default:
60 | return "N/A"
61 | }
62 | }
63 | }
--------------------------------------------------------------------------------
/src/common/chainid.ts:
--------------------------------------------------------------------------------
1 | import type {ValueOf} from "@internal/types";
2 |
3 | export const ChainId = {
4 | "ETH": 1,
5 | "OPTIMISM": 10,
6 | "CRONOS": 25,
7 | "BSC": 56,
8 | "POLYGON": 137,
9 | "FANTOM": 250,
10 | "BOBA": 288,
11 | "METIS": 1088,
12 | "MOONBEAM": 1284,
13 | "MOONRIVER": 1285,
14 | "ARBITRUM": 42161,
15 | "AVALANCHE": 43114,
16 | "DFK": 53935,
17 | "AURORA": 1313161554,
18 | "HARMONY": 1666600000,
19 | "KLAYTN": 8217,
20 | } as const;
21 |
22 | export type ChainId = ValueOf | number
23 |
24 | export type ChainIdTypeMap = {[k in ChainId]?: T}
25 |
26 | /* c8 ignore next 4 */
27 | export function supportedChainIds(): number[] {
28 | return Object.values(ChainId)
29 | .map(k => Number.isNaN(Number(k)) ? null : Number(k))
30 | .filter(c => c !== null);
31 | }
32 |
33 | export const EIP1559Chains: ChainIdTypeMap = {
34 | [ChainId.ETH]: true,
35 | [ChainId.OPTIMISM]: false,
36 | [ChainId.CRONOS]: false,
37 | [ChainId.BSC]: false,
38 | [ChainId.POLYGON]: true,
39 | [ChainId.FANTOM]: false,
40 | [ChainId.BOBA]: false,
41 | [ChainId.METIS]: false,
42 | [ChainId.MOONBEAM]: true,
43 | [ChainId.MOONRIVER]: true,
44 | [ChainId.ARBITRUM]: false,
45 | [ChainId.AVALANCHE]: true,
46 | [ChainId.DFK]: true,
47 | [ChainId.AURORA]: false,
48 | [ChainId.HARMONY]: false,
49 | [ChainId.KLAYTN]: false
50 | } as const;
51 |
52 | export function chainSupportsEIP1559(chainId: number): boolean { return chainId in EIP1559Chains ? EIP1559Chains[chainId] : false }
--------------------------------------------------------------------------------
/src/common/gasoptions.ts:
--------------------------------------------------------------------------------
1 | import {chainSupportsEIP1559} from "@chainid";
2 | import {BigNumber, BigNumberish} from "@ethersproject/bignumber";
3 | import type {PopulatedTransaction} from "@ethersproject/contracts";
4 |
5 | import {parseUnits} from "@ethersproject/units";
6 |
7 | /**
8 | * This interface allows for passing custom options for transaction gas fees.
9 | * Note: all values must be passed as GWei.
10 | *
11 | * @see {@link https://eips.ethereum.org/EIPS/eip-1559|EIP-1559} for more information regarding EIP-1559.
12 | *
13 | * @param {BigNumber} gasPrice For chains which don't support EIP-1559,
14 | * `gasPrice` controls the price in GWei per unit of gas used by a transaction.
15 | * @param {BigNumber} gasLimit Maximum number of gas units a transaction is allowed to use.
16 | * @param {BigNumber} maxFeePerGas For chains which do support EIP-1559,
17 | * `maxFeePerGas` controls the maximum fee in GWei which a sender is willing to pay per unit of gas used by a transaction.
18 | * @param {BigNumber} maxPriorityFeePerGas For chains which do support EIP-1559,
19 | * `maxPriorityFeePerGas` controls the fee in GWei which a sender is willing to pay block miners or validators to have their
20 | * transaction included in an upcoming block. Defaults to 1.5 GWei.
21 | */
22 | export interface GasOptions {
23 | gasPrice?: BigNumber;
24 | gasLimit?: BigNumber;
25 | maxFeePerGas?: BigNumber;
26 | maxPriorityFeePerGas?: BigNumber;
27 | }
28 |
29 | /**
30 | * This interface partially implements the Overrides interface which can be passed to
31 | * Ethers.js Transaction functions.
32 | *
33 | * @param {BigNumberish | Promise} gasPrice For chains which don't support EIP-1559,
34 | * `gasPrice` controls the price in GWei per unit of gas used by a transaction.
35 | * @param {BigNumberish | Promise} gasLimit Maximum number of gas units a transaction is allowed to use.
36 | * @param {BigNumberish | Promise} maxFeePerGas For chains which do support EIP-1559,
37 | * `maxFeePerGas` controls the maximum fee in GWei which a sender is willing to pay per unit of gas used by a transaction.
38 | * @param {BigNumberish | Promise} maxPriorityFeePerGas For chains which do support EIP-1559,
39 | * `maxPriorityFeePerGas` controls the fee in GWei which a sender is willing to pay block miners or validators to have their
40 | * transaction included in an upcoming block. Defaults to 1.5 GWei.
41 | */
42 | export interface TransactionGasOverrides {
43 | gasLimit?: BigNumberish | Promise;
44 | gasPrice?: BigNumberish | Promise;
45 | maxFeePerGas?: BigNumberish | Promise;
46 | maxPriorityFeePerGas?: BigNumberish | Promise;
47 | }
48 |
49 | /* c8 ignore next */
50 | export function makeGwei(n: string): BigNumber { return parseUnits(n, "gwei") }
51 |
52 | /**
53 | * populateGasOptions adds gas limit, fee, and/or price options as provided in the `gasOptions` parameter to a built,
54 | * but as of yet unsent {@link PopulatedTransaction} object.
55 | *
56 | * @param {PopulatedTransaction} txn Ethers.js {@link PopulatedTransaction} object to add gas params to.
57 | * @param {GasOptions} gasOptions Gas limit/fee/price options for {@link PopulatedTransaction} objects.
58 | * @param {number} chainId Chain ID of the network which `txn` will be sent on, used for determining EIP-1559 support.
59 | * @param {boolean} ignoreLimit Optional. If set to `true`, the `gasLimit` field of `gasOptions` is entirely ignored.
60 | *
61 | * @return {PopulatedTransaction} Transaction object as passed as a param, but with any of `gasPrice`, `gasLimit`,
62 | * `maxFeePerGas`, and `maxPriorityFeePerGas` respectively set to values provided in `gasOptions`.
63 | */
64 | export function populateGasOptions(
65 | txn: PopulatedTransaction,
66 | gasOptions: GasOptions,
67 | chainId: number,
68 | ignoreLimit: boolean = false
69 | ): PopulatedTransaction {
70 | if (!chainSupportsEIP1559(chainId)) {
71 | if (gasOptions?.gasPrice) {
72 | txn.gasPrice = gasOptions.gasPrice;
73 | }
74 | } else {
75 | if (gasOptions?.maxFeePerGas) {
76 | txn.maxFeePerGas = gasOptions.maxFeePerGas;
77 | }
78 |
79 | if (gasOptions?.maxPriorityFeePerGas) {
80 | txn.maxPriorityFeePerGas = gasOptions.maxPriorityFeePerGas;
81 | }
82 | }
83 |
84 | if (!ignoreLimit) {
85 | if (gasOptions?.gasLimit) {
86 | txn.gasLimit = gasOptions.gasLimit;
87 | }
88 | }
89 |
90 | return txn
91 | }
92 |
93 | /* c8 ignore start */
94 | export function makeTransactionGasOverrides(
95 | gasOptions: GasOptions,
96 | chainId: number,
97 | ignoreLimit: boolean = false
98 | ): TransactionGasOverrides {
99 | let overrides: TransactionGasOverrides = {};
100 |
101 | if (!chainSupportsEIP1559(chainId)) {
102 | if (gasOptions?.gasPrice) {
103 | overrides.gasPrice = gasOptions.gasPrice;
104 | }
105 | } else {
106 | if (gasOptions?.maxFeePerGas) {
107 | overrides.maxFeePerGas = gasOptions.maxFeePerGas;
108 | }
109 |
110 | if (gasOptions?.maxPriorityFeePerGas) {
111 | overrides.maxPriorityFeePerGas = gasOptions.maxPriorityFeePerGas;
112 | }
113 | }
114 |
115 | if (!ignoreLimit) {
116 | if (gasOptions?.gasLimit) {
117 | overrides.gasLimit = gasOptions.gasLimit;
118 | }
119 | }
120 |
121 | return overrides
122 | }
123 | /* c8 ignore stop */
--------------------------------------------------------------------------------
/src/common/synapse_contracts.ts:
--------------------------------------------------------------------------------
1 | import { isNil } from "@internal/utils";
2 | import {ChainId, type ChainIdTypeMap} from "@chainid";
3 |
4 | export namespace SynapseContracts {
5 |
6 | interface SynapseContractArgs {
7 | bridge: string;
8 | bridgeZap?: string;
9 | }
10 |
11 | export class SynapseContract {
12 | readonly bridgeAddress: string;
13 | readonly bridgeZapAddress?: string;
14 |
15 | constructor({bridge, bridgeZap}: SynapseContractArgs) {
16 |
17 | this.bridgeAddress = bridge;
18 |
19 | if (!isNil(bridgeZap)) {
20 | this.bridgeZapAddress = bridgeZap;
21 | }
22 | }
23 | }
24 |
25 | export const Ethereum = new SynapseContract({
26 | bridge: "0x2796317b0fF8538F253012862c06787Adfb8cEb6",
27 | bridgeZap: "0x6571d6be3d8460CF5F7d6711Cd9961860029D85F",
28 | });
29 |
30 | export const Optimism = new SynapseContract({
31 | bridge: "0xAf41a65F786339e7911F4acDAD6BD49426F2Dc6b",
32 | bridgeZap: "0x470f9522ff620eE45DF86C58E54E6A645fE3b4A7",
33 | });
34 |
35 | export const Cronos = new SynapseContract({
36 | bridge: "0xE27BFf97CE92C3e1Ff7AA9f86781FDd6D48F5eE9",
37 | bridgeZap: "0x991adb00eF4c4a6D1eA6036811138Db4379377C2",
38 | });
39 |
40 | export const BSC = new SynapseContract({
41 | bridge: "0xd123f70AE324d34A9E76b67a27bf77593bA8749f",
42 | bridgeZap: "0x749F37Df06A99D6A8E065dd065f8cF947ca23697",
43 | });
44 |
45 | export const Polygon = new SynapseContract({
46 | bridge: "0x8F5BBB2BB8c2Ee94639E55d5F41de9b4839C1280",
47 | bridgeZap: "0xb883A9f35650ff82fdBC9Ed867e98FEd0457b584",
48 | });
49 |
50 | export const Fantom = new SynapseContract({
51 | bridge: "0xAf41a65F786339e7911F4acDAD6BD49426F2Dc6b",
52 | bridgeZap: "0x1c6aE197fF4BF7BA96c66C5FD64Cb22450aF9cC8",
53 | });
54 |
55 | export const Boba = new SynapseContract({
56 | bridge: "0x432036208d2717394d2614d6697c46DF3Ed69540",
57 | bridgeZap: "0x64B4097bCCD27D49BC2A081984C39C3EeC427a2d",
58 | });
59 |
60 | export const Metis = new SynapseContract({
61 | bridge: "0x06Fea8513FF03a0d3f61324da709D4cf06F42A5c",
62 | bridgeZap: "0x6571D58b3BF2469DF5878e213453E28dC1A4DA81",
63 | });
64 |
65 | export const Moonbeam = new SynapseContract(({
66 | bridge: "0x84A420459cd31C3c34583F67E0f0fB191067D32f",
67 | bridgeZap: "0xadA10A7474f4c71A829b55D2cB4232C281383fd5",
68 | }));
69 |
70 | export const Moonriver = new SynapseContract(({
71 | bridge: "0xaeD5b25BE1c3163c907a471082640450F928DDFE",
72 | bridgeZap: "0xfA28DdB74b08B2b6430f5F61A1Dd5104268CC29e",
73 | }));
74 |
75 | export const Arbitrum = new SynapseContract({
76 | bridge: "0x6F4e8eBa4D337f874Ab57478AcC2Cb5BACdc19c9",
77 | bridgeZap: "0x37f9aE2e0Ea6742b9CAD5AbCfB6bBC3475b3862B",
78 | });
79 |
80 | export const Avalanche = new SynapseContract({
81 | bridge: "0xC05e61d0E7a63D27546389B7aD62FdFf5A91aACE",
82 | bridgeZap: "0x0EF812f4c68DC84c22A4821EF30ba2ffAB9C2f3A",
83 | });
84 |
85 | export const DFK = new SynapseContract({
86 | bridge: "0xE05c976d3f045D0E6E7A6f61083d98A15603cF6A",
87 | bridgeZap: "0x75224b0f245Fe51d5bf47A898DbB6720D4150BA7",
88 | });
89 |
90 | export const Aurora = new SynapseContract({
91 | bridge: "0xaeD5b25BE1c3163c907a471082640450F928DDFE",
92 | bridgeZap: "0x2D8Ee8d6951cB4Eecfe4a79eb9C2F973C02596Ed",
93 | });
94 |
95 | export const Harmony = new SynapseContract({
96 | bridge: "0xAf41a65F786339e7911F4acDAD6BD49426F2Dc6b",
97 | bridgeZap: "0xB003e75f7E0B5365e814302192E99b4EE08c0DEd",
98 | });
99 |
100 | export const Klaytn = new SynapseContract({
101 | bridge: "0xAf41a65F786339e7911F4acDAD6BD49426F2Dc6b",
102 | bridgeZap: "0x8671A0465844a15eb7230C5dd8d6032c26c655B7",
103 | });
104 |
105 | const chainIdContractsMap: ChainIdTypeMap = {
106 | [ChainId.ETH]: Ethereum,
107 | [ChainId.OPTIMISM]: Optimism,
108 | [ChainId.CRONOS]: Cronos,
109 | [ChainId.BSC]: BSC,
110 | [ChainId.POLYGON]: Polygon,
111 | [ChainId.FANTOM]: Fantom,
112 | [ChainId.BOBA]: Boba,
113 | [ChainId.METIS]: Metis,
114 | [ChainId.MOONBEAM]: Moonbeam,
115 | [ChainId.MOONRIVER]: Moonriver,
116 | [ChainId.ARBITRUM]: Arbitrum,
117 | [ChainId.AVALANCHE]: Avalanche,
118 | [ChainId.DFK]: DFK,
119 | [ChainId.AURORA]: Aurora,
120 | [ChainId.HARMONY]: Harmony,
121 | [ChainId.KLAYTN]: Klaytn,
122 | }
123 |
124 | export function contractsForChainId(chainId: number): SynapseContract { return chainIdContractsMap[chainId] }
125 | }
--------------------------------------------------------------------------------
/src/common/types.ts:
--------------------------------------------------------------------------------
1 | import type {ChainIdTypeMap} from "@chainid";
2 | import type {Signer} from "@ethersproject/abstract-signer";
3 | import type {Provider} from "@ethersproject/providers";
4 |
5 | export type SignerOrProvider = Signer | Provider;
6 |
7 | export type StringMap = ChainIdTypeMap
8 | export type DecimalsMap = ChainIdTypeMap
9 |
10 | export type AddressMap = StringMap
11 |
12 | export type Resolveable = T | Promise
13 |
14 | export enum StaticCallResult {
15 | Success,
16 | Failure
17 | }
--------------------------------------------------------------------------------
/src/common/utils.ts:
--------------------------------------------------------------------------------
1 | import {ChainId} from "@chainid";
2 | import {SynapseContracts} from "@synapsecontracts";
3 | import {
4 | StaticCallResult,
5 | type Resolveable
6 | } from "./types";
7 |
8 | import {BigNumber} from "@ethersproject/bignumber";
9 |
10 | import type {Signer} from "@ethersproject/abstract-signer";
11 | import type {
12 | PopulatedTransaction,
13 | ContractTransaction,
14 | } from "@ethersproject/contracts";
15 |
16 | export function makeError(e: any): Error { return e instanceof Error ? e : new Error(e) }
17 |
18 | export function rejectPromise(e: any): Promise { return Promise.reject(makeError(e)) }
19 |
20 | export function executePopulatedTransaction(
21 | populatedTxn: Resolveable,
22 | signer: Signer,
23 | ): Promise {
24 | return Promise.resolve(populatedTxn)
25 | .then(txn => signer.sendTransaction(txn))
26 | .catch(rejectPromise)
27 | }
28 |
29 | export function staticCallPopulatedTransaction(
30 | populatedTxn: Resolveable,
31 | signer: Signer
32 | ): Promise {
33 | return Promise.resolve(populatedTxn)
34 | .then(txn => {
35 | return signer.call(txn)
36 | .then(() => StaticCallResult.Success)
37 | .catch((err) => StaticCallResult.Failure)
38 | })
39 | }
40 |
41 | export function pow10(exp: number): BigNumber { return BigNumber.from(10).pow(exp) }
42 |
43 | /**
44 | * "Fixes" a value into units of Wei; should be used when tokens
45 | * have a decimals value which isn't 18
46 | * (such as USDC/USDT on chains which aren't BSC) and you need to do
47 | * calculations using proper units of Wei instead of, for example in the case of
48 | * USDC/USDT, Szabo (10^-6)
49 | * @param amt
50 | * @param decimals
51 | */
52 | export function fixWeiValue(amt: BigNumber, decimals: number): BigNumber {
53 | const multiplier = pow10(18).div(pow10(decimals));
54 | return amt.mul(multiplier)
55 | }
56 |
57 | export function contractAddressFor(chainId: number, key: "bridgeAddress" | "bridgeZapAddress"): string {
58 | let address: string;
59 |
60 | const contractsForChain = contractsForChainId(chainId);
61 |
62 | switch (key) {
63 | case "bridgeAddress":
64 | address = contractsForChain.bridgeAddress;
65 | break;
66 | case "bridgeZapAddress":
67 | address = contractsForChain.bridgeZapAddress;
68 | break;
69 | }
70 |
71 | return address
72 | }
73 |
74 |
75 | const CHAINID_CONTRACTS_MAP: {[c: number]: SynapseContracts.SynapseContract} = {
76 | [ChainId.ETH]: SynapseContracts.Ethereum,
77 | [ChainId.OPTIMISM]: SynapseContracts.Optimism,
78 | [ChainId.CRONOS]: SynapseContracts.Cronos,
79 | [ChainId.BSC]: SynapseContracts.BSC,
80 | [ChainId.POLYGON]: SynapseContracts.Polygon,
81 | [ChainId.FANTOM]: SynapseContracts.Fantom,
82 | [ChainId.BOBA]: SynapseContracts.Boba,
83 | [ChainId.METIS]: SynapseContracts.Metis,
84 | [ChainId.MOONBEAM]: SynapseContracts.Moonbeam,
85 | [ChainId.MOONRIVER]: SynapseContracts.Moonriver,
86 | [ChainId.ARBITRUM]: SynapseContracts.Arbitrum,
87 | [ChainId.AVALANCHE]: SynapseContracts.Avalanche,
88 | [ChainId.DFK]: SynapseContracts.DFK,
89 | [ChainId.AURORA]: SynapseContracts.Aurora,
90 | [ChainId.HARMONY]: SynapseContracts.Harmony,
91 | [ChainId.KLAYTN]: SynapseContracts.Klaytn,
92 | }
93 |
94 | export const contractsForChainId = (chainId: number): SynapseContracts.SynapseContract => CHAINID_CONTRACTS_MAP[chainId] ?? null
--------------------------------------------------------------------------------
/src/contracts.ts:
--------------------------------------------------------------------------------
1 | import type {
2 | L1BridgeZap as L1BridgeZapContract,
3 | L2BridgeZap as L2BridgeZapContract
4 | } from "@internal/gen/index";
5 |
6 | type GenericZapBridgeContract = L1BridgeZapContract | L2BridgeZapContract;
7 |
8 | export type {
9 | L1BridgeZapContract,
10 | L2BridgeZapContract,
11 | GenericZapBridgeContract
12 | }
13 |
14 | export type {
15 | SynapseBridge as SynapseBridgeContract,
16 | SynapseERC20 as SynapseERC20Contract,
17 | SwapFlashLoan as SwapContract,
18 | BridgeConfigV3 as BridgeConfigV3Contract,
19 | ERC20 as ERC20Contract,
20 | AvaxJewelMigration as AvaxJewelMigrationContract
21 | } from "@internal/gen/index";
22 |
23 | export {
24 | SynapseBridge__factory as SynapseBridgeFactory,
25 | L1BridgeZap__factory as L1BridgeZapFactory,
26 | L2BridgeZap__factory as L2BridgeZapFactory,
27 | SynapseERC20__factory as SynapseERC20Factory,
28 | SwapFlashLoan__factory as SwapFactory,
29 | BridgeConfigV3__factory as BridgeConfigV3Factory,
30 | ERC20__factory as ERC20Factory,
31 | AvaxJewelMigration__factory as AvaxJewelMigrationFactory
32 | } from "@internal/gen/index";
--------------------------------------------------------------------------------
/src/entities.ts:
--------------------------------------------------------------------------------
1 | import {
2 | SynapseBridgeFactory,
3 | L1BridgeZapFactory,
4 | L2BridgeZapFactory,
5 | BridgeConfigV3Factory,
6 | AvaxJewelMigrationFactory,
7 | type SynapseBridgeContract,
8 | type L1BridgeZapContract,
9 | type L2BridgeZapContract,
10 | type GenericZapBridgeContract,
11 | type BridgeConfigV3Contract,
12 | type AvaxJewelMigrationContract
13 | } from "./contracts";
14 |
15 | import {ChainId} from "@chainid";
16 | import {contractAddressFor} from "@common/utils";
17 | import type {SignerOrProvider} from "@common/types";
18 |
19 | import {rpcProviderForChain} from "@internal/rpcproviders";
20 |
21 |
22 | const bridgeConfigV3Address: string = "0x5217c83ca75559B1f8a8803824E5b7ac233A12a1";
23 |
24 | export const AvaxJewelMigrationAddress: string = "0x82d4aCF0DA013Ee3649C7eAdF5Db9093A7EFa7B0";
25 |
26 | enum ContractKind {bridge="bridgeAddress", bridgeZap="bridgeZapAddress"}
27 |
28 | interface NewInstanceParams {
29 | chainId: number;
30 | signerOrProvider?: SignerOrProvider
31 | }
32 |
33 | enum EntityKind {
34 | SynapseBridge = "SynapseBridge",
35 | L1BridgeZap = "L1BridgeZap",
36 | L2BridgeZap = "L2BridgeZap",
37 | BridgeConfig = "BridgeConfig",
38 | AvaxJewelMigration = "AvaxJewelMigration",
39 | }
40 |
41 | namespace Connector {
42 | function makeConnectorName(chainId: number, entityKind: EntityKind): string {
43 | return `${entityKind.toString()}.${chainId}`
44 | }
45 |
46 | type EntityContract =
47 | SynapseBridgeContract |
48 | L1BridgeZapContract | L2BridgeZapContract |
49 | BridgeConfigV3Contract | AvaxJewelMigrationContract
50 |
51 | type ConnectorEntity = {
52 | contract: EntityContract;
53 | entityKind: EntityKind;
54 | chainId: number;
55 | }
56 |
57 | export class EntityConnector {
58 | private readonly entityMap: {[k: string]: ConnectorEntity};
59 |
60 | constructor() {
61 | this.entityMap = {};
62 | }
63 |
64 | private addEntity(connectorName: string, contract: EntityContract, entityKind: EntityKind, chainId: number) {
65 | this.entityMap[connectorName] = {contract, entityKind, chainId}
66 | }
67 |
68 | private checkEntity(connectorName: string): EntityContract | null {
69 | if (connectorName in this.entityMap) {
70 | return this.entityMap[connectorName].contract
71 | }
72 | }
73 |
74 | synapseBridge(params: NewInstanceParams): SynapseBridgeContract {
75 | const
76 | {chainId, signerOrProvider} = params,
77 | entityKind = EntityKind.SynapseBridge,
78 | connectorName = makeConnectorName(chainId, entityKind);
79 |
80 | const check = this.checkEntity(connectorName);
81 | if (check) {
82 | return check as SynapseBridgeContract
83 | }
84 |
85 | const newEntity = SynapseBridgeFactory.connect(
86 | contractAddressFor(chainId, ContractKind.bridge),
87 | signerOrProvider
88 | );
89 |
90 | this.addEntity(connectorName, newEntity, entityKind, chainId);
91 |
92 | return newEntity
93 | }
94 |
95 | l1BridgeZap(params: NewInstanceParams): L1BridgeZapContract {
96 | const
97 | {chainId, signerOrProvider} = params,
98 | entityKind = EntityKind.L1BridgeZap,
99 | connectorName = makeConnectorName(chainId, entityKind);
100 |
101 | const check = this.checkEntity(connectorName);
102 | if (check) {
103 | return check as L1BridgeZapContract
104 | }
105 |
106 | const newEntity = L1BridgeZapFactory.connect(
107 | contractAddressFor(chainId, ContractKind.bridgeZap),
108 | signerOrProvider
109 | );
110 |
111 | this.addEntity(connectorName, newEntity, entityKind, chainId);
112 |
113 | return newEntity
114 | }
115 |
116 | l2BridgeZap(params: NewInstanceParams): L2BridgeZapContract {
117 | const
118 | {chainId, signerOrProvider} = params,
119 | entityKind = EntityKind.L2BridgeZap,
120 | connectorName = makeConnectorName(chainId, entityKind);
121 |
122 | const check = this.checkEntity(connectorName);
123 | if (check) {
124 | return check as L2BridgeZapContract
125 | }
126 |
127 | const newEntity = L2BridgeZapFactory.connect(
128 | contractAddressFor(chainId, ContractKind.bridgeZap),
129 | signerOrProvider
130 | );
131 |
132 | this.addEntity(connectorName, newEntity, entityKind, chainId);
133 |
134 | return newEntity
135 | }
136 |
137 | bridgeConfig(): BridgeConfigV3Contract {
138 | const
139 | chainId = ChainId.ETH,
140 | entityKind = EntityKind.BridgeConfig,
141 | connectorName = makeConnectorName(chainId, entityKind);
142 |
143 | const check = this.checkEntity(connectorName);
144 | if (check) {
145 | return check as BridgeConfigV3Contract
146 | }
147 |
148 | const newEntity = BridgeConfigV3Factory.connect(
149 | bridgeConfigV3Address,
150 | rpcProviderForChain(chainId)
151 | );
152 |
153 | this.addEntity(connectorName, newEntity, entityKind, chainId);
154 |
155 | return newEntity
156 | }
157 |
158 | avaxJewelMigration(): AvaxJewelMigrationContract {
159 | const
160 | chainId = ChainId.AVALANCHE,
161 | entityKind = EntityKind.AvaxJewelMigration,
162 | connectorName = makeConnectorName(chainId, entityKind);
163 |
164 | const check = this.checkEntity(connectorName);
165 | if (check) {
166 | return check as AvaxJewelMigrationContract
167 | }
168 |
169 | const newEntity = AvaxJewelMigrationFactory.connect(
170 | AvaxJewelMigrationAddress,
171 | rpcProviderForChain(ChainId.AVALANCHE)
172 | );
173 |
174 | this.addEntity(connectorName, newEntity, entityKind, chainId);
175 |
176 | return newEntity
177 | }
178 | }
179 | }
180 |
181 | const ENTITY_CONNECTOR = new Connector.EntityConnector();
182 |
183 | export function SynapseBridgeContractInstance(params: NewInstanceParams): SynapseBridgeContract {
184 | return ENTITY_CONNECTOR.synapseBridge(params)
185 | }
186 |
187 | export function L1BridgeZapContractInstance(params: NewInstanceParams): L1BridgeZapContract {
188 | return ENTITY_CONNECTOR.l1BridgeZap(params)
189 | }
190 |
191 | export function L2BridgeZapContractInstance(params: NewInstanceParams): L2BridgeZapContract {
192 | return ENTITY_CONNECTOR.l2BridgeZap(params)
193 | }
194 |
195 | export function GenericZapBridgeContractInstance(params: NewInstanceParams): GenericZapBridgeContract {
196 | return params.chainId === ChainId.ETH || params.chainId === ChainId.DFK
197 | ? L1BridgeZapContractInstance(params)
198 | : L2BridgeZapContractInstance(params)
199 | }
200 |
201 | export function BridgeConfigV3ContractInstance(): BridgeConfigV3Contract {
202 | return ENTITY_CONNECTOR.bridgeConfig()
203 | }
204 |
205 | export function AvaxJewelMigrationContractInstance(): AvaxJewelMigrationContract {
206 | return ENTITY_CONNECTOR.avaxJewelMigration()
207 | }
--------------------------------------------------------------------------------
/src/explorer/bridge_transactions/query.ts:
--------------------------------------------------------------------------------
1 | import {
2 | EXPLORER_GQL_API,
3 | DEFAULT_QUERY_RESPONSE,
4 | } from "../gqlutils.js";
5 | import fetch from "node-fetch";
6 |
7 | /**
8 | * @notice Builds string with txn parameters for a GraphQL query
9 | * @param chainId - Chain ID. Optional.
10 | * @param address - Address. Optional.
11 | * @param txnHash - Transaction hash. Optional.
12 | * @param kappa - Kappa. Optional.
13 | * @returns String with txn parameters for a GraphQL query
14 | */
15 | function buildTxnParams(
16 | chainId?: number,
17 | address?: string,
18 | txnHash?: string,
19 | kappa?: string
20 | ): string {
21 | if (!chainId && !address && !txnHash && !kappa) {
22 | throw new Error(
23 | "Must provide at least one of chainId, address, txnHash, kappa"
24 | );
25 | }
26 |
27 | let query_params = "";
28 |
29 | if (chainId) {
30 | query_params += `chainId: ${chainId}`;
31 | }
32 | if (address) {
33 | if (query_params.length > 0) {
34 | query_params += ", ";
35 | }
36 | query_params += `address: "${address}"`;
37 | }
38 | if (txnHash) {
39 | if (query_params.length > 0) {
40 | query_params += ", ";
41 | }
42 | query_params += `txnHash: "${txnHash}"`;
43 | }
44 | if (kappa) {
45 | if (query_params.length > 0) {
46 | query_params += ", ";
47 | }
48 | query_params += `kappa: "${kappa}"`;
49 | }
50 |
51 | return query_params;
52 | }
53 |
54 | /**
55 | * @notice Builds a GraphQL query string for the given query type
56 | * @param chainId The chainId to query for. Optional.
57 | * @param address The address to query for. Optional.
58 | * @param txnHash The transaction hash to query for. Optional.
59 | * @param kappa The kappa to query for. Optional.
60 | * @returns A GraphQL query string
61 | */
62 | function buildTxnQuery(
63 | chainId?: number,
64 | address?: string,
65 | txnHash?: string,
66 | kappa?: string
67 | ): string {
68 | // Get the parameters of the query
69 | const query_params = buildTxnParams(chainId, address, txnHash, kappa);
70 |
71 | // Construct the query
72 | const query = `
73 | query{
74 | bridgeTransactions(${query_params}) {
75 | ${DEFAULT_QUERY_RESPONSE}
76 | }
77 | }
78 | `;
79 |
80 | return query;
81 | }
82 |
83 |
84 | // - [EXTERNAL] - //
85 |
86 | /**
87 | * @notice Gets transaction info with a GraphQL query
88 | * @param chainId The chainId to query for. Optional.
89 | * @param address The address to query for. Optional.
90 | * @param txnHash The transaction hash to query for. Optional.
91 | * @param kappa The kappa to query for. Optional.
92 | * @returns A promise that resolves to the transaction info as a JSON object
93 | */
94 | export async function getBridgeTxnInfo(args: {
95 | chainId?: number,
96 | address?: string,
97 | txnHash?: string,
98 | kappa?: string,
99 | }): Promise {
100 | const query = buildTxnQuery(args.chainId, args.address, args.txnHash, args.kappa);
101 | try {
102 | const response = await fetch(EXPLORER_GQL_API, {
103 | method: "post",
104 | body: JSON.stringify({ query }),
105 | headers: { "Content-Type": "application/json" },
106 | });
107 | return (await response.json()) as JSON;
108 | } catch (error) {
109 | throw new Error(`Error fetching bridge transactions: ${error}`);
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/src/explorer/gqlutils.ts:
--------------------------------------------------------------------------------
1 | export const EXPLORER_GQL_API =
2 | "https://syn-explorer-api.metagabbar.xyz/graphql";
3 |
4 | export const DEFAULT_QUERY_RESPONSE = `
5 | kappa
6 | toInfo {
7 | txnHash
8 | chainId
9 | value
10 | USDValue
11 | formattedValue
12 | time
13 | }
14 | fromInfo {
15 | txnHash
16 | chainId
17 | value
18 | USDValue
19 | formattedValue
20 | time
21 | }
22 | `;
23 |
--------------------------------------------------------------------------------
/src/explorer/index.ts:
--------------------------------------------------------------------------------
1 | export { getBridgeTxnInfo } from "./bridge_transactions/query";
2 |
--------------------------------------------------------------------------------
/src/gasprice.ts:
--------------------------------------------------------------------------------
1 | import {
2 | first,
3 | last,
4 | sortBy
5 | } from "lodash-es";
6 |
7 | import {rpcProviderForChain} from "@internal/rpcproviders";
8 |
9 | import {BigNumber} from "@ethersproject/bignumber";
10 | import {formatUnits} from "@ethersproject/units";
11 | import {chainSupportsEIP1559} from "@chainid";
12 | import {TransactionResponse} from "@ethersproject/providers";
13 |
14 | export type ChainGasPrices = {
15 | min: BigNumber;
16 | avg: BigNumber;
17 | max: BigNumber;
18 | last: BigNumber;
19 | }
20 |
21 | /**
22 | * Returns the minimum, average, and maximum gas prices from the most recently mined block
23 | * on a given Chain, as well as the result of eth_getGasPrice.
24 | * @param chainId
25 | */
26 | export function fetchChainGasPrices(chainId: number): Promise {
27 | const
28 | supports1559 = chainSupportsEIP1559(chainId),
29 | provider = rpcProviderForChain(chainId),
30 | lastPriceProm = provider.getGasPrice();
31 |
32 | return provider.getBlockWithTransactions('latest')
33 | .then(({transactions}) => lastPriceProm.then(lastPrice => {
34 | const gasPrices: BigNumber[] = extractGasPrices(transactions, supports1559);
35 |
36 | let sum: BigNumber = BigNumber.from(0);
37 | gasPrices.forEach(n => sum = sum.add(n));
38 |
39 | return {
40 | min: first(gasPrices),
41 | avg: sum.div(gasPrices.length),
42 | max: last(gasPrices),
43 | last: lastPrice,
44 | }
45 | }))
46 | }
47 |
48 | function extractGasPrices(txns: TransactionResponse[], supports1559: boolean): BigNumber[] {
49 | let gasPrices: BigNumber[] = txns
50 | .map(txn => supports1559 ? txn.maxFeePerGas : txn.gasPrice)
51 | .filter(val => typeof val !== 'undefined' && val !== null && !val.isZero());
52 |
53 | return sortBy(gasPrices, [gweiSortFn]);
54 | }
55 |
56 | function gweiSortFn(n: BigNumber): number {
57 | return gweiToFloat(toGwei(n))
58 | }
59 |
60 | function toGwei(n: BigNumber): string {
61 | return formatUnits(n, "gwei")
62 | }
63 |
64 | function gweiToFloat(gwei: string): number {
65 | return parseFloat(gwei)
66 | }
--------------------------------------------------------------------------------
/src/hooks/bridge.ts:
--------------------------------------------------------------------------------
1 | import type {Token} from "@token";
2 | import {Bridge} from "@bridge/bridge";
3 |
4 | import {useSignerFromEthereum} from "./signer";
5 | import {
6 | logError,
7 | parseBigNumberish
8 | } from "./helpers";
9 | import {
10 | useApproveStatus,
11 | useCheckAllowance
12 | } from "./tokens";
13 |
14 | import type {
15 | ActionHook,
16 | ApproveActionHook,
17 | ContractTransactionHook,
18 | AllowanceHook,
19 | NeedsApprovalHook,
20 | ApproveTokenState,
21 | UseApproveHook
22 | } from "./types";
23 |
24 | import type {BigNumberish} from "@ethersproject/bignumber";
25 | import type {ContractTransaction} from "@ethersproject/contracts";
26 |
27 | import {useEffect, useState} from "react";
28 |
29 |
30 | function useCalculateBridgeSwapOutput(args: {
31 | ethereum: any,
32 | chainId: number,
33 | tokenFrom: Token,
34 | tokenTo: Token,
35 | amountFrom: BigNumberish,
36 | chainIdTo: number
37 | }): ActionHook {
38 | const {ethereum, chainId, ...rest} = args;
39 |
40 | const [result, setResult] = useState(null);
41 |
42 | function fn() {
43 | const fnArgs = {
44 | ...rest,
45 | amountFrom: parseBigNumberish(rest.amountFrom, rest.tokenFrom, chainId),
46 | };
47 |
48 | const synapseBridge = new Bridge.SynapseBridge({network: chainId});
49 | synapseBridge.estimateBridgeTokenOutput(fnArgs)
50 | .then(setResult)
51 | .catch(logError)
52 | }
53 |
54 | return [fn, result]
55 | }
56 |
57 | function useExecuteBridgeSwap(args: {
58 | ethereum: any,
59 | chainId: number,
60 | tokenFrom: Token,
61 | tokenTo: Token,
62 | amountFrom: BigNumberish,
63 | amountTo: BigNumberish,
64 | chainIdTo: number,
65 | addressTo?: string
66 | }): ContractTransactionHook {
67 | const {ethereum, chainId, ...rest} = args;
68 | const [getSigner] = useSignerFromEthereum();
69 | const [tx, setTx] = useState(null);
70 |
71 | function fn() {
72 | const fnArgs = {
73 | ...rest,
74 | amountFrom: parseBigNumberish(rest.amountFrom, rest.tokenFrom, chainId),
75 | amountTo: parseBigNumberish(rest.amountTo, rest.tokenTo, rest.chainIdTo)
76 | };
77 |
78 | const synapseBridge = new Bridge.SynapseBridge({network: chainId});
79 | synapseBridge.executeBridgeTokenTransaction(fnArgs, getSigner(ethereum))
80 | .then(setTx)
81 | .catch(logError)
82 | }
83 |
84 | return [fn, tx]
85 | }
86 |
87 | function useApproveBridgeSwap(args: {
88 | ethereum: any,
89 | chainId: number,
90 | token: Token,
91 | amount?: BigNumberish
92 | }): ApproveActionHook {
93 | const {ethereum, chainId, ...rest} = args;
94 |
95 | const [getSigner] = useSignerFromEthereum();
96 |
97 | const [queryApproveStatus, approvalStatus] = useApproveStatus(ethereum, chainId);
98 |
99 | const
100 | [approveTx, setApproveTx] = useState(null),
101 | [approveData, setApproveData] = useState(null);
102 |
103 | const amt = rest.amount
104 | ? parseBigNumberish(rest.amount, rest.token, chainId)
105 | : undefined
106 |
107 | const fnArgs = {
108 | ...rest,
109 | amount: amt
110 | };
111 |
112 | function fn() {
113 | const synapseBridge = new Bridge.SynapseBridge({network: chainId});
114 |
115 | const [{spender}] = synapseBridge.buildERC20ApproveArgs(fnArgs);
116 |
117 | synapseBridge.executeApproveTransaction(fnArgs, getSigner(ethereum))
118 | .then(res => {
119 | setApproveTx(res);
120 | setApproveData({
121 | ...fnArgs,
122 | spender
123 | });
124 | })
125 | .catch(logError)
126 | }
127 |
128 | useEffect(() => {
129 | if (approvalStatus !== null) {
130 | return
131 | }
132 |
133 | if ((approveTx && approveData)) {
134 | const {token, spender, amount} = approveData;
135 | queryApproveStatus({
136 | token,
137 | spender,
138 | amount,
139 | approveTx
140 | });
141 | }
142 | }, [approveTx, approveData, approvalStatus]);
143 |
144 | return [fn, approveTx, approvalStatus]
145 | }
146 |
147 | function useBridgeAllowance(args: {
148 | ethereum: any,
149 | chainId: number,
150 | token: Token,
151 | }): AllowanceHook {
152 | const {ethereum, chainId, token} = args;
153 |
154 | const [checkAllowance, allowance] = useCheckAllowance(ethereum, chainId);
155 |
156 | useEffect(() => {
157 | const synapseBridge = new Bridge.SynapseBridge({network: chainId});
158 | const [{spender}] = synapseBridge.buildERC20ApproveArgs({token});
159 |
160 | checkAllowance({token, spender});
161 | }, [chainId, token])
162 |
163 | return [allowance]
164 | }
165 |
166 | function useNeedsBridgeSwapApproval(args: {
167 | ethereum: any,
168 | chainId: number,
169 | token: Token,
170 | amount: BigNumberish
171 | }): NeedsApprovalHook {
172 | const {chainId, token, amount} = args;
173 |
174 | const amt = parseBigNumberish(amount, token, chainId);
175 |
176 | const [allowance] = useBridgeAllowance(args);
177 |
178 | const [needsApprove, setNeedsApprove] = useState(null);
179 |
180 | useEffect(() => {
181 | if (allowance) {
182 | setNeedsApprove(allowance.lt(amt));
183 | }
184 | }, [allowance, chainId, token, amount]);
185 |
186 | return [needsApprove, allowance]
187 | }
188 |
189 | function useBridgeSwapApproval(args: {
190 | ethereum: any,
191 | chainId: number,
192 | token: Token,
193 | amount: BigNumberish
194 | }): UseApproveHook {
195 | const [needsApprove, allowance] = useNeedsBridgeSwapApproval(args)
196 | const [execApprove, approveTx, approveStatus] = useApproveBridgeSwap({...args, amount: undefined});
197 |
198 | return {
199 | needsApprove,
200 | allowance,
201 | execApprove,
202 | approveTx,
203 | approveStatus
204 | } as const
205 | }
206 |
207 | export {
208 | useApproveBridgeSwap,
209 | useExecuteBridgeSwap,
210 | useCalculateBridgeSwapOutput,
211 | useBridgeAllowance,
212 | useNeedsBridgeSwapApproval,
213 | useBridgeSwapApproval
214 | }
--------------------------------------------------------------------------------
/src/hooks/errors.ts:
--------------------------------------------------------------------------------
1 | import {Token} from "@token";
2 |
3 | export class TransactionError extends Error {
4 | constructor(txHash: string, message: string, cause?: Error) {
5 | super(message)
6 | this.name = this.constructor.name;
7 | this.message = `Error in transaction ${txHash}: ${message}`;
8 |
9 | if (cause) {
10 | this.cause = cause;
11 | }
12 | }
13 | }
14 |
15 | export class AllowanceError extends Error {
16 | constructor(owner: string, spender: string, token: Token, message: string, cause?: Error) {
17 | super(message)
18 | this.name = this.constructor.name;
19 | this.message = `Error querying spend allowance of ${spender} for ${owner} token ${token.name}`;
20 |
21 | if (cause) {
22 | this.cause = cause;
23 | }
24 | }
25 | }
--------------------------------------------------------------------------------
/src/hooks/helpers.ts:
--------------------------------------------------------------------------------
1 | import type {Token} from "@token";
2 | import {SwapPools} from "@swappools";
3 |
4 | import {
5 | BigNumber,
6 | type BigNumberish
7 | } from "@ethersproject/bignumber";
8 |
9 |
10 | export function parseBigNumberish(n: BigNumberish, token: Token, chainId: number): BigNumber {
11 | return n instanceof BigNumber
12 | ? n as BigNumber
13 | : token.etherToWei(n, chainId)
14 | }
15 |
16 | export function parseLPTokenBigNumberishArray(
17 | lpToken: SwapPools.SwapPoolToken,
18 | amounts: BigNumberish[],
19 | chainId: number
20 | ): BigNumber[] {
21 | const poolTokens = lpToken.poolTokens;
22 |
23 | return amounts.map((n, idx) =>
24 | parseBigNumberish(n, poolTokens[idx], chainId)
25 | )
26 | }
27 |
28 | export function parseApproveAmount(amount: BigNumberish, token: Token, chainId: number): BigNumber | undefined {
29 | return amount
30 | ? parseBigNumberish(amount, token, chainId)
31 | : undefined
32 | }
33 |
34 | export function logError(e: any) {
35 | const err = e instanceof Error ? e : new Error(e);
36 | console.error(err);
37 | }
--------------------------------------------------------------------------------
/src/hooks/index.ts:
--------------------------------------------------------------------------------
1 | export {
2 | useApproveBridgeSwap,
3 | useExecuteBridgeSwap,
4 | useCalculateBridgeSwapOutput,
5 | useBridgeAllowance,
6 | useNeedsBridgeSwapApproval,
7 | useBridgeSwapApproval
8 | } from "./bridge";
9 |
10 | export {
11 | useChainStableswapLPToken,
12 | useChainETHSwapLPToken,
13 | useHarmonyJewelLPToken,
14 | useHarmonyAVAXLPToken,
15 | useCalculateAddLiquidity,
16 | useCalculateRemoveLiquidity,
17 | useCalculateRemoveLiquidityOneToken,
18 | useAddLiquidity,
19 | useRemoveLiquidity,
20 | useRemoveLiquidityOneToken,
21 | useCalculateSwapRate,
22 | useSwapTokens,
23 | useApproveLPToken,
24 | useApprovePoolToken,
25 | useLPTokenAllowance,
26 | useLPTokenNeedsApproval,
27 | useLPTokenApproval,
28 | usePoolTokenAllowance,
29 | usePoolTokenNeedsApproval,
30 | usePoolTokenApproval
31 | } from "./lptokens";
32 |
33 | export {
34 | useApproveStatus,
35 | useApproveTokenSpend,
36 | AllowanceError,
37 | TransactionError
38 | } from "./tokens";
--------------------------------------------------------------------------------
/src/hooks/signer.ts:
--------------------------------------------------------------------------------
1 | import {Web3Provider} from "@ethersproject/providers";
2 |
3 |
4 | function useSignerFromEthereum() {
5 | const fn = (ethereum: any) => {
6 | const newProvider = new Web3Provider(ethereum, 'any');
7 | return newProvider.getSigner();
8 | }
9 |
10 | return [fn]
11 | }
12 |
13 | export {useSignerFromEthereum}
--------------------------------------------------------------------------------
/src/hooks/tokens.ts:
--------------------------------------------------------------------------------
1 | import type {Token} from "@token";
2 | import {Tokens} from "@tokens";
3 |
4 | import {allowanceOf, MAX_APPROVAL_AMOUNT} from "@bridge/erc20";
5 |
6 | import {useSignerFromEthereum} from "./signer";
7 | import {logError, parseApproveAmount} from "./helpers";
8 | import {
9 | AllowanceError,
10 | TransactionError
11 | } from "./errors";
12 | import {ApproveTokenState} from "./types";
13 |
14 | import {useEffect, useState} from "react";
15 | import {BigNumber, type BigNumberish} from "@ethersproject/bignumber";
16 | import type {ContractTransaction} from "@ethersproject/contracts";
17 |
18 | const TX_STATUS_SUCCESS: number = 1;
19 |
20 | function useCheckAllowance(ethereum: any, chainId: number) {
21 | const [getSigner] = useSignerFromEthereum();
22 |
23 | const [allowance, setAllowance] = useState(null);
24 |
25 | function fn(args: {
26 | token: Token,
27 | spender: string
28 | }) {
29 | getSigner(ethereum).getAddress()
30 | .then(ownerAddress => {
31 | const {token, spender} = args;
32 | const tokenArgs = {tokenAddress: token.address(chainId), chainId};
33 | allowanceOf(ownerAddress, spender, tokenArgs)
34 | .then(setAllowance)
35 | .catch(e => {
36 | const err = e instanceof Error ? e : new Error(e);
37 | const allowanceErr = new AllowanceError(
38 | ownerAddress,
39 | spender,
40 | token,
41 | "exception thrown from ERC20.allowanceOf()",
42 | err
43 | );
44 | console.error(allowanceErr);
45 | console.error(allowanceErr.cause);
46 | })
47 | })
48 | }
49 |
50 | return [fn, allowance] as const
51 | }
52 |
53 | function useApproveTokenSpend(ethereum: any, chainId: number) {
54 | const [getSigner] = useSignerFromEthereum();
55 |
56 | const [tx, setTx] = useState(null);
57 |
58 | function fn(args: {
59 | token: Token,
60 | spender: string,
61 | amount?: BigNumberish
62 | }) {
63 | Tokens.approveTokenSpend({
64 | ...args,
65 | amount: parseApproveAmount(args.amount, args.token, chainId),
66 | chainId,
67 | signer: getSigner(ethereum)
68 | })
69 | .then(setTx)
70 | .catch(logError)
71 | }
72 |
73 | return [fn, tx] as const
74 | }
75 |
76 | function useApproveStatus(ethereum: any, chainId: number) {
77 | const [approveComplete, setApproveComplete] = useState(false);
78 | const [approveState, setApproveState] = useState(null);
79 |
80 | function fn(args: {
81 | token: Token,
82 | spender: string,
83 | amount?: BigNumberish,
84 | approveTx: ContractTransaction
85 | }) {
86 | if (approveComplete) {
87 | return
88 | }
89 |
90 | const {
91 | token,
92 | spender,
93 | amount,
94 | approveTx
95 | } = args;
96 |
97 |
98 | const wantAmt: BigNumber = amount ? parseApproveAmount(amount, token, chainId) : MAX_APPROVAL_AMOUNT;
99 | if (!approveTx) {
100 | return
101 | }
102 |
103 | const txHash = approveTx.hash;
104 | const confirmations = approveTx.confirmations;
105 |
106 | if (confirmations >= 1) {
107 | setApproveState({
108 | token,
109 | spender,
110 | amount: wantAmt
111 | });
112 |
113 | return
114 | }
115 |
116 | approveTx.wait(1)
117 | .then(txResult => {
118 | const {status: txStatus} = txResult;
119 | if (txStatus) {
120 | if (txStatus !== TX_STATUS_SUCCESS) {
121 | const txErr = new TransactionError(txHash, "reverted");
122 | console.error(txErr);
123 | return
124 | }
125 | }
126 |
127 | setApproveState({
128 | token,
129 | spender,
130 | amount: wantAmt
131 | });
132 | })
133 | .catch(e => {
134 | const err = e instanceof Error ? e : new Error(e);
135 | const txErr = new TransactionError(txHash, "exception thrown from .wait(1)", err);
136 | console.error(txErr);
137 | console.error(txErr.cause)
138 | })
139 | }
140 |
141 | const [checkAllowance, allowance] = useCheckAllowance(ethereum, chainId);
142 | const [checkFired, setCheckFired] = useState(false);
143 |
144 | useEffect(() => {
145 | if (!checkFired && approveState) {
146 | checkAllowance(approveState);
147 | setCheckFired(true);
148 | }
149 | }, [checkFired, approveState])
150 |
151 | useEffect(() => {
152 | if (allowance && approveState) {
153 | let checkAmt: BigNumber = BigNumber.from(approveState.amount);
154 |
155 | if (MAX_APPROVAL_AMOUNT.eq(approveState.amount)) {
156 | checkAmt = MAX_APPROVAL_AMOUNT.sub(5);
157 | }
158 |
159 | if (allowance.gte(checkAmt)) {
160 | setApproveComplete(true);
161 | }
162 | }
163 | }, [allowance, approveState]);
164 |
165 | return [fn, approveComplete, allowance] as const
166 | }
167 |
168 | export {
169 | TransactionError,
170 | AllowanceError,
171 | useApproveTokenSpend,
172 | useApproveStatus,
173 | useCheckAllowance
174 | }
--------------------------------------------------------------------------------
/src/hooks/types.ts:
--------------------------------------------------------------------------------
1 | import type {Token} from "@token";
2 |
3 | import type {BigNumber, BigNumberish} from "@ethersproject/bignumber";
4 | import type {ContractTransaction} from "@ethersproject/contracts";
5 |
6 | export type ActionHook = [() => void, T]
7 |
8 | export type ContractTransactionHook = ActionHook
9 |
10 | export type ApproveActionHook = [() => void, ContractTransaction, boolean]
11 |
12 | export type AllowanceHook = [BigNumber]
13 |
14 | export type NeedsApprovalHook = [boolean, BigNumber]
15 |
16 | export type ApproveTokenState = {
17 | token: Token;
18 | spender: string;
19 | amount?: BigNumberish;
20 | }
21 |
22 | export interface UseApproveHook {
23 | needsApprove: boolean;
24 | allowance: BigNumber;
25 | execApprove: () => void;
26 | approveTx: ContractTransaction;
27 | approveStatus: boolean;
28 | }
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | export {
2 | SynapseBridgeContractInstance,
3 | L1BridgeZapContractInstance,
4 | L2BridgeZapContractInstance
5 | } from "@entities";
6 |
7 | export {
8 | Bridge,
9 | SynapseBridge,
10 | getRequiredConfirmationsForBridge,
11 | bridgeSwapSupported,
12 | checkBridgeTransactionComplete
13 | } from "@bridge/bridge";
14 |
15 | export type {
16 | CanBridgeResult,
17 | BridgeOutputEstimate,
18 | BridgeParams,
19 | BridgeTransactionParams
20 | } from "@bridge/bridge";
21 |
22 | export {Slippages} from "@bridge/slippages";
23 |
24 | export {
25 | ChainId,
26 | supportedChainIds,
27 | EIP1559Chains,
28 | chainSupportsEIP1559
29 | } from "@common/chainid";
30 |
31 | export type {ChainGasPrices} from "./gasprice";
32 | export {fetchChainGasPrices} from "./gasprice";
33 |
34 | export type {GasOptions} from "@common/gasoptions";
35 | export {populateGasOptions, makeTransactionGasOverrides} from "@common/gasoptions";
36 |
37 | export {
38 | Networks,
39 | supportedNetworks
40 | } from "@common/networks";
41 |
42 | export {
43 | type Token,
44 | BaseToken,
45 | WrapperToken
46 | } from "@token";
47 |
48 | export {Tokens} from "@tokens";
49 |
50 | export {
51 | SwapPools,
52 | networkSwapTokensMap,
53 | allNetworksSwapTokensMap,
54 | type NetworkSwappableTokensMap
55 | } from "@swappools";
56 |
57 | import {TokenSwap, UnsupportedSwapErrors} from "@tokenswap";
58 |
59 | import detailedTokenSwapMap = TokenSwap.detailedTokenSwapMap;
60 |
61 | export {TokenSwap, UnsupportedSwapErrors, detailedTokenSwapMap};
62 |
63 | export {
64 | type RPCEndpointsConfig,
65 | configureRPCEndpoints
66 | } from "@internal/rpcproviders";
67 |
68 | export type {
69 | SynapseBridgeContract,
70 | GenericZapBridgeContract,
71 | L1BridgeZapContract,
72 | L2BridgeZapContract,
73 | SynapseERC20Contract,
74 | BridgeConfigV3Contract
75 | } from "@contracts";
76 |
77 | import {
78 | useApproveBridgeSwap,
79 | useExecuteBridgeSwap,
80 | useCalculateBridgeSwapOutput,
81 | useBridgeAllowance,
82 | useNeedsBridgeSwapApproval,
83 | useBridgeSwapApproval,
84 | useChainStableswapLPToken,
85 | useChainETHSwapLPToken,
86 | useHarmonyJewelLPToken,
87 | useHarmonyAVAXLPToken,
88 | useCalculateAddLiquidity,
89 | useCalculateRemoveLiquidity,
90 | useCalculateRemoveLiquidityOneToken,
91 | useAddLiquidity,
92 | useRemoveLiquidity,
93 | useRemoveLiquidityOneToken,
94 | useCalculateSwapRate,
95 | useSwapTokens,
96 | useApproveLPToken,
97 | useApprovePoolToken,
98 | useLPTokenAllowance,
99 | useLPTokenNeedsApproval,
100 | useLPTokenApproval,
101 | usePoolTokenAllowance,
102 | usePoolTokenNeedsApproval,
103 | usePoolTokenApproval,
104 | AllowanceError,
105 | TransactionError
106 | } from "./hooks/index";
107 |
108 | export {
109 | useApproveBridgeSwap,
110 | useExecuteBridgeSwap,
111 | useCalculateBridgeSwapOutput,
112 | useBridgeAllowance,
113 | useNeedsBridgeSwapApproval,
114 | useBridgeSwapApproval,
115 | useChainStableswapLPToken,
116 | useChainETHSwapLPToken,
117 | useHarmonyJewelLPToken,
118 | useHarmonyAVAXLPToken,
119 | useCalculateAddLiquidity,
120 | useCalculateRemoveLiquidity,
121 | useCalculateRemoveLiquidityOneToken,
122 | useAddLiquidity,
123 | useRemoveLiquidity,
124 | useRemoveLiquidityOneToken,
125 | useCalculateSwapRate,
126 | useSwapTokens,
127 | useApproveLPToken,
128 | useApprovePoolToken,
129 | useLPTokenAllowance,
130 | useLPTokenNeedsApproval,
131 | useLPTokenApproval,
132 | usePoolTokenAllowance,
133 | usePoolTokenNeedsApproval,
134 | usePoolTokenApproval,
135 | AllowanceError,
136 | TransactionError
137 | };
--------------------------------------------------------------------------------
/src/internal/gen/common.ts:
--------------------------------------------------------------------------------
1 | /* Autogenerated file. Do not edit manually. */
2 | /* tslint:disable */
3 | /* eslint-disable */
4 | import type { Listener } from "@ethersproject/providers";
5 | import type { Event, EventFilter } from "ethers";
6 |
7 | export interface TypedEvent<
8 | TArgsArray extends Array = any,
9 | TArgsObject = any
10 | > extends Event {
11 | args: TArgsArray & TArgsObject;
12 | }
13 |
14 | export interface TypedEventFilter<_TEvent extends TypedEvent>
15 | extends EventFilter {}
16 |
17 | export interface TypedListener {
18 | (...listenerArg: [...__TypechainArgsArray, TEvent]): void;
19 | }
20 |
21 | type __TypechainArgsArray = T extends TypedEvent ? U : never;
22 |
23 | export interface OnEvent {
24 | (
25 | eventFilter: TypedEventFilter,
26 | listener: TypedListener
27 | ): TRes;
28 | (eventName: string, listener: Listener): TRes;
29 | }
30 |
31 | export type MinEthersFactory = {
32 | deploy(...a: ARGS[]): Promise;
33 | };
34 |
35 | export type GetContractTypeFromFactory = F extends MinEthersFactory<
36 | infer C,
37 | any
38 | >
39 | ? C
40 | : never;
41 |
42 | export type GetARGsTypeFromFactory = F extends MinEthersFactory
43 | ? Parameters
44 | : never;
45 |
46 | export type PromiseOrValue = T | Promise;
47 |
--------------------------------------------------------------------------------
/src/internal/gen/factories/AvaxJewelMigration__factory.ts:
--------------------------------------------------------------------------------
1 | /* Autogenerated file. Do not edit manually. */
2 | /* tslint:disable */
3 | /* eslint-disable */
4 |
5 | import { Contract, Signer, utils } from "ethers";
6 | import type { Provider } from "@ethersproject/providers";
7 | import type {
8 | AvaxJewelMigration,
9 | AvaxJewelMigrationInterface,
10 | } from "../AvaxJewelMigration";
11 |
12 | const _abi = [
13 | {
14 | inputs: [],
15 | stateMutability: "nonpayable",
16 | type: "constructor",
17 | },
18 | {
19 | anonymous: false,
20 | inputs: [
21 | {
22 | indexed: true,
23 | internalType: "address",
24 | name: "previousOwner",
25 | type: "address",
26 | },
27 | {
28 | indexed: true,
29 | internalType: "address",
30 | name: "newOwner",
31 | type: "address",
32 | },
33 | ],
34 | name: "OwnershipTransferred",
35 | type: "event",
36 | },
37 | {
38 | inputs: [],
39 | name: "LEGACY_TOKEN",
40 | outputs: [
41 | {
42 | internalType: "contract IERC20",
43 | name: "",
44 | type: "address",
45 | },
46 | ],
47 | stateMutability: "view",
48 | type: "function",
49 | },
50 | {
51 | inputs: [],
52 | name: "NEW_TOKEN",
53 | outputs: [
54 | {
55 | internalType: "contract IERC20Mintable",
56 | name: "",
57 | type: "address",
58 | },
59 | ],
60 | stateMutability: "view",
61 | type: "function",
62 | },
63 | {
64 | inputs: [],
65 | name: "SYNAPSE_BRIDGE",
66 | outputs: [
67 | {
68 | internalType: "contract ISynapseBridge",
69 | name: "",
70 | type: "address",
71 | },
72 | ],
73 | stateMutability: "view",
74 | type: "function",
75 | },
76 | {
77 | inputs: [
78 | {
79 | internalType: "uint256",
80 | name: "amount",
81 | type: "uint256",
82 | },
83 | ],
84 | name: "migrate",
85 | outputs: [],
86 | stateMutability: "nonpayable",
87 | type: "function",
88 | },
89 | {
90 | inputs: [
91 | {
92 | internalType: "uint256",
93 | name: "amount",
94 | type: "uint256",
95 | },
96 | {
97 | internalType: "address",
98 | name: "to",
99 | type: "address",
100 | },
101 | {
102 | internalType: "uint256",
103 | name: "chainId",
104 | type: "uint256",
105 | },
106 | ],
107 | name: "migrateAndBridge",
108 | outputs: [],
109 | stateMutability: "nonpayable",
110 | type: "function",
111 | },
112 | {
113 | inputs: [],
114 | name: "owner",
115 | outputs: [
116 | {
117 | internalType: "address",
118 | name: "",
119 | type: "address",
120 | },
121 | ],
122 | stateMutability: "view",
123 | type: "function",
124 | },
125 | {
126 | inputs: [],
127 | name: "redeemLegacy",
128 | outputs: [],
129 | stateMutability: "nonpayable",
130 | type: "function",
131 | },
132 | {
133 | inputs: [],
134 | name: "renounceOwnership",
135 | outputs: [],
136 | stateMutability: "nonpayable",
137 | type: "function",
138 | },
139 | {
140 | inputs: [
141 | {
142 | internalType: "address",
143 | name: "newOwner",
144 | type: "address",
145 | },
146 | ],
147 | name: "transferOwnership",
148 | outputs: [],
149 | stateMutability: "nonpayable",
150 | type: "function",
151 | },
152 | ];
153 |
154 | export class AvaxJewelMigration__factory {
155 | static readonly abi = _abi;
156 | static createInterface(): AvaxJewelMigrationInterface {
157 | return new utils.Interface(_abi) as AvaxJewelMigrationInterface;
158 | }
159 | static connect(
160 | address: string,
161 | signerOrProvider: Signer | Provider
162 | ): AvaxJewelMigration {
163 | return new Contract(address, _abi, signerOrProvider) as AvaxJewelMigration;
164 | }
165 | }
166 |
--------------------------------------------------------------------------------
/src/internal/gen/factories/ERC20__factory.ts:
--------------------------------------------------------------------------------
1 | /* Autogenerated file. Do not edit manually. */
2 | /* tslint:disable */
3 | /* eslint-disable */
4 |
5 | import { Contract, Signer, utils } from "ethers";
6 | import type { Provider } from "@ethersproject/providers";
7 | import type { ERC20, ERC20Interface } from "../ERC20";
8 |
9 | const _abi = [
10 | {
11 | constant: true,
12 | inputs: [],
13 | name: "name",
14 | outputs: [
15 | {
16 | name: "",
17 | type: "string",
18 | },
19 | ],
20 | payable: false,
21 | stateMutability: "view",
22 | type: "function",
23 | },
24 | {
25 | constant: false,
26 | inputs: [
27 | {
28 | name: "_spender",
29 | type: "address",
30 | },
31 | {
32 | name: "_value",
33 | type: "uint256",
34 | },
35 | ],
36 | name: "approve",
37 | outputs: [
38 | {
39 | name: "",
40 | type: "bool",
41 | },
42 | ],
43 | payable: false,
44 | stateMutability: "nonpayable",
45 | type: "function",
46 | },
47 | {
48 | constant: true,
49 | inputs: [],
50 | name: "totalSupply",
51 | outputs: [
52 | {
53 | name: "",
54 | type: "uint256",
55 | },
56 | ],
57 | payable: false,
58 | stateMutability: "view",
59 | type: "function",
60 | },
61 | {
62 | constant: false,
63 | inputs: [
64 | {
65 | name: "_from",
66 | type: "address",
67 | },
68 | {
69 | name: "_to",
70 | type: "address",
71 | },
72 | {
73 | name: "_value",
74 | type: "uint256",
75 | },
76 | ],
77 | name: "transferFrom",
78 | outputs: [
79 | {
80 | name: "",
81 | type: "bool",
82 | },
83 | ],
84 | payable: false,
85 | stateMutability: "nonpayable",
86 | type: "function",
87 | },
88 | {
89 | constant: true,
90 | inputs: [],
91 | name: "decimals",
92 | outputs: [
93 | {
94 | name: "",
95 | type: "uint8",
96 | },
97 | ],
98 | payable: false,
99 | stateMutability: "view",
100 | type: "function",
101 | },
102 | {
103 | constant: true,
104 | inputs: [
105 | {
106 | name: "_owner",
107 | type: "address",
108 | },
109 | ],
110 | name: "balanceOf",
111 | outputs: [
112 | {
113 | name: "balance",
114 | type: "uint256",
115 | },
116 | ],
117 | payable: false,
118 | stateMutability: "view",
119 | type: "function",
120 | },
121 | {
122 | constant: true,
123 | inputs: [],
124 | name: "symbol",
125 | outputs: [
126 | {
127 | name: "",
128 | type: "string",
129 | },
130 | ],
131 | payable: false,
132 | stateMutability: "view",
133 | type: "function",
134 | },
135 | {
136 | constant: false,
137 | inputs: [
138 | {
139 | name: "_to",
140 | type: "address",
141 | },
142 | {
143 | name: "_value",
144 | type: "uint256",
145 | },
146 | ],
147 | name: "transfer",
148 | outputs: [
149 | {
150 | name: "",
151 | type: "bool",
152 | },
153 | ],
154 | payable: false,
155 | stateMutability: "nonpayable",
156 | type: "function",
157 | },
158 | {
159 | constant: true,
160 | inputs: [
161 | {
162 | name: "_owner",
163 | type: "address",
164 | },
165 | {
166 | name: "_spender",
167 | type: "address",
168 | },
169 | ],
170 | name: "allowance",
171 | outputs: [
172 | {
173 | name: "",
174 | type: "uint256",
175 | },
176 | ],
177 | payable: false,
178 | stateMutability: "view",
179 | type: "function",
180 | },
181 | {
182 | payable: true,
183 | stateMutability: "payable",
184 | type: "fallback",
185 | },
186 | {
187 | anonymous: false,
188 | inputs: [
189 | {
190 | indexed: true,
191 | name: "owner",
192 | type: "address",
193 | },
194 | {
195 | indexed: true,
196 | name: "spender",
197 | type: "address",
198 | },
199 | {
200 | indexed: false,
201 | name: "value",
202 | type: "uint256",
203 | },
204 | ],
205 | name: "Approval",
206 | type: "event",
207 | },
208 | {
209 | anonymous: false,
210 | inputs: [
211 | {
212 | indexed: true,
213 | name: "from",
214 | type: "address",
215 | },
216 | {
217 | indexed: true,
218 | name: "to",
219 | type: "address",
220 | },
221 | {
222 | indexed: false,
223 | name: "value",
224 | type: "uint256",
225 | },
226 | ],
227 | name: "Transfer",
228 | type: "event",
229 | },
230 | ];
231 |
232 | export class ERC20__factory {
233 | static readonly abi = _abi;
234 | static createInterface(): ERC20Interface {
235 | return new utils.Interface(_abi) as ERC20Interface;
236 | }
237 | static connect(address: string, signerOrProvider: Signer | Provider): ERC20 {
238 | return new Contract(address, _abi, signerOrProvider) as ERC20;
239 | }
240 | }
241 |
--------------------------------------------------------------------------------
/src/internal/gen/factories/index.ts:
--------------------------------------------------------------------------------
1 | /* Autogenerated file. Do not edit manually. */
2 | /* tslint:disable */
3 | /* eslint-disable */
4 | export { AvaxJewelMigration__factory } from "./AvaxJewelMigration__factory";
5 | export { BridgeConfigV3__factory } from "./BridgeConfigV3__factory";
6 | export { ERC20__factory } from "./ERC20__factory";
7 | export { L1BridgeZap__factory } from "./L1BridgeZap__factory";
8 | export { L2BridgeZap__factory } from "./L2BridgeZap__factory";
9 | export { SwapFlashLoan__factory } from "./SwapFlashLoan__factory";
10 | export { SynapseBridge__factory } from "./SynapseBridge__factory";
11 | export { SynapseERC20__factory } from "./SynapseERC20__factory";
12 |
--------------------------------------------------------------------------------
/src/internal/gen/index.ts:
--------------------------------------------------------------------------------
1 | /* Autogenerated file. Do not edit manually. */
2 | /* tslint:disable */
3 | /* eslint-disable */
4 | export type { AvaxJewelMigration } from "./AvaxJewelMigration";
5 | export type { BridgeConfigV3 } from "./BridgeConfigV3";
6 | export type { ERC20 } from "./ERC20";
7 | export type { L1BridgeZap } from "./L1BridgeZap";
8 | export type { L2BridgeZap } from "./L2BridgeZap";
9 | export type { SwapFlashLoan } from "./SwapFlashLoan";
10 | export type { SynapseBridge } from "./SynapseBridge";
11 | export type { SynapseERC20 } from "./SynapseERC20";
12 | export { AvaxJewelMigration__factory } from "./factories/AvaxJewelMigration__factory";
13 | export { BridgeConfigV3__factory } from "./factories/BridgeConfigV3__factory";
14 | export { ERC20__factory } from "./factories/ERC20__factory";
15 | export { L1BridgeZap__factory } from "./factories/L1BridgeZap__factory";
16 | export { L2BridgeZap__factory } from "./factories/L2BridgeZap__factory";
17 | export { SwapFlashLoan__factory } from "./factories/SwapFlashLoan__factory";
18 | export { SynapseBridge__factory } from "./factories/SynapseBridge__factory";
19 | export { SynapseERC20__factory } from "./factories/SynapseERC20__factory";
20 |
--------------------------------------------------------------------------------
/src/internal/minirpc.ts:
--------------------------------------------------------------------------------
1 | import {fetchJson} from "@ethersproject/web";
2 | import type {ExternalProvider} from "@ethersproject/providers";
3 |
4 | export class RequestError extends Error {
5 | readonly code: number;
6 | readonly data: any;
7 |
8 | constructor(message: string, code: number, data: any) {
9 | super(message)
10 | this.code = code
11 | this.data = data
12 | this.name = this.constructor.name // dafuq
13 | this.message = message // dafuq message
14 | }
15 | }
16 |
17 | interface RPCRequest {
18 | method: string;
19 | params?: Array;
20 | }
21 |
22 | interface JsonRPCRequest extends RPCRequest {
23 | jsonrpc: "2.0";
24 | id: number;
25 | }
26 |
27 | interface BatchItem {
28 | request: JsonRPCRequest;
29 | resolve: (result: any) => void;
30 | reject: (reason?: any) => void,
31 | }
32 |
33 | export class MiniRpcProvider implements ExternalProvider {
34 | readonly isMetaMask: boolean = false;
35 | readonly chainId: number;
36 |
37 | private _url: string;
38 | private _host: string;
39 | private _path: string;
40 |
41 | protected _batchInterval: number;
42 |
43 | private _nextId: number = 1;
44 | private _batchAggregator: NodeJS.Timeout = null;
45 | private _pendingBatch: BatchItem[] = null;
46 |
47 | /* c8 ignore next */
48 | constructor(chainId: number, url: string, batchWaitTimeMs: number=50) {
49 | this.chainId = chainId;
50 |
51 | const parsed = new URL(url);
52 | this._url = parsed.toString();
53 | this._host = parsed.host;
54 | this._path = parsed.pathname;
55 |
56 | // how long to wait to batch calls
57 | this._batchInterval = batchWaitTimeMs;
58 | }
59 |
60 | /* c8 ignore start */
61 | /**
62 | * Amount of time, in milliseconds, between batch RPC calls
63 | */
64 | get batchInterval(): number {
65 | return this._batchInterval
66 | }
67 |
68 | /**
69 | * Sets the provider's interval for sending batch RPC calls.
70 | * @param interval amount of time in milliseconds the provider will wait between sending batch RPC calls
71 | * @internal
72 | */
73 | set batchInterval(interval: number) {
74 | this._batchInterval = interval;
75 | }
76 |
77 | get url(): string {
78 | return this._url
79 | }
80 |
81 | /**
82 | * @internal
83 | */
84 | set url(newUrl: string | URL) {
85 | const parsed = newUrl instanceof URL ? newUrl : new URL(newUrl);
86 | this._host = parsed.host;
87 | this._path = parsed.pathname;
88 | this._url = parsed.toString();
89 | }
90 |
91 | get host(): string {
92 | return this._host
93 | }
94 |
95 | get path(): string {
96 | return this._path
97 | }
98 | /* c8 ignore stop */
99 |
100 | async request(request: RPCRequest): Promise {
101 | if (request.method === 'eth_chainId') {
102 | return `0x${this.chainId.toString(16)}`
103 | }
104 |
105 | if (this._pendingBatch === null) {
106 | this._pendingBatch = [];
107 | }
108 |
109 | const batchItem: BatchItem = {
110 | request: {
111 | jsonrpc: "2.0",
112 | id: (this._nextId++),
113 | ...request
114 | },
115 | resolve: null,
116 | reject: null,
117 | };
118 |
119 | const prom: Promise = new Promise((resolve, reject) => {
120 | batchItem.resolve = resolve;
121 | batchItem.reject = reject;
122 | });
123 |
124 | this._pendingBatch.push(batchItem);
125 |
126 | if (!this._batchAggregator) {
127 | setTimeout(() => this._processBatch(), this._batchInterval);
128 | }
129 |
130 | return prom
131 | }
132 |
133 | private async _processBatch() {
134 | let currentBatch = this._pendingBatch;
135 |
136 | this._pendingBatch = null;
137 | this._batchAggregator = null;
138 |
139 | if (currentBatch === null) {
140 | currentBatch = [];
141 | }
142 |
143 | const requests: JsonRPCRequest[] = currentBatch.map(req => req.request);
144 |
145 | if (requests.length === 0) {
146 | return
147 | }
148 |
149 | return fetchJson(this._url, JSON.stringify(requests))
150 | .then(result =>
151 | currentBatch.forEach((req, idx) => {
152 | const payload = result[idx];
153 | if (payload) {
154 | if (payload.error) {
155 | const {message, code, data} = payload.error;
156 | req.reject(new RequestError(message, code, data));
157 | } else {
158 | req.resolve(payload.result);
159 | }
160 | }
161 | })
162 | )
163 | .catch(error => currentBatch.forEach(batchItem => batchItem.reject(error)))
164 | }
165 | }
--------------------------------------------------------------------------------
/src/internal/rpcconnector.ts:
--------------------------------------------------------------------------------
1 | import type {ChainIdTypeMap} from "@chainid";
2 | import type {StringMap} from "@common/types";
3 |
4 | import type {Provider} from "@ethersproject/providers";
5 | import {Web3Provider} from "@ethersproject/providers";
6 |
7 | import {MiniRpcProvider} from "./minirpc";
8 |
9 | interface RpcConnectorArgs {
10 | urls: StringMap;
11 | batchInterval?: number;
12 | }
13 |
14 | export class RpcConnector {
15 | protected _providers: ChainIdTypeMap;
16 | protected _web3Providers: ChainIdTypeMap
17 |
18 | protected _chainEndpoints: {[chainId: number]: string};
19 |
20 |
21 | constructor(args: RpcConnectorArgs) {
22 | /* c8 ignore next */
23 | const {urls, batchInterval=50} = args;
24 |
25 | this._chainEndpoints = urls;
26 |
27 | const miniRpcProviders = Object.keys(urls).reduce((acc, chainId) => {
28 | const cid = Number(chainId);
29 | acc[cid] = this._newProvider(cid, urls[cid], batchInterval);
30 | return acc
31 | }, {});
32 |
33 | const web3Providers = Object.keys(miniRpcProviders).reduce((acc, chainId) => {
34 | const cid = Number(chainId);
35 | acc[cid] = new Web3Provider(miniRpcProviders[cid]);
36 | return acc
37 | }, {});
38 |
39 | this._providers = miniRpcProviders;
40 | this._web3Providers = web3Providers;
41 | }
42 |
43 | provider(chainId: number): Provider {
44 | return this._web3Providers[chainId]
45 | }
46 |
47 | /**
48 | * @internal
49 | */
50 | setProviderConfig(
51 | chainId: number,
52 | endpoint: string,
53 | batchInterval: number = 50,
54 | ) {
55 | const provider = this._newProvider(chainId, endpoint, batchInterval);
56 |
57 | delete this._chainEndpoints[chainId];
58 | delete this._providers[chainId];
59 | delete this._web3Providers[chainId];
60 |
61 | this._chainEndpoints[chainId] = endpoint;
62 | this._providers[chainId] = provider
63 | this._web3Providers[chainId] = new Web3Provider(provider);
64 | }
65 |
66 | private _newProvider(
67 | chainId: number,
68 | endpoint: string,
69 | batchInterval: number
70 | ): MiniRpcProvider {
71 | return new MiniRpcProvider(
72 | chainId,
73 | endpoint,
74 | batchInterval
75 | );
76 | }
77 | }
--------------------------------------------------------------------------------
/src/internal/rpcproviders.ts:
--------------------------------------------------------------------------------
1 | import {findKey, fromPairs} from "lodash-es";
2 |
3 | import type {Provider} from "@ethersproject/providers";
4 |
5 | import {ChainId, supportedChainIds} from "@chainid";
6 | import type {StringMap} from "@common/types";
7 |
8 | import {RpcConnector} from "./rpcconnector";
9 |
10 | const RPC_URI_SUFFIX: string = "RPC_URI";
11 |
12 | const makeRpcUriEnvKey = (chainId: number): string => {
13 | const key: string = findKey(ChainId, (o) => o === chainId);
14 |
15 | return `${key}_${RPC_URI_SUFFIX}`
16 | }
17 |
18 | const ENV_KEY_MAP: StringMap = {
19 | [ChainId.ETH]: makeRpcUriEnvKey(ChainId.ETH),
20 | [ChainId.OPTIMISM]: makeRpcUriEnvKey(ChainId.OPTIMISM),
21 | [ChainId.CRONOS]: makeRpcUriEnvKey(ChainId.CRONOS),
22 | [ChainId.BSC]: makeRpcUriEnvKey(ChainId.BSC),
23 | [ChainId.POLYGON]: makeRpcUriEnvKey(ChainId.POLYGON),
24 | [ChainId.FANTOM]: makeRpcUriEnvKey(ChainId.FANTOM),
25 | [ChainId.BOBA]: makeRpcUriEnvKey(ChainId.BOBA),
26 | [ChainId.METIS]: makeRpcUriEnvKey(ChainId.METIS),
27 | [ChainId.MOONBEAM]: makeRpcUriEnvKey(ChainId.MOONBEAM),
28 | [ChainId.MOONRIVER]: makeRpcUriEnvKey(ChainId.MOONRIVER),
29 | [ChainId.ARBITRUM]: makeRpcUriEnvKey(ChainId.ARBITRUM),
30 | [ChainId.AVALANCHE]: makeRpcUriEnvKey(ChainId.AVALANCHE),
31 | [ChainId.DFK]: makeRpcUriEnvKey(ChainId.DFK),
32 | [ChainId.AURORA]: makeRpcUriEnvKey(ChainId.AURORA),
33 | [ChainId.HARMONY]: makeRpcUriEnvKey(ChainId.HARMONY),
34 | [ChainId.KLAYTN]: makeRpcUriEnvKey(ChainId.KLAYTN),
35 | }
36 |
37 | const CHAIN_RPC_URIS: StringMap = {
38 | [ChainId.ETH]: "https://mainnet.infura.io/v3/9aa3d95b3bc440fa88ea12eaa4456161",
39 | [ChainId.OPTIMISM]: "https://mainnet.optimism.io",
40 | [ChainId.CRONOS]: "https://evm-cronos.crypto.org",
41 | [ChainId.BSC]: "https://bsc-dataseed.binance.org/",
42 | [ChainId.POLYGON]: "https://polygon-rpc.com/",
43 | [ChainId.FANTOM]: "https://rpc.ftm.tools/",
44 | [ChainId.BOBA]: "https://replica-oolong.boba.network/",
45 | [ChainId.METIS]: "https://andromeda.metis.io/?owner=1088",
46 | [ChainId.MOONBEAM]: "https://rpc.api.moonbeam.network",
47 | [ChainId.MOONRIVER]: "https://rpc.api.moonriver.moonbeam.network",
48 | [ChainId.ARBITRUM]: "https://arb1.arbitrum.io/rpc",
49 | [ChainId.AVALANCHE]: "https://api.avax.network/ext/bc/C/rpc",
50 | [ChainId.DFK]: "https://subnets.avax.network/defi-kingdoms/dfk-chain/rpc",
51 | [ChainId.AURORA]: "https://mainnet.aurora.dev",
52 | [ChainId.HARMONY]: "https://api.harmony.one/",
53 | [ChainId.KLAYTN]: "https://cypress.chain.thebifrost.io/",
54 | }
55 |
56 | const CHAINID_URI_MAP: StringMap = fromPairs(supportedChainIds().map(cid => [cid, getChainRpcUri(cid)]));
57 |
58 | const RPC_BATCH_INTERVAL = Number(process.env["RPC_BATCH_INTERVAL"]) || 60;
59 |
60 | const RPC_CONNECTOR = new RpcConnector({
61 | urls: CHAINID_URI_MAP,
62 | batchInterval: RPC_BATCH_INTERVAL
63 | });
64 |
65 | export function getChainRpcUri(chainId: number): string {
66 | const
67 | rpcEnvKey: string = ENV_KEY_MAP[chainId],
68 | rpcEnvVal: string|undefined = rpcEnvKey in process.env ? process.env[rpcEnvKey] : undefined;
69 |
70 | return rpcEnvVal ?? CHAIN_RPC_URIS[chainId]
71 | }
72 |
73 | /**
74 | * @param chainId chain id of the network for which to return a provider
75 | */
76 | export function rpcProviderForChain(chainId: number): Provider {
77 | return RPC_CONNECTOR.provider(chainId)
78 | }
79 |
80 | export interface RPCEndpointsConfig {
81 | [chainId: number]: {
82 | endpoint: string;
83 | batchInterval?: number;
84 | }
85 | }
86 |
87 | export function configureRPCEndpoints(config: RPCEndpointsConfig) {
88 | for (const chainId of supportedChainIds()) {
89 | if (config[chainId]) {
90 | let {endpoint, batchInterval} = config[chainId];
91 | RPC_CONNECTOR.setProviderConfig(chainId, endpoint, batchInterval);
92 | }
93 | }
94 | }
--------------------------------------------------------------------------------
/src/internal/swaptype.ts:
--------------------------------------------------------------------------------
1 | import type {ValueOf} from "./types";
2 |
3 | export const SwapType = {
4 | USD: "USD",
5 | SYN: "SYN",
6 | ETH: "ETH",
7 | HIGH: "HIGHSTREET",
8 | DOG: "DOG",
9 | JUMP: "JUMP",
10 | FRAX: "FRAX",
11 | NFD: "NFD",
12 | OHM: "OHM",
13 | GMX: "GMX",
14 | SOLAR: "SOLAR",
15 | AVAX: "AVAX",
16 | MOVR: "MOVR",
17 | FTM: "FTM",
18 | KLAY: "KLAY",
19 | MATIC: "MATIC",
20 | BTCB: "BTCB",
21 | LINK: "LINK",
22 | UST: "UST",
23 | NEWO: "NEWO",
24 | SDT: "SDT",
25 | LUNA: "LUNA",
26 | USDB: "USDB",
27 | JEWEL: "JEWEL",
28 | XJEWEL: "XJEWEL",
29 | DFKTEARS: "DFKTEARS",
30 | VSTA: "VSTA",
31 | H20: "H20",
32 | WBTC: "WBTC",
33 | SFI: "SFI",
34 | } as const;
35 |
36 | export type SwapType = ValueOf
37 |
38 | export type SwapTypeMap = {[k in SwapType]?: T}
39 |
40 | export const mintBurnSwapTypes: SwapType[] = [
41 | SwapType.HIGH, SwapType.DOG, SwapType.JUMP,
42 | SwapType.NFD, SwapType.OHM, SwapType.SOLAR,
43 | SwapType.GMX, SwapType.UST, SwapType.NEWO,
44 | SwapType.SDT, SwapType.LUNA, SwapType.USDB,
45 | SwapType.JEWEL, SwapType.XJEWEL, SwapType.VSTA,
46 | SwapType.H20, SwapType.SFI, SwapType.WBTC,
47 | SwapType.SFI, SwapType.BTCB, SwapType.KLAY,
48 | SwapType.MATIC, SwapType.FTM
49 | ];
--------------------------------------------------------------------------------
/src/internal/types.ts:
--------------------------------------------------------------------------------
1 | export type ID = symbol;
2 |
3 | export interface Distinct {
4 | readonly id: ID;
5 | }
6 |
7 | export type ValueOf = T[keyof T]
--------------------------------------------------------------------------------
/src/internal/utils.ts:
--------------------------------------------------------------------------------
1 | import {find, isNull, isUndefined} from "lodash-es";
2 |
3 | import type {Token} from "@token";
4 | import {Tokens} from "@tokens";
5 |
6 | function tokenReducer(check: Token): Token {
7 | const ret: Token = find(Tokens.AllTokens, (t => check.isEqual(t)));
8 |
9 | return !ret ? undefined : ret
10 | }
11 |
12 | export const tokenSwitch = (check: Token): Token => tokenReducer(check);
13 |
14 | export const isNil = (check: any) => isNull(check) || isUndefined(check)
--------------------------------------------------------------------------------
/test/basic/Env-test.ts:
--------------------------------------------------------------------------------
1 | import path from "path";
2 |
3 | import dotenv from "dotenv";
4 | import * as fs from "fs";
5 |
6 | import {expectEqual} from "@tests/helpers";
7 |
8 | import {ChainId} from "@sdk";
9 |
10 | const
11 | BASE_ENV_PATH: string = path.resolve(path.join("./", ".env")),
12 | TEST_ENV_PATH: string = path.resolve(path.join("./", "test", "basic", "test_env.env"));
13 |
14 | describe('Test ENV values', function(this: Mocha.Suite) {
15 | this.afterAll("reset env", function(this: Mocha.Context) {
16 | dotenv.config({path: BASE_ENV_PATH});
17 | })
18 |
19 |
20 | describe("Test setting URIs from ENV", function(this: Mocha.Suite) {
21 | const valPrefix: string = "carl_";
22 |
23 | function loadTestEnv(): void {
24 | let envData = fs.readFileSync(TEST_ENV_PATH);
25 | let parsedEnv = dotenv.parse(envData);
26 | for (const [k, v] of Object.entries(parsedEnv)) {
27 | process.env[`${k}`] = `${v}`;
28 | }
29 | }
30 |
31 | type testCase = [number, string, string];
32 | const testCases: testCase[] = [
33 | [ChainId.AVALANCHE, "AVALANCHE", "AVALANCHE_RPC_URI"],
34 | [ChainId.MOONRIVER, "MOONRIVER", "MOONRIVER_RPC_URI"],
35 | [ChainId.ETH, "ETH", "ETH_RPC_URI"],
36 | ];
37 |
38 | testCases.forEach((tc: testCase) => {
39 | const
40 | [chainId, name, envKey] = tc,
41 | expectedVal: string = `${valPrefix}${name}`,
42 | testTitle: string = `rpc uri for chainId ${chainId} should be ${expectedVal}`;
43 |
44 |
45 | it(
46 | testTitle,
47 | function(this: Mocha.Context) {
48 | loadTestEnv();
49 | const gotVal: string = process.env[envKey];
50 | expectEqual(gotVal, expectedVal);
51 | }
52 | );
53 | });
54 | });
55 | });
--------------------------------------------------------------------------------
/test/basic/test_env.env:
--------------------------------------------------------------------------------
1 | ETH_RPC_URI="carl_ETH"
2 | MOONRIVER_RPC_URI="carl_MOONRIVER"
3 | AVALANCHE_RPC_URI="carl_AVALANCHE"
--------------------------------------------------------------------------------
/test/entities/entities-test.ts:
--------------------------------------------------------------------------------
1 | import {
2 | wrapExpect,
3 | expectNull
4 | } from "@tests/helpers";
5 |
6 | import {
7 | ChainId,
8 | Networks
9 | } from "@sdk";
10 |
11 | import {
12 | SynapseBridgeContractInstance,
13 | L1BridgeZapContractInstance,
14 | L2BridgeZapContractInstance,
15 | GenericZapBridgeContractInstance,
16 | BridgeConfigV3ContractInstance
17 | } from "@sdk/entities";
18 |
19 | import {rpcProviderForChain} from "@sdk/internal/rpcproviders";
20 | import type {SignerOrProvider} from "@sdk/common/types";
21 |
22 | import type {BaseContract} from "@ethersproject/contracts";
23 |
24 |
25 | describe("Entities tests", function(this: Mocha.Suite) {
26 | enum EntityKind {
27 | SynapseBridge = "SynapseBridge",
28 | L1BridgeZap = "L1BridgeZap",
29 | L2BridgeZap = "L2BridgeZap",
30 | GenericZapBridge = "GenericZapBridge",
31 | BridgeConfigV3 = "BridgeConfigV3",
32 | }
33 |
34 | interface fnArgs {
35 | chainId: number;
36 | signerOrProvider: SignerOrProvider;
37 | }
38 |
39 | interface TestCase {
40 | chainId: number;
41 | fn: (args: fnArgs) => BaseContract;
42 | kind: EntityKind;
43 | }
44 |
45 | function makeTestCase(chainId: number, kind: EntityKind): TestCase {
46 | let fn: (args: fnArgs) => BaseContract;
47 |
48 | switch (kind) {
49 | case EntityKind.SynapseBridge:
50 | fn = SynapseBridgeContractInstance;
51 | break;
52 | case EntityKind.L1BridgeZap:
53 | fn = L1BridgeZapContractInstance;
54 | break;
55 | case EntityKind.L2BridgeZap:
56 | fn = L2BridgeZapContractInstance;
57 | break;
58 | case EntityKind.GenericZapBridge:
59 | fn = GenericZapBridgeContractInstance;
60 | break;
61 | case EntityKind.BridgeConfigV3:
62 | fn = BridgeConfigV3ContractInstance;
63 | break;
64 | }
65 |
66 | return {chainId, fn, kind}
67 | }
68 |
69 | [
70 | makeTestCase(ChainId.FANTOM, EntityKind.SynapseBridge),
71 | makeTestCase(ChainId.FANTOM, EntityKind.L2BridgeZap),
72 | makeTestCase(ChainId.BSC, EntityKind.SynapseBridge),
73 | makeTestCase(ChainId.BSC, EntityKind.L2BridgeZap),
74 | makeTestCase(ChainId.ETH, EntityKind.SynapseBridge),
75 | makeTestCase(ChainId.ETH, EntityKind.L1BridgeZap),
76 | makeTestCase(ChainId.ETH, EntityKind.BridgeConfigV3),
77 | ].forEach(tc => {
78 | describe(Networks.networkName(tc.chainId), function(this: Mocha.Suite) {
79 | const
80 | provider = rpcProviderForChain(tc.chainId),
81 | newInstanceArgs: fnArgs = {chainId: tc.chainId, signerOrProvider: provider};
82 |
83 | let instance: BaseContract = tc.fn(newInstanceArgs);
84 |
85 | it(
86 | `Test ${EntityKind[tc.kind]} instance`,
87 | wrapExpect(expectNull(instance, false))
88 | );
89 | });
90 | });
91 | });
--------------------------------------------------------------------------------
/test/erc20/ERC20-test.ts:
--------------------------------------------------------------------------------
1 | import {expect} from "chai";
2 |
3 | import {
4 | Tokens,
5 | ChainId,
6 | } from "@sdk";
7 |
8 | import {
9 | balanceOf,
10 | allowanceOf,
11 | buildApproveTransaction, approve
12 | } from "@sdk/bridge/erc20";
13 |
14 | import type {
15 | TokenParams,
16 | ApproveArgs
17 | } from "@sdk/bridge/erc20";
18 |
19 | import {SynapseContracts} from "@sdk/common/synapse_contracts";
20 |
21 | import {
22 | DEFAULT_TEST_TIMEOUT,
23 | getTestAmount,
24 | expectGteZero,
25 | expectNotZero,
26 | expectNull,
27 | makeFakeWallet
28 | } from "@tests/helpers";
29 |
30 | import type {BigNumberish} from "@ethersproject/bignumber";
31 | import type {ContractTransaction, PopulatedTransaction} from "@ethersproject/contracts";
32 | import {BigNumber} from "@ethersproject/bignumber";
33 |
34 | describe("ERC20 tests", function(this: Mocha.Suite) {
35 | const testAddr: string = "0xe972647539816442e0987817DF777a9fd9878650";
36 |
37 | const tokenParams = (c: number): TokenParams => ({
38 | chainId: c,
39 | tokenAddress: Tokens.NUSD.address(c),
40 | });
41 |
42 | describe("Approval tests", function(this: Mocha.Suite) {
43 | interface TestCase {
44 | chainId: number;
45 | address: string;
46 | amount?: BigNumberish;
47 | }
48 |
49 | function makeTestCase(chainId: ChainId, amount?: BigNumberish): TestCase {
50 | return {
51 | chainId,
52 | amount,
53 | address: SynapseContracts.contractsForChainId(chainId).bridgeZapAddress,
54 | }
55 | }
56 |
57 | let testCases: TestCase[] = [
58 | makeTestCase(ChainId.BSC),
59 | makeTestCase(ChainId.ETH),
60 | makeTestCase(ChainId.AVALANCHE),
61 | makeTestCase(ChainId.BSC, getTestAmount(Tokens.NUSD, ChainId.BSC)),
62 | makeTestCase(ChainId.ETH, getTestAmount(Tokens.NUSD, ChainId.ETH)),
63 | makeTestCase(ChainId.AVALANCHE, getTestAmount(Tokens.NUSD, ChainId.AVALANCHE)),
64 | ];
65 |
66 | testCases.forEach(tc => {
67 | let {chainId, address: spender, amount} = tc;
68 |
69 | const args: ApproveArgs = {spender, amount};
70 |
71 | it("should build a transaction successfully", async function(this: Mocha.Context) {
72 | this.timeout(DEFAULT_TEST_TIMEOUT);
73 |
74 | let
75 | res: PopulatedTransaction,
76 | prom: Promise = buildApproveTransaction(args, tokenParams(chainId));
77 |
78 | try {
79 | res = await prom;
80 | } catch (err) {
81 | return (await expect(prom, (err as Error).message).to.not.be.rejected)
82 | }
83 |
84 | return expectNull(res, false)
85 | });
86 |
87 | it("Should fail to fire approve() successfully", async function(this: Mocha.Context) {
88 | this.timeout(DEFAULT_TEST_TIMEOUT);
89 |
90 | const fakeWallet = makeFakeWallet(chainId);
91 |
92 | let prom: Promise = approve(args, tokenParams(chainId), fakeWallet);
93 |
94 | return (await expect(prom).to.eventually.be.rejected)
95 | });
96 |
97 | it("Should fail to fire approveTokenSpend() successfully", async function(this: Mocha.Context) {
98 | this.timeout(DEFAULT_TEST_TIMEOUT);
99 |
100 | const fakeWallet = makeFakeWallet(chainId);
101 |
102 | let approveSpendArgs: Tokens.ApproveTokenParams = {
103 | spender,
104 | chainId,
105 | token: Tokens.NUSD,
106 | signer: fakeWallet,
107 | };
108 |
109 | if (amount) {
110 | approveSpendArgs.amount = BigNumber.from(amount);
111 | }
112 |
113 | let prom: Promise = Tokens.approveTokenSpend(approveSpendArgs);
114 |
115 | return (await expect(prom).to.eventually.be.rejected)
116 | });
117 | });
118 | });
119 |
120 | describe("Balance of test", function(this: Mocha.Suite) {
121 | it("should have an nUSD balance greater than zero", async function(this: Mocha.Context) {
122 | this.timeout(DEFAULT_TEST_TIMEOUT);
123 |
124 | let
125 | res: BigNumber,
126 | prom: Promise = balanceOf(testAddr, tokenParams(ChainId.BSC));
127 |
128 | try {
129 | res = await prom;
130 | } catch (err) {
131 | return (await expect(prom, (err as Error).message).to.not.be.rejected)
132 | }
133 |
134 | return expectNotZero(res)
135 | });
136 | });
137 |
138 | describe("allowanceOf test", function(this: Mocha.Suite) {
139 | it("synapsebridgezap should have an nUSD allowance gte zero", async function(this: Mocha.Context) {
140 | this.timeout(DEFAULT_TEST_TIMEOUT);
141 |
142 | let
143 | res: BigNumber,
144 | prom: Promise = allowanceOf(
145 | testAddr,
146 | SynapseContracts.contractsForChainId(ChainId.BSC).bridgeZapAddress,
147 | tokenParams(ChainId.BSC)
148 | );
149 |
150 | try {
151 | res = await prom;
152 | } catch (err) {
153 | return (await expect(prom, (err as Error).message).to.not.be.rejected)
154 | }
155 |
156 | return expectGteZero(res)
157 | });
158 | });
159 | });
--------------------------------------------------------------------------------
/test/explorer/explorer-test.ts:
--------------------------------------------------------------------------------
1 | import { expect } from "chai";
2 | import { getBridgeTxnInfo } from "@sdk/explorer/index.js";
3 |
4 | describe("Explorer tests", function (this: Mocha.Suite) {
5 | it("Query tests", async () => {
6 | interface TestCase {
7 | expected: JSON; // the expected JSON output
8 | chainId?: number;
9 | address?: string;
10 | txnHash?: string;
11 | kappa?: string;
12 | }
13 |
14 | const testCases: TestCase[] = [
15 | {
16 | expected: JSON.parse(
17 | '{"data":{"bridgeTransactions":[{"kappa":"0x5f102aacb2bb0f900df542e7d736c186de3838c762eb0a953e6f0a834243da5b","toInfo":{"txnHash":"0x90031dfcf2d5bb98d0df6c43cf0241701fd08b94c74d958168ed8aa5a5b24f4a","chainId":42161,"value":"580205100363451049","USDValue":637.59058613517,"formattedValue":0.5802051003634511,"time":1656623480},"fromInfo":{"txnHash":"0x44bc91e3cb5d6694a169b661a714cef9dc9ae0e6973ad4371d22701de20592fc","chainId":1,"value":"585000000000000000","USDValue":642.8597278021624,"formattedValue":0.585,"time":1656623433}}]}}'
18 | ),
19 |
20 | kappa:
21 | "0x5f102aacb2bb0f900df542e7d736c186de3838c762eb0a953e6f0a834243da5b",
22 | },
23 | {
24 | expected: JSON.parse(
25 | '{"data":{"bridgeTransactions":[{"kappa":"0x5f102aacb2bb0f900df542e7d736c186de3838c762eb0a953e6f0a834243da5b","toInfo":{"txnHash":"0x90031dfcf2d5bb98d0df6c43cf0241701fd08b94c74d958168ed8aa5a5b24f4a","chainId":42161,"value":"580205100363451049","USDValue":637.59058613517,"formattedValue":0.5802051003634511,"time":1656623480},"fromInfo":{"txnHash":"0x44bc91e3cb5d6694a169b661a714cef9dc9ae0e6973ad4371d22701de20592fc","chainId":1,"value":"585000000000000000","USDValue":642.8597278021624,"formattedValue":0.585,"time":1656623433}}]}}'
26 | ),
27 |
28 | chainId: 42161,
29 | kappa:
30 | "0x5f102aacb2bb0f900df542e7d736c186de3838c762eb0a953e6f0a834243da5b",
31 | },
32 | {
33 | expected: JSON.parse('{"data":{"bridgeTransactions":[]}}'),
34 |
35 | chainId: 10,
36 | kappa:
37 | "0x5f102aacb2bb0f900df542e7d736c186de3838c762eb0a953e6f0a834243da5b",
38 | },
39 | {
40 | expected: JSON.parse(
41 | '{"data":{"bridgeTransactions":[{"kappa":"0x5f102aacb2bb0f900df542e7d736c186de3838c762eb0a953e6f0a834243da5b","toInfo":{"txnHash":"0x90031dfcf2d5bb98d0df6c43cf0241701fd08b94c74d958168ed8aa5a5b24f4a","chainId":42161,"value":"580205100363451049","USDValue":637.59058613517,"formattedValue":0.5802051003634511,"time":1656623480},"fromInfo":{"txnHash":"0x44bc91e3cb5d6694a169b661a714cef9dc9ae0e6973ad4371d22701de20592fc","chainId":1,"value":"585000000000000000","USDValue":642.8597278021624,"formattedValue":0.585,"time":1656623433}}]}}'
42 | ),
43 |
44 | chainId: 1,
45 | txnHash:
46 | "0x44bc91e3cb5d6694a169b661a714cef9dc9ae0e6973ad4371d22701de20592fc",
47 | kappa:
48 | "0x5f102aacb2bb0f900df542e7d736c186de3838c762eb0a953e6f0a834243da5b",
49 | },
50 | {
51 | expected: JSON.parse('{"data":{"bridgeTransactions":[]}}'),
52 |
53 | chainId: 1,
54 | txnHash: "0xabcdefghijklmnopqrstuvwxyz1234567890",
55 | kappa:
56 | "0x5f102aacb2bb0f900df542e7d736c186de3838c762eb0a953e6f0a834243da5b",
57 | },
58 | ];
59 |
60 | testCases.forEach(async (tc: TestCase) => {
61 | const query_result = await getBridgeTxnInfo(tc);
62 | it("Query test case", function (this: Mocha.Context) {
63 | expect(query_result).to.equal(tc.expected);
64 | });
65 | });
66 | });
67 | });
68 |
--------------------------------------------------------------------------------
/test/helpers/index.ts:
--------------------------------------------------------------------------------
1 | import {expect} from "chai";
2 |
3 | import {Zero} from "@ethersproject/constants";
4 | import {Wallet} from "@ethersproject/wallet";
5 |
6 | import {
7 | BigNumber,
8 | type BigNumberish
9 | } from "@ethersproject/bignumber";
10 |
11 | import type {Token} from "@sdk";
12 | import {rpcProviderForChain} from "@sdk/internal/rpcproviders";
13 |
14 | const TEN_BN: BigNumber = BigNumber.from(10);
15 |
16 | // Completely clean privkey with low balances.
17 | export const
18 | bridgeTestPrivkey1: string = "53354287e3023f0629b7a5e187aa1ca3458c4b7ff9d66a6e3f4b2e821aafded7",
19 | fakeWalletPrivKey: string = "0x8ab0e165c2ea461b01cdd49aec882d179dccdbdb5c85c3f9c94c448aa65c5ace";
20 |
21 | export const
22 | makeTimeout = (seconds: number): number => seconds * 1000,
23 | getActualWei = (n: BigNumber, decimals: number): BigNumber => n.mul(TEN_BN.pow(18 - decimals)),
24 | randomBigNumber = (max: number = 313337) => BigNumber.from(Math.floor(Math.random() * max));
25 |
26 | export const getTestAmount = (
27 | t: Token,
28 | c: number,
29 | amt?: BigNumberish
30 | ): BigNumber => t.etherToWei(amt ?? randomBigNumber(), c);
31 |
32 | export const makeWalletSignerWithProvider = (
33 | chainId: number,
34 | privKey: string
35 | ): Wallet => new Wallet(privKey, rpcProviderForChain(chainId));
36 |
37 | export function makeFakeWallet(chainId: number): Wallet {
38 | return new Wallet(fakeWalletPrivKey, rpcProviderForChain(chainId))
39 | }
40 |
41 |
42 | export const
43 | DEFAULT_TEST_TIMEOUT = makeTimeout(10),
44 | EXECUTORS_TEST_TIMEOUT = makeTimeout(180);
45 |
46 | const
47 | expectTo = (data: any): Chai.Assertion => expect(data).to,
48 | expectToNot = (data: any): Chai.Assertion => expectTo(data).not,
49 | expectToBe = (data: any): Chai.Assertion => expectTo(data).be,
50 | expectToNotBe = (data: any): Chai.Assertion => expectToNot(data).be,
51 | expectToEventuallyBe = (data: Promise): Chai.PromisedAssertion => expectTo(data).eventually.be;
52 |
53 | const
54 | toOrNotTo = (data: any, wantTo: boolean): Chai.Assertion => wantTo ? expectTo(data) : expectToNot(data),
55 | toBeOrNotToBe = (data: any, wantBe: boolean): Chai.Assertion => wantBe ? expectToBe(data) : expectToNotBe(data);
56 |
57 | export const
58 | expectFulfilled = (data: Promise): Chai.PromisedAssertion => expectToEventuallyBe(data).fulfilled,
59 | expectRejected = (data: Promise): Chai.PromisedAssertion => expectToEventuallyBe(data).rejected,
60 | expectPromiseResolve = (
61 | data: Promise,
62 | wantResolve: boolean
63 | ): Chai.PromisedAssertion => wantResolve ? expectFulfilled(data) : expectRejected(data),
64 | expectNothingFromPromise = async (data: Promise): Promise => {
65 | let promReturned: boolean = false;
66 |
67 | if (!data) {
68 | return expectToEventuallyBe(data).undefined
69 | }
70 |
71 | const promFn = (): void => { promReturned = true; }
72 |
73 | await Promise.resolve(data)
74 | .then(promFn)
75 | .catch(promFn);
76 |
77 | return expect(promReturned).to.be.true
78 | };
79 |
80 | export const
81 | expectBoolean = (data: boolean, want: boolean): Chai.Assertion => expectToBe(data)[want ? "true" : "false"],
82 | expectNull = (data: any, wantNull: boolean): Chai.Assertion => toBeOrNotToBe(data, wantNull).null,
83 | expectUndefined = (data: any, wantUndef: boolean): Chai.Assertion => toBeOrNotToBe(data, wantUndef).undefined,
84 | expectProperty = (data: any, want: string): Chai.Assertion => expectTo(data).have.property(want),
85 | expectEqual = (data: any, want: any, errMsg?: string): Chai.Assertion => expectTo(data).equal(want, errMsg),
86 | expectEqualArray = (data: any, want: any, errMsg?: string): Chai.Assertion => expect(data).to.have.all.members(want, errMsg),
87 | expectLength = (data: any[], want: number, errMsg?: string): Chai.Assertion => expectTo(data).have.a.lengthOf(want, errMsg),
88 | expectIncludes = (
89 | data: any,
90 | check: any,
91 | wantIncludes: boolean,
92 | errMsg?: string
93 | ): Chai.Assertion => toOrNotTo(data, wantIncludes).include(check, errMsg);
94 |
95 | export const
96 | expectGt = (data: BigNumber, want: BigNumberish): Chai.Assertion => expectToBe(data).gt(want),
97 | expectGte = (data: BigNumber, want: BigNumberish): Chai.Assertion => expectToBe(data).gte(want),
98 | expectBnEqual = (data: BigNumber, want: BigNumberish): Chai.Assertion => expectTo(data).equal(want),
99 | expectGteZero = (data: BigNumber): Chai.Assertion => expectGte(data, Zero),
100 | expectNotZero = (data: BigNumber): Chai.Assertion => expectGt(data, Zero);
101 |
102 | export function wrapExpect(expectFn: Chai.Assertion): Mocha.Func {
103 | return function(this: Mocha.Context): void {
104 | expect(expectFn);
105 | }
106 | }
107 |
108 | export async function wrapExpectAsync(expectFn: Chai.Assertion, prom: Promise, wantResolve: boolean=true): Promise {
109 | try {
110 | await prom;
111 | expect(expectFn)
112 | return
113 | } catch (err) {
114 | return (await (wantResolve
115 | ? expectFulfilled(prom)
116 | : expectRejected(prom)
117 | ))
118 | }
119 | }
--------------------------------------------------------------------------------
/test/synapsebridge/Slippages-test.ts:
--------------------------------------------------------------------------------
1 | import {expect} from "chai";
2 |
3 | import {Slippages} from "@sdk";
4 |
5 | import {expectEqual} from "@tests/helpers";
6 |
7 | import {BigNumber} from "@ethersproject/bignumber";
8 |
9 | describe("Slippages tests", function(this: Mocha.Suite) {
10 | interface TestCase {
11 | func: (BigNumber, string) => BigNumber;
12 | value: BigNumber;
13 | slippage: string;
14 | expected: BigNumber;
15 | add: boolean;
16 | }
17 |
18 | describe("_applySlippage", function(this: Mocha.Suite) {
19 | const makeTitle = (tc: TestCase): string =>
20 | `value of ${tc.value.toString()} with slippage of ${tc.slippage} should return ${tc.expected.toString()}`;
21 |
22 | [
23 | {
24 | value: BigNumber.from(69420),
25 | slippage: "foo", // This should default to 1% slippage -> (value - value * 0.01).
26 | expected: BigNumber.from(70114),
27 | add: true,
28 | },
29 | {
30 | value: BigNumber.from(1337),
31 | slippage: "TWO_TENTH",
32 | expected: BigNumber.from(1339),
33 | add: true,
34 | },
35 | {
36 | value: BigNumber.from(101),
37 | slippage: Slippages.Quarter,
38 | expected: BigNumber.from(103),
39 | add: true,
40 | },
41 | {
42 | value: BigNumber.from(69420),
43 | slippage: "foo", // This should default to 1% slippage -> (value - value * 0.01).
44 | expected: BigNumber.from(68725),
45 | add: false,
46 | },
47 | {
48 | value: BigNumber.from(1337),
49 | slippage: "TWO_TENTH",
50 | expected: BigNumber.from(1334),
51 | add: false,
52 | },
53 | {
54 | value: BigNumber.from(101),
55 | slippage: Slippages.Quarter,
56 | expected: BigNumber.from(98),
57 | add: false,
58 | }
59 | ].forEach((tc: TestCase) => {
60 | it(makeTitle(tc), function(this: Mocha.Context) {
61 | expect(Slippages._applySlippage(tc.value, tc.slippage, tc.add)).to.equal(tc.expected);
62 | });
63 | });
64 | });
65 |
66 | describe("addSlippage", function(this: Mocha.Suite) {
67 | const mainValue = BigNumber.from(69420);
68 | const want1 = BigNumber.from(69489);
69 | let ret: BigNumber;
70 |
71 | it(`Slippages.addSlippage(${mainValue.toString()}, Slippages.OneTenth) should return ${want1.toString()}`, function(this: Mocha.Context) {
72 | ret = Slippages.addSlippage(mainValue, Slippages.OneTenth);
73 | expect(ret).to.equal(want1);
74 | });
75 |
76 | it("_applySlippage with add=true vs add=false", function(this: Mocha.Context) {
77 | const
78 | ret1 = Slippages._applySlippage(mainValue, Slippages.OneTenth),
79 | subVal = ret.sub(ret1),
80 | want = BigNumber.from('0x8b');
81 |
82 | expect(subVal).to.equal(want);
83 | });
84 | });
85 |
86 | describe("formatSlippageToString", function(this: Mocha.Suite) {
87 | interface formatTestCase {
88 | slippage: string,
89 | slippageName: string,
90 | want: string,
91 | }
92 |
93 | [
94 | {slippage: Slippages.One, slippageName: "Slippages.One", want: "1.0"},
95 | {slippage: Slippages.OneTenth, slippageName: "Slippages.OneTenth", want: "0.1"},
96 | {slippage: Slippages.TwoTenth, slippageName: "Slippages.TwoTenth", want: "0.2"},
97 | {slippage: Slippages.Quarter, slippageName: "Slippages.Quarter", want: "2.0"},
98 | {slippage: "foo", slippageName: "foo", want: "N/A"},
99 | ].forEach((tc: formatTestCase) => {
100 | const testTitle: string = `${tc.slippageName} should return ${tc.want}`;
101 |
102 | it(testTitle, function(this: Mocha.Context) {
103 | expectEqual(Slippages.formatSlippageToString(tc.slippage), tc.want);
104 | });
105 | });
106 | });
107 | });
108 |
--------------------------------------------------------------------------------
/test/synapsebridge/bridge_test_utils.ts:
--------------------------------------------------------------------------------
1 | import dotenv from "dotenv";
2 |
3 | import {
4 | Networks,
5 | type Token
6 | } from "@sdk";
7 |
8 | import {BigNumber} from "@ethersproject/bignumber";
9 | import {ethers} from "ethers";
10 |
11 | dotenv.config();
12 |
13 | const makeTestPrivkeyEnvKeys = (name: string): [string, string] => {
14 | const k: string = name + "_PRIVKEY";
15 | return [k, k + "_ADDRESS"]
16 | }
17 |
18 | export interface TestPrivKey {
19 | address: string;
20 | privkey: string;
21 | }
22 |
23 | function loadTestPrivkey(name: string): TestPrivKey {
24 | const [privkeyEnv, addrEnv] = makeTestPrivkeyEnvKeys(name);
25 |
26 | // TODO: this breaks tests that require an externally approval locally.
27 | const testWallet = ethers.Wallet.createRandom()
28 |
29 | return {
30 | address: process.env[addrEnv] || testWallet.address,
31 | privkey: process.env[privkeyEnv] || testWallet.privateKey,
32 | }
33 | }
34 |
35 | export const
36 | infiniteApprovalsPrivkey: TestPrivKey = loadTestPrivkey("INFINITE_APPROVALS"),
37 | bridgeInteractionsPrivkey: TestPrivKey = loadTestPrivkey("BRIDGE_INTERACTIONS");
38 |
39 | export interface BridgeSwapTestArgs {
40 | chainIdFrom: number;
41 | chainIdTo: number;
42 | tokenFrom: Token;
43 | tokenTo: Token;
44 | amountFrom: BigNumber;
45 | execute?: boolean;
46 | }
47 |
48 | export interface BridgeSwapTestCase {
49 | args: BridgeSwapTestArgs;
50 | expected: T;
51 | }
52 |
53 | export function makeBridgeSwapTestCase(
54 | chainIdFrom: number|Networks.Network,
55 | tokenFrom: Token,
56 | chainIdTo: number|Networks.Network,
57 | tokenTo: Token,
58 | expected: T,
59 | amountFrom: BigNumber=BigNumber.from(0),
60 | execute: boolean=true,
61 | ): BridgeSwapTestCase {
62 | const
63 | c1 = chainIdFrom instanceof Networks.Network ? chainIdFrom.chainId : chainIdFrom,
64 | c2 = chainIdTo instanceof Networks.Network ? chainIdTo.chainId : chainIdTo;
65 |
66 | return {args: {chainIdFrom: c1, tokenFrom, chainIdTo: c2, tokenTo, amountFrom, execute}, expected}
67 | }
--------------------------------------------------------------------------------
/test/test_setup.ts:
--------------------------------------------------------------------------------
1 | import "dotenv/config";
2 |
3 | import chai from "chai";
4 | import {waffleChai} from "@ethereum-waffle/chai"
5 | import chaiAsPromised from "chai-as-promised";
6 | import chaiThings from "chai-things";
7 |
8 | export const mochaHooks = {
9 | beforeAll() {
10 | chai.should();
11 |
12 | chai.config.truncateThreshold = 0;
13 |
14 | chai.use(chaiThings);
15 | chai.use(waffleChai);
16 | chai.use(chaiAsPromised);
17 |
18 | // Do nothing; we test these all the time.
19 | process.on("unhandledRejection", () => {});
20 | process.on("rejectionHandled", () => {});
21 | }
22 | }
--------------------------------------------------------------------------------
/test/tokenswap/SwapRate-test.ts:
--------------------------------------------------------------------------------
1 | import {expect} from "chai";
2 |
3 | import {step} from "mocha-steps";
4 |
5 | import {
6 | type Token,
7 | ChainId,
8 | Networks,
9 | Tokens,
10 | TokenSwap,
11 | UnsupportedSwapErrors
12 | } from "@sdk";
13 |
14 | import {rejectPromise} from "@sdk/common/utils";
15 |
16 | import {
17 | DEFAULT_TEST_TIMEOUT,
18 | getTestAmount
19 | } from "@tests/helpers";
20 |
21 | import {Zero} from "@ethersproject/constants";
22 | import {BigNumber} from "@ethersproject/bignumber";
23 | import {BaseContract} from "@ethersproject/contracts";
24 |
25 |
26 | import UnsupportedSwapError = UnsupportedSwapErrors.UnsupportedSwapError;
27 |
28 |
29 | describe("TokenSwap -- Asynchronous Tests", function(this: Mocha.Suite) {
30 | describe("calculateSwapRate() tests", function(this: Mocha.Suite) {
31 | interface TestCase {
32 | chainId: number;
33 | tokenFrom: Token;
34 | tokenTo: Token;
35 | amountIn: BigNumber;
36 | wantError: boolean;
37 | }
38 |
39 | const makeTestCase = (c: number, t1: Token, t2: Token, amt?: string, wantError?: boolean): TestCase =>
40 | ({
41 | chainId: c,
42 | tokenFrom: t1,
43 | tokenTo: t2,
44 | amountIn: getTestAmount(t1, c, amt),
45 | wantError: wantError ?? false,
46 | });
47 |
48 | const testCases: TestCase[] = [
49 | makeTestCase(ChainId.ETH, Tokens.DAI, Tokens.USDC),
50 | makeTestCase(ChainId.ETH, Tokens.ETH, Tokens.NETH, null, true),
51 | makeTestCase(ChainId.OPTIMISM, Tokens.WETH, Tokens.NETH),
52 | makeTestCase(ChainId.BSC, Tokens.BUSD, Tokens.USDT),
53 | makeTestCase(ChainId.BSC, Tokens.NUSD, Tokens.BUSD),
54 | makeTestCase(ChainId.BSC, Tokens.NUSD, Tokens.DAI, null, true),
55 | makeTestCase(ChainId.ARBITRUM, Tokens.NEWO, Tokens.UST, null, true),
56 | makeTestCase(ChainId.AVALANCHE, Tokens.DAI, Tokens.USDT),
57 | makeTestCase(ChainId.CRONOS, Tokens.NUSD, Tokens.USDC),
58 | ];
59 |
60 | testCases.forEach((tc: TestCase) => {
61 | const
62 | titleSuffix: string = tc.wantError ? "should fail" : "should pass",
63 | tokFrom: string = tc.tokenFrom.symbol,
64 | tokTo: string = tc.tokenTo.symbol,
65 | testTitle: string = `for ${tokFrom} => ${tokTo} on ${Networks.networkName(tc.chainId)} ${titleSuffix}`,
66 | testTitle1: string = `calculateSwapRate ${testTitle}`,
67 | testTitle2: string = `buildSwapTokensTransaction ${testTitle}`,
68 | testTitle3: string = `swapSetup ${testTitle}`;
69 |
70 | let amountOut: BigNumber;
71 |
72 | step(testTitle1, async function(this: Mocha.Context) {
73 | this.timeout(DEFAULT_TEST_TIMEOUT);
74 |
75 | let prom: Promise = Promise.resolve(TokenSwap.calculateSwapRate({
76 | chainId: tc.chainId,
77 | tokenFrom: tc.tokenFrom,
78 | tokenTo: tc.tokenTo,
79 | amountIn: tc.amountIn,
80 | }))
81 | .then((res) => {
82 | amountOut = res.amountOut;
83 | return res
84 | })
85 | .catch(rejectPromise)
86 |
87 | if (tc.wantError) {
88 | let err: Error;
89 | try {
90 | await prom;
91 | } catch (e) {
92 | err = e;
93 | }
94 |
95 | if (err instanceof UnsupportedSwapError) {
96 | expect(err).to.be.an.instanceof(UnsupportedSwapError).and.to.haveOwnProperty("errorKind");
97 | }
98 |
99 | return (await expect(prom).to.eventually.be.rejected)
100 | }
101 |
102 | return (await expect(prom).to.eventually
103 | .haveOwnProperty("amountOut")
104 | .that.is.an.instanceOf(BigNumber)
105 | .and.is.gt(Zero.toNumber())
106 | )
107 | });
108 |
109 | step(testTitle2, async function(this: Mocha.Context) {
110 | this.timeout(DEFAULT_TEST_TIMEOUT);
111 |
112 | const args: TokenSwap.SwapTokensParams = {
113 | ...tc,
114 | minAmountOut: amountOut,
115 | };
116 |
117 | let prom = TokenSwap.buildSwapTokensTransaction(args);
118 |
119 | try {
120 | await prom;
121 | } catch (e) {
122 | if (tc.wantError) {
123 | return (await expect(prom).to.eventually.be.rejected);
124 | }
125 | }
126 |
127 | return (await expect(prom).to.eventually.be.fulfilled)
128 | });
129 |
130 | step(testTitle3, async function(this: Mocha.Context) {
131 | this.timeout(DEFAULT_TEST_TIMEOUT);
132 |
133 | let prom = TokenSwap.swapSetup(
134 | tc.tokenFrom,
135 | tc.tokenTo,
136 | tc.chainId,
137 | );
138 |
139 | try {
140 | let res = await prom;
141 | expect(res).to.have.property("swapInstance");
142 | expect(res.swapInstance).to.be.an.instanceof(BaseContract);
143 | return
144 | } catch (e) {
145 | if (tc.wantError) {
146 | return (await expect(prom).to.eventually.be.rejected);
147 | } else {
148 | expect.fail(e);
149 | }
150 | }
151 | });
152 | });
153 | });
154 | });
--------------------------------------------------------------------------------
/test/tokenswap/checkTokenAllowance-test.ts:
--------------------------------------------------------------------------------
1 | import {expect} from "chai";
2 | import {ChainId, SwapPools, Token, Tokens} from "@sdk";
3 | import {BigNumber} from "@ethersproject/bignumber";
4 |
5 |
6 | describe("TokenSwap -- checkTokenAllowance tests", function(this: Mocha.Suite) {
7 | interface TestCase {
8 | owner: string;
9 | spender: string;
10 | token: Token;
11 | chainId: number;
12 | }
13 |
14 | function makeTestTitle(tc: TestCase): string {
15 | const
16 | testSuffix: string = "should not fail",
17 | testParams: string = `{owner: ${tc.owner}, spender: ${tc.spender}, token: ${tc.token.name}, chainId: ${tc.chainId}}`;
18 |
19 | return `checkTokenAllowance(${testParams}) ${testSuffix}`
20 | }
21 |
22 | const testCases: TestCase[] = [
23 | {
24 | owner: "0xe972647539816442e0987817DF777a9fd9878650",
25 | spender: "0x7145a092158c215ff10cce4ddcb84b3a090bdd4e",
26 | token: SwapPools.AVALANCHE_POOL_SWAP_TOKEN.baseToken,
27 | chainId: ChainId.AVALANCHE
28 | },
29 | ];
30 |
31 | testCases.forEach(tc => {
32 | const testTitle: string = makeTestTitle(tc);
33 |
34 | it(testTitle, async function(this: Mocha.Context) {
35 | this.timeout(8 * 1000);
36 | this.slow(2 * 1000);
37 |
38 | const gotProm: Promise = Tokens.checkTokenAllowance(tc);
39 |
40 | return (await expect(gotProm).to.eventually.be.fulfilled)
41 | });
42 | });
43 | });
--------------------------------------------------------------------------------
/test/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "baseUrl": "./",
5 | "module": "ESNext",
6 | "paths": {
7 | "@tests/setup": ["./test_setup"],
8 | "@tests/helpers": ["./helpers"],
9 | "@sdk": ["../src/index"],
10 | "@sdk/*": ["../src/*"],
11 | "@bridge": ["../src/bridge"],
12 | "@bridge/*": ["../src/bridge/*"],
13 | "@common/*": ["../src/common/*"],
14 | "@chainid": ["../src/common/chainid"],
15 | "@networks": ["../src/common/networks"],
16 | "@utils": ["../src/common/utils"],
17 | "@synapsecontracts": ["../src/common/synapse_contracts"],
18 | "@entities": ["../src/entities"],
19 | "@contracts": ["../src/contracts"],
20 | "@internal/*": ["../src/internal/*"],
21 | "@swappools": ["../src/swappools"],
22 | "@token": ["../src/token"],
23 | "@tokens": ["../src/tokens"],
24 | "@tokenswap": ["../src/tokenswap"],
25 | },
26 | "types": [
27 | "node",
28 | "chai",
29 | "mocha"
30 | ],
31 | "plugins": [
32 | { "transform": "typescript-transform-paths" } // Transform paths in output .js files
33 | ]
34 | },
35 | "exclude": [
36 | "**/node_modules/**/*",
37 | "**/dist/**/*",
38 | "**/examples/**/*",
39 | "../src/hooks/**/*"
40 | ],
41 | "include": [
42 | "../src",
43 | "./"
44 | ]
45 | }
--------------------------------------------------------------------------------
/tsconfig.build.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "compilerOptions": {
4 | "stripInternal": true,
5 | "plugins": [
6 | {
7 | "transform": "typescript-transform-paths"
8 | }, // Transform paths in output .js files
9 | {
10 | "transform": "typescript-transform-paths",
11 | "afterDeclarations": true
12 | }, // Transform paths in output .d.ts files (Include this line if you output declarations files)
13 | {
14 | "transform": "@zoltu/typescript-transformer-append-js-extension/output/index.js",
15 | "after": true
16 | }
17 | ]
18 | }
19 | }
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "baseUrl": "./",
4 | "paths": {
5 | "@hooks/*": ["./src/hooks/*"],
6 | "@bridge": ["./src/bridge"],
7 | "@bridge/*": ["./src/bridge/*"],
8 | "@common/*": ["./src/common/*"],
9 | "@chainid": ["./src/common/chainid"],
10 | "@networks": ["./src/common/networks"],
11 | "@utils": ["./src/common/utils"],
12 | "@synapsecontracts": ["./src/common/synapse_contracts"],
13 | "@entities": ["./src/entities"],
14 | "@contracts": ["./src/contracts"],
15 | "@internal/*": ["./src/internal/*"],
16 | "@swappools": ["./src/swappools"],
17 | "@token": ["./src/token"],
18 | "@tokens": ["./src/tokens"],
19 | "@tokenswap": ["./src/tokenswap"]
20 | },
21 | "target": "ES5",
22 | "module": "ESNext",
23 | "outDir": "dist",
24 | "strict": false,
25 | "esModuleInterop": true,
26 | "resolveJsonModule": true,
27 | "moduleResolution": "node",
28 | "declaration": true,
29 | "removeComments": false,
30 | "types": [
31 | "node"
32 | ],
33 | "lib": [
34 | "es2017",
35 | "esnext",
36 | "dom",
37 | "dom.iterable"
38 | ],
39 | "isolatedModules": true,
40 | "skipLibCheck": true,
41 | "jsx": "react-jsx"
42 | },
43 | "include": [
44 | "src/**/*"
45 | ],
46 | "exclude": [
47 | "node_modules",
48 | "dist",
49 | "test",
50 | "examples"
51 | ]
52 | }
--------------------------------------------------------------------------------