Skip to content

Commit

Permalink
GSW-1845 refactor: assert too many emission amount
Browse files Browse the repository at this point in the history
  • Loading branch information
r3v4s committed Dec 20, 2024
1 parent 6b66aef commit b7cf8fb
Show file tree
Hide file tree
Showing 10 changed files with 292 additions and 165 deletions.
3 changes: 2 additions & 1 deletion _deploy/r/gnoswap/gns/errors.gno
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import (
)

var (
errNoPermission = errors.New("[GNOSWAP-GNS-001] caller has no permission")
errNoPermission = errors.New("[GNOSWAP-GNS-001] caller has no permission")
errTooManyEmission = errors.New("[GNOSWAP-GNS-002] too many emission reward")
)

func addDetailToError(err error, detail string) string {
Expand Down
39 changes: 30 additions & 9 deletions _deploy/r/gnoswap/gns/gns.gno
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ var (
leftEmissionAmount uint64
mintedEmissionAmount uint64
lastMintedHeight int64

burnAmount uint64
)

func init() {
Expand All @@ -56,15 +58,15 @@ func MintGns(address pusers.AddressOrName) uint64 {

// skip minting process if following conditions are met
// - if gns for current block is already minted
// - if last minted height is same as last height to mint ( 12 years passed )
if lastMintedHeight == currentHeight || lastMintedHeight == GetEndHeight() {
// - if last minted height is same or later than emission end height
if lastMintedHeight == currentHeight || lastMintedHeight >= GetEndHeight() {
return 0
}

assertShouldNotBeHalted()
assertCallerIsEmission()

// calculate gns amount to mint, and the mint to the target address
// calculate gns amount to mint
amountToMint := calculateAmountToMint(lastMintedHeight+1, currentHeight)

// update
Expand All @@ -84,6 +86,8 @@ func Burn(from pusers.AddressOrName, amount uint64) {
owner.AssertCallerIsOwner()
fromAddr := users.Resolve(from)
checkErr(privateLedger.Burn(fromAddr, amount))

burnAmount += amount
}

func TotalSupply() uint64 {
Expand Down Expand Up @@ -153,37 +157,38 @@ func calculateAmountToMint(fromHeight, toHeight int64) uint64 {

totalAmountToMint := uint64(0)

for i := fromYear; i <= toYear; i++ {
yearEndHeight := GetHalvingYearBlock(i)
for year := fromYear; year <= toYear; year++ {
yearEndHeight := GetHalvingYearEndBlock(year)
mintUntilHeight := i64Min(yearEndHeight, toHeight)

// how many blocks to calculate
blocks := uint64(mintUntilHeight-fromHeight) + 1

// amount of gns to mint for each block for current year
singleBlockAmount := GetAmountPerBlockPerHalvingYear(i)
singleBlockAmount := GetAmountPerBlockPerHalvingYear(year)

// amount of gns to mint for current year
yearAmountToMint := singleBlockAmount * blocks

// if last block of halving year, handle left emission amount
if isLastBlockOfHalvingYear(mintUntilHeight) {
yearAmountToMint += handleLeftEmissionAmount(i, yearAmountToMint)
yearAmountToMint += handleLeftEmissionAmount(year, yearAmountToMint)
}
totalAmountToMint += yearAmountToMint
setHalvingYearMintAmount(i, GetHalvingYearMintAmount(i)+yearAmountToMint)
setHalvingYearMintAmount(year, GetHalvingYearMintAmount(year)+yearAmountToMint)

// update fromHeight for next year (if necessary)
fromHeight = mintUntilHeight + 1
}

assertTooManyEmission(totalAmountToMint)
return totalAmountToMint
}

// isLastBlockOfHalvingYear returns true if the current block is the last block of a halving year.
func isLastBlockOfHalvingYear(height int64) bool {
year := GetHalvingYearByHeight(height)
lastBlock := GetHalvingYearBlock(year)
lastBlock := GetHalvingYearEndBlock(year)

return height == lastBlock
}
Expand Down Expand Up @@ -219,6 +224,11 @@ func GetLeftEmissionAmount() uint64 {
return leftEmissionAmount
}

// GetBurnAmount returns the amount of GNS that has been burned.
func GetBurnAmount() uint64 {
return burnAmount
}

// GetMintedEmissionAmount returns the amount of GNS that has been minted by the emission contract.
// It does not include initial minted amount.
func GetMintedEmissionAmount() uint64 {
Expand All @@ -236,3 +246,14 @@ func setLeftEmissionAmount(amount uint64) {
func setMintedEmissionAmount(amount uint64) {
mintedEmissionAmount = amount
}

// assertTooManyEmission asserts if the amount of gns to mint is too many.
// It checks if the amount of gns to mint is greater than the left emission amount or the total emission amount.
func assertTooManyEmission(amount uint64) {
if amount > GetLeftEmissionAmount() || (amount+GetMintedEmissionAmount()) > MAX_EMISSION_AMOUNT {
panic(addDetailToError(
errTooManyEmission,
ufmt.Sprintf("amount: %d", amount),
))
}
}
10 changes: 5 additions & 5 deletions _deploy/r/gnoswap/gns/gns_test.gno
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,14 @@ func TestIsLastBlockOfHalvingYear(t *testing.T) {
want bool
}, 0, 24)

for i := int64(1); i <= 12; i++ {
for i := HALVING_START_YEAR; i <= HALVING_END_YEAR; i++ {
tests = append(tests, struct {
name string
height int64
want bool
}{
name: fmt.Sprintf("last block of halving year %d", i),
height: GetHalvingYearBlock(i),
height: GetHalvingYearEndBlock(i),
want: true,
})

Expand All @@ -51,7 +51,7 @@ func TestIsLastBlockOfHalvingYear(t *testing.T) {
want bool
}{
name: fmt.Sprintf("not last block of halving year %d", i),
height: GetHalvingYearBlock(i) - 1,
height: GetHalvingYearEndBlock(i) - 1,
want: false,
})
}
Expand Down Expand Up @@ -120,13 +120,13 @@ func TestSkipIfEmissionEnded(t *testing.T) {

func TestGetterSetter(t *testing.T) {
t.Run("last minted height", func(t *testing.T) {
value := int64(1234)
value := int64(123)
setLastMintedHeight(value)
uassert.Equal(t, value, GetLastMintedHeight())
})

t.Run("left emission amount", func(t *testing.T) {
value := uint64(123456)
value := uint64(0)
setLeftEmissionAmount(value)
uassert.Equal(t, value, GetLeftEmissionAmount())
})
Expand Down
101 changes: 49 additions & 52 deletions _deploy/r/gnoswap/gns/halving_test.gno
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,22 @@ var (
)

var FIRST_BLOCK_OF_YEAR = []int64{
startHeight,
startHeight + BLOCK_PER_YEAR*1,
startHeight + BLOCK_PER_YEAR*2,
startHeight + BLOCK_PER_YEAR*3,
startHeight + BLOCK_PER_YEAR*4,
startHeight + BLOCK_PER_YEAR*5,
startHeight + BLOCK_PER_YEAR*6,
startHeight + BLOCK_PER_YEAR*7,
startHeight + BLOCK_PER_YEAR*8,
startHeight + BLOCK_PER_YEAR*9,
startHeight + BLOCK_PER_YEAR*10,
startHeight + BLOCK_PER_YEAR*11,
startHeight + (BLOCK_PER_YEAR * 0) + 1,
startHeight + (BLOCK_PER_YEAR * 1) + 2,
startHeight + (BLOCK_PER_YEAR * 2) + 3,
startHeight + (BLOCK_PER_YEAR * 3) + 4,
startHeight + (BLOCK_PER_YEAR * 4) + 5,
startHeight + (BLOCK_PER_YEAR * 5) + 6,
startHeight + (BLOCK_PER_YEAR * 6) + 7,
startHeight + (BLOCK_PER_YEAR * 7) + 8,
startHeight + (BLOCK_PER_YEAR * 8) + 9,
startHeight + (BLOCK_PER_YEAR * 9) + 10,
startHeight + (BLOCK_PER_YEAR * 10) + 11,
startHeight + (BLOCK_PER_YEAR * 11) + 12,
}

var FIRST_TIMESTAMP_OF_YEAR = []int64{
startTimestamp,
startTimestamp + consts.TIMESTAMP_YEAR*0,
startTimestamp + consts.TIMESTAMP_YEAR*1,
startTimestamp + consts.TIMESTAMP_YEAR*2,
startTimestamp + consts.TIMESTAMP_YEAR*3,
Expand Down Expand Up @@ -60,45 +60,6 @@ var END_TIMESTAMP_OF_YEAR = []int64{
startTimestamp + consts.TIMESTAMP_YEAR*12,
}

func TestSetAvgBlockTimeInMsByAdmin(t *testing.T) {
t.Run("panic if caller is not admin", func(t *testing.T) {
uassert.PanicsWithMessage(t,
"caller(g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm) has no permission",
func() {
SetAvgBlockTimeInMsByAdmin(1)
},
)
})

t.Run("success if caller is admin", func(t *testing.T) {
std.TestSkipHeights(1)
std.TestSetRealm(adminRealm)
SetAvgBlockTimeInMsByAdmin(2)
uassert.Equal(t, GetAvgBlockTimeInMs(), int64(2))
})
}

func TestSetAvgBlockTimeInMs(t *testing.T) {
t.Run("panic if caller is not governance contract", func(t *testing.T) {
uassert.PanicsWithMessage(t,
"caller(g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm) has no permission",
func() {
SetAvgBlockTimeInMs(3)
},
)
})

t.Run("success if caller is governance contract", func(t *testing.T) {
std.TestSkipHeights(3)
std.TestSetRealm(govRealm)
SetAvgBlockTimeInMs(4)
uassert.Equal(t, GetAvgBlockTimeInMs(), int64(4))
})

// reset to default for further test
setAvgBlockTimeInMs(consts.SECOND_IN_MILLISECOND * consts.BLOCK_GENERATION_INTERVAL)
}

func TestGetAmountByHeight(t *testing.T) {
for year := HALVING_START_YEAR; year <= HALVING_END_YEAR; year++ {
firstBlockOfYear := FIRST_BLOCK_OF_YEAR[year-1]
Expand Down Expand Up @@ -173,3 +134,39 @@ func TestGetHalvingInfo(t *testing.T) {
halving := jsonStr.MustKey("halvings").MustArray()
uassert.Equal(t, len(halving), 12)
}

func TestSetAvgBlockTimeInMsByAdmin(t *testing.T) {
t.Run("panic if caller is not admin", func(t *testing.T) {
uassert.PanicsWithMessage(t,
"caller(g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm) has no permission",
func() {
SetAvgBlockTimeInMsByAdmin(1)
},
)
})

t.Run("success if caller is admin", func(t *testing.T) {
std.TestSkipHeights(1)
std.TestSetRealm(adminRealm)
SetAvgBlockTimeInMsByAdmin(2)
uassert.Equal(t, GetAvgBlockTimeInMs(), int64(2))
})
}

func TestSetAvgBlockTimeInMs(t *testing.T) {
t.Run("panic if caller is not governance contract", func(t *testing.T) {
uassert.PanicsWithMessage(t,
"caller(g1wymu47drhr0kuq2098m792lytgtj2nyx77yrsm) has no permission",
func() {
SetAvgBlockTimeInMs(3)
},
)
})

t.Run("success if caller is governance contract", func(t *testing.T) {
std.TestSkipHeights(3)
std.TestSetRealm(govRealm)
SetAvgBlockTimeInMs(4)
uassert.Equal(t, GetAvgBlockTimeInMs(), int64(4))
})
}
37 changes: 23 additions & 14 deletions _deploy/r/gnoswap/gns/tests/gns_calculate_and_mint_test.gnoA
Original file line number Diff line number Diff line change
Expand Up @@ -18,44 +18,45 @@ var (
func TestCalculateAmountToMint(t *testing.T) {
t.Run("1 block for same year 01", func(t *testing.T) {
amount := calculateAmountToMint(GetLastMintedHeight()+1, GetLastMintedHeight()+1)
uassert.Equal(t, amountPerBlockPerHalvingYear[1], amount)
uassert.Equal(t, amountPerBlockPerHalvingYear[0], amount)
})

t.Run("2 block for same year 01", func(t *testing.T) {
amount := calculateAmountToMint(GetLastMintedHeight()+1, GetLastMintedHeight()+2)
uassert.Equal(t, amountPerBlockPerHalvingYear[2]*2, amount)
amount := calculateAmountToMint(GetLastMintedHeight()+2, GetLastMintedHeight()+3)
uassert.Equal(t, amountPerBlockPerHalvingYear[1]*2, amount)
})

t.Run("entire block for year 01 + 1 block for year 02", func(t *testing.T) {
calculateAmountToMint(halvingYearBlock[1], halvingYearBlock[1]+1)
minted := calculateAmountToMint(GetLastMintedHeight()+4, GetHalvingYearBlock(2))

// minted all amount for year 01
uassert.Equal(t, GetHalvingYearAmount(1), GetHalvingYearMintAmount(1))
uassert.Equal(t, GetHalvingYearMaxAmount(1), GetHalvingYearMintAmount(1))

// minted 1 block for year 02
uassert.Equal(t, amountPerBlockPerHalvingYear[2], GetHalvingYearMintAmount(2))
uassert.Equal(t, amountPerBlockPerHalvingYear[1], GetHalvingYearMintAmount(2))
})

t.Run("entire block for 12 years", func(t *testing.T) {
calculateAmountToMint(halvingYearBlock[1], halvingYearBlock[12])
calculateAmountToMint(GetHalvingYearBlock(1), GetHalvingYearBlock(12))

for year := int64(1); year <= 12; year++ {
uassert.Equal(t, GetHalvingYearAmount(year), GetHalvingYearMintAmount(year))
uassert.Equal(t, GetHalvingYearMaxAmount(year), GetHalvingYearMintAmount(year))
}
})

t.Run("no emission amount for after 12 years", func(t *testing.T) {
amount := calculateAmountToMint(halvingYearBlock[12], halvingYearBlock[12]+1)
amount := calculateAmountToMint(GetHalvingYearBlock(12), GetHalvingYearBlock(12)+1)
uassert.Equal(t, uint64(0), amount)
})

// clear for further test
halvingYearMintAmount = make(map[int64]uint64)
halvingYearMintAmount = []uint64{}
}

func TestMintGns(t *testing.T) {
t.Run("panic for swap is halted", func(t *testing.T) {
std.TestSetRealm(adminRealm)
std.TestSkipHeights(123) // pass some block to bypass last block check
common.SetHaltByAdmin(true) // set halt
uassert.PanicsWithMessage(t, "[GNOSWAP-COMMON-002] halted || gnoswap halted", func() {
MintGns(a2u(consts.ADMIN))
Expand All @@ -72,20 +73,21 @@ func TestMintGns(t *testing.T) {

t.Run("do not mint for same block", func(t *testing.T) {
std.TestSetRealm(emissionRealm)
std.TestSkipHeights(-123) // revert height to get caught by last block check
mintedAmount := MintGns(a2u(consts.ADMIN))
uassert.Equal(t, uint64(0), mintedAmount)
})

t.Run("mint by year, until emission ends", func(t *testing.T) {
for year := int64(1); year <= 12; year++ {
std.TestSkipHeights(BLOCK_PER_YEAR)
skipUntilLastHeightOfHalvingYear(t, year)

std.TestSetRealm(emissionRealm)
mintedAmount := MintGns(a2u(consts.ADMIN))

uassert.Equal(t, halvingYearAmount[year], mintedAmount)
uassert.Equal(t, halvingYearAmount[year], halvingYearMintAmount[year])
uassert.Equal(t, halvingYearAccuAmount[year], MintedEmissionAmount())
uassert.Equal(t, GetHalvingYearMaxAmount(year), mintedAmount)
uassert.Equal(t, GetHalvingYearMaxAmount(year), GetHalvingYearMintAmount(year))
uassert.Equal(t, GetHalvingYearAccuAmount(year), GetMintedEmissionAmount())
}
})

Expand All @@ -97,3 +99,10 @@ func TestMintGns(t *testing.T) {
uassert.Equal(t, uint64(0), mintedAmount)
})
}

func skipUntilLastHeightOfHalvingYear(t *testing.T, year int64) {
t.Helper()

lastHeight := GetHalvingYearEndBlock(year)
std.TestSkipHeights(lastHeight - std.GetHeight())
}
Loading

0 comments on commit b7cf8fb

Please sign in to comment.