diff --git a/bridge_ui/package.json b/bridge_ui/package.json index d5fd9e35..721cbdb3 100644 --- a/bridge_ui/package.json +++ b/bridge_ui/package.json @@ -12,7 +12,9 @@ "@solana/spl-token": "^0.1.6", "@solana/wallet-base": "^0.0.1", "@solana/web3.js": "^1.22.0", + "@terra-money/wallet-provider": "^1.4.0-alpha.1", "ethers": "^5.4.1", + "js-base64": "^3.6.1", "react": "^17.0.2", "react-dom": "^17.0.2", "react-redux": "^7.2.4", diff --git a/bridge_ui/src/components/Attest/Create.tsx b/bridge_ui/src/components/Attest/Create.tsx index d365897c..fd86b86a 100644 --- a/bridge_ui/src/components/Attest/Create.tsx +++ b/bridge_ui/src/components/Attest/Create.tsx @@ -1,6 +1,7 @@ -import { CHAIN_ID_ETH, CHAIN_ID_SOLANA } from "@certusone/wormhole-sdk"; +import { CHAIN_ID_TERRA, CHAIN_ID_ETH, CHAIN_ID_SOLANA } from "@certusone/wormhole-sdk"; import { Button, CircularProgress, makeStyles } from "@material-ui/core"; import { useCallback } from "react"; +import { useConnectedWallet } from "@terra-money/wallet-provider"; import { useDispatch, useSelector } from "react-redux"; import { useEthereumProvider } from "../../contexts/EthereumProviderContext"; import { useSolanaWallet } from "../../contexts/SolanaWalletContext"; @@ -13,6 +14,7 @@ import { import { createWrappedOnEth, createWrappedOnSolana, + createWrappedOnTerra, } from "../../utils/createWrappedOn"; const useStyles = makeStyles((theme) => ({ @@ -32,6 +34,7 @@ function Create() { const signedVAA = useAttestSignedVAA(); const isCreating = useSelector(selectAttestIsCreating); const { signer } = useEthereumProvider(); + const terraWallet = useConnectedWallet(); const handleCreateClick = useCallback(() => { if (targetChain === CHAIN_ID_SOLANA && signedVAA) { (async () => { @@ -47,6 +50,12 @@ function Create() { dispatch(reset()); })(); } + if (targetChain === CHAIN_ID_TERRA && signedVAA) { + (async () => { + dispatch(setIsCreating(true)); + createWrappedOnTerra(terraWallet, signedVAA); + })(); + } }, [dispatch, targetChain, wallet, solPK, signedVAA, signer]); return (
diff --git a/bridge_ui/src/components/Attest/Send.tsx b/bridge_ui/src/components/Attest/Send.tsx index f2ea49ae..46da0a00 100644 --- a/bridge_ui/src/components/Attest/Send.tsx +++ b/bridge_ui/src/components/Attest/Send.tsx @@ -1,9 +1,10 @@ -import { CHAIN_ID_ETH, CHAIN_ID_SOLANA } from "@certusone/wormhole-sdk"; +import { CHAIN_ID_TERRA, CHAIN_ID_ETH, CHAIN_ID_SOLANA } from "@certusone/wormhole-sdk"; import { Button, CircularProgress, makeStyles } from "@material-ui/core"; import { useCallback } from "react"; import { useDispatch, useSelector } from "react-redux"; import { useEthereumProvider } from "../../contexts/EthereumProviderContext"; import { useSolanaWallet } from "../../contexts/SolanaWalletContext"; +import { useConnectedWallet } from "@terra-money/wallet-provider"; import { setIsSending, setSignedVAAHex } from "../../store/attestSlice"; import { selectAttestIsSendComplete, @@ -13,7 +14,11 @@ import { selectAttestSourceChain, } from "../../store/selectors"; import { uint8ArrayToHex } from "../../utils/array"; -import { attestFromEth, attestFromSolana } from "../../utils/attestFrom"; +import { + attestFromEth, + attestFromSolana, + attestFromTerra, +} from "../../utils/attestFrom"; const useStyles = makeStyles((theme) => ({ transferButton: { @@ -35,6 +40,7 @@ function Send() { const isSendComplete = useSelector(selectAttestIsSendComplete); const { signer } = useEthereumProvider(); const { wallet } = useSolanaWallet(); + const terraWallet = useConnectedWallet(); const solPK = wallet?.publicKey; // TODO: dynamically get "to" wallet const handleAttestClick = useCallback(() => { @@ -68,7 +74,20 @@ function Send() { dispatch(setIsSending(false)); } })(); - } + } else if (sourceChain === CHAIN_ID_TERRA) { + //TODO: just for testing, this should eventually use the store to communicate between steps + (async () => { + dispatch(setIsSending(true)); + try { + const vaaBytes = await attestFromTerra(terraWallet, sourceAsset); + console.log("bytes in attest", vaaBytes); + vaaBytes && dispatch(setSignedVAAHex(uint8ArrayToHex(vaaBytes))); + } catch (e) { + console.error(e); + dispatch(setIsSending(false)); + } + })(); + } }, [dispatch, sourceChain, signer, wallet, solPK, sourceAsset]); return ( <> diff --git a/bridge_ui/src/components/KeyAndBalance.tsx b/bridge_ui/src/components/KeyAndBalance.tsx index 23f53344..fd428cf2 100644 --- a/bridge_ui/src/components/KeyAndBalance.tsx +++ b/bridge_ui/src/components/KeyAndBalance.tsx @@ -2,10 +2,12 @@ import { ChainId, CHAIN_ID_ETH, CHAIN_ID_SOLANA, + CHAIN_ID_TERRA, } from "@certusone/wormhole-sdk"; import { Typography } from "@material-ui/core"; import EthereumSignerKey from "./EthereumSignerKey"; import SolanaWalletKey from "./SolanaWalletKey"; +import TerraWalletKey from "./TerraWalletKey"; function KeyAndBalance({ chainId, @@ -30,6 +32,14 @@ function KeyAndBalance({ ); } + if (chainId === CHAIN_ID_TERRA) { + return ( + <> + + {balance} + + ); + } return null; } diff --git a/bridge_ui/src/components/TerraWalletKey.tsx b/bridge_ui/src/components/TerraWalletKey.tsx new file mode 100644 index 00000000..38456c33 --- /dev/null +++ b/bridge_ui/src/components/TerraWalletKey.tsx @@ -0,0 +1,22 @@ +import { useTerraWallet } from "../contexts/TerraWalletContext"; +import ToggleConnectedButton from "./ToggleConnectedButton"; + +const TerraWalletKey = () => { + const { connect, disconnect, connected, wallet } = useTerraWallet(); + const pk = + (wallet && + wallet.wallets && + wallet.wallets.length > 0 && + wallet.wallets[0].terraAddress) || + ""; + return ( + + ); +}; + +export default TerraWalletKey; diff --git a/bridge_ui/src/components/Transfer/Redeem.tsx b/bridge_ui/src/components/Transfer/Redeem.tsx index d62ed145..981d26ea 100644 --- a/bridge_ui/src/components/Transfer/Redeem.tsx +++ b/bridge_ui/src/components/Transfer/Redeem.tsx @@ -1,7 +1,12 @@ -import { CHAIN_ID_ETH, CHAIN_ID_SOLANA } from "@certusone/wormhole-sdk"; +import { + CHAIN_ID_TERRA, + CHAIN_ID_ETH, + CHAIN_ID_SOLANA, +} from "@certusone/wormhole-sdk"; import { Button, CircularProgress, makeStyles } from "@material-ui/core"; import { useCallback } from "react"; import { useDispatch, useSelector } from "react-redux"; +import { useConnectedWallet } from "@terra-money/wallet-provider"; import { useEthereumProvider } from "../../contexts/EthereumProviderContext"; import { useSolanaWallet } from "../../contexts/SolanaWalletContext"; import useTransferSignedVAA from "../../hooks/useTransferSignedVAA"; @@ -13,7 +18,11 @@ import { selectTransferTargetChain, } from "../../store/selectors"; import { reset, setIsRedeeming } from "../../store/transferSlice"; -import { redeemOnEth, redeemOnSolana } from "../../utils/redeemOn"; +import { + redeemOnEth, + redeemOnSolana, + redeemOnTerra, +} from "../../utils/redeemOn"; const useStyles = makeStyles((theme) => ({ transferButton: { @@ -35,6 +44,7 @@ function Redeem() { const { wallet } = useSolanaWallet(); const solPK = wallet?.publicKey; const { signer } = useEthereumProvider(); + const terraWallet = useConnectedWallet(); const signedVAA = useTransferSignedVAA(); const isRedeeming = useSelector(selectTransferIsRedeeming); const handleRedeemClick = useCallback(() => { @@ -58,8 +68,13 @@ function Redeem() { dispatch(reset()); })(); } + if (targetChain === CHAIN_ID_TERRA && signedVAA) { + dispatch(setIsRedeeming(true)); + redeemOnTerra(terraWallet, signedVAA); + } }, [ dispatch, + terraWallet, targetChain, signer, signedVAA, diff --git a/bridge_ui/src/components/Transfer/Send.tsx b/bridge_ui/src/components/Transfer/Send.tsx index c133e2cc..8298141b 100644 --- a/bridge_ui/src/components/Transfer/Send.tsx +++ b/bridge_ui/src/components/Transfer/Send.tsx @@ -1,4 +1,4 @@ -import { CHAIN_ID_ETH, CHAIN_ID_SOLANA } from "@certusone/wormhole-sdk"; +import { CHAIN_ID_TERRA, CHAIN_ID_ETH, CHAIN_ID_SOLANA } from "@certusone/wormhole-sdk"; import { Button, CircularProgress, makeStyles } from "@material-ui/core"; import { ASSOCIATED_TOKEN_PROGRAM_ID, @@ -27,7 +27,11 @@ import { } from "../../store/selectors"; import { setIsSending, setSignedVAAHex } from "../../store/transferSlice"; import { uint8ArrayToHex } from "../../utils/array"; -import { transferFromEth, transferFromSolana } from "../../utils/transferFrom"; +import { + transferFromEth, + transferFromSolana, + transferFromTerra, +} from "../../utils/transferFrom"; const useStyles = makeStyles((theme) => ({ transferButton: { @@ -146,6 +150,19 @@ function Send() { } })(); } + if (sourceChain === CHAIN_ID_TERRA && decimals) { + (async () => { + dispatch(setIsSending(true)); + try { + const vaaBytes = await transferFromTerra(undefined); + console.log("bytes in transfer", vaaBytes); + vaaBytes && dispatch(setSignedVAAHex(uint8ArrayToHex(vaaBytes))); + } catch (e) { + console.error(e); + dispatch(setIsSending(false)); + } + })(); + } }, [ dispatch, sourceChain, diff --git a/bridge_ui/src/components/Transfer/index.tsx b/bridge_ui/src/components/Transfer/index.tsx index 3e69ba84..992921ad 100644 --- a/bridge_ui/src/components/Transfer/index.tsx +++ b/bridge_ui/src/components/Transfer/index.tsx @@ -29,9 +29,11 @@ function Transfer() { useCheckIfWormholeWrapped(); useFetchTargetAsset(); useGetBalanceEffect("target"); + const dispatch = useDispatch(); const activeStep = useSelector(selectTransferActiveStep); const signedVAAHex = useSelector(selectTransferSignedVAAHex); + return ( diff --git a/bridge_ui/src/contexts/TerraWalletContext.tsx b/bridge_ui/src/contexts/TerraWalletContext.tsx new file mode 100644 index 00000000..55df570f --- /dev/null +++ b/bridge_ui/src/contexts/TerraWalletContext.tsx @@ -0,0 +1,105 @@ +import { + NetworkInfo, + Wallet, + WalletProvider, + useWallet, +} from "@terra-money/wallet-provider"; +import React, { + ReactChildren, + useCallback, + useContext, + useMemo, + useState, +} from "react"; +import { TERRA_HOST } from "../utils/consts"; + +const mainnet = { + name: "mainnet", + chainID: "columbus-4", + lcd: "https://lcd.terra.dev", +}; + +const localnet = { + name: "localnet", + chainID: "localnet", + lcd: TERRA_HOST, +}; + +const walletConnectChainIds: Record = { + 0: localnet, + 1: mainnet, +}; + +interface ITerraWalletContext { + connect(): void; + disconnect(): void; + connected: boolean; + wallet: any; +} + +const TerraWalletContext = React.createContext({ + connect: () => {}, + disconnect: () => {}, + connected: false, + wallet: null, +}); + +export const TerraWalletWrapper = ({ + children, +}: { + children: ReactChildren; +}) => { + // TODO: Use wallet instead of useConnectedWallet. + const terraWallet = useWallet(); + const [wallet, setWallet] = useState(undefined); + const [connected, setConnected] = useState(false); + + const connect = useCallback(() => { + const CHROME_EXTENSION = 1; + if (terraWallet) { + terraWallet.connect(terraWallet.availableConnectTypes[CHROME_EXTENSION]); + setWallet(terraWallet); + setConnected(true); + } + }, [terraWallet]); + + const disconnect = useCallback(() => { + setConnected(false); + setWallet(undefined); + }, []); + + const contextValue = useMemo( + () => ({ + connect, + disconnect, + connected, + wallet: terraWallet, + }), + [connect, disconnect, connected, terraWallet] + ); + + return ( + + {children} + + ); +}; + +export const TerraWalletProvider = ({ + children, +}: { + children: ReactChildren; +}) => { + return ( + + {children} + + ); +}; + +export const useTerraWallet = () => { + return useContext(TerraWalletContext); +}; diff --git a/bridge_ui/src/hooks/useCheckIfWormholeWrapped.ts b/bridge_ui/src/hooks/useCheckIfWormholeWrapped.ts index 4fa71767..b10227b7 100644 --- a/bridge_ui/src/hooks/useCheckIfWormholeWrapped.ts +++ b/bridge_ui/src/hooks/useCheckIfWormholeWrapped.ts @@ -1,4 +1,4 @@ -import { CHAIN_ID_ETH, CHAIN_ID_SOLANA } from "@certusone/wormhole-sdk"; +import { CHAIN_ID_ETH, CHAIN_ID_SOLANA, CHAIN_ID_TERRA } from "@certusone/wormhole-sdk"; import { useEffect } from "react"; import { useDispatch, useSelector } from "react-redux"; import { useEthereumProvider } from "../contexts/EthereumProviderContext"; @@ -10,8 +10,12 @@ import { setSourceWormholeWrappedInfo } from "../store/transferSlice"; import { getOriginalAssetEth, getOriginalAssetSol, + getOriginalAssetTerra, } from "../utils/getOriginalAsset"; +// Check if the tokens in the configured source chain/address are wrapped +// tokens. Wrapped tokens are tokens that are non-native, I.E, are locked up on +// a different chain than this one. function useCheckIfWormholeWrapped() { const dispatch = useDispatch(); const sourceChain = useSelector(selectTransferSourceChain); @@ -27,7 +31,8 @@ function useCheckIfWormholeWrapped() { if (!cancelled) { dispatch(setSourceWormholeWrappedInfo(wrappedInfo)); } - } else if (sourceChain === CHAIN_ID_SOLANA) { + } + if (sourceChain === CHAIN_ID_SOLANA) { try { const wrappedInfo = await getOriginalAssetSol(sourceAsset); if (!cancelled) { @@ -35,6 +40,14 @@ function useCheckIfWormholeWrapped() { } } catch (e) {} } + if (sourceChain === CHAIN_ID_TERRA) { + try { + const wrappedInfo = await getOriginalAssetTerra(sourceAsset); + if (!cancelled) { + dispatch(setSourceWormholeWrappedInfo(wrappedInfo)); + } + } catch (e) {} + } })(); return () => { cancelled = true; diff --git a/bridge_ui/src/hooks/useFetchTargetAsset.ts b/bridge_ui/src/hooks/useFetchTargetAsset.ts index e4f60a54..d63ea888 100644 --- a/bridge_ui/src/hooks/useFetchTargetAsset.ts +++ b/bridge_ui/src/hooks/useFetchTargetAsset.ts @@ -1,4 +1,4 @@ -import { CHAIN_ID_ETH, CHAIN_ID_SOLANA } from "@certusone/wormhole-sdk"; +import { CHAIN_ID_TERRA, CHAIN_ID_ETH, CHAIN_ID_SOLANA } from "@certusone/wormhole-sdk"; import { useEffect } from "react"; import { useDispatch, useSelector } from "react-redux"; import { useEthereumProvider } from "../contexts/EthereumProviderContext"; @@ -14,6 +14,7 @@ import { setTargetAsset } from "../store/transferSlice"; import { getForeignAssetEth, getForeignAssetSol, + getForeignAssetTerra, } from "../utils/getForeignAsset"; function useFetchTargetAsset() { @@ -51,7 +52,8 @@ function useFetchTargetAsset() { if (!cancelled) { dispatch(setTargetAsset(asset)); } - } else if (targetChain === CHAIN_ID_SOLANA) { + } + if (targetChain === CHAIN_ID_SOLANA) { try { const asset = await getForeignAssetSol(sourceChain, sourceAsset); if (!cancelled) { @@ -64,6 +66,19 @@ function useFetchTargetAsset() { } } } + if (targetChain === CHAIN_ID_TERRA) { + try { + const asset = await getForeignAssetTerra(sourceChain, sourceAsset); + if (!cancelled) { + console.log("terra target asset", asset); + dispatch(setTargetAsset(asset)); + } + } catch (e) { + if (!cancelled) { + // TODO: warning for this + } + } + } })(); return () => { cancelled = true; diff --git a/bridge_ui/src/hooks/useGetBalanceEffect.ts b/bridge_ui/src/hooks/useGetBalanceEffect.ts index e7c5c554..1acaf0b9 100644 --- a/bridge_ui/src/hooks/useGetBalanceEffect.ts +++ b/bridge_ui/src/hooks/useGetBalanceEffect.ts @@ -1,6 +1,7 @@ import { CHAIN_ID_ETH, CHAIN_ID_SOLANA, + CHAIN_ID_TERRA, TokenImplementation__factory, } from "@certusone/wormhole-sdk"; import { Connection, PublicKey } from "@solana/web3.js"; @@ -68,6 +69,13 @@ function useGetBalanceEffect(sourceOrTarget: "source" | "target") { return; } let cancelled = false; + if (lookupChain === CHAIN_ID_TERRA && wallet) { + dispatch( + setSourceParsedTokenAccount( + createParsedTokenAccount(undefined, "0", 0, 0, "0") + ) + ); + } if (lookupChain === CHAIN_ID_SOLANA && solPK) { let mint; try { @@ -143,6 +151,7 @@ function useGetBalanceEffect(sourceOrTarget: "source" | "target") { }; }, [ dispatch, + wallet, sourceOrTarget, setAction, lookupChain, diff --git a/bridge_ui/src/index.js b/bridge_ui/src/index.js index 2be6801f..39c2dc77 100644 --- a/bridge_ui/src/index.js +++ b/bridge_ui/src/index.js @@ -7,6 +7,7 @@ import App from "./App"; import RadialGradient from "./components/RadialGradient"; import { EthereumProviderProvider } from "./contexts/EthereumProviderContext"; import { SolanaWalletProvider } from "./contexts/SolanaWalletContext.tsx"; +import { TerraWalletProvider } from "./contexts/TerraWalletContext.tsx"; import { theme } from "./muiTheme"; import { store } from "./store"; @@ -17,9 +18,11 @@ ReactDOM.render( - - - + + + + + diff --git a/bridge_ui/src/store/attestSlice.ts b/bridge_ui/src/store/attestSlice.ts index cc0d1bd5..fab187d4 100644 --- a/bridge_ui/src/store/attestSlice.ts +++ b/bridge_ui/src/store/attestSlice.ts @@ -2,11 +2,13 @@ import { ChainId, CHAIN_ID_ETH, CHAIN_ID_SOLANA, + CHAIN_ID_TERRA, } from "@certusone/wormhole-sdk"; import { createSlice, PayloadAction } from "@reduxjs/toolkit"; import { ETH_TEST_TOKEN_ADDRESS, SOL_TEST_TOKEN_ADDRESS, + TERRA_TEST_TOKEN_ADDRESS, } from "../utils/consts"; const LAST_STEP = 3; @@ -56,6 +58,9 @@ export const attestSlice = createSlice({ if (action.payload === CHAIN_ID_SOLANA) { state.sourceAsset = SOL_TEST_TOKEN_ADDRESS; } + if (action.payload === CHAIN_ID_TERRA) { + state.sourceAsset = TERRA_TEST_TOKEN_ADDRESS; + } if (state.targetChain === action.payload) { state.targetChain = prevSourceChain; } @@ -76,6 +81,9 @@ export const attestSlice = createSlice({ if (state.targetChain === CHAIN_ID_SOLANA) { state.sourceAsset = SOL_TEST_TOKEN_ADDRESS; } + if (state.targetChain === CHAIN_ID_TERRA) { + state.sourceAsset = TERRA_TEST_TOKEN_ADDRESS; + } } }, setSignedVAAHex: (state, action: PayloadAction) => { diff --git a/bridge_ui/src/store/transferSlice.ts b/bridge_ui/src/store/transferSlice.ts index c9fd9c18..92d7e3b8 100644 --- a/bridge_ui/src/store/transferSlice.ts +++ b/bridge_ui/src/store/transferSlice.ts @@ -2,11 +2,13 @@ import { ChainId, CHAIN_ID_ETH, CHAIN_ID_SOLANA, + CHAIN_ID_TERRA, } from "@certusone/wormhole-sdk"; import { createSlice, PayloadAction } from "@reduxjs/toolkit"; import { ETH_TEST_TOKEN_ADDRESS, SOL_TEST_TOKEN_ADDRESS, + TERRA_TEST_TOKEN_ADDRESS, } from "../utils/consts"; import { StateSafeWormholeWrappedInfo } from "../utils/getOriginalAsset"; @@ -79,6 +81,9 @@ export const transferSlice = createSlice({ if (action.payload === CHAIN_ID_SOLANA) { state.sourceAsset = SOL_TEST_TOKEN_ADDRESS; } + if (action.payload === CHAIN_ID_TERRA) { + state.sourceAsset = TERRA_TEST_TOKEN_ADDRESS; + } if (state.targetChain === action.payload) { state.targetChain = prevSourceChain; } @@ -122,6 +127,9 @@ export const transferSlice = createSlice({ if (state.targetChain === CHAIN_ID_SOLANA) { state.sourceAsset = SOL_TEST_TOKEN_ADDRESS; } + if (state.targetChain === CHAIN_ID_TERRA) { + state.sourceAsset = TERRA_TEST_TOKEN_ADDRESS; + } } }, setTargetAsset: ( diff --git a/bridge_ui/src/utils/attestFrom.ts b/bridge_ui/src/utils/attestFrom.ts index 0abad55c..2f592b26 100644 --- a/bridge_ui/src/utils/attestFrom.ts +++ b/bridge_ui/src/utils/attestFrom.ts @@ -3,6 +3,7 @@ import { attestFromSolana as attestSolanaTx, CHAIN_ID_ETH, CHAIN_ID_SOLANA, + CHAIN_ID_TERRA, getEmitterAddressEth, getEmitterAddressSolana, parseSequenceFromLogEth, @@ -10,10 +11,13 @@ import { } from "@certusone/wormhole-sdk"; import Wallet from "@project-serum/sol-wallet-adapter"; import { Connection } from "@solana/web3.js"; +import { MsgExecuteContract } from "@terra-money/terra.js"; +import { ConnectedWallet as TerraConnectedWallet } from "@terra-money/wallet-provider"; import { ethers } from "ethers"; import { ETH_BRIDGE_ADDRESS, ETH_TOKEN_BRIDGE_ADDRESS, + TERRA_TOKEN_BRIDGE_ADDRESS, SOLANA_HOST, SOL_BRIDGE_ADDRESS, SOL_TOKEN_BRIDGE_ADDRESS, @@ -71,3 +75,29 @@ export async function attestFromSolana( ); return vaaBytes; } + +export async function attestFromTerra( + wallet: TerraConnectedWallet | undefined, + asset: string | undefined +) { + const nonceConst = Math.random() * 100000; + const nonceBuffer = Buffer.alloc(4); + nonceBuffer.writeUInt32LE(nonceConst, 0); + wallet && + (await wallet.post({ + msgs: [ + new MsgExecuteContract( + wallet.terraAddress, + TERRA_TOKEN_BRIDGE_ADDRESS, + { + register_asset_hook: { + asset_id: asset, + }, + }, + { uluna: 1000 } + ), + ], + memo: "Create Wrapped", + })); + return null; +} diff --git a/bridge_ui/src/utils/consts.ts b/bridge_ui/src/utils/consts.ts index 219eb6ec..2f6771d1 100644 --- a/bridge_ui/src/utils/consts.ts +++ b/bridge_ui/src/utils/consts.ts @@ -36,6 +36,7 @@ export const CHAINS_BY_ID: ChainsById = CHAINS.reduce((obj, chain) => { }, {} as ChainsById); export const WORMHOLE_RPC_HOST = "http://localhost:8080"; export const SOLANA_HOST = "http://localhost:8899"; +export const TERRA_HOST = "http://localhost:1317"; export const ETH_TEST_TOKEN_ADDRESS = getAddress( "0x67B5656d60a809915323Bf2C40A8bEF15A152e3e" ); @@ -50,3 +51,9 @@ export const SOL_TEST_TOKEN_ADDRESS = export const SOL_BRIDGE_ADDRESS = "Bridge1p5gheXUvJ6jGWGeCsgPKgnE3YgdGKRVCMY9o"; export const SOL_TOKEN_BRIDGE_ADDRESS = "B6RHG3mfcckmrYN1UhmJzyS1XX3fZKbkeUcpJe9Sy3FE"; +export const TERRA_TEST_TOKEN_ADDRESS = + "terra13nkgqrfymug724h8pprpexqj9h629sa3ncw7sh"; +export const TERRA_BRIDGE_ADDRESS = + "terra18vd8fpwxzck93qlwghaj6arh4p7c5n896xzem5"; +export const TERRA_TOKEN_BRIDGE_ADDRESS = + "terra10pyejy66429refv3g35g2t7am0was7ya7kz2a4"; diff --git a/bridge_ui/src/utils/createWrappedOn.ts b/bridge_ui/src/utils/createWrappedOn.ts index 7c22e049..7faa51be 100644 --- a/bridge_ui/src/utils/createWrappedOn.ts +++ b/bridge_ui/src/utils/createWrappedOn.ts @@ -6,11 +6,16 @@ import { import Wallet from "@project-serum/sol-wallet-adapter"; import { Connection } from "@solana/web3.js"; import { ethers } from "ethers"; +import { fromUint8Array } from "js-base64"; +import { MsgExecuteContract } from "@terra-money/terra.js"; +import { ConnectedWallet as TerraConnectedWallet } from "@terra-money/wallet-provider"; import { ETH_TOKEN_BRIDGE_ADDRESS, SOLANA_HOST, SOL_BRIDGE_ADDRESS, SOL_TOKEN_BRIDGE_ADDRESS, + TERRA_TOKEN_BRIDGE_ADDRESS, + TERRA_BRIDGE_ADDRESS, } from "./consts"; import { signSendAndConfirm } from "./solana"; @@ -22,6 +27,32 @@ export async function createWrappedOnEth( await createWrappedOnEthTx(ETH_TOKEN_BRIDGE_ADDRESS, signer, signedVAA); } +export async function createWrappedOnTerra( + wallet: TerraConnectedWallet | undefined, + signedVAA: Uint8Array +) { + console.log("creating wrapped"); + console.log("PROGRAM:", TERRA_TOKEN_BRIDGE_ADDRESS); + console.log("BRIDGE:", TERRA_BRIDGE_ADDRESS); + console.log("VAA:", signedVAA); + wallet && + (await wallet.post({ + msgs: [ + new MsgExecuteContract( + wallet.terraAddress, + TERRA_TOKEN_BRIDGE_ADDRESS, + { + submit_vaa: { + data: fromUint8Array(signedVAA), + }, + }, + { uluna: 1000 } + ), + ], + memo: "Create Wrapped", + })); +} + export async function createWrappedOnSolana( wallet: Wallet | undefined, payerAddress: string | undefined, //TODO: we may not need this since we have wallet diff --git a/bridge_ui/src/utils/getForeignAsset.ts b/bridge_ui/src/utils/getForeignAsset.ts index e2786b14..7e25ed72 100644 --- a/bridge_ui/src/utils/getForeignAsset.ts +++ b/bridge_ui/src/utils/getForeignAsset.ts @@ -56,3 +56,16 @@ export async function getForeignAssetSol( originAssetBytes ); } + +/** + * Returns a foreign asset address on Terra for a provided native chain and asset address + * @param originChain + * @param originAsset + * @returns + */ +export async function getForeignAssetTerra( + originChain: ChainId, + originAsset: string +) { + return null; +} diff --git a/bridge_ui/src/utils/getOriginalAsset.ts b/bridge_ui/src/utils/getOriginalAsset.ts index 539725a9..8924556d 100644 --- a/bridge_ui/src/utils/getOriginalAsset.ts +++ b/bridge_ui/src/utils/getOriginalAsset.ts @@ -52,3 +52,13 @@ export async function getOriginalAssetSol( ) ); } + +export async function getOriginalAssetTerra( + mintAddress: string +): Promise { + return { + assetAddress: "", + chainId: 3, + isWrapped: false, + }; +} diff --git a/bridge_ui/src/utils/redeemOn.ts b/bridge_ui/src/utils/redeemOn.ts index 74eb50f1..2ae1eee9 100644 --- a/bridge_ui/src/utils/redeemOn.ts +++ b/bridge_ui/src/utils/redeemOn.ts @@ -6,11 +6,15 @@ import { import Wallet from "@project-serum/sol-wallet-adapter"; import { Connection } from "@solana/web3.js"; import { ethers } from "ethers"; +import { fromUint8Array } from 'js-base64'; +import { ConnectedWallet as TerraConnectedWallet } from "@terra-money/wallet-provider"; +import { MsgExecuteContract } from "@terra-money/terra.js"; import { ETH_TOKEN_BRIDGE_ADDRESS, SOLANA_HOST, SOL_BRIDGE_ADDRESS, SOL_TOKEN_BRIDGE_ADDRESS, + TERRA_TOKEN_BRIDGE_ADDRESS, } from "./consts"; import { signSendAndConfirm } from "./solana"; @@ -50,3 +54,25 @@ export async function redeemOnSolana( ); await signSendAndConfirm(wallet, connection, transaction); } + +export async function redeemOnTerra( + wallet: TerraConnectedWallet | undefined, + signedVAA: Uint8Array, +) { + if (!wallet) return; + wallet && await wallet.post({ + msgs: [ + new MsgExecuteContract( + wallet.terraAddress, + TERRA_TOKEN_BRIDGE_ADDRESS, + { + submit_vaa: { + data: fromUint8Array(signedVAA) + }, + }, + { uluna: 1000 } + ), + ], + memo: "Complete Transfer", + }); +} diff --git a/bridge_ui/src/utils/transferFrom.ts b/bridge_ui/src/utils/transferFrom.ts index 350872b6..4cb798af 100644 --- a/bridge_ui/src/utils/transferFrom.ts +++ b/bridge_ui/src/utils/transferFrom.ts @@ -1,6 +1,7 @@ import { ChainId, CHAIN_ID_ETH, + CHAIN_ID_TERRA, CHAIN_ID_SOLANA, getEmitterAddressEth, getEmitterAddressSolana, @@ -9,6 +10,7 @@ import { transferFromEth as transferFromEthTx, transferFromSolana as transferFromSolanaTx, } from "@certusone/wormhole-sdk"; +import { Wallet as TerraWallet } from "@terra-money/wallet-provider"; import Wallet from "@project-serum/sol-wallet-adapter"; import { Connection } from "@solana/web3.js"; import { ethers } from "ethers"; @@ -110,3 +112,9 @@ export async function transferFromSolana( ); return vaaBytes; } + +export async function transferFromTerra( + wallet: TerraWallet | undefined, +) { + return null; +}