-
Notifications
You must be signed in to change notification settings - Fork 4.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Update token transfer value in use value hook
- Loading branch information
1 parent
e3c31d5
commit 147e2d9
Showing
5 changed files
with
202 additions
and
146 deletions.
There are no files selected for viewing
148 changes: 77 additions & 71 deletions
148
ui/pages/confirmations/components/confirm/info/hooks/use-token-values.test.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,120 +1,126 @@ | ||
import { TransactionMeta } from '@metamask/transaction-controller'; | ||
import { Numeric } from '../../../../../../../shared/modules/Numeric'; | ||
import { genUnapprovedTokenTransferConfirmation } from '../../../../../../../test/data/confirmations/token-transfer'; | ||
import mockState from '../../../../../../../test/data/mock-state.json'; | ||
import { renderHookWithProvider } from '../../../../../../../test/lib/render-helpers'; | ||
// import useTokenExchangeRate from '../../../../../../components/app/currency-input/hooks/useTokenExchangeRate'; | ||
import { Numeric } from '../../../../../../../shared/modules/Numeric'; | ||
import { renderHookWithConfirmContextProvider } from '../../../../../../../test/lib/confirmations/render-helpers'; | ||
import useTokenExchangeRate from '../../../../../../components/app/currency-input/hooks/useTokenExchangeRate'; | ||
import { useTokenTracker } from '../../../../../../hooks/useTokenTracker'; | ||
import { useAssetDetails } from '../../../../hooks/useAssetDetails'; | ||
import { useTokenValues } from './use-token-values'; | ||
import { useDecodedTransactionData } from './useDecodedTransactionData'; | ||
|
||
jest.mock('../../../../hooks/useAssetDetails', () => ({ | ||
...jest.requireActual('../../../../hooks/useAssetDetails'), | ||
useAssetDetails: jest.fn(), | ||
})); | ||
|
||
jest.mock('./useDecodedTransactionData', () => ({ | ||
...jest.requireActual('./useDecodedTransactionData'), | ||
useDecodedTransactionData: jest.fn(), | ||
})); | ||
|
||
jest.mock( | ||
'../../../../../../components/app/currency-input/hooks/useTokenExchangeRate', | ||
() => jest.fn(), | ||
); | ||
|
||
jest.mock('../../../../../../hooks/useTokenTracker', () => ({ | ||
...jest.requireActual('../../../../../../hooks/useTokenTracker'), | ||
useTokenTracker: jest.fn(), | ||
})); | ||
|
||
describe('useTokenValues', () => { | ||
const useAssetDetailsMock = jest.mocked(useAssetDetails); | ||
const useDecodedTransactionDataMock = jest.mocked(useDecodedTransactionData); | ||
const useTokenExchangeRateMock = jest.mocked(useTokenExchangeRate); | ||
const useTokenTrackerMock = jest.mocked(useTokenTracker); | ||
|
||
const TEST_SELECTED_TOKEN = { | ||
address: 'address', | ||
decimals: 18, | ||
symbol: 'symbol', | ||
iconUrl: 'iconUrl', | ||
image: 'image', | ||
}; | ||
|
||
it('returns native and fiat balances', async () => { | ||
(useTokenTrackerMock as jest.Mock).mockResolvedValue({ | ||
tokensWithBalances: [ | ||
{ | ||
address: '0x076146c765189d51be3160a2140cf80bfc73ad68', | ||
balance: '1000000000000000000', | ||
decimals: 18, | ||
}, | ||
], | ||
}); | ||
|
||
(useTokenExchangeRateMock as jest.Mock).mockResolvedValue( | ||
new Numeric(1, 10), | ||
); | ||
|
||
const transactionMeta = genUnapprovedTokenTransferConfirmation( | ||
{}, | ||
) as TransactionMeta; | ||
|
||
const { result, waitForNextUpdate } = renderHookWithProvider( | ||
() => useTokenValues(transactionMeta, TEST_SELECTED_TOKEN), | ||
mockState, | ||
); | ||
|
||
await waitForNextUpdate(); | ||
|
||
expect(result.current).toEqual({ | ||
fiatDisplayValue: '$1.00', | ||
tokenBalance: '1', | ||
}); | ||
beforeEach(() => { | ||
jest.resetAllMocks(); | ||
}); | ||
|
||
it('returns undefined native and fiat balances if no token with balances is returned', async () => { | ||
(useTokenTrackerMock as jest.Mock).mockResolvedValue({ | ||
tokensWithBalances: [], | ||
}); | ||
|
||
it('returns native and fiat balances', async () => { | ||
(useAssetDetailsMock as jest.Mock).mockImplementation(() => ({ | ||
decimals: '10', | ||
})); | ||
(useDecodedTransactionDataMock as jest.Mock).mockImplementation(() => ({ | ||
pending: false, | ||
value: { | ||
data: [ | ||
{ | ||
name: 'transfer', | ||
params: [ | ||
{ | ||
type: 'address', | ||
value: '0x9bc5baF874d2DA8D216aE9f137804184EE5AfEF4', | ||
}, | ||
{ | ||
type: 'uint256', | ||
value: 70000000000, | ||
}, | ||
], | ||
}, | ||
], | ||
source: 'FourByte', | ||
}, | ||
})); | ||
(useTokenExchangeRateMock as jest.Mock).mockResolvedValue( | ||
new Numeric(1, 10), | ||
new Numeric(0.91, 10), | ||
); | ||
|
||
const transactionMeta = genUnapprovedTokenTransferConfirmation( | ||
{}, | ||
) as TransactionMeta; | ||
|
||
const { result, waitForNextUpdate } = renderHookWithProvider( | ||
() => useTokenValues(transactionMeta, TEST_SELECTED_TOKEN), | ||
const { result, waitForNextUpdate } = renderHookWithConfirmContextProvider( | ||
() => useTokenValues(transactionMeta), | ||
mockState, | ||
); | ||
|
||
await waitForNextUpdate(); | ||
|
||
expect(result.current).toEqual({ | ||
fiatDisplayValue: undefined, | ||
tokenBalance: undefined, | ||
decodedTransferValue: 7, | ||
fiatDisplayValue: '$6.37', | ||
pending: false, | ||
}); | ||
}); | ||
|
||
it('returns undefined fiat balance if no token rate is returned', async () => { | ||
(useTokenTrackerMock as jest.Mock).mockResolvedValue({ | ||
tokensWithBalances: [ | ||
{ | ||
address: '0x076146c765189d51be3160a2140cf80bfc73ad68', | ||
balance: '1000000000000000000', | ||
decimals: 18, | ||
}, | ||
], | ||
}); | ||
|
||
(useAssetDetailsMock as jest.Mock).mockImplementation(() => ({ | ||
decimals: '10', | ||
})); | ||
(useDecodedTransactionDataMock as jest.Mock).mockImplementation(() => ({ | ||
pending: false, | ||
value: { | ||
data: [ | ||
{ | ||
name: 'transfer', | ||
params: [ | ||
{ | ||
type: 'address', | ||
value: '0x9bc5baF874d2DA8D216aE9f137804184EE5AfEF4', | ||
}, | ||
{ | ||
type: 'uint256', | ||
value: 70000000000, | ||
}, | ||
], | ||
}, | ||
], | ||
source: 'FourByte', | ||
}, | ||
})); | ||
(useTokenExchangeRateMock as jest.Mock).mockResolvedValue(null); | ||
|
||
const transactionMeta = genUnapprovedTokenTransferConfirmation( | ||
{}, | ||
) as TransactionMeta; | ||
|
||
const { result, waitForNextUpdate } = renderHookWithProvider( | ||
() => useTokenValues(transactionMeta, TEST_SELECTED_TOKEN), | ||
const { result, waitForNextUpdate } = renderHookWithConfirmContextProvider( | ||
() => useTokenValues(transactionMeta), | ||
mockState, | ||
); | ||
|
||
await waitForNextUpdate(); | ||
|
||
expect(result.current).toEqual({ | ||
decodedTransferValue: 7, | ||
fiatDisplayValue: null, | ||
tokenBalance: '1', | ||
pending: false, | ||
}); | ||
}); | ||
}); |
87 changes: 37 additions & 50 deletions
87
ui/pages/confirmations/components/confirm/info/hooks/use-token-values.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,77 +1,64 @@ | ||
import { TransactionMeta } from '@metamask/transaction-controller'; | ||
import { isHexString } from '@metamask/utils'; | ||
import { BigNumber } from 'bignumber.js'; | ||
import { isBoolean } from 'lodash'; | ||
import { useMemo, useState } from 'react'; | ||
import { calcTokenAmount } from '../../../../../../../shared/lib/transactions-controller-utils'; | ||
import { toChecksumHexAddress } from '../../../../../../../shared/modules/hexstring-utils'; | ||
import { Numeric } from '../../../../../../../shared/modules/Numeric'; | ||
import useTokenExchangeRate from '../../../../../../components/app/currency-input/hooks/useTokenExchangeRate'; | ||
import { useFiatFormatter } from '../../../../../../hooks/useFiatFormatter'; | ||
import { useTokenTracker } from '../../../../../../hooks/useTokenTracker'; | ||
import { SelectedToken } from '../shared/selected-token'; | ||
import { useAssetDetails } from '../../../../hooks/useAssetDetails'; | ||
import { useDecodedTransactionData } from './useDecodedTransactionData'; | ||
|
||
export const useTokenValues = ( | ||
transactionMeta: TransactionMeta, | ||
selectedToken: SelectedToken, | ||
) => { | ||
const [tokensWithBalances, setTokensWithBalances] = useState< | ||
{ balance: string; address: string; decimals: number; string: string }[] | ||
>([]); | ||
export const useTokenValues = (transactionMeta: TransactionMeta) => { | ||
const { decimals } = useAssetDetails( | ||
transactionMeta.txParams.to, | ||
transactionMeta.txParams.from, | ||
transactionMeta.txParams.data, | ||
); | ||
|
||
const fetchTokenBalances = async () => { | ||
const result: { | ||
tokensWithBalances: { | ||
balance: string; | ||
address: string; | ||
decimals: number; | ||
string: string; | ||
}[]; | ||
} = await useTokenTracker({ | ||
tokens: [selectedToken], | ||
address: undefined, | ||
}); | ||
const decodedResponse = useDecodedTransactionData(); | ||
const { value, pending } = decodedResponse; | ||
|
||
setTokensWithBalances(result.tokensWithBalances); | ||
}; | ||
const decodedTransferValue = useMemo(() => { | ||
if (!value) { | ||
return 0; | ||
} | ||
|
||
fetchTokenBalances(); | ||
const paramIndex = value.data[0].params.findIndex( | ||
(param) => | ||
param.value !== undefined && | ||
!isHexString(param.value) && | ||
param.value.length === undefined && | ||
!isBoolean(param.value), | ||
); | ||
if (paramIndex === -1) { | ||
return 0; | ||
} | ||
|
||
return new BigNumber(value.data[0].params[paramIndex].value.toString()) | ||
.dividedBy(new BigNumber(10).pow(Number(decimals))) | ||
.toNumber(); | ||
}, [value, decimals]); | ||
|
||
const [exchangeRate, setExchangeRate] = useState<Numeric | undefined>(); | ||
const fetchExchangeRate = async () => { | ||
const result = await useTokenExchangeRate(transactionMeta?.txParams?.to); | ||
|
||
setExchangeRate(result); | ||
}; | ||
|
||
fetchExchangeRate(); | ||
|
||
const tokenBalance = useMemo(() => { | ||
const tokenWithBalance = tokensWithBalances.find( | ||
(token: { | ||
balance: string; | ||
address: string; | ||
decimals: number; | ||
string: string; | ||
}) => | ||
toChecksumHexAddress(token.address) === | ||
toChecksumHexAddress(transactionMeta?.txParams?.to as string), | ||
); | ||
|
||
if (!tokenWithBalance) { | ||
return undefined; | ||
} | ||
|
||
return calcTokenAmount(tokenWithBalance.balance, tokenWithBalance.decimals); | ||
}, [tokensWithBalances]); | ||
|
||
const fiatValue = | ||
exchangeRate && tokenBalance && exchangeRate.times(tokenBalance).toNumber(); | ||
|
||
exchangeRate && | ||
decodedTransferValue && | ||
exchangeRate.times(decodedTransferValue, 10).toNumber(); | ||
const fiatFormatter = useFiatFormatter(); | ||
|
||
const fiatDisplayValue = | ||
fiatValue && fiatFormatter(fiatValue, { shorten: true }); | ||
|
||
return { | ||
decodedTransferValue, | ||
fiatDisplayValue, | ||
tokenBalance: tokenBalance && String(tokenBalance.toNumber()), | ||
pending, | ||
}; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.