Skip to content

Commit

Permalink
Merge pull request #225 from m-Peter/use-evm-dry-run-method
Browse files Browse the repository at this point in the history
Use `EVM.dryRun` method
  • Loading branch information
sideninja authored Apr 30, 2024
2 parents 4997c78 + 9601246 commit 2e47e88
Show file tree
Hide file tree
Showing 8 changed files with 105 additions and 120 deletions.
24 changes: 18 additions & 6 deletions api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -398,13 +398,19 @@ func (b *BlockChainAPI) Call(
return handleError[hexutil.Bytes](b.logger, err)
}

txData, err := signTxFromArgs(args)
tx, err := encodeTxFromArgs(args)
if err != nil {
b.logger.Error().Err(err).Msg("failed to sign transaction for call")
b.logger.Error().Err(err).Msg("failed to encode transaction for call")
return handleError[hexutil.Bytes](b.logger, errs.ErrInternal)
}

res, err := b.evm.Call(ctx, txData, cadenceHeight)
// Default address in case user does not provide one
from := b.config.Coinbase
if args.From != nil {
from = *args.From
}

res, err := b.evm.Call(ctx, tx, from, cadenceHeight)
if err != nil {
b.logger.Error().Err(err).Msg("failed to execute call")
return handleError[hexutil.Bytes](b.logger, errs.ErrInternal)
Expand Down Expand Up @@ -492,13 +498,19 @@ func (b *BlockChainAPI) EstimateGas(
blockNumberOrHash *rpc.BlockNumberOrHash,
overrides *StateOverride,
) (hexutil.Uint64, error) {
txData, err := signTxFromArgs(args)
tx, err := encodeTxFromArgs(args)
if err != nil {
b.logger.Error().Err(err).Msg("failed to sign transaction for gas estimate")
b.logger.Error().Err(err).Msg("failed to encode transaction for gas estimate")
return hexutil.Uint64(defaultGasLimit), nil // return default gas limit
}

estimatedGas, err := b.evm.EstimateGas(ctx, txData)
// Default address in case user does not provide one
from := b.config.Coinbase
if args.From != nil {
from = *args.From
}

estimatedGas, err := b.evm.EstimateGas(ctx, tx, from)
if err != nil {
b.logger.Error().Err(err).Msg("failed to estimate gas")
return hexutil.Uint64(0), err
Expand Down
48 changes: 48 additions & 0 deletions api/encode_transaction.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package api

import (
"math/big"

"github.com/onflow/go-ethereum/core/types"
)

const defaultGasLimit uint64 = 15_000_000

// encodeTxFromArgs will create a transaction from the given arguments.
// The resulting unsigned transaction is only supposed to be used through
// `EVM.dryRun` inside Cadence scripts, meaning that no state change
// will occur.
// This is only useful for `eth_estimateGas` and `eth_call` endpoints.
func encodeTxFromArgs(args TransactionArgs) ([]byte, error) {
var data []byte
if args.Data != nil {
data = *args.Data
} else if args.Input != nil {
data = *args.Input
}

// provide a high enough gas for the tx to be able to execute,
// capped by the gas set in transaction args.
gasLimit := defaultGasLimit
if args.Gas != nil {
gasLimit = uint64(*args.Gas)
}

value := big.NewInt(0)
if args.Value != nil {
value = args.Value.ToInt()
}

tx := types.NewTx(
&types.LegacyTx{
Nonce: 0,
To: args.To,
Value: value,
Gas: gasLimit,
GasPrice: big.NewInt(0),
Data: data,
},
)

return tx.MarshalBinary()
}
58 changes: 0 additions & 58 deletions api/sign_transaction.go

This file was deleted.

12 changes: 0 additions & 12 deletions services/requester/cadence/call.cdc

This file was deleted.

9 changes: 9 additions & 0 deletions services/requester/cadence/dry_run.cdc
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import EVM

access(all)
fun main(hexEncodedTx: String, hexEncodedAddress: String): EVM.Result {
let addressBytes = hexEncodedAddress.decodeHex().toConstantSized<[UInt8; 20]>()!
let address = EVM.EVMAddress(bytes: addressBytes)

return EVM.dryRun(tx: hexEncodedTx.decodeHex(), from: address)
}
12 changes: 0 additions & 12 deletions services/requester/cadence/estimate_gas.cdc

This file was deleted.

36 changes: 24 additions & 12 deletions services/requester/requester.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ import (
)

var (
//go:embed cadence/call.cdc
callScript []byte
//go:embed cadence/dry_run.cdc
dryRunScript []byte

//go:embed cadence/run.cdc
runTxScript []byte
Expand All @@ -39,9 +39,6 @@ var (
//go:embed cadence/create_coa.cdc
createCOAScript []byte

//go:embed cadence/estimate_gas.cdc
estimateGasScript []byte

//go:embed cadence/get_nonce.cdc
getNonceScript []byte

Expand Down Expand Up @@ -69,12 +66,12 @@ type Requester interface {
// Call executes the given signed transaction data on the state for the given block number.
// Note, this function doesn't make and changes in the state/blockchain and is
// useful to execute and retrieve values.
Call(ctx context.Context, data []byte, height uint64) ([]byte, error)
Call(ctx context.Context, data []byte, from common.Address, height uint64) ([]byte, error)

// EstimateGas executes the given signed transaction data on the state.
// Note, this function doesn't make any changes in the state/blockchain and is
// useful to executed and retrieve the gas consumption and possible failures.
EstimateGas(ctx context.Context, data []byte) (uint64, error)
EstimateGas(ctx context.Context, data []byte, from common.Address) (uint64, error)

// GetNonce gets nonce from the network at the given block height.
GetNonce(ctx context.Context, address common.Address, height uint64) (uint64, error)
Expand Down Expand Up @@ -322,22 +319,28 @@ func (e *EVM) GetNonce(
func (e *EVM) Call(
ctx context.Context,
data []byte,
from common.Address,
height uint64,
) ([]byte, error) {
hexEncodedTx, err := cadence.NewString(hex.EncodeToString(data))
if err != nil {
return nil, err
}

hexEncodedAddress, err := addressToCadenceString(from)
if err != nil {
return nil, err
}

e.logger.Debug().
Str("data", fmt.Sprintf("%x", data)).
Msg("call")

scriptResult, err := e.executeScriptAtHeight(
ctx,
callScript,
dryRunScript,
height,
[]cadence.Value{hexEncodedTx},
[]cadence.Value{hexEncodedTx, hexEncodedAddress},
)
if err != nil {
return nil, fmt.Errorf("failed to execute script: %w", err)
Expand All @@ -361,7 +364,11 @@ func (e *EVM) Call(
return result, nil
}

func (e *EVM) EstimateGas(ctx context.Context, data []byte) (uint64, error) {
func (e *EVM) EstimateGas(
ctx context.Context,
data []byte,
from common.Address,
) (uint64, error) {
e.logger.Debug().
Str("data", fmt.Sprintf("%x", data)).
Msg("estimate gas")
Expand All @@ -371,10 +378,15 @@ func (e *EVM) EstimateGas(ctx context.Context, data []byte) (uint64, error) {
return 0, err
}

hexEncodedAddress, err := addressToCadenceString(from)
if err != nil {
return 0, err
}

scriptResult, err := e.client.ExecuteScriptAtLatestBlock(
ctx,
e.replaceAddresses(estimateGasScript),
[]cadence.Value{hexEncodedTx},
e.replaceAddresses(dryRunScript),
[]cadence.Value{hexEncodedTx, hexEncodedAddress},
)
if err != nil {
return 0, fmt.Errorf("failed to execute script: %w", err)
Expand Down
26 changes: 6 additions & 20 deletions tests/web3js/eth_transfer_between_eoa_accounts_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,6 @@ const helpers = require('./helpers')
const web3 = conf.web3

it('transfer flow between two EOA accounts', async () => {
// TODO: Temporary workaround until `EVM.dryRun` is introduced
let transferValue = utils.toWei("0.5", "ether")
const signedTx = await conf.eoa.signTransaction({
from: conf.eoa.address,
to: '0xa94f5374Fce5edBC8E2a8697C15331677e6EbF0B',
value: transferValue,
gasPrice: '0',
gasLimit: 55000,
})
let response = await helpers.callRPCMethod('eth_sendRawTransaction', [signedTx.rawTransaction])
assert.equal(200, response.status)
assert.isDefined(response.body['result'])

let receiver = web3.eth.accounts.create()

// make sure receiver balance is initially 0
Expand All @@ -26,13 +13,12 @@ it('transfer flow between two EOA accounts', async () => {

// get sender balance
let senderBalance = await web3.eth.getBalance(conf.eoa.address)
// we moved some value before, for `eth_call` to work properly with the
// test EOA.
assert.equal(senderBalance, utils.toWei(conf.fundedAmount, "ether") - transferValue)
assert.equal(senderBalance, utils.toWei(conf.fundedAmount, "ether"))

let txCount = await web3.eth.getTransactionCount(conf.eoa.address)
assert.equal(1n, txCount)
assert.equal(0n, txCount)

let transferValue = utils.toWei("0.5", "ether")
let transfer = await helpers.signAndSend({
from: conf.eoa.address,
to: receiver.address,
Expand All @@ -46,14 +32,14 @@ it('transfer flow between two EOA accounts', async () => {

// check that transaction count was increased
txCount = await web3.eth.getTransactionCount(conf.eoa.address)
assert.equal(2n, txCount)
assert.equal(1n, txCount)

// check balance was moved
receiverWei = await web3.eth.getBalance(receiver.address)
assert.equal(receiverWei, transferValue)

senderBalance = await web3.eth.getBalance(conf.eoa.address)
assert.equal(senderBalance, utils.toWei(conf.fundedAmount, "ether") - (2*transferValue))
assert.equal(senderBalance, utils.toWei(conf.fundedAmount, "ether") - transferValue)

// make sure latest block includes the transfer tx
let latest = await web3.eth.getBlockNumber()
Expand All @@ -63,7 +49,7 @@ it('transfer flow between two EOA accounts', async () => {

// check that getTransactionCount can handle specific block heights
txCount = await web3.eth.getTransactionCount(conf.eoa.address, latest - 1n)
assert.equal(1n, txCount)
assert.equal(0n, txCount)

// get balance at special block tags
let blockTags = ["latest", "pending", "safe", "finalized"]
Expand Down

0 comments on commit 2e47e88

Please sign in to comment.