1
0
mirror of https://github.com/kremalicious/metamask-extension.git synced 2024-11-22 18:00:18 +01:00

Handle an RPC provider delay in Swaps (#14821)

* Return an estimated amount for a completed swap if an RPC provider has a delay

* Create a recursive function for updating post tx balance

* Add a few tests for the "getSwapsTokensReceivedFromTxMeta" fn

* Trigger Build
This commit is contained in:
Daniel 2022-06-08 16:32:04 +02:00 committed by GitHub
parent 4fa4930c8a
commit b0557daa05
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 153 additions and 30 deletions

View File

@ -41,6 +41,7 @@ import {
PRIORITY_LEVELS, PRIORITY_LEVELS,
} from '../../../../shared/constants/gas'; } from '../../../../shared/constants/gas';
import { decGWEIToHexWEI } from '../../../../shared/modules/conversion.utils'; import { decGWEIToHexWEI } from '../../../../shared/modules/conversion.utils';
import { isSwapsDefaultTokenAddress } from '../../../../shared/modules/swaps.utils';
import { EVENT } from '../../../../shared/constants/metametrics'; import { EVENT } from '../../../../shared/constants/metametrics';
import { import {
HARDFORKS, HARDFORKS,
@ -60,6 +61,7 @@ import PendingTransactionTracker from './pending-tx-tracker';
import * as txUtils from './lib/util'; import * as txUtils from './lib/util';
const MAX_MEMSTORE_TX_LIST_SIZE = 100; // Number of transactions (by unique nonces) to keep in memory const MAX_MEMSTORE_TX_LIST_SIZE = 100; // Number of transactions (by unique nonces) to keep in memory
const UPDATE_POST_TX_BALANCE_TIMEOUT = 5000;
const SWAP_TRANSACTION_TYPES = [ const SWAP_TRANSACTION_TYPES = [
TRANSACTION_TYPES.SWAP, TRANSACTION_TYPES.SWAP,
@ -1465,6 +1467,39 @@ export default class TransactionController extends EventEmitter {
this._trackTransactionMetricsEvent(txMeta, TRANSACTION_EVENTS.SUBMITTED); this._trackTransactionMetricsEvent(txMeta, TRANSACTION_EVENTS.SUBMITTED);
} }
async updatePostTxBalance({ txMeta, txId, numberOfAttempts = 6 }) {
const postTxBalance = await this.query.getBalance(txMeta.txParams.from);
const latestTxMeta = this.txStateManager.getTransaction(txId);
const approvalTxMeta = latestTxMeta.approvalTxId
? this.txStateManager.getTransaction(latestTxMeta.approvalTxId)
: null;
latestTxMeta.postTxBalance = postTxBalance.toString(16);
const isDefaultTokenAddress = isSwapsDefaultTokenAddress(
txMeta.destinationTokenAddress,
txMeta.chainId,
);
if (
isDefaultTokenAddress &&
txMeta.preTxBalance === latestTxMeta.postTxBalance &&
numberOfAttempts > 0
) {
setTimeout(() => {
// If postTxBalance is the same as preTxBalance, try it again.
this.updatePostTxBalance({
txMeta,
txId,
numberOfAttempts: numberOfAttempts - 1,
});
}, UPDATE_POST_TX_BALANCE_TIMEOUT);
} else {
this.txStateManager.updateTransaction(
latestTxMeta,
'transactions#confirmTransaction - add postTxBalance',
);
this._trackSwapsMetrics(latestTxMeta, approvalTxMeta);
}
}
/** /**
* Sets the status of the transaction to confirmed and sets the status of nonce duplicates as * Sets the status of the transaction to confirmed and sets the status of nonce duplicates as
* dropped if the txParams have data it will fetch the txReceipt * dropped if the txParams have data it will fetch the txReceipt
@ -1528,21 +1563,10 @@ export default class TransactionController extends EventEmitter {
); );
if (txMeta.type === TRANSACTION_TYPES.SWAP) { if (txMeta.type === TRANSACTION_TYPES.SWAP) {
const postTxBalance = await this.query.getBalance(txMeta.txParams.from); await this.updatePostTxBalance({
const latestTxMeta = this.txStateManager.getTransaction(txId); txMeta,
txId,
const approvalTxMeta = latestTxMeta.approvalTxId });
? this.txStateManager.getTransaction(latestTxMeta.approvalTxId)
: null;
latestTxMeta.postTxBalance = postTxBalance.toString(16);
this.txStateManager.updateTransaction(
latestTxMeta,
'transactions#confirmTransaction - add postTxBalance',
);
this._trackSwapsMetrics(latestTxMeta, approvalTxMeta);
} }
} catch (err) { } catch (err) {
log.error(err); log.error(err);
@ -1600,21 +1624,10 @@ export default class TransactionController extends EventEmitter {
); );
if (txMeta.type === TRANSACTION_TYPES.SWAP) { if (txMeta.type === TRANSACTION_TYPES.SWAP) {
const postTxBalance = await this.query.getBalance(txMeta.txParams.from); await this.updatePostTxBalance({
const latestTxMeta = this.txStateManager.getTransaction(txId); txMeta,
txId,
const approvalTxMeta = latestTxMeta.approvalTxId });
? this.txStateManager.getTransaction(latestTxMeta.approvalTxId)
: null;
latestTxMeta.postTxBalance = postTxBalance.toString(16);
this.txStateManager.updateTransaction(
latestTxMeta,
'transactions#confirmTransaction - add postTxBalance',
);
this._trackSwapsMetrics(latestTxMeta, approvalTxMeta);
} }
} catch (err) { } catch (err) {
log.error(err); log.error(err);

View File

@ -758,6 +758,12 @@ export function getSwapsTokensReceivedFromTxMeta(
return null; return null;
} }
if (txMeta.swapMetaData && txMeta.preTxBalance === txMeta.postTxBalance) {
// If preTxBalance and postTxBalance are equal, postTxBalance hasn't been updated on time
// because of the RPC provider delay, so we return an estimated receiving amount instead.
return txMeta.swapMetaData.token_to_amount;
}
let approvalTxGasCost = '0x0'; let approvalTxGasCost = '0x0';
if (approvalTxMeta && approvalTxMeta.txReceipt) { if (approvalTxMeta && approvalTxMeta.txReceipt) {
approvalTxGasCost = calcGasTotal( approvalTxGasCost = calcGasTotal(

View File

@ -8,6 +8,7 @@ import {
RINKEBY_CHAIN_ID, RINKEBY_CHAIN_ID,
KOVAN_CHAIN_ID, KOVAN_CHAIN_ID,
AVALANCHE_CHAIN_ID, AVALANCHE_CHAIN_ID,
ETH_SYMBOL,
} from '../../../shared/constants/network'; } from '../../../shared/constants/network';
import { import {
SWAPS_CHAINID_CONTRACT_ADDRESS_MAP, SWAPS_CHAINID_CONTRACT_ADDRESS_MAP,
@ -39,6 +40,7 @@ import {
countDecimals, countDecimals,
shouldEnableDirectWrapping, shouldEnableDirectWrapping,
showRemainingTimeInMinAndSec, showRemainingTimeInMinAndSec,
getSwapsTokensReceivedFromTxMeta,
} from './swaps.util'; } from './swaps.util';
jest.mock('../../helpers/utils/storage-helpers.js', () => ({ jest.mock('../../helpers/utils/storage-helpers.js', () => ({
@ -567,4 +569,106 @@ describe('Swaps Util', () => {
expect(true).toBe(true); expect(true).toBe(true);
}); });
}); });
describe('getSwapsTokensReceivedFromTxMeta', () => {
const createProps = () => {
return {
tokenSymbol: ETH_SYMBOL,
txMeta: {
swapMetaData: {
token_to_amount: 5,
},
txReceipt: {
status: '0x0',
},
preTxBalance: '8b11',
postTxBalance: '8b11',
},
tokenAddress: '0x881d40237659c251811cec9c364ef91234567890',
accountAddress: '0xc011a73ee8576fb46f5e1c5751ca3b9fe0af2a6f',
tokenDecimals: 6,
approvalTxMeta: null,
chainId: MAINNET_CHAIN_ID,
};
};
it('returns an estimated amount if preTxBalance and postTxBalance are the same for ETH', () => {
const props = createProps();
expect(
getSwapsTokensReceivedFromTxMeta(
props.tokenSymbol,
props.txMeta,
props.tokenAddress,
props.accountAddress,
props.tokenDecimals,
props.approvalTxMeta,
props.chainId,
),
).toBe(props.txMeta.swapMetaData.token_to_amount);
});
it('returns null if there is no txMeta', () => {
const props = createProps();
props.txMeta = undefined;
expect(
getSwapsTokensReceivedFromTxMeta(
props.tokenSymbol,
props.txMeta,
props.tokenAddress,
props.accountAddress,
props.tokenDecimals,
props.approvalTxMeta,
props.chainId,
),
).toBeNull();
});
it('returns null if there is no txMeta.txReceipt', () => {
const props = createProps();
props.txMeta.txReceipt = undefined;
expect(
getSwapsTokensReceivedFromTxMeta(
props.tokenSymbol,
props.txMeta,
props.tokenAddress,
props.accountAddress,
props.tokenDecimals,
props.approvalTxMeta,
props.chainId,
),
).toBeNull();
});
it('returns null if there is no txMeta.postTxBalance', () => {
const props = createProps();
props.txMeta.postTxBalance = undefined;
expect(
getSwapsTokensReceivedFromTxMeta(
props.tokenSymbol,
props.txMeta,
props.tokenAddress,
props.accountAddress,
props.tokenDecimals,
props.approvalTxMeta,
props.chainId,
),
).toBeNull();
});
it('returns null if there is no txMeta.preTxBalance', () => {
const props = createProps();
props.txMeta.preTxBalance = undefined;
expect(
getSwapsTokensReceivedFromTxMeta(
props.tokenSymbol,
props.txMeta,
props.tokenAddress,
props.accountAddress,
props.tokenDecimals,
props.approvalTxMeta,
props.chainId,
),
).toBeNull();
});
});
}); });