mirror of
https://github.com/0glabs/0g-chain.git
synced 2024-12-26 00:05:18 +00:00
feat(evmutil): track deployed contracts in state (#1598)
* feat(evmutil): track deployed contracts in state * docs(evmutil): update state spec * update changelog
This commit is contained in:
parent
8495619130
commit
6585ac24b0
@ -36,9 +36,10 @@ Ref: https://keepachangelog.com/en/1.0.0/
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
## Features
|
### Features
|
||||||
- (evmutil) [#1590] & [#1596] Add allow list param of sdk native denoms that can be transferred to evm
|
- (evmutil) [#1590] & [#1596] Add allow list param of sdk native denoms that can be transferred to evm
|
||||||
- (evmutil) [#1591] & [#1596] Configure module to support deploying ERC20KavaWrappedCosmosCoin contracts
|
- (evmutil) [#1591] & [#1596] Configure module to support deploying ERC20KavaWrappedCosmosCoin contracts
|
||||||
|
- (evmutil) [#1598] Track deployed ERC20 contract addresses for representing cosmos coins in module state
|
||||||
|
|
||||||
## [v0.23.0]
|
## [v0.23.0]
|
||||||
|
|
||||||
@ -241,6 +242,7 @@ the [changelog](https://github.com/cosmos/cosmos-sdk/blob/v0.38.4/CHANGELOG.md).
|
|||||||
- [#257](https://github.com/Kava-Labs/kava/pulls/257) Include scripts to run
|
- [#257](https://github.com/Kava-Labs/kava/pulls/257) Include scripts to run
|
||||||
large-scale simulations remotely using aws-batch
|
large-scale simulations remotely using aws-batch
|
||||||
|
|
||||||
|
[#1598]: https://github.com/Kava-Labs/kava/pull/1598
|
||||||
[#1596]: https://github.com/Kava-Labs/kava/pull/1596
|
[#1596]: https://github.com/Kava-Labs/kava/pull/1596
|
||||||
[#1591]: https://github.com/Kava-Labs/kava/pull/1591
|
[#1591]: https://github.com/Kava-Labs/kava/pull/1591
|
||||||
[#1590]: https://github.com/Kava-Labs/kava/pull/1590
|
[#1590]: https://github.com/Kava-Labs/kava/pull/1590
|
||||||
|
@ -110,6 +110,32 @@ func (k Keeper) DeployKavaWrappedCosmosCoinERC20Contract(
|
|||||||
return types.NewInternalEVMAddress(contractAddr), nil
|
return types.NewInternalEVMAddress(contractAddr), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetOrDeployCosmosCoinERC20Contract checks the module store for a deployed contract for the given
|
||||||
|
// token info and returns it if preset. Otherwise, it deploys and registers the contract.
|
||||||
|
func (k *Keeper) GetOrDeployCosmosCoinERC20Contract(
|
||||||
|
ctx sdk.Context,
|
||||||
|
tokenInfo types.AllowedCosmosCoinERC20Token,
|
||||||
|
) (types.InternalEVMAddress, error) {
|
||||||
|
contractAddress, found := k.GetDeployedCosmosCoinContract(ctx, tokenInfo.CosmosDenom)
|
||||||
|
if found {
|
||||||
|
// contract has already been deployed
|
||||||
|
return contractAddress, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// deploy a new contract
|
||||||
|
contractAddress, err := k.DeployKavaWrappedCosmosCoinERC20Contract(ctx, tokenInfo)
|
||||||
|
if err != nil {
|
||||||
|
return contractAddress, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// register the contract to the module store
|
||||||
|
err = k.SetDeployedCosmosCoinContract(ctx, tokenInfo.CosmosDenom, contractAddress)
|
||||||
|
|
||||||
|
// TODO: emit event that contract was deployed
|
||||||
|
|
||||||
|
return contractAddress, err
|
||||||
|
}
|
||||||
|
|
||||||
// MintERC20 mints the given amount of an ERC20 token to an address. This is
|
// MintERC20 mints the given amount of an ERC20 token to an address. This is
|
||||||
// unchecked and should only be called after permission and enabled ERC20 checks.
|
// unchecked and should only be called after permission and enabled ERC20 checks.
|
||||||
func (k Keeper) MintERC20(
|
func (k Keeper) MintERC20(
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
|
|
||||||
|
"github.com/kava-labs/kava/app"
|
||||||
"github.com/kava-labs/kava/x/evmutil/testutil"
|
"github.com/kava-labs/kava/x/evmutil/testutil"
|
||||||
"github.com/kava-labs/kava/x/evmutil/types"
|
"github.com/kava-labs/kava/x/evmutil/types"
|
||||||
)
|
)
|
||||||
@ -145,3 +146,66 @@ func (suite *ERC20TestSuite) TestDeployKavaWrappedCosmosCoinERC20Contract() {
|
|||||||
suite.ErrorContains(err, "Ownable: caller is not the owner")
|
suite.ErrorContains(err, "Ownable: caller is not the owner")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (suite *ERC20TestSuite) TestGetOrDeployCosmosCoinERC20Contract() {
|
||||||
|
suite.Run("finds existing contract address", func() {
|
||||||
|
suite.SetupTest()
|
||||||
|
denom := "magic"
|
||||||
|
addr := types.BytesToInternalEVMAddress(app.RandomAddress().Bytes())
|
||||||
|
// pretend like we've registered a contract in a previous life
|
||||||
|
err := suite.Keeper.SetDeployedCosmosCoinContract(suite.Ctx, denom, addr)
|
||||||
|
suite.NoError(err)
|
||||||
|
|
||||||
|
// expect it to find the registered address
|
||||||
|
tokenInfo := types.AllowedCosmosCoinERC20Token{CosmosDenom: denom}
|
||||||
|
contractAddress, err := suite.Keeper.GetOrDeployCosmosCoinERC20Contract(suite.Ctx, tokenInfo)
|
||||||
|
suite.NoError(err)
|
||||||
|
suite.Equal(addr, contractAddress)
|
||||||
|
|
||||||
|
// expect it to still be registered
|
||||||
|
contractAddress, found := suite.Keeper.GetDeployedCosmosCoinContract(suite.Ctx, denom)
|
||||||
|
suite.True(found)
|
||||||
|
suite.Equal(addr, contractAddress)
|
||||||
|
})
|
||||||
|
|
||||||
|
suite.Run("deploys & registers contract when one does not exist", func() {
|
||||||
|
suite.SetupTest()
|
||||||
|
denom := "magic"
|
||||||
|
tokenInfo := types.NewAllowedCosmosCoinERC20Token(denom, "Magic Coin", "MAGIC", 6)
|
||||||
|
|
||||||
|
// expect it to not be registered
|
||||||
|
_, found := suite.Keeper.GetDeployedCosmosCoinContract(suite.Ctx, denom)
|
||||||
|
suite.False(found)
|
||||||
|
|
||||||
|
// deploy the contract
|
||||||
|
contractAddress, err := suite.Keeper.GetOrDeployCosmosCoinERC20Contract(suite.Ctx, tokenInfo)
|
||||||
|
suite.NoError(err)
|
||||||
|
|
||||||
|
// expect it to be registered now
|
||||||
|
registeredAddress, found := suite.Keeper.GetDeployedCosmosCoinContract(suite.Ctx, denom)
|
||||||
|
suite.True(found)
|
||||||
|
suite.False(registeredAddress.IsNil())
|
||||||
|
suite.Equal(contractAddress, registeredAddress)
|
||||||
|
})
|
||||||
|
|
||||||
|
// this can only happen if governance passes a bad allowed token
|
||||||
|
suite.Run("fails when token can't be deployed", func() {
|
||||||
|
suite.SetupTest()
|
||||||
|
denom := "nope"
|
||||||
|
// empty other fields means this token is invalid.
|
||||||
|
invalidToken := types.AllowedCosmosCoinERC20Token{CosmosDenom: denom}
|
||||||
|
|
||||||
|
// expect it to not be registered
|
||||||
|
_, found := suite.Keeper.GetDeployedCosmosCoinContract(suite.Ctx, denom)
|
||||||
|
suite.False(found)
|
||||||
|
|
||||||
|
// attempt to deploy the contract
|
||||||
|
contractAddress, err := suite.Keeper.GetOrDeployCosmosCoinERC20Contract(suite.Ctx, invalidToken)
|
||||||
|
suite.ErrorContains(err, "failed to deploy erc20")
|
||||||
|
suite.True(contractAddress.IsNil())
|
||||||
|
|
||||||
|
// still expect it to not be registered
|
||||||
|
_, found = suite.Keeper.GetDeployedCosmosCoinContract(suite.Ctx, denom)
|
||||||
|
suite.False(found)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
@ -183,3 +183,32 @@ func (k Keeper) RemoveBalance(ctx sdk.Context, addr sdk.AccAddress, amt sdkmath.
|
|||||||
}
|
}
|
||||||
return k.SetBalance(ctx, addr, finalBal)
|
return k.SetBalance(ctx, addr, finalBal)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetDeployedCosmosCoinContract stores a single deployed ERC20KavaWrappedCosmosCoin contract address
|
||||||
|
func (k *Keeper) SetDeployedCosmosCoinContract(ctx sdk.Context, cosmosDenom string, contractAddress types.InternalEVMAddress) error {
|
||||||
|
if err := sdk.ValidateDenom(cosmosDenom); err != nil {
|
||||||
|
return errorsmod.Wrap(types.ErrInvalidCosmosDenom, cosmosDenom)
|
||||||
|
}
|
||||||
|
if contractAddress.IsNil() {
|
||||||
|
return errorsmod.Wrapf(
|
||||||
|
sdkerrors.ErrInvalidAddress,
|
||||||
|
"attempting to register empty contract address for denom '%s'",
|
||||||
|
cosmosDenom,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
store := ctx.KVStore(k.storeKey)
|
||||||
|
storeKey := types.DeployedCosmosCoinContractKey(cosmosDenom)
|
||||||
|
|
||||||
|
store.Set(storeKey, contractAddress.Bytes())
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetDeployedCosmosCoinContract gets a deployed ERC20KavaWrappedCosmosCoin contract address by cosmos denom
|
||||||
|
// Returns the stored address and a bool indicating if it was found or not
|
||||||
|
func (k *Keeper) GetDeployedCosmosCoinContract(ctx sdk.Context, cosmosDenom string) (types.InternalEVMAddress, bool) {
|
||||||
|
store := ctx.KVStore(k.storeKey)
|
||||||
|
storeKey := types.DeployedCosmosCoinContractKey(cosmosDenom)
|
||||||
|
bz := store.Get(storeKey)
|
||||||
|
found := len(bz) != 0
|
||||||
|
return types.BytesToInternalEVMAddress(bz), found
|
||||||
|
}
|
||||||
|
@ -355,6 +355,42 @@ func (suite *keeperTestSuite) TestGetBalance() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (suite *keeperTestSuite) TestDeployedCosmosCoinContractStoreState() {
|
||||||
|
suite.Run("returns nil for nonexistent denom", func() {
|
||||||
|
suite.SetupTest()
|
||||||
|
addr, found := suite.Keeper.GetDeployedCosmosCoinContract(suite.Ctx, "undeployed-denom")
|
||||||
|
suite.False(found)
|
||||||
|
suite.Equal(addr, types.InternalEVMAddress{})
|
||||||
|
})
|
||||||
|
|
||||||
|
suite.Run("handles setting & getting a contract address", func() {
|
||||||
|
suite.SetupTest()
|
||||||
|
denom := "ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2"
|
||||||
|
address := testutil.RandomInternalEVMAddress()
|
||||||
|
|
||||||
|
err := suite.Keeper.SetDeployedCosmosCoinContract(suite.Ctx, denom, address)
|
||||||
|
suite.NoError(err)
|
||||||
|
|
||||||
|
stored, found := suite.Keeper.GetDeployedCosmosCoinContract(suite.Ctx, denom)
|
||||||
|
suite.True(found)
|
||||||
|
suite.Equal(address, stored)
|
||||||
|
})
|
||||||
|
|
||||||
|
suite.Run("fails when setting an invalid denom", func() {
|
||||||
|
suite.SetupTest()
|
||||||
|
invalidDenom := ""
|
||||||
|
err := suite.Keeper.SetDeployedCosmosCoinContract(suite.Ctx, invalidDenom, testutil.RandomInternalEVMAddress())
|
||||||
|
suite.ErrorContains(err, "invalid cosmos denom")
|
||||||
|
})
|
||||||
|
|
||||||
|
suite.Run("fails when setting 0 address", func() {
|
||||||
|
suite.SetupTest()
|
||||||
|
invalidAddr := types.InternalEVMAddress{}
|
||||||
|
err := suite.Keeper.SetDeployedCosmosCoinContract(suite.Ctx, "denom", invalidAddr)
|
||||||
|
suite.ErrorContains(err, "attempting to register empty contract address")
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func TestKeeperTestSuite(t *testing.T) {
|
func TestKeeperTestSuite(t *testing.T) {
|
||||||
suite.Run(t, new(keeperTestSuite))
|
suite.Run(t, new(keeperTestSuite))
|
||||||
}
|
}
|
||||||
|
@ -114,8 +114,8 @@ func (s msgServer) ConvertERC20ToCoin(
|
|||||||
// ConvertCosmosCoinToERC20 converts a native sdk.Coin to an ERC20.
|
// ConvertCosmosCoinToERC20 converts a native sdk.Coin to an ERC20.
|
||||||
// If no ERC20 contract has been deployed for the given denom, a new
|
// If no ERC20 contract has been deployed for the given denom, a new
|
||||||
// contract will be deployed and registered to the module.
|
// contract will be deployed and registered to the module.
|
||||||
func (msgServer) ConvertCosmosCoinToERC20(
|
func (s msgServer) ConvertCosmosCoinToERC20(
|
||||||
ctx context.Context,
|
goCtx context.Context,
|
||||||
msg *types.MsgConvertCosmosCoinToERC20,
|
msg *types.MsgConvertCosmosCoinToERC20,
|
||||||
) (*types.MsgConvertCosmosCoinToERC20Response, error) {
|
) (*types.MsgConvertCosmosCoinToERC20Response, error) {
|
||||||
return nil, fmt.Errorf("unimplemented - coming soon")
|
return nil, fmt.Errorf("unimplemented - coming soon")
|
||||||
|
@ -4,13 +4,16 @@ import (
|
|||||||
"math/big"
|
"math/big"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/suite"
|
||||||
|
|
||||||
sdkmath "cosmossdk.io/math"
|
sdkmath "cosmossdk.io/math"
|
||||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common/math"
|
"github.com/ethereum/go-ethereum/common/math"
|
||||||
|
|
||||||
"github.com/kava-labs/kava/x/evmutil/keeper"
|
"github.com/kava-labs/kava/x/evmutil/keeper"
|
||||||
"github.com/kava-labs/kava/x/evmutil/testutil"
|
"github.com/kava-labs/kava/x/evmutil/testutil"
|
||||||
"github.com/kava-labs/kava/x/evmutil/types"
|
"github.com/kava-labs/kava/x/evmutil/types"
|
||||||
"github.com/stretchr/testify/suite"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type MsgServerSuite struct {
|
type MsgServerSuite struct {
|
||||||
|
@ -20,6 +20,18 @@ func (k Keeper) SetParams(ctx sdk.Context, params types.Params) {
|
|||||||
k.paramSubspace.SetParamSet(ctx, ¶ms)
|
k.paramSubspace.SetParamSet(ctx, ¶ms)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetAllowedTokenMetadata gets the token metadata for the given cosmosDenom if it is allowed.
|
||||||
|
// Returns the metadata if allowed, and a bool indicating if the denom was in the allow list or not.
|
||||||
|
func (k Keeper) GetAllowedTokenMetadata(ctx sdk.Context, cosmosDenom string) (types.AllowedCosmosCoinERC20Token, bool) {
|
||||||
|
params := k.GetParams(ctx)
|
||||||
|
for _, token := range params.AllowedCosmosDenoms {
|
||||||
|
if token.CosmosDenom == cosmosDenom {
|
||||||
|
return token, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return types.AllowedCosmosCoinERC20Token{}, false
|
||||||
|
}
|
||||||
|
|
||||||
// GetEnabledConversionPairFromERC20Address returns an ConversionPair from the internal contract address.
|
// GetEnabledConversionPairFromERC20Address returns an ConversionPair from the internal contract address.
|
||||||
func (k Keeper) GetEnabledConversionPairFromERC20Address(
|
func (k Keeper) GetEnabledConversionPairFromERC20Address(
|
||||||
ctx sdk.Context,
|
ctx sdk.Context,
|
||||||
|
@ -60,3 +60,30 @@ func (suite *ParamsTestSuite) TestHistoricParamsQuery() {
|
|||||||
_ = oldStateKeeper.GetParams(suite.Ctx)
|
_ = oldStateKeeper.GetParams(suite.Ctx)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (suite *keeperTestSuite) TestGetAllowedTokenMetadata() {
|
||||||
|
suite.SetupTest()
|
||||||
|
|
||||||
|
atom := types.NewAllowedCosmosCoinERC20Token(
|
||||||
|
"ibc/27394FB092D2ECCD56123C74F36E4C1F926001CEADA9CA97EA622B25F41E5EB2",
|
||||||
|
"Kava EVM ATOM", "ATOM", 6,
|
||||||
|
)
|
||||||
|
hard := types.NewAllowedCosmosCoinERC20Token("hard", "Kava EVM Hard", "HARD", 6)
|
||||||
|
|
||||||
|
// init state with some allowed tokens
|
||||||
|
params := suite.Keeper.GetParams(suite.Ctx)
|
||||||
|
params.AllowedCosmosDenoms = types.NewAllowedCosmosCoinERC20Tokens(atom, hard)
|
||||||
|
suite.Keeper.SetParams(suite.Ctx, params)
|
||||||
|
|
||||||
|
// finds allowed tokens by denom
|
||||||
|
storedAtom, allowed := suite.Keeper.GetAllowedTokenMetadata(suite.Ctx, atom.CosmosDenom)
|
||||||
|
suite.True(allowed)
|
||||||
|
suite.Equal(atom, storedAtom)
|
||||||
|
storedHard, allowed := suite.Keeper.GetAllowedTokenMetadata(suite.Ctx, hard.CosmosDenom)
|
||||||
|
suite.True(allowed)
|
||||||
|
suite.Equal(hard, storedHard)
|
||||||
|
|
||||||
|
// returns not-allowed when token not allowed
|
||||||
|
_, allowed = suite.Keeper.GetAllowedTokenMetadata(suite.Ctx, "not-in-list")
|
||||||
|
suite.False(allowed)
|
||||||
|
}
|
||||||
|
@ -45,7 +45,6 @@ message AllowedCosmosCoinERC20Token {
|
|||||||
// Number of decimals ERC20 contract is deployed with.
|
// Number of decimals ERC20 contract is deployed with.
|
||||||
uint32 decimals = 4;
|
uint32 decimals = 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
`GenesisState` defines the state that must be persisted when the blockchain stops/restarts in order for normal function of the evmutil module to resume.
|
`GenesisState` defines the state that must be persisted when the blockchain stops/restarts in order for normal function of the evmutil module to resume.
|
||||||
@ -70,6 +69,17 @@ message Account {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Deployed Cosmos Coin Contract Addresses
|
||||||
|
|
||||||
|
Addresses for the ERC20 contracts representing cosmos-sdk `Coin`s are kept in the module store. They are stored as bytes by the cosmos-sdk denom they represent.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
If a contract for representing the cosmos-sdk denom `cow` as an ERC20 in the EVM is deployed by the module to the address `0xbeef00000000000000000000000000000000beef`, the module store will contain:
|
||||||
|
|
||||||
|
`0x01 | bytes("cow") => bytes(0xbeef00000000000000000000000000000000beef)`
|
||||||
|
|
||||||
|
Where `0x01` is the `DeployedCosmosCoinContractKeyPrefix` defined in [keys.go](../types/keys.go).
|
||||||
|
|
||||||
## Store
|
## Store
|
||||||
|
|
||||||
For complete implementation details for how items are stored, see [keys.go](../types/keys.go). `x/evmutil` store state consists of accounts.
|
For complete implementation details for how items are stored, see [keys.go](../types/keys.go). `x/evmutil` store state consists of accounts and deployed contract addresses.
|
||||||
|
@ -180,6 +180,10 @@ func (suite *Suite) Commit() {
|
|||||||
suite.Ctx = suite.App.NewContext(false, header)
|
suite.Ctx = suite.App.NewContext(false, header)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (suite *Suite) ModuleBalance(denom string) sdk.Int {
|
||||||
|
return suite.App.GetModuleAccountBalance(suite.Ctx, types.ModuleName, denom)
|
||||||
|
}
|
||||||
|
|
||||||
func (suite *Suite) FundAccountWithKava(addr sdk.AccAddress, coins sdk.Coins) {
|
func (suite *Suite) FundAccountWithKava(addr sdk.AccAddress, coins sdk.Coins) {
|
||||||
ukava := coins.AmountOf("ukava")
|
ukava := coins.AmountOf("ukava")
|
||||||
if ukava.IsPositive() {
|
if ukava.IsPositive() {
|
||||||
@ -411,3 +415,12 @@ func RandomEvmAccount() (common.Address, *ethsecp256k1.PrivKey) {
|
|||||||
addr := common.BytesToAddress(privKey.PubKey().Address())
|
addr := common.BytesToAddress(privKey.PubKey().Address())
|
||||||
return addr, privKey
|
return addr, privKey
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func RandomEvmAddress() common.Address {
|
||||||
|
addr, _ := RandomEvmAccount()
|
||||||
|
return addr
|
||||||
|
}
|
||||||
|
|
||||||
|
func RandomInternalEVMAddress() types.InternalEVMAddress {
|
||||||
|
return types.NewInternalEVMAddress(RandomEvmAddress())
|
||||||
|
}
|
||||||
|
@ -12,6 +12,11 @@ type InternalEVMAddress struct {
|
|||||||
common.Address
|
common.Address
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsNil returns true when the address is the 0 address
|
||||||
|
func (a InternalEVMAddress) IsNil() bool {
|
||||||
|
return a.Address == common.Address{}
|
||||||
|
}
|
||||||
|
|
||||||
// NewInternalEVMAddress returns a new InternalEVMAddress from a common.Address.
|
// NewInternalEVMAddress returns a new InternalEVMAddress from a common.Address.
|
||||||
func NewInternalEVMAddress(addr common.Address) InternalEVMAddress {
|
func NewInternalEVMAddress(addr common.Address) InternalEVMAddress {
|
||||||
return InternalEVMAddress{
|
return InternalEVMAddress{
|
||||||
@ -19,6 +24,11 @@ func NewInternalEVMAddress(addr common.Address) InternalEVMAddress {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BytesToInternalEVMAddress creates an InternalEVMAddress from a slice of bytes
|
||||||
|
func BytesToInternalEVMAddress(bz []byte) InternalEVMAddress {
|
||||||
|
return NewInternalEVMAddress(common.BytesToAddress(bz))
|
||||||
|
}
|
||||||
|
|
||||||
// NewInternalEVMAddressFromString returns a new InternalEVMAddress from a hex
|
// NewInternalEVMAddressFromString returns a new InternalEVMAddress from a hex
|
||||||
// string. Returns an error if hex string is invalid.
|
// string. Returns an error if hex string is invalid.
|
||||||
func NewInternalEVMAddressFromString(addrStr string) (InternalEVMAddress, error) {
|
func NewInternalEVMAddressFromString(addrStr string) (InternalEVMAddress, error) {
|
||||||
|
38
x/evmutil/types/address_test.go
Normal file
38
x/evmutil/types/address_test.go
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
package types_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/kava-labs/kava/x/evmutil/testutil"
|
||||||
|
"github.com/kava-labs/kava/x/evmutil/types"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestInternalEVMAddress_BytesToInternalEVMAddress(t *testing.T) {
|
||||||
|
addr := testutil.RandomEvmAddress()
|
||||||
|
require.Equal(t,
|
||||||
|
types.NewInternalEVMAddress(addr),
|
||||||
|
types.BytesToInternalEVMAddress(addr.Bytes()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInternalEVMAddress_IsNil(t *testing.T) {
|
||||||
|
addr := types.InternalEVMAddress{}
|
||||||
|
require.True(t, addr.IsNil())
|
||||||
|
addr.Address = testutil.RandomEvmAddress()
|
||||||
|
require.False(t, addr.IsNil())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestInternalEVMAddress_NewInternalEVMAddressFromString(t *testing.T) {
|
||||||
|
t.Run("works with valid address string", func(t *testing.T) {
|
||||||
|
validAddr := testutil.RandomEvmAddress()
|
||||||
|
addr, err := types.NewInternalEVMAddressFromString(validAddr.Hex())
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, types.NewInternalEVMAddress(validAddr), addr)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("fails with invalid hex string", func(t *testing.T) {
|
||||||
|
_, err := types.NewInternalEVMAddressFromString("0xinvalid-address")
|
||||||
|
require.ErrorContains(t, err, "string is not a hex address")
|
||||||
|
})
|
||||||
|
}
|
@ -9,4 +9,6 @@ var (
|
|||||||
ErrConversionNotEnabled = errorsmod.Register(ModuleName, 4, "ERC20 token not enabled to convert to sdk.Coin")
|
ErrConversionNotEnabled = errorsmod.Register(ModuleName, 4, "ERC20 token not enabled to convert to sdk.Coin")
|
||||||
ErrBalanceInvariance = errorsmod.Register(ModuleName, 5, "post EVM transfer balance invariant failed")
|
ErrBalanceInvariance = errorsmod.Register(ModuleName, 5, "post EVM transfer balance invariant failed")
|
||||||
ErrUnexpectedContractEvent = errorsmod.Register(ModuleName, 6, "unexpected contract event")
|
ErrUnexpectedContractEvent = errorsmod.Register(ModuleName, 6, "unexpected contract event")
|
||||||
|
ErrInvalidCosmosDenom = errorsmod.Register(ModuleName, 7, "invalid cosmos denom")
|
||||||
|
ErrSDKConversionNotEnabled = errorsmod.Register(ModuleName, 8, "sdk.Coin not enabled to convert to ERC20 token")
|
||||||
)
|
)
|
||||||
|
@ -17,13 +17,25 @@ const (
|
|||||||
RouterKey = ModuleName
|
RouterKey = ModuleName
|
||||||
)
|
)
|
||||||
|
|
||||||
var AccountStoreKeyPrefix = []byte{0x00} // prefix for keys that store accounts
|
// KVStore keys
|
||||||
|
var (
|
||||||
|
// AccountStoreKeyPrefix is the prefix for keys that store accounts
|
||||||
|
AccountStoreKeyPrefix = []byte{0x00}
|
||||||
|
// DeployedCosmosCoinContractKeyPrefix is the key for storing deployed KavaWrappedCosmosCoinERC20s contract addresses
|
||||||
|
DeployedCosmosCoinContractKeyPrefix = []byte{0x01}
|
||||||
|
)
|
||||||
|
|
||||||
// AccountStoreKey turns an address to a key used to get the account from the store
|
// AccountStoreKey turns an address to a key used to get the account from the store
|
||||||
func AccountStoreKey(addr sdk.AccAddress) []byte {
|
func AccountStoreKey(addr sdk.AccAddress) []byte {
|
||||||
return append(AccountStoreKeyPrefix, address.MustLengthPrefix(addr)...)
|
return append(AccountStoreKeyPrefix, address.MustLengthPrefix(addr)...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeployedCosmosCoinContractKey gives the store key that holds the address of the deployed ERC20
|
||||||
|
// that wraps the given cosmosDenom sdk.Coin
|
||||||
|
func DeployedCosmosCoinContractKey(cosmosDenom string) []byte {
|
||||||
|
return append(DeployedCosmosCoinContractKeyPrefix, []byte(cosmosDenom)...)
|
||||||
|
}
|
||||||
|
|
||||||
// ModuleAddress is the native module address for EVM
|
// ModuleAddress is the native module address for EVM
|
||||||
var ModuleEVMAddress common.Address
|
var ModuleEVMAddress common.Address
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user