diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml index e5de4bc9..88b64889 100644 --- a/.github/workflows/golangci-lint.yml +++ b/.github/workflows/golangci-lint.yml @@ -12,6 +12,9 @@ jobs: name: lint runs-on: ubuntu-latest steps: + - uses: actions/setup-go@v3 + with: + go-version: 1.18 - uses: actions/checkout@v3 - name: golangci-lint uses: golangci/golangci-lint-action@v3 diff --git a/app/keepers/keepers.go b/app/keepers/keepers.go index 9ffc0d28..f0bd5c7a 100644 --- a/app/keepers/keepers.go +++ b/app/keepers/keepers.go @@ -196,6 +196,7 @@ func (appKeepers *AppKeepers) InitNormalKeepers( appKeepers.AccountKeeper, appKeepers.BankKeeper, &appKeepers.IBCKeeper.PortKeeper, + &appKeepers.IBCKeeper.ChannelKeeper, appKeepers.ScopedOracleKeeper, ) expIBCModule := exp.NewIBCModule(appCodec, appKeepers.ExpKeeper) diff --git a/test_proposal.json b/test_proposal.json index 89082255..d66888b7 100644 --- a/test_proposal.json +++ b/test_proposal.json @@ -2,7 +2,7 @@ "messages": [ { "@type": "/craft.exp.v1beta1.MsgJoinDaoByIbcAsset", - "join_address": "craft1hj5fveer5cjtn4wd6wstzugjfdxzl0xp86p9fl", + "join_address": "craft1n28rexc05uhay2alqtuhlsymz0npyspfuwedy2", "gov_address": "craft10d07y265gmmuvt4z0w9aw880jnsr700jm5qjn0", "amount": "1000000" } diff --git a/testmint.sh b/testmint.sh index 89aee9d9..196dabe3 100644 --- a/testmint.sh +++ b/testmint.sh @@ -2,8 +2,8 @@ craftd tx gov submit-proposal test_proposal.json --from mykey --fees 200stake - sleep 5 -craftd tx gov vote 1 yes --from mykey --fees 200stake -y +craftd tx gov vote 5 yes --from mykey --fees 200stake -y sleep 20 -craftd tx exp spend 1000token --from mykey --fees 2000stake -y \ No newline at end of file +craftd tx exp spend 1000token --from vuong2 --fees 2000stake -y \ No newline at end of file diff --git a/x/exp/client/cli/tx.go b/x/exp/client/cli/tx.go index e0747b42..d7432bcb 100644 --- a/x/exp/client/cli/tx.go +++ b/x/exp/client/cli/tx.go @@ -102,7 +102,7 @@ func NewSpendIbcAssetForExpCmd() *cobra.Command { Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { srcPort := "ibc-exp" - srcChannel := "channel-1" + srcChannel := "channel-0" clientCtx, err := client.GetClientTxContext(cmd) if err != nil { return err @@ -139,7 +139,6 @@ func NewSpendIbcAssetForExpCmd() *cobra.Command { if err != nil { return err } - if !timeoutHeight.IsZero() { absoluteHeight := height absoluteHeight.RevisionNumber += timeoutHeight.RevisionNumber @@ -162,7 +161,7 @@ func NewSpendIbcAssetForExpCmd() *cobra.Command { return errors.New("timeoutTimestamp is not greater than current local clock time") } } - msg := types.NewMsgSpendIbcAssetToExp(clientCtx.GetFromAddress().String(), coins) + msg := types.NewMsgSpendIbcAssetToExp(clientCtx.GetFromAddress().String(), coins, timeoutHeight, timeoutTimestamp) return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg) }, diff --git a/x/exp/genesis.go b/x/exp/genesis.go index 5dd95247..97fdaa0a 100644 --- a/x/exp/genesis.go +++ b/x/exp/genesis.go @@ -28,6 +28,7 @@ func InitGenesis(ctx sdk.Context, keeper keeper.ExpKeeper, ak types.AccountKeepe } } keeper.SetDaoAssetInfo(ctx, *data.DaoAsset) + keeper.SetOracleID(ctx, 0) ak.GetModuleAccount(ctx, types.ModuleName) } diff --git a/x/exp/ibc_module.go b/x/exp/ibc_module.go index b292ac44..feadc8b2 100644 --- a/x/exp/ibc_module.go +++ b/x/exp/ibc_module.go @@ -181,12 +181,14 @@ func (am IBCModule) OnRecvPacket( if err != nil { return channeltypes.Acknowledgement{} } + if data.ResolveStatus == oracletypes.RESOLVE_STATUS_SUCCESS { var result resultData err := obi.Decode(data.Result, &result) if err != nil { return channeltypes.Acknowledgement{} } + switch result.Status { case "mint": err = am.keeper.ProccessRecvPacketMintRequest(ctx, result.AddressRequest, result.ExpPrice, oracleID) diff --git a/x/exp/keeper/execute_request.go b/x/exp/keeper/execute_request.go index 27338c19..cae919cc 100644 --- a/x/exp/keeper/execute_request.go +++ b/x/exp/keeper/execute_request.go @@ -5,7 +5,7 @@ import ( "github.com/notional-labs/craft/x/exp/types" ) -func (k ExpKeeper) GetMintRequest(ctx sdk.Context, accAddress sdk.AccAddress) (types.MintRequest, error) { +func (k ExpKeeper) GetMintRequest(ctx sdk.Context, accAddress sdk.AccAddress) (mintRequset types.MintRequest, found bool) { return k.GetMintRequestByKey(ctx, types.GetMintRequestAddressBytes(accAddress)) } @@ -60,7 +60,8 @@ func (k ExpKeeper) ExecuteBurnExp(ctx sdk.Context, burnRequest types.BurnRequest } func (k ExpKeeper) ExecuteMintExp(ctx sdk.Context, mintRequest types.MintRequest) error { - if mintRequest.DaoTokenMinted == sdk.NewDec(0) { + // must mint 1 exp + if sdk.NewDec(1).GTE(mintRequest.DaoTokenMinted) { mintRequest.Status = types.StatusNoFundRequest k.completeMintRequest(ctx, mintRequest) return nil @@ -70,6 +71,7 @@ func (k ExpKeeper) ExecuteMintExp(ctx sdk.Context, mintRequest types.MintRequest maxToken := sdk.NewCoin(k.GetDenom(ctx), mintRequest.DaoTokenMinted.TruncateInt()) err := k.verifyAccountToWhiteList(ctx, memberAccount) + if err == nil { err := k.addAddressToWhiteList(ctx, memberAccount, maxToken) if err != nil { @@ -107,6 +109,6 @@ func (k ExpKeeper) ValidateBurnRequestByTime(ctx sdk.Context, burnRequest types. } func (k ExpKeeper) ValidateMintRequestByTime(ctx sdk.Context, mintRequest types.MintRequest) bool { - mintPeriod := k.GetBurnExpPeriod(ctx) + mintPeriod := k.GetMintExpPeriod(ctx) return mintRequest.RequestTime.Add(mintPeriod).After(ctx.BlockTime()) } diff --git a/x/exp/keeper/exp_request.go b/x/exp/keeper/exp_request.go index 8a5d9837..ceff4d20 100644 --- a/x/exp/keeper/exp_request.go +++ b/x/exp/keeper/exp_request.go @@ -62,7 +62,8 @@ func (k ExpKeeper) GetDaoTokenPrice(ctx sdk.Context) sdk.Dec { func (k ExpKeeper) calculateDaoTokenValue(ctx sdk.Context, amount math.Int) sdk.Dec { daoTokenPrice := k.GetDaoTokenPrice(ctx) - return daoTokenPrice.MulInt(amount) + decCoin := sdk.NewDecFromInt(amount) + return decCoin.Quo(daoTokenPrice) } func (k ExpKeeper) SetBurnRequest(ctx sdk.Context, burnRequest types.BurnRequest) { @@ -219,21 +220,19 @@ func (k ExpKeeper) setEndedMintRequest(ctx sdk.Context, mintRequest types.MintRe store.Set(types.GetEndedMintRequestKey(accAddress), bz) } -func (k ExpKeeper) GetMintRequestByKey(ctx sdk.Context, key []byte) (types.MintRequest, error) { - var mintRequest types.MintRequest - +func (k ExpKeeper) GetMintRequestByKey(ctx sdk.Context, key []byte) (mintRequest types.MintRequest, found bool) { store := ctx.KVStore(k.storeKey) if !store.Has(key) { - return types.MintRequest{}, sdkerrors.Wrapf(types.ErrInvalidKey, "mintRequest") + return types.MintRequest{}, false } bz := store.Get(key) err := k.cdc.Unmarshal(bz, &mintRequest) if err != nil { - return types.MintRequest{}, err + return types.MintRequest{}, false } - return mintRequest, nil + return mintRequest, true } // IterateMintRequest iterates over the all the MintRequest and performs a callback function . @@ -276,10 +275,10 @@ func (k ExpKeeper) IterateStatusMintRequests(ctx sdk.Context, status int, cb fun // IncreaseOracleID increase oracle ID by 1. func (k ExpKeeper) IncreaseOracleID(ctx sdk.Context) { - k.setOracleID(ctx, k.GetNextOracleID(ctx)) + k.SetOracleID(ctx, k.GetNextOracleID(ctx)) } -func (k ExpKeeper) setOracleID(ctx sdk.Context, id uint64) { +func (k ExpKeeper) SetOracleID(ctx sdk.Context, id uint64) { store := ctx.KVStore(k.storeKey) store.Set(types.KeyOracleID, GetOracleIDBytes(id)) @@ -324,7 +323,10 @@ func (k ExpKeeper) SetNextOracleRequest(ctx sdk.Context, oracleRequest types.Ora // GetOracleRequest get oracle request by oracleID. func (k ExpKeeper) GetOracleRequest(ctx sdk.Context, oracleID uint64) (oracleRequest types.OracleRequest) { store := ctx.KVStore(k.storeKey) - bz := store.Get(GetOracleIDBytes(oracleID)) + key := types.KeyOracleRequest + key = append(key, GetOracleIDBytes(oracleID)...) + bz := store.Get(key) + if bz == nil { return types.OracleRequest{} } diff --git a/x/exp/keeper/keeper.go b/x/exp/keeper/keeper.go index 12d008a5..38243c70 100644 --- a/x/exp/keeper/keeper.go +++ b/x/exp/keeper/keeper.go @@ -36,8 +36,8 @@ type ExpKeeper struct { // oracleScriptCallData represents the data that should be OBI-encoded and sent to perform an oracle request. type oracleScriptCallData struct { - AddressRequest string `obi:"address_request"` RequestType string `obi:"request_type"` + AddressRequest string `obi:"address_request"` Status string `obi:"status"` } @@ -48,6 +48,7 @@ func NewKeeper( ak types.AccountKeeper, bk types.BankKeeper, pk types.PortKeeper, + ck types.ChannelKeeper, sk capabilitykeeper.ScopedKeeper, ) ExpKeeper { // ensure module account is set @@ -66,6 +67,7 @@ func NewKeeper( paramSpace: paramSpace, accountKeeper: ak, bankKeeper: bk, + channelKeeper: ck, portKeeper: pk, scopedKeeper: sk, } @@ -233,8 +235,8 @@ func (k ExpKeeper) SendIbcOracle(ctx sdk.Context, fromAddress string, coin sdk.C ) error { requestType := "exp_price" // get IBC params - sourcePort := "oracle" - sourceChannel := "channel-1" + sourcePort := "ibc-exp" + sourceChannel := "channel-0" sourceChannelEnd, found := k.channelKeeper.GetChannel(ctx, sourcePort, sourceChannel) if !found { @@ -271,8 +273,8 @@ func (k ExpKeeper) SendIbcOracle(ctx sdk.Context, fromAddress string, coin sdk.C } // Create the call data to be used data := oracleScriptCallData{ - AddressRequest: strings.ToLower(fromAddress), RequestType: requestType, + AddressRequest: strings.ToLower(fromAddress), Status: status, } @@ -294,6 +296,12 @@ func (k ExpKeeper) SendIbcOracle(ctx sdk.Context, fromAddress string, coin sdk.C 300000, // oraclePrams.ExecuteGas,need change to use gov param ) + _ = timeoutHeight + _ = timeoutTimestamp + + timeoutHeight = clienttypes.NewHeight(0, 100000000) + timeoutTimestamp = 0 + // Create the IBC packet packet := channeltypes.NewPacket( packetData.GetBytes(), diff --git a/x/exp/keeper/msgserver.go b/x/exp/keeper/msgserver.go index 5b182f0f..2e5fd1d7 100644 --- a/x/exp/keeper/msgserver.go +++ b/x/exp/keeper/msgserver.go @@ -180,9 +180,17 @@ func (k msgServer) SpendIbcAssetToExp(goCtx context.Context, msg *types.MsgSpend if len(msg.Amount) != 1 || msg.Amount[0].Denom != k.GetIbcDenom(ctx) { return nil, types.ErrDenomNotMatch } + mintRequest, found := k.GetMintRequest(ctx, fromAddress) + if !found { + return nil, types.ErrAddressdNotFound + } - // oracle for exp price + // verify time + if !k.ValidateMintRequestByTime(ctx, mintRequest) { + return nil, types.ErrTimeOut + } + // oracle for exp price err = k.ExpKeeper.SendIbcOracle(ctx, fromAddress.String(), msg.Amount[0], "mint", msg.TimeoutHeight, msg.TimeoutTimestamp) if err != nil { return nil, err diff --git a/x/exp/keeper/msgserver_test.go b/x/exp/keeper/msgserver_test.go index b8ad4e77..e6ffe23d 100644 --- a/x/exp/keeper/msgserver_test.go +++ b/x/exp/keeper/msgserver_test.go @@ -5,12 +5,10 @@ import ( "time" sdk "github.com/cosmos/cosmos-sdk/types" - abci "github.com/tendermint/tendermint/abci/types" - + govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" "github.com/notional-labs/craft/x/exp/keeper" "github.com/notional-labs/craft/x/exp/types" - - govtypes "github.com/cosmos/cosmos-sdk/x/gov/types" + abci "github.com/tendermint/tendermint/abci/types" ) var ( @@ -122,7 +120,7 @@ func (suite *KeeperTestSuite) TestJoinDaoByIbcAsset() { suite.Require().NoError(err) // check record - mintRequest, err := suite.App.ExpKeeper.GetMintRequest(suite.Ctx, suite.TestAccs[0]) + mintRequest, _ := suite.App.ExpKeeper.GetMintRequest(suite.Ctx, suite.TestAccs[0]) suite.Require().Equal(mintRequest.Account, suite.TestAccs[0].String()) suite.Require().Equal(mintRequest.DaoTokenLeft, sdk.NewDec(1000000)) suite.Require().Equal(mintRequest.DaoTokenMinted, sdk.NewDec(0)) diff --git a/x/exp/keeper/params.go b/x/exp/keeper/params.go index 2d7c0514..f6374ac6 100644 --- a/x/exp/keeper/params.go +++ b/x/exp/keeper/params.go @@ -27,7 +27,13 @@ func (k ExpKeeper) GetVestingPeriodEnd(ctx sdk.Context) (duration time.Duration) // GetBurnExpPeriod get's the BurnExpPeriod from the paramSpace . func (k ExpKeeper) GetBurnExpPeriod(ctx sdk.Context) (duration time.Duration) { - k.paramSpace.Get(ctx, types.ParamStoreKeyBurnPeriod, &duration) + k.paramSpace.Get(ctx, types.ParamStoreKeyClosePoolPeriod, &duration) + return duration +} + +// GetMintExpPeriod get's the BurnExpPeriod from the paramSpace . +func (k ExpKeeper) GetMintExpPeriod(ctx sdk.Context) (duration time.Duration) { + k.paramSpace.Get(ctx, types.ParamStoreKeyClosePoolPeriod, &duration) return duration } diff --git a/x/exp/keeper/relay.go b/x/exp/keeper/relay.go index 20eb0240..f6957834 100644 --- a/x/exp/keeper/relay.go +++ b/x/exp/keeper/relay.go @@ -1,7 +1,7 @@ package keeper import ( - "strconv" + "strings" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" @@ -26,18 +26,25 @@ func (k ExpKeeper) ProccessRecvPacketMintRequest(ctx sdk.Context, addressRequest } // set price to state - price, err := strconv.ParseInt(strExpPrice, 10, 64) + price, err := sdk.NewDecFromStr(strings.TrimSpace(strExpPrice)) if err != nil { return err } - k.setDaoTokenPrice(ctx, sdk.NewDec(price)) + k.setDaoTokenPrice(ctx, price) - mintRequest, err := k.GetMintRequest(ctx, accAddress) - oracleRequest := k.GetOracleRequest(ctx, oracleID) - if err != nil { - return err + mintRequest, found := k.GetMintRequest(ctx, accAddress) + + if !found { + return types.ErrAddressdNotFound + } + // verify time + if !k.ValidateMintRequestByTime(ctx, mintRequest) { + return types.ErrTimeOut } + + oracleRequest := k.GetOracleRequest(ctx, oracleID) + err = k.ExecuteMintExpByIbcToken(ctx, mintRequest, oracleRequest.AmountInRequest) if err != nil { return err @@ -52,12 +59,12 @@ func (k ExpKeeper) ProccessRecvPacketBurnRequest(ctx sdk.Context, addressRequest } // set price to state - price, err := strconv.ParseInt(strExpPrice, 10, 64) + price, err := sdk.NewDecFromStr(strExpPrice) if err != nil { return err } - k.setDaoTokenPrice(ctx, sdk.NewDec(price)) + k.setDaoTokenPrice(ctx, price) burnRequest, err := k.GetBurnRequest(ctx, accAddress) if err != nil { @@ -88,16 +95,21 @@ func (k ExpKeeper) ExecuteMintExpByIbcToken(ctx sdk.Context, mintRequest types.M mintRequest.DaoTokenMinted = mintRequest.DaoTokenLeft.Add(mintRequest.DaoTokenMinted) k.SetMintRequest(ctx, mintRequest) + + return nil } - err := k.FundPoolForExp(ctx, sdk.NewCoins(coin), sdk.AccAddress(mintRequest.Account)) + accAddress, err := sdk.AccAddressFromBech32(mintRequest.Account) + if err != nil { + return err + } + err = k.FundPoolForExp(ctx, sdk.NewCoins(coin), accAddress) if err != nil { return sdkerrors.Wrap(err, "fund error") } k.removeMintRequest(ctx, mintRequest) - decCoin := sdk.NewDecFromInt(coin.Amount) - mintRequest.DaoTokenMinted = mintRequest.DaoTokenMinted.Add(decCoin) - mintRequest.DaoTokenLeft = mintRequest.DaoTokenLeft.Sub(decCoin) + mintRequest.DaoTokenMinted = mintRequest.DaoTokenMinted.Add(expWillGet).TruncateDec() + mintRequest.DaoTokenLeft = mintRequest.DaoTokenLeft.Sub(expWillGet).TruncateDec() k.SetMintRequest(ctx, mintRequest) @@ -123,6 +135,10 @@ func (k ExpKeeper) SendBurnOracleRequest(ctx sdk.Context, burnRequest types.Burn RevisionNumber: 1, RevisionHeight: uint64(ctx.BlockHeight() + 100), } - err := k.SendIbcOracle(ctx, burnRequest.Account, *burnRequest.BurnTokenLeft, "burn", timeoutHeight, 0) - return err + err := k.SendIbcOracle(ctx, burnRequest.Account, *burnRequest.BurnTokenLeft, "burn", timeoutHeight, types.DefaultRelativePacketTimeoutTimestamp) + if err != nil { + return err + } + + return nil } diff --git a/x/exp/types/msgs.go b/x/exp/types/msgs.go index d0776e96..741a700e 100644 --- a/x/exp/types/msgs.go +++ b/x/exp/types/msgs.go @@ -4,6 +4,7 @@ import ( "github.com/cosmos/cosmos-sdk/codec/legacy" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + clienttypes "github.com/cosmos/ibc-go/v4/modules/core/02-client/types" ) var _ sdk.Msg = &MsgMintAndAllocateExp{} @@ -223,10 +224,12 @@ func (m MsgSpendIbcAssetToExp) ValidateBasic() error { return nil } -func NewMsgSpendIbcAssetToExp(fromAddress string, amount sdk.Coins) *MsgSpendIbcAssetToExp { +func NewMsgSpendIbcAssetToExp(fromAddress string, amount sdk.Coins, timeoutHeight clienttypes.Height, timeoutTimestamp uint64) *MsgSpendIbcAssetToExp { return &MsgSpendIbcAssetToExp{ - FromAddress: fromAddress, - Amount: amount, + FromAddress: fromAddress, + Amount: amount, + TimeoutHeight: timeoutHeight, + TimeoutTimestamp: timeoutTimestamp, } } diff --git a/x/exp/types/packet.go b/x/exp/types/packet.go index 49d96545..ede4c8f5 100644 --- a/x/exp/types/packet.go +++ b/x/exp/types/packet.go @@ -7,7 +7,7 @@ var ( // DefaultRelativePacketTimeoutHeight is the default packet timeout height (in blocks) relative // to the current block height of the counterparty chain provided by the client state. The // timeout is disabled when set to 0. - DefaultRelativePacketTimeoutHeight = "0-1000" + DefaultRelativePacketTimeoutHeight = "1-1000" // DefaultRelativePacketTimeoutTimestamp is the default packet timeout timestamp (in nanoseconds) // relative to the current block timestamp of the counterparty chain provided by the client diff --git a/x/exp/types/params.go b/x/exp/types/params.go index ca03b055..20397fc7 100644 --- a/x/exp/types/params.go +++ b/x/exp/types/params.go @@ -13,7 +13,7 @@ const ( // MUST MODIFY IN GENESIS WHEN MAINNET // After pass, ISO 8601 format for when they can no longer mint EXP from this proposal // TODO: Justify our choice of default here. - DefaultClosePoolPeriod time.Duration = time.Minute * 1 + DefaultClosePoolPeriod time.Duration = time.Minute * 100 // After pass, ISO 8601 format for when they can no longer burn EXP // TODO: Justify our choice of default here.