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

Impl WithdrawMargin message #628

Merged
merged 4 commits into from
Jun 25, 2023
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
9 changes: 9 additions & 0 deletions proto/ununifi/derivatives/tx.proto
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ service Msg {
rpc ReportLiquidation(MsgReportLiquidation) returns (MsgReportLiquidationResponse);
rpc ReportLevyPeriod(MsgReportLevyPeriod) returns (MsgReportLevyPeriodResponse);
rpc AddMargin(MsgAddMargin) returns (MsgAddMarginResponse);
rpc WithdrawMargin(MsgWithdrawMargin) returns (MsgWithdrawMarginResponse);
}

message MsgDepositToPool {
Expand Down Expand Up @@ -79,3 +80,11 @@ message MsgAddMargin {
}

message MsgAddMarginResponse {}

message MsgWithdrawMargin {
string sender = 1 [(gogoproto.moretags) = "yaml:\"sender\""];
cosmos.base.v1beta1.Coin amount = 2 [(gogoproto.moretags) = "yaml:\"amount\"", (gogoproto.nullable) = false];
string position_id = 3 [(gogoproto.moretags) = "yaml:\"position_id\""];
}

message MsgWithdrawMarginResponse {}
6 changes: 6 additions & 0 deletions scripts/commands/derivatives/msgs.sh
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,12 @@ $BINARY tx derivatives withdraw-from-pool 1 ubtc --from=$USER1 $conf | jq .raw_l
$BINARY tx derivatives open-position perpetual-futures 100ubtc ubtc uusdc long --from=$USER1 $conf | jq .raw_log | sed 's/\\n/\n/g'
$BINARY tx derivatives open-position perpetual-futures 100uusdc ubtc uusdc short --from=$USER1 $conf | jq .raw_log | sed 's/\\n/\n/g'

# add-margin
$BINARY tx derivatives add-margin 1 100ubtc --from=$USER1 $conf | jq .raw_log | sed 's/\\n/\n/g'

# withdraw-margin
$BINARY tx derivatives withdraw-margin 1 100ubtc --from=$USER1 $conf | jq .raw_log | sed 's/\\n/\n/g'

# query positions
$BINARY q derivatives positions $USER_ADDRESS_1

Expand Down
44 changes: 44 additions & 0 deletions x/derivatives/keeper/margin.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,50 @@ func (k Keeper) AddMargin(ctx sdk.Context, sender sdk.AccAddress, positionId str
return nil
}

func (k Keeper) WithdrawMargin(ctx sdk.Context, withdrawer sdk.AccAddress, positionId string, amount sdk.Coin) error {
position := k.GetPositionWithId(ctx, positionId)
if position == nil {
return types.ErrPositionDoesNotExist
}

// Check withdrawer (sender) matches the owner of the position
if position.Address != withdrawer.String() {
return types.ErrUnauthorized
}

// Update RemainingMargin
position.RemainingMargin = position.RemainingMargin.Sub(amount)

// Check if the updated margin is positive
if position.RemainingMargin.IsNegative() {
return types.ErrNegativeMargin
}

// Check if the updated margin is not under liquidation
params := k.GetParams(ctx)
currentBaseUsdRate, currentQuoteUsdRate, err := k.GetPairUsdPriceFromMarket(ctx, position.Market)
if err != nil {
return err
}
quoteTicker := k.GetPoolQuoteTicker(ctx)
baseMetricsRate := types.NewMetricsRateType(quoteTicker, position.Market.BaseDenom, currentBaseUsdRate)
quoteMetricsRate := types.NewMetricsRateType(quoteTicker, position.Market.QuoteDenom, currentQuoteUsdRate)

if position.NeedLiquidation(params.PerpetualFutures.MarginMaintenanceRate, baseMetricsRate, quoteMetricsRate) {
// Return err as the result of this tx
return types.ErrTooMuchMarginToWithdraw
}

// Send margin from the margin manager module account to the withdrawer
if err := k.SendBackMargin(ctx, withdrawer, sdk.NewCoins(amount)); err != nil {
return err
}

k.SetPosition(ctx, *position)

return nil
}

func (k Keeper) SendMarginToMarginManager(ctx sdk.Context, sender sdk.AccAddress, margin sdk.Coins) error {
return k.bankKeeper.SendCoinsFromAccountToModule(ctx, sender, types.MarginManager, margin)
}
Expand Down
6 changes: 6 additions & 0 deletions x/derivatives/keeper/margin_test.go
Original file line number Diff line number Diff line change
@@ -1 +1,7 @@
package keeper_test

// TODO: Impl unit test
// func (suite *KeeperTestSuite) TestAddMargin() {}

// TODO: Impl unit test
// func (suite *KeeperTestSuite) TestWithdrawMargin() {}
11 changes: 11 additions & 0 deletions x/derivatives/keeper/msg_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,14 @@ func (k msgServer) AddMargin(c context.Context, msg *types.MsgAddMargin) (*types

return &types.MsgAddMarginResponse{}, nil
}

func (k msgServer) WithdrawMargin(c context.Context, msg *types.MsgWithdrawMargin) (*types.MsgWithdrawMarginResponse, error) {
ctx := sdk.UnwrapSDKContext(c)

err := k.Keeper.WithdrawMargin(ctx, sdk.AccAddress(msg.Sender), msg.PositionId, msg.Amount)
if err != nil {
return nil, err
}

return &types.MsgWithdrawMarginResponse{}, nil
}
2 changes: 2 additions & 0 deletions x/derivatives/types/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,6 @@ var (
ErrPositionDoesNotExist = sdkerrors.Register(ModuleName, 15, "position does not exist")
ErrLiquidationNotNeeded = sdkerrors.Register(ModuleName, 16, "liquidation is not needed")
ErrLiquidationNeeded = sdkerrors.Register(ModuleName, 17, "liquidation is needed")
ErrUnauthorized = sdkerrors.Register(ModuleName, 18, "unauthorized")
ErrTooMuchMarginToWithdraw = sdkerrors.Register(ModuleName, 19, "too much margin to withdraw")
)
26 changes: 26 additions & 0 deletions x/derivatives/types/msgs.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,3 +176,29 @@ func (msg MsgAddMargin) GetSigners() []sdk.AccAddress {
addr, _ := sdk.AccAddressFromBech32(msg.Sender)
return []sdk.AccAddress{addr}
}

var _ sdk.Msg = &MsgWithdrawMargin{}

func NewMsgWithdrawMargin(sender string, positionId string, amount sdk.Coin) MsgWithdrawMargin {
return MsgWithdrawMargin{
Sender: sender,
PositionId: positionId,
Amount: amount,
}
}

// ValidateBasic does a simple validation check that doesn't require access to state.
func (msg MsgWithdrawMargin) ValidateBasic() error {
_, err := sdk.AccAddressFromBech32(msg.Sender)
if err != nil {
return sdkerrors.Wrap(sdkerrors.ErrInvalidAddress, "sender address is not valid")
}

return nil
}

// GetSigners returns the addresses of signers that must sign.
func (msg MsgWithdrawMargin) GetSigners() []sdk.AccAddress {
addr, _ := sdk.AccAddressFromBech32(msg.Sender)
return []sdk.AccAddress{addr}
}
Loading