mirror of https://github.com/certusone/oyster.git
Working sol -> eth wormhole (#87)
* Workin sol -> eth wormhole * remove console * fixed selecting same chains and showing same balances
This commit is contained in:
parent
dce6790f4e
commit
2fddc4d024
|
@ -28,24 +28,6 @@ export function Input(props: {
|
||||||
}) {
|
}) {
|
||||||
const { connected } = useWallet();
|
const { connected } = useWallet();
|
||||||
const [lastAmount, setLastAmount] = useState<string>('');
|
const [lastAmount, setLastAmount] = useState<string>('');
|
||||||
const { userAccounts } = useUserAccounts();
|
|
||||||
const [balance, setBalance] = useState<number>(0);
|
|
||||||
const mint = useMint(props.asset?.startsWith('0x') ? '' : props.asset);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (props.chain === ASSET_CHAIN.Solana) {
|
|
||||||
const currentAccount = userAccounts?.find(
|
|
||||||
a => a.info.mint.toBase58() === props.asset,
|
|
||||||
);
|
|
||||||
if (currentAccount && mint) {
|
|
||||||
setBalance(
|
|
||||||
currentAccount.info.amount.toNumber() / Math.pow(10, mint.decimals),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
setBalance(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [props.asset, props.chain, userAccounts, mint]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={`dashed-input-container ${props.className}`}>
|
<div className={`dashed-input-container ${props.className}`}>
|
||||||
|
@ -53,25 +35,14 @@ export function Input(props: {
|
||||||
<div className="input-chain">
|
<div className="input-chain">
|
||||||
<TokenChain chain={props.chain} className={'input-icon'} />
|
<TokenChain chain={props.chain} className={'input-icon'} />
|
||||||
{chainToName(props.chain)}
|
{chainToName(props.chain)}
|
||||||
{props.chain !== ASSET_CHAIN.Solana ? (
|
<div
|
||||||
typeof props.balance === 'number' && (
|
className="balance"
|
||||||
<div
|
onClick={() =>
|
||||||
className="balance"
|
props.onInputChange && props.onInputChange(props.balance)
|
||||||
onClick={() =>
|
}
|
||||||
props.onInputChange && props.onInputChange(props.balance)
|
>
|
||||||
}
|
{props.balance}
|
||||||
>
|
</div>
|
||||||
{props.balance.toFixed(10)}
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
) : (
|
|
||||||
<div
|
|
||||||
className="balance"
|
|
||||||
onClick={() => props.onInputChange && props.onInputChange(balance)}
|
|
||||||
>
|
|
||||||
{balance.toFixed(10)}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
<div className="input-container">
|
<div className="input-container">
|
||||||
<NumericInput
|
<NumericInput
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { Table } from 'antd';
|
import { Button, Table, Tabs, notification } from 'antd';
|
||||||
import React from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
|
|
||||||
import './index.less';
|
import './index.less';
|
||||||
|
|
||||||
|
@ -12,22 +12,39 @@ import { toChainSymbol } from '../../contexts/chainPair';
|
||||||
import {
|
import {
|
||||||
formatUSD,
|
formatUSD,
|
||||||
shortenAddress,
|
shortenAddress,
|
||||||
EtherscanLink,
|
|
||||||
ExplorerLink,
|
|
||||||
Identicon,
|
Identicon,
|
||||||
|
programIds,
|
||||||
} from '@oyster/common';
|
} from '@oyster/common';
|
||||||
import { useWormholeTransactions } from '../../hooks/useWormholeTransactions';
|
import { useWormholeTransactions } from '../../hooks/useWormholeTransactions';
|
||||||
import { ASSET_CHAIN } from '../../utils/assets';
|
import { ASSET_CHAIN } from '../../utils/assets';
|
||||||
import { TokenChain } from '../TokenDisplay/tokenChain';
|
import { TokenChain } from '../TokenDisplay/tokenChain';
|
||||||
import bs58 from 'bs58';
|
import bs58 from 'bs58';
|
||||||
|
import { SyncOutlined } from '@ant-design/icons';
|
||||||
|
import { typeToIcon } from '../Transfer';
|
||||||
|
import { ProgressUpdate } from '../../models/bridge';
|
||||||
|
import { WormholeFactory } from '../../contracts/WormholeFactory';
|
||||||
|
import { useEthereum } from '../../contexts';
|
||||||
|
import { useBridge } from '../../contexts/bridge';
|
||||||
|
|
||||||
TimeAgo.addDefaultLocale(en);
|
TimeAgo.addDefaultLocale(en);
|
||||||
const timeAgo = new TimeAgo('en-US');
|
const timeAgo = new TimeAgo('en-US');
|
||||||
|
|
||||||
export const RecentTransactionsTable = () => {
|
const { TabPane } = Tabs;
|
||||||
const { loading: loadingTransfers, transfers } = useWormholeTransactions();
|
|
||||||
|
|
||||||
const columns = [
|
export const RecentTransactionsTable = (props: {
|
||||||
|
showUserTransactions?: boolean;
|
||||||
|
}) => {
|
||||||
|
const {
|
||||||
|
loading: loadingTransfers,
|
||||||
|
transfers,
|
||||||
|
userTransfers,
|
||||||
|
} = useWormholeTransactions();
|
||||||
|
const { provider } = useEthereum();
|
||||||
|
const bridge = useBridge();
|
||||||
|
|
||||||
|
const [completedVAAs, setCompletedVAAs] = useState<Array<string>>([]);
|
||||||
|
|
||||||
|
const baseColumns = [
|
||||||
{
|
{
|
||||||
title: '',
|
title: '',
|
||||||
dataIndex: 'logo',
|
dataIndex: 'logo',
|
||||||
|
@ -68,31 +85,29 @@ export const RecentTransactionsTable = () => {
|
||||||
dataIndex: 'symbol',
|
dataIndex: 'symbol',
|
||||||
key: 'symbol',
|
key: 'symbol',
|
||||||
render(text: string, record: any) {
|
render(text: string, record: any) {
|
||||||
|
const urlText = record.symbol || record.address;
|
||||||
return {
|
return {
|
||||||
props: { style: {} },
|
props: { style: {} },
|
||||||
children: record.symbol ? (
|
children:
|
||||||
<Link
|
record.lockup.assetChain === ASSET_CHAIN.Solana ? (
|
||||||
to={`/move?from=${toChainSymbol(record.chain)}&token=${
|
<a
|
||||||
record.symbol
|
href={`https://explorer.solana.com/address/${record.address}`}
|
||||||
}`}
|
// eslint-disable-next-line react/jsx-no-target-blank
|
||||||
>
|
target="_blank"
|
||||||
<span style={{ display: 'inline-flex', alignItems: 'center' }}>
|
title={urlText}
|
||||||
{record.symbol}
|
>
|
||||||
</span>
|
{record.symbol || shortenAddress(urlText, 5)}
|
||||||
</Link>
|
</a>
|
||||||
) : record.lockup.assetChain === ASSET_CHAIN.Solana ? (
|
) : (
|
||||||
<ExplorerLink
|
<a
|
||||||
address={record.address}
|
href={`https://etherscan.io/address/${record.address}`}
|
||||||
length={5}
|
// eslint-disable-next-line react/jsx-no-target-blank
|
||||||
type={'address'}
|
target="_blank"
|
||||||
/>
|
title={urlText}
|
||||||
) : (
|
>
|
||||||
<EtherscanLink
|
{record.symbol || shortenAddress(urlText, 5)}
|
||||||
address={record.address}
|
</a>
|
||||||
type={'address'}
|
),
|
||||||
length={5}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -138,6 +153,9 @@ export const RecentTransactionsTable = () => {
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
];
|
||||||
|
const columns = [
|
||||||
|
...baseColumns,
|
||||||
{
|
{
|
||||||
title: 'Status',
|
title: 'Status',
|
||||||
dataIndex: 'status',
|
dataIndex: 'status',
|
||||||
|
@ -154,20 +172,203 @@ export const RecentTransactionsTable = () => {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const userColumns = [
|
||||||
|
...baseColumns,
|
||||||
|
{
|
||||||
|
title: 'Status',
|
||||||
|
dataIndex: 'status',
|
||||||
|
key: 'status',
|
||||||
|
render(text: string, record: any) {
|
||||||
|
const status =
|
||||||
|
completedVAAs.indexOf(record.txhash) > 0
|
||||||
|
? 'Completed'
|
||||||
|
: record.status;
|
||||||
|
return {
|
||||||
|
props: { style: {} },
|
||||||
|
children: (
|
||||||
|
<>
|
||||||
|
<span className={`${record.status?.toLowerCase()}`}>
|
||||||
|
{status}
|
||||||
|
</span>
|
||||||
|
{status === 'Failed' ? (
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
const NotificationContent = () => {
|
||||||
|
const [activeSteps, setActiveSteps] = useState<
|
||||||
|
ProgressUpdate[]
|
||||||
|
>([]);
|
||||||
|
let counter = 0;
|
||||||
|
useEffect(() => {
|
||||||
|
(async () => {
|
||||||
|
const signer = provider?.getSigner();
|
||||||
|
if (!signer || !bridge) {
|
||||||
|
setActiveSteps([
|
||||||
|
...activeSteps,
|
||||||
|
{
|
||||||
|
message: 'Connect your Ethereum Wallet',
|
||||||
|
type: 'error',
|
||||||
|
group: 'error',
|
||||||
|
step: counter++,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
const lockup = record.lockup;
|
||||||
|
let vaa = lockup.vaa;
|
||||||
|
for (let i = vaa.length; i > 0; i--) {
|
||||||
|
if (vaa[i] == 0xff) {
|
||||||
|
vaa = vaa.slice(0, i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let signatures = await bridge.fetchSignatureStatus(
|
||||||
|
lockup.signatureAccount,
|
||||||
|
);
|
||||||
|
let sigData = Buffer.of(
|
||||||
|
...signatures.reduce(
|
||||||
|
(previousValue, currentValue) => {
|
||||||
|
previousValue.push(currentValue.index);
|
||||||
|
previousValue.push(...currentValue.signature);
|
||||||
|
|
||||||
|
return previousValue;
|
||||||
|
},
|
||||||
|
new Array<number>(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
vaa = Buffer.concat([
|
||||||
|
vaa.slice(0, 5),
|
||||||
|
Buffer.of(signatures.length),
|
||||||
|
sigData,
|
||||||
|
vaa.slice(6),
|
||||||
|
]);
|
||||||
|
let wh = WormholeFactory.connect(
|
||||||
|
programIds().wormhole.bridge,
|
||||||
|
signer,
|
||||||
|
);
|
||||||
|
let group = 'Finalizing transfer';
|
||||||
|
setActiveSteps([
|
||||||
|
...activeSteps,
|
||||||
|
{
|
||||||
|
message: 'Sign the claim...',
|
||||||
|
type: 'wait',
|
||||||
|
group,
|
||||||
|
step: counter++,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
let tx = await wh.submitVAA(vaa);
|
||||||
|
setActiveSteps([
|
||||||
|
...activeSteps,
|
||||||
|
{
|
||||||
|
message:
|
||||||
|
'Waiting for tokens unlock to be mined...',
|
||||||
|
type: 'wait',
|
||||||
|
group,
|
||||||
|
step: counter++,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
await tx.wait(1);
|
||||||
|
setActiveSteps([
|
||||||
|
...activeSteps,
|
||||||
|
{
|
||||||
|
message: 'Execution of VAA succeeded',
|
||||||
|
type: 'done',
|
||||||
|
group,
|
||||||
|
step: counter++,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
}, [setActiveSteps]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
textAlign: 'left',
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{(() => {
|
||||||
|
let group = '';
|
||||||
|
return activeSteps.map((step, i) => {
|
||||||
|
let prevGroup = group;
|
||||||
|
group = step.group;
|
||||||
|
let newGroup = prevGroup !== group;
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{newGroup && <span>{group}</span>}
|
||||||
|
<span style={{ marginLeft: 15 }}>
|
||||||
|
{typeToIcon(
|
||||||
|
step.type,
|
||||||
|
activeSteps.length - 1 === i,
|
||||||
|
)}{' '}
|
||||||
|
{step.message}
|
||||||
|
</span>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
})()}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
notification.open({
|
||||||
|
message: '',
|
||||||
|
duration: 0,
|
||||||
|
placement: 'bottomLeft',
|
||||||
|
description: <NotificationContent />,
|
||||||
|
className: 'custom-class',
|
||||||
|
style: {
|
||||||
|
width: 500,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
shape="circle"
|
||||||
|
size="large"
|
||||||
|
type="text"
|
||||||
|
style={{ color: '#547595', fontSize: '18px' }}
|
||||||
|
title={'Retry Transaction'}
|
||||||
|
icon={<SyncOutlined />}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
return (
|
return (
|
||||||
<div id={'recent-tx-container'}>
|
<div id={'recent-tx-container'}>
|
||||||
<div className={'home-subtitle'} style={{ marginBottom: '70px' }}>
|
<div className={'home-subtitle'} style={{ marginBottom: '70px' }}>
|
||||||
Recent Transactions
|
Transactions
|
||||||
</div>
|
</div>
|
||||||
<Table
|
<Tabs defaultActiveKey="1" centered>
|
||||||
scroll={{
|
<TabPane tab="Recent Transactions" key="1">
|
||||||
scrollToFirstRowOnChange: false,
|
<Table
|
||||||
x: 900,
|
scroll={{
|
||||||
}}
|
scrollToFirstRowOnChange: false,
|
||||||
dataSource={transfers.sort((a, b) => b.date - a.date)}
|
x: 900,
|
||||||
columns={columns}
|
}}
|
||||||
loading={loadingTransfers}
|
dataSource={transfers.sort((a, b) => b.date - a.date)}
|
||||||
/>
|
columns={columns}
|
||||||
|
loading={loadingTransfers}
|
||||||
|
/>
|
||||||
|
</TabPane>
|
||||||
|
<TabPane tab="My Transactions" key="2">
|
||||||
|
<Table
|
||||||
|
scroll={{
|
||||||
|
scrollToFirstRowOnChange: false,
|
||||||
|
x: 900,
|
||||||
|
}}
|
||||||
|
dataSource={userTransfers.sort((a, b) => b.date - a.date)}
|
||||||
|
columns={userColumns}
|
||||||
|
loading={loadingTransfers}
|
||||||
|
/>
|
||||||
|
</TabPane>
|
||||||
|
</Tabs>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -18,6 +18,7 @@ import { useTokenChainPairState } from '../../contexts/chainPair';
|
||||||
import { LABELS } from '../../constants';
|
import { LABELS } from '../../constants';
|
||||||
import { useCorrectNetwork } from '../../hooks/useCorrectNetwork';
|
import { useCorrectNetwork } from '../../hooks/useCorrectNetwork';
|
||||||
import { RecentTransactionsTable } from '../RecentTransactionsTable';
|
import { RecentTransactionsTable } from '../RecentTransactionsTable';
|
||||||
|
import { useBridge } from '../../contexts/bridge';
|
||||||
|
|
||||||
const { useConnection } = contexts.Connection;
|
const { useConnection } = contexts.Connection;
|
||||||
const { useWallet } = contexts.Wallet;
|
const { useWallet } = contexts.Wallet;
|
||||||
|
@ -40,6 +41,7 @@ export const typeToIcon = (type: string, isLast: boolean) => {
|
||||||
|
|
||||||
export const Transfer = () => {
|
export const Transfer = () => {
|
||||||
const connection = useConnection();
|
const connection = useConnection();
|
||||||
|
const bridge = useBridge();
|
||||||
const { wallet, connected } = useWallet();
|
const { wallet, connected } = useWallet();
|
||||||
const { provider, tokenMap } = useEthereum();
|
const { provider, tokenMap } = useEthereum();
|
||||||
const hasCorrespondingNetworks = useCorrectNetwork();
|
const hasCorrespondingNetworks = useCorrectNetwork();
|
||||||
|
@ -50,6 +52,7 @@ export const Transfer = () => {
|
||||||
setMintAddress,
|
setMintAddress,
|
||||||
setLastTypedAccount,
|
setLastTypedAccount,
|
||||||
} = useTokenChainPairState();
|
} = useTokenChainPairState();
|
||||||
|
|
||||||
const [request, setRequest] = useState<TransferRequest>({
|
const [request, setRequest] = useState<TransferRequest>({
|
||||||
from: ASSET_CHAIN.Ethereum,
|
from: ASSET_CHAIN.Ethereum,
|
||||||
to: ASSET_CHAIN.Solana,
|
to: ASSET_CHAIN.Solana,
|
||||||
|
@ -77,7 +80,7 @@ export const Transfer = () => {
|
||||||
to: B.chain,
|
to: B.chain,
|
||||||
info: A.info,
|
info: A.info,
|
||||||
});
|
});
|
||||||
}, [A, B, mintAddress]);
|
}, [A, B, mintAddress, A.info]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -92,7 +95,9 @@ export const Transfer = () => {
|
||||||
onChain={(chain: ASSET_CHAIN) => {
|
onChain={(chain: ASSET_CHAIN) => {
|
||||||
const from = A.chain;
|
const from = A.chain;
|
||||||
A.setChain(chain);
|
A.setChain(chain);
|
||||||
B.setChain(from);
|
if (B.chain === chain) {
|
||||||
|
B.setChain(from);
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
onInputChange={amount => {
|
onInputChange={amount => {
|
||||||
setLastTypedAccount(A.chain);
|
setLastTypedAccount(A.chain);
|
||||||
|
@ -126,7 +131,9 @@ export const Transfer = () => {
|
||||||
onChain={(chain: ASSET_CHAIN) => {
|
onChain={(chain: ASSET_CHAIN) => {
|
||||||
const to = B.chain;
|
const to = B.chain;
|
||||||
B.setChain(chain);
|
B.setChain(chain);
|
||||||
A.setChain(to);
|
if (A.chain === chain) {
|
||||||
|
A.setChain(to);
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
onInputChange={amount => {
|
onInputChange={amount => {
|
||||||
setLastTypedAccount(B.chain);
|
setLastTypedAccount(B.chain);
|
||||||
|
@ -171,6 +178,7 @@ export const Transfer = () => {
|
||||||
|
|
||||||
setActiveSteps(steps);
|
setActiveSteps(steps);
|
||||||
},
|
},
|
||||||
|
bridge,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,9 @@ import {
|
||||||
TransferRequestInfo,
|
TransferRequestInfo,
|
||||||
wrappedAssetMintKey,
|
wrappedAssetMintKey,
|
||||||
} from '../models/bridge';
|
} from '../models/bridge';
|
||||||
|
import { useBridge } from './bridge';
|
||||||
|
import { PublicKey } from '@solana/web3.js';
|
||||||
|
|
||||||
export interface TokenChainContextState {
|
export interface TokenChainContextState {
|
||||||
info?: TransferRequestInfo;
|
info?: TransferRequestInfo;
|
||||||
|
|
||||||
|
@ -91,11 +94,22 @@ export const useCurrencyLeg = (mintAddress: string) => {
|
||||||
const [chain, setChain] = useState(ASSET_CHAIN.Ethereum);
|
const [chain, setChain] = useState(ASSET_CHAIN.Ethereum);
|
||||||
const [info, setInfo] = useState<TransferRequestInfo>();
|
const [info, setInfo] = useState<TransferRequestInfo>();
|
||||||
const { userAccounts } = useUserAccounts();
|
const { userAccounts } = useUserAccounts();
|
||||||
|
const bridge = useBridge();
|
||||||
|
|
||||||
const { provider, tokens: ethTokens } = useEthereum();
|
const { provider, tokens: ethTokens } = useEthereum();
|
||||||
const { tokens: solTokens } = useConnectionConfig();
|
const { tokens: solTokens } = useConnectionConfig();
|
||||||
const connection = useConnection();
|
const connection = useConnection();
|
||||||
|
const defaultCoinInfo = {
|
||||||
|
address: '',
|
||||||
|
name: '',
|
||||||
|
balance: new BigNumber(0),
|
||||||
|
decimals: 0,
|
||||||
|
allowance: new BigNumber(0),
|
||||||
|
isWrapped: false,
|
||||||
|
chainID: 0,
|
||||||
|
assetAddress: new Buffer(0),
|
||||||
|
mint: '',
|
||||||
|
};
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!provider || !connection) {
|
if (!provider || !connection) {
|
||||||
return;
|
return;
|
||||||
|
@ -111,23 +125,44 @@ export const useCurrencyLeg = (mintAddress: string) => {
|
||||||
// sol asset on sol chain
|
// sol asset on sol chain
|
||||||
|
|
||||||
//let ethAddress: string = '';
|
//let ethAddress: string = '';
|
||||||
if (solToken) {
|
// console.log({ chain, solToken, ethToken });
|
||||||
// let signer = provider.getSigner();
|
if (chain === ASSET_CHAIN.Solana) {
|
||||||
// let e = WrappedAssetFactory.connect(asset, provider);
|
if (!solToken) {
|
||||||
// let addr = await signer.getAddress();
|
setInfo(defaultCoinInfo);
|
||||||
// let decimals = await e.decimals();
|
return;
|
||||||
// let symbol = await e.symbol();
|
}
|
||||||
|
|
||||||
// TODO: checked if mint is wrapped mint from eth...
|
// TODO: checked if mint is wrapped mint from eth...
|
||||||
|
const currentAccount = userAccounts?.find(
|
||||||
|
a => a.info.mint.toBase58() === solToken.address,
|
||||||
|
);
|
||||||
|
const assetMeta = await bridge?.fetchAssetMeta(
|
||||||
|
new PublicKey(solToken.address),
|
||||||
|
);
|
||||||
|
|
||||||
const accounts = userAccounts
|
if (!assetMeta || !currentAccount) {
|
||||||
.filter(a => a.info.mint.toBase58() === solToken.address)
|
setInfo(defaultCoinInfo);
|
||||||
.sort((a, b) => a.info.amount.toNumber() - b.info.amount.toNumber());
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
console.log(accounts);
|
let info = {
|
||||||
|
address: currentAccount.pubkey.toBase58(),
|
||||||
|
name: solToken.symbol,
|
||||||
|
balance: new BigNumber(currentAccount?.info.amount.toNumber() || 0),
|
||||||
|
allowance: new BigNumber(0),
|
||||||
|
decimals: solToken.decimals,
|
||||||
|
isWrapped: assetMeta.chain != ASSET_CHAIN.Solana,
|
||||||
|
chainID: assetMeta.chain,
|
||||||
|
assetAddress: assetMeta.address,
|
||||||
|
mint: solToken.address,
|
||||||
|
};
|
||||||
|
setInfo(info);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ethToken) {
|
if (chain === ASSET_CHAIN.Ethereum) {
|
||||||
|
if (!ethToken) {
|
||||||
|
setInfo(defaultCoinInfo);
|
||||||
|
return;
|
||||||
|
}
|
||||||
let signer = provider.getSigner();
|
let signer = provider.getSigner();
|
||||||
let e = WrappedAssetFactory.connect(mintAddress, provider);
|
let e = WrappedAssetFactory.connect(mintAddress, provider);
|
||||||
let addr = await signer.getAddress();
|
let addr = await signer.getAddress();
|
||||||
|
@ -170,12 +205,9 @@ export const useCurrencyLeg = (mintAddress: string) => {
|
||||||
address: info.assetAddress,
|
address: info.assetAddress,
|
||||||
chain: info.chainID,
|
chain: info.chainID,
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(mint.toBase58());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(info);
|
// console.log({ info });
|
||||||
|
|
||||||
setInfo(info);
|
setInfo(info);
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
|
@ -190,16 +222,13 @@ export const useCurrencyLeg = (mintAddress: string) => {
|
||||||
userAccounts,
|
userAccounts,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return useMemo(
|
return {
|
||||||
() => ({
|
amount: amount,
|
||||||
amount: amount,
|
setAmount: setAmount,
|
||||||
setAmount: setAmount,
|
chain: chain,
|
||||||
chain: chain,
|
setChain: setChain,
|
||||||
setChain: setChain,
|
info,
|
||||||
info,
|
};
|
||||||
}),
|
|
||||||
[amount, setAmount, chain, setChain],
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export function TokenChainPairProvider({ children = null as any }) {
|
export function TokenChainPairProvider({ children = null as any }) {
|
||||||
|
|
|
@ -5,6 +5,7 @@ import assert from 'assert';
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
import * as BufferLayout from 'buffer-layout';
|
import * as BufferLayout from 'buffer-layout';
|
||||||
import * as bs58 from 'bs58';
|
import * as bs58 from 'bs58';
|
||||||
|
import { AssetMeta } from '../models/bridge';
|
||||||
|
|
||||||
export interface Lockup {
|
export interface Lockup {
|
||||||
lockupAddress: PublicKey;
|
lockupAddress: PublicKey;
|
||||||
|
@ -71,7 +72,40 @@ class SolanaBridge {
|
||||||
data,
|
data,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
// fetchAssetMeta fetches the AssetMeta for an SPL token
|
||||||
|
async fetchAssetMeta(mint: PublicKey): Promise<AssetMeta> {
|
||||||
|
// @ts-ignore
|
||||||
|
let configKey = await this.getConfigKey();
|
||||||
|
let seeds: Array<Buffer> = [
|
||||||
|
Buffer.from('meta'),
|
||||||
|
configKey.toBuffer(),
|
||||||
|
mint.toBuffer(),
|
||||||
|
];
|
||||||
|
// @ts-ignore
|
||||||
|
let metaKey = (
|
||||||
|
await solanaWeb3.PublicKey.findProgramAddress(seeds, this.programID)
|
||||||
|
)[0];
|
||||||
|
let metaInfo = await this.connection.getAccountInfo(metaKey);
|
||||||
|
if (metaInfo == null || metaInfo.lamports == 0) {
|
||||||
|
return {
|
||||||
|
address: mint.toBuffer(),
|
||||||
|
chain: CHAIN_ID_SOLANA,
|
||||||
|
decimals: 0,
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
const dataLayout = BufferLayout.struct([
|
||||||
|
BufferLayout.u8('assetChain'),
|
||||||
|
BufferLayout.blob(32, 'assetAddress'),
|
||||||
|
]);
|
||||||
|
let wrappedMeta = dataLayout.decode(metaInfo?.data);
|
||||||
|
|
||||||
|
return {
|
||||||
|
address: wrappedMeta.assetAddress,
|
||||||
|
chain: wrappedMeta.assetChain,
|
||||||
|
decimals: 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
// fetchSignatureStatus fetches the signatures for a VAA
|
// fetchSignatureStatus fetches the signatures for a VAA
|
||||||
async fetchSignatureStatus(signatureStatus: PublicKey): Promise<Signature[]> {
|
async fetchSignatureStatus(signatureStatus: PublicKey): Promise<Signature[]> {
|
||||||
let signatureInfo = await this.connection.getAccountInfo(
|
let signatureInfo = await this.connection.getAccountInfo(
|
||||||
|
|
|
@ -4,13 +4,17 @@ import {
|
||||||
useConnectionConfig,
|
useConnectionConfig,
|
||||||
programIds,
|
programIds,
|
||||||
notify,
|
notify,
|
||||||
|
useWallet,
|
||||||
} from '@oyster/common';
|
} from '@oyster/common';
|
||||||
import { WORMHOLE_PROGRAM_ID, POSTVAA_INSTRUCTION } from '../utils/ids';
|
import {
|
||||||
|
WORMHOLE_PROGRAM_ID,
|
||||||
|
POSTVAA_INSTRUCTION,
|
||||||
|
TRANSFER_ASSETS_OUT_INSTRUCTION,
|
||||||
|
} from '../utils/ids';
|
||||||
import { ASSET_CHAIN } from '../utils/assets';
|
import { ASSET_CHAIN } from '../utils/assets';
|
||||||
import { useEthereum } from '../contexts';
|
import { useEthereum } from '../contexts';
|
||||||
import {
|
import {
|
||||||
Connection,
|
Connection,
|
||||||
ParsedInstruction,
|
|
||||||
PartiallyDecodedInstruction,
|
PartiallyDecodedInstruction,
|
||||||
PublicKey,
|
PublicKey,
|
||||||
} from '@solana/web3.js';
|
} from '@solana/web3.js';
|
||||||
|
@ -31,11 +35,6 @@ import { ethers } from 'ethers';
|
||||||
import { useBridge } from '../contexts/bridge';
|
import { useBridge } from '../contexts/bridge';
|
||||||
import { SolanaBridge } from '../core';
|
import { SolanaBridge } from '../core';
|
||||||
|
|
||||||
interface ParsedData {
|
|
||||||
info: any;
|
|
||||||
type: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
type WrappedTransferMeta = {
|
type WrappedTransferMeta = {
|
||||||
chain: number;
|
chain: number;
|
||||||
decimals: number;
|
decimals: number;
|
||||||
|
@ -53,6 +52,7 @@ type WrappedTransferMeta = {
|
||||||
txhash?: string;
|
txhash?: string;
|
||||||
date: number; // timestamp
|
date: number; // timestamp
|
||||||
status?: string;
|
status?: string;
|
||||||
|
owner?: string;
|
||||||
lockup?: any;
|
lockup?: any;
|
||||||
vaa?: any;
|
vaa?: any;
|
||||||
};
|
};
|
||||||
|
@ -105,7 +105,9 @@ const queryWrappedMetaTransactions = async (
|
||||||
|
|
||||||
const dec = new BN(10).pow(new BN(metaTransfer.assetDecimals));
|
const dec = new BN(10).pow(new BN(metaTransfer.assetDecimals));
|
||||||
const rawAmount = new BN(metaTransfer.amount, 2, 'le');
|
const rawAmount = new BN(metaTransfer.amount, 2, 'le');
|
||||||
const amount = rawAmount.div(dec).toNumber();
|
const div = rawAmount.div(dec).toNumber();
|
||||||
|
const mod = rawAmount.mod(dec).toNumber();
|
||||||
|
const amount = parseFloat(div + '.' + mod.toString());
|
||||||
const txhash = acc.publicKey.toBase58();
|
const txhash = acc.publicKey.toBase58();
|
||||||
|
|
||||||
transfers.set(assetAddress, {
|
transfers.set(assetAddress, {
|
||||||
|
@ -142,6 +144,14 @@ const queryWrappedMetaTransactions = async (
|
||||||
if (filteredInstructions && filteredInstructions?.length > 0) {
|
if (filteredInstructions && filteredInstructions?.length > 0) {
|
||||||
for (const ins of filteredInstructions) {
|
for (const ins of filteredInstructions) {
|
||||||
const data = bs58.decode((ins as PartiallyDecodedInstruction).data);
|
const data = bs58.decode((ins as PartiallyDecodedInstruction).data);
|
||||||
|
if (data[0] === TRANSFER_ASSETS_OUT_INSTRUCTION) {
|
||||||
|
try {
|
||||||
|
transfer.owner = (ins as PartiallyDecodedInstruction).accounts[10].toBase58();
|
||||||
|
} catch {
|
||||||
|
// Catch no owner
|
||||||
|
transfer.owner = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
data[0] === POSTVAA_INSTRUCTION &&
|
data[0] === POSTVAA_INSTRUCTION &&
|
||||||
|
@ -207,10 +217,12 @@ export const useWormholeTransactions = () => {
|
||||||
const { tokenMap: ethTokens } = useEthereum();
|
const { tokenMap: ethTokens } = useEthereum();
|
||||||
const { tokenMap } = useConnectionConfig();
|
const { tokenMap } = useConnectionConfig();
|
||||||
const { coinList } = useCoingecko();
|
const { coinList } = useCoingecko();
|
||||||
|
const { wallet, connected: walletConnected } = useWallet();
|
||||||
const bridge = useBridge();
|
const bridge = useBridge();
|
||||||
|
|
||||||
const [loading, setLoading] = useState<boolean>(true);
|
const [loading, setLoading] = useState<boolean>(true);
|
||||||
const [transfers, setTransfers] = useState<WrappedTransferMeta[]>([]);
|
const [transfers, setTransfers] = useState<WrappedTransferMeta[]>([]);
|
||||||
|
const [userTransfers, setUserTransfers] = useState<WrappedTransferMeta[]>([]);
|
||||||
const [amountInUSD, setAmountInUSD] = useState<number>(0);
|
const [amountInUSD, setAmountInUSD] = useState<number>(0);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -241,6 +253,16 @@ export const useWormholeTransactions = () => {
|
||||||
})();
|
})();
|
||||||
}, [connection, setTransfers]);
|
}, [connection, setTransfers]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (transfers && walletConnected && wallet?.publicKey) {
|
||||||
|
setUserTransfers(
|
||||||
|
transfers.filter(t => {
|
||||||
|
return t.owner === wallet?.publicKey?.toBase58();
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}, [wallet, walletConnected, transfers]);
|
||||||
|
|
||||||
const coingeckoTimer = useRef<number>(0);
|
const coingeckoTimer = useRef<number>(0);
|
||||||
const dataSourcePriceQuery = useCallback(async () => {
|
const dataSourcePriceQuery = useCallback(async () => {
|
||||||
if (transfers.length === 0) return;
|
if (transfers.length === 0) return;
|
||||||
|
@ -306,6 +328,7 @@ export const useWormholeTransactions = () => {
|
||||||
return {
|
return {
|
||||||
loading,
|
loading,
|
||||||
transfers,
|
transfers,
|
||||||
|
userTransfers,
|
||||||
totalInUSD: amountInUSD,
|
totalInUSD: amountInUSD,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -9,6 +9,7 @@ import { ProgressUpdate, TransferRequest } from './interface';
|
||||||
import BN from 'bn.js';
|
import BN from 'bn.js';
|
||||||
import { createLockAssetInstruction } from '../lock';
|
import { createLockAssetInstruction } from '../lock';
|
||||||
import { TransferOutProposalLayout } from '../transferOutProposal';
|
import { TransferOutProposalLayout } from '../transferOutProposal';
|
||||||
|
import { SolanaBridge } from '../../../core';
|
||||||
|
|
||||||
export const fromSolana = async (
|
export const fromSolana = async (
|
||||||
connection: Connection,
|
connection: Connection,
|
||||||
|
@ -16,17 +17,17 @@ export const fromSolana = async (
|
||||||
request: TransferRequest,
|
request: TransferRequest,
|
||||||
provider: ethers.providers.Web3Provider,
|
provider: ethers.providers.Web3Provider,
|
||||||
setProgress: (update: ProgressUpdate) => void,
|
setProgress: (update: ProgressUpdate) => void,
|
||||||
|
bridge?: SolanaBridge,
|
||||||
) => {
|
) => {
|
||||||
if (
|
if (
|
||||||
!request.asset ||
|
!request.asset ||
|
||||||
!request.amount ||
|
!request.amount ||
|
||||||
!request.recipient ||
|
|
||||||
!request.to ||
|
!request.to ||
|
||||||
!request.info
|
!request.info ||
|
||||||
|
!bridge
|
||||||
) {
|
) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const walletName = 'MetaMask';
|
|
||||||
const signer = provider?.getSigner();
|
const signer = provider?.getSigner();
|
||||||
request.recipient = Buffer.from((await signer.getAddress()).slice(2), 'hex');
|
request.recipient = Buffer.from((await signer.getAddress()).slice(2), 'hex');
|
||||||
const nonce = await provider.getTransactionCount(
|
const nonce = await provider.getTransactionCount(
|
||||||
|
@ -34,11 +35,6 @@ export const fromSolana = async (
|
||||||
'pending',
|
'pending',
|
||||||
);
|
);
|
||||||
|
|
||||||
const amountBN = ethers.utils.parseUnits(
|
|
||||||
request.amount.toString(),
|
|
||||||
request.info.decimals,
|
|
||||||
);
|
|
||||||
|
|
||||||
let counter = 0;
|
let counter = 0;
|
||||||
// check difference between lock/approve (invoke lock if allowance < amount)
|
// check difference between lock/approve (invoke lock if allowance < amount)
|
||||||
const steps = {
|
const steps = {
|
||||||
|
@ -66,7 +62,7 @@ export const fromSolana = async (
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let group = 'Lock assets';
|
let group = 'Initiate transfer';
|
||||||
const programs = programIds();
|
const programs = programIds();
|
||||||
const bridgeId = programs.wormhole.pubkey;
|
const bridgeId = programs.wormhole.pubkey;
|
||||||
const authorityKey = await bridgeAuthorityKey(bridgeId);
|
const authorityKey = await bridgeAuthorityKey(bridgeId);
|
||||||
|
@ -79,7 +75,7 @@ export const fromSolana = async (
|
||||||
wallet.publicKey,
|
wallet.publicKey,
|
||||||
new PublicKey(request.info.address),
|
new PublicKey(request.info.address),
|
||||||
new PublicKey(request.info.mint),
|
new PublicKey(request.info.mint),
|
||||||
new BN(request.amount.toString()),
|
new BN(amount),
|
||||||
request.to,
|
request.to,
|
||||||
request.recipient,
|
request.recipient,
|
||||||
{
|
{
|
||||||
|
@ -100,6 +96,12 @@ export const fromSolana = async (
|
||||||
amount,
|
amount,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
setProgress({
|
||||||
|
message: 'Waiting for Solana approval...',
|
||||||
|
type: 'user',
|
||||||
|
group,
|
||||||
|
step: counter++,
|
||||||
|
});
|
||||||
let fee_ix = SystemProgram.transfer({
|
let fee_ix = SystemProgram.transfer({
|
||||||
fromPubkey: wallet.publicKey,
|
fromPubkey: wallet.publicKey,
|
||||||
toPubkey: authorityKey,
|
toPubkey: authorityKey,
|
||||||
|
@ -126,29 +128,28 @@ export const fromSolana = async (
|
||||||
let startSlot = slot;
|
let startSlot = slot;
|
||||||
|
|
||||||
let group = 'Lock assets';
|
let group = 'Lock assets';
|
||||||
|
const solConfirmationMessage = (current: number) =>
|
||||||
|
`Awaiting ETH confirmations: ${current} out of 32`;
|
||||||
let slotUpdateListener = connection.onSlotChange(slot => {
|
let slotUpdateListener = connection.onSlotChange(slot => {
|
||||||
if (completed) return;
|
if (completed) return;
|
||||||
const passedSlots = slot.slot - startSlot;
|
const passedSlots = slot.slot - startSlot;
|
||||||
const isLast = passedSlots - 1 === 31;
|
const isLast = passedSlots - 1 === 31;
|
||||||
if (passedSlots < 32) {
|
if (passedSlots < 32) {
|
||||||
// setLoading({
|
setProgress({
|
||||||
// loading: true,
|
message: solConfirmationMessage(passedSlots),
|
||||||
// message: "Awaiting confirmations",
|
type: isLast ? 'done' : 'wait',
|
||||||
// progress: {
|
step: counter++,
|
||||||
// completion: (slot.slot - startSlot) / 32 * 100,
|
group,
|
||||||
// content: `${slot.slot - startSlot}/${32}`
|
replace: passedSlots > 0,
|
||||||
// }
|
});
|
||||||
// })
|
if (isLast) {
|
||||||
// setProgress({
|
setProgress({
|
||||||
// message: ethConfirmationMessage(passedBlocks),
|
message: 'Awaiting guardian confirmation',
|
||||||
// type: isLast ? 'done' : 'wait',
|
type: 'wait',
|
||||||
// step: counter++,
|
step: counter++,
|
||||||
// group,
|
group,
|
||||||
// replace: passedBlocks > 0,
|
});
|
||||||
// });
|
}
|
||||||
} else {
|
|
||||||
//setLoading({loading: true, message: "Awaiting guardian confirmation"})
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -176,27 +177,27 @@ export const fromSolana = async (
|
||||||
connection.removeAccountChangeListener(accountChangeListener);
|
connection.removeAccountChangeListener(accountChangeListener);
|
||||||
connection.removeSlotChangeListener(slotUpdateListener);
|
connection.removeSlotChangeListener(slotUpdateListener);
|
||||||
|
|
||||||
// let signatures = await bridge.fetchSignatureStatus(
|
let signatures = await bridge.fetchSignatureStatus(
|
||||||
// lockup.signatureAccount,
|
lockup.signatureAccount,
|
||||||
// );
|
);
|
||||||
// let sigData = Buffer.of(
|
let sigData = Buffer.of(
|
||||||
// ...signatures.reduce((previousValue, currentValue) => {
|
...signatures.reduce((previousValue, currentValue) => {
|
||||||
// previousValue.push(currentValue.index);
|
previousValue.push(currentValue.index);
|
||||||
// previousValue.push(...currentValue.signature);
|
previousValue.push(...currentValue.signature);
|
||||||
|
|
||||||
// return previousValue;
|
return previousValue;
|
||||||
// }, new Array<number>()),
|
}, new Array<number>()),
|
||||||
// );
|
);
|
||||||
|
|
||||||
|
vaa = Buffer.concat([
|
||||||
|
vaa.slice(0, 5),
|
||||||
|
Buffer.of(signatures.length),
|
||||||
|
sigData,
|
||||||
|
vaa.slice(6),
|
||||||
|
]);
|
||||||
|
|
||||||
// vaa = Buffer.concat([
|
|
||||||
// vaa.slice(0, 5),
|
|
||||||
// Buffer.of(signatures.length),
|
|
||||||
// sigData,
|
|
||||||
// vaa.slice(6),
|
|
||||||
// ]);
|
|
||||||
// transferVAA = vaa
|
|
||||||
try {
|
try {
|
||||||
await steps.postVAA(request);
|
await steps.postVAA(request, vaa);
|
||||||
resolve();
|
resolve();
|
||||||
} catch {
|
} catch {
|
||||||
reject();
|
reject();
|
||||||
|
@ -206,22 +207,30 @@ export const fromSolana = async (
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
postVAA: async (request: TransferRequest) => {
|
postVAA: async (request: TransferRequest, vaa: any) => {
|
||||||
let wh = WormholeFactory.connect(programIds().wormhole.bridge, signer);
|
let wh = WormholeFactory.connect(programIds().wormhole.bridge, signer);
|
||||||
|
let group = 'Finalizing transfer';
|
||||||
// setLoading({
|
setProgress({
|
||||||
// ...loading,
|
message: 'Sign the claim...',
|
||||||
// loading: true,
|
type: 'wait',
|
||||||
// message: "Sign the claim...",
|
group,
|
||||||
// })
|
step: counter++,
|
||||||
// let tx = await wh.submitVAA(vaa);
|
});
|
||||||
// setLoading({
|
let tx = await wh.submitVAA(vaa);
|
||||||
// ...loading,
|
setProgress({
|
||||||
// loading: true,
|
message: 'Waiting for tokens unlock to be mined...',
|
||||||
// message: "Waiting for tokens unlock to be mined...",
|
type: 'wait',
|
||||||
// })
|
group,
|
||||||
// await tx.wait(1);
|
step: counter++,
|
||||||
// message.success({content: "Execution of VAA succeeded", key: "eth_tx"})
|
});
|
||||||
|
await tx.wait(1);
|
||||||
|
setProgress({
|
||||||
|
message: 'Execution of VAA succeeded',
|
||||||
|
type: 'done',
|
||||||
|
group,
|
||||||
|
step: counter++,
|
||||||
|
});
|
||||||
|
//message.success({content: "", key: "eth_tx"})
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -37,12 +37,10 @@ export interface TransferRequest {
|
||||||
|
|
||||||
export const displayBalance = (info?: TransferRequestInfo) => {
|
export const displayBalance = (info?: TransferRequestInfo) => {
|
||||||
try {
|
try {
|
||||||
return (
|
const balance = info?.balance || new BigNumber(0);
|
||||||
new BN(info?.balance?.toString() || 0)
|
const precision = new BigNumber(10).pow(info?.decimals || new BigNumber(0));
|
||||||
.div(new BN(10).pow(new BN(Math.min((info?.decimals || 0) - 2, 0))))
|
return balance.div(precision).toNumber();
|
||||||
.toNumber() / 100
|
} catch (e) {
|
||||||
);
|
|
||||||
} catch {
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,11 +4,12 @@ export const WORMHOLE_PROGRAM_ID = new PublicKey(
|
||||||
'WormT3McKhFJ2RkiGpdw9GKvNCrB2aB54gb2uV9MfQC',
|
'WormT3McKhFJ2RkiGpdw9GKvNCrB2aB54gb2uV9MfQC',
|
||||||
);
|
);
|
||||||
|
|
||||||
|
export const TRANSFER_ASSETS_OUT_INSTRUCTION: number = 1;
|
||||||
export const POSTVAA_INSTRUCTION: number = 2;
|
export const POSTVAA_INSTRUCTION: number = 2;
|
||||||
|
|
||||||
const INSTRUCTION_LOOKUP: { [key: number]: string } = {
|
const INSTRUCTION_LOOKUP: { [key: number]: string } = {
|
||||||
0: 'Initialize Bridge',
|
0: 'Initialize Bridge',
|
||||||
1: 'Transfer Assets Out',
|
[TRANSFER_ASSETS_OUT_INSTRUCTION]: 'Transfer Assets Out',
|
||||||
[POSTVAA_INSTRUCTION]: 'Post VAA',
|
[POSTVAA_INSTRUCTION]: 'Post VAA',
|
||||||
3: 'Evict Transfer Proposal',
|
3: 'Evict Transfer Proposal',
|
||||||
4: 'Evict Claimed VAA',
|
4: 'Evict Claimed VAA',
|
||||||
|
|
Loading…
Reference in New Issue