-
Notifications
You must be signed in to change notification settings - Fork 44
/
msg_server_withdraw.go
119 lines (107 loc) · 4.07 KB
/
msg_server_withdraw.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
package keeper
import (
"context"
"cosmossdk.io/errors"
"cosmossdk.io/math"
sdk "github.com/cosmos/cosmos-sdk/types"
upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types"
"github.com/bnb-chain/greenfield/x/payment/types"
)
func (k msgServer) Withdraw(goCtx context.Context, msg *types.MsgWithdraw) (*types.MsgWithdrawResponse, error) {
ctx := sdk.UnwrapSDKContext(goCtx)
creator := sdk.MustAccAddressFromHex(msg.Creator)
if ctx.IsUpgraded(upgradetypes.Nagqu) {
if msg.From == "" { // withdraw from the locked one
delayedWithdrawal, found := k.GetDelayedWithdrawalRecord(ctx, creator)
if !found {
return nil, errors.Wrapf(types.ErrNoDelayedWithdrawal, "delayed withdrawal not found %s", creator.String())
}
if !delayedWithdrawal.Amount.Equal(msg.Amount) {
return nil, errors.Wrapf(types.ErrIncorrectWithdrawAmount, "withdrawal amount should be equal to the delayed %s", delayedWithdrawal.Amount)
}
now := ctx.BlockTime().Unix()
end := delayedWithdrawal.UnlockTimestamp
if now <= end {
return nil, errors.Wrapf(types.ErrNotReachTimeLockDuration, "delayed withdrawal should be after %d", end)
}
k.RemoveDelayedWithdrawalRecord(ctx, creator)
// withdraw it from module account directly
delayedFrom := sdk.MustAccAddressFromHex(delayedWithdrawal.From)
err := k.bankTransfer(ctx, creator, delayedFrom, msg.Amount)
if err != nil {
return nil, err
}
return &types.MsgWithdrawResponse{}, nil
}
}
from := sdk.MustAccAddressFromHex(msg.From)
// check stream record
streamRecord, found := k.Keeper.GetStreamRecord(ctx, from)
if !found {
return nil, types.ErrStreamRecordNotFound
}
// check status
if streamRecord.Status == types.STREAM_ACCOUNT_STATUS_FROZEN {
return nil, errors.Wrapf(types.ErrInvalidStreamAccountStatus, "stream record is frozen")
}
// check whether creator can withdraw
if !creator.Equals(from) {
paymentAccount, found := k.Keeper.GetPaymentAccount(ctx, from)
if !found {
return nil, types.ErrPaymentAccountNotFound
}
owner := sdk.MustAccAddressFromHex(paymentAccount.Owner)
if !creator.Equals(owner) {
return nil, types.ErrNotPaymentAccountOwner
}
if !paymentAccount.Refundable {
return nil, types.ErrPaymentAccountAlreadyNonRefundable
}
}
change := types.NewDefaultStreamRecordChangeWithAddr(from).WithStaticBalanceChange(msg.Amount.Neg())
err := k.UpdateStreamRecord(ctx, streamRecord, change)
if err != nil {
return nil, err
}
k.SetStreamRecord(ctx, streamRecord)
if streamRecord.StaticBalance.IsNegative() {
return nil, errors.Wrapf(types.ErrInsufficientBalance, "static balance: %s after withdraw", streamRecord.StaticBalance)
}
if ctx.IsUpgraded(upgradetypes.Nagqu) {
params := k.GetParams(ctx)
if msg.Amount.GTE(*params.WithdrawTimeLockThreshold) {
// check whether there is delayed withdrawal, if there is delayed withdrawal, must withdraw it firstly
if _, found := k.GetDelayedWithdrawalRecord(ctx, creator); found {
return nil, errors.Wrapf(types.ErrExistsDelayedWithdrawal, "delayed withdrawal should be proceed firstly %s", creator.String())
}
delayedWithdrawal := &types.DelayedWithdrawalRecord{
Addr: creator.String(),
Amount: msg.Amount,
From: from.String(),
UnlockTimestamp: ctx.BlockTime().Unix() + int64(params.WithdrawTimeLockDuration),
}
k.SetDelayedWithdrawalRecord(ctx, delayedWithdrawal)
return &types.MsgWithdrawResponse{}, nil // user can query `DelayedWithdrawal` to find the details
}
}
// bank transfer
err = k.bankTransfer(ctx, creator, from, msg.Amount)
if err != nil {
return nil, err
}
return &types.MsgWithdrawResponse{}, nil
}
func (k msgServer) bankTransfer(ctx sdk.Context, creator, from sdk.AccAddress, amount math.Int) error {
coins := sdk.NewCoins(sdk.NewCoin(k.GetParams(ctx).FeeDenom, amount))
err := k.bankKeeper.SendCoinsFromModuleToAccount(ctx, types.ModuleName, creator, coins)
if err != nil {
return err
}
// emit event
err = ctx.EventManager().EmitTypedEvents(&types.EventWithdraw{
From: from.String(),
To: creator.String(),
Amount: amount,
})
return err
}