@ -1,23 +1,23 @@
import React from 'react' // eslint-disable-line
import React from 'react' // eslint-disable-line
import { fromWei , toBigInt , toWei } from 'web3-utils'
import { fromWei , toBigInt , toWei } from 'web3-utils'
import { Plugin } from '@remixproject/engine'
import { Plugin } from '@remixproject/engine'
import { toBuffer , addHexPrefix } from '@ethereumjs/util'
import { toBuffer , addHexPrefix } from '@ethereumjs/util'
import { EventEmitter } from 'events'
import { EventEmitter } from 'events'
import { format } from 'util'
import { format } from 'util'
import { ExecutionContext } from './execution-context'
import { ExecutionContext } from './execution-context'
import Config from '../config'
import Config from '../config'
import { VMProvider } from './providers/vm'
import { VMProvider } from './providers/vm'
import { InjectedProvider } from './providers/injected'
import { InjectedProvider } from './providers/injected'
import { NodeProvider } from './providers/node'
import { NodeProvider } from './providers/node'
import { execution , EventManager , helpers } from '@remix-project/remix-lib'
import { execution , EventManager , helpers } from '@remix-project/remix-lib'
import { etherScanLink } from './helper'
import { etherScanLink } from './helper'
import { logBuilder , cancelUpgradeMsg , cancelProxyMsg , addressToString } from "@remix-ui/helper"
import { logBuilder , cancelUpgradeMsg , cancelProxyMsg , addressToString } from '@remix-ui/helper'
const { txFormat , txExecution , typeConversion , txListener : Txlistener , TxRunner , TxRunnerWeb3 , txHelper } = execution
const { txFormat , txExecution , typeConversion , txListener : Txlistener , TxRunner , TxRunnerWeb3 , txHelper } = execution
const { txResultHelper } = helpers
const { txResultHelper } = helpers
const { resultToRemixTx } = txResultHelper
const { resultToRemixTx } = txResultHelper
import * as packageJson from '../../../../package.json'
import * as packageJson from '../../../../package.json'
const _paq = window . _paq = window . _paq || [ ] //eslint-disable-line
const _paq = ( window . _paq = window . _paq || [ ] ) //eslint-disable-line
const profile = {
const profile = {
name : 'blockchain' ,
name : 'blockchain' ,
@ -28,19 +28,19 @@ const profile = {
}
}
export type TransactionContextAPI = {
export type TransactionContextAPI = {
getAddress : ( cb : ( error : Error , result : string ) = > void ) = > void ,
getAddress : ( cb : ( error : Error , result : string ) = > void ) = > void
getValue : ( cb : ( error : Error , result : string ) = > void ) = > void ,
getValue : ( cb : ( error : Error , result : string ) = > void ) = > void
getGasLimit : ( cb : ( error : Error , result : string ) = > void ) = > void
getGasLimit : ( cb : ( error : Error , result : string ) = > void ) = > void
}
}
// see TxRunner.ts in remix-lib
// see TxRunner.ts in remix-lib
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
}
}
@ -54,16 +54,16 @@ export class Blockchain extends Plugin {
networkcallid : number
networkcallid : number
networkStatus : {
networkStatus : {
network : {
network : {
name : string ,
name : string
id : string
id : string
}
}
error? : string
error? : string
}
}
providers : { [ key : string ] : VMProvider | InjectedProvider | NodeProvider }
providers : { [ key : string ] : VMProvider | InjectedProvider | NodeProvider }
transactionContextAPI : TransactionContextAPI
transactionContextAPI : TransactionContextAPI
// NOTE: the config object will need to be refactored out in remix-lib
// NOTE: the config object will need to be refactored out in remix-lib
constructor ( config : Config ) {
constructor ( config : Config ) {
super ( profile )
super ( profile )
this . active = false
this . active = false
this . event = new EventManager ( )
this . event = new EventManager ( )
@ -71,55 +71,61 @@ export class Blockchain extends Plugin {
this . events = new EventEmitter ( )
this . events = new EventEmitter ( )
this . config = config
this . config = config
const web3Runner = new TxRunnerWeb3 ( {
const web3Runner = new TxRunnerWeb3 (
config : this.config ,
{
detectNetwork : ( cb ) = > {
config : this.config ,
this . executionContext . detectNetwork ( cb )
detectNetwork : ( cb ) = > {
this . executionContext . detectNetwork ( cb )
} ,
isVM : ( ) = > {
return this . executionContext . isVM ( )
} ,
personalMode : ( ) = > {
return this . getProvider ( ) === 'web3' ? this . config . get ( 'settings/personal-mode' ) : false
}
} ,
} ,
isVM : ( ) = > { return this . executionContext . isVM ( ) } ,
( _ ) = > this . executionContext . web3 ( ) ,
personalMode : ( ) = > {
( _ ) = > this . executionContext . currentblockGasLimit ( )
return this . getProvider ( ) === 'web3' ? this . config . get ( 'settings/personal-mode' ) : false
)
}
} , _ = > this . executionContext . web3 ( ) , _ = > this . executionContext . currentblockGasLimit ( ) )
this . txRunner = new TxRunner ( web3Runner , { } )
this . txRunner = new TxRunner ( web3Runner , { } )
this . networkcallid = 0
this . networkcallid = 0
this . networkStatus = { network : { name : ' - ' , id : ' - ' } }
this . networkStatus = { network : { name : ' - ' , id : ' - ' } }
this . setupEvents ( )
this . setupEvents ( )
this . setupProviders ( )
this . setupProviders ( )
}
}
_triggerEvent ( name , args ) {
_triggerEvent ( name , args ) {
if ( ! this . active ) return
if ( ! this . active ) return
this . event . trigger ( name , args )
this . event . trigger ( name , args )
this . emit ( name , . . . args )
this . emit ( name , . . . args )
}
}
onActivation ( ) {
onActivation() {
this . active = true
this . active = true
this . on ( 'injected' , 'chainChanged' , ( ) = > {
this . on ( 'injected' , 'chainChanged' , ( ) = > {
this . detectNetwork ( ( error , network ) = > {
this . detectNetwork ( ( error , network ) = > {
this . networkStatus = { network , error }
this . networkStatus = { network , error }
this . _triggerEvent ( 'networkStatus' , [ this . networkStatus ] )
this . _triggerEvent ( 'networkStatus' , [ this . networkStatus ] )
} )
} )
} )
} )
this . on ( 'injected-trustwallet' , 'chainChanged' , ( ) = > {
this . on ( 'injected-trustwallet' , 'chainChanged' , ( ) = > {
this . detectNetwork ( ( error , network ) = > {
this . detectNetwork ( ( error , network ) = > {
this . networkStatus = { network , error }
this . networkStatus = { network , error }
this . _triggerEvent ( 'networkStatus' , [ this . networkStatus ] )
this . _triggerEvent ( 'networkStatus' , [ this . networkStatus ] )
} )
} )
} )
} )
this . on ( 'walletconnect' , 'chainChanged' , ( ) = > {
this . on ( 'walletconnect' , 'chainChanged' , ( ) = > {
this . detectNetwork ( ( error , network ) = > {
this . detectNetwork ( ( error , network ) = > {
this . networkStatus = { network , error }
this . networkStatus = { network , error }
this . _triggerEvent ( 'networkStatus' , [ this . networkStatus ] )
this . _triggerEvent ( 'networkStatus' , [ this . networkStatus ] )
} )
} )
} )
} )
}
}
onDeactivation ( ) {
onDeactivation() {
this . active = false
this . active = false
this . off ( 'injected' , 'chainChanged' )
this . off ( 'injected' , 'chainChanged' )
this . off ( 'injected-trustwallet' , 'chainChanged' )
this . off ( 'injected-trustwallet' , 'chainChanged' )
@ -127,12 +133,12 @@ export class Blockchain extends Plugin {
this . off ( 'walletconnect' , 'accountsChanged' )
this . off ( 'walletconnect' , 'accountsChanged' )
}
}
setupEvents ( ) {
setupEvents() {
this . executionContext . event . register ( 'contextChanged' , async ( context ) = > {
this . executionContext . event . register ( 'contextChanged' , async ( context ) = > {
await this . resetEnvironment ( )
await this . resetEnvironment ( )
this . _triggerEvent ( 'contextChanged' , [ context ] )
this . _triggerEvent ( 'contextChanged' , [ context ] )
this . detectNetwork ( ( error , network ) = > {
this . detectNetwork ( ( error , network ) = > {
this . networkStatus = { network , error }
this . networkStatus = { network , error }
this . _triggerEvent ( 'networkStatus' , [ this . networkStatus ] )
this . _triggerEvent ( 'networkStatus' , [ this . networkStatus ] )
} )
} )
} )
} )
@ -147,17 +153,17 @@ export class Blockchain extends Plugin {
setInterval ( ( ) = > {
setInterval ( ( ) = > {
this . detectNetwork ( ( error , network ) = > {
this . detectNetwork ( ( error , network ) = > {
this . networkStatus = { network , error }
this . networkStatus = { network , error }
this . _triggerEvent ( 'networkStatus' , [ this . networkStatus ] )
this . _triggerEvent ( 'networkStatus' , [ this . networkStatus ] )
} )
} )
} , 30000 )
} , 30000 )
}
}
getCurrentNetworkStatus ( ) {
getCurrentNetworkStatus() {
return this . networkStatus
return this . networkStatus
}
}
setupProviders ( ) {
setupProviders() {
const vmProvider = new VMProvider ( this . executionContext )
const vmProvider = new VMProvider ( this . executionContext )
this . providers = { }
this . providers = { }
this . providers [ 'vm' ] = vmProvider
this . providers [ 'vm' ] = vmProvider
@ -165,7 +171,7 @@ export class Blockchain extends Plugin {
this . providers . web3 = new NodeProvider ( this . executionContext , this . config )
this . providers . web3 = new NodeProvider ( this . executionContext , this . config )
}
}
getCurrentProvider ( ) {
getCurrentProvider() {
const provider = this . getProvider ( )
const provider = this . getProvider ( )
if ( provider && provider . startsWith ( 'vm' ) ) return this . providers [ 'vm' ]
if ( provider && provider . startsWith ( 'vm' ) ) return this . providers [ 'vm' ]
if ( this . providers [ provider ] ) return this . providers [ provider ]
if ( this . providers [ provider ] ) return this . providers [ provider ]
@ -174,11 +180,11 @@ export class Blockchain extends Plugin {
/** Return the list of accounts */
/** Return the list of accounts */
// note: the dual promise/callback is kept for now as it was before
// note: the dual promise/callback is kept for now as it was before
getAccounts ( cb ) {
getAccounts ( cb ) {
console . log ( 'getAccounts' )
console . log ( 'getAccounts' )
return new Promise ( ( resolve , reject ) = > {
return new Promise ( ( resolve , reject ) = > {
this . getCurrentProvider ( ) . getAccounts ( ( error , accounts ) = > {
this . getCurrentProvider ( ) . getAccounts ( ( error , accounts ) = > {
console . log ( 'get accounts res' , error , accounts )
console . log ( 'get accounts res' , error , accounts )
if ( cb ) {
if ( cb ) {
return cb ( error , accounts )
return cb ( error , accounts )
}
}
@ -190,36 +196,53 @@ export class Blockchain extends Plugin {
} )
} )
}
}
deployContractAndLibraries ( selectedContract , args , contractMetadata , compilerContracts , callbacks , confirmationCb ) {
deployContractAndLibraries ( selectedContract , args , contractMetadata , compilerContracts , callbacks , confirmationCb ) {
const { continueCb , promptCb , statusCb , finalCb } = callbacks
const { continueCb , promptCb , statusCb , finalCb } = callbacks
const constructor = selectedContract . getConstructorInterface ( )
const constructor = selectedContract . getConstructorInterface ( )
txFormat . buildData ( selectedContract . name , selectedContract . object , compilerContracts , true , constructor , args , ( error , data ) = > {
txFormat . buildData (
if ( error ) {
selectedContract . name ,
return statusCb ( ` creation of ${ selectedContract . name } errored: ${ error . message ? error.message : error } ` )
selectedContract . object ,
}
compilerContracts ,
true ,
constructor ,
args ,
( error , data ) = > {
if ( error ) {
return statusCb ( ` creation of ${ selectedContract . name } errored: ${ error . message ? error.message : error } ` )
}
statusCb ( ` creation of ${ selectedContract . name } pending... ` )
statusCb ( ` creation of ${ selectedContract . name } pending... ` )
this . createContract ( selectedContract , data , continueCb , promptCb , confirmationCb , finalCb )
this . createContract ( selectedContract , data , continueCb , promptCb , confirmationCb , finalCb )
} , statusCb , ( data , runTxCallback ) = > {
} ,
// called for libraries deployment
statusCb ,
this . runTx ( data , confirmationCb , continueCb , promptCb , runTxCallback )
( data , runTxCallback ) = > {
} )
// called for libraries deployment
this . runTx ( data , confirmationCb , continueCb , promptCb , runTxCallback )
}
)
}
}
deployContractWithLibrary ( selectedContract , args , contractMetadata , compilerContracts , callbacks , confirmationCb ) {
deployContractWithLibrary ( selectedContract , args , contractMetadata , compilerContracts , callbacks , confirmationCb ) {
const { continueCb , promptCb , statusCb , finalCb } = callbacks
const { continueCb , promptCb , statusCb , finalCb } = callbacks
const constructor = selectedContract . getConstructorInterface ( )
const constructor = selectedContract . getConstructorInterface ( )
txFormat . encodeConstructorCallAndLinkLibraries ( selectedContract . object , args , constructor , contractMetadata . linkReferences , selectedContract . bytecodeLinkReferences , ( error , data ) = > {
txFormat . encodeConstructorCallAndLinkLibraries (
if ( error ) {
selectedContract . object ,
return statusCb ( ` creation of ${ selectedContract . name } errored: ${ error . message ? error.message : error } ` )
args ,
}
constructor ,
contractMetadata . linkReferences ,
selectedContract . bytecodeLinkReferences ,
( error , data ) = > {
if ( error ) {
return statusCb ( ` creation of ${ selectedContract . name } errored: ${ error . message ? error.message : error } ` )
}
statusCb ( ` creation of ${ selectedContract . name } pending... ` )
statusCb ( ` creation of ${ selectedContract . name } pending... ` )
this . createContract ( selectedContract , data , continueCb , promptCb , confirmationCb , finalCb )
this . createContract ( selectedContract , data , continueCb , promptCb , confirmationCb , finalCb )
} )
}
)
}
}
async deployProxy ( proxyData , implementationContractObject ) {
async deployProxy ( proxyData , implementationContractObject ) {
const proxyModal = {
const proxyModal = {
id : 'confirmProxyDeployment' ,
id : 'confirmProxyDeployment' ,
title : 'Confirm Deploy Proxy (ERC1967)' ,
title : 'Confirm Deploy Proxy (ERC1967)' ,
@ -241,16 +264,20 @@ export class Blockchain extends Plugin {
this . call ( 'notification' , 'modal' , proxyModal )
this . call ( 'notification' , 'modal' , proxyModal )
}
}
async runProxyTx ( proxyData , implementationContractObject ) {
async runProxyTx ( proxyData , implementationContractObject ) {
const args = { useCall : false , data : proxyData }
const args = { useCall : false , data : proxyData }
let networkInfo
let networkInfo
const confirmationCb = ( network , tx , gasEstimation , continueTxExecution , cancelCb ) = > {
const confirmationCb = ( network , tx , gasEstimation , continueTxExecution , cancelCb ) = > {
networkInfo = network
networkInfo = network
// continue using original authorization given by user
// continue using original authorization given by user
continueTxExecution ( null )
continueTxExecution ( null )
}
}
const continueCb = ( error , continueTxExecution , cancelCb ) = > { continueTxExecution ( ) }
const continueCb = ( error , continueTxExecution , cancelCb ) = > {
const promptCb = ( okCb , cancelCb ) = > { okCb ( ) }
continueTxExecution ( )
}
const promptCb = ( okCb , cancelCb ) = > {
okCb ( )
}
const finalCb = async ( error , txResult , address , returnValue ) = > {
const finalCb = async ( error , txResult , address , returnValue ) = > {
if ( error ) {
if ( error ) {
const log = logBuilder ( error )
const log = logBuilder ( error )
@ -288,16 +315,20 @@ export class Blockchain extends Plugin {
this . call ( 'notification' , 'modal' , upgradeModal )
this . call ( 'notification' , 'modal' , upgradeModal )
}
}
async runUpgradeTx ( proxyAddress , data , newImplementationContractObject ) {
async runUpgradeTx ( proxyAddress , data , newImplementationContractObject ) {
const args = { useCall : false , data , to : proxyAddress }
const args = { useCall : false , data , to : proxyAddress }
let networkInfo
let networkInfo
const confirmationCb = ( network , tx , gasEstimation , continueTxExecution , cancelCb ) = > {
const confirmationCb = ( network , tx , gasEstimation , continueTxExecution , cancelCb ) = > {
// continue using original authorization given by user
// continue using original authorization given by user
networkInfo = network
networkInfo = network
continueTxExecution ( null )
continueTxExecution ( null )
}
}
const continueCb = ( error , continueTxExecution , cancelCb ) = > { continueTxExecution ( ) }
const continueCb = ( error , continueTxExecution , cancelCb ) = > {
const promptCb = ( okCb , cancelCb ) = > { okCb ( ) }
continueTxExecution ( )
}
const promptCb = ( okCb , cancelCb ) = > {
okCb ( )
}
const finalCb = async ( error , txResult , address , returnValue ) = > {
const finalCb = async ( error , txResult , address , returnValue ) = > {
if ( error ) {
if ( error ) {
const log = logBuilder ( error )
const log = logBuilder ( error )
@ -312,8 +343,8 @@ export class Blockchain extends Plugin {
this . runTx ( args , confirmationCb , continueCb , promptCb , finalCb )
this . runTx ( args , confirmationCb , continueCb , promptCb , finalCb )
}
}
async saveDeployedContractStorageLayout ( contractObject , proxyAddress , networkInfo ) {
async saveDeployedContractStorageLayout ( contractObject , proxyAddress , networkInfo ) {
const { contractName , implementationAddress } = contractObject
const { contractName , implementationAddress } = contractObject
const networkName = networkInfo . name === 'custom' ? networkInfo . name + '-' + networkInfo.id : networkInfo.name
const networkName = networkInfo . name === 'custom' ? networkInfo . name + '-' + networkInfo.id : networkInfo.name
const hasPreviousDeploys = await this . call ( 'fileManager' , 'exists' , ` .deploys/upgradeable-contracts/ ${ networkName } /UUPS.json ` )
const hasPreviousDeploys = await this . call ( 'fileManager' , 'exists' , ` .deploys/upgradeable-contracts/ ${ networkName } /UUPS.json ` )
// TODO: make deploys folder read only.
// TODO: make deploys folder read only.
@ -336,32 +367,59 @@ export class Blockchain extends Plugin {
solcOutput : contractObject.compiler.data ,
solcOutput : contractObject.compiler.data ,
solcInput : contractObject.compiler.source
solcInput : contractObject.compiler.source
}
}
await this . call ( 'fileManager' , 'writeFile' , ` .deploys/upgradeable-contracts/ ${ networkName } /solc- ${ implementationAddress } .json ` , JSON . stringify ( {
await this . call (
solcInput : contractObject.compiler.source ,
'fileManager' ,
solcOutput : contractObject.compiler.data
'writeFile' ,
} , null , 2 ) )
` .deploys/upgradeable-contracts/ ${ networkName } /solc- ${ implementationAddress } .json ` ,
JSON . stringify (
{
solcInput : contractObject.compiler.source ,
solcOutput : contractObject.compiler.data
} ,
null ,
2
)
)
await this . call ( 'fileManager' , 'writeFile' , ` .deploys/upgradeable-contracts/ ${ networkName } /UUPS.json ` , JSON . stringify ( parsedDeployments , null , 2 ) )
await this . call ( 'fileManager' , 'writeFile' , ` .deploys/upgradeable-contracts/ ${ networkName } /UUPS.json ` , JSON . stringify ( parsedDeployments , null , 2 ) )
} else {
} else {
await this . call ( 'fileManager' , 'writeFile' , ` .deploys/upgradeable-contracts/ ${ networkName } /solc- ${ implementationAddress } .json ` , JSON . stringify ( {
await this . call (
solcInput : contractObject.compiler.source ,
'fileManager' ,
solcOutput : contractObject.compiler.data
'writeFile' ,
} , null , 2 ) )
` .deploys/upgradeable-contracts/ ${ networkName } /solc- ${ implementationAddress } .json ` ,
await this . call ( 'fileManager' , 'writeFile' , ` .deploys/upgradeable-contracts/ ${ networkName } /UUPS.json ` , JSON . stringify ( {
JSON . stringify (
id : networkInfo.id ,
{
network : networkInfo.name ,
solcInput : contractObject.compiler.source ,
deployments : {
solcOutput : contractObject.compiler.data
[ proxyAddress ] : {
} ,
date : new Date ( ) . toISOString ( ) ,
null ,
contractName : contractName ,
2
fork : networkInfo.currentFork ,
)
implementationAddress : implementationAddress
)
}
await this . call (
}
'fileManager' ,
} , null , 2 ) )
'writeFile' ,
` .deploys/upgradeable-contracts/ ${ networkName } /UUPS.json ` ,
JSON . stringify (
{
id : networkInfo.id ,
network : networkInfo.name ,
deployments : {
[ proxyAddress ] : {
date : new Date ( ) . toISOString ( ) ,
contractName : contractName ,
fork : networkInfo.currentFork ,
implementationAddress : implementationAddress
}
}
} ,
null ,
2
)
)
}
}
}
}
async getEncodedFunctionHex ( args , funABI ) {
async getEncodedFunctionHex ( args , funABI ) {
return new Promise ( ( resolve , reject ) = > {
return new Promise ( ( resolve , reject ) = > {
txFormat . encodeFunctionCall ( args , funABI , ( error , data ) = > {
txFormat . encodeFunctionCall ( args , funABI , ( error , data ) = > {
if ( error ) return reject ( error )
if ( error ) return reject ( error )
@ -370,7 +428,7 @@ export class Blockchain extends Plugin {
} )
} )
}
}
async getEncodedParams ( args , funABI ) {
async getEncodedParams ( args , funABI ) {
return new Promise ( ( resolve , reject ) = > {
return new Promise ( ( resolve , reject ) = > {
txFormat . encodeParams ( args , funABI , ( error , encodedParams ) = > {
txFormat . encodeParams ( args , funABI , ( error , encodedParams ) = > {
if ( error ) return reject ( error )
if ( error ) return reject ( error )
@ -379,27 +437,25 @@ export class Blockchain extends Plugin {
} )
} )
}
}
createContract ( selectedContract , data , continueCb , promptCb , confirmationCb , finalCb ) {
createContract ( selectedContract , data , continueCb , promptCb , confirmationCb , finalCb ) {
if ( data ) {
if ( data ) {
data . contractName = selectedContract . name
data . contractName = selectedContract . name
data . linkReferences = selectedContract . bytecodeLinkReferences
data . linkReferences = selectedContract . bytecodeLinkReferences
data . contractABI = selectedContract . abi
data . contractABI = selectedContract . abi
}
}
this . runTx ( { data : data , useCall : false } , confirmationCb , continueCb , promptCb ,
this . runTx ( { data : data , useCall : false } , confirmationCb , continueCb , promptCb , ( error , txResult , address ) = > {
( error , txResult , address ) = > {
if ( error ) {
if ( error ) {
return finalCb ( ` creation of ${ selectedContract . name } errored: ${ error . message ? error.message : error } ` )
return finalCb ( ` creation of ${ selectedContract . name } errored: ${ error . message ? error.message : error } ` )
}
if ( txResult . receipt . status === false || txResult . receipt . status === '0x0' || txResult . receipt . status === 0 ) {
return finalCb ( ` creation of ${ selectedContract . name } errored: transaction execution failed ` )
}
finalCb ( null , selectedContract , address )
}
}
)
if ( txResult . receipt . status === false || txResult . receipt . status === '0x0' || txResult . receipt . status === 0 ) {
return finalCb ( ` creation of ${ selectedContract . name } errored: transaction execution failed ` )
}
finalCb ( null , selectedContract , address )
} )
}
}
determineGasPrice ( cb ) {
determineGasPrice ( cb ) {
this . getCurrentProvider ( ) . getGasPrice ( ( error , gasPrice ) = > {
this . getCurrentProvider ( ) . getGasPrice ( ( error , gasPrice ) = > {
const warnMessage = ' Please fix this issue before sending any transaction. '
const warnMessage = ' Please fix this issue before sending any transaction. '
if ( error ) {
if ( error ) {
@ -414,29 +470,29 @@ export class Blockchain extends Plugin {
} )
} )
}
}
getInputs ( funABI ) {
getInputs ( funABI ) {
if ( ! funABI . inputs ) {
if ( ! funABI . inputs ) {
return ''
return ''
}
}
return txHelper . inputParametersDeclarationToString ( funABI . inputs )
return txHelper . inputParametersDeclarationToString ( funABI . inputs )
}
}
fromWei ( value , doTypeConversion , unit ) {
fromWei ( value , doTypeConversion , unit ) {
if ( doTypeConversion ) {
if ( doTypeConversion ) {
return fromWei ( typeConversion . toInt ( value ) , unit || 'ether' )
return fromWei ( typeConversion . toInt ( value ) , unit || 'ether' )
}
}
return fromWei ( value . toString ( 10 ) , unit || 'ether' )
return fromWei ( value . toString ( 10 ) , unit || 'ether' )
}
}
toWei ( value , unit ) {
toWei ( value , unit ) {
return toWei ( value , unit || 'gwei' )
return toWei ( value , unit || 'gwei' )
}
}
calculateFee ( gas , gasPrice , unit ? ) {
calculateFee ( gas , gasPrice , unit ? ) {
return toBigInt ( gas ) * toBigInt ( toWei ( gasPrice . toString ( 10 ) as string , unit || 'gwei' ) )
return toBigInt ( gas ) * toBigInt ( toWei ( gasPrice . toString ( 10 ) as string , unit || 'gwei' ) )
}
}
determineGasFees ( tx ) {
determineGasFees ( tx ) {
const determineGasFeesCb = ( gasPrice , cb ) = > {
const determineGasFeesCb = ( gasPrice , cb ) = > {
let txFeeText , priceStatus
let txFeeText , priceStatus
// TODO: this try catch feels like an anti pattern, can/should be
// TODO: this try catch feels like an anti pattern, can/should be
@ -455,19 +511,19 @@ export class Blockchain extends Plugin {
return determineGasFeesCb
return determineGasFeesCb
}
}
changeExecutionContext ( context , confirmCb , infoCb , cb ) {
changeExecutionContext ( context , confirmCb , infoCb , cb ) {
return this . executionContext . executionContextChange ( context , null , confirmCb , infoCb , cb )
return this . executionContext . executionContextChange ( context , null , confirmCb , infoCb , cb )
}
}
detectNetwork ( cb ) {
detectNetwork ( cb ) {
return this . executionContext . detectNetwork ( cb )
return this . executionContext . detectNetwork ( cb )
}
}
getProvider ( ) {
getProvider() {
return this . executionContext . getProvider ( )
return this . executionContext . getProvider ( )
}
}
getInjectedWeb3Address ( ) {
getInjectedWeb3Address() {
return this . executionContext . getSelectedAddress ( )
return this . executionContext . getSelectedAddress ( )
}
}
@ -475,29 +531,29 @@ export class Blockchain extends Plugin {
* return the fork name applied to the current envionment
* return the fork name applied to the current envionment
* @return { String } - fork name
* @return { String } - fork name
* /
* /
getCurrentFork ( ) {
getCurrentFork() {
return this . executionContext . getCurrentFork ( )
return this . executionContext . getCurrentFork ( )
}
}
isWeb3Provider ( ) {
isWeb3Provider() {
const isVM = this . executionContext . isVM ( )
const isVM = this . executionContext . isVM ( )
const isInjected = this . getProvider ( ) === 'injected'
const isInjected = this . getProvider ( ) === 'injected'
return ( ! isVM && ! isInjected )
return ! isVM && ! isInjected
}
}
isInjectedWeb3 ( ) {
isInjectedWeb3() {
return this . getProvider ( ) === 'injected'
return this . getProvider ( ) === 'injected'
}
}
signMessage ( message , account , passphrase , cb ) {
signMessage ( message , account , passphrase , cb ) {
this . getCurrentProvider ( ) . signMessage ( message , account , passphrase , cb )
this . getCurrentProvider ( ) . signMessage ( message , account , passphrase , cb )
}
}
web3VM ( ) {
web3VM() {
return ( this . providers . vm as VMProvider ) . web3
return ( this . providers . vm as VMProvider ) . web3
}
}
web3 ( ) {
web3() {
// @todo(https://github.com/ethereum/remix-project/issues/431)
// @todo(https://github.com/ethereum/remix-project/issues/431)
const isVM = this . executionContext . isVM ( )
const isVM = this . executionContext . isVM ( )
if ( isVM ) {
if ( isVM ) {
@ -506,7 +562,7 @@ export class Blockchain extends Plugin {
return this . executionContext . web3 ( )
return this . executionContext . web3 ( )
}
}
getTxListener ( opts ) {
getTxListener ( opts ) {
opts . event = {
opts . event = {
// udapp: this.udapp.event
// udapp: this.udapp.event
udapp : this.event
udapp : this.event
@ -515,84 +571,107 @@ export class Blockchain extends Plugin {
return txlistener
return txlistener
}
}
runOrCallContractMethod ( contractName , contractAbi , funABI , contract , value , address , callType , lookupOnly , logMsg , logCallback , outputCb , confirmationCb , continueCb , promptCb ) {
runOrCallContractMethod ( contractName , contractAbi , funABI , contract , value , address , callType , lookupOnly , logMsg , logCallback , outputCb , confirmationCb , continueCb , promptCb ) {
// contractsDetails is used to resolve libraries
// contractsDetails is used to resolve libraries
txFormat . buildData ( contractName , contractAbi , { } , false , funABI , callType , ( error , data ) = > {
txFormat . buildData (
if ( error ) {
contractName ,
return logCallback ( ` ${ logMsg } errored: ${ error . message ? error.message : error } ` )
contractAbi ,
}
{ } ,
if ( ! lookupOnly ) {
false ,
logCallback ( ` ${ logMsg } pending ... ` )
funABI ,
} else {
callType ,
logCallback ( ` ${ logMsg } ` )
( error , data ) = > {
}
if ( funABI . type === 'fallback' ) data . dataHex = value
if ( data ) {
data . contractName = contractName
data . contractABI = contractAbi
data . contract = contract
}
const useCall = funABI . stateMutability === 'view' || funABI . stateMutability === 'pure'
this . runTx ( { to : address , data , useCall } , confirmationCb , continueCb , promptCb , ( error , txResult , _address , returnValue ) = > {
if ( error ) {
if ( error ) {
return logCallback ( ` ${ logMsg } errored: ${ error . message ? error.message : error } ` )
return logCallback ( ` ${ logMsg } errored: ${ error . message ? error.message : error } ` )
}
}
if ( lookupOnly ) {
if ( ! lookupOnly ) {
outputCb ( returnValue )
logCallback ( ` ${ logMsg } pending ... ` )
} else {
logCallback ( ` ${ logMsg } ` )
}
}
} )
if ( funABI . type === 'fallback' ) data . dataHex = value
} ,
( msg ) = > {
if ( data ) {
logCallback ( msg )
data . contractName = contractName
} ,
data . contractABI = contractAbi
( data , runTxCallback ) = > {
data . contract = contract
// called for libraries deployment
}
this . runTx ( data , confirmationCb , runTxCallback , promptCb , ( ) = > { /* Do nothing. */ } )
const useCall = funABI . stateMutability === 'view' || funABI . stateMutability === 'pure'
} )
this . runTx ( { to : address , data , useCall } , confirmationCb , continueCb , promptCb , ( error , txResult , _address , returnValue ) = > {
if ( error ) {
return logCallback ( ` ${ logMsg } errored: ${ error . message ? error.message : error } ` )
}
if ( lookupOnly ) {
outputCb ( returnValue )
}
} )
} ,
( msg ) = > {
logCallback ( msg )
} ,
( data , runTxCallback ) = > {
// called for libraries deployment
this . runTx ( data , confirmationCb , runTxCallback , promptCb , ( ) = > {
/* Do nothing. */
} )
}
)
}
}
context ( ) {
context() {
return ( this . executionContext . isVM ( ) ? 'memory' : 'blockchain' )
return this . executionContext . isVM ( ) ? 'memory' : 'blockchain'
}
}
// NOTE: the config is only needed because exectuionContext.init does
// NOTE: the config is only needed because exectuionContext.init does
async resetAndInit ( config : Config , transactionContextAPI : TransactionContextAPI ) {
async resetAndInit ( config : Config , transactionContextAPI : TransactionContextAPI ) {
this . transactionContextAPI = transactionContextAPI
this . transactionContextAPI = transactionContextAPI
this . executionContext . init ( config )
this . executionContext . init ( config )
this . executionContext . stopListenOnLastBlock ( )
this . executionContext . stopListenOnLastBlock ( )
this . executionContext . listenOnLastBlock ( )
this . executionContext . listenOnLastBlock ( )
}
}
addProvider ( provider ) {
addProvider ( provider ) {
this . executionContext . addProvider ( provider )
this . executionContext . addProvider ( provider )
}
}
removeProvider ( name ) {
removeProvider ( name ) {
this . executionContext . removeProvider ( name )
this . executionContext . removeProvider ( name )
}
}
// TODO : event should be triggered by Udapp instead of TxListener
// TODO : event should be triggered by Udapp instead of TxListener
/** Listen on New Transaction. (Cannot be done inside constructor because txlistener doesn't exist yet) */
/** Listen on New Transaction. (Cannot be done inside constructor because txlistener doesn't exist yet) */
startListening ( txlistener ) {
startListening ( txlistener ) {
txlistener . event . register ( 'newTransaction' , ( tx , receipt ) = > {
txlistener . event . register ( 'newTransaction' , ( tx , receipt ) = > {
this . events . emit ( 'newTransaction' , tx , receipt )
this . events . emit ( 'newTransaction' , tx , receipt )
} )
} )
}
}
async resetEnvironment ( ) {
async resetEnvironment() {
await this . getCurrentProvider ( ) . resetEnvironment ( )
await this . getCurrentProvider ( ) . resetEnvironment ( )
// TODO: most params here can be refactored away in txRunner
// TODO: most params here can be refactored away in txRunner
const web3Runner = new TxRunnerWeb3 ( {
const web3Runner = new TxRunnerWeb3 (
config : this.config ,
{
detectNetwork : ( cb ) = > {
config : this.config ,
this . executionContext . detectNetwork ( cb )
detectNetwork : ( cb ) = > {
this . executionContext . detectNetwork ( cb )
} ,
isVM : ( ) = > {
return this . executionContext . isVM ( )
} ,
personalMode : ( ) = > {
return this . getProvider ( ) === 'web3' ? this . config . get ( 'settings/personal-mode' ) : false
}
} ,
} ,
isVM : ( ) = > { return this . executionContext . isVM ( ) } ,
personalMode : ( ) = > {
// isVM: () => { return this.executionContext.isVM() },
return this . getProvider ( ) === 'web3' ? this . config . get ( 'settings/personal-mode' ) : false
// personalMode: () => {
}
// return this.getProvider() === 'web3' ? this.config.get('settings/personal-mode') : false
} , _ = > this . executionContext . web3 ( ) , _ = > this . executionContext . currentblockGasLimit ( ) )
// }
// }, _ => this.executionContext.web3(), _ => this.executionContext.currentblockGasLimit())
( _ ) = > this . executionContext . web3 ( ) ,
( _ ) = > this . executionContext . currentblockGasLimit ( )
)
web3Runner . event . register ( 'transactionBroadcasted' , ( txhash ) = > {
web3Runner . event . register ( 'transactionBroadcasted' , ( txhash ) = > {
this . executionContext . detectNetwork ( ( error , network ) = > {
this . executionContext . detectNetwork ( ( error , network ) = > {
@ -601,10 +680,13 @@ export class Blockchain extends Plugin {
const viewEtherScanLink = etherScanLink ( network . name , txhash )
const viewEtherScanLink = etherScanLink ( network . name , txhash )
if ( viewEtherScanLink ) {
if ( viewEtherScanLink ) {
this . call ( 'terminal' , 'logHtml' ,
this . call (
( < a href = { etherScanLink ( network . name , txhash ) } target = "_blank" >
'terminal' ,
view on etherscan
'logHtml' ,
< / a > ) )
< a href = { etherScanLink ( network . name , txhash ) } target = "_blank" >
view on etherscan
< / a >
)
}
}
} )
} )
} )
} )
@ -615,23 +697,23 @@ export class Blockchain extends Plugin {
* Create a VM Account
* Create a VM Account
* @param { { privateKey : string , balance : string } } newAccount The new account to create
* @param { { privateKey : string , balance : string } } newAccount The new account to create
* /
* /
createVMAccount ( newAccount ) {
createVMAccount ( newAccount ) {
if ( ! this . executionContext . isVM ( ) ) {
if ( ! this . executionContext . isVM ( ) ) {
throw new Error ( 'plugin API does not allow creating a new account through web3 connection. Only vm mode is allowed' )
throw new Error ( 'plugin API does not allow creating a new account through web3 connection. Only vm mode is allowed' )
}
}
return ( this . providers . vm as VMProvider ) . createVMAccount ( newAccount )
return ( this . providers . vm as VMProvider ) . createVMAccount ( newAccount )
}
}
newAccount ( _password , passwordPromptCb , cb ) {
newAccount ( _password , passwordPromptCb , cb ) {
return this . getCurrentProvider ( ) . newAccount ( passwordPromptCb , cb )
return this . getCurrentProvider ( ) . newAccount ( passwordPromptCb , cb )
}
}
/** Get the balance of an address, and convert wei to ether */
/** Get the balance of an address, and convert wei to ether */
getBalanceInEther ( address ) {
getBalanceInEther ( address ) {
return this . getCurrentProvider ( ) . getBalanceInEther ( address )
return this . getCurrentProvider ( ) . getBalanceInEther ( address )
}
}
pendingTransactionsCount ( ) {
pendingTransactionsCount() {
return Object . keys ( this . txRunner . pendingTxs ) . length
return Object . keys ( this . txRunner . pendingTxs ) . length
}
}
@ -639,7 +721,7 @@ export class Blockchain extends Plugin {
return await this . web3 ( ) . eth . getCode ( address )
return await this . web3 ( ) . eth . getCode ( address )
}
}
async getTransactionReceipt ( hash ) {
async getTransactionReceipt ( hash ) {
return await this . web3 ( ) . eth . getTransactionReceipt ( hash )
return await this . web3 ( ) . eth . getTransactionReceipt ( hash )
}
}
@ -649,7 +731,7 @@ export class Blockchain extends Plugin {
*
*
* @param { Object } tx - transaction .
* @param { Object } tx - transaction .
* /
* /
sendTransaction ( tx : Transaction ) {
sendTransaction ( tx : Transaction ) {
return new Promise ( ( resolve , reject ) = > {
return new Promise ( ( resolve , reject ) = > {
this . executionContext . detectNetwork ( ( error , network ) = > {
this . executionContext . detectNetwork ( ( error , network ) = > {
if ( error ) return reject ( error )
if ( error ) return reject ( error )
@ -659,17 +741,26 @@ export class Blockchain extends Plugin {
this . txRunner . rawRun (
this . txRunner . rawRun (
tx ,
tx ,
( network , tx , gasEstimation , continueTxExecution , cancelCb ) = > { continueTxExecution ( ) } ,
( network , tx , gasEstimation , continueTxExecution , cancelCb ) = > {
( error , continueTxExecution , cancelCb ) = > { if ( error ) { reject ( error ) } else { continueTxExecution ( ) } } ,
continueTxExecution ( )
( okCb , cancelCb ) = > { okCb ( ) } ,
} ,
( error , continueTxExecution , cancelCb ) = > {
if ( error ) {
reject ( error )
} else {
continueTxExecution ( )
}
} ,
( okCb , cancelCb ) = > {
okCb ( )
} ,
async ( error , result ) = > {
async ( error , result ) = > {
if ( error ) return reject ( error )
if ( error ) return reject ( error )
try {
try {
if ( this . executionContext . isVM ( ) ) {
if ( this . executionContext . isVM ( ) ) {
const execResult = await this . web3 ( ) . testPlugin . getExecutionResultFromSimulator ( result . transactionHash )
const execResult = await this . web3 ( ) . testPlugin . getExecutionResultFromSimulator ( result . transactionHash )
resolve ( resultToRemixTx ( result , execResult ) )
resolve ( resultToRemixTx ( result , execResult ) )
} else
} else resolve ( resultToRemixTx ( result ) )
resolve ( resultToRemixTx ( result ) )
} catch ( e ) {
} catch ( e ) {
reject ( e )
reject ( e )
}
}
@ -679,7 +770,7 @@ export class Blockchain extends Plugin {
} )
} )
}
}
async runTx ( args , confirmationCb , continueCb , promptCb , cb ) {
async runTx ( args , confirmationCb , continueCb , promptCb , cb ) {
const getGasLimit = ( ) = > {
const getGasLimit = ( ) = > {
return new Promise ( ( resolve , reject ) = > {
return new Promise ( ( resolve , reject ) = > {
if ( this . transactionContextAPI . getGasLimit ) {
if ( this . transactionContextAPI . getGasLimit ) {
@ -713,7 +804,10 @@ export class Blockchain extends Plugin {
if ( this . transactionContextAPI . getAddress ) {
if ( this . transactionContextAPI . getAddress ) {
return this . transactionContextAPI . getAddress ( function ( err , address ) {
return this . transactionContextAPI . getAddress ( function ( err , address ) {
if ( err ) return reject ( err )
if ( err ) return reject ( err )
if ( ! address ) return reject ( '"from" is not defined. Please make sure an account is selected. If you are using a public node, it is likely that no account will be provided. In that case, add the public node to your injected provider (type Metamask) and use injected provider in Remix.' )
if ( ! address )
return reject (
'"from" is not defined. Please make sure an account is selected. If you are using a public node, it is likely that no account will be provided. In that case, add the public node to your injected provider (type Metamask) and use injected provider in Remix.'
)
return resolve ( address )
return resolve ( address )
} )
} )
}
}
@ -744,46 +838,67 @@ export class Blockchain extends Plugin {
return
return
}
}
const tx = { to : args.to , data : args.data.dataHex , useCall : args.useCall , from : fromAddress , value : value , gasLimit : gasLimit , timestamp : args.data.timestamp }
const tx = {
const payLoad = { funAbi : args.data.funAbi , funArgs : args.data.funArgs , contractBytecode : args.data.contractBytecode , contractName : args.data.contractName , contractABI : args.data.contractABI , linkReferences : args.data.linkReferences }
to : args.to ,
data : args.data.dataHex ,
useCall : args.useCall ,
from : fromAddress ,
value : value ,
gasLimit : gasLimit ,
timestamp : args.data.timestamp
}
const payLoad = {
funAbi : args.data.funAbi ,
funArgs : args.data.funArgs ,
contractBytecode : args.data.contractBytecode ,
contractName : args.data.contractName ,
contractABI : args.data.contractABI ,
linkReferences : args.data.linkReferences
}
if ( ! tx . timestamp ) tx . timestamp = Date . now ( )
if ( ! tx . timestamp ) tx . timestamp = Date . now ( )
const timestamp = tx . timestamp
const timestamp = tx . timestamp
this . _triggerEvent ( 'initiatingTransaction' , [ timestamp , tx , payLoad ] )
this . _triggerEvent ( 'initiatingTransaction' , [ timestamp , tx , payLoad ] )
try {
try {
this . txRunner . rawRun ( tx , confirmationCb , continueCb , promptCb ,
this . txRunner . rawRun ( tx , confirmationCb , continueCb , promptCb , async ( error , result ) = > {
async ( error , result ) = > {
if ( error ) {
if ( error ) {
if ( typeof error !== 'string' ) {
if ( typeof ( error ) !== 'string' ) {
if ( error . message ) error = error . message
if ( error . message ) error = error . message
else {
else {
try {
try { error = 'error: ' + JSON . stringify ( error ) } catch ( e ) { console . log ( e ) }
error = 'error: ' + JSON . stringify ( error )
} catch ( e ) {
console . log ( e )
}
}
}
}
return reject ( error )
}
}
return reject ( error )
}
const isVM = this . executionContext . isVM ( )
const isVM = this . executionContext . isVM ( )
if ( isVM && tx . useCall ) {
if ( isVM && tx . useCall ) {
try {
try {
result . transactionHash = await this . web3 ( ) . testPlugin . getHashFromTagBySimulator ( timestamp )
result . transactionHash = await this . web3 ( ) . eth . getHashFromTagBySimulator ( timestamp )
} catch ( e ) {
} catch ( e ) {
console . log ( 'unable to retrieve back the "call" hash' , e )
console . log ( 'unable to retrieve back the "call" hash' , e )
}
}
}
const eventName = ( tx . useCall ? 'callExecuted' : 'transactionExecuted' )
this . _triggerEvent ( eventName , [ error , tx . from , tx . to , tx . data , tx . useCall , result , timestamp , payLoad ] )
return resolve ( { result , tx } )
}
}
)
const eventName = tx . useCall ? 'callExecuted' : 'transactionExecuted'
this . _triggerEvent ( eventName , [ error , tx . from , tx . to , tx . data , tx . useCall , result , timestamp , payLoad ] )
return resolve ( { result , tx } )
} )
} catch ( err ) {
} catch ( err ) {
let error = err
let error = err
if ( error && ( typeof ( error ) !== 'string' ) ) {
if ( error && typeof error !== 'string' ) {
if ( error . message ) error = error . message
if ( error . message ) error = error . message
else {
else {
try { error = 'error: ' + JSON . stringify ( error ) } catch ( e ) { console . log ( e ) }
try {
error = 'error: ' + JSON . stringify ( error )
} catch ( e ) {
console . log ( e )
}
}
}
}
}
return reject ( error )
return reject ( error )
@ -808,9 +923,12 @@ export class Blockchain extends Plugin {
const hhlogs = await this . web3 ( ) . testPlugin . getHHLogsForTx ( txResult . transactionHash )
const hhlogs = await this . web3 ( ) . testPlugin . getHHLogsForTx ( txResult . transactionHash )
if ( hhlogs && hhlogs . length ) {
if ( hhlogs && hhlogs . length ) {
const finalLogs = < div > < div > < b > console . log : < / b > < / div >
const finalLogs = (
{
< div >
hhlogs . map ( ( log ) = > {
< div >
< b > console . log : < / b >
< / div >
{ hhlogs . map ( ( log ) = > {
let formattedLog
let formattedLog
// Hardhat implements the same formatting options that can be found in Node.js' console.log,
// Hardhat implements the same formatting options that can be found in Node.js' console.log,
// which in turn uses util.format: https://nodejs.org/dist/latest-v12.x/docs/api/util.html#util_util_format_format_args
// which in turn uses util.format: https://nodejs.org/dist/latest-v12.x/docs/api/util.html#util_util_format_format_args
@ -823,14 +941,17 @@ export class Blockchain extends Plugin {
}
}
return < div > { formattedLog } < / div >
return < div > { formattedLog } < / div >
} ) }
} ) }
< / div >
< / div >
)
_paq . push ( [ 'trackEvent' , 'udapp' , 'hardhat' , 'console.log' ] )
_paq . push ( [ 'trackEvent' , 'udapp' , 'hardhat' , 'console.log' ] )
this . call ( 'terminal' , 'logHtml' , finalLogs )
this . call ( 'terminal' , 'logHtml' , finalLogs )
}
}
execResult = await this . web3 ( ) . testPlugin . getExecutionResultFromSimulator ( txResult . transactionHash )
execResult = await this . web3 ( ) . testPlugin . getExecutionResultFromSimulator ( txResult . transactionHash )
if ( execResult ) {
if ( execResult ) {
// if it's not the VM, we don't have return value. We only have the transaction, and it does not contain the return value.
// if it's not the VM, we don't have return value. We only have the transaction, and it does not contain the return value.
returnValue = execResult ? toBuffer ( execResult . returnValue ) : toBuffer ( addHexPrefix ( txResult . result ) || '0x0000000000000000000000000000000000000000000000000000000000000000' )
returnValue = execResult
? toBuffer ( execResult . returnValue )
: toBuffer ( addHexPrefix ( txResult . result ) || '0x0000000000000000000000000000000000000000000000000000000000000000' )
const compiledContracts = await this . call ( 'compilerArtefacts' , 'getAllContractDatas' )
const compiledContracts = await this . call ( 'compilerArtefacts' , 'getAllContractDatas' )
const vmError = txExecution . checkVMError ( execResult , compiledContracts )
const vmError = txExecution . checkVMError ( execResult , compiledContracts )
if ( vmError . error ) {
if ( vmError . error ) {