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): use atto denomination for the wei units in the EVM so that NIBI is "ether" to clients #1985

Merged
merged 20 commits into from
Aug 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 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
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
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
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,10 +93,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- [#1973](https://github.com/NibiruChain/nibiru/pull/1973) - chore(appconst): Add chain IDs ending in "3" to the "knownEthChainIDMap". This makes it possible to use devnet 3 and testnet 3.
- [#1976](https://github.com/NibiruChain/nibiru/pull/1976) - refactor(evm): unique chain ids for all networks
- [#1977](https://github.com/NibiruChain/nibiru/pull/1977) - fix(localnet): rolled back change of evm validator address with cosmos derivation path
- [#1979](https://github.com/NibiruChain/nibiru/pull/1979) -refactor(db): use pebbledb as the default db in integration tests
- [#1981](https://github.com/NibiruChain/nibiru/pull/1981) - fix(evm): remove isCheckTx() short circuit on `AnteDecVerifyEthAcc`
- [#1979](https://github.com/NibiruChain/nibiru/pull/1979) - refactor(db): use pebbledb as the default db in integration tests
- [#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.
=======

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

Expand Down
4 changes: 3 additions & 1 deletion app/evmante/evmante_can_transfer.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,10 +87,12 @@ func (ctd CanTransferDecorator) AnteHandle(
// NOTE: here the gas consumed is from the context with the infinite gas meter
if coreMsg.Value().Sign() > 0 &&
!evmInstance.Context.CanTransfer(stateDB, coreMsg.From(), coreMsg.Value()) {
balanceWei := stateDB.GetBalance(coreMsg.From())
return ctx, errors.Wrapf(
errortypes.ErrInsufficientFunds,
"failed to transfer %s from address %s using the EVM block context transfer function",
"failed to transfer %s wei (balance=%s) from address %s using the EVM block context transfer function",
coreMsg.Value(),
balanceWei,
coreMsg.From(),
)
}
Expand Down
12 changes: 9 additions & 3 deletions app/evmante/evmante_can_transfer_test.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
package evmante_test

import (
"math/big"

sdk "github.com/cosmos/cosmos-sdk/types"

"github.com/NibiruChain/nibiru/app/evmante"
"github.com/NibiruChain/nibiru/eth"
"github.com/NibiruChain/nibiru/x/common/testutil/testapp"
"github.com/NibiruChain/nibiru/x/evm/evmtest"
"github.com/NibiruChain/nibiru/x/evm/statedb"
)
Expand All @@ -22,7 +21,14 @@ func (s *TestSuite) TestCanTransferDecorator() {
{
name: "happy: signed tx, sufficient funds",
beforeTxSetup: func(deps *evmtest.TestDeps, sdb *statedb.StateDB) {
sdb.AddBalance(deps.Sender.EthAddr, big.NewInt(100))
s.NoError(
testapp.FundAccount(
deps.Chain.BankKeeper,
deps.Ctx,
deps.Sender.NibiruAddr,
sdk.NewCoins(sdk.NewInt64Coin(eth.EthBaseDenom, 100)),
),
)
},
txSetup: func(deps *evmtest.TestDeps) sdk.FeeTx {
txMsg := evmtest.HappyTransferTx(deps, 0)
Expand Down
4 changes: 2 additions & 2 deletions app/evmante/evmante_gas_consume_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ func (s *TestSuite) TestAnteDecEthGasConsume() {
name: "happy: sender with funds",
beforeTxSetup: func(deps *evmtest.TestDeps, sdb *statedb.StateDB) {
gasLimit := happyGasLimit()
balance := new(big.Int).Add(gasLimit, big.NewInt(100))
balance := evm.NativeToWei(new(big.Int).Add(gasLimit, big.NewInt(100)))
sdb.AddBalance(deps.Sender.EthAddr, balance)
},
txSetup: evmtest.HappyCreateContractTx,
Expand All @@ -46,7 +46,7 @@ func (s *TestSuite) TestAnteDecEthGasConsume() {
name: "sad: out of gas",
beforeTxSetup: func(deps *evmtest.TestDeps, sdb *statedb.StateDB) {
gasLimit := happyGasLimit()
balance := new(big.Int).Add(gasLimit, big.NewInt(100))
balance := evm.NativeToWei(new(big.Int).Add(gasLimit, big.NewInt(100)))
sdb.AddBalance(deps.Sender.EthAddr, balance)
},
txSetup: evmtest.HappyCreateContractTx,
Expand Down
7 changes: 5 additions & 2 deletions app/evmante/evmante_handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/NibiruChain/nibiru/app/ante"
"github.com/NibiruChain/nibiru/app/evmante"
"github.com/NibiruChain/nibiru/eth"
"github.com/NibiruChain/nibiru/x/evm"
"github.com/NibiruChain/nibiru/x/evm/evmtest"
"github.com/NibiruChain/nibiru/x/evm/statedb"
)
Expand All @@ -26,16 +27,18 @@ func (s *TestSuite) TestAnteHandlerEVM() {
{
name: "happy: signed tx, sufficient funds",
beforeTxSetup: func(deps *evmtest.TestDeps, sdb *statedb.StateDB) {
balanceMicronibi := new(big.Int).Add(evmtest.GasLimitCreateContract(), big.NewInt(100))
sdb.AddBalance(
deps.Sender.EthAddr,
new(big.Int).Add(evmtest.GasLimitCreateContract(), big.NewInt(100)),
evm.NativeToWei(balanceMicronibi),
)
},
ctxSetup: func(deps *evmtest.TestDeps) {
gasPrice := sdk.NewInt64Coin("unibi", 1)
maxGasMicronibi := new(big.Int).Add(evmtest.GasLimitCreateContract(), big.NewInt(100))
cp := &tmproto.ConsensusParams{
Block: &tmproto.BlockParams{
MaxGas: new(big.Int).Add(evmtest.GasLimitCreateContract(), big.NewInt(100)).Int64(),
MaxGas: evm.NativeToWei(maxGasMicronibi).Int64(),
},
}
deps.Ctx = deps.Ctx.
Expand Down
5 changes: 3 additions & 2 deletions app/evmante/evmante_verify_eth_acc.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package evmante

import (
"cosmossdk.io/errors"
sdkmath "cosmossdk.io/math"
sdk "github.com/cosmos/cosmos-sdk/types"
errortypes "github.com/cosmos/cosmos-sdk/types/errors"
gethcommon "github.com/ethereum/go-ethereum/common"
Expand Down Expand Up @@ -69,7 +68,9 @@ func (anteDec AnteDecVerifyEthAcc) AnteHandle(
"the sender is not EOA: address %s, codeHash <%s>", fromAddr, acct.CodeHash)
}

if err := keeper.CheckSenderBalance(sdkmath.NewIntFromBigInt(acct.Balance), txData); err != nil {
if err := keeper.CheckSenderBalance(
evm.NativeToWei(acct.BalanceNative), txData,
); err != nil {
return ctx, errors.Wrap(err, "failed to check sender balance")
}
}
Expand Down
2 changes: 1 addition & 1 deletion app/evmante/evmante_verify_eth_acc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ func (s *TestSuite) TestAnteDecoratorVerifyEthAcc_CheckTx() {
{
name: "happy: sender with funds",
beforeTxSetup: func(deps *evmtest.TestDeps, sdb *statedb.StateDB) {
sdb.AddBalance(deps.Sender.EthAddr, happyGasLimit())
sdb.AddBalance(deps.Sender.EthAddr, evm.NativeToWei(happyGasLimit()))
},
txSetup: evmtest.HappyCreateContractTx,
wantErr: "",
Expand Down
25 changes: 18 additions & 7 deletions e2e/evm/test/basic_queries.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { describe, expect, it } from "bun:test"; // eslint-disable-line import/no-unresolved
import { toBigInt, Wallet } from "ethers";
import { account, provider } from "./setup";
import { describe, expect, it } from "bun:test" // eslint-disable-line import/no-unresolved
import { toBigInt, Wallet } from "ethers"
import { account, provider } from "./setup"

describe("Basic Queries", () => {
it("Simple transfer, balance check", async () => {
const alice = Wallet.createRandom()
const amountToSend = toBigInt(1e3) // unibi
const amountToSend = toBigInt(5e12) * toBigInt(1e6) // unibi

const senderBalanceBefore = await provider.getBalance(account)
const recipientBalanceBefore = await provider.getBalance(alice)
Expand All @@ -19,14 +19,25 @@ describe("Basic Queries", () => {
value: amountToSend,
}
const txResponse = await account.sendTransaction(transaction)
await txResponse.wait()
await txResponse.wait(1, 10e3)
expect(txResponse).toHaveProperty("blockHash")

const senderBalanceAfter = await provider.getBalance(account)
const recipientBalanceAfter = await provider.getBalance(alice)

const expectedSenderBalance = senderBalanceBefore - amountToSend - 50000n // 50k gas for the transaction
expect(senderBalanceAfter).toEqual(expectedSenderBalance)
// Assert balances with logging
const tenPow12 = toBigInt(1e12)
const gasUsed = 50000n // 50k gas for the transaction
const txCostMicronibi = amountToSend / tenPow12 + gasUsed
const txCostWei = txCostMicronibi * tenPow12
const expectedSenderWei = senderBalanceBefore - txCostWei
console.debug("DEBUG should send via transfer method %o:", {
Comment on lines +29 to +33
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe we better use ether / wei everywhere in JS rather than using tenPow12?

senderBalanceBefore,
amountToSend,
expectedSenderWei,
senderBalanceAfter,
})
expect(senderBalanceAfter).toEqual(expectedSenderWei)
expect(recipientBalanceAfter).toEqual(amountToSend)
}, 20e3)
})
67 changes: 48 additions & 19 deletions e2e/evm/test/contract_send_nibi.test.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import { describe, expect, it } from "bun:test"; // eslint-disable-line import/no-unresolved
import { toBigInt, Wallet } from "ethers";
import { SendNibiCompiled__factory } from "../types/ethers-contracts";
import { account, provider } from "./setup";
import { describe, expect, it } from "bun:test" // eslint-disable-line import/no-unresolved
import { toBigInt, Wallet } from "ethers"
import { SendNibiCompiled__factory } from "../types/ethers-contracts"
import { account, provider } from "./setup"

describe("Send NIBI via smart contract", async () => {
const factory = new SendNibiCompiled__factory(account);
const contract = await factory.deploy();
const factory = new SendNibiCompiled__factory(account)
const contract = await factory.deploy()
await contract.waitForDeployment()
expect(contract.getAddress()).resolves.toBeDefined()

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

const ownerBalanceBefore = await provider.getBalance(account) // NIBI
const recipientBalanceBefore = await provider.getBalance(recipient) // NIBI
Expand All @@ -22,15 +22,25 @@ describe("Send NIBI via smart contract", async () => {
})
const receipt = await tx.wait(1, 5e3)

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

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

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is equal to ethers.parseEther("100.0")

const ownerBalanceBefore = await provider.getBalance(account) // NIBI
const recipientBalanceBefore = await provider.getBalance(recipient) // NIBI
Expand All @@ -41,15 +51,25 @@ describe("Send NIBI via smart contract", async () => {
})
const receipt = await tx.wait(1, 5e3)

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

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

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is equal to ethers.parseEther("100.0")

const ownerBalanceBefore = await provider.getBalance(account) // NIBI
const recipientBalanceBefore = await provider.getBalance(recipient) // NIBI
Expand All @@ -60,10 +80,19 @@ describe("Send NIBI via smart contract", async () => {
})
const receipt = await tx.wait(1, 5e3)

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

})
31 changes: 20 additions & 11 deletions eth/eth_account.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,19 @@
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"

"github.com/ethereum/go-ethereum/common"
sdk "github.com/cosmos/cosmos-sdk/types"
gethcommon "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
)

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

func NibiruAddrToEthAddr(nibiruAddr sdk.AccAddress) gethcommon.Address {
return gethcommon.BytesToAddress(nibiruAddr.Bytes())
}

var (
_ authtypes.AccountI = (*EthAccount)(nil)
_ EthAccountI = (*EthAccount)(nil)
Expand All @@ -32,11 +41,11 @@
type EthAccountI interface { //revive:disable-line:exported
authtypes.AccountI
// EthAddress returns the ethereum Address representation of the AccAddress
EthAddress() common.Address
EthAddress() gethcommon.Address
// CodeHash is the keccak256 hash of the contract code (if any)
GetCodeHash() common.Hash
GetCodeHash() gethcommon.Hash
// SetCodeHash sets the code hash to the account fields
SetCodeHash(code common.Hash) error
SetCodeHash(code gethcommon.Hash) error
// Type returns the type of Ethereum Account (EOA or Contract)
Type() EthAccType
}
Expand All @@ -46,23 +55,23 @@
}

// EthAddress returns the account address ethereum format.
func (acc EthAccount) EthAddress() common.Address {
return common.BytesToAddress(acc.GetAddress().Bytes())
func (acc EthAccount) EthAddress() gethcommon.Address {
return gethcommon.BytesToAddress(acc.GetAddress().Bytes())

Check warning on line 59 in eth/eth_account.go

View check run for this annotation

Codecov / codecov/patch

eth/eth_account.go#L58-L59

Added lines #L58 - L59 were not covered by tests
}

func (acc EthAccount) GetCodeHash() common.Hash {
return common.HexToHash(acc.CodeHash)
func (acc EthAccount) GetCodeHash() gethcommon.Hash {
return gethcommon.HexToHash(acc.CodeHash)

Check warning on line 63 in eth/eth_account.go

View check run for this annotation

Codecov / codecov/patch

eth/eth_account.go#L62-L63

Added lines #L62 - L63 were not covered by tests
}

func (acc *EthAccount) SetCodeHash(codeHash common.Hash) error {
func (acc *EthAccount) SetCodeHash(codeHash gethcommon.Hash) error {

Check warning on line 66 in eth/eth_account.go

View check run for this annotation

Codecov / codecov/patch

eth/eth_account.go#L66

Added line #L66 was not covered by tests
acc.CodeHash = codeHash.Hex()
return nil
}

// Type returns the type of Ethereum Account (EOA or Contract)
func (acc EthAccount) Type() EthAccType {
if bytes.Equal(
emptyCodeHash, common.HexToHash(acc.CodeHash).Bytes(),
emptyCodeHash, gethcommon.HexToHash(acc.CodeHash).Bytes(),

Check warning on line 74 in eth/eth_account.go

View check run for this annotation

Codecov / codecov/patch

eth/eth_account.go#L74

Added line #L74 was not covered by tests
) {
return EthAccType_EOA
}
Expand All @@ -79,6 +88,6 @@
func ProtoBaseAccount() authtypes.AccountI {
return &EthAccount{
BaseAccount: &authtypes.BaseAccount{},
CodeHash: common.BytesToHash(emptyCodeHash).String(),
CodeHash: gethcommon.BytesToHash(emptyCodeHash).String(),

Check warning on line 91 in eth/eth_account.go

View check run for this annotation

Codecov / codecov/patch

eth/eth_account.go#L91

Added line #L91 was not covered by tests
}
}
Loading
Loading