commit
e5cff9c0cd
File diff suppressed because it is too large
Load Diff
|
@ -8,6 +8,15 @@
|
|||
"@material-ui/icons": "^4.11.2",
|
||||
"@material-ui/lab": "^4.0.0-alpha.60",
|
||||
"@metamask/detect-provider": "^1.2.0",
|
||||
"@near-wallet-selector/core": "^7.0.1",
|
||||
"@near-wallet-selector/default-wallets": "^7.0.1",
|
||||
"@near-wallet-selector/math-wallet": "^7.0.1",
|
||||
"@near-wallet-selector/meteor-wallet": "^7.0.1",
|
||||
"@near-wallet-selector/modal-ui": "^7.0.1",
|
||||
"@near-wallet-selector/my-near-wallet": "^7.0.1",
|
||||
"@near-wallet-selector/near-wallet": "^7.0.1",
|
||||
"@near-wallet-selector/nightly": "^7.0.1",
|
||||
"@near-wallet-selector/sender": "^7.0.1",
|
||||
"@project-serum/serum": "^0.13.60",
|
||||
"@randlabs/myalgo-connect": "^1.1.3",
|
||||
"@reduxjs/toolkit": "^1.6.1",
|
||||
|
@ -30,6 +39,7 @@
|
|||
"ethers": "^5.6.8",
|
||||
"js-base64": "^3.6.1",
|
||||
"luxon": "^2.3.1",
|
||||
"near-api-js": "^0.44.2",
|
||||
"notistack": "^1.0.10",
|
||||
"numeral": "^2.0.6",
|
||||
"react": "^17.0.2",
|
||||
|
@ -40,6 +50,7 @@
|
|||
"react-table": "^7.7.0",
|
||||
"recharts": "^2.1.9",
|
||||
"redux": "^3.7.2",
|
||||
"rxjs": "^7.5.6",
|
||||
"use-debounce": "^7.0.0"
|
||||
},
|
||||
"scripts": {
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
import {
|
||||
ChainId,
|
||||
CHAIN_ID_ALGORAND,
|
||||
CHAIN_ID_NEAR,
|
||||
CHAIN_ID_SOLANA,
|
||||
isEVMChain,
|
||||
isTerraChain,
|
||||
} from "@certusone/wormhole-sdk";
|
||||
import AlgorandWalletKey from "./AlgorandWalletKey";
|
||||
import EthereumSignerKey from "./EthereumSignerKey";
|
||||
import NearWalletKey from "./NearWalletKey";
|
||||
import SolanaWalletKey from "./SolanaWalletKey";
|
||||
import TerraWalletKey from "./TerraWalletKey";
|
||||
|
||||
|
@ -23,6 +25,9 @@ function KeyAndBalance({ chainId }: { chainId: ChainId }) {
|
|||
if (chainId === CHAIN_ID_ALGORAND) {
|
||||
return <AlgorandWalletKey />;
|
||||
}
|
||||
if (chainId === CHAIN_ID_NEAR) {
|
||||
return <NearWalletKey />;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
import { useNearContext } from "../contexts/NearWalletContext";
|
||||
import ToggleConnectedButton from "./ToggleConnectedButton";
|
||||
|
||||
const NearWalletKey = () => {
|
||||
const { connect, disconnect, accountId: activeAccount } = useNearContext();
|
||||
|
||||
return (
|
||||
<ToggleConnectedButton
|
||||
connect={connect}
|
||||
disconnect={disconnect}
|
||||
connected={!!activeAccount}
|
||||
pk={activeAccount || ""}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default NearWalletKey;
|
|
@ -3,6 +3,7 @@ import {
|
|||
CHAIN_ID_ACALA,
|
||||
CHAIN_ID_ALGORAND,
|
||||
CHAIN_ID_KARURA,
|
||||
CHAIN_ID_NEAR,
|
||||
CHAIN_ID_SOLANA,
|
||||
CHAIN_ID_TERRA2,
|
||||
getEmitterAddressAlgorand,
|
||||
|
@ -45,11 +46,13 @@ import { LCDClient } from "@terra-money/terra.js";
|
|||
import algosdk from "algosdk";
|
||||
import axios from "axios";
|
||||
import { ethers } from "ethers";
|
||||
import { base58 } from "ethers/lib/utils";
|
||||
import { useSnackbar } from "notistack";
|
||||
import { useCallback, useEffect, useMemo, useState } from "react";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { useHistory, useLocation } from "react-router";
|
||||
import { useEthereumProvider } from "../contexts/EthereumProviderContext";
|
||||
import { useNearContext } from "../contexts/NearWalletContext";
|
||||
import { useAcalaRelayerInfo } from "../hooks/useAcalaRelayerInfo";
|
||||
import useIsWalletReady from "../hooks/useIsWalletReady";
|
||||
import useRelayersAvailable, { Relayer } from "../hooks/useRelayersAvailable";
|
||||
|
@ -71,8 +74,14 @@ import {
|
|||
SOL_TOKEN_BRIDGE_ADDRESS,
|
||||
getTerraConfig,
|
||||
WORMHOLE_RPC_HOSTS,
|
||||
NEAR_TOKEN_BRIDGE_ACCOUNT,
|
||||
} from "../utils/consts";
|
||||
import { getSignedVAAWithRetry } from "../utils/getSignedVAAWithRetry";
|
||||
import {
|
||||
getEmitterAddressNear,
|
||||
makeNearAccount,
|
||||
parseSequenceFromLogNear,
|
||||
} from "../utils/near";
|
||||
import parseError from "../utils/parseError";
|
||||
import { queryExternalId } from "../utils/terra";
|
||||
import ButtonWithLoader from "./ButtonWithLoader";
|
||||
|
@ -183,6 +192,24 @@ async function evm(
|
|||
}
|
||||
}
|
||||
|
||||
async function near(tx: string, enqueueSnackbar: any, nearAccountId: string) {
|
||||
try {
|
||||
const account = await makeNearAccount(nearAccountId);
|
||||
const receipt = await account.connection.provider.txStatusReceipts(
|
||||
base58.decode(tx),
|
||||
nearAccountId
|
||||
);
|
||||
const sequence = parseSequenceFromLogNear(receipt);
|
||||
if (!sequence) {
|
||||
throw new Error("Sequence not found");
|
||||
}
|
||||
const emitterAddress = getEmitterAddressNear(NEAR_TOKEN_BRIDGE_ACCOUNT);
|
||||
return await fetchSignedVAA(CHAIN_ID_NEAR, emitterAddress, sequence);
|
||||
} catch (e) {
|
||||
return handleError(e, enqueueSnackbar);
|
||||
}
|
||||
}
|
||||
|
||||
async function solana(tx: string, enqueueSnackbar: any, nft: boolean) {
|
||||
try {
|
||||
const connection = new Connection(SOLANA_HOST, "confirmed");
|
||||
|
@ -369,6 +396,7 @@ export default function Recovery() {
|
|||
const [recoveryParsedVAA, setRecoveryParsedVAA] = useState<any>(null);
|
||||
const [isVAAPending, setIsVAAPending] = useState(false);
|
||||
const [terra2TokenId, setTerra2TokenId] = useState("");
|
||||
const { accountId: nearAccountId } = useNearContext();
|
||||
const { isReady, statusMessage } = useIsWalletReady(recoverySourceChain);
|
||||
const walletConnectError =
|
||||
isEVMChain(recoverySourceChain) && !isReady ? statusMessage : "";
|
||||
|
@ -515,6 +543,26 @@ export default function Recovery() {
|
|||
setIsVAAPending(isPending);
|
||||
}
|
||||
})();
|
||||
} else if (recoverySourceChain === CHAIN_ID_NEAR && nearAccountId) {
|
||||
setRecoverySourceTxError("");
|
||||
setRecoverySourceTxIsLoading(true);
|
||||
(async () => {
|
||||
const { vaa, isPending, error } = await near(
|
||||
recoverySourceTx,
|
||||
enqueueSnackbar,
|
||||
nearAccountId
|
||||
);
|
||||
if (!cancelled) {
|
||||
setRecoverySourceTxIsLoading(false);
|
||||
if (vaa) {
|
||||
setRecoverySignedVAA(vaa);
|
||||
}
|
||||
if (error) {
|
||||
setRecoverySourceTxError(error);
|
||||
}
|
||||
setIsVAAPending(isPending);
|
||||
}
|
||||
})();
|
||||
}
|
||||
return () => {
|
||||
cancelled = true;
|
||||
|
@ -527,6 +575,7 @@ export default function Recovery() {
|
|||
enqueueSnackbar,
|
||||
isNFT,
|
||||
isReady,
|
||||
nearAccountId,
|
||||
]);
|
||||
const handleTypeChange = useCallback((event) => {
|
||||
setRecoverySourceChain((prevChain) =>
|
||||
|
|
|
@ -18,6 +18,7 @@ import {
|
|||
isTerraChain,
|
||||
CHAIN_ID_TERRA2,
|
||||
} from "@certusone/wormhole-sdk";
|
||||
import { CHAIN_ID_NEAR } from "@certusone/wormhole-sdk/lib/esm";
|
||||
import { Button, makeStyles, Typography } from "@material-ui/core";
|
||||
import { Transaction } from "../store/transferSlice";
|
||||
import { CLUSTER, getExplorerName } from "../utils/consts";
|
||||
|
@ -124,6 +125,10 @@ export default function ShowTx({
|
|||
? `https://${CLUSTER === "testnet" ? "testnet." : ""}algoexplorer.io/tx/${
|
||||
tx?.id
|
||||
}`
|
||||
: chainId === CHAIN_ID_NEAR
|
||||
? `https://explorer.${
|
||||
CLUSTER === "testnet" ? "testnet." : ""
|
||||
}near.org/transactions/${tx?.id}`
|
||||
: undefined;
|
||||
const explorerName = getExplorerName(chainId);
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ import {
|
|||
isTerraChain,
|
||||
CHAIN_ID_TERRA2,
|
||||
TerraChainId,
|
||||
CHAIN_ID_NEAR,
|
||||
} from "@certusone/wormhole-sdk";
|
||||
import { Button, makeStyles, Tooltip, Typography } from "@material-ui/core";
|
||||
import { FileCopy, OpenInNew } from "@material-ui/icons";
|
||||
|
@ -188,6 +189,10 @@ export default function SmartAddress({
|
|||
? `https://${CLUSTER === "testnet" ? "testnet." : ""}algoexplorer.io/${
|
||||
isAsset ? "asset" : "address"
|
||||
}/${useableAddress}`
|
||||
: chainId === CHAIN_ID_NEAR
|
||||
? `https://explorer.${
|
||||
CLUSTER === "testnet" ? "testnet." : ""
|
||||
}near.org/accounts/${useableAddress}`
|
||||
: undefined;
|
||||
const explorerName = getExplorerName(chainId);
|
||||
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
import {
|
||||
ChainId,
|
||||
CHAIN_ID_ETH,
|
||||
CHAIN_ID_NEAR,
|
||||
CHAIN_ID_SOLANA,
|
||||
CHAIN_ID_TERRA2,
|
||||
isEVMChain,
|
||||
nativeToHexString,
|
||||
} from "@certusone/wormhole-sdk";
|
||||
|
@ -148,10 +150,21 @@ function SecondaryAssetInformation({
|
|||
<RegisterNowButtonCore
|
||||
originChain={originAssetInfo?.originChain || undefined}
|
||||
originAsset={
|
||||
nativeToHexString(
|
||||
originAssetInfo?.originAddress || undefined,
|
||||
originAssetInfo?.originChain || CHAIN_ID_SOLANA // this should exist
|
||||
) || undefined
|
||||
// use pre-image for these
|
||||
originAssetInfo?.originChain === CHAIN_ID_TERRA2 ||
|
||||
originAssetInfo?.originChain === CHAIN_ID_NEAR
|
||||
? originAssetInfo?.originAddress || undefined
|
||||
: nativeToHexString(
|
||||
originAssetInfo?.originAddress || undefined,
|
||||
originAssetInfo?.originChain || CHAIN_ID_SOLANA // this should exist
|
||||
) || undefined
|
||||
}
|
||||
forceAsset={
|
||||
// use pre-image for these
|
||||
originAssetInfo?.originChain === CHAIN_ID_TERRA2 ||
|
||||
originAssetInfo?.originChain === CHAIN_ID_NEAR
|
||||
? originAssetInfo?.originAddress || undefined
|
||||
: undefined
|
||||
}
|
||||
targetChain={chainId}
|
||||
/>
|
||||
|
|
|
@ -0,0 +1,108 @@
|
|||
import { CHAIN_ID_NEAR } from "@certusone/wormhole-sdk";
|
||||
import { formatUnits } from "ethers/lib/utils";
|
||||
import { useCallback } from "react";
|
||||
import { createParsedTokenAccount } from "../../hooks/useGetSourceParsedTokenAccounts";
|
||||
import useIsWalletReady from "../../hooks/useIsWalletReady";
|
||||
import { fetchSingleMetadata } from "../../hooks/useNearMetadata";
|
||||
import { DataWrapper } from "../../store/helpers";
|
||||
import { NFTParsedTokenAccount } from "../../store/nftSlice";
|
||||
import { ParsedTokenAccount } from "../../store/transferSlice";
|
||||
import { makeNearAccount } from "../../utils/near";
|
||||
import TokenPicker, { BasicAccountRender } from "./TokenPicker";
|
||||
|
||||
type NearTokenPickerProps = {
|
||||
value: ParsedTokenAccount | null;
|
||||
onChange: (newValue: ParsedTokenAccount | null) => void;
|
||||
tokenAccounts: DataWrapper<ParsedTokenAccount[]> | undefined;
|
||||
disabled: boolean;
|
||||
resetAccounts: (() => void) | undefined;
|
||||
};
|
||||
|
||||
const returnsFalse = () => false;
|
||||
|
||||
export default function NearTokenPicker(props: NearTokenPickerProps) {
|
||||
const { value, onChange, disabled, tokenAccounts, resetAccounts } = props;
|
||||
const { walletAddress } = useIsWalletReady(CHAIN_ID_NEAR);
|
||||
|
||||
const resetAccountWrapper = useCallback(() => {
|
||||
resetAccounts && resetAccounts();
|
||||
}, [resetAccounts]);
|
||||
const isLoading = tokenAccounts?.isFetching || false;
|
||||
|
||||
const onChangeWrapper = useCallback(
|
||||
async (account: NFTParsedTokenAccount | null) => {
|
||||
if (account === null) {
|
||||
onChange(null);
|
||||
return Promise.resolve();
|
||||
}
|
||||
onChange(account);
|
||||
return Promise.resolve();
|
||||
},
|
||||
[onChange]
|
||||
);
|
||||
|
||||
const lookupNearAddress = useCallback(
|
||||
(lookupAsset: string) => {
|
||||
if (!walletAddress) {
|
||||
return Promise.reject("Wallet not connected");
|
||||
}
|
||||
return makeNearAccount(walletAddress)
|
||||
.then((account) => {
|
||||
return fetchSingleMetadata(lookupAsset, account)
|
||||
.then((metadata) => {
|
||||
return account
|
||||
.viewFunction(lookupAsset, "ft_balance_of", {
|
||||
account_id: walletAddress,
|
||||
})
|
||||
.then((amount) => {
|
||||
return createParsedTokenAccount(
|
||||
walletAddress,
|
||||
lookupAsset,
|
||||
amount,
|
||||
metadata.decimals,
|
||||
parseFloat(formatUnits(amount, metadata.decimals)),
|
||||
formatUnits(amount, metadata.decimals).toString(),
|
||||
metadata.symbol,
|
||||
metadata.tokenName,
|
||||
undefined,
|
||||
false
|
||||
);
|
||||
})
|
||||
.catch(() => Promise.reject());
|
||||
})
|
||||
.catch(() => Promise.reject());
|
||||
})
|
||||
.catch(() => Promise.reject());
|
||||
},
|
||||
[walletAddress]
|
||||
);
|
||||
|
||||
const isSearchableAddress = useCallback(
|
||||
(address: string) => address.length > 0,
|
||||
[]
|
||||
);
|
||||
|
||||
const RenderComp = useCallback(
|
||||
({ account }: { account: NFTParsedTokenAccount }) => {
|
||||
return BasicAccountRender(account, returnsFalse, false);
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
return (
|
||||
<TokenPicker
|
||||
value={value}
|
||||
options={tokenAccounts?.data || []}
|
||||
RenderOption={RenderComp}
|
||||
onChange={onChangeWrapper}
|
||||
isValidAddress={isSearchableAddress}
|
||||
getAddress={lookupNearAddress}
|
||||
disabled={disabled}
|
||||
resetAccounts={resetAccountWrapper}
|
||||
error={""}
|
||||
showLoader={isLoading}
|
||||
nft={false}
|
||||
chainId={CHAIN_ID_NEAR}
|
||||
/>
|
||||
);
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
//import Autocomplete from '@material-ui/lab/Autocomplete';
|
||||
import {
|
||||
CHAIN_ID_ALGORAND,
|
||||
CHAIN_ID_NEAR,
|
||||
CHAIN_ID_SOLANA,
|
||||
isEVMChain,
|
||||
isTerraChain,
|
||||
|
@ -27,6 +28,7 @@ import {
|
|||
} from "../../store/transferSlice";
|
||||
import AlgoTokenPicker from "./AlgoTokenPicker";
|
||||
import EvmTokenPicker from "./EvmTokenPicker";
|
||||
import NearTokenPicker from "./NearTokenPicker";
|
||||
import RefreshButtonWrapper from "./RefreshButtonWrapper";
|
||||
import SolanaTokenPicker from "./SolanaTokenPicker";
|
||||
import TerraTokenPicker from "./TerraTokenPicker";
|
||||
|
@ -125,6 +127,14 @@ export const TokenSelector = (props: TokenSelectorProps) => {
|
|||
resetAccounts={maps?.resetAccounts}
|
||||
tokenAccounts={maps?.tokenAccounts}
|
||||
/>
|
||||
) : lookupChain === CHAIN_ID_NEAR ? (
|
||||
<NearTokenPicker
|
||||
value={sourceParsedTokenAccount || null}
|
||||
disabled={disabled}
|
||||
onChange={handleOnChange}
|
||||
resetAccounts={maps?.resetAccounts}
|
||||
tokenAccounts={maps?.tokenAccounts}
|
||||
/>
|
||||
) : (
|
||||
<TextField
|
||||
variant="outlined"
|
||||
|
|
|
@ -17,6 +17,7 @@ import {
|
|||
} from "../../store/selectors";
|
||||
import {
|
||||
ChainId,
|
||||
CHAIN_ID_NEAR,
|
||||
CHAIN_ID_TERRA2,
|
||||
hexToNativeAssetString,
|
||||
} from "@certusone/wormhole-sdk";
|
||||
|
@ -25,10 +26,12 @@ export function RegisterNowButtonCore({
|
|||
originChain,
|
||||
originAsset,
|
||||
targetChain,
|
||||
forceAsset,
|
||||
}: {
|
||||
originChain: ChainId | undefined;
|
||||
originAsset: string | undefined;
|
||||
targetChain: ChainId;
|
||||
forceAsset?: string;
|
||||
}) {
|
||||
const dispatch = useDispatch();
|
||||
const history = useHistory();
|
||||
|
@ -38,8 +41,8 @@ export function RegisterNowButtonCore({
|
|||
const canSwitch = originChain && originAsset && !signedVAAHex;
|
||||
const handleClick = useCallback(() => {
|
||||
const nativeAsset = originChain
|
||||
? originChain === CHAIN_ID_TERRA2
|
||||
? sourceAsset // use the preimage address for terra2
|
||||
? originChain === CHAIN_ID_TERRA2 || originChain === CHAIN_ID_NEAR
|
||||
? sourceAsset || forceAsset // use the preimage address for terra2
|
||||
: hexToNativeAssetString(originAsset, originChain)
|
||||
: undefined;
|
||||
if (originChain && originAsset && nativeAsset && canSwitch) {
|
||||
|
@ -57,6 +60,7 @@ export function RegisterNowButtonCore({
|
|||
targetChain,
|
||||
history,
|
||||
sourceAsset,
|
||||
forceAsset,
|
||||
]);
|
||||
if (!canSwitch) return null;
|
||||
return (
|
||||
|
|
|
@ -3,9 +3,11 @@ import {
|
|||
hexToNativeString,
|
||||
isEVMChain,
|
||||
} from "@certusone/wormhole-sdk";
|
||||
import { CHAIN_ID_NEAR } from "@certusone/wormhole-sdk/lib/esm";
|
||||
import { makeStyles, Typography } from "@material-ui/core";
|
||||
import { useCallback, useMemo } from "react";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { useNearContext } from "../../contexts/NearWalletContext";
|
||||
import useGetTargetParsedTokenAccounts from "../../hooks/useGetTargetParsedTokenAccounts";
|
||||
import useIsWalletReady from "../../hooks/useIsWalletReady";
|
||||
import useSyncTargetAddress from "../../hooks/useSyncTargetAddress";
|
||||
|
@ -24,6 +26,7 @@ import {
|
|||
} from "../../store/selectors";
|
||||
import { incrementStep, setTargetChain } from "../../store/transferSlice";
|
||||
import { CHAINS, CLUSTER } from "../../utils/consts";
|
||||
import { getEmitterAddressNear } from "../../utils/near";
|
||||
import ButtonWithLoader from "../ButtonWithLoader";
|
||||
import ChainSelect from "../ChainSelect";
|
||||
import FeeMethodSelector from "../FeeMethodSelector";
|
||||
|
@ -48,6 +51,8 @@ const useStyles = makeStyles((theme) => ({
|
|||
}));
|
||||
|
||||
export const useTargetInfo = () => {
|
||||
const { accountId: nearAccountId } = useNearContext();
|
||||
|
||||
const targetChain = useSelector(selectTransferTargetChain);
|
||||
const targetAddressHex = useSelector(selectTransferTargetAddressHex);
|
||||
const targetAsset = useSelector(selectTransferTargetAsset);
|
||||
|
@ -58,7 +63,14 @@ export const useTargetInfo = () => {
|
|||
const symbol = targetParsedTokenAccount?.symbol;
|
||||
const logo = targetParsedTokenAccount?.logo;
|
||||
const readableTargetAddress =
|
||||
hexToNativeString(targetAddressHex, targetChain) || "";
|
||||
targetChain === CHAIN_ID_NEAR
|
||||
? // Near uses a hashed address, which isn't very readable - check that the hash matches and show them their account id
|
||||
nearAccountId &&
|
||||
// this just happens to be the same hashing mechanism as emitters
|
||||
getEmitterAddressNear(nearAccountId) === targetAddressHex
|
||||
? nearAccountId
|
||||
: targetAddressHex || ""
|
||||
: hexToNativeString(targetAddressHex, targetChain) || "";
|
||||
return useMemo(
|
||||
() => ({
|
||||
targetChain,
|
||||
|
|
|
@ -0,0 +1,200 @@
|
|||
import {
|
||||
AccountState,
|
||||
Network,
|
||||
setupWalletSelector,
|
||||
Wallet,
|
||||
WalletSelector,
|
||||
WalletSelectorState,
|
||||
} from "@near-wallet-selector/core";
|
||||
import { setupDefaultWallets } from "@near-wallet-selector/default-wallets";
|
||||
import { setupMathWallet } from "@near-wallet-selector/math-wallet";
|
||||
import { setupMeteorWallet } from "@near-wallet-selector/meteor-wallet";
|
||||
import {
|
||||
setupModal,
|
||||
WalletSelectorModal,
|
||||
} from "@near-wallet-selector/modal-ui";
|
||||
import "@near-wallet-selector/modal-ui/styles.css";
|
||||
import { setupMyNearWallet } from "@near-wallet-selector/my-near-wallet";
|
||||
import { setupNearWallet } from "@near-wallet-selector/near-wallet";
|
||||
import { setupNightly } from "@near-wallet-selector/nightly";
|
||||
import { setupSender } from "@near-wallet-selector/sender";
|
||||
import { KeyPair, WalletConnection } from "near-api-js";
|
||||
import React, {
|
||||
ReactChildren,
|
||||
useCallback,
|
||||
useContext,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useState,
|
||||
} from "react";
|
||||
import { distinctUntilChanged, map, Subscription } from "rxjs";
|
||||
import { CLUSTER, NEAR_TOKEN_BRIDGE_ACCOUNT } from "../utils/consts";
|
||||
|
||||
// monkeypatch to allow for full permissions
|
||||
// https://github.com/near/near-api-js/blob/96785cb3db14be593b6e6d013b6870ba56a212a8/packages/near-api-js/src/wallet-account.ts#L177
|
||||
const LOGIN_WALLET_URL_SUFFIX = "/login/";
|
||||
const PENDING_ACCESS_KEY_PREFIX = "pending_key"; // browser storage key for a pending access key (i.e. key has been generated but we are not sure it was added yet)
|
||||
WalletConnection.prototype.requestSignIn = async function requestSignIn({
|
||||
contractId,
|
||||
methodNames,
|
||||
successUrl,
|
||||
failureUrl,
|
||||
}: any) {
|
||||
const currentUrl = new URL(window.location.href);
|
||||
const newUrl = new URL(this._walletBaseUrl + LOGIN_WALLET_URL_SUFFIX);
|
||||
newUrl.searchParams.set("success_url", successUrl || currentUrl.href);
|
||||
newUrl.searchParams.set("failure_url", failureUrl || currentUrl.href);
|
||||
if (contractId) {
|
||||
/* Throws exception if contract account does not exist */
|
||||
const contractAccount = await this._near.account(contractId);
|
||||
await contractAccount.state();
|
||||
|
||||
// THIS IS THE EDIT
|
||||
// newUrl.searchParams.set("contract_id", contractId);
|
||||
const accessKey = KeyPair.fromRandom("ed25519");
|
||||
newUrl.searchParams.set("public_key", accessKey.getPublicKey().toString());
|
||||
await this._keyStore.setKey(
|
||||
this._networkId,
|
||||
PENDING_ACCESS_KEY_PREFIX + accessKey.getPublicKey(),
|
||||
accessKey
|
||||
);
|
||||
}
|
||||
|
||||
if (methodNames) {
|
||||
methodNames.forEach((methodName: any) => {
|
||||
newUrl.searchParams.append("methodNames", methodName);
|
||||
});
|
||||
}
|
||||
|
||||
window.location.assign(newUrl.toString());
|
||||
};
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
selector: WalletSelector;
|
||||
modal: WalletSelectorModal;
|
||||
}
|
||||
}
|
||||
|
||||
interface INearContext {
|
||||
connect(): void;
|
||||
disconnect(): void;
|
||||
accounts: AccountState[];
|
||||
accountId: string | null;
|
||||
wallet: Wallet | null;
|
||||
}
|
||||
|
||||
const NearContext = React.createContext<INearContext>({
|
||||
connect: () => {},
|
||||
disconnect: () => {},
|
||||
accounts: [],
|
||||
accountId: null,
|
||||
wallet: null,
|
||||
});
|
||||
|
||||
const NearDevnet: Network = {
|
||||
networkId: "sandbox",
|
||||
nodeUrl: "http://localhost:3030",
|
||||
helperUrl: "",
|
||||
explorerUrl: "",
|
||||
indexerUrl: "",
|
||||
};
|
||||
|
||||
export const NearContextProvider = ({
|
||||
children,
|
||||
}: {
|
||||
children: ReactChildren;
|
||||
}) => {
|
||||
const [selector, setSelector] = useState<WalletSelector | null>(null);
|
||||
const [modal, setModal] = useState<WalletSelectorModal | null>(null);
|
||||
const [accounts, setAccounts] = useState<AccountState[]>([]);
|
||||
const [accountId, setAccountId] = useState<string | null>(null);
|
||||
const [wallet, setWallet] = useState<Wallet | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
let subscription: Subscription;
|
||||
(async () => {
|
||||
const selector = await setupWalletSelector({
|
||||
network:
|
||||
CLUSTER === "mainnet"
|
||||
? "mainnet"
|
||||
: "testnet"
|
||||
? "testnet"
|
||||
: NearDevnet,
|
||||
modules: [
|
||||
...(await setupDefaultWallets()),
|
||||
setupNearWallet(),
|
||||
setupMyNearWallet(),
|
||||
setupSender(),
|
||||
setupMathWallet(),
|
||||
setupNightly(),
|
||||
setupMeteorWallet(),
|
||||
],
|
||||
debug: true,
|
||||
});
|
||||
const modal = setupModal(selector, {
|
||||
contractId: NEAR_TOKEN_BRIDGE_ACCOUNT || "",
|
||||
});
|
||||
const accounts = selector.store.getState().accounts;
|
||||
subscription = selector.store.observable
|
||||
.pipe(
|
||||
map((state: WalletSelectorState) => state.accounts),
|
||||
distinctUntilChanged()
|
||||
)
|
||||
.subscribe((nextAccounts: AccountState[]) => {
|
||||
if (!cancelled) {
|
||||
setAccounts(nextAccounts);
|
||||
}
|
||||
});
|
||||
if (!cancelled) {
|
||||
setSelector(selector);
|
||||
setModal(modal);
|
||||
setAccounts(accounts);
|
||||
}
|
||||
})();
|
||||
return () => {
|
||||
subscription?.unsubscribe();
|
||||
cancelled = true;
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const accountId =
|
||||
accounts.find((account) => account.active)?.accountId || null;
|
||||
setAccountId(accountId);
|
||||
(async () => {
|
||||
setWallet((await selector?.wallet()) || null);
|
||||
})();
|
||||
}, [selector, accounts]);
|
||||
|
||||
const connect = useCallback(() => {
|
||||
modal?.show();
|
||||
}, [modal]);
|
||||
|
||||
const disconnect = useCallback(() => {
|
||||
modal?.hide();
|
||||
selector
|
||||
?.wallet()
|
||||
.then((wallet) =>
|
||||
wallet.signOut().catch((error) => console.error(error))
|
||||
);
|
||||
}, [selector, modal]);
|
||||
|
||||
const value = useMemo(
|
||||
() => ({
|
||||
connect,
|
||||
disconnect,
|
||||
accounts,
|
||||
accountId,
|
||||
wallet,
|
||||
}),
|
||||
[connect, disconnect, accounts, accountId, wallet]
|
||||
);
|
||||
|
||||
return <NearContext.Provider value={value}>{children}</NearContext.Provider>;
|
||||
};
|
||||
|
||||
export function useNearContext() {
|
||||
return useContext(NearContext);
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
import {
|
||||
ChainId,
|
||||
CHAIN_ID_ALGORAND,
|
||||
CHAIN_ID_NEAR,
|
||||
CHAIN_ID_SOLANA,
|
||||
getOriginalAssetAlgorand,
|
||||
getOriginalAssetCosmWasm,
|
||||
|
@ -21,6 +22,7 @@ import { Algodv2 } from "algosdk";
|
|||
import { useEffect } from "react";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { useEthereumProvider } from "../contexts/EthereumProviderContext";
|
||||
import { useNearContext } from "../contexts/NearWalletContext";
|
||||
import { setSourceWormholeWrappedInfo as setNFTSourceWormholeWrappedInfo } from "../store/nftSlice";
|
||||
import {
|
||||
selectNFTIsRecovery,
|
||||
|
@ -38,10 +40,13 @@ import {
|
|||
getNFTBridgeAddressForChain,
|
||||
getTerraConfig,
|
||||
getTokenBridgeAddressForChain,
|
||||
NATIVE_NEAR_PLACEHOLDER,
|
||||
NEAR_TOKEN_BRIDGE_ACCOUNT,
|
||||
SOLANA_HOST,
|
||||
SOL_NFT_BRIDGE_ADDRESS,
|
||||
SOL_TOKEN_BRIDGE_ADDRESS,
|
||||
} from "../utils/consts";
|
||||
import { getOriginalAssetNear, makeNearAccount } from "../utils/near";
|
||||
|
||||
export interface StateSafeWormholeWrappedInfo {
|
||||
isWrapped: boolean;
|
||||
|
@ -76,6 +81,7 @@ function useCheckIfWormholeWrapped(nft?: boolean) {
|
|||
? setNFTSourceWormholeWrappedInfo
|
||||
: setTransferSourceWormholeWrappedInfo;
|
||||
const { provider } = useEthereumProvider();
|
||||
const { accountId: nearAccountId } = useNearContext();
|
||||
const isRecovery = useSelector(
|
||||
nft ? selectNFTIsRecovery : selectTransferIsRecovery
|
||||
);
|
||||
|
@ -158,6 +164,25 @@ function useCheckIfWormholeWrapped(nft?: boolean) {
|
|||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
if (
|
||||
sourceChain === CHAIN_ID_NEAR &&
|
||||
nearAccountId &&
|
||||
sourceAsset !== undefined
|
||||
) {
|
||||
try {
|
||||
const account = await makeNearAccount(nearAccountId);
|
||||
const wrappedInfo = makeStateSafe(
|
||||
await getOriginalAssetNear(
|
||||
account,
|
||||
NEAR_TOKEN_BRIDGE_ACCOUNT,
|
||||
sourceAsset === NATIVE_NEAR_PLACEHOLDER ? "" : sourceAsset
|
||||
)
|
||||
);
|
||||
if (!cancelled) {
|
||||
dispatch(setSourceWormholeWrappedInfo(wrappedInfo));
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
})();
|
||||
return () => {
|
||||
cancelled = true;
|
||||
|
@ -171,6 +196,7 @@ function useCheckIfWormholeWrapped(nft?: boolean) {
|
|||
nft,
|
||||
setSourceWormholeWrappedInfo,
|
||||
tokenId,
|
||||
nearAccountId,
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
import {
|
||||
ChainId,
|
||||
CHAIN_ID_ALGORAND,
|
||||
CHAIN_ID_NEAR,
|
||||
CHAIN_ID_SOLANA,
|
||||
CHAIN_ID_TERRA2,
|
||||
getForeignAssetAlgorand,
|
||||
getForeignAssetEth,
|
||||
getForeignAssetSolana,
|
||||
|
@ -25,9 +27,19 @@ import {
|
|||
SOLANA_HOST,
|
||||
SOL_TOKEN_BRIDGE_ADDRESS,
|
||||
getTerraConfig,
|
||||
NEAR_TOKEN_BRIDGE_ACCOUNT,
|
||||
NATIVE_NEAR_PLACEHOLDER,
|
||||
NATIVE_NEAR_WH_ADDRESS,
|
||||
} from "../utils/consts";
|
||||
import useIsWalletReady from "./useIsWalletReady";
|
||||
import { Algodv2 } from "algosdk";
|
||||
import {
|
||||
getEmitterAddressNear,
|
||||
getForeignAssetNear,
|
||||
makeNearAccount,
|
||||
} from "../utils/near";
|
||||
import { useNearContext } from "../contexts/NearWalletContext";
|
||||
import { buildTokenId } from "@certusone/wormhole-sdk/lib/esm/cosmwasm/address";
|
||||
|
||||
export type ForeignAssetInfo = {
|
||||
doesExist: boolean;
|
||||
|
@ -43,6 +55,7 @@ function useFetchForeignAsset(
|
|||
const { isReady } = useIsWalletReady(foreignChain, false);
|
||||
const correctEvmNetwork = getEvmChainId(foreignChain);
|
||||
const hasCorrectEvmNetwork = evmChainId === correctEvmNetwork;
|
||||
const { accountId: nearAccountId } = useNearContext();
|
||||
|
||||
const [assetAddress, setAssetAddress] = useState<string | null>(null);
|
||||
const [doesExist, setDoesExist] = useState<boolean | null>(null);
|
||||
|
@ -50,6 +63,15 @@ function useFetchForeignAsset(
|
|||
const [isLoading, setIsLoading] = useState(false);
|
||||
const originAssetHex = useMemo(() => {
|
||||
try {
|
||||
if (originChain === CHAIN_ID_TERRA2) {
|
||||
return buildTokenId(originAsset);
|
||||
}
|
||||
if (originChain === CHAIN_ID_NEAR) {
|
||||
if (originAsset === NATIVE_NEAR_PLACEHOLDER) {
|
||||
return NATIVE_NEAR_WH_ADDRESS;
|
||||
}
|
||||
return getEmitterAddressNear(originAsset);
|
||||
}
|
||||
return nativeToHexString(originAsset, originChain);
|
||||
} catch (e) {
|
||||
return null;
|
||||
|
@ -148,6 +170,19 @@ function useFetchForeignAsset(
|
|||
originAssetHex
|
||||
);
|
||||
}
|
||||
: foreignChain === CHAIN_ID_NEAR && nearAccountId
|
||||
? () => {
|
||||
return makeNearAccount(nearAccountId)
|
||||
.then((account) =>
|
||||
getForeignAssetNear(
|
||||
account,
|
||||
NEAR_TOKEN_BRIDGE_ACCOUNT,
|
||||
originChain,
|
||||
originAssetHex
|
||||
)
|
||||
)
|
||||
.catch(() => Promise.reject("Failed to make Near account"));
|
||||
}
|
||||
: () => Promise.resolve(null);
|
||||
|
||||
getterFunc()
|
||||
|
@ -193,6 +228,7 @@ function useFetchForeignAsset(
|
|||
provider,
|
||||
setArgs,
|
||||
argsEqual,
|
||||
nearAccountId,
|
||||
]);
|
||||
|
||||
const compoundError = useMemo(() => {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import {
|
||||
ChainId,
|
||||
CHAIN_ID_ALGORAND,
|
||||
CHAIN_ID_NEAR,
|
||||
CHAIN_ID_SOLANA,
|
||||
CHAIN_ID_TERRA2,
|
||||
getForeignAssetAlgorand,
|
||||
|
@ -25,6 +26,7 @@ import { ethers } from "ethers";
|
|||
import { useCallback, useEffect, useState } from "react";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { useEthereumProvider } from "../contexts/EthereumProviderContext";
|
||||
import { useNearContext } from "../contexts/NearWalletContext";
|
||||
import {
|
||||
errorDataWrapper,
|
||||
fetchDataWrapper,
|
||||
|
@ -53,7 +55,15 @@ import {
|
|||
SOL_NFT_BRIDGE_ADDRESS,
|
||||
SOL_TOKEN_BRIDGE_ADDRESS,
|
||||
getTerraConfig,
|
||||
NEAR_TOKEN_BRIDGE_ACCOUNT,
|
||||
NATIVE_NEAR_WH_ADDRESS,
|
||||
NATIVE_NEAR_PLACEHOLDER,
|
||||
} from "../utils/consts";
|
||||
import {
|
||||
getForeignAssetNear,
|
||||
lookupHash,
|
||||
makeNearAccount,
|
||||
} from "../utils/near";
|
||||
import { queryExternalId } from "../utils/terra";
|
||||
|
||||
function useFetchTargetAsset(nft?: boolean) {
|
||||
|
@ -78,6 +88,7 @@ function useFetchTargetAsset(nft?: boolean) {
|
|||
const { provider, chainId: evmChainId } = useEthereumProvider();
|
||||
const correctEvmNetwork = getEvmChainId(targetChain);
|
||||
const hasCorrectEvmNetwork = evmChainId === correctEvmNetwork;
|
||||
const { accountId: nearAccountId } = useNearContext();
|
||||
const [lastSuccessfulArgs, setLastSuccessfulArgs] = useState<{
|
||||
isSourceAssetWormholeWrapped: boolean | undefined;
|
||||
originChain: ChainId | undefined;
|
||||
|
@ -134,6 +145,34 @@ function useFetchTargetAsset(nft?: boolean) {
|
|||
)
|
||||
);
|
||||
}
|
||||
} else if (originChain === CHAIN_ID_NEAR && nearAccountId) {
|
||||
if (originAsset === NATIVE_NEAR_WH_ADDRESS) {
|
||||
dispatch(
|
||||
setTargetAsset(
|
||||
receiveDataWrapper({
|
||||
doesExist: true,
|
||||
address: NATIVE_NEAR_PLACEHOLDER,
|
||||
})
|
||||
)
|
||||
);
|
||||
} else {
|
||||
const account = await makeNearAccount(nearAccountId);
|
||||
const tokenAccount = await lookupHash(
|
||||
account,
|
||||
NEAR_TOKEN_BRIDGE_ACCOUNT,
|
||||
originAsset || ""
|
||||
);
|
||||
if (!cancelled) {
|
||||
dispatch(
|
||||
setTargetAsset(
|
||||
receiveDataWrapper({
|
||||
doesExist: true,
|
||||
address: tokenAccount[1] || null,
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!cancelled) {
|
||||
dispatch(
|
||||
|
@ -302,6 +341,45 @@ function useFetchTargetAsset(nft?: boolean) {
|
|||
}
|
||||
}
|
||||
}
|
||||
if (
|
||||
targetChain === CHAIN_ID_NEAR &&
|
||||
originChain &&
|
||||
originAsset &&
|
||||
nearAccountId
|
||||
) {
|
||||
dispatch(setTargetAsset(fetchDataWrapper()));
|
||||
try {
|
||||
const account = await makeNearAccount(nearAccountId);
|
||||
const asset = await getForeignAssetNear(
|
||||
account,
|
||||
NEAR_TOKEN_BRIDGE_ACCOUNT,
|
||||
originChain,
|
||||
originAsset
|
||||
);
|
||||
if (!cancelled) {
|
||||
dispatch(
|
||||
setTargetAsset(
|
||||
receiveDataWrapper({
|
||||
doesExist: !!asset,
|
||||
address: asset === null ? asset : asset.toString(),
|
||||
})
|
||||
)
|
||||
);
|
||||
setArgs();
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
if (!cancelled) {
|
||||
dispatch(
|
||||
setTargetAsset(
|
||||
errorDataWrapper(
|
||||
"Unable to determine existence of wrapped asset"
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
})();
|
||||
return () => {
|
||||
cancelled = true;
|
||||
|
@ -319,6 +397,7 @@ function useFetchTargetAsset(nft?: boolean) {
|
|||
hasCorrectEvmNetwork,
|
||||
argsMatchLastSuccess,
|
||||
setArgs,
|
||||
nearAccountId,
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import {
|
||||
CHAIN_ID_ALGORAND,
|
||||
CHAIN_ID_NEAR,
|
||||
CHAIN_ID_SOLANA,
|
||||
getIsTransferCompletedAlgorand,
|
||||
getIsTransferCompletedEth,
|
||||
|
@ -14,6 +15,7 @@ import algosdk from "algosdk";
|
|||
import { useEffect, useState } from "react";
|
||||
import { useSelector } from "react-redux";
|
||||
import { useEthereumProvider } from "../contexts/EthereumProviderContext";
|
||||
import { useNearContext } from "../contexts/NearWalletContext";
|
||||
import {
|
||||
selectTransferIsRecovery,
|
||||
selectTransferTargetAddressHex,
|
||||
|
@ -27,7 +29,9 @@ import {
|
|||
SOLANA_HOST,
|
||||
getTerraGasPricesUrl,
|
||||
getTerraConfig,
|
||||
NEAR_TOKEN_BRIDGE_ACCOUNT,
|
||||
} from "../utils/consts";
|
||||
import { getIsTransferCompletedNear, makeNearAccount } from "../utils/near";
|
||||
import useIsWalletReady from "./useIsWalletReady";
|
||||
import useTransferSignedVAA from "./useTransferSignedVAA";
|
||||
|
||||
|
@ -50,6 +54,7 @@ export default function useGetIsTransferCompleted(
|
|||
|
||||
const { isReady } = useIsWalletReady(targetChain, false);
|
||||
const { provider, chainId: evmChainId } = useEthereumProvider();
|
||||
const { accountId: nearAccountId } = useNearContext();
|
||||
const signedVAA = useTransferSignedVAA();
|
||||
|
||||
const hasCorrectEvmNetwork = evmChainId === getEvmChainId(targetChain);
|
||||
|
@ -160,6 +165,24 @@ export default function useGetIsTransferCompleted(
|
|||
setIsLoading(false);
|
||||
}
|
||||
})();
|
||||
} else if (targetChain === CHAIN_ID_NEAR && nearAccountId) {
|
||||
setIsLoading(true);
|
||||
(async () => {
|
||||
try {
|
||||
const account = await makeNearAccount(nearAccountId);
|
||||
transferCompleted = await getIsTransferCompletedNear(
|
||||
account,
|
||||
NEAR_TOKEN_BRIDGE_ACCOUNT,
|
||||
signedVAA
|
||||
);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
if (!cancelled) {
|
||||
setIsTransferCompleted(transferCompleted);
|
||||
setIsLoading(false);
|
||||
}
|
||||
})();
|
||||
}
|
||||
}
|
||||
return () => {
|
||||
|
@ -174,6 +197,7 @@ export default function useGetIsTransferCompleted(
|
|||
isReady,
|
||||
provider,
|
||||
pollState,
|
||||
nearAccountId,
|
||||
]);
|
||||
|
||||
return { isTransferCompletedLoading: isLoading, isTransferCompleted };
|
||||
|
|
|
@ -11,6 +11,7 @@ import {
|
|||
CHAIN_ID_FANTOM,
|
||||
CHAIN_ID_KARURA,
|
||||
CHAIN_ID_KLAYTN,
|
||||
CHAIN_ID_NEAR,
|
||||
CHAIN_ID_NEON,
|
||||
CHAIN_ID_OASIS,
|
||||
CHAIN_ID_POLYGON,
|
||||
|
@ -40,6 +41,7 @@ import {
|
|||
Provider,
|
||||
useEthereumProvider,
|
||||
} from "../contexts/EthereumProviderContext";
|
||||
import { useNearContext } from "../contexts/NearWalletContext";
|
||||
import { useSolanaWallet } from "../contexts/SolanaWalletContext";
|
||||
import acalaIcon from "../icons/acala.svg";
|
||||
import auroraIcon from "../icons/aurora.svg";
|
||||
|
@ -85,11 +87,16 @@ import {
|
|||
ACA_DECIMALS,
|
||||
ALGORAND_HOST,
|
||||
ALGO_DECIMALS,
|
||||
COVALENT_GET_TOKENS_URL,
|
||||
BLOCKSCOUT_GET_TOKENS_URL,
|
||||
CELO_ADDRESS,
|
||||
CELO_DECIMALS,
|
||||
COVALENT_GET_TOKENS_URL,
|
||||
getDefaultNativeCurrencyAddressEvm,
|
||||
KAR_ADDRESS,
|
||||
KAR_DECIMALS,
|
||||
logoOverrides,
|
||||
NATIVE_NEAR_DECIMALS,
|
||||
NATIVE_NEAR_PLACEHOLDER,
|
||||
ROPSTEN_WETH_ADDRESS,
|
||||
ROPSTEN_WETH_DECIMALS,
|
||||
SOLANA_HOST,
|
||||
|
@ -97,8 +104,6 @@ import {
|
|||
WAVAX_DECIMALS,
|
||||
WBNB_ADDRESS,
|
||||
WBNB_DECIMALS,
|
||||
CELO_ADDRESS,
|
||||
CELO_DECIMALS,
|
||||
WETH_ADDRESS,
|
||||
WETH_AURORA_ADDRESS,
|
||||
WETH_AURORA_DECIMALS,
|
||||
|
@ -110,11 +115,11 @@ import {
|
|||
WMATIC_ADDRESS,
|
||||
WMATIC_DECIMALS,
|
||||
WNEON_ADDRESS,
|
||||
WNEON_DECIMALS,
|
||||
WNEON_DECIMALS,
|
||||
WROSE_ADDRESS,
|
||||
WROSE_DECIMALS,
|
||||
getDefaultNativeCurrencyAddressEvm,
|
||||
} from "../utils/consts";
|
||||
import { makeNearAccount } from "../utils/near";
|
||||
import {
|
||||
ExtractedMintInfo,
|
||||
extractMintInfo,
|
||||
|
@ -811,6 +816,44 @@ const getAlgorandParsedTokenAccounts = async (
|
|||
}
|
||||
};
|
||||
|
||||
const getNearParsedTokenAccounts = async (
|
||||
walletAddress: string,
|
||||
dispatch: Dispatch,
|
||||
nft: boolean
|
||||
) => {
|
||||
dispatch(
|
||||
nft ? fetchSourceParsedTokenAccountsNFT() : fetchSourceParsedTokenAccounts()
|
||||
);
|
||||
try {
|
||||
if (nft) {
|
||||
dispatch(receiveSourceParsedTokenAccountsNFT([]));
|
||||
return;
|
||||
}
|
||||
const account = await makeNearAccount(walletAddress);
|
||||
const balance = await account.getAccountBalance();
|
||||
const nativeNear = createParsedTokenAccount(
|
||||
walletAddress, //publicKey
|
||||
NATIVE_NEAR_PLACEHOLDER, //the app doesn't like when this isn't truthy
|
||||
balance.available, //amount
|
||||
NATIVE_NEAR_DECIMALS,
|
||||
parseFloat(formatUnits(balance.available, NATIVE_NEAR_DECIMALS)),
|
||||
formatUnits(balance.available, NATIVE_NEAR_DECIMALS).toString(),
|
||||
"NEAR",
|
||||
"Near",
|
||||
undefined, //TODO logo
|
||||
true
|
||||
);
|
||||
dispatch(receiveSourceParsedTokenAccounts([nativeNear]));
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
dispatch(
|
||||
nft
|
||||
? errorSourceParsedTokenAccountsNFT("Failed to load NFT metadata")
|
||||
: errorSourceParsedTokenAccounts("Failed to load token metadata.")
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Fetches the balance of an asset for the connected wallet
|
||||
* This should handle every type of chain in the future, but only reads the Transfer state.
|
||||
|
@ -831,6 +874,7 @@ function useGetAvailableTokens(nft: boolean = false) {
|
|||
const solPK = solanaWallet?.publicKey;
|
||||
const { provider, signerAddress } = useEthereumProvider();
|
||||
const { accounts: algoAccounts } = useAlgorandContext();
|
||||
const { accountId: nearAccountId } = useNearContext();
|
||||
|
||||
const [covalent, setCovalent] = useState<any>(undefined);
|
||||
const [covalentLoading, setCovalentLoading] = useState(false);
|
||||
|
@ -862,6 +906,8 @@ function useGetAvailableTokens(nft: boolean = false) {
|
|||
? solPK?.toString()
|
||||
: lookupChain === CHAIN_ID_ALGORAND
|
||||
? algoAccounts[0]?.address
|
||||
: lookupChain === CHAIN_ID_NEAR
|
||||
? nearAccountId || undefined
|
||||
: undefined;
|
||||
|
||||
const resetSourceAccounts = useCallback(() => {
|
||||
|
@ -1376,7 +1422,7 @@ function useGetAvailableTokens(nft: boolean = false) {
|
|||
cancelled = true;
|
||||
};
|
||||
}, [lookupChain, provider, signerAddress, nft, ethNativeAccount]);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
if (
|
||||
|
@ -1513,6 +1559,18 @@ function useGetAvailableTokens(nft: boolean = false) {
|
|||
|
||||
return () => {};
|
||||
}, [dispatch, lookupChain, currentSourceWalletAddress, tokenAccounts, nft]);
|
||||
//Near accounts load
|
||||
useEffect(() => {
|
||||
if (lookupChain === CHAIN_ID_NEAR && currentSourceWalletAddress) {
|
||||
if (
|
||||
!(tokenAccounts.data || tokenAccounts.isFetching || tokenAccounts.error)
|
||||
) {
|
||||
getNearParsedTokenAccounts(currentSourceWalletAddress, dispatch, nft);
|
||||
}
|
||||
}
|
||||
|
||||
return () => {};
|
||||
}, [dispatch, lookupChain, currentSourceWalletAddress, tokenAccounts, nft]);
|
||||
|
||||
const ethAccounts = useMemo(() => {
|
||||
const output = { ...tokenAccounts };
|
||||
|
@ -1555,6 +1613,11 @@ function useGetAvailableTokens(nft: boolean = false) {
|
|||
resetAccounts: resetSourceAccounts,
|
||||
}
|
||||
: lookupChain === CHAIN_ID_ALGORAND
|
||||
? {
|
||||
tokenAccounts,
|
||||
resetAccounts: resetSourceAccounts,
|
||||
}
|
||||
: lookupChain === CHAIN_ID_NEAR
|
||||
? {
|
||||
tokenAccounts,
|
||||
resetAccounts: resetSourceAccounts,
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import {
|
||||
CHAIN_ID_ALGORAND,
|
||||
CHAIN_ID_NEAR,
|
||||
CHAIN_ID_SOLANA,
|
||||
isEVMChain,
|
||||
isNativeDenom,
|
||||
|
@ -25,11 +26,16 @@ import {
|
|||
getEvmChainId,
|
||||
SOLANA_HOST,
|
||||
getTerraConfig,
|
||||
NATIVE_NEAR_PLACEHOLDER,
|
||||
NATIVE_NEAR_DECIMALS,
|
||||
} from "../utils/consts";
|
||||
import { NATIVE_TERRA_DECIMALS } from "../utils/terra";
|
||||
import { createParsedTokenAccount } from "./useGetSourceParsedTokenAccounts";
|
||||
import useMetadata from "./useMetadata";
|
||||
import { Algodv2 } from "algosdk";
|
||||
import { useNearContext } from "../contexts/NearWalletContext";
|
||||
import { makeNearAccount } from "../utils/near";
|
||||
import { fetchSingleMetadata } from "./useNearMetadata";
|
||||
|
||||
function useGetTargetParsedTokenAccounts() {
|
||||
const dispatch = useDispatch();
|
||||
|
@ -58,6 +64,7 @@ function useGetTargetParsedTokenAccounts() {
|
|||
} = useEthereumProvider();
|
||||
const hasCorrectEvmNetwork = evmChainId === getEvmChainId(targetChain);
|
||||
const { accounts: algoAccounts } = useAlgorandContext();
|
||||
const { accountId: nearAccountId } = useNearContext();
|
||||
const hasResolvedMetadata = metadata.data || metadata.error;
|
||||
useEffect(() => {
|
||||
// targetParsedTokenAccount is cleared on setTargetAsset, but we need to clear it on wallet changes too
|
||||
|
@ -270,6 +277,93 @@ function useGetTargetParsedTokenAccounts() {
|
|||
}
|
||||
}
|
||||
}
|
||||
if (targetChain === CHAIN_ID_NEAR && nearAccountId) {
|
||||
try {
|
||||
makeNearAccount(nearAccountId)
|
||||
.then((account) => {
|
||||
if (targetAsset === NATIVE_NEAR_PLACEHOLDER) {
|
||||
account
|
||||
.getAccountBalance()
|
||||
.then((balance) => {
|
||||
if (!cancelled) {
|
||||
dispatch(
|
||||
setTargetParsedTokenAccount(
|
||||
createParsedTokenAccount(
|
||||
nearAccountId, //publicKey
|
||||
NATIVE_NEAR_PLACEHOLDER, //the app doesn't like when this isn't truthy
|
||||
balance.available, //amount
|
||||
NATIVE_NEAR_DECIMALS,
|
||||
parseFloat(
|
||||
formatUnits(balance.available, NATIVE_NEAR_DECIMALS)
|
||||
),
|
||||
formatUnits(
|
||||
balance.available,
|
||||
NATIVE_NEAR_DECIMALS
|
||||
).toString(),
|
||||
"NEAR",
|
||||
"Near",
|
||||
undefined, //TODO logo
|
||||
true
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
if (!cancelled) {
|
||||
// TODO: error state
|
||||
}
|
||||
});
|
||||
} else {
|
||||
fetchSingleMetadata(targetAsset, account)
|
||||
.then(({ decimals }) => {
|
||||
account
|
||||
.viewFunction(targetAsset, "ft_balance_of", {
|
||||
account_id: nearAccountId,
|
||||
})
|
||||
.then((balance) => {
|
||||
if (!cancelled) {
|
||||
dispatch(
|
||||
setTargetParsedTokenAccount(
|
||||
createParsedTokenAccount(
|
||||
nearAccountId,
|
||||
targetAsset,
|
||||
balance.toString(),
|
||||
decimals,
|
||||
Number(formatUnits(balance, decimals)),
|
||||
formatUnits(balance, decimals),
|
||||
symbol,
|
||||
tokenName,
|
||||
logo
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
if (!cancelled) {
|
||||
// TODO: error state
|
||||
}
|
||||
});
|
||||
})
|
||||
.catch(() => {
|
||||
if (!cancelled) {
|
||||
// TODO: error state
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
if (!cancelled) {
|
||||
// TODO: error state
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
if (!cancelled) {
|
||||
// TODO: error state
|
||||
}
|
||||
}
|
||||
}
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
|
@ -289,6 +383,7 @@ function useGetTargetParsedTokenAccounts() {
|
|||
logo,
|
||||
algoAccounts,
|
||||
decimals,
|
||||
nearAccountId,
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@ import {
|
|||
ChainId,
|
||||
CHAIN_ID_ALGORAND,
|
||||
CHAIN_ID_KLAYTN,
|
||||
CHAIN_ID_NEAR,
|
||||
CHAIN_ID_SOLANA,
|
||||
getEmitterAddressAlgorand,
|
||||
getEmitterAddressEth,
|
||||
|
@ -22,6 +23,7 @@ import {
|
|||
uint8ArrayToHex,
|
||||
} from "@certusone/wormhole-sdk";
|
||||
import { Alert } from "@material-ui/lab";
|
||||
import { Wallet } from "@near-wallet-selector/core";
|
||||
import { WalletContextState } from "@solana/wallet-adapter-react";
|
||||
import { Connection, PublicKey } from "@solana/web3.js";
|
||||
import {
|
||||
|
@ -35,6 +37,7 @@ import { useCallback, useMemo } from "react";
|
|||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { useAlgorandContext } from "../contexts/AlgorandWalletContext";
|
||||
import { useEthereumProvider } from "../contexts/EthereumProviderContext";
|
||||
import { useNearContext } from "../contexts/NearWalletContext";
|
||||
import { useSolanaWallet } from "../contexts/SolanaWalletContext";
|
||||
import {
|
||||
setAttestTx,
|
||||
|
@ -56,11 +59,23 @@ import {
|
|||
ALGORAND_TOKEN_BRIDGE_ID,
|
||||
getBridgeAddressForChain,
|
||||
getTokenBridgeAddressForChain,
|
||||
NATIVE_NEAR_PLACEHOLDER,
|
||||
NEAR_CORE_BRIDGE_ACCOUNT,
|
||||
NEAR_TOKEN_BRIDGE_ACCOUNT,
|
||||
SOLANA_HOST,
|
||||
SOL_BRIDGE_ADDRESS,
|
||||
SOL_TOKEN_BRIDGE_ADDRESS,
|
||||
WORMHOLE_RPC_HOSTS,
|
||||
} from "../utils/consts";
|
||||
import {
|
||||
attestNearFromNear,
|
||||
attestTokenFromNear,
|
||||
// attestTokenFromNear,
|
||||
getEmitterAddressNear,
|
||||
makeNearAccount,
|
||||
parseSequenceFromLogNear,
|
||||
signAndSendTransactions,
|
||||
} from "../utils/near";
|
||||
import parseError from "../utils/parseError";
|
||||
import { signSendAndConfirm } from "../utils/solana";
|
||||
import { postWithFees, waitForTerraExecution } from "../utils/terra";
|
||||
|
@ -175,6 +190,63 @@ async function evm(
|
|||
}
|
||||
}
|
||||
|
||||
async function near(
|
||||
dispatch: any,
|
||||
enqueueSnackbar: any,
|
||||
senderAddr: string,
|
||||
sourceAsset: string,
|
||||
wallet: Wallet
|
||||
) {
|
||||
dispatch(setIsSending(true));
|
||||
try {
|
||||
const account = await makeNearAccount(senderAddr);
|
||||
const msgs =
|
||||
sourceAsset === NATIVE_NEAR_PLACEHOLDER
|
||||
? await attestNearFromNear(
|
||||
account,
|
||||
NEAR_CORE_BRIDGE_ACCOUNT,
|
||||
NEAR_TOKEN_BRIDGE_ACCOUNT
|
||||
)
|
||||
: await attestTokenFromNear(
|
||||
account,
|
||||
NEAR_CORE_BRIDGE_ACCOUNT,
|
||||
NEAR_TOKEN_BRIDGE_ACCOUNT,
|
||||
sourceAsset
|
||||
);
|
||||
const receipt = await signAndSendTransactions(account, wallet, msgs);
|
||||
const sequence = parseSequenceFromLogNear(receipt);
|
||||
dispatch(
|
||||
setAttestTx({
|
||||
id: receipt.transaction_outcome.id,
|
||||
block: 0,
|
||||
})
|
||||
);
|
||||
enqueueSnackbar(null, {
|
||||
content: <Alert severity="success">Transaction confirmed</Alert>,
|
||||
});
|
||||
const emitterAddress = getEmitterAddressNear(NEAR_TOKEN_BRIDGE_ACCOUNT);
|
||||
enqueueSnackbar(null, {
|
||||
content: <Alert severity="info">Fetching VAA</Alert>,
|
||||
});
|
||||
const { vaaBytes } = await getSignedVAAWithRetry(
|
||||
WORMHOLE_RPC_HOSTS,
|
||||
CHAIN_ID_NEAR,
|
||||
emitterAddress,
|
||||
sequence
|
||||
);
|
||||
dispatch(setSignedVAAHex(uint8ArrayToHex(vaaBytes)));
|
||||
enqueueSnackbar(null, {
|
||||
content: <Alert severity="success">Fetched Signed VAA</Alert>,
|
||||
});
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
enqueueSnackbar(null, {
|
||||
content: <Alert severity="error">{parseError(e)}</Alert>,
|
||||
});
|
||||
dispatch(setIsSending(false));
|
||||
}
|
||||
}
|
||||
|
||||
async function solana(
|
||||
dispatch: any,
|
||||
enqueueSnackbar: any,
|
||||
|
@ -297,6 +369,7 @@ export function useHandleAttest() {
|
|||
const terraWallet = useConnectedWallet();
|
||||
const terraFeeDenom = useSelector(selectTerraFeeDenom);
|
||||
const { accounts: algoAccounts } = useAlgorandContext();
|
||||
const { accountId: nearAccountId, wallet } = useNearContext();
|
||||
const disabled = !isTargetComplete || isSending || isSendComplete;
|
||||
const handleAttestClick = useCallback(() => {
|
||||
if (isEVMChain(sourceChain) && !!signer) {
|
||||
|
@ -314,6 +387,8 @@ export function useHandleAttest() {
|
|||
);
|
||||
} else if (sourceChain === CHAIN_ID_ALGORAND && algoAccounts[0]) {
|
||||
algo(dispatch, enqueueSnackbar, algoAccounts[0].address, sourceAsset);
|
||||
} else if (sourceChain === CHAIN_ID_NEAR && nearAccountId && wallet) {
|
||||
near(dispatch, enqueueSnackbar, nearAccountId, sourceAsset, wallet);
|
||||
} else {
|
||||
}
|
||||
}, [
|
||||
|
@ -327,6 +402,8 @@ export function useHandleAttest() {
|
|||
sourceAsset,
|
||||
terraFeeDenom,
|
||||
algoAccounts,
|
||||
nearAccountId,
|
||||
wallet,
|
||||
]);
|
||||
return useMemo(
|
||||
() => ({
|
||||
|
|
|
@ -4,6 +4,7 @@ import {
|
|||
CHAIN_ID_ALGORAND,
|
||||
CHAIN_ID_KARURA,
|
||||
CHAIN_ID_KLAYTN,
|
||||
CHAIN_ID_NEAR,
|
||||
CHAIN_ID_SOLANA,
|
||||
createWrappedOnAlgorand,
|
||||
createWrappedOnEth,
|
||||
|
@ -17,6 +18,7 @@ import {
|
|||
updateWrappedOnTerra,
|
||||
} from "@certusone/wormhole-sdk";
|
||||
import { Alert } from "@material-ui/lab";
|
||||
import { Wallet } from "@near-wallet-selector/core";
|
||||
import { WalletContextState } from "@solana/wallet-adapter-react";
|
||||
import { Connection } from "@solana/web3.js";
|
||||
import {
|
||||
|
@ -30,6 +32,7 @@ import { useCallback, useMemo } from "react";
|
|||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { useAlgorandContext } from "../contexts/AlgorandWalletContext";
|
||||
import { useEthereumProvider } from "../contexts/EthereumProviderContext";
|
||||
import { useNearContext } from "../contexts/NearWalletContext";
|
||||
import { useSolanaWallet } from "../contexts/SolanaWalletContext";
|
||||
import { setCreateTx, setIsCreating } from "../store/attestSlice";
|
||||
import {
|
||||
|
@ -46,11 +49,17 @@ import {
|
|||
getTokenBridgeAddressForChain,
|
||||
KARURA_HOST,
|
||||
MAX_VAA_UPLOAD_RETRIES_SOLANA,
|
||||
NEAR_TOKEN_BRIDGE_ACCOUNT,
|
||||
SOLANA_HOST,
|
||||
SOL_BRIDGE_ADDRESS,
|
||||
SOL_TOKEN_BRIDGE_ADDRESS,
|
||||
} from "../utils/consts";
|
||||
import { getKaruraGasParams } from "../utils/karura";
|
||||
import {
|
||||
createWrappedOnNear,
|
||||
makeNearAccount,
|
||||
signAndSendTransactions,
|
||||
} from "../utils/near";
|
||||
import parseError from "../utils/parseError";
|
||||
import { postVaaWithRetry } from "../utils/postVaa";
|
||||
import { signSendAndConfirm } from "../utils/solana";
|
||||
|
@ -142,6 +151,39 @@ async function evm(
|
|||
}
|
||||
}
|
||||
|
||||
async function near(
|
||||
dispatch: any,
|
||||
enqueueSnackbar: any,
|
||||
senderAddr: string,
|
||||
signedVAA: Uint8Array,
|
||||
wallet: Wallet
|
||||
) {
|
||||
dispatch(setIsCreating(true));
|
||||
try {
|
||||
const account = await makeNearAccount(senderAddr);
|
||||
const msgs = await createWrappedOnNear(
|
||||
account,
|
||||
NEAR_TOKEN_BRIDGE_ACCOUNT,
|
||||
signedVAA
|
||||
);
|
||||
const receipt = await signAndSendTransactions(account, wallet, msgs);
|
||||
dispatch(
|
||||
setCreateTx({
|
||||
id: receipt.transaction_outcome.id,
|
||||
block: 0,
|
||||
})
|
||||
);
|
||||
enqueueSnackbar(null, {
|
||||
content: <Alert severity="success">Transaction confirmed</Alert>,
|
||||
});
|
||||
} catch (e) {
|
||||
enqueueSnackbar(null, {
|
||||
content: <Alert severity="error">{parseError(e)}</Alert>,
|
||||
});
|
||||
dispatch(setIsCreating(false));
|
||||
}
|
||||
}
|
||||
|
||||
async function solana(
|
||||
dispatch: any,
|
||||
enqueueSnackbar: any,
|
||||
|
@ -249,6 +291,7 @@ export function useHandleCreateWrapped(shouldUpdate: boolean) {
|
|||
const terraWallet = useConnectedWallet();
|
||||
const terraFeeDenom = useSelector(selectTerraFeeDenom);
|
||||
const { accounts: algoAccounts } = useAlgorandContext();
|
||||
const { accountId: nearAccountId, wallet } = useNearContext();
|
||||
const handleCreateClick = useCallback(() => {
|
||||
if (isEVMChain(targetChain) && !!signer && !!signedVAA) {
|
||||
evm(
|
||||
|
@ -289,6 +332,13 @@ export function useHandleCreateWrapped(shouldUpdate: boolean) {
|
|||
!!signedVAA
|
||||
) {
|
||||
algo(dispatch, enqueueSnackbar, algoAccounts[0]?.address, signedVAA);
|
||||
} else if (
|
||||
targetChain === CHAIN_ID_NEAR &&
|
||||
nearAccountId &&
|
||||
wallet &&
|
||||
!!signedVAA
|
||||
) {
|
||||
near(dispatch, enqueueSnackbar, nearAccountId, signedVAA, wallet);
|
||||
} else {
|
||||
// enqueueSnackbar(
|
||||
// "Creating wrapped tokens on this chain is not yet supported",
|
||||
|
@ -309,6 +359,8 @@ export function useHandleCreateWrapped(shouldUpdate: boolean) {
|
|||
shouldUpdate,
|
||||
terraFeeDenom,
|
||||
algoAccounts,
|
||||
nearAccountId,
|
||||
wallet,
|
||||
]);
|
||||
return useMemo(
|
||||
() => ({
|
||||
|
|
|
@ -2,6 +2,7 @@ import {
|
|||
ChainId,
|
||||
CHAIN_ID_ALGORAND,
|
||||
CHAIN_ID_KLAYTN,
|
||||
CHAIN_ID_NEAR,
|
||||
CHAIN_ID_SOLANA,
|
||||
isEVMChain,
|
||||
isTerraChain,
|
||||
|
@ -15,6 +16,7 @@ import {
|
|||
uint8ArrayToHex,
|
||||
} from "@certusone/wormhole-sdk";
|
||||
import { Alert } from "@material-ui/lab";
|
||||
import { Wallet } from "@near-wallet-selector/core";
|
||||
import { WalletContextState } from "@solana/wallet-adapter-react";
|
||||
import { Connection } from "@solana/web3.js";
|
||||
import {
|
||||
|
@ -29,6 +31,7 @@ import { useCallback, useMemo } from "react";
|
|||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { useAlgorandContext } from "../contexts/AlgorandWalletContext";
|
||||
import { useEthereumProvider } from "../contexts/EthereumProviderContext";
|
||||
import { useNearContext } from "../contexts/NearWalletContext";
|
||||
import { useSolanaWallet } from "../contexts/SolanaWalletContext";
|
||||
import {
|
||||
selectTerraFeeDenom,
|
||||
|
@ -44,10 +47,16 @@ import {
|
|||
ALGORAND_TOKEN_BRIDGE_ID,
|
||||
getTokenBridgeAddressForChain,
|
||||
MAX_VAA_UPLOAD_RETRIES_SOLANA,
|
||||
NEAR_TOKEN_BRIDGE_ACCOUNT,
|
||||
SOLANA_HOST,
|
||||
SOL_BRIDGE_ADDRESS,
|
||||
SOL_TOKEN_BRIDGE_ADDRESS,
|
||||
} from "../utils/consts";
|
||||
import {
|
||||
makeNearAccount,
|
||||
redeemOnNear,
|
||||
signAndSendTransactions,
|
||||
} from "../utils/near";
|
||||
import parseError from "../utils/parseError";
|
||||
import { postVaaWithRetry } from "../utils/postVaa";
|
||||
import { signSendAndConfirm } from "../utils/solana";
|
||||
|
@ -135,6 +144,39 @@ async function evm(
|
|||
}
|
||||
}
|
||||
|
||||
async function near(
|
||||
dispatch: any,
|
||||
enqueueSnackbar: any,
|
||||
senderAddr: string,
|
||||
signedVAA: Uint8Array,
|
||||
wallet: Wallet
|
||||
) {
|
||||
dispatch(setIsRedeeming(true));
|
||||
try {
|
||||
const account = await makeNearAccount(senderAddr);
|
||||
const msgs = await redeemOnNear(
|
||||
account,
|
||||
NEAR_TOKEN_BRIDGE_ACCOUNT,
|
||||
signedVAA
|
||||
);
|
||||
const receipt = await signAndSendTransactions(account, wallet, msgs);
|
||||
dispatch(
|
||||
setRedeemTx({
|
||||
id: receipt.transaction_outcome.id,
|
||||
block: 0,
|
||||
})
|
||||
);
|
||||
enqueueSnackbar(null, {
|
||||
content: <Alert severity="success">Transaction confirmed</Alert>,
|
||||
});
|
||||
} catch (e) {
|
||||
enqueueSnackbar(null, {
|
||||
content: <Alert severity="error">{parseError(e)}</Alert>,
|
||||
});
|
||||
dispatch(setIsRedeeming(false));
|
||||
}
|
||||
}
|
||||
|
||||
async function solana(
|
||||
dispatch: any,
|
||||
enqueueSnackbar: any,
|
||||
|
@ -233,6 +275,7 @@ export function useHandleRedeem() {
|
|||
const terraWallet = useConnectedWallet();
|
||||
const terraFeeDenom = useSelector(selectTerraFeeDenom);
|
||||
const { accounts: algoAccounts } = useAlgorandContext();
|
||||
const { accountId: nearAccountId, wallet } = useNearContext();
|
||||
const signedVAA = useTransferSignedVAA();
|
||||
const isRedeeming = useSelector(selectTransferIsRedeeming);
|
||||
const handleRedeemClick = useCallback(() => {
|
||||
|
@ -267,6 +310,13 @@ export function useHandleRedeem() {
|
|||
!!signedVAA
|
||||
) {
|
||||
algo(dispatch, enqueueSnackbar, algoAccounts[0]?.address, signedVAA);
|
||||
} else if (
|
||||
targetChain === CHAIN_ID_NEAR &&
|
||||
nearAccountId &&
|
||||
wallet &&
|
||||
!!signedVAA
|
||||
) {
|
||||
near(dispatch, enqueueSnackbar, nearAccountId, signedVAA, wallet);
|
||||
} else {
|
||||
}
|
||||
}, [
|
||||
|
@ -280,6 +330,8 @@ export function useHandleRedeem() {
|
|||
terraWallet,
|
||||
terraFeeDenom,
|
||||
algoAccounts,
|
||||
nearAccountId,
|
||||
wallet,
|
||||
]);
|
||||
|
||||
const handleRedeemNativeClick = useCallback(() => {
|
||||
|
|
|
@ -23,7 +23,9 @@ import {
|
|||
transferNativeSol,
|
||||
uint8ArrayToHex,
|
||||
} from "@certusone/wormhole-sdk";
|
||||
import { CHAIN_ID_NEAR } from "@certusone/wormhole-sdk/lib/esm";
|
||||
import { Alert } from "@material-ui/lab";
|
||||
import { Wallet } from "@near-wallet-selector/core";
|
||||
import { WalletContextState } from "@solana/wallet-adapter-react";
|
||||
import { Connection } from "@solana/web3.js";
|
||||
import {
|
||||
|
@ -38,6 +40,7 @@ import { useCallback, useMemo } from "react";
|
|||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { useAlgorandContext } from "../contexts/AlgorandWalletContext";
|
||||
import { useEthereumProvider } from "../contexts/EthereumProviderContext";
|
||||
import { useNearContext } from "../contexts/NearWalletContext";
|
||||
import { useSolanaWallet } from "../contexts/SolanaWalletContext";
|
||||
import {
|
||||
selectTerraFeeDenom,
|
||||
|
@ -54,8 +57,8 @@ import {
|
|||
selectTransferTargetChain,
|
||||
} from "../store/selectors";
|
||||
import {
|
||||
setIsVAAPending,
|
||||
setIsSending,
|
||||
setIsVAAPending,
|
||||
setSignedVAAHex,
|
||||
setTransferTx,
|
||||
} from "../store/transferSlice";
|
||||
|
@ -66,11 +69,22 @@ import {
|
|||
ALGORAND_TOKEN_BRIDGE_ID,
|
||||
getBridgeAddressForChain,
|
||||
getTokenBridgeAddressForChain,
|
||||
NATIVE_NEAR_PLACEHOLDER,
|
||||
NEAR_CORE_BRIDGE_ACCOUNT,
|
||||
NEAR_TOKEN_BRIDGE_ACCOUNT,
|
||||
SOLANA_HOST,
|
||||
SOL_BRIDGE_ADDRESS,
|
||||
SOL_TOKEN_BRIDGE_ADDRESS,
|
||||
} from "../utils/consts";
|
||||
import { getSignedVAAWithRetry } from "../utils/getSignedVAAWithRetry";
|
||||
import {
|
||||
getEmitterAddressNear,
|
||||
makeNearAccount,
|
||||
parseSequenceFromLogNear,
|
||||
signAndSendTransactions,
|
||||
transferNearFromNear,
|
||||
transferTokenFromNear,
|
||||
} from "../utils/near";
|
||||
import parseError from "../utils/parseError";
|
||||
import { signSendAndConfirm } from "../utils/solana";
|
||||
import { postWithFees, waitForTerraExecution } from "../utils/terra";
|
||||
|
@ -249,6 +263,70 @@ async function evm(
|
|||
}
|
||||
}
|
||||
|
||||
async function near(
|
||||
dispatch: any,
|
||||
enqueueSnackbar: any,
|
||||
wallet: Wallet,
|
||||
senderAddr: string,
|
||||
tokenAddress: string,
|
||||
decimals: number,
|
||||
amount: string,
|
||||
recipientChain: ChainId,
|
||||
recipientAddress: Uint8Array,
|
||||
chainId: ChainId,
|
||||
relayerFee?: string
|
||||
) {
|
||||
dispatch(setIsSending(true));
|
||||
try {
|
||||
const baseAmountParsed = parseUnits(amount, decimals);
|
||||
const feeParsed = parseUnits(relayerFee || "0", decimals);
|
||||
const transferAmountParsed = baseAmountParsed.add(feeParsed);
|
||||
const account = await makeNearAccount(senderAddr);
|
||||
const msgs =
|
||||
tokenAddress === NATIVE_NEAR_PLACEHOLDER
|
||||
? await transferNearFromNear(
|
||||
account,
|
||||
NEAR_CORE_BRIDGE_ACCOUNT,
|
||||
NEAR_TOKEN_BRIDGE_ACCOUNT,
|
||||
transferAmountParsed.toBigInt(),
|
||||
recipientAddress,
|
||||
recipientChain,
|
||||
feeParsed.toBigInt()
|
||||
)
|
||||
: await transferTokenFromNear(
|
||||
account,
|
||||
NEAR_CORE_BRIDGE_ACCOUNT,
|
||||
NEAR_TOKEN_BRIDGE_ACCOUNT,
|
||||
tokenAddress,
|
||||
transferAmountParsed.toBigInt(),
|
||||
recipientAddress,
|
||||
recipientChain,
|
||||
feeParsed.toBigInt()
|
||||
);
|
||||
const receipt = await signAndSendTransactions(account, wallet, msgs);
|
||||
const sequence = parseSequenceFromLogNear(receipt);
|
||||
dispatch(
|
||||
setTransferTx({
|
||||
id: receipt.transaction_outcome.id,
|
||||
block: 0,
|
||||
})
|
||||
);
|
||||
enqueueSnackbar(null, {
|
||||
content: <Alert severity="success">Transaction confirmed</Alert>,
|
||||
});
|
||||
const emitterAddress = getEmitterAddressNear(NEAR_TOKEN_BRIDGE_ACCOUNT);
|
||||
await fetchSignedVAA(
|
||||
chainId,
|
||||
emitterAddress,
|
||||
sequence,
|
||||
enqueueSnackbar,
|
||||
dispatch
|
||||
);
|
||||
} catch (e) {
|
||||
handleError(e, enqueueSnackbar, dispatch);
|
||||
}
|
||||
}
|
||||
|
||||
async function solana(
|
||||
dispatch: any,
|
||||
enqueueSnackbar: any,
|
||||
|
@ -404,6 +482,7 @@ export function useHandleTransfer() {
|
|||
const terraWallet = useConnectedWallet();
|
||||
const terraFeeDenom = useSelector(selectTerraFeeDenom);
|
||||
const { accounts: algoAccounts } = useAlgorandContext();
|
||||
const { accountId: nearAccountId, wallet } = useNearContext();
|
||||
const sourceParsedTokenAccount = useSelector(
|
||||
selectTransferSourceParsedTokenAccount
|
||||
);
|
||||
|
@ -501,6 +580,27 @@ export function useHandleTransfer() {
|
|||
sourceChain,
|
||||
relayerFee
|
||||
);
|
||||
} else if (
|
||||
sourceChain === CHAIN_ID_NEAR &&
|
||||
nearAccountId &&
|
||||
wallet &&
|
||||
!!sourceAsset &&
|
||||
decimals !== undefined &&
|
||||
!!targetAddress
|
||||
) {
|
||||
near(
|
||||
dispatch,
|
||||
enqueueSnackbar,
|
||||
wallet,
|
||||
nearAccountId,
|
||||
sourceAsset,
|
||||
decimals,
|
||||
amount,
|
||||
targetChain,
|
||||
targetAddress,
|
||||
sourceChain,
|
||||
relayerFee
|
||||
);
|
||||
} else {
|
||||
}
|
||||
}, [
|
||||
|
@ -523,6 +623,8 @@ export function useHandleTransfer() {
|
|||
isNative,
|
||||
terraFeeDenom,
|
||||
algoAccounts,
|
||||
nearAccountId,
|
||||
wallet,
|
||||
]);
|
||||
return useMemo(
|
||||
() => ({
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import {
|
||||
ChainId,
|
||||
CHAIN_ID_ALGORAND,
|
||||
CHAIN_ID_NEAR,
|
||||
CHAIN_ID_SOLANA,
|
||||
isEVMChain,
|
||||
isTerraChain,
|
||||
|
@ -13,6 +14,7 @@ import {
|
|||
ConnectType,
|
||||
useEthereumProvider,
|
||||
} from "../contexts/EthereumProviderContext";
|
||||
import { useNearContext } from "../contexts/NearWalletContext";
|
||||
import { useSolanaWallet } from "../contexts/SolanaWalletContext";
|
||||
import { CLUSTER, getEvmChainId } from "../utils/consts";
|
||||
import {
|
||||
|
@ -58,6 +60,7 @@ function useIsWalletReady(
|
|||
const hasCorrectEvmNetwork = evmChainId === correctEvmNetwork;
|
||||
const { accounts: algorandAccounts } = useAlgorandContext();
|
||||
const algoPK = algorandAccounts[0]?.address;
|
||||
const { accountId: nearPK } = useNearContext();
|
||||
|
||||
const forceNetworkSwitch = useCallback(async () => {
|
||||
if (provider && correctEvmNetwork) {
|
||||
|
@ -118,6 +121,9 @@ function useIsWalletReady(
|
|||
if (chainId === CHAIN_ID_ALGORAND && algoPK) {
|
||||
return createWalletStatus(true, undefined, forceNetworkSwitch, algoPK);
|
||||
}
|
||||
if (chainId === CHAIN_ID_NEAR && nearPK) {
|
||||
return createWalletStatus(true, undefined, forceNetworkSwitch, nearPK);
|
||||
}
|
||||
if (isEVMChain(chainId) && hasEthInfo && signerAddress) {
|
||||
if (hasCorrectEvmNetwork) {
|
||||
return createWalletStatus(
|
||||
|
@ -158,6 +164,7 @@ function useIsWalletReady(
|
|||
signerAddress,
|
||||
terraWallet,
|
||||
algoPK,
|
||||
nearPK,
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import {
|
||||
ChainId,
|
||||
CHAIN_ID_ALGORAND,
|
||||
CHAIN_ID_NEAR,
|
||||
CHAIN_ID_SOLANA,
|
||||
CHAIN_ID_TERRA2,
|
||||
isEVMChain,
|
||||
|
@ -15,6 +16,7 @@ import { Metadata } from "../utils/metaplex";
|
|||
import useAlgoMetadata, { AlgoMetadata } from "./useAlgoMetadata";
|
||||
import useEvmMetadata, { EvmMetadata } from "./useEvmMetadata";
|
||||
import useMetaplexData from "./useMetaplexData";
|
||||
import useNearMetadata from "./useNearMetadata";
|
||||
import useSolanaTokenMap from "./useSolanaTokenMap";
|
||||
import useTerraMetadata, { TerraMetadata } from "./useTerraMetadata";
|
||||
import useTerraTokenMap, { TerraTokenMap } from "./useTerraTokenMap";
|
||||
|
@ -165,6 +167,9 @@ export default function useMetadata(
|
|||
const algoAddresses = useMemo(() => {
|
||||
return chainId === CHAIN_ID_ALGORAND ? addresses : [];
|
||||
}, [chainId, addresses]);
|
||||
const nearAddresses = useMemo(() => {
|
||||
return chainId === CHAIN_ID_NEAR ? addresses : [];
|
||||
}, [chainId, addresses]);
|
||||
|
||||
const metaplexData = useMetaplexData(solanaAddresses);
|
||||
const terraMetadata = useTerraMetadata(
|
||||
|
@ -173,6 +178,7 @@ export default function useMetadata(
|
|||
);
|
||||
const ethMetadata = useEvmMetadata(ethereumAddresses, chainId);
|
||||
const algoMetadata = useAlgoMetadata(algoAddresses);
|
||||
const nearMetadata = useNearMetadata(nearAddresses);
|
||||
|
||||
const output: DataWrapper<Map<string, GenericMetadata>> = useMemo(
|
||||
() =>
|
||||
|
@ -189,6 +195,8 @@ export default function useMetadata(
|
|||
)
|
||||
: chainId === CHAIN_ID_ALGORAND
|
||||
? constructAlgoMetadata(algoAddresses, algoMetadata)
|
||||
: chainId === CHAIN_ID_NEAR
|
||||
? constructAlgoMetadata(nearAddresses, nearMetadata)
|
||||
: getEmptyDataWrapper(),
|
||||
[
|
||||
chainId,
|
||||
|
@ -202,6 +210,8 @@ export default function useMetadata(
|
|||
terraTokenMap,
|
||||
algoAddresses,
|
||||
algoMetadata,
|
||||
nearAddresses,
|
||||
nearMetadata,
|
||||
]
|
||||
);
|
||||
|
||||
|
|
|
@ -0,0 +1,83 @@
|
|||
import { Account } from "near-api-js";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { useNearContext } from "../contexts/NearWalletContext";
|
||||
import { DataWrapper } from "../store/helpers";
|
||||
import { makeNearAccount } from "../utils/near";
|
||||
import { AlgoMetadata } from "./useAlgoMetadata";
|
||||
|
||||
export const fetchSingleMetadata = async (
|
||||
address: string,
|
||||
account: Account
|
||||
): Promise<AlgoMetadata> => {
|
||||
const assetInfo = await account.viewFunction(address, "ft_metadata");
|
||||
return {
|
||||
tokenName: assetInfo.name,
|
||||
symbol: assetInfo.symbol,
|
||||
decimals: assetInfo.decimals,
|
||||
};
|
||||
};
|
||||
|
||||
const fetchNearMetadata = async (
|
||||
addresses: string[],
|
||||
nearAccountId: string
|
||||
) => {
|
||||
const account = await makeNearAccount(nearAccountId);
|
||||
const promises: Promise<AlgoMetadata>[] = [];
|
||||
addresses.forEach((address) => {
|
||||
promises.push(fetchSingleMetadata(address, account));
|
||||
});
|
||||
const resultsArray = await Promise.all(promises);
|
||||
const output = new Map<string, AlgoMetadata>();
|
||||
addresses.forEach((address, index) => {
|
||||
output.set(address, resultsArray[index]);
|
||||
});
|
||||
|
||||
return output;
|
||||
};
|
||||
|
||||
function useNearMetadata(
|
||||
addresses: string[]
|
||||
): DataWrapper<Map<string, AlgoMetadata>> {
|
||||
const { accountId: nearAccountId } = useNearContext();
|
||||
const [isFetching, setIsFetching] = useState(false);
|
||||
const [error, setError] = useState("");
|
||||
const [data, setData] = useState<Map<string, AlgoMetadata> | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
if (addresses.length && nearAccountId) {
|
||||
setIsFetching(true);
|
||||
setError("");
|
||||
setData(null);
|
||||
fetchNearMetadata(addresses, nearAccountId).then(
|
||||
(results) => {
|
||||
if (!cancelled) {
|
||||
setData(results);
|
||||
setIsFetching(false);
|
||||
}
|
||||
},
|
||||
() => {
|
||||
if (!cancelled) {
|
||||
setError("Could not retrieve contract metadata");
|
||||
setIsFetching(false);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [addresses, nearAccountId]);
|
||||
|
||||
return useMemo(
|
||||
() => ({
|
||||
data,
|
||||
isFetching,
|
||||
error,
|
||||
receivedAt: null,
|
||||
}),
|
||||
[data, isFetching, error]
|
||||
);
|
||||
}
|
||||
|
||||
export default useNearMetadata;
|
|
@ -1,6 +1,7 @@
|
|||
import {
|
||||
ChainId,
|
||||
CHAIN_ID_ALGORAND,
|
||||
CHAIN_ID_NEAR,
|
||||
CHAIN_ID_SOLANA,
|
||||
CHAIN_ID_TERRA2,
|
||||
getOriginalAssetAlgorand,
|
||||
|
@ -28,6 +29,7 @@ import {
|
|||
Provider,
|
||||
useEthereumProvider,
|
||||
} from "../contexts/EthereumProviderContext";
|
||||
import { useNearContext } from "../contexts/NearWalletContext";
|
||||
import { DataWrapper } from "../store/helpers";
|
||||
import {
|
||||
ALGORAND_HOST,
|
||||
|
@ -35,11 +37,19 @@ import {
|
|||
getNFTBridgeAddressForChain,
|
||||
getTerraConfig,
|
||||
getTokenBridgeAddressForChain,
|
||||
NATIVE_NEAR_PLACEHOLDER,
|
||||
NATIVE_NEAR_WH_ADDRESS,
|
||||
NEAR_TOKEN_BRIDGE_ACCOUNT,
|
||||
SOLANA_HOST,
|
||||
SOLANA_SYSTEM_PROGRAM_ADDRESS,
|
||||
SOL_NFT_BRIDGE_ADDRESS,
|
||||
SOL_TOKEN_BRIDGE_ADDRESS,
|
||||
} from "../utils/consts";
|
||||
import {
|
||||
getOriginalAssetNear,
|
||||
lookupHash,
|
||||
makeNearAccount,
|
||||
} from "../utils/near";
|
||||
import { queryExternalId } from "../utils/terra";
|
||||
import useIsWalletReady from "./useIsWalletReady";
|
||||
|
||||
|
@ -52,7 +62,8 @@ export type OriginalAssetInfo = {
|
|||
export async function getOriginalAssetToken(
|
||||
foreignChain: ChainId,
|
||||
foreignNativeStringAddress: string,
|
||||
provider?: Web3Provider
|
||||
provider?: Web3Provider,
|
||||
nearAccountId?: string | null
|
||||
) {
|
||||
let promise = null;
|
||||
try {
|
||||
|
@ -88,6 +99,13 @@ export async function getOriginalAssetToken(
|
|||
ALGORAND_TOKEN_BRIDGE_ID,
|
||||
BigInt(foreignNativeStringAddress)
|
||||
);
|
||||
} else if (foreignChain === CHAIN_ID_NEAR && nearAccountId) {
|
||||
const account = await makeNearAccount(nearAccountId);
|
||||
promise = await getOriginalAssetNear(
|
||||
account,
|
||||
NEAR_TOKEN_BRIDGE_ACCOUNT,
|
||||
foreignNativeStringAddress
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
promise = Promise.reject("Invalid foreign arguments.");
|
||||
|
@ -137,7 +155,8 @@ export async function getOriginalAsset(
|
|||
foreignNativeStringAddress: string,
|
||||
nft: boolean,
|
||||
tokenId?: string,
|
||||
provider?: Provider
|
||||
provider?: Provider,
|
||||
nearAccountId?: string | null
|
||||
): Promise<WormholeWrappedNFTInfo> {
|
||||
const result = nft
|
||||
? await getOriginalAssetNFT(
|
||||
|
@ -149,7 +168,8 @@ export async function getOriginalAsset(
|
|||
: await getOriginalAssetToken(
|
||||
foreignChain,
|
||||
foreignNativeStringAddress,
|
||||
provider
|
||||
provider,
|
||||
nearAccountId
|
||||
);
|
||||
|
||||
if (
|
||||
|
@ -178,6 +198,7 @@ function useOriginalAsset(
|
|||
tokenId?: string
|
||||
): DataWrapper<OriginalAssetInfo> {
|
||||
const { provider } = useEthereumProvider();
|
||||
const { accountId: nearAccountId } = useNearContext();
|
||||
const { isReady } = useIsWalletReady(foreignChain, false);
|
||||
const [originAddress, setOriginAddress] = useState<string | null>(null);
|
||||
const [originTokenId, setOriginTokenId] = useState<string | null>(null);
|
||||
|
@ -222,10 +243,26 @@ function useOriginalAsset(
|
|||
if (argumentError) {
|
||||
return;
|
||||
}
|
||||
// short circuit for near native
|
||||
if (
|
||||
foreignChain === CHAIN_ID_NEAR &&
|
||||
foreignAddress === NATIVE_NEAR_PLACEHOLDER
|
||||
) {
|
||||
setOriginChain(CHAIN_ID_NEAR);
|
||||
setOriginAddress(NATIVE_NEAR_PLACEHOLDER);
|
||||
return;
|
||||
}
|
||||
let cancelled = false;
|
||||
setIsLoading(true);
|
||||
|
||||
getOriginalAsset(foreignChain, foreignAddress, nft, tokenId, provider)
|
||||
getOriginalAsset(
|
||||
foreignChain,
|
||||
foreignAddress,
|
||||
nft,
|
||||
tokenId,
|
||||
provider,
|
||||
nearAccountId
|
||||
)
|
||||
.then((result) => {
|
||||
if (!cancelled) {
|
||||
setIsLoading(false);
|
||||
|
@ -234,6 +271,24 @@ function useOriginalAsset(
|
|||
queryExternalId(uint8ArrayToHex(result.assetAddress)).then(
|
||||
(tokenId) => setOriginAddress(tokenId || null)
|
||||
);
|
||||
} else if (result.chainId === CHAIN_ID_NEAR) {
|
||||
if (
|
||||
uint8ArrayToHex(result.assetAddress) === NATIVE_NEAR_WH_ADDRESS
|
||||
) {
|
||||
setOriginAddress(NATIVE_NEAR_PLACEHOLDER);
|
||||
} else if (nearAccountId) {
|
||||
makeNearAccount(nearAccountId).then((account) => {
|
||||
lookupHash(
|
||||
account,
|
||||
NEAR_TOKEN_BRIDGE_ACCOUNT,
|
||||
uint8ArrayToHex(result.assetAddress)
|
||||
).then((tokenAccount) => {
|
||||
if (!cancelled) {
|
||||
setOriginAddress(tokenAccount[1] || null);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
} else {
|
||||
setOriginAddress(
|
||||
hexToNativeAssetString(
|
||||
|
@ -261,6 +316,7 @@ function useOriginalAsset(
|
|||
argumentError,
|
||||
tokenId,
|
||||
argsEqual,
|
||||
nearAccountId,
|
||||
]);
|
||||
|
||||
const output: DataWrapper<OriginalAssetInfo> = useMemo(
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import {
|
||||
canonicalAddress,
|
||||
CHAIN_ID_ALGORAND,
|
||||
CHAIN_ID_NEAR,
|
||||
CHAIN_ID_SOLANA,
|
||||
isEVMChain,
|
||||
isTerraChain,
|
||||
|
@ -29,6 +30,11 @@ import {
|
|||
} from "../store/selectors";
|
||||
import { setTargetAddressHex as setTransferTargetAddressHex } from "../store/transferSlice";
|
||||
import { decodeAddress } from "algosdk";
|
||||
import { useNearContext } from "../contexts/NearWalletContext";
|
||||
import { makeNearAccount, signAndSendTransactions } from "../utils/near";
|
||||
import { NEAR_TOKEN_BRIDGE_ACCOUNT } from "../utils/consts";
|
||||
import { getTransactionLastResult } from "near-api-js/lib/providers";
|
||||
import BN from "bn.js";
|
||||
|
||||
function useSyncTargetAddress(shouldFire: boolean, nft?: boolean) {
|
||||
const dispatch = useDispatch();
|
||||
|
@ -47,6 +53,7 @@ function useSyncTargetAddress(shouldFire: boolean, nft?: boolean) {
|
|||
const targetTokenAccountPublicKey = targetParsedTokenAccount?.publicKey;
|
||||
const terraWallet = useConnectedWallet();
|
||||
const { accounts: algoAccounts } = useAlgorandContext();
|
||||
const { accountId: nearAccountId, wallet } = useNearContext();
|
||||
const setTargetAddressHex = nft
|
||||
? setNFTTargetAddressHex
|
||||
: setTransferTargetAddressHex;
|
||||
|
@ -116,6 +123,55 @@ function useSyncTargetAddress(shouldFire: boolean, nft?: boolean) {
|
|||
uint8ArrayToHex(decodeAddress(algoAccounts[0].address).publicKey)
|
||||
)
|
||||
);
|
||||
} else if (targetChain === CHAIN_ID_NEAR && nearAccountId && wallet) {
|
||||
(async () => {
|
||||
try {
|
||||
const account = await makeNearAccount(nearAccountId);
|
||||
// So, near can have account names up to 64 bytes but wormhole can only have 32...
|
||||
// as a result, we have to hash our account names to sha256's.. What we are doing
|
||||
// here is doing a RPC call (does not require any interaction with the wallet and is free)
|
||||
// that both tells us our account hash AND if we are already registered...
|
||||
let account_hash = await account.viewFunction(
|
||||
NEAR_TOKEN_BRIDGE_ACCOUNT,
|
||||
"hash_account",
|
||||
{
|
||||
account: nearAccountId,
|
||||
}
|
||||
);
|
||||
if (!cancelled) {
|
||||
let myAddress = account_hash[1];
|
||||
console.log("account hash for", nearAccountId, account_hash);
|
||||
|
||||
if (!account_hash[0]) {
|
||||
console.log("Registering the receiving account");
|
||||
|
||||
let myAddress2 = getTransactionLastResult(
|
||||
await signAndSendTransactions(account, wallet, [
|
||||
{
|
||||
contractId: NEAR_TOKEN_BRIDGE_ACCOUNT,
|
||||
methodName: "register_account",
|
||||
args: { account: nearAccountId },
|
||||
gas: new BN("100000000000000"),
|
||||
attachedDeposit: new BN("2000000000000000000000"), // 0.002 NEAR
|
||||
},
|
||||
])
|
||||
);
|
||||
|
||||
console.log("account hash returned: " + myAddress2);
|
||||
} else {
|
||||
console.log("account already registered");
|
||||
}
|
||||
if (!cancelled) {
|
||||
dispatch(setTargetAddressHex(myAddress));
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
if (!cancelled) {
|
||||
dispatch(setTargetAddressHex(undefined));
|
||||
}
|
||||
}
|
||||
})();
|
||||
} else {
|
||||
dispatch(setTargetAddressHex(undefined));
|
||||
}
|
||||
|
@ -135,6 +191,8 @@ function useSyncTargetAddress(shouldFire: boolean, nft?: boolean) {
|
|||
nft,
|
||||
setTargetAddressHex,
|
||||
algoAccounts,
|
||||
nearAccountId,
|
||||
wallet,
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 288 288"><g id="Layer_1" data-name="Layer 1"><path d="M187.58,79.81l-30.1,44.69a3.2,3.2,0,0,0,4.75,4.2L191.86,103a1.2,1.2,0,0,1,2,.91v80.46a1.2,1.2,0,0,1-2.12.77L102.18,77.93A15.35,15.35,0,0,0,90.47,72.5H87.34A15.34,15.34,0,0,0,72,87.84V201.16A15.34,15.34,0,0,0,87.34,216.5h0a15.35,15.35,0,0,0,13.08-7.31l30.1-44.69a3.2,3.2,0,0,0-4.75-4.2L96.14,186a1.2,1.2,0,0,1-2-.91V104.61a1.2,1.2,0,0,1,2.12-.77l89.55,107.23a15.35,15.35,0,0,0,11.71,5.43h3.13A15.34,15.34,0,0,0,216,201.16V87.84A15.34,15.34,0,0,0,200.66,72.5h0A15.35,15.35,0,0,0,187.58,79.81Z"/></g></svg>
|
After Width: | Height: | Size: 610 B |
|
@ -9,6 +9,7 @@ import BackgroundImage from "./components/BackgroundImage";
|
|||
import { AlgorandContextProvider } from "./contexts/AlgorandWalletContext";
|
||||
import { BetaContextProvider } from "./contexts/BetaContext";
|
||||
import { EthereumProviderProvider } from "./contexts/EthereumProviderContext";
|
||||
import { NearContextProvider } from "./contexts/NearWalletContext";
|
||||
import { SolanaWalletProvider } from "./contexts/SolanaWalletContext.tsx";
|
||||
import { TerraWalletProvider } from "./contexts/TerraWalletContext.tsx";
|
||||
import ErrorBoundary from "./ErrorBoundary";
|
||||
|
@ -27,10 +28,12 @@ ReactDOM.render(
|
|||
<EthereumProviderProvider>
|
||||
<TerraWalletProvider>
|
||||
<AlgorandContextProvider>
|
||||
<HashRouter>
|
||||
<BackgroundImage />
|
||||
<App />
|
||||
</HashRouter>
|
||||
<NearContextProvider>
|
||||
<HashRouter>
|
||||
<BackgroundImage />
|
||||
<App />
|
||||
</HashRouter>
|
||||
</NearContextProvider>
|
||||
</AlgorandContextProvider>
|
||||
</TerraWalletProvider>
|
||||
</EthereumProviderProvider>
|
||||
|
|
|
@ -11,6 +11,7 @@ import {
|
|||
CHAIN_ID_FANTOM,
|
||||
CHAIN_ID_KARURA,
|
||||
CHAIN_ID_KLAYTN,
|
||||
CHAIN_ID_NEAR,
|
||||
CHAIN_ID_NEON,
|
||||
CHAIN_ID_OASIS,
|
||||
CHAIN_ID_POLYGON,
|
||||
|
@ -41,6 +42,8 @@ import polygonIcon from "../icons/polygon.svg";
|
|||
import solanaIcon from "../icons/solana.svg";
|
||||
import terraIcon from "../icons/terra.svg";
|
||||
import terra2Icon from "../icons/terra2.svg";
|
||||
import nearIcon from "../icons/near.svg";
|
||||
import { ConnectConfig, keyStores } from "near-api-js";
|
||||
|
||||
export type Cluster = "devnet" | "testnet" | "mainnet";
|
||||
export const CLUSTER: Cluster =
|
||||
|
@ -107,6 +110,11 @@ export const CHAINS: ChainInfo[] =
|
|||
name: "Klaytn",
|
||||
logo: klaytnIcon,
|
||||
},
|
||||
{
|
||||
id: CHAIN_ID_NEAR,
|
||||
name: "Near",
|
||||
logo: nearIcon,
|
||||
},
|
||||
{
|
||||
id: CHAIN_ID_OASIS,
|
||||
name: "Oasis",
|
||||
|
@ -190,6 +198,11 @@ export const CHAINS: ChainInfo[] =
|
|||
name: "Klaytn",
|
||||
logo: klaytnIcon,
|
||||
},
|
||||
{
|
||||
id: CHAIN_ID_NEAR,
|
||||
name: "Near",
|
||||
logo: nearIcon,
|
||||
},
|
||||
{
|
||||
id: CHAIN_ID_NEON,
|
||||
name: "Neon",
|
||||
|
@ -237,6 +250,11 @@ export const CHAINS: ChainInfo[] =
|
|||
name: "Ethereum",
|
||||
logo: ethIcon,
|
||||
},
|
||||
{
|
||||
id: CHAIN_ID_NEAR,
|
||||
name: "Near",
|
||||
logo: nearIcon,
|
||||
},
|
||||
{
|
||||
id: CHAIN_ID_SOLANA,
|
||||
name: "Solana",
|
||||
|
@ -846,6 +864,20 @@ export const ALGORAND_TOKEN_BRIDGE_ID = BigInt(
|
|||
export const ALGORAND_WAIT_FOR_CONFIRMATIONS =
|
||||
CLUSTER === "mainnet" ? 4 : CLUSTER === "testnet" ? 4 : 1;
|
||||
|
||||
export const NEAR_CORE_BRIDGE_ACCOUNT =
|
||||
CLUSTER === "mainnet"
|
||||
? "contract.wormhole_crypto.near"
|
||||
: CLUSTER === "testnet"
|
||||
? "wormhole.wormhole.testnet"
|
||||
: "wormhole.test.near";
|
||||
|
||||
export const NEAR_TOKEN_BRIDGE_ACCOUNT =
|
||||
CLUSTER === "mainnet"
|
||||
? "contract.portalbridge.near"
|
||||
: CLUSTER === "testnet"
|
||||
? "token.wormhole.testnet"
|
||||
: "token.test.near";
|
||||
|
||||
export const getBridgeAddressForChain = (chainId: ChainId) =>
|
||||
chainId === CHAIN_ID_SOLANA
|
||||
? SOL_BRIDGE_ADDRESS
|
||||
|
@ -1409,6 +1441,40 @@ export const getTerraFCDBaseUrl = (chainId: TerraChainId) =>
|
|||
export const getTerraGasPricesUrl = (chainId: TerraChainId) =>
|
||||
`${getTerraFCDBaseUrl(chainId)}/v1/txs/gas_prices`;
|
||||
|
||||
export const nearKeyStore = new keyStores.BrowserLocalStorageKeyStore();
|
||||
|
||||
export const getNearConnectionConfig = (): ConnectConfig =>
|
||||
CLUSTER === "mainnet"
|
||||
? {
|
||||
networkId: "mainnet",
|
||||
keyStore: nearKeyStore,
|
||||
nodeUrl: "https://rpc.mainnet.near.org",
|
||||
walletUrl: "https://wallet.mainnet.near.org",
|
||||
helperUrl: "https://helper.mainnet.near.org",
|
||||
headers: {},
|
||||
}
|
||||
: CLUSTER === "testnet"
|
||||
? {
|
||||
networkId: "testnet",
|
||||
keyStore: nearKeyStore,
|
||||
nodeUrl: "https://rpc.testnet.near.org",
|
||||
walletUrl: "https://wallet.testnet.near.org",
|
||||
helperUrl: "https://helper.testnet.near.org",
|
||||
headers: {},
|
||||
}
|
||||
: {
|
||||
networkId: "sandbox",
|
||||
keyStore: nearKeyStore,
|
||||
nodeUrl: "http://localhost:3030",
|
||||
helperUrl: "",
|
||||
headers: {},
|
||||
};
|
||||
|
||||
export const NATIVE_NEAR_DECIMALS = 24;
|
||||
export const NATIVE_NEAR_PLACEHOLDER = "near";
|
||||
export const NATIVE_NEAR_WH_ADDRESS =
|
||||
"0000000000000000000000000000000000000000000000000000000000000000";
|
||||
|
||||
export const TOTAL_TRANSACTIONS_WORMHOLE = `https://europe-west3-wormhole-315720.cloudfunctions.net/mainnet-totals?groupBy=address`;
|
||||
|
||||
export const RECENT_TRANSACTIONS_WORMHOLE = `https://europe-west3-wormhole-315720.cloudfunctions.net/mainnet-recent?groupBy=address&numRows=2`;
|
||||
|
|
|
@ -0,0 +1,476 @@
|
|||
import {
|
||||
ChainId,
|
||||
ChainName,
|
||||
CHAIN_ID_NEAR,
|
||||
coalesceChainId,
|
||||
hexToUint8Array,
|
||||
uint8ArrayToHex,
|
||||
WormholeWrappedInfo,
|
||||
} from "@certusone/wormhole-sdk";
|
||||
import { _parseVAAAlgorand } from "@certusone/wormhole-sdk/lib/esm/algorand";
|
||||
import { Wallet } from "@near-wallet-selector/core/lib/wallet";
|
||||
import BN from "bn.js";
|
||||
import { arrayify, sha256, zeroPad } from "ethers/lib/utils";
|
||||
import { Account, connect } from "near-api-js";
|
||||
import { FunctionCallOptions } from "near-api-js/lib/account";
|
||||
import { FinalExecutionOutcome } from "near-api-js/lib/providers";
|
||||
import { getNearConnectionConfig } from "./consts";
|
||||
|
||||
export const makeNearAccount = async (senderAddr: string) =>
|
||||
await (await connect(getNearConnectionConfig())).account(senderAddr);
|
||||
|
||||
export const signAndSendTransactions = async (
|
||||
account: Account,
|
||||
wallet: Wallet,
|
||||
messages: FunctionCallOptions[]
|
||||
): Promise<FinalExecutionOutcome> => {
|
||||
// the browser wallet's signAndSendTransactions call navigates away from the page which is incompatible with the current app design
|
||||
if (wallet.type === "browser" && account) {
|
||||
let lastReceipt: FinalExecutionOutcome | null = null;
|
||||
for (const message of messages) {
|
||||
lastReceipt = await account.functionCall(message);
|
||||
}
|
||||
if (!lastReceipt) {
|
||||
throw new Error("An error occurred while fetching the transaction info");
|
||||
}
|
||||
return lastReceipt;
|
||||
}
|
||||
const receipts = await wallet.signAndSendTransactions({
|
||||
transactions: messages.map((options) => ({
|
||||
signerId: wallet.id,
|
||||
receiverId: options.contractId,
|
||||
actions: [
|
||||
{
|
||||
type: "FunctionCall",
|
||||
params: {
|
||||
methodName: options.methodName,
|
||||
args: options.args,
|
||||
gas: options.gas?.toString() || "0",
|
||||
deposit: options.attachedDeposit?.toString() || "0",
|
||||
},
|
||||
},
|
||||
],
|
||||
})),
|
||||
});
|
||||
if (!receipts || receipts.length === 0) {
|
||||
throw new Error("An error occurred while fetching the transaction info");
|
||||
}
|
||||
return receipts[receipts.length - 1];
|
||||
};
|
||||
|
||||
export function getIsWrappedAssetNear(
|
||||
tokenBridge: string,
|
||||
asset: string
|
||||
): boolean {
|
||||
return asset.endsWith("." + tokenBridge);
|
||||
}
|
||||
|
||||
export async function lookupHash(
|
||||
account: Account,
|
||||
tokenBridge: string,
|
||||
hash: string
|
||||
): Promise<[boolean, string]> {
|
||||
return await account.viewFunction(tokenBridge, "hash_lookup", {
|
||||
hash,
|
||||
});
|
||||
}
|
||||
|
||||
export async function getOriginalAssetNear(
|
||||
client: Account,
|
||||
tokenAccount: string,
|
||||
assetAccount: string
|
||||
): Promise<WormholeWrappedInfo> {
|
||||
let retVal: WormholeWrappedInfo = {
|
||||
isWrapped: false,
|
||||
chainId: CHAIN_ID_NEAR,
|
||||
assetAddress: new Uint8Array(),
|
||||
};
|
||||
retVal.isWrapped = await getIsWrappedAssetNear(tokenAccount, assetAccount);
|
||||
if (!retVal.isWrapped) {
|
||||
retVal.assetAddress = assetAccount
|
||||
? arrayify(sha256(Buffer.from(assetAccount)))
|
||||
: zeroPad(arrayify("0x"), 32);
|
||||
return retVal;
|
||||
}
|
||||
|
||||
let buf = await client.viewFunction(tokenAccount, "get_original_asset", {
|
||||
token: assetAccount,
|
||||
});
|
||||
|
||||
retVal.chainId = buf[1];
|
||||
retVal.assetAddress = hexToUint8Array(buf[0]);
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
export function getEmitterAddressNear(programAddress: string): string {
|
||||
return uint8ArrayToHex(arrayify(sha256(Buffer.from(programAddress, "utf8"))));
|
||||
}
|
||||
|
||||
export function parseSequenceFromLogNear(
|
||||
result: FinalExecutionOutcome
|
||||
): string {
|
||||
let sequence = "";
|
||||
for (const o of result.receipts_outcome) {
|
||||
for (const l of o.outcome.logs) {
|
||||
if (l.startsWith("EVENT_JSON:")) {
|
||||
const body = JSON.parse(l.slice(11));
|
||||
if (body.standard === "wormhole" && body.event === "publish") {
|
||||
return body.seq;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return sequence;
|
||||
}
|
||||
|
||||
export async function getForeignAssetNear(
|
||||
client: Account,
|
||||
tokenAccount: string,
|
||||
chain: ChainId | ChainName,
|
||||
contract: string
|
||||
): Promise<string | null> {
|
||||
const chainId = coalesceChainId(chain);
|
||||
|
||||
let ret = await client.viewFunction(tokenAccount, "get_foreign_asset", {
|
||||
chain: chainId,
|
||||
address: contract,
|
||||
});
|
||||
if (ret === "") return null;
|
||||
else return ret;
|
||||
}
|
||||
|
||||
export async function getIsTransferCompletedNear(
|
||||
client: Account,
|
||||
tokenAccount: string,
|
||||
signedVAA: Uint8Array
|
||||
): Promise<boolean> {
|
||||
// Could we just pass in the vaa already as hex?
|
||||
let vaa = Buffer.from(signedVAA).toString("hex");
|
||||
|
||||
return (
|
||||
await client.viewFunction(tokenAccount, "is_transfer_completed", {
|
||||
vaa: vaa,
|
||||
})
|
||||
)[1];
|
||||
}
|
||||
|
||||
export async function transferTokenFromNear(
|
||||
client: Account,
|
||||
coreBridge: string,
|
||||
tokenBridge: string,
|
||||
assetId: string,
|
||||
qty: bigint,
|
||||
receiver: Uint8Array,
|
||||
chain: ChainId | ChainName,
|
||||
fee: bigint,
|
||||
payload: string = ""
|
||||
): Promise<FunctionCallOptions[]> {
|
||||
let isWrapped = getIsWrappedAssetNear(tokenBridge, assetId);
|
||||
|
||||
let message_fee = await client.viewFunction(coreBridge, "message_fee", {});
|
||||
|
||||
if (isWrapped) {
|
||||
return [
|
||||
{
|
||||
contractId: tokenBridge,
|
||||
methodName: "send_transfer_wormhole_token",
|
||||
args: {
|
||||
token: assetId,
|
||||
amount: qty.toString(10),
|
||||
receiver: uint8ArrayToHex(receiver),
|
||||
chain: chain,
|
||||
fee: fee.toString(10),
|
||||
payload: payload,
|
||||
message_fee: message_fee,
|
||||
},
|
||||
attachedDeposit: new BN(message_fee + 1),
|
||||
gas: new BN("100000000000000"),
|
||||
},
|
||||
];
|
||||
} else {
|
||||
const msgs = [];
|
||||
let bal = await client.viewFunction(assetId, "storage_balance_of", {
|
||||
account_id: tokenBridge,
|
||||
});
|
||||
if (bal === null) {
|
||||
// Looks like we have to stake some storage for this asset
|
||||
// for the token bridge...
|
||||
msgs.push({
|
||||
contractId: assetId,
|
||||
methodName: "storage_deposit",
|
||||
args: { account_id: tokenBridge, registration_only: true },
|
||||
gas: new BN("100000000000000"),
|
||||
attachedDeposit: new BN("2000000000000000000000"), // 0.002 NEAR
|
||||
});
|
||||
}
|
||||
|
||||
if (message_fee > 0) {
|
||||
let bank = await client.viewFunction(tokenBridge, "bank_balance", {
|
||||
acct: client.accountId,
|
||||
});
|
||||
|
||||
if (!bank[0]) {
|
||||
msgs.push({
|
||||
contractId: tokenBridge,
|
||||
methodName: "register_bank",
|
||||
args: {},
|
||||
gas: new BN("100000000000000"),
|
||||
attachedDeposit: new BN("2000000000000000000000"), // 0.002 NEAR
|
||||
});
|
||||
}
|
||||
|
||||
if (bank[1] < message_fee) {
|
||||
msgs.push({
|
||||
contractId: tokenBridge,
|
||||
methodName: "fill_bank",
|
||||
args: {},
|
||||
gas: new BN("100000000000000"),
|
||||
attachedDeposit: new BN(message_fee),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
msgs.push({
|
||||
contractId: assetId,
|
||||
methodName: "ft_transfer_call",
|
||||
args: {
|
||||
receiver_id: tokenBridge,
|
||||
amount: qty.toString(10),
|
||||
msg: JSON.stringify({
|
||||
receiver: uint8ArrayToHex(receiver),
|
||||
chain: chain,
|
||||
fee: fee.toString(10),
|
||||
payload: payload,
|
||||
message_fee: message_fee,
|
||||
}),
|
||||
},
|
||||
attachedDeposit: new BN(1),
|
||||
gas: new BN("100000000000000"),
|
||||
});
|
||||
return msgs;
|
||||
}
|
||||
}
|
||||
|
||||
export async function transferNearFromNear(
|
||||
client: Account,
|
||||
coreBridge: string,
|
||||
tokenBridge: string,
|
||||
qty: bigint,
|
||||
receiver: Uint8Array,
|
||||
chain: ChainId | ChainName,
|
||||
fee: bigint,
|
||||
payload: string = ""
|
||||
): Promise<FunctionCallOptions[]> {
|
||||
let message_fee = await client.viewFunction(coreBridge, "message_fee", {});
|
||||
|
||||
return [
|
||||
{
|
||||
contractId: tokenBridge,
|
||||
methodName: "send_transfer_near",
|
||||
args: {
|
||||
receiver: uint8ArrayToHex(receiver),
|
||||
chain: chain,
|
||||
fee: fee.toString(10),
|
||||
payload: payload,
|
||||
message_fee: message_fee,
|
||||
},
|
||||
attachedDeposit: new BN(qty.toString(10)).add(new BN(message_fee)),
|
||||
gas: new BN("100000000000000"),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
export async function redeemOnNear(
|
||||
client: Account,
|
||||
tokenBridge: string,
|
||||
vaa: Uint8Array
|
||||
): Promise<FunctionCallOptions[]> {
|
||||
const msgs = [];
|
||||
let p = _parseVAAAlgorand(vaa);
|
||||
|
||||
if (p.ToChain !== CHAIN_ID_NEAR) {
|
||||
throw new Error("Not destined for NEAR");
|
||||
}
|
||||
|
||||
let user = await client.viewFunction(tokenBridge, "hash_lookup", {
|
||||
hash: uint8ArrayToHex(p.ToAddress as Uint8Array),
|
||||
});
|
||||
|
||||
if (!user[0]) {
|
||||
throw new Error(
|
||||
"Unregistered receiver (receiving account is not registered)"
|
||||
);
|
||||
}
|
||||
|
||||
user = user[1];
|
||||
|
||||
let token = await getForeignAssetNear(
|
||||
client,
|
||||
tokenBridge,
|
||||
p.FromChain as ChainId,
|
||||
p.Contract as string
|
||||
);
|
||||
|
||||
if (token === "") {
|
||||
throw new Error("Unregistered token (this been attested yet?)");
|
||||
}
|
||||
|
||||
if (
|
||||
(p.Contract as string) !==
|
||||
"0000000000000000000000000000000000000000000000000000000000000000"
|
||||
) {
|
||||
let bal = await client.viewFunction(token as string, "storage_balance_of", {
|
||||
account_id: user,
|
||||
});
|
||||
|
||||
if (bal === null) {
|
||||
console.log("Registering ", user, " for ", token);
|
||||
msgs.push({
|
||||
contractId: token as string,
|
||||
methodName: "storage_deposit",
|
||||
args: { account_id: user, registration_only: true },
|
||||
gas: new BN("100000000000000"),
|
||||
attachedDeposit: new BN("2000000000000000000000"), // 0.002 NEAR
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
p.Fee !== undefined &&
|
||||
Buffer.compare(
|
||||
p.Fee,
|
||||
Buffer.from(
|
||||
"0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"hex"
|
||||
)
|
||||
) !== 0
|
||||
) {
|
||||
let bal = await client.viewFunction(
|
||||
token as string,
|
||||
"storage_balance_of",
|
||||
{
|
||||
account_id: client.accountId,
|
||||
}
|
||||
);
|
||||
|
||||
if (bal === null) {
|
||||
console.log("Registering ", client.accountId, " for ", token);
|
||||
msgs.push({
|
||||
contractId: token as string,
|
||||
methodName: "storage_deposit",
|
||||
args: { account_id: client.accountId, registration_only: true },
|
||||
gas: new BN("100000000000000"),
|
||||
attachedDeposit: new BN("2000000000000000000000"), // 0.002 NEAR
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
msgs.push({
|
||||
contractId: tokenBridge,
|
||||
methodName: "submit_vaa",
|
||||
args: {
|
||||
vaa: uint8ArrayToHex(vaa),
|
||||
},
|
||||
attachedDeposit: new BN("100000000000000000000000"),
|
||||
gas: new BN("150000000000000"),
|
||||
});
|
||||
|
||||
msgs.push({
|
||||
contractId: tokenBridge,
|
||||
methodName: "submit_vaa",
|
||||
args: {
|
||||
vaa: uint8ArrayToHex(vaa),
|
||||
},
|
||||
attachedDeposit: new BN("100000000000000000000000"),
|
||||
gas: new BN("150000000000000"),
|
||||
});
|
||||
return msgs;
|
||||
}
|
||||
|
||||
export async function createWrappedOnNear(
|
||||
client: Account,
|
||||
tokenBridge: string,
|
||||
attestVAA: Uint8Array
|
||||
): Promise<FunctionCallOptions[]> {
|
||||
const msgs = [];
|
||||
let vaa = Buffer.from(attestVAA).toString("hex");
|
||||
|
||||
let res = await client.viewFunction(tokenBridge, "deposit_estimates", {});
|
||||
|
||||
msgs.push({
|
||||
contractId: tokenBridge,
|
||||
methodName: "submit_vaa",
|
||||
args: { vaa: vaa },
|
||||
attachedDeposit: new BN(res[1]),
|
||||
gas: new BN("150000000000000"),
|
||||
});
|
||||
|
||||
msgs.push({
|
||||
contractId: tokenBridge,
|
||||
methodName: "submit_vaa",
|
||||
args: { vaa: vaa },
|
||||
attachedDeposit: new BN(res[1]),
|
||||
gas: new BN("150000000000000"),
|
||||
});
|
||||
return msgs;
|
||||
}
|
||||
|
||||
export async function attestTokenFromNear(
|
||||
client: Account,
|
||||
coreBridge: string,
|
||||
tokenBridge: string,
|
||||
asset: string
|
||||
): Promise<FunctionCallOptions[]> {
|
||||
const msgs = [];
|
||||
let message_fee = await client.viewFunction(coreBridge, "message_fee", {});
|
||||
// Non-signing event
|
||||
if (!getIsWrappedAssetNear(tokenBridge, asset)) {
|
||||
// Non-signing event that hits the RPC
|
||||
let res = await client.viewFunction(tokenBridge, "hash_account", {
|
||||
account: asset,
|
||||
});
|
||||
|
||||
// if res[0] == false, the account has not been
|
||||
// registered... The first user to attest a non-wormhole token
|
||||
// is gonna have to pay for the space
|
||||
if (!res[0]) {
|
||||
// Signing event
|
||||
msgs.push({
|
||||
contractId: tokenBridge,
|
||||
methodName: "register_account",
|
||||
args: { account: asset },
|
||||
gas: new BN("100000000000000"),
|
||||
attachedDeposit: new BN("2000000000000000000000"), // 0.002 NEAR
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
msgs.push({
|
||||
contractId: tokenBridge,
|
||||
methodName: "attest_token",
|
||||
args: { token: asset, message_fee: message_fee },
|
||||
attachedDeposit: new BN("3000000000000000000000").add(new BN(message_fee)), // 0.003 NEAR
|
||||
gas: new BN("100000000000000"),
|
||||
});
|
||||
return msgs;
|
||||
}
|
||||
|
||||
export async function attestNearFromNear(
|
||||
client: Account,
|
||||
coreBridge: string,
|
||||
tokenBridge: string
|
||||
): Promise<FunctionCallOptions[]> {
|
||||
let message_fee =
|
||||
(await client.viewFunction(coreBridge, "message_fee", {})) + 1;
|
||||
|
||||
return [
|
||||
{
|
||||
contractId: tokenBridge,
|
||||
methodName: "attest_near",
|
||||
args: { message_fee: message_fee },
|
||||
attachedDeposit: new BN(message_fee),
|
||||
gas: new BN("100000000000000"),
|
||||
},
|
||||
];
|
||||
}
|
Loading…
Reference in New Issue