check for numeric type of amount

using sendRawTransaction
refactoring
add check for hexadecimal:wq
added ids
pull/5370/head
LianaHus 5 years ago committed by Liana Husikyan
parent c7c982f9d9
commit 70f893269b
  1. 388
      src/app/ui/universal-dapp-ui.js
  2. 8
      test-browser/commands/journalLastChild.js

@ -120,19 +120,16 @@ UniversalDAppUI.prototype.renderInstanceFromABI = function (contractABI, address
contractActionsWrapper.appendChild(this.getCallButton({ contractActionsWrapper.appendChild(this.getCallButton({
funABI: funABI, funABI: funABI,
address: address, address: address,
contractAbi: contractABI, contractABI: contractABI,
contractName: contractName contractName: contractName
})) }))
}) })
this.calldataInput = yo` const calldataInput = yo`
<input class="m-0" title="Input the amount of Ether to send to receive function."> <input class="w-100 m-0" title="The Calldata to send to fallback function of the contract.">
` `
this.amountInput = yo` const llIError = yo`
<input class="m-0" title="Input calldata to send to fallback function."> <label id="deployAndRunLLTxError" class="text-danger"></label>
`
this.llIError = yo`
<label class="text-danger"></label>
` `
// constract LLInteractions elements // constract LLInteractions elements
const lowLevelInteracions = yo` const lowLevelInteracions = yo`
@ -149,100 +146,171 @@ UniversalDAppUI.prototype.renderInstanceFromABI = function (contractABI, address
class="fas fa-info text-info my-3 mx-1"></i> class="fas fa-info text-info my-3 mx-1"></i>
</div> </div>
<div class="d-flex flex-column"> <div class="d-flex flex-column">
<div class="d-flex justify-content-end m-2 align-items-center">
<label class="mr-2 m-0">Amount</label>
${this.amountInput}
<button class="btn btn-sm btn-secondary" title="Send ether to contract." onclick=${() => sendEther()}>Send</button>
</div>
<div class="d-flex justify-content-end m-2 align-items-center"> <div class="d-flex justify-content-end m-2 align-items-center">
<label class="mr-2 m-0">Calldata</label> <label class="mr-2 m-0">Calldata</label>
${this.calldataInput} ${calldataInput}
<button class="btn btn-sm btn-secondary" title="Send data to contract." onclick=${() => sendCalldata()}>Send</button> <button id="deployAndRunLLTxSendTransaction" class="btn btn-sm btn-secondary" title="Send data to contract." onclick=${() => sendData()}>Transact</button>
</div> </div>
</div> </div>
<div> <div>
${this.llIError} ${llIError}
</div> </div>
</div> </div>
` `
function setLLIError (text) { function sendData () {
self.llIError.innerText = text let error = false
} function setLLIError (text) {
llIError.innerText = text
if (text !== '') error = true
}
function sendCalldata () {
setLLIError('') setLLIError('')
const fallback = self.udapp.getFallbackInterface(contractABI) const fallback = self.udapp.getFallbackInterface(contractABI)
const receive = self.udapp.getReceiveInterface(contractABI)
const args = { const args = {
funABI: fallback, funABI: fallback,
address: address, address: address,
contractAbi: contractABI, contractName: contractName,
contractName: contractName contractABI: contractABI
} }
if (!self.calldataInput.value) { let calldata = calldataInput.value
// show error: const amount = document.querySelector('#value').value
setLLIError('Calldata field is empty') if (amount !== '0') {
} else { // check for numeric and receive/fallback
if (fallback) { if (!isNumeric(amount)) {
// fallback is defined. call the fallback function setLLIError('Value to send should be a number')
self.clickButton(args) } else if (!receive && !(fallback && fallback.stateMutability === 'payable')) {
} else { setLLIError("In order to receive Ether transfer the contract should have either 'receive' or payable 'fallback' function")
// show error }
}
if (calldata) {
if (calldata.length > 3 && calldata.substr(0, 2) === '0x') {
if (!isHexadecimal(calldata.substr(2, calldata.length))) {
setLLIError('the calldata should be a valid hexadecimal value.')
}
}
if (!fallback) {
setLLIError("'fallback' function is not defined") setLLIError("'fallback' function is not defined")
} }
} }
if ((calldata || amount !== '0') && !error) self.runTransaction(false, args, null, calldata, null)
} }
function sendEther () { function isHexadecimal (value) {
setLLIError('') return /^[0-9a-fA-F]+$/.test(value)
const fallback = self.udapp.getFallbackInterface(contractABI) }
const receive = self.udapp.getReceiveInterface(contractABI)
const argsR = { function isNumeric (value) {
funABI: receive, return /^\+?(0|[1-9]\d*)$/.test(value)
address: address, }
contractAbi: contractABI,
contractName: contractName contractActionsWrapper.appendChild(lowLevelInteracions)
} return instance
const argsF = { }
funABI: receive,
address: address, UniversalDAppUI.prototype.confirmationCb = function (network, tx, gasEstimation, continueTxExecution, cancelCb) {
contractAbi: contractABI, let self = this
contractName: contractName if (network.name !== 'Main') {
return continueTxExecution(null)
}
var amount = Web3.utils.fromWei(typeConversion.toInt(tx.value), 'ether')
var content = confirmDialog(tx, amount, gasEstimation, self.udapp,
(gasPrice, cb) => {
let txFeeText, priceStatus
// TODO: this try catch feels like an anti pattern, can/should be
// removed, but for now keeping the original logic
try {
var fee = Web3.utils.toBN(tx.gas).mul(Web3.utils.toBN(Web3.utils.toWei(gasPrice.toString(10), 'gwei')))
txFeeText = ' ' + Web3.utils.fromWei(fee.toString(10), 'ether') + ' Ether'
priceStatus = true
} catch (e) {
txFeeText = ' Please fix this issue before sending any transaction. ' + e.message
priceStatus = false
}
cb(txFeeText, priceStatus)
},
(cb) => {
self.executionContext.web3().eth.getGasPrice((error, gasPrice) => {
const warnMessage = ' Please fix this issue before sending any transaction. '
if (error) {
return cb('Unable to retrieve the current network gas price.' + warnMessage + error)
}
try {
var gasPriceValue = Web3.utils.fromWei(gasPrice.toString(10), 'gwei')
cb(null, gasPriceValue)
} catch (e) {
cb(warnMessage + e.message, null, false)
}
})
} }
if (!self.amountInput.value) { )
// show error: modalDialog(
setLLIError('Ether amount field is empty') 'Confirm transaction',
} else { content,
if (receive) { { label: 'Confirm',
self.clickButton(argsR) fn: () => {
// receive is defined. call the fallback function self.udapp.config.setUnpersistedProperty(
} else if (fallback && fallback.stateMutability === 'payable') { 'doNotShowTransactionConfirmationAgain',
// receive is not defined but there is payable fallback function, call it content.querySelector('input#confirmsetting').checked
self.clickButton(argsF) )
} else { // TODO: check if this is check is still valid given the refactor
// show error if (!content.gasPriceStatus) {
setLLIError("In order to receive Ether transfer the contract should have either 'receive' or payable 'fallback' function") cancelCb('Given gas price is not correct')
} else {
var gasPrice = Web3.utils.toWei(content.querySelector('#gasprice').value, 'gwei')
continueTxExecution(gasPrice)
}
}
},
{
label: 'Cancel',
fn: () => {
return cancelCb('Transaction canceled by user.')
} }
} }
)
}
const continueCb = (error, continueTxExecution, cancelCb) => {
if (error) {
const msg = typeof error !== 'string' ? error.message : error
modalDialog(
'Gas estimation failed',
yo`
<div>Gas estimation errored with the following message (see below).
The transaction execution will likely fail. Do you want to force sending? <br>${msg}</div>
`,
{
label: 'Send Transaction',
fn: () => continueTxExecution()
},
{
label: 'Cancel Transaction',
fn: () => cancelCb()
}
)
} else {
continueTxExecution()
} }
}
contractActionsWrapper.appendChild(lowLevelInteracions) const promptCb = (okCb, cancelCb) => {
return instance modalCustom.promptPassphrase('Passphrase requested', 'Personal mode is enabled. Please provide passphrase of account', '', okCb, cancelCb)
} }
// TODO this is used by renderInstance when a new instance is displayed. // TODO this is used by renderInstance when a new instance is displayed.
// this returns a DOM element. // this returns a DOM element.
UniversalDAppUI.prototype.getCallButton = function (args) { UniversalDAppUI.prototype.getCallButton = function (args) {
let self = this let self = this
// args.funABI, args.address [fun only]
// args.contractName [constr only]
const lookupOnly = args.funABI.stateMutability === 'view' || args.funABI.stateMutability === 'pure' || !!args.funABI.constant
var outputOverride = yo`<div class=${css.value}></div>` // show return value var outputOverride = yo`<div class=${css.value}></div>` // show return value
const lookupOnly = args.funABI.stateMutability === 'view' || args.funABI.stateMutability === 'pure' || args.funABI.constant
const multiParamManager = new MultiParamManager(lookupOnly, args.funABI, (valArray, inputsValue) => { const multiParamManager = new MultiParamManager(
this.clickButton(args, valArray, inputsValue) lookupOnly,
}, self.udapp.getInputs(args.funABI)) args.funABI,
(valArray, inputsValues) => self.runTransaction(lookupOnly, args, valArray, inputsValues, outputOverride),
self.udapp.getInputs(args.funABI)
)
const contractActionsContainer = yo`<div class="${css.contractActionsContainer}" >${multiParamManager.render()}</div>` const contractActionsContainer = yo`<div class="${css.contractActionsContainer}" >${multiParamManager.render()}</div>`
contractActionsContainer.appendChild(outputOverride) contractActionsContainer.appendChild(outputOverride)
@ -250,155 +318,59 @@ UniversalDAppUI.prototype.getCallButton = function (args) {
return contractActionsContainer return contractActionsContainer
} }
UniversalDAppUI.prototype.clickButton = function (args, valArr, inputsValue) { UniversalDAppUI.prototype.runTransaction = function (lookupOnly, args, valArr, inputsValues, outputOverride) {
let self = this let self = this
// check if it's a special function and add a name in case it is let logMsg
const fuctionName = args.contractName + if (!lookupOnly) {
(args.funABI.name ? args.funABI.name : args.funABI.type === 'receive' ? '(receive)' : '(fallback)') logMsg = `call to ${args.contractName}.${(args.funABI.name) ? args.funABI.name : '(fallback)'}`
} else {
const lookupOnly = args.funABI.stateMutability === 'view' || args.funABI.stateMutability === 'pure' || !!args.funABI.constant logMsg = `transact to ${args.contractName}.${(args.funABI.name) ? args.funABI.name : '(fallback)'}`
const logMsg = lookupOnly ? `transact to ${fuctionName}` : `call to ${fuctionName}`
var value = inputsValue
const confirmationCb = (network, tx, gasEstimation, continueTxExecution, cancelCb) => {
if (network.name !== 'Main') {
return continueTxExecution(null)
}
var amount = Web3.utils.fromWei(typeConversion.toInt(tx.value), 'ether')
var content = confirmDialog(tx, amount, gasEstimation, self.udapp,
(gasPrice, cb) => {
let txFeeText, priceStatus
// TODO: this try catch feels like an anti pattern, can/should be
// removed, but for now keeping the original logic
try {
var fee = Web3.utils.toBN(tx.gas).mul(Web3.utils.toBN(Web3.utils.toWei(gasPrice.toString(10), 'gwei')))
txFeeText = ' ' + Web3.utils.fromWei(fee.toString(10), 'ether') + ' Ether'
priceStatus = true
} catch (e) {
txFeeText = ' Please fix this issue before sending any transaction. ' + e.message
priceStatus = false
}
cb(txFeeText, priceStatus)
},
(cb) => {
self.executionContext.web3().eth.getGasPrice((error, gasPrice) => {
const warnMessage = ' Please fix this issue before sending any transaction. '
if (error) {
return cb('Unable to retrieve the current network gas price.' + warnMessage + error)
}
try {
var gasPriceValue = Web3.utils.fromWei(gasPrice.toString(10), 'gwei')
cb(null, gasPriceValue)
} catch (e) {
cb(warnMessage + e.message, null, false)
}
})
}
)
modalDialog(
'Confirm transaction',
content,
{ label: 'Confirm',
fn: () => {
self.udapp.config.setUnpersistedProperty(
'doNotShowTransactionConfirmationAgain',
content.querySelector('input#confirmsetting').checked
)
// TODO: check if this is check is still valid given the refactor
if (!content.gasPriceStatus) {
cancelCb('Given gas price is not correct')
} else {
var gasPrice = Web3.utils.toWei(content.querySelector('#gasprice').value, 'gwei')
continueTxExecution(gasPrice)
}
}}, {
label: 'Cancel',
fn: () => {
return cancelCb('Transaction canceled by user.')
}
}
)
} }
const continueCb = (error, continueTxExecution, cancelCb) => { var value = inputsValues
if (error) {
const msg = typeof error !== 'string' ? error.message : error
modalDialog(
'Gas estimation failed',
yo`
<div>Gas estimation errored with the following message (see below).
The transaction execution will likely fail. Do you want to force sending? <br>${msg}</div>
`,
{
label: 'Send Transaction',
fn: () => continueTxExecution()
},
{
label: 'Cancel Transaction',
fn: () => cancelCb()
}
)
} else {
continueTxExecution()
}
}
const outputCb = (decoded) => { const outputCb = (decoded) => {
outputOverride.innerHTML = '' if (outputOverride) {
outputOverride.appendChild(decoded) outputOverride.innerHTML = ''
} outputOverride.appendChild(decoded)
}
const promptCb = (okCb, cancelCb) => {
modalCustom.promptPassphrase('Passphrase requested', 'Personal mode is enabled. Please provide passphrase of account', '', okCb, cancelCb)
} }
const isSpecialFunction = args.funABI.type === 'fallback' || args.funABI.type === 'receive'
// contractsDetails is used to resolve libraries // contractsDetails is used to resolve libraries
txFormat.buildData( txFormat.buildData(args.contractName, args.contractABI, {}, false, args.funABI, args.funABI.type !== 'fallback' ? value : '', (error, data) => {
args.contractName, if (!error) {
args.contractAbi, if (!lookupOnly) {
{}, self.logCallback(`${logMsg} pending ... `)
false,
args.funABI,
!isSpecialFunction ? value : '', // input parameters for the function to call
(error, data) => {
if (!error) {
if (!lookupOnly) {
self.logCallback(`${logMsg} pending ... `)
} else {
self.logCallback(`${logMsg}`)
}
if (isSpecialFunction) data.dataHex = value
self.udapp.callFunction(args.address, data, args.funABI, confirmationCb, continueCb, promptCb, (error, txResult) => {
if (!error) {
var isVM = self.executionContext.isVM()
if (isVM) {
var vmError = txExecution.checkVMError(txResult)
if (vmError.error) {
self.logCallback(`${logMsg} errored: ${vmError.message} `)
return
}
}
if (lookupOnly) {
const decoded = decodeResponseToTreeView(self.executionContext.isVM() ? txResult.result.execResult.returnValue : ethJSUtil.toBuffer(txResult.result), args.funABI)
outputCb(decoded)
}
} else {
self.logCallback(`${logMsg} errored: ${error} `)
}
})
} else { } else {
self.logCallback(`${logMsg} errored: ${error} `) self.logCallback(`${logMsg}`)
} }
}, (msg) => { if (args.funABI.type === 'fallback') data.dataHex = value
self.logCallback(msg) self.udapp.callFunction(args.address, data, args.funABI, this.confirmationCb, continueCb, promptCb, (error, txResult) => {
}, (data, runTxCallback) => { if (!error) {
// called for libraries deployment var isVM = self.executionContext.isVM()
self.udapp.runTx(data, confirmationCb, runTxCallback) if (isVM) {
var vmError = txExecution.checkVMError(txResult)
if (vmError.error) {
self.logCallback(`${logMsg} errored: ${vmError.message} `)
return
}
}
if (lookupOnly) {
const decoded = decodeResponseToTreeView(self.executionContext.isVM() ? txResult.result.execResult.returnValue : ethJSUtil.toBuffer(txResult.result), args.funABI)
outputCb(decoded)
}
} else {
self.logCallback(`${logMsg} errored: ${error} `)
}
})
} else {
self.logCallback(`${logMsg} errored: ${error} `)
} }
) }, (msg) => {
self.logCallback(msg)
}, (data, runTxCallback) => {
// called for libraries deployment
self.udapp.runTx(data, this.confirmationCb, runTxCallback)
})
} }
module.exports = UniversalDAppUI module.exports = UniversalDAppUI

@ -3,10 +3,10 @@ const EventEmitter = require('events')
class JournalLastChild extends EventEmitter { class JournalLastChild extends EventEmitter {
command (val) { command (val) {
this.api this.api
.waitForElementVisible('#journal div:last-child span.text-info', 10000) .waitForElementVisible('#journal div:last-child span.text-info', 10000)
.assert.containsText('#journal div:last-child span.text-info', val).perform(() => { .assert.containsText('#journal div:last-child span.text-info', val).perform(() => {
this.emit('complete') this.emit('complete')
}) })
return this return this
} }
} }

Loading…
Cancel
Save