(goCtx context.Context, msg *types.MsgRemoveLiquidityUnits)
| 772 | } |
| 773 | |
| 774 | func (k msgServer) RemoveLiquidityUnits(goCtx context.Context, msg *types.MsgRemoveLiquidityUnits) (*types.MsgRemoveLiquidityUnitsResponse, error) { |
| 775 | ctx := sdk.UnwrapSDKContext(goCtx) |
| 776 | registry := k.tokenRegistryKeeper.GetRegistry(ctx) |
| 777 | eAsset, err := k.tokenRegistryKeeper.GetEntry(registry, msg.ExternalAsset.Symbol) |
| 778 | if err != nil { |
| 779 | return nil, types.ErrTokenNotSupported |
| 780 | } |
| 781 | if !k.tokenRegistryKeeper.CheckEntryPermissions(eAsset, []tokenregistrytypes.Permission{tokenregistrytypes.Permission_CLP}) { |
| 782 | return nil, tokenregistrytypes.ErrPermissionDenied |
| 783 | } |
| 784 | pool, err := k.Keeper.GetPool(ctx, msg.ExternalAsset.Symbol) |
| 785 | if err != nil { |
| 786 | return nil, types.ErrPoolDoesNotExist |
| 787 | } |
| 788 | //Get LP |
| 789 | lp, err := k.Keeper.GetLiquidityProvider(ctx, msg.ExternalAsset.Symbol, msg.Signer) |
| 790 | if err != nil { |
| 791 | return nil, types.ErrLiquidityProviderDoesNotExist |
| 792 | } |
| 793 | |
| 794 | // ensure requested removal amount is less than available - what is already on the queue |
| 795 | lpQueuedUnits := k.GetRemovalQueueUnitsForLP(ctx, lp) |
| 796 | if msg.WithdrawUnits.GT(lp.LiquidityProviderUnits.Sub(lpQueuedUnits)) { |
| 797 | return nil, sdkerrors.Wrap(types.ErrUnableToRemoveLiquidity, fmt.Sprintf("WithdrawUnits %s greater than total LP units %s minus queued removals", msg.WithdrawUnits, lp.LiquidityProviderUnits)) |
| 798 | } |
| 799 | |
| 800 | pmtpCurrentRunningRate := k.GetPmtpRateParams(ctx).PmtpCurrentRunningRate |
| 801 | swapFeeRate := k.GetSwapFeeRate(ctx, *msg.ExternalAsset, false) |
| 802 | // Prune pools |
| 803 | params := k.GetRewardsParams(ctx) |
| 804 | k.PruneUnlockRecords(ctx, &lp, params.LiquidityRemovalLockPeriod, params.LiquidityRemovalCancelPeriod) |
| 805 | |
| 806 | nativeAssetDepth, externalAssetDepth := pool.ExtractDebt(pool.NativeAssetBalance, pool.ExternalAssetBalance, false) |
| 807 | |
| 808 | //Calculate amount to withdraw |
| 809 | withdrawNativeAssetAmount, withdrawExternalAssetAmount, lpUnitsLeft := CalculateWithdrawalFromUnits(pool.PoolUnits, |
| 810 | nativeAssetDepth.String(), externalAssetDepth.String(), lp.LiquidityProviderUnits.String(), |
| 811 | msg.WithdrawUnits) |
| 812 | |
| 813 | err = k.Keeper.UseUnlockedLiquidity(ctx, lp, lp.LiquidityProviderUnits.Sub(lpUnitsLeft), false) |
| 814 | if err != nil { |
| 815 | return nil, err |
| 816 | } |
| 817 | |
| 818 | // Skip pools that are not margin enabled, to avoid health being zero and queueing being triggered. |
| 819 | if k.GetMarginKeeper().IsPoolEnabled(ctx, eAsset.Denom) { |
| 820 | extRowanValue := CalculateWithdrawalRowanValue(withdrawExternalAssetAmount, types.GetSettlementAsset(), pool, pmtpCurrentRunningRate, swapFeeRate) |
| 821 | |
| 822 | futurePool := pool |
| 823 | futurePool.NativeAssetBalance = futurePool.NativeAssetBalance.Sub(withdrawNativeAssetAmount) |
| 824 | futurePool.ExternalAssetBalance = futurePool.ExternalAssetBalance.Sub(withdrawExternalAssetAmount) |
| 825 | if k.GetMarginKeeper().CalculatePoolHealth(&futurePool).LT(k.GetMarginKeeper().GetRemovalQueueThreshold(ctx)) { |
| 826 | if k.IsRemovalQueueEnabled(ctx) { |
| 827 | k.QueueRemoval(ctx, &types.MsgRemoveLiquidity{ |
| 828 | Signer: msg.Signer, |
| 829 | ExternalAsset: msg.ExternalAsset, |
| 830 | WBasisPoints: ConvUnitsToWBasisPoints(lp.LiquidityProviderUnits, msg.WithdrawUnits), |
| 831 | Asymmetry: sdk.ZeroInt(), |
nothing calls this directly
no test coverage detected