Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(evm): Combine both account queries into "/eth.evm.v1.Query/EthAccount", accepting both nibi-prefixed Bech32 addresses and Ethereum-type hexadecimal addresses as input. #1986

Merged
merged 27 commits into from
Aug 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
1f0d8d0
refactor: remove unused vars. improve error clarity for testnetwork/New
Unique-Divine Jul 30, 2024
3335463
refactor: use pebbledb as the test db
Unique-Divine Jul 30, 2024
e106002
changelog
Unique-Divine Jul 30, 2024
a1c5782
refactor(statedb): separate Account and AccountWei to have state obje…
Unique-Divine Aug 4, 2024
cd9642b
math functions for unibi and wei
Unique-Divine Aug 4, 2024
9cd1edf
chore: wei unit migration
Unique-Divine Aug 4, 2024
18995b6
test(statedb): complete the wei-based account migration. Remove all m…
Unique-Divine Aug 4, 2024
4419067
test(statedb_test.go): more thorough test cases
Unique-Divine Aug 5, 2024
e171a5e
Merge branch 'main' into ud/attonibi
Unique-Divine Aug 5, 2024
37c09ff
Squashed commit of the following: merge ud/attonibi
Unique-Divine Aug 5, 2024
7b8addd
address conversion fns and tests
Unique-Divine Aug 5, 2024
82a1d33
feat(evm): Combine both account queries into "/eth.evm.v1.Query/EthAc…
Unique-Divine Aug 5, 2024
3f83bcf
fix(evmante): check balance in wei, deduct unibi gas
Unique-Divine Aug 5, 2024
811d6b5
fix(e2e): JavaScript BigInt is a joke
Unique-Divine Aug 6, 2024
ec8216f
fix(e2e): avoid BigInt overflow with 10^18 values
Unique-Divine Aug 6, 2024
d14ccf3
pull /eth from ud/account-query
Unique-Divine Aug 6, 2024
a90ef2f
fix(evmante): CheckSenderBalance needs to use wei
Unique-Divine Aug 6, 2024
82bf5f0
revert: add back NibiruAccount query to mock client
Unique-Divine Aug 6, 2024
9f2fc57
Merge branch 'main' into ud/attonibi
Unique-Divine Aug 6, 2024
6ae937f
fix(e2e-evm): add logging and fix tests
Unique-Divine Aug 6, 2024
a6c17b5
chore: resolve last few merge conflicts
Unique-Divine Aug 6, 2024
598b0ce
Merge branch 'main' into ud/attonibi
Unique-Divine Aug 6, 2024
7126a88
refactor: include variable name change suggestion for BalanceNative
Unique-Divine Aug 6, 2024
2c1d2b3
refactor: include variable name change suggestion for BalanceNative
Unique-Divine Aug 6, 2024
dfab619
Merge branch 'ud/attonibi' of https://github.com/NibiruChain/nibiru i…
Unique-Divine Aug 6, 2024
fd2fadb
Merge branch 'main' into ud/attonibi
Unique-Divine Aug 6, 2024
13c6ae9
Merge branch 'ud/attonibi' into ud/account-query
Unique-Divine Aug 6, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- [#1982](https://github.com/NibiruChain/nibiru/pull/1982) - feat(evm): add GlobalMinGasPrices
- [#1983](https://github.com/NibiruChain/nibiru/pull/1983) - chore(evm): remove ExtensionOptionsWeb3Tx and ExtensionOptionDynamicFeeTx
- [#1985](https://github.com/NibiruChain/nibiru/pull/1985) - feat(evm)!: Use atto denomination for the wei units in the EVM so that NIBI is "ether" to clients. Only micronibi (unibi) amounts can be transferred. All clients follow the constraint equation, 1 ether == 1 NIBI == 10^6 unibi == 10^18 wei.
=======
- [#1986](https://github.com/NibiruChain/nibiru/pull/1986) - feat(evm): Combine both account queries into "/eth.evm.v1.Query/EthAccount", accepting both nibi-prefixed Bech32 addresses and Ethereum-type hexadecimal addresses as input.

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

Expand Down
30 changes: 15 additions & 15 deletions e2e/evm/test/contract_send_nibi.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,88 +11,88 @@ describe("Send NIBI via smart contract", async () => {

it("should send via transfer method", async () => {
const recipient = Wallet.createRandom()
const transferValue = toBigInt(5e12) * toBigInt(1e6) // 5 micro NIBI
const weiToSend = toBigInt(5e12) * toBigInt(1e6) // 5 micro NIBI

const ownerBalanceBefore = await provider.getBalance(account) // NIBI
const recipientBalanceBefore = await provider.getBalance(recipient) // NIBI
expect(recipientBalanceBefore).toEqual(BigInt(0))

const tx = await contract.sendViaTransfer(recipient, {
value: transferValue,
value: weiToSend,
})
const receipt = await tx.wait(1, 5e3)

// Assert balances with logging
const tenPow12 = toBigInt(1e12)
const txCostMicronibi = transferValue / tenPow12 + receipt.gasUsed
const txCostMicronibi = weiToSend / tenPow12 + receipt.gasUsed
const txCostWei = txCostMicronibi * tenPow12
const expectedOwnerWei = ownerBalanceBefore - txCostWei
console.debug("DEBUG should send via transfer method %o:", {
ownerBalanceBefore,
transferValue,
weiToSend,
gasUsed: receipt.gasUsed,
gasPrice: `${receipt.gasPrice.toString()} micronibi`,
expectedOwnerWei,
})
expect(provider.getBalance(account)).resolves.toBe(expectedOwnerWei)
expect(provider.getBalance(recipient)).resolves.toBe(transferValue)
expect(provider.getBalance(recipient)).resolves.toBe(weiToSend)
}, 20e3)

it("should send via send method", async () => {
const recipient = Wallet.createRandom()
const transferValue = toBigInt(100e12) * toBigInt(1e6) // 100 NIBi
const weiToSend = toBigInt(100e12) * toBigInt(1e6) // 100 NIBi

const ownerBalanceBefore = await provider.getBalance(account) // NIBI
const recipientBalanceBefore = await provider.getBalance(recipient) // NIBI
expect(recipientBalanceBefore).toEqual(BigInt(0))

const tx = await contract.sendViaSend(recipient, {
value: transferValue,
value: weiToSend,
})
const receipt = await tx.wait(1, 5e3)

// Assert balances with logging
const tenPow12 = toBigInt(1e12)
const txCostMicronibi = transferValue / tenPow12 + receipt.gasUsed
const txCostMicronibi = weiToSend / tenPow12 + receipt.gasUsed
const txCostWei = txCostMicronibi * tenPow12
const expectedOwnerWei = ownerBalanceBefore - txCostWei
console.debug("DEBUG send via send method %o:", {
ownerBalanceBefore,
transferValue,
weiToSend,
gasUsed: receipt.gasUsed,
gasPrice: `${receipt.gasPrice.toString()} micronibi`,
expectedOwnerWei,
})
expect(provider.getBalance(account)).resolves.toBe(expectedOwnerWei)
expect(provider.getBalance(recipient)).resolves.toBe(transferValue)
expect(provider.getBalance(recipient)).resolves.toBe(weiToSend)
}, 20e3)

it("should send via transfer method", async () => {
const recipient = Wallet.createRandom()
const transferValue = toBigInt(100e12) * toBigInt(1e6) // 100 NIBI
const weiToSend = toBigInt(100e12) * toBigInt(1e6) // 100 NIBI

const ownerBalanceBefore = await provider.getBalance(account) // NIBI
const recipientBalanceBefore = await provider.getBalance(recipient) // NIBI
expect(recipientBalanceBefore).toEqual(BigInt(0))

const tx = await contract.sendViaCall(recipient, {
value: transferValue,
value: weiToSend,
})
const receipt = await tx.wait(1, 5e3)

// Assert balances with logging
const tenPow12 = toBigInt(1e12)
const txCostMicronibi = transferValue / tenPow12 + receipt.gasUsed
const txCostMicronibi = weiToSend / tenPow12 + receipt.gasUsed
const txCostWei = txCostMicronibi * tenPow12
const expectedOwnerWei = ownerBalanceBefore - txCostWei
console.debug("DEBUG should send via transfer method %o:", {
ownerBalanceBefore,
transferValue,
weiToSend,
gasUsed: receipt.gasUsed,
gasPrice: `${receipt.gasPrice.toString()} micronibi`,
expectedOwnerWei,
})
expect(provider.getBalance(account)).resolves.toBe(expectedOwnerWei)
expect(provider.getBalance(recipient)).resolves.toBe(transferValue)
expect(provider.getBalance(recipient)).resolves.toBe(weiToSend)
}, 20e3)
})
32 changes: 0 additions & 32 deletions eth/rpc/backend/mocks/evm_query_client.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

49 changes: 17 additions & 32 deletions proto/eth/evm/v1/query.proto
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright (c) 2023-2024 Nibi, Inc.

Check failure on line 1 in proto/eth/evm/v1/query.proto

View workflow job for this annotation

GitHub Actions / break-check

Previously present message "QueryNibiruAccountRequest" was deleted from file.

Check failure on line 1 in proto/eth/evm/v1/query.proto

View workflow job for this annotation

GitHub Actions / break-check

Previously present message "QueryNibiruAccountResponse" was deleted from file.
syntax = "proto3";
package eth.evm.v1;

Expand All @@ -12,17 +12,13 @@
option go_package = "github.com/NibiruChain/nibiru/x/evm";

// Query defines the gRPC querier service.
service Query {

Check failure on line 15 in proto/eth/evm/v1/query.proto

View workflow job for this annotation

GitHub Actions / break-check

Previously present RPC "NibiruAccount" on service "Query" was deleted.
// EthAccount queries an Ethereum account.
// EthAccount queries a Nibiru account using its EVM address or Bech32 Nibiru
// address.
rpc EthAccount(QueryEthAccountRequest) returns (QueryEthAccountResponse) {
option (google.api.http).get = "/nibiru/evm/v1/eth_account/{address}";
}

// NibiruAccount queries the Bech32 Nibiru address corresponding to Nibiru EVM account.
rpc NibiruAccount(QueryNibiruAccountRequest) returns (QueryNibiruAccountResponse) {
option (google.api.http).get = "/nibiru/evm/v1/nibiru_account/{address}";
}

// ValidatorAccount queries an Ethereum account's from a validator consensus
// Address.
rpc ValidatorAccount(QueryValidatorAccountRequest) returns (QueryValidatorAccountResponse) {
Expand Down Expand Up @@ -86,39 +82,28 @@
option (gogoproto.equal) = false;
option (gogoproto.goproto_getters) = false;

// address is the ethereum hex address to query the account for.
// address is the Ethereum hex address or nibi Bech32 address to query the account for.
string address = 1;
}

// QueryEthAccountResponse is the response type for the Query/Account RPC method.
// QueryEthAccountResponse is the response type for the Query/EthAccount RPC method.
message QueryEthAccountResponse {
// balance is the balance of unibi (micronibi).
string balance = 1;

Check failure on line 92 in proto/eth/evm/v1/query.proto

View workflow job for this annotation

GitHub Actions / break-check

Field "1" with name "balance" on message "QueryEthAccountResponse" changed option "json_name" from "balanceWei" to "balance".

Check failure on line 92 in proto/eth/evm/v1/query.proto

View workflow job for this annotation

GitHub Actions / break-check

Field "1" on message "QueryEthAccountResponse" changed name from "balance_wei" to "balance".
// balance_wei is the balance of wei (attoether, where NIBI is ether).
string balance_wei = 1;
string balance_wei = 2;

Check failure on line 94 in proto/eth/evm/v1/query.proto

View workflow job for this annotation

GitHub Actions / break-check

Field "2" with name "balance_wei" on message "QueryEthAccountResponse" changed option "json_name" from "codeHash" to "balanceWei".

Check failure on line 94 in proto/eth/evm/v1/query.proto

View workflow job for this annotation

GitHub Actions / break-check

Field "2" on message "QueryEthAccountResponse" changed name from "code_hash" to "balance_wei".
// code_hash is the hex-formatted code bytes from the EOA.
string code_hash = 2;
string code_hash = 3;

Check failure on line 96 in proto/eth/evm/v1/query.proto

View workflow job for this annotation

GitHub Actions / break-check

Field "3" with name "code_hash" on message "QueryEthAccountResponse" changed option "json_name" from "nonce" to "codeHash".

Check failure on line 96 in proto/eth/evm/v1/query.proto

View workflow job for this annotation

GitHub Actions / break-check

Field "3" with name "code_hash" on message "QueryEthAccountResponse" changed type from "uint64" to "string".

Check failure on line 96 in proto/eth/evm/v1/query.proto

View workflow job for this annotation

GitHub Actions / break-check

Field "3" on message "QueryEthAccountResponse" changed name from "nonce" to "code_hash".
// nonce is the account's sequence number.
uint64 nonce = 3;
}
uint64 nonce = 4;

// QueryNibiruAccountRequest is the request type for the Query/NibiruAccount RPC
// method.
message QueryNibiruAccountRequest {
option (gogoproto.equal) = false;
option (gogoproto.goproto_getters) = false;
// eth_address: The hexadecimal-encoded string representing the 20 byte address
// of a Nibiru EVM account.
string eth_address = 5;

// address is the ethereum hex address to query the account for.
string address = 1;
}

// QueryNibiruAccountResponse is the response type for the Query/NibiruAccount
// RPC method.
message QueryNibiruAccountResponse {
// Nibiru bech32 account "address"
string address = 1;
// sequence is the account's sequence number.
uint64 sequence = 2;
// account_number is the account number
uint64 account_number = 3;
// bech32_address is the nibi-prefixed address of the account that can receive
// bank transfers ("cosmos.bank.v1beta1.MsgSend").
string bech32_address = 6;
}

// QueryValidatorAccountRequest is the request type for the
Expand Down Expand Up @@ -248,10 +233,10 @@
reserved 2;
reserved "tx_index";
// trace_config holds extra parameters to trace functions.
TraceConfig trace_config = 3;
eth.evm.v1.TraceConfig trace_config = 3;
// predecessors is an array of transactions included in the same block
// need to be replayed first to get correct context for tracing.
repeated MsgEthereumTx predecessors = 4;
repeated eth.evm.v1.MsgEthereumTx predecessors = 4;
// block_number of requested transaction
int64 block_number = 5;
// block_hash of requested transaction
Expand Down
11 changes: 3 additions & 8 deletions x/evm/evm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -129,17 +129,12 @@ func (s *TestSuite) TestModuleAddressEVM() {
// EVM module should have mint perms
deps := evmtest.NewTestDeps()
{
_, err := deps.EvmKeeper.EthAccount(deps.GoCtx(), &evm.QueryEthAccountRequest{
resp, err := deps.EvmKeeper.EthAccount(deps.GoCtx(), &evm.QueryEthAccountRequest{
Address: evmModuleAddr.Hex(),
})
s.NoError(err)
}
{
resp, err := deps.EvmKeeper.NibiruAccount(deps.GoCtx(), &evm.QueryNibiruAccountRequest{
Address: evmModuleAddr.Hex(),
})
s.NoError(err)
s.Equal(nibiAddr.String(), resp.Address)
s.Equal(nibiAddr.String(), resp.Bech32Address)
s.Equal(evmModuleAddr.String(), resp.EthAddress)
}
}

Expand Down
4 changes: 0 additions & 4 deletions x/evm/evmtest/eth.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,6 @@ func NewEthAccInfo() EthPrivKeyAcc {
}
}

func EthAddrToNibiruAddr(ethAddr gethcommon.Address) sdk.AccAddress {
return ethAddr.Bytes()
}

type EthPrivKeyAcc struct {
EthAddr gethcommon.Address
NibiruAddr sdk.AccAddress
Expand Down
2 changes: 1 addition & 1 deletion x/evm/keeper/gas_fees.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@
if balanceWei.Cmp(big.NewInt(0)) < 0 || balanceWei.Cmp(cost) < 0 {
return errors.Wrapf(
errortypes.ErrInsufficientFunds,
"sender balance < tx cost (%s < %s)", balanceWei, txData.Cost(),
"sender balance < tx cost (%s < %s)", balanceWei, cost,

Check warning on line 100 in x/evm/keeper/gas_fees.go

View check run for this annotation

Codecov / codecov/patch

x/evm/keeper/gas_fees.go#L100

Added line #L100 was not covered by tests
)
}
return nil
Expand Down
68 changes: 23 additions & 45 deletions x/evm/keeper/grpc_query.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,65 +37,43 @@ import (
var _ evm.QueryServer = &Keeper{}

// EthAccount: Implements the gRPC query for "/eth.evm.v1.Query/EthAccount".
// EthAccount retrieves the account details for a given Ethereum hex address.
// EthAccount retrieves the account and balance details for an account with the
// given address.
//
// Parameters:
// - goCtx: The context.Context object representing the request context.
// - req: Request containing the Ethereum hexadecimal address.
//
// Returns:
// - A pointer to the QueryEthAccountResponse object containing the account details.
// - An error if the account retrieval process encounters any issues.
// - req: Request containing the address in either Ethereum hexadecimal or
// Bech32 format.
func (k Keeper) EthAccount(
goCtx context.Context, req *evm.QueryEthAccountRequest,
) (*evm.QueryEthAccountResponse, error) {
if err := req.Validate(); err != nil {
isBech32, err := req.Validate()
if err != nil {
return nil, err
}

addr := gethcommon.HexToAddress(req.Address)
ctx := sdk.UnwrapSDKContext(goCtx)
acct := k.GetAccountOrEmpty(ctx, addr)

return &evm.QueryEthAccountResponse{
BalanceWei: evm.NativeToWei(acct.BalanceNative).String(),
CodeHash: gethcommon.BytesToHash(acct.CodeHash).Hex(),
Nonce: acct.Nonce,
}, nil
}
var addrEth gethcommon.Address
var addrBech32 sdk.AccAddress

// NibiruAccount: Implements the gRPC query for "/eth.evm.v1.Query/NibiruAccount".
// NibiruAccount retrieves the Cosmos account details for a given Ethereum address.
//
// Parameters:
// - goCtx: The context.Context object representing the request context.
// - req: The QueryNibiruAccountRequest object containing the Ethereum address.
//
// Returns:
// - A pointer to the QueryNibiruAccountResponse object containing the Cosmos account details.
// - An error if the account retrieval process encounters any issues.
func (k Keeper) NibiruAccount(
goCtx context.Context, req *evm.QueryNibiruAccountRequest,
) (resp *evm.QueryNibiruAccountResponse, err error) {
if err := req.Validate(); err != nil {
return resp, err
if isBech32 {
addrBech32 = sdk.MustAccAddressFromBech32(req.Address)
addrEth = eth.NibiruAddrToEthAddr(addrBech32)
} else {
addrEth = gethcommon.HexToAddress(req.Address)
addrBech32 = eth.EthAddrToNibiruAddr(addrEth)
}

ctx := sdk.UnwrapSDKContext(goCtx)
ethAddr := gethcommon.HexToAddress(req.Address)
nibiruAddr := sdk.AccAddress(ethAddr.Bytes())
acct := k.GetAccountOrEmpty(ctx, addrEth)

accountOrNil := k.accountKeeper.GetAccount(ctx, nibiruAddr)
resp = &evm.QueryNibiruAccountResponse{
Address: nibiruAddr.String(),
}

if accountOrNil != nil {
resp.Sequence = accountOrNil.GetSequence()
resp.AccountNumber = accountOrNil.GetAccountNumber()
}

return resp, nil
return &evm.QueryEthAccountResponse{
Balance: acct.BalanceNative.String(),
BalanceWei: evm.NativeToWei(acct.BalanceNative).String(),
CodeHash: gethcommon.BytesToHash(acct.CodeHash).Hex(),
Nonce: acct.Nonce,
EthAddress: addrEth.Hex(),
Bech32Address: addrBech32.String(),
}, nil
}

// ValidatorAccount: Implements the gRPC query for
Expand Down
Loading
Loading