bridge_ui: transfer & unwrap SOL
fixes https://github.com/certusone/wormhole/issues/486 Change-Id: I81b97ff0e1358bf0b88567ba9872ee615344a27c
This commit is contained in:
parent
302368d704
commit
7b1a0bf3ad
|
@ -1,12 +1,15 @@
|
||||||
import {
|
import {
|
||||||
ChainId,
|
|
||||||
CHAIN_ID_BSC,
|
CHAIN_ID_BSC,
|
||||||
CHAIN_ID_ETH,
|
CHAIN_ID_ETH,
|
||||||
CHAIN_ID_SOLANA,
|
CHAIN_ID_SOLANA,
|
||||||
getEmitterAddressEth,
|
getEmitterAddressEth,
|
||||||
getEmitterAddressSolana,
|
getEmitterAddressSolana,
|
||||||
|
hexToNativeString,
|
||||||
|
hexToUint8Array,
|
||||||
|
parseNFTPayload,
|
||||||
parseSequenceFromLogEth,
|
parseSequenceFromLogEth,
|
||||||
parseSequenceFromLogSolana,
|
parseSequenceFromLogSolana,
|
||||||
|
uint8ArrayToHex,
|
||||||
} from "@certusone/wormhole-sdk";
|
} from "@certusone/wormhole-sdk";
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
|
@ -27,7 +30,7 @@ import {
|
||||||
import { Restore } from "@material-ui/icons";
|
import { Restore } from "@material-ui/icons";
|
||||||
import { Alert } from "@material-ui/lab";
|
import { Alert } from "@material-ui/lab";
|
||||||
import { Connection } from "@solana/web3.js";
|
import { Connection } from "@solana/web3.js";
|
||||||
import { BigNumber, ethers } from "ethers";
|
import { ethers } from "ethers";
|
||||||
import { useSnackbar } from "notistack";
|
import { useSnackbar } from "notistack";
|
||||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
|
@ -37,11 +40,6 @@ import {
|
||||||
selectNFTSignedVAAHex,
|
selectNFTSignedVAAHex,
|
||||||
selectNFTSourceChain,
|
selectNFTSourceChain,
|
||||||
} from "../../store/selectors";
|
} from "../../store/selectors";
|
||||||
import {
|
|
||||||
hexToNativeString,
|
|
||||||
hexToUint8Array,
|
|
||||||
uint8ArrayToHex,
|
|
||||||
} from "../../utils/array";
|
|
||||||
import {
|
import {
|
||||||
CHAINS,
|
CHAINS,
|
||||||
ETH_BRIDGE_ADDRESS,
|
ETH_BRIDGE_ADDRESS,
|
||||||
|
@ -51,7 +49,6 @@ import {
|
||||||
WORMHOLE_RPC_HOSTS,
|
WORMHOLE_RPC_HOSTS,
|
||||||
} from "../../utils/consts";
|
} from "../../utils/consts";
|
||||||
import { getSignedVAAWithRetry } from "../../utils/getSignedVAAWithRetry";
|
import { getSignedVAAWithRetry } from "../../utils/getSignedVAAWithRetry";
|
||||||
import { METADATA_REPLACE } from "../../utils/metaplex";
|
|
||||||
import parseError from "../../utils/parseError";
|
import parseError from "../../utils/parseError";
|
||||||
import KeyAndBalance from "../KeyAndBalance";
|
import KeyAndBalance from "../KeyAndBalance";
|
||||||
|
|
||||||
|
@ -111,49 +108,6 @@ async function solana(tx: string, enqueueSnackbar: any) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// note: actual first byte is message type
|
|
||||||
// 0 [u8; 32] token_address
|
|
||||||
// 32 u16 token_chain
|
|
||||||
// 34 [u8; 32] symbol
|
|
||||||
// 66 [u8; 32] name
|
|
||||||
// 98 u256 tokenId
|
|
||||||
// 130 u8 uri_len
|
|
||||||
// 131 [u8;len] uri
|
|
||||||
// ? [u8; 32] recipient
|
|
||||||
// ? u16 recipient_chain
|
|
||||||
|
|
||||||
// TODO: move to wasm / sdk, share with solana
|
|
||||||
export const parsePayload = (arr: Buffer) => {
|
|
||||||
const originAddress = arr.slice(1, 1 + 32).toString("hex");
|
|
||||||
const originChain = arr.readUInt16BE(33) as ChainId;
|
|
||||||
const symbol = Buffer.from(arr.slice(35, 35 + 32))
|
|
||||||
.toString("utf8")
|
|
||||||
.replace(METADATA_REPLACE, "");
|
|
||||||
const name = Buffer.from(arr.slice(67, 67 + 32))
|
|
||||||
.toString("utf8")
|
|
||||||
.replace(METADATA_REPLACE, "");
|
|
||||||
const tokenId = BigNumber.from(arr.slice(99, 99 + 32));
|
|
||||||
const uri_len = arr.readUInt8(131);
|
|
||||||
const uri = Buffer.from(arr.slice(132, 132 + uri_len))
|
|
||||||
.toString("utf8")
|
|
||||||
.replace(METADATA_REPLACE, "");
|
|
||||||
const target_offset = 132 + uri_len;
|
|
||||||
const targetAddress = arr
|
|
||||||
.slice(target_offset, target_offset + 32)
|
|
||||||
.toString("hex");
|
|
||||||
const targetChain = arr.readUInt16BE(target_offset + 32) as ChainId;
|
|
||||||
return {
|
|
||||||
originAddress,
|
|
||||||
originChain,
|
|
||||||
symbol,
|
|
||||||
name,
|
|
||||||
tokenId,
|
|
||||||
uri,
|
|
||||||
targetAddress,
|
|
||||||
targetChain,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
function RecoveryDialogContent({
|
function RecoveryDialogContent({
|
||||||
onClose,
|
onClose,
|
||||||
disabled,
|
disabled,
|
||||||
|
@ -266,7 +220,9 @@ function RecoveryDialogContent({
|
||||||
const parsedPayload = useMemo(
|
const parsedPayload = useMemo(
|
||||||
() =>
|
() =>
|
||||||
recoveryParsedVAA?.payload
|
recoveryParsedVAA?.payload
|
||||||
? parsePayload(Buffer.from(new Uint8Array(recoveryParsedVAA.payload)))
|
? parseNFTPayload(
|
||||||
|
Buffer.from(new Uint8Array(recoveryParsedVAA.payload))
|
||||||
|
)
|
||||||
: null,
|
: null,
|
||||||
[recoveryParsedVAA]
|
[recoveryParsedVAA]
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,4 +1,9 @@
|
||||||
import { CHAIN_ID_ETH, CHAIN_ID_SOLANA } from "@certusone/wormhole-sdk";
|
import {
|
||||||
|
CHAIN_ID_ETH,
|
||||||
|
CHAIN_ID_SOLANA,
|
||||||
|
hexToNativeString,
|
||||||
|
hexToUint8Array,
|
||||||
|
} from "@certusone/wormhole-sdk";
|
||||||
import { makeStyles, MenuItem, TextField, Typography } from "@material-ui/core";
|
import { makeStyles, MenuItem, TextField, Typography } from "@material-ui/core";
|
||||||
import { Alert } from "@material-ui/lab";
|
import { Alert } from "@material-ui/lab";
|
||||||
import { PublicKey } from "@solana/web3.js";
|
import { PublicKey } from "@solana/web3.js";
|
||||||
|
@ -22,7 +27,6 @@ import {
|
||||||
selectNFTTargetChain,
|
selectNFTTargetChain,
|
||||||
selectNFTTargetError,
|
selectNFTTargetError,
|
||||||
} from "../../store/selectors";
|
} from "../../store/selectors";
|
||||||
import { hexToNativeString, hexToUint8Array } from "../../utils/array";
|
|
||||||
import { CHAINS, CHAINS_BY_ID } from "../../utils/consts";
|
import { CHAINS, CHAINS_BY_ID } from "../../utils/consts";
|
||||||
import ButtonWithLoader from "../ButtonWithLoader";
|
import ButtonWithLoader from "../ButtonWithLoader";
|
||||||
import KeyAndBalance from "../KeyAndBalance";
|
import KeyAndBalance from "../KeyAndBalance";
|
||||||
|
|
|
@ -4,7 +4,7 @@ import {
|
||||||
selectNFTTargetAddressHex,
|
selectNFTTargetAddressHex,
|
||||||
selectNFTTargetChain,
|
selectNFTTargetChain,
|
||||||
} from "../../store/selectors";
|
} from "../../store/selectors";
|
||||||
import { hexToNativeString } from "../../utils/array";
|
import { hexToNativeString } from "@certusone/wormhole-sdk";
|
||||||
import { CHAINS_BY_ID } from "../../utils/consts";
|
import { CHAINS_BY_ID } from "../../utils/consts";
|
||||||
import SmartAddress from "../SmartAddress";
|
import SmartAddress from "../SmartAddress";
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,8 @@ import {
|
||||||
CHAIN_ID_BSC,
|
CHAIN_ID_BSC,
|
||||||
CHAIN_ID_ETH,
|
CHAIN_ID_ETH,
|
||||||
CHAIN_ID_SOLANA,
|
CHAIN_ID_SOLANA,
|
||||||
|
hexToNativeString,
|
||||||
|
uint8ArrayToHex,
|
||||||
} from "@certusone/wormhole-sdk";
|
} from "@certusone/wormhole-sdk";
|
||||||
import {
|
import {
|
||||||
getOriginalAssetEth,
|
getOriginalAssetEth,
|
||||||
|
@ -27,7 +29,6 @@ import useIsWalletReady from "../hooks/useIsWalletReady";
|
||||||
import { getMetaplexData } from "../hooks/useMetaplexData";
|
import { getMetaplexData } from "../hooks/useMetaplexData";
|
||||||
import { COLORS } from "../muiTheme";
|
import { COLORS } from "../muiTheme";
|
||||||
import { NFTParsedTokenAccount } from "../store/nftSlice";
|
import { NFTParsedTokenAccount } from "../store/nftSlice";
|
||||||
import { hexToNativeString, uint8ArrayToHex } from "../utils/array";
|
|
||||||
import {
|
import {
|
||||||
CHAINS,
|
CHAINS,
|
||||||
CHAINS_BY_ID,
|
CHAINS_BY_ID,
|
||||||
|
|
|
@ -85,6 +85,7 @@ export default function SolanaSourceTokenSelector(
|
||||||
const getLogo = useCallback(
|
const getLogo = useCallback(
|
||||||
(account: ParsedTokenAccount) => {
|
(account: ParsedTokenAccount) => {
|
||||||
return (
|
return (
|
||||||
|
(account.isNativeAsset && account.logo) ||
|
||||||
memoizedTokenMap.get(account.mintKey)?.logoURI ||
|
memoizedTokenMap.get(account.mintKey)?.logoURI ||
|
||||||
metaplex.data?.get(account.mintKey)?.data?.uri ||
|
metaplex.data?.get(account.mintKey)?.data?.uri ||
|
||||||
undefined
|
undefined
|
||||||
|
@ -96,6 +97,7 @@ export default function SolanaSourceTokenSelector(
|
||||||
const getSymbol = useCallback(
|
const getSymbol = useCallback(
|
||||||
(account: ParsedTokenAccount) => {
|
(account: ParsedTokenAccount) => {
|
||||||
return (
|
return (
|
||||||
|
(account.isNativeAsset && account.symbol) ||
|
||||||
memoizedTokenMap.get(account.mintKey)?.symbol ||
|
memoizedTokenMap.get(account.mintKey)?.symbol ||
|
||||||
metaplex.data?.get(account.mintKey)?.data?.symbol ||
|
metaplex.data?.get(account.mintKey)?.data?.symbol ||
|
||||||
undefined
|
undefined
|
||||||
|
@ -107,6 +109,7 @@ export default function SolanaSourceTokenSelector(
|
||||||
const getName = useCallback(
|
const getName = useCallback(
|
||||||
(account: ParsedTokenAccount) => {
|
(account: ParsedTokenAccount) => {
|
||||||
return (
|
return (
|
||||||
|
(account.isNativeAsset && account.name) ||
|
||||||
memoizedTokenMap.get(account.mintKey)?.name ||
|
memoizedTokenMap.get(account.mintKey)?.name ||
|
||||||
metaplex.data?.get(account.mintKey)?.data?.name ||
|
metaplex.data?.get(account.mintKey)?.data?.name ||
|
||||||
undefined
|
undefined
|
||||||
|
@ -175,12 +178,18 @@ export default function SolanaSourceTokenSelector(
|
||||||
<Typography variant="subtitle2">{name}</Typography>
|
<Typography variant="subtitle2">{name}</Typography>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<Typography variant="body1">
|
{account.isNativeAsset ? (
|
||||||
{"Mint : " + mintPrettyString}
|
<Typography>{"Native"}</Typography>
|
||||||
</Typography>
|
) : (
|
||||||
<Typography variant="body1">
|
<>
|
||||||
{"Account :" + accountAddressPrettyString}
|
<Typography variant="body1">
|
||||||
</Typography>
|
{"Mint : " + mintPrettyString}
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="body1">
|
||||||
|
{"Account :" + accountAddressPrettyString}
|
||||||
|
</Typography>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<Typography variant="body2">{"Balance"}</Typography>
|
<Typography variant="body2">{"Balance"}</Typography>
|
||||||
|
@ -282,11 +291,15 @@ export default function SolanaSourceTokenSelector(
|
||||||
);
|
);
|
||||||
|
|
||||||
const getOptionLabel = useCallback(
|
const getOptionLabel = useCallback(
|
||||||
(option) => {
|
(option: ParsedTokenAccount) => {
|
||||||
const symbol = getSymbol(option);
|
const symbol = getSymbol(option);
|
||||||
return `${symbol ? symbol : "Unknown"} (Account: ${shortenAddress(
|
const label = option.isNativeAsset
|
||||||
option.publicKey
|
? symbol || "" //default for non-null guarantee
|
||||||
)}, Mint: ${shortenAddress(option.mintKey)})`;
|
: `${symbol ? symbol : "Unknown"} (Account: ${shortenAddress(
|
||||||
|
option.publicKey
|
||||||
|
)}, Mint: ${shortenAddress(option.mintKey)})`;
|
||||||
|
|
||||||
|
return label;
|
||||||
},
|
},
|
||||||
[getSymbol]
|
[getSymbol]
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import {
|
import {
|
||||||
ChainId,
|
|
||||||
CHAIN_ID_BSC,
|
CHAIN_ID_BSC,
|
||||||
CHAIN_ID_ETH,
|
CHAIN_ID_ETH,
|
||||||
CHAIN_ID_SOLANA,
|
CHAIN_ID_SOLANA,
|
||||||
|
@ -7,9 +6,13 @@ import {
|
||||||
getEmitterAddressEth,
|
getEmitterAddressEth,
|
||||||
getEmitterAddressSolana,
|
getEmitterAddressSolana,
|
||||||
getEmitterAddressTerra,
|
getEmitterAddressTerra,
|
||||||
|
hexToNativeString,
|
||||||
|
hexToUint8Array,
|
||||||
parseSequenceFromLogEth,
|
parseSequenceFromLogEth,
|
||||||
parseSequenceFromLogSolana,
|
parseSequenceFromLogSolana,
|
||||||
parseSequenceFromLogTerra,
|
parseSequenceFromLogTerra,
|
||||||
|
parseTransferPayload,
|
||||||
|
uint8ArrayToHex,
|
||||||
} from "@certusone/wormhole-sdk";
|
} from "@certusone/wormhole-sdk";
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
|
@ -31,7 +34,7 @@ import { Restore } from "@material-ui/icons";
|
||||||
import { Alert } from "@material-ui/lab";
|
import { Alert } from "@material-ui/lab";
|
||||||
import { Connection } from "@solana/web3.js";
|
import { Connection } from "@solana/web3.js";
|
||||||
import { LCDClient } from "@terra-money/terra.js";
|
import { LCDClient } from "@terra-money/terra.js";
|
||||||
import { BigNumber, ethers } from "ethers";
|
import { ethers } from "ethers";
|
||||||
import { useSnackbar } from "notistack";
|
import { useSnackbar } from "notistack";
|
||||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
|
@ -45,11 +48,6 @@ import {
|
||||||
setStep,
|
setStep,
|
||||||
setTargetChain,
|
setTargetChain,
|
||||||
} from "../../store/transferSlice";
|
} from "../../store/transferSlice";
|
||||||
import {
|
|
||||||
hexToNativeString,
|
|
||||||
hexToUint8Array,
|
|
||||||
uint8ArrayToHex,
|
|
||||||
} from "../../utils/array";
|
|
||||||
import {
|
import {
|
||||||
CHAINS,
|
CHAINS,
|
||||||
ETH_BRIDGE_ADDRESS,
|
ETH_BRIDGE_ADDRESS,
|
||||||
|
@ -145,22 +143,6 @@ async function terra(tx: string, enqueueSnackbar: any) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 0 u256 amount
|
|
||||||
// 32 [u8; 32] token_address
|
|
||||||
// 64 u16 token_chain
|
|
||||||
// 66 [u8; 32] recipient
|
|
||||||
// 98 u16 recipient_chain
|
|
||||||
// 100 u256 fee
|
|
||||||
|
|
||||||
// TODO: move to wasm / sdk, share with solana
|
|
||||||
const parsePayload = (arr: Buffer) => ({
|
|
||||||
amount: BigNumber.from(arr.slice(1, 1 + 32)).toBigInt(),
|
|
||||||
originAddress: arr.slice(33, 33 + 32).toString("hex"),
|
|
||||||
originChain: arr.readUInt16BE(65) as ChainId,
|
|
||||||
targetAddress: arr.slice(67, 67 + 32).toString("hex"),
|
|
||||||
targetChain: arr.readUInt16BE(99) as ChainId,
|
|
||||||
});
|
|
||||||
|
|
||||||
function RecoveryDialogContent({
|
function RecoveryDialogContent({
|
||||||
onClose,
|
onClose,
|
||||||
disabled,
|
disabled,
|
||||||
|
@ -288,7 +270,9 @@ function RecoveryDialogContent({
|
||||||
const parsedPayload = useMemo(
|
const parsedPayload = useMemo(
|
||||||
() =>
|
() =>
|
||||||
recoveryParsedVAA?.payload
|
recoveryParsedVAA?.payload
|
||||||
? parsePayload(Buffer.from(new Uint8Array(recoveryParsedVAA.payload)))
|
? parseTransferPayload(
|
||||||
|
Buffer.from(new Uint8Array(recoveryParsedVAA.payload))
|
||||||
|
)
|
||||||
: null,
|
: null,
|
||||||
[recoveryParsedVAA]
|
[recoveryParsedVAA]
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,4 +1,8 @@
|
||||||
import { CHAIN_ID_ETH } from "@certusone/wormhole-sdk";
|
import {
|
||||||
|
CHAIN_ID_ETH,
|
||||||
|
CHAIN_ID_SOLANA,
|
||||||
|
WSOL_ADDRESS,
|
||||||
|
} from "@certusone/wormhole-sdk";
|
||||||
import { Checkbox, FormControlLabel } from "@material-ui/core";
|
import { Checkbox, FormControlLabel } from "@material-ui/core";
|
||||||
import { useCallback, useState } from "react";
|
import { useCallback, useState } from "react";
|
||||||
import { useSelector } from "react-redux";
|
import { useSelector } from "react-redux";
|
||||||
|
@ -18,13 +22,18 @@ function Redeem() {
|
||||||
const { handleClick, handleNativeClick, disabled, showLoader } =
|
const { handleClick, handleNativeClick, disabled, showLoader } =
|
||||||
useHandleRedeem();
|
useHandleRedeem();
|
||||||
const targetChain = useSelector(selectTransferTargetChain);
|
const targetChain = useSelector(selectTransferTargetChain);
|
||||||
const targetAssetHex = useSelector(selectTransferTargetAsset);
|
const targetAsset = useSelector(selectTransferTargetAsset);
|
||||||
const { isReady, statusMessage } = useIsWalletReady(targetChain);
|
const { isReady, statusMessage } = useIsWalletReady(targetChain);
|
||||||
//TODO better check, probably involving a hook & the VAA
|
//TODO better check, probably involving a hook & the VAA
|
||||||
const isNativeEligible =
|
const isEthNative =
|
||||||
targetChain === CHAIN_ID_ETH &&
|
targetChain === CHAIN_ID_ETH &&
|
||||||
targetAssetHex &&
|
targetAsset &&
|
||||||
targetAssetHex.toLowerCase() === WETH_ADDRESS.toLowerCase();
|
targetAsset.toLowerCase() === WETH_ADDRESS.toLowerCase();
|
||||||
|
const isSolNative =
|
||||||
|
targetChain === CHAIN_ID_SOLANA &&
|
||||||
|
targetAsset &&
|
||||||
|
targetAsset === WSOL_ADDRESS;
|
||||||
|
const isNativeEligible = isEthNative || isSolNative;
|
||||||
const [useNativeRedeem, setUseNativeRedeem] = useState(true);
|
const [useNativeRedeem, setUseNativeRedeem] = useState(true);
|
||||||
const toggleNativeRedeem = useCallback(() => {
|
const toggleNativeRedeem = useCallback(() => {
|
||||||
setUseNativeRedeem(!useNativeRedeem);
|
setUseNativeRedeem(!useNativeRedeem);
|
||||||
|
|
|
@ -14,7 +14,7 @@ import {
|
||||||
selectTransferOriginChain,
|
selectTransferOriginChain,
|
||||||
selectTransferTargetChain,
|
selectTransferTargetChain,
|
||||||
} from "../../store/selectors";
|
} from "../../store/selectors";
|
||||||
import { hexToNativeString } from "../../utils/array";
|
import { hexToNativeString } from "@certusone/wormhole-sdk";
|
||||||
|
|
||||||
export default function RegisterNowButton() {
|
export default function RegisterNowButton() {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
|
|
@ -1,4 +1,8 @@
|
||||||
import { CHAIN_ID_ETH, CHAIN_ID_SOLANA } from "@certusone/wormhole-sdk";
|
import {
|
||||||
|
CHAIN_ID_SOLANA,
|
||||||
|
CHAIN_ID_ETH,
|
||||||
|
hexToNativeString,
|
||||||
|
} from "@certusone/wormhole-sdk";
|
||||||
import { makeStyles, MenuItem, TextField, Typography } from "@material-ui/core";
|
import { makeStyles, MenuItem, TextField, Typography } from "@material-ui/core";
|
||||||
import { Alert } from "@material-ui/lab";
|
import { Alert } from "@material-ui/lab";
|
||||||
import { useCallback, useMemo } from "react";
|
import { useCallback, useMemo } from "react";
|
||||||
|
@ -18,7 +22,6 @@ import {
|
||||||
UNREGISTERED_ERROR_MESSAGE,
|
UNREGISTERED_ERROR_MESSAGE,
|
||||||
} from "../../store/selectors";
|
} from "../../store/selectors";
|
||||||
import { incrementStep, setTargetChain } from "../../store/transferSlice";
|
import { incrementStep, setTargetChain } from "../../store/transferSlice";
|
||||||
import { hexToNativeString } from "../../utils/array";
|
|
||||||
import { CHAINS, CHAINS_BY_ID } from "../../utils/consts";
|
import { CHAINS, CHAINS_BY_ID } from "../../utils/consts";
|
||||||
import ButtonWithLoader from "../ButtonWithLoader";
|
import ButtonWithLoader from "../ButtonWithLoader";
|
||||||
import KeyAndBalance from "../KeyAndBalance";
|
import KeyAndBalance from "../KeyAndBalance";
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
|
import { hexToNativeString } from "@certusone/wormhole-sdk";
|
||||||
import { makeStyles, Typography } from "@material-ui/core";
|
import { makeStyles, Typography } from "@material-ui/core";
|
||||||
import { useSelector } from "react-redux";
|
import { useSelector } from "react-redux";
|
||||||
import {
|
import {
|
||||||
selectTransferTargetAddressHex,
|
selectTransferTargetAddressHex,
|
||||||
selectTransferTargetChain,
|
selectTransferTargetChain,
|
||||||
} from "../../store/selectors";
|
} from "../../store/selectors";
|
||||||
import { hexToNativeString } from "../../utils/array";
|
|
||||||
import { CHAINS_BY_ID } from "../../utils/consts";
|
import { CHAINS_BY_ID } from "../../utils/consts";
|
||||||
import SmartAddress from "../SmartAddress";
|
import SmartAddress from "../SmartAddress";
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import { useSelector } from "react-redux";
|
import { useSelector } from "react-redux";
|
||||||
import { selectAttestSignedVAAHex } from "../store/selectors";
|
import { selectAttestSignedVAAHex } from "../store/selectors";
|
||||||
import { hexToUint8Array } from "../utils/array";
|
import { hexToUint8Array } from "@certusone/wormhole-sdk";
|
||||||
|
|
||||||
export default function useAttestSignedVAA() {
|
export default function useAttestSignedVAA() {
|
||||||
const signedVAAHex = useSelector(selectAttestSignedVAAHex);
|
const signedVAAHex = useSelector(selectAttestSignedVAAHex);
|
||||||
|
|
|
@ -7,6 +7,7 @@ import {
|
||||||
getOriginalAssetSol,
|
getOriginalAssetSol,
|
||||||
getOriginalAssetTerra,
|
getOriginalAssetTerra,
|
||||||
WormholeWrappedInfo,
|
WormholeWrappedInfo,
|
||||||
|
uint8ArrayToHex,
|
||||||
} from "@certusone/wormhole-sdk";
|
} from "@certusone/wormhole-sdk";
|
||||||
import {
|
import {
|
||||||
getOriginalAssetEth as getOriginalAssetEthNFT,
|
getOriginalAssetEth as getOriginalAssetEthNFT,
|
||||||
|
@ -26,7 +27,6 @@ import {
|
||||||
} from "../store/selectors";
|
} from "../store/selectors";
|
||||||
import { setSourceWormholeWrappedInfo as setNFTSourceWormholeWrappedInfo } from "../store/nftSlice";
|
import { setSourceWormholeWrappedInfo as setNFTSourceWormholeWrappedInfo } from "../store/nftSlice";
|
||||||
import { setSourceWormholeWrappedInfo as setTransferSourceWormholeWrappedInfo } from "../store/transferSlice";
|
import { setSourceWormholeWrappedInfo as setTransferSourceWormholeWrappedInfo } from "../store/transferSlice";
|
||||||
import { uint8ArrayToHex } from "../utils/array";
|
|
||||||
import {
|
import {
|
||||||
ETH_NFT_BRIDGE_ADDRESS,
|
ETH_NFT_BRIDGE_ADDRESS,
|
||||||
ETH_TOKEN_BRIDGE_ADDRESS,
|
ETH_TOKEN_BRIDGE_ADDRESS,
|
||||||
|
|
|
@ -5,6 +5,8 @@ import {
|
||||||
getForeignAssetEth,
|
getForeignAssetEth,
|
||||||
getForeignAssetSolana,
|
getForeignAssetSolana,
|
||||||
getForeignAssetTerra,
|
getForeignAssetTerra,
|
||||||
|
hexToNativeString,
|
||||||
|
hexToUint8Array,
|
||||||
} from "@certusone/wormhole-sdk";
|
} from "@certusone/wormhole-sdk";
|
||||||
import {
|
import {
|
||||||
getForeignAssetEth as getForeignAssetEthNFT,
|
getForeignAssetEth as getForeignAssetEthNFT,
|
||||||
|
@ -30,7 +32,6 @@ import {
|
||||||
selectTransferTargetChain,
|
selectTransferTargetChain,
|
||||||
} from "../store/selectors";
|
} from "../store/selectors";
|
||||||
import { setTargetAsset as setTransferTargetAsset } from "../store/transferSlice";
|
import { setTargetAsset as setTransferTargetAsset } from "../store/transferSlice";
|
||||||
import { hexToNativeString, hexToUint8Array } from "../utils/array";
|
|
||||||
import {
|
import {
|
||||||
ETH_NFT_BRIDGE_ADDRESS,
|
ETH_NFT_BRIDGE_ADDRESS,
|
||||||
ETH_TOKEN_BRIDGE_ADDRESS,
|
ETH_TOKEN_BRIDGE_ADDRESS,
|
||||||
|
|
|
@ -2,6 +2,8 @@ import {
|
||||||
CHAIN_ID_ETH,
|
CHAIN_ID_ETH,
|
||||||
CHAIN_ID_SOLANA,
|
CHAIN_ID_SOLANA,
|
||||||
CHAIN_ID_TERRA,
|
CHAIN_ID_TERRA,
|
||||||
|
WSOL_ADDRESS,
|
||||||
|
WSOL_DECIMALS,
|
||||||
} from "@certusone/wormhole-sdk";
|
} from "@certusone/wormhole-sdk";
|
||||||
import { ethers } from "@certusone/wormhole-sdk/node_modules/ethers";
|
import { ethers } from "@certusone/wormhole-sdk/node_modules/ethers";
|
||||||
import { Dispatch } from "@reduxjs/toolkit";
|
import { Dispatch } from "@reduxjs/toolkit";
|
||||||
|
@ -155,6 +157,31 @@ const createParsedTokenAccountFromCovalent = (
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const createNativeSolParsedTokenAccount = async (
|
||||||
|
connection: Connection,
|
||||||
|
walletAddress: string
|
||||||
|
) => {
|
||||||
|
const fetchAccounts = await getMultipleAccountsRPC(connection, [
|
||||||
|
new PublicKey(walletAddress),
|
||||||
|
]);
|
||||||
|
if (!fetchAccounts || !fetchAccounts.length || !fetchAccounts[0]) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return createParsedTokenAccount(
|
||||||
|
walletAddress, //publicKey
|
||||||
|
WSOL_ADDRESS, //Mint key
|
||||||
|
fetchAccounts[0].lamports.toString(), //amount
|
||||||
|
WSOL_DECIMALS, //decimals, 9
|
||||||
|
parseFloat(formatUnits(fetchAccounts[0].lamports, WSOL_DECIMALS)),
|
||||||
|
formatUnits(fetchAccounts[0].lamports, WSOL_DECIMALS).toString(),
|
||||||
|
"SOL",
|
||||||
|
"Solana",
|
||||||
|
undefined, //TODO logo. It's in the solana token map, so we could potentially use that URL.
|
||||||
|
true
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const createNativeEthParsedTokenAccount = (
|
const createNativeEthParsedTokenAccount = (
|
||||||
provider: Provider,
|
provider: Provider,
|
||||||
signerAddress: string | undefined
|
signerAddress: string | undefined
|
||||||
|
@ -271,7 +298,7 @@ const getEthereumAccountsCovalent = async (
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const getSolanaParsedTokenAccounts = (
|
const getSolanaParsedTokenAccounts = async (
|
||||||
walletAddress: string,
|
walletAddress: string,
|
||||||
dispatch: Dispatch,
|
dispatch: Dispatch,
|
||||||
nft: boolean
|
nft: boolean
|
||||||
|
@ -280,29 +307,40 @@ const getSolanaParsedTokenAccounts = (
|
||||||
dispatch(
|
dispatch(
|
||||||
nft ? fetchSourceParsedTokenAccountsNFT() : fetchSourceParsedTokenAccounts()
|
nft ? fetchSourceParsedTokenAccountsNFT() : fetchSourceParsedTokenAccounts()
|
||||||
);
|
);
|
||||||
return connection
|
try {
|
||||||
.getParsedTokenAccountsByOwner(new PublicKey(walletAddress), {
|
//No matter what, we retrieve the spl tokens associated to this address.
|
||||||
programId: new PublicKey(TOKEN_PROGRAM_ID),
|
let splParsedTokenAccounts = await connection
|
||||||
})
|
.getParsedTokenAccountsByOwner(new PublicKey(walletAddress), {
|
||||||
.then(
|
programId: new PublicKey(TOKEN_PROGRAM_ID),
|
||||||
(result) => {
|
})
|
||||||
const mappedItems = result.value.map((item) =>
|
.then((result) => {
|
||||||
|
return result.value.map((item) =>
|
||||||
createParsedTokenAccountFromInfo(item.pubkey, item.account)
|
createParsedTokenAccountFromInfo(item.pubkey, item.account)
|
||||||
);
|
);
|
||||||
dispatch(
|
});
|
||||||
nft
|
|
||||||
? receiveSourceParsedTokenAccountsNFT(mappedItems)
|
if (nft) {
|
||||||
: receiveSourceParsedTokenAccounts(mappedItems)
|
//In the case of NFTs, we are done, and we set the accounts in redux
|
||||||
);
|
dispatch(receiveSourceParsedTokenAccountsNFT(splParsedTokenAccounts));
|
||||||
},
|
} else {
|
||||||
(error) => {
|
//In the transfer case, we also pull the SOL balance of the wallet, and prepend it at the beginning of the list.
|
||||||
dispatch(
|
const nativeAccount = await createNativeSolParsedTokenAccount(
|
||||||
nft
|
connection,
|
||||||
? errorSourceParsedTokenAccountsNFT("Failed to load NFT metadata")
|
walletAddress
|
||||||
: errorSourceParsedTokenAccounts("Failed to load token metadata.")
|
);
|
||||||
);
|
if (nativeAccount !== null) {
|
||||||
|
splParsedTokenAccounts.unshift(nativeAccount);
|
||||||
}
|
}
|
||||||
|
dispatch(receiveSourceParsedTokenAccounts(splParsedTokenAccounts));
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error(e);
|
||||||
|
dispatch(
|
||||||
|
nft
|
||||||
|
? errorSourceParsedTokenAccountsNFT("Failed to load NFT metadata")
|
||||||
|
: errorSourceParsedTokenAccounts("Failed to load token metadata.")
|
||||||
);
|
);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -11,6 +11,7 @@ import {
|
||||||
parseSequenceFromLogEth,
|
parseSequenceFromLogEth,
|
||||||
parseSequenceFromLogSolana,
|
parseSequenceFromLogSolana,
|
||||||
parseSequenceFromLogTerra,
|
parseSequenceFromLogTerra,
|
||||||
|
uint8ArrayToHex,
|
||||||
} from "@certusone/wormhole-sdk";
|
} from "@certusone/wormhole-sdk";
|
||||||
import { WalletContextState } from "@solana/wallet-adapter-react";
|
import { WalletContextState } from "@solana/wallet-adapter-react";
|
||||||
import { Connection, PublicKey } from "@solana/web3.js";
|
import { Connection, PublicKey } from "@solana/web3.js";
|
||||||
|
@ -36,7 +37,6 @@ import {
|
||||||
selectAttestSourceAsset,
|
selectAttestSourceAsset,
|
||||||
selectAttestSourceChain,
|
selectAttestSourceChain,
|
||||||
} from "../store/selectors";
|
} from "../store/selectors";
|
||||||
import { uint8ArrayToHex } from "../utils/array";
|
|
||||||
import {
|
import {
|
||||||
ETH_BRIDGE_ADDRESS,
|
ETH_BRIDGE_ADDRESS,
|
||||||
ETH_TOKEN_BRIDGE_ADDRESS,
|
ETH_TOKEN_BRIDGE_ADDRESS,
|
||||||
|
|
|
@ -3,6 +3,8 @@ import {
|
||||||
CHAIN_ID_SOLANA,
|
CHAIN_ID_SOLANA,
|
||||||
getClaimAddressSolana,
|
getClaimAddressSolana,
|
||||||
postVaaSolana,
|
postVaaSolana,
|
||||||
|
parseNFTPayload,
|
||||||
|
hexToUint8Array,
|
||||||
} from "@certusone/wormhole-sdk";
|
} from "@certusone/wormhole-sdk";
|
||||||
import {
|
import {
|
||||||
createMetaOnSolana,
|
createMetaOnSolana,
|
||||||
|
@ -18,12 +20,10 @@ import { Signer } from "ethers";
|
||||||
import { useSnackbar } from "notistack";
|
import { useSnackbar } from "notistack";
|
||||||
import { useCallback, useMemo } from "react";
|
import { useCallback, useMemo } from "react";
|
||||||
import { useDispatch, useSelector } from "react-redux";
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import { parsePayload } from "../components/NFT/Recovery";
|
|
||||||
import { useEthereumProvider } from "../contexts/EthereumProviderContext";
|
import { useEthereumProvider } from "../contexts/EthereumProviderContext";
|
||||||
import { useSolanaWallet } from "../contexts/SolanaWalletContext";
|
import { useSolanaWallet } from "../contexts/SolanaWalletContext";
|
||||||
import { setIsRedeeming, setRedeemTx } from "../store/nftSlice";
|
import { setIsRedeeming, setRedeemTx } from "../store/nftSlice";
|
||||||
import { selectNFTIsRedeeming, selectNFTTargetChain } from "../store/selectors";
|
import { selectNFTIsRedeeming, selectNFTTargetChain } from "../store/selectors";
|
||||||
import { hexToUint8Array } from "../utils/array";
|
|
||||||
import {
|
import {
|
||||||
ETH_NFT_BRIDGE_ADDRESS,
|
ETH_NFT_BRIDGE_ADDRESS,
|
||||||
SOLANA_HOST,
|
SOLANA_HOST,
|
||||||
|
@ -102,7 +102,7 @@ async function solana(
|
||||||
"@certusone/wormhole-sdk/lib/solana/core/bridge"
|
"@certusone/wormhole-sdk/lib/solana/core/bridge"
|
||||||
);
|
);
|
||||||
const parsedVAA = parse_vaa(signedVAA);
|
const parsedVAA = parse_vaa(signedVAA);
|
||||||
const { originChain, originAddress, tokenId } = parsePayload(
|
const { originChain, originAddress, tokenId } = parseNFTPayload(
|
||||||
Buffer.from(new Uint8Array(parsedVAA.payload))
|
Buffer.from(new Uint8Array(parsedVAA.payload))
|
||||||
);
|
);
|
||||||
const mintAddress = await getForeignAssetSol(
|
const mintAddress = await getForeignAssetSol(
|
||||||
|
|
|
@ -6,6 +6,8 @@ import {
|
||||||
getEmitterAddressSolana,
|
getEmitterAddressSolana,
|
||||||
parseSequenceFromLogEth,
|
parseSequenceFromLogEth,
|
||||||
parseSequenceFromLogSolana,
|
parseSequenceFromLogSolana,
|
||||||
|
hexToUint8Array,
|
||||||
|
uint8ArrayToHex,
|
||||||
} from "@certusone/wormhole-sdk";
|
} from "@certusone/wormhole-sdk";
|
||||||
import {
|
import {
|
||||||
transferFromEth,
|
transferFromEth,
|
||||||
|
@ -37,7 +39,6 @@ import {
|
||||||
selectNFTSourceParsedTokenAccount,
|
selectNFTSourceParsedTokenAccount,
|
||||||
selectNFTTargetChain,
|
selectNFTTargetChain,
|
||||||
} from "../store/selectors";
|
} from "../store/selectors";
|
||||||
import { hexToUint8Array, uint8ArrayToHex } from "../utils/array";
|
|
||||||
import {
|
import {
|
||||||
ETH_BRIDGE_ADDRESS,
|
ETH_BRIDGE_ADDRESS,
|
||||||
ETH_NFT_BRIDGE_ADDRESS,
|
ETH_NFT_BRIDGE_ADDRESS,
|
||||||
|
|
|
@ -3,6 +3,7 @@ import {
|
||||||
CHAIN_ID_SOLANA,
|
CHAIN_ID_SOLANA,
|
||||||
CHAIN_ID_TERRA,
|
CHAIN_ID_TERRA,
|
||||||
postVaaSolana,
|
postVaaSolana,
|
||||||
|
redeemAndUnwrapOnSolana,
|
||||||
redeemOnEth,
|
redeemOnEth,
|
||||||
redeemOnEthNative,
|
redeemOnEthNative,
|
||||||
redeemOnSolana,
|
redeemOnSolana,
|
||||||
|
@ -63,7 +64,8 @@ async function solana(
|
||||||
enqueueSnackbar: any,
|
enqueueSnackbar: any,
|
||||||
wallet: WalletContextState,
|
wallet: WalletContextState,
|
||||||
payerAddress: string, //TODO: we may not need this since we have wallet
|
payerAddress: string, //TODO: we may not need this since we have wallet
|
||||||
signedVAA: Uint8Array
|
signedVAA: Uint8Array,
|
||||||
|
isNative: boolean
|
||||||
) {
|
) {
|
||||||
dispatch(setIsRedeeming(true));
|
dispatch(setIsRedeeming(true));
|
||||||
try {
|
try {
|
||||||
|
@ -79,13 +81,21 @@ async function solana(
|
||||||
Buffer.from(signedVAA)
|
Buffer.from(signedVAA)
|
||||||
);
|
);
|
||||||
// TODO: how do we retry in between these steps
|
// TODO: how do we retry in between these steps
|
||||||
const transaction = await redeemOnSolana(
|
const transaction = isNative
|
||||||
connection,
|
? await redeemAndUnwrapOnSolana(
|
||||||
SOL_BRIDGE_ADDRESS,
|
connection,
|
||||||
SOL_TOKEN_BRIDGE_ADDRESS,
|
SOL_BRIDGE_ADDRESS,
|
||||||
payerAddress,
|
SOL_TOKEN_BRIDGE_ADDRESS,
|
||||||
signedVAA
|
payerAddress,
|
||||||
);
|
signedVAA
|
||||||
|
)
|
||||||
|
: await redeemOnSolana(
|
||||||
|
connection,
|
||||||
|
SOL_BRIDGE_ADDRESS,
|
||||||
|
SOL_TOKEN_BRIDGE_ADDRESS,
|
||||||
|
payerAddress,
|
||||||
|
signedVAA
|
||||||
|
);
|
||||||
const txid = await signSendAndConfirm(wallet, connection, transaction);
|
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?
|
// 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, block: 1 }));
|
dispatch(setRedeemTx({ id: txid, block: 1 }));
|
||||||
|
@ -147,7 +157,8 @@ export function useHandleRedeem() {
|
||||||
enqueueSnackbar,
|
enqueueSnackbar,
|
||||||
solanaWallet,
|
solanaWallet,
|
||||||
solPK.toString(),
|
solPK.toString(),
|
||||||
signedVAA
|
signedVAA,
|
||||||
|
false
|
||||||
);
|
);
|
||||||
} else if (targetChain === CHAIN_ID_TERRA && !!terraWallet && signedVAA) {
|
} else if (targetChain === CHAIN_ID_TERRA && !!terraWallet && signedVAA) {
|
||||||
terra(dispatch, enqueueSnackbar, terraWallet, signedVAA);
|
terra(dispatch, enqueueSnackbar, terraWallet, signedVAA);
|
||||||
|
@ -181,7 +192,8 @@ export function useHandleRedeem() {
|
||||||
enqueueSnackbar,
|
enqueueSnackbar,
|
||||||
solanaWallet,
|
solanaWallet,
|
||||||
solPK.toString(),
|
solPK.toString(),
|
||||||
signedVAA
|
signedVAA,
|
||||||
|
true
|
||||||
);
|
);
|
||||||
} else if (targetChain === CHAIN_ID_TERRA && !!terraWallet && signedVAA) {
|
} else if (targetChain === CHAIN_ID_TERRA && !!terraWallet && signedVAA) {
|
||||||
terra(dispatch, enqueueSnackbar, terraWallet, signedVAA); //TODO isNative = true
|
terra(dispatch, enqueueSnackbar, terraWallet, signedVAA); //TODO isNative = true
|
||||||
|
|
|
@ -12,7 +12,10 @@ import {
|
||||||
transferFromEth,
|
transferFromEth,
|
||||||
transferFromEthNative,
|
transferFromEthNative,
|
||||||
transferFromSolana,
|
transferFromSolana,
|
||||||
|
transferNativeSol,
|
||||||
transferFromTerra,
|
transferFromTerra,
|
||||||
|
hexToUint8Array,
|
||||||
|
uint8ArrayToHex,
|
||||||
} from "@certusone/wormhole-sdk";
|
} from "@certusone/wormhole-sdk";
|
||||||
import { WalletContextState } from "@solana/wallet-adapter-react";
|
import { WalletContextState } from "@solana/wallet-adapter-react";
|
||||||
import { Connection } from "@solana/web3.js";
|
import { Connection } from "@solana/web3.js";
|
||||||
|
@ -44,7 +47,6 @@ import {
|
||||||
setSignedVAAHex,
|
setSignedVAAHex,
|
||||||
setTransferTx,
|
setTransferTx,
|
||||||
} from "../store/transferSlice";
|
} from "../store/transferSlice";
|
||||||
import { hexToUint8Array, uint8ArrayToHex } from "../utils/array";
|
|
||||||
import {
|
import {
|
||||||
ETH_BRIDGE_ADDRESS,
|
ETH_BRIDGE_ADDRESS,
|
||||||
ETH_TOKEN_BRIDGE_ADDRESS,
|
ETH_TOKEN_BRIDGE_ADDRESS,
|
||||||
|
@ -121,6 +123,7 @@ async function solana(
|
||||||
decimals: number,
|
decimals: number,
|
||||||
targetChain: ChainId,
|
targetChain: ChainId,
|
||||||
targetAddress: Uint8Array,
|
targetAddress: Uint8Array,
|
||||||
|
isNative: boolean,
|
||||||
originAddressStr?: string,
|
originAddressStr?: string,
|
||||||
originChain?: ChainId
|
originChain?: ChainId
|
||||||
) {
|
) {
|
||||||
|
@ -131,19 +134,30 @@ async function solana(
|
||||||
const originAddress = originAddressStr
|
const originAddress = originAddressStr
|
||||||
? zeroPad(hexToUint8Array(originAddressStr), 32)
|
? zeroPad(hexToUint8Array(originAddressStr), 32)
|
||||||
: undefined;
|
: undefined;
|
||||||
const transaction = await transferFromSolana(
|
const promise = isNative
|
||||||
connection,
|
? transferNativeSol(
|
||||||
SOL_BRIDGE_ADDRESS,
|
connection,
|
||||||
SOL_TOKEN_BRIDGE_ADDRESS,
|
SOL_BRIDGE_ADDRESS,
|
||||||
payerAddress,
|
SOL_TOKEN_BRIDGE_ADDRESS,
|
||||||
fromAddress,
|
payerAddress,
|
||||||
mintAddress,
|
amountParsed,
|
||||||
amountParsed,
|
targetAddress,
|
||||||
targetAddress,
|
targetChain
|
||||||
targetChain,
|
)
|
||||||
originAddress,
|
: transferFromSolana(
|
||||||
originChain
|
connection,
|
||||||
);
|
SOL_BRIDGE_ADDRESS,
|
||||||
|
SOL_TOKEN_BRIDGE_ADDRESS,
|
||||||
|
payerAddress,
|
||||||
|
fromAddress,
|
||||||
|
mintAddress,
|
||||||
|
amountParsed,
|
||||||
|
targetAddress,
|
||||||
|
targetChain,
|
||||||
|
originAddress,
|
||||||
|
originChain
|
||||||
|
);
|
||||||
|
const transaction = await promise;
|
||||||
const txid = await signSendAndConfirm(wallet, connection, transaction);
|
const txid = await signSendAndConfirm(wallet, connection, transaction);
|
||||||
enqueueSnackbar("Transaction confirmed", { variant: "success" });
|
enqueueSnackbar("Transaction confirmed", { variant: "success" });
|
||||||
const info = await connection.getTransaction(txid);
|
const info = await connection.getTransaction(txid);
|
||||||
|
@ -289,6 +303,7 @@ export function useHandleTransfer() {
|
||||||
decimals,
|
decimals,
|
||||||
targetChain,
|
targetChain,
|
||||||
targetAddress,
|
targetAddress,
|
||||||
|
isNative,
|
||||||
originAsset,
|
originAsset,
|
||||||
originChain
|
originChain
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import { useSelector } from "react-redux";
|
import { useSelector } from "react-redux";
|
||||||
import { selectNFTSignedVAAHex } from "../store/selectors";
|
import { selectNFTSignedVAAHex } from "../store/selectors";
|
||||||
import { hexToUint8Array } from "../utils/array";
|
import { hexToUint8Array } from "@certusone/wormhole-sdk";
|
||||||
|
|
||||||
export default function useNFTSignedVAA() {
|
export default function useNFTSignedVAA() {
|
||||||
const signedVAAHex = useSelector(selectNFTSignedVAAHex);
|
const signedVAAHex = useSelector(selectNFTSignedVAAHex);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import { useSelector } from "react-redux";
|
import { useSelector } from "react-redux";
|
||||||
import { selectNFTTargetAddressHex } from "../store/selectors";
|
import { selectNFTTargetAddressHex } from "../store/selectors";
|
||||||
import { hexToUint8Array } from "../utils/array";
|
import { hexToUint8Array } from "@certusone/wormhole-sdk";
|
||||||
|
|
||||||
export default function useNFTTargetAddressHex() {
|
export default function useNFTTargetAddressHex() {
|
||||||
const targetAddressHex = useSelector(selectNFTTargetAddressHex);
|
const targetAddressHex = useSelector(selectNFTTargetAddressHex);
|
||||||
|
|
|
@ -3,6 +3,7 @@ import {
|
||||||
CHAIN_ID_SOLANA,
|
CHAIN_ID_SOLANA,
|
||||||
CHAIN_ID_TERRA,
|
CHAIN_ID_TERRA,
|
||||||
canonicalAddress,
|
canonicalAddress,
|
||||||
|
uint8ArrayToHex,
|
||||||
} from "@certusone/wormhole-sdk";
|
} from "@certusone/wormhole-sdk";
|
||||||
import { arrayify, zeroPad } from "@ethersproject/bytes";
|
import { arrayify, zeroPad } from "@ethersproject/bytes";
|
||||||
import {
|
import {
|
||||||
|
@ -25,7 +26,6 @@ import {
|
||||||
} from "../store/selectors";
|
} from "../store/selectors";
|
||||||
import { setTargetAddressHex as setNFTTargetAddressHex } from "../store/nftSlice";
|
import { setTargetAddressHex as setNFTTargetAddressHex } from "../store/nftSlice";
|
||||||
import { setTargetAddressHex as setTransferTargetAddressHex } from "../store/transferSlice";
|
import { setTargetAddressHex as setTransferTargetAddressHex } from "../store/transferSlice";
|
||||||
import { uint8ArrayToHex } from "../utils/array";
|
|
||||||
|
|
||||||
function useSyncTargetAddress(shouldFire: boolean, nft?: boolean) {
|
function useSyncTargetAddress(shouldFire: boolean, nft?: boolean) {
|
||||||
const dispatch = useDispatch();
|
const dispatch = useDispatch();
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import { useSelector } from "react-redux";
|
import { useSelector } from "react-redux";
|
||||||
import { selectTransferSignedVAAHex } from "../store/selectors";
|
import { selectTransferSignedVAAHex } from "../store/selectors";
|
||||||
import { hexToUint8Array } from "../utils/array";
|
import { hexToUint8Array } from "@certusone/wormhole-sdk";
|
||||||
|
|
||||||
export default function useTransferSignedVAA() {
|
export default function useTransferSignedVAA() {
|
||||||
const signedVAAHex = useSelector(selectTransferSignedVAAHex);
|
const signedVAAHex = useSelector(selectTransferSignedVAAHex);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { useMemo } from "react";
|
import { useMemo } from "react";
|
||||||
import { useSelector } from "react-redux";
|
import { useSelector } from "react-redux";
|
||||||
import { selectTransferTargetAddressHex } from "../store/selectors";
|
import { selectTransferTargetAddressHex } from "../store/selectors";
|
||||||
import { hexToUint8Array } from "../utils/array";
|
import { hexToUint8Array } from "@certusone/wormhole-sdk";
|
||||||
|
|
||||||
export default function useTransferTargetAddressHex() {
|
export default function useTransferTargetAddressHex() {
|
||||||
const targetAddressHex = useSelector(selectTransferTargetAddressHex);
|
const targetAddressHex = useSelector(selectTransferTargetAddressHex);
|
||||||
|
|
|
@ -1,10 +1,24 @@
|
||||||
import { Connection, PublicKey, Transaction } from "@solana/web3.js";
|
import { AccountLayout, Token, TOKEN_PROGRAM_ID, u64 } from "@solana/spl-token";
|
||||||
|
import {
|
||||||
|
Connection,
|
||||||
|
Keypair,
|
||||||
|
PublicKey,
|
||||||
|
SystemProgram,
|
||||||
|
Transaction,
|
||||||
|
} from "@solana/web3.js";
|
||||||
import { MsgExecuteContract } from "@terra-money/terra.js";
|
import { MsgExecuteContract } from "@terra-money/terra.js";
|
||||||
import { ethers } from "ethers";
|
import { ethers } from "ethers";
|
||||||
import { fromUint8Array } from "js-base64";
|
import { fromUint8Array } from "js-base64";
|
||||||
import { Bridge__factory } from "../ethers-contracts";
|
import { Bridge__factory } from "../ethers-contracts";
|
||||||
import { ixFromRust } from "../solana";
|
import { ixFromRust } from "../solana";
|
||||||
import { CHAIN_ID_SOLANA } from "../utils";
|
import {
|
||||||
|
CHAIN_ID_SOLANA,
|
||||||
|
WSOL_ADDRESS,
|
||||||
|
WSOL_DECIMALS,
|
||||||
|
MAX_VAA_DECIMALS,
|
||||||
|
} from "../utils";
|
||||||
|
import { hexToNativeString } from "../utils/array";
|
||||||
|
import { parseTransferPayload } from "../utils/parseVaa";
|
||||||
|
|
||||||
export async function redeemOnEth(
|
export async function redeemOnEth(
|
||||||
tokenBridgeAddress: string,
|
tokenBridgeAddress: string,
|
||||||
|
@ -45,6 +59,96 @@ export async function redeemOnTerra(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function redeemAndUnwrapOnSolana(
|
||||||
|
connection: Connection,
|
||||||
|
bridgeAddress: string,
|
||||||
|
tokenBridgeAddress: string,
|
||||||
|
payerAddress: string,
|
||||||
|
signedVAA: Uint8Array
|
||||||
|
) {
|
||||||
|
const { parse_vaa } = await import("../solana/core/bridge");
|
||||||
|
const { complete_transfer_native_ix } = await import(
|
||||||
|
"../solana/token/token_bridge"
|
||||||
|
);
|
||||||
|
const parsedVAA = parse_vaa(signedVAA);
|
||||||
|
const parsedPayload = parseTransferPayload(
|
||||||
|
Buffer.from(new Uint8Array(parsedVAA.payload))
|
||||||
|
);
|
||||||
|
const targetAddress = hexToNativeString(
|
||||||
|
parsedPayload.targetAddress,
|
||||||
|
CHAIN_ID_SOLANA
|
||||||
|
);
|
||||||
|
if (!targetAddress) {
|
||||||
|
throw new Error("Failed to read the target address.");
|
||||||
|
}
|
||||||
|
const targetPublicKey = new PublicKey(targetAddress);
|
||||||
|
const targetAmount =
|
||||||
|
parsedPayload.amount *
|
||||||
|
BigInt(WSOL_DECIMALS - MAX_VAA_DECIMALS) *
|
||||||
|
BigInt(10);
|
||||||
|
const rentBalance = await Token.getMinBalanceRentForExemptAccount(connection);
|
||||||
|
const mintPublicKey = new PublicKey(WSOL_ADDRESS);
|
||||||
|
const payerPublicKey = new PublicKey(payerAddress);
|
||||||
|
const ancillaryKeypair = Keypair.generate();
|
||||||
|
|
||||||
|
const completeTransferIx = ixFromRust(
|
||||||
|
complete_transfer_native_ix(
|
||||||
|
tokenBridgeAddress,
|
||||||
|
bridgeAddress,
|
||||||
|
payerAddress,
|
||||||
|
signedVAA
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
//This will create a temporary account where the wSOL will be moved
|
||||||
|
const createAncillaryAccountIx = SystemProgram.createAccount({
|
||||||
|
fromPubkey: payerPublicKey,
|
||||||
|
newAccountPubkey: ancillaryKeypair.publicKey,
|
||||||
|
lamports: rentBalance, //spl token accounts need rent exemption
|
||||||
|
space: AccountLayout.span,
|
||||||
|
programId: TOKEN_PROGRAM_ID,
|
||||||
|
});
|
||||||
|
|
||||||
|
//Initialize the account as a WSOL account, with the original payerAddress as owner
|
||||||
|
const initAccountIx = await Token.createInitAccountInstruction(
|
||||||
|
TOKEN_PROGRAM_ID,
|
||||||
|
mintPublicKey,
|
||||||
|
ancillaryKeypair.publicKey,
|
||||||
|
payerPublicKey
|
||||||
|
);
|
||||||
|
|
||||||
|
//Send in the amount of wSOL which we want converted to SOL
|
||||||
|
const balanceTransferIx = Token.createTransferInstruction(
|
||||||
|
TOKEN_PROGRAM_ID,
|
||||||
|
targetPublicKey,
|
||||||
|
ancillaryKeypair.publicKey,
|
||||||
|
payerPublicKey,
|
||||||
|
[],
|
||||||
|
new u64(targetAmount.toString(16), 16)
|
||||||
|
);
|
||||||
|
|
||||||
|
//Close the ancillary account for cleanup. Payer address receives any remaining funds
|
||||||
|
const closeAccountIx = Token.createCloseAccountInstruction(
|
||||||
|
TOKEN_PROGRAM_ID,
|
||||||
|
ancillaryKeypair.publicKey, //account to close
|
||||||
|
payerPublicKey, //Remaining funds destination
|
||||||
|
payerPublicKey, //authority
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
|
const { blockhash } = await connection.getRecentBlockhash();
|
||||||
|
const transaction = new Transaction();
|
||||||
|
transaction.recentBlockhash = blockhash;
|
||||||
|
transaction.feePayer = new PublicKey(payerAddress);
|
||||||
|
transaction.add(completeTransferIx);
|
||||||
|
transaction.add(createAncillaryAccountIx);
|
||||||
|
transaction.add(initAccountIx);
|
||||||
|
transaction.add(balanceTransferIx);
|
||||||
|
transaction.add(closeAccountIx);
|
||||||
|
transaction.partialSign(ancillaryKeypair);
|
||||||
|
return transaction;
|
||||||
|
}
|
||||||
|
|
||||||
export async function redeemOnSolana(
|
export async function redeemOnSolana(
|
||||||
connection: Connection,
|
connection: Connection,
|
||||||
bridgeAddress: string,
|
bridgeAddress: string,
|
||||||
|
|
|
@ -1,5 +1,11 @@
|
||||||
import { Token, TOKEN_PROGRAM_ID, u64 } from "@solana/spl-token";
|
import { AccountLayout, Token, TOKEN_PROGRAM_ID, u64 } from "@solana/spl-token";
|
||||||
import { Connection, Keypair, PublicKey, Transaction } from "@solana/web3.js";
|
import {
|
||||||
|
Connection,
|
||||||
|
Keypair,
|
||||||
|
PublicKey,
|
||||||
|
SystemProgram,
|
||||||
|
Transaction,
|
||||||
|
} from "@solana/web3.js";
|
||||||
import { MsgExecuteContract } from "@terra-money/terra.js";
|
import { MsgExecuteContract } from "@terra-money/terra.js";
|
||||||
import { ethers } from "ethers";
|
import { ethers } from "ethers";
|
||||||
import {
|
import {
|
||||||
|
@ -7,7 +13,7 @@ import {
|
||||||
TokenImplementation__factory,
|
TokenImplementation__factory,
|
||||||
} from "../ethers-contracts";
|
} from "../ethers-contracts";
|
||||||
import { getBridgeFeeIx, ixFromRust } from "../solana";
|
import { getBridgeFeeIx, ixFromRust } from "../solana";
|
||||||
import { ChainId, CHAIN_ID_SOLANA, createNonce } from "../utils";
|
import { ChainId, CHAIN_ID_SOLANA, createNonce, WSOL_ADDRESS } from "../utils";
|
||||||
|
|
||||||
export async function getAllowanceEth(
|
export async function getAllowanceEth(
|
||||||
tokenBridgeAddress: string,
|
tokenBridgeAddress: string,
|
||||||
|
@ -117,6 +123,104 @@ export async function transferFromTerra(
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function transferNativeSol(
|
||||||
|
connection: Connection,
|
||||||
|
bridgeAddress: string,
|
||||||
|
tokenBridgeAddress: string,
|
||||||
|
payerAddress: string,
|
||||||
|
amount: BigInt,
|
||||||
|
targetAddress: Uint8Array,
|
||||||
|
targetChain: ChainId
|
||||||
|
) {
|
||||||
|
//https://github.com/solana-labs/solana-program-library/blob/master/token/js/client/token.js
|
||||||
|
const rentBalance = await Token.getMinBalanceRentForExemptAccount(connection);
|
||||||
|
const mintPublicKey = new PublicKey(WSOL_ADDRESS);
|
||||||
|
const payerPublicKey = new PublicKey(payerAddress);
|
||||||
|
const ancillaryKeypair = Keypair.generate();
|
||||||
|
|
||||||
|
//This will create a temporary account where the wSOL will be created.
|
||||||
|
const createAncillaryAccountIx = SystemProgram.createAccount({
|
||||||
|
fromPubkey: payerPublicKey,
|
||||||
|
newAccountPubkey: ancillaryKeypair.publicKey,
|
||||||
|
lamports: rentBalance, //spl token accounts need rent exemption
|
||||||
|
space: AccountLayout.span,
|
||||||
|
programId: TOKEN_PROGRAM_ID,
|
||||||
|
});
|
||||||
|
|
||||||
|
//Send in the amount of SOL which we want converted to wSOL
|
||||||
|
const initialBalanceTransferIx = SystemProgram.transfer({
|
||||||
|
fromPubkey: payerPublicKey,
|
||||||
|
lamports: Number(amount),
|
||||||
|
toPubkey: ancillaryKeypair.publicKey,
|
||||||
|
});
|
||||||
|
//Initialize the account as a WSOL account, with the original payerAddress as owner
|
||||||
|
const initAccountIx = await Token.createInitAccountInstruction(
|
||||||
|
TOKEN_PROGRAM_ID,
|
||||||
|
mintPublicKey,
|
||||||
|
ancillaryKeypair.publicKey,
|
||||||
|
payerPublicKey
|
||||||
|
);
|
||||||
|
|
||||||
|
//Normal approve & transfer instructions, except that the wSOL is sent from the ancillary account.
|
||||||
|
const { transfer_native_ix, approval_authority_address } = await import(
|
||||||
|
"../solana/token/token_bridge"
|
||||||
|
);
|
||||||
|
const nonce = createNonce().readUInt32LE(0);
|
||||||
|
const fee = BigInt(0); // for now, this won't do anything, we may add later
|
||||||
|
const transferIx = await getBridgeFeeIx(
|
||||||
|
connection,
|
||||||
|
bridgeAddress,
|
||||||
|
payerAddress
|
||||||
|
);
|
||||||
|
const approvalIx = Token.createApproveInstruction(
|
||||||
|
TOKEN_PROGRAM_ID,
|
||||||
|
ancillaryKeypair.publicKey,
|
||||||
|
new PublicKey(approval_authority_address(tokenBridgeAddress)),
|
||||||
|
payerPublicKey, //owner
|
||||||
|
[],
|
||||||
|
new u64(amount.toString(16), 16)
|
||||||
|
);
|
||||||
|
let messageKey = Keypair.generate();
|
||||||
|
|
||||||
|
const ix = ixFromRust(
|
||||||
|
transfer_native_ix(
|
||||||
|
tokenBridgeAddress,
|
||||||
|
bridgeAddress,
|
||||||
|
payerAddress,
|
||||||
|
messageKey.publicKey.toString(),
|
||||||
|
ancillaryKeypair.publicKey.toString(),
|
||||||
|
WSOL_ADDRESS,
|
||||||
|
nonce,
|
||||||
|
amount,
|
||||||
|
fee,
|
||||||
|
targetAddress,
|
||||||
|
targetChain
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
//Close the ancillary account for cleanup. Payer address receives any remaining funds
|
||||||
|
const closeAccountIx = Token.createCloseAccountInstruction(
|
||||||
|
TOKEN_PROGRAM_ID,
|
||||||
|
ancillaryKeypair.publicKey, //account to close
|
||||||
|
payerPublicKey, //Remaining funds destination
|
||||||
|
payerPublicKey, //authority
|
||||||
|
[]
|
||||||
|
);
|
||||||
|
|
||||||
|
const { blockhash } = await connection.getRecentBlockhash();
|
||||||
|
const transaction = new Transaction();
|
||||||
|
transaction.recentBlockhash = blockhash;
|
||||||
|
transaction.feePayer = new PublicKey(payerAddress);
|
||||||
|
transaction.add(createAncillaryAccountIx);
|
||||||
|
transaction.add(initialBalanceTransferIx);
|
||||||
|
transaction.add(initAccountIx);
|
||||||
|
transaction.add(transferIx, approvalIx, ix);
|
||||||
|
transaction.add(closeAccountIx);
|
||||||
|
transaction.partialSign(messageKey);
|
||||||
|
transaction.partialSign(ancillaryKeypair);
|
||||||
|
return transaction;
|
||||||
|
}
|
||||||
|
|
||||||
export async function transferFromSolana(
|
export async function transferFromSolana(
|
||||||
connection: Connection,
|
connection: Connection,
|
||||||
bridgeAddress: string,
|
bridgeAddress: string,
|
||||||
|
|
|
@ -3,8 +3,8 @@ import {
|
||||||
CHAIN_ID_ETH,
|
CHAIN_ID_ETH,
|
||||||
CHAIN_ID_SOLANA,
|
CHAIN_ID_SOLANA,
|
||||||
CHAIN_ID_TERRA,
|
CHAIN_ID_TERRA,
|
||||||
humanAddress,
|
} from "./consts";
|
||||||
} from "@certusone/wormhole-sdk";
|
import { humanAddress } from "../terra";
|
||||||
import { PublicKey } from "@solana/web3.js";
|
import { PublicKey } from "@solana/web3.js";
|
||||||
import { hexValue, hexZeroPad } from "ethers/lib/utils";
|
import { hexValue, hexZeroPad } from "ethers/lib/utils";
|
||||||
|
|
|
@ -3,3 +3,7 @@ export const CHAIN_ID_SOLANA: ChainId = 1;
|
||||||
export const CHAIN_ID_ETH: ChainId = 2;
|
export const CHAIN_ID_ETH: ChainId = 2;
|
||||||
export const CHAIN_ID_TERRA: ChainId = 3;
|
export const CHAIN_ID_TERRA: ChainId = 3;
|
||||||
export const CHAIN_ID_BSC: ChainId = 4;
|
export const CHAIN_ID_BSC: ChainId = 4;
|
||||||
|
|
||||||
|
export const WSOL_ADDRESS = "So11111111111111111111111111111111111111112";
|
||||||
|
export const WSOL_DECIMALS = 9;
|
||||||
|
export const MAX_VAA_DECIMALS = 8;
|
||||||
|
|
|
@ -1,2 +1,4 @@
|
||||||
export * from "./consts";
|
export * from "./consts";
|
||||||
export * from "./createNonce";
|
export * from "./createNonce";
|
||||||
|
export * from "./parseVaa";
|
||||||
|
export * from "./array";
|
||||||
|
|
|
@ -0,0 +1,84 @@
|
||||||
|
import { BigNumber } from "@ethersproject/bignumber";
|
||||||
|
import { ChainId } from "./consts";
|
||||||
|
|
||||||
|
export const METADATA_REPLACE = new RegExp("\u0000", "g");
|
||||||
|
|
||||||
|
// note: actual first byte is message type
|
||||||
|
// 0 [u8; 32] token_address
|
||||||
|
// 32 u16 token_chain
|
||||||
|
// 34 [u8; 32] symbol
|
||||||
|
// 66 [u8; 32] name
|
||||||
|
// 98 u256 tokenId
|
||||||
|
// 130 u8 uri_len
|
||||||
|
// 131 [u8;len] uri
|
||||||
|
// ? [u8; 32] recipient
|
||||||
|
// ? u16 recipient_chain
|
||||||
|
export const parseNFTPayload = (arr: Buffer) => {
|
||||||
|
const originAddress = arr.slice(1, 1 + 32).toString("hex");
|
||||||
|
const originChain = arr.readUInt16BE(33) as ChainId;
|
||||||
|
const symbol = Buffer.from(arr.slice(35, 35 + 32))
|
||||||
|
.toString("utf8")
|
||||||
|
.replace(METADATA_REPLACE, "");
|
||||||
|
const name = Buffer.from(arr.slice(67, 67 + 32))
|
||||||
|
.toString("utf8")
|
||||||
|
.replace(METADATA_REPLACE, "");
|
||||||
|
const tokenId = BigNumber.from(arr.slice(99, 99 + 32));
|
||||||
|
const uri_len = arr.readUInt8(131);
|
||||||
|
const uri = Buffer.from(arr.slice(132, 132 + uri_len))
|
||||||
|
.toString("utf8")
|
||||||
|
.replace(METADATA_REPLACE, "");
|
||||||
|
const target_offset = 132 + uri_len;
|
||||||
|
const targetAddress = arr
|
||||||
|
.slice(target_offset, target_offset + 32)
|
||||||
|
.toString("hex");
|
||||||
|
const targetChain = arr.readUInt16BE(target_offset + 32) as ChainId;
|
||||||
|
return {
|
||||||
|
originAddress,
|
||||||
|
originChain,
|
||||||
|
symbol,
|
||||||
|
name,
|
||||||
|
tokenId,
|
||||||
|
uri,
|
||||||
|
targetAddress,
|
||||||
|
targetChain,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// 0 u256 amount
|
||||||
|
// 32 [u8; 32] token_address
|
||||||
|
// 64 u16 token_chain
|
||||||
|
// 66 [u8; 32] recipient
|
||||||
|
// 98 u16 recipient_chain
|
||||||
|
// 100 u256 fee
|
||||||
|
export const parseTransferPayload = (arr: Buffer) => ({
|
||||||
|
amount: BigNumber.from(arr.slice(1, 1 + 32)).toBigInt(),
|
||||||
|
originAddress: arr.slice(33, 33 + 32).toString("hex"),
|
||||||
|
originChain: arr.readUInt16BE(65) as ChainId,
|
||||||
|
targetAddress: arr.slice(67, 67 + 32).toString("hex"),
|
||||||
|
targetChain: arr.readUInt16BE(99) as ChainId,
|
||||||
|
});
|
||||||
|
|
||||||
|
//This returns a corrected amount, which accounts for the difference between the VAA
|
||||||
|
//decimals, and the decimals of the asset.
|
||||||
|
// const normalizeVaaAmount = (
|
||||||
|
// amount: bigint,
|
||||||
|
// assetDecimals: number
|
||||||
|
// ): bigint => {
|
||||||
|
// const MAX_VAA_DECIMALS = 8;
|
||||||
|
// if (assetDecimals <= MAX_VAA_DECIMALS) {
|
||||||
|
// return amount;
|
||||||
|
// }
|
||||||
|
// const decimalStringVaa = formatUnits(amount, MAX_VAA_DECIMALS);
|
||||||
|
// const normalizedAmount = parseUnits(decimalStringVaa, assetDecimals);
|
||||||
|
// const normalizedBigInt = BigInt(truncate(normalizedAmount.toString(), 0));
|
||||||
|
|
||||||
|
// return normalizedBigInt;
|
||||||
|
// };
|
||||||
|
|
||||||
|
// function truncate(str: string, maxDecimalDigits: number) {
|
||||||
|
// if (str.includes(".")) {
|
||||||
|
// const parts = str.split(".");
|
||||||
|
// return parts[0] + "." + parts[1].slice(0, maxDecimalDigits);
|
||||||
|
// }
|
||||||
|
// return str;
|
||||||
|
// }
|
Loading…
Reference in New Issue