Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat/manage protocol fee list #464

Merged
merged 7 commits into from
Jan 19, 2025
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions _deploy/r/gnoswap/common/access.gno
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,14 @@ func RouterOnly(caller std.Address) error {
return nil
}

// PoolOnly checks if the caller is the pool contract.
func PoolOnly(caller std.Address) error {
if caller != consts.POOL_ADDR {
return ufmt.Errorf(ErrNoPermission, caller.String())
}
return nil
}

// PositionOnly checks if the caller is the position contract.
func PositionOnly(caller std.Address) error {
if caller != consts.POSITION_ADDR {
Expand Down
4 changes: 4 additions & 0 deletions pool/pool_manager.gno
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ import (

"gno.land/r/gnoswap/v1/common"
"gno.land/r/gnoswap/v1/consts"

en "gno.land/r/gnoswap/v1/emission"
pf "gno.land/r/gnoswap/v1/protocol_fee"

"gno.land/r/gnoswap/v1/gns"
)

Expand Down Expand Up @@ -189,6 +192,7 @@ func CreatePool(

if poolCreationFee > 0 {
gns.TransferFrom(a2u(std.PrevRealm().Addr()), a2u(consts.PROTOCOL_FEE_ADDR), poolCreationFee)
pf.AddToProtocolFee(consts.GNS_PATH, poolCreationFee)

std.Emit(
"PoolCreationFee",
Expand Down
4 changes: 4 additions & 0 deletions pool/protocol_fee_withdrawal.gno
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ import (

"gno.land/p/demo/ufmt"
u256 "gno.land/p/gnoswap/uint256"

"gno.land/r/gnoswap/v1/common"
"gno.land/r/gnoswap/v1/consts"
pf "gno.land/r/gnoswap/v1/protocol_fee"
)

// withdrawalFeeBPS is the fee that is charged when a user withdraws their collected fees
Expand Down Expand Up @@ -68,9 +70,11 @@ func HandleWithdrawalFee(

token0Teller := common.GetTokenTeller(token0Path)
checkTransferError(token0Teller.TransferFrom(positionCaller, consts.PROTOCOL_FEE_ADDR, feeAmount0.Uint64()))
pf.AddToProtocolFee(token0Path, feeAmount0.Uint64())

token1Teller := common.GetTokenTeller(token1Path)
checkTransferError(token1Teller.TransferFrom(positionCaller, consts.PROTOCOL_FEE_ADDR, feeAmount1.Uint64()))
pf.AddToProtocolFee(token1Path, feeAmount1.Uint64())

prevAddr, prevPkgPath := getPrevAsString()
std.Emit(
Expand Down
2 changes: 1 addition & 1 deletion pool/protocol_fee_withdrawal_test.gno
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ func TestHandleWithdrawalFee(t *testing.T) {
HandleWithdrawalFee(0, "pkgPath", "1000", "pkgPath", "1000", "poolPath", users.Resolve(admin))
},
verify: nil,
expected: "[GNOSWAP-COMMON-004] token is not registered || token(pkgPath) is not registered",
expected: "[GNOSWAP-COMMON-004] token is not registered || token(pkgPath)",
shouldPanic: true,
},
{
Expand Down
9 changes: 3 additions & 6 deletions protocol_fee/errors.gno
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,9 @@ import (
)

var (
errNoPermission = errors.New("[GNOSWAP-PROTOCOL_FEE-001] caller has no permission")
errNotRegistered = errors.New("[GNOSWAP-PROTOCOL_FEE-002] not registered token")
errAlreadyRegistered = errors.New("[GNOSWAP-PROTOCOL_FEE-003] already registered token")
errLocked = errors.New("[GNOSWAP-PROTOCOL_FEE-004] can't transfer token while locked")
errInvalidInput = errors.New("[GNOSWAP-PROTOCOL_FEE-005] invalid input data")
errInvalidPct = errors.New("[GNOSWAP-PROTOCOL_FEE-006] invalid percentage")
errNoPermission = errors.New("[GNOSWAP-PROTOCOL_FEE-001] caller has no permission")
errInvalidPct = errors.New("[GNOSWAP-PROTOCOL_FEE-002] invalid percentage")
errInvalidAmount = errors.New("[GNOSWAP-PROTOCOL_FEE-003] invalid amount")
)

func addDetailToError(err error, detail string) string {
Expand Down
5 changes: 0 additions & 5 deletions protocol_fee/protocol_fee.gno
Original file line number Diff line number Diff line change
Expand Up @@ -135,11 +135,6 @@ func ClearAccuTransferToGovStaker() {
accuToGovStaker = avl.NewTree()
}

// assertOnlyNotHalted panics if the contract is halted.
func assertOnlyNotHalted() {
common.IsHalted()
}

// addAccuToGovStaker adds the amount to the accuToGovStaker by token path.
func addAccuToGovStaker(path string, amount uint64) {
before := GetAccuTransferToGovStakerByTokenPath(path)
Expand Down
2 changes: 1 addition & 1 deletion protocol_fee/protocol_fee_test.gno
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ func TestSetDevOpsPctByAdminInvalidFee(t *testing.T) {

uassert.PanicsWithMessage(
t,
`[GNOSWAP-PROTOCOL_FEE-006] invalid percentage || pct(100001) should not be bigger than 10000`,
`[GNOSWAP-PROTOCOL_FEE-002] invalid percentage || pct(100001) should not be bigger than 10000`,
func() {
SetDevOpsPctByAdmin(100001)
},
Expand Down
137 changes: 137 additions & 0 deletions protocol_fee/token_list_with_amount.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
package protocol_fee

import (
"std"
"strings"

"gno.land/p/demo/ufmt"

"gno.land/r/gnoswap/v1/common"
"gno.land/r/gnoswap/v1/consts"
)

var (
tokenListWithAmount = make(map[string]uint64) // tokenPath -> amount
)

// TokenList returns only the list of token path.
// If positive is true, it returns only the token path with amount > 0.
// If positive is false, it returns all the token path.
func TokenList(positive bool) []string {
tokens := []string{}

for tokenPath, amount := range tokenListWithAmount {
if positive && amount == 0 {
continue
}

tokens = append(tokens, tokenPath)
}

return tokens
}

// TokenListWithAmount returns the token path and amount.
func TokenListWithAmount() map[string]uint64 {
return tokenListWithAmount
}

// AddToProtocolFee adds the amount to the tokenListWithAmount
// Only `pool + router + staker` can execute this function.
func AddToProtocolFee(tokenPath string, amount uint64) {
assertOnlyPoolRouterStaker()
tokenListWithAmount[tokenPath] += amount
}

// ClearTokenListWithAmount clears the tokenListWithAmount.
// only `gov/staker` can execute this function.
func ClearTokenListWithAmount() {
assertOnlyGovStaker()
clearTokenListWithAmount()
}

// TransferProtocolFee transfers the protocol fee to devOps and gov/staker.
// only `gov/staker` can execute this function.
// It returns list of token with amount has been sent to gov/staker.
func TransferProtocolFee() map[string]uint64 {
assertOnlyGovStaker()

sentToDevOps := []string{}
sentToGovStaker := []string{}
toReturn := map[string]uint64{}

for token, amount := range tokenListWithAmount {
balance := common.BalanceOf(token, consts.PROTOCOL_FEE_ADDR)

// anyone can just send certain grc20 token to `protocol_fee` contract
// therefore, we don't need any guard logic to check whether protocol_fee's xxx token balance is equal to `amount`
// however, amount always should be less than or equal to balance
if amount > balance {
panic(addDetailToError(
errInvalidAmount,
ufmt.Sprintf("amount: %d should be less than or equal to balance: %d", amount, balance),
))
}

if amount > 0 {
toDevOps := balance * devOpsPct / 10000 // default 0%
toGovStaker := balance - toDevOps // default 100%

tokenTeller := common.GetTokenTeller(token)
if toDevOps > 0 {
tokenTeller.Transfer(consts.DEV_OPS, toDevOps)
sentToDevOps = append(sentToDevOps, makeEventString(token, toDevOps))
}

if toGovStaker > 0 {
tokenTeller.Transfer(consts.GOV_STAKER_ADDR, toGovStaker)
sentToGovStaker = append(sentToGovStaker, makeEventString(token, toGovStaker))

toReturn[token] = toGovStaker
}
}
}

clearTokenListWithAmount()

prevAddr, prevRealm := getPrev()
std.Emit(
"TransferProtocolFee",
"prevAddr", prevAddr,
"prevRealm", prevRealm,
"toDevOps", strings.Join(sentToDevOps, ","),
"toGovStaker", strings.Join(sentToGovStaker, ","),
)

return toReturn
}

// clearTokenListWithAmount clears the tokenListWithAmount.
func clearTokenListWithAmount() {
tokenListWithAmount = map[string]uint64{}
}

// assertOnlyPoolRouterStaker panics if the caller is not the pool, router, or staker contract.
func assertOnlyPoolRouterStaker() {
caller := std.PrevRealm().Addr()

poolOnlyErr := common.PoolOnly(caller)
routerOnlyErr := common.RouterOnly(caller)
stakerOnlyErr := common.StakerOnly(caller)

if poolOnlyErr != nil && routerOnlyErr != nil && stakerOnlyErr != nil {
panic(errNoPermission)
}
}

// assertOnlyGovStaker panics if the caller is not the gov/staker contract.
func assertOnlyGovStaker() {
caller := std.PrevRealm().Addr()
if err := common.GovStakerOnly(caller); err != nil {
panic(err.Error())
}
}

func makeEventString(tokenPath string, amount uint64) string {
return tokenPath + "*FEE*" + ufmt.Sprintf("%d", amount)
}
Loading
Loading