From 7d64e40b93056710b7f8c189ffb28b4692bb05ea Mon Sep 17 00:00:00 2001 From: Evan Gray Date: Wed, 2 Mar 2022 20:11:00 +0000 Subject: [PATCH] karura and acala testnet support --- bridge_ui/package-lock.json | 34 ++-- bridge_ui/package.json | 2 +- bridge_ui/src/components/Attest/Create.tsx | 2 - .../src/hooks/useHandleCreateWrapped.tsx | 30 +++- bridge_ui/src/icons/acala.svg | 162 ++++++++++++++++++ bridge_ui/src/icons/karura.svg | 28 +++ bridge_ui/src/utils/consts.ts | 102 ++++++++++- bridge_ui/src/utils/karura.ts | 22 +++ 8 files changed, 353 insertions(+), 29 deletions(-) create mode 100644 bridge_ui/src/icons/acala.svg create mode 100644 bridge_ui/src/icons/karura.svg create mode 100644 bridge_ui/src/utils/karura.ts diff --git a/bridge_ui/package-lock.json b/bridge_ui/package-lock.json index ef425c91b..b0e1c89fb 100644 --- a/bridge_ui/package-lock.json +++ b/bridge_ui/package-lock.json @@ -8,7 +8,7 @@ "name": "test_ui", "version": "0.1.0", "dependencies": { - "@certusone/wormhole-sdk": "^0.2.0", + "@certusone/wormhole-sdk": "^0.2.1", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.11.2", "@material-ui/lab": "^4.0.0-alpha.60", @@ -67,15 +67,14 @@ }, "../sdk/js": { "name": "@certusone/wormhole-sdk", - "version": "0.1.5", + "version": "0.2.1", "extraneous": true, "license": "Apache-2.0", "dependencies": { "@improbable-eng/grpc-web": "^0.14.0", "@solana/spl-token": "^0.1.8", "@solana/web3.js": "^1.24.0", - "@terra-money/terra.js": "^2.0.14", - "@terra-money/wallet-provider": "^2.2.0", + "@terra-money/terra.js": "^3.0.7", "axios": "^0.24.0", "bech32": "^2.0.0", "js-base64": "^3.6.1", @@ -97,7 +96,8 @@ "ts-jest": "^27.0.7", "tslint": "^6.1.3", "tslint-config-prettier": "^1.18.0", - "typescript": "^4.3.5" + "typescript": "^4.3.5", + "web3": "^1.6.1" } }, "node_modules/@apollo/client": { @@ -2006,9 +2006,9 @@ } }, "node_modules/@certusone/wormhole-sdk": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@certusone/wormhole-sdk/-/wormhole-sdk-0.2.0.tgz", - "integrity": "sha512-M5DnyPbt8Wm2gSG596yH3Fw1cXulQSzJ/4b1wVeQBrZ4g2s0ztSLgSctUGTGwR4wacK5R1IeGo9jfn29KBmdwA==", + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@certusone/wormhole-sdk/-/wormhole-sdk-0.2.1.tgz", + "integrity": "sha512-L85tiUHwnH4nbUEDgQtS2hNm3Q0IsUP29Z/DGbN2zggdvR0KTC6nLQ+LufCM6IcdUQYpYuwXjOYKD1Et8qc0mw==", "dependencies": { "@improbable-eng/grpc-web": "^0.14.0", "@solana/spl-token": "^0.1.8", @@ -2046,9 +2046,9 @@ "integrity": "sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg==" }, "node_modules/@certusone/wormhole-sdk/node_modules/rxjs": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.4.tgz", - "integrity": "sha512-h5M3Hk78r6wAheJF0a5YahB1yRQKCsZ4MsGdZ5O9ETbVtjPcScGfrMmoOq7EBsCRzd4BDkvDJ7ogP8Sz5tTFiQ==", + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.5.tgz", + "integrity": "sha512-sy+H0pQofO95VDmFLzyaw9xNJU4KTRSwQIGM6+iG3SypAtCiLDzpeG8sJrNCWn2Up9km+KhkvTdbkrdy+yzZdw==", "dependencies": { "tslib": "^2.1.0" } @@ -46140,9 +46140,9 @@ } }, "@certusone/wormhole-sdk": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@certusone/wormhole-sdk/-/wormhole-sdk-0.2.0.tgz", - "integrity": "sha512-M5DnyPbt8Wm2gSG596yH3Fw1cXulQSzJ/4b1wVeQBrZ4g2s0ztSLgSctUGTGwR4wacK5R1IeGo9jfn29KBmdwA==", + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@certusone/wormhole-sdk/-/wormhole-sdk-0.2.1.tgz", + "integrity": "sha512-L85tiUHwnH4nbUEDgQtS2hNm3Q0IsUP29Z/DGbN2zggdvR0KTC6nLQ+LufCM6IcdUQYpYuwXjOYKD1Et8qc0mw==", "requires": { "@improbable-eng/grpc-web": "^0.14.0", "@solana/spl-token": "^0.1.8", @@ -46177,9 +46177,9 @@ "integrity": "sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg==" }, "rxjs": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.4.tgz", - "integrity": "sha512-h5M3Hk78r6wAheJF0a5YahB1yRQKCsZ4MsGdZ5O9ETbVtjPcScGfrMmoOq7EBsCRzd4BDkvDJ7ogP8Sz5tTFiQ==", + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.5.tgz", + "integrity": "sha512-sy+H0pQofO95VDmFLzyaw9xNJU4KTRSwQIGM6+iG3SypAtCiLDzpeG8sJrNCWn2Up9km+KhkvTdbkrdy+yzZdw==", "requires": { "tslib": "^2.1.0" } diff --git a/bridge_ui/package.json b/bridge_ui/package.json index 3b42ef99f..9b75ee0d3 100644 --- a/bridge_ui/package.json +++ b/bridge_ui/package.json @@ -3,7 +3,7 @@ "version": "0.1.0", "private": true, "dependencies": { - "@certusone/wormhole-sdk": "^0.2.0", + "@certusone/wormhole-sdk": "^0.2.1", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.11.2", "@material-ui/lab": "^4.0.0-alpha.60", diff --git a/bridge_ui/src/components/Attest/Create.tsx b/bridge_ui/src/components/Attest/Create.tsx index 65912a2f1..84ca24510 100644 --- a/bridge_ui/src/components/Attest/Create.tsx +++ b/bridge_ui/src/components/Attest/Create.tsx @@ -42,8 +42,6 @@ function Create() { shouldUpdate || false ); - console.log("foreign asset info", foreignAssetInfo); - return ( <> diff --git a/bridge_ui/src/hooks/useHandleCreateWrapped.tsx b/bridge_ui/src/hooks/useHandleCreateWrapped.tsx index 79054b305..b23719d87 100644 --- a/bridge_ui/src/hooks/useHandleCreateWrapped.tsx +++ b/bridge_ui/src/hooks/useHandleCreateWrapped.tsx @@ -1,16 +1,19 @@ import { ChainId, + CHAIN_ID_ACALA, + CHAIN_ID_KARURA, CHAIN_ID_SOLANA, CHAIN_ID_TERRA, createWrappedOnEth, createWrappedOnSolana, createWrappedOnTerra, - updateWrappedOnEth, - updateWrappedOnTerra, - updateWrappedOnSolana, - postVaaSolanaWithRetry, isEVMChain, + postVaaSolanaWithRetry, + updateWrappedOnEth, + updateWrappedOnSolana, + updateWrappedOnTerra, } from "@certusone/wormhole-sdk"; +import { Alert } from "@material-ui/lab"; import { WalletContextState } from "@solana/wallet-adapter-react"; import { Connection } from "@solana/web3.js"; import { @@ -23,7 +26,6 @@ import { useCallback, useMemo } from "react"; import { useDispatch, useSelector } from "react-redux"; import { useEthereumProvider } from "../contexts/EthereumProviderContext"; import { useSolanaWallet } from "../contexts/SolanaWalletContext"; -import useAttestSignedVAA from "./useAttestSignedVAA"; import { setCreateTx, setIsCreating } from "../store/attestSlice"; import { selectAttestIsCreating, @@ -31,17 +33,20 @@ import { selectTerraFeeDenom, } from "../store/selectors"; import { + ACALA_HOST, getTokenBridgeAddressForChain, + KARURA_HOST, MAX_VAA_UPLOAD_RETRIES_SOLANA, SOLANA_HOST, SOL_BRIDGE_ADDRESS, SOL_TOKEN_BRIDGE_ADDRESS, TERRA_TOKEN_BRIDGE_ADDRESS, } from "../utils/consts"; +import { getKaruraGasParams } from "../utils/karura"; import parseError from "../utils/parseError"; import { signSendAndConfirm } from "../utils/solana"; -import { Alert } from "@material-ui/lab"; import { postWithFees } from "../utils/terra"; +import useAttestSignedVAA from "./useAttestSignedVAA"; async function evm( dispatch: any, @@ -53,16 +58,25 @@ async function evm( ) { dispatch(setIsCreating(true)); try { + // Karura and Acala need gas params for contract deploys + const overrides = + chainId === CHAIN_ID_KARURA + ? await getKaruraGasParams(KARURA_HOST) + : chainId === CHAIN_ID_ACALA + ? await getKaruraGasParams(ACALA_HOST) + : {}; const receipt = shouldUpdate ? await updateWrappedOnEth( getTokenBridgeAddressForChain(chainId), signer, - signedVAA + signedVAA, + overrides ) : await createWrappedOnEth( getTokenBridgeAddressForChain(chainId), signer, - signedVAA + signedVAA, + overrides ); dispatch( setCreateTx({ id: receipt.transactionHash, block: receipt.blockNumber }) diff --git a/bridge_ui/src/icons/acala.svg b/bridge_ui/src/icons/acala.svg new file mode 100644 index 000000000..3cd2abb99 --- /dev/null +++ b/bridge_ui/src/icons/acala.svg @@ -0,0 +1,162 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/bridge_ui/src/icons/karura.svg b/bridge_ui/src/icons/karura.svg new file mode 100644 index 000000000..7eaa7809d --- /dev/null +++ b/bridge_ui/src/icons/karura.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + diff --git a/bridge_ui/src/utils/consts.ts b/bridge_ui/src/utils/consts.ts index 7e8692ce8..a1e547fea 100644 --- a/bridge_ui/src/utils/consts.ts +++ b/bridge_ui/src/utils/consts.ts @@ -1,10 +1,12 @@ import { ChainId, + CHAIN_ID_ACALA, CHAIN_ID_AVAX, CHAIN_ID_BSC, CHAIN_ID_ETH, CHAIN_ID_ETHEREUM_ROPSTEN, CHAIN_ID_FANTOM, + CHAIN_ID_KARURA, CHAIN_ID_OASIS, CHAIN_ID_POLYGON, CHAIN_ID_SOLANA, @@ -13,10 +15,12 @@ import { } from "@certusone/wormhole-sdk"; import { clusterApiUrl } from "@solana/web3.js"; import { getAddress } from "ethers/lib/utils"; +import acalaIcon from "../icons/acala.svg"; import avaxIcon from "../icons/avax.svg"; import bscIcon from "../icons/bsc.svg"; import ethIcon from "../icons/eth.svg"; import fantomIcon from "../icons/fantom.svg"; +import karuraIcon from "../icons/karura.svg"; import oasisIcon from "../icons/oasis-network-rose-logo.svg"; import polygonIcon from "../icons/polygon.svg"; import solanaIcon from "../icons/solana.svg"; @@ -80,6 +84,11 @@ export const CHAINS: ChainInfo[] = ] : CLUSTER === "testnet" ? [ + { + id: CHAIN_ID_ACALA, + name: "Acala", + logo: acalaIcon, + }, { id: CHAIN_ID_AVAX, name: "Avalanche", @@ -105,6 +114,11 @@ export const CHAINS: ChainInfo[] = name: "Fantom", logo: fantomIcon, }, + { + id: CHAIN_ID_KARURA, + name: "Karura", + logo: karuraIcon, + }, { id: CHAIN_ID_OASIS, name: "Oasis", @@ -158,7 +172,9 @@ export const CHAINS_WITH_NFT_SUPPORT = CHAINS.filter( id === CHAIN_ID_POLYGON || id === CHAIN_ID_OASIS || id === CHAIN_ID_SOLANA || - id === CHAIN_ID_FANTOM + id === CHAIN_ID_FANTOM || + id === CHAIN_ID_KARURA || + id === CHAIN_ID_ACALA ); export type ChainsById = { [key in ChainId]: ChainInfo }; export const CHAINS_BY_ID: ChainsById = CHAINS.reduce((obj, chain) => { @@ -184,6 +200,10 @@ export const getDefaultNativeCurrencySymbol = (chainId: ChainId) => ? "ROSE" : chainId === CHAIN_ID_FANTOM ? "FTM" + : chainId === CHAIN_ID_KARURA + ? "KAR" + : chainId === CHAIN_ID_ACALA + ? "ACA" : ""; export const getExplorerName = (chainId: ChainId) => chainId === CHAIN_ID_ETH || chainId === CHAIN_ID_ETHEREUM_ROPSTEN @@ -226,6 +246,10 @@ export const OASIS_NETWORK_CHAIN_ID = CLUSTER === "mainnet" ? 42262 : CLUSTER === "testnet" ? 42261 : 1381; export const FANTOM_NETWORK_CHAIN_ID = CLUSTER === "mainnet" ? 250 : CLUSTER === "testnet" ? 4002 : 1381; +export const KARURA_NETWORK_CHAIN_ID = + CLUSTER === "mainnet" ? 686 : CLUSTER === "testnet" ? 686 : 1381; +export const ACALA_NETWORK_CHAIN_ID = + CLUSTER === "mainnet" ? 787 : CLUSTER === "testnet" ? 787 : 1381; export const getEvmChainId = (chainId: ChainId) => chainId === CHAIN_ID_ETH ? ETH_NETWORK_CHAIN_ID @@ -241,6 +265,10 @@ export const getEvmChainId = (chainId: ChainId) => ? OASIS_NETWORK_CHAIN_ID : chainId === CHAIN_ID_FANTOM ? FANTOM_NETWORK_CHAIN_ID + : chainId === CHAIN_ID_KARURA + ? KARURA_NETWORK_CHAIN_ID + : chainId === CHAIN_ID_ACALA + ? ACALA_NETWORK_CHAIN_ID : undefined; export const SOLANA_HOST = process.env.REACT_APP_SOLANA_API_URL ? process.env.REACT_APP_SOLANA_API_URL @@ -268,6 +296,18 @@ export const TERRA_HOST = chainID: "columbus-5", name: "localterra", }; +export const KARURA_HOST = + CLUSTER === "mainnet" + ? "" + : CLUSTER === "testnet" + ? "http://103.253.145.222:8545" + : ""; +export const ACALA_HOST = + CLUSTER === "mainnet" + ? "" + : CLUSTER === "testnet" + ? "http://157.245.252.103:8545" + : ""; export const ETH_BRIDGE_ADDRESS = getAddress( CLUSTER === "mainnet" ? "0x98f3c9e6E3fAce36bAAd05FE09d375Ef1464288B" @@ -394,6 +434,48 @@ export const FANTOM_TOKEN_BRIDGE_ADDRESS = getAddress( ? "0x599CEa2204B4FaECd584Ab1F2b6aCA137a0afbE8" : "0x0290FB167208Af455bB137780163b7B7a9a10C16" ); +export const KARURA_BRIDGE_ADDRESS = getAddress( + CLUSTER === "mainnet" + ? "0x0000000000000000000000000000000000000000" + : CLUSTER === "testnet" + ? "0xE4eacc10990ba3308DdCC72d985f2a27D20c7d03" + : "0xC89Ce4735882C9F0f0FE26686c53074E09B0D550" +); +export const KARURA_NFT_BRIDGE_ADDRESS = getAddress( + CLUSTER === "mainnet" + ? "0x0000000000000000000000000000000000000000" + : CLUSTER === "testnet" + ? "0x0A693c2D594292B6Eb89Cb50EFe4B0b63Dd2760D" + : "0x26b4afb60d6c903165150c6f0aa14f8016be4aec" +); +export const KARURA_TOKEN_BRIDGE_ADDRESS = getAddress( + CLUSTER === "mainnet" + ? "0x0000000000000000000000000000000000000000" + : CLUSTER === "testnet" + ? "0xd11De1f930eA1F7Dd0290Fe3a2e35b9C91AEFb37" + : "0x0290FB167208Af455bB137780163b7B7a9a10C16" +); +export const ACALA_BRIDGE_ADDRESS = getAddress( + CLUSTER === "mainnet" + ? "0x0000000000000000000000000000000000000000" + : CLUSTER === "testnet" + ? "0x4377B49d559c0a9466477195C6AdC3D433e265c0" + : "0xC89Ce4735882C9F0f0FE26686c53074E09B0D550" +); +export const ACALA_NFT_BRIDGE_ADDRESS = getAddress( + CLUSTER === "mainnet" + ? "0x0000000000000000000000000000000000000000" + : CLUSTER === "testnet" + ? "0x96f1335e0AcAB3cfd9899B30b2374e25a2148a6E" + : "0x26b4afb60d6c903165150c6f0aa14f8016be4aec" +); +export const ACALA_TOKEN_BRIDGE_ADDRESS = getAddress( + CLUSTER === "mainnet" + ? "0x0000000000000000000000000000000000000000" + : CLUSTER === "testnet" + ? "0xebA00cbe08992EdD08ed7793E07ad6063c807004" + : "0x0290FB167208Af455bB137780163b7B7a9a10C16" +); export const SOL_BRIDGE_ADDRESS = CLUSTER === "mainnet" ? "worm2ZoG2kUd4vFXhvjh93UUH596ayRfgQ2MgjNMTth" @@ -472,6 +554,10 @@ export const getBridgeAddressForChain = (chainId: ChainId) => ? OASIS_BRIDGE_ADDRESS : chainId === CHAIN_ID_FANTOM ? FANTOM_BRIDGE_ADDRESS + : chainId === CHAIN_ID_KARURA + ? KARURA_BRIDGE_ADDRESS + : chainId === CHAIN_ID_ACALA + ? ACALA_BRIDGE_ADDRESS : ""; export const getNFTBridgeAddressForChain = (chainId: ChainId) => chainId === CHAIN_ID_SOLANA @@ -490,6 +576,10 @@ export const getNFTBridgeAddressForChain = (chainId: ChainId) => ? OASIS_NFT_BRIDGE_ADDRESS : chainId === CHAIN_ID_FANTOM ? FANTOM_NFT_BRIDGE_ADDRESS + : chainId === CHAIN_ID_KARURA + ? KARURA_NFT_BRIDGE_ADDRESS + : chainId === CHAIN_ID_ACALA + ? ACALA_NFT_BRIDGE_ADDRESS : ""; export const getTokenBridgeAddressForChain = (chainId: ChainId) => chainId === CHAIN_ID_SOLANA @@ -510,6 +600,10 @@ export const getTokenBridgeAddressForChain = (chainId: ChainId) => ? OASIS_TOKEN_BRIDGE_ADDRESS : chainId === CHAIN_ID_FANTOM ? FANTOM_TOKEN_BRIDGE_ADDRESS + : chainId === CHAIN_ID_KARURA + ? KARURA_TOKEN_BRIDGE_ADDRESS + : chainId === CHAIN_ID_ACALA + ? ACALA_TOKEN_BRIDGE_ADDRESS : ""; export const COVALENT_API_KEY = process.env.REACT_APP_COVALENT_API_KEY @@ -524,6 +618,8 @@ export const COVALENT_AVAX = CLUSTER === "devnet" ? 137 : AVAX_NETWORK_CHAIN_ID; export const COVALENT_OASIS = CLUSTER === "devnet" ? null : null; export const COVALENT_FANTOM = CLUSTER === "devnet" ? 250 : FANTOM_NETWORK_CHAIN_ID; +export const COVALENT_KARURA = CLUSTER === "devnet" ? null : null; +export const COVALENT_ACALA = CLUSTER === "devnet" ? null : null; export const COVALENT_GET_TOKENS_URL = ( chainId: ChainId, walletAddress: string, @@ -543,6 +639,10 @@ export const COVALENT_GET_TOKENS_URL = ( ? COVALENT_OASIS : chainId === CHAIN_ID_FANTOM ? COVALENT_FANTOM + : chainId === CHAIN_ID_KARURA + ? COVALENT_KARURA + : chainId === CHAIN_ID_ACALA + ? COVALENT_ACALA : ""; // https://www.covalenthq.com/docs/api/#get-/v1/{chain_id}/address/{address}/balances_v2/ return `https://api.covalenthq.com/v1/${chainNum}/address/${walletAddress}/balances_v2/?key=${COVALENT_API_KEY}${ diff --git a/bridge_ui/src/utils/karura.ts b/bridge_ui/src/utils/karura.ts new file mode 100644 index 000000000..bbca10ac6 --- /dev/null +++ b/bridge_ui/src/utils/karura.ts @@ -0,0 +1,22 @@ +import axios from "axios"; + +export async function getKaruraGasParams(rpc: string): Promise<{ + gasPrice: number; + gasLimit: number; +}> { + const gasLimit = 21000000; + const storageLimit = 64001; + const res = ( + await axios.post(rpc, { + id: 0, + jsonrpc: "2.0", + method: "eth_getEthGas", + params: [gasLimit, storageLimit], + }) + ).data.result; + + return { + gasLimit: parseInt(res.gasLimit, 16), + gasPrice: parseInt(res.gasPrice, 16), + }; +}