From 324e005b9d189bf4573b1fe6870a5eaf6da01a67 Mon Sep 17 00:00:00 2001 From: lbqds Date: Wed, 20 Nov 2024 12:10:18 +0800 Subject: [PATCH] Update wormhole sdk to 0.4.0-rc.0 --- bridge_ui/package-lock.json | 54 +- bridge_ui/package.json | 2 +- bridge_ui/src/App.js | 30 -- bridge_ui/src/components/Attest/Create.tsx | 5 - bridge_ui/src/components/Attest/Send.tsx | 6 +- .../components/Migration/EvmQuickMigrate.tsx | 392 -------------- .../src/components/Migration/EvmWorkflow.tsx | 247 --------- .../Migration/SolanaQuickMigrate.tsx | 383 ------------- .../components/Migration/SolanaWorkflow.tsx | 507 ------------------ bridge_ui/src/components/Migration/index.tsx | 134 ----- bridge_ui/src/components/NFT/Redeem.tsx | 6 +- bridge_ui/src/components/NFT/Send.tsx | 4 - .../src/components/NFTOriginVerifier.tsx | 51 +- bridge_ui/src/components/Recovery.tsx | 171 ------ bridge_ui/src/components/SmartAddress.tsx | 9 +- .../SolanaCreateAssociatedAddress.tsx | 20 +- .../src/components/TerraFeeDenomPicker.tsx | 107 ---- .../TokenSelectors/SourceTokenSelector.tsx | 9 - .../TokenSelectors/TerraTokenPicker.tsx | 168 ------ bridge_ui/src/components/Transfer/Redeem.tsx | 5 - bridge_ui/src/components/Transfer/Send.tsx | 10 +- .../src/components/WithdrawTokensTerra.tsx | 140 ----- .../src/hooks/useCheckIfWormholeWrapped.ts | 84 +-- bridge_ui/src/hooks/useFetchForeignAsset.ts | 51 +- bridge_ui/src/hooks/useFetchTargetAsset.ts | 129 +---- .../src/hooks/useGetIsTransferCompleted.ts | 75 +-- .../hooks/useGetTargetParsedTokenAccounts.ts | 86 +-- bridge_ui/src/hooks/useHandleAttest.tsx | 213 +------- .../src/hooks/useHandleCreateWrapped.tsx | 203 +------ bridge_ui/src/hooks/useHandleNFTRedeem.tsx | 124 +---- bridge_ui/src/hooks/useHandleNFTTransfer.tsx | 130 +---- bridge_ui/src/hooks/useHandleRedeem.tsx | 238 +------- bridge_ui/src/hooks/useHandleTransfer.tsx | 336 +----------- bridge_ui/src/hooks/useOriginalAsset.ts | 45 +- .../hooks/useSolanaMigratorInformation.tsx | 430 --------------- bridge_ui/src/hooks/useSyncTargetAddress.ts | 14 - bridge_ui/src/hooks/useTransactionFees.tsx | 43 -- bridge_ui/src/utils/algorand.ts | 46 -- bridge_ui/src/utils/postVaa.ts | 210 -------- bridge_ui/src/utils/terra.ts | 108 ---- 40 files changed, 56 insertions(+), 4969 deletions(-) delete mode 100644 bridge_ui/src/components/Migration/EvmQuickMigrate.tsx delete mode 100644 bridge_ui/src/components/Migration/EvmWorkflow.tsx delete mode 100644 bridge_ui/src/components/Migration/SolanaQuickMigrate.tsx delete mode 100644 bridge_ui/src/components/Migration/SolanaWorkflow.tsx delete mode 100644 bridge_ui/src/components/Migration/index.tsx delete mode 100644 bridge_ui/src/components/TerraFeeDenomPicker.tsx delete mode 100644 bridge_ui/src/components/TokenSelectors/TerraTokenPicker.tsx delete mode 100644 bridge_ui/src/components/WithdrawTokensTerra.tsx delete mode 100644 bridge_ui/src/hooks/useSolanaMigratorInformation.tsx delete mode 100644 bridge_ui/src/utils/algorand.ts delete mode 100644 bridge_ui/src/utils/postVaa.ts delete mode 100644 bridge_ui/src/utils/terra.ts diff --git a/bridge_ui/package-lock.json b/bridge_ui/package-lock.json index 883b6e93c..c1b6f2d77 100644 --- a/bridge_ui/package-lock.json +++ b/bridge_ui/package-lock.json @@ -13,7 +13,7 @@ "@alephium/walletconnect-qrcode-modal": "0.1.0", "@alephium/web3": "^1.8.5", "@alephium/web3-react": "^1.8.5", - "@alephium/wormhole-sdk": "^0.3.5", + "@alephium/wormhole-sdk": "^0.4.0-rc.0", "@material-ui/core": "^4.12.2", "@material-ui/icons": "^4.11.2", "@material-ui/lab": "^4.0.0-alpha.60", @@ -741,18 +741,13 @@ } }, "node_modules/@alephium/wormhole-sdk": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@alephium/wormhole-sdk/-/wormhole-sdk-0.3.5.tgz", - "integrity": "sha512-JV2VZWpAKP/K4rnSqo8ZjwLWe98B6Npoqj5KEm1JoYUODgcMvtGFFWz3u3UT1SZxB3gIz4jdzpg/7+YpOA/83g==", + "version": "0.4.0-rc.0", + "resolved": "https://registry.npmjs.org/@alephium/wormhole-sdk/-/wormhole-sdk-0.4.0-rc.0.tgz", + "integrity": "sha512-pPg9hzYIkS3HoisVcfHbi/Gl3vT564Uaj6XCeCpKb052y80Q5wrdAmciUQJ7/V4gVOyQTyjoen1C19tFNlT/Xw==", "dependencies": { "@alephium/token-list": "0.0.11", "@alephium/web3": "^1.8.5", "@improbable-eng/grpc-web": "^0.14.0", - "@solana/spl-token": "^0.1.8", - "@solana/web3.js": "^1.24.0", - "@terra-money/terra.js": "^3.0.7", - "algosdk": "2.4.0", - "axios": "^0.24.0", "bech32": "^2.0.0", "bs58": "^5.0.0", "ethers": "^5.6.8", @@ -785,14 +780,6 @@ "google-protobuf": "^3.14.0" } }, - "node_modules/@alephium/wormhole-sdk/node_modules/axios": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.24.0.tgz", - "integrity": "sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==", - "dependencies": { - "follow-redirects": "^1.14.4" - } - }, "node_modules/@alephium/wormhole-sdk/node_modules/bech32": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/bech32/-/bech32-2.0.0.tgz", @@ -7824,6 +7811,7 @@ "node_modules/@terra-money/terra.js": { "version": "3.0.7", "license": "MIT", + "peer": true, "dependencies": { "@terra-money/terra.proto": "^0.1.7", "axios": "^0.24.0", @@ -7846,17 +7834,20 @@ "node_modules/@terra-money/terra.js/node_modules/axios": { "version": "0.24.0", "license": "MIT", + "peer": true, "dependencies": { "follow-redirects": "^1.14.4" } }, "node_modules/@terra-money/terra.js/node_modules/bech32": { "version": "2.0.0", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/@terra-money/terra.proto": { "version": "0.1.7", "license": "Apache-2.0", + "peer": true, "dependencies": { "google-protobuf": "^3.17.3", "long": "^4.0.0", @@ -41127,6 +41118,7 @@ "node_modules/tmp": { "version": "0.2.1", "license": "MIT", + "peer": true, "dependencies": { "rimraf": "^3.0.0" }, @@ -45269,18 +45261,13 @@ } }, "@alephium/wormhole-sdk": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@alephium/wormhole-sdk/-/wormhole-sdk-0.3.5.tgz", - "integrity": "sha512-JV2VZWpAKP/K4rnSqo8ZjwLWe98B6Npoqj5KEm1JoYUODgcMvtGFFWz3u3UT1SZxB3gIz4jdzpg/7+YpOA/83g==", + "version": "0.4.0-rc.0", + "resolved": "https://registry.npmjs.org/@alephium/wormhole-sdk/-/wormhole-sdk-0.4.0-rc.0.tgz", + "integrity": "sha512-pPg9hzYIkS3HoisVcfHbi/Gl3vT564Uaj6XCeCpKb052y80Q5wrdAmciUQJ7/V4gVOyQTyjoen1C19tFNlT/Xw==", "requires": { "@alephium/token-list": "0.0.11", "@alephium/web3": "^1.8.5", "@improbable-eng/grpc-web": "^0.14.0", - "@solana/spl-token": "^0.1.8", - "@solana/web3.js": "^1.24.0", - "@terra-money/terra.js": "^3.0.7", - "algosdk": "2.4.0", - "axios": "^0.24.0", "bech32": "^2.0.0", "bs58": "^5.0.0", "ethers": "^5.6.8", @@ -45306,14 +45293,6 @@ "browser-headers": "^0.4.1" } }, - "axios": { - "version": "0.24.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-0.24.0.tgz", - "integrity": "sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==", - "requires": { - "follow-redirects": "^1.14.4" - } - }, "bech32": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/bech32/-/bech32-2.0.0.tgz", @@ -49903,6 +49882,7 @@ }, "@terra-money/terra.js": { "version": "3.0.7", + "peer": true, "requires": { "@terra-money/terra.proto": "^0.1.7", "axios": "^0.24.0", @@ -49921,17 +49901,20 @@ "dependencies": { "axios": { "version": "0.24.0", + "peer": true, "requires": { "follow-redirects": "^1.14.4" } }, "bech32": { - "version": "2.0.0" + "version": "2.0.0", + "peer": true } } }, "@terra-money/terra.proto": { "version": "0.1.7", + "peer": true, "requires": { "google-protobuf": "^3.17.3", "long": "^4.0.0", @@ -73507,6 +73490,7 @@ }, "tmp": { "version": "0.2.1", + "peer": true, "requires": { "rimraf": "^3.0.0" } diff --git a/bridge_ui/package.json b/bridge_ui/package.json index a30c041fc..45115fc48 100644 --- a/bridge_ui/package.json +++ b/bridge_ui/package.json @@ -6,7 +6,7 @@ "@alephium/token-list": "0.0.12", "@alephium/web3": "^1.8.5", "@alephium/web3-react": "^1.8.5", - "@alephium/wormhole-sdk": "^0.3.5", + "@alephium/wormhole-sdk": "^0.4.0-rc.0", "@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/App.js b/bridge_ui/src/App.js index 4127bf374..3b9409bd4 100644 --- a/bridge_ui/src/App.js +++ b/bridge_ui/src/App.js @@ -1,8 +1,3 @@ -import { - CHAIN_ID_BSC, - CHAIN_ID_ETH, - CHAIN_ID_SOLANA, -} from "@alephium/wormhole-sdk"; import { AppBar, Container, @@ -29,9 +24,6 @@ import { import Attest from "./components/Attest"; import Footer from "./components/Footer"; import HeaderText from "./components/HeaderText"; -import Migration from "./components/Migration"; -import EvmQuickMigrate from "./components/Migration/EvmQuickMigrate"; -import SolanaQuickMigrate from "./components/Migration/SolanaQuickMigrate"; import Recovery from "./components/Recovery"; import Stats from "./components/Stats"; import CustodyAddresses from "./components/Stats/CustodyAddresses"; @@ -39,7 +31,6 @@ import TokenOriginVerifier from "./components/TokenOriginVerifier"; import Transactions from "./components/Transactions"; import Transfer from "./components/Transfer"; import UnwrapNative from "./components/UnwrapNative"; -import WithdrawTokensTerra from "./components/WithdrawTokensTerra"; import { useBetaContext } from "./contexts/BetaContext"; import Alephium from "./icons/alephium.svg"; import { CLUSTER } from "./utils/consts"; @@ -280,30 +271,9 @@ function App() { - - - - - - - - - - - - - - - - - - - - - diff --git a/bridge_ui/src/components/Attest/Create.tsx b/bridge_ui/src/components/Attest/Create.tsx index 95070d1d5..cff41e614 100644 --- a/bridge_ui/src/components/Attest/Create.tsx +++ b/bridge_ui/src/components/Attest/Create.tsx @@ -1,4 +1,3 @@ -import { CHAIN_ID_TERRA } from "@alephium/wormhole-sdk"; import { CircularProgress, makeStyles } from "@material-ui/core"; import { useTranslation } from "react-i18next"; import { useSelector } from "react-redux"; @@ -12,7 +11,6 @@ import { } from "../../store/selectors"; import ButtonWithLoader from "../ButtonWithLoader"; import KeyAndBalance from "../KeyAndBalance"; -import TerraFeeDenomPicker from "../TerraFeeDenomPicker"; import WaitingForWalletMessage from "./WaitingForWalletMessage"; const useStyles = makeStyles((theme) => ({ @@ -47,9 +45,6 @@ function Create() { return ( <> - {targetChain === CHAIN_ID_TERRA && ( - - )} {foreignAssetInfo.isFetching ? ( <>
diff --git a/bridge_ui/src/components/Attest/Send.tsx b/bridge_ui/src/components/Attest/Send.tsx index e5bb70de2..6b5e28ed4 100644 --- a/bridge_ui/src/components/Attest/Send.tsx +++ b/bridge_ui/src/components/Attest/Send.tsx @@ -1,4 +1,4 @@ -import { CHAIN_ID_ALEPHIUM, CHAIN_ID_SOLANA, CHAIN_ID_TERRA, waitAlphTxConfirmed } from "@alephium/wormhole-sdk"; +import { CHAIN_ID_ALEPHIUM, CHAIN_ID_SOLANA, waitAlphTxConfirmed } from "@alephium/wormhole-sdk"; import { Alert } from "@material-ui/lab"; import { Link, makeStyles } from "@material-ui/core"; import { useCallback, useMemo, useState } from "react"; @@ -18,7 +18,6 @@ import KeyAndBalance from "../KeyAndBalance"; import TransactionProgress from "../TransactionProgress"; import WaitingForWalletMessage from "./WaitingForWalletMessage"; import { ALEPHIUM_ATTEST_TOKEN_CONSISTENCY_LEVEL, SOLANA_TOKEN_METADATA_PROGRAM_URL } from "../../utils/consts"; -import TerraFeeDenomPicker from "../TerraFeeDenomPicker"; import { createLocalTokenPool } from "../../utils/alephium"; import { useWallet } from "@alephium/web3-react"; import { useSnackbar } from "notistack"; @@ -125,9 +124,6 @@ function Send() { return ( <> - {sourceChain === CHAIN_ID_TERRA && ( - - )} ({ - spacer: { - height: "2rem", - }, - containerDiv: { - textAlign: "center", - padding: theme.spacing(2), - }, - lineItem: { - display: "flex", - flexWrap: "nowrap", - justifyContent: "space-between", - "& > *": { - alignSelf: "flex-start", - width: "max-content", - }, - }, - flexGrow: { - flewGrow: 1, - }, - mainPaper: { - backgroundColor: COLORS.whiteWithTransparency, - textAlign: "center", - padding: "2rem", - "& > h, p ": { - margin: ".5rem", - }, - }, - hidden: { - display: "none", - }, - divider: { - margin: "2rem 0rem 2rem 0rem", - }, - balance: { - display: "inline-block", - }, - convertButton: { - alignSelf: "flex-end", - }, -})); - -//TODO move elsewhere -export const compareWithDecimalOffset = ( - valueA: string, - decimalsA: number, - valueB: string, - decimalsB: number -) => { - //find which is larger, and offset by that amount - const decimalsBasis = decimalsA > decimalsB ? decimalsA : decimalsB; - const normalizedA = parseUnits(valueA, decimalsBasis).toBigInt(); - const normalizedB = parseUnits(valueB, decimalsBasis).toBigInt(); - - if (normalizedA < normalizedB) { - return -1; - } else if (normalizedA === normalizedB) { - return 0; - } else { - return 1; - } -}; - -function EvmMigrationLineItem({ - chainId, - migratorAddress, - onLoadComplete, -}: { - chainId: ChainId; - migratorAddress: string; - onLoadComplete: () => void; -}) { - const { t } = useTranslation(); - const classes = useStyles(); - const { enqueueSnackbar } = useSnackbar(); - const { signer, signerAddress } = useEthereumProvider(); - const poolInfo = useEthereumMigratorInformation( - migratorAddress, - signer, - signerAddress, - false - ); - const [loaded, setLoaded] = useState(false); - const [migrationIsProcessing, setMigrationIsProcessing] = useState(false); - const [transaction, setTransaction] = useState(""); - const [error, setError] = useState(""); - const fromSymbol = poolInfo?.data?.fromSymbol; - const toSymbol = poolInfo?.data?.toSymbol; - - const sufficientPoolBalance = - poolInfo.data && - compareWithDecimalOffset( - poolInfo.data.fromWalletBalance, - poolInfo.data.fromDecimals, - poolInfo.data.toPoolBalance, - poolInfo.data.toDecimals - ) !== 1; - - useEffect(() => { - if (!loaded && (poolInfo.data || poolInfo.error)) { - onLoadComplete(); - setLoaded(true); - } - }, [loaded, poolInfo, onLoadComplete]); - - //TODO use transaction loader - const migrateTokens = useCallback(async () => { - if (!poolInfo.data) { - enqueueSnackbar(null, { - content: {t("Could not migrate the tokens.")}, - }); //Should never be hit - return; - } - try { - const migrationAmountAbs = parseUnits( - poolInfo.data.fromWalletBalance, - poolInfo.data.fromDecimals - ); - setMigrationIsProcessing(true); - await poolInfo.data.fromToken.approve( - poolInfo.data.migrator.address, - migrationAmountAbs - ); - const transaction = await poolInfo.data.migrator.migrate( - migrationAmountAbs - ); - await transaction.wait(); - setTransaction(transaction.hash); - enqueueSnackbar(null, { - content: ( - {t("Successfully migrated the tokens.")} - ), - }); - setMigrationIsProcessing(false); - } catch (e) { - console.error(e); - enqueueSnackbar(null, { - content: {t("Could not migrate the tokens.")}, - }); - setMigrationIsProcessing(false); - setError("Failed to send the transaction."); - } - }, [poolInfo.data, enqueueSnackbar, t]); - - if (!poolInfo.data) { - return null; - } else if (transaction) { - return ( -
-
- - {t("Successfully migrated your tokens. They will become available once this transaction confirms.")} - - -
-
- ); - } else { - return ( -
-
- - {t("Current Token")} - - - {poolInfo.data.fromWalletBalance} - - -
-
- - {t("will become")} - - -
-
- - {t("Wormhole Token")} - - - {poolInfo.data.fromWalletBalance} - - -
-
- - {t("Convert")} - -
-
- ); - } -} - -const getAddressBalances = async ( - signer: Signer, - signerAddress: string, - addresses: string[] -): Promise> => { - try { - const promises: Promise[] = []; - const output = new Map(); - addresses.forEach((address) => { - const factory = ethers_contracts.TokenImplementation__factory.connect(address, signer); - promises.push( - factory.balanceOf(signerAddress).then( - (result) => { - output.set(address, result); - }, - (error) => { - output.set(address, null); - } - ) - ); - }); - await Promise.all(promises); - return output; - } catch (e) { - return Promise.reject("Unable to retrieve token balances."); - } -}; - -export default function EvmQuickMigrate({ chainId }: { chainId: ChainId }) { - const classes = useStyles(); - const { signer, signerAddress } = useEthereumProvider(); - const { isReady } = useIsWalletReady(chainId); - const migrationMap = useMemo(() => getMigrationAssetMap(chainId), [chainId]); - const eligibleTokens = useMemo( - () => Array.from(migrationMap.keys()), - [migrationMap] - ); - const [migrators, setMigrators] = useState(null); - const [migratorsError, setMigratorsError] = useState(""); - const [migratorsLoading, setMigratorsLoading] = useState(false); - - //This is for a callback into the line items, so a loader can be displayed while - //they are loading - //TODO don't just swallow loading errors. - const [migratorsFinishedLoading, setMigratorsFinishedLoading] = useState(0); - const reportLoadComplete = useCallback(() => { - setMigratorsFinishedLoading((prevState) => prevState + 1); - }, []); - const isLoading = - migratorsLoading || - (migrators && - migrators.length && - migratorsFinishedLoading < migrators.length); - - useEffect(() => { - if (isReady && signer && signerAddress) { - let cancelled = false; - setMigratorsLoading(true); - setMigratorsError(""); - getAddressBalances(signer, signerAddress, eligibleTokens).then( - (result) => { - if (!cancelled) { - const migratorAddresses = []; - for (const tokenAddress of result.keys()) { - if (result.get(tokenAddress) && result.get(tokenAddress)?.gt(0)) { - const migratorAddress = migrationMap.get( - getAddress(tokenAddress) - ); - if (migratorAddress) { - migratorAddresses.push(migratorAddress); - } - } - } - setMigratorsFinishedLoading(0); - setMigrators(migratorAddresses); - setMigratorsLoading(false); - } - }, - (error) => { - if (!cancelled) { - setMigratorsLoading(false); - setMigratorsError( - "Failed to retrieve available token information." - ); - } - } - ); - - return () => { - cancelled = true; - }; - } - }, [isReady, signer, signerAddress, eligibleTokens, migrationMap]); - - const hasEligibleAssets = migrators && migrators.length > 0; - const chainName = CHAINS_BY_ID[chainId]?.name; - - const content = ( -
- - {`This page allows you to convert certain wrapped tokens ${ - chainName ? "on " + chainName : "" - } into - Wormhole V2 tokens.`} - - - {!isReady ? ( - Please connect your wallet. - ) : migratorsError ? ( - {migratorsError} - ) : ( - <> -
- -
- - {hasEligibleAssets - ? "You have some assets that are eligible for migration! Click the 'Convert' button to swap them for Wormhole tokens." - : "You don't have any assets eligible for migration."} - -
- {migrators?.map((address) => { - return ( - - ); - })} -
- - )} -
- ); - - return ( - - - Migrate Assets - - {content} - - ); -} diff --git a/bridge_ui/src/components/Migration/EvmWorkflow.tsx b/bridge_ui/src/components/Migration/EvmWorkflow.tsx deleted file mode 100644 index 0c57abee3..000000000 --- a/bridge_ui/src/components/Migration/EvmWorkflow.tsx +++ /dev/null @@ -1,247 +0,0 @@ -import { ChainId } from "@alephium/wormhole-sdk"; -import { CircularProgress, makeStyles, Typography } from "@material-ui/core"; -import { Alert } from "@material-ui/lab"; -import { parseUnits } from "ethers/lib/utils"; -import { useSnackbar } from "notistack"; -import { useCallback, useState } from "react"; -import { useTranslation } from "react-i18next"; -import { useEthereumProvider } from "../../contexts/EthereumProviderContext"; -import useEthereumMigratorInformation from "../../hooks/useEthereumMigratorInformation"; -import useIsWalletReady from "../../hooks/useIsWalletReady"; -import ButtonWithLoader from "../ButtonWithLoader"; -import EthereumSignerKey from "../EthereumSignerKey"; -import NumberTextField from "../NumberTextField"; -import ShowTx from "../ShowTx"; -import SmartAddress from "../SmartAddress"; - -const useStyles = makeStyles((theme) => ({ - spacer: { - height: "2rem", - }, - containerDiv: { - textAlign: "center", - padding: theme.spacing(2), - }, -})); - -export default function EvmWorkflow({ - chainId, - migratorAddress, -}: { - chainId: ChainId; - migratorAddress: string; -}) { - const { t } = useTranslation(); - const classes = useStyles(); - const { enqueueSnackbar } = useSnackbar(); - const { signer, signerAddress } = useEthereumProvider(); - const { isReady } = useIsWalletReady(chainId); - const [toggleRefresh, setToggleRefresh] = useState(false); - const forceRefresh = useCallback( - () => setToggleRefresh((prevState) => !prevState), - [] - ); - const poolInfo = useEthereumMigratorInformation( - migratorAddress, - signer, - signerAddress, - toggleRefresh - ); - const fromWalletBalance = poolInfo.data?.fromWalletBalance; - - const [migrationAmount, setMigrationAmount] = useState(""); - const [migrationIsProcessing, setMigrationIsProcessing] = useState(false); - const [error, setError] = useState(""); - const [transaction, setTransaction] = useState(null); - - const fromParse = (amount: string) => { - try { - if (!poolInfo.data?.fromDecimals || !migrationAmount) { - return BigInt(0); - } - return parseUnits(amount, poolInfo.data.fromDecimals).toBigInt(); - } catch (e) { - return BigInt(0); - } - }; - - const hasRequisiteData = poolInfo.data; - const amountGreaterThanZero = fromParse(migrationAmount) > BigInt(0); - const sufficientFromTokens = - fromWalletBalance && - migrationAmount && - fromParse(migrationAmount) <= fromParse(fromWalletBalance); - const sufficientPoolBalance = - poolInfo.data?.toPoolBalance && - migrationAmount && - parseFloat(migrationAmount) <= parseFloat(poolInfo.data.toPoolBalance); - - const isReadyToTransfer = - isReady && - amountGreaterThanZero && - sufficientFromTokens && - sufficientPoolBalance && - hasRequisiteData; - - const getNotReadyCause = () => { - if (!isReady) { - return t("Connect your wallet to proceed."); - } else if (poolInfo.error) { - return t("Unable to retrieve necessary information. This asset may not be supported."); - } else if (!migrationAmount) { - return t("Enter an amount to transfer."); - } else if (!amountGreaterThanZero) { - return t("The transfer amount must be greater than zero."); - } else if (!sufficientFromTokens) { - return t("There are not sufficient funds in your wallet for this transfer."); - } else if (!sufficientPoolBalance) { - return t("There are not sufficient funds in the pool for this transfer."); - } else { - return ""; - } - }; - - const handleAmountChange = useCallback( - (event: any) => setMigrationAmount(event.target.value), - [setMigrationAmount] - ); - const handleMaxClick = useCallback(() => { - if (fromWalletBalance) { - setMigrationAmount(fromWalletBalance); - } - }, [fromWalletBalance]); - - const migrateTokens = useCallback(async () => { - if (!poolInfo.data) { - enqueueSnackbar(null, { - content: {t("Could not migrate the tokens.")}, - }); //Should never be hit - return; - } - try { - setMigrationIsProcessing(true); - setError(""); - await poolInfo.data.fromToken.approve( - poolInfo.data.migrator.address, - parseUnits(migrationAmount, poolInfo.data.fromDecimals) - ); - const transaction = await poolInfo.data.migrator.migrate( - parseUnits(migrationAmount, poolInfo.data.fromDecimals) - ); - await transaction.wait(); - setTransaction(transaction.hash); - forceRefresh(); - enqueueSnackbar(null, { - content: ( - {t("Successfully migrated the tokens.")} - ), - }); - setMigrationIsProcessing(false); - } catch (e) { - console.error(e); - enqueueSnackbar(null, { - content: {t("Could not migrate the tokens.")}, - }); - setMigrationIsProcessing(false); - setError(t("Failed to send the transaction.")); - } - }, [poolInfo.data, enqueueSnackbar, t, migrationAmount, forceRefresh]); - - //TODO tokenName - const toTokenPretty = ( - - ); - const fromTokenPretty = ( - - ); - const poolPretty = ( - - ); - - const fatalError = poolInfo.error - ? t("Unable to retrieve necessary information. This asset may not be supported.") - : null; - - const explainerContent = ( -
- {t("This action will convert")} - - {fromTokenPretty} {`(${t('Balance')}: ${fromWalletBalance || ""})`} - -
- {t("to")} - - {toTokenPretty} {`(${t('Balance')}: ${poolInfo.data?.toWalletBalance || ""})`} - -
- {t("Utilizing this pool")} - - {poolPretty} {`(${t('Balance')}: ${poolInfo.data?.toPoolBalance || ""})`} - -
- ); - - const mainWorkflow = ( - <> - {explainerContent} -
- - - {!transaction && ( - - {migrationAmount && isReadyToTransfer - ? t('Migrate {{ tokensAmount }} Tokens', { tokensAmount: migrationAmount }) - : t('Migrate')} - - )} - - {(error || !isReadyToTransfer) && ( - {error || getNotReadyCause()} - )} - {transaction ? ( - <> - - {t("Successfully migrated your tokens! They will be available once this transaction confirms.")} - - - - ) : null} - - ); - - return ( -
- - {!isReady ? ( - {t("Please connect your wallet.")} - ) : poolInfo.isLoading ? ( - - ) : fatalError ? ( - {fatalError} - ) : ( - mainWorkflow - )} -
- ); -} diff --git a/bridge_ui/src/components/Migration/SolanaQuickMigrate.tsx b/bridge_ui/src/components/Migration/SolanaQuickMigrate.tsx deleted file mode 100644 index 439e3e9b8..000000000 --- a/bridge_ui/src/components/Migration/SolanaQuickMigrate.tsx +++ /dev/null @@ -1,383 +0,0 @@ -import { CHAIN_ID_SOLANA } from "@alephium/wormhole-sdk"; -import { - CircularProgress, - Container, - makeStyles, - Paper, - Typography, -} from "@material-ui/core"; -import ArrowRightAltIcon from "@material-ui/icons/ArrowRightAlt"; -import { TOKEN_PROGRAM_ID } from "@solana/spl-token"; -import { - AccountInfo, - Connection, - ParsedAccountData, - PublicKey, -} from "@solana/web3.js"; -import { useCallback, useEffect, useMemo, useState } from "react"; -import { useTranslation } from "react-i18next"; -import useIsWalletReady from "../../hooks/useIsWalletReady"; -import useSolanaMigratorInformation from "../../hooks/useSolanaMigratorInformation"; -import i18n from "../../i18n"; -import { COLORS } from "../../muiTheme"; -import { - CHAINS_BY_ID, - getMigrationAssetMap, - SOLANA_HOST, -} from "../../utils/consts"; -import ButtonWithLoader from "../ButtonWithLoader"; -import HeaderText from "../HeaderText"; -import ShowTx from "../ShowTx"; -import SmartAddress from "../SmartAddress"; -import SolanaCreateAssociatedAddress from "../SolanaCreateAssociatedAddress"; -import SolanaWalletKey from "../SolanaWalletKey"; - -const useStyles = makeStyles((theme) => ({ - spacer: { - height: "2rem", - }, - containerDiv: { - textAlign: "center", - padding: theme.spacing(2), - }, - centered: { - textAlign: "center", - }, - lineItem: { - display: "flex", - flexWrap: "nowrap", - justifyContent: "space-between", - "& > *": { - alignSelf: "flex-start", - width: "max-content", - }, - }, - flexGrow: { - flewGrow: 1, - }, - mainPaper: { - backgroundColor: COLORS.whiteWithTransparency, - textAlign: "center", - padding: "2rem", - "& > h, p ": { - margin: ".5rem", - }, - }, - hidden: { - display: "none", - }, - divider: { - margin: "2rem 0rem 2rem 0rem", - }, - balance: { - display: "inline-block", - }, - convertButton: { - alignSelf: "flex-end", - }, -})); - -function SolanaMigrationLineItem({ - migratorInfo, - onLoadComplete, -}: { - migratorInfo: DefaultAssociatedTokenAccountInfo; - onLoadComplete: () => void; -}) { - const { t } = useTranslation(); - const classes = useStyles(); - const poolInfo = useSolanaMigratorInformation( - migratorInfo.fromMintKey, - migratorInfo.toMintKey, - migratorInfo.defaultFromTokenAccount - ); - - const [migrationIsProcessing, setMigrationIsProcessing] = useState(false); - const [transaction, setTransaction] = useState(""); - const [migrationError, setMigrationError] = useState(""); - - const handleMigrateClick = useCallback(() => { - if (!poolInfo.data) { - return; - } - setMigrationIsProcessing(true); - setMigrationError(""); - poolInfo.data - .migrateTokens(poolInfo.data.fromAssociatedTokenAccountBalance) - .then((result) => { - setMigrationIsProcessing(false); - setTransaction(result); - }) - .catch((e) => { - setMigrationError(t("Unable to perform migration.")); - setMigrationIsProcessing(false); - }); - }, [poolInfo.data, t]); - - const precheckError = - poolInfo.data && - poolInfo.data.getNotReadyCause( - poolInfo.data.fromAssociatedTokenAccountBalance - ); - - useEffect(() => { - if (poolInfo.data || poolInfo.error) { - onLoadComplete(); - } - }, [poolInfo, onLoadComplete]); - - if (!poolInfo.data) { - return ( -
-
- - {t("Failed to load migration information for token")} - - -
-
- ); - } else if (transaction) { - return ( -
-
- - {t("Successfully migrated your tokens. They will become available once this transaction confirms.")} - - -
-
- ); - } else { - return ( -
-
- - {t("Current Token")} - - - {poolInfo.data.fromAssociatedTokenAccountBalance} - - -
-
- - {t("will become")} - - -
-
- - {t("Wormhole Token")} - - - {poolInfo.data.fromAssociatedTokenAccountBalance} - - -
- {!poolInfo.data.toAssociatedTokenAccountExists ? ( -
- -
- ) : ( -
- - {t("Convert")} - -
- )} -
- ); - } -} - -type DefaultAssociatedTokenAccountInfo = { - fromMintKey: string; - toMintKey: string; - defaultFromTokenAccount: string; - fromAccountInfo: AccountInfo | null; -}; - -const getTokenBalances = async ( - walletAddress: string, - migrationMap: Map -): Promise => { - try { - const connection = new Connection(SOLANA_HOST); - const output: DefaultAssociatedTokenAccountInfo[] = []; - const tokenAccounts = await connection.getParsedTokenAccountsByOwner( - new PublicKey(walletAddress), - { programId: TOKEN_PROGRAM_ID }, - "confirmed" - ); - tokenAccounts.value.forEach((item) => { - if ( - item.account != null && - item.account.data?.parsed?.info?.tokenAmount?.uiAmountString && - item.account.data?.parsed.info?.tokenAmount?.amount !== "0" - ) { - const fromMintKey = item.account.data.parsed.info.mint; - const toMintKey = migrationMap.get(fromMintKey); - if (toMintKey) { - output.push({ - fromMintKey, - toMintKey: toMintKey, - defaultFromTokenAccount: item.pubkey.toString(), - fromAccountInfo: item.account, - }); - } - } - }); - - return output; - } catch (e) { - console.error(e); - return Promise.reject(i18n.t("Unable to retrieve token balances.")); - } -}; - -export default function SolanaQuickMigrate() { - const { t } = useTranslation(); - const chainId = CHAIN_ID_SOLANA; - const classes = useStyles(); - const { isReady, walletAddress } = useIsWalletReady(chainId); - const migrationMap = useMemo(() => getMigrationAssetMap(chainId), [chainId]); - const [migrators, setMigrators] = useState< - DefaultAssociatedTokenAccountInfo[] | null - >(null); - const [migratorsError, setMigratorsError] = useState(""); - const [migratorsLoading, setMigratorsLoading] = useState(false); - - //This is for a callback into the line items, so a loader can be displayed while - //they are loading - //TODO don't just swallow loading errors. - const [migratorsFinishedLoading, setMigratorsFinishedLoading] = useState(0); - const reportLoadComplete = useCallback(() => { - setMigratorsFinishedLoading((prevState) => prevState + 1); - }, []); - const isLoading = - migratorsLoading || - (migrators && - migrators.length && - migratorsFinishedLoading < migrators.length); - - useEffect(() => { - if (isReady && walletAddress) { - let cancelled = false; - setMigratorsLoading(true); - setMigratorsError(""); - getTokenBalances(walletAddress, migrationMap).then( - (result) => { - if (!cancelled) { - setMigratorsFinishedLoading(0); - setMigrators(result.filter((x) => x.fromAccountInfo && x)); - setMigratorsLoading(false); - } - }, - (error) => { - if (!cancelled) { - setMigratorsLoading(false); - setMigratorsError( - t("Failed to retrieve available token information.") - ); - } - } - ); - - return () => { - cancelled = true; - }; - } - }, [isReady, walletAddress, migrationMap, t]); - - const hasEligibleAssets = migrators && migrators.length > 0; - const chainName = CHAINS_BY_ID[chainId]?.name; - - const content = ( -
- - {chainName ? - t('This page allows you to convert certain wrapped tokens on {{ chainName }} into Wormhole V2 tokens.', { chainName }) : - t('This page allows you to convert certain wrapped tokens into Wormhole V2 tokens.') } - - - {!isReady ? ( - {t("Please connect your wallet.")} - ) : migratorsError ? ( - {migratorsError} - ) : ( - <> -
- -
- - {hasEligibleAssets - ? t("You have some assets that are eligible for migration! Click the 'Convert' button to swap them for Wormhole tokens.") - : t("You don't have any assets eligible for migration.")} - -
- {migrators?.map((info) => { - return ( - - ); - })} -
- - )} -
- ); - - return ( - - - {t("Migrate Assets")} - - {content} - - ); -} diff --git a/bridge_ui/src/components/Migration/SolanaWorkflow.tsx b/bridge_ui/src/components/Migration/SolanaWorkflow.tsx deleted file mode 100644 index 77befb73b..000000000 --- a/bridge_ui/src/components/Migration/SolanaWorkflow.tsx +++ /dev/null @@ -1,507 +0,0 @@ -import { CHAIN_ID_SOLANA } from "@alephium/wormhole-sdk"; -import migrateTokensTx from "@alephium/wormhole-sdk/lib/esm/migration/migrateTokens"; -import getPoolAddress from "@alephium/wormhole-sdk/lib/esm/migration/poolAddress"; -import getToCustodyAddress from "@alephium/wormhole-sdk/lib/esm/migration/toCustodyAddress"; -import { makeStyles, Typography } from "@material-ui/core"; -import { - ASSOCIATED_TOKEN_PROGRAM_ID, - Token, - TOKEN_PROGRAM_ID, -} from "@solana/spl-token"; -import { Connection, PublicKey } from "@solana/web3.js"; -import { parseUnits } from "ethers/lib/utils"; -import { useCallback, useEffect, useMemo, useState } from "react"; -import { useSolanaWallet } from "../../contexts/SolanaWalletContext"; -import useIsWalletReady from "../../hooks/useIsWalletReady"; -import useMetaplexData from "../../hooks/useMetaplexData"; -import useSolanaTokenMap from "../../hooks/useSolanaTokenMap"; -import { COLORS } from "../../muiTheme"; -import { MIGRATION_PROGRAM_ADDRESS, SOLANA_HOST } from "../../utils/consts"; -import { getMultipleAccounts, signSendAndConfirm } from "../../utils/solana"; -import ButtonWithLoader from "../ButtonWithLoader"; -import NumberTextField from "../NumberTextField"; -import ShowTx from "../ShowTx"; -import SmartAddress from "../SmartAddress"; -import SolanaCreateAssociatedAddress, { - useAssociatedAccountExistsState, -} from "../SolanaCreateAssociatedAddress"; -import SolanaWalletKey from "../SolanaWalletKey"; - -const useStyles = makeStyles(() => ({ - mainPaper: { - backgroundColor: COLORS.whiteWithTransparency, - textAlign: "center", - padding: "2rem", - "& > h, p ": { - margin: ".5rem", - }, - }, - divider: { - margin: "2rem 0rem 2rem 0rem", - }, - spacer: { - height: "2rem", - }, -})); - -//TODO move to utils/solana -const getDecimals = async ( - connection: Connection, - mint: string, - setter: (decimals: number | undefined) => void -) => { - setter(undefined); - if (mint) { - try { - const pk = new PublicKey(mint); - const info = await connection.getParsedAccountInfo(pk); - // @ts-ignore - const decimals = info.value?.data.parsed.info.decimals; - setter(decimals); - } catch (e) { - console.log(`Unable to determine decimals of ${mint}`); - } - } -}; - -//TODO move to utils/solana -const getBalance = async ( - connection: Connection, - address: string | undefined, - setter: (balance: string | undefined) => void -) => { - setter(undefined); - if (address) { - try { - const pk = new PublicKey(address); - const info = await connection.getParsedAccountInfo(pk); - // @ts-ignore - const balance = info.value?.data.parsed.info.tokenAmount.uiAmountString; - setter(balance); - } catch (e) { - console.log(`Unable to determine balance of ${address}`); - } - } -}; - -export default function Workflow({ - fromMint, - toMint, - fromTokenAccount, -}: { - fromMint: string; - toMint: string; - fromTokenAccount: string; -}) { - const classes = useStyles(); - - const connection = useMemo( - () => new Connection(SOLANA_HOST, "confirmed"), - [] - ); - const wallet = useSolanaWallet(); - const { isReady } = useIsWalletReady(CHAIN_ID_SOLANA); - const solanaTokenMap = useSolanaTokenMap(); - const metaplexArray = useMemo(() => [fromMint, toMint], [fromMint, toMint]); - const metaplexData = useMetaplexData(metaplexArray); - - const [poolAddress, setPoolAddress] = useState(""); - const [poolExists, setPoolExists] = useState(undefined); - const [fromTokenAccountBalance, setFromTokenAccountBalance] = useState< - string | undefined - >(undefined); - const [toTokenAccount, setToTokenAccount] = useState( - undefined - ); - const [toTokenAccountBalance, setToTokenAccountBalance] = useState< - string | undefined - >(undefined); - const [fromMintDecimals, setFromMintDecimals] = useState( - undefined - ); - - const { - associatedAccountExists: fromTokenAccountExists, - //setAssociatedAccountExists: setFromTokenAccountExists, - } = useAssociatedAccountExistsState( - CHAIN_ID_SOLANA, - fromMint, - fromTokenAccount - ); - const { - associatedAccountExists: toTokenAccountExists, - setAssociatedAccountExists: setToTokenAccountExists, - } = useAssociatedAccountExistsState(CHAIN_ID_SOLANA, toMint, toTokenAccount); - - const [toCustodyAddress, setToCustodyAddress] = useState( - undefined - ); - const [toCustodyBalance, setToCustodyBalance] = useState( - undefined - ); - - const [migrationAmount, setMigrationAmount] = useState(""); - const [migrationIsProcessing, setMigrationIsProcessing] = useState(false); - const [error, setError] = useState(""); - const [transaction, setTransaction] = useState(null); - - /* Effects - */ - useEffect(() => { - getDecimals(connection, fromMint, setFromMintDecimals); - }, [connection, fromMint]); - - //Retrieve user balance when fromTokenAccount changes - useEffect(() => { - // TODO: cancellable - if (fromTokenAccount && fromTokenAccountExists) { - getBalance(connection, fromTokenAccount, setFromTokenAccountBalance); - } else { - setFromTokenAccountBalance(undefined); - } - }, [ - connection, - fromTokenAccountExists, - fromTokenAccount, - setFromTokenAccountBalance, - ]); - - useEffect(() => { - // TODO: cancellable - if (toTokenAccount && toTokenAccountExists) { - getBalance(connection, toTokenAccount, setToTokenAccountBalance); - } else { - setToTokenAccountBalance(undefined); - } - }, [ - connection, - toTokenAccountExists, - toTokenAccount, - setFromTokenAccountBalance, - ]); - - useEffect(() => { - // TODO: cancellable - if (toCustodyAddress) { - getBalance(connection, toCustodyAddress, setToCustodyBalance); - } else { - setToCustodyBalance(undefined); - } - }, [connection, toCustodyAddress, setToCustodyBalance]); - - //Retrieve pool address on selectedTokens change - useEffect(() => { - if (toMint && fromMint) { - setPoolAddress(""); - setPoolExists(undefined); - getPoolAddress(MIGRATION_PROGRAM_ADDRESS, fromMint, toMint).then( - (result) => { - const key = new PublicKey(result).toString(); - setPoolAddress(key); - }, - (error) => console.log("Could not calculate pool address.") - ); - } - }, [toMint, fromMint, setPoolAddress]); - - //Retrieve the poolAccount every time the pool address changes. - useEffect(() => { - if (poolAddress) { - setPoolExists(undefined); - try { - getMultipleAccounts( - connection, - [new PublicKey(poolAddress)], - "confirmed" - ).then((result) => { - if (result.length && result[0] !== null) { - setPoolExists(true); - } else if (result.length && result[0] === null) { - setPoolExists(false); - setError("There is no swap pool for this token."); - } else { - setError( - "unexpected error in fetching pool address. Please reload and try again" - ); - } - }); - } catch (e) { - setError("Could not fetch pool address"); - } - } - }, [connection, poolAddress]); - - //Set relevant information derived from poolAddress - useEffect(() => { - if (poolAddress) { - getToCustodyAddress(MIGRATION_PROGRAM_ADDRESS, poolAddress) - .then((result: any) => - setToCustodyAddress(new PublicKey(result).toString()) - ) - .catch((e) => { - setToCustodyAddress(undefined); - }); - } else { - setToCustodyAddress(undefined); - } - }, [poolAddress]); - - useEffect(() => { - if (wallet?.publicKey && toMint) { - Token.getAssociatedTokenAddress( - ASSOCIATED_TOKEN_PROGRAM_ID, - TOKEN_PROGRAM_ID, - new PublicKey(toMint), - wallet?.publicKey || new PublicKey([]) - ).then( - (result) => { - setToTokenAccount(result.toString()); - }, - (error) => {} - ); - } - }, [toMint, wallet?.publicKey]); - /* - End effects - */ - - const migrateTokens = useCallback(async () => { - try { - setError(""); - const instruction = await migrateTokensTx( - connection, - wallet?.publicKey?.toString() || "", - MIGRATION_PROGRAM_ADDRESS, - fromMint, - toMint, - fromTokenAccount || "", - toTokenAccount || "", - parseUnits(migrationAmount, fromMintDecimals).toBigInt() - ); - setMigrationIsProcessing(true); - signSendAndConfirm(wallet, connection, instruction).then( - (transaction: any) => { - setMigrationIsProcessing(false); - setTransaction(transaction); - }, - (error) => { - console.log(error); - setError("Could not complete the migrateTokens transaction."); - setMigrationIsProcessing(false); - } - ); - } catch (e) { - console.log(e); - setError("Could not complete the migrateTokens transaction."); - setMigrationIsProcessing(false); - } - }, [ - connection, - fromMint, - fromTokenAccount, - migrationAmount, - toMint, - toTokenAccount, - wallet, - fromMintDecimals, - ]); - - const fromParse = (amount: string) => { - try { - return parseUnits(amount, fromMintDecimals).toBigInt(); - } catch (e) { - return BigInt(0); - } - }; - - const hasRequisiteData = fromMint && toMint && poolAddress && poolExists; - const accountsReady = - fromTokenAccountExists && toTokenAccountExists && poolExists; - const amountGreaterThanZero = fromParse(migrationAmount) > BigInt(0); - const sufficientFromTokens = - fromTokenAccountBalance && - migrationAmount && - fromParse(migrationAmount) <= fromParse(fromTokenAccountBalance); - const sufficientPoolBalance = - toCustodyBalance && - migrationAmount && - parseFloat(migrationAmount) <= parseFloat(toCustodyBalance); - - const isReadyToTransfer = - isReady && - amountGreaterThanZero && - sufficientFromTokens && - sufficientPoolBalance && - accountsReady && - hasRequisiteData; - - const getNotReadyCause = () => { - if (!fromMint || !toMint || !poolAddress || !poolExists) { - return "This asset is not supported."; - } else if (!isReady) { - return "Wallet is not connected."; - } else if (!toTokenAccountExists || !fromTokenAccountExists) { - return "You have not created the necessary token accounts."; - } else if (!migrationAmount) { - return "Enter an amount to transfer."; - } else if (!amountGreaterThanZero) { - return "Enter an amount greater than zero."; - } else if (!sufficientFromTokens) { - return "There are not sufficient funds in your wallet for this transfer."; - } else if (!sufficientPoolBalance) { - return "There are not sufficient funds in the pool for this transfer."; - } else { - return ""; - } - }; - - const handleAmountChange = useCallback( - (event: any) => setMigrationAmount(event.target.value), - [setMigrationAmount] - ); - const handleMaxClick = useCallback(() => { - if (fromTokenAccountBalance) { - setMigrationAmount(fromTokenAccountBalance); - } - }, [fromTokenAccountBalance]); - - const getMetadata = (address: string) => { - const tokenMapItem = solanaTokenMap.data?.find( - (x) => x.address === address - ); - const metaplexItem = metaplexData.data?.get(address); - - return { - symbol: tokenMapItem?.symbol || metaplexItem?.data?.symbol || undefined, - name: tokenMapItem?.name || metaplexItem?.data?.name || undefined, - logo: tokenMapItem?.logoURI || metaplexItem?.data?.uri || undefined, - }; - }; - - const toMetadata = getMetadata(toMint); - const fromMetadata = getMetadata(fromMint); - - const toMintPretty = ( - - ); - const fromMintPretty = ( - - ); - - return ( -
- -
- {fromTokenAccount && toTokenAccount ? ( - <> - - This will migrate - {fromMintPretty} - tokens in this account: - - - - {`(Balance: ${fromTokenAccountBalance}${ - fromMetadata.symbol && " " + fromMetadata.symbol - })`} - -
- - into - {toMintPretty} - tokens in this account: - - - - - {toTokenAccountExists - ? ` (Balance: ${toTokenAccountBalance}${ - (toMetadata.symbol && " " + toMetadata.symbol) || "" - })` - : " (Not created yet)"} - - - - {poolAddress && toCustodyAddress && toCustodyBalance ? ( - <> -
- - Using pool - - holding tokens in this account: - - - - {` (Balance: ${toCustodyBalance}${ - toMetadata.symbol && " " + toMetadata.symbol - })`} - - - ) : null} - - ) : null} -
- - - {!transaction && ( - - {migrationAmount && isReadyToTransfer - ? "Migrate " + migrationAmount + " Tokens" - : "Migrate"} - - )} - {(error || !isReadyToTransfer) && ( - {error || getNotReadyCause()} - )} - {transaction ? ( - <> - - Successfully migrated your tokens! They will be available once this - transaction confirms. - - - - ) : null} -
- ); -} diff --git a/bridge_ui/src/components/Migration/index.tsx b/bridge_ui/src/components/Migration/index.tsx deleted file mode 100644 index 1296df4ab..000000000 --- a/bridge_ui/src/components/Migration/index.tsx +++ /dev/null @@ -1,134 +0,0 @@ -import { - ChainId, - CHAIN_ID_BSC, - CHAIN_ID_ETH, - CHAIN_ID_SOLANA, -} from "@alephium/wormhole-sdk"; -import { getAddress } from "@ethersproject/address"; -import { Container, makeStyles, Paper, Typography } from "@material-ui/core"; -import { PublicKey } from "@solana/web3.js"; -import { useTranslation } from "react-i18next"; -import { withRouter } from "react-router"; -import { RouteComponentProps } from "react-router-dom"; -import { COLORS } from "../../muiTheme"; -import { getMigrationAssetMap, MIGRATION_ASSET_MAP } from "../../utils/consts"; -import HeaderText from "../HeaderText"; -import EvmWorkflow from "./EvmWorkflow"; -import SolanaWorkflow from "./SolanaWorkflow"; - -const useStyles = makeStyles(() => ({ - mainPaper: { - backgroundColor: COLORS.whiteWithTransparency, - textAlign: "center", - padding: "2rem", - "& > h, p ": { - margin: ".5rem", - }, - }, - divider: { - margin: "2rem 0rem 2rem 0rem", - }, - spacer: { - height: "2rem", - }, -})); - -interface RouteParams { - legacyAsset: string; - fromTokenAccount: string; -} - -interface Migration extends RouteComponentProps { - chainId: ChainId; -} - -const SolanaRoot: React.FC = (props) => { - const { t } = useTranslation(); - const legacyAsset: string = props.match.params.legacyAsset; - const fromTokenAccount: string = props.match.params.fromTokenAccount; - const targetAsset: string | undefined = MIGRATION_ASSET_MAP.get(legacyAsset); - - let fromMint: string | undefined = ""; - let toMint: string | undefined = ""; - let fromTokenAcct: string | undefined = ""; - try { - fromMint = legacyAsset && new PublicKey(legacyAsset).toString(); - toMint = targetAsset && new PublicKey(targetAsset).toString(); - fromTokenAcct = - fromTokenAccount && new PublicKey(fromTokenAccount).toString(); - } catch (e) {} - - let content = null; - - if (!fromMint || !toMint) { - content = ( - - {t("This asset is not eligible for migration.")} - - ); - } else if (!fromTokenAcct) { - content = ( - - {t("Invalid token account.")} - - ); - } else { - content = ( - - ); - } - - return content; -}; - -const EthereumRoot: React.FC = (props) => { - const { t } = useTranslation(); - const legacyAsset: string = props.match.params.legacyAsset; - const assetMap = getMigrationAssetMap(props.chainId); - const targetPool = assetMap.get(getAddress(legacyAsset)); - - let content = null; - if (!legacyAsset || !targetPool) { - content = ( - - {t("This asset is not eligible for migration.")} - - ); - } else { - content = ( - - ); - } - - return content; -}; - -const MigrationRoot: React.FC = (props) => { - const { t } = useTranslation(); - const classes = useStyles(); - let content = null; - - if (props.chainId === CHAIN_ID_SOLANA) { - content = ; - } else if (props.chainId === CHAIN_ID_ETH || props.chainId === CHAIN_ID_BSC) { - content = ; - } - - return ( - - - {t("Migrate Assets")} - - {content} - - ); -}; - -export default withRouter(MigrationRoot); diff --git a/bridge_ui/src/components/NFT/Redeem.tsx b/bridge_ui/src/components/NFT/Redeem.tsx index 7c585825b..e000b4e0f 100644 --- a/bridge_ui/src/components/NFT/Redeem.tsx +++ b/bridge_ui/src/components/NFT/Redeem.tsx @@ -1,4 +1,4 @@ -import { CHAIN_ID_SOLANA, CHAIN_ID_TERRA } from "@alephium/wormhole-sdk"; +import { CHAIN_ID_SOLANA } from "@alephium/wormhole-sdk"; import { useSelector } from "react-redux"; import { useHandleNFTRedeem } from "../../hooks/useHandleNFTRedeem"; import useIsWalletReady from "../../hooks/useIsWalletReady"; @@ -8,7 +8,6 @@ import ButtonWithLoader from "../ButtonWithLoader"; import KeyAndBalance from "../KeyAndBalance"; import SolanaTPSWarning from "../SolanaTPSWarning"; import StepDescription from "../StepDescription"; -import TerraFeeDenomPicker from "../TerraFeeDenomPicker"; import WaitingForWalletMessage from "./WaitingForWalletMessage"; function Redeem() { @@ -19,9 +18,6 @@ function Redeem() { <> Receive the NFT on the target chain - {targetChain === CHAIN_ID_TERRA && ( - - )} {targetChain === CHAIN_ID_SOLANA && CLUSTER === "mainnet" && ( )} diff --git a/bridge_ui/src/components/NFT/Send.tsx b/bridge_ui/src/components/NFT/Send.tsx index 685533175..123129790 100644 --- a/bridge_ui/src/components/NFT/Send.tsx +++ b/bridge_ui/src/components/NFT/Send.tsx @@ -16,7 +16,6 @@ import KeyAndBalance from "../KeyAndBalance"; import ShowTx from "../ShowTx"; import SolanaTPSWarning from "../SolanaTPSWarning"; import StepDescription from "../StepDescription"; -import TerraFeeDenomPicker from "../TerraFeeDenomPicker"; import TransactionProgress from "../TransactionProgress"; import WaitingForWalletMessage from "./WaitingForWalletMessage"; @@ -44,9 +43,6 @@ function Send() { Transfer the NFT to the Wormhole Token Bridge. - {sourceChain === CHAIN_ID_TERRA && ( - - )} This will initiate the transfer on {CHAINS_BY_ID[sourceChain].name} and wait for finalization. If you navigate away from this page before diff --git a/bridge_ui/src/components/NFTOriginVerifier.tsx b/bridge_ui/src/components/NFTOriginVerifier.tsx index 773841243..b4a3a774d 100644 --- a/bridge_ui/src/components/NFTOriginVerifier.tsx +++ b/bridge_ui/src/components/NFTOriginVerifier.tsx @@ -12,11 +12,7 @@ import { isEVMChain, uint8ArrayToHex, } from "@alephium/wormhole-sdk"; -import { - getOriginalAssetEth, - getOriginalAssetSol, - WormholeWrappedNFTInfo, -} from "@alephium/wormhole-sdk/lib/esm/nft_bridge"; +import { getOriginalAssetEth, WormholeWrappedNFTInfo } from "@alephium/wormhole-sdk/lib/esm/nft_bridge"; import { Button, Card, @@ -29,21 +25,17 @@ import { } from "@material-ui/core"; import { Launch } from "@material-ui/icons"; import { Alert } from "@material-ui/lab"; -import { Connection } from "@solana/web3.js"; import { useCallback, useEffect, useState } from "react"; import { useBetaContext } from "../contexts/BetaContext"; import { useEthereumProvider } from "../contexts/EthereumProviderContext"; import useIsWalletReady from "../hooks/useIsWalletReady"; -import { getMetaplexData } from "../hooks/useMetaplexData"; import { COLORS } from "../muiTheme"; import { NFTParsedTokenAccount } from "../store/nftSlice"; import { BETA_CHAINS, CHAINS_BY_ID, CHAINS_WITH_NFT_SUPPORT, - getNFTBridgeAddressForChain, - SOLANA_HOST, - SOL_NFT_BRIDGE_ADDRESS, + getNFTBridgeAddressForChain } from "../utils/consts"; import { ethNFTToNFTParsedTokenAccount, @@ -156,45 +148,6 @@ export default function NFTOriginVerifier() { } else { setLookupError("Invalid address"); } - } else if (lookupChain === CHAIN_ID_SOLANA && lookupAsset) { - (async () => { - try { - setIsLoading(true); - const [metadata] = await getMetaplexData([lookupAsset]); - if (metadata) { - const connection = new Connection(SOLANA_HOST, "confirmed"); - const info = await getOriginalAssetSol( - connection, - SOL_NFT_BRIDGE_ADDRESS, - lookupAsset - ); - if (!cancelled) { - setIsLoading(false); - setParsedTokenAccount({ - amount: "0", - decimals: 0, - mintKey: lookupAsset, - publicKey: "", - uiAmount: 0, - uiAmountString: "0", - uri: metadata.data.uri, - }); - setOriginInfo(info); - } - } else { - if (!cancelled) { - setIsLoading(false); - setLookupError("Error fetching metadata"); - } - } - } catch (e) { - console.error(e); - if (!cancelled) { - setIsLoading(false); - setLookupError("Invalid token"); - } - } - })(); } return () => { cancelled = true; diff --git a/bridge_ui/src/components/Recovery.tsx b/bridge_ui/src/components/Recovery.tsx index 166a8ae7b..2a8bb48f8 100644 --- a/bridge_ui/src/components/Recovery.tsx +++ b/bridge_ui/src/components/Recovery.tsx @@ -2,25 +2,15 @@ import { ChainId, CHAIN_ID_ALEPHIUM, CHAIN_ID_ACALA, - CHAIN_ID_ALGORAND, CHAIN_ID_KARURA, - CHAIN_ID_SOLANA, - CHAIN_ID_TERRA, - getEmitterAddressAlgorand, getEmitterAddressEth, - getEmitterAddressSolana, - getEmitterAddressTerra, hexToNativeAssetString, hexToNativeString, hexToUint8Array, isEVMChain, - parseSequenceFromLogAlgorand, parseSequenceFromLogEth, - parseSequenceFromLogSolana, - parseSequenceFromLogTerra, uint8ArrayToHex, parseTargetChainFromLogEth, - CHAIN_ID_UNSET, TransferToken, TransferNFT, deserializeTransferTokenVAA, @@ -41,9 +31,6 @@ import { } from "@material-ui/core"; import { ExpandMore } from "@material-ui/icons"; import { Alert } from "@material-ui/lab"; -import { Connection } from "@solana/web3.js"; -import { LCDClient } from "@terra-money/terra.js"; -import algosdk from "algosdk"; import axios from "axios"; import { ethers } from "ethers"; import { useSnackbar } from "notistack"; @@ -61,8 +48,6 @@ import { getAlphTxInfoByTxId } from "../utils/alephium"; import { ALEPHIUM_MINIMAL_CONSISTENCY_LEVEL, ALEPHIUM_TOKEN_BRIDGE_CONTRACT_ID, - ALGORAND_HOST, - ALGORAND_TOKEN_BRIDGE_ID, CHAINS, CHAINS_BY_ID, CHAINS_WITH_NFT_SUPPORT, @@ -70,11 +55,6 @@ import { getNFTBridgeAddressForChain, getTokenBridgeAddressForChain, RELAY_URL_EXTENSION, - SOLANA_HOST, - SOL_NFT_BRIDGE_ADDRESS, - SOL_TOKEN_BRIDGE_ADDRESS, - TERRA_HOST, - TERRA_TOKEN_BRIDGE_ADDRESS, WORMHOLE_RPC_HOSTS } from "../utils/consts"; import { getSignedVAAWithRetry } from "../utils/getSignedVAAWithRetry"; @@ -106,52 +86,6 @@ const useStyles = makeStyles((theme) => ({ }, })); -async function algo(tx: string, enqueueSnackbar: any) { - try { - const algodClient = new algosdk.Algodv2( - ALGORAND_HOST.algodToken, - ALGORAND_HOST.algodServer, - ALGORAND_HOST.algodPort - ); - const pendingInfo = await algodClient - .pendingTransactionInformation(tx) - .do(); - let confirmedTxInfo: Record | undefined = undefined; - // This is the code from waitForConfirmation - if (pendingInfo !== undefined) { - if ( - pendingInfo["confirmed-round"] !== null && - pendingInfo["confirmed-round"] > 0 - ) { - //Got the completed Transaction - confirmedTxInfo = pendingInfo; - } - } - if (!confirmedTxInfo) { - throw new Error(i18n.t("Transaction not found or not confirmed")); - } - const sequence = parseSequenceFromLogAlgorand(confirmedTxInfo); - if (!sequence) { - throw new Error(i18n.t("Sequence not found")); - } - const emitterAddress = getEmitterAddressAlgorand(ALGORAND_TOKEN_BRIDGE_ID); - const { vaaBytes } = await getSignedVAAWithRetry( - CHAIN_ID_ALGORAND, - emitterAddress, - CHAIN_ID_UNSET, - sequence, - WORMHOLE_RPC_HOSTS.length - ); - return { vaa: uint8ArrayToHex(vaaBytes), error: null }; - } catch (e) { - console.error(e); - enqueueSnackbar(null, { - content: {parseError(e)}, - }); - return { vaa: null, error: parseError(e) }; - } -} - async function evm( provider: ethers.providers.Web3Provider, tx: string, @@ -195,62 +129,6 @@ async function evm( } } -async function solana(tx: string, enqueueSnackbar: any, nft: boolean) { - try { - const connection = new Connection(SOLANA_HOST, "confirmed"); - const info = await connection.getTransaction(tx); - if (!info) { - throw new Error(i18n.t("An error occurred while fetching the transaction info")); - } - const sequence = parseSequenceFromLogSolana(info); - const emitterAddress = await getEmitterAddressSolana( - nft ? SOL_NFT_BRIDGE_ADDRESS : SOL_TOKEN_BRIDGE_ADDRESS - ); - const { vaaBytes } = await getSignedVAAWithRetry( - CHAIN_ID_SOLANA, - emitterAddress, - CHAIN_ID_UNSET, - sequence.toString(), - WORMHOLE_RPC_HOSTS.length - ); - return { vaa: uint8ArrayToHex(vaaBytes), error: null }; - } catch (e) { - console.error(e); - enqueueSnackbar(null, { - content: {parseError(e)}, - }); - return { vaa: null, error: parseError(e) }; - } -} - -async function terra(tx: string, enqueueSnackbar: any) { - try { - const lcd = new LCDClient(TERRA_HOST); - const info = await lcd.tx.txInfo(tx); - const sequence = parseSequenceFromLogTerra(info); - if (!sequence) { - throw new Error(i18n.t("Sequence not found")); - } - const emitterAddress = await getEmitterAddressTerra( - TERRA_TOKEN_BRIDGE_ADDRESS - ); - const { vaaBytes } = await getSignedVAAWithRetry( - CHAIN_ID_TERRA, - emitterAddress, - CHAIN_ID_UNSET, - sequence, - WORMHOLE_RPC_HOSTS.length - ); - return { vaa: uint8ArrayToHex(vaaBytes), error: null }; - } catch (e) { - console.error(e); - enqueueSnackbar(null, { - content: {parseError(e)}, - }); - return { vaa: null, error: parseError(e) }; - } -} - async function alephium(wallet: Wallet, txId: string, enqueueSnackbar: any) { try { if (wallet.nodeProvider === undefined) { @@ -501,40 +379,6 @@ export default function Recovery() { } } })(); - } else if (recoverySourceChain === CHAIN_ID_SOLANA) { - setRecoverySourceTxError(""); - setRecoverySourceTxIsLoading(true); - (async () => { - const { vaa, error } = await solana( - recoverySourceTx, - enqueueSnackbar, - isNFT - ); - if (!cancelled) { - setRecoverySourceTxIsLoading(false); - if (vaa) { - setRecoverySignedVAA(vaa); - } - if (error) { - setRecoverySourceTxError(error); - } - } - })(); - } else if (recoverySourceChain === CHAIN_ID_TERRA) { - setRecoverySourceTxError(""); - setRecoverySourceTxIsLoading(true); - (async () => { - const { vaa, error } = await terra(recoverySourceTx, enqueueSnackbar); - if (!cancelled) { - setRecoverySourceTxIsLoading(false); - if (vaa) { - setRecoverySignedVAA(vaa); - } - if (error) { - setRecoverySourceTxError(error); - } - } - })(); } else if (recoverySourceChain === CHAIN_ID_ALEPHIUM && isReady) { setRecoverySourceTxError(""); setRecoverySourceTxIsLoading(true); @@ -550,21 +394,6 @@ export default function Recovery() { } } })(alphWallet.nodeProvider); - } else if (recoverySourceChain === CHAIN_ID_ALGORAND) { - setRecoverySourceTxError(""); - setRecoverySourceTxIsLoading(true); - (async () => { - const { vaa, error } = await algo(recoverySourceTx, enqueueSnackbar); - if (!cancelled) { - setRecoverySourceTxIsLoading(false); - if (vaa) { - setRecoverySignedVAA(vaa); - } - if (error) { - setRecoverySourceTxError(error); - } - } - })(); } return () => { cancelled = true; diff --git a/bridge_ui/src/components/SmartAddress.tsx b/bridge_ui/src/components/SmartAddress.tsx index 84d9fbeb7..48efb7f54 100644 --- a/bridge_ui/src/components/SmartAddress.tsx +++ b/bridge_ui/src/components/SmartAddress.tsx @@ -14,7 +14,6 @@ import { CHAIN_ID_POLYGON, CHAIN_ID_SOLANA, CHAIN_ID_TERRA, - isNativeDenom, CHAIN_ID_ACALA, CHAIN_ID_ALEPHIUM, } from "@alephium/wormhole-sdk"; @@ -27,7 +26,6 @@ import useCopyToClipboard from "../hooks/useCopyToClipboard"; import { ParsedTokenAccount } from "../store/transferSlice"; import { CLUSTER, WETH_ADDRESS, getExplorerName } from "../utils/consts"; import { shortenAddress } from "../utils/solana"; -import { formatNativeDenom } from "../utils/terra"; import { addressFromContractId, ALPH_TOKEN_ID, isBase58 } from "@alephium/web3"; import { useTranslation } from "react-i18next"; @@ -92,16 +90,13 @@ export default function SmartAddress({ const { t } = useTranslation(); const classes = useStyles(); const isNativeETH = chainId === CHAIN_ID_ETH && address?.toLowerCase() === WETH_ADDRESS.toLowerCase() - const isNativeTerra = chainId === CHAIN_ID_TERRA && isNativeDenom(address); const isNativeALPH = chainId === CHAIN_ID_ALEPHIUM && address === ALPH_TOKEN_ID const useableAddress = parsedTokenAccount?.mintKey || address || ""; - const useableSymbol = isNativeTerra - ? formatNativeDenom(address) - : isNativeETH + const useableSymbol = isNativeETH ? 'ETH' : parsedTokenAccount?.symbol || symbol || ""; // const useableLogo = logo || isNativeTerra ? getNativeTerraIcon(useableSymbol) : null - const isNative = parsedTokenAccount?.isNativeAsset || isNativeETH || isNativeTerra || isNativeALPH || false; + const isNative = parsedTokenAccount?.isNativeAsset || isNativeETH || isNativeALPH || false; const addressShort = shortenAddress(useableAddress) || ""; const useableName = isNative diff --git a/bridge_ui/src/components/SolanaCreateAssociatedAddress.tsx b/bridge_ui/src/components/SolanaCreateAssociatedAddress.tsx index 01c43d7b2..70e927f36 100644 --- a/bridge_ui/src/components/SolanaCreateAssociatedAddress.tsx +++ b/bridge_ui/src/components/SolanaCreateAssociatedAddress.tsx @@ -1,10 +1,8 @@ import { ChainId, CHAIN_ID_SOLANA, - getForeignAssetSolana, hexToNativeAssetString, hexToNativeString, - hexToUint8Array, } from "@alephium/wormhole-sdk"; import { Button, Typography } from "@material-ui/core"; import { Alert } from "@material-ui/lab"; @@ -23,7 +21,7 @@ import { selectTransferOriginChain, selectTransferTargetAddressHex, } from "../store/selectors"; -import { SOLANA_HOST, SOL_TOKEN_BRIDGE_ADDRESS } from "../utils/consts"; +import { SOLANA_HOST } from "../utils/consts"; import parseError from "../utils/parseError"; import { signSendAndConfirm } from "../utils/solana"; import ButtonWithLoader from "./ButtonWithLoader"; @@ -183,27 +181,11 @@ export function SolanaCreateAssociatedAddressAlternate() { const [targetAsset, setTargetAsset] = useState(null); useEffect(() => { - let cancelled = false; if (!(originChain && originAsset && addressHex && base58TargetAddress)) { setTargetAsset(null); } else if (originChain === CHAIN_ID_SOLANA && base58OriginAddress) { setTargetAsset(base58OriginAddress); - } else { - getForeignAssetSolana( - connection, - SOL_TOKEN_BRIDGE_ADDRESS, - originChain, - hexToUint8Array(originAsset) - ).then((result) => { - if (!cancelled) { - setTargetAsset(result); - } - }); } - - return () => { - cancelled = true; - }; }, [ originChain, originAsset, diff --git a/bridge_ui/src/components/TerraFeeDenomPicker.tsx b/bridge_ui/src/components/TerraFeeDenomPicker.tsx deleted file mode 100644 index 820bdd26d..000000000 --- a/bridge_ui/src/components/TerraFeeDenomPicker.tsx +++ /dev/null @@ -1,107 +0,0 @@ -import { - MenuItem, - makeStyles, - TextField, - Typography, - ListItemIcon, -} from "@material-ui/core"; -import { useConnectedWallet } from "@terra-money/wallet-provider"; -import { useMemo } from "react"; -import { useDispatch, useSelector } from "react-redux"; -import { setTerraFeeDenom } from "../store/feeSlice"; -import { selectTerraFeeDenom } from "../store/selectors"; -import useTerraNativeBalances from "../hooks/useTerraNativeBalances"; -import { formatNativeDenom, getNativeTerraIcon } from "../utils/terra"; - -const useStyles = makeStyles((theme) => ({ - feePickerContainer: { - display: "flex", - flexDirection: "column", - margin: `${theme.spacing(1)}px auto`, - maxWidth: 200, - width: "100%", - }, - select: { - "& .MuiSelect-root": { - display: "flex", - alignItems: "center", - }, - }, - listItemIcon: { - minWidth: 40, - }, - icon: { - height: 24, - maxWidth: 24, - }, -})); - -type TerraFeeDenomPickerProps = { - disabled: boolean; -}; - -export default function TerraFeeDenomPicker(props: TerraFeeDenomPickerProps) { - const terraFeeDenom = useSelector(selectTerraFeeDenom); - const wallet = useConnectedWallet(); - const { balances } = useTerraNativeBalances(wallet?.walletAddress); - const dispatch = useDispatch(); - const classes = useStyles(); - - const feeDenomItems = useMemo(() => { - const items = []; - if (balances) { - for (const [denom, amount] of Object.entries(balances)) { - if (amount === "0") continue; - const symbol = formatNativeDenom(denom); - if (symbol) { - items.push({ - denom, - symbol, - icon: getNativeTerraIcon(symbol), - }); - } - } - } - // prevent an out-of-range value from being selected - if (!items.find((item) => item.denom === terraFeeDenom)) { - const symbol = formatNativeDenom(terraFeeDenom); - items.push({ - denom: terraFeeDenom, - symbol, - icon: getNativeTerraIcon(symbol), - }); - } - return items; - }, [balances, terraFeeDenom]); - - return ( -
- Fee Denomination - dispatch(setTerraFeeDenom(event.target.value))} - disabled={props.disabled} - className={classes.select} - > - {feeDenomItems.map((item) => { - return ( - - - {item.symbol} - - {item.symbol} - - ); - })} - -
- ); -} diff --git a/bridge_ui/src/components/TokenSelectors/SourceTokenSelector.tsx b/bridge_ui/src/components/TokenSelectors/SourceTokenSelector.tsx index 7434182b0..49906ae48 100644 --- a/bridge_ui/src/components/TokenSelectors/SourceTokenSelector.tsx +++ b/bridge_ui/src/components/TokenSelectors/SourceTokenSelector.tsx @@ -32,7 +32,6 @@ import AlgoTokenPicker from "./AlgoTokenPicker"; import EvmTokenPicker from "./EvmTokenPicker"; import RefreshButtonWrapper from "./RefreshButtonWrapper"; import SolanaTokenPicker from "./SolanaTokenPicker"; -import TerraTokenPicker from "./TerraTokenPicker"; type TokenSelectorProps = { disabled: boolean; @@ -112,14 +111,6 @@ export const TokenSelector = (props: TokenSelectorProps) => { chainId={lookupChain} nft={nft} /> - ) : lookupChain === CHAIN_ID_TERRA ? ( - ) : lookupChain === CHAIN_ID_ALEPHIUM ? ( void; - tokenAccounts: DataWrapper | undefined; - disabled: boolean; - resetAccounts: (() => void) | undefined; -}; - -const returnsFalse = () => false; - -export default function TerraTokenPicker(props: TerraTokenPickerProps) { - const { value, onChange, disabled } = props; - const { walletAddress } = useIsWalletReady(CHAIN_ID_TERRA); - const nativeRefresh = useRef<() => void>(() => {}); - const { balances, isLoading: nativeIsLoading } = useTerraNativeBalances( - walletAddress, - nativeRefresh - ); - - const resetAccountWrapper = useCallback(() => { - //we can currently skip calling this as we don't read from sourceParsedTokenAccounts - //resetAccounts && resetAccounts(); - nativeRefresh.current(); - }, []); - const isLoading = nativeIsLoading; // || (tokenMap?.isFetching || false); - - const onChangeWrapper = useCallback( - async (account: NFTParsedTokenAccount | null) => { - if (account === null) { - onChange(null); - return Promise.resolve(); - } - onChange(account); - return Promise.resolve(); - }, - [onChange] - ); - - const terraTokenArray = useMemo(() => { - const balancesItems = - balances && walletAddress - ? Object.keys(balances).map((denom) => - // ({ - // protocol: "native", - // symbol: formatNativeDenom(denom), - // token: denom, - // icon: getNativeTerraIcon(formatNativeDenom(denom)), - // balance: balances[denom], - // } as TerraTokenMetadata) - - //TODO support non-natives in the SUPPORTED_TERRA_TOKENS - //This token account makes a lot of assumptions - createParsedTokenAccount( - walletAddress, - denom, - balances[denom], //amount - NATIVE_TERRA_DECIMALS, //TODO actually get decimals rather than hardcode - 0, //uiAmount is unused - formatUnits(balances[denom], NATIVE_TERRA_DECIMALS), //uiAmountString - formatNativeDenom(denom), // symbol - undefined, //name - getNativeTerraIcon(formatNativeDenom(denom)), //logo - true //is native asset - ) - ) - : []; - return balancesItems.filter((metadata) => - SUPPORTED_TERRA_TOKENS.includes(metadata.mintKey) - ); - // const values = tokenMap.data?.mainnet; - // const tokenMapItems = Object.values(values || {}) || []; - // return [...balancesItems, ...tokenMapItems]; - }, [ - walletAddress, - balances, - // tokenMap - ]); - - //TODO this only supports non-native assets. Native assets come from the hook. - //TODO correlate against token list to get metadata - const lookupTerraAddress = useCallback( - (lookupAsset: string) => { - if (!walletAddress) { - return Promise.reject("Wallet not connected"); - } - const lcd = new LCDClient(TERRA_HOST); - return lcd.wasm - .contractQuery(lookupAsset, { - token_info: {}, - }) - .then((info: any) => - lcd.wasm - .contractQuery(lookupAsset, { - balance: { - address: walletAddress, - }, - }) - .then((balance: any) => { - if (balance && info) { - return createParsedTokenAccount( - walletAddress, - lookupAsset, - balance.balance.toString(), - info.decimals, - Number(formatUnits(balance.balance, info.decimals)), - formatUnits(balance.balance, info.decimals), - info.symbol, - info.name - ); - } else { - throw new Error("Failed to retrieve Terra account."); - } - }) - ) - .catch(() => { - return Promise.reject(); - }); - }, - [walletAddress] - ); - - const isSearchableAddress = useCallback((address: string) => { - return isValidTerraAddress(address) && !isNativeDenom(address); - }, []); - - const RenderComp = useCallback( - ({ account }: { account: NFTParsedTokenAccount }) => { - return BasicAccountRender(account, returnsFalse, false); - }, - [] - ); - - return ( - - ); -} diff --git a/bridge_ui/src/components/Transfer/Redeem.tsx b/bridge_ui/src/components/Transfer/Redeem.tsx index e4193cc36..8633b41cb 100644 --- a/bridge_ui/src/components/Transfer/Redeem.tsx +++ b/bridge_ui/src/components/Transfer/Redeem.tsx @@ -13,7 +13,6 @@ import { CHAIN_ID_OASIS, CHAIN_ID_POLYGON, CHAIN_ID_SOLANA, - CHAIN_ID_TERRA, isEVMChain, WSOL_ADDRESS, } from "@alephium/wormhole-sdk"; @@ -61,7 +60,6 @@ import SmartAddress from "../SmartAddress"; import { SolanaCreateAssociatedAddressAlternate } from "../SolanaCreateAssociatedAddress"; import SolanaTPSWarning from "../SolanaTPSWarning"; import StepDescription from "../StepDescription"; -import TerraFeeDenomPicker from "../TerraFeeDenomPicker"; import AddToMetamask from "./AddToMetamask"; import RedeemPreview from "./RedeemPreview"; import WaitingForWalletMessage from "./WaitingForWalletMessage"; @@ -253,9 +251,6 @@ function Redeem() { const nonRelayContent = ( <> - {targetChain === CHAIN_ID_TERRA && ( - - )} {isNativeEligible && ( - {sourceChain === CHAIN_ID_TERRA && ( - - )} {t("This will initiate the transfer on {{ chainName }} and wait for finalization. If you navigate away from this page before completing Step 4, you will have to perform the recovery workflow to complete the transfer.", { chainName: CHAINS_BY_ID[sourceChain].name })} diff --git a/bridge_ui/src/components/WithdrawTokensTerra.tsx b/bridge_ui/src/components/WithdrawTokensTerra.tsx deleted file mode 100644 index c4637ce84..000000000 --- a/bridge_ui/src/components/WithdrawTokensTerra.tsx +++ /dev/null @@ -1,140 +0,0 @@ -import { useCallback, useState } from "react"; -import { MsgExecuteContract } from "@terra-money/terra.js"; -import { - ConnectedWallet, - useConnectedWallet, -} from "@terra-money/wallet-provider"; -import { - SUPPORTED_TERRA_TOKENS, - TERRA_TOKEN_BRIDGE_ADDRESS, -} from "../utils/consts"; -import TerraWalletKey from "./TerraWalletKey"; -import { - Container, - FormControl, - InputLabel, - makeStyles, - MenuItem, - Paper, - Select, - Typography, -} from "@material-ui/core"; -import { postWithFees, waitForTerraExecution } from "../utils/terra"; -import ButtonWithLoader from "./ButtonWithLoader"; -import { useSnackbar } from "notistack"; -import { Alert } from "@material-ui/lab"; -import { useSelector } from "react-redux"; -import { selectTerraFeeDenom } from "../store/selectors"; -import TerraFeeDenomPicker from "./TerraFeeDenomPicker"; -import HeaderText from "./HeaderText"; -import { COLORS } from "../muiTheme"; - -const useStyles = makeStyles((theme) => ({ - formControl: { - display: "flex", - margin: `${theme.spacing(1)}px auto`, - width: "100%", - maxWidth: 400, - textAlign: "center", - }, - mainPaper: { - backgroundColor: COLORS.whiteWithTransparency, - textAlign: "center", - padding: "2rem", - "& > h, p ": { - margin: ".5rem", - }, - }, -})); - -const withdraw = async ( - wallet: ConnectedWallet, - token: string, - feeDenom: string -) => { - const withdraw = new MsgExecuteContract( - wallet.walletAddress, - TERRA_TOKEN_BRIDGE_ADDRESS, - { - withdraw_tokens: { - asset: { - native_token: { - denom: token, - }, - }, - }, - }, - {} - ); - const txResult = await postWithFees( - wallet, - [withdraw], - "Wormhole - Withdraw Tokens", - [feeDenom] - ); - await waitForTerraExecution(txResult); -}; - -export default function WithdrawTokensTerra() { - const wallet = useConnectedWallet(); - const [token, setToken] = useState(SUPPORTED_TERRA_TOKENS[0]); - const [isLoading, setIsLoading] = useState(false); - const classes = useStyles(); - const { enqueueSnackbar } = useSnackbar(); - const feeDenom = useSelector(selectTerraFeeDenom); - - const handleClick = useCallback(() => { - if (wallet) { - (async () => { - setIsLoading(true); - try { - await withdraw(wallet, token, feeDenom); - enqueueSnackbar(null, { - content: Transaction confirmed., - }); - } catch (e) { - enqueueSnackbar(null, { - content: Error withdrawing tokens., - }); - console.error(e); - } - setIsLoading(false); - })(); - } - }, [wallet, token, enqueueSnackbar, feeDenom]); - - return ( - - Withdraw Tokens - - - Withdraw tokens from the Terra token bridge - - - - Token - - - - Withdraw - - - - - ); -} diff --git a/bridge_ui/src/hooks/useCheckIfWormholeWrapped.ts b/bridge_ui/src/hooks/useCheckIfWormholeWrapped.ts index 5085302d7..7e5646c20 100644 --- a/bridge_ui/src/hooks/useCheckIfWormholeWrapped.ts +++ b/bridge_ui/src/hooks/useCheckIfWormholeWrapped.ts @@ -1,13 +1,7 @@ import { ChainId, CHAIN_ID_ALEPHIUM, - CHAIN_ID_ALGORAND, - CHAIN_ID_SOLANA, - CHAIN_ID_TERRA, - getOriginalAssetAlgorand, getOriginalAssetEth, - getOriginalAssetSol, - getOriginalAssetTerra, isEVMChain, uint8ArrayToHex, WormholeWrappedInfo, @@ -15,10 +9,7 @@ import { } from "@alephium/wormhole-sdk"; import { getOriginalAssetEth as getOriginalAssetEthNFT, - getOriginalAssetSol as getOriginalAssetSolNFT, } from "@alephium/wormhole-sdk/lib/esm/nft_bridge"; -import { Connection } from "@solana/web3.js"; -import { LCDClient } from "@terra-money/terra.js"; import { useEffect } from "react"; import { useDispatch, useSelector } from "react-redux"; import { useEthereumProvider } from "../contexts/EthereumProviderContext"; @@ -34,18 +25,11 @@ import { } from "../store/selectors"; import { setSourceWormholeWrappedInfo as setTransferSourceWormholeWrappedInfo } from "../store/transferSlice"; import { - ALGORAND_HOST, - ALGORAND_TOKEN_BRIDGE_ID, getNFTBridgeAddressForChain, - getTokenBridgeAddressForChain, - SOLANA_HOST, - SOL_NFT_BRIDGE_ADDRESS, - SOL_TOKEN_BRIDGE_ADDRESS, - TERRA_HOST, + getTokenBridgeAddressForChain } from "../utils/consts"; import { NodeProvider } from '@alephium/web3' import { getAlephiumTokenWrappedInfo } from "../utils/alephium"; -import { Algodv2 } from "algosdk"; import { errorDataWrapper, fetchDataWrapper, receiveDataWrapper } from "../store/helpers"; import { useWallet } from "@alephium/web3-react"; import { useTranslation } from "react-i18next"; @@ -148,48 +132,6 @@ function useCheckIfWormholeWrapped(nft?: boolean) { } } } - if (sourceChain === CHAIN_ID_SOLANA && sourceAsset) { - try { - dispatch(setSourceWormholeWrappedInfo(fetchDataWrapper())); - const connection = new Connection(SOLANA_HOST, "confirmed"); - const wrappedInfo = makeStateSafe( - await (nft - ? getOriginalAssetSolNFT( - connection, - SOL_NFT_BRIDGE_ADDRESS, - sourceAsset - ) - : getOriginalAssetSol( - connection, - SOL_TOKEN_BRIDGE_ADDRESS, - sourceAsset - )) - ); - if (!cancelled) { - dispatch(setSourceWormholeWrappedInfo(receiveDataWrapper(wrappedInfo))); - } - } catch (e) { - if (!cancelled) { - dispatch(setSourceWormholeWrappedInfo(errorDataWrapper(`${t('Failed to get source asset info from solana')}, ${t('Error')}: ${e}`))); - } - } - } - if (sourceChain === CHAIN_ID_TERRA && sourceAsset) { - try { - dispatch(setSourceWormholeWrappedInfo(fetchDataWrapper())); - const lcd = new LCDClient(TERRA_HOST); - const wrappedInfo = makeStateSafe( - await getOriginalAssetTerra(lcd, sourceAsset) - ); - if (!cancelled) { - dispatch(setSourceWormholeWrappedInfo(receiveDataWrapper(wrappedInfo))); - } - } catch (e) { - if (!cancelled) { - dispatch(setSourceWormholeWrappedInfo(errorDataWrapper(`${t('Failed to get source asset info from terra')}, ${t('Error')}: ${e}`))); - } - } - } if (sourceChain === CHAIN_ID_ALEPHIUM && sourceAsset && alphWallet?.nodeProvider !== undefined) { try { dispatch(setSourceWormholeWrappedInfo(fetchDataWrapper())); @@ -203,30 +145,6 @@ function useCheckIfWormholeWrapped(nft?: boolean) { } } } - if (sourceChain === CHAIN_ID_ALGORAND && sourceAsset) { - try { - dispatch(setSourceWormholeWrappedInfo(fetchDataWrapper())); - const algodClient = new Algodv2( - ALGORAND_HOST.algodToken, - ALGORAND_HOST.algodServer, - ALGORAND_HOST.algodPort - ); - const wrappedInfo = makeStateSafe( - await getOriginalAssetAlgorand( - algodClient, - ALGORAND_TOKEN_BRIDGE_ID, - BigInt(sourceAsset) - ) - ); - if (!cancelled) { - dispatch(setSourceWormholeWrappedInfo(receiveDataWrapper(wrappedInfo))); - } - } catch (e) { - if (!cancelled) { - dispatch(setSourceWormholeWrappedInfo(errorDataWrapper(`${t('Failed to get source asset info from algorand')}, ${t('Error')}: ${e}`))); - } - } - } })(); return () => { cancelled = true; diff --git a/bridge_ui/src/hooks/useFetchForeignAsset.ts b/bridge_ui/src/hooks/useFetchForeignAsset.ts index ffe84c184..0b20e2cd0 100644 --- a/bridge_ui/src/hooks/useFetchForeignAsset.ts +++ b/bridge_ui/src/hooks/useFetchForeignAsset.ts @@ -1,20 +1,12 @@ import { ChainId, CHAIN_ID_ALEPHIUM, - CHAIN_ID_ALGORAND, - CHAIN_ID_SOLANA, - CHAIN_ID_TERRA, getForeignAssetAlephium, - getForeignAssetAlgorand, getForeignAssetEth, - getForeignAssetSolana, - getForeignAssetTerra, hexToUint8Array, isEVMChain, nativeToHexString, } from "@alephium/wormhole-sdk"; -import { Connection } from "@solana/web3.js"; -import { LCDClient } from "@terra-money/terra.js"; import { ethers } from "ethers"; import { useCallback, useEffect, useMemo, useState } from "react"; import { useEthereumProvider } from "../contexts/EthereumProviderContext"; @@ -22,17 +14,10 @@ import { DataWrapper } from "../store/helpers"; import { ALEPHIUM_BRIDGE_GROUP_INDEX, ALEPHIUM_TOKEN_BRIDGE_CONTRACT_ID, - ALGORAND_HOST, - ALGORAND_TOKEN_BRIDGE_ID, getEvmChainId, - getTokenBridgeAddressForChain, - SOLANA_HOST, - SOL_TOKEN_BRIDGE_ADDRESS, - TERRA_HOST, - TERRA_TOKEN_BRIDGE_ADDRESS, + getTokenBridgeAddressForChain } from "../utils/consts"; import useIsWalletReady from "./useIsWalletReady"; -import { Algodv2 } from "algosdk"; import { useWallet } from "@alephium/web3-react"; import { useTranslation } from "react-i18next"; @@ -133,40 +118,6 @@ function useFetchForeignAsset( ALEPHIUM_BRIDGE_GROUP_INDEX ) } - : foreignChain === CHAIN_ID_TERRA - ? () => { - const lcd = new LCDClient(TERRA_HOST); - return getForeignAssetTerra( - TERRA_TOKEN_BRIDGE_ADDRESS, - lcd, - originChain, - hexToUint8Array(originAssetHex) - ); - } - : foreignChain === CHAIN_ID_SOLANA - ? () => { - const connection = new Connection(SOLANA_HOST, "confirmed"); - return getForeignAssetSolana( - connection, - SOL_TOKEN_BRIDGE_ADDRESS, - originChain, - hexToUint8Array(originAssetHex) - ); - } - : foreignChain === CHAIN_ID_ALGORAND - ? () => { - const algodClient = new Algodv2( - ALGORAND_HOST.algodToken, - ALGORAND_HOST.algodServer, - ALGORAND_HOST.algodPort - ); - return getForeignAssetAlgorand( - algodClient, - ALGORAND_TOKEN_BRIDGE_ID, - originChain, - originAssetHex - ); - } : () => Promise.resolve(null); getterFunc() diff --git a/bridge_ui/src/hooks/useFetchTargetAsset.ts b/bridge_ui/src/hooks/useFetchTargetAsset.ts index 93f1eff20..70ff2b41a 100644 --- a/bridge_ui/src/hooks/useFetchTargetAsset.ts +++ b/bridge_ui/src/hooks/useFetchTargetAsset.ts @@ -1,27 +1,13 @@ import { ChainId, CHAIN_ID_ALEPHIUM, - CHAIN_ID_ALGORAND, - CHAIN_ID_SOLANA, - CHAIN_ID_TERRA, getForeignAssetAlephium, - getForeignAssetAlgorand, getForeignAssetEth, - getForeignAssetSolana, - getForeignAssetTerra, hexToNativeAssetString, hexToUint8Array, isEVMChain, } from "@alephium/wormhole-sdk"; -import { - getForeignAssetEth as getForeignAssetEthNFT, - getForeignAssetSol as getForeignAssetSolNFT, -} from "@alephium/wormhole-sdk/lib/esm/nft_bridge"; -import { BigNumber } from "@ethersproject/bignumber"; -import { arrayify } from "@ethersproject/bytes"; -import { Connection } from "@solana/web3.js"; -import { LCDClient } from "@terra-money/terra.js"; -import algosdk from "algosdk"; +import { getForeignAssetEth as getForeignAssetEthNFT } from "@alephium/wormhole-sdk/lib/esm/nft_bridge"; import { ethers } from "ethers"; import { useCallback, useEffect, useState } from "react"; import { useDispatch, useSelector } from "react-redux"; @@ -47,16 +33,9 @@ import { setTargetAsset as setTransferTargetAsset } from "../store/transferSlice import { ALEPHIUM_BRIDGE_GROUP_INDEX, ALEPHIUM_TOKEN_BRIDGE_CONTRACT_ID, - ALGORAND_HOST, - ALGORAND_TOKEN_BRIDGE_ID, getEvmChainId, getNFTBridgeAddressForChain, getTokenBridgeAddressForChain, - SOLANA_HOST, - SOL_NFT_BRIDGE_ADDRESS, - SOL_TOKEN_BRIDGE_ADDRESS, - TERRA_HOST, - TERRA_TOKEN_BRIDGE_ADDRESS, } from "../utils/consts"; import { useWallet } from "@alephium/web3-react"; import { useTranslation } from "react-i18next"; @@ -189,73 +168,6 @@ function useFetchTargetAsset(nft?: boolean) { } } } - if (targetChain === CHAIN_ID_SOLANA && originChain && originAsset) { - dispatch(setTargetAsset(fetchDataWrapper())); - try { - const connection = new Connection(SOLANA_HOST, "confirmed"); - const asset = await (nft - ? getForeignAssetSolNFT( - SOL_NFT_BRIDGE_ADDRESS, - originChain, - hexToUint8Array(originAsset), - arrayify(BigNumber.from(tokenId || "0")) - ) - : getForeignAssetSolana( - connection, - SOL_TOKEN_BRIDGE_ADDRESS, - originChain, - hexToUint8Array(originAsset) - )); - if (!cancelled) { - dispatch( - setTargetAsset( - receiveDataWrapper({ doesExist: !!asset, address: asset }) - ) - ); - setArgs(); - } - } catch (e) { - if (!cancelled) { - dispatch( - setTargetAsset( - errorDataWrapper( - t("Unable to determine existence of wrapped asset") - ) - ) - ); - } - } - } - if (targetChain === CHAIN_ID_TERRA && originChain && originAsset) { - dispatch(setTargetAsset(fetchDataWrapper())); - try { - const lcd = new LCDClient(TERRA_HOST); - const asset = await getForeignAssetTerra( - TERRA_TOKEN_BRIDGE_ADDRESS, - lcd, - originChain, - hexToUint8Array(originAsset) - ); - if (!cancelled) { - dispatch( - setTargetAsset( - receiveDataWrapper({ doesExist: !!asset, address: asset }) - ) - ); - setArgs(); - } - } catch (e) { - if (!cancelled) { - dispatch( - setTargetAsset( - errorDataWrapper( - t("Unable to determine existence of wrapped asset") - ) - ) - ); - } - } - } if (targetChain === CHAIN_ID_ALEPHIUM && originChain && originAsset && alphWallet?.nodeProvider !== undefined) { dispatch(setTargetAsset(fetchDataWrapper())) try { @@ -284,45 +196,6 @@ function useFetchTargetAsset(nft?: boolean) { } } } - if (targetChain === CHAIN_ID_ALGORAND && originChain && originAsset) { - dispatch(setTargetAsset(fetchDataWrapper())); - try { - const algodClient = new algosdk.Algodv2( - ALGORAND_HOST.algodToken, - ALGORAND_HOST.algodServer, - ALGORAND_HOST.algodPort - ); - const asset = await getForeignAssetAlgorand( - algodClient, - ALGORAND_TOKEN_BRIDGE_ID, - originChain, - originAsset - ); - console.log("foreign asset algo:", asset); - if (!cancelled) { - dispatch( - setTargetAsset( - receiveDataWrapper({ - doesExist: !!asset, - address: asset === null ? asset : asset.toString(), - }) - ) - ); - setArgs(); - } - } catch (e) { - console.error(e); - if (!cancelled) { - dispatch( - setTargetAsset( - errorDataWrapper( - t("Unable to determine existence of wrapped asset") - ) - ) - ); - } - } - } })(); return () => { cancelled = true; diff --git a/bridge_ui/src/hooks/useGetIsTransferCompleted.ts b/bridge_ui/src/hooks/useGetIsTransferCompleted.ts index 28c67c99e..59ab0e938 100644 --- a/bridge_ui/src/hooks/useGetIsTransferCompleted.ts +++ b/bridge_ui/src/hooks/useGetIsTransferCompleted.ts @@ -1,19 +1,10 @@ import { CHAIN_ID_ALEPHIUM, - CHAIN_ID_ALGORAND, - CHAIN_ID_SOLANA, - CHAIN_ID_TERRA, - getIsTransferCompletedAlgorand, getIsTransferCompletedEth, - getIsTransferCompletedSolana, - getIsTransferCompletedTerra, getIsTransferCompletedAlph, isEVMChain, getTokenBridgeForChainId, } from "@alephium/wormhole-sdk"; -import { Connection } from "@solana/web3.js"; -import { LCDClient } from "@terra-money/terra.js"; -import algosdk from "algosdk"; import { useEffect, useState } from "react"; import { useSelector } from "react-redux"; import { useEthereumProvider } from "../contexts/EthereumProviderContext"; @@ -26,13 +17,8 @@ import { import { ALEPHIUM_BRIDGE_GROUP_INDEX, ALEPHIUM_TOKEN_BRIDGE_CONTRACT_ID, - ALGORAND_HOST, - ALGORAND_TOKEN_BRIDGE_ID, getEvmChainId, - getTokenBridgeAddressForChain, - SOLANA_HOST, - TERRA_GAS_PRICES_URL, - TERRA_HOST, + getTokenBridgeAddressForChain } from "../utils/consts"; import useIsWalletReady from "./useIsWalletReady"; import useTransferSignedVAA from "./useTransferSignedVAA"; @@ -116,43 +102,6 @@ export default function useGetIsTransferCompleted( setIsLoading(false); } })(); - } else if (targetChain === CHAIN_ID_SOLANA) { - setIsLoading(true); - (async () => { - try { - const connection = new Connection(SOLANA_HOST, "confirmed"); - transferCompleted = await getIsTransferCompletedSolana( - getTokenBridgeAddressForChain(targetChain), - signedVAA, - connection - ); - } catch (error) { - console.error(error); - } - if (!cancelled) { - setIsTransferCompleted(transferCompleted); - setIsLoading(false); - } - })(); - } else if (targetChain === CHAIN_ID_TERRA) { - setIsLoading(true); - (async () => { - try { - const lcdClient = new LCDClient(TERRA_HOST); - transferCompleted = await getIsTransferCompletedTerra( - getTokenBridgeAddressForChain(targetChain), - signedVAA, - lcdClient, - TERRA_GAS_PRICES_URL - ); - } catch (error) { - console.error(error); - } - if (!cancelled) { - setIsTransferCompleted(transferCompleted); - setIsLoading(false); - } - })(); } else if (targetChain === CHAIN_ID_ALEPHIUM && alphWallet.connectionStatus === 'connected') { setIsLoading(true); (async () => { @@ -177,28 +126,6 @@ export default function useGetIsTransferCompleted( setIsLoading(false) } })() - } else if (targetChain === CHAIN_ID_ALGORAND) { - setIsLoading(true); - (async () => { - try { - const algodClient = new algosdk.Algodv2( - ALGORAND_HOST.algodToken, - ALGORAND_HOST.algodServer, - ALGORAND_HOST.algodPort - ); - transferCompleted = await getIsTransferCompletedAlgorand( - algodClient, - ALGORAND_TOKEN_BRIDGE_ID, - signedVAA - ); - } catch (error) { - console.error(error); - } - if (!cancelled) { - setIsTransferCompleted(transferCompleted); - setIsLoading(false); - } - })(); } } return () => { diff --git a/bridge_ui/src/hooks/useGetTargetParsedTokenAccounts.ts b/bridge_ui/src/hooks/useGetTargetParsedTokenAccounts.ts index 64e9dd0ab..d355902a4 100644 --- a/bridge_ui/src/hooks/useGetTargetParsedTokenAccounts.ts +++ b/bridge_ui/src/hooks/useGetTargetParsedTokenAccounts.ts @@ -1,14 +1,5 @@ -import { - CHAIN_ID_ALEPHIUM, - CHAIN_ID_ALGORAND, - CHAIN_ID_SOLANA, - CHAIN_ID_TERRA, - isEVMChain, - isNativeDenom -} from "@alephium/wormhole-sdk"; +import { CHAIN_ID_ALEPHIUM, CHAIN_ID_ALGORAND, CHAIN_ID_SOLANA, isEVMChain } from "@alephium/wormhole-sdk"; import { Connection, PublicKey } from "@solana/web3.js"; -import { LCDClient } from "@terra-money/terra.js"; -import { useConnectedWallet } from "@terra-money/wallet-provider"; import { formatUnits } from "ethers/lib/utils"; import { useEffect, useMemo } from "react"; import { useDispatch, useSelector } from "react-redux"; @@ -24,10 +15,8 @@ import { setTargetParsedTokenAccount } from "../store/transferSlice"; import { ALGORAND_HOST, getEvmChainId, - SOLANA_HOST, - TERRA_HOST, + SOLANA_HOST } from "../utils/consts"; -import { NATIVE_TERRA_DECIMALS } from "../utils/terra"; import { createParsedTokenAccount } from "./useGetSourceParsedTokenAccounts"; import useMetadata from "./useMetadata"; import { Algodv2 } from "algosdk"; @@ -44,7 +33,6 @@ function useGetTargetParsedTokenAccounts() { ); const solanaWallet = useSolanaWallet(); const solPK = solanaWallet?.publicKey; - const terraWallet = useConnectedWallet(); const alphWallet = useWallet(); const { provider, @@ -100,75 +88,6 @@ function useGetTargetParsedTokenAccounts() { ) } } - if (targetChain === CHAIN_ID_TERRA && terraWallet) { - const lcd = new LCDClient(TERRA_HOST); - if (isNativeDenom(targetAsset)) { - lcd.bank - .balance(terraWallet.walletAddress) - .then(([coins]) => { - const balance = coins.get(targetAsset)?.amount?.toString(); - if (balance && !cancelled) { - dispatch( - setTargetParsedTokenAccount( - createParsedTokenAccount( - "", - "", - balance, - NATIVE_TERRA_DECIMALS, - Number(formatUnits(balance, NATIVE_TERRA_DECIMALS)), - formatUnits(balance, NATIVE_TERRA_DECIMALS), - symbol, - tokenName, - logo - ) - ) - ); - } - }) - .catch(() => { - if (!cancelled) { - // TODO: error state - } - }); - } else { - lcd.wasm - .contractQuery(targetAsset, { - token_info: {}, - }) - .then((info: any) => - lcd.wasm - .contractQuery(targetAsset, { - balance: { - address: terraWallet.walletAddress, - }, - }) - .then((balance: any) => { - if (balance && info && !cancelled) { - dispatch( - setTargetParsedTokenAccount( - createParsedTokenAccount( - "", - "", - balance.balance.toString(), - info.decimals, - Number(formatUnits(balance.balance, info.decimals)), - formatUnits(balance.balance, info.decimals), - symbol, - tokenName, - logo - ) - ) - ); - } - }) - ) - .catch(() => { - if (!cancelled) { - // TODO: error state - } - }); - } - } if (targetChain === CHAIN_ID_SOLANA && solPK) { let mint; try { @@ -304,7 +223,6 @@ function useGetTargetParsedTokenAccounts() { signerAddress, solanaWallet, solPK, - terraWallet, alphWallet, hasCorrectEvmNetwork, hasResolvedMetadata, diff --git a/bridge_ui/src/hooks/useHandleAttest.tsx b/bridge_ui/src/hooks/useHandleAttest.tsx index d7f26d5b7..a48b7ecc7 100644 --- a/bridge_ui/src/hooks/useHandleAttest.tsx +++ b/bridge_ui/src/hooks/useHandleAttest.tsx @@ -1,44 +1,22 @@ import { attestFromAlph, - attestFromAlgorand, - attestFromSolana, - attestFromTerra, ChainId, - CHAIN_ID_ALGORAND, CHAIN_ID_KLAYTN, - CHAIN_ID_SOLANA, - CHAIN_ID_TERRA, CHAIN_ID_ALEPHIUM, - getEmitterAddressAlgorand, getEmitterAddressEth, - getEmitterAddressSolana, - getEmitterAddressTerra, isEVMChain, - parseSequenceFromLogAlgorand, parseSequenceFromLogEth, - parseSequenceFromLogSolana, - parseSequenceFromLogTerra, uint8ArrayToHex, parseTargetChainFromLogEth, CHAIN_ID_ETH, CHAIN_ID_BSC } from "@alephium/wormhole-sdk"; -import { CHAIN_ID_UNSET } from "@alephium/wormhole-sdk/lib/esm"; import { Alert } from "@material-ui/lab"; -import { WalletContextState } from "@solana/wallet-adapter-react"; -import { Connection, PublicKey } from "@solana/web3.js"; -import { - ConnectedWallet, - useConnectedWallet, -} from "@terra-money/wallet-provider"; -import algosdk from "algosdk"; import { Signer } from "ethers"; import { useSnackbar } from "notistack"; import { useCallback, useMemo } from "react"; import { useDispatch, useSelector } from "react-redux"; -import { useAlgorandContext } from "../contexts/AlgorandWalletContext"; import { useEthereumProvider } from "../contexts/EthereumProviderContext"; -import { useSolanaWallet } from "../contexts/SolanaWalletContext"; import { setAttestTx, setIsSending, @@ -50,89 +28,22 @@ import { selectAttestIsSending, selectAttestIsTargetComplete, selectAttestSourceAsset, - selectAttestSourceChain, - selectTerraFeeDenom, + selectAttestSourceChain } from "../store/selectors"; import { getAndCheckLocalTokenInfo, isValidAlephiumTokenId, waitALPHTxConfirmed, waitTxConfirmedAndGetTxInfo } from "../utils/alephium"; -import { signSendAndConfirmAlgorand } from "../utils/algorand"; import { ALEPHIUM_ATTEST_TOKEN_CONSISTENCY_LEVEL, ALEPHIUM_TOKEN_BRIDGE_CONTRACT_ID, ALEPHIUM_MESSAGE_FEE, - ALGORAND_BRIDGE_ID, - ALGORAND_HOST, - ALGORAND_TOKEN_BRIDGE_ID, getBridgeAddressForChain, getTokenBridgeAddressForChain, - SOLANA_HOST, - SOL_BRIDGE_ADDRESS, - SOL_TOKEN_BRIDGE_ADDRESS, - TERRA_TOKEN_BRIDGE_ADDRESS } from "../utils/consts"; import { getSignedVAAWithRetry } from "../utils/getSignedVAAWithRetry"; import parseError from "../utils/parseError"; -import { signSendAndConfirm } from "../utils/solana"; -import { postWithFees, waitForTerraExecution } from "../utils/terra"; import { attestFromEthWithoutWait, waitEVMTxConfirmed, checkETHToken, checkBSCToken } from "../utils/evm"; import { useWallet, Wallet as AlephiumWallet } from "@alephium/web3-react"; import i18n from "../i18n"; -async function algo( - dispatch: any, - enqueueSnackbar: any, - senderAddr: string, - sourceAsset: string -) { - dispatch(setIsSending(true)); - try { - console.log("ALGO", sourceAsset); - const algodClient = new algosdk.Algodv2( - ALGORAND_HOST.algodToken, - ALGORAND_HOST.algodServer, - ALGORAND_HOST.algodPort - ); - const txs = await attestFromAlgorand( - algodClient, - ALGORAND_TOKEN_BRIDGE_ID, - ALGORAND_BRIDGE_ID, - senderAddr, - BigInt(sourceAsset) - ); - const result = await signSendAndConfirmAlgorand(algodClient, txs); - const sequence = parseSequenceFromLogAlgorand(result); - // TODO: fill these out correctly - dispatch( - setAttestTx({ - id: txs[txs.length - 1].tx.txID(), - blockHeight: result["confirmed-round"], - }) - ); - enqueueSnackbar(null, { - content: {i18n.t('Transaction confirmed')}, - }); - const emitterAddress = getEmitterAddressAlgorand(ALGORAND_TOKEN_BRIDGE_ID); - enqueueSnackbar(null, { - content: {i18n.t('Fetching VAA')}, - }); - const { vaaBytes } = await getSignedVAAWithRetry( - CHAIN_ID_ALGORAND, - emitterAddress, - CHAIN_ID_UNSET, - sequence - ); - dispatch(setSignedVAAHex(uint8ArrayToHex(vaaBytes))); - enqueueSnackbar(null, { - content: {i18n.t('Fetched Signed VAA')}, - }); - } catch (e) { - console.error(e); - enqueueSnackbar(null, { - content: {parseError(e)}, - }); - dispatch(setIsSending(false)); - } -} - async function evm( dispatch: any, enqueueSnackbar: any, @@ -203,110 +114,6 @@ async function evm( } } -async function solana( - dispatch: any, - enqueueSnackbar: any, - solPK: PublicKey, - sourceAsset: string, - wallet: WalletContextState -) { - dispatch(setIsSending(true)); - try { - const connection = new Connection(SOLANA_HOST, "confirmed"); - const transaction = await attestFromSolana( - connection, - SOL_BRIDGE_ADDRESS, - SOL_TOKEN_BRIDGE_ADDRESS, - solPK.toString(), - sourceAsset - ); - const txid = await signSendAndConfirm(wallet, connection, transaction); - enqueueSnackbar(null, { - content: {i18n.t('Transaction confirmed')}, - }); - const info = await connection.getTransaction(txid); - if (!info) { - // TODO: error state - throw new Error(i18n.t("An error occurred while fetching the transaction info")); - } - dispatch(setAttestTx({ id: txid, blockHeight: info.slot })); - const sequence = parseSequenceFromLogSolana(info); - const emitterAddress = await getEmitterAddressSolana( - SOL_TOKEN_BRIDGE_ADDRESS - ); - enqueueSnackbar(null, { - content: {i18n.t('Fetching VAA')}, - }); - const { vaaBytes } = await getSignedVAAWithRetry( - CHAIN_ID_SOLANA, - emitterAddress, - CHAIN_ID_UNSET, - sequence - ); - dispatch(setSignedVAAHex(uint8ArrayToHex(vaaBytes))); - enqueueSnackbar(null, { - content: {i18n.t('Fetched Signed VAA')}, - }); - } catch (e) { - console.error(e); - enqueueSnackbar(null, { - content: {parseError(e)}, - }); - dispatch(setIsSending(false)); - } -} - -async function terra( - dispatch: any, - enqueueSnackbar: any, - wallet: ConnectedWallet, - asset: string, - feeDenom: string -) { - dispatch(setIsSending(true)); - try { - const msg = await attestFromTerra( - TERRA_TOKEN_BRIDGE_ADDRESS, - wallet.terraAddress, - asset - ); - const result = await postWithFees(wallet, [msg], "Create Wrapped", [ - feeDenom, - ]); - const info = await waitForTerraExecution(result); - dispatch(setAttestTx({ id: info.txhash, blockHeight: info.height })); - enqueueSnackbar(null, { - content: Transaction confirmed, - }); - const sequence = parseSequenceFromLogTerra(info); - if (!sequence) { - throw new Error("Sequence not found"); - } - const emitterAddress = await getEmitterAddressTerra( - TERRA_TOKEN_BRIDGE_ADDRESS - ); - enqueueSnackbar(null, { - content: Fetching VAA, - }); - const { vaaBytes } = await getSignedVAAWithRetry( - CHAIN_ID_TERRA, - emitterAddress, - CHAIN_ID_UNSET, - sequence - ); - dispatch(setSignedVAAHex(uint8ArrayToHex(vaaBytes))); - enqueueSnackbar(null, { - content: Fetched Signed VAA, - }); - } catch (e) { - console.error(e); - enqueueSnackbar(null, { - content: {parseError(e)}, - }); - dispatch(setIsSending(false)); - } -} - async function alephium( dispatch: any, enqueueSnackbar: any, @@ -371,24 +178,13 @@ export function useHandleAttest() { const isSending = useSelector(selectAttestIsSending); const isSendComplete = useSelector(selectAttestIsSendComplete); const { signer } = useEthereumProvider(); - const solanaWallet = useSolanaWallet(); - const solPK = solanaWallet?.publicKey; - const terraWallet = useConnectedWallet(); - const terraFeeDenom = useSelector(selectTerraFeeDenom); const alphWallet = useWallet(); - const { accounts: algoAccounts } = useAlgorandContext(); const disabled = !isTargetComplete || isSending || isSendComplete; const handleAttestClick = useCallback(() => { if (isEVMChain(sourceChain) && !!signer) { evm(dispatch, enqueueSnackbar, signer, sourceAsset, sourceChain); - } else if (sourceChain === CHAIN_ID_SOLANA && !!solanaWallet && !!solPK) { - solana(dispatch, enqueueSnackbar, solPK, sourceAsset, solanaWallet); - } else if (sourceChain === CHAIN_ID_TERRA && !!terraWallet) { - terra(dispatch, enqueueSnackbar, terraWallet, sourceAsset, terraFeeDenom); } else if (sourceChain === CHAIN_ID_ALEPHIUM && !!alphWallet) { alephium(dispatch, enqueueSnackbar, alphWallet, sourceAsset) - } else if (sourceChain === CHAIN_ID_ALGORAND && algoAccounts[0]) { - algo(dispatch, enqueueSnackbar, algoAccounts[0].address, sourceAsset); } else { } }, [ @@ -396,13 +192,8 @@ export function useHandleAttest() { enqueueSnackbar, sourceChain, signer, - solanaWallet, - solPK, - terraWallet, alphWallet, - sourceAsset, - terraFeeDenom, - algoAccounts, + sourceAsset ]); return useMemo( () => ({ diff --git a/bridge_ui/src/hooks/useHandleCreateWrapped.tsx b/bridge_ui/src/hooks/useHandleCreateWrapped.tsx index 36c9aa86d..413b42e19 100644 --- a/bridge_ui/src/hooks/useHandleCreateWrapped.tsx +++ b/bridge_ui/src/hooks/useHandleCreateWrapped.tsx @@ -1,65 +1,35 @@ import { ChainId, CHAIN_ID_ACALA, - CHAIN_ID_ALGORAND, CHAIN_ID_KARURA, CHAIN_ID_KLAYTN, - CHAIN_ID_SOLANA, - CHAIN_ID_TERRA, CHAIN_ID_ALEPHIUM, - createWrappedOnAlgorand, - createWrappedOnSolana, - createWrappedOnTerra, isEVMChain, - updateWrappedOnSolana, createRemoteTokenPoolOnAlph, updateRemoteTokenPoolOnAlph, - updateWrappedOnTerra, getAttestTokenHandlerId } from "@alephium/wormhole-sdk"; import { Alert } from "@material-ui/lab"; -import { WalletContextState } from "@solana/wallet-adapter-react"; -import { Connection } from "@solana/web3.js"; -import { - ConnectedWallet, - useConnectedWallet, -} from "@terra-money/wallet-provider"; -import algosdk from "algosdk"; import { Signer } from "ethers"; import { useSnackbar } from "notistack"; import { useCallback, useMemo } from "react"; import { useDispatch, useSelector } from "react-redux"; -import { useAlgorandContext } from "../contexts/AlgorandWalletContext"; import { useEthereumProvider } from "../contexts/EthereumProviderContext"; -import { useSolanaWallet } from "../contexts/SolanaWalletContext"; import { setCreateTx, setIsCreating, setIsWalletApproved } from "../store/attestSlice"; import { selectAttestIsCreating, selectAttestSourceChain, - selectAttestTargetChain, - selectTerraFeeDenom, + selectAttestTargetChain } from "../store/selectors"; -import { signSendAndConfirmAlgorand } from "../utils/algorand"; import { ACALA_HOST, - ALGORAND_BRIDGE_ID, - ALGORAND_HOST, - ALGORAND_TOKEN_BRIDGE_ID, getTokenBridgeAddressForChain, KARURA_HOST, - MAX_VAA_UPLOAD_RETRIES_SOLANA, - SOLANA_HOST, - SOL_BRIDGE_ADDRESS, - SOL_TOKEN_BRIDGE_ADDRESS, - TERRA_TOKEN_BRIDGE_ADDRESS, ALEPHIUM_TOKEN_BRIDGE_CONTRACT_ID, ALEPHIUM_BRIDGE_GROUP_INDEX, } from "../utils/consts"; import { getKaruraGasParams } from "../utils/karura"; import parseError from "../utils/parseError"; -import { postVaaWithRetry } from "../utils/postVaa"; -import { signSendAndConfirm } from "../utils/solana"; -import { postWithFees } from "../utils/terra"; import { waitALPHTxConfirmed } from "../utils/alephium"; import useAttestSignedVAA from "./useAttestSignedVAA"; import { createWrappedOnEthWithoutWait, updateWrappedOnEthWithoutWait } from "../utils/evm"; @@ -67,44 +37,6 @@ import { useWallet, Wallet as AlephiumWallet } from "@alephium/web3-react"; import { MINIMAL_CONTRACT_DEPOSIT } from "@alephium/web3"; import i18n from "../i18n"; -async function algo( - dispatch: any, - enqueueSnackbar: any, - senderAddr: string, - signedVAA: Uint8Array -) { - dispatch(setIsCreating(true)); - try { - const algodClient = new algosdk.Algodv2( - ALGORAND_HOST.algodToken, - ALGORAND_HOST.algodServer, - ALGORAND_HOST.algodPort - ); - const txs = await createWrappedOnAlgorand( - algodClient, - ALGORAND_TOKEN_BRIDGE_ID, - ALGORAND_BRIDGE_ID, - senderAddr, - signedVAA - ); - const result = await signSendAndConfirmAlgorand(algodClient, txs); - dispatch( - setCreateTx({ - id: txs[txs.length - 1].tx.txID(), - blockHeight: result["confirmed-round"], - }) - ); - enqueueSnackbar(null, { - content: Transaction confirmed, - }); - } catch (e) { - enqueueSnackbar(null, { - content: {parseError(e)}, - }); - dispatch(setIsCreating(false)); - } -} - async function evm( dispatch: any, enqueueSnackbar: any, @@ -154,98 +86,6 @@ async function evm( } } -async function solana( - dispatch: any, - enqueueSnackbar: any, - wallet: WalletContextState, - payerAddress: string, // TODO: we may not need this since we have wallet - signedVAA: Uint8Array, - shouldUpdate: boolean -) { - dispatch(setIsCreating(true)); - try { - if (!wallet.signTransaction) { - throw new Error("wallet.signTransaction is undefined"); - } - const connection = new Connection(SOLANA_HOST, "confirmed"); - await postVaaWithRetry( - connection, - wallet.signTransaction, - SOL_BRIDGE_ADDRESS, - payerAddress, - Buffer.from(signedVAA), - MAX_VAA_UPLOAD_RETRIES_SOLANA - ); - const transaction = shouldUpdate - ? await updateWrappedOnSolana( - connection, - SOL_BRIDGE_ADDRESS, - SOL_TOKEN_BRIDGE_ADDRESS, - payerAddress, - signedVAA - ) - : await createWrappedOnSolana( - connection, - SOL_BRIDGE_ADDRESS, - SOL_TOKEN_BRIDGE_ADDRESS, - payerAddress, - signedVAA - ); - const txid = await signSendAndConfirm(wallet, connection, transaction); - // TODO: didn't want to make an info call we didn't need, can we get the block without it by modifying the above call? - dispatch(setCreateTx({ id: txid, blockHeight: 1 })); - enqueueSnackbar(null, { - content: {i18n.t('Transaction confirmed')}, - }); - } catch (e) { - enqueueSnackbar(null, { - content: {parseError(e)}, - }); - dispatch(setIsCreating(false)); - } -} - -async function terra( - dispatch: any, - enqueueSnackbar: any, - wallet: ConnectedWallet, - signedVAA: Uint8Array, - shouldUpdate: boolean, - feeDenom: string -) { - dispatch(setIsCreating(true)); - try { - const msg = shouldUpdate - ? await updateWrappedOnTerra( - TERRA_TOKEN_BRIDGE_ADDRESS, - wallet.terraAddress, - signedVAA - ) - : await createWrappedOnTerra( - TERRA_TOKEN_BRIDGE_ADDRESS, - wallet.terraAddress, - signedVAA - ); - const result = await postWithFees( - wallet, - [msg], - "Wormhole - Create Wrapped", - [feeDenom] - ); - dispatch( - setCreateTx({ id: result.result.txhash, blockHeight: result.result.height }) - ); - enqueueSnackbar(null, { - content: {i18n.t('Transaction confirmed')}, - }); - } catch (e) { - enqueueSnackbar(null, { - content: {parseError(e)}, - }); - dispatch(setIsCreating(false)); - } -} - async function alephium( dispatch: any, enqueueSnackbar: any, @@ -285,15 +125,10 @@ export function useHandleCreateWrapped(shouldUpdate: boolean) { const { enqueueSnackbar } = useSnackbar(); const sourceChain = useSelector(selectAttestSourceChain); const targetChain = useSelector(selectAttestTargetChain); - const solanaWallet = useSolanaWallet(); - const solPK = solanaWallet?.publicKey; const signedVAA = useAttestSignedVAA(); const isCreating = useSelector(selectAttestIsCreating); const { signer } = useEthereumProvider(); - const terraWallet = useConnectedWallet(); - const terraFeeDenom = useSelector(selectTerraFeeDenom); const alphWallet = useWallet(); - const { accounts: algoAccounts } = useAlgorandContext(); const handleCreateClick = useCallback(() => { if (isEVMChain(targetChain) && !!signer && !!signedVAA) { evm( @@ -304,29 +139,6 @@ export function useHandleCreateWrapped(shouldUpdate: boolean) { targetChain, shouldUpdate ); - } else if ( - targetChain === CHAIN_ID_SOLANA && - !!solanaWallet && - !!solPK && - !!signedVAA - ) { - solana( - dispatch, - enqueueSnackbar, - solanaWallet, - solPK.toString(), - signedVAA, - shouldUpdate - ); - } else if (targetChain === CHAIN_ID_TERRA && !!terraWallet && !!signedVAA) { - terra( - dispatch, - enqueueSnackbar, - terraWallet, - signedVAA, - shouldUpdate, - terraFeeDenom - ); } else if (targetChain === CHAIN_ID_ALEPHIUM && !!alphWallet && !!signedVAA) { alephium( dispatch, @@ -336,12 +148,6 @@ export function useHandleCreateWrapped(shouldUpdate: boolean) { signedVAA, shouldUpdate ) - } else if ( - targetChain === CHAIN_ID_ALGORAND && - algoAccounts[0] && - !!signedVAA - ) { - algo(dispatch, enqueueSnackbar, algoAccounts[0]?.address, signedVAA); } else { // enqueueSnackbar( // "Creating wrapped tokens on this chain is not yet supported", @@ -355,15 +161,10 @@ export function useHandleCreateWrapped(shouldUpdate: boolean) { enqueueSnackbar, sourceChain, targetChain, - solanaWallet, - solPK, - terraWallet, alphWallet, signedVAA, signer, - shouldUpdate, - terraFeeDenom, - algoAccounts, + shouldUpdate ]); return useMemo( () => ({ diff --git a/bridge_ui/src/hooks/useHandleNFTRedeem.tsx b/bridge_ui/src/hooks/useHandleNFTRedeem.tsx index 8dc7160dc..59b86375c 100644 --- a/bridge_ui/src/hooks/useHandleNFTRedeem.tsx +++ b/bridge_ui/src/hooks/useHandleNFTRedeem.tsx @@ -3,45 +3,21 @@ import { CHAIN_ID_ACALA, CHAIN_ID_KARURA, CHAIN_ID_KLAYTN, - CHAIN_ID_SOLANA, - getClaimAddressSolana, - isEVMChain + isEVMChain, + redeemOnEth } from "@alephium/wormhole-sdk"; -import { - createMetaOnSolana, - getForeignAssetSol, - isNFTVAASolanaNative, - redeemOnEth, - redeemOnSolana, -} from "@alephium/wormhole-sdk/lib/esm/nft_bridge"; -import { arrayify } from "@ethersproject/bytes"; import { Alert } from "@material-ui/lab"; -import { WalletContextState } from "@solana/wallet-adapter-react"; -import { Connection } from "@solana/web3.js"; -import { BigNumber, Signer } from "ethers"; +import { Signer } from "ethers"; import { useSnackbar } from "notistack"; import { useCallback, useMemo } from "react"; import { useDispatch, useSelector } from "react-redux"; import { useEthereumProvider } from "../contexts/EthereumProviderContext"; -import { useSolanaWallet } from "../contexts/SolanaWalletContext"; import { setIsRedeeming, setRedeemTx } from "../store/nftSlice"; import { selectNFTIsRedeeming, selectNFTTargetChain } from "../store/selectors"; -import { - ACALA_HOST, - getNFTBridgeAddressForChain, - KARURA_HOST, - MAX_VAA_UPLOAD_RETRIES_SOLANA, - SOLANA_HOST, - SOL_BRIDGE_ADDRESS, - SOL_NFT_BRIDGE_ADDRESS, -} from "../utils/consts"; +import { ACALA_HOST, getNFTBridgeAddressForChain, KARURA_HOST } from "../utils/consts"; import { getKaruraGasParams } from "../utils/karura"; -import { getMetadataAddress } from "../utils/metaplex"; import parseError from "../utils/parseError"; -import { postVaaWithRetry } from "../utils/postVaa"; -import { signSendAndConfirm } from "../utils/solana"; import useNFTSignedVAA from "./useNFTSignedVAA"; -import { deserializeTransferNFTVAA } from "@alephium/wormhole-sdk/lib/esm"; async function evm( dispatch: any, @@ -82,104 +58,16 @@ async function evm( } } -async function solana( - dispatch: any, - enqueueSnackbar: any, - wallet: WalletContextState, - payerAddress: string, //TODO: we may not need this since we have wallet - signedVAA: Uint8Array -) { - dispatch(setIsRedeeming(true)); - try { - if (!wallet.signTransaction) { - throw new Error("wallet.signTransaction is undefined"); - } - const connection = new Connection(SOLANA_HOST, "confirmed"); - const claimAddress = await getClaimAddressSolana( - SOL_NFT_BRIDGE_ADDRESS, - signedVAA - ); - const claimInfo = await connection.getAccountInfo(claimAddress); - let txid; - if (!claimInfo) { - await postVaaWithRetry( - connection, - wallet.signTransaction, - SOL_BRIDGE_ADDRESS, - payerAddress, - Buffer.from(signedVAA), - MAX_VAA_UPLOAD_RETRIES_SOLANA - ); - // TODO: how do we retry in between these steps - const transaction = await redeemOnSolana( - connection, - SOL_BRIDGE_ADDRESS, - SOL_NFT_BRIDGE_ADDRESS, - payerAddress, - signedVAA - ); - txid = await signSendAndConfirm(wallet, connection, transaction); - // TODO: didn't want to make an info call we didn't need, can we get the block without it by modifying the above call? - } - const isNative = await isNFTVAASolanaNative(signedVAA); - if (!isNative) { - const payload = deserializeTransferNFTVAA(signedVAA).body.payload - const mintAddress = await getForeignAssetSol( - SOL_NFT_BRIDGE_ADDRESS, - payload.originChain, - payload.originAddress, - arrayify(BigNumber.from(payload.tokenId)) - ); - const [metadataAddress] = await getMetadataAddress(mintAddress); - const metadata = await connection.getAccountInfo(metadataAddress); - if (!metadata) { - const transaction = await createMetaOnSolana( - connection, - SOL_BRIDGE_ADDRESS, - SOL_NFT_BRIDGE_ADDRESS, - payerAddress, - signedVAA - ); - txid = await signSendAndConfirm(wallet, connection, transaction); - } - } - dispatch(setRedeemTx({ id: txid || "", blockHeight: 1 })); - enqueueSnackbar(null, { - content: Transaction confirmed, - }); - } catch (e) { - enqueueSnackbar(null, { - content: {parseError(e)}, - }); - dispatch(setIsRedeeming(false)); - } -} - export function useHandleNFTRedeem() { const dispatch = useDispatch(); const { enqueueSnackbar } = useSnackbar(); const targetChain = useSelector(selectNFTTargetChain); - const solanaWallet = useSolanaWallet(); - const solPK = solanaWallet?.publicKey; const { signer } = useEthereumProvider(); const signedVAA = useNFTSignedVAA(); const isRedeeming = useSelector(selectNFTIsRedeeming); const handleRedeemClick = useCallback(() => { if (isEVMChain(targetChain) && !!signer && signedVAA) { evm(dispatch, enqueueSnackbar, signer, signedVAA, targetChain); - } else if ( - targetChain === CHAIN_ID_SOLANA && - !!solanaWallet && - !!solPK && - signedVAA - ) { - solana( - dispatch, - enqueueSnackbar, - solanaWallet, - solPK.toString(), - signedVAA - ); } else { } }, [ @@ -187,9 +75,7 @@ export function useHandleNFTRedeem() { enqueueSnackbar, targetChain, signer, - signedVAA, - solanaWallet, - solPK, + signedVAA ]); return useMemo( () => ({ diff --git a/bridge_ui/src/hooks/useHandleNFTTransfer.tsx b/bridge_ui/src/hooks/useHandleNFTTransfer.tsx index 58f5749c0..2d5860f39 100644 --- a/bridge_ui/src/hooks/useHandleNFTTransfer.tsx +++ b/bridge_ui/src/hooks/useHandleNFTTransfer.tsx @@ -1,29 +1,18 @@ import { ChainId, CHAIN_ID_KLAYTN, - CHAIN_ID_SOLANA, getEmitterAddressEth, - getEmitterAddressSolana, - hexToUint8Array, isEVMChain, parseSequenceFromLogEth, - parseSequenceFromLogSolana, uint8ArrayToHex, } from "@alephium/wormhole-sdk"; -import { - transferFromEth, - transferFromSolana, -} from "@alephium/wormhole-sdk/lib/esm/nft_bridge"; +import { transferFromEth } from "@alephium/wormhole-sdk/lib/esm/nft_bridge"; import { Alert } from "@material-ui/lab"; -import { WalletContextState } from "@solana/wallet-adapter-react"; -import { Connection } from "@solana/web3.js"; -import { BigNumber, Signer } from "ethers"; -import { arrayify, zeroPad } from "ethers/lib/utils"; +import { Signer } from "ethers"; import { useSnackbar } from "notistack"; import { useCallback, useMemo } from "react"; import { useDispatch, useSelector } from "react-redux"; import { useEthereumProvider } from "../contexts/EthereumProviderContext"; -import { useSolanaWallet } from "../contexts/SolanaWalletContext"; import { setIsSending, setSignedVAAHex, @@ -33,9 +22,6 @@ import { selectNFTIsSendComplete, selectNFTIsSending, selectNFTIsTargetComplete, - selectNFTOriginAsset, - selectNFTOriginChain, - selectNFTOriginTokenId, selectNFTSourceAsset, selectNFTSourceChain, selectNFTSourceParsedTokenAccount, @@ -43,14 +29,10 @@ import { } from "../store/selectors"; import { getBridgeAddressForChain, - getNFTBridgeAddressForChain, - SOLANA_HOST, - SOL_BRIDGE_ADDRESS, - SOL_NFT_BRIDGE_ADDRESS, + getNFTBridgeAddressForChain } from "../utils/consts"; import { getSignedVAAWithRetry } from "../utils/getSignedVAAWithRetry"; import parseError from "../utils/parseError"; -import { signSendAndConfirm } from "../utils/solana"; import useNFTTargetAddressHex from "./useNFTTargetAddress"; async function evm( @@ -114,74 +96,6 @@ async function evm( } } -async function solana( - dispatch: any, - enqueueSnackbar: any, - wallet: WalletContextState, - payerAddress: string, //TODO: we may not need this since we have wallet - fromAddress: string, - mintAddress: string, - targetChain: ChainId, - targetAddress: Uint8Array, - originAddressStr?: string, - originChain?: ChainId, - originTokenId?: string -) { - dispatch(setIsSending(true)); - try { - const connection = new Connection(SOLANA_HOST, "confirmed"); - const originAddress = originAddressStr - ? zeroPad(hexToUint8Array(originAddressStr), 32) - : undefined; - const transaction = await transferFromSolana( - connection, - SOL_BRIDGE_ADDRESS, - SOL_NFT_BRIDGE_ADDRESS, - payerAddress, - fromAddress, - mintAddress, - targetAddress, - targetChain, - originAddress, - originChain, - arrayify(BigNumber.from(originTokenId || "0")) - ); - const txid = await signSendAndConfirm(wallet, connection, transaction); - enqueueSnackbar(null, { - content: Transaction confirmed, - }); - const info = await connection.getTransaction(txid); - if (!info) { - throw new Error("An error occurred while fetching the transaction info"); - } - dispatch(setTransferTx({ id: txid, blockHeight: info.slot })); - const sequence = parseSequenceFromLogSolana(info); - const emitterAddress = await getEmitterAddressSolana( - SOL_NFT_BRIDGE_ADDRESS - ); - enqueueSnackbar(null, { - content: Fetching VAA, - }); - const { vaaBytes } = await getSignedVAAWithRetry( - CHAIN_ID_SOLANA, - emitterAddress, - targetChain, - sequence - ); - - dispatch(setSignedVAAHex(uint8ArrayToHex(vaaBytes))); - enqueueSnackbar(null, { - content: Fetched Signed VAA, - }); - } catch (e) { - console.error(e); - enqueueSnackbar(null, { - content: {parseError(e)}, - }); - dispatch(setIsSending(false)); - } -} - export function useHandleNFTTransfer() { const dispatch = useDispatch(); const { enqueueSnackbar } = useSnackbar(); @@ -191,21 +105,12 @@ export function useHandleNFTTransfer() { selectNFTSourceParsedTokenAccount ); const sourceTokenId = nftSourceParsedTokenAccount?.tokenId || ""; // this should exist by this step for NFT transfers - const originChain = useSelector(selectNFTOriginChain); - const originAsset = useSelector(selectNFTOriginAsset); - const originTokenId = useSelector(selectNFTOriginTokenId); const targetChain = useSelector(selectNFTTargetChain); const targetAddress = useNFTTargetAddressHex(); const isTargetComplete = useSelector(selectNFTIsTargetComplete); const isSending = useSelector(selectNFTIsSending); const isSendComplete = useSelector(selectNFTIsSendComplete); const { signer } = useEthereumProvider(); - const solanaWallet = useSolanaWallet(); - const solPK = solanaWallet?.publicKey; - const sourceParsedTokenAccount = useSelector( - selectNFTSourceParsedTokenAccount - ); - const sourceTokenPublicKey = sourceParsedTokenAccount?.publicKey; const disabled = !isTargetComplete || isSending || isSendComplete; const handleTransferClick = useCallback(() => { // TODO: we should separate state for transaction vs fetching vaa @@ -226,27 +131,6 @@ export function useHandleNFTTransfer() { targetAddress, sourceChain ); - } else if ( - sourceChain === CHAIN_ID_SOLANA && - !!solanaWallet && - !!solPK && - !!sourceAsset && - !!sourceTokenPublicKey && - !!targetAddress - ) { - solana( - dispatch, - enqueueSnackbar, - solanaWallet, - solPK.toString(), - sourceTokenPublicKey, - sourceAsset, - targetChain, - targetAddress, - originAsset, - originChain, - originTokenId - ); } else { } }, [ @@ -254,16 +138,10 @@ export function useHandleNFTTransfer() { enqueueSnackbar, sourceChain, signer, - solanaWallet, - solPK, - sourceTokenPublicKey, sourceAsset, sourceTokenId, targetChain, - targetAddress, - originAsset, - originChain, - originTokenId, + targetAddress ]); return useMemo( () => ({ diff --git a/bridge_ui/src/hooks/useHandleRedeem.tsx b/bridge_ui/src/hooks/useHandleRedeem.tsx index c21744470..ff99d8575 100644 --- a/bridge_ui/src/hooks/useHandleRedeem.tsx +++ b/bridge_ui/src/hooks/useHandleRedeem.tsx @@ -1,14 +1,7 @@ import { ChainId, - CHAIN_ID_ALGORAND, CHAIN_ID_KLAYTN, - CHAIN_ID_SOLANA, - CHAIN_ID_TERRA, isEVMChain, - redeemAndUnwrapOnSolana, - redeemOnAlgorand, - redeemOnSolana, - redeemOnTerra, CHAIN_ID_ALEPHIUM, uint8ArrayToHex, getTokenBridgeForChainId, @@ -20,52 +13,25 @@ import { redeemOnAlph } from "@alephium/wormhole-sdk"; import { Alert } from "@material-ui/lab"; -import { WalletContextState } from "@solana/wallet-adapter-react"; -import { Connection } from "@solana/web3.js"; -import { - ConnectedWallet, - useConnectedWallet, -} from "@terra-money/wallet-provider"; -import algosdk from "algosdk"; import axios from "axios"; import { Signer } from "ethers"; import { useSnackbar } from "notistack"; import { useCallback, useMemo } from "react"; import { useDispatch, useSelector } from "react-redux"; -import { useAlgorandContext } from "../contexts/AlgorandWalletContext"; import { useEthereumProvider } from "../contexts/EthereumProviderContext"; -import { useSolanaWallet } from "../contexts/SolanaWalletContext"; -import { - selectTerraFeeDenom, - selectTransferIsRedeeming, - selectTransferRecoverySourceTxId, - selectTransferSourceChain, - selectTransferTargetChain, -} from "../store/selectors"; +import { selectTransferIsRedeeming, selectTransferTargetChain } from "../store/selectors"; import { setIsRedeemedViaRelayer, setIsRedeeming, setIsRedeemingViaRelayer, setIsWalletApproved, setRedeemCompleted, setRedeemTx } from "../store/transferSlice"; -import { signSendAndConfirmAlgorand } from "../utils/algorand"; import { ACALA_RELAY_URL, ALEPHIUM_BRIDGE_GROUP_INDEX, ALEPHIUM_BRIDGE_REWARD_ROUTER_ID, ALEPHIUM_TOKEN_BRIDGE_CONTRACT_ID, - ALGORAND_BRIDGE_ID, - ALGORAND_HOST, - ALGORAND_TOKEN_BRIDGE_ID, getTokenBridgeAddressForChain, - MAX_VAA_UPLOAD_RETRIES_SOLANA, - SOLANA_HOST, - SOL_BRIDGE_ADDRESS, - SOL_TOKEN_BRIDGE_ADDRESS, - TERRA_TOKEN_BRIDGE_ADDRESS, CLUSTER, RELAYER_HOST, BSC_TOKENS_FOR_REWARD, } from "../utils/consts"; import parseError from "../utils/parseError"; -import { postVaaWithRetry } from "../utils/postVaa"; -import { signSendAndConfirm } from "../utils/solana"; -import { postWithFees } from "../utils/terra"; import { getEmitterChainId, waitALPHTxConfirmed } from "../utils/alephium"; import useTransferSignedVAA from "./useTransferSignedVAA"; import { redeemOnEthNativeWithoutWait, redeemOnEthWithoutWait } from "../utils/evm"; @@ -73,54 +39,13 @@ import { useWallet, Wallet as AlephiumWallet } from "@alephium/web3-react"; import { SignerProvider } from "@alephium/web3"; import i18n from "../i18n"; -async function algo( - dispatch: any, - enqueueSnackbar: any, - senderAddr: string, - signedVAA: Uint8Array -) { - dispatch(setIsRedeeming(true)); - try { - const algodClient = new algosdk.Algodv2( - ALGORAND_HOST.algodToken, - ALGORAND_HOST.algodServer, - ALGORAND_HOST.algodPort - ); - const txs = await redeemOnAlgorand( - algodClient, - ALGORAND_TOKEN_BRIDGE_ID, - ALGORAND_BRIDGE_ID, - signedVAA, - senderAddr - ); - const result = await signSendAndConfirmAlgorand(algodClient, txs); - // TODO: fill these out correctly - dispatch( - setRedeemTx({ - id: txs[txs.length - 1].tx.txID(), - blockHeight: result["confirmed-round"], - }) - ); - enqueueSnackbar(null, { - content: {i18n.t('Transaction confirmed')}, - }); - } catch (e) { - enqueueSnackbar(null, { - content: {parseError(e)}, - }); - dispatch(setIsRedeeming(false)); - } -} - async function evm( dispatch: any, enqueueSnackbar: any, signer: Signer, signedVAA: Uint8Array, isNative: boolean, - targetChainId: ChainId, - sourceChainId: ChainId, - sourceTxId: string | undefined + targetChainId: ChainId ) { dispatch(setIsRedeeming(true)); try { @@ -158,92 +83,6 @@ async function evm( } } -async function solana( - dispatch: any, - enqueueSnackbar: any, - wallet: WalletContextState, - payerAddress: string, //TODO: we may not need this since we have wallet - signedVAA: Uint8Array, - isNative: boolean -) { - dispatch(setIsRedeeming(true)); - try { - if (!wallet.signTransaction) { - throw new Error("wallet.signTransaction is undefined"); - } - const connection = new Connection(SOLANA_HOST, "confirmed"); - await postVaaWithRetry( - connection, - wallet.signTransaction, - SOL_BRIDGE_ADDRESS, - payerAddress, - Buffer.from(signedVAA), - MAX_VAA_UPLOAD_RETRIES_SOLANA - ); - // TODO: how do we retry in between these steps - const transaction = isNative - ? await redeemAndUnwrapOnSolana( - connection, - SOL_BRIDGE_ADDRESS, - SOL_TOKEN_BRIDGE_ADDRESS, - payerAddress, - signedVAA - ) - : await redeemOnSolana( - connection, - SOL_BRIDGE_ADDRESS, - SOL_TOKEN_BRIDGE_ADDRESS, - payerAddress, - signedVAA - ); - const txid = await signSendAndConfirm(wallet, connection, transaction); - // TODO: didn't want to make an info call we didn't need, can we get the block without it by modifying the above call? - dispatch(setRedeemTx({ id: txid, blockHeight: 1 })); - enqueueSnackbar(null, { - content: {i18n.t('Transaction confirmed')}, - }); - } catch (e) { - enqueueSnackbar(null, { - content: {parseError(e)}, - }); - dispatch(setIsRedeeming(false)); - } -} - -async function terra( - dispatch: any, - enqueueSnackbar: any, - wallet: ConnectedWallet, - signedVAA: Uint8Array, - feeDenom: string -) { - dispatch(setIsRedeeming(true)); - try { - const msg = await redeemOnTerra( - TERRA_TOKEN_BRIDGE_ADDRESS, - wallet.terraAddress, - signedVAA - ); - const result = await postWithFees( - wallet, - [msg], - "Wormhole - Complete Transfer", - [feeDenom] - ); - dispatch( - setRedeemTx({ id: result.result.txhash, blockHeight: result.result.height }) - ); - enqueueSnackbar(null, { - content: {i18n.t('Transaction confirmed')}, - }); - } catch (e) { - enqueueSnackbar(null, { - content: {parseError(e)}, - }); - dispatch(setIsRedeeming(false)); - } -} - async function redeemViaRelayer( dispatch: any, signer: SignerProvider, @@ -354,102 +193,37 @@ export function useHandleRedeem() { const dispatch = useDispatch(); const { enqueueSnackbar } = useSnackbar(); const targetChain = useSelector(selectTransferTargetChain); - const sourceChain = useSelector(selectTransferSourceChain); - const recoverySourceTxId = useSelector(selectTransferRecoverySourceTxId); - const solanaWallet = useSolanaWallet(); - const solPK = solanaWallet?.publicKey; const { signer } = useEthereumProvider(); - const terraWallet = useConnectedWallet(); - const terraFeeDenom = useSelector(selectTerraFeeDenom); const alphWallet = useWallet(); - const { accounts: algoAccounts } = useAlgorandContext(); const signedVAA = useTransferSignedVAA(); const isRedeeming = useSelector(selectTransferIsRedeeming); const handleRedeemClick = useCallback(() => { if (isEVMChain(targetChain) && !!signer && signedVAA) { - evm(dispatch, enqueueSnackbar, signer, signedVAA, false, targetChain, sourceChain, recoverySourceTxId); - } else if ( - targetChain === CHAIN_ID_SOLANA && - !!solanaWallet && - !!solPK && - signedVAA - ) { - solana( - dispatch, - enqueueSnackbar, - solanaWallet, - solPK.toString(), - signedVAA, - false - ); - } else if (targetChain === CHAIN_ID_TERRA && !!terraWallet && signedVAA) { - terra(dispatch, enqueueSnackbar, terraWallet, signedVAA, terraFeeDenom); + evm(dispatch, enqueueSnackbar, signer, signedVAA, false, targetChain); } else if (targetChain === CHAIN_ID_ALEPHIUM && !!alphWallet && signedVAA) { alephium(dispatch, enqueueSnackbar, alphWallet, signedVAA) - } else if ( - targetChain === CHAIN_ID_ALGORAND && - algoAccounts[0] && - !!signedVAA - ) { - algo(dispatch, enqueueSnackbar, algoAccounts[0]?.address, signedVAA); } else { } }, [ dispatch, enqueueSnackbar, - sourceChain, - recoverySourceTxId, targetChain, signer, signedVAA, - solanaWallet, - solPK, - terraWallet, - terraFeeDenom, - alphWallet, - algoAccounts, + alphWallet ]); const handleRedeemNativeClick = useCallback(() => { if (isEVMChain(targetChain) && !!signer && signedVAA) { - evm(dispatch, enqueueSnackbar, signer, signedVAA, true, targetChain, sourceChain, recoverySourceTxId); - } else if ( - targetChain === CHAIN_ID_SOLANA && - !!solanaWallet && - !!solPK && - signedVAA - ) { - solana( - dispatch, - enqueueSnackbar, - solanaWallet, - solPK.toString(), - signedVAA, - true - ); - } else if (targetChain === CHAIN_ID_TERRA && !!terraWallet && signedVAA) { - terra(dispatch, enqueueSnackbar, terraWallet, signedVAA, terraFeeDenom); //TODO isNative = true - } else if ( - targetChain === CHAIN_ID_ALGORAND && - algoAccounts[0] && - !!signedVAA - ) { - algo(dispatch, enqueueSnackbar, algoAccounts[0]?.address, signedVAA); + evm(dispatch, enqueueSnackbar, signer, signedVAA, true, targetChain); } else { } }, [ dispatch, enqueueSnackbar, targetChain, - sourceChain, - recoverySourceTxId, signer, - signedVAA, - solanaWallet, - solPK, - terraWallet, - terraFeeDenom, - algoAccounts, + signedVAA ]); const handleAcalaRelayerRedeemClick = useCallback(async () => { diff --git a/bridge_ui/src/hooks/useHandleTransfer.tsx b/bridge_ui/src/hooks/useHandleTransfer.tsx index 5dc48c7c5..75885bcbf 100644 --- a/bridge_ui/src/hooks/useHandleTransfer.tsx +++ b/bridge_ui/src/hooks/useHandleTransfer.tsx @@ -1,46 +1,22 @@ import { ChainId, CHAIN_ID_ALEPHIUM, - CHAIN_ID_ALGORAND, CHAIN_ID_KLAYTN, - CHAIN_ID_SOLANA, - CHAIN_ID_TERRA, - getEmitterAddressAlgorand, getEmitterAddressEth, - getEmitterAddressSolana, - getEmitterAddressTerra, - hexToUint8Array, isEVMChain, - parseSequenceFromLogAlgorand, parseSequenceFromLogEth, - parseSequenceFromLogSolana, - parseSequenceFromLogTerra, - transferFromAlgorand, - transferFromSolana, - transferFromTerra, - transferNativeSol, uint8ArrayToHex, transferRemoteTokenFromAlph, transferLocalTokenFromAlph } from "@alephium/wormhole-sdk"; import { Alert } from "@material-ui/lab"; -import { WalletContextState } from "@solana/wallet-adapter-react"; -import { Connection } from "@solana/web3.js"; -import { - ConnectedWallet, - useConnectedWallet, -} from "@terra-money/wallet-provider"; -import algosdk from "algosdk"; import { Signer } from "ethers"; -import { parseUnits, zeroPad } from "ethers/lib/utils"; +import { parseUnits } from "ethers/lib/utils"; import { useSnackbar } from "notistack"; import { useCallback, useMemo } from "react"; import { useDispatch, useSelector } from "react-redux"; -import { useAlgorandContext } from "../contexts/AlgorandWalletContext"; import { useEthereumProvider } from "../contexts/EthereumProviderContext"; -import { useSolanaWallet } from "../contexts/SolanaWalletContext"; import { - selectTerraFeeDenom, selectTransferAmount, selectTransferIsSendComplete, selectTransferIsSending, @@ -60,27 +36,17 @@ import { setRecoverySourceTxId, setIsWalletApproved } from "../store/transferSlice"; -import { signSendAndConfirmAlgorand } from "../utils/algorand"; import { ALEPHIUM_MINIMAL_CONSISTENCY_LEVEL, ALEPHIUM_TOKEN_BRIDGE_CONTRACT_ID, alphArbiterFee, ALEPHIUM_MESSAGE_FEE, - ALGORAND_BRIDGE_ID, - ALGORAND_HOST, - ALGORAND_TOKEN_BRIDGE_ID, getBridgeAddressForChain, getTokenBridgeAddressForChain, - SOLANA_HOST, - SOL_BRIDGE_ADDRESS, - SOL_TOKEN_BRIDGE_ADDRESS, - TERRA_TOKEN_BRIDGE_ADDRESS, ALEPHIUM_BRIDGE_GROUP_INDEX, } from "../utils/consts"; import { getSignedVAAWithRetry } from "../utils/getSignedVAAWithRetry"; import parseError from "../utils/parseError"; -import { signSendAndConfirm } from "../utils/solana"; -import { postWithFees, waitForTerraExecution } from "../utils/terra"; import useTransferTargetAddressHex from "./useTransferTargetAddress"; import { validateAlephiumRecipientAddress, waitALPHTxConfirmed, waitTxConfirmedAndGetTxInfo } from "../utils/alephium"; import { ExecuteScriptResult } from "@alephium/web3"; @@ -88,73 +54,6 @@ import { transferFromEthNativeWithoutWait, transferFromEthWithoutWait, waitEVMTx import { useWallet, Wallet as AlephiumWallet } from "@alephium/web3-react"; import i18n from "../i18n"; -async function algo( - dispatch: any, - enqueueSnackbar: any, - senderAddr: string, - tokenAddress: string, - decimals: number, - amount: string, - recipientChain: ChainId, - recipientAddress: Uint8Array, - chainId: ChainId, - relayerFee?: string -) { - dispatch(setIsSending(true)); - try { - const baseAmountParsed = parseUnits(amount, decimals); - const feeParsed = parseUnits(relayerFee || "0", decimals); - const transferAmountParsed = baseAmountParsed.add(feeParsed); - const algodClient = new algosdk.Algodv2( - ALGORAND_HOST.algodToken, - ALGORAND_HOST.algodServer, - ALGORAND_HOST.algodPort - ); - const txs = await transferFromAlgorand( - algodClient, - ALGORAND_TOKEN_BRIDGE_ID, - ALGORAND_BRIDGE_ID, - senderAddr, - BigInt(tokenAddress), - transferAmountParsed.toBigInt(), - uint8ArrayToHex(recipientAddress), - recipientChain, - feeParsed.toBigInt() - ); - const result = await signSendAndConfirmAlgorand(algodClient, txs); - const sequence = parseSequenceFromLogAlgorand(result); - dispatch( - setTransferTx({ - id: txs[txs.length - 1].tx.txID(), - blockHeight: result["confirmed-round"], - }) - ); - enqueueSnackbar(null, { - content: {i18n.t('Transaction confirmed')}, - }); - const emitterAddress = getEmitterAddressAlgorand(ALGORAND_TOKEN_BRIDGE_ID); - enqueueSnackbar(null, { - content: {i18n.t('Fetching VAA')}, - }); - const { vaaBytes } = await getSignedVAAWithRetry( - chainId, - emitterAddress, - recipientChain, - sequence - ); - dispatch(setSignedVAAHex(uint8ArrayToHex(vaaBytes))); - enqueueSnackbar(null, { - content: {i18n.t('Fetched Signed VAA')}, - }); - } catch (e) { - console.error(e); - enqueueSnackbar(null, { - content: {parseError(e)}, - }); - dispatch(setIsSending(false)); - } -} - async function evm( dispatch: any, enqueueSnackbar: any, @@ -249,94 +148,6 @@ async function evm( } } -async function solana( - dispatch: any, - enqueueSnackbar: any, - wallet: WalletContextState, - payerAddress: string, //TODO: we may not need this since we have wallet - fromAddress: string, - mintAddress: string, - amount: string, - decimals: number, - targetChain: ChainId, - targetAddress: Uint8Array, - isNative: boolean, - originAddressStr?: string, - originChain?: ChainId, - relayerFee?: string -) { - dispatch(setIsSending(true)); - try { - const connection = new Connection(SOLANA_HOST, "confirmed"); - const baseAmountParsed = parseUnits(amount, decimals); - const feeParsed = parseUnits(relayerFee || "0", decimals); - const transferAmountParsed = baseAmountParsed.add(feeParsed); - const originAddress = originAddressStr - ? zeroPad(hexToUint8Array(originAddressStr), 32) - : undefined; - const promise = isNative - ? transferNativeSol( - connection, - SOL_BRIDGE_ADDRESS, - SOL_TOKEN_BRIDGE_ADDRESS, - payerAddress, - transferAmountParsed.toBigInt(), - targetAddress, - targetChain, - feeParsed.toBigInt() - ) - : transferFromSolana( - connection, - SOL_BRIDGE_ADDRESS, - SOL_TOKEN_BRIDGE_ADDRESS, - payerAddress, - fromAddress, - mintAddress, - transferAmountParsed.toBigInt(), - targetAddress, - targetChain, - originAddress, - originChain, - undefined, - feeParsed.toBigInt() - ); - const transaction = await promise; - const txid = await signSendAndConfirm(wallet, connection, transaction); - enqueueSnackbar(null, { - content: {i18n.t('Transaction confirmed')}, - }); - const info = await connection.getTransaction(txid); - if (!info) { - throw new Error("An error occurred while fetching the transaction info"); - } - dispatch(setTransferTx({ id: txid, blockHeight: info.slot })); - const sequence = parseSequenceFromLogSolana(info); - const emitterAddress = await getEmitterAddressSolana( - SOL_TOKEN_BRIDGE_ADDRESS - ); - enqueueSnackbar(null, { - content: {i18n.t('Fetching VAA')}, - }); - const { vaaBytes } = await getSignedVAAWithRetry( - CHAIN_ID_SOLANA, - emitterAddress, - targetChain, - sequence - ); - - dispatch(setSignedVAAHex(uint8ArrayToHex(vaaBytes))); - enqueueSnackbar(null, { - content: {i18n.t('Fetched Signed VAA')}, - }); - } catch (e) { - console.error(e); - enqueueSnackbar(null, { - content: {parseError(e)}, - }); - dispatch(setIsSending(false)); - } -} - async function alephium( dispatch: any, enqueueSnackbar: any, @@ -415,74 +226,6 @@ async function alephium( } } -async function terra( - dispatch: any, - enqueueSnackbar: any, - wallet: ConnectedWallet, - asset: string, - amount: string, - decimals: number, - targetChain: ChainId, - targetAddress: Uint8Array, - feeDenom: string, - relayerFee?: string -) { - dispatch(setIsSending(true)); - try { - const baseAmountParsed = parseUnits(amount, decimals); - const feeParsed = parseUnits(relayerFee || "0", decimals); - const transferAmountParsed = baseAmountParsed.add(feeParsed); - const msgs = await transferFromTerra( - wallet.terraAddress, - TERRA_TOKEN_BRIDGE_ADDRESS, - asset, - transferAmountParsed.toString(), - targetChain, - targetAddress, - feeParsed.toString() - ); - - const result = await postWithFees( - wallet, - msgs, - "Wormhole - Initiate Transfer", - [feeDenom] - ); - - const info = await waitForTerraExecution(result); - dispatch(setTransferTx({ id: info.txhash, blockHeight: info.height })); - enqueueSnackbar(null, { - content: {i18n.t('Transaction confirmed')}, - }); - const sequence = parseSequenceFromLogTerra(info); - if (!sequence) { - throw new Error("Sequence not found"); - } - const emitterAddress = await getEmitterAddressTerra( - TERRA_TOKEN_BRIDGE_ADDRESS - ); - enqueueSnackbar(null, { - content: {i18n.t('Fetching VAA')}, - }); - const { vaaBytes } = await getSignedVAAWithRetry( - CHAIN_ID_TERRA, - emitterAddress, - targetChain, - sequence - ); - enqueueSnackbar(null, { - content: {i18n.t('Fetched Signed VAA')}, - }); - dispatch(setSignedVAAHex(uint8ArrayToHex(vaaBytes))); - } catch (e) { - console.error(e); - enqueueSnackbar(null, { - content: {parseError(e)}, - }); - dispatch(setIsSending(false)); - } -} - export function useHandleTransfer() { const dispatch = useDispatch(); const { enqueueSnackbar } = useSnackbar(); @@ -497,19 +240,13 @@ export function useHandleTransfer() { const isSending = useSelector(selectTransferIsSending); const isSendComplete = useSelector(selectTransferIsSendComplete); const { signer } = useEthereumProvider(); - const solanaWallet = useSolanaWallet(); - const solPK = solanaWallet?.publicKey; - const terraWallet = useConnectedWallet(); const alphWallet = useWallet(); - const terraFeeDenom = useSelector(selectTerraFeeDenom); - const { accounts: algoAccounts } = useAlgorandContext(); const sourceParsedTokenAccount = useSelector( selectTransferSourceParsedTokenAccount ); const relayerFee = useSelector(selectTransferRelayerFee); console.log("relayerFee", relayerFee); - const sourceTokenPublicKey = sourceParsedTokenAccount?.publicKey; const decimals = sourceParsedTokenAccount?.decimals; const isNative = sourceParsedTokenAccount?.isNativeAsset || false; const disabled = !isTargetComplete || isSending || isSendComplete; @@ -536,69 +273,6 @@ export function useHandleTransfer() { sourceChain, relayerFee ); - } else if ( - sourceChain === CHAIN_ID_SOLANA && - !!solanaWallet && - !!solPK && - !!sourceAsset && - !!sourceTokenPublicKey && - !!targetAddress && - decimals !== undefined - ) { - solana( - dispatch, - enqueueSnackbar, - solanaWallet, - solPK.toString(), - sourceTokenPublicKey, - sourceAsset, - amount, - decimals, - targetChain, - targetAddress, - isNative, - originAsset, - originChain, - relayerFee - ); - } else if ( - sourceChain === CHAIN_ID_TERRA && - !!terraWallet && - !!sourceAsset && - decimals !== undefined && - !!targetAddress - ) { - terra( - dispatch, - enqueueSnackbar, - terraWallet, - sourceAsset, - amount, - decimals, - targetChain, - targetAddress, - terraFeeDenom, - relayerFee - ); - } else if ( - sourceChain === CHAIN_ID_ALGORAND && - algoAccounts[0] && - !!sourceAsset && - decimals !== undefined && - !!targetAddress - ) { - algo( - dispatch, - enqueueSnackbar, - algoAccounts[0].address, - sourceAsset, - decimals, - amount, - targetChain, - targetAddress, - sourceChain, - relayerFee - ); } else if ( sourceChain === CHAIN_ID_ALEPHIUM && !!alphWallet && @@ -627,11 +301,7 @@ export function useHandleTransfer() { sourceChain, signer, relayerFee, - solanaWallet, - solPK, - terraWallet, alphWallet, - sourceTokenPublicKey, sourceAsset, amount, decimals, @@ -639,9 +309,7 @@ export function useHandleTransfer() { targetAddress, originAsset, originChain, - isNative, - terraFeeDenom, - algoAccounts, + isNative ]); return useMemo( () => ({ diff --git a/bridge_ui/src/hooks/useOriginalAsset.ts b/bridge_ui/src/hooks/useOriginalAsset.ts index 302ab92e4..1e2584b23 100644 --- a/bridge_ui/src/hooks/useOriginalAsset.ts +++ b/bridge_ui/src/hooks/useOriginalAsset.ts @@ -1,13 +1,8 @@ import { ChainId, CHAIN_ID_ALEPHIUM, - CHAIN_ID_ALGORAND, CHAIN_ID_SOLANA, - CHAIN_ID_TERRA, - getOriginalAssetAlgorand, getOriginalAssetEth, - getOriginalAssetSol, - getOriginalAssetTerra, hexToNativeAssetString, isEVMChain, uint8ArrayToHex, @@ -15,13 +10,9 @@ import { } from "@alephium/wormhole-sdk"; import { getOriginalAssetEth as getOriginalAssetEthNFT, - getOriginalAssetSol as getOriginalAssetSolNFT, WormholeWrappedNFTInfo, } from "@alephium/wormhole-sdk/lib/esm/nft_bridge"; import { Web3Provider } from "@ethersproject/providers"; -import { Connection } from "@solana/web3.js"; -import { LCDClient } from "@terra-money/terra.js"; -import { Algodv2 } from "algosdk"; import { ethers } from "ethers"; import { useCallback, useEffect, useMemo, useState } from "react"; import { @@ -30,15 +21,9 @@ import { } from "../contexts/EthereumProviderContext"; import { DataWrapper } from "../store/helpers"; import { - ALGORAND_HOST, - ALGORAND_TOKEN_BRIDGE_ID, getNFTBridgeAddressForChain, getTokenBridgeAddressForChain, - SOLANA_HOST, - SOLANA_SYSTEM_PROGRAM_ADDRESS, - SOL_NFT_BRIDGE_ADDRESS, - SOL_TOKEN_BRIDGE_ADDRESS, - TERRA_HOST, + SOLANA_SYSTEM_PROGRAM_ADDRESS } from "../utils/consts"; import useIsWalletReady from "./useIsWalletReady"; import { NodeProvider } from "@alephium/web3"; @@ -68,30 +53,9 @@ export async function getOriginalAssetToken( foreignNativeStringAddress, foreignChain ); - } else if (foreignChain === CHAIN_ID_SOLANA) { - const connection = new Connection(SOLANA_HOST, "confirmed"); - promise = await getOriginalAssetSol( - connection, - SOL_TOKEN_BRIDGE_ADDRESS, - foreignNativeStringAddress - ); - } else if (foreignChain === CHAIN_ID_TERRA) { - const lcd = new LCDClient(TERRA_HOST); - promise = await getOriginalAssetTerra(lcd, foreignNativeStringAddress); } else if (foreignChain === CHAIN_ID_ALEPHIUM && alphNodeProvider) { const tokenId = tryGetContractId(foreignNativeStringAddress) promise = await getAlephiumTokenWrappedInfo(tokenId, alphNodeProvider) - } else if (foreignChain === CHAIN_ID_ALGORAND) { - const algodClient = new Algodv2( - ALGORAND_HOST.algodToken, - ALGORAND_HOST.algodServer, - ALGORAND_HOST.algodPort - ); - promise = await getOriginalAssetAlgorand( - algodClient, - ALGORAND_TOKEN_BRIDGE_ID, - BigInt(foreignNativeStringAddress) - ); } } catch (e) { promise = Promise.reject(i18n.t("Invalid foreign arguments.")); @@ -118,13 +82,6 @@ export async function getOriginalAssetNFT( tokenId, foreignChain ); - } else if (foreignChain === CHAIN_ID_SOLANA) { - const connection = new Connection(SOLANA_HOST, "confirmed"); - promise = getOriginalAssetSolNFT( - connection, - SOL_NFT_BRIDGE_ADDRESS, - foreignNativeStringAddress - ); } } catch (e) { promise = Promise.reject(i18n.t("Invalid foreign arguments.")); diff --git a/bridge_ui/src/hooks/useSolanaMigratorInformation.tsx b/bridge_ui/src/hooks/useSolanaMigratorInformation.tsx deleted file mode 100644 index 3e0d279e5..000000000 --- a/bridge_ui/src/hooks/useSolanaMigratorInformation.tsx +++ /dev/null @@ -1,430 +0,0 @@ -import { CHAIN_ID_SOLANA } from "@alephium/wormhole-sdk"; -import migrateTokensTx from "@alephium/wormhole-sdk/lib/esm/migration/migrateTokens"; -import getPoolAddress from "@alephium/wormhole-sdk/lib/esm/migration/poolAddress"; -import getToCustodyAddress from "@alephium/wormhole-sdk/lib/esm/migration/toCustodyAddress"; -import { - ASSOCIATED_TOKEN_PROGRAM_ID, - Token, - TOKEN_PROGRAM_ID, -} from "@solana/spl-token"; -import { Connection, PublicKey } from "@solana/web3.js"; -import { parseUnits } from "ethers/lib/utils"; -import { useCallback, useEffect, useMemo, useState } from "react"; -import { useAssociatedAccountExistsState } from "../components/SolanaCreateAssociatedAddress"; -import { useSolanaWallet } from "../contexts/SolanaWalletContext"; -import useIsWalletReady from "../hooks/useIsWalletReady"; -import useMetaplexData from "../hooks/useMetaplexData"; -import useSolanaTokenMap from "../hooks/useSolanaTokenMap"; -import { DataWrapper } from "../store/helpers"; -import { MIGRATION_PROGRAM_ADDRESS, SOLANA_HOST } from "../utils/consts"; -import { getMultipleAccounts, signSendAndConfirm } from "../utils/solana"; - -const getDecimals = async ( - connection: Connection, - mint: string, - setter: (decimals: number | undefined) => void -) => { - setter(undefined); - if (mint) { - try { - const pk = new PublicKey(mint); - const info = await connection.getParsedAccountInfo(pk); - // @ts-ignore - const decimals = info.value?.data.parsed.info.decimals; - setter(decimals); - } catch (e) { - console.log(`Unable to determine decimals of ${mint}`); - } - } -}; - -const getBalance = async ( - connection: Connection, - address: string | undefined, - setter: (balance: string | undefined) => void -) => { - setter(undefined); - if (address) { - try { - const pk = new PublicKey(address); - const info = await connection.getParsedAccountInfo(pk); - // @ts-ignore - const balance = info.value?.data.parsed.info.tokenAmount.uiAmountString; - setter(balance); - } catch (e) { - console.log(`Unable to determine balance of ${address}`); - } - } -}; - -//If the pool doesn't exist in this app, it's an error. -export type SolanaMigratorInformation = { - poolAddress: string; - fromMint: string; - toMint: string; - fromMintDecimals: number; - fromAssociatedTokenAccountExists: boolean; - toAssociatedTokenAccountExists: boolean; - setToTokenAccountExists: any; - fromAssociatedTokenAccount: string; - toAssociatedTokenAccount: string; - fromAssociatedTokenAccountBalance: string; - toAssociatedTokenAccountBalance: string | null; - toCustodyAddress: string; - toCustodyBalance: string; - - fromName: string | null; - fromSymbol: string | null; - fromLogo: string | null; - toName: string | null; - toSymbol: string | null; - toLogo: string | null; - - getNotReadyCause: (amount: string) => string | null; - - migrateTokens: (amount: string) => Promise; -}; - -//TODO refactor the workflow page to use this hook -export default function useSolanaMigratorInformation( - fromMint: string, - toMint: string, - fromTokenAccount: string -): DataWrapper { - const connection = useMemo( - () => new Connection(SOLANA_HOST, "confirmed"), - [] - ); - const wallet = useSolanaWallet(); - const { isReady } = useIsWalletReady(CHAIN_ID_SOLANA, false); - const solanaTokenMap = useSolanaTokenMap(); - const metaplexArray = useMemo(() => [fromMint, toMint], [fromMint, toMint]); - const metaplexData = useMetaplexData(metaplexArray); - - const [poolAddress, setPoolAddress] = useState(""); - const [poolExists, setPoolExists] = useState(undefined); - const [fromTokenAccountBalance, setFromTokenAccountBalance] = useState< - string | undefined - >(undefined); - const [toTokenAccount, setToTokenAccount] = useState( - undefined - ); - const [toTokenAccountBalance, setToTokenAccountBalance] = useState< - string | undefined - >(undefined); - const [fromMintDecimals, setFromMintDecimals] = useState( - undefined - ); - - const { - associatedAccountExists: fromTokenAccountExists, - //setAssociatedAccountExists: setFromTokenAccountExists, - } = useAssociatedAccountExistsState( - CHAIN_ID_SOLANA, - fromMint, - fromTokenAccount - ); - const { - associatedAccountExists: toTokenAccountExists, - setAssociatedAccountExists: setToTokenAccountExists, - } = useAssociatedAccountExistsState(CHAIN_ID_SOLANA, toMint, toTokenAccount); - - const [toCustodyAddress, setToCustodyAddress] = useState( - undefined - ); - const [toCustodyBalance, setToCustodyBalance] = useState( - undefined - ); - - const [error, setError] = useState(""); - - /* Effects - */ - useEffect(() => { - getDecimals(connection, fromMint, setFromMintDecimals); - }, [connection, fromMint]); - - //Retrieve user balance when fromTokenAccount changes - useEffect(() => { - // TODO: cancellable - if (fromTokenAccount && fromTokenAccountExists) { - getBalance(connection, fromTokenAccount, setFromTokenAccountBalance); - } else { - setFromTokenAccountBalance(undefined); - } - }, [ - connection, - fromTokenAccountExists, - fromTokenAccount, - setFromTokenAccountBalance, - ]); - - useEffect(() => { - // TODO: cancellable - if (toTokenAccount && toTokenAccountExists) { - getBalance(connection, toTokenAccount, setToTokenAccountBalance); - } else { - setToTokenAccountBalance(undefined); - } - }, [ - connection, - toTokenAccountExists, - toTokenAccount, - setFromTokenAccountBalance, - ]); - - useEffect(() => { - // TODO: cancellable - if (toCustodyAddress) { - getBalance(connection, toCustodyAddress, setToCustodyBalance); - } else { - setToCustodyBalance(undefined); - } - }, [connection, toCustodyAddress, setToCustodyBalance]); - - //Retrieve pool address on selectedTokens change - useEffect(() => { - if (toMint && fromMint) { - setPoolAddress(""); - setPoolExists(undefined); - getPoolAddress(MIGRATION_PROGRAM_ADDRESS, fromMint, toMint).then( - (result) => { - const key = new PublicKey(result).toString(); - setPoolAddress(key); - }, - (error) => console.log("Could not calculate pool address.") - ); - } - }, [toMint, fromMint, setPoolAddress]); - - //Retrieve the poolAccount every time the pool address changes. - useEffect(() => { - if (poolAddress) { - setPoolExists(undefined); - try { - getMultipleAccounts( - connection, - [new PublicKey(poolAddress)], - "confirmed" - ).then((result) => { - if (result.length && result[0] !== null) { - setPoolExists(true); - } else if (result.length && result[0] === null) { - setPoolExists(false); - setError("There is no swap pool for this token."); - } else { - setError( - "unexpected error in fetching pool address. Please reload and try again" - ); - } - }); - } catch (e) { - setError("Could not fetch pool address"); - } - } - }, [connection, poolAddress]); - - //Set relevant information derived from poolAddress - useEffect(() => { - if (poolAddress) { - getToCustodyAddress(MIGRATION_PROGRAM_ADDRESS, poolAddress) - .then((result: any) => - setToCustodyAddress(new PublicKey(result).toString()) - ) - .catch((e) => { - setToCustodyAddress(undefined); - }); - } else { - setToCustodyAddress(undefined); - } - }, [poolAddress]); - - useEffect(() => { - if (wallet && wallet.publicKey && toMint) { - Token.getAssociatedTokenAddress( - ASSOCIATED_TOKEN_PROGRAM_ID, - TOKEN_PROGRAM_ID, - new PublicKey(toMint), - wallet.publicKey || new PublicKey([]) - ).then( - (result) => { - setToTokenAccount(result.toString()); - }, - (error) => {} - ); - } - }, [toMint, wallet]); - /* - End effects - */ - - const migrateTokens = useCallback( - async (amount) => { - const instruction = await migrateTokensTx( - connection, - wallet.publicKey?.toString() || "", - MIGRATION_PROGRAM_ADDRESS, - fromMint, - toMint, - fromTokenAccount || "", - toTokenAccount || "", - parseUnits(amount, fromMintDecimals).toBigInt() - ); - return await signSendAndConfirm(wallet, connection, instruction); - }, - [ - connection, - fromMint, - fromTokenAccount, - toMint, - toTokenAccount, - wallet, - fromMintDecimals, - ] - ); - - const fromParse = useCallback( - (amount: string) => { - try { - return parseUnits(amount, fromMintDecimals).toBigInt(); - } catch (e) { - return BigInt(0); - } - }, - [fromMintDecimals] - ); - - const getNotReadyCause = useCallback( - (amount: string) => { - const hasRequisiteData = fromMint && toMint && poolAddress && poolExists; - const accountsReady = fromTokenAccountExists && toTokenAccountExists; - const amountGreaterThanZero = fromParse(amount) > BigInt(0); - const sufficientFromTokens = - fromTokenAccountBalance && - amount && - fromParse(amount) <= fromParse(fromTokenAccountBalance); - const sufficientPoolBalance = - toCustodyBalance && - amount && - parseFloat(amount) <= parseFloat(toCustodyBalance); - - if (!hasRequisiteData) { - return "This asset is not supported."; - } else if (!isReady) { - return "Wallet is not connected."; - } else if (!accountsReady) { - return "You have not created the necessary token accounts."; - } else if (!amount) { - return "Enter an amount to transfer."; - } else if (!amountGreaterThanZero) { - return "Enter an amount greater than zero."; - } else if (!sufficientFromTokens) { - return "There are not sufficient funds in your wallet for this transfer."; - } else if (!sufficientPoolBalance) { - return "There are not sufficient funds in the pool for this transfer."; - } else { - return ""; - } - }, - [ - fromMint, - fromParse, - fromTokenAccountBalance, - fromTokenAccountExists, - isReady, - poolAddress, - poolExists, - toCustodyBalance, - toMint, - toTokenAccountExists, - ] - ); - - const getMetadata = useCallback( - (address: string) => { - const tokenMapItem = solanaTokenMap.data?.find( - (x) => x.address === address - ); - const metaplexItem = metaplexData.data?.get(address); - - return { - symbol: tokenMapItem?.symbol || metaplexItem?.data?.symbol || undefined, - name: tokenMapItem?.name || metaplexItem?.data?.name || undefined, - logo: tokenMapItem?.logoURI || metaplexItem?.data?.uri || undefined, - }; - }, - [metaplexData.data, solanaTokenMap.data] - ); - - const isFetching = solanaTokenMap.isFetching || metaplexData.isFetching; //TODO add loading state on the actual Solana information - const hasRequisiteData = !!( - fromMintDecimals !== null && - fromMintDecimals !== undefined && - toTokenAccount && - fromTokenAccountBalance && - toCustodyAddress && - toCustodyBalance - ); - - const output: DataWrapper = useMemo(() => { - let data: SolanaMigratorInformation | null = null; - if (hasRequisiteData) { - data = { - poolAddress, - fromMint, - toMint, - fromMintDecimals, - fromAssociatedTokenAccountExists: fromTokenAccountExists, - toAssociatedTokenAccountExists: toTokenAccountExists, - fromAssociatedTokenAccount: fromTokenAccount, - toAssociatedTokenAccount: toTokenAccount, - fromAssociatedTokenAccountBalance: fromTokenAccountBalance, - toAssociatedTokenAccountBalance: toTokenAccountBalance || null, - toCustodyAddress, - toCustodyBalance, - - fromName: getMetadata(fromMint)?.name || null, - fromSymbol: getMetadata(fromMint)?.symbol || null, - fromLogo: getMetadata(fromMint)?.logo || null, - toName: getMetadata(toMint)?.name || null, - toSymbol: getMetadata(toMint)?.symbol || null, - toLogo: getMetadata(toMint)?.logo || null, - - setToTokenAccountExists, - - getNotReadyCause: getNotReadyCause, - - migrateTokens, - }; - } - - return { - isFetching: isFetching, - error: error || !hasRequisiteData, - receivedAt: null, - data, - }; - }, [ - error, - isFetching, - hasRequisiteData, - poolAddress, - fromMint, - toMint, - fromMintDecimals, - fromTokenAccountExists, - toTokenAccountExists, - fromTokenAccount, - toTokenAccount, - fromTokenAccountBalance, - toTokenAccountBalance, - toCustodyAddress, - toCustodyBalance, - getMetadata, - getNotReadyCause, - migrateTokens, - setToTokenAccountExists, - ]); - - return output; -} diff --git a/bridge_ui/src/hooks/useSyncTargetAddress.ts b/bridge_ui/src/hooks/useSyncTargetAddress.ts index 907e593dc..cc4b20821 100644 --- a/bridge_ui/src/hooks/useSyncTargetAddress.ts +++ b/bridge_ui/src/hooks/useSyncTargetAddress.ts @@ -1,9 +1,7 @@ import { - canonicalAddress, CHAIN_ID_ALEPHIUM, CHAIN_ID_ALGORAND, CHAIN_ID_SOLANA, - CHAIN_ID_TERRA, isEVMChain, uint8ArrayToHex, } from "@alephium/wormhole-sdk"; @@ -102,18 +100,6 @@ function useSyncTargetAddress(shouldFire: boolean, nft?: boolean) { } } })(); - } else if ( - targetChain === CHAIN_ID_TERRA && - terraWallet && - terraWallet.walletAddress - ) { - dispatch( - setTargetAddressHex( - uint8ArrayToHex( - zeroPad(canonicalAddress(terraWallet.walletAddress), 32) - ) - ) - ); } else if(targetChain === CHAIN_ID_ALEPHIUM && alphWallet.connectionStatus === 'connected') { dispatch(setTargetAddressHex(uint8ArrayToHex(base58.decode(alphWallet.account.address)))) } else if (targetChain === CHAIN_ID_ALGORAND && algoAccounts[0]) { diff --git a/bridge_ui/src/hooks/useTransactionFees.tsx b/bridge_ui/src/hooks/useTransactionFees.tsx index 0341decb0..310af7faf 100644 --- a/bridge_ui/src/hooks/useTransactionFees.tsx +++ b/bridge_ui/src/hooks/useTransactionFees.tsx @@ -18,7 +18,6 @@ import { TERRA_HOST, } from "../utils/consts"; import { getMultipleAccountsRPC } from "../utils/solana"; -import { NATIVE_TERRA_DECIMALS } from "../utils/terra"; import useIsWalletReady from "./useIsWalletReady"; import { LCDClient } from "@terra-money/terra.js"; import { NodeProvider } from "@alephium/web3"; @@ -163,8 +162,6 @@ const toBalanceString = (balance: bigint | undefined, chainId: ChainId) => { return formatUnits(balance, 18); //wei decimals } else if (chainId === CHAIN_ID_SOLANA) { return formatUnits(balance, 9); //lamports to sol decmals - } else if (chainId === CHAIN_ID_TERRA) { - return formatUnits(balance, NATIVE_TERRA_DECIMALS); } }; @@ -343,13 +340,6 @@ function EthGasEstimateSummary({ ); } -const terraEstimatesByContract = { - transfer: { - lowGasEstimate: BigInt(400000), - highGasEstimate: BigInt(700000), - }, -}; - export const evmEstimatesByContract = { transfer: { lowGasEstimate: BigInt(250000), @@ -402,37 +392,6 @@ export async function getGasEstimates( return output; } -function TerraGasEstimateSummary({ methodType }: { methodType: MethodType }) { - if (methodType === "transfer") { - const lowEstimate = formatUnits( - terraEstimatesByContract.transfer.lowGasEstimate, - NATIVE_TERRA_DECIMALS - ); - const highEstimate = formatUnits( - terraEstimatesByContract.transfer.highGasEstimate, - NATIVE_TERRA_DECIMALS - ); - return ( - -
- Est. Fees: {lowEstimate} - {highEstimate} - {" UST"} -
-
- ); - } else { - return null; - } -} - export function GasEstimateSummary({ methodType, chainId, @@ -450,8 +409,6 @@ export function GasEstimateSummary({ priceQuote={priceQuote} /> ); - } else if (chainId === CHAIN_ID_TERRA) { - return ; } else { return null; } diff --git a/bridge_ui/src/utils/algorand.ts b/bridge_ui/src/utils/algorand.ts deleted file mode 100644 index 4c0daf32d..000000000 --- a/bridge_ui/src/utils/algorand.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { TransactionSignerPair } from "@alephium/wormhole-sdk/lib/esm/algorand"; -import MyAlgoConnect from "@randlabs/myalgo-connect"; -import { Algodv2, assignGroupID, waitForConfirmation } from "algosdk"; -import { ALGORAND_WAIT_FOR_CONFIRMATIONS } from "./consts"; - -export async function signSendAndConfirmAlgorand( - algodClient: Algodv2, - txs: TransactionSignerPair[] -) { - const myAlgoConnect = new MyAlgoConnect(); - assignGroupID(txs.map((tx) => tx.tx)); - const signedTxns: Uint8Array[] = []; - const lsigSignedTxns: Uint8Array[] = []; - const walletUnsignedTxns: Uint8Array[] = []; - // sign all the lsigs - for (const lsigTx of txs) { - if (lsigTx.signer) { - lsigSignedTxns.push(await lsigTx.signer.signTxn(lsigTx.tx)); - } - } - // assemble the txs for the wallet to sign - for (const walletTx of txs) { - if (!walletTx.signer) { - walletUnsignedTxns.push(walletTx.tx.toByte()); - } - } - const walletSignedTxns = await myAlgoConnect.signTransaction( - walletUnsignedTxns - ); - let lsigIdx = 0; - let walletIdx = 0; - for (const originalTx of txs) { - if (originalTx.signer) { - signedTxns.push(lsigSignedTxns[lsigIdx++]); - } else { - signedTxns.push(walletSignedTxns[walletIdx++].blob); - } - } - await algodClient.sendRawTransaction(signedTxns).do(); - const result = await waitForConfirmation( - algodClient, - txs[txs.length - 1].tx.txID(), - ALGORAND_WAIT_FOR_CONFIRMATIONS - ); - return result; -} diff --git a/bridge_ui/src/utils/postVaa.ts b/bridge_ui/src/utils/postVaa.ts deleted file mode 100644 index 6106ef23a..000000000 --- a/bridge_ui/src/utils/postVaa.ts +++ /dev/null @@ -1,210 +0,0 @@ -import { chunks, importCoreWasm, ixFromRust } from "@alephium/wormhole-sdk"; -import { sendAndConfirmTransactionsWithRetry } from "@alephium/wormhole-sdk/lib/esm/utils/solana"; -import { - Connection, - Keypair, - PublicKey, - Transaction, - TransactionInstruction, -} from "@solana/web3.js"; - -export async function postVaaWithRetry( - connection: Connection, - signTransaction: (transaction: Transaction) => Promise, - bridge_id: string, - payer: string, - vaa: Buffer, - maxRetries: number -) { - const unsignedTransactions: Transaction[] = []; - const signature_set = Keypair.generate(); - const instructions = await createVerifySignaturesInstructions( - connection, - bridge_id, - payer, - vaa, - signature_set - ); - const finalInstruction = await createPostVaaInstruction( - bridge_id, - payer, - vaa, - signature_set - ); - if (!finalInstruction) { - return Promise.reject("Failed to construct the transaction."); - } - - //The verify signatures instructions can be batched into groups of 2 safely, - //reducing the total number of transactions. - const batchableChunks = chunks(instructions, 2); - batchableChunks.forEach((chunk) => { - let transaction; - if (chunk.length === 1) { - transaction = new Transaction().add(chunk[0]); - } else { - transaction = new Transaction().add(chunk[0], chunk[1]); - } - unsignedTransactions.push(transaction); - }); - - //the postVaa instruction can only execute after the verifySignature transactions have - //successfully completed. - const finalTransaction = new Transaction().add(finalInstruction); - - //The signature_set keypair also needs to sign the verifySignature transactions, thus a wrapper is needed. - const partialSignWrapper = (transaction: Transaction) => { - transaction.partialSign(signature_set); - return signTransaction(transaction); - }; - - await sendAndConfirmTransactionsWithRetry( - connection, - partialSignWrapper, - payer, - unsignedTransactions, - maxRetries - ); - //While the signature_set is used to create the final instruction, it doesn't need to sign it. - await sendAndConfirmTransactionsWithRetry( - connection, - signTransaction, - payer, - [finalTransaction], - maxRetries - ); - - return Promise.resolve(); -} - -/* - This returns an array of instructions required to verify the signatures of a VAA, and upload it to the blockchain. - signature_set should be a new keypair, and also needs to partial sign the transaction when these instructions are submitted. - */ -export async function createVerifySignaturesInstructions( - connection: Connection, - bridge_id: string, - payer: string, - vaa: Buffer, - signature_set: Keypair -): Promise { - const output: TransactionInstruction[] = []; - const { - guardian_set_address, - parse_guardian_set, - parse_vaa, - verify_signatures_ix, - } = await importCoreWasm(); - const { guardian_set_index } = parse_vaa(new Uint8Array(vaa)); - let guardian_addr = new PublicKey( - guardian_set_address(bridge_id, guardian_set_index) - ); - let acc = await connection.getAccountInfo(guardian_addr); - if (acc?.data === undefined) { - return output; - } - let guardian_data = parse_guardian_set(new Uint8Array(acc?.data)); - - let txs = verify_signatures_ix( - bridge_id, - payer, - guardian_set_index, - guardian_data, - signature_set.publicKey.toString(), - vaa - ); - // Add transfer instruction to transaction - for (let tx of txs) { - let ixs: Array = tx.map((v: any) => { - return ixFromRust(v); - }); - output.push(ixs[0], ixs[1]); - } - return output; -} - -/* - This will return the postVaaInstruction. This should only be executed after the verifySignaturesInstructions have been executed. - signatureSetKeypair should be the same keypair used for verifySignaturesInstructions, but does not need to partialSign the transaction - when this instruction is submitted. - */ -export async function createPostVaaInstruction( - bridge_id: string, - payer: string, - vaa: Buffer, - signatureSetKeypair: Keypair -): Promise { - const { post_vaa_ix } = await importCoreWasm(); - return ixFromRust( - post_vaa_ix(bridge_id, payer, signatureSetKeypair.publicKey.toString(), vaa) - ); -} - -/* - @deprecated - Instead, either use postVaaWithRetry or create, sign, and send the verifySignaturesInstructions & postVaaInstruction yourself. - - This function is equivalent to a postVaaWithRetry with a maxRetries of 0. - */ -export async function postVaa( - connection: Connection, - signTransaction: (transaction: Transaction) => Promise, - bridge_id: string, - payer: string, - vaa: Buffer -) { - const { - guardian_set_address, - parse_guardian_set, - parse_vaa, - post_vaa_ix, - verify_signatures_ix, - } = await importCoreWasm(); - const { guardian_set_index } = parse_vaa(new Uint8Array(vaa)); - let guardian_addr = new PublicKey( - guardian_set_address(bridge_id, guardian_set_index) - ); - let acc = await connection.getAccountInfo(guardian_addr); - if (acc?.data === undefined) { - return; - } - let guardian_data = parse_guardian_set(new Uint8Array(acc?.data)); - - let signature_set = Keypair.generate(); - let txs = verify_signatures_ix( - bridge_id, - payer, - guardian_set_index, - guardian_data, - signature_set.publicKey.toString(), - vaa - ); - // Add transfer instruction to transaction - for (let tx of txs) { - let ixs: Array = tx.map((v: any) => { - return ixFromRust(v); - }); - let transaction = new Transaction().add(...ixs); - const { blockhash } = await connection.getRecentBlockhash(); - transaction.recentBlockhash = blockhash; - transaction.feePayer = new PublicKey(payer); - transaction.partialSign(signature_set); - - // Sign transaction, broadcast, and confirm - const signed = await signTransaction(transaction); - const txid = await connection.sendRawTransaction(signed.serialize()); - await connection.confirmTransaction(txid); - } - - let ix = ixFromRust( - post_vaa_ix(bridge_id, payer, signature_set.publicKey.toString(), vaa) - ); - let transaction = new Transaction().add(ix); - const { blockhash } = await connection.getRecentBlockhash(); - transaction.recentBlockhash = blockhash; - transaction.feePayer = new PublicKey(payer); - - const signed = await signTransaction(transaction); - const txid = await connection.sendRawTransaction(signed.serialize()); - await connection.confirmTransaction(txid); -} diff --git a/bridge_ui/src/utils/terra.ts b/bridge_ui/src/utils/terra.ts deleted file mode 100644 index e9b43a3c1..000000000 --- a/bridge_ui/src/utils/terra.ts +++ /dev/null @@ -1,108 +0,0 @@ -import { - canonicalAddress, - isNativeDenom, - isNativeTerra, -} from "@alephium/wormhole-sdk"; -import { formatUnits } from "@ethersproject/units"; -import { LCDClient, isTxError } from "@terra-money/terra.js"; -import { ConnectedWallet, TxResult } from "@terra-money/wallet-provider"; -import axios from "axios"; -// import { TerraTokenMetadata } from "../hooks/useTerraTokenMap"; -import { TERRA_GAS_PRICES_URL, TERRA_HOST } from "./consts"; - -export const NATIVE_TERRA_DECIMALS = 6; -export const LUNA_CLASSIC_SYMBOL = "LUNC" - -export const getNativeTerraIcon = (symbol = "") => - `https://assets.terra.money/icon/60/${ - symbol === LUNA_CLASSIC_SYMBOL ? "Luna" : symbol.slice(0, symbol.length - 1) - }.png`; - -// inspired by https://github.com/terra-money/station/blob/dca7de43958ce075c6e46605622203b9859b0e14/src/lib/utils/format.ts#L38 -export const formatNativeDenom = (denom = ""): string => { - const unit = denom.slice(1).toUpperCase(); - const isValidTerra = isNativeTerra(denom); - return denom === "uluna" - ? LUNA_CLASSIC_SYMBOL - : isValidTerra - ? unit.slice(0, 2) + "TC" - : ""; -}; - -export const formatTerraNativeBalance = (balance = ""): string => - formatUnits(balance, 6); - -export async function waitForTerraExecution(transaction: TxResult) { - const lcd = new LCDClient(TERRA_HOST); - let info; - while (!info) { - await new Promise((resolve) => setTimeout(resolve, 1000)); - try { - info = await lcd.tx.txInfo(transaction.result.txhash); - } catch (e) { - console.error(e); - } - } - if (isTxError(info)) { - throw new Error( - `Tx ${transaction.result.txhash}: error code ${info.code}: ${info.raw_log}` - ); - } - return info; -} - -export const isValidTerraAddress = (address: string) => { - if (isNativeDenom(address)) { - return true; - } - try { - const startsWithTerra = address && address.startsWith("terra"); - const isParseable = canonicalAddress(address); - const isLength20 = isParseable.length === 20; - return !!(startsWithTerra && isParseable && isLength20); - } catch (error) { - return false; - } -}; - -export async function postWithFees( - wallet: ConnectedWallet, - msgs: any[], - memo: string, - feeDenoms: string[] -) { - // don't try/catch, let errors propagate - const lcd = new LCDClient(TERRA_HOST); - //let gasPrices = await lcd.config.gasPrices //Unsure if the values returned from this are hardcoded or not. - //Thus, we are going to pull it directly from the current FCD. - const gasPrices = await axios - .get(TERRA_GAS_PRICES_URL) - .then((result) => result.data); - - const account = await lcd.auth.accountInfo(wallet.walletAddress); - - const feeEstimate = await lcd.tx.estimateFee( - [ - { - sequenceNumber: account.getSequenceNumber(), - publicKey: account.getPublicKey(), - }, - ], - { - msgs: [...msgs], - memo, - feeDenoms, - gasPrices, - } - ); - - const result = await wallet.post({ - msgs: [...msgs], - memo, - feeDenoms, - gasPrices, - fee: feeEstimate, - }); - - return result; -}