-
Notifications
You must be signed in to change notification settings - Fork 608
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
TWAP proto, types package, and osmoutils (#2175)
* Extract osmoutils, proto & types from main TWAP PR * Delete merge conflict * Add TestGetAllUniqueDenomPairs * Resolve more comments * Fix incorrect pool interface (but was a no-op) * Apply suggestions from code review Co-authored-by: Aleksandr Bezobchuk <[email protected]> * apply review comment * Make NewTwapRecord return an error Co-authored-by: Aleksandr Bezobchuk <[email protected]>
- Loading branch information
1 parent
e1223ad
commit e69e260
Showing
11 changed files
with
1,406 additions
and
15 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,97 @@ | ||
package osmoutils | ||
|
||
import ( | ||
"errors" | ||
|
||
"github.com/cosmos/cosmos-sdk/store" | ||
"github.com/gogo/protobuf/proto" | ||
) | ||
|
||
func GatherAllKeysFromStore(storeObj store.KVStore) []string { | ||
iterator := storeObj.Iterator(nil, nil) | ||
defer iterator.Close() | ||
|
||
keys := []string{} | ||
for ; iterator.Valid(); iterator.Next() { | ||
keys = append(keys, string(iterator.Key())) | ||
} | ||
return keys | ||
} | ||
|
||
func GatherValuesFromStore[T any](storeObj store.KVStore, keyStart []byte, keyEnd []byte, parseValue func([]byte) (T, error)) ([]T, error) { | ||
iterator := storeObj.Iterator(keyStart, keyEnd) | ||
defer iterator.Close() | ||
|
||
values := []T{} | ||
for ; iterator.Valid(); iterator.Next() { | ||
val, err := parseValue(iterator.Value()) | ||
if err != nil { | ||
return nil, err | ||
} | ||
values = append(values, val) | ||
} | ||
return values, nil | ||
} | ||
|
||
func GetValuesUntilDerivedStop[T any](storeObj store.KVStore, keyStart []byte, stopFn func([]byte) bool, parseValue func([]byte) (T, error)) ([]T, error) { | ||
// SDK iterator is broken for nil end time, and non-nil start time | ||
// https://github.com/cosmos/cosmos-sdk/issues/12661 | ||
// hence we use []byte{0xff} | ||
keyEnd := []byte{0xff} | ||
return GetIterValuesWithStop(storeObj, keyStart, keyEnd, false, stopFn, parseValue) | ||
} | ||
|
||
func GetIterValuesWithStop[T any]( | ||
storeObj store.KVStore, | ||
keyStart []byte, | ||
keyEnd []byte, | ||
reverse bool, | ||
stopFn func([]byte) bool, | ||
parseValue func([]byte) (T, error)) ([]T, error) { | ||
var iter store.Iterator | ||
if reverse { | ||
iter = storeObj.ReverseIterator(keyStart, keyEnd) | ||
} else { | ||
iter = storeObj.Iterator(keyStart, keyEnd) | ||
} | ||
defer iter.Close() | ||
|
||
values := []T{} | ||
for ; iter.Valid(); iter.Next() { | ||
if stopFn(iter.Key()) { | ||
break | ||
} | ||
val, err := parseValue(iter.Value()) | ||
if err != nil { | ||
return nil, err | ||
} | ||
values = append(values, val) | ||
} | ||
return values, nil | ||
} | ||
|
||
func GetFirstValueAfterPrefix[T any](storeObj store.KVStore, keyStart []byte, parseValue func([]byte) (T, error)) (T, error) { | ||
// SDK iterator is broken for nil end time, and non-nil start time | ||
// https://github.com/cosmos/cosmos-sdk/issues/12661 | ||
// hence we use []byte{0xff} | ||
iterator := storeObj.Iterator(keyStart, []byte{0xff}) | ||
defer iterator.Close() | ||
|
||
if !iterator.Valid() { | ||
var blankValue T | ||
return blankValue, errors.New("No values in iterator") | ||
} | ||
|
||
return parseValue(iterator.Value()) | ||
} | ||
|
||
// MustSet runs store.Set(key, proto.Marshal(value)) | ||
// but panics on any error. | ||
func MustSet(storeObj store.KVStore, key []byte, value proto.Message) { | ||
bz, err := proto.Marshal(value) | ||
if err != nil { | ||
panic(err) | ||
} | ||
|
||
storeObj.Set(key, bz) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
package osmoutils | ||
|
||
import ( | ||
"time" | ||
|
||
sdk "github.com/cosmos/cosmos-sdk/types" | ||
) | ||
|
||
func FormatTimeString(t time.Time) string { | ||
return t.UTC().Round(0).Format(sdk.SortableTimeFormat) | ||
} | ||
|
||
// Parses a string encoded using FormatTimeString back into a time.Time | ||
func ParseTimeString(s string) (time.Time, error) { | ||
t, err := time.Parse(sdk.SortableTimeFormat, s) | ||
if err != nil { | ||
return t, err | ||
} | ||
return t.UTC().Round(0), nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
syntax = "proto3"; | ||
package osmosis.gamm.twap.v1beta1; | ||
|
||
import "gogoproto/gogo.proto"; | ||
import "google/protobuf/any.proto"; | ||
import "cosmos_proto/cosmos.proto"; | ||
import "cosmos/base/v1beta1/coin.proto"; | ||
import "google/protobuf/timestamp.proto"; | ||
|
||
option go_package = "github.com/osmosis-labs/osmosis/v10/x/gamm/twap/types"; | ||
|
||
// A TWAP record should be indexed in state by pool_id, (asset pair), timestamp | ||
// The asset pair assets should be lexicographically sorted. | ||
// Technically (pool_id, asset_0_denom, asset_1_denom, height) do not need to | ||
// appear in the struct however we view this as the wrong performance tradeoff | ||
// given SDK today. Would rather we optimize for readability and correctness, | ||
// than an optimal state storage format. The system bottleneck is elsewhere for | ||
// now. | ||
message TwapRecord { | ||
uint64 pool_id = 1; | ||
// Lexicographically smaller denom of the pair | ||
string asset0_denom = 2; | ||
// Lexicographically larger denom of the pair | ||
string asset1_denom = 3; | ||
// height this record corresponds to, for debugging purposes | ||
int64 height = 4 [ | ||
(gogoproto.moretags) = "yaml:\"record_height\"", | ||
(gogoproto.jsontag) = "record_height" | ||
]; | ||
// This field should only exist until we have a global registry in the state | ||
// machine, mapping prior block heights within {TIME RANGE} to times. | ||
google.protobuf.Timestamp time = 5 [ | ||
(gogoproto.nullable) = false, | ||
(gogoproto.stdtime) = true, | ||
(gogoproto.moretags) = "yaml:\"record_time\"" | ||
]; | ||
|
||
// We store the last spot prices in the struct, so that we can interpolate | ||
// accumulator values for times between when accumulator records are stored. | ||
string p0_last_spot_price = 6 [ | ||
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", | ||
(gogoproto.nullable) = false | ||
]; | ||
string p1_last_spot_price = 7 [ | ||
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", | ||
(gogoproto.nullable) = false | ||
]; | ||
|
||
string p0_arithmetic_twap_accumulator = 8 [ | ||
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", | ||
(gogoproto.nullable) = false | ||
]; | ||
string p1_arithmetic_twap_accumulator = 9 [ | ||
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", | ||
(gogoproto.nullable) = false | ||
]; | ||
// string geometric_twap_accumulator = 7 [(gogoproto.customtype) = | ||
// "github.com/cosmos/cosmos-sdk/types.Dec", | ||
// (gogoproto.nullable) = false]; | ||
} | ||
|
||
// GenesisState defines the gamm module's genesis state. | ||
message GenesisState { repeated TwapRecord twaps = 1; } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package types | ||
|
||
import sdk "github.com/cosmos/cosmos-sdk/types" | ||
|
||
// AmmInterface is the functionality needed from a given pool ID, in order to maintain records and serve TWAPs. | ||
type AmmInterface interface { | ||
GetPoolDenoms(ctx sdk.Context, poolId uint64) (denoms []string, err error) | ||
// CalculateSpotPrice returns the spot price of the quote asset in terms of the base asset, | ||
// using the specified pool. | ||
// E.g. if pool 1 traded 2 atom for 3 osmo, the quote asset was atom, and the base asset was osmo, | ||
// this would return 1.5. (Meaning that 1 atom costs 1.5 osmo) | ||
CalculateSpotPrice(ctx sdk.Context, | ||
poolID uint64, | ||
baseAssetDenom string, | ||
quoteAssetDenom string) (price sdk.Dec, err error) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.