Minimise transactions progress (#4942)
* Watch the requests and display them throughout the app * Linting * Showing Requests * Fully working Transaction Requests Display * Add FormattedMessage to Requests * Clean-up the Transfer dialog * Update Validations * Cleanup Create Wallet * Clean Deploy Contract Dialog * Cleanup Contract Execution * Fix Requests * Cleanup Wallet Settings * Don't show stepper in Portal if less than 2 steps * WIP local storage requests * Caching requests and saving contract deployments * Add Historic prop to Requests MethodDecoding * Fix tests * Add Contract address to MethodDecoding * PR Grumbles - Part I * PR Grumbles - Part II * Use API Subscription methods * Linting * Move SavedRequests and add tests * Added tests for Requests Actions * Fixing tests * PR Grumbles + Playground fix * Revert Playground changes * PR Grumbles * Better showEth in MethodDecoding
This commit is contained in:
committed by
Jaco Greeff
parent
e28c477075
commit
a99721004b
@@ -16,6 +16,25 @@
|
||||
|
||||
import WalletsUtils from '~/util/wallets';
|
||||
|
||||
export function trackRequest (api, options, statusCallback) {
|
||||
const { requestId, transactionHash } = options;
|
||||
const txHashPromise = transactionHash
|
||||
? Promise.resolve(transactionHash)
|
||||
: api.pollMethod('parity_checkRequest', requestId);
|
||||
|
||||
return txHashPromise
|
||||
.then((transactionHash) => {
|
||||
statusCallback(null, { transactionHash });
|
||||
return api.pollMethod('eth_getTransactionReceipt', transactionHash, isValidReceipt);
|
||||
})
|
||||
.then((transactionReceipt) => {
|
||||
statusCallback(null, { transactionReceipt });
|
||||
})
|
||||
.catch((error) => {
|
||||
statusCallback(error);
|
||||
});
|
||||
}
|
||||
|
||||
const isValidReceipt = (receipt) => {
|
||||
return receipt && receipt.blockNumber && receipt.blockNumber.gt(0);
|
||||
};
|
||||
@@ -73,100 +92,6 @@ export function postTransaction (_func, _options, _values = []) {
|
||||
});
|
||||
}
|
||||
|
||||
export function deploy (contract, _options, values, metadata = {}, statecb = () => {}, skipGasEstimate = false) {
|
||||
const options = { ..._options };
|
||||
const { api } = contract;
|
||||
const address = options.from;
|
||||
|
||||
return WalletsUtils
|
||||
.isWallet(api, address)
|
||||
.then((isWallet) => {
|
||||
if (!isWallet) {
|
||||
return contract.deploy(options, values, statecb, skipGasEstimate);
|
||||
}
|
||||
|
||||
let gasEstPromise;
|
||||
|
||||
if (skipGasEstimate) {
|
||||
gasEstPromise = Promise.resolve(null);
|
||||
} else {
|
||||
statecb(null, { state: 'estimateGas' });
|
||||
|
||||
gasEstPromise = deployEstimateGas(contract, options, values)
|
||||
.then(([gasEst, gas]) => gas);
|
||||
}
|
||||
|
||||
return gasEstPromise
|
||||
.then((gas) => {
|
||||
if (gas) {
|
||||
options.gas = gas.toFixed(0);
|
||||
}
|
||||
|
||||
statecb(null, { state: 'postTransaction', gas: options.gas });
|
||||
|
||||
return WalletsUtils.getDeployArgs(contract, options, values);
|
||||
})
|
||||
.then((callArgs) => {
|
||||
const { func, options, values } = callArgs;
|
||||
|
||||
return func._postTransaction(options, values)
|
||||
.then((requestId) => {
|
||||
statecb(null, { state: 'checkRequest', requestId });
|
||||
return contract._pollCheckRequest(requestId);
|
||||
})
|
||||
.then((txhash) => {
|
||||
statecb(null, { state: 'getTransactionReceipt', txhash });
|
||||
return contract._pollTransactionReceipt(txhash, options.gas);
|
||||
})
|
||||
.then((receipt) => {
|
||||
if (receipt.gasUsed.eq(options.gas)) {
|
||||
throw new Error(`Contract not deployed, gasUsed == ${options.gas.toFixed(0)}`);
|
||||
}
|
||||
|
||||
const logs = WalletsUtils.parseLogs(api, receipt.logs || []);
|
||||
|
||||
const confirmationLog = logs.find((log) => log.event === 'ConfirmationNeeded');
|
||||
const transactionLog = logs.find((log) => log.event === 'SingleTransact');
|
||||
|
||||
if (!confirmationLog && !transactionLog) {
|
||||
throw new Error('Something went wrong in the Wallet Contract (no logs have been emitted)...');
|
||||
}
|
||||
|
||||
// Confirmations are needed from the other owners
|
||||
if (confirmationLog) {
|
||||
const operationHash = api.util.bytesToHex(confirmationLog.params.operation.value);
|
||||
|
||||
// Add the contract to pending contracts
|
||||
WalletsUtils.addPendingContract(address, operationHash, metadata);
|
||||
statecb(null, { state: 'confirmationNeeded' });
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the contract address in the receip
|
||||
receipt.contractAddress = transactionLog.params.created.value;
|
||||
|
||||
const contractAddress = receipt.contractAddress;
|
||||
|
||||
statecb(null, { state: 'hasReceipt', receipt });
|
||||
contract._receipt = receipt;
|
||||
contract._address = contractAddress;
|
||||
|
||||
statecb(null, { state: 'getCode' });
|
||||
|
||||
return api.eth.getCode(contractAddress)
|
||||
.then((code) => {
|
||||
if (code === '0x') {
|
||||
throw new Error('Contract not deployed, getCode returned 0x');
|
||||
}
|
||||
|
||||
statecb(null, { state: 'completed' });
|
||||
return contractAddress;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function deployEstimateGas (contract, _options, values) {
|
||||
const options = { ..._options };
|
||||
const { api } = contract;
|
||||
@@ -192,6 +117,86 @@ export function deployEstimateGas (contract, _options, values) {
|
||||
});
|
||||
}
|
||||
|
||||
export function deploy (contract, options, values, skipGasEstimate = false) {
|
||||
const { api } = contract;
|
||||
const address = options.from;
|
||||
|
||||
const gasEstPromise = skipGasEstimate
|
||||
? Promise.resolve(null)
|
||||
: deployEstimateGas(contract, options, values).then(([gasEst, gas]) => gas);
|
||||
|
||||
return gasEstPromise
|
||||
.then((gas) => {
|
||||
if (gas) {
|
||||
options.gas = gas.toFixed(0);
|
||||
}
|
||||
|
||||
return WalletsUtils.isWallet(api, address);
|
||||
})
|
||||
.then((isWallet) => {
|
||||
if (!isWallet) {
|
||||
const encodedOptions = contract._encodeOptions(contract.constructors[0], options, values);
|
||||
|
||||
return api.parity.postTransaction(encodedOptions);
|
||||
}
|
||||
|
||||
return WalletsUtils.getDeployArgs(contract, options, values)
|
||||
.then((callArgs) => {
|
||||
const { func, options, values } = callArgs;
|
||||
|
||||
return func._postTransaction(options, values);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function parseTransactionReceipt (api, options, receipt) {
|
||||
const { metadata } = options;
|
||||
const address = options.from;
|
||||
|
||||
if (receipt.gasUsed.eq(options.gas)) {
|
||||
const error = new Error(`Contract not deployed, gasUsed == ${options.gas.toFixed(0)}`);
|
||||
|
||||
return Promise.reject(error);
|
||||
}
|
||||
|
||||
const logs = WalletsUtils.parseLogs(api, receipt.logs || []);
|
||||
|
||||
const confirmationLog = logs.find((log) => log.event === 'ConfirmationNeeded');
|
||||
const transactionLog = logs.find((log) => log.event === 'SingleTransact');
|
||||
|
||||
if (!confirmationLog && !transactionLog && !receipt.contractAddress) {
|
||||
const error = new Error('Something went wrong in the contract deployment...');
|
||||
|
||||
return Promise.reject(error);
|
||||
}
|
||||
|
||||
// Confirmations are needed from the other owners
|
||||
if (confirmationLog) {
|
||||
const operationHash = api.util.bytesToHex(confirmationLog.params.operation.value);
|
||||
|
||||
// Add the contract to pending contracts
|
||||
WalletsUtils.addPendingContract(address, operationHash, metadata);
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
|
||||
if (transactionLog) {
|
||||
// Set the contract address in the receipt
|
||||
receipt.contractAddress = transactionLog.params.created.value;
|
||||
}
|
||||
|
||||
const contractAddress = receipt.contractAddress;
|
||||
|
||||
return api.eth
|
||||
.getCode(contractAddress)
|
||||
.then((code) => {
|
||||
if (code === '0x') {
|
||||
throw new Error('Contract not deployed, getCode returned 0x');
|
||||
}
|
||||
|
||||
return contractAddress;
|
||||
});
|
||||
}
|
||||
|
||||
export function patchApi (api) {
|
||||
api.patch = {
|
||||
...api.patch,
|
||||
|
||||
@@ -25,6 +25,7 @@ import { NULL_ADDRESS } from './constants';
|
||||
export const ERRORS = {
|
||||
invalidAddress: 'address is an invalid network address',
|
||||
invalidAmount: 'the supplied amount should be a valid positive number',
|
||||
invalidAmountDecimals: 'the supplied amount exceeds the allowed decimals',
|
||||
duplicateAddress: 'the address is already in your address book',
|
||||
invalidChecksum: 'address has failed the checksum formatting',
|
||||
invalidName: 'name should not be blank and longer than 2',
|
||||
@@ -48,6 +49,7 @@ export function validateAbi (abi) {
|
||||
abiError = ERRORS.invalidAbi;
|
||||
|
||||
return {
|
||||
error: abiError,
|
||||
abi,
|
||||
abiError,
|
||||
abiParsed
|
||||
@@ -66,6 +68,7 @@ export function validateAbi (abi) {
|
||||
abiError = `${ERRORS.invalidAbi} (#${invalidIndex}: ${invalid.name || invalid.type})`;
|
||||
|
||||
return {
|
||||
error: abiError,
|
||||
abi,
|
||||
abiError,
|
||||
abiParsed
|
||||
@@ -78,6 +81,7 @@ export function validateAbi (abi) {
|
||||
}
|
||||
|
||||
return {
|
||||
error: abiError,
|
||||
abi,
|
||||
abiError,
|
||||
abiParsed
|
||||
@@ -123,6 +127,7 @@ export function validateAddress (address) {
|
||||
}
|
||||
|
||||
return {
|
||||
error: addressError,
|
||||
address,
|
||||
addressError
|
||||
};
|
||||
@@ -138,6 +143,7 @@ export function validateCode (code) {
|
||||
}
|
||||
|
||||
return {
|
||||
error: codeError,
|
||||
code,
|
||||
codeError
|
||||
};
|
||||
@@ -149,6 +155,7 @@ export function validateName (name) {
|
||||
: null;
|
||||
|
||||
return {
|
||||
error: nameError,
|
||||
name,
|
||||
nameError
|
||||
};
|
||||
@@ -168,6 +175,27 @@ export function validatePositiveNumber (number) {
|
||||
}
|
||||
|
||||
return {
|
||||
error: numberError,
|
||||
number,
|
||||
numberError
|
||||
};
|
||||
}
|
||||
|
||||
export function validateDecimalsNumber (number, base = 1) {
|
||||
let numberError = null;
|
||||
|
||||
try {
|
||||
const s = new BigNumber(number).mul(base).toFixed();
|
||||
|
||||
if (s.indexOf('.') !== -1) {
|
||||
numberError = ERRORS.invalidAmountDecimals;
|
||||
}
|
||||
} catch (e) {
|
||||
numberError = ERRORS.invalidAmount;
|
||||
}
|
||||
|
||||
return {
|
||||
error: numberError,
|
||||
number,
|
||||
numberError
|
||||
};
|
||||
@@ -189,6 +217,7 @@ export function validateUint (value) {
|
||||
}
|
||||
|
||||
return {
|
||||
error: valueError,
|
||||
value,
|
||||
valueError
|
||||
};
|
||||
|
||||
@@ -32,7 +32,8 @@ describe('util/validation', () => {
|
||||
name: 'test',
|
||||
inputs: [],
|
||||
outputs: []
|
||||
}]
|
||||
}],
|
||||
error: null
|
||||
});
|
||||
});
|
||||
|
||||
@@ -47,7 +48,8 @@ describe('util/validation', () => {
|
||||
name: 'test',
|
||||
inputs: [],
|
||||
outputs: []
|
||||
}]
|
||||
}],
|
||||
error: null
|
||||
});
|
||||
});
|
||||
|
||||
@@ -57,7 +59,8 @@ describe('util/validation', () => {
|
||||
expect(validateAbi(abi)).to.deep.equal({
|
||||
abi,
|
||||
abiError: ERRORS.invalidAbi,
|
||||
abiParsed: null
|
||||
abiParsed: null,
|
||||
error: ERRORS.invalidAbi
|
||||
});
|
||||
});
|
||||
|
||||
@@ -67,7 +70,8 @@ describe('util/validation', () => {
|
||||
expect(validateAbi(abi)).to.deep.equal({
|
||||
abi,
|
||||
abiError: ERRORS.invalidAbi,
|
||||
abiParsed: {}
|
||||
abiParsed: {},
|
||||
error: ERRORS.invalidAbi
|
||||
});
|
||||
});
|
||||
|
||||
@@ -77,7 +81,8 @@ describe('util/validation', () => {
|
||||
expect(validateAbi(abi)).to.deep.equal({
|
||||
abi,
|
||||
abiError: `${ERRORS.invalidAbi} (#0: event)`,
|
||||
abiParsed: [{ type: 'event' }]
|
||||
abiParsed: [{ type: 'event' }],
|
||||
error: `${ERRORS.invalidAbi} (#0: event)`
|
||||
});
|
||||
});
|
||||
|
||||
@@ -87,7 +92,8 @@ describe('util/validation', () => {
|
||||
expect(validateAbi(abi)).to.deep.equal({
|
||||
abi,
|
||||
abiError: `${ERRORS.invalidAbi} (#0: function)`,
|
||||
abiParsed: [{ type: 'function' }]
|
||||
abiParsed: [{ type: 'function' }],
|
||||
error: `${ERRORS.invalidAbi} (#0: function)`
|
||||
});
|
||||
});
|
||||
|
||||
@@ -97,7 +103,8 @@ describe('util/validation', () => {
|
||||
expect(validateAbi(abi)).to.deep.equal({
|
||||
abi,
|
||||
abiError: `${ERRORS.invalidAbi} (#0: somethingElse)`,
|
||||
abiParsed: [{ type: 'somethingElse' }]
|
||||
abiParsed: [{ type: 'somethingElse' }],
|
||||
error: `${ERRORS.invalidAbi} (#0: somethingElse)`
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -108,7 +115,8 @@ describe('util/validation', () => {
|
||||
|
||||
expect(validateAddress(address)).to.deep.equal({
|
||||
address,
|
||||
addressError: null
|
||||
addressError: null,
|
||||
error: null
|
||||
});
|
||||
});
|
||||
|
||||
@@ -117,14 +125,16 @@ describe('util/validation', () => {
|
||||
|
||||
expect(validateAddress(address.toLowerCase())).to.deep.equal({
|
||||
address,
|
||||
addressError: null
|
||||
addressError: null,
|
||||
error: null
|
||||
});
|
||||
});
|
||||
|
||||
it('sets error on null addresses', () => {
|
||||
expect(validateAddress(null)).to.deep.equal({
|
||||
address: null,
|
||||
addressError: ERRORS.invalidAddress
|
||||
addressError: ERRORS.invalidAddress,
|
||||
error: ERRORS.invalidAddress
|
||||
});
|
||||
});
|
||||
|
||||
@@ -133,7 +143,8 @@ describe('util/validation', () => {
|
||||
|
||||
expect(validateAddress(address)).to.deep.equal({
|
||||
address,
|
||||
addressError: ERRORS.invalidAddress
|
||||
addressError: ERRORS.invalidAddress,
|
||||
error: ERRORS.invalidAddress
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -142,35 +153,40 @@ describe('util/validation', () => {
|
||||
it('validates hex code', () => {
|
||||
expect(validateCode('0x123abc')).to.deep.equal({
|
||||
code: '0x123abc',
|
||||
codeError: null
|
||||
codeError: null,
|
||||
error: null
|
||||
});
|
||||
});
|
||||
|
||||
it('validates hex code (non-prefix)', () => {
|
||||
expect(validateCode('123abc')).to.deep.equal({
|
||||
code: '123abc',
|
||||
codeError: null
|
||||
codeError: null,
|
||||
error: null
|
||||
});
|
||||
});
|
||||
|
||||
it('sets error on invalid code', () => {
|
||||
expect(validateCode(null)).to.deep.equal({
|
||||
code: null,
|
||||
codeError: ERRORS.invalidCode
|
||||
codeError: ERRORS.invalidCode,
|
||||
error: ERRORS.invalidCode
|
||||
});
|
||||
});
|
||||
|
||||
it('sets error on empty code', () => {
|
||||
expect(validateCode('')).to.deep.equal({
|
||||
code: '',
|
||||
codeError: ERRORS.invalidCode
|
||||
codeError: ERRORS.invalidCode,
|
||||
error: ERRORS.invalidCode
|
||||
});
|
||||
});
|
||||
|
||||
it('sets error on non-hex code', () => {
|
||||
expect(validateCode('123hfg')).to.deep.equal({
|
||||
code: '123hfg',
|
||||
codeError: ERRORS.invalidCode
|
||||
codeError: ERRORS.invalidCode,
|
||||
error: ERRORS.invalidCode
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -179,21 +195,24 @@ describe('util/validation', () => {
|
||||
it('validates names', () => {
|
||||
expect(validateName('Joe Bloggs')).to.deep.equal({
|
||||
name: 'Joe Bloggs',
|
||||
nameError: null
|
||||
nameError: null,
|
||||
error: null
|
||||
});
|
||||
});
|
||||
|
||||
it('sets error on null names', () => {
|
||||
expect(validateName(null)).to.deep.equal({
|
||||
name: null,
|
||||
nameError: ERRORS.invalidName
|
||||
nameError: ERRORS.invalidName,
|
||||
error: ERRORS.invalidName
|
||||
});
|
||||
});
|
||||
|
||||
it('sets error on short names', () => {
|
||||
expect(validateName(' 1 ')).to.deep.equal({
|
||||
name: ' 1 ',
|
||||
nameError: ERRORS.invalidName
|
||||
nameError: ERRORS.invalidName,
|
||||
error: ERRORS.invalidName
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -202,35 +221,40 @@ describe('util/validation', () => {
|
||||
it('validates numbers', () => {
|
||||
expect(validatePositiveNumber(123)).to.deep.equal({
|
||||
number: 123,
|
||||
numberError: null
|
||||
numberError: null,
|
||||
error: null
|
||||
});
|
||||
});
|
||||
|
||||
it('validates strings', () => {
|
||||
expect(validatePositiveNumber('123')).to.deep.equal({
|
||||
number: '123',
|
||||
numberError: null
|
||||
numberError: null,
|
||||
error: null
|
||||
});
|
||||
});
|
||||
|
||||
it('validates bignumbers', () => {
|
||||
expect(validatePositiveNumber(new BigNumber(123))).to.deep.equal({
|
||||
number: new BigNumber(123),
|
||||
numberError: null
|
||||
numberError: null,
|
||||
error: null
|
||||
});
|
||||
});
|
||||
|
||||
it('sets error on invalid numbers', () => {
|
||||
expect(validatePositiveNumber(null)).to.deep.equal({
|
||||
number: null,
|
||||
numberError: ERRORS.invalidAmount
|
||||
numberError: ERRORS.invalidAmount,
|
||||
error: ERRORS.invalidAmount
|
||||
});
|
||||
});
|
||||
|
||||
it('sets error on negative numbers', () => {
|
||||
expect(validatePositiveNumber(-1)).to.deep.equal({
|
||||
number: -1,
|
||||
numberError: ERRORS.invalidAmount
|
||||
numberError: ERRORS.invalidAmount,
|
||||
error: ERRORS.invalidAmount
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -239,42 +263,48 @@ describe('util/validation', () => {
|
||||
it('validates numbers', () => {
|
||||
expect(validateUint(123)).to.deep.equal({
|
||||
value: 123,
|
||||
valueError: null
|
||||
valueError: null,
|
||||
error: null
|
||||
});
|
||||
});
|
||||
|
||||
it('validates strings', () => {
|
||||
expect(validateUint('123')).to.deep.equal({
|
||||
value: '123',
|
||||
valueError: null
|
||||
valueError: null,
|
||||
error: null
|
||||
});
|
||||
});
|
||||
|
||||
it('validates bignumbers', () => {
|
||||
expect(validateUint(new BigNumber(123))).to.deep.equal({
|
||||
value: new BigNumber(123),
|
||||
valueError: null
|
||||
valueError: null,
|
||||
error: null
|
||||
});
|
||||
});
|
||||
|
||||
it('sets error on invalid numbers', () => {
|
||||
expect(validateUint(null)).to.deep.equal({
|
||||
value: null,
|
||||
valueError: ERRORS.invalidNumber
|
||||
valueError: ERRORS.invalidNumber,
|
||||
error: ERRORS.invalidNumber
|
||||
});
|
||||
});
|
||||
|
||||
it('sets error on negative numbers', () => {
|
||||
expect(validateUint(-1)).to.deep.equal({
|
||||
value: -1,
|
||||
valueError: ERRORS.negativeNumber
|
||||
valueError: ERRORS.negativeNumber,
|
||||
error: ERRORS.negativeNumber
|
||||
});
|
||||
});
|
||||
|
||||
it('sets error on decimal numbers', () => {
|
||||
expect(validateUint(3.1415927)).to.deep.equal({
|
||||
value: 3.1415927,
|
||||
valueError: ERRORS.decimalNumber
|
||||
valueError: ERRORS.decimalNumber,
|
||||
error: ERRORS.decimalNumber
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user