Merge branch 'master' of https://github.com/ethereum/remix-project into github_fe

pull/5370/head
bunsenstraat 3 months ago
commit b67cd10d92
  1. 9
      libs/remix-lib/src/execution/txExecution.ts
  2. 14
      libs/remix-lib/src/execution/txRunner.ts
  3. 73
      libs/remix-lib/src/execution/txRunnerVM.ts
  4. 1
      libs/remix-lib/src/index.ts
  5. 24
      libs/remix-simulator/src/methods/transactions.ts
  6. 22
      libs/remix-simulator/src/methods/txProcess.ts
  7. 37
      libs/remix-simulator/test/transactions.ts

@ -1,6 +1,7 @@
'use strict' 'use strict'
import { ethers } from 'ethers' import { ethers } from 'ethers'
import { getFunctionFragment } from './txHelper' import { getFunctionFragment } from './txHelper'
import { Transaction } from './txRunner'
/** /**
* deploy the given contract * deploy the given contract
@ -16,11 +17,11 @@ import { getFunctionFragment } from './txHelper'
* [personal mode enabled, need password to continue] promptCb (okCb, cancelCb) * [personal mode enabled, need password to continue] promptCb (okCb, cancelCb)
* @param {Function} finalCallback - last callback. * @param {Function} finalCallback - last callback.
*/ */
export function createContract (from, data, value, gasLimit, txRunner, callbacks, finalCallback) { export function createContract ({ from, data, value, gasLimit, signed }: Transaction, txRunner, callbacks, finalCallback) {
if (!callbacks.confirmationCb || !callbacks.gasEstimationForceSend || !callbacks.promptCb) { if (!callbacks.confirmationCb || !callbacks.gasEstimationForceSend || !callbacks.promptCb) {
return finalCallback('all the callbacks must have been defined') return finalCallback('all the callbacks must have been defined')
} }
const tx = { from: from, to: null, data: data, useCall: false, value: value, gasLimit: gasLimit } const tx = { from: from, to: null, data: data, useCall: false, value: value, gasLimit: gasLimit, signed }
txRunner.rawRun(tx, callbacks.confirmationCb, callbacks.gasEstimationForceSend, callbacks.promptCb, (error, txResult) => { txRunner.rawRun(tx, callbacks.confirmationCb, callbacks.gasEstimationForceSend, callbacks.promptCb, (error, txResult) => {
// see universaldapp.js line 660 => 700 to check possible values of txResult (error case) // see universaldapp.js line 660 => 700 to check possible values of txResult (error case)
finalCallback(error, txResult) finalCallback(error, txResult)
@ -42,9 +43,9 @@ export function createContract (from, data, value, gasLimit, txRunner, callbacks
* [personal mode enabled, need password to continue] promptCb (okCb, cancelCb) * [personal mode enabled, need password to continue] promptCb (okCb, cancelCb)
* @param {Function} finalCallback - last callback. * @param {Function} finalCallback - last callback.
*/ */
export function callFunction (from, to, data, value, gasLimit, funAbi, txRunner, callbacks, finalCallback) { export function callFunction ({ from, to, data, value, gasLimit, signed }: Transaction, funAbi , txRunner, callbacks, finalCallback) {
const useCall = funAbi.stateMutability === 'view' || funAbi.stateMutability === 'pure' || funAbi.constant const useCall = funAbi.stateMutability === 'view' || funAbi.stateMutability === 'pure' || funAbi.constant
const tx = { from, to, data, useCall, value, gasLimit } const tx = { from, to, data, useCall, value, gasLimit, signed }
txRunner.rawRun(tx, callbacks.confirmationCb, callbacks.gasEstimationForceSend, callbacks.promptCb, (error, txResult) => { txRunner.rawRun(tx, callbacks.confirmationCb, callbacks.gasEstimationForceSend, callbacks.promptCb, (error, txResult) => {
// see universaldapp.js line 660 => 700 to check possible values of txResult (error case) // see universaldapp.js line 660 => 700 to check possible values of txResult (error case)
finalCallback(error, txResult) finalCallback(error, txResult)

@ -3,13 +3,14 @@ import { EventManager } from '../eventManager'
export type Transaction = { export type Transaction = {
from: string, from: string,
to: string, to?: string,
value: string, value: string,
data: string, data: string,
gasLimit: number, gasLimit: number,
useCall: boolean, useCall?: boolean,
timestamp?: number timestamp?: number,
type: '0x1' | '0x2' signed?: boolean,
type?: '0x1' | '0x2'
} }
export class TxRunner { export class TxRunner {
@ -32,9 +33,8 @@ export class TxRunner {
} }
execute (args: Transaction, confirmationCb, gasEstimationForceSend, promptCb, callback) { execute (args: Transaction, confirmationCb, gasEstimationForceSend, promptCb, callback) {
let data = args.data if (args.data && args.data.slice(0, 2) !== '0x') {
if (data.slice(0, 2) !== '0x') { args.data = '0x' + args.data
data = '0x' + data
} }
this.internalRunner.execute(args, confirmationCb, gasEstimationForceSend, promptCb, callback) this.internalRunner.execute(args, confirmationCb, gasEstimationForceSend, promptCb, callback)
} }

@ -69,47 +69,60 @@ export class TxRunnerVM {
} }
try { try {
this.runInVm(args.from, args.to, data, args.value, args.gasLimit, args.useCall, callback) this.runInVm(args, callback)
} catch (e) { } catch (e) {
callback(e, null) callback(e, null)
} }
} }
async runInVm (from: string, to: string, data: string, value: string, gasLimit: number, useCall: boolean, callback: VMExecutionCallBack) { async runInVm (tx: InternalTransaction, callback: VMExecutionCallBack) {
const { to, data, value, gasLimit, useCall, signed } = tx
let { from } = tx
let account let account
if (!from && useCall && Object.keys(this.vmaccounts).length) {
from = Object.keys(this.vmaccounts)[0]
account = this.vmaccounts[from]
} else account = this.vmaccounts[from]
if (!account) {
return callback('Invalid account selected')
}
try { try {
const res = await this.getVMObject().stateManager.getAccount(Address.fromString(from))
const EIP1559 = this.commonContext.hardfork() !== 'berlin' // berlin is the only pre eip1559 fork that we handle. const EIP1559 = this.commonContext.hardfork() !== 'berlin' // berlin is the only pre eip1559 fork that we handle.
let tx let tx
if (!EIP1559) { if (signed) {
tx = LegacyTransaction.fromTxData({ if (!EIP1559) {
nonce: useCall ? this.nextNonceForCall : res.nonce, tx = LegacyTransaction.fromSerializedTx(hexToBytes(data), { common: this.commonContext })
gasPrice: '0x1', } else {
gasLimit: gasLimit, tx = FeeMarketEIP1559Transaction.fromSerializedTx(hexToBytes(data), { common: this.commonContext })
to: (to as AddressLike), }
value: (value as BigIntLike),
data: hexToBytes(data)
}, { common: this.commonContext }).sign(account.privateKey)
} else {
tx = FeeMarketEIP1559Transaction.fromTxData({
nonce: useCall ? this.nextNonceForCall : res.nonce,
maxPriorityFeePerGas: '0x01',
maxFeePerGas: '0x7',
gasLimit: gasLimit,
to: (to as AddressLike),
value: (value as BigIntLike),
data: hexToBytes(data)
}).sign(account.privateKey)
} }
else {
if (!from && useCall && Object.keys(this.vmaccounts).length) {
from = Object.keys(this.vmaccounts)[0]
account = this.vmaccounts[from]
} else account = this.vmaccounts[from]
if (!account) {
return callback('Invalid account selected')
}
const res = await this.getVMObject().stateManager.getAccount(Address.fromString(from))
if (!EIP1559) {
tx = LegacyTransaction.fromTxData({
nonce: useCall ? this.nextNonceForCall : res.nonce,
gasPrice: '0x1',
gasLimit: gasLimit,
to: (to as AddressLike),
value: (value as BigIntLike),
data: hexToBytes(data)
}, { common: this.commonContext }).sign(account.privateKey)
} else {
tx = FeeMarketEIP1559Transaction.fromTxData({
nonce: useCall ? this.nextNonceForCall : res.nonce,
maxPriorityFeePerGas: '0x01',
maxFeePerGas: '0x7',
gasLimit: gasLimit,
to: (to as AddressLike),
value: (value as BigIntLike),
data: hexToBytes(data)
}).sign(account.privateKey)
}
}
if (useCall) this.nextNonceForCall++ if (useCall) this.nextNonceForCall++
const coinbases = ['0x0e9281e9c6a0808672eaba6bd1220e144c9bb07a', '0x8945a1288dc78a6d8952a92c77aee6730b414778', '0x94d76e24f818426ae84aa404140e8d5f60e10e7e'] const coinbases = ['0x0e9281e9c6a0808672eaba6bd1220e144c9bb07a', '0x8945a1288dc78a6d8952a92c77aee6730b414778', '0x94d76e24f818426ae84aa404140e8d5f60e10e7e']

@ -21,6 +21,7 @@ export { ICompilerApi, ConfigurationSettings, iSolJsonBinData, iSolJsonBinDataBu
export { QueryParams } from './query-params' export { QueryParams } from './query-params'
export { VMexecutionResult } from './execution/txRunnerVM' export { VMexecutionResult } from './execution/txRunnerVM'
export { Registry } from './registry' export { Registry } from './registry'
export type { Transaction } from './execution/txRunner'
const helpers = { const helpers = {
ui: uiHelper, ui: uiHelper,

@ -63,6 +63,7 @@ export class Transactions {
methods () { methods () {
return { return {
eth_sendTransaction: this.eth_sendTransaction.bind(this), eth_sendTransaction: this.eth_sendTransaction.bind(this),
eth_sendRawTransaction: this.eth_sendRawTransaction.bind(this),
eth_getTransactionReceipt: this.eth_getTransactionReceipt.bind(this), eth_getTransactionReceipt: this.eth_getTransactionReceipt.bind(this),
eth_getCode: this.eth_getCode.bind(this), eth_getCode: this.eth_getCode.bind(this),
eth_call: this.eth_call.bind(this), eth_call: this.eth_call.bind(this),
@ -80,6 +81,29 @@ export class Transactions {
} }
} }
eth_sendRawTransaction (payload, cb) {
payload.params[0] = { data: payload.params[0], signed: true }
processTx(this.txRunnerInstance, payload, false, (error, result: VMexecutionResult) => {
if (!error && result) {
this.vmContext.addBlock(result.block)
const hash = bytesToHex(result.tx.hash())
this.vmContext.trackTx(hash, result.block, result.tx)
const returnValue = `${bytesToHex(result.result.execResult.returnValue) || '0x0'}`
const execResult: VMExecResult = {
exceptionError: result.result.execResult.exceptionError,
executionGasUsed: result.result.execResult.executionGasUsed,
gas: result.result.execResult.gas,
gasRefund: result.result.execResult.gasRefund,
logs: result.result.execResult.logs,
returnValue
}
this.vmContext.trackExecResult(hash, execResult)
return cb(null, result.transactionHash)
}
cb(error)
})
}
eth_sendTransaction (payload, cb) { eth_sendTransaction (payload, cb) {
// from might be lowercased address (web3) // from might be lowercased address (web3)
if (payload.params && payload.params.length > 0 && payload.params[0].from) { if (payload.params && payload.params.length > 0 && payload.params[0].from) {

@ -1,7 +1,7 @@
import { execution } from '@remix-project/remix-lib' import { execution, Transaction } from '@remix-project/remix-lib'
const TxExecution = execution.txExecution const TxExecution = execution.txExecution
function runCall (payload, from, to, data, value, gasLimit, txRunner, callbacks, callback) { function runCall ({ from, to, data, value, gasLimit, signed }: Transaction, txRunner, callbacks, callback) {
const finalCallback = function (err, result) { const finalCallback = function (err, result) {
if (err) { if (err) {
return callback(err) return callback(err)
@ -9,10 +9,10 @@ function runCall (payload, from, to, data, value, gasLimit, txRunner, callbacks,
return callback(null, result) return callback(null, result)
} }
TxExecution.callFunction(from, to, data, value, gasLimit, { constant: true }, txRunner, callbacks, finalCallback) TxExecution.callFunction({ from, to, data, value, gasLimit, signed }, { constant: true }, txRunner, callbacks, finalCallback)
} }
function runTx (payload, from, to, data, value, gasLimit, txRunner, callbacks, callback) { function runTx ({ from, to, data, value, gasLimit, signed }: Transaction, txRunner, callbacks, callback) {
const finalCallback = function (err, result) { const finalCallback = function (err, result) {
if (err) { if (err) {
return callback(err) return callback(err)
@ -20,10 +20,10 @@ function runTx (payload, from, to, data, value, gasLimit, txRunner, callbacks, c
callback(null, result) callback(null, result)
} }
TxExecution.callFunction(from, to, data, value, gasLimit, { constant: false }, txRunner, callbacks, finalCallback) TxExecution.callFunction({ from, to, data, value, gasLimit, signed }, { constant: false }, txRunner, callbacks, finalCallback)
} }
function createContract (payload, from, data, value, gasLimit, txRunner, callbacks, callback) { function createContract ({ from, data, value, gasLimit, signed }: Transaction, txRunner, callbacks, callback) {
const finalCallback = function (err, result) { const finalCallback = function (err, result) {
if (err) { if (err) {
return callback(err) return callback(err)
@ -31,11 +31,11 @@ function createContract (payload, from, data, value, gasLimit, txRunner, callbac
callback(null, result) callback(null, result)
} }
TxExecution.createContract(from, data, value, gasLimit, txRunner, callbacks, finalCallback) TxExecution.createContract({ from, data, value, gasLimit, signed }, txRunner, callbacks, finalCallback)
} }
export function processTx (txRunnerInstance, payload, isCall, callback) { export function processTx (txRunnerInstance, payload, isCall, callback) {
let { from, to, data, input, value, gas } = payload.params[0] // eslint-disable-line let { from, to, data, input, value, gas, signed } = payload.params[0] // eslint-disable-line
gas = gas || 3000000 gas = gas || 3000000
const callbacks = { const callbacks = {
@ -54,10 +54,10 @@ export function processTx (txRunnerInstance, payload, isCall, callback) {
} }
if (isCall) { if (isCall) {
runCall(payload, from, to, data||input, value, gas, txRunnerInstance, callbacks, callback) runCall({ from, to, data: data||input, value, gasLimit: gas, signed }, txRunnerInstance, callbacks, callback)
} else if (to) { } else if (to) {
runTx(payload, from, to, data||input, value, gas, txRunnerInstance, callbacks, callback) runTx({ from, to, data: data||input, value, gasLimit: gas, signed }, txRunnerInstance, callbacks, callback)
} else { } else {
createContract(payload, from, data||input, value, gas, txRunnerInstance, callbacks, callback) createContract({ from, to: undefined, data: data||input, value, gasLimit: gas, signed }, txRunnerInstance, callbacks, callback)
} }
} }

@ -1,8 +1,11 @@
/* global describe, before, it */ /* global describe, before, it */
import Web3 from 'web3' import Web3 from 'web3'
import { LegacyTransaction, FeeMarketEIP1559Transaction } from '@ethereumjs/tx'
import { Provider } from '../src/index' import { Provider } from '../src/index'
const web3 = new Web3() const web3 = new Web3()
import * as assert from 'assert' import * as assert from 'assert'
import { bytesToHex, hexToBytes } from 'web3-utils'
import type { AddressLike } from '@ethereumjs/util'
describe('Transactions', () => { describe('Transactions', () => {
before(async function () { before(async function () {
@ -37,4 +40,38 @@ describe('Transactions', () => {
assert.equal(value, 14) assert.equal(value, 14)
}) })
}) })
describe('eth_sendRawTransaction', () => {
it('should deploy a contract and call a function using sendRawTransaction', async function () {
const accounts: string[] = await web3.eth.getAccounts()
const contractCreation = `0x02f908df0105010783069f638080b9088e608060405234801561001057600080fd5b5061005a6040518060400160405280601b81526020017f4f776e657220636f6e7472616374206465706c6f7965642062793a00000000008152503361011a60201b6101e91760201c565b336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555060008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16600073ffffffffffffffffffffffffffffffffffffffff167f342827c97908e5e2f71151c08502a66d44b6f758e3ac2f1de95f02eb95f0a73560405160405180910390a3610356565b6101b88282604051602401610130929190610265565b6040516020818303038152906040527f319af333000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506101bc60201b60201c565b5050565b6101dd816101d86101e060201b6102851761020160201b60201c565b60201c565b50565b60006a636f6e736f6c652e6c6f679050600080835160208501845afa505050565b61021360201b6102cb17819050919050565b61021b610316565b565b610226816102b1565b82525050565b600061023782610295565b61024181856102a0565b93506102518185602086016102e3565b61025a81610345565b840191505092915050565b6000604082019050818103600083015261027f818561022c565b905061028e602083018461021d565b9392505050565b600081519050919050565b600082825260208201905092915050565b60006102bc826102c3565b9050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60005b838110156103015780820151818401526020810190506102e6565b83811115610310576000848401525b50505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052605160045260246000fd5b6000601f19601f8301169050919050565b610529806103656000396000f3fe608060405234801561001057600080fd5b50600436106100365760003560e01c8063893d20e81461003b578063a6f9dae114610059575b600080fd5b610043610075565b6040516100509190610382565b60405180910390f35b610073600480360381019061006e91906102ea565b61009e565b005b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16905090565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461012c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610123906103cd565b60405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff1660008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f342827c97908e5e2f71151c08502a66d44b6f758e3ac2f1de95f02eb95f0a73560405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b61028182826040516024016101ff92919061039d565b6040516020818303038152906040527f319af333000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff83818316178352505050506102a6565b5050565b60006a636f6e736f6c652e6c6f679050600080835160208501845afa505050565b6102bd816102b56102856102c0565b63ffffffff16565b50565b6102cb819050919050565b6102d361046e565b565b6000813590506102e4816104dc565b92915050565b600060208284031215610300576102ff61049d565b5b600061030e848285016102d5565b91505092915050565b61032081610409565b82525050565b6000610331826103ed565b61033b81856103f8565b935061034b81856020860161043b565b610354816104a2565b840191505092915050565b600061036c6013836103f8565b9150610377826104b3565b602082019050919050565b60006020820190506103976000830184610317565b92915050565b600060408201905081810360008301526103b78185610326565b90506103c66020830184610317565b9392505050565b600060208201905081810360008301526103e68161035f565b9050919050565b600081519050919050565b600082825260208201905092915050565b60006104148261041b565b9050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60005b8381101561045957808201518184015260208101905061043e565b83811115610468576000848401525b50505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052605160045260246000fd5b600080fd5b6000601f19601f8301169050919050565b7f43616c6c6572206973206e6f74206f776e657200000000000000000000000000600082015250565b6104e581610409565b81146104f057600080fd5b5056fea2646970667358221220de534e7b87a9a1d73de47545c783b70d95ffebe049e62821ef2ef1c9a0fd7e8664736f6c63430008070033c080a0e9deb1e1b2cebf79e29cd9e69074f47a6680da51cb175f899a0b8c87aa893fb3a06ab1b35f54e47cd86191accdf8c915f998135c8cb58fccdb85125a9d1d1129da`
const receipt = await web3.eth.sendSignedTransaction(contractCreation)
// owner should be 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4
let value = await web3.eth.call({
from: accounts[0],
to: receipt.contractAddress,
data: '0x893d20e8'
})
assert.equal(value, '0x0000000000000000000000005B38Da6a701c568545dCfcB03FcB875f56beddC4'.toLowerCase())
const tx = FeeMarketEIP1559Transaction.fromTxData({
nonce: 1,
gasLimit: 1000000,
maxPriorityFeePerGas: '0x01',
maxFeePerGas: '0x7',
to: (hexToBytes(receipt.contractAddress) as AddressLike),
value: 0,
data: hexToBytes('0xa6f9dae1000000000000000000000000Ab8483F64d9C6d1EcF9b849Ae677dD3315835cb2')
}).sign(hexToBytes('0x503f38a9c967ed597e47fe25643985f032b072db8075426a92110f82df48dfcb'))
await web3.eth.sendSignedTransaction(bytesToHex(tx.serialize()))
// owner should be 0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2
value = await web3.eth.call({
from: accounts[0],
to: receipt.contractAddress,
data: '0x893d20e8'
})
assert.equal(value, '0x000000000000000000000000Ab8483F64d9C6d1EcF9b849Ae677dD3315835cb2'.toLowerCase())
})
})
}) })

Loading…
Cancel
Save