bridge_ui: set target address in state

Change-Id: Ie2f87582ffdc8da53ccc0f34721c3985d7807933
This commit is contained in:
Evan Gray 2021-08-25 01:55:26 -04:00
parent 1f214029f5
commit c33f3c0cb9
10 changed files with 194 additions and 71 deletions

View File

@ -29,6 +29,7 @@
"@solana/wallet-base": "^0.0.1", "@solana/wallet-base": "^0.0.1",
"@solana/web3.js": "^1.22.0", "@solana/web3.js": "^1.22.0",
"@terra-money/wallet-provider": "^1.4.0-alpha.1", "@terra-money/wallet-provider": "^1.4.0-alpha.1",
"bech32": "^1.1.4",
"ethers": "^5.4.1", "ethers": "^5.4.1",
"js-base64": "^3.6.1", "js-base64": "^3.6.1",
"notistack": "^1.0.10", "notistack": "^1.0.10",
@ -57,6 +58,9 @@
"@improbable-eng/grpc-web": "^0.14.0", "@improbable-eng/grpc-web": "^0.14.0",
"@solana/spl-token": "^0.1.8", "@solana/spl-token": "^0.1.8",
"@solana/web3.js": "^1.24.0", "@solana/web3.js": "^1.24.0",
"@terra-money/terra.js": "^1.8.10",
"@terra-money/wallet-provider": "^1.2.4",
"js-base64": "^3.6.1",
"protobufjs": "^6.11.2", "protobufjs": "^6.11.2",
"rxjs": "^7.3.0" "rxjs": "^7.3.0"
}, },
@ -65,6 +69,7 @@
"@typechain/ethers-v5": "^7.0.1", "@typechain/ethers-v5": "^7.0.1",
"@types/long": "^4.0.1", "@types/long": "^4.0.1",
"@types/node": "^16.6.1", "@types/node": "^16.6.1",
"@types/react": "^17.0.19",
"copy-dir": "^1.3.0", "copy-dir": "^1.3.0",
"ethers": "^5.4.4", "ethers": "^5.4.4",
"prettier": "^2.3.2", "prettier": "^2.3.2",
@ -36739,6 +36744,7 @@
"resolved": "https://registry.npmjs.org/wasm-dce/-/wasm-dce-1.0.2.tgz", "resolved": "https://registry.npmjs.org/wasm-dce/-/wasm-dce-1.0.2.tgz",
"integrity": "sha512-Fq1+nu43ybsjSnBquLrW/cULmKs61qbv9k8ep13QUe0nABBezMoNAA+j6QY66MW0/eoDVDp1rjXDqQ2VKyS/Xg==", "integrity": "sha512-Fq1+nu43ybsjSnBquLrW/cULmKs61qbv9k8ep13QUe0nABBezMoNAA+j6QY66MW0/eoDVDp1rjXDqQ2VKyS/Xg==",
"dev": true, "dev": true,
"peer": true,
"dependencies": { "dependencies": {
"@babel/core": "^7.0.0-beta.39", "@babel/core": "^7.0.0-beta.39",
"@babel/traverse": "^7.0.0-beta.39", "@babel/traverse": "^7.0.0-beta.39",
@ -36752,6 +36758,7 @@
"resolved": "https://registry.npmjs.org/babylon/-/babylon-7.0.0-beta.47.tgz", "resolved": "https://registry.npmjs.org/babylon/-/babylon-7.0.0-beta.47.tgz",
"integrity": "sha512-+rq2cr4GDhtToEzKFD6KZZMDBXhjFAr9JjPw9pAppZACeEWqNM294j+NdBzkSHYXwzzBmVjZ3nEVJlOhbR2gOQ==", "integrity": "sha512-+rq2cr4GDhtToEzKFD6KZZMDBXhjFAr9JjPw9pAppZACeEWqNM294j+NdBzkSHYXwzzBmVjZ3nEVJlOhbR2gOQ==",
"dev": true, "dev": true,
"peer": true,
"bin": { "bin": {
"babylon": "bin/babylon.js" "babylon": "bin/babylon.js"
}, },
@ -37587,6 +37594,7 @@
"resolved": "https://registry.npmjs.org/webassembly-floating-point-hex-parser/-/webassembly-floating-point-hex-parser-0.1.2.tgz", "resolved": "https://registry.npmjs.org/webassembly-floating-point-hex-parser/-/webassembly-floating-point-hex-parser-0.1.2.tgz",
"integrity": "sha512-TUf1H++8U10+stJbFydnvrpG5Sznz5Rilez/oZlV5zI0C/e4cSxd8rALAJ8VpTvjVWxLmL3SVSJUK6Ap9AoiNg==", "integrity": "sha512-TUf1H++8U10+stJbFydnvrpG5Sznz5Rilez/oZlV5zI0C/e4cSxd8rALAJ8VpTvjVWxLmL3SVSJUK6Ap9AoiNg==",
"dev": true, "dev": true,
"peer": true,
"engines": { "engines": {
"node": "*" "node": "*"
} }
@ -37596,6 +37604,7 @@
"resolved": "https://registry.npmjs.org/webassembly-interpreter/-/webassembly-interpreter-0.0.30.tgz", "resolved": "https://registry.npmjs.org/webassembly-interpreter/-/webassembly-interpreter-0.0.30.tgz",
"integrity": "sha512-+Jdy2piEvz9T5j751mOE8+rBO12p+nNW6Fg4kJZ+zP1oUfsm+151sbAbM8AFxWTURmWCGP+r8Lxwfv3pzN1bCQ==", "integrity": "sha512-+Jdy2piEvz9T5j751mOE8+rBO12p+nNW6Fg4kJZ+zP1oUfsm+151sbAbM8AFxWTURmWCGP+r8Lxwfv3pzN1bCQ==",
"dev": true, "dev": true,
"peer": true,
"dependencies": { "dependencies": {
"@babel/code-frame": "^7.0.0-beta.36", "@babel/code-frame": "^7.0.0-beta.36",
"long": "^3.2.0", "long": "^3.2.0",
@ -37615,6 +37624,7 @@
"resolved": "https://registry.npmjs.org/long/-/long-3.2.0.tgz", "resolved": "https://registry.npmjs.org/long/-/long-3.2.0.tgz",
"integrity": "sha1-2CG3E4yhy1gcFymQ7xTbIAtcR0s=", "integrity": "sha1-2CG3E4yhy1gcFymQ7xTbIAtcR0s=",
"dev": true, "dev": true,
"peer": true,
"engines": { "engines": {
"node": ">=0.6" "node": ">=0.6"
} }
@ -41022,11 +41032,15 @@
"@openzeppelin/contracts": "^4.2.0", "@openzeppelin/contracts": "^4.2.0",
"@solana/spl-token": "^0.1.8", "@solana/spl-token": "^0.1.8",
"@solana/web3.js": "^1.24.0", "@solana/web3.js": "^1.24.0",
"@terra-money/terra.js": "^1.8.10",
"@terra-money/wallet-provider": "^1.2.4",
"@typechain/ethers-v5": "^7.0.1", "@typechain/ethers-v5": "^7.0.1",
"@types/long": "^4.0.1", "@types/long": "^4.0.1",
"@types/node": "^16.6.1", "@types/node": "^16.6.1",
"@types/react": "^17.0.19",
"copy-dir": "^1.3.0", "copy-dir": "^1.3.0",
"ethers": "^5.4.4", "ethers": "^5.4.4",
"js-base64": "^3.6.1",
"prettier": "^2.3.2", "prettier": "^2.3.2",
"protobufjs": "^6.11.2", "protobufjs": "^6.11.2",
"rxjs": "^7.3.0", "rxjs": "^7.3.0",
@ -48382,7 +48396,6 @@
"dev": true, "dev": true,
"optional": true, "optional": true,
"requires": { "requires": {
"bitcore-lib": "^8.25.10",
"unorm": "^1.4.1" "unorm": "^1.4.1"
} }
}, },
@ -69246,6 +69259,7 @@
"resolved": "https://registry.npmjs.org/wasm-dce/-/wasm-dce-1.0.2.tgz", "resolved": "https://registry.npmjs.org/wasm-dce/-/wasm-dce-1.0.2.tgz",
"integrity": "sha512-Fq1+nu43ybsjSnBquLrW/cULmKs61qbv9k8ep13QUe0nABBezMoNAA+j6QY66MW0/eoDVDp1rjXDqQ2VKyS/Xg==", "integrity": "sha512-Fq1+nu43ybsjSnBquLrW/cULmKs61qbv9k8ep13QUe0nABBezMoNAA+j6QY66MW0/eoDVDp1rjXDqQ2VKyS/Xg==",
"dev": true, "dev": true,
"peer": true,
"requires": { "requires": {
"@babel/core": "^7.0.0-beta.39", "@babel/core": "^7.0.0-beta.39",
"@babel/traverse": "^7.0.0-beta.39", "@babel/traverse": "^7.0.0-beta.39",
@ -69258,7 +69272,8 @@
"version": "7.0.0-beta.47", "version": "7.0.0-beta.47",
"resolved": "https://registry.npmjs.org/babylon/-/babylon-7.0.0-beta.47.tgz", "resolved": "https://registry.npmjs.org/babylon/-/babylon-7.0.0-beta.47.tgz",
"integrity": "sha512-+rq2cr4GDhtToEzKFD6KZZMDBXhjFAr9JjPw9pAppZACeEWqNM294j+NdBzkSHYXwzzBmVjZ3nEVJlOhbR2gOQ==", "integrity": "sha512-+rq2cr4GDhtToEzKFD6KZZMDBXhjFAr9JjPw9pAppZACeEWqNM294j+NdBzkSHYXwzzBmVjZ3nEVJlOhbR2gOQ==",
"dev": true "dev": true,
"peer": true
} }
} }
}, },
@ -69268,8 +69283,7 @@
"integrity": "sha512-R4s75XH+o8qM+WaRrAU9S2rbAMDzob18/S3V8R9ZoFpZkPWLAohWWlzWAp1ybeTkOuuku/X1zJtxiV0pBYxZww==", "integrity": "sha512-R4s75XH+o8qM+WaRrAU9S2rbAMDzob18/S3V8R9ZoFpZkPWLAohWWlzWAp1ybeTkOuuku/X1zJtxiV0pBYxZww==",
"dev": true, "dev": true,
"requires": { "requires": {
"loader-utils": "^1.1.0", "loader-utils": "^1.1.0"
"wasm-dce": "^1.0.0"
}, },
"dependencies": { "dependencies": {
"json5": { "json5": {
@ -69991,13 +70005,15 @@
"version": "0.1.2", "version": "0.1.2",
"resolved": "https://registry.npmjs.org/webassembly-floating-point-hex-parser/-/webassembly-floating-point-hex-parser-0.1.2.tgz", "resolved": "https://registry.npmjs.org/webassembly-floating-point-hex-parser/-/webassembly-floating-point-hex-parser-0.1.2.tgz",
"integrity": "sha512-TUf1H++8U10+stJbFydnvrpG5Sznz5Rilez/oZlV5zI0C/e4cSxd8rALAJ8VpTvjVWxLmL3SVSJUK6Ap9AoiNg==", "integrity": "sha512-TUf1H++8U10+stJbFydnvrpG5Sznz5Rilez/oZlV5zI0C/e4cSxd8rALAJ8VpTvjVWxLmL3SVSJUK6Ap9AoiNg==",
"dev": true "dev": true,
"peer": true
}, },
"webassembly-interpreter": { "webassembly-interpreter": {
"version": "0.0.30", "version": "0.0.30",
"resolved": "https://registry.npmjs.org/webassembly-interpreter/-/webassembly-interpreter-0.0.30.tgz", "resolved": "https://registry.npmjs.org/webassembly-interpreter/-/webassembly-interpreter-0.0.30.tgz",
"integrity": "sha512-+Jdy2piEvz9T5j751mOE8+rBO12p+nNW6Fg4kJZ+zP1oUfsm+151sbAbM8AFxWTURmWCGP+r8Lxwfv3pzN1bCQ==", "integrity": "sha512-+Jdy2piEvz9T5j751mOE8+rBO12p+nNW6Fg4kJZ+zP1oUfsm+151sbAbM8AFxWTURmWCGP+r8Lxwfv3pzN1bCQ==",
"dev": true, "dev": true,
"peer": true,
"requires": { "requires": {
"@babel/code-frame": "^7.0.0-beta.36", "@babel/code-frame": "^7.0.0-beta.36",
"long": "^3.2.0", "long": "^3.2.0",
@ -70008,7 +70024,8 @@
"version": "3.2.0", "version": "3.2.0",
"resolved": "https://registry.npmjs.org/long/-/long-3.2.0.tgz", "resolved": "https://registry.npmjs.org/long/-/long-3.2.0.tgz",
"integrity": "sha1-2CG3E4yhy1gcFymQ7xTbIAtcR0s=", "integrity": "sha1-2CG3E4yhy1gcFymQ7xTbIAtcR0s=",
"dev": true "dev": true,
"peer": true
} }
} }
}, },

View File

@ -23,6 +23,7 @@
"@solana/wallet-base": "^0.0.1", "@solana/wallet-base": "^0.0.1",
"@solana/web3.js": "^1.22.0", "@solana/web3.js": "^1.22.0",
"@terra-money/wallet-provider": "^1.4.0-alpha.1", "@terra-money/wallet-provider": "^1.4.0-alpha.1",
"bech32": "^1.1.4",
"ethers": "^5.4.1", "ethers": "^5.4.1",
"js-base64": "^3.6.1", "js-base64": "^3.6.1",
"notistack": "^1.0.10", "notistack": "^1.0.10",

View File

@ -63,7 +63,7 @@ function Source() {
</TextField> </TextField>
<KeyAndBalance chainId={sourceChain} /> <KeyAndBalance chainId={sourceChain} />
<TextField <TextField
placeholder="Asset" label="Asset"
fullWidth fullWidth
className={classes.transferField} className={classes.transferField}
value={sourceAsset} value={sourceAsset}

View File

@ -75,7 +75,7 @@ function Source() {
<KeyAndBalance chainId={sourceChain} balance={uiAmountString} /> <KeyAndBalance chainId={sourceChain} balance={uiAmountString} />
{/* TODO: token list for eth, check own */} {/* TODO: token list for eth, check own */}
<TextField <TextField
placeholder="Asset" label="Asset"
fullWidth fullWidth
className={classes.transferField} className={classes.transferField}
value={sourceAsset} value={sourceAsset}
@ -83,7 +83,7 @@ function Source() {
disabled={shouldLockFields} disabled={shouldLockFields}
/> />
<TextField <TextField
placeholder="Amount" label="Amount"
type="number" type="number"
fullWidth fullWidth
className={classes.transferField} className={classes.transferField}

View File

@ -3,11 +3,13 @@ import { Button, makeStyles, MenuItem, TextField } from "@material-ui/core";
import { PublicKey } from "@solana/web3.js"; import { PublicKey } from "@solana/web3.js";
import { useCallback, useMemo } from "react"; import { useCallback, useMemo } from "react";
import { useDispatch, useSelector } from "react-redux"; import { useDispatch, useSelector } from "react-redux";
import useSyncTargetAddress from "../../hooks/useSyncTargetAddress";
import { import {
selectTransferIsSourceAssetWormholeWrapped, selectTransferIsSourceAssetWormholeWrapped,
selectTransferIsTargetComplete, selectTransferIsTargetComplete,
selectTransferShouldLockFields, selectTransferShouldLockFields,
selectTransferSourceChain, selectTransferSourceChain,
selectTransferTargetAddressHex,
selectTransferTargetAsset, selectTransferTargetAsset,
selectTransferTargetBalanceString, selectTransferTargetBalanceString,
selectTransferTargetChain, selectTransferTargetChain,
@ -32,6 +34,7 @@ function Target() {
[sourceChain] [sourceChain]
); );
const targetChain = useSelector(selectTransferTargetChain); const targetChain = useSelector(selectTransferTargetChain);
const targetAddressHex = useSelector(selectTransferTargetAddressHex); // TODO: make readable
const targetAsset = useSelector(selectTransferTargetAsset); const targetAsset = useSelector(selectTransferTargetAsset);
const isSourceAssetWormholeWrapped = useSelector( const isSourceAssetWormholeWrapped = useSelector(
selectTransferIsSourceAssetWormholeWrapped selectTransferIsSourceAssetWormholeWrapped
@ -47,6 +50,7 @@ function Target() {
const uiAmountString = useSelector(selectTransferTargetBalanceString); const uiAmountString = useSelector(selectTransferTargetBalanceString);
const isTargetComplete = useSelector(selectTransferIsTargetComplete); const isTargetComplete = useSelector(selectTransferIsTargetComplete);
const shouldLockFields = useSelector(selectTransferShouldLockFields); const shouldLockFields = useSelector(selectTransferShouldLockFields);
useSyncTargetAddress(!shouldLockFields);
const handleTargetChange = useCallback( const handleTargetChange = useCallback(
(event) => { (event) => {
dispatch(setTargetChain(event.target.value)); dispatch(setTargetChain(event.target.value));
@ -76,7 +80,14 @@ function Target() {
</TextField> </TextField>
<KeyAndBalance chainId={targetChain} balance={uiAmountString} /> <KeyAndBalance chainId={targetChain} balance={uiAmountString} />
<TextField <TextField
placeholder="Asset" label="Address"
fullWidth
className={classes.transferField}
value={targetAddressHex || ""}
disabled={true}
/>
<TextField
label="Asset"
fullWidth fullWidth
className={classes.transferField} className={classes.transferField}
value={readableTargetAsset} value={readableTargetAsset}

View File

@ -11,22 +11,17 @@ import {
transferFromEth, transferFromEth,
transferFromSolana, transferFromSolana,
} from "@certusone/wormhole-sdk"; } from "@certusone/wormhole-sdk";
import {
ASSOCIATED_TOKEN_PROGRAM_ID,
Token,
TOKEN_PROGRAM_ID,
} from "@solana/spl-token";
import { WalletContextState } from "@solana/wallet-adapter-react"; import { WalletContextState } from "@solana/wallet-adapter-react";
import { Connection, PublicKey } from "@solana/web3.js"; import { Connection } from "@solana/web3.js";
import { MsgExecuteContract } from "@terra-money/terra.js"; import { MsgExecuteContract } from "@terra-money/terra.js";
import { import {
ConnectedWallet, ConnectedWallet,
useConnectedWallet, useConnectedWallet,
} from "@terra-money/wallet-provider"; } from "@terra-money/wallet-provider";
import { Signer } from "ethers"; import { Signer } from "ethers";
import { arrayify, parseUnits, zeroPad } from "ethers/lib/utils"; import { parseUnits, zeroPad } from "ethers/lib/utils";
import { useSnackbar } from "notistack"; import { useSnackbar } from "notistack";
import { useCallback, useEffect, useMemo, useRef } from "react"; import { useCallback, useMemo } from "react";
import { useDispatch, useSelector } from "react-redux"; import { useDispatch, useSelector } from "react-redux";
import { useEthereumProvider } from "../contexts/EthereumProviderContext"; import { useEthereumProvider } from "../contexts/EthereumProviderContext";
import { useSolanaWallet } from "../contexts/SolanaWalletContext"; import { useSolanaWallet } from "../contexts/SolanaWalletContext";
@ -40,9 +35,7 @@ import {
selectTransferSourceAsset, selectTransferSourceAsset,
selectTransferSourceChain, selectTransferSourceChain,
selectTransferSourceParsedTokenAccount, selectTransferSourceParsedTokenAccount,
selectTransferTargetAsset,
selectTransferTargetChain, selectTransferTargetChain,
selectTransferTargetParsedTokenAccount,
} from "../store/selectors"; } from "../store/selectors";
import { setIsSending, setSignedVAAHex } from "../store/transferSlice"; import { setIsSending, setSignedVAAHex } from "../store/transferSlice";
import { hexToUint8Array, uint8ArrayToHex } from "../utils/array"; import { hexToUint8Array, uint8ArrayToHex } from "../utils/array";
@ -56,6 +49,7 @@ import {
} from "../utils/consts"; } from "../utils/consts";
import { getSignedVAAWithRetry } from "../utils/getSignedVAAWithRetry"; import { getSignedVAAWithRetry } from "../utils/getSignedVAAWithRetry";
import { signSendAndConfirm } from "../utils/solana"; import { signSendAndConfirm } from "../utils/solana";
import useTransferTargetAddressHex from "./useTransferTargetAddress";
async function eth( async function eth(
dispatch: any, dispatch: any,
@ -104,8 +98,8 @@ async function solana(
mintAddress: string, mintAddress: string,
amount: string, amount: string,
decimals: number, decimals: number,
targetAddressStr: string,
targetChain: ChainId, targetChain: ChainId,
targetAddress: Uint8Array,
originAddressStr?: string, originAddressStr?: string,
originChain?: ChainId originChain?: ChainId
) { ) {
@ -114,7 +108,6 @@ async function solana(
//TODO: check if token attestation exists on the target chain //TODO: check if token attestation exists on the target chain
// TODO: share connection in context? // TODO: share connection in context?
const connection = new Connection(SOLANA_HOST, "confirmed"); const connection = new Connection(SOLANA_HOST, "confirmed");
const targetAddress = zeroPad(arrayify(targetAddressStr), 32);
const amountParsed = parseUnits(amount, decimals).toBigInt(); const amountParsed = parseUnits(amount, decimals).toBigInt();
const originAddress = originAddressStr const originAddress = originAddressStr
? zeroPad(hexToUint8Array(originAddressStr), 32) ? zeroPad(hexToUint8Array(originAddressStr), 32)
@ -164,8 +157,8 @@ async function terra(
wallet: ConnectedWallet, wallet: ConnectedWallet,
asset: string, asset: string,
amount: string, amount: string,
targetAddressStr: string, targetChain: ChainId,
targetChain: ChainId targetAddress: Uint8Array
) { ) {
dispatch(setIsSending(true)); dispatch(setIsSending(true));
try { try {
@ -180,7 +173,7 @@ async function terra(
asset: asset, asset: asset,
amount: amount, amount: amount,
recipient_chain: targetChain, recipient_chain: targetChain,
recipient: targetAddressStr, recipient: targetAddress,
fee: 1000, fee: 1000,
nonce: 0, nonce: 0,
}, },
@ -221,11 +214,11 @@ export function useHandleTransfer() {
const originAsset = useSelector(selectTransferOriginAsset); const originAsset = useSelector(selectTransferOriginAsset);
const amount = useSelector(selectTransferAmount); const amount = useSelector(selectTransferAmount);
const targetChain = useSelector(selectTransferTargetChain); const targetChain = useSelector(selectTransferTargetChain);
const targetAsset = useSelector(selectTransferTargetAsset); const targetAddress = useTransferTargetAddressHex();
const isTargetComplete = useSelector(selectTransferIsTargetComplete); const isTargetComplete = useSelector(selectTransferIsTargetComplete);
const isSending = useSelector(selectTransferIsSending); const isSending = useSelector(selectTransferIsSending);
const isSendComplete = useSelector(selectTransferIsSendComplete); const isSendComplete = useSelector(selectTransferIsSendComplete);
const { signer, signerAddress } = useEthereumProvider(); const { signer } = useEthereumProvider();
const solanaWallet = useSolanaWallet(); const solanaWallet = useSolanaWallet();
const solPK = solanaWallet?.publicKey; const solPK = solanaWallet?.publicKey;
const terraWallet = useConnectedWallet(); const terraWallet = useConnectedWallet();
@ -234,37 +227,7 @@ export function useHandleTransfer() {
); );
const sourceTokenPublicKey = sourceParsedTokenAccount?.publicKey; const sourceTokenPublicKey = sourceParsedTokenAccount?.publicKey;
const decimals = sourceParsedTokenAccount?.decimals; const decimals = sourceParsedTokenAccount?.decimals;
const targetParsedTokenAccount = useSelector(
selectTransferTargetParsedTokenAccount
);
const disabled = !isTargetComplete || isSending || isSendComplete; const disabled = !isTargetComplete || isSending || isSendComplete;
// TODO: we probably shouldn't get here if we don't have this public key
// TODO: also this is just for solana... send help(ers)
const targetTokenAccountPublicKey = targetParsedTokenAccount?.publicKey;
// TODO: AVOID THIS DANGEROUS CACOPHONY
const tpkRef = useRef<undefined | Uint8Array>(undefined);
useEffect(() => {
(async () => {
if (targetChain === CHAIN_ID_SOLANA) {
tpkRef.current = targetTokenAccountPublicKey
? zeroPad(new PublicKey(targetTokenAccountPublicKey).toBytes(), 32) // use the target's TokenAccount if it exists
: solPK && targetAsset // otherwise, use the associated token account (which we create in the case it doesn't exist)
? zeroPad(
(
await Token.getAssociatedTokenAddress(
ASSOCIATED_TOKEN_PROGRAM_ID,
TOKEN_PROGRAM_ID,
new PublicKey(targetAsset),
solPK
)
).toBytes(),
32
)
: undefined;
} else tpkRef.current = undefined;
})();
}, [targetChain, solPK, targetAsset, targetTokenAccountPublicKey]);
// TODO: dynamically get "to" wallet
const handleTransferClick = useCallback(() => { const handleTransferClick = useCallback(() => {
// TODO: we should separate state for transaction vs fetching vaa // TODO: we should separate state for transaction vs fetching vaa
// TODO: more generic way of calling these // TODO: more generic way of calling these
@ -272,7 +235,7 @@ export function useHandleTransfer() {
sourceChain === CHAIN_ID_ETH && sourceChain === CHAIN_ID_ETH &&
!!signer && !!signer &&
decimals !== undefined && decimals !== undefined &&
!!tpkRef.current !!targetAddress
) { ) {
eth( eth(
dispatch, dispatch,
@ -282,14 +245,14 @@ export function useHandleTransfer() {
decimals, decimals,
amount, amount,
targetChain, targetChain,
tpkRef.current targetAddress
); );
} else if ( } else if (
sourceChain === CHAIN_ID_SOLANA && sourceChain === CHAIN_ID_SOLANA &&
!!solanaWallet && !!solanaWallet &&
!!solPK && !!solPK &&
!!sourceTokenPublicKey && !!sourceTokenPublicKey &&
!!signerAddress && !!targetAddress &&
decimals !== undefined decimals !== undefined
) { ) {
solana( solana(
@ -299,10 +262,10 @@ export function useHandleTransfer() {
solPK.toString(), solPK.toString(),
sourceTokenPublicKey, sourceTokenPublicKey,
sourceAsset, sourceAsset,
amount, //TODO: avoid decimals, pass in parsed amount amount,
decimals, decimals,
signerAddress,
targetChain, targetChain,
targetAddress,
originAsset, originAsset,
originChain originChain
); );
@ -310,7 +273,7 @@ export function useHandleTransfer() {
sourceChain === CHAIN_ID_TERRA && sourceChain === CHAIN_ID_TERRA &&
!!terraWallet && !!terraWallet &&
decimals !== undefined && decimals !== undefined &&
!!signerAddress !!targetAddress
) { ) {
terra( terra(
dispatch, dispatch,
@ -318,8 +281,8 @@ export function useHandleTransfer() {
terraWallet, terraWallet,
sourceAsset, sourceAsset,
amount, amount,
signerAddress, // TODO: only works for Eth targetChain,
targetChain targetAddress
); );
} else { } else {
// enqueueSnackbar("Transfers from this chain are not yet supported", { // enqueueSnackbar("Transfers from this chain are not yet supported", {
@ -331,7 +294,6 @@ export function useHandleTransfer() {
enqueueSnackbar, enqueueSnackbar,
sourceChain, sourceChain,
signer, signer,
signerAddress,
solanaWallet, solanaWallet,
solPK, solPK,
terraWallet, terraWallet,
@ -340,6 +302,7 @@ export function useHandleTransfer() {
amount, amount,
decimals, decimals,
targetChain, targetChain,
targetAddress,
originAsset, originAsset,
originChain, originChain,
]); ]);

View File

@ -0,0 +1,110 @@
import {
CHAIN_ID_ETH,
CHAIN_ID_SOLANA,
CHAIN_ID_TERRA,
} from "@certusone/wormhole-sdk";
import { arrayify, zeroPad } from "@ethersproject/bytes";
import {
ASSOCIATED_TOKEN_PROGRAM_ID,
Token,
TOKEN_PROGRAM_ID,
} from "@solana/spl-token";
import { PublicKey } from "@solana/web3.js";
import { useConnectedWallet } from "@terra-money/wallet-provider";
import { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { useEthereumProvider } from "../contexts/EthereumProviderContext";
import { useSolanaWallet } from "../contexts/SolanaWalletContext";
import {
selectTransferTargetAsset,
selectTransferTargetChain,
selectTransferTargetParsedTokenAccount,
} from "../store/selectors";
import { setTargetAddressHex } from "../store/transferSlice";
import { uint8ArrayToHex } from "../utils/array";
import bech32 from "bech32";
function useSyncTargetAddress(shouldFire: boolean) {
const dispatch = useDispatch();
const targetChain = useSelector(selectTransferTargetChain);
const { signerAddress } = useEthereumProvider();
const solanaWallet = useSolanaWallet();
const solPK = solanaWallet?.publicKey;
const targetAsset = useSelector(selectTransferTargetAsset);
const targetParsedTokenAccount = useSelector(
selectTransferTargetParsedTokenAccount
);
const targetTokenAccountPublicKey = targetParsedTokenAccount?.publicKey;
const terraWallet = useConnectedWallet();
useEffect(() => {
if (shouldFire) {
let cancelled = false;
if (targetChain === CHAIN_ID_ETH && signerAddress) {
dispatch(
setTargetAddressHex(
uint8ArrayToHex(zeroPad(arrayify(signerAddress), 32))
)
);
}
// TODO: have the user explicitly select an account on solana
else if (targetChain === CHAIN_ID_SOLANA && targetTokenAccountPublicKey) {
// use the target's TokenAccount if it exists
dispatch(
setTargetAddressHex(
uint8ArrayToHex(
zeroPad(new PublicKey(targetTokenAccountPublicKey).toBytes(), 32)
)
)
);
} else if (targetChain === CHAIN_ID_SOLANA && solPK && targetAsset) {
// otherwise, use the associated token account (which we create in the case it doesn't exist)
(async () => {
const associatedTokenAccount = await Token.getAssociatedTokenAddress(
ASSOCIATED_TOKEN_PROGRAM_ID,
TOKEN_PROGRAM_ID,
new PublicKey(targetAsset),
solPK
);
if (!cancelled) {
dispatch(
setTargetAddressHex(
uint8ArrayToHex(zeroPad(associatedTokenAccount.toBytes(), 32))
)
);
}
})();
} else if (
targetChain === CHAIN_ID_TERRA &&
terraWallet &&
terraWallet.walletAddress
) {
dispatch(
setTargetAddressHex(
uint8ArrayToHex(
zeroPad(
new Uint8Array(bech32.decode(terraWallet.walletAddress).words),
32
)
)
)
);
} else {
dispatch(setTargetAddressHex(undefined));
}
return () => {
cancelled = true;
};
}
}, [
dispatch,
shouldFire,
targetChain,
signerAddress,
solPK,
targetAsset,
targetTokenAccountPublicKey,
terraWallet,
]);
}
export default useSyncTargetAddress;

View File

@ -0,0 +1,13 @@
import { useMemo } from "react";
import { useSelector } from "react-redux";
import { selectTransferTargetAddressHex } from "../store/selectors";
import { hexToUint8Array } from "../utils/array";
export default function useTransferTargetAddressHex() {
const targetAddressHex = useSelector(selectTransferTargetAddressHex);
const targetAddress = useMemo(
() => (targetAddressHex ? hexToUint8Array(targetAddressHex) : undefined),
[targetAddressHex]
);
return targetAddress;
}

View File

@ -57,6 +57,8 @@ export const selectTransferSourceBalanceString = (state: RootState) =>
export const selectTransferAmount = (state: RootState) => state.transfer.amount; export const selectTransferAmount = (state: RootState) => state.transfer.amount;
export const selectTransferTargetChain = (state: RootState) => export const selectTransferTargetChain = (state: RootState) =>
state.transfer.targetChain; state.transfer.targetChain;
export const selectTransferTargetAddressHex = (state: RootState) =>
state.transfer.targetAddressHex;
export const selectTransferTargetAsset = (state: RootState) => export const selectTransferTargetAsset = (state: RootState) =>
state.transfer.targetAsset; state.transfer.targetAsset;
export const selectTransferTargetParsedTokenAccount = (state: RootState) => export const selectTransferTargetParsedTokenAccount = (state: RootState) =>
@ -96,11 +98,8 @@ export const selectTransferIsTargetComplete = (state: RootState) =>
!!state.transfer.targetChain && !!state.transfer.targetChain &&
!!state.transfer.targetAsset && !!state.transfer.targetAsset &&
(state.transfer.targetChain !== CHAIN_ID_ETH || (state.transfer.targetChain !== CHAIN_ID_ETH ||
state.transfer.targetAsset !== ethers.constants.AddressZero); //&& state.transfer.targetAsset !== ethers.constants.AddressZero) &&
// Associated Token Account exists !!state.transfer.targetAddressHex;
// (state.transfer.targetChain !== CHAIN_ID_SOLANA ||
// (!!state.transfer.targetParsedTokenAccount &&
// !!state.transfer.targetParsedTokenAccount.publicKey));
export const selectTransferIsSendComplete = (state: RootState) => export const selectTransferIsSendComplete = (state: RootState) =>
!!selectTransferSignedVAAHex(state); !!selectTransferSignedVAAHex(state);
export const selectTransferShouldLockFields = (state: RootState) => export const selectTransferShouldLockFields = (state: RootState) =>

View File

@ -34,6 +34,7 @@ export interface TransferState {
sourceParsedTokenAccount: ParsedTokenAccount | undefined; sourceParsedTokenAccount: ParsedTokenAccount | undefined;
amount: string; amount: string;
targetChain: ChainId; targetChain: ChainId;
targetAddressHex: string | undefined;
targetAsset: string | null | undefined; targetAsset: string | null | undefined;
targetParsedTokenAccount: ParsedTokenAccount | undefined; targetParsedTokenAccount: ParsedTokenAccount | undefined;
signedVAAHex: string | undefined; signedVAAHex: string | undefined;
@ -51,6 +52,7 @@ const initialState: TransferState = {
originAsset: undefined, originAsset: undefined,
amount: "", amount: "",
targetChain: CHAIN_ID_ETH, targetChain: CHAIN_ID_ETH,
targetAddressHex: undefined,
targetAsset: undefined, targetAsset: undefined,
targetParsedTokenAccount: undefined, targetParsedTokenAccount: undefined,
signedVAAHex: undefined, signedVAAHex: undefined,
@ -86,6 +88,7 @@ export const transferSlice = createSlice({
} }
if (state.targetChain === action.payload) { if (state.targetChain === action.payload) {
state.targetChain = prevSourceChain; state.targetChain = prevSourceChain;
state.targetAddressHex = undefined;
} }
}, },
setSourceAsset: (state, action: PayloadAction<string>) => { setSourceAsset: (state, action: PayloadAction<string>) => {
@ -117,6 +120,8 @@ export const transferSlice = createSlice({
setTargetChain: (state, action: PayloadAction<ChainId>) => { setTargetChain: (state, action: PayloadAction<ChainId>) => {
const prevTargetChain = state.targetChain; const prevTargetChain = state.targetChain;
state.targetChain = action.payload; state.targetChain = action.payload;
state.targetAddressHex = undefined;
// targetAsset is handled by useFetchTargetAsset
if (state.sourceChain === action.payload) { if (state.sourceChain === action.payload) {
state.sourceChain = prevTargetChain; state.sourceChain = prevTargetChain;
state.activeStep = 0; state.activeStep = 0;
@ -132,6 +137,9 @@ export const transferSlice = createSlice({
} }
} }
}, },
setTargetAddressHex: (state, action: PayloadAction<string | undefined>) => {
state.targetAddressHex = action.payload;
},
setTargetAsset: ( setTargetAsset: (
state, state,
action: PayloadAction<string | null | undefined> action: PayloadAction<string | null | undefined>
@ -169,6 +177,7 @@ export const {
setSourceParsedTokenAccount, setSourceParsedTokenAccount,
setAmount, setAmount,
setTargetChain, setTargetChain,
setTargetAddressHex,
setTargetAsset, setTargetAsset,
setTargetParsedTokenAccount, setTargetParsedTokenAccount,
setSignedVAAHex, setSignedVAAHex,