Skip to content

Commit 63b2be4

Browse files
committed
Merge branch 'main' into releases/v2.0.0
2 parents 82302cc + 0ef4099 commit 63b2be4

29 files changed

+2413
-52
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,11 +76,12 @@ needed to include double quotes around the hexadecimal string.
7676
- [#2156](https://github.com/NibiruChain/nibiru/pull/2156) - test(evm-e2e): add E2E test using the Nibiru Oracle's ChainLink impl
7777
- [#2157](https://github.com/NibiruChain/nibiru/pull/2157) - fix(evm): Fix unit inconsistency related to AuthInfo.Fee and txData.Fee using effective fee
7878
- [#2160](https://github.com/NibiruChain/nibiru/pull/2160) - fix(evm-precompile): use bank.MsgServer Send in precompile IFunToken.bankMsgSend
79+
- [#2161](https://github.com/NibiruChain/nibiru/pull/2161) - fix(evm): added tx logs events to the funtoken related txs
7980
- [#2162](https://github.com/NibiruChain/nibiru/pull/2162) - test(testutil): try retrying for 'panic: pebbledb: closed'
8081
- [#2167](https://github.com/NibiruChain/nibiru/pull/2167) - refactor(evm): removed blockGasUsed transient variable
8182
- [#2168](https://github.com/NibiruChain/nibiru/pull/2168) - chore(evm-solidity): Move unrelated docs, gen-embeds, and add Solidity docs
8283
- [#2165](https://github.com/NibiruChain/nibiru/pull/2165) - fix(evm): use Singleton StateDB pattern for EVM txs
83-
84+
- [#2169](https://github.com/NibiruChain/nibiru/pull/2169) - fix(evm): Better handling erc20 metadata
8485
#### Nibiru EVM | Before Audit 2 - 2024-12-06
8586

8687
The codebase went through a third-party [Code4rena

eth/rpc/backend/backend_suite_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,7 @@ func (s *BackendSuite) buildContractCallTx(
252252
nonce uint64,
253253
gasLimit uint64,
254254
) gethcore.Transaction {
255-
// recipient := crypto.CreateAddress(s.fundedAccEthAddr, 29381)
255+
//recipient := crypto.CreateAddress(s.fundedAccEthAddr, 29381)
256256
transferAmount := big.NewInt(100)
257257

258258
packedArgs, err := embeds.SmartContract_TestERC20.ABI.Pack(

eth/rpc/backend/gas_used_test.go

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -76,10 +76,10 @@ func (s *BackendSuite) TestGasUsedFunTokens() {
7676
erc20Addr, err := eth.NewEIP55AddrFromStr(testContractAddress.String())
7777
s.Require().NoError(err)
7878

79-
_, err = s.backend.GetTransactionCount(s.fundedAccEthAddr, rpc.EthPendingBlockNumber)
80-
s.Require().NoError(err)
79+
nonce := s.getCurrentNonce(s.node.EthAddress)
80+
balanceBefore := s.getUnibiBalance(s.fundedAccEthAddr)
8181

82-
txResp, err := s.network.BroadcastMsgs(s.node.Address, &evm.MsgCreateFunToken{
82+
txResp, err := s.network.BroadcastMsgs(s.node.Address, &nonce, &evm.MsgCreateFunToken{
8383
Sender: s.node.Address.String(),
8484
FromErc20: &erc20Addr,
8585
})
@@ -96,15 +96,11 @@ func (s *BackendSuite) TestGasUsedFunTokens() {
9696
)
9797
s.Require().NoError(err)
9898

99-
nonce, err := s.backend.GetTransactionCount(s.fundedAccEthAddr, rpc.EthPendingBlockNumber)
100-
s.Require().NoError(err)
101-
102-
balanceBefore := s.getUnibiBalance(s.fundedAccEthAddr)
103-
99+
nonce = s.getCurrentNonce(s.fundedAccEthAddr)
104100
txHash1 := SendTransaction(
105101
s,
106102
&gethcore.LegacyTx{
107-
Nonce: uint64(*nonce),
103+
Nonce: nonce,
108104
To: &precompile.PrecompileAddr_FunToken,
109105
Data: packedArgsPass,
110106
Gas: 1_500_000,
@@ -123,7 +119,7 @@ func (s *BackendSuite) TestGasUsedFunTokens() {
123119
txHash2 := SendTransaction( // should fail due to invalid recipient address
124120
s,
125121
&gethcore.LegacyTx{
126-
Nonce: uint64(*nonce + 1),
122+
Nonce: nonce + 1,
127123
To: &precompile.PrecompileAddr_FunToken,
128124
Data: packedArgsFail,
129125
Gas: 1_500_000,
@@ -134,7 +130,7 @@ func (s *BackendSuite) TestGasUsedFunTokens() {
134130
txHash3 := SendTransaction(
135131
s,
136132
&gethcore.LegacyTx{
137-
Nonce: uint64(*nonce + 2),
133+
Nonce: nonce + 2,
138134
To: &precompile.PrecompileAddr_FunToken,
139135
Data: packedArgsPass,
140136
Gas: 1_500_000,

eth/rpc/backend/tx_logs_test.go

Lines changed: 299 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,299 @@
1+
package backend_test
2+
3+
import (
4+
"fmt"
5+
"math/big"
6+
7+
tmrpctypes "github.com/cometbft/cometbft/rpc/core/types"
8+
sdk "github.com/cosmos/cosmos-sdk/types"
9+
gethcommon "github.com/ethereum/go-ethereum/common"
10+
gethcore "github.com/ethereum/go-ethereum/core/types"
11+
"github.com/ethereum/go-ethereum/crypto"
12+
13+
"github.com/NibiruChain/nibiru/v2/eth"
14+
"github.com/NibiruChain/nibiru/v2/eth/rpc/backend"
15+
"github.com/NibiruChain/nibiru/v2/x/common/testutil"
16+
"github.com/NibiruChain/nibiru/v2/x/evm"
17+
"github.com/NibiruChain/nibiru/v2/x/evm/embeds"
18+
"github.com/NibiruChain/nibiru/v2/x/evm/evmtest"
19+
"github.com/NibiruChain/nibiru/v2/x/evm/precompile"
20+
)
21+
22+
// TestEthLogs checks that eth txs as well as funtoken txs produce tx_logs events and update tx index properly.
23+
// To check that, we send a series of transactions:
24+
// - 1: simple eth transfer
25+
// - 2: deploying erc20 contract
26+
// - 3. creating funtoken from erc20
27+
// - 4: creating funtoken from coin
28+
// - 5. converting coin to erc20
29+
// - 6. converting erc20 born token to coin via precompile
30+
// Each tx should emit some tx logs and emit proper tx index within ethereum tx event.
31+
func (s *BackendSuite) TestLogs() {
32+
// Test is broadcasting txs. Lock to avoid nonce conflicts.
33+
testMutex.Lock()
34+
defer testMutex.Unlock()
35+
36+
// Start with fresh block
37+
s.Require().NoError(s.network.WaitForNextBlock())
38+
39+
s.T().Log("TX1: Send simple nibi transfer")
40+
randomEthAddr := evmtest.NewEthPrivAcc().EthAddr
41+
txHashFirst := s.SendNibiViaEthTransfer(randomEthAddr, amountToSend, false)
42+
43+
s.T().Log("TX2: Deploy ERC20 contract")
44+
_, erc20ContractAddr := s.DeployTestContract(false)
45+
erc20Addr, _ := eth.NewEIP55AddrFromStr(erc20ContractAddr.String())
46+
47+
s.T().Log("TX3: Create FunToken from ERC20")
48+
nonce := s.getCurrentNonce(eth.NibiruAddrToEthAddr(s.node.Address))
49+
txResp, err := s.network.BroadcastMsgs(s.node.Address, &nonce, &evm.MsgCreateFunToken{
50+
Sender: s.node.Address.String(),
51+
FromErc20: &erc20Addr,
52+
})
53+
s.Require().NoError(err)
54+
s.Require().NotNil(txResp)
55+
s.Require().Equal(
56+
uint32(0),
57+
txResp.Code,
58+
fmt.Sprintf("Failed to create FunToken from ERC20. RawLog: %s", txResp.RawLog),
59+
)
60+
61+
s.T().Log("TX4: Create FunToken from unibi coin")
62+
nonce++
63+
erc20FromCoinAddr := crypto.CreateAddress(evm.EVM_MODULE_ADDRESS, s.getCurrentNonce(evm.EVM_MODULE_ADDRESS)+1)
64+
65+
txResp, err = s.network.BroadcastMsgs(s.node.Address, &nonce, &evm.MsgCreateFunToken{
66+
Sender: s.node.Address.String(),
67+
FromBankDenom: evm.EVMBankDenom,
68+
})
69+
s.Require().NoError(err)
70+
s.Require().NotNil(txResp)
71+
s.Require().Equal(
72+
uint32(0),
73+
txResp.Code,
74+
fmt.Sprintf("Failed to create FunToken from unibi coin. RawLog: %s", txResp.RawLog),
75+
)
76+
77+
s.T().Log("TX5: Convert coin to EVM")
78+
nonce++
79+
txResp, err = s.network.BroadcastMsgs(s.node.Address, &nonce, &evm.MsgConvertCoinToEvm{
80+
Sender: s.node.Address.String(),
81+
BankCoin: sdk.NewCoin(evm.EVMBankDenom, sdk.NewInt(1)),
82+
ToEthAddr: eth.EIP55Addr{
83+
Address: s.fundedAccEthAddr,
84+
},
85+
})
86+
s.Require().NoError(err)
87+
s.Require().NotNil(txResp)
88+
s.Require().Equal(
89+
uint32(0),
90+
txResp.Code,
91+
fmt.Sprintf("Failed converting coin to evm. RawLog: %s", txResp.RawLog),
92+
)
93+
94+
s.T().Log("TX6: Send erc20 token to coin using precompile")
95+
randomNibiAddress := testutil.AccAddress()
96+
packedArgsPass, err := embeds.SmartContract_FunToken.ABI.Pack(
97+
"sendToBank",
98+
erc20Addr.Address,
99+
big.NewInt(1),
100+
randomNibiAddress.String(),
101+
)
102+
s.Require().NoError(err)
103+
txHashLast := SendTransaction(
104+
s,
105+
&gethcore.LegacyTx{
106+
Nonce: s.getCurrentNonce(s.fundedAccEthAddr),
107+
To: &precompile.PrecompileAddr_FunToken,
108+
Data: packedArgsPass,
109+
Gas: 1_500_000,
110+
GasPrice: big.NewInt(1),
111+
},
112+
false,
113+
)
114+
115+
// Wait for all txs to be included in a block
116+
blockNumFirstTx, _, _ := WaitForReceipt(s, txHashFirst)
117+
blockNumLastTx, _, _ := WaitForReceipt(s, txHashLast)
118+
s.Require().NotNil(blockNumFirstTx)
119+
s.Require().NotNil(blockNumLastTx)
120+
121+
// Check tx logs for each tx
122+
type logsCheck struct {
123+
txInfo string
124+
logs []*gethcore.Log
125+
expectEthTx bool
126+
}
127+
checks := []logsCheck{
128+
{
129+
txInfo: "TX1 - simple eth transfer, should have empty logs",
130+
logs: nil,
131+
expectEthTx: true,
132+
},
133+
{
134+
txInfo: "TX2 - deploying erc20 contract, should have logs",
135+
logs: []*gethcore.Log{
136+
// minting initial balance to the account
137+
{
138+
Address: erc20ContractAddr,
139+
Topics: []gethcommon.Hash{
140+
crypto.Keccak256Hash([]byte("Transfer(address,address,uint256)")),
141+
gethcommon.Address{}.Hash(),
142+
s.fundedAccEthAddr.Hash(),
143+
},
144+
},
145+
},
146+
expectEthTx: true,
147+
},
148+
{
149+
txInfo: "TX3 - create FunToken from ERC20, no eth tx, no logs",
150+
logs: nil,
151+
expectEthTx: false,
152+
},
153+
{
154+
txInfo: "TX4 - create FunToken from unibi coin, no eth tx, logs for contract deployment",
155+
logs: []*gethcore.Log{
156+
// contract ownership to evm module
157+
{
158+
Address: erc20FromCoinAddr,
159+
Topics: []gethcommon.Hash{
160+
crypto.Keccak256Hash([]byte("OwnershipTransferred(address,address)")),
161+
gethcommon.Address{}.Hash(),
162+
evm.EVM_MODULE_ADDRESS.Hash(),
163+
},
164+
},
165+
},
166+
expectEthTx: false,
167+
},
168+
{
169+
txInfo: "TX5 - Convert coin to EVM, no eth tx, logs for minting tokens to the account",
170+
logs: []*gethcore.Log{
171+
// minting to the account
172+
{
173+
Address: erc20FromCoinAddr,
174+
Topics: []gethcommon.Hash{
175+
crypto.Keccak256Hash([]byte("Transfer(address,address,uint256)")),
176+
gethcommon.Address{}.Hash(),
177+
s.fundedAccEthAddr.Hash(),
178+
},
179+
},
180+
},
181+
expectEthTx: false,
182+
},
183+
{
184+
txInfo: "TX6 - Send erc20 token to coin using precompile, eth tx, logs for transferring tokens to evm module",
185+
logs: []*gethcore.Log{
186+
// transfer from account to evm module
187+
{
188+
Address: erc20ContractAddr,
189+
Topics: []gethcommon.Hash{
190+
crypto.Keccak256Hash([]byte("Transfer(address,address,uint256)")),
191+
s.fundedAccEthAddr.Hash(),
192+
evm.EVM_MODULE_ADDRESS.Hash(),
193+
},
194+
},
195+
},
196+
expectEthTx: true,
197+
},
198+
}
199+
200+
// Getting block results. Note: txs could be included in more than one block
201+
blockNumber := blockNumFirstTx.Int64()
202+
blockRes, err := s.backend.TendermintBlockResultByNumber(&blockNumber)
203+
s.Require().NoError(err)
204+
s.Require().NotNil(blockRes)
205+
txIndex := 0
206+
ethTxIndex := 0
207+
for idx, check := range checks {
208+
if txIndex+1 > len(blockRes.TxsResults) {
209+
blockNumber++
210+
if blockNumber > blockNumLastTx.Int64() {
211+
s.Fail("TX %d not found in block results", idx)
212+
}
213+
txIndex = 0
214+
ethTxIndex = 0
215+
blockRes, err = s.backend.TendermintBlockResultByNumber(&blockNumber)
216+
s.Require().NoError(err)
217+
s.Require().NotNil(blockRes)
218+
}
219+
s.assertTxLogsAndTxIndex(blockRes, txIndex, ethTxIndex, check.logs, check.expectEthTx, check.txInfo)
220+
txIndex++
221+
if check.expectEthTx {
222+
ethTxIndex++
223+
}
224+
}
225+
}
226+
227+
// assertTxLogsAndTxIndex gets tx results from the block and checks tx logs and tx index.
228+
func (s *BackendSuite) assertTxLogsAndTxIndex(
229+
blockRes *tmrpctypes.ResultBlockResults,
230+
txIndex int,
231+
ethTxIndex int,
232+
expectedTxLogs []*gethcore.Log,
233+
expectedEthTx bool,
234+
txInfo string,
235+
) {
236+
txResults := blockRes.TxsResults[txIndex]
237+
s.Require().Equal(uint32(0x0), txResults.Code, "tx failed, %s. RawLog: %s", txInfo, txResults.Log)
238+
239+
events := blockRes.TxsResults[txIndex].Events
240+
241+
foundEthTx := false
242+
for _, event := range events {
243+
if event.Type == evm.TypeUrlEventTxLog {
244+
logs, err := backend.ParseTxLogsFromEvent(event)
245+
s.Require().NoError(err)
246+
if len(expectedTxLogs) > 0 {
247+
s.Require().GreaterOrEqual(len(logs), len(expectedTxLogs))
248+
s.assertTxLogsMatch(expectedTxLogs, logs, txInfo)
249+
} else {
250+
s.Require().Nil(logs)
251+
}
252+
}
253+
if event.Type == evm.TypeUrlEventEthereumTx {
254+
foundEthTx = true
255+
if !expectedEthTx {
256+
s.Fail("unexpected EventEthereumTx event for non-eth tx, %s", txInfo)
257+
}
258+
ethereumTx, err := evm.EventEthereumTxFromABCIEvent(event)
259+
s.Require().NoError(err)
260+
s.Require().Equal(
261+
fmt.Sprintf("%d", ethTxIndex),
262+
ethereumTx.Index,
263+
"tx index mismatch, %s", txInfo,
264+
)
265+
}
266+
}
267+
if expectedEthTx && !foundEthTx {
268+
s.Fail("expected EventEthereumTx event not found, %s", txInfo)
269+
}
270+
}
271+
272+
// assertTxLogsMatch checks that actual tx logs include the expected logs
273+
func (s *BackendSuite) assertTxLogsMatch(
274+
expectedLogs []*gethcore.Log,
275+
actualLogs []*gethcore.Log,
276+
txInfo string,
277+
) {
278+
for idx, expectedLog := range expectedLogs {
279+
actualLog := actualLogs[idx]
280+
s.Require().Equal(
281+
expectedLog.Address,
282+
actualLog.Address, fmt.Sprintf("log contract address mismatch, log index %d, %s", idx, txInfo),
283+
)
284+
285+
s.Require().Equal(
286+
len(expectedLog.Topics),
287+
len(actualLog.Topics),
288+
fmt.Sprintf("topics length mismatch, log index %d, %s", idx, txInfo),
289+
)
290+
291+
for idx, topic := range expectedLog.Topics {
292+
s.Require().Equal(
293+
topic,
294+
actualLog.Topics[idx],
295+
fmt.Sprintf("topic mismatch, log index %d, %s", idx, txInfo),
296+
)
297+
}
298+
}
299+
}

0 commit comments

Comments
 (0)