bridge_ui: support terra source

Change-Id: Ia137e1c161851b946f86d08355fc197f43ae4fbf
This commit is contained in:
chase-45 2021-08-30 19:58:06 -04:00
parent 69349ab5c7
commit 0b9a2e05ad
4 changed files with 154 additions and 32 deletions

View File

@ -1,6 +1,10 @@
//import Autocomplete from '@material-ui/lab/Autocomplete'; //import Autocomplete from '@material-ui/lab/Autocomplete';
import { CHAIN_ID_ETH, CHAIN_ID_SOLANA } from "@certusone/wormhole-sdk"; import {
import { TextField } from "@material-ui/core"; CHAIN_ID_ETH,
CHAIN_ID_SOLANA,
CHAIN_ID_TERRA,
} from "@certusone/wormhole-sdk";
import { TextField, Typography } from "@material-ui/core";
import { useCallback } from "react"; import { useCallback } from "react";
import { useDispatch, useSelector } from "react-redux"; import { useDispatch, useSelector } from "react-redux";
import useGetSourceParsedTokens from "../../hooks/useGetSourceParsedTokenAccounts"; import useGetSourceParsedTokens from "../../hooks/useGetSourceParsedTokenAccounts";
@ -14,6 +18,7 @@ import {
} from "../../store/transferSlice"; } from "../../store/transferSlice";
import EthereumSourceTokenSelector from "./EthereumSourceTokenSelector"; import EthereumSourceTokenSelector from "./EthereumSourceTokenSelector";
import SolanaSourceTokenSelector from "./SolanaSourceTokenSelector"; import SolanaSourceTokenSelector from "./SolanaSourceTokenSelector";
import TerraSourceTokenSelector from "./TerraSourceTokenSelector";
type TokenSelectorProps = { type TokenSelectorProps = {
disabled: boolean; disabled: boolean;
@ -37,29 +42,37 @@ export const TokenSelector = (props: TokenSelectorProps) => {
const maps = useGetSourceParsedTokens(); const maps = useGetSourceParsedTokens();
const content = //This is only for errors so bad that we shouldn't even mount the component
lookupChain === CHAIN_ID_SOLANA ? ( const fatalError = maps?.tokenAccounts?.error;
<SolanaSourceTokenSelector
value={sourceParsedTokenAccount || null} const content = fatalError ? (
onChange={handleSolanaOnChange} <Typography>{fatalError}</Typography>
accounts={maps?.tokenAccounts?.data || []} ) : lookupChain === CHAIN_ID_SOLANA ? (
solanaTokenMap={maps?.tokenMap} <SolanaSourceTokenSelector
metaplexData={maps?.metaplex} value={sourceParsedTokenAccount || null}
/> onChange={handleSolanaOnChange}
) : lookupChain === CHAIN_ID_ETH ? ( accounts={maps?.tokenAccounts?.data || []}
<EthereumSourceTokenSelector solanaTokenMap={maps?.tokenMap}
value={sourceParsedTokenAccount || null} metaplexData={maps?.metaplex}
onChange={handleSolanaOnChange} />
/> ) : lookupChain === CHAIN_ID_ETH ? (
) : ( <EthereumSourceTokenSelector
<TextField value={sourceParsedTokenAccount || null}
placeholder="Asset" onChange={handleSolanaOnChange}
fullWidth />
value={"hardcoded"} ) : lookupChain === CHAIN_ID_TERRA ? (
onChange={() => {}} <TerraSourceTokenSelector
disabled={true} value={sourceParsedTokenAccount || null}
/> onChange={handleSolanaOnChange}
); />
) : (
<TextField
placeholder="Asset"
fullWidth
value={"Not Implemented"}
disabled={true}
/>
);
return <div>{content}</div>; return <div>{content}</div>;
}; };

View File

@ -0,0 +1,111 @@
import { Button, TextField, Typography } from "@material-ui/core";
import { LCDClient } from "@terra-money/terra.js";
import {
ConnectedWallet,
useConnectedWallet,
} from "@terra-money/wallet-provider";
import { formatUnits } from "ethers/lib/utils";
import React, { useCallback, useState } from "react";
import { createParsedTokenAccount } from "../../hooks/useGetSourceParsedTokenAccounts";
import { ParsedTokenAccount } from "../../store/transferSlice";
import { TERRA_HOST } from "../../utils/consts";
type TerraSourceTokenSelectorProps = {
value: ParsedTokenAccount | null;
onChange: (newValue: ParsedTokenAccount | null) => void;
};
//TODO move elsewhere
//TODO async
const lookupTerraAddress = (
lookupAsset: string,
terraWallet: ConnectedWallet
) => {
const lcd = new LCDClient(TERRA_HOST);
return lcd.wasm
.contractQuery(lookupAsset, {
token_info: {},
})
.then((info: any) =>
lcd.wasm
.contractQuery(lookupAsset, {
balance: {
address: terraWallet.walletAddress,
},
})
.then((balance: any) => {
if (balance && info) {
return createParsedTokenAccount(
terraWallet.walletAddress,
lookupAsset,
balance.balance.toString(),
info.decimals,
Number(formatUnits(balance.balance, info.decimals)),
formatUnits(balance.balance, info.decimals)
);
} else {
throw new Error("Failed to retrieve Terra account.");
}
})
)
.catch(() => {
return Promise.reject();
});
};
export default function TerraSourceTokenSelector(
props: TerraSourceTokenSelectorProps
) {
const { onChange, value } = props;
//const advancedMode = true; //const [advancedMode, setAdvancedMode] = useState(true);
const [advancedModeHolderString, setAdvancedModeHolderString] = useState("");
const [advancedModeError, setAdvancedModeError] = useState("");
const terraWallet = useConnectedWallet();
const handleClick = useCallback(() => {
onChange(null);
setAdvancedModeHolderString("");
}, [onChange]);
const handleOnChange = useCallback(
(event) => setAdvancedModeHolderString(event.target.value),
[]
);
const handleConfirm = () => {
if (terraWallet === undefined) {
setAdvancedModeError("Terra wallet not connected.");
return;
}
lookupTerraAddress(advancedModeHolderString, terraWallet).then(
(result) => {
onChange(result);
},
(error) => {
setAdvancedModeError("Unable to retrieve address.");
}
);
setAdvancedModeError("");
};
const content = value ? (
<>
<Typography>{value.mintKey}</Typography>
<Button onClick={handleClick}>Clear</Button>
</>
) : (
<>
<TextField
fullWidth
label="Asset Address"
value={advancedModeHolderString}
onChange={handleOnChange}
error={advancedModeHolderString !== "" && !!advancedModeError}
helperText={advancedModeError === "" ? undefined : advancedModeError}
/>
<Button onClick={handleConfirm}>Confirm</Button>
</>
);
return <React.Fragment>{content}</React.Fragment>;
}

View File

@ -89,7 +89,6 @@ const getMetaplexData = async (mintAddresses: string[]) => {
connection, connection,
metaAddresses.map((pair) => pair && pair[0]) metaAddresses.map((pair) => pair && pair[0])
); );
console.log(results, "metaplex results");
const output = results.map((account) => { const output = results.map((account) => {
if (account === null) { if (account === null) {
@ -109,7 +108,6 @@ const getMetaplexData = async (mintAddresses: string[]) => {
} }
}); });
console.log(output, "metaplex output");
return output; return output;
}; };

View File

@ -2,6 +2,7 @@ import {
ChainId, ChainId,
getOriginalAssetEth as getOriginalAssetEthTx, getOriginalAssetEth as getOriginalAssetEthTx,
getOriginalAssetSol as getOriginalAssetSolTx, getOriginalAssetSol as getOriginalAssetSolTx,
getOriginalAssetTerra as getOriginalAssetTerraTx,
WormholeWrappedInfo, WormholeWrappedInfo,
} from "@certusone/wormhole-sdk"; } from "@certusone/wormhole-sdk";
import { Connection } from "@solana/web3.js"; import { Connection } from "@solana/web3.js";
@ -11,8 +12,10 @@ import {
ETH_TOKEN_BRIDGE_ADDRESS, ETH_TOKEN_BRIDGE_ADDRESS,
SOLANA_HOST, SOLANA_HOST,
SOL_TOKEN_BRIDGE_ADDRESS, SOL_TOKEN_BRIDGE_ADDRESS,
TERRA_HOST,
TERRA_TEST_TOKEN_ADDRESS, TERRA_TEST_TOKEN_ADDRESS,
} from "./consts"; } from "./consts";
import { LCDClient } from "@terra-money/terra.js";
export interface StateSafeWormholeWrappedInfo { export interface StateSafeWormholeWrappedInfo {
isWrapped: boolean; isWrapped: boolean;
@ -57,9 +60,6 @@ export async function getOriginalAssetSol(
export async function getOriginalAssetTerra( export async function getOriginalAssetTerra(
mintAddress: string mintAddress: string
): Promise<StateSafeWormholeWrappedInfo> { ): Promise<StateSafeWormholeWrappedInfo> {
return { const lcd = new LCDClient(TERRA_HOST);
assetAddress: TERRA_TEST_TOKEN_ADDRESS, return makeStateSafe(await getOriginalAssetTerraTx(lcd, mintAddress));
chainId: 3,
isWrapped: false,
};
} }