Skip to content

Commit

Permalink
refactor: pool mint
Browse files Browse the repository at this point in the history
  • Loading branch information
onlyhyde committed Dec 16, 2024
1 parent 765634d commit af230b5
Show file tree
Hide file tree
Showing 12 changed files with 474 additions and 75 deletions.
12 changes: 7 additions & 5 deletions _deploy/r/gnoswap/consts/consts.gno
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,8 @@ import (

// GNOSWAP SERVICE
const (
ADMIN std.Address = "g17290cwvmrapvp869xfnhhawa8sm9edpufzat7d" // Admin
DEV_OPS std.Address = "g1mjvd83nnjee3z2g7683er55me9f09688pd4mj9" // DevOps

ADMIN std.Address = "g17290cwvmrapvp869xfnhhawa8sm9edpufzat7d"
DEV_OPS std.Address = "g1mjvd83nnjee3z2g7683er55me9f09688pd4mj9"
TOKEN_REGISTER std.Address = "g1er355fkjksqpdtwmhf5penwa82p0rhqxkkyhk5"

TOKEN_REGISTER_NAMESPACE string = "gno.land/r/g1er355fkjksqpdtwmhf5penwa82p0rhqxkkyhk5"
Expand All @@ -21,7 +20,8 @@ const (
GNOT string = "gnot"
WRAPPED_WUGNOT string = "gno.land/r/demo/wugnot"

UGNOT_MIN_DEPOSIT_TO_WRAP uint64 = 1000 // defined in https://github.com/gnolang/gno/blob/81a88a2976ba9f2f9127ebbe7fb7d1e1f7fa4bd4/examples/gno.land/r/demo/wugnot/wugnot.gno#L19
// defined in https://github.com/gnolang/gno/blob/81a88a2976ba9f2f9127ebbe7fb7d1e1f7fa4bd4/examples/gno.land/r/demo/wugnot/wugnot.gno#L19
UGNOT_MIN_DEPOSIT_TO_WRAP uint64 = 1000
)

// CONTRACT PATH & ADDRESS
Expand Down Expand Up @@ -91,9 +91,11 @@ const (

MAX_UINT128 string = "340282366920938463463374607431768211455"
MAX_UINT160 string = "1461501637330902918203684832716283019655932542975"
MAX_INT256 string = "57896044618658097711785492504343953926634992332820282019728792003956564819968"
MAX_UINT256 string = "115792089237316195423570985008687907853269984665640564039457584007913129639935"

MAX_INT128 string = "170141183460469231731687303715884105727"
MAX_INT256 string = "57896044618658097711785492504343953926634992332820282019728792003956564819968"

// Tick Related
MIN_TICK int32 = -887272
MAX_TICK int32 = 887272
Expand Down
4 changes: 3 additions & 1 deletion pool/errors.gno
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ var (
errInvalidTickAndTickSpacing = errors.New("[GNOSWAP-POOL-022] invalid tick and tick spacing requested")
errInvalidAddress = errors.New("[GNOSWAP-POOL-023] invalid address")
errInvalidTickRange = errors.New("[GNOSWAP-POOL-024] tickLower is greater than tickUpper")
errUnderflow = errors.New("[GNOSWAP-POOL-025] underflow") // TODO: make as common error code
errUnderflow = errors.New("[GNOSWAP-POOL-025] underflow")
errOverFlow = errors.New("[GNOSWAP-POOL-026] overflow")
errBalanceUpdateFailed = errors.New("[GNOSWAP-POOL-027] balance update failed")
)

// addDetailToError adds detail to an error message
Expand Down
17 changes: 9 additions & 8 deletions pool/pool.gno
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ func Mint(
recipient std.Address,
tickLower int32,
tickUpper int32,
_liquidityAmount string,
liquidityAmount string,
positionCaller std.Address,
) (string, string) {
common.IsHalted()
Expand All @@ -36,13 +36,14 @@ func Mint(
}
}

liquidityAmount := u256.MustFromDecimal(_liquidityAmount)
if liquidityAmount.IsZero() {
liquidity := u256.MustFromDecimal(liquidityAmount)
if liquidity.IsZero() {
panic(errZeroLiquidity)
}

pool := GetPool(token0Path, token1Path, fee)
position := newModifyPositionParams(recipient, tickLower, tickUpper, i256.FromUint256(liquidityAmount))
liquidityDelta := safeConvertToInt128(liquidity)
position := newModifyPositionParams(recipient, tickLower, tickUpper, liquidityDelta)
_, amount0, amount1 := pool.modifyPosition(position)

if amount0.Gt(u256.Zero()) {
Expand Down Expand Up @@ -198,7 +199,7 @@ func SetFeeProtocolByAdmin(

newFee := setFeeProtocol(feeProtocol0, feeProtocol1)

prevAddr, prevRealm := getPrev()
prevAddr, prevRealm := getPrevAsString()
std.Emit(
"SetFeeProtocolByAdmin",
"prevAddr", prevAddr,
Expand All @@ -221,7 +222,7 @@ func SetFeeProtocol(feeProtocol0, feeProtocol1 uint8) {

newFee := setFeeProtocol(feeProtocol0, feeProtocol1)

prevAddr, prevRealm := getPrev()
prevAddr, prevRealm := getPrevAsString()
std.Emit(
"SetFeeProtocol",
"prevAddr", prevAddr,
Expand Down Expand Up @@ -323,7 +324,7 @@ func CollectProtocolByAdmin(
amount1Requested,
)

prevAddr, prevRealm := getPrev()
prevAddr, prevRealm := getPrevAsString()
std.Emit(
"CollectProtocolByAdmin",
"prevAddr", prevAddr,
Expand Down Expand Up @@ -368,7 +369,7 @@ func CollectProtocol(
amount1Requested,
)

prevAddr, prevRealm := getPrev()
prevAddr, prevRealm := getPrevAsString()
std.Emit(
"CollectProtocol",
"prevAddr", prevAddr,
Expand Down
93 changes: 69 additions & 24 deletions pool/pool_manager.gno
Original file line number Diff line number Diff line change
Expand Up @@ -130,12 +130,12 @@ func CreatePool(
token0Path string,
token1Path string,
fee uint32,
_sqrtPriceX96 string,
sqrtPriceX96 string,
) {
common.IsHalted()
en.MintAndDistributeGns()

poolInfo := newPoolParams(token0Path, token1Path, fee, _sqrtPriceX96)
poolInfo := newPoolParams(token0Path, token1Path, fee, sqrtPriceX96)

if poolInfo.isSameTokenPath() {
panic(addDetailToError(
Expand All @@ -153,7 +153,7 @@ func CreatePool(
poolPath := GetPoolPath(token0Path, token1Path, fee)

// reinitialize poolInfo with wrapped tokens
poolInfo = newPoolParams(token0Path, token1Path, fee, _sqrtPriceX96)
poolInfo = newPoolParams(token0Path, token1Path, fee, sqrtPriceX96)

// then check if token0Path == token1Path
if poolInfo.isSameTokenPath() {
Expand All @@ -174,7 +174,7 @@ func CreatePool(
}

// TODO: make this as a parameter
prevAddr, prevRealm := getPrev()
prevAddr, prevRealm := getPrevAsString()

// check whether the pool already exist
pool, exist := pools.Get(poolPath)
Expand Down Expand Up @@ -208,7 +208,7 @@ func CreatePool(
"token0Path", token0Path,
"token1Path", token1Path,
"fee", ufmt.Sprintf("%d", fee),
"sqrtPriceX96", _sqrtPriceX96,
"sqrtPriceX96", sqrtPriceX96,
"internal_poolPath", poolPath,
)
}
Expand All @@ -220,46 +220,91 @@ func DoesPoolPathExist(poolPath string) bool {
return exist
}

// GetPool retrieves the pool for the given token paths and fee.
// It constructs the poolPath from the given parameters and returns the corresponding pool.
// Returns pool struct
// GetPool retrieves a pool instance based on the provided token paths and fee tier.
//
// This function determines the pool path by combining the paths of token0 and token1 along with the fee tier,
// and then retrieves the corresponding pool instance using that path.
//
// Parameters:
// - token0Path (string): The unique identifier or path for token0.
// - token1Path (string): The unique identifier or path for token1.
// - fee (uint32): The fee tier for the pool, expressed in basis points (e.g., 3000 for 0.3%).
//
// Returns:
// - *Pool: A pointer to the Pool instance corresponding to the provided tokens and fee tier.
//
// Notes:
// - The order of token paths (token0Path and token1Path) matters and should match the pool's configuration.
// - Ensure that the tokens and fee tier provided are valid and registered in the system.
//
// Example:
// pool := GetPool("path/to/token0", "path/to/token1", 3000)
func GetPool(token0Path, token1Path string, fee uint32) *Pool {
poolPath := GetPoolPath(token0Path, token1Path, fee)
pool, exist := pools[poolPath]
if !exist {
panic(addDetailToError(
errDataNotFound,
ufmt.Sprintf("pool_manager.gno__GetPool() || expected poolPath(%s) to exist", poolPath),
))
}

return pool
return GetPoolFromPoolPath(poolPath)
}

// GetPoolFromPoolPath retrieves the pool for the given poolPath.
// GetPoolFromPoolPath retrieves a pool instance based on the provided pool path.
//
// This function checks if a pool exists for the given poolPath in the `pools` mapping.
// If the pool exists, it returns the pool instance. Otherwise, it panics with a descriptive error.
//
// Parameters:
// - poolPath (string): The unique identifier or path for the pool.
//
// Returns:
// - *Pool: A pointer to the Pool instance corresponding to the given poolPath.
//
// Panics:
// - If the `poolPath` does not exist in the `pools` mapping, it panics with an error message
// indicating that the expected poolPath was not found.
//
// Notes:
// - Ensure that the `poolPath` provided is valid and corresponds to an existing pool in the `pools` mapping.
//
// Example:
// pool := GetPoolFromPoolPath("path/to/pool")
func GetPoolFromPoolPath(poolPath string) *Pool {
pool, exist := pools[poolPath]
if !exist {
panic(addDetailToError(
errDataNotFound,
ufmt.Sprintf("pool_manager.gno__GetPoolFromPoolPath() || expected poolPath(%s) to exist", poolPath),
ufmt.Sprintf("expected poolPath(%s) to exist", poolPath),
))
}

return pool
}

// GetPoolPath generates a poolPath from the given token paths and fee.
// The poolPath is constructed by joining the token paths and fee with colons.
// GetPoolPath generates a unique pool path string based on the token paths and fee tier.
//
// This function ensures that the token paths are registered and sorted in alphabetical order
// before combining them with the fee tier to create a unique identifier for the pool.
//
// Parameters:
// - token0Path (string): The unique identifier or path for token0.
// - token1Path (string): The unique identifier or path for token1.
// - fee (uint32): The fee tier for the pool, expressed in basis points (e.g., 3000 for 0.3%).
//
// Returns:
// - string: A unique pool path string in the format "token0Path:token1Path:fee".
//
// Notes:
// - The function validates that both `token0Path` and `token1Path` are registered in the system
// using `common.MustRegistered`.
// - The token paths are sorted alphabetically to ensure consistent pool path generation, regardless
// of the input order.
// - This sorting guarantees that the pool path remains deterministic for the same pair of tokens and fee.
//
// Example:
// poolPath := GetPoolPath("path/to/token0", "path/to/token1", 3000)
// // Output: "path/to/token0:path/to/token1:3000"
func GetPoolPath(token0Path, token1Path string, fee uint32) string {
common.MustRegistered(token0Path)
common.MustRegistered(token1Path)

// TODO: this check is not unnecessary, if we are sure that
// all the token paths in the pool are sorted in alphabetical order.
if strings.Compare(token1Path, token0Path) < 0 {
token0Path, token1Path = token1Path, token0Path
}

return ufmt.Sprintf("%s:%s:%d", token0Path, token1Path, fee)
}
56 changes: 56 additions & 0 deletions pool/pool_manager_test.gno
Original file line number Diff line number Diff line change
Expand Up @@ -202,3 +202,59 @@ func TestCreatePool(t *testing.T) {

resetObject(t)
}

func TestGetPool(t *testing.T) {
tests := []struct {
name string
setupFn func(t *testing.T)
action func(t *testing.T)
shouldPanic bool
expected string
}{
{
name: "Panic - unregisterd poolPath ",
setupFn: func(t *testing.T) {
InitialisePoolTest(t)
},
action: func(t *testing.T) {
GetPool(barPath, fooPath, fee500)
},
shouldPanic: true,
expected: "[GNOSWAP-POOL-008] requested data not found || expected poolPath(gno.land/r/onbloc/bar:gno.land/r/onbloc/foo:500) to exist",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
defer func() {
if r := recover(); r == nil {
if tt.shouldPanic {
t.Errorf(">>> %s: expected panic but got none", tt.name)
return
}
} else {
switch r.(type) {
case string:
if r.(string) != tt.expected {
t.Errorf(">>> %s: got panic %v, want %v", tt.name, r, tt.expected)
}
case error:
if r.(error).Error() != tt.expected {
t.Errorf(">>> %s: got panic %v, want %v", tt.name, r.(error).Error(), tt.expected)
}
default:
t.Errorf(">>> %s: got panic %v, want %v", tt.name, r, tt.expected)
}
}
}()
if tt.setupFn != nil {
tt.setupFn(t)
}

if tt.shouldPanic {
tt.action(t)
} else {
}
})
}
}
44 changes: 31 additions & 13 deletions pool/pool_transfer.gno
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,7 @@ func (p *Pool) transferAndVerify(
if err := validatePoolBalance(token0, token1, absAmount, isToken0); err != nil {
panic(err)
}
amountUint64, err := safeConvertToUint64(absAmount)
if err != nil {
panic(err)
}
amountUint64 := safeConvertToUint64(absAmount)

token := common.GetTokenTeller(tokenPath)
checkTransferError(token.Transfer(to, amountUint64))
Expand Down Expand Up @@ -98,20 +95,41 @@ func (p *Pool) transferFromAndVerify(
amount *u256.Uint,
isToken0 bool,
) {
absAmount := amount
amountUint64, err := safeConvertToUint64(absAmount)
if err != nil {
panic(err)
}
amountUint64 := safeConvertToUint64(amount)

token := common.GetTokenTeller(tokenPath)
checkTransferError(token.TransferFrom(from, to, amountUint64))
token := common.GetToken(tokenPath)
beforeBalance := token.BalanceOf(to)

teller := common.GetTokenTeller(tokenPath)
checkTransferError(teller.TransferFrom(from, to, amountUint64))

afterBalance := token.BalanceOf(to)
if (beforeBalance + amountUint64) != afterBalance {
panic(ufmt.Sprintf(
"%v. beforeBalance(%d) + amount(%d) != afterBalance(%d)",
errTransferFailed, beforeBalance, amountUint64, afterBalance,
))
}

// update pool balances
if isToken0 {
p.balances.token0 = new(u256.Uint).Add(p.balances.token0, absAmount)
beforeToken0 := p.balances.token0.Clone()
p.balances.token0 = new(u256.Uint).Add(p.balances.token0, amount)
if p.balances.token0.Lt(beforeToken0) {
panic(ufmt.Sprintf(
"%v. token0(%s) < beforeToken0(%s)",
errBalanceUpdateFailed, p.balances.token0.ToString(), beforeToken0.ToString(),
))
}
} else {
p.balances.token1 = new(u256.Uint).Add(p.balances.token1, absAmount)
beforeToken1 := p.balances.token1.Clone()
p.balances.token1 = new(u256.Uint).Add(p.balances.token1, amount)
if p.balances.token1.Lt(beforeToken1) {
panic(ufmt.Sprintf(
"%v. token1(%s) < beforeToken1(%s)",
errBalanceUpdateFailed, p.balances.token1.ToString(), beforeToken1.ToString(),
))
}
}
}

Expand Down
4 changes: 2 additions & 2 deletions pool/pool_transfer_test.gno
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,8 @@ func TestTransferFromAndVerify(t *testing.T) {

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
TokenFaucet(t, fooPath, pusers.AddressOrName(tt.from))
TokenApprove(t, fooPath, pusers.AddressOrName(tt.from), pool, u256.MustFromDecimal(tt.amount.ToString()).Uint64())
TokenFaucet(t, tt.tokenPath, pusers.AddressOrName(tt.from))
TokenApprove(t, tt.tokenPath, pusers.AddressOrName(tt.from), pool, u256.MustFromDecimal(tt.amount.ToString()).Uint64())

tt.pool.transferFromAndVerify(tt.from, tt.to, tt.tokenPath, u256.MustFromDecimal(tt.amount.ToString()), tt.isToken0)

Expand Down
Loading

0 comments on commit af230b5

Please sign in to comment.