diff --git a/app/test_common.go b/app/test_common.go index b822ddd2..b4657884 100644 --- a/app/test_common.go +++ b/app/test_common.go @@ -49,6 +49,7 @@ import ( issuancekeeper "github.com/0glabs/0g-chain/x/issuance/keeper" precisebankkeeper "github.com/0glabs/0g-chain/x/precisebank/keeper" pricefeedkeeper "github.com/0glabs/0g-chain/x/pricefeed/keeper" + wrappeda0gibasekeeper "github.com/0glabs/0g-chain/x/wrapped-a0gi-base/keeper" ) var ( @@ -118,6 +119,9 @@ func (tApp TestApp) GetEvmKeeper() *evmkeeper.Keeper { return tAp func (tApp TestApp) GetFeeMarketKeeper() feemarketkeeper.Keeper { return tApp.feeMarketKeeper } func (tApp TestApp) GetDASignersKeeper() dasignerskeeper.Keeper { return tApp.dasignersKeeper } func (tApp TestApp) GetPrecisebankKeeper() precisebankkeeper.Keeper { return tApp.precisebankKeeper } +func (tApp TestApp) GetWrappedA0GIBaseKeeper() wrappeda0gibasekeeper.Keeper { + return tApp.wrappeda0gibaseKeeper +} func (tApp TestApp) GetKVStoreKey(key string) *storetypes.KVStoreKey { return tApp.keys[key] diff --git a/x/wrapped-a0gi-base/cli/query.go b/x/wrapped-a0gi-base/cli/query.go index 52e4eaca..c693ab23 100644 --- a/x/wrapped-a0gi-base/cli/query.go +++ b/x/wrapped-a0gi-base/cli/query.go @@ -11,7 +11,7 @@ import ( func GetQueryCmd() *cobra.Command { cmd := &cobra.Command{ Use: types.ModuleName, - Short: "Querying commands for the dasigners module", + Short: "Querying commands for the wrapped a0gi base module", DisableFlagParsing: true, SuggestionsMinimumDistance: 2, RunE: client.ValidateCmd, diff --git a/x/wrapped-a0gi-base/genesis_test.go b/x/wrapped-a0gi-base/genesis_test.go new file mode 100644 index 00000000..831efd82 --- /dev/null +++ b/x/wrapped-a0gi-base/genesis_test.go @@ -0,0 +1,67 @@ +package wrappeda0gibase_test + +import ( + "testing" + + "github.com/stretchr/testify/suite" + + tmproto "github.com/cometbft/cometbft/proto/tendermint/types" + + "github.com/0glabs/0g-chain/app" + wrappeda0gibase "github.com/0glabs/0g-chain/x/wrapped-a0gi-base" + "github.com/0glabs/0g-chain/x/wrapped-a0gi-base/testutil" + "github.com/0glabs/0g-chain/x/wrapped-a0gi-base/types" +) + +type GenesisTestSuite struct { + testutil.Suite +} + +func (suite *GenesisTestSuite) TestInitGenesis() { + // Most genesis validation tests are located in the types directory. The 'invalid' test cases are + // randomly selected subset of those tests. + testCases := []struct { + name string + genState *types.GenesisState + expectPass bool + }{ + { + name: "default genesis", + genState: types.DefaultGenesisState(), + expectPass: true, + }, + } + for _, tc := range testCases { + suite.Run(tc.name, func() { + // Setup (note: suite.SetupTest is not run before every suite.Run) + suite.App = app.NewTestApp() + suite.Keeper = suite.App.GetWrappedA0GIBaseKeeper() + suite.Ctx = suite.App.NewContext(true, tmproto.Header{}) + + // Run + var exportedGenState *types.GenesisState + run := func() { + wrappeda0gibase.InitGenesis(suite.Ctx, suite.Keeper, *tc.genState) + exportedGenState = wrappeda0gibase.ExportGenesis(suite.Ctx, suite.Keeper) + } + if tc.expectPass { + suite.Require().NotPanics(run) + } else { + suite.Require().Panics(run) + } + + // Check + if tc.expectPass { + expectedJson, err := suite.App.AppCodec().MarshalJSON(tc.genState) + suite.Require().NoError(err) + actualJson, err := suite.App.AppCodec().MarshalJSON(exportedGenState) + suite.Require().NoError(err) + suite.Equal(expectedJson, actualJson) + } + }) + } +} + +func TestGenesisTestSuite(t *testing.T) { + suite.Run(t, new(GenesisTestSuite)) +} diff --git a/x/wrapped-a0gi-base/keeper/keeper_test.go b/x/wrapped-a0gi-base/keeper/keeper_test.go new file mode 100644 index 00000000..b045109f --- /dev/null +++ b/x/wrapped-a0gi-base/keeper/keeper_test.go @@ -0,0 +1,40 @@ +package keeper_test + +import ( + "testing" + + "github.com/0glabs/0g-chain/x/wrapped-a0gi-base/testutil" + "github.com/0glabs/0g-chain/x/wrapped-a0gi-base/types" + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/suite" +) + +type KeeperTestSuite struct { + testutil.Suite +} + +func (s *KeeperTestSuite) TestSetWA0GIAddress() { + testCases := []struct { + name string + wa0gi common.Address + }{ + { + name: "zero address", + wa0gi: common.HexToAddress("0x0000000000000000000000000000000000000000"), + }, + } + for _, tc := range testCases { + s.Run(tc.name, func() { + s.SetupTest() + + s.Keeper.SetWA0GIAddress(s.Ctx, tc.wa0gi) + response, err := s.Keeper.GetWA0GI(s.Ctx, &types.GetWA0GIRequest{}) + s.Require().NoError(err) + s.Require().Equal(common.BytesToAddress(response.Address), tc.wa0gi) + }) + } +} + +func TestKeeperSuite(t *testing.T) { + suite.Run(t, new(KeeperTestSuite)) +} diff --git a/x/wrapped-a0gi-base/keeper/msg_server.go b/x/wrapped-a0gi-base/keeper/msg_server.go index 0d47034c..f3de20dd 100644 --- a/x/wrapped-a0gi-base/keeper/msg_server.go +++ b/x/wrapped-a0gi-base/keeper/msg_server.go @@ -18,10 +18,6 @@ var _ types.MsgServer = &Keeper{} func (k Keeper) Burn(goCtx context.Context, msg *types.MsgBurn) (*types.MsgBurnResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) minter := common.BytesToAddress(msg.Minter) - cap, err := k.getMinterCap(ctx, minter) - if err != nil { - return nil, err - } supply, err := k.getMinterSupply(ctx, minter) if err != nil { return nil, err @@ -29,17 +25,17 @@ func (k Keeper) Burn(goCtx context.Context, msg *types.MsgBurn) (*types.MsgBurnR amount := new(big.Int).SetBytes(msg.Amount) // check & update mint supply supply.Sub(supply, amount) - if supply.Cmp(cap) > 0 { - return nil, types.ErrInsufficientMintCap - } - if err = k.setMinterSupply(ctx, minter, supply); err != nil { - return nil, err + if supply.Cmp(big.NewInt(0)) < 0 { + return nil, types.ErrInsufficientMintSupply } // burn c := sdk.NewCoin(precisebanktypes.ExtendedCoinDenom, sdk.NewIntFromBigInt(amount)) if err = k.pbkeeper.BurnCoins(ctx, types.ModuleName, sdk.NewCoins(c)); err != nil { return nil, err } + if err = k.setMinterSupply(ctx, minter, supply); err != nil { + return nil, err + } return &types.MsgBurnResponse{}, nil } @@ -61,14 +57,14 @@ func (k Keeper) Mint(goCtx context.Context, msg *types.MsgMint) (*types.MsgMintR if supply.Cmp(cap) > 0 { return nil, types.ErrInsufficientMintCap } - if err = k.setMinterSupply(ctx, minter, supply); err != nil { - return nil, err - } // mint c := sdk.NewCoin(precisebanktypes.ExtendedCoinDenom, sdk.NewIntFromBigInt(amount)) if err = k.pbkeeper.MintCoins(ctx, types.ModuleName, sdk.NewCoins(c)); err != nil { return nil, err } + if err = k.setMinterSupply(ctx, minter, supply); err != nil { + return nil, err + } return &types.MsgMintResponse{}, nil } diff --git a/x/wrapped-a0gi-base/keeper/msg_server_test.go b/x/wrapped-a0gi-base/keeper/msg_server_test.go new file mode 100644 index 00000000..8c54cd3f --- /dev/null +++ b/x/wrapped-a0gi-base/keeper/msg_server_test.go @@ -0,0 +1,284 @@ +package keeper_test + +import ( + "fmt" + "math/big" + "testing" + + precisebanktypes "github.com/0glabs/0g-chain/x/precisebank/types" + "github.com/0glabs/0g-chain/x/wrapped-a0gi-base/testutil" + "github.com/0glabs/0g-chain/x/wrapped-a0gi-base/types" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/suite" +) + +type MsgServerTestSuite struct { + testutil.Suite +} + +func (s *MsgServerTestSuite) TestSetWA0GI() { + govAccAddr := s.GovKeeper.GetGovernanceAccount(s.Ctx).GetAddress().String() + testCases := []struct { + name string + req *types.MsgSetWA0GI + expectErr bool + errMsg string + }{ + { + name: "invalid signer", + req: &types.MsgSetWA0GI{ + Authority: s.Addresses[0].String(), + Address: common.HexToAddress("0x0000000000000000000000000000000000000001").Bytes(), + }, + expectErr: true, + errMsg: "expected gov account as only signer for proposal message", + }, + { + name: "success", + req: &types.MsgSetWA0GI{ + Authority: govAccAddr, + Address: common.HexToAddress("0x0000000000000000000000000000000000000001").Bytes(), + }, + expectErr: false, + }, + } + for _, tc := range testCases { + s.Run(tc.name, func() { + _, err := s.Keeper.SetWA0GI(sdk.WrapSDKContext(s.Ctx), tc.req) + if tc.expectErr { + s.Require().Error(err) + s.Require().Contains(err.Error(), tc.errMsg) + } else { + s.Require().NoError(err) + response, err := s.Keeper.GetWA0GI(s.Ctx, &types.GetWA0GIRequest{}) + s.Require().NoError(err) + s.Require().Equal(response.Address, tc.req.Address) + } + }) + } +} + +func (s *MsgServerTestSuite) TestSetMinterCap() { + testCases := []struct { + name string + caps []struct { + account common.Address + cap *big.Int + } + }{ + { + name: "success", + caps: []struct { + account common.Address + cap *big.Int + }{ + { + account: common.HexToAddress("0x0000000000000000000000000000000000000000"), + cap: big.NewInt(100000), + }, + { + account: common.HexToAddress("0x0000000000000000000000000000000000000001"), + cap: big.NewInt(200000), + }, + { + account: common.HexToAddress("0x0000000000000000000000000000000000000002"), + cap: big.NewInt(300000), + }, + { + account: common.HexToAddress("0x0000000000000000000000000000000000000003"), + cap: big.NewInt(400000), + }, + { + account: common.HexToAddress("0x0000000000000000000000000000000000000002"), + cap: big.NewInt(500000), + }, + { + account: common.HexToAddress("0x0000000000000000000000000000000000000001"), + cap: big.NewInt(600000), + }, + { + account: common.HexToAddress("0x0000000000000000000000000000000000000000"), + cap: big.NewInt(700000), + }, + }, + }, + } + s.Run("invalid authority", func() { + s.SetupTest() + _, err := s.Keeper.SetMinterCap(sdk.WrapSDKContext(s.Ctx), &types.MsgSetMintCap{ + Authority: s.Addresses[0].String(), + Minter: common.HexToAddress("0x0000000000000000000000000000000000000000").Bytes(), + Cap: big.NewInt(600000).Bytes(), + }) + s.Require().Error(err) + s.Require().Contains(err.Error(), "expected gov account as only signer for proposal message") + }) + govAccAddr := s.GovKeeper.GetGovernanceAccount(s.Ctx).GetAddress().String() + for _, tc := range testCases { + s.Run(tc.name, func() { + s.SetupTest() + + c := make(map[common.Address]*big.Int) + for _, cap := range tc.caps { + _, err := s.Keeper.SetMinterCap(sdk.WrapSDKContext(s.Ctx), &types.MsgSetMintCap{ + Authority: govAccAddr, + Minter: cap.account.Bytes(), + Cap: cap.cap.Bytes(), + }) + s.Require().NoError(err) + response, err := s.Keeper.MinterSupply(s.Ctx, &types.MinterSupplyRequest{ + Address: cap.account.Bytes(), + }) + s.Require().NoError(err) + s.Require().Equal(new(big.Int).SetBytes(response.Cap), cap.cap) + c[cap.account] = cap.cap + } + for account, cap := range c { + response, err := s.Keeper.MinterSupply(s.Ctx, &types.MinterSupplyRequest{ + Address: account.Bytes(), + }) + s.Require().NoError(err) + s.Require().Equal(new(big.Int).SetBytes(response.Cap), cap) + } + }) + } +} + +type MintBurn struct { + IsMint bool + Minter common.Address + Amount *big.Int + Success bool +} + +func (s *MsgServerTestSuite) TestSetMintBurn() { + precisebankKeeper := s.App.GetPrecisebankKeeper() + accountKeeper := s.App.GetAccountKeeper() + moduleAcc := accountKeeper.GetModuleAccount(s.Ctx, types.ModuleName).GetAddress() + govAccAddr := s.GovKeeper.GetGovernanceAccount(s.Ctx).GetAddress().String() + + minter1 := common.HexToAddress("0x0000000000000000000000000000000000000001") + minter2 := common.HexToAddress("0x0000000000000000000000000000000000000002") + + // set mint cap of minter 1 to 8 a0gi + _, err := s.Keeper.SetMinterCap(sdk.WrapSDKContext(s.Ctx), &types.MsgSetMintCap{ + Authority: govAccAddr, + Minter: minter1.Bytes(), + Cap: big.NewInt(8e18).Bytes(), + }) + s.Require().NoError(err) + // set mint cap of minter 2 to 5 a0gi + _, err = s.Keeper.SetMinterCap(sdk.WrapSDKContext(s.Ctx), &types.MsgSetMintCap{ + Authority: govAccAddr, + Minter: minter2.Bytes(), + Cap: big.NewInt(5e18).Bytes(), + }) + s.Require().NoError(err) + + testCases := []MintBurn{ + // #0, failed burn + { + IsMint: false, + Minter: minter1, + Amount: big.NewInt(1e18), + Success: false, + }, + // #1, mint 5 a0gi by minter 1 + { + IsMint: true, + Minter: minter1, + Amount: big.NewInt(5e18), + Success: true, + }, + // #2, burn 0.5 a0gi by minter 1 + { + IsMint: false, + Minter: minter1, + Amount: big.NewInt(5e17), + Success: true, + }, + // #3, mint 0.7 a0gi by minter 2 + { + IsMint: true, + Minter: minter2, + Amount: big.NewInt(7e17), + Success: true, + }, + // #4, mint 2 a0gi by minter 2 + { + IsMint: true, + Minter: minter2, + Amount: big.NewInt(2e18), + Success: true, + }, + // #5, burn 0.3 a0gi by minter 2 + { + IsMint: false, + Minter: minter1, + Amount: big.NewInt(3e17), + Success: true, + }, + // #6, failed to mint 4 a0gi by minter 1 + { + IsMint: true, + Minter: minter1, + Amount: big.NewInt(4e18), + Success: false, + }, + // #7, mint 3.5 a0gi by minter 1 + { + IsMint: true, + Minter: minter1, + Amount: big.NewInt(3e18 + 5e17), + Success: true, + }, + } + minted := big.NewInt(0) + supplied := make(map[common.Address]*big.Int) + for id, c := range testCases { + fmt.Println(id) + if c.IsMint { + _, err = s.Keeper.Mint(sdk.WrapSDKContext(s.Ctx), &types.MsgMint{ + Minter: c.Minter.Bytes(), + Amount: c.Amount.Bytes(), + }) + } else { + _, err = s.Keeper.Burn(sdk.WrapSDKContext(s.Ctx), &types.MsgBurn{ + Minter: c.Minter.Bytes(), + Amount: c.Amount.Bytes(), + }) + } + if c.Success { + if c.IsMint { + minted.Add(minted, c.Amount) + if amt, ok := supplied[c.Minter]; ok { + amt.Add(amt, c.Amount) + } else { + supplied[c.Minter] = new(big.Int).Set(c.Amount) + } + } else { + minted.Sub(minted, c.Amount) + if amt, ok := supplied[c.Minter]; ok { + amt.Sub(amt, c.Amount) + } else { + supplied[c.Minter] = new(big.Int).Set(c.Amount) + } + } + s.Require().NoError(err) + response, err := s.Keeper.MinterSupply(s.Ctx, &types.MinterSupplyRequest{ + Address: c.Minter.Bytes(), + }) + s.Require().NoError(err) + s.Require().Equal(supplied[c.Minter].Bytes(), response.Supply) + } else { + s.Require().Error(err) + } + coins := precisebankKeeper.GetBalance(s.Ctx, moduleAcc, precisebanktypes.ExtendedCoinDenom) + s.Require().Equal(coins.Amount.BigInt(), minted) + } +} + +func TestMsgServerSuite(t *testing.T) { + suite.Run(t, new(MsgServerTestSuite)) +} diff --git a/x/wrapped-a0gi-base/testutil/suite.go b/x/wrapped-a0gi-base/testutil/suite.go new file mode 100644 index 00000000..071d87de --- /dev/null +++ b/x/wrapped-a0gi-base/testutil/suite.go @@ -0,0 +1,66 @@ +package testutil + +import ( + "strings" + + tmproto "github.com/cometbft/cometbft/proto/tendermint/types" + sdk "github.com/cosmos/cosmos-sdk/types" + stakingkeeper "github.com/cosmos/cosmos-sdk/x/staking/keeper" + "github.com/ethereum/go-ethereum/crypto" + "github.com/stretchr/testify/suite" + + "github.com/0glabs/0g-chain/app" + "github.com/0glabs/0g-chain/chaincfg" + "github.com/0glabs/0g-chain/x/wrapped-a0gi-base/keeper" + "github.com/0glabs/0g-chain/x/wrapped-a0gi-base/types" + govkeeper "github.com/cosmos/cosmos-sdk/x/gov/keeper" + stakingtypes "github.com/cosmos/cosmos-sdk/x/staking/types" + "github.com/evmos/ethermint/crypto/ethsecp256k1" +) + +// Suite implements a test suite for the module integration tests +type Suite struct { + suite.Suite + + Keeper keeper.Keeper + StakingKeeper *stakingkeeper.Keeper + GovKeeper govkeeper.Keeper + App app.TestApp + Ctx sdk.Context + QueryClient types.QueryClient + Addresses []sdk.AccAddress +} + +// SetupTest instantiates a new app, keepers, and sets suite state +func (suite *Suite) SetupTest() { + chaincfg.SetSDKConfig() + suite.App = app.NewTestApp() + suite.App.InitializeFromGenesisStates() + suite.Keeper = suite.App.GetWrappedA0GIBaseKeeper() + suite.GovKeeper = suite.App.GetGovKeeper() + suite.StakingKeeper = suite.App.GetStakingKeeper() + + // make block header + privkey, _ := ethsecp256k1.GenerateKey() + consAddress := sdk.ConsAddress(privkey.PubKey().Address()) + key, err := privkey.ToECDSA() + suite.Assert().NoError(err) + hexAddr := strings.ToLower(crypto.PubkeyToAddress(key.PublicKey).Hex()[2:]) + valAddr, err := sdk.ValAddressFromHex(hexAddr) + suite.Assert().NoError(err) + suite.Ctx = suite.App.NewContext(true, tmproto.Header{Height: 1, ChainID: app.TestChainId, ProposerAddress: consAddress}) + newValidator, err := stakingtypes.NewValidator(valAddr, privkey.PubKey(), stakingtypes.Description{}) + suite.Assert().NoError(err) + err = suite.StakingKeeper.SetValidatorByConsAddr(suite.Ctx, newValidator) + suite.Assert().NoError(err) + suite.StakingKeeper.SetValidator(suite.Ctx, newValidator) + + _, accAddresses := app.GeneratePrivKeyAddressPairs(10) + suite.Addresses = accAddresses + + // Set query client + queryHelper := suite.App.NewQueryServerTestHelper(suite.Ctx) + queryHandler := suite.Keeper + types.RegisterQueryServer(queryHelper, queryHandler) + suite.QueryClient = types.NewQueryClient(queryHelper) +} diff --git a/x/wrapped-a0gi-base/testutil/types.go b/x/wrapped-a0gi-base/testutil/types.go new file mode 100644 index 00000000..1ae3828c --- /dev/null +++ b/x/wrapped-a0gi-base/testutil/types.go @@ -0,0 +1,25 @@ +package testutil + +import ( + "testing" + + sdkmath "cosmossdk.io/math" + "github.com/cosmos/cosmos-sdk/codec" + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/gogo/protobuf/proto" + "github.com/stretchr/testify/assert" +) + +// Avoid cluttering test cases with long function names +func I(in int64) sdkmath.Int { return sdkmath.NewInt(in) } +func D(str string) sdk.Dec { return sdk.MustNewDecFromStr(str) } +func C(denom string, amount int64) sdk.Coin { return sdk.NewInt64Coin(denom, amount) } +func Cs(coins ...sdk.Coin) sdk.Coins { return sdk.NewCoins(coins...) } + +func AssertProtoMessageJSON(t *testing.T, cdc codec.Codec, expected proto.Message, actual proto.Message) { + expectedJson, err := cdc.MarshalJSON(expected) + assert.NoError(t, err) + actualJson, err := cdc.MarshalJSON(actual) + assert.NoError(t, err) + assert.Equal(t, string(expectedJson), string(actualJson)) +} diff --git a/x/wrapped-a0gi-base/types/errors.go b/x/wrapped-a0gi-base/types/errors.go index 7b38b98c..cd8bff0d 100644 --- a/x/wrapped-a0gi-base/types/errors.go +++ b/x/wrapped-a0gi-base/types/errors.go @@ -3,6 +3,7 @@ package types import errorsmod "cosmossdk.io/errors" var ( - ErrTxForbidden = errorsmod.Register(ModuleName, 1, "cosmos tx forbidden") - ErrInsufficientMintCap = errorsmod.Register(ModuleName, 2, "insufficient mint cap") + ErrTxForbidden = errorsmod.Register(ModuleName, 1, "cosmos tx forbidden") + ErrInsufficientMintCap = errorsmod.Register(ModuleName, 2, "insufficient mint cap") + ErrInsufficientMintSupply = errorsmod.Register(ModuleName, 3, "insufficient mint supply") ) diff --git a/x/wrapped-a0gi-base/types/genesis.go b/x/wrapped-a0gi-base/types/genesis.go index 0b622117..d306dc5f 100644 --- a/x/wrapped-a0gi-base/types/genesis.go +++ b/x/wrapped-a0gi-base/types/genesis.go @@ -3,7 +3,16 @@ package types import "github.com/ethereum/go-ethereum/common" const ( - DEFAULT_WRAPPED_A0GI = "0000000000000000000000000000000000000000" + // This is a Wrapped A0GI contract deployed by a raw transaction: + // raw tx params: + // from: 0x43db6cef54678f25310c0804bc1c1c2418f68e5b + // nonce: 0 + // gasPrice: 100 Gwei + // gasLimit: 1000000 + // The sender is an ephemeral account, nobody holds its private key and this is the only transaction it signed. + // This transaction is a legacy transaction without chain ID so it can be deployed at any EVM chain which supports pre-EIP155 transactions. + // raw tx: 0xf90c378085174876e800830f42408080b90be460c0604052600c60809081526b57726170706564204130474960a01b60a05260009061002b908261011c565b50604080518082019091526005815264574130474960d81b6020820152600190610055908261011c565b50600280546001600160a81b0319166210021217905534801561007757600080fd5b506101db565b634e487b7160e01b600052604160045260246000fd5b600181811c908216806100a757607f821691505b6020821081036100c757634e487b7160e01b600052602260045260246000fd5b50919050565b601f82111561011757600081815260208120601f850160051c810160208610156100f45750805b601f850160051c820191505b8181101561011357828155600101610100565b5050505b505050565b81516001600160401b038111156101355761013561007d565b610149816101438454610093565b846100cd565b602080601f83116001811461017e57600084156101665750858301515b600019600386901b1c1916600185901b178555610113565b600085815260208120601f198616915b828110156101ad5788860151825594840194600190910190840161018e565b50858210156101cb5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b6109f980620001eb6000396000f3fe6080604052600436106100e15760003560e01c806342966c681161007f57806395d89b411161005957806395d89b4114610283578063a9059cbb14610298578063d0e30db0146102b8578063dd62ed3e146102c057600080fd5b806342966c68146101f95780635001f3b51461021957806370a082311461025657600080fd5b806323b872dd116100bb57806323b872dd1461016d5780632e1a7d4d1461018d578063313ce567146101ad57806340c10f19146101d957600080fd5b806306fdde03146100f5578063095ea7b31461012057806318160ddd1461015057600080fd5b366100f0576100ee6102f8565b005b600080fd5b34801561010157600080fd5b5061010a610353565b6040516101179190610816565b60405180910390f35b34801561012c57600080fd5b5061014061013b366004610880565b6103e1565b6040519015158152602001610117565b34801561015c57600080fd5b50475b604051908152602001610117565b34801561017957600080fd5b506101406101883660046108aa565b61044e565b34801561019957600080fd5b506100ee6101a83660046108e6565b6105d2565b3480156101b957600080fd5b506002546101c79060ff1681565b60405160ff9091168152602001610117565b3480156101e557600080fd5b506100ee6101f4366004610880565b61065d565b34801561020557600080fd5b506100ee6102143660046108e6565b610736565b34801561022557600080fd5b5060025461023e9061010090046001600160a01b031681565b6040516001600160a01b039091168152602001610117565b34801561026257600080fd5b5061015f6102713660046108ff565b60036020526000908152604090205481565b34801561028f57600080fd5b5061010a6107f5565b3480156102a457600080fd5b506101406102b3366004610880565b610802565b6100ee6102f8565b3480156102cc57600080fd5b5061015f6102db36600461091a565b600460209081526000928352604080842090915290825290205481565b3360009081526003602052604081208054349290610317908490610963565b909155505060405134815233907fe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c9060200160405180910390a2565b6000805461036090610976565b80601f016020809104026020016040519081016040528092919081815260200182805461038c90610976565b80156103d95780601f106103ae576101008083540402835291602001916103d9565b820191906000526020600020905b8154815290600101906020018083116103bc57829003601f168201915b505050505081565b3360008181526004602090815260408083206001600160a01b038716808552925280832085905551919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259061043c9086815260200190565b60405180910390a35060015b92915050565b6001600160a01b03831660009081526003602052604081205482111561047357600080fd5b6001600160a01b03841633148015906104b157506001600160a01b038416600090815260046020908152604080832033845290915290205460001914155b1561051f576001600160a01b03841660009081526004602090815260408083203384529091529020548211156104e657600080fd5b6001600160a01b0384166000908152600460209081526040808320338452909152812080548492906105199084906109b0565b90915550505b6001600160a01b038416600090815260036020526040812080548492906105479084906109b0565b90915550506001600160a01b03831660009081526003602052604081208054849290610574908490610963565b92505081905550826001600160a01b0316846001600160a01b03167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040516105c091815260200190565b60405180910390a35060019392505050565b33600090815260036020526040812080548392906105f19084906109b0565b9091555050604051339082156108fc029083906000818181858888f19350505050158015610623573d6000803e3d6000fd5b5060405181815233907f7fcf532c15f0a6db0bd6d0e038bea71d30d808c7d98cb3bf7268a95bf5081b65906020015b60405180910390a250565b6002546040516340c10f1960e01b8152336004820152602481018390526101009091046001600160a01b0316906340c10f1990604401600060405180830381600087803b1580156106ad57600080fd5b505af11580156106c1573d6000803e3d6000fd5b505050506001600160a01b038216600090815260036020526040812080548392906106ed908490610963565b90915550506040518181526001600160a01b0383169033907fab8530f87dc9b59234c4623bf917212bb2536d647574c8e7e5da92c2ede0c9f89060200160405180910390a35050565b600254604051632770a7eb60e21b8152336004820152602481018390526101009091046001600160a01b031690639dc29fac90604401600060405180830381600087803b15801561078657600080fd5b505af115801561079a573d6000803e3d6000fd5b505033600090815260036020526040812080548594509092506107be9084906109b0565b909155505060405181815233907fcc16f5dbb4873280815c1ee09dbd06736cffcc184412cf7a71a0fdb75d397ca590602001610652565b6001805461036090610976565b600061080f33848461044e565b9392505050565b600060208083528351808285015260005b8181101561084357858101830151858201604001528201610827565b506000604082860101526040601f19601f8301168501019250505092915050565b80356001600160a01b038116811461087b57600080fd5b919050565b6000806040838503121561089357600080fd5b61089c83610864565b946020939093013593505050565b6000806000606084860312156108bf57600080fd5b6108c884610864565b92506108d660208501610864565b9150604084013590509250925092565b6000602082840312156108f857600080fd5b5035919050565b60006020828403121561091157600080fd5b61080f82610864565b6000806040838503121561092d57600080fd5b61093683610864565b915061094460208401610864565b90509250929050565b634e487b7160e01b600052601160045260246000fd5b808201808211156104485761044861094d565b600181811c9082168061098a57607f821691505b6020821081036109aa57634e487b7160e01b600052602260045260246000fd5b50919050565b818103818111156104485761044861094d56fea2646970667358221220076f43aa18ca3555e080ebb6815ba9bdfa3617ec5fad0223a8d6007ea588a6f364736f6c634300081400331ba07f6fb2afc9cb39989c4e35299f44266a2f800f009fdeb24914df0b51de47c85fa02acc21d7f401e1529a4fbd0b3cf888414a07494af162ddca5670a6fa181ebe07 + DEFAULT_WRAPPED_A0GI = "0xCDf97Ac4453AD85C663d2555BF4424601e1f2A9F" ) // NewGenesisState returns a new genesis state object for the module.