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: update factory address in wrapper contract #139

Merged
merged 1 commit into from
Jan 6, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
96 changes: 52 additions & 44 deletions app/upgrade.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
opchildtypes "github.com/initia-labs/OPinit/x/opchild/types"
)

const upgradeName = "0.6.7"
const upgradeName = "0.6.8"

// RegisterUpgradeHandlers returns upgrade handlers
func (app *MinitiaApp) RegisterUpgradeHandlers(cfg module.Configurator) {
Expand All @@ -49,14 +49,14 @@

//////////////////////////// MINIEVM ///////////////////////////////////

// deploy and store erc20 wrapper contract address
// try to deploy and store erc20 wrapper contract address
if err := app.EVMKeeper.DeployERC20Wrapper(ctx); err != nil &&
// ignore contract address collision error (contract already deployed)
!strings.Contains(err.Error(), vm.ErrContractAddressCollision.Error()) {
return nil, err
}

// deploy and store erc20 factory contract address
// try to deploy and store erc20 factory contract address
if err := app.EVMKeeper.DeployERC20Factory(ctx); err != nil &&
// ignore contract address collision error (contract already deployed)
!strings.Contains(err.Error(), vm.ErrContractAddressCollision.Error()) {
Expand All @@ -67,47 +67,7 @@
// address collision error is ignored because it means that the contract has already been deployed
// and the erc20 contracts have already been updated.
//
code := hexutil.MustDecode(erc20.Erc20MetaData.Bin)

// runtime code
initCodeOP := common.Hex2Bytes("5ff3fe")
initCodePos := bytes.Index(code, initCodeOP)
code = code[initCodePos+3:]

// code hash
codeHash := crypto.Keccak256Hash(code).Bytes()

// iterate all erc20 contracts and replace contract code to new version
err = app.EVMKeeper.ERC20s.Walk(ctx, nil, func(contractAddr []byte) (bool, error) {
acc := app.AccountKeeper.GetAccount(ctx, contractAddr)
if acc == nil {
return true, fmt.Errorf("account not found for contract address %s", contractAddr)
}

contractAcc, ok := acc.(*evmtypes.ContractAccount)
if !ok {
return true, fmt.Errorf("account is not a contract account for contract address %s", contractAddr)
}

contractAcc.CodeHash = codeHash
app.AccountKeeper.SetAccount(ctx, contractAcc)

// set code
codeKey := append(contractAddr, append(state.CodeKeyPrefix, codeHash...)...)
err := app.EVMKeeper.VMStore.Set(ctx, codeKey, code)
if err != nil {
return true, err
}

// set code size
codeSizeKey := append(contractAddr, append(state.CodeSizeKeyPrefix, codeHash...)...)
err = app.EVMKeeper.VMStore.Set(ctx, codeSizeKey, uint64ToBytes(uint64(len(code))))
if err != nil {
return true, err
}

return false, nil
})
err = app.updateERC20s(ctx)

Check warning on line 70 in app/upgrade.go

View check run for this annotation

Codecov / codecov/patch

app/upgrade.go#L70

Added line #L70 was not covered by tests
if err != nil {
return nil, err
}
Expand All @@ -118,6 +78,54 @@
)
}

// updateERC20s updates all erc20 contracts to the new version
// - update contract code
// - update contract code hash
// - update contract code size
func (app *MinitiaApp) updateERC20s(ctx context.Context) error {
code := hexutil.MustDecode(erc20.Erc20MetaData.Bin)

// runtime code
initCodeOP := common.Hex2Bytes("5ff3fe")
initCodePos := bytes.Index(code, initCodeOP)
code = code[initCodePos+3:]

// code hash
codeHash := crypto.Keccak256Hash(code).Bytes()

// iterate all erc20 contracts and replace contract code to new version
return app.EVMKeeper.ERC20s.Walk(ctx, nil, func(contractAddr []byte) (bool, error) {
acc := app.AccountKeeper.GetAccount(ctx, contractAddr)
if acc == nil {
return true, fmt.Errorf("account not found for contract address %s", contractAddr)
}

Check warning on line 101 in app/upgrade.go

View check run for this annotation

Codecov / codecov/patch

app/upgrade.go#L85-L101

Added lines #L85 - L101 were not covered by tests

contractAcc, ok := acc.(*evmtypes.ContractAccount)
if !ok {
return true, fmt.Errorf("account is not a contract account for contract address %s", contractAddr)
}

Check warning on line 106 in app/upgrade.go

View check run for this annotation

Codecov / codecov/patch

app/upgrade.go#L103-L106

Added lines #L103 - L106 were not covered by tests

contractAcc.CodeHash = codeHash
app.AccountKeeper.SetAccount(ctx, contractAcc)

// set code
codeKey := append(contractAddr, append(state.CodeKeyPrefix, codeHash...)...)
err := app.EVMKeeper.VMStore.Set(ctx, codeKey, code)
if err != nil {
return true, err
}

Check warning on line 116 in app/upgrade.go

View check run for this annotation

Codecov / codecov/patch

app/upgrade.go#L108-L116

Added lines #L108 - L116 were not covered by tests

// set code size
codeSizeKey := append(contractAddr, append(state.CodeSizeKeyPrefix, codeHash...)...)
err = app.EVMKeeper.VMStore.Set(ctx, codeSizeKey, uint64ToBytes(uint64(len(code))))
if err != nil {
return true, err
}

Check warning on line 123 in app/upgrade.go

View check run for this annotation

Codecov / codecov/patch

app/upgrade.go#L119-L123

Added lines #L119 - L123 were not covered by tests

return false, nil

Check warning on line 125 in app/upgrade.go

View check run for this annotation

Codecov / codecov/patch

app/upgrade.go#L125

Added line #L125 was not covered by tests
})
}

func uint64ToBytes(v uint64) []byte {
bz := make([]byte, 8)
binary.BigEndian.PutUint64(bz, v)
Expand Down
25 changes: 23 additions & 2 deletions x/evm/contracts/erc20_wrapper/ERC20Wrapper.go

Large diffs are not rendered by default.

10 changes: 8 additions & 2 deletions x/evm/contracts/erc20_wrapper/ERC20Wrapper.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import "../erc20_acl/ERC20ACL.sol";
import "../i_ibc_async_callback/IIBCAsyncCallback.sol";
import {ERC165, IERC165} from "../erc165/ERC165.sol";

contract ERC20Wrapper is Ownable, ERC165, IIBCAsyncCallback {
contract ERC20Wrapper is Ownable, ERC165, IIBCAsyncCallback, ERC20ACL {
struct IbcCallBack {
address sender;
address originToken;
Expand All @@ -21,14 +21,20 @@ contract ERC20Wrapper is Ownable, ERC165, IIBCAsyncCallback {
string constant NAME_PREFIX = "Wrapped";
string constant SYMBOL_PREFIX = "W";
uint64 callBackId = 0;
ERC20Factory public immutable factory;
ERC20Factory public factory;
beer-1 marked this conversation as resolved.
Show resolved Hide resolved
mapping(address => address) public wrappedTokens; // origin -> wrapped
mapping(uint64 => IbcCallBack) private ibcCallBack; // id -> CallBackInfo

constructor(address erc20Factory) {
factory = ERC20Factory(erc20Factory);
}

// This function can only be called by the chain at upgrade.
function setFactory(address newFactory) external onlyChain {
require(newFactory != address(0), "invalid factory address");
factory = ERC20Factory(newFactory);
}

modifier onlyContract() {
require(
msg.sender == address(this),
Expand Down
19 changes: 18 additions & 1 deletion x/evm/keeper/erc20.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

"github.com/initia-labs/minievm/x/evm/contracts/erc20"
"github.com/initia-labs/minievm/x/evm/contracts/erc20_factory"
"github.com/initia-labs/minievm/x/evm/contracts/erc20_wrapper"
"github.com/initia-labs/minievm/x/evm/types"
)

Expand All @@ -29,6 +30,7 @@
ERC20Bin []byte
ERC20ABI *abi.ABI
ERC20FactoryABI *abi.ABI
ERC20WrapperABI *abi.ABI
}

func NewERC20Keeper(k *Keeper) (types.IERC20Keeper, error) {
Expand All @@ -42,19 +44,34 @@
return ERC20Keeper{}, err
}

wrapperABI, err := erc20_wrapper.Erc20WrapperMetaData.GetAbi()
if err != nil {
return ERC20Keeper{}, err
}

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

View check run for this annotation

Codecov / codecov/patch

x/evm/keeper/erc20.go#L49-L50

Added lines #L49 - L50 were not covered by tests

erc20Bin, err := hexutil.Decode(erc20.Erc20Bin)
if err != nil {
return ERC20Keeper{}, err
}

return &ERC20Keeper{k, erc20Bin, erc20ABI, factoryABI}, nil
return &ERC20Keeper{k, erc20Bin, erc20ABI, factoryABI, wrapperABI}, nil
}

// GetERC20ABI implements IERC20Keeper.
func (k ERC20Keeper) GetERC20ABI() *abi.ABI {
return k.ERC20ABI
}

// GetERC20FactoryABI implements IERC20Keeper.
func (K ERC20Keeper) GetERC20FactoryABI() *abi.ABI {
return K.ERC20FactoryABI

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

View check run for this annotation

Codecov / codecov/patch

x/evm/keeper/erc20.go#L66-L67

Added lines #L66 - L67 were not covered by tests
}

// GetERC20WrapperABI implements IERC20Keeper.
func (K ERC20Keeper) GetERC20WrapperABI() *abi.ABI {
return K.ERC20WrapperABI
}

// BurnCoins implements IERC20Keeper.
func (k ERC20Keeper) BurnCoins(ctx context.Context, addr sdk.AccAddress, amount sdk.Coins) error {
evmAddr, err := k.convertToEVMAddress(ctx, addr, false)
Expand Down
20 changes: 19 additions & 1 deletion x/evm/keeper/genesis.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,25 @@
return err
}

return k.ERC20FactoryAddr.Set(ctx, factoryAddr.Bytes())
err = k.ERC20FactoryAddr.Set(ctx, factoryAddr.Bytes())
if err != nil {
return err
}

Check warning on line 68 in x/evm/keeper/genesis.go

View check run for this annotation

Codecov / codecov/patch

x/evm/keeper/genesis.go#L67-L68

Added lines #L67 - L68 were not covered by tests

wrapperAddr, err := k.ERC20WrapperAddr.Get(ctx)
if err == nil {
inputBz, err := k.ERC20Keeper().GetERC20WrapperABI().Pack("setFactory", common.BytesToAddress(factoryAddr.Bytes()))
if err != nil {
return err
}

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

View check run for this annotation

Codecov / codecov/patch

x/evm/keeper/genesis.go#L74-L75

Added lines #L74 - L75 were not covered by tests

_, _, err = k.EVMCall(ctx, types.StdAddress, common.BytesToAddress(wrapperAddr), inputBz, nil, nil)
if err != nil {
return err
}

Check warning on line 80 in x/evm/keeper/genesis.go

View check run for this annotation

Codecov / codecov/patch

x/evm/keeper/genesis.go#L79-L80

Added lines #L79 - L80 were not covered by tests
}

return nil
}

// DeployERC20Wrapper deploys the ERC20 wrapper contract and stores the address in the keeper.
Expand Down
54 changes: 54 additions & 0 deletions x/evm/keeper/genesis_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package keeper_test
import (
"testing"

sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/ethereum/go-ethereum/common"
"github.com/initia-labs/minievm/x/evm/contracts/erc20_wrapper"
"github.com/initia-labs/minievm/x/evm/types"
Expand Down Expand Up @@ -103,3 +104,56 @@ func Test_Initialize(t *testing.T) {

require.Equal(t, expectedFactoryAddr, factoryAddr)
}

func Test_DeployERC20Factory(t *testing.T) {
ctx, input := createTestInput(t, false, false)

// set random factory address and check if it is set correctly
err := input.EVMKeeper.ERC20FactoryAddr.Set(ctx, common.HexToAddress("0x123").Bytes())
require.NoError(t, err)

// set params
input.EVMKeeper.Params.Set(ctx, types.DefaultParams())

factoryAddr, err := input.EVMKeeper.GetERC20FactoryAddr(ctx)
require.NoError(t, err)

require.Equal(t, common.HexToAddress("0x123"), factoryAddr)

// deploy wrapper contract and check if the factory address is set correctly
err = input.EVMKeeper.DeployERC20Wrapper(ctx)
require.NoError(t, err)

expectedFactoryAddr, err := input.EVMKeeper.GetERC20FactoryAddr(ctx)
require.NoError(t, err)

require.Equal(t, expectedFactoryAddr, queryFactoryAddressFromWrapper(t, ctx, input))

// deploy factory contract again and check if the factory address is set correctly
err = input.EVMKeeper.DeployERC20Factory(ctx)
require.NoError(t, err)

expectedFactoryAddr, err = input.EVMKeeper.GetERC20FactoryAddr(ctx)
require.NoError(t, err)

require.Equal(t, expectedFactoryAddr, queryFactoryAddressFromWrapper(t, ctx, input))
require.NotEqual(t, factoryAddr, expectedFactoryAddr)
}

func queryFactoryAddressFromWrapper(t *testing.T, ctx sdk.Context, input TestKeepers) common.Address {
wrapperAddr, err := input.EVMKeeper.GetERC20WrapperAddr(ctx)
require.NoError(t, err)

caller := common.HexToAddress("0x0")
abi, err := erc20_wrapper.Erc20WrapperMetaData.GetAbi()
require.NoError(t, err)

viewArg, err := abi.Pack("factory")
require.NoError(t, err)

factoryAddrBytes, err := input.EVMKeeper.EVMStaticCall(ctx, caller, wrapperAddr, viewArg, nil)
require.NoError(t, err)

factoryAddr := common.BytesToAddress(factoryAddrBytes)
return factoryAddr
}
2 changes: 2 additions & 0 deletions x/evm/types/expected_keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ type IERC20Keeper interface {

// ABI
GetERC20ABI() *abi.ABI
GetERC20FactoryABI() *abi.ABI
GetERC20WrapperABI() *abi.ABI

// erc20 queries
GetDecimals(ctx context.Context, denom string) (uint8, error)
Expand Down
Loading