diff --git a/x/evm/keeper/erc20.go b/x/evm/keeper/erc20.go index ef8140f92..904937ae4 100644 --- a/x/evm/keeper/erc20.go +++ b/x/evm/keeper/erc20.go @@ -2,6 +2,7 @@ package keeper import ( + "bytes" "fmt" "math" "math/big" @@ -191,13 +192,23 @@ func (k Keeper) LoadERC20String( } erc20Val := new(ERC20String) - err = erc20Abi.UnpackIntoInterface( + if err := erc20Abi.UnpackIntoInterface( erc20Val, methodName, res.Ret, - ) - if err != nil { - return out, err + ); err == nil { + return erc20Val.Value, err + } + + erc20Bytes32Val := new(ERC20Bytes32) + if err := erc20Abi.UnpackIntoInterface(erc20Bytes32Val, methodName, res.Ret); err == nil { + + return bytes32ToString(erc20Bytes32Val.Value), nil } - return erc20Val.Value, err + + return "", fmt.Errorf("failed to decode response for method %s; unable to unpack as string or bytes32", methodName) +} + +func bytes32ToString(b [32]byte) string { + return string(bytes.Trim(b[:], "\x00")) } func (k Keeper) loadERC20Uint8( @@ -219,13 +230,22 @@ func (k Keeper) loadERC20Uint8( } erc20Val := new(ERC20Uint8) - err = erc20Abi.UnpackIntoInterface( + if err := erc20Abi.UnpackIntoInterface( erc20Val, methodName, res.Ret, - ) - if err != nil { - return out, err + ); err == nil { + return erc20Val.Value, err } - return erc20Val.Value, err + + erc20Uint256Val := new(ERC20BigInt) + if err := erc20Abi.UnpackIntoInterface( + erc20Uint256Val, methodName, res.Ret, + ); err == nil { + // We can safely cast to uint8 because it's nonsense for decimals to be larger than 255 + return uint8(erc20Uint256Val.Value.Uint64()), err + } + + return 0, fmt.Errorf("failed to decode response for method %s; unable to unpack as uint8 or uint256", methodName) + } func (k Keeper) LoadERC20BigInt( diff --git a/x/evm/keeper/funtoken_from_coin_test.go b/x/evm/keeper/funtoken_from_coin_test.go index f790ca896..7d408de32 100644 --- a/x/evm/keeper/funtoken_from_coin_test.go +++ b/x/evm/keeper/funtoken_from_coin_test.go @@ -27,7 +27,7 @@ func (s *FunTokenFromCoinSuite) TestCreateFunTokenFromCoin() { // Compute contract address. FindERC20 should fail nonce := deps.NewStateDB().GetNonce(deps.Sender.EthAddr) contractAddress := crypto.CreateAddress(deps.Sender.EthAddr, nonce) - metadata, err := deps.EvmKeeper.FindERC20Metadata(deps.Ctx, contractAddress) + metadata, err := deps.EvmKeeper.FindERC20Metadata(deps.Ctx, contractAddress, nil) s.Require().Error(err) s.Require().Nil(metadata) @@ -109,7 +109,7 @@ func (s *FunTokenFromCoinSuite) TestCreateFunTokenFromCoin() { s.Require().NoError(err) s.T().Log("Expect ERC20 metadata on contract") - info, err := deps.EvmKeeper.FindERC20Metadata(deps.Ctx, erc20Addr.Address) + info, err := deps.EvmKeeper.FindERC20Metadata(deps.Ctx, erc20Addr.Address, nil) s.Require().NoError(err, info) s.Equal( keeper.ERC20Metadata{ diff --git a/x/evm/keeper/funtoken_from_erc20.go b/x/evm/keeper/funtoken_from_erc20.go index f1fa3b0b2..e43e7279b 100644 --- a/x/evm/keeper/funtoken_from_erc20.go +++ b/x/evm/keeper/funtoken_from_erc20.go @@ -8,6 +8,7 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" bank "github.com/cosmos/cosmos-sdk/x/bank/types" gethcommon "github.com/ethereum/go-ethereum/common" + gethabi "github.com/ethereum/go-ethereum/accounts/abi" "github.com/NibiruChain/nibiru/v2/eth" "github.com/NibiruChain/nibiru/v2/x/evm" @@ -26,19 +27,26 @@ import ( func (k Keeper) FindERC20Metadata( ctx sdk.Context, contract gethcommon.Address, + abi *gethabi.ABI, ) (info *ERC20Metadata, err error) { + + effectiveAbi := embeds.SmartContract_ERC20Minter.ABI + + if abi != nil { + effectiveAbi = abi + } // Load name, symbol, decimals - name, err := k.LoadERC20Name(ctx, embeds.SmartContract_ERC20Minter.ABI, contract) + name, err := k.LoadERC20Name(ctx, effectiveAbi, contract) if err != nil { return nil, err } - symbol, err := k.LoadERC20Symbol(ctx, embeds.SmartContract_ERC20Minter.ABI, contract) + symbol, err := k.LoadERC20Symbol(ctx, effectiveAbi, contract) if err != nil { return nil, err } - decimals, err := k.LoadERC20Decimals(ctx, embeds.SmartContract_ERC20Minter.ABI, contract) + decimals, err := k.LoadERC20Decimals(ctx, effectiveAbi, contract) if err != nil { return nil, err } @@ -80,6 +88,7 @@ type ( ERC20Bool struct{ Value bool } // ERC20BigInt: Unpacking type for "uint256" from Solidity. ERC20BigInt struct{ Value *big.Int } + ERC20Bytes32 struct{ Value [32]byte } ) // createFunTokenFromERC20 creates a new FunToken mapping from an existing ERC20 token. @@ -114,7 +123,7 @@ func (k *Keeper) createFunTokenFromERC20( } // 2 | Get existing ERC20 metadata - erc20Info, err := k.FindERC20Metadata(ctx, erc20) + erc20Info, err := k.FindERC20Metadata(ctx, erc20, nil) if err != nil { return funtoken, err } diff --git a/x/evm/keeper/funtoken_from_erc20_test.go b/x/evm/keeper/funtoken_from_erc20_test.go index cc5b47d0a..07c89129e 100644 --- a/x/evm/keeper/funtoken_from_erc20_test.go +++ b/x/evm/keeper/funtoken_from_erc20_test.go @@ -2,6 +2,7 @@ package keeper_test import ( + "encoding/hex" "fmt" "math/big" "testing" @@ -26,7 +27,7 @@ func (s *FunTokenFromErc20Suite) TestCreateFunTokenFromERC20() { // assert that the ERC20 contract is not deployed expectedERC20Addr := crypto.CreateAddress(deps.Sender.EthAddr, deps.NewStateDB().GetNonce(deps.Sender.EthAddr)) - _, err := deps.EvmKeeper.FindERC20Metadata(deps.Ctx, expectedERC20Addr) + _, err := deps.EvmKeeper.FindERC20Metadata(deps.Ctx, expectedERC20Addr, nil) s.Error(err) s.T().Log("Deploy ERC20") @@ -42,7 +43,7 @@ func (s *FunTokenFromErc20Suite) TestCreateFunTokenFromERC20() { s.Require().NoError(err) s.Require().Equal(expectedERC20Addr, deployResp.ContractAddr) - info, err := deps.EvmKeeper.FindERC20Metadata(deps.Ctx, deployResp.ContractAddr) + info, err := deps.EvmKeeper.FindERC20Metadata(deps.Ctx, deployResp.ContractAddr, nil) s.Require().NoError(err) s.Require().Equal(metadata, *info) @@ -535,6 +536,61 @@ func (s *FunTokenFromErc20Suite) TestSendERC20WithFee() { s.Require().True(deps.App.BankKeeper.GetBalance(deps.Ctx, evm.EVM_MODULE_ADDRESS_NIBI, bankDemon).Amount.Equal(sdk.NewInt(0))) } +type MkrMetadata struct { + Symbol [32]byte +} + +func (s *FunTokenFromErc20Suite) TestMuahaha() { + deps := evmtest.NewTestDeps() + + s.T().Log("Deploy MKR") + + byteSlice, err := hex.DecodeString("4d4b520000000000000000000000000000000000000000000000000000000000") + s.Require().NoError(err) + var byteArray [32]byte + copy(byteArray[:], byteSlice) + + metadata := MkrMetadata{ + Symbol: byteArray, + } + deployResp, err := evmtest.DeployContract( + &deps, embeds.SmartContract_TestBytes32Metadata, + metadata.Symbol, + ) + s.Require().NoError(err) + + s.T().Log("set name") + + deps.ResetGasMeter() + + byteSlice, err = hex.DecodeString("4d616b6572000000000000000000000000000000000000000000000000000000") + s.Require().NoError(err) + copy(byteArray[:], byteSlice) + + _, err = deps.EvmKeeper.CallContract( + deps.Ctx, + embeds.SmartContract_TestBytes32Metadata.ABI, + deps.Sender.EthAddr, + &deployResp.ContractAddr, + true, + evmtest.FunTokenGasLimitSendToEvm, + "setName", + byteArray, + ) + + s.Require().NoError(err) + + info, err := deps.EvmKeeper.FindERC20Metadata(deps.Ctx, deployResp.ContractAddr, embeds.SmartContract_TestBytes32Metadata.ABI) + s.Require().NoError(err) + + actualMetadata := keeper.ERC20Metadata{ + Name: "Maker", + Symbol: "MKR", + Decimals: 18, + } + s.Require().Equal(actualMetadata, *info) +} + type FunTokenFromErc20Suite struct { suite.Suite }