Repay a loan
The RepayLoan method is responsible for handling the repayment of a loan. This
involves transferring the borrowed funds, along with any agreed upon fees, from
the borrower to the lender. In addition, the collateral that was provided as
part of the loan agreement will be released from the escrow account and returned
to the borrower.
It is important to note that the RepayLoan method can only be called under
certain conditions. Firstly, the transaction must be signed by the borrower of
the loan. This ensures that only the borrower has the ability to initiate the
repayment process. Secondly, the loan must be in an approved status. This means
that the loan has received approval and is ready to be repaid.
To implement the RepayLoan method, we must ensure that these conditions are
met before proceeding with the repayment process. Once the necessary checks have
been performed, the method can then handle the transfer of funds and the release
of the collateral from the escrow account.
Keeper method
package keeper
import (
"context"
"fmt"
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)
loan, found := k.GetLoan(ctx, msg.Id)
if !found {
return nil, sdkerrors.Wrapf(sdkerrors.ErrKeyNotFound, "key %d doesn't exist", msg.Id)
}
if loan.State != "approved" {
return nil, sdkerrors.Wrapf(types.ErrWrongLoanState, "%v", loan.State)
}
lender, _ := sdk.AccAddressFromBech32(loan.Lender)
borrower, _ := sdk.AccAddressFromBech32(loan.Borrower)
if msg.Creator != loan.Borrower {
return nil, sdkerrors.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
}
RepayLoan takes in two arguments: a context and a pointer to a
types.MsgRepayLoan type. It returns a pointer to a
types.MsgRepayLoanResponse type and an error.
The method first retrieves a loan from storage by passing the provided loan ID
to the k.GetLoan method. If the loan cannot be found, the method returns an
error wrapped in a sdkerrors.ErrKeyNotFound error.
The method then checks that the state of the loan is "approved". If it is not,
the method returns an error wrapped in a types.ErrWrongLoanState error.
Next, the method converts the lender and borrower addresses stored in the loan
struct to sdk.AccAddress types using the sdk.AccAddressFromBech32 function.
It then checks that the transaction is signed by the borrower of the loan by
comparing the msg.Creator field to the borrower address stored in the loan
struct. If these do not match, the method returns an error wrapped in a
sdkerrors.ErrUnauthorized error.
The method then parses the loan amount, fee, and collateral stored in the loan
struct as sdk.Coins using the sdk.ParseCoinsNormalized function. It then
uses the k.bankKeeper.SendCoins function to transfer the loan amount and fee
from the borrower to the lender. It then uses the
k.bankKeeper.SendCoinsFromModuleToAccount function to transfer the collateral
from the escrow account to the borrower.
Finally, the method updates the state of the loan to "repayed" and stores the
updated loan in storage using the k.SetLoan method. The method returns a
types.MsgRepayLoanResponse and a nil error to indicate that the repayment
process was successful.