From 002059674c40ae42922505fa7581941c591fd5d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C5=A9=20Kh=C6=B0=C6=A1ng=20Duy?= Date: Sun, 30 Jun 2024 15:41:20 +0000 Subject: [PATCH] add handler --- config.yml | 28 +++++++------- x/loan/keeper/msg_server_approve_loan.go | 30 ++++++++++++--- x/loan/keeper/msg_server_cancel_loan.go | 28 +++++++++++--- x/loan/keeper/msg_server_liquidate_loan.go | 36 +++++++++++++++--- x/loan/keeper/msg_server_repay_loan.go | 39 +++++++++++++++++--- x/loan/keeper/msg_server_request_loan.go | 29 ++++++++++++--- x/loan/types/errors.go | 7 +--- x/loan/types/expected_keepers.go | 6 ++- x/loan/types/message_request_loan.go | 43 ++++++++++++++-------- 9 files changed, 180 insertions(+), 66 deletions(-) diff --git a/config.yml b/config.yml index 1bb23a6..8c3318a 100644 --- a/config.yml +++ b/config.yml @@ -1,22 +1,22 @@ version: 1 -validation: sovereign -accounts: -- name: alice - coins: - - 20000token - - 200000000stake -- name: bob - coins: - - 10000token - - 100000000stake +accounts: + - name: alice + coins: + - 20000token + - 10000foocoin + - 200000000stake + - name: bob + coins: + - 10000token + - 100000000stake client: openapi: path: docs/static/openapi.yml faucet: name: bob coins: - - 5token - - 100000stake + - 5token + - 100000stake validators: -- name: alice - bonded: 100000000stake + - name: alice + bonded: 100000000stake \ No newline at end of file diff --git a/x/loan/keeper/msg_server_approve_loan.go b/x/loan/keeper/msg_server_approve_loan.go index a496f34..5efe1bc 100644 --- a/x/loan/keeper/msg_server_approve_loan.go +++ b/x/loan/keeper/msg_server_approve_loan.go @@ -3,16 +3,34 @@ package keeper import ( "context" - "loan/x/loan/types" - + errorsmod "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + + "loan/x/loan/types" ) func (k msgServer) ApproveLoan(goCtx context.Context, msg *types.MsgApproveLoan) (*types.MsgApproveLoanResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) - - // TODO: Handling the message - _ = ctx - + loan, found := k.GetLoan(ctx, msg.Id) + if !found { + return nil, errorsmod.Wrapf(sdkerrors.ErrKeyNotFound, "key %d doesn't exist", msg.Id) + } + if loan.State != "requested" { + return nil, errorsmod.Wrapf(types.ErrWrongLoanState, "%v", loan.State) + } + lender, _ := sdk.AccAddressFromBech32(msg.Creator) + borrower, _ := sdk.AccAddressFromBech32(loan.Borrower) + amount, err := sdk.ParseCoinsNormalized(loan.Amount) + if err != nil { + return nil, errorsmod.Wrap(types.ErrWrongLoanState, "Cannot parse coins in loan amount") + } + err = k.bankKeeper.SendCoins(ctx, lender, borrower, amount) + if err != nil { + return nil, err + } + loan.Lender = msg.Creator + loan.State = "approved" + k.SetLoan(ctx, loan) return &types.MsgApproveLoanResponse{}, nil } diff --git a/x/loan/keeper/msg_server_cancel_loan.go b/x/loan/keeper/msg_server_cancel_loan.go index e003ed8..d5d4c95 100644 --- a/x/loan/keeper/msg_server_cancel_loan.go +++ b/x/loan/keeper/msg_server_cancel_loan.go @@ -3,16 +3,32 @@ package keeper import ( "context" - "loan/x/loan/types" - + errorsmod "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + + "loan/x/loan/types" ) func (k msgServer) CancelLoan(goCtx context.Context, msg *types.MsgCancelLoan) (*types.MsgCancelLoanResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) - - // TODO: Handling the message - _ = ctx - + loan, found := k.GetLoan(ctx, msg.Id) + if !found { + return nil, errorsmod.Wrapf(sdkerrors.ErrKeyNotFound, "key %d doesn't exist", msg.Id) + } + if loan.Borrower != msg.Creator { + return nil, errorsmod.Wrap(sdkerrors.ErrUnauthorized, "Cannot cancel: not the borrower") + } + if loan.State != "requested" { + return nil, errorsmod.Wrapf(types.ErrWrongLoanState, "%v", loan.State) + } + borrower, _ := sdk.AccAddressFromBech32(loan.Borrower) + collateral, _ := sdk.ParseCoinsNormalized(loan.Collateral) + err := k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, borrower, collateral) + if err != nil { + return nil, err + } + loan.State = "cancelled" + k.SetLoan(ctx, loan) return &types.MsgCancelLoanResponse{}, nil } diff --git a/x/loan/keeper/msg_server_liquidate_loan.go b/x/loan/keeper/msg_server_liquidate_loan.go index a417cf1..0e42777 100644 --- a/x/loan/keeper/msg_server_liquidate_loan.go +++ b/x/loan/keeper/msg_server_liquidate_loan.go @@ -2,17 +2,41 @@ package keeper import ( "context" + "strconv" - "loan/x/loan/types" - + errorsmod "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + + "loan/x/loan/types" ) func (k msgServer) LiquidateLoan(goCtx context.Context, msg *types.MsgLiquidateLoan) (*types.MsgLiquidateLoanResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) - - // TODO: Handling the message - _ = ctx - + loan, found := k.GetLoan(ctx, msg.Id) + if !found { + return nil, errorsmod.Wrapf(sdkerrors.ErrKeyNotFound, "key %d doesn't exist", msg.Id) + } + if loan.Lender != msg.Creator { + return nil, errorsmod.Wrap(sdkerrors.ErrUnauthorized, "Cannot liquidate: not the lender") + } + if loan.State != "approved" { + return nil, errorsmod.Wrapf(types.ErrWrongLoanState, "%v", loan.State) + } + lender, _ := sdk.AccAddressFromBech32(loan.Lender) + collateral, _ := sdk.ParseCoinsNormalized(loan.Collateral) + deadline, err := strconv.ParseInt(loan.Deadline, 10, 64) + if err != nil { + panic(err) + } + if ctx.BlockHeight() < deadline { + return nil, errorsmod.Wrap(types.ErrDeadline, "Cannot liquidate before deadline") + } + err = k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, lender, collateral) + if err != nil { + return nil, err + } + loan.State = "liquidated" + k.SetLoan(ctx, loan) return &types.MsgLiquidateLoanResponse{}, nil } diff --git a/x/loan/keeper/msg_server_repay_loan.go b/x/loan/keeper/msg_server_repay_loan.go index 77140a9..c06dd39 100644 --- a/x/loan/keeper/msg_server_repay_loan.go +++ b/x/loan/keeper/msg_server_repay_loan.go @@ -3,16 +3,43 @@ package keeper import ( "context" - "loan/x/loan/types" - + errorsmod "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" + + "loan/x/loan/types" ) func (k msgServer) RepayLoan(goCtx context.Context, msg *types.MsgRepayLoan) (*types.MsgRepayLoanResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) - - // TODO: Handling the message - _ = ctx - + loan, found := k.GetLoan(ctx, msg.Id) + if !found { + return nil, errorsmod.Wrapf(sdkerrors.ErrKeyNotFound, "key %d doesn't exist", msg.Id) + } + if loan.State != "approved" { + return nil, errorsmod.Wrapf(types.ErrWrongLoanState, "%v", loan.State) + } + lender, _ := sdk.AccAddressFromBech32(loan.Lender) + borrower, _ := sdk.AccAddressFromBech32(loan.Borrower) + if msg.Creator != loan.Borrower { + return nil, errorsmod.Wrap(sdkerrors.ErrUnauthorized, "Cannot repay: not the borrower") + } + amount, _ := sdk.ParseCoinsNormalized(loan.Amount) + fee, _ := sdk.ParseCoinsNormalized(loan.Fee) + collateral, _ := sdk.ParseCoinsNormalized(loan.Collateral) + err := k.bankKeeper.SendCoins(ctx, borrower, lender, amount) + if err != nil { + return nil, err + } + err = k.bankKeeper.SendCoins(ctx, borrower, lender, fee) + if err != nil { + return nil, err + } + err = k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, borrower, collateral) + if err != nil { + return nil, err + } + loan.State = "repayed" + k.SetLoan(ctx, loan) return &types.MsgRepayLoanResponse{}, nil } diff --git a/x/loan/keeper/msg_server_request_loan.go b/x/loan/keeper/msg_server_request_loan.go index 73786f4..e054c2a 100644 --- a/x/loan/keeper/msg_server_request_loan.go +++ b/x/loan/keeper/msg_server_request_loan.go @@ -3,16 +3,33 @@ package keeper import ( "context" - "loan/x/loan/types" - sdk "github.com/cosmos/cosmos-sdk/types" + + "loan/x/loan/types" ) func (k msgServer) RequestLoan(goCtx context.Context, msg *types.MsgRequestLoan) (*types.MsgRequestLoanResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) - - // TODO: Handling the message - _ = ctx - + var loan = types.Loan{ + Amount: msg.Amount, + Fee: msg.Fee, + Collateral: msg.Collateral, + Deadline: msg.Deadline, + State: "requested", + Borrower: msg.Creator, + } + borrower, err := sdk.AccAddressFromBech32(msg.Creator) + if err != nil { + panic(err) + } + collateral, err := sdk.ParseCoinsNormalized(loan.Collateral) + if err != nil { + panic(err) + } + sdkError := k.bankKeeper.SendCoinsFromAccountToModule(ctx, borrower, types.ModuleName, collateral) + if sdkError != nil { + return nil, sdkError + } + k.AppendLoan(ctx, loan) return &types.MsgRequestLoanResponse{}, nil } diff --git a/x/loan/types/errors.go b/x/loan/types/errors.go index 2e8e406..b57629d 100644 --- a/x/loan/types/errors.go +++ b/x/loan/types/errors.go @@ -1,13 +1,10 @@ package types -// DONTCOVER - import ( sdkerrors "cosmossdk.io/errors" ) -// x/loan module sentinel errors var ( - ErrInvalidSigner = sdkerrors.Register(ModuleName, 1100, "expected gov account as only signer for proposal message") - ErrSample = sdkerrors.Register(ModuleName, 1101, "sample error") + ErrWrongLoanState = sdkerrors.Register(ModuleName, 2, "wrong loan state") + ErrDeadline = sdkerrors.Register(ModuleName, 3, "deadline") ) diff --git a/x/loan/types/expected_keepers.go b/x/loan/types/expected_keepers.go index 4a50d01..aa12d6d 100644 --- a/x/loan/types/expected_keepers.go +++ b/x/loan/types/expected_keepers.go @@ -14,8 +14,10 @@ type AccountKeeper interface { // BankKeeper defines the expected interface for the Bank module. type BankKeeper interface { - SpendableCoins(context.Context, sdk.AccAddress) sdk.Coins - // Methods imported from bank should be defined here + // SpendableCoins(ctx sdk.Context, addr sdk.AccAddress) sdk.Coins + SendCoins(ctx context.Context, fromAddr sdk.AccAddress, toAddr sdk.AccAddress, amt sdk.Coins) error + SendCoinsFromAccountToModule(ctx context.Context, senderAddr sdk.AccAddress, recipientModule string, amt sdk.Coins) error + SendCoinsFromModuleToAccount(ctx context.Context, senderModule string, recipientAddr sdk.AccAddress, amt sdk.Coins) error } // ParamSubspace defines the expected Subspace interface for parameters. diff --git a/x/loan/types/message_request_loan.go b/x/loan/types/message_request_loan.go index 31f048d..f27ec03 100644 --- a/x/loan/types/message_request_loan.go +++ b/x/loan/types/message_request_loan.go @@ -1,27 +1,40 @@ -package types - import ( + "strconv" + errorsmod "cosmossdk.io/errors" sdk "github.com/cosmos/cosmos-sdk/types" sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ) -var _ sdk.Msg = &MsgRequestLoan{} - -func NewMsgRequestLoan(creator string, amount string, fee string, collateral string, deadline string) *MsgRequestLoan { - return &MsgRequestLoan{ - Creator: creator, - Amount: amount, - Fee: fee, - Collateral: collateral, - Deadline: deadline, - } -} - func (msg *MsgRequestLoan) ValidateBasic() error { _, err := sdk.AccAddressFromBech32(msg.Creator) if err != nil { return errorsmod.Wrapf(sdkerrors.ErrInvalidAddress, "invalid creator address (%s)", err) } + amount, _ := sdk.ParseCoinsNormalized(msg.Amount) + if !amount.IsValid() { + return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "amount is not a valid Coins object") + } + if amount.Empty() { + return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "amount is empty") + } + fee, _ := sdk.ParseCoinsNormalized(msg.Fee) + if !fee.IsValid() { + return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "fee is not a valid Coins object") + } + deadline, err := strconv.ParseInt(msg.Deadline, 10, 64) + if err != nil { + return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "deadline is not an integer") + } + if deadline <= 0 { + return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "deadline should be a positive integer") + } + collateral, _ := sdk.ParseCoinsNormalized(msg.Collateral) + if !collateral.IsValid() { + return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "collateral is not a valid Coins object") + } + if collateral.Empty() { + return errorsmod.Wrap(sdkerrors.ErrInvalidRequest, "collateral is empty") + } return nil -} +} \ No newline at end of file