/
pool.go
339 lines (281 loc) · 9.94 KB
/
pool.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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
package keeper
import (
"fmt"
gogotypes "github.com/gogo/protobuf/types"
errorsmod "cosmossdk.io/errors"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
"github.com/osmosis-labs/osmosis/osmoutils"
"github.com/osmosis-labs/osmosis/v16/x/gamm/pool-models/balancer"
"github.com/osmosis-labs/osmosis/v16/x/gamm/pool-models/stableswap"
"github.com/osmosis-labs/osmosis/v16/x/gamm/types"
poolmanagertypes "github.com/osmosis-labs/osmosis/v16/x/poolmanager/types"
)
func (k Keeper) MarshalPool(pool poolmanagertypes.PoolI) ([]byte, error) {
return k.cdc.MarshalInterface(pool)
}
func (k Keeper) UnmarshalPool(bz []byte) (types.CFMMPoolI, error) {
var acc types.CFMMPoolI
return acc, k.cdc.UnmarshalInterface(bz, &acc)
}
// GetPool returns a pool with a given id.
func (k Keeper) GetPool(ctx sdk.Context, poolId uint64) (poolmanagertypes.PoolI, error) {
return k.GetPoolAndPoke(ctx, poolId)
}
func (k Keeper) GetPools(ctx sdk.Context) ([]poolmanagertypes.PoolI, error) {
return osmoutils.GatherValuesFromStorePrefix(ctx.KVStore(k.storeKey), types.KeyPrefixPools, func(bz []byte) (poolmanagertypes.PoolI, error) {
pool, err := k.UnmarshalPool(bz)
if err != nil {
return nil, err
}
if pokePool, ok := pool.(types.WeightedPoolExtension); ok {
pokePool.PokePool(ctx.BlockTime())
}
return pool, nil
})
}
// GetPoolAndPoke returns a PoolI based on it's identifier if one exists. If poolId corresponds
// to a pool with weights (e.g. balancer), the weights of the pool are updated via PokePool prior to returning.
// TODO: Consider rename to GetPool due to downstream API confusion.
func (k Keeper) GetPoolAndPoke(ctx sdk.Context, poolId uint64) (types.CFMMPoolI, error) {
store := ctx.KVStore(k.storeKey)
poolKey := types.GetKeyPrefixPools(poolId)
if !store.Has(poolKey) {
return nil, types.PoolDoesNotExistError{PoolId: poolId}
}
bz := store.Get(poolKey)
pool, err := k.UnmarshalPool(bz)
if err != nil {
return nil, err
}
if pokePool, ok := pool.(types.WeightedPoolExtension); ok {
pokePool.PokePool(ctx.BlockTime())
}
return pool, nil
}
// GetCFMMPool gets CFMMPool and checks if the pool is active, i.e. allowed to be swapped against.
// The difference from GetPools is that this function returns an error if the pool is inactive.
// Additionally, it returns x/gamm specific CFMMPool type.
func (k Keeper) GetCFMMPool(ctx sdk.Context, poolId uint64) (types.CFMMPoolI, error) {
pool, err := k.GetPoolAndPoke(ctx, poolId)
if err != nil {
return &balancer.Pool{}, err
}
if !pool.IsActive(ctx) {
return &balancer.Pool{}, errorsmod.Wrapf(types.ErrPoolLocked, "swap on inactive pool")
}
return pool, nil
}
func (k Keeper) iterator(ctx sdk.Context, prefix []byte) sdk.Iterator {
store := ctx.KVStore(k.storeKey)
return sdk.KVStorePrefixIterator(store, prefix)
}
func (k Keeper) GetPoolsAndPoke(ctx sdk.Context) (res []types.CFMMPoolI, err error) {
iter := k.iterator(ctx, types.KeyPrefixPools)
defer iter.Close()
for ; iter.Valid(); iter.Next() {
bz := iter.Value()
pool, err := k.UnmarshalPool(bz)
if err != nil {
return nil, err
}
if pokePool, ok := pool.(types.WeightedPoolExtension); ok {
pokePool.PokePool(ctx.BlockTime())
}
res = append(res, pool)
}
return res, nil
}
func (k Keeper) setPool(ctx sdk.Context, pool poolmanagertypes.PoolI) error {
bz, err := k.MarshalPool(pool)
if err != nil {
return err
}
store := ctx.KVStore(k.storeKey)
poolKey := types.GetKeyPrefixPools(pool.GetId())
store.Set(poolKey, bz)
return nil
}
// OverwritePoolV15MigrationUnsafe is a temporary method for calling from the v15 upgrade handler
// for balancer to stableswap pool migration. Do not use for other purposes.
func (k Keeper) OverwritePoolV15MigrationUnsafe(ctx sdk.Context, pool poolmanagertypes.PoolI) error {
return k.setPool(ctx, pool)
}
func (k Keeper) DeletePool(ctx sdk.Context, poolId uint64) error {
store := ctx.KVStore(k.storeKey)
poolKey := types.GetKeyPrefixPools(poolId)
if !store.Has(poolKey) {
return fmt.Errorf("pool with ID %d does not exist", poolId)
}
store.Delete(poolKey)
return nil
}
// CleanupBalancerPool destructs a pool and refund all the assets according to
// the shares held by the accounts. CleanupBalancerPool should not be called during
// the chain execution time, as it iterates the entire account balances.
// TODO: once SDK v0.46.0, use https://github.com/cosmos/cosmos-sdk/pull/9611
//
// All locks on this pool share must be unlocked prior to execution. Use LockupKeeper.ForceUnlock
// on remaining locks before calling this function.
// func (k Keeper) CleanupBalancerPool(ctx sdk.Context, poolIds []uint64, excludedModules []string) (err error) {
// pools := make(map[string]types.CFMMPoolI)
// totalShares := make(map[string]sdk.Int)
// for _, poolId := range poolIds {
// pool, err := k.GetPool(ctx, poolId)
// if err != nil {
// return err
// }
// shareDenom := pool.GetTotalShares().Denom
// pools[shareDenom] = pool
// totalShares[shareDenom] = pool.GetTotalShares().Amount
// }
// moduleAccounts := make(map[string]string)
// for _, module := range excludedModules {
// moduleAccounts[string(authtypes.NewModuleAddress(module))] = module
// }
// // first iterate through the share holders and burn them
// k.bankKeeper.IterateAllBalances(ctx, func(addr sdk.AccAddress, coin sdk.Coin) (stop bool) {
// if coin.Amount.IsZero() {
// return
// }
// pool, ok := pools[coin.Denom]
// if !ok {
// return
// }
// // track the iterated shares
// pool.SubTotalShares(coin.Amount)
// pools[coin.Denom] = pool
// // check if the shareholder is a module
// if _, ok = moduleAccounts[coin.Denom]; ok {
// return
// }
// // Burn the share tokens
// err = k.bankKeeper.SendCoinsFromAccountToModule(ctx, addr, types.ModuleName, sdk.Coins{coin})
// if err != nil {
// return true
// }
// err = k.bankKeeper.BurnCoins(ctx, types.ModuleName, sdk.Coins{coin})
// if err != nil {
// return true
// }
// // Refund assets
// for _, asset := range pool.GetAllPoolAssets() {
// // lpShareEquivalentTokens = (amount in pool) * (your shares) / (total shares)
// lpShareEquivalentTokens := asset.Token.Amount.Mul(coin.Amount).Quo(totalShares[coin.Denom])
// if lpShareEquivalentTokens.IsZero() {
// continue
// }
// err = k.bankKeeper.SendCoins(
// ctx, pool.GetAddress(), addr, sdk.Coins{{asset.Token.Denom, lpShareEquivalentTokens}})
// if err != nil {
// return true
// }
// }
// return false
// })
// if err != nil {
// return err
// }
// for _, pool := range pools {
// // sanity check
// if !pool.GetTotalShares().IsZero() {
// panic("pool total share should be zero after cleanup")
// }
// err = k.DeletePool(ctx, pool.GetId())
// if err != nil {
// return err
// }
// }
// return nil
// }
// GetPoolDenom retrieves the pool based on PoolId and
// returns the coin denoms that it holds.
func (k Keeper) GetPoolDenoms(ctx sdk.Context, poolId uint64) ([]string, error) {
pool, err := k.GetPoolAndPoke(ctx, poolId)
if err != nil {
return nil, err
}
denoms := osmoutils.CoinsDenoms(pool.GetTotalPoolLiquidity(ctx))
return denoms, err
}
// setNextPoolId sets next pool Id.
func (k Keeper) setNextPoolId(ctx sdk.Context, poolId uint64) {
store := ctx.KVStore(k.storeKey)
bz := k.cdc.MustMarshal(&gogotypes.UInt64Value{Value: poolId})
store.Set(types.KeyNextGlobalPoolId, bz)
}
// Deprecated: pool id index has been moved to x/poolmanager.
func (k Keeper) GetNextPoolId(ctx sdk.Context) uint64 {
var nextPoolId uint64
store := ctx.KVStore(k.storeKey)
bz := store.Get(types.KeyNextGlobalPoolId)
if bz == nil {
panic(fmt.Errorf("pool has not been initialized -- Should have been done in InitGenesis"))
} else {
val := gogotypes.UInt64Value{}
err := k.cdc.Unmarshal(bz, &val)
if err != nil {
panic(err)
}
nextPoolId = val.GetValue()
}
return nextPoolId
}
func (k Keeper) GetPoolType(ctx sdk.Context, poolId uint64) (poolmanagertypes.PoolType, error) {
pool, err := k.GetPoolAndPoke(ctx, poolId)
if err != nil {
return -1, err
}
switch pool := pool.(type) {
case *balancer.Pool:
return poolmanagertypes.Balancer, nil
case *stableswap.Pool:
return poolmanagertypes.Stableswap, nil
default:
errMsg := fmt.Sprintf("unrecognized %s pool type: %T", types.ModuleName, pool)
return -1, errorsmod.Wrap(sdkerrors.ErrUnpackAny, errMsg)
}
}
// GetTotalPoolLiquidity returns the coins in the pool owned by all LPs
func (k Keeper) GetTotalPoolLiquidity(ctx sdk.Context, poolId uint64) (sdk.Coins, error) {
pool, err := k.GetCFMMPool(ctx, poolId)
if err != nil {
return nil, err
}
return pool.GetTotalPoolLiquidity(ctx), nil
}
// GetTotalPoolShares returns the total number of pool shares for the given pool.
func (k Keeper) GetTotalPoolShares(ctx sdk.Context, poolId uint64) (sdk.Int, error) {
pool, err := k.GetCFMMPool(ctx, poolId)
if err != nil {
return sdk.Int{}, err
}
return pool.GetTotalShares(), nil
}
// setStableSwapScalingFactors sets the stable swap scaling factors.
// errors if the pool does not exist, the sender is not the scaling factor controller, or due to other
// internal errors.
func (k Keeper) setStableSwapScalingFactors(ctx sdk.Context, poolId uint64, scalingFactors []uint64, sender string) error {
pool, err := k.GetPoolAndPoke(ctx, poolId)
if err != nil {
return err
}
stableswapPool, ok := pool.(*stableswap.Pool)
if !ok {
return fmt.Errorf("pool id %d is not of type stableswap pool", poolId)
}
if err := stableswapPool.SetScalingFactors(ctx, scalingFactors, sender); err != nil {
return err
}
return k.setPool(ctx, stableswapPool)
}
// asCFMMPool converts PoolI to CFMMPoolI by casting the input.
// Returns the pool of the CFMMPoolI or error if the given pool does not implement
// CFMMPoolI.
func asCFMMPool(pool poolmanagertypes.PoolI) (types.CFMMPoolI, error) {
cfmmPool, ok := pool.(types.CFMMPoolI)
if !ok {
return nil, fmt.Errorf("given pool does not implement CFMMPoolI, implements %T", pool)
}
return cfmmPool, nil
}