only show and fetch own transactions (#107)

* fixed button with no provider

* feat: fix bridge transactions

* fix: connection selection

* only show and fetch own transactions

* Added better messages

* added memo to columns

Co-authored-by: bartosz-lipinski <264380+bartosz-lipinski@users.noreply.github.com>
This commit is contained in:
Juan Diego García 2021-05-01 19:59:01 -05:00 committed by GitHub
parent ea2af3b06f
commit 18bda5527f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 352 additions and 257 deletions

View File

@ -1,4 +1,4 @@
import { programIds, sendTransactionWithRetry } from '@oyster/common'; import { programIds, sendTransactionWithRetry, sleep } from '@oyster/common';
import { WalletAdapter } from '@solana/wallet-base'; import { WalletAdapter } from '@solana/wallet-base';
import { ethers } from 'ethers'; import { ethers } from 'ethers';
import { WormholeFactory } from '../../contracts/WormholeFactory'; import { WormholeFactory } from '../../contracts/WormholeFactory';
@ -113,6 +113,17 @@ export const fromSolana = async (
wallet, wallet,
[ix, fee_ix, lock_ix], [ix, fee_ix, lock_ix],
[], [],
undefined,
false,
undefined,
() => {
setProgress({
message: 'Executing Solana Transaction',
type: 'wait',
group,
step: counter++,
});
},
); );
return steps.wait(request, transferKey, slot); return steps.wait(request, transferKey, slot);
@ -124,31 +135,39 @@ export const fromSolana = async (
) => { ) => {
return new Promise<void>((resolve, reject) => { return new Promise<void>((resolve, reject) => {
let completed = false; let completed = false;
let unsubscribed = false;
let startSlot = slot; let startSlot = slot;
let group = 'Lock assets'; let group = 'Lock assets';
const solConfirmationMessage = (current: number) => const solConfirmationMessage = (current: number) =>
`Awaiting ETH confirmations: ${current} out of 32`; `Awaiting Solana confirmations: ${current} out of 32`;
let replaceMessage = false;
let slotUpdateListener = connection.onSlotChange(slot => { let slotUpdateListener = connection.onSlotChange(slot => {
if (completed) return; if (unsubscribed) {
const passedSlots = slot.slot - startSlot; return;
}
const passedSlots = Math.min(Math.max(slot.slot - startSlot, 0), 32);
const isLast = passedSlots - 1 === 31; const isLast = passedSlots - 1 === 31;
if (passedSlots < 32) { if (passedSlots <= 32) {
setProgress({ setProgress({
message: solConfirmationMessage(passedSlots), message: solConfirmationMessage(passedSlots),
type: isLast ? 'done' : 'wait', type: isLast ? 'done' : 'wait',
step: counter++, step: counter++,
group, group,
replace: passedSlots > 0, replace: replaceMessage,
});
replaceMessage = true;
}
if (completed || isLast) {
unsubscribed = true;
setProgress({
message: 'Awaiting guardian confirmation. (Up to few min.)',
type: 'wait',
step: counter++,
group,
}); });
if (isLast) {
setProgress({
message: 'Awaiting guardian confirmation',
type: 'wait',
step: counter++,
group,
});
}
} }
}); });
@ -175,10 +194,19 @@ export const fromSolana = async (
completed = true; completed = true;
connection.removeAccountChangeListener(accountChangeListener); connection.removeAccountChangeListener(accountChangeListener);
connection.removeSlotChangeListener(slotUpdateListener); connection.removeSlotChangeListener(slotUpdateListener);
let signatures;
while (!signatures) {
try {
signatures = await bridge.fetchSignatureStatus(
lockup.signatureAccount,
);
break;
} catch {
await sleep(500);
}
}
let signatures = await bridge.fetchSignatureStatus(
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);
@ -217,7 +245,7 @@ export const fromSolana = async (
}); });
let tx = await wh.submitVAA(vaa); let tx = await wh.submitVAA(vaa);
setProgress({ setProgress({
message: 'Waiting for tokens unlock to be mined...', message: 'Waiting for tokens unlock to be mined... (Up to few min.)',
type: 'wait', type: 'wait',
group, group,
step: counter++, step: counter++,

View File

@ -1,5 +1,5 @@
import { BigNumber } from 'bignumber.js'; import { BigNumber } from 'bignumber.js';
import {ethers} from "ethers"; import { ethers } from 'ethers';
import { ASSET_CHAIN } from '../constants'; import { ASSET_CHAIN } from '../constants';
export interface ProgressUpdate { export interface ProgressUpdate {

View File

@ -21,7 +21,7 @@ import {
import { AccountInfo } from '@solana/spl-token'; import { AccountInfo } from '@solana/spl-token';
import { TransferRequest, ProgressUpdate } from './interface'; import { TransferRequest, ProgressUpdate } from './interface';
import { WalletAdapter } from '@solana/wallet-base'; import { WalletAdapter } from '@solana/wallet-base';
import { BigNumber } from "bignumber.js"; import { BigNumber } from 'bignumber.js';
export const toSolana = async ( export const toSolana = async (
connection: Connection, connection: Connection,
@ -180,7 +180,7 @@ export const toSolana = async (
}); });
let res = await e.approve(programIds().wormhole.bridge, amountBN); let res = await e.approve(programIds().wormhole.bridge, amountBN);
setProgress({ setProgress({
message: 'Waiting for ETH transaction to be mined...', message: 'Waiting for ETH transaction to be mined... (Up to few min.)',
type: 'wait', type: 'wait',
group, group,
step: counter++, step: counter++,
@ -243,7 +243,7 @@ export const toSolana = async (
false, false,
); );
setProgress({ setProgress({
message: 'Waiting for ETH transaction to be mined...', message: 'Waiting for ETH transaction to be mined... (Up to few min.)',
type: 'wait', type: 'wait',
group, group,
step: counter++, step: counter++,

View File

@ -7,6 +7,16 @@ import * as BufferLayout from 'buffer-layout';
import * as bs58 from 'bs58'; import * as bs58 from 'bs58';
import { AssetMeta } from '../bridge'; import { AssetMeta } from '../bridge';
export enum LockupStatus {
AWAITING_VAA,
UNCLAIMED_VAA,
COMPLETED,
}
export interface LockupWithStatus extends Lockup {
status: LockupStatus;
}
export interface Lockup { export interface Lockup {
lockupAddress: PublicKey; lockupAddress: PublicKey;
amount: BN; amount: BN;

View File

@ -1,5 +1,5 @@
import { Button, Table, Tabs, notification } from 'antd'; import { Button, Table, Tabs, notification } from 'antd';
import React, { useEffect, useState } from 'react'; import React, { useEffect, useMemo, useState } from 'react';
import './index.less'; import './index.less';
@ -34,11 +34,7 @@ const { TabPane } = Tabs;
export const RecentTransactionsTable = (props: { export const RecentTransactionsTable = (props: {
showUserTransactions?: boolean; showUserTransactions?: boolean;
}) => { }) => {
const { const { loading: loadingTransfers, transfers } = useWormholeTransactions();
loading: loadingTransfers,
transfers,
userTransfers,
} = useWormholeTransactions();
const { provider } = useEthereum(); const { provider } = useEthereum();
const bridge = useBridge(); const bridge = useBridge();
@ -154,221 +150,197 @@ export const RecentTransactionsTable = (props: {
}, },
}, },
]; ];
const columns = [
...baseColumns,
{
title: 'Status',
dataIndex: 'status',
key: 'status',
render(text: string, record: any) {
return {
props: { style: {} },
children: (
<span className={`${record.status?.toLowerCase()}`}>
{record.status}
</span>
),
};
},
},
];
const userColumns = [ const userColumns = useMemo(
...baseColumns, () => [
{ ...baseColumns,
title: 'Status', {
dataIndex: 'status', title: 'Status',
key: 'status', dataIndex: 'status',
render(text: string, record: any) { key: 'status',
const status = render(text: string, record: any) {
completedVAAs.indexOf(record.txhash) > 0 const status =
? 'Completed' completedVAAs.indexOf(record.txhash) > 0
: record.status; ? 'Completed'
return { : record.status;
props: { style: {} }, return {
children: ( props: { style: {} },
<> children: (
<span className={`${record.status?.toLowerCase()}`}> <>
{status} <span className={`${record.status?.toLowerCase()}`}>
</span> {status}
{status === 'Failed' ? ( </span>
<Button {status === 'Failed' ? (
onClick={() => { <Button
const NotificationContent = () => { onClick={() => {
const [activeSteps, setActiveSteps] = useState< const NotificationContent = () => {
ProgressUpdate[] const [activeSteps, setActiveSteps] = useState<
>([]); ProgressUpdate[]
let counter = 0; >([]);
useEffect(() => { let counter = 0;
(async () => { useEffect(() => {
const signer = provider?.getSigner(); (async () => {
if (!signer || !bridge) { const signer = provider?.getSigner();
setActiveSteps([ if (!signer || !bridge) {
...activeSteps, setActiveSteps([
{ ...activeSteps,
message: 'Connect your Ethereum Wallet', {
type: 'error', message: 'Connect your Ethereum Wallet',
group: 'error', type: 'error',
step: counter++, 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>(), ]);
), } 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,
);
vaa = Buffer.concat([ return previousValue;
vaa.slice(0, 5), },
Buffer.of(signatures.length), new Array<number>(),
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 ( vaa = Buffer.concat([
<div> vaa.slice(0, 5),
<div Buffer.of(signatures.length),
style={{ sigData,
textAlign: 'left', vaa.slice(6),
display: 'flex', ]);
flexDirection: 'column', let wh = WormholeFactory.connect(
}} programIds().wormhole.bridge,
> signer,
{(() => { );
let group = ''; let group = 'Finalizing transfer';
return activeSteps.map((step, i) => { setActiveSteps([
let prevGroup = group; ...activeSteps,
group = step.group; {
let newGroup = prevGroup !== group; message: 'Sign the claim...',
return ( type: 'wait',
<> group,
{newGroup && <span>{group}</span>} step: counter++,
<span style={{ marginLeft: 15 }}> },
{typeToIcon( ]);
step.type, let tx = await wh.submitVAA(vaa);
activeSteps.length - 1 === i, setActiveSteps([
)}{' '} ...activeSteps,
{step.message} {
</span> message:
</> 'Waiting for tokens unlock to be mined... (Up to few min.)',
); type: 'wait',
}); group,
})()} step: counter++,
},
]);
await tx.wait(1);
setActiveSteps([
...activeSteps,
{
message: 'Execution of VAA succeeded',
type: 'done',
group,
step: counter++,
},
]);
setCompletedVAAs([
...completedVAAs,
record.txhash,
]);
}
})();
}, [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> </div>
</div> );
); };
};
notification.open({ notification.open({
message: '', message: '',
duration: 0, duration: 0,
placement: 'bottomLeft', placement: 'bottomLeft',
description: <NotificationContent />, description: <NotificationContent />,
className: 'custom-class', className: 'custom-class',
style: { style: {
width: 500, width: 500,
}, },
}); });
}} }}
shape="circle" shape="circle"
size="large" size="large"
type="text" type="text"
style={{ color: '#547595', fontSize: '18px' }} style={{ color: '#547595', fontSize: '18px' }}
title={'Retry Transaction'} title={'Retry Transaction'}
icon={<SyncOutlined />} icon={<SyncOutlined />}
/> />
) : null} ) : null}
</> </>
), ),
}; };
},
}, },
}, ],
]; [completedVAAs, bridge, provider],
);
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' }}>
Transactions My Recent Transactions
</div> </div>
<Tabs defaultActiveKey="1" centered> <Table
<TabPane tab="Recent Transactions" key="1"> scroll={{
<Table scrollToFirstRowOnChange: false,
scroll={{ x: 900,
scrollToFirstRowOnChange: false, }}
x: 900, dataSource={transfers.sort((a, b) => b.date - a.date)}
}} columns={userColumns}
dataSource={transfers.sort((a, b) => b.date - a.date)} loading={loadingTransfers}
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>
); );
}; };

View File

@ -14,7 +14,7 @@ export const useCorrectNetwork = () => {
if (chainId === 5) { if (chainId === 5) {
setHasCorrespondingNetworks(env === 'testnet'); setHasCorrespondingNetworks(env === 'testnet');
} else if (chainId === 1) { } else if (chainId === 1) {
setHasCorrespondingNetworks(env === 'mainnet-beta'); setHasCorrespondingNetworks(env.includes('mainnet-beta'));
} else { } else {
setHasCorrespondingNetworks(false); setHasCorrespondingNetworks(false);
} }

View File

@ -1,27 +1,33 @@
import { useCallback, useEffect, useRef, useState } from 'react'; import { useCallback, useEffect, useRef, useState } from 'react';
import { import {
notify,
programIds,
useConnection, useConnection,
useConnectionConfig, useConnectionConfig,
programIds,
notify,
useWallet, useWallet,
ParsedAccountBase,
} from '@oyster/common'; } from '@oyster/common';
import { import {
WORMHOLE_PROGRAM_ID,
POSTVAA_INSTRUCTION, POSTVAA_INSTRUCTION,
TRANSFER_ASSETS_OUT_INSTRUCTION, TRANSFER_ASSETS_OUT_INSTRUCTION,
WORMHOLE_PROGRAM_ID,
} from '../utils/ids'; } 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 {
AccountInfo,
Connection, Connection,
ParsedAccountData,
PartiallyDecodedInstruction, PartiallyDecodedInstruction,
PublicKey, PublicKey,
RpcResponseAndContext,
} from '@solana/web3.js'; } from '@solana/web3.js';
import { import {
bridgeAuthorityKey, bridgeAuthorityKey,
LockupStatus,
LockupWithStatus,
SolanaBridge,
TransferOutProposalLayout, TransferOutProposalLayout,
WormholeFactory,
} from '@solana/bridge-sdk'; } from '@solana/bridge-sdk';
import bs58 from 'bs58'; import bs58 from 'bs58';
@ -31,11 +37,10 @@ import {
useCoingecko, useCoingecko,
} from '../contexts/coingecko'; } from '../contexts/coingecko';
import { BigNumber } from 'bignumber.js'; import { BigNumber } from 'bignumber.js';
import { WormholeFactory } from '@solana/bridge-sdk';
import { ethers } from 'ethers'; import { ethers } from 'ethers';
import { useBridge } from '../contexts/bridge'; import { useBridge } from '../contexts/bridge';
import { SolanaBridge } from '@solana/bridge-sdk';
import BN from 'bn.js'; import BN from 'bn.js';
import { keccak256 } from 'ethers/utils';
type WrappedTransferMeta = { type WrappedTransferMeta = {
chain: number; chain: number;
@ -59,6 +64,86 @@ type WrappedTransferMeta = {
const transferCache = new Map<string, WrappedTransferMeta>(); const transferCache = new Map<string, WrappedTransferMeta>();
const queryOwnWrappedMetaTransactions = async (
authorityKey: PublicKey,
connection: Connection,
setTransfers: (arr: WrappedTransferMeta[]) => void,
provider: ethers.providers.Web3Provider,
bridge?: SolanaBridge,
owner?: PublicKey | null,
) => {
if (owner && bridge) {
const transfers = new Map<string, WrappedTransferMeta>();
let wh = WormholeFactory.connect(programIds().wormhole.bridge, provider);
const res: RpcResponseAndContext<
Array<{ pubkey: PublicKey; account: AccountInfo<ParsedAccountData> }>
> = await connection.getParsedTokenAccountsByOwner(
owner,
{ programId: programIds().token },
'single',
);
let lockups: LockupWithStatus[] = [];
for (const acc of res.value) {
const accLockups = await bridge.fetchTransferProposals(acc.pubkey);
lockups.push(
...accLockups.map(v => {
return {
status: LockupStatus.AWAITING_VAA,
...v,
};
}),
);
for (let lockup of lockups) {
if (lockup.vaaTime === undefined || lockup.vaaTime === 0) continue;
let signingData = lockup.vaa.slice(lockup.vaa[5] * 66 + 6);
for (let i = signingData.length; i > 0; i--) {
if (signingData[i] == 0xff) {
signingData = signingData.slice(0, i);
break;
}
}
let hash = keccak256(signingData);
let submissionStatus = await wh.consumedVAAs(hash);
lockup.status = submissionStatus
? LockupStatus.COMPLETED
: LockupStatus.UNCLAIMED_VAA;
}
}
for (const ls of lockups) {
const txhash = ls.lockupAddress.toBase58();
let assetAddress: string = '';
if (ls.assetChain !== ASSET_CHAIN.Solana) {
assetAddress = Buffer.from(ls.assetAddress.slice(12)).toString('hex');
} else {
assetAddress = new PublicKey(ls.assetAddress).toBase58();
}
const dec = new BigNumber(10).pow(new BigNumber(ls.assetDecimals));
const rawAmount = new BigNumber(ls.amount.toString());
const amount = rawAmount.div(dec).toNumber();
transfers.set(txhash, {
publicKey: ls.lockupAddress,
amount,
date: ls.vaaTime,
chain: ls.assetChain,
address: assetAddress,
decimals: 9,
txhash,
explorer: `https://explorer.solana.com/address/${txhash}`,
lockup: ls,
status:
ls.status === LockupStatus.UNCLAIMED_VAA
? 'Failed'
: ls.status === LockupStatus.AWAITING_VAA
? 'In Process'
: 'Completed',
});
}
setTransfers([...transfers.values()]);
}
};
const queryWrappedMetaTransactions = async ( const queryWrappedMetaTransactions = async (
authorityKey: PublicKey, authorityKey: PublicKey,
connection: Connection, connection: Connection,
@ -238,12 +323,11 @@ 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 { wallet } = 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(() => {
@ -263,26 +347,17 @@ export const useWormholeTransactions = () => {
(window as any).ethereum, (window as any).ethereum,
); );
// query wrapped assets that were imported to solana from other chains // query wrapped assets that were imported to solana from other chains
queryWrappedMetaTransactions( queryOwnWrappedMetaTransactions(
authorityKey, authorityKey,
connection, connection,
setTransfers, setTransfers,
provider, provider,
bridge, bridge,
wallet?.publicKey,
).then(() => setLoading(false)); ).then(() => setLoading(false));
} }
})(); })();
}, [connection, setTransfers]); }, [connection, setTransfers, wallet?.publicKey]);
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 () => {
@ -350,7 +425,6 @@ export const useWormholeTransactions = () => {
return { return {
loading, loading,
transfers, transfers,
userTransfers,
totalInUSD: amountInUSD, totalInUSD: amountInUSD,
}; };
}; };

View File

@ -23,6 +23,7 @@ import {
} from '@solana/spl-token-registry'; } from '@solana/spl-token-registry';
export type ENV = export type ENV =
| 'mainnet-beta (Serum)'
| 'mainnet-beta' | 'mainnet-beta'
| 'testnet' | 'testnet'
| 'devnet' | 'devnet'
@ -31,10 +32,15 @@ export type ENV =
export const ENDPOINTS = [ export const ENDPOINTS = [
{ {
name: 'mainnet-beta' as ENV, name: 'mainnet-beta (Serum)' as ENV,
endpoint: 'https://solana-api.projectserum.com/', endpoint: 'https://solana-api.projectserum.com/',
ChainId: ChainId.MainnetBeta, ChainId: ChainId.MainnetBeta,
}, },
{
name: 'mainnet-beta' as ENV,
endpoint: 'https://api.mainnet-beta.solana.com',
ChainId: ChainId.MainnetBeta,
},
{ {
name: 'testnet' as ENV, name: 'testnet' as ENV,
endpoint: clusterApiUrl('testnet'), endpoint: clusterApiUrl('testnet'),
@ -412,6 +418,7 @@ export const sendTransactionWithRetry = async (
commitment: Commitment = 'singleGossip', commitment: Commitment = 'singleGossip',
includesFeePayer: boolean = false, includesFeePayer: boolean = false,
block?: BlockhashAndFeeCalculator, block?: BlockhashAndFeeCalculator,
beforeSend?: () => void,
) => { ) => {
let transaction = new Transaction(); let transaction = new Transaction();
instructions.forEach(instruction => transaction.add(instruction)); instructions.forEach(instruction => transaction.add(instruction));
@ -436,6 +443,10 @@ export const sendTransactionWithRetry = async (
transaction = await wallet.signTransaction(transaction); transaction = await wallet.signTransaction(transaction);
} }
if (beforeSend) {
beforeSend();
}
const { txid, slot } = await sendSignedTransaction({ const { txid, slot } = await sendSignedTransaction({
connection, connection,
signedTransaction: transaction, signedTransaction: transaction,
@ -521,7 +532,7 @@ export async function sendSignedTransaction({
} }
throw new Error(JSON.stringify(simulateResult.err)); throw new Error(JSON.stringify(simulateResult.err));
} }
throw new Error('Transaction failed'); // throw new Error('Transaction failed');
} finally { } finally {
done = true; done = true;
} }

View File

@ -143,7 +143,7 @@ export const PROGRAM_IDS = [
]; ];
export const setProgramIds = (envName: string) => { export const setProgramIds = (envName: string) => {
let instance = PROGRAM_IDS.find(env => env.name === envName); let instance = PROGRAM_IDS.find(env => envName.indexOf(env.name) >= 0);
if (!instance) { if (!instance) {
return; return;
} }