Skip to content

Commit

Permalink
fix: zkevm tx confirmation callback (#3676)
Browse files Browse the repository at this point in the history
* fix: Wait for 10 confirmations on zkevm

* refactor: Use BalActionStep success/fail events

* chore: Fix tests

* chore: Move notification trigger to action
  • Loading branch information
garethfuller authored and joehquak committed Jul 30, 2023
1 parent b54285d commit 91f8c3f
Show file tree
Hide file tree
Showing 7 changed files with 95 additions and 80 deletions.
15 changes: 6 additions & 9 deletions src/components/_global/BalActionSteps/BalActionSteps.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
* Useful if there are an arbitrary number of actions the user must take such as
* "approve n tokens, then add liquidity to a pool.""
*/
import { ChainId } from '@aave/protocol-js';
import {
TransactionReceipt,
TransactionResponse,
Expand All @@ -17,7 +16,6 @@ import AnimatePresence from '@/components/animate/AnimatePresence.vue';
import useEthers from '@/composables/useEthers';
import { dateTimeLabelFor } from '@/composables/useTime';
import useTransactionErrors from '@/composables/useTransactionErrors';
import { configService } from '@/services/config/config.service';
import { Step, StepState } from '@/types';
import {
TransactionAction,
Expand All @@ -27,6 +25,7 @@ import {
import signature from '@/assets/images/icons/signature.svg';
import { captureException } from '@sentry/core';
import { useI18n } from 'vue-i18n';
import { postConfirmationDelay } from '@/composables/useTransactions';
/**
* TYPES
Expand All @@ -52,7 +51,8 @@ const props = withDefaults(defineProps<Props>(), {
});
const emit = defineEmits<{
(e: 'success', value: any): void;
(e: 'success', tx: TransactionReceipt, confirmedAt: string): void;
(e: 'failed'): void;
(e: 'setCurrentActionIndex', value: number): void;
}>();
Expand Down Expand Up @@ -221,11 +221,7 @@ async function handleTransaction(
onTxConfirmed: async (receipt: TransactionReceipt) => {
state.receipt = receipt;
// need to explicity wait for a number of confirmations
// on polygon
if (Number(configService.network.chainId) === ChainId.polygon) {
await tx.wait(10);
}
await postConfirmationDelay(tx);
state.confirming = false;
Expand All @@ -235,7 +231,7 @@ async function handleTransaction(
state.confirmedAt = dateTimeLabelFor(confirmedAt);
state.confirmed = true;
if (currentActionIndex.value >= actions.value.length - 1) {
emit('success', { receipt, confirmedAt: state.confirmedAt });
emit('success', receipt, state.confirmedAt);
} else {
currentActionIndex.value += 1;
}
Expand All @@ -247,6 +243,7 @@ async function handleTransaction(
},
onTxFailed: () => {
state.confirming = false;
emit('failed');
},
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import {
StakePreviewProps,
useStakePreview,
} from './useStakePreview';
import { TransactionReceipt } from '@ethersproject/abstract-provider';

initDependenciesWithDefaultMocks();
walletServiceInstance.setUserProvider(computed(() => walletProviderMock));
Expand Down Expand Up @@ -185,7 +186,7 @@ test('Handles staking action success', async () => {

expect(isActionConfirmed.value).toBeFalse();

await handleSuccess({ receipt: vi.fn });
await handleSuccess({} as TransactionReceipt);

expect(isActionConfirmed.value).toBeTrue();
expect(emit).toHaveBeenCalledOnceWith('success');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ export function useStakePreview(props: StakePreviewProps, emit) {
if (approvalActions) stakeActions.value.unshift(...approvalActions);
}

async function handleSuccess({ receipt }) {
async function handleSuccess(receipt: TransactionReceipt) {
isActionConfirmed.value = true;
confirmationReceipt.value = receipt;
await Promise.all([refetchBalances(), refetchAllPoolStakingData()]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,7 @@ import { useI18n } from 'vue-i18n';
import BalActionSteps from '@/components/_global/BalActionSteps/BalActionSteps.vue';
import ConfirmationIndicator from '@/components/web3/ConfirmationIndicator.vue';
import useEthers from '@/composables/useEthers';
import { usePoolHelpers } from '@/composables/usePoolHelpers';
import { dateTimeLabelFor } from '@/composables/useTime';
import useTransactions from '@/composables/useTransactions';
import useVeBal from '@/composables/useVeBAL';
import { Pool } from '@/services/pool/types';
Expand Down Expand Up @@ -43,7 +41,6 @@ const emit = defineEmits<{
const { t } = useI18n();
const { fNum } = useNumbers();
const { addTransaction } = useTransactions();
const { txListener, getTxConfirmedAt } = useEthers();
const { lockablePoolId } = useVeBal();
const { isStakablePool } = usePoolStaking();
const { isMismatchedNetwork } = useWeb3();
Expand Down Expand Up @@ -76,51 +73,41 @@ const actions = computed((): TransactionActionInfo[] => [
/**
* METHODS
*/
async function handleTransaction(tx): Promise<void> {
addTransaction({
id: tx.hash,
type: 'tx',
action: 'invest',
summary: t('transactionSummary.investInPool', [
fNum(fiatValueOut.value, FNumFormats.fiat),
poolWeightsLabel(props.pool),
]),
details: {
total: fNum(fiatValueOut.value, FNumFormats.fiat),
pool: props.pool,
},
});
await txListener(tx, {
onTxConfirmed: async (receipt: TransactionReceipt) => {
emit('success', receipt);
txState.receipt = receipt;
const confirmedAt = await getTxConfirmedAt(receipt);
txState.confirmedAt = dateTimeLabelFor(confirmedAt);
txState.confirmed = true;
txState.confirming = false;
},
onTxFailed: () => {
console.error('Invest failed');
txState.confirming = false;
},
});
async function handleSuccess(
receipt: TransactionReceipt,
confirmedAt: string
): Promise<void> {
txState.receipt = receipt;
txState.confirmedAt = confirmedAt;
txState.confirmed = true;
txState.confirming = false;
emit('success', receipt);
}
onUnmounted(() => {
// Reset tx state after Invest Modal is closed. Ready for another Invest transaction
resetTxState();
});
function handleFailed() {
txState.confirming = false;
}
async function submit(): Promise<TransactionResponse> {
txState.init = true;
try {
const tx = await join();
console.log('tx', tx);
txState.confirming = true;
handleTransaction(tx);
addTransaction({
id: tx.hash,
type: 'tx',
action: 'invest',
summary: t('transactionSummary.investInPool', [
fNum(fiatValueOut.value, FNumFormats.fiat),
poolWeightsLabel(props.pool),
]),
details: {
total: fNum(fiatValueOut.value, FNumFormats.fiat),
pool: props.pool,
},
});
return tx;
} catch (error) {
txState.confirming = false;
Expand All @@ -131,6 +118,14 @@ async function submit(): Promise<TransactionResponse> {
txState.init = false;
}
}
/**
* LIFECYCLE
*/
onUnmounted(() => {
// Reset tx state after Invest Modal is closed. Ready for another Invest transaction
resetTxState();
});
</script>

<template>
Expand All @@ -139,6 +134,8 @@ async function submit(): Promise<TransactionResponse> {
v-if="!txState.confirmed || !txState.receipt"
:actions="actions"
:disabled="rektPriceImpact || isMismatchedNetwork"
@success="handleSuccess"
@failed="handleFailed"
/>
<div v-else>
<ConfirmationIndicator :txReceipt="txState.receipt" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@ import { ref, toRef, watch } from 'vue';
import { useI18n } from 'vue-i18n';
import ConfirmationIndicator from '@/components/web3/ConfirmationIndicator.vue';
import useEthers from '@/composables/useEthers';
import { usePoolHelpers } from '@/composables/usePoolHelpers';
import { dateTimeLabelFor } from '@/composables/useTime';
import useNetwork from '@/composables/useNetwork';
import useTransactions from '@/composables/useTransactions';
// Types
Expand Down Expand Up @@ -45,7 +43,6 @@ const emit = defineEmits<{
const { t } = useI18n();
const { blockNumber, isMismatchedNetwork } = useWeb3();
const { addTransaction } = useTransactions();
const { txListener, getTxConfirmedAt } = useEthers();
const { poolWeightsLabel } = usePoolHelpers(toRef(props, 'pool'));
const { networkSlug } = useNetwork();
const { fNum } = useNumbers();
Expand Down Expand Up @@ -110,31 +107,20 @@ const isBuildingTx = computed((): boolean => {
/**
* METHODS
*/
async function handleTransaction(tx): Promise<void> {
addTransaction({
id: tx.hash,
type: 'tx',
action: 'withdraw',
summary: txSummary.value,
details: {
total: fNum(fiatTotalOut.value, FNumFormats.fiat),
pool: props.pool,
},
});
txState.confirmed = await txListener(tx, {
onTxConfirmed: async (receipt: TransactionReceipt) => {
emit('success', receipt);
txState.confirming = false;
txState.receipt = receipt;
const confirmedAt = await getTxConfirmedAt(receipt);
txState.confirmedAt = dateTimeLabelFor(confirmedAt);
},
onTxFailed: () => {
txState.confirming = false;
},
});
async function handleSuccess(
receipt: TransactionReceipt,
confirmedAt: string
): Promise<void> {
txState.confirmed = true;
txState.confirming = false;
txState.receipt = receipt;
txState.confirmedAt = confirmedAt;
emit('success', receipt);
}
function handleFailed(): void {
txState.confirming = false;
emit('error');
}
async function submit(): Promise<TransactionResponse> {
Expand All @@ -143,7 +129,17 @@ async function submit(): Promise<TransactionResponse> {
txState.confirming = true;
handleTransaction(tx);
addTransaction({
id: tx.hash,
type: 'tx',
action: 'withdraw',
summary: txSummary.value,
details: {
total: fNum(fiatTotalOut.value, FNumFormats.fiat),
pool: props.pool,
},
});
return tx;
} catch (error) {
txState.confirming = false;
Expand Down Expand Up @@ -184,6 +180,8 @@ watch(blockNumber, () => {
:loadingLabel="
isBuildingTx ? $t('withdraw.preview.loadingLabel.building') : undefined
"
@success="handleSuccess"
@failed="handleFailed"
/>
<div v-else>
<ConfirmationIndicator :txReceipt="txState.receipt" />
Expand Down
1 change: 1 addition & 0 deletions src/composables/useNetwork.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export const networkId = ref<Network>(NETWORK_ID);

export const isMainnet = computed(() => networkId.value === Network.MAINNET);
export const isPolygon = computed(() => networkId.value === Network.POLYGON);
export const isZkevm = computed(() => networkId.value === Network.ZKEVM);
export const isOptimism = computed(() => networkId.value === Network.OPTIMISM);
export const isArbitrum = computed(() => networkId.value === Network.ARBITRUM);
export const isGnosis = computed(() => networkId.value === Network.GNOSIS);
Expand Down
23 changes: 22 additions & 1 deletion src/composables/useTransactions.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import { TransactionReceipt } from '@ethersproject/providers';
import {
TransactionReceipt,
TransactionResponse,
} from '@ethersproject/providers';
import { formatUnits } from '@ethersproject/units';
import { merge, orderBy } from 'lodash';
import { computed, ref } from 'vue';
Expand All @@ -16,6 +19,7 @@ import { CowswapTransactionDetails } from './swap/useCowswap';
import { processedTxs } from './useEthers';
import useNotifications from './useNotifications';
import useNumbers, { FNumFormats } from './useNumbers';
import { isPolygon, isZkevm } from './useNetwork';

const WEEK_MS = 86_400_000 * 7;
// Please update the schema version when making changes to the transaction structure.
Expand Down Expand Up @@ -253,6 +257,23 @@ function shouldCheckTx(transaction: Transaction, lastBlockNumber: number) {
}
}

/**
* postConfirmationDelay
*
* Delay in N confirmations before a transaction is considered finalized for
* specific networks.
*
* @param {TransactionResponse} tx - The transaction to wait N confirmations for.
*/
export async function postConfirmationDelay(
tx: TransactionResponse
): Promise<TransactionReceipt> {
if (isPolygon.value) return tx.wait(10);
if (isZkevm.value) return tx.wait(10);

return tx.wait(1);
}

export default function useTransactions() {
// COMPOSABLES
const {
Expand Down

0 comments on commit 91f8c3f

Please sign in to comment.