-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
* GSW-1145 feat: distribution calculation in emission
- Loading branch information
Showing
8 changed files
with
364 additions
and
1 deletion.
There are no files selected for viewing
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
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,62 @@ | ||
package emission | ||
|
||
import ( | ||
"std" | ||
"testing" | ||
|
||
"gno.land/r/demo/gns" | ||
|
||
"gno.land/r/demo/gnoswap/consts" | ||
|
||
pusers "gno.land/p/demo/users" | ||
) | ||
|
||
var ( | ||
gsa std.Address = consts.GNOSWAP_ADMIN | ||
) | ||
|
||
// Realms to mock frames | ||
var ( | ||
gsaRealm = std.NewUserRealm(gsa) | ||
govRealm = std.NewCodeRealm(consts.GOV_PATH) | ||
) | ||
|
||
func gnsBalance(addr std.Address) uint64 { | ||
a2u := pusers.AddressOrName(addr) | ||
|
||
return gns.BalanceOf(a2u) | ||
} | ||
|
||
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, didn't expected %v", got, expected) | ||
} | ||
} | ||
|
||
func shouldPanic(t *testing.T, f func()) { | ||
defer func() { | ||
if r := recover(); r == nil { | ||
t.Errorf("expected panic") | ||
} | ||
}() | ||
f() | ||
} | ||
|
||
func shouldPanicWithMsg(t *testing.T, f func(), msg string) { | ||
defer func() { | ||
if r := recover(); r == nil { | ||
t.Errorf("The code did not panic") | ||
} else { | ||
if r != msg { | ||
t.Errorf("excepted panic(%v), got(%v)", msg, r) | ||
} | ||
} | ||
}() | ||
f() | ||
} |
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,69 @@ | ||
package emission | ||
|
||
import ( | ||
"std" | ||
"testing" | ||
|
||
"gno.land/r/demo/gns" | ||
) | ||
|
||
func TestEmitGns(t *testing.T) { | ||
shouldEQ(t, gns.TotalSupply(), 100000000000000) // GSA has | ||
shouldEQ(t, gnsBalance(emissionAddr), 0) | ||
|
||
EmitGns() // 1 ~ 123 height | ||
|
||
shouldEQ(t, gnsBalance(emissionAddr), 4387842345) | ||
shouldEQ(t, gns.TotalSupply(), 100000000000000+4387842345) | ||
|
||
shouldEQ(t, std.GetHeight(), 123) | ||
} | ||
|
||
func TestDistributeToTarget(t *testing.T) { | ||
shouldEQ(t, gnsBalance(emissionAddr), 4387842345) | ||
DistributeToTarget(gnsBalance(emissionAddr)) | ||
shouldEQ(t, gnsBalance(emissionAddr), 1) // 1 left | ||
} | ||
|
||
func TestDistributeToTargetAfter5Block(t *testing.T) { | ||
std.TestSkipHeights(5) | ||
EmitGns() | ||
shouldEQ(t, gnsBalance(emissionAddr), 178367576) | ||
|
||
DistributeToTarget(gnsBalance(emissionAddr)) | ||
shouldEQ(t, gnsBalance(emissionAddr), 1) // 1 left again | ||
} | ||
|
||
func TestChangeDistributionPctByAdmin(t *testing.T) { | ||
std.TestSetRealm(gsaRealm) | ||
|
||
shouldEQ(t, GetDistributionPct(LIQUIDITY_STAKING), 7500) | ||
shouldEQ(t, GetDistributionPct(DEVOPS), 2000) | ||
|
||
ChangeDistributionPct02( | ||
1, 5000, | ||
2, 4500, | ||
) | ||
shouldEQ(t, GetDistributionPct(LIQUIDITY_STAKING), 5000) | ||
shouldEQ(t, GetDistributionPct(DEVOPS), 4500) | ||
|
||
ChangeDistributionPct03( | ||
1, 5000, | ||
2, 4000, | ||
3, 1000, | ||
) | ||
shouldEQ(t, GetDistributionPct(LIQUIDITY_STAKING), 5000) | ||
shouldEQ(t, GetDistributionPct(DEVOPS), 4000) | ||
shouldEQ(t, GetDistributionPct(COMMUNITY_POOL), 1000) | ||
|
||
ChangeDistributionPct04( | ||
1, 10000, | ||
2, 0, | ||
3, 0, | ||
4, 0, | ||
) | ||
shouldEQ(t, GetDistributionPct(LIQUIDITY_STAKING), 10000) | ||
shouldEQ(t, GetDistributionPct(DEVOPS), 0) | ||
shouldEQ(t, GetDistributionPct(COMMUNITY_POOL), 0) | ||
shouldEQ(t, GetDistributionPct(XGNS), 0) | ||
} |
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,41 @@ | ||
package emission | ||
|
||
import ( | ||
"std" | ||
"testing" | ||
|
||
"gno.land/r/demo/gns" | ||
) | ||
|
||
func TestEmitGns(t *testing.T) { | ||
shouldEQ(t, gns.TotalSupply(), 100000000000000) // GSA has | ||
shouldEQ(t, gnsBalance(emissionAddr), 0) | ||
|
||
EmitGns() // 1 ~ 123 height | ||
|
||
shouldEQ(t, gnsBalance(emissionAddr), 4387842345) | ||
shouldEQ(t, gns.TotalSupply(), 100000000000000+4387842345) | ||
|
||
shouldEQ(t, std.GetHeight(), 123) | ||
} | ||
|
||
func TestEmitGnsSameBlock(t *testing.T) { | ||
// request mint again in same block => do not mint again | ||
// it may happen because single block can have multiple txs & msgs | ||
EmitGns() | ||
shouldEQ(t, gns.TotalSupply(), 100000000000000+4387842345) | ||
} | ||
|
||
func TestEmitGnsAllAmount(t *testing.T) { | ||
std.TestSkipHeights(75686400 - 1) | ||
gns.TestSetLastMintedHeight(std.GetHeight()) | ||
std.TestSkipHeights(1) | ||
|
||
EmitGns() | ||
shouldEQ(t, gns.TotalSupply(), 100000000000000+4387842345+2229594) | ||
// all emission duration has been passed | ||
|
||
std.TestSkipHeights(1) | ||
EmitGns() // since all emission has been done, no more minting | ||
shouldEQ(t, gns.TotalSupply(), 100000000000000+4387842345+2229594) | ||
} |
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,154 @@ | ||
package emission | ||
|
||
import ( | ||
"std" | ||
|
||
"gno.land/r/demo/gnoswap/consts" | ||
"gno.land/r/demo/gns" | ||
|
||
"gno.land/p/demo/ufmt" | ||
) | ||
|
||
// emissionTarget represents different targets for token emission. | ||
type emissionTarget int | ||
|
||
// distributionPctMap maps emission targets to their respective distribution percentages. | ||
type distributionPctMap map[emissionTarget]uint64 | ||
|
||
const ( | ||
LIQUIDITY_STAKING emissionTarget = iota + 1 | ||
DEVOPS | ||
COMMUNITY_POOL | ||
XGNS | ||
) | ||
|
||
// distributionPct defines the distribution percentages. | ||
var distributionPct distributionPctMap = distributionPctMap{ | ||
LIQUIDITY_STAKING: 7500, // 75% | ||
DEVOPS: 2000, // 20% | ||
COMMUNITY_POOL: 500, // 5% | ||
XGNS: 0, // 0% | ||
} | ||
|
||
// DistributeToTarget distributes the specified amount to different targets based on their percentages. | ||
func DistributeToTarget(amount uint64) { | ||
totalSent := uint64(0) | ||
for target, pct := range distributionPct { | ||
distAmount := calculateAmount(amount, pct) | ||
totalSent += distAmount | ||
|
||
transferToTarget(target, distAmount) | ||
} | ||
|
||
// `amount-totalSent` can be left due to rounding | ||
// it will be distributed next time | ||
} | ||
|
||
// GetDistributionPct returns the distribution percentage for the given target. | ||
func GetDistributionPct(target emissionTarget) uint64 { | ||
return distributionPct[target] | ||
} | ||
|
||
// ChangeDistributionPct01 changes the distribution percentage for the given single target. | ||
func ChangeDistributionPct01(target01 emissionTarget, pct01 uint64) { | ||
changeDistributionPct(target01, pct01) | ||
|
||
checkSumDistributionPct() | ||
} | ||
|
||
// ChangeDistributionPct02 changes the distribution percentage for the given two targets. | ||
func ChangeDistributionPct02( | ||
target01 emissionTarget, pct01 uint64, | ||
target02 emissionTarget, pct02 uint64, | ||
) { | ||
changeDistributionPct(target01, pct01) | ||
changeDistributionPct(target02, pct02) | ||
|
||
checkSumDistributionPct() | ||
} | ||
|
||
// ChangeDistributionPct03 changes the distribution percentage for the given three targets. | ||
func ChangeDistributionPct03( | ||
target01 emissionTarget, pct01 uint64, | ||
target02 emissionTarget, pct02 uint64, | ||
target03 emissionTarget, pct03 uint64, | ||
) { | ||
changeDistributionPct(target01, pct01) | ||
changeDistributionPct(target02, pct02) | ||
changeDistributionPct(target03, pct03) | ||
|
||
checkSumDistributionPct() | ||
} | ||
|
||
// ChangeDistributionPct04 changes the distribution percentage for the given four targets. | ||
func ChangeDistributionPct04( | ||
target01 emissionTarget, pct01 uint64, | ||
target02 emissionTarget, pct02 uint64, | ||
target03 emissionTarget, pct03 uint64, | ||
target04 emissionTarget, pct04 uint64, | ||
) { | ||
changeDistributionPct(target01, pct01) | ||
changeDistributionPct(target02, pct02) | ||
changeDistributionPct(target03, pct03) | ||
changeDistributionPct(target04, pct04) | ||
|
||
checkSumDistributionPct() | ||
} | ||
|
||
// calculateAmount calculates the amount based on the given percentage in basis points. | ||
func calculateAmount(amount, bptPct uint64) uint64 { | ||
return amount * bptPct / 10000 | ||
} | ||
|
||
// transferToTarget transfers the specified amount to the given addresses. | ||
func transferToTarget(target emissionTarget, amount uint64) { | ||
switch target { | ||
case LIQUIDITY_STAKING: | ||
// transfer to staker contract | ||
gns.Transfer(a2u(consts.STAKER_ADDR), amount) | ||
case DEVOPS: | ||
// transfer to devops | ||
gns.Transfer(a2u(consts.DEV_OPS), amount) | ||
case COMMUNITY_POOL: | ||
// TBD, transfer to community pool | ||
gns.Transfer(a2u(consts.ZERO_ADDRESS), amount) | ||
case XGNS: | ||
// TBD, transfer to xGNS | ||
gns.Transfer(a2u(consts.ZERO_ADDRESS), amount) | ||
default: | ||
panic("invalid target") | ||
} | ||
} | ||
|
||
// changeDistributionPct changes the distribution percentage for the given target. | ||
func changeDistributionPct(target emissionTarget, pct uint64) { | ||
// only admin or governance can change | ||
caller := std.PrevRealm().Addr() | ||
if caller != consts.GNOSWAP_ADMIN && caller != consts.GOV_ADDR { | ||
panic("only admin or governance can change distribution percentages") | ||
} | ||
|
||
// cannot add new target | ||
if target != LIQUIDITY_STAKING && target != DEVOPS && target != COMMUNITY_POOL && target != XGNS { | ||
panic("invalid target") | ||
} | ||
|
||
// Maximum pct for a single target is 10000 basis points (100%) | ||
if pct > 10000 { | ||
panic("percentage too high") | ||
} | ||
|
||
distributionPct[target] = pct | ||
} | ||
|
||
// checkSumDistributionPct ensures the sum of all distribution percentages is 100% | ||
func checkSumDistributionPct() { | ||
sum := uint64(0) | ||
for _, pct := range distributionPct { | ||
sum += pct | ||
} | ||
|
||
if sum != 10000 { | ||
panic(ufmt.Sprintf("sum of all pct should be 100%% (10000 bps), got %d\n", sum)) | ||
} | ||
} |
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,14 @@ | ||
package emission | ||
|
||
import ( | ||
"std" | ||
|
||
"gno.land/r/demo/gnoswap/consts" | ||
"gno.land/r/demo/gns" | ||
) | ||
|
||
var emissionAddr std.Address = consts.EMISSION_ADDR | ||
|
||
func EmitGns() { | ||
gns.Mint(a2u(emissionAddr)) | ||
} |
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,11 @@ | ||
package emission | ||
|
||
import ( | ||
"std" | ||
|
||
pusers "gno.land/p/demo/users" | ||
) | ||
|
||
func a2u(addr std.Address) pusers.AddressOrName { | ||
return pusers.AddressOrName(addr) | ||
} |