Skip to content

Commit

Permalink
feat(evm): typed events for contract creation, contract execution and…
Browse files Browse the repository at this point in the history
… transfer (#1971)

* refactor(evm): funtoken events, cli commands and queries

* chore: changelog update

* feat(evm): typed events for contract creation, contract execution and transfer

* chore: changelog update
  • Loading branch information
onikonychev authored Jul 24, 2024
1 parent afc7c0f commit 197f652
Show file tree
Hide file tree
Showing 10 changed files with 1,069 additions and 136 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- [#1963](https://github.com/NibiruChain/nibiru/pull/1963) - feat(evm): Deduct a fee during the creation of a FunToken mapping. Implemented by `deductCreateFunTokenFee` inside of the `eth.evm.v1.MsgCreateFunToken` transaction.
- [#1965](https://github.com/NibiruChain/nibiru/pull/1965) - refactor(evm): remove evm post-processing hooks
- [#1968](https://github.com/NibiruChain/nibiru/pull/1968) - refactor(evm): funtoken events, cli commands and queries
- [#1971](https://github.com/NibiruChain/nibiru/pull/1971) - feat(evm): typed events for contract creation, contract execution and transfer

#### Dapp modules: perp, spot, oracle, etc

Expand Down
19 changes: 19 additions & 0 deletions proto/eth/evm/v1/events.proto
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,22 @@ message EventSendFunTokenToEvm {
(gogoproto.nullable) = false
];
}

// EventTransfer defines event for EVM transfer
message EventTransfer {
string sender = 1;
string recipient = 2;
string amount = 3;
}

// EventContractDeployed defines event for EVM contract deployment
message EventContractDeployed {
string sender = 1;
string contract_addr = 2;
}

// EventContractExecuted defines event for EVM contract execution
message EventContractExecuted {
string sender = 1;
string contract_addr = 2;
}
798 changes: 760 additions & 38 deletions x/evm/events.pb.go

Large diffs are not rendered by default.

47 changes: 46 additions & 1 deletion x/evm/evmtest/smart_contract.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@ package evmtest
import (
"math/big"

gethcommon "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/params"
gethparams "github.com/ethereum/go-ethereum/params"

"github.com/NibiruChain/nibiru/x/evm/embeds"

gethcore "github.com/ethereum/go-ethereum/core/types"

"github.com/NibiruChain/nibiru/x/evm"
Expand All @@ -26,18 +29,34 @@ type ArgsCreateContract struct {
GasLimit *big.Int
}

// ArgsExecuteContract: Arguments to call with `ExecuteContractTxMsg`
// to make Ethereum transactions that execute contracts.
type ArgsExecuteContract struct {
EthAcc EthPrivKeyAcc
EthChainIDInt *big.Int
ContractAddress *gethcommon.Address
Data []byte
GasPrice *big.Int
Nonce uint64
GasLimit *big.Int
}

func CreateContractTxMsg(
args ArgsCreateContract,
) (ethTxMsg *evm.MsgEthereumTx, err error) {
gasLimit := args.GasLimit
if gasLimit == nil {
gasLimit = new(big.Int).SetUint64(gethparams.TxGasContractCreation)
}
testContract, err := embeds.SmartContract_TestERC20.Load()
if err != nil {
return nil, err
}
gethTxCreateCntract := &gethcore.AccessListTx{
GasPrice: args.GasPrice,
Gas: gasLimit.Uint64(),
To: nil,
Data: []byte("contract_data"),
Data: testContract.Bytecode,
Nonce: args.Nonce,
}
ethTx := gethcore.NewTx(gethTxCreateCntract)
Expand Down Expand Up @@ -67,3 +86,29 @@ func CreateContractGethCoreMsg(
signer := gethcore.MakeSigner(cfg, blockHeight)
return ethTxMsg.AsMessage(signer, nil)
}

func ExecuteContractTxMsg(args ArgsExecuteContract) (ethTxMsg *evm.MsgEthereumTx, err error) {
gasLimit := args.GasLimit
if gasLimit == nil {
gasLimit = new(big.Int).SetUint64(gethparams.TxGas)
}
gethTxExecuteContract := &gethcore.AccessListTx{
GasPrice: args.GasPrice,
Gas: gasLimit.Uint64(),
To: args.ContractAddress,
Data: args.Data,
Nonce: args.Nonce,
}
ethTx := gethcore.NewTx(gethTxExecuteContract)
ethTxMsg = new(evm.MsgEthereumTx)
err = ethTxMsg.FromEthereumTx(ethTx)
if err != nil {
return ethTxMsg, err
}
fromAcc := args.EthAcc
ethTxMsg.From = fromAcc.EthAddr.Hex()

gethSigner := fromAcc.GethSigner(args.EthChainIDInt)
keyringSigner := fromAcc.KeyringSigner
return ethTxMsg, ethTxMsg.Sign(gethSigner, keyringSigner)
}
20 changes: 20 additions & 0 deletions x/evm/evmtest/smart_contract_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ package evmtest_test
import (
"math/big"

gethcommon "github.com/ethereum/go-ethereum/common"

"github.com/NibiruChain/nibiru/x/evm"
"github.com/NibiruChain/nibiru/x/evm/evmtest"
)
Expand Down Expand Up @@ -46,3 +48,21 @@ func (s *Suite) TestCreateContractGethCoreMsg() {
)
s.NoError(err)
}

func (s *Suite) TestExecuteContractTxMsg() {
deps := evmtest.NewTestDeps()
ethAcc := evmtest.NewEthAccInfo()
contractAddress := gethcommon.HexToAddress("0x5aaeb6053f3e94c9b9a09f33669435e7ef1beaed")
args := evmtest.ArgsExecuteContract{
EthAcc: ethAcc,
EthChainIDInt: deps.K.EthChainID(deps.Ctx),
GasPrice: big.NewInt(1),
Nonce: deps.StateDB().GetNonce(ethAcc.EthAddr),
ContractAddress: &contractAddress,
Data: nil,
}

ethTxMsg, err := evmtest.ExecuteContractTxMsg(args)
s.NoError(err)
s.Require().NoError(ethTxMsg.ValidateBasic())
}
12 changes: 12 additions & 0 deletions x/evm/evmtest/tx.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,9 @@ func NewEthTxMsgFromTxData(
txType GethTxType,
innerTxData []byte,
nonce uint64,
to *gethcommon.Address,
value *big.Int,
gas uint64,
accessList gethcore.AccessList,
) (*evm.MsgEthereumTx, error) {
if innerTxData == nil {
Expand All @@ -71,17 +74,26 @@ func NewEthTxMsgFromTxData(
innerTx := TxTemplateLegacyTx()
innerTx.Nonce = nonce
innerTx.Data = innerTxData
innerTx.To = to
innerTx.Value = value
innerTx.Gas = gas
ethCoreTx = gethcore.NewTx(innerTx)
case gethcore.AccessListTxType:
innerTx := TxTemplateAccessListTx()
innerTx.Nonce = nonce
innerTx.Data = innerTxData
innerTx.AccessList = accessList
innerTx.To = to
innerTx.Value = value
innerTx.Gas = gas
ethCoreTx = gethcore.NewTx(innerTx)
case gethcore.DynamicFeeTxType:
innerTx := TxTemplateDynamicFeeTx()
innerTx.Nonce = nonce
innerTx.Data = innerTxData
innerTx.To = to
innerTx.Value = value
innerTx.Gas = gas
innerTx.AccessList = accessList
ethCoreTx = gethcore.NewTx(innerTx)
default:
Expand Down
74 changes: 35 additions & 39 deletions x/evm/keeper/erc20_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,25 +104,16 @@ func (s *Suite) TestCreateFunTokenFromERC20() {
})

// Event "EventFunTokenCreated" must present
var sdkEvents = deps.Ctx.EventManager().Events()
funTokenCreatedEventType := "eth.evm.v1.EventFunTokenCreated"
err = testutil.AssertEventPresent(sdkEvents, funTokenCreatedEventType)
s.Require().NoError(err)

var funTokenCreatedEvent sdk.Event
for _, abciEvent := range sdkEvents {
if abciEvent.Type == funTokenCreatedEventType {
funTokenCreatedEvent = abciEvent
}
}
for _, err = range []error{
testutil.EventHasAttributeValue(funTokenCreatedEvent, "bank_denom", expectedBankDenom),
testutil.EventHasAttributeValue(funTokenCreatedEvent, "erc20_contract_address", erc20Addr.String()),
testutil.EventHasAttributeValue(funTokenCreatedEvent, "creator", deps.Sender.NibiruAddr.String()),
testutil.EventHasAttributeValue(funTokenCreatedEvent, "is_made_from_coin", "false"),
} {
s.Require().NoError(err)
}
testutil.RequireContainsTypedEvent(
s.T(),
deps.Ctx,
&evm.EventFunTokenCreated{
BankDenom: expectedBankDenom,
Erc20ContractAddress: erc20Addr.String(),
Creator: deps.Sender.NibiruAddr.String(),
IsMadeFromCoin: false,
},
)

s.T().Log("sad: CreateFunToken for the ERC20: already registered")
// Give the sender funds for the fee
Expand Down Expand Up @@ -254,25 +245,17 @@ func (s *Suite) TestCreateFunTokenFromCoin() {
s.Equal(metadata, info)

// Event "EventFunTokenCreated" must present
var sdkEvents = deps.Ctx.EventManager().Events()
funTokenCreatedEventType := "eth.evm.v1.EventFunTokenCreated"
err = testutil.AssertEventPresent(sdkEvents, funTokenCreatedEventType)
s.Require().NoError(err)

var funTokenCreatedEvent sdk.Event
for _, abciEvent := range sdkEvents {
if abciEvent.Type == funTokenCreatedEventType {
funTokenCreatedEvent = abciEvent
}
}
for _, err = range []error{
testutil.EventHasAttributeValue(funTokenCreatedEvent, "bank_denom", bankDenom),
testutil.EventHasAttributeValue(funTokenCreatedEvent, "erc20_contract_address", erc20Addr.String()),
testutil.EventHasAttributeValue(funTokenCreatedEvent, "creator", deps.Sender.NibiruAddr.String()),
testutil.EventHasAttributeValue(funTokenCreatedEvent, "is_made_from_coin", "true"),
} {
s.Require().NoError(err)
}
// Event "EventFunTokenCreated" must present
testutil.RequireContainsTypedEvent(
s.T(),
deps.Ctx,
&evm.EventFunTokenCreated{
BankDenom: bankDenom,
Erc20ContractAddress: erc20Addr.String(),
Creator: deps.Sender.NibiruAddr.String(),
IsMadeFromCoin: true,
},
)

s.T().Log("sad: CreateFunToken for the bank coin: already registered")
// Give the sender funds for the fee
Expand Down Expand Up @@ -367,11 +350,12 @@ func (s *Suite) TestSendFunTokenToEvm() {
funTokenErc20Addr := createFunTokenResp.FuntokenMapping.Erc20Addr.ToAddr()

// Send fun token to ERC-20 contract
bankCoin := sdk.Coin{Denom: tc.bankDenom, Amount: tc.amountToSend}
_, err = deps.K.SendFunTokenToEvm(
ctx,
&evm.MsgSendFunTokenToEvm{
Sender: deps.Sender.NibiruAddr.String(),
BankCoin: sdk.Coin{Denom: tc.bankDenom, Amount: tc.amountToSend},
BankCoin: bankCoin,
ToEthAddr: recipientEVMAddr,
},
)
Expand All @@ -381,6 +365,18 @@ func (s *Suite) TestSendFunTokenToEvm() {
}
s.Require().NoError(err)

// Event "EventSendFunTokenToEvm" must present
testutil.RequireContainsTypedEvent(
s.T(),
deps.Ctx,
&evm.EventSendFunTokenToEvm{
Sender: deps.Sender.NibiruAddr.String(),
Erc20ContractAddress: funTokenErc20Addr.String(),
ToEthAddr: recipientEVMAddr.String(),
BankCoin: bankCoin,
},
)

// Check 1: coins are stored on a module balance
moduleBalance, err := deps.Chain.BankKeeper.Balance(ctx, &bank.QueryBalanceRequest{
Address: evmModuleAddr.String(),
Expand Down
2 changes: 1 addition & 1 deletion x/evm/keeper/grpc_query_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -912,7 +912,7 @@ func (s *Suite) TestTraceBlock() {
}
}

func (s *Suite) TestQueryFunToken() {
func (s *Suite) TestQueryFunTokenMapping() {
type In = *evm.QueryFunTokenMappingRequest
type Out = *evm.QueryFunTokenMappingResponse
testCases := []TestCase[In, Out]{
Expand Down
Loading

0 comments on commit 197f652

Please sign in to comment.