parent
f1403f4849
commit
1739ed9710
@ -0,0 +1,399 @@ |
|||||||
|
import { TRANSACTION_STATUSES } from '../../../shared/constants/transaction'; |
||||||
|
import * as actionConstants from '../../store/actionConstants'; |
||||||
|
import reduceMetamask, { |
||||||
|
getBlockGasLimit, |
||||||
|
getConversionRate, |
||||||
|
getNativeCurrency, |
||||||
|
getSendHexDataFeatureFlagState, |
||||||
|
getSendToAccounts, |
||||||
|
getUnapprovedTxs, |
||||||
|
} from './metamask'; |
||||||
|
|
||||||
|
describe('MetaMask Reducers', () => { |
||||||
|
const mockState = { |
||||||
|
metamask: reduceMetamask( |
||||||
|
{ |
||||||
|
isInitialized: true, |
||||||
|
isUnlocked: true, |
||||||
|
featureFlags: { sendHexData: true }, |
||||||
|
identities: { |
||||||
|
'0xfdea65c8e26263f6d9a1b5de9555d2931a33b825': { |
||||||
|
address: '0xfdea65c8e26263f6d9a1b5de9555d2931a33b825', |
||||||
|
name: 'Send Account 1', |
||||||
|
}, |
||||||
|
'0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb': { |
||||||
|
address: '0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb', |
||||||
|
name: 'Send Account 2', |
||||||
|
}, |
||||||
|
'0x2f8d4a878cfa04a6e60d46362f5644deab66572d': { |
||||||
|
address: '0x2f8d4a878cfa04a6e60d46362f5644deab66572d', |
||||||
|
name: 'Send Account 3', |
||||||
|
}, |
||||||
|
'0xd85a4b6a394794842887b8284293d69163007bbb': { |
||||||
|
address: '0xd85a4b6a394794842887b8284293d69163007bbb', |
||||||
|
name: 'Send Account 4', |
||||||
|
}, |
||||||
|
}, |
||||||
|
cachedBalances: {}, |
||||||
|
currentBlockGasLimit: '0x4c1878', |
||||||
|
conversionRate: 1200.88200327, |
||||||
|
nativeCurrency: 'ETH', |
||||||
|
network: '3', |
||||||
|
provider: { |
||||||
|
type: 'testnet', |
||||||
|
chainId: '0x3', |
||||||
|
}, |
||||||
|
accounts: { |
||||||
|
'0xfdea65c8e26263f6d9a1b5de9555d2931a33b825': { |
||||||
|
code: '0x', |
||||||
|
balance: '0x47c9d71831c76efe', |
||||||
|
nonce: '0x1b', |
||||||
|
address: '0xfdea65c8e26263f6d9a1b5de9555d2931a33b825', |
||||||
|
}, |
||||||
|
'0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb': { |
||||||
|
code: '0x', |
||||||
|
balance: '0x37452b1315889f80', |
||||||
|
nonce: '0xa', |
||||||
|
address: '0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb', |
||||||
|
}, |
||||||
|
'0x2f8d4a878cfa04a6e60d46362f5644deab66572d': { |
||||||
|
code: '0x', |
||||||
|
balance: '0x30c9d71831c76efe', |
||||||
|
nonce: '0x1c', |
||||||
|
address: '0x2f8d4a878cfa04a6e60d46362f5644deab66572d', |
||||||
|
}, |
||||||
|
'0xd85a4b6a394794842887b8284293d69163007bbb': { |
||||||
|
code: '0x', |
||||||
|
balance: '0x0', |
||||||
|
nonce: '0x0', |
||||||
|
address: '0xd85a4b6a394794842887b8284293d69163007bbb', |
||||||
|
}, |
||||||
|
}, |
||||||
|
addressBook: { |
||||||
|
'0x3': { |
||||||
|
'0x06195827297c7a80a443b6894d3bdb8824b43896': { |
||||||
|
address: '0x06195827297c7a80a443b6894d3bdb8824b43896', |
||||||
|
name: 'Address Book Account 1', |
||||||
|
chainId: '0x3', |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
unapprovedTxs: { |
||||||
|
4768706228115573: { |
||||||
|
id: 4768706228115573, |
||||||
|
time: 1487363153561, |
||||||
|
status: TRANSACTION_STATUSES.UNAPPROVED, |
||||||
|
gasMultiplier: 1, |
||||||
|
metamaskNetworkId: '3', |
||||||
|
txParams: { |
||||||
|
from: '0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb', |
||||||
|
to: '0x18a3462427bcc9133bb46e88bcbe39cd7ef0e761', |
||||||
|
value: '0xde0b6b3a7640000', |
||||||
|
metamaskId: 4768706228115573, |
||||||
|
metamaskNetworkId: '3', |
||||||
|
gas: '0x5209', |
||||||
|
}, |
||||||
|
txFee: '17e0186e60800', |
||||||
|
txValue: 'de0b6b3a7640000', |
||||||
|
maxCost: 'de234b52e4a0800', |
||||||
|
gasPrice: '4a817c800', |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
{}, |
||||||
|
), |
||||||
|
}; |
||||||
|
it('init state', () => { |
||||||
|
const initState = reduceMetamask(undefined, {}); |
||||||
|
|
||||||
|
expect.anything(initState); |
||||||
|
}); |
||||||
|
|
||||||
|
it('locks MetaMask', () => { |
||||||
|
const unlockMetaMaskState = { |
||||||
|
isUnlocked: true, |
||||||
|
selectedAddress: 'test address', |
||||||
|
}; |
||||||
|
const lockMetaMask = reduceMetamask(unlockMetaMaskState, { |
||||||
|
type: actionConstants.LOCK_METAMASK, |
||||||
|
}); |
||||||
|
|
||||||
|
expect(lockMetaMask.isUnlocked).toStrictEqual(false); |
||||||
|
}); |
||||||
|
|
||||||
|
it('sets rpc target', () => { |
||||||
|
const state = reduceMetamask( |
||||||
|
{}, |
||||||
|
{ |
||||||
|
type: actionConstants.SET_RPC_TARGET, |
||||||
|
value: 'https://custom.rpc', |
||||||
|
}, |
||||||
|
); |
||||||
|
|
||||||
|
expect(state.provider.rpcUrl).toStrictEqual('https://custom.rpc'); |
||||||
|
}); |
||||||
|
|
||||||
|
it('sets provider type', () => { |
||||||
|
const state = reduceMetamask( |
||||||
|
{}, |
||||||
|
{ |
||||||
|
type: actionConstants.SET_PROVIDER_TYPE, |
||||||
|
value: 'provider type', |
||||||
|
}, |
||||||
|
); |
||||||
|
|
||||||
|
expect(state.provider.type).toStrictEqual('provider type'); |
||||||
|
}); |
||||||
|
|
||||||
|
it('shows account detail', () => { |
||||||
|
const state = reduceMetamask( |
||||||
|
{}, |
||||||
|
{ |
||||||
|
type: actionConstants.SHOW_ACCOUNT_DETAIL, |
||||||
|
}, |
||||||
|
); |
||||||
|
|
||||||
|
expect(state.isUnlocked).toStrictEqual(true); |
||||||
|
expect(state.isInitialized).toStrictEqual(true); |
||||||
|
}); |
||||||
|
|
||||||
|
it('sets account label', () => { |
||||||
|
const state = reduceMetamask( |
||||||
|
{}, |
||||||
|
{ |
||||||
|
type: actionConstants.SET_ACCOUNT_LABEL, |
||||||
|
value: { |
||||||
|
account: 'test account', |
||||||
|
label: 'test label', |
||||||
|
}, |
||||||
|
}, |
||||||
|
); |
||||||
|
|
||||||
|
expect(state.identities).toStrictEqual({ |
||||||
|
'test account': { name: 'test label' }, |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
||||||
|
it('updates tokens', () => { |
||||||
|
const newTokens = { |
||||||
|
address: '0x617b3f8050a0bd94b6b1da02b4384ee5b4df13f4', |
||||||
|
decimals: 18, |
||||||
|
symbol: 'META', |
||||||
|
}; |
||||||
|
|
||||||
|
const state = reduceMetamask( |
||||||
|
{}, |
||||||
|
{ |
||||||
|
type: actionConstants.UPDATE_TOKENS, |
||||||
|
newTokens, |
||||||
|
}, |
||||||
|
); |
||||||
|
|
||||||
|
expect(state.tokens).toStrictEqual(newTokens); |
||||||
|
}); |
||||||
|
|
||||||
|
it('toggles account menu', () => { |
||||||
|
const state = reduceMetamask( |
||||||
|
{}, |
||||||
|
{ |
||||||
|
type: actionConstants.TOGGLE_ACCOUNT_MENU, |
||||||
|
}, |
||||||
|
); |
||||||
|
|
||||||
|
expect(state.isAccountMenuOpen).toStrictEqual(true); |
||||||
|
}); |
||||||
|
|
||||||
|
it('updates value of tx by id', () => { |
||||||
|
const oldState = { |
||||||
|
currentNetworkTxList: [ |
||||||
|
{ |
||||||
|
id: 1, |
||||||
|
txParams: 'foo', |
||||||
|
}, |
||||||
|
], |
||||||
|
}; |
||||||
|
|
||||||
|
const state = reduceMetamask(oldState, { |
||||||
|
type: actionConstants.UPDATE_TRANSACTION_PARAMS, |
||||||
|
id: 1, |
||||||
|
value: 'bar', |
||||||
|
}); |
||||||
|
|
||||||
|
expect(state.currentNetworkTxList[0].txParams).toStrictEqual('bar'); |
||||||
|
}); |
||||||
|
|
||||||
|
it('sets blockies', () => { |
||||||
|
const state = reduceMetamask( |
||||||
|
{}, |
||||||
|
{ |
||||||
|
type: actionConstants.SET_USE_BLOCKIE, |
||||||
|
value: true, |
||||||
|
}, |
||||||
|
); |
||||||
|
|
||||||
|
expect(state.useBlockie).toStrictEqual(true); |
||||||
|
}); |
||||||
|
|
||||||
|
it('updates an arbitrary feature flag', () => { |
||||||
|
const state = reduceMetamask( |
||||||
|
{}, |
||||||
|
{ |
||||||
|
type: actionConstants.UPDATE_FEATURE_FLAGS, |
||||||
|
value: { |
||||||
|
foo: true, |
||||||
|
}, |
||||||
|
}, |
||||||
|
); |
||||||
|
|
||||||
|
expect(state.featureFlags.foo).toStrictEqual(true); |
||||||
|
}); |
||||||
|
|
||||||
|
it('close welcome screen', () => { |
||||||
|
const state = reduceMetamask( |
||||||
|
{}, |
||||||
|
{ |
||||||
|
type: actionConstants.CLOSE_WELCOME_SCREEN, |
||||||
|
}, |
||||||
|
); |
||||||
|
|
||||||
|
expect(state.welcomeScreenSeen).toStrictEqual(true); |
||||||
|
}); |
||||||
|
|
||||||
|
it('sets current locale', () => { |
||||||
|
const state = reduceMetamask( |
||||||
|
{}, |
||||||
|
{ |
||||||
|
type: actionConstants.SET_CURRENT_LOCALE, |
||||||
|
value: { locale: 'ge' }, |
||||||
|
}, |
||||||
|
); |
||||||
|
|
||||||
|
expect(state.currentLocale).toStrictEqual('ge'); |
||||||
|
}); |
||||||
|
|
||||||
|
it('sets pending tokens', () => { |
||||||
|
const payload = { |
||||||
|
address: '0x617b3f8050a0bd94b6b1da02b4384ee5b4df13f4', |
||||||
|
decimals: 18, |
||||||
|
symbol: 'META', |
||||||
|
}; |
||||||
|
|
||||||
|
const pendingTokensState = reduceMetamask( |
||||||
|
{}, |
||||||
|
{ |
||||||
|
type: actionConstants.SET_PENDING_TOKENS, |
||||||
|
payload, |
||||||
|
}, |
||||||
|
); |
||||||
|
|
||||||
|
expect(pendingTokensState.pendingTokens).toStrictEqual(payload); |
||||||
|
}); |
||||||
|
|
||||||
|
it('clears pending tokens', () => { |
||||||
|
const payload = { |
||||||
|
address: '0x617b3f8050a0bd94b6b1da02b4384ee5b4df13f4', |
||||||
|
decimals: 18, |
||||||
|
symbol: 'META', |
||||||
|
}; |
||||||
|
|
||||||
|
const pendingTokensState = { |
||||||
|
pendingTokens: payload, |
||||||
|
}; |
||||||
|
|
||||||
|
const state = reduceMetamask(pendingTokensState, { |
||||||
|
type: actionConstants.CLEAR_PENDING_TOKENS, |
||||||
|
}); |
||||||
|
|
||||||
|
expect(state.pendingTokens).toStrictEqual({}); |
||||||
|
}); |
||||||
|
|
||||||
|
describe('metamask state selectors', () => { |
||||||
|
describe('getBlockGasLimit', () => { |
||||||
|
it('should return the current block gas limit', () => { |
||||||
|
expect(getBlockGasLimit(mockState)).toStrictEqual('0x4c1878'); |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
||||||
|
describe('getConversionRate()', () => { |
||||||
|
it('should return the eth conversion rate', () => { |
||||||
|
expect(getConversionRate(mockState)).toStrictEqual(1200.88200327); |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
||||||
|
describe('getNativeCurrency()', () => { |
||||||
|
it('should return the ticker symbol of the selected network', () => { |
||||||
|
expect(getNativeCurrency(mockState)).toStrictEqual('ETH'); |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
||||||
|
describe('getSendHexDataFeatureFlagState()', () => { |
||||||
|
it('should return the sendHexData feature flag state', () => { |
||||||
|
expect(getSendHexDataFeatureFlagState(mockState)).toStrictEqual(true); |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
||||||
|
describe('getSendToAccounts()', () => { |
||||||
|
it('should return an array including all the users accounts and the address book', () => { |
||||||
|
expect(getSendToAccounts(mockState)).toStrictEqual([ |
||||||
|
{ |
||||||
|
code: '0x', |
||||||
|
balance: '0x47c9d71831c76efe', |
||||||
|
nonce: '0x1b', |
||||||
|
address: '0xfdea65c8e26263f6d9a1b5de9555d2931a33b825', |
||||||
|
name: 'Send Account 1', |
||||||
|
}, |
||||||
|
{ |
||||||
|
code: '0x', |
||||||
|
balance: '0x37452b1315889f80', |
||||||
|
nonce: '0xa', |
||||||
|
address: '0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb', |
||||||
|
name: 'Send Account 2', |
||||||
|
}, |
||||||
|
{ |
||||||
|
code: '0x', |
||||||
|
balance: '0x30c9d71831c76efe', |
||||||
|
nonce: '0x1c', |
||||||
|
address: '0x2f8d4a878cfa04a6e60d46362f5644deab66572d', |
||||||
|
name: 'Send Account 3', |
||||||
|
}, |
||||||
|
{ |
||||||
|
code: '0x', |
||||||
|
balance: '0x0', |
||||||
|
nonce: '0x0', |
||||||
|
address: '0xd85a4b6a394794842887b8284293d69163007bbb', |
||||||
|
name: 'Send Account 4', |
||||||
|
}, |
||||||
|
{ |
||||||
|
address: '0x06195827297c7a80a443b6894d3bdb8824b43896', |
||||||
|
name: 'Address Book Account 1', |
||||||
|
chainId: '0x3', |
||||||
|
}, |
||||||
|
]); |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
||||||
|
it('should return the unapproved txs', () => { |
||||||
|
expect(getUnapprovedTxs(mockState)).toStrictEqual({ |
||||||
|
4768706228115573: { |
||||||
|
id: 4768706228115573, |
||||||
|
time: 1487363153561, |
||||||
|
status: TRANSACTION_STATUSES.UNAPPROVED, |
||||||
|
gasMultiplier: 1, |
||||||
|
metamaskNetworkId: '3', |
||||||
|
txParams: { |
||||||
|
from: '0xc5b8dbac4c1d3f152cdeb400e2313f309c410acb', |
||||||
|
to: '0x18a3462427bcc9133bb46e88bcbe39cd7ef0e761', |
||||||
|
value: '0xde0b6b3a7640000', |
||||||
|
metamaskId: 4768706228115573, |
||||||
|
metamaskNetworkId: '3', |
||||||
|
gas: '0x5209', |
||||||
|
}, |
||||||
|
txFee: '17e0186e60800', |
||||||
|
txValue: 'de0b6b3a7640000', |
||||||
|
maxCost: 'de234b52e4a0800', |
||||||
|
gasPrice: '4a817c800', |
||||||
|
}, |
||||||
|
}); |
||||||
|
}); |
||||||
|
}); |
||||||
|
}); |
@ -0,0 +1,382 @@ |
|||||||
|
import log from 'loglevel'; |
||||||
|
import { estimateGas } from '../../store/actions'; |
||||||
|
import { setCustomGasLimit } from '../gas/gas.duck'; |
||||||
|
import { |
||||||
|
estimateGasForSend, |
||||||
|
calcTokenBalance, |
||||||
|
} from '../../pages/send/send.utils'; |
||||||
|
|
||||||
|
// Actions
|
||||||
|
const OPEN_TO_DROPDOWN = 'metamask/send/OPEN_TO_DROPDOWN'; |
||||||
|
const CLOSE_TO_DROPDOWN = 'metamask/send/CLOSE_TO_DROPDOWN'; |
||||||
|
const UPDATE_SEND_ERRORS = 'metamask/send/UPDATE_SEND_ERRORS'; |
||||||
|
const RESET_SEND_STATE = 'metamask/send/RESET_SEND_STATE'; |
||||||
|
const SHOW_GAS_BUTTON_GROUP = 'metamask/send/SHOW_GAS_BUTTON_GROUP'; |
||||||
|
const HIDE_GAS_BUTTON_GROUP = 'metamask/send/HIDE_GAS_BUTTON_GROUP'; |
||||||
|
const UPDATE_GAS_LIMIT = 'UPDATE_GAS_LIMIT'; |
||||||
|
const UPDATE_GAS_PRICE = 'UPDATE_GAS_PRICE'; |
||||||
|
const UPDATE_GAS_TOTAL = 'UPDATE_GAS_TOTAL'; |
||||||
|
const UPDATE_SEND_HEX_DATA = 'UPDATE_SEND_HEX_DATA'; |
||||||
|
const UPDATE_SEND_TOKEN_BALANCE = 'UPDATE_SEND_TOKEN_BALANCE'; |
||||||
|
const UPDATE_SEND_TO = 'UPDATE_SEND_TO'; |
||||||
|
const UPDATE_SEND_AMOUNT = 'UPDATE_SEND_AMOUNT'; |
||||||
|
const UPDATE_MAX_MODE = 'UPDATE_MAX_MODE'; |
||||||
|
const UPDATE_SEND = 'UPDATE_SEND'; |
||||||
|
const UPDATE_SEND_TOKEN = 'UPDATE_SEND_TOKEN'; |
||||||
|
const CLEAR_SEND = 'CLEAR_SEND'; |
||||||
|
const GAS_LOADING_STARTED = 'GAS_LOADING_STARTED'; |
||||||
|
const GAS_LOADING_FINISHED = 'GAS_LOADING_FINISHED'; |
||||||
|
const UPDATE_SEND_ENS_RESOLUTION = 'UPDATE_SEND_ENS_RESOLUTION'; |
||||||
|
const UPDATE_SEND_ENS_RESOLUTION_ERROR = 'UPDATE_SEND_ENS_RESOLUTION_ERROR'; |
||||||
|
|
||||||
|
const initState = { |
||||||
|
toDropdownOpen: false, |
||||||
|
gasButtonGroupShown: true, |
||||||
|
errors: {}, |
||||||
|
gasLimit: null, |
||||||
|
gasPrice: null, |
||||||
|
gasTotal: null, |
||||||
|
tokenBalance: '0x0', |
||||||
|
from: '', |
||||||
|
to: '', |
||||||
|
amount: '0', |
||||||
|
memo: '', |
||||||
|
maxModeOn: false, |
||||||
|
editingTransactionId: null, |
||||||
|
toNickname: '', |
||||||
|
ensResolution: null, |
||||||
|
ensResolutionError: '', |
||||||
|
gasIsLoading: false, |
||||||
|
}; |
||||||
|
|
||||||
|
// Reducer
|
||||||
|
export default function reducer(state = initState, action) { |
||||||
|
switch (action.type) { |
||||||
|
case OPEN_TO_DROPDOWN: |
||||||
|
return { |
||||||
|
...state, |
||||||
|
toDropdownOpen: true, |
||||||
|
}; |
||||||
|
case CLOSE_TO_DROPDOWN: |
||||||
|
return { |
||||||
|
...state, |
||||||
|
toDropdownOpen: false, |
||||||
|
}; |
||||||
|
case UPDATE_SEND_ERRORS: |
||||||
|
return { |
||||||
|
...state, |
||||||
|
errors: { |
||||||
|
...state.errors, |
||||||
|
...action.value, |
||||||
|
}, |
||||||
|
}; |
||||||
|
case SHOW_GAS_BUTTON_GROUP: |
||||||
|
return { |
||||||
|
...state, |
||||||
|
gasButtonGroupShown: true, |
||||||
|
}; |
||||||
|
case HIDE_GAS_BUTTON_GROUP: |
||||||
|
return { |
||||||
|
...state, |
||||||
|
gasButtonGroupShown: false, |
||||||
|
}; |
||||||
|
case UPDATE_GAS_LIMIT: |
||||||
|
return { |
||||||
|
...state, |
||||||
|
gasLimit: action.value, |
||||||
|
}; |
||||||
|
case UPDATE_GAS_PRICE: |
||||||
|
return { |
||||||
|
...state, |
||||||
|
gasPrice: action.value, |
||||||
|
}; |
||||||
|
case RESET_SEND_STATE: |
||||||
|
return { ...initState }; |
||||||
|
case UPDATE_GAS_TOTAL: |
||||||
|
return { |
||||||
|
...state, |
||||||
|
gasTotal: action.value, |
||||||
|
}; |
||||||
|
case UPDATE_SEND_TOKEN_BALANCE: |
||||||
|
return { |
||||||
|
...state, |
||||||
|
tokenBalance: action.value, |
||||||
|
}; |
||||||
|
case UPDATE_SEND_HEX_DATA: |
||||||
|
return { |
||||||
|
...state, |
||||||
|
data: action.value, |
||||||
|
}; |
||||||
|
case UPDATE_SEND_TO: |
||||||
|
return { |
||||||
|
...state, |
||||||
|
to: action.value.to, |
||||||
|
toNickname: action.value.nickname, |
||||||
|
}; |
||||||
|
case UPDATE_SEND_AMOUNT: |
||||||
|
return { |
||||||
|
...state, |
||||||
|
amount: action.value, |
||||||
|
}; |
||||||
|
case UPDATE_MAX_MODE: |
||||||
|
return { |
||||||
|
...state, |
||||||
|
maxModeOn: action.value, |
||||||
|
}; |
||||||
|
case UPDATE_SEND: |
||||||
|
return Object.assign(state, action.value); |
||||||
|
case UPDATE_SEND_TOKEN: { |
||||||
|
const newSend = { |
||||||
|
...state, |
||||||
|
token: action.value, |
||||||
|
}; |
||||||
|
// erase token-related state when switching back to native currency
|
||||||
|
if (newSend.editingTransactionId && !newSend.token) { |
||||||
|
const unapprovedTx = |
||||||
|
newSend?.unapprovedTxs?.[newSend.editingTransactionId] || {}; |
||||||
|
const txParams = unapprovedTx.txParams || {}; |
||||||
|
Object.assign(newSend, { |
||||||
|
tokenBalance: null, |
||||||
|
balance: '0', |
||||||
|
from: unapprovedTx.from || '', |
||||||
|
unapprovedTxs: { |
||||||
|
...newSend.unapprovedTxs, |
||||||
|
[newSend.editingTransactionId]: { |
||||||
|
...unapprovedTx, |
||||||
|
txParams: { |
||||||
|
...txParams, |
||||||
|
data: '', |
||||||
|
}, |
||||||
|
}, |
||||||
|
}, |
||||||
|
}); |
||||||
|
} |
||||||
|
return Object.assign(state, newSend); |
||||||
|
} |
||||||
|
case UPDATE_SEND_ENS_RESOLUTION: |
||||||
|
return { |
||||||
|
...state, |
||||||
|
ensResolution: action.payload, |
||||||
|
ensResolutionError: '', |
||||||
|
}; |
||||||
|
case UPDATE_SEND_ENS_RESOLUTION_ERROR: |
||||||
|
return { |
||||||
|
...state, |
||||||
|
ensResolution: null, |
||||||
|
ensResolutionError: action.payload, |
||||||
|
}; |
||||||
|
case CLEAR_SEND: |
||||||
|
return { |
||||||
|
...state, |
||||||
|
gasLimit: null, |
||||||
|
gasPrice: null, |
||||||
|
gasTotal: null, |
||||||
|
tokenBalance: null, |
||||||
|
from: '', |
||||||
|
to: '', |
||||||
|
amount: '0x0', |
||||||
|
memo: '', |
||||||
|
errors: {}, |
||||||
|
maxModeOn: false, |
||||||
|
editingTransactionId: null, |
||||||
|
toNickname: '', |
||||||
|
}; |
||||||
|
case GAS_LOADING_STARTED: |
||||||
|
return { |
||||||
|
...state, |
||||||
|
gasIsLoading: true, |
||||||
|
}; |
||||||
|
|
||||||
|
case GAS_LOADING_FINISHED: |
||||||
|
return { |
||||||
|
...state, |
||||||
|
gasIsLoading: false, |
||||||
|
}; |
||||||
|
default: |
||||||
|
return state; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Action Creators
|
||||||
|
export function openToDropdown() { |
||||||
|
return { type: OPEN_TO_DROPDOWN }; |
||||||
|
} |
||||||
|
|
||||||
|
export function closeToDropdown() { |
||||||
|
return { type: CLOSE_TO_DROPDOWN }; |
||||||
|
} |
||||||
|
|
||||||
|
export function showGasButtonGroup() { |
||||||
|
return { type: SHOW_GAS_BUTTON_GROUP }; |
||||||
|
} |
||||||
|
|
||||||
|
export function hideGasButtonGroup() { |
||||||
|
return { type: HIDE_GAS_BUTTON_GROUP }; |
||||||
|
} |
||||||
|
|
||||||
|
export function updateSendErrors(errorObject) { |
||||||
|
return { |
||||||
|
type: UPDATE_SEND_ERRORS, |
||||||
|
value: errorObject, |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
export function resetSendState() { |
||||||
|
return { type: RESET_SEND_STATE }; |
||||||
|
} |
||||||
|
|
||||||
|
export function setGasLimit(gasLimit) { |
||||||
|
return { |
||||||
|
type: UPDATE_GAS_LIMIT, |
||||||
|
value: gasLimit, |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
export function setGasPrice(gasPrice) { |
||||||
|
return { |
||||||
|
type: UPDATE_GAS_PRICE, |
||||||
|
value: gasPrice, |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
export function setGasTotal(gasTotal) { |
||||||
|
return { |
||||||
|
type: UPDATE_GAS_TOTAL, |
||||||
|
value: gasTotal, |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
export function updateGasData({ |
||||||
|
gasPrice, |
||||||
|
blockGasLimit, |
||||||
|
selectedAddress, |
||||||
|
sendToken, |
||||||
|
to, |
||||||
|
value, |
||||||
|
data, |
||||||
|
}) { |
||||||
|
return (dispatch) => { |
||||||
|
dispatch(gasLoadingStarted()); |
||||||
|
return estimateGasForSend({ |
||||||
|
estimateGasMethod: estimateGas, |
||||||
|
blockGasLimit, |
||||||
|
selectedAddress, |
||||||
|
sendToken, |
||||||
|
to, |
||||||
|
value, |
||||||
|
estimateGasPrice: gasPrice, |
||||||
|
data, |
||||||
|
}) |
||||||
|
.then((gas) => { |
||||||
|
dispatch(setGasLimit(gas)); |
||||||
|
dispatch(setCustomGasLimit(gas)); |
||||||
|
dispatch(updateSendErrors({ gasLoadingError: null })); |
||||||
|
dispatch(gasLoadingFinished()); |
||||||
|
}) |
||||||
|
.catch((err) => { |
||||||
|
log.error(err); |
||||||
|
dispatch(updateSendErrors({ gasLoadingError: 'gasLoadingError' })); |
||||||
|
dispatch(gasLoadingFinished()); |
||||||
|
}); |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
export function gasLoadingStarted() { |
||||||
|
return { |
||||||
|
type: GAS_LOADING_STARTED, |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
export function gasLoadingFinished() { |
||||||
|
return { |
||||||
|
type: GAS_LOADING_FINISHED, |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
export function updateSendTokenBalance({ sendToken, tokenContract, address }) { |
||||||
|
return (dispatch) => { |
||||||
|
const tokenBalancePromise = tokenContract |
||||||
|
? tokenContract.balanceOf(address) |
||||||
|
: Promise.resolve(); |
||||||
|
return tokenBalancePromise |
||||||
|
.then((usersToken) => { |
||||||
|
if (usersToken) { |
||||||
|
const newTokenBalance = calcTokenBalance({ sendToken, usersToken }); |
||||||
|
dispatch(setSendTokenBalance(newTokenBalance)); |
||||||
|
} |
||||||
|
}) |
||||||
|
.catch((err) => { |
||||||
|
log.error(err); |
||||||
|
updateSendErrors({ tokenBalance: 'tokenBalanceError' }); |
||||||
|
}); |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
export function setSendTokenBalance(tokenBalance) { |
||||||
|
return { |
||||||
|
type: UPDATE_SEND_TOKEN_BALANCE, |
||||||
|
value: tokenBalance, |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
export function updateSendHexData(value) { |
||||||
|
return { |
||||||
|
type: UPDATE_SEND_HEX_DATA, |
||||||
|
value, |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
export function updateSendTo(to, nickname = '') { |
||||||
|
return { |
||||||
|
type: UPDATE_SEND_TO, |
||||||
|
value: { to, nickname }, |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
export function updateSendAmount(amount) { |
||||||
|
return { |
||||||
|
type: UPDATE_SEND_AMOUNT, |
||||||
|
value: amount, |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
export function setMaxModeTo(bool) { |
||||||
|
return { |
||||||
|
type: UPDATE_MAX_MODE, |
||||||
|
value: bool, |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
export function updateSend(newSend) { |
||||||
|
return { |
||||||
|
type: UPDATE_SEND, |
||||||
|
value: newSend, |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
export function updateSendToken(token) { |
||||||
|
return { |
||||||
|
type: UPDATE_SEND_TOKEN, |
||||||
|
value: token, |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
export function clearSend() { |
||||||
|
return { |
||||||
|
type: CLEAR_SEND, |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
export function updateSendEnsResolution(ensResolution) { |
||||||
|
return { |
||||||
|
type: UPDATE_SEND_ENS_RESOLUTION, |
||||||
|
payload: ensResolution, |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
export function updateSendEnsResolutionError(errorMessage) { |
||||||
|
return { |
||||||
|
type: UPDATE_SEND_ENS_RESOLUTION_ERROR, |
||||||
|
payload: errorMessage, |
||||||
|
}; |
||||||
|
} |
@ -1,9 +0,0 @@ |
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP |
|
||||||
|
|
||||||
exports[`IntroPopup renders the component with initial props 1`] = ` |
|
||||||
<div> |
|
||||||
<div |
|
||||||
class="intro-popup" |
|
||||||
/> |
|
||||||
</div> |
|
||||||
`; |
|
@ -1 +0,0 @@ |
|||||||
export { default } from './intro-popup'; |
|
@ -1,71 +0,0 @@ |
|||||||
.intro-popup { |
|
||||||
&__liquidity-sources-label { |
|
||||||
@include H7; |
|
||||||
|
|
||||||
font-weight: bold; |
|
||||||
margin-bottom: 6px; |
|
||||||
color: $Black-100; |
|
||||||
|
|
||||||
@media screen and (min-width: 576px) { |
|
||||||
@include H6; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
&__learn-more-header { |
|
||||||
@include H4; |
|
||||||
|
|
||||||
font-weight: bold; |
|
||||||
margin-bottom: 12px; |
|
||||||
margin-top: 16px; |
|
||||||
} |
|
||||||
|
|
||||||
&__learn-more-link { |
|
||||||
@include H6; |
|
||||||
|
|
||||||
color: $Blue-500; |
|
||||||
margin-bottom: 8px; |
|
||||||
cursor: pointer; |
|
||||||
} |
|
||||||
|
|
||||||
&__content { |
|
||||||
margin-left: 24px; |
|
||||||
|
|
||||||
> img { |
|
||||||
width: 96%; |
|
||||||
margin-left: -9px; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
&__footer { |
|
||||||
border-top: none; |
|
||||||
} |
|
||||||
|
|
||||||
&__button { |
|
||||||
border-radius: 100px; |
|
||||||
height: 44px; |
|
||||||
} |
|
||||||
|
|
||||||
&__source-logo-container { |
|
||||||
width: 276px; |
|
||||||
display: flex; |
|
||||||
justify-content: center; |
|
||||||
align-items: center; |
|
||||||
padding: 20px 16px; |
|
||||||
background: $Grey-000; |
|
||||||
border-radius: 8px; |
|
||||||
|
|
||||||
@media screen and (min-width: 576px) { |
|
||||||
width: 412px; |
|
||||||
|
|
||||||
img { |
|
||||||
width: 364px; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
&__popover { |
|
||||||
@media screen and (min-width: 576px) { |
|
||||||
width: 460px; |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
@ -1,108 +0,0 @@ |
|||||||
import React, { useContext } from 'react'; |
|
||||||
import { useDispatch, useSelector } from 'react-redux'; |
|
||||||
import { useHistory } from 'react-router-dom'; |
|
||||||
import PropTypes from 'prop-types'; |
|
||||||
import { setSwapsFromToken } from '../../../ducks/swaps/swaps'; |
|
||||||
import { I18nContext } from '../../../contexts/i18n'; |
|
||||||
import { BUILD_QUOTE_ROUTE } from '../../../helpers/constants/routes'; |
|
||||||
import { useNewMetricEvent } from '../../../hooks/useMetricEvent'; |
|
||||||
import { getSwapsDefaultToken } from '../../../selectors'; |
|
||||||
import Button from '../../../components/ui/button'; |
|
||||||
import Popover from '../../../components/ui/popover'; |
|
||||||
|
|
||||||
export default function IntroPopup({ onClose }) { |
|
||||||
const dispatch = useDispatch(useDispatch); |
|
||||||
const history = useHistory(); |
|
||||||
const t = useContext(I18nContext); |
|
||||||
|
|
||||||
const swapsDefaultToken = useSelector(getSwapsDefaultToken); |
|
||||||
const enteredSwapsEvent = useNewMetricEvent({ |
|
||||||
event: 'Swaps Opened', |
|
||||||
properties: { |
|
||||||
source: 'Intro popup', |
|
||||||
active_currency: swapsDefaultToken.symbol, |
|
||||||
}, |
|
||||||
category: 'swaps', |
|
||||||
}); |
|
||||||
const blogPostVisitedEvent = useNewMetricEvent({ |
|
||||||
event: 'Blog Post Visited ', |
|
||||||
category: 'swaps', |
|
||||||
}); |
|
||||||
const contractAuditVisitedEvent = useNewMetricEvent({ |
|
||||||
event: 'Contract Audit Visited', |
|
||||||
category: 'swaps', |
|
||||||
}); |
|
||||||
const productOverviewDismissedEvent = useNewMetricEvent({ |
|
||||||
event: 'Product Overview Dismissed', |
|
||||||
category: 'swaps', |
|
||||||
}); |
|
||||||
|
|
||||||
return ( |
|
||||||
<div className="intro-popup"> |
|
||||||
<Popover |
|
||||||
className="intro-popup__popover" |
|
||||||
title={t('swapIntroPopupTitle')} |
|
||||||
subtitle={t('swapIntroPopupSubTitle')} |
|
||||||
onClose={() => { |
|
||||||
productOverviewDismissedEvent(); |
|
||||||
onClose(); |
|
||||||
}} |
|
||||||
footerClassName="intro-popup__footer" |
|
||||||
footer={ |
|
||||||
<Button |
|
||||||
type="confirm" |
|
||||||
className="intro-popup__button" |
|
||||||
onClick={() => { |
|
||||||
onClose(); |
|
||||||
enteredSwapsEvent(); |
|
||||||
dispatch(setSwapsFromToken(swapsDefaultToken)); |
|
||||||
history.push(BUILD_QUOTE_ROUTE); |
|
||||||
}} |
|
||||||
> |
|
||||||
{t('swapStartSwapping')} |
|
||||||
</Button> |
|
||||||
} |
|
||||||
> |
|
||||||
<div className="intro-popup__content"> |
|
||||||
<div className="intro-popup__liquidity-sources-label"> |
|
||||||
{t('swapIntroLiquiditySourcesLabel')} |
|
||||||
</div> |
|
||||||
<div className="intro-popup__source-logo-container"> |
|
||||||
<img src="images/source-logos-all.svg" alt="" /> |
|
||||||
</div> |
|
||||||
<div className="intro-popup__learn-more-header"> |
|
||||||
{t('swapIntroLearnMoreHeader')} |
|
||||||
</div> |
|
||||||
<div |
|
||||||
className="intro-popup__learn-more-link" |
|
||||||
onClick={() => { |
|
||||||
global.platform.openTab({ |
|
||||||
url: |
|
||||||
'https://medium.com/metamask/introducing-metamask-swaps-84318c643785', |
|
||||||
}); |
|
||||||
blogPostVisitedEvent(); |
|
||||||
}} |
|
||||||
> |
|
||||||
{t('swapIntroLearnMoreLink')} |
|
||||||
</div> |
|
||||||
<div |
|
||||||
className="intro-popup__learn-more-link" |
|
||||||
onClick={() => { |
|
||||||
global.platform.openTab({ |
|
||||||
url: |
|
||||||
'https://diligence.consensys.net/audits/private/lsjipyllnw2/', |
|
||||||
}); |
|
||||||
contractAuditVisitedEvent(); |
|
||||||
}} |
|
||||||
> |
|
||||||
{t('swapLearnMoreContractsAuditReview')} |
|
||||||
</div> |
|
||||||
</div> |
|
||||||
</Popover> |
|
||||||
</div> |
|
||||||
); |
|
||||||
} |
|
||||||
|
|
||||||
IntroPopup.propTypes = { |
|
||||||
onClose: PropTypes.func.isRequired, |
|
||||||
}; |
|
@ -1,24 +0,0 @@ |
|||||||
import React from 'react'; |
|
||||||
import configureMockStore from 'redux-mock-store'; |
|
||||||
|
|
||||||
import { |
|
||||||
renderWithProvider, |
|
||||||
createSwapsMockStore, |
|
||||||
} from '../../../../test/jest'; |
|
||||||
import IntroPopup from '.'; |
|
||||||
|
|
||||||
const createProps = (customProps = {}) => { |
|
||||||
return { |
|
||||||
onClose: jest.fn(), |
|
||||||
...customProps, |
|
||||||
}; |
|
||||||
}; |
|
||||||
|
|
||||||
describe('IntroPopup', () => { |
|
||||||
it('renders the component with initial props', () => { |
|
||||||
const store = configureMockStore()(createSwapsMockStore()); |
|
||||||
const props = createProps(); |
|
||||||
const { container } = renderWithProvider(<IntroPopup {...props} />, store); |
|
||||||
expect(container).toMatchSnapshot(); |
|
||||||
}); |
|
||||||
}); |
|
Loading…
Reference in new issue