mirror of
https://github.com/kremalicious/metamask-extension.git
synced 2024-12-23 01:39:44 +01:00
Extract selected token from token input (#8692)
The `TokenInput` component now takes the token as a prop, instead of using the `selectedTokenAddress` state. The `UserPreferencedTokenInput` component that wrapped `TokenInput` has also been updated to take the token as a prop.
This commit is contained in:
parent
91a26bae89
commit
7ff3b4c928
@ -8,7 +8,7 @@ describe('UserPreferencedCurrencyInput Component', function () {
|
||||
describe('rendering', function () {
|
||||
it('should render properly', function () {
|
||||
const wrapper = shallow(
|
||||
<UserPreferencedTokenInput />
|
||||
<UserPreferencedTokenInput token={{ address: '0x0' }} />
|
||||
)
|
||||
|
||||
assert.ok(wrapper)
|
||||
@ -18,6 +18,7 @@ describe('UserPreferencedCurrencyInput Component', function () {
|
||||
it('should render showFiat for TokenInput based on preferences.useNativeCurrencyAsPrimaryCurrency', function () {
|
||||
const wrapper = shallow(
|
||||
<UserPreferencedTokenInput
|
||||
token={{ address: '0x0' }}
|
||||
useNativeCurrencyAsPrimaryCurrency
|
||||
/>
|
||||
)
|
||||
|
@ -4,6 +4,11 @@ import TokenInput from '../../ui/token-input'
|
||||
|
||||
export default class UserPreferencedTokenInput extends PureComponent {
|
||||
static propTypes = {
|
||||
token: PropTypes.shape({
|
||||
address: PropTypes.string.isRequired,
|
||||
decimals: PropTypes.number,
|
||||
symbol: PropTypes.string,
|
||||
}).isRequired,
|
||||
useNativeCurrencyAsPrimaryCurrency: PropTypes.bool,
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { connect } from 'react-redux'
|
||||
import PropTypes from 'prop-types'
|
||||
import UserPreferencedTokenInput from './user-preferenced-token-input.component'
|
||||
import { getPreferences } from '../../../selectors'
|
||||
|
||||
@ -10,4 +11,14 @@ const mapStateToProps = (state) => {
|
||||
}
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps)(UserPreferencedTokenInput)
|
||||
const UserPreferencedTokenInputContainer = connect(mapStateToProps)(UserPreferencedTokenInput)
|
||||
|
||||
UserPreferencedTokenInputContainer.propTypes = {
|
||||
token: PropTypes.shape({
|
||||
address: PropTypes.string.isRequired,
|
||||
decimals: PropTypes.number,
|
||||
symbol: PropTypes.string,
|
||||
}).isRequired,
|
||||
}
|
||||
|
||||
export default UserPreferencedTokenInputContainer
|
||||
|
@ -13,17 +13,7 @@ describe('TokenInput Component', function () {
|
||||
const t = (key) => `translate ${key}`
|
||||
|
||||
describe('rendering', function () {
|
||||
it('should render properly without a token', function () {
|
||||
const wrapper = shallow(
|
||||
<TokenInput />,
|
||||
{ context: { t } }
|
||||
)
|
||||
|
||||
assert.ok(wrapper)
|
||||
assert.equal(wrapper.find(UnitInput).length, 1)
|
||||
})
|
||||
|
||||
it('should render properly with a token', function () {
|
||||
it('should render properly', function () {
|
||||
const mockStore = {
|
||||
metamask: {
|
||||
currentCurrency: 'usd',
|
||||
@ -35,12 +25,11 @@ describe('TokenInput Component', function () {
|
||||
const wrapper = mount(
|
||||
<Provider store={store}>
|
||||
<TokenInput
|
||||
selectedToken={{
|
||||
token={{
|
||||
address: '0x1',
|
||||
decimals: '4',
|
||||
decimals: 4,
|
||||
symbol: 'ABC',
|
||||
}}
|
||||
suffix="ABC"
|
||||
/>
|
||||
</Provider>,
|
||||
{ context: { t },
|
||||
@ -57,7 +46,7 @@ describe('TokenInput Component', function () {
|
||||
assert.equal(wrapper.find('.currency-input__conversion-component').text(), 'translate noConversionRateAvailable')
|
||||
})
|
||||
|
||||
it('should render properly with a token and selectedTokenExchangeRate', function () {
|
||||
it('should render properly with tokenExchangeRates', function () {
|
||||
const mockStore = {
|
||||
metamask: {
|
||||
currentCurrency: 'usd',
|
||||
@ -69,13 +58,12 @@ describe('TokenInput Component', function () {
|
||||
const wrapper = mount(
|
||||
<Provider store={store}>
|
||||
<TokenInput
|
||||
selectedToken={{
|
||||
token={{
|
||||
address: '0x1',
|
||||
decimals: '4',
|
||||
decimals: 4,
|
||||
symbol: 'ABC',
|
||||
}}
|
||||
suffix="ABC"
|
||||
selectedTokenExchangeRate={2}
|
||||
tokenExchangeRates={{ '0x1': 2 }}
|
||||
/>
|
||||
</Provider>,
|
||||
{ context: { t },
|
||||
@ -104,13 +92,12 @@ describe('TokenInput Component', function () {
|
||||
<Provider store={store}>
|
||||
<TokenInput
|
||||
value="2710"
|
||||
selectedToken={{
|
||||
token={{
|
||||
address: '0x1',
|
||||
decimals: '4',
|
||||
decimals: 4,
|
||||
symbol: 'ABC',
|
||||
}}
|
||||
suffix="ABC"
|
||||
selectedTokenExchangeRate={2}
|
||||
tokenExchangeRates={{ '0x1': 2 }}
|
||||
/>
|
||||
</Provider>
|
||||
)
|
||||
@ -138,13 +125,12 @@ describe('TokenInput Component', function () {
|
||||
<Provider store={store}>
|
||||
<TokenInput
|
||||
value="2710"
|
||||
selectedToken={{
|
||||
token={{
|
||||
address: '0x1',
|
||||
decimals: '4',
|
||||
decimals: 4,
|
||||
symbol: 'ABC',
|
||||
}}
|
||||
suffix="ABC"
|
||||
selectedTokenExchangeRate={2}
|
||||
tokenExchangeRates={{ '0x1': 2 }}
|
||||
showFiat
|
||||
/>
|
||||
</Provider>
|
||||
@ -173,13 +159,12 @@ describe('TokenInput Component', function () {
|
||||
<Provider store={store}>
|
||||
<TokenInput
|
||||
value="2710"
|
||||
selectedToken={{
|
||||
token={{
|
||||
address: '0x1',
|
||||
decimals: '4',
|
||||
decimals: 4,
|
||||
symbol: 'ABC',
|
||||
}}
|
||||
suffix="ABC"
|
||||
selectedTokenExchangeRate={2}
|
||||
tokenExchangeRates={{ '0x1': 2 }}
|
||||
showFiat
|
||||
hideConversion
|
||||
/>
|
||||
@ -224,13 +209,12 @@ describe('TokenInput Component', function () {
|
||||
<Provider store={store}>
|
||||
<TokenInput
|
||||
onChange={handleChangeSpy}
|
||||
selectedToken={{
|
||||
token={{
|
||||
address: '0x1',
|
||||
decimals: '4',
|
||||
decimals: 4,
|
||||
symbol: 'ABC',
|
||||
}}
|
||||
suffix="ABC"
|
||||
selectedTokenExchangeRate={2}
|
||||
tokenExchangeRates={{ '0x1': 2 }}
|
||||
/>
|
||||
</Provider>
|
||||
)
|
||||
@ -266,13 +250,12 @@ describe('TokenInput Component', function () {
|
||||
<Provider store={store}>
|
||||
<TokenInput
|
||||
onChange={handleChangeSpy}
|
||||
selectedToken={{
|
||||
token={{
|
||||
address: '0x1',
|
||||
decimals: '4',
|
||||
decimals: 4,
|
||||
symbol: 'ABC',
|
||||
}}
|
||||
suffix="ABC"
|
||||
selectedTokenExchangeRate={2}
|
||||
tokenExchangeRates={{ '0x1': 2 }}
|
||||
showFiat
|
||||
/>
|
||||
</Provider>
|
||||
@ -309,13 +292,12 @@ describe('TokenInput Component', function () {
|
||||
<Provider store={store}>
|
||||
<TokenInput
|
||||
onChange={handleChangeSpy}
|
||||
selectedToken={{
|
||||
token={{
|
||||
address: '0x1',
|
||||
decimals: '4',
|
||||
decimals: 4,
|
||||
symbol: 'ABC',
|
||||
}}
|
||||
suffix="ABC"
|
||||
selectedTokenExchangeRate={2}
|
||||
tokenExchangeRates={{ '0x1': 2 }}
|
||||
showFiat
|
||||
/>
|
||||
</Provider>
|
||||
|
@ -1,255 +0,0 @@
|
||||
import assert from 'assert'
|
||||
import proxyquire from 'proxyquire'
|
||||
|
||||
let mapStateToProps, mergeProps
|
||||
|
||||
proxyquire('../token-input.container.js', {
|
||||
'react-redux': {
|
||||
connect: (ms, _, mp) => {
|
||||
mapStateToProps = ms
|
||||
mergeProps = mp
|
||||
return () => ({})
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
describe('TokenInput container', function () {
|
||||
describe('mapStateToProps()', function () {
|
||||
it('should return the correct props when send is empty', function () {
|
||||
const mockState = {
|
||||
metamask: {
|
||||
currentCurrency: 'usd',
|
||||
tokens: [
|
||||
{
|
||||
address: '0x1',
|
||||
decimals: '4',
|
||||
symbol: 'ABC',
|
||||
},
|
||||
],
|
||||
selectedTokenAddress: '0x1',
|
||||
contractExchangeRates: {},
|
||||
send: {},
|
||||
preferences: {
|
||||
showFiatInTestnets: false,
|
||||
},
|
||||
provider: {
|
||||
type: 'mainnet',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
assert.deepEqual(mapStateToProps(mockState), {
|
||||
currentCurrency: 'usd',
|
||||
selectedToken: {
|
||||
address: '0x1',
|
||||
decimals: '4',
|
||||
symbol: 'ABC',
|
||||
},
|
||||
selectedTokenExchangeRate: 0,
|
||||
hideConversion: false,
|
||||
})
|
||||
})
|
||||
|
||||
it('should return the correct props when selectedTokenAddress is not found and send is populated', function () {
|
||||
const mockState = {
|
||||
metamask: {
|
||||
currentCurrency: 'usd',
|
||||
tokens: [
|
||||
{
|
||||
address: '0x1',
|
||||
decimals: '4',
|
||||
symbol: 'ABC',
|
||||
},
|
||||
],
|
||||
selectedTokenAddress: '0x2',
|
||||
contractExchangeRates: {},
|
||||
send: {
|
||||
token: { address: 'test' },
|
||||
},
|
||||
preferences: {
|
||||
showFiatInTestnets: false,
|
||||
},
|
||||
provider: {
|
||||
type: 'mainnet',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
assert.deepEqual(mapStateToProps(mockState), {
|
||||
currentCurrency: 'usd',
|
||||
selectedToken: {
|
||||
address: 'test',
|
||||
},
|
||||
selectedTokenExchangeRate: 0,
|
||||
hideConversion: false,
|
||||
})
|
||||
})
|
||||
|
||||
it('should return the correct props when contractExchangeRates is populated', function () {
|
||||
const mockState = {
|
||||
metamask: {
|
||||
currentCurrency: 'usd',
|
||||
tokens: [
|
||||
{
|
||||
address: '0x1',
|
||||
decimals: '4',
|
||||
symbol: 'ABC',
|
||||
},
|
||||
],
|
||||
selectedTokenAddress: '0x1',
|
||||
contractExchangeRates: {
|
||||
'0x1': 5,
|
||||
},
|
||||
send: {},
|
||||
preferences: {
|
||||
showFiatInTestnets: false,
|
||||
},
|
||||
provider: {
|
||||
type: 'mainnet',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
assert.deepEqual(mapStateToProps(mockState), {
|
||||
currentCurrency: 'usd',
|
||||
selectedToken: {
|
||||
address: '0x1',
|
||||
decimals: '4',
|
||||
symbol: 'ABC',
|
||||
},
|
||||
selectedTokenExchangeRate: 5,
|
||||
hideConversion: false,
|
||||
})
|
||||
})
|
||||
|
||||
it('should return the correct props when not in mainnet and showFiatInTestnets is false', function () {
|
||||
const mockState = {
|
||||
metamask: {
|
||||
currentCurrency: 'usd',
|
||||
tokens: [
|
||||
{
|
||||
address: '0x1',
|
||||
decimals: '4',
|
||||
symbol: 'ABC',
|
||||
},
|
||||
],
|
||||
selectedTokenAddress: '0x1',
|
||||
contractExchangeRates: {},
|
||||
send: {},
|
||||
preferences: {
|
||||
showFiatInTestnets: false,
|
||||
},
|
||||
provider: {
|
||||
type: 'rinkeby',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
assert.deepEqual(mapStateToProps(mockState), {
|
||||
currentCurrency: 'usd',
|
||||
selectedToken: {
|
||||
address: '0x1',
|
||||
decimals: '4',
|
||||
symbol: 'ABC',
|
||||
},
|
||||
selectedTokenExchangeRate: 0,
|
||||
hideConversion: true,
|
||||
})
|
||||
})
|
||||
|
||||
it('should return the correct props when not in mainnet and showFiatInTestnets is true', function () {
|
||||
const mockState = {
|
||||
metamask: {
|
||||
currentCurrency: 'usd',
|
||||
tokens: [
|
||||
{
|
||||
address: '0x1',
|
||||
decimals: '4',
|
||||
symbol: 'ABC',
|
||||
},
|
||||
],
|
||||
selectedTokenAddress: '0x1',
|
||||
contractExchangeRates: {},
|
||||
send: {},
|
||||
preferences: {
|
||||
showFiatInTestnets: true,
|
||||
},
|
||||
provider: {
|
||||
type: 'rinkeby',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
assert.deepEqual(mapStateToProps(mockState), {
|
||||
currentCurrency: 'usd',
|
||||
selectedToken: {
|
||||
address: '0x1',
|
||||
decimals: '4',
|
||||
symbol: 'ABC',
|
||||
},
|
||||
selectedTokenExchangeRate: 0,
|
||||
hideConversion: false,
|
||||
})
|
||||
})
|
||||
|
||||
it('should return the correct props when in mainnet and showFiatInTestnets is true', function () {
|
||||
const mockState = {
|
||||
metamask: {
|
||||
currentCurrency: 'usd',
|
||||
tokens: [
|
||||
{
|
||||
address: '0x1',
|
||||
decimals: '4',
|
||||
symbol: 'ABC',
|
||||
},
|
||||
],
|
||||
selectedTokenAddress: '0x1',
|
||||
contractExchangeRates: {},
|
||||
send: {},
|
||||
preferences: {
|
||||
showFiatInTestnets: true,
|
||||
},
|
||||
provider: {
|
||||
type: 'mainnet',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
assert.deepEqual(mapStateToProps(mockState), {
|
||||
currentCurrency: 'usd',
|
||||
selectedToken: {
|
||||
address: '0x1',
|
||||
decimals: '4',
|
||||
symbol: 'ABC',
|
||||
},
|
||||
selectedTokenExchangeRate: 0,
|
||||
hideConversion: false,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('mergeProps()', function () {
|
||||
it('should return the correct props', function () {
|
||||
const mockStateProps = {
|
||||
currentCurrency: 'usd',
|
||||
selectedToken: {
|
||||
address: '0x1',
|
||||
decimals: '4',
|
||||
symbol: 'ABC',
|
||||
},
|
||||
selectedTokenExchangeRate: 5,
|
||||
}
|
||||
|
||||
assert.deepEqual(mergeProps(mockStateProps, {}, {}), {
|
||||
currentCurrency: 'usd',
|
||||
selectedToken: {
|
||||
address: '0x1',
|
||||
decimals: '4',
|
||||
symbol: 'ABC',
|
||||
},
|
||||
selectedTokenExchangeRate: 5,
|
||||
suffix: 'ABC',
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
@ -21,11 +21,14 @@ export default class TokenInput extends PureComponent {
|
||||
currentCurrency: PropTypes.string,
|
||||
onChange: PropTypes.func,
|
||||
value: PropTypes.string,
|
||||
suffix: PropTypes.string,
|
||||
showFiat: PropTypes.bool,
|
||||
hideConversion: PropTypes.bool,
|
||||
selectedToken: PropTypes.object,
|
||||
selectedTokenExchangeRate: PropTypes.number,
|
||||
token: PropTypes.shape({
|
||||
address: PropTypes.string.isRequired,
|
||||
decimals: PropTypes.number,
|
||||
symbol: PropTypes.string,
|
||||
}).isRequired,
|
||||
tokenExchangeRates: PropTypes.object,
|
||||
}
|
||||
|
||||
constructor (props) {
|
||||
@ -52,7 +55,7 @@ export default class TokenInput extends PureComponent {
|
||||
}
|
||||
|
||||
getValue (props) {
|
||||
const { value: hexValue, selectedToken: { decimals, symbol } = {} } = props
|
||||
const { value: hexValue, token: { decimals, symbol } = {} } = props
|
||||
|
||||
const multiplier = Math.pow(10, Number(decimals || 0))
|
||||
const decimalValueString = conversionUtil(ethUtil.addHexPrefix(hexValue), {
|
||||
@ -67,7 +70,7 @@ export default class TokenInput extends PureComponent {
|
||||
}
|
||||
|
||||
handleChange = (decimalValue) => {
|
||||
const { selectedToken: { decimals } = {}, onChange } = this.props
|
||||
const { token: { decimals } = {}, onChange } = this.props
|
||||
|
||||
const multiplier = Math.pow(10, Number(decimals || 0))
|
||||
const hexValue = multiplyCurrencies(decimalValue || 0, multiplier, { toNumericBase: 'hex' })
|
||||
@ -77,8 +80,10 @@ export default class TokenInput extends PureComponent {
|
||||
}
|
||||
|
||||
renderConversionComponent () {
|
||||
const { selectedTokenExchangeRate, showFiat, currentCurrency, hideConversion } = this.props
|
||||
const { tokenExchangeRates, showFiat, currentCurrency, hideConversion, token } = this.props
|
||||
const { decimalValue } = this.state
|
||||
|
||||
const tokenExchangeRate = tokenExchangeRates?.[token.address] || 0
|
||||
let currency, numberOfDecimals
|
||||
|
||||
if (hideConversion) {
|
||||
@ -99,14 +104,14 @@ export default class TokenInput extends PureComponent {
|
||||
numberOfDecimals = 6
|
||||
}
|
||||
|
||||
const decimalEthValue = (decimalValue * selectedTokenExchangeRate) || 0
|
||||
const decimalEthValue = (decimalValue * tokenExchangeRate) || 0
|
||||
const hexWeiValue = getWeiHexFromDecimalValue({
|
||||
value: decimalEthValue,
|
||||
fromCurrency: ETH,
|
||||
fromDenomination: ETH,
|
||||
})
|
||||
|
||||
return selectedTokenExchangeRate
|
||||
return tokenExchangeRate
|
||||
? (
|
||||
<CurrencyDisplay
|
||||
className="currency-input__conversion-component"
|
||||
@ -122,13 +127,13 @@ export default class TokenInput extends PureComponent {
|
||||
}
|
||||
|
||||
render () {
|
||||
const { suffix, ...restProps } = this.props
|
||||
const { token, ...restProps } = this.props
|
||||
const { decimalValue } = this.state
|
||||
|
||||
return (
|
||||
<UnitInput
|
||||
{...restProps}
|
||||
suffix={suffix}
|
||||
suffix={token.symbol}
|
||||
onChange={this.handleChange}
|
||||
value={decimalValue}
|
||||
>
|
||||
|
@ -1,9 +1,9 @@
|
||||
import { connect } from 'react-redux'
|
||||
import PropTypes from 'prop-types'
|
||||
import TokenInput from './token-input.component'
|
||||
import {
|
||||
getIsMainnet,
|
||||
getSelectedToken,
|
||||
getSelectedTokenExchangeRate,
|
||||
getTokenExchangeRates,
|
||||
getPreferences,
|
||||
} from '../../../selectors'
|
||||
|
||||
@ -14,22 +14,19 @@ const mapStateToProps = (state) => {
|
||||
|
||||
return {
|
||||
currentCurrency,
|
||||
selectedToken: getSelectedToken(state),
|
||||
selectedTokenExchangeRate: getSelectedTokenExchangeRate(state),
|
||||
tokenExchangeRates: getTokenExchangeRates(state),
|
||||
hideConversion: (!isMainnet && !showFiatInTestnets),
|
||||
}
|
||||
}
|
||||
|
||||
const mergeProps = (stateProps, dispatchProps, ownProps) => {
|
||||
const { selectedToken } = stateProps
|
||||
const suffix = selectedToken && selectedToken.symbol
|
||||
const TokenInputContainer = connect(mapStateToProps)(TokenInput)
|
||||
|
||||
return {
|
||||
...stateProps,
|
||||
...dispatchProps,
|
||||
...ownProps,
|
||||
suffix,
|
||||
}
|
||||
TokenInputContainer.propTypes = {
|
||||
token: PropTypes.shape({
|
||||
address: PropTypes.string.isRequired,
|
||||
decimals: PropTypes.number,
|
||||
symbol: PropTypes.string,
|
||||
}).isRequired,
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, null, mergeProps)(TokenInput)
|
||||
export default TokenInputContainer
|
||||
|
@ -101,15 +101,23 @@ export default class SendAmountRow extends Component {
|
||||
|
||||
renderInput () {
|
||||
const { amount, inError, selectedToken } = this.props
|
||||
const Component = selectedToken ? UserPreferencedTokenInput : UserPreferencedCurrencyInput
|
||||
|
||||
return (
|
||||
<Component
|
||||
onChange={this.handleChange}
|
||||
error={inError}
|
||||
value={amount}
|
||||
/>
|
||||
)
|
||||
return selectedToken ?
|
||||
(
|
||||
<UserPreferencedTokenInput
|
||||
error={inError}
|
||||
onChange={this.handleChange}
|
||||
token={selectedToken}
|
||||
value={amount}
|
||||
/>
|
||||
)
|
||||
: (
|
||||
<UserPreferencedCurrencyInput
|
||||
error={inError}
|
||||
onChange={this.handleChange}
|
||||
value={amount}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
render () {
|
||||
|
@ -156,12 +156,7 @@ export function getTargetAccount (state, targetAddress) {
|
||||
return accounts[targetAddress]
|
||||
}
|
||||
|
||||
export function getSelectedTokenExchangeRate (state) {
|
||||
const contractExchangeRates = state.metamask.contractExchangeRates
|
||||
const selectedToken = getSelectedToken(state) || {}
|
||||
const { address } = selectedToken
|
||||
return contractExchangeRates[address] || 0
|
||||
}
|
||||
export const getTokenExchangeRates = (state) => state.metamask.contractExchangeRates
|
||||
|
||||
export function getAssetImages (state) {
|
||||
const assetImages = state.metamask.assetImages || {}
|
||||
|
@ -32,10 +32,13 @@ describe('Selectors', function () {
|
||||
assert.equal(account.address, '0x0dcd5d886577d5081b0c52e242ef29e70be3e7bc')
|
||||
})
|
||||
|
||||
describe('#getSelectedTokenExchangeRate', function () {
|
||||
it('returns token exchange rate for first token', function () {
|
||||
const tokenRate = selectors.getSelectedTokenExchangeRate(mockState)
|
||||
assert.equal(tokenRate, '0.00039345803819379796')
|
||||
describe('#getTokenExchangeRates', function () {
|
||||
it('returns token exchange rates', function () {
|
||||
const tokenExchangeRates = selectors.getTokenExchangeRates(mockState)
|
||||
assert.deepEqual(tokenExchangeRates, {
|
||||
'0x108cf70c7d384c552f42c07c41c0e1e46d77ea0d': 0.00039345803819379796,
|
||||
'0xd8f6a2ffb0fc5952d16c9768b71cfd35b6399aa5': 0.00008189274407698049,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user