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;
+}