import Web3 from 'web3' import { toChecksumAddress, BN, Address } from 'ethereumjs-util' import { processTx } from './txProcess' import { execution } from '@remix-project/remix-lib' const TxRunnerVM = execution.TxRunnerVM const TxRunner = execution.TxRunner export class Transactions { vmContext accounts tags txRunnerVMInstance txRunnerInstance constructor (vmContext) { this.vmContext = vmContext this.tags = {} } init (accounts) { this.accounts = accounts const api = { logMessage: (msg) => { }, logHtmlMessage: (msg) => { }, config: { getUnpersistedProperty: (key) => { return true }, get: () => { return true } }, detectNetwork: (cb) => { cb() }, personalMode: () => { return false } } this.txRunnerVMInstance = new TxRunnerVM(accounts, api, _ => this.vmContext.vm()) this.txRunnerInstance = new TxRunner(this.txRunnerVMInstance, { runAsync: false }) this.txRunnerInstance.vmaccounts = accounts } methods () { return { eth_sendTransaction: this.eth_sendTransaction.bind(this), eth_getTransactionReceipt: this.eth_getTransactionReceipt.bind(this), eth_getCode: this.eth_getCode.bind(this), eth_call: this.eth_call.bind(this), eth_estimateGas: this.eth_estimateGas.bind(this), eth_getTransactionCount: this.eth_getTransactionCount.bind(this), eth_getTransactionByHash: this.eth_getTransactionByHash.bind(this), eth_getTransactionByBlockHashAndIndex: this.eth_getTransactionByBlockHashAndIndex.bind(this), eth_getTransactionByBlockNumberAndIndex: this.eth_getTransactionByBlockNumberAndIndex.bind(this), eth_getExecutionResultFromSimulator: this.eth_getExecutionResultFromSimulator.bind(this), eth_getHashFromTagBySimulator: this.eth_getHashFromTagBySimulator.bind(this) } } eth_sendTransaction (payload, cb) { // from might be lowercased address (web3) if (payload.params && payload.params.length > 0 && payload.params[0].from) { payload.params[0].from = toChecksumAddress(payload.params[0].from) } processTx(this.txRunnerInstance, payload, false, (error, result) => { if (!error && result) { this.vmContext.addBlock(result.block) const hash = '0x' + result.tx.hash().toString('hex') this.vmContext.trackTx(hash, result.block) this.vmContext.trackExecResult(hash, result.result.execResult) return cb(null, result.transactionHash) } cb(error) }) } eth_getExecutionResultFromSimulator (payload, cb) { const txHash = payload.params[0] cb(null, this.vmContext.exeResults[txHash]) } eth_getTransactionReceipt (payload, cb) { this.vmContext.web3().eth.getTransactionReceipt(payload.params[0], (error, receipt) => { if (error) { return cb(error) } const txBlock = this.vmContext.txs[receipt.hash] const r: Record = { transactionHash: receipt.hash, transactionIndex: '0x00', blockHash: '0x' + txBlock.hash().toString('hex'), blockNumber: '0x' + txBlock.header.number.toString('hex'), gasUsed: Web3.utils.toHex(receipt.gas), cumulativeGasUsed: Web3.utils.toHex(receipt.gas), contractAddress: receipt.contractAddress, logs: receipt.logs, status: receipt.status, to: receipt.to } if (r.blockNumber === '0x') { r.blockNumber = '0x0' } cb(null, r) }) } eth_estimateGas (payload, cb) { cb(null, 3000000) } eth_getCode (payload, cb) { const address = payload.params[0] this.vmContext.web3().eth.getCode(address, (error, result) => { if (error) { console.dir('error getting code') console.dir(error) } cb(error, result) }) } eth_call (payload, cb) { // from might be lowercased address (web3) if (payload.params && payload.params.length > 0 && payload.params[0].from) { payload.params[0].from = toChecksumAddress(payload.params[0].from) } if (payload.params && payload.params.length > 0 && payload.params[0].to) { payload.params[0].to = toChecksumAddress(payload.params[0].to) } payload.params[0].value = undefined const tag = payload.params[0].timestamp // e2e reference processTx(this.txRunnerInstance, payload, true, (error, result) => { if (!error && result) { this.vmContext.addBlock(result.block) const hash = '0x' + result.tx.hash().toString('hex') this.vmContext.trackTx(hash, result.block) this.vmContext.trackExecResult(hash, result.result.execResult) this.tags[tag] = result.transactionHash // calls are not supposed to return a transaction hash. we do this for keeping track of it and allowing debugging calls. const returnValue = `0x${result.result.execResult.returnValue.toString('hex') || '0'}` return cb(null, returnValue) } cb(error) }) } eth_getHashFromTagBySimulator (payload, cb) { return cb(null, this.tags[payload.params[0]]) } eth_getTransactionCount (payload, cb) { const address = payload.params[0] this.vmContext.vm().stateManager.getAccount(Address.fromString(address)).then((account) => { const nonce = new BN(account.nonce).toString(10) cb(null, nonce) }).catch((error) => { cb(error) }) } eth_getTransactionByHash (payload, cb) { const address = payload.params[0] this.vmContext.web3().eth.getTransactionReceipt(address, (error, receipt) => { if (error) { return cb(error) } const txBlock = this.vmContext.txs[receipt.transactionHash] // TODO: params to add later const r: Record = { blockHash: '0x' + txBlock.hash().toString('hex'), blockNumber: '0x' + txBlock.header.number.toString('hex'), from: receipt.from, gas: Web3.utils.toHex(receipt.gas), // 'gasPrice': '2000000000000', // 0x123 gasPrice: '0x4a817c800', // 20000000000 hash: receipt.transactionHash, input: receipt.input, nonce: 2, // 0x15 // the nonce should be updated // "transactionIndex": 0, value: receipt.value // "value":"0xf3dbb76162000" // 4290000000000000 // "v": "0x25", // 37 // "r": "0x1b5e176d927f8e9ab405058b2d2457392da3e20f328b16ddabcebc33eaac5fea", // "s": "0x4ba69724e8f69de52f0125ad8b3c5c2cef33019bac3249e2c0a2192766d1721c" } if (receipt.to) { r['to'] = receipt.to } if (r.value === '0x') { r.value = '0x0' } if (r.blockNumber === '0x') { r.blockNumber = '0x0' } cb(null, r) }) } eth_getTransactionByBlockHashAndIndex (payload, cb) { const txIndex = payload.params[1] const txBlock = this.vmContext.blocks[payload.params[0]] const txHash = '0x' + txBlock.transactions[Web3.utils.toDecimal(txIndex)].hash().toString('hex') this.vmContext.web3().eth.getTransactionReceipt(txHash, (error, receipt) => { if (error) { return cb(error) } // TODO: params to add later const r: Record = { blockHash: '0x' + txBlock.hash().toString('hex'), blockNumber: '0x' + txBlock.header.number.toString('hex'), from: receipt.from, gas: Web3.utils.toHex(receipt.gas), // 'gasPrice': '2000000000000', // 0x123 gasPrice: '0x4a817c800', // 20000000000 hash: receipt.transactionHash, input: receipt.input, nonce: 2, // 0x15 // the nonce should be updated // "transactionIndex": 0, value: receipt.value // "value":"0xf3dbb76162000" // 4290000000000000 // "v": "0x25", // 37 // "r": "0x1b5e176d927f8e9ab405058b2d2457392da3e20f328b16ddabcebc33eaac5fea", // "s": "0x4ba69724e8f69de52f0125ad8b3c5c2cef33019bac3249e2c0a2192766d1721c" } if (receipt.to) { r['to'] = receipt.to } if (r.value === '0x') { r.value = '0x0' } cb(null, r) }) } eth_getTransactionByBlockNumberAndIndex (payload, cb) { const txIndex = payload.params[1] const txBlock = this.vmContext.blocks[payload.params[0]] const txHash = '0x' + txBlock.transactions[Web3.utils.toDecimal(txIndex)].hash().toString('hex') this.vmContext.web3().eth.getTransactionReceipt(txHash, (error, receipt) => { if (error) { return cb(error) } // TODO: params to add later const r: Record = { blockHash: '0x' + txBlock.hash().toString('hex'), blockNumber: '0x' + txBlock.header.number.toString('hex'), from: receipt.from, gas: Web3.utils.toHex(receipt.gas), // 'gasPrice': '2000000000000', // 0x123 gasPrice: '0x4a817c800', // 20000000000 hash: receipt.transactionHash, input: receipt.input, nonce: 2, // 0x15 // the nonce should be updated // "transactionIndex": 0, value: receipt.value // "value":"0xf3dbb76162000" // 4290000000000000 // "v": "0x25", // 37 // "r": "0x1b5e176d927f8e9ab405058b2d2457392da3e20f328b16ddabcebc33eaac5fea", // "s": "0x4ba69724e8f69de52f0125ad8b3c5c2cef33019bac3249e2c0a2192766d1721c" } if (receipt.to) { r['to'] = receipt.to } if (r.value === '0x') { r.value = '0x0' } cb(null, r) }) } }