Skip to content

Commit

Permalink
Merge pull request #3434 from dessaya/emit-transfer-events-send2
Browse files Browse the repository at this point in the history
evm: emit events on same tx if possible
  • Loading branch information
jorgemmsilva authored Jun 6, 2024
2 parents 23a7a67 + 8d039e6 commit 3f7deb7
Show file tree
Hide file tree
Showing 7 changed files with 60 additions and 106 deletions.
74 changes: 27 additions & 47 deletions packages/vm/core/evm/evmimpl/impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,6 @@ var Processor = evm.Contract.Processor(nil,
evm.FuncRegisterERC721NFTCollection.WithHandler(restricted(registerERC721NFTCollection)),

evm.FuncNewL1Deposit.WithHandler(newL1Deposit),
evm.FuncNewL1Withdrawal.WithHandler(newL1Withdrawal),
evm.FuncNewTransferBetweenL2Accounts.WithHandler(newTransferBetweenL2Accounts),

// views
evm.FuncGetERC20ExternalNativeTokenAddress.WithHandler(viewERC20ExternalNativeTokenAddress),
Expand Down Expand Up @@ -447,33 +445,7 @@ func newL1Deposit(ctx isc.Sandbox) dict.Dict {
assets, err := isc.AssetsFromBytes(params.MustGetBytes(evm.FieldAssets))
ctx.RequireNoError(err, "unable to parse assets from params")
txData := l1DepositOriginatorBytes
addDummyTxWithTransferEvents(ctx, fromAddress, toAddress, assets, txData)
return nil
}

func newL1Withdrawal(ctx isc.Sandbox) dict.Dict {
// can only be called from the accounts contract
ctx.RequireCaller(isc.NewContractAgentID(ctx.ChainID(), accounts.Contract.Hname()))
params := ctx.Params()
targetL1AddressBytes := params.MustGetBytes(evm.FieldAgentIDWithdrawalTarget)
fromAddress := common.BytesToAddress(params.MustGetBytes(evm.FieldAddress))
toAddress := common.Address{}
assets, err := isc.AssetsFromBytes(params.MustGetBytes(evm.FieldAssets))
ctx.RequireNoError(err, "unable to parse assets from params")
txData := targetL1AddressBytes
addDummyTxWithTransferEvents(ctx, fromAddress, toAddress, assets, txData)
return nil
}

func newTransferBetweenL2Accounts(ctx isc.Sandbox) dict.Dict {
// can only be called from the accounts contract
ctx.RequireCaller(isc.NewContractAgentID(ctx.ChainID(), accounts.Contract.Hname()))
params := ctx.Params()
fromAddress := common.BytesToAddress(params.MustGetBytes(evm.FieldFromAddress))
toAddress := common.BytesToAddress(params.MustGetBytes(evm.FieldToAddress))
assets, err := isc.AssetsFromBytes(params.MustGetBytes(evm.FieldAssets))
ctx.RequireNoError(err, "unable to parse assets from params")
txData := fromAddress.Bytes()
// create a fake tx so that the operation is visible by the EVM
addDummyTxWithTransferEvents(ctx, fromAddress, toAddress, assets, txData)
return nil
}
Expand All @@ -484,8 +456,13 @@ func addDummyTxWithTransferEvents(
assets *isc.Assets,
txData []byte,
) {
// create a fake tx so that the operation is visible by the EVM
logs := makeTransferEvents(ctx, fromAddress, toAddress, assets)

wei := util.BaseTokensDecimalsToEthereumDecimals(assets.BaseTokens, newEmulatorContext(ctx).BaseTokensDecimals())
if wei.Sign() == 0 && len(logs) == 0 {
return
}

nonce := uint64(0)
chainInfo := ctx.ChainInfo()
gasPrice := chainInfo.GasFeePolicy.DefaultGasPriceFullDecimals(parameters.L1().BaseToken.Decimals)
Expand All @@ -507,6 +484,25 @@ func addDummyTxWithTransferEvents(
},
)

receipt := &types.Receipt{
Type: types.LegacyTxType,
Logs: logs,
Status: types.ReceiptStatusSuccessful,
}
receipt.Bloom = types.CreateBloom(types.Receipts{receipt})

ctx.Privileged().OnWriteReceipt(func(evmPartition kv.KVStore, gasBurned uint64) {
receipt.GasUsed = gas.ISCGasBurnedToEVM(gasBurned, &chainInfo.GasFeePolicy.EVMGasRatio)
receipt.CumulativeGasUsed = createBlockchainDB(evmPartition, chainInfo).GetPendingCumulativeGasUsed() + receipt.GasUsed
createBlockchainDB(evmPartition, ctx.ChainInfo()).AddTransaction(tx, receipt)
})
}

func makeTransferEvents(
ctx isc.Sandbox,
fromAddress, toAddress common.Address,
assets *isc.Assets,
) []*types.Log {
logs := make([]*types.Log, 0)
for _, nt := range assets.NativeTokens {
if nt.Amount.Sign() == 0 {
Expand Down Expand Up @@ -535,23 +531,7 @@ func addDummyTxWithTransferEvents(
// otherwise, emit a Transfer event from the ERC721NFTs contract
logs = append(logs, makeTransferEventERC721(iscmagic.ERC721NFTsAddress, fromAddress, toAddress, iscmagic.WrapNFTID(nftID).TokenID()))
}

if wei.Sign() == 0 && len(logs) == 0 {
return
}

receipt := &types.Receipt{
Type: types.LegacyTxType,
Logs: logs,
Status: types.ReceiptStatusSuccessful,
}
receipt.Bloom = types.CreateBloom(types.Receipts{receipt})

ctx.Privileged().OnWriteReceipt(func(evmPartition kv.KVStore, gasBurned uint64) {
receipt.GasUsed = gas.ISCGasBurnedToEVM(gasBurned, &chainInfo.GasFeePolicy.EVMGasRatio)
receipt.CumulativeGasUsed = createBlockchainDB(evmPartition, chainInfo).GetPendingCumulativeGasUsed() + receipt.GasUsed
createBlockchainDB(evmPartition, ctx.ChainInfo()).AddTransaction(tx, receipt)
})
return logs
}

var transferEventTopic = crypto.Keccak256Hash([]byte("Transfer(address,address,uint256)"))
Expand Down
2 changes: 1 addition & 1 deletion packages/vm/core/evm/evmimpl/iscmagic.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ func (c *magicContract) Run(evm *vm.EVM, caller vm.ContractRef, input []byte, va
return nil, gas, ErrPayingUnpayableMethod.Create(method.Name)
}

ret = callHandler(c.ctx, caller, value, method, args)
ret = callHandler(c.ctx, evm, caller, value, method, args)
return ret, gas, nil
}

Expand Down
4 changes: 3 additions & 1 deletion packages/vm/core/evm/evmimpl/iscmagic_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,17 @@ import (
// correspond to a call to [GetL2NFTs].
type magicContractHandler struct {
ctx isc.Sandbox
evm *vm.EVM
caller vm.ContractRef
callValue *uint256.Int
}

// callHandler finds the requested ISC magic method by reflection, and executes
// it.
func callHandler(ctx isc.Sandbox, caller vm.ContractRef, callValue *uint256.Int, method *abi.Method, args []any) []byte {
func callHandler(ctx isc.Sandbox, evm *vm.EVM, caller vm.ContractRef, callValue *uint256.Int, method *abi.Method, args []any) []byte {
return reflectCall(&magicContractHandler{
ctx: ctx,
evm: evm,
caller: caller,
callValue: callValue,
}, method, args)
Expand Down
29 changes: 6 additions & 23 deletions packages/vm/core/evm/evmimpl/iscmagic_sandbox.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import (
"github.com/iotaledger/wasp/packages/kv/dict"
"github.com/iotaledger/wasp/packages/parameters"
"github.com/iotaledger/wasp/packages/util"
"github.com/iotaledger/wasp/packages/vm/core/accounts"
"github.com/iotaledger/wasp/packages/vm/core/errors/coreerrors"
"github.com/iotaledger/wasp/packages/vm/core/evm"
"github.com/iotaledger/wasp/packages/vm/core/evm/iscmagic"
Expand Down Expand Up @@ -55,17 +54,9 @@ func (h *magicContractHandler) TakeAllowedFunds(addr common.Address, allowance i
assets,
)
// emit ERC20 / ERC721 events for native tokens & NFTs
h.ctx.Privileged().CallOnBehalfOf(
isc.NewContractAgentID(h.ctx.ChainID(), accounts.Contract.Hname()),
evm.Contract.Hname(),
evm.FuncNewTransferBetweenL2Accounts.Hname(),
dict.Dict{
evm.FieldFromAddress: addr.Bytes(),
evm.FieldToAddress: h.caller.Address().Bytes(),
evm.FieldAssets: assets.Bytes(),
},
nil,
)
for _, log := range makeTransferEvents(h.ctx, addr, h.caller.Address(), assets) {
h.evm.StateDB.AddLog(log)
}
}

var errInvalidAllowance = coreerrors.Register("allowance must not be greater than sent tokens").Create()
Expand Down Expand Up @@ -118,17 +109,9 @@ func (h *magicContractHandler) Send(
h.moveAssetsToCommonAccount(req.Assets)

// emit ERC20 / ERC721 events for native tokens & NFTs
h.ctx.Privileged().CallOnBehalfOf(
isc.NewContractAgentID(h.ctx.ChainID(), accounts.Contract.Hname()),
evm.Contract.Hname(),
evm.FuncNewL1Withdrawal.Hname(),
dict.Dict{
evm.FieldAgentIDWithdrawalTarget: isc.AddressToBytes(req.TargetAddress),
evm.FieldAddress: h.caller.Address().Bytes(),
evm.FieldAssets: req.Assets.Bytes(),
},
nil,
)
for _, log := range makeTransferEvents(h.ctx, h.caller.Address(), common.Address{}, req.Assets) {
h.evm.StateDB.AddLog(log)
}
h.ctx.Privileged().SendOnBehalfOf(
isc.ContractIdentityFromEVMAddress(h.caller.Address()),
req,
Expand Down
4 changes: 1 addition & 3 deletions packages/vm/core/evm/evmnames/evmnames.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,7 @@ const (
FuncGetERC20ExternalNativeTokenAddress = "getERC20ExternalNativeTokenAddress"
FuncRegisterERC721NFTCollection = "registerERC721NFTCollection"

FuncNewL1Deposit = "newL1Deposit"
FuncNewL1Withdrawal = "newL1Withdrawal"
FuncNewTransferBetweenL2Accounts = "newTransferBetweenL2Accounts"
FuncNewL1Deposit = "newL1Deposit"

FieldTransaction = "t"
FieldCallMsg = "c"
Expand Down
49 changes: 21 additions & 28 deletions packages/vm/core/evm/evmtest/evm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -904,33 +904,26 @@ func TestSendNFT(t *testing.T) {
// 2. Transfer NFT ISCTest -> 0x0 (send to L1)
{
blockTxs := lo.Must(env.evmChain.BlockByNumber(nil)).Transactions()
require.Len(t, blockTxs, 3)
{
tx1 := blockTxs[0]
receipt1 := env.evmChain.TransactionReceipt(tx1.Hash())
require.Len(t, receipt1.Logs, 1)
checkTransferEventERC721(
t,
receipt1.Logs[0],
iscmagic.ERC721NFTsAddress,
ethAddr,
iscTest.address,
iscmagic.WrapNFTID(nft.ID).TokenID(),
)
}
{
tx2 := blockTxs[1]
receipt2 := env.evmChain.TransactionReceipt(tx2.Hash())
require.Len(t, receipt2.Logs, 1)
checkTransferEventERC721(
t,
receipt2.Logs[0],
iscmagic.ERC721NFTsAddress,
iscTest.address,
common.Address{},
iscmagic.WrapNFTID(nft.ID).TokenID(),
)
}
require.Len(t, blockTxs, 1)
tx := blockTxs[0]
receipt := env.evmChain.TransactionReceipt(tx.Hash())
require.Len(t, receipt.Logs, 2)
checkTransferEventERC721(
t,
receipt.Logs[0],
iscmagic.ERC721NFTsAddress,
ethAddr,
iscTest.address,
iscmagic.WrapNFTID(nft.ID).TokenID(),
)
checkTransferEventERC721(
t,
receipt.Logs[1],
iscmagic.ERC721NFTsAddress,
iscTest.address,
common.Address{},
iscmagic.WrapNFTID(nft.ID).TokenID(),
)
}
}

Expand Down Expand Up @@ -1676,7 +1669,7 @@ func TestERC20NativeTokensWithExternalFoundry(t *testing.T) {
// there must be a Transfer event emitted from the foundry chain's ERC20NativeTokens contract
{
blockTxs := lo.Must(foundryChain.EVM().BlockByNumber(nil)).Transactions()
require.Len(t, blockTxs, 2)
require.Len(t, blockTxs, 1)
tx := blockTxs[0]
receipt := foundryChain.EVM().TransactionReceipt(tx.Hash())
require.Len(t, receipt.Logs, 1)
Expand Down
4 changes: 1 addition & 3 deletions packages/vm/core/evm/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,7 @@ var (
FuncGetERC20ExternalNativeTokenAddress = coreutil.ViewFunc(evmnames.FuncGetERC20ExternalNativeTokenAddress)
FuncRegisterERC721NFTCollection = coreutil.Func(evmnames.FuncRegisterERC721NFTCollection)

FuncNewL1Deposit = coreutil.Func(evmnames.FuncNewL1Deposit)
FuncNewL1Withdrawal = coreutil.Func(evmnames.FuncNewL1Withdrawal)
FuncNewTransferBetweenL2Accounts = coreutil.Func(evmnames.FuncNewTransferBetweenL2Accounts)
FuncNewL1Deposit = coreutil.Func(evmnames.FuncNewL1Deposit)
)

const (
Expand Down

0 comments on commit 3f7deb7

Please sign in to comment.