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

GSW-358 feat: use caller address when calculation incentive id #84

Merged
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
Expand Up @@ -200,9 +200,9 @@ func TestUnstakeToken(t *testing.T) {
}

func TestEndExternalIncentive(t *testing.T) {
std.TestSetOrigCaller(lp01)
std.TestSetOrigCaller(ci01)
std.TestSkipHeights(9999999)
EndExternalIncentive("gno.land/r/bar:gno.land/r/foo:500", "gno.land/r/obl") // use same parameter as CreateExternalIncentive()
EndExternalIncentive(GetOrigCaller().String(), "gno.land/r/bar:gno.land/r/foo:500", "gno.land/r/obl") // use same parameter as CreateExternalIncentive()
std.TestSkipHeights(1)

shouldEQ(t, len(incentives), 0)
Expand Down
260 changes: 260 additions & 0 deletions staker/_TEST_staker_one_increase_external_test.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,260 @@
package staker

import (
"std"
"testing"

"encoding/gjson"

"gno.land/p/demo/testutils"

g "gno.land/r/gov"
p "gno.land/r/pool"
pos "gno.land/r/position"

gnft "gno.land/r/gnft" // GNFT, Gnoswap NFT
gns "gno.land/r/gns" // GNS, Gnoswap Share
obl "gno.land/r/obl"

_ "gno.land/r/grc20_wrapper"
)

var (
pc01 = testutils.TestAddress("pc01") // Pool Creator
ci01 = testutils.TestAddress("ci01") // Create Incentive Caller
lp01 = testutils.TestAddress("lp01") // Liquidity Provider 01
lp02 = testutils.TestAddress("lp02") // Liquidity Provider 02

ira = testutils.TestAddress("ira") // Internal Reward Account
)

var (
fooPath = "gno.land/r/foo"
barPath = "gno.land/r/bar"
)

func init() {
// init pool tiers
// tier 1
poolTiers["gno.land/r/bar:gno.land/r/foo:500"] = 1 // DEV

// tier 2
poolTiers["GNS/USDT_500"] = 2
poolTiers["ATOM/GNS_500"] = 2

// tier 3
poolTiers["ATOM/GNOT_500"] = 3
poolTiers["ATOM/USDT_500"] = 3
poolTiers["ATOM/WETH_500"] = 3
}

func TestPoolInitCreatePool(t *testing.T) {
std.TestSetOrigCaller(pc01)

p.InitManual()
std.TestSkipHeights(1)

p.CreatePool(fooPath, barPath, 500, 130621891405341611593710811006)
std.TestSkipHeights(1)
}

func TestPositionMint(t *testing.T) {
{
std.TestSetOrigCaller(lp01)
tPosTokenId, tPosLiquidity, tPosAmount0, tPosAmount1 := pos.Mint(
fooPath, // token0
barPath, // token1
uint16(500), // fee
int32(9000), // tickLower
int32(11000), // tickUpper
bigint(1000), // amount0Desired
bigint(1000), // amount1Desired
bigint(1), // amount0Min
bigint(1), // amount1Min
bigint(2345678901), // deadline
)
std.TestSkipHeights(1)

shouldEQ(t, tPosTokenId, 1)
shouldEQ(t, gnft.OwnerOf(tid(tPosTokenId)), GetOrigCaller()) // lp01

// approve nft to staker
std.TestSetPrevAddr(lp01)
gnft.Approve(a2u(GetOrigPkgAddr()), tid(tPosTokenId))
std.TestSkipHeights(1)
}

{
std.TestSetOrigCaller(lp02)
tPosTokenId, tPosLiquidity, tPosAmount0, tPosAmount1 := pos.Mint(
fooPath, // token0
barPath, // token1
uint16(500), // fee
int32(9100), // tickLower
int32(12000), // tickUpper
bigint(5000), // amount0Desired
bigint(5000), // amount1Desired
bigint(1), // amount0Min
bigint(1), // amount1Min
bigint(2345678901), // deadline
)
std.TestSkipHeights(1)

shouldEQ(t, tPosTokenId, 2)
shouldEQ(t, gnft.OwnerOf(tid(tPosTokenId)), GetOrigCaller()) // lp02

// approve nft to staker
std.TestSetPrevAddr(lp02)
gnft.Approve(a2u(GetOrigPkgAddr()), tid(tPosTokenId))
std.TestSkipHeights(1)
}
}

func TestCreateExternalIncentive(t *testing.T) {
std.TestSetOrigCaller(ci01)

CreateExternalIncentive(
"gno.land/r/bar:gno.land/r/foo:500", // targetPoolPath
"gno.land/r/obl", // rewardToken
10_000_000_000, // rewardAmount
GetTimestamp(), // startTimestamp
GetTimestamp()+TIMESTAMP_90DAYS, // endTimestamp
)
CreateExternalIncentive("gno.land/r/bar:gno.land/r/foo:500", "gno.land/r/obl", 10_000_000_000, GetTimestamp(), GetTimestamp()+TIMESTAMP_90DAYS)
std.TestSkipHeights(5)
}

func TestStakeToken(t *testing.T) {
{
std.TestSetOrigCaller(lp01)
StakeToken(1) // GNFT tokenId
std.TestSkipHeights(2)

shouldEQ(t, gnft.OwnerOf(tid(1)), GetOrigPkgAddr()) // staker
shouldEQ(t, len(deposits), 1)
}

{
std.TestSetOrigCaller(lp02)
StakeToken(2) // GNFT tokenId
std.TestSkipHeights(2)

shouldEQ(t, gnft.OwnerOf(tid(2)), GetOrigPkgAddr()) // staker
shouldEQ(t, len(deposits), 2)
}
}

func TestApiGetRewardsByAddress(t *testing.T) {
{
// lp01 reward check
gra := ApiGetRewardByAddress(lp01)
jsonStr := gjson.Parse(gra)
shouldEQ(t, jsonStr.Get("response.data.0.type").String(), "Internal")
shouldEQ(t, jsonStr.Get("response.data.0.token").String(), "GNS")
shouldEQ(t, jsonStr.Get("response.data.0.reward").Int(), 252)
shouldEQ(t, jsonStr.Get("response.data.1.type").String(), "External")
shouldEQ(t, jsonStr.Get("response.data.1.token").String(), "gno.land/r/obl")
shouldEQ(t, jsonStr.Get("response.data.1.reward").Int(), 648)
}

{
// lp02 reward check
gra := ApiGetRewardByAddress(lp02)
jsonStr := gjson.Parse(gra)
shouldEQ(t, jsonStr.Get("response.data.0.type").String(), "Internal")
shouldEQ(t, jsonStr.Get("response.data.0.token").String(), "GNS")
shouldEQ(t, jsonStr.Get("response.data.0.reward").Int(), 1397)
shouldEQ(t, jsonStr.Get("response.data.1.type").String(), "External")
shouldEQ(t, jsonStr.Get("response.data.1.token").String(), "gno.land/r/obl")
shouldEQ(t, jsonStr.Get("response.data.1.reward").Int(), 3595)
}
}

func TestUnstakeToken(t *testing.T) {
{
std.TestSetOrigCaller(lp01)
UnstakeToken(1) // GNFT tokenId
std.TestSkipHeights(1)

shouldEQ(t, gnft.OwnerOf(tid(1)), lp01)

// check reward
shouldEQ(t, gns.BalanceOf(a2u(lp01)), 252) // internal
shouldEQ(t, obl.BalanceOf(a2u(lp01)), 648) // external
}

{
std.TestSetOrigCaller(lp02)
UnstakeToken(2) // GNFT tokenId
std.TestSkipHeights(1)

shouldEQ(t, gnft.OwnerOf(tid(2)), lp02)

// check reward
shouldEQ(t, gns.BalanceOf(a2u(lp02)), 1650) // internal
shouldEQ(t, obl.BalanceOf(a2u(lp02)), 4243) // external
}
}

func TestEndExternalIncentive(t *testing.T) {
std.TestSetOrigCaller(ci01)
std.TestSkipHeights(9999999)
EndExternalIncentive(GetOrigCaller().String(), "gno.land/r/bar:gno.land/r/foo:500", "gno.land/r/obl") // use same parameter as CreateExternalIncentive()
std.TestSkipHeights(1)

shouldEQ(t, len(incentives), 0)
shouldEQ(t, len(poolIncentives["gno.land/r/bar:gno.land/r/foo:500"]), 0)
}

// GOV
func TestSubmitProposalParameterStakingReward(t *testing.T) {
// Init GOV Contract
g.Init()

id := SubmitProposalParameterStakingReward(
"staking reward change", // title
"change staking rewards", // summary
"", // metadata
0, // initialDeposit

10, // newStakingReward1
8, // newStakingReward2
6, // newStakingReward3
4, // newStakingReward4
)
shouldEQ(t, id, uint64(1))
}

/* HELPERS */
func shouldEQ(t *testing.T, got, expected interface{}) {
if got != expected {
t.Errorf("got %v, expected %v", got, expected)
}
}

func shouldNEQ(t *testing.T, got, expected interface{}) {
if got == expected {
t.Errorf("got %v, expected %v", got, expected)
}
}

func shouldGT(t *testing.T, l, r interface{}) {
if !(l < r) {
t.Errorf("expected %v < %v", l, r)
}
}

func shouldLT(t *testing.T, l, r interface{}) {
if !(l > r) {
t.Errorf("expected %v > %v", l, r)
}
}

func shouldPanic(t *testing.T, f func()) {
defer func() {
if r := recover(); r == nil {
t.Errorf("expected panic")
}
}()
f()
}
4 changes: 2 additions & 2 deletions staker/incentive_id.gno
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import (
"gno.land/p/demo/ufmt"
)

func incentiveIdCompute(targetPoolPath, rewardToken string) string {
key := ufmt.Sprintf("%s_%s", targetPoolPath, rewardToken)
func incentiveIdCompute(caller, targetPoolPath, rewardToken string) string {
key := ufmt.Sprintf("%s:%s:%s", caller, targetPoolPath, rewardToken)

encoded := base64.StdEncoding.EncodeToString([]byte(key))
return encoded
Expand Down
27 changes: 13 additions & 14 deletions staker/staker.gno
Original file line number Diff line number Diff line change
Expand Up @@ -49,25 +49,25 @@ func CreateExternalIncentive(

externalDuration := uint64(endTimestamp - startTimestamp)
if !(externalDuration == TIMESTAMP_90DAYS || externalDuration == TIMESTAMP_180DAYS || externalDuration == TIMESTAMP_360DAYS) {
println("externalDuration:", externalDuration)
println("TIMESTAMP_90DAYS:", TIMESTAMP_90DAYS)
panic(ufmt.Sprintf("[STAKER] staker.gno__CreateExternalIncentive() || externalDuration(%d) must be 90, 180, 360 days)", externalDuration))
}

incentiveId := incentiveIdCompute(targetPoolPath, rewardToken)
fromBalanceBefore := balanceOfByRegisterCall(rewardToken, GetOrigCaller())
require(fromBalanceBefore >= rewardAmount, ufmt.Sprintf("[STAKER] staker.gno__CreateExternalIncentive() || not enough rewardAmount(%d) to create incentive(%d)", fromBalanceBefore, rewardAmount))

poolRewardBalanceBefore := balanceOfByRegisterCall(rewardToken, GetOrigPkgAddr())

incentiveId := incentiveIdCompute(GetOrigCaller().String(), targetPoolPath, rewardToken)

// check whether incentive already exists or not
// if same incentiveId exists => increase rewardTokenAmount
for _, v := range poolIncentives[targetPoolPath] {
if v == incentiveId {
panic(ufmt.Sprintf("[STAKER] staker.gno__CreateExternalIncentive() || incentive(%s) already exists", incentiveId))
transferFromByRegisterCall(rewardToken, GetOrigCaller(), GetOrigPkgAddr(), rewardAmount)
incentives[v].rewardAmount += rewardAmount
return
}
}

fromBalanceBefore := balanceOfByRegisterCall(rewardToken, GetOrigCaller())
require(fromBalanceBefore >= rewardAmount, ufmt.Sprintf("[STAKER] staker.gno__CreateExternalIncentive() || not enough rewardAmount(%d) to create incentive(%d)", fromBalanceBefore, rewardAmount))

poolRewardBalanceBefore := balanceOfByRegisterCall(rewardToken, GetOrigPkgAddr())

transferFromByRegisterCall(rewardToken, GetOrigCaller(), GetOrigPkgAddr(), rewardAmount)

poolRewardBalanceAfter := balanceOfByRegisterCall(rewardToken, GetOrigPkgAddr())
Expand Down Expand Up @@ -156,14 +156,13 @@ func UnstakeToken(
gnft.TransferFrom(a2u(GetOrigPkgAddr()), a2u(deposit.owner), tid(tokenId))
}

func EndExternalIncentive(targetPoolPath, rewardToken string) {
incentiveId := incentiveIdCompute(targetPoolPath, rewardToken)
func EndExternalIncentive(refundee, targetPoolPath, rewardToken string) {
incentiveId := incentiveIdCompute(refundee, targetPoolPath, rewardToken)
incentive, exist := incentives[incentiveId]
require(exist, ufmt.Sprintf("[STAKER] staker.gno__EndExternalIncentive() || cannot end non existent incentive(%s)", incentiveId))
require(GetTimestamp() >= incentive.endTimestamp, ufmt.Sprintf("[STAKER] staker.gno__EndExternalIncentive() || cannot end incentive before endTimestamp(%d), current(%d)", incentive.endTimestamp, GetTimestamp()))

// r3v4_xxx: who can end incentive ??
// require(incentive.refundee == std.GetOrigCaller(), "[STAKER] staker.gno__EndExternalIncentive() || only refundee can end incentive")
require(incentive.refundee == std.GetOrigCaller(), "[STAKER] staker.gno__EndExternalIncentive() || only refundee can end incentive")

refund := incentive.rewardAmount

Expand Down