Skip to content

Commit 45b9917

Browse files
authored
Merge pull request #119 from gnoswap-labs/GSW-603-feat-collect-reward
GSW-603 feat: CollectReward()
2 parents aa4ecaf + c9e8af2 commit 45b9917

File tree

3 files changed

+261
-4
lines changed

3 files changed

+261
-4
lines changed
Lines changed: 240 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,240 @@
1+
package staker
2+
3+
import (
4+
"std"
5+
"testing"
6+
7+
"encoding/gjson"
8+
9+
"gno.land/p/demo/testutils"
10+
11+
p "gno.land/r/pool"
12+
pos "gno.land/r/position"
13+
14+
gnft "gno.land/r/gnft" // GNFT, Gnoswap NFT
15+
gns "gno.land/r/gns" // GNS, Gnoswap Share
16+
obl "gno.land/r/obl"
17+
18+
_ "gno.land/r/grc20_wrapper"
19+
)
20+
21+
var (
22+
pc01 = testutils.TestAddress("pc01") // Pool Creator
23+
ci01 = testutils.TestAddress("ci01") // Create Incentive Caller
24+
lp01 = testutils.TestAddress("lp01") // Liquidity Provider 01
25+
lp02 = testutils.TestAddress("lp02") // Liquidity Provider 02
26+
27+
ira = testutils.TestAddress("ira") // Internal Reward Account
28+
)
29+
30+
var (
31+
barPath = "gno.land/r/bar"
32+
quxPath = "gno.land/r/qux"
33+
)
34+
35+
func init() {
36+
// init pool tiers
37+
// tier 1
38+
poolTiers["gno.land/r/bar:gno.land/r/qux:500"] = 1 // DEV
39+
40+
// tier 2
41+
poolTiers["GNS/USDT_500"] = 2
42+
poolTiers["ATOM/GNS_500"] = 2
43+
44+
// tier 3
45+
poolTiers["ATOM/GNOT_500"] = 3
46+
poolTiers["ATOM/USDT_500"] = 3
47+
poolTiers["ATOM/WETH_500"] = 3
48+
}
49+
50+
func TestPoolInitCreatePool(t *testing.T) {
51+
std.TestSetOrigCaller(pc01)
52+
53+
p.InitManual()
54+
std.TestSkipHeights(1)
55+
56+
p.CreatePool(barPath, quxPath, 500, 130621891405341611593710811006)
57+
std.TestSkipHeights(1)
58+
}
59+
60+
func TestPositionMint(t *testing.T) {
61+
{
62+
std.TestSetOrigCaller(lp01)
63+
tPosTokenId, tPosLiquidity, tPosAmount0, tPosAmount1 := pos.Mint(
64+
barPath, // token0
65+
quxPath, // token1
66+
uint16(500), // fee
67+
int32(9000), // tickLower
68+
int32(11000), // tickUpper
69+
bigint(1000), // amount0Desired
70+
bigint(1000), // amount1Desired
71+
bigint(1), // amount0Min
72+
bigint(1), // amount1Min
73+
bigint(2345678901), // deadline
74+
)
75+
std.TestSkipHeights(1)
76+
77+
shouldEQ(t, tPosTokenId, 1)
78+
shouldEQ(t, gnft.OwnerOf(tid(tPosTokenId)), GetOrigCaller()) // lp01
79+
80+
// approve nft to staker
81+
std.TestSetPrevAddr(lp01)
82+
gnft.Approve(a2u(GetOrigPkgAddr()), tid(tPosTokenId))
83+
std.TestSkipHeights(1)
84+
}
85+
86+
{
87+
std.TestSetOrigCaller(lp02)
88+
tPosTokenId, tPosLiquidity, tPosAmount0, tPosAmount1 := pos.Mint(
89+
barPath, // token0
90+
quxPath, // token1
91+
uint16(500), // fee
92+
int32(9100), // tickLower
93+
int32(12000), // tickUpper
94+
bigint(5000), // amount0Desired
95+
bigint(5000), // amount1Desired
96+
bigint(1), // amount0Min
97+
bigint(1), // amount1Min
98+
bigint(2345678901), // deadline
99+
)
100+
std.TestSkipHeights(1)
101+
102+
shouldEQ(t, tPosTokenId, 2)
103+
shouldEQ(t, gnft.OwnerOf(tid(tPosTokenId)), GetOrigCaller()) // lp02
104+
105+
// approve nft to staker
106+
std.TestSetPrevAddr(lp02)
107+
gnft.Approve(a2u(GetOrigPkgAddr()), tid(tPosTokenId))
108+
std.TestSkipHeights(1)
109+
}
110+
}
111+
112+
func TestCreateExternalIncentive(t *testing.T) {
113+
std.TestSetOrigCaller(ci01)
114+
115+
CreateExternalIncentive(
116+
"gno.land/r/bar:gno.land/r/qux:500", // targetPoolPath
117+
"gno.land/r/obl", // rewardToken
118+
10_000_000_000, // rewardAmount
119+
GetTimestamp(), // startTimestamp
120+
GetTimestamp()+TIMESTAMP_90DAYS, // endTimestamp
121+
)
122+
CreateExternalIncentive("gno.land/r/bar:gno.land/r/qux:500", "gno.land/r/obl", 10_000_000_000, GetTimestamp(), GetTimestamp()+TIMESTAMP_90DAYS)
123+
std.TestSkipHeights(5)
124+
}
125+
126+
func TestStakeToken(t *testing.T) {
127+
{
128+
std.TestSetOrigCaller(lp01)
129+
StakeToken(1) // GNFT tokenId
130+
std.TestSkipHeights(2)
131+
132+
shouldEQ(t, gnft.OwnerOf(tid(1)), GetOrigPkgAddr()) // staker
133+
shouldEQ(t, len(deposits), 1)
134+
}
135+
136+
{
137+
std.TestSetOrigCaller(lp02)
138+
StakeToken(2) // GNFT tokenId
139+
std.TestSkipHeights(2)
140+
141+
shouldEQ(t, gnft.OwnerOf(tid(2)), GetOrigPkgAddr()) // staker
142+
shouldEQ(t, len(deposits), 2)
143+
}
144+
}
145+
146+
func TestApiGetRewardsByAddress(t *testing.T) {
147+
{
148+
// lp01 reward check
149+
gra := ApiGetRewardByAddress(lp01)
150+
jsonStr := gjson.Parse(gra)
151+
shouldEQ(t, jsonStr.Get("response.data.0.type").String(), "Internal")
152+
shouldEQ(t, jsonStr.Get("response.data.0.token").String(), "GNS")
153+
shouldEQ(t, jsonStr.Get("response.data.0.reward").Int(), 126)
154+
shouldEQ(t, jsonStr.Get("response.data.1.type").String(), "External")
155+
shouldEQ(t, jsonStr.Get("response.data.1.token").String(), "gno.land/r/obl")
156+
shouldEQ(t, jsonStr.Get("response.data.1.reward").Int(), 648)
157+
}
158+
159+
{
160+
// lp02 reward check
161+
gra := ApiGetRewardByAddress(lp02)
162+
jsonStr := gjson.Parse(gra)
163+
shouldEQ(t, jsonStr.Get("response.data.0.type").String(), "Internal")
164+
shouldEQ(t, jsonStr.Get("response.data.0.token").String(), "GNS")
165+
shouldEQ(t, jsonStr.Get("response.data.0.reward").Int(), 698)
166+
shouldEQ(t, jsonStr.Get("response.data.1.type").String(), "External")
167+
shouldEQ(t, jsonStr.Get("response.data.1.token").String(), "gno.land/r/obl")
168+
shouldEQ(t, jsonStr.Get("response.data.1.reward").Int(), 3595)
169+
}
170+
}
171+
172+
func TestUnstakeToken(t *testing.T) {
173+
{
174+
std.TestSetOrigCaller(lp01)
175+
UnstakeToken(1) // GNFT tokenId
176+
std.TestSkipHeights(1)
177+
178+
shouldEQ(t, gnft.OwnerOf(tid(1)), lp01)
179+
180+
// check reward
181+
shouldEQ(t, gns.BalanceOf(a2u(lp01)), 126) // internal
182+
shouldEQ(t, obl.BalanceOf(a2u(lp01)), 648) // external
183+
}
184+
185+
{
186+
std.TestSetOrigCaller(lp02)
187+
UnstakeToken(2) // GNFT tokenId
188+
std.TestSkipHeights(1)
189+
190+
shouldEQ(t, gnft.OwnerOf(tid(2)), lp02)
191+
192+
// check reward
193+
shouldEQ(t, gns.BalanceOf(a2u(lp02)), 825) // internal
194+
shouldEQ(t, obl.BalanceOf(a2u(lp02)), 4243) // external
195+
}
196+
}
197+
198+
func TestEndExternalIncentive(t *testing.T) {
199+
std.TestSetOrigCaller(ci01)
200+
std.TestSkipHeights(9999999)
201+
EndExternalIncentive(GetOrigCaller().String(), "gno.land/r/bar:gno.land/r/qux:500", "gno.land/r/obl") // use same parameter as CreateExternalIncentive()
202+
std.TestSkipHeights(1)
203+
204+
shouldEQ(t, len(incentives), 0)
205+
shouldEQ(t, len(poolIncentives["gno.land/r/bar:gno.land/r/qux:500"]), 0)
206+
}
207+
208+
/* HELPERS */
209+
func shouldEQ(t *testing.T, got, expected interface{}) {
210+
if got != expected {
211+
t.Errorf("got %v, expected %v", got, expected)
212+
}
213+
}
214+
215+
func shouldNEQ(t *testing.T, got, expected interface{}) {
216+
if got == expected {
217+
t.Errorf("got %v, expected %v", got, expected)
218+
}
219+
}
220+
221+
func shouldGT(t *testing.T, l, r interface{}) {
222+
if !(l < r) {
223+
t.Errorf("expected %v < %v", l, r)
224+
}
225+
}
226+
227+
func shouldLT(t *testing.T, l, r interface{}) {
228+
if !(l > r) {
229+
t.Errorf("expected %v > %v", l, r)
230+
}
231+
}
232+
233+
func shouldPanic(t *testing.T, f func()) {
234+
defer func() {
235+
if r := recover(); r == nil {
236+
t.Errorf("expected panic")
237+
}
238+
}()
239+
f()
240+
}

staker/_TEST_staker_one_increase_external_test.gno renamed to staker/_TEST_staker_one_increase_external_test.gnoa

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ func init() {
4343
poolTiers["ATOM/GNS_500"] = 2
4444

4545
// tier 3
46-
poolTiers["ATOM/GNOT_500"] = 3
46+
poolTiers["ATOM/GNOT_500"] = 3
4747
poolTiers["ATOM/USDT_500"] = 3
4848
poolTiers["ATOM/WETH_500"] = 3
4949
}

staker/staker.gno

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import (
1212
)
1313

1414
const (
15-
INTERNAL_REWARD_ACCOUNT = std.Address("g1d9exzh6lta047h6lta047h6lta047h6l8ylkpa") // hardcoded
15+
INTERNAL_REWARD_ACCOUNT = std.Address("g1d9exzh6lta047h6lta047h6lta047h6l8ylkpa") // hardcoded at gns.gno
1616
)
1717

1818
var (
@@ -89,7 +89,7 @@ func CreateExternalIncentive(
8989
}
9090

9191
func StakeToken(
92-
tokenId uint64, // GNFT ID
92+
tokenId uint64, // LP TokenID
9393
) {
9494
// check whether tokenId already staked or not
9595
_, exist := deposits[tokenId]
@@ -122,8 +122,25 @@ func StakeToken(
122122
transferDeposit(tokenId, GetOrigPkgAddr())
123123
}
124124

125+
func CollectReward(
126+
tokenId uint64, // LP TokenID
127+
) {
128+
// check whether tokenId is staked or not
129+
_, exist := deposits[tokenId]
130+
require(exist, ufmt.Sprintf("[STAKER] staker.gno__CollectReward() || tokenId(%d) not staked", tokenId))
131+
132+
// check tokenId owner
133+
require(
134+
gnft.OwnerOf(tid(tokenId)) == GetOrigCaller(),
135+
ufmt.Sprintf(
136+
"[STAKER] staker.gno__CollectReward() || only owner can collect reward__gnft.OwnerOf(tid(tokenId(%d)))(%s) == GetOrigCaller()(%s)",
137+
tokenId, gnft.OwnerOf(tid(tokenId)), GetOrigCaller(),
138+
),
139+
)
140+
}
141+
125142
func UnstakeToken(
126-
tokenId uint64, // GNFT TokenID
143+
tokenId uint64, // LP TokenID
127144
) {
128145
deposit, exist := deposits[tokenId]
129146
require(exist, ufmt.Sprintf("[STAKER] staker.gno__UnstakeToken() || tokenId(%d) not staked", tokenId))

0 commit comments

Comments
 (0)