1
0
mirror of https://github.com/trezor/trezor-wallet synced 2024-11-24 09:18:09 +00:00

estimateGasLimit while sending data + feeLevels fix

This commit is contained in:
Szymon Lesisz 2018-05-11 12:04:18 +02:00
parent d4f921ba84
commit 55728f6ba3
5 changed files with 116 additions and 53 deletions

View File

@ -120,15 +120,41 @@ const calculateMaxAmount = (balance: string, gasPrice: string, gasLimit: string)
} }
const getMaxAmount = () => { const setAmountAndTotal = (state: State): State => {
// if (state.setMax) {
// const account: ?Account = findAccount(getState().accounts, accountState.index, accountState.deviceState, accountState.network);
// if (!account) return;
// if (isToken) {
// const token: ?Token = findToken(getState().tokens, account.address, state.selectedCurrency, account.deviceState);
// if (!token) return;
// state.amount = token.balance;
// } else {
// state.amount = calculateMaxAmount(account.balance, state.gasPrice, state.gasLimit);
// }
// }
// state.total = calculateTotal(isToken ? '0' : state.amount, state.gasPrice, state.gasLimit);
return state;
} }
export const getFeeLevels = (symbol: string, gasPrice: BigNumber | string, gasLimit: string): Array<FeeLevel> => { export const getFeeLevels = (symbol: string, gasPrice: BigNumber | string, gasLimit: string, selected?: FeeLevel): Array<FeeLevel> => {
const price: BigNumber = typeof gasPrice === 'string' ? new BigNumber(gasPrice) : gasPrice const price: BigNumber = typeof gasPrice === 'string' ? new BigNumber(gasPrice) : gasPrice
const quarter: BigNumber = price.dividedBy(4); const quarter: BigNumber = price.dividedBy(4);
const high: string = price.plus(quarter.times(2)).toString(); const high: string = price.plus(quarter.times(2)).toString();
const low: string = price.minus(quarter.times(2)).toString(); const low: string = price.minus(quarter.times(2)).toString();
const customLevel: FeeLevel = selected && selected.value === 'Custom' ? {
value: 'Custom',
gasPrice: selected.gasPrice,
// label: `${ calculateFee(gasPrice, gasLimit) } ${ symbol }`
label: `${ calculateFee(selected.gasPrice, gasLimit) } ${ symbol }`
} : {
value: 'Custom',
gasPrice: low,
label: ''
}
return [ return [
{ {
@ -146,11 +172,7 @@ export const getFeeLevels = (symbol: string, gasPrice: BigNumber | string, gasLi
gasPrice: low, gasPrice: low,
label: `${ calculateFee(low, gasLimit) } ${ symbol }` label: `${ calculateFee(low, gasLimit) } ${ symbol }`
}, },
{ customLevel
value: 'Custom',
gasPrice: low,
label: '',
},
] ]
} }
@ -424,12 +446,10 @@ export const onCurrencyChange = (currency: any): ThunkAction => {
let gasLimit: string = ''; let gasLimit: string = '';
let amount: string = currentState.amount; let amount: string = currentState.amount;
let total: string; let total: string;
console.warn("SEL", currency, currentState.coinSymbol)
if (isToken) { if (isToken) {
gasLimit = coin.defaultGasLimitTokens.toString(); gasLimit = coin.defaultGasLimitTokens.toString();
if (currentState.setMax) { if (currentState.setMax) {
const token: ?Token = findToken(getState().tokens, account.address, currency.value, accountState.deviceState); const token: ?Token = findToken(getState().tokens, account.address, currency.value, accountState.deviceState);
console.warn("SEL", token, currency.value)
if (!token) return; if (!token) return;
amount = token.balance; amount = token.balance;
} }
@ -442,7 +462,7 @@ export const onCurrencyChange = (currency: any): ThunkAction => {
total = calculateTotal(amount, currentState.gasPrice, currentState.gasLimit); total = calculateTotal(amount, currentState.gasPrice, currentState.gasLimit);
} }
const feeLevels: Array<FeeLevel> = getFeeLevels(currentState.coinSymbol, currentState.gasPrice, gasLimit); const feeLevels: Array<FeeLevel> = getFeeLevels(currentState.coinSymbol, currentState.gasPrice, gasLimit, currentState.selectedFeeLevel);
const selectedFeeLevel: ?FeeLevel = feeLevels.find(f => f.value === currentState.selectedFeeLevel.value); const selectedFeeLevel: ?FeeLevel = feeLevels.find(f => f.value === currentState.selectedFeeLevel.value);
if (!selectedFeeLevel) return; if (!selectedFeeLevel) return;
@ -528,7 +548,6 @@ export const onFeeLevelChange = (feeLevel: FeeLevel): ThunkAction => {
}; };
if (feeLevel.value === 'Custom') { if (feeLevel.value === 'Custom') {
// TODO: update value for custom fee
state.advanced = true; state.advanced = true;
feeLevel.gasPrice = state.gasPrice; feeLevel.gasPrice = state.gasPrice;
feeLevel.label = `${ calculateFee(state.gasPrice, state.gasLimit) } ${ state.coinSymbol }`; feeLevel.label = `${ calculateFee(state.gasPrice, state.gasLimit) } ${ state.coinSymbol }`;
@ -538,7 +557,6 @@ export const onFeeLevelChange = (feeLevel: FeeLevel): ThunkAction => {
customLevel.label = ''; customLevel.label = '';
state.gasPrice = feeLevel.gasPrice; state.gasPrice = feeLevel.gasPrice;
state.gasLimit = isToken ? coin.defaultGasLimitTokens.toString() : coin.defaultGasLimit.toString(); state.gasLimit = isToken ? coin.defaultGasLimitTokens.toString() : coin.defaultGasLimit.toString();
// reset custom gasLimit
} }
if (currentState.setMax) { if (currentState.setMax) {
@ -563,6 +581,9 @@ export const onFeeLevelChange = (feeLevel: FeeLevel): ThunkAction => {
} }
} }
// Manually triggered from user
// Update gasPrice to recommended value
export const updateFeeLevels = (): ThunkAction => { export const updateFeeLevels = (): ThunkAction => {
return (dispatch: Dispatch, getState: GetState): void => { return (dispatch: Dispatch, getState: GetState): void => {
const accountState: ?AccountState = getState().abstractAccount; const accountState: ?AccountState = getState().abstractAccount;
@ -570,7 +591,7 @@ export const updateFeeLevels = (): ThunkAction => {
const currentState: State = getState().sendForm; const currentState: State = getState().sendForm;
const isToken: boolean = currentState.selectedCurrency !== currentState.coinSymbol; const isToken: boolean = currentState.selectedCurrency !== currentState.coinSymbol;
const feeLevels: Array<FeeLevel> = getFeeLevels(currentState.coinSymbol, currentState.recommendedGasPrice, currentState.gasLimit); const feeLevels: Array<FeeLevel> = getFeeLevels(currentState.coinSymbol, currentState.recommendedGasPrice, currentState.gasLimit, currentState.selectedFeeLevel);
const selectedFeeLevel: ?FeeLevel = feeLevels.find(f => f.value === currentState.selectedFeeLevel.value); const selectedFeeLevel: ?FeeLevel = feeLevels.find(f => f.value === currentState.selectedFeeLevel.value);
if (!selectedFeeLevel) return; if (!selectedFeeLevel) return;
@ -578,8 +599,7 @@ export const updateFeeLevels = (): ThunkAction => {
...currentState, ...currentState,
feeLevels, feeLevels,
selectedFeeLevel, selectedFeeLevel,
//gasPrice: currentState.recommendedGasPrice, // TODO HERE! gasPrice: selectedFeeLevel.gasPrice,
gasPrice: selectedFeeLevel.gasPrice, // TODO HERE!
gasPriceNeedsUpdate: false, gasPriceNeedsUpdate: false,
}; };
@ -642,9 +662,13 @@ export const onGasPriceChange = (gasPrice: string): ThunkAction => {
state.amount = calculateMaxAmount(account.balance, state.gasPrice, state.gasLimit); state.amount = calculateMaxAmount(account.balance, state.gasPrice, state.gasLimit);
} }
} }
state.total = calculateTotal(isToken ? '0' : state.amount, state.gasPrice, state.gasLimit);
} else {
// state.gasPrice = currentState.gasPrice;
} }
state.total = calculateTotal(isToken ? '0' : state.amount, state.gasPrice, state.gasLimit);
dispatch({ dispatch({
type: SEND.GAS_PRICE_CHANGE, type: SEND.GAS_PRICE_CHANGE,
@ -703,8 +727,8 @@ export const onGasLimitChange = (gasLimit: string): ThunkAction => {
} }
} }
export const onDataChange = (data: string): ThunkAction => { export const onDataChange = (data: string): AsyncAction => {
return (dispatch: Dispatch, getState: GetState): void => { return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const currentState: State = getState().sendForm; const currentState: State = getState().sendForm;
const touched = { ...currentState.touched }; const touched = { ...currentState.touched };
touched.data = true; touched.data = true;
@ -721,12 +745,44 @@ export const onDataChange = (data: string): ThunkAction => {
state state
}); });
dispatch( validation() ); dispatch( validation() );
dispatch( estimateGasPrice() );
} }
} }
const estimateGasPrice = (): AsyncAction => {
return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const accountState: ?AccountState = getState().abstractAccount;
if (!accountState) return;
const web3instance: ?Web3Instance = getState().web3.filter(w3 => w3.network === accountState.network)[0];
if (!web3instance) return;
const web3 = web3instance.web3;
const currentState: State = getState().sendForm;
const data: string = '0x' + (currentState.data.length % 2 === 0 ? currentState.data : '0' + currentState.data);
const gasLimit = await estimateGas(web3instance.web3, {
to: '0xdb6e09ddca62d0959dc4725697e66b8152222aee',
data,
value: web3.toHex(web3.toWei(currentState.amount, 'ether')),
gasPrice: web3.toHex( EthereumjsUnits.convert(currentState.gasPrice, 'gwei', 'wei') ),
});
dispatch({
type: SEND.GAS_LIMIT_CHANGE,
state: {
...currentState,
gasLimit: gasLimit.toString()
}
});
}
}
export const onSend = (): AsyncAction => { export const onSend = (): AsyncAction => {
//return onSendERC20(); //return onSendERC20();
return async (dispatch: Dispatch, getState: GetState): Promise<any> => { return async (dispatch: Dispatch, getState: GetState): Promise<void> => {
const accountState: ?AccountState = getState().abstractAccount; const accountState: ?AccountState = getState().abstractAccount;
if (!accountState) return; if (!accountState) return;
@ -741,7 +797,7 @@ export const onSend = (): AsyncAction => {
const address_n = account.addressPath; const address_n = account.addressPath;
let data: string = ''; let data: string = '0x' + currentState.data;
let txAmount: string = web3.toHex(web3.toWei(currentState.amount, 'ether')); let txAmount: string = web3.toHex(web3.toWei(currentState.amount, 'ether'));
let txAddress: string = currentState.address; let txAddress: string = currentState.address;
if (isToken) { if (isToken) {

View File

@ -49,8 +49,6 @@ export const load = (input: string, network: string): AsyncAction => {
if (!web3instance) return; if (!web3instance) return;
const info = await getTokenInfoAsync(web3instance.erc20, input); const info = await getTokenInfoAsync(web3instance.erc20, input);
info.address = input;
if (info) { if (info) {
return { return {
options: [ info ] options: [ info ]

View File

@ -18,13 +18,14 @@ import type {
Action, Action,
AsyncAction, AsyncAction,
} from '../flowtype'; } from '../flowtype';
import type { ContractFactory } from 'web3'; import type { ContractFactory, EstimateGasOptions } from 'web3';
import type { BigNumber } from 'bignumber.js'; import type { BigNumber } from 'bignumber.js';
import type { Account } from '../reducers/AccountsReducer'; import type { Account } from '../reducers/AccountsReducer';
import type { PendingTx } from '../reducers/PendingTxReducer'; import type { PendingTx } from '../reducers/PendingTxReducer';
import type { Web3Instance } from '../reducers/Web3Reducer'; import type { Web3Instance } from '../reducers/Web3Reducer';
import type { Token } from '../reducers/TokensReducer'; import type { Token } from '../reducers/TokensReducer';
import type { NetworkToken } from '../reducers/LocalStorageReducer';
export type Web3Action = { export type Web3Action = {
type: typeof WEB3.READY, type: typeof WEB3.READY,
@ -384,30 +385,39 @@ export const getNonceAsync = (web3: Web3, address: string): Promise<number> => {
} }
export const getTokenInfoAsync = (erc20: ContractFactory, address: string): Promise<any> => { export const getTokenInfoAsync = (erc20: ContractFactory, address: string): Promise<?NetworkToken> => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const contract = erc20.at(address, (error, res) => { const contract = erc20.at(address, (error, res) => {
console.warn("callback", error, res) // console.warn("callback", error, res)
}); });
const info = {}; const info: NetworkToken = {
// TODO: handle errors address,
name: '',
symbol: '',
decimals: 0
};
contract.name.call((error: ?Error, name: ?string) => { contract.name.call((error: ?Error, name: ?string) => {
if (error) { if (error) {
//resolve(null); resolve(null);
//return; return;
} else if (name) {
info.name = name;
} }
info.name = name;
contract.symbol.call((error: ?Error, symbol: ?string) => { contract.symbol.call((error: ?Error, symbol: ?string) => {
if (error) { if (error) {
resolve(null); resolve(null);
return; return;
} else if (symbol) {
info.symbol = symbol;
} }
info.symbol = symbol;
contract.decimals.call((error: ?Error, decimals: ?BigNumber) => { contract.decimals.call((error: ?Error, decimals: ?BigNumber) => {
if (decimals) { if (decimals) {
info.decimals = decimals.toString(); info.decimals = decimals.toNumber();
resolve(info); resolve(info);
} else { } else {
resolve(null); resolve(null);
@ -418,25 +428,13 @@ export const getTokenInfoAsync = (erc20: ContractFactory, address: string): Prom
}); });
} }
export const estimateGas = (web3: Web3, gasOptions: any): Promise<any> => { export const estimateGas = (web3: Web3, options: EstimateGasOptions): Promise<number> => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
web3.eth.estimateGas(gasOptions, (error, result) => { web3.eth.estimateGas(options, (error: ?Error, gas: ?number) => {
if (error) { if (error) {
reject(error); reject(error);
} else { } else if (typeof gas === 'number'){
resolve(result); resolve(gas);
}
});
})
}
export const getGasPrice2 = (web3: Web3): Promise<any> => {
return new Promise((resolve, reject) => {
web3.eth.getGasPrice((error, result) => {
if (error) {
reject(error);
} else {
resolve(result);
} }
}); });
}) })

View File

@ -85,6 +85,7 @@ const AdvancedForm = (props: Props) => {
autoCapitalize="off" autoCapitalize="off"
spellCheck="false" spellCheck="false"
value={ gasLimit } value={ gasLimit }
disabled={ coinSymbol === selectedCurrency && data.length > 0 }
onChange={ event => onGasLimitChange(event.target.value) } /> onChange={ event => onGasLimitChange(event.target.value) } />
{ errors.gasLimit ? (<span className="error">{ errors.gasLimit }</span>) : null } { errors.gasLimit ? (<span className="error">{ errors.gasLimit }</span>) : null }
{ warnings.gasLimit ? (<span className="warning">{ warnings.gasLimit }</span>) : null } { warnings.gasLimit ? (<span className="warning">{ warnings.gasLimit }</span>) : null }
@ -121,8 +122,7 @@ const AdvancedForm = (props: Props) => {
<span className="what-is-it"></span> <span className="what-is-it"></span>
</Tooltip> </Tooltip>
</label> </label>
<textarea value={ data } onChange={ event => onDataChange(event.target.value) }></textarea> <textarea disabled={ coinSymbol !== selectedCurrency } value={ coinSymbol !== selectedCurrency ? '' : data } onChange={ event => onDataChange(event.target.value) }></textarea>
{/* <textarea disabled={ coinSymbol !== selectedCurrency } value={ coinSymbol !== selectedCurrency ? '' : data } onChange={ event => onDataChange(event.target.value) }></textarea> */}
{ errors.data ? (<span className="error">{ errors.data }</span>) : null } { errors.data ? (<span className="error">{ errors.data }</span>) : null }
</div> </div>

View File

@ -36,6 +36,17 @@ declare module 'web3' {
} }
declare export type EstimateGasOptions = {
to: string;
data: string;
value?: string;
gasPrice?: string;
}
declare export type RawTransaction = {
id: string;
}
declare class Eth { declare class Eth {
getGasPrice: (callback: (error: Error, gasPrice: string) => void) => void, getGasPrice: (callback: (error: Error, gasPrice: string) => void) => void,
getBalance: (address: string, callback: (error: Error, balance: BigNumber) => void) => void, getBalance: (address: string, callback: (error: Error, balance: BigNumber) => void) => void,
@ -43,10 +54,10 @@ declare module 'web3' {
getTransaction: (txid: string, callback: (error: Error, result: any) => void) => void, getTransaction: (txid: string, callback: (error: Error, result: any) => void) => void,
getBlockNumber: (callback: (error: Error, blockNumber: number) => void) => void, getBlockNumber: (callback: (error: Error, blockNumber: number) => void) => void,
getBlock: (hash: string, callback: (error: Error, result: any) => void) => void, getBlock: (hash: string, callback: (error: Error, result: any) => void) => void,
getAccounts: (callback: (error: Error, accounts: Array<EthereumAddressT>) => void) => void, // getAccounts: (callback: (error: Error, accounts: Array<EthereumAddressT>) => void) => void,
sign: (payload: string, signer: EthereumAddressT) => Promise<string>, // sign: (payload: string, signer: EthereumAddressT) => Promise<string>,
contract: (abi: Array<Object>) => ContractFactory, contract: (abi: Array<Object>) => ContractFactory,
estimateGas: (options: any, callback: (error: Error, result: any) => void) => void, estimateGas: (options: EstimateGasOptions, callback: (error: ?Error, gas: ?number) => void) => void,
sendRawTransaction: (tx: any, callback: (error: Error, result: any) => void) => void, sendRawTransaction: (tx: any, callback: (error: Error, result: any) => void) => void,
filter: (type: string) => Filter; // return intance with "watch" filter: (type: string) => Filter; // return intance with "watch"
} }