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

refactor(evm): clean up erc20 and funtoken methods #2000

Merged
merged 25 commits into from
Aug 14, 2024
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- [#1989](https://github.com/NibiruChain/nibiru/pull/1989) - refactor(evm): simplify evm module address
- [#1996](https://github.com/NibiruChain/nibiru/pull/1996) - perf(evm-keeper-precompile): implement sorted map for `k.precompiles` to remove dead code
- [#1997](https://github.com/NibiruChain/nibiru/pull/1997) - refactor(evm): Remove unnecessary params: "enable_call", "enable_create".
- [#2000](https://github.com/NibiruChain/nibiru/pull/2000) - refactor(evm): simplify ERC-20 keeper methods

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

Expand Down
15 changes: 7 additions & 8 deletions x/evm/keeper/erc20.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@
commit, gasLimit, &fromAcc, contract, contractInput, k, ctx,
)
if err != nil {
return
return evmResp, err
}

unusedBigInt := big.NewInt(0)
Expand All @@ -195,26 +195,25 @@
)

// Apply EVM message
cfg, err := k.GetEVMConfig(
evmCfg, err := k.GetEVMConfig(
ctx,
sdk.ConsAddress(ctx.BlockHeader().ProposerAddress),
k.EthChainID(ctx),
)
if err != nil {
err = fmt.Errorf("failed to load evm config: %s", err)
return
return evmResp, fmt.Errorf("failed to load evm config: %s", err)

Check warning on line 204 in x/evm/keeper/erc20.go

View check run for this annotation

Codecov / codecov/patch

x/evm/keeper/erc20.go#L204

Added line #L204 was not covered by tests
}

txConfig := statedb.NewEmptyTxConfig(gethcommon.BytesToHash(ctx.HeaderHash()))
evmResp, err = k.ApplyEvmMsg(
ctx, evmMsg, evm.NewNoOpTracer(), commit, cfg, txConfig,
ctx, evmMsg, evm.NewNoOpTracer(), commit, evmCfg, txConfig,
)
if err != nil {
return
return evmResp, err

Check warning on line 212 in x/evm/keeper/erc20.go

View check run for this annotation

Codecov / codecov/patch

x/evm/keeper/erc20.go#L212

Added line #L212 was not covered by tests
}

if evmResp.Failed() {
err = fmt.Errorf("%w: EVM error: %s", err, evmResp.VmError)
return
return evmResp, fmt.Errorf("%w: EVM error: %s", err, evmResp.VmError)

Check warning on line 216 in x/evm/keeper/erc20.go

View check run for this annotation

Codecov / codecov/patch

x/evm/keeper/erc20.go#L216

Added line #L216 was not covered by tests
}

return evmResp, err
Expand Down
28 changes: 10 additions & 18 deletions x/evm/keeper/funtoken_from_coin.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,7 @@
ctx sdk.Context, bankDenom string,
) (funtoken evm.FunToken, err error) {
// 1 | Coin already registered with FunToken?
if funtokens := k.FunTokens.Collect(
ctx, k.FunTokens.Indexes.BankDenom.ExactMatch(ctx, bankDenom),
); len(funtokens) > 0 {
if funtokens := k.FunTokens.Collect(ctx, k.FunTokens.Indexes.BankDenom.ExactMatch(ctx, bankDenom)); len(funtokens) > 0 {
return funtoken, fmt.Errorf("funtoken mapping already created for bank denom \"%s\"", bankDenom)
}

Expand All @@ -33,13 +31,11 @@
// 3 | deploy ERC20 for metadata
erc20Addr, err := k.DeployERC20ForBankCoin(ctx, bankCoin)
if err != nil {
return
return funtoken, errors.Wrap(err, "failed to deploy ERC20 for bank coin")

Check warning on line 34 in x/evm/keeper/funtoken_from_coin.go

View check run for this annotation

Codecov / codecov/patch

x/evm/keeper/funtoken_from_coin.go#L34

Added line #L34 was not covered by tests
}

// 4 | ERC20 already registered with FunToken?
if funtokens := k.FunTokens.Collect(
ctx, k.FunTokens.Indexes.ERC20Addr.ExactMatch(ctx, erc20Addr),
); len(funtokens) > 0 {
if funtokens := k.FunTokens.Collect(ctx, k.FunTokens.Indexes.ERC20Addr.ExactMatch(ctx, erc20Addr)); len(funtokens) > 0 {
return funtoken, fmt.Errorf("funtoken mapping already created for ERC20 \"%s\"", erc20Addr.Hex())
}

Expand Down Expand Up @@ -71,23 +67,19 @@
}

erc20Embed := embeds.SmartContract_ERC20Minter
callArgs := []any{bankCoin.Name, bankCoin.Symbol, decimals}
methodName := "" // pass empty method name to deploy the contract
packedArgs, err := erc20Embed.ABI.Pack(methodName, callArgs...)

// pass empty method name to deploy the contract
packedArgs, err := erc20Embed.ABI.Pack("", bankCoin.Name, bankCoin.Symbol, decimals)
if err != nil {
err = errors.Wrap(err, "failed to pack ABI args")
return
return gethcommon.Address{}, err

Check warning on line 75 in x/evm/keeper/funtoken_from_coin.go

View check run for this annotation

Codecov / codecov/patch

x/evm/keeper/funtoken_from_coin.go#L75

Added line #L75 was not covered by tests
}
bytecodeForCall := append(erc20Embed.Bytecode, packedArgs...)

fromEvmAddr := evm.EVM_MODULE_ADDRESS
nonce := k.GetAccNonce(ctx, fromEvmAddr)
erc20Addr = crypto.CreateAddress(fromEvmAddr, nonce)
erc20Contract := (*gethcommon.Address)(nil) // nil >> doesn't exist yet
commit := true
erc20Addr = crypto.CreateAddress(evm.EVM_MODULE_ADDRESS, k.GetAccNonce(ctx, evm.EVM_MODULE_ADDRESS))

bytecodeForCall := append(erc20Embed.Bytecode, packedArgs...)
_, err = k.CallContractWithInput(
ctx, fromEvmAddr, erc20Contract, commit, bytecodeForCall,
ctx, evm.EVM_MODULE_ADDRESS, nil, true, bytecodeForCall,
)
if err != nil {
err = errors.Wrap(err, "deploy ERC20 failed")
Expand Down
34 changes: 15 additions & 19 deletions x/evm/keeper/funtoken_from_erc20.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
gethcommon "github.com/ethereum/go-ethereum/common"

"github.com/NibiruChain/nibiru/v2/eth"
"github.com/NibiruChain/nibiru/v2/x/common"
"github.com/NibiruChain/nibiru/v2/x/evm"
"github.com/NibiruChain/nibiru/v2/x/evm/embeds"
)
Expand All @@ -31,19 +30,19 @@
) (info ERC20Metadata, err error) {
var abi *gethabi.ABI = embeds.SmartContract_ERC20Minter.ABI

errs := []error{}

// Load name, symbol, decimals
name, err := k.LoadERC20Name(ctx, abi, contract)
errs = append(errs, err)
if err != nil {
return info, err
}

symbol, err := k.LoadERC20Symbol(ctx, abi, contract)
errs = append(errs, err)
decimals, err := k.LoadERC20Decimals(ctx, abi, contract)
errs = append(errs, err)
if err != nil {
return info, err

Check warning on line 41 in x/evm/keeper/funtoken_from_erc20.go

View check run for this annotation

Codecov / codecov/patch

x/evm/keeper/funtoken_from_erc20.go#L41

Added line #L41 was not covered by tests
}

err = common.CombineErrors(errs...)
decimals, err := k.LoadERC20Decimals(ctx, abi, contract)
if err != nil {
err = fmt.Errorf("failed to \"FindERC20Metadata\": %w", err)
return info, err
}

Expand Down Expand Up @@ -99,27 +98,24 @@
erc20Addr := erc20.ToAddr()

// 1 | ERC20 already registered with FunToken?
if funtokens := k.FunTokens.Collect(
ctx, k.FunTokens.Indexes.ERC20Addr.ExactMatch(ctx, erc20Addr),
); len(funtokens) > 0 {
if funtokens := k.FunTokens.Collect(ctx, k.FunTokens.Indexes.ERC20Addr.ExactMatch(ctx, erc20Addr)); len(funtokens) > 0 {
return funtoken, fmt.Errorf("funtoken mapping already created for ERC20 \"%s\"", erc20Addr.Hex())
}

// 2 | Get existing ERC20 metadata
info, err := k.FindERC20Metadata(ctx, erc20Addr)
if err != nil {
return
return funtoken, err

Check warning on line 108 in x/evm/keeper/funtoken_from_erc20.go

View check run for this annotation

Codecov / codecov/patch

x/evm/keeper/funtoken_from_erc20.go#L108

Added line #L108 was not covered by tests
}
bankDenom := fmt.Sprintf("erc20/%s", erc20.String())

// 3 | Coin already registered with FunToken?
_, isAlreadyCoin := k.bankKeeper.GetDenomMetaData(ctx, bankDenom)
if isAlreadyCoin {
_, isFound := k.bankKeeper.GetDenomMetaData(ctx, bankDenom)
if isFound {
return funtoken, fmt.Errorf("bank coin denom already registered with denom \"%s\"", bankDenom)
}
if funtokens := k.FunTokens.Collect(
ctx, k.FunTokens.Indexes.BankDenom.ExactMatch(ctx, bankDenom),
); len(funtokens) > 0 {

if funtokens := k.FunTokens.Collect(ctx, k.FunTokens.Indexes.BankDenom.ExactMatch(ctx, bankDenom)); len(funtokens) > 0 {
return funtoken, fmt.Errorf("funtoken mapping already created for bank denom \"%s\"", bankDenom)
}

Expand All @@ -142,7 +138,7 @@

err = bankMetadata.Validate()
if err != nil {
return
return funtoken, fmt.Errorf("failed to validate bank metadata: %w", err)

Check warning on line 141 in x/evm/keeper/funtoken_from_erc20.go

View check run for this annotation

Codecov / codecov/patch

x/evm/keeper/funtoken_from_erc20.go#L141

Added line #L141 was not covered by tests
}
k.bankKeeper.SetDenomMetaData(ctx, bankMetadata)

Expand Down
71 changes: 25 additions & 46 deletions x/evm/precompile/funtoken.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,25 +13,19 @@
"github.com/ethereum/go-ethereum/core/vm"

"github.com/NibiruChain/nibiru/v2/app/keepers"
"github.com/NibiruChain/nibiru/v2/eth"
"github.com/NibiruChain/nibiru/v2/x/evm"
"github.com/NibiruChain/nibiru/v2/x/evm/embeds"
)

var (
_ vm.PrecompiledContract = (*precompileFunToken)(nil)
_ NibiruPrecompile = (*precompileFunToken)(nil)
)
var _ vm.PrecompiledContract = (*precompileFunToken)(nil)

// Precompile address for "FunToken.sol", the contract that
// enables transfers of ERC20 tokens to "nibi" addresses as bank coins
// using the ERC20's `FunToken` mapping.
var PrecompileAddr_FuntokenGateway = eth.MustNewHexAddrFromStr(
"0x0000000000000000000000000000000000000800",
)
var PrecompileAddr_FuntokenGateway = gethcommon.HexToAddress("0x0000000000000000000000000000000000000800")

func (p precompileFunToken) Address() gethcommon.Address {
return PrecompileAddr_FuntokenGateway.ToAddr()
return PrecompileAddr_FuntokenGateway
}

func (p precompileFunToken) RequiredGas(input []byte) (gasPrice uint64) {
Expand Down Expand Up @@ -62,18 +56,16 @@
}
}()

contractInput := contract.Input
ctx, method, args, err := OnRunStart(p, evm, contractInput)
ctx, method, args, err := OnRunStart(embeds.SmartContract_FunToken.ABI, evm, contract.Input)
if err != nil {
return nil, err
}

caller := contract.CallerAddress
switch FunTokenMethod(method.Name) {
case FunTokenMethod_BankSend:
// TODO: UD-DEBUG: Test that calling non-method on the right address does
// nothing.
bz, err = p.bankSend(ctx, caller, method, args, readonly)
bz, err = p.bankSend(ctx, contract.CallerAddress, method, args, readonly)
default:
// TODO: UD-DEBUG: test invalid method called
err = fmt.Errorf("invalid method called with name \"%s\"", method.Name)
Expand All @@ -88,13 +80,8 @@
}
}

func (p precompileFunToken) ABI() *gethabi.ABI {
return embeds.SmartContract_FunToken.ABI
}

type precompileFunToken struct {
keepers.PublicKeepers
NibiruPrecompile
}

var executionGuard sync.Mutex
Expand All @@ -120,8 +107,7 @@
) (bz []byte, err error) {
if readOnly {
// Check required for transactions but not needed for queries
err = fmt.Errorf("cannot write state from staticcall (a read-only call)")
return
return nil, fmt.Errorf("cannot write state from staticcall (a read-only call)")

Check warning on line 110 in x/evm/precompile/funtoken.go

View check run for this annotation

Codecov / codecov/patch

x/evm/precompile/funtoken.go#L110

Added line #L110 was not covered by tests
}
if !executionGuard.TryLock() {
return nil, fmt.Errorf("bankSend is already in progress")
Expand Down Expand Up @@ -188,8 +174,7 @@
// Since we're sending them away and want accurate total supply tracking, the
// tokens need to be burned.
if funtoken.IsMadeFromCoin {
caller := evm.EVM_MODULE_ADDRESS
_, err = p.EvmKeeper.ERC20().Burn(erc20, caller, amount, ctx)
_, err = p.EvmKeeper.ERC20().Burn(erc20, evm.EVM_MODULE_ADDRESS, amount, ctx)
if err != nil {
err = fmt.Errorf("ERC20.Burn: %w", err)
return
Expand All @@ -202,40 +187,34 @@
return method.Outputs.Pack() // TODO: change interface
}

// ArgsFunTokenBankSend: Constructor for an "args" array of arguments for the
// "IFunToken.bankSend" function.
func ArgsFunTokenBankSend(
erc20 gethcommon.Address,
amount *big.Int,
to sdk.AccAddress,
) []any {
return []any{erc20, amount, to.String()}
}

func (p precompileFunToken) AssertArgTypesBankSend(args []any) (
erc20 gethcommon.Address,
amount *big.Int,
to string,
err error,
) {
err = AssertArgCount(args, 3)
if err != nil {
if len(args) != 3 {
err = fmt.Errorf("expected 3 arguments but got %d", len(args))

Check warning on line 197 in x/evm/precompile/funtoken.go

View check run for this annotation

Codecov / codecov/patch

x/evm/precompile/funtoken.go#L197

Added line #L197 was not covered by tests
return
}

erc20, ok1 := args[0].(gethcommon.Address)
amount, ok2 := args[1].(*big.Int)
to, ok3 := args[2].(string)
if !(ok1 && ok2 && ok3) {
err = fmt.Errorf("type validation for failed for \"%s\"",
"function bankSend(address erc20, uint256 amount, string memory to) external")
erc20, ok := args[0].(gethcommon.Address)
if !ok {
err = fmt.Errorf("type validation for failed for (address erc20) argument")
return

Check warning on line 204 in x/evm/precompile/funtoken.go

View check run for this annotation

Codecov / codecov/patch

x/evm/precompile/funtoken.go#L203-L204

Added lines #L203 - L204 were not covered by tests
}
return
}

func AssertArgCount(args []interface{}, wantNumArgs int) error {
if len(args) != wantNumArgs {
return fmt.Errorf("expected %d arguments but got %d", wantNumArgs, len(args))
amount, ok = args[1].(*big.Int)
if !ok {
err = fmt.Errorf("type validation for failed for (uint256 amount) argument")
return

Check warning on line 210 in x/evm/precompile/funtoken.go

View check run for this annotation

Codecov / codecov/patch

x/evm/precompile/funtoken.go#L209-L210

Added lines #L209 - L210 were not covered by tests
}
return nil

to, ok = args[2].(string)
if !ok {
err = fmt.Errorf("type validation for failed for (string to) argument")
return

Check warning on line 216 in x/evm/precompile/funtoken.go

View check run for this annotation

Codecov / codecov/patch

x/evm/precompile/funtoken.go#L215-L216

Added lines #L215 - L216 were not covered by tests
}

return
}
11 changes: 5 additions & 6 deletions x/evm/precompile/funtoken_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ func (s *Suite) FunToken_PrecompileExists() {
s.NoError(err)
s.Equal(string(codeResp.Code), "")

s.True(deps.EvmKeeper.IsAvailablePrecompile(precompileAddr.ToAddr()),
s.True(deps.EvmKeeper.IsAvailablePrecompile(precompileAddr),
"did not see precompile address during \"InitPrecompiles\"")

callArgs := []any{"nonsense", "args here", "to see if", "precompile is", "called"}
Expand All @@ -59,11 +59,10 @@ func (s *Suite) FunToken_PrecompileExists() {
"callArgs: ", callArgs)

fromEvmAddr := evm.EVM_MODULE_ADDRESS
contractAddr := precompileAddr.ToAddr()
commit := true
bytecodeForCall := packedArgs
_, err = deps.EvmKeeper.CallContractWithInput(
deps.Ctx, fromEvmAddr, &contractAddr, commit,
deps.Ctx, fromEvmAddr, &precompileAddr, commit,
bytecodeForCall,
)
s.ErrorContains(err, "precompile error")
Expand All @@ -77,7 +76,7 @@ func (s *Suite) FunToken_HappyPath() {
theUser := deps.Sender.EthAddr
theEvm := evm.EVM_MODULE_ADDRESS

s.True(deps.EvmKeeper.IsAvailablePrecompile(precompileAddr.ToAddr()),
s.True(deps.EvmKeeper.IsAvailablePrecompile(precompileAddr),
"did not see precompile address during \"InitPrecompiles\"")

s.T().Log("Create FunToken mapping and ERC20")
Expand Down Expand Up @@ -128,13 +127,13 @@ func (s *Suite) FunToken_HappyPath() {

s.T().Log("Send using precompile")
amtToSend := int64(419)
callArgs := precompile.ArgsFunTokenBankSend(contract, big.NewInt(amtToSend), randomAcc)
callArgs := []any{contract, big.NewInt(amtToSend), randomAcc.String()}
methodName := string(precompile.FunTokenMethod_BankSend)
input, err := abi.Pack(methodName, callArgs...)
s.NoError(err)

from := theUser
_, err = evmtest.DoEthTx(&deps, precompileAddr.ToAddr(), from, input)
_, err = evmtest.DoEthTx(&deps, precompileAddr, from, input)
s.Require().NoError(err)

evmtest.AssertERC20BalanceEqual(s.T(), deps, contract, theUser, big.NewInt(69_419-amtToSend))
Expand Down
Loading
Loading