Skip to content

Commit

Permalink
GSW-1845 refactor: calculate height for next halving when avg time ch…
Browse files Browse the repository at this point in the history
…anges
  • Loading branch information
r3v4s committed Dec 20, 2024
1 parent b7cf8fb commit b98a424
Show file tree
Hide file tree
Showing 6 changed files with 113 additions and 78 deletions.
23 changes: 5 additions & 18 deletions _deploy/r/gnoswap/gns/_helper_test.gno
Original file line number Diff line number Diff line change
Expand Up @@ -35,22 +35,9 @@ func resetGnsTokenObject(t *testing.T) {
func resetHalvingRelatedObject(t *testing.T) {
t.Helper()

for year := int64(1); year < 13; year++ {
setHalvingYearMaxAmount(year, HALVING_AMOUNTS_PER_YEAR[year-1])

if year == 1 {
setHalvingYearAccuAmount(year, HALVING_AMOUNTS_PER_YEAR[year-1])
} else {
setHalvingYearAccuAmount(year, GetHalvingYearAccuAmount(year-1)+HALVING_AMOUNTS_PER_YEAR[year-1])
}

setHalvingYearBlock(year, startHeight+BLOCK_PER_YEAR*year)
setHalvingYearTimestamp(year, startTimestamp+(consts.TIMESTAMP_YEAR*year))
setHalvingYearMintAmount(year, uint64(0))

amountPerYear := GetHalvingYearMaxAmount(year) // amount per year
amountPerDay := amountPerYear / consts.DAY_PER_YEAR // amount per day
amountPerBlock := amountPerDay / uint64(BLOCK_PER_DAY) // amount per block
setAmountPerBlockPerHalvingYear(year, uint64(amountPerBlock))
}
startHeight = std.GetHeight()
startTimestamp = time.Now().Unix()

initializeHalvingData()
setEndTimestamp(startTimestamp + consts.TIMESTAMP_YEAR*HALVING_END_YEAR)
}
122 changes: 86 additions & 36 deletions _deploy/r/gnoswap/gns/halving.gno
Original file line number Diff line number Diff line change
Expand Up @@ -44,55 +44,74 @@ const (
)

var (
BLOCK_PER_YEAR = consts.TIMESTAMP_YEAR / consts.BLOCK_GENERATION_INTERVAL
BLOCK_PER_DAY = consts.TIMESTAMP_DAY / consts.BLOCK_GENERATION_INTERVAL
blockPerYear = consts.TIMESTAMP_YEAR / consts.BLOCK_GENERATION_INTERVAL
blockPerDay = consts.TIMESTAMP_DAY / consts.BLOCK_GENERATION_INTERVAL

avgBlockTimeMs int64 = consts.SECOND_IN_MILLISECOND * consts.BLOCK_GENERATION_INTERVAL
)

var (
startHeight int64
startTimestamp int64

// (exactly) after 12 years, timestamp which gns emission will be ended
// can not be changed
endTimestamp int64
endTimestamp int64
)

var (
halvingYearBlock = make([]int64, HALVING_END_YEAR) // start block of each halving year
halvingYearTimestamp = make([]int64, HALVING_END_YEAR) // start timestamp of each halving year

halvingYearMaxAmount = make([]uint64, HALVING_END_YEAR)
halvingYearMintAmount = make([]uint64, HALVING_END_YEAR)
halvingYearAccuAmount = make([]uint64, HALVING_END_YEAR)
amountPerBlockPerHalvingYear = make([]uint64, HALVING_END_YEAR)
halvingYearStartBlock = make([]int64, HALVING_END_YEAR) // start block of each halving year
halvingYearEndBlock = make([]int64, HALVING_END_YEAR) // end block of each halving year
halvingYearTimestamp = make([]int64, HALVING_END_YEAR) // start timestamp of each halving year

halvingYearMaxAmount = make([]uint64, HALVING_END_YEAR) // max amount per year can be minted
halvingYearMintAmount = make([]uint64, HALVING_END_YEAR) // actual minted amount per year
halvingYearAccuAmount = make([]uint64, HALVING_END_YEAR) // accumulated minted amount per year
amountPerBlockPerHalvingYear = make([]uint64, HALVING_END_YEAR) // amount per block per year to mint
)

func init() {
startHeight = std.GetHeight()
startTimestamp = time.Now().Unix()

for year := HALVING_START_YEAR; year <= HALVING_END_YEAR; year++ {
setHalvingYearMaxAmount(year, HALVING_AMOUNTS_PER_YEAR[year-1])
// initialize halving data
initializeHalvingData()

if year == 1 {
setHalvingYearAccuAmount(year, HALVING_AMOUNTS_PER_YEAR[year-1])
// set end timestamp
// after 12 years, timestamp which gns emission will be ended
// it can not be changed
setEndTimestamp(startTimestamp + consts.TIMESTAMP_YEAR*HALVING_END_YEAR)
}

// initializeHalvingData initializes the halving data
// it should be called only once, so we call this in init()
func initializeHalvingData() {
for year := HALVING_START_YEAR; year <= HALVING_END_YEAR; year++ {
// set max emission amount per year
// each year can not mint more than this amount
currentYearMaxAmount := GetHalvingAmountsPerYear(year)
setHalvingYearMaxAmount(year, currentYearMaxAmount)

if year == HALVING_START_YEAR {
setHalvingYearAccuAmount(year, currentYearMaxAmount)
setHalvingYearStartBlock(year, startHeight)
setHalvingYearEndBlock(year, startHeight+(blockPerYear*year))
} else {
setHalvingYearAccuAmount(year, GetHalvingYearAccuAmount(year-1)+HALVING_AMOUNTS_PER_YEAR[year-1])
// accumulate amount until current year, is the sum of current year max amount and accumulated amount until previous year
setHalvingYearAccuAmount(year, currentYearMaxAmount+GetHalvingYearAccuAmount(year-1))

// start block of current year, is the next block of previous year end block
setHalvingYearStartBlock(year, GetHalvingYearEndBlock(year-1)+1)

// end block of current year, is sum of start block and block per year
setHalvingYearEndBlock(year, GetHalvingYearStartBlock(year)+blockPerYear)
}

setHalvingYearBlock(year, startHeight+BLOCK_PER_YEAR*(year-1))
setHalvingYearTimestamp(year, startTimestamp+(consts.TIMESTAMP_YEAR*(year-1)))
setHalvingYearMintAmount(year, uint64(0))

amountPerYear := GetHalvingYearMaxAmount(year) // amount per year
amountPerDay := amountPerYear / consts.DAY_PER_YEAR // amount per day
amountPerBlock := amountPerDay / uint64(BLOCK_PER_DAY) // amount per block
amountPerDay := currentYearMaxAmount / consts.DAY_PER_YEAR
amountPerBlock := amountPerDay / uint64(blockPerDay)
setAmountPerBlockPerHalvingYear(year, uint64(amountPerBlock))
}

setEndTimestamp(startTimestamp + consts.TIMESTAMP_YEAR*HALVING_END_YEAR)
setHalvingYearMintAmount(year, uint64(0))
}
}

func GetAvgBlockTimeInMs() int64 {
Expand Down Expand Up @@ -139,6 +158,11 @@ func SetAvgBlockTimeInMs(ms int64) {
func setAvgBlockTimeInMs(ms int64) {
common.IsHalted()

// update block per year
value1 := int64(consts.TIMESTAMP_YEAR * consts.SECOND_IN_MILLISECOND)
value2 := int64(value1 / ms)
blockPerYear = value2

now := time.Now().Unix()
height := std.GetHeight()

Expand All @@ -150,8 +174,7 @@ func setAvgBlockTimeInMs(ms int64) {
timeLeftMs := timeLeft * consts.SECOND_IN_MILLISECOND

// how many block left for current halving year
blockLeft := timeLeftMs / ms

blockLeft := (timeLeftMs / ms)
// how many reward left for current halving year
minted := GetMintedEmissionAmount()
amountLeft := GetHalvingYearAccuAmount(currentYear) - minted
Expand All @@ -160,6 +183,8 @@ func setAvgBlockTimeInMs(ms int64) {
adjustedAmountPerBlock := amountLeft / uint64(blockLeft)
setAmountPerBlockPerHalvingYear(currentYear, adjustedAmountPerBlock)

yearEndHeight := int64(0)
nextYearStartHeight := int64(0)
for year := HALVING_START_YEAR; year <= HALVING_END_YEAR; year++ {
if year < currentYear {
// pass past halving years
Expand All @@ -169,7 +194,20 @@ func setAvgBlockTimeInMs(ms int64) {
yearEndTimestamp := GetHalvingYearTimestamp(year) + consts.TIMESTAMP_YEAR
timeLeftForYear := yearEndTimestamp - now
numBlock := (timeLeftForYear * consts.SECOND_IN_MILLISECOND) / ms
setHalvingYearBlock(year, height+numBlock)
yearEndHeight := height + numBlock

if year == currentYear {
// for current year, update only end block
setHalvingYearEndBlock(year, yearEndHeight)
} else {
// update start block
prevYearEnd := GetHalvingYearEndBlock(year - 1)
nextYearStart := prevYearEnd + 1
nextYearEnd := nextYearStart + blockPerYear

setHalvingYearStartBlock(year, nextYearStart)
setHalvingYearEndBlock(year, nextYearEnd)
}
}

avgBlockTimeMs = ms
Expand All @@ -192,8 +230,8 @@ func GetHalvingYearByHeight(height int64) int64 {
}

for year := HALVING_START_YEAR; year <= HALVING_END_YEAR; year++ {
block := GetHalvingYearBlock(year)
if height <= block {
endBlock := GetHalvingYearEndBlock(year)
if height <= endBlock {
return year
}
}
Expand All @@ -216,12 +254,20 @@ func getHalvingYearAndEndTimestamp(timestamp int64) (int64, int64) {
return year, startTimestamp + (consts.TIMESTAMP_YEAR * year)
}

func GetHalvingYearBlock(year int64) int64 {
return halvingYearBlock[year-1]
func GetHalvingYearStartBlock(year int64) int64 {
return halvingYearStartBlock[year-1]
}

func setHalvingYearBlock(year int64, block int64) {
halvingYearBlock[year-1] = block
func setHalvingYearStartBlock(year int64, block int64) {
halvingYearStartBlock[year-1] = block
}

func GetHalvingYearEndBlock(year int64) int64 {
return halvingYearEndBlock[year-1]
}

func setHalvingYearEndBlock(year int64, block int64) {
halvingYearEndBlock[year-1] = block
}

func GetHalvingYearTimestamp(year int64) int64 {
Expand Down Expand Up @@ -264,10 +310,14 @@ func setAmountPerBlockPerHalvingYear(year int64, amount uint64) {
amountPerBlockPerHalvingYear[year-1] = amount
}

func GetHalvingAmountsPerYear(year int64) uint64 {
return HALVING_AMOUNTS_PER_YEAR[year-1]
}

func GetEndHeight() int64 {
// last block of last halving year(12) is last block of emission
// later than this block, no more gns will be minted
return GetHalvingYearBlock(HALVING_END_YEAR)
return GetHalvingYearEndBlock(HALVING_END_YEAR)
}

func GetEndTimestamp() int64 {
Expand All @@ -287,7 +337,7 @@ func GetHalvingInfo() string {
for year := HALVING_START_YEAR; year <= HALVING_END_YEAR; year++ {
halvings = append(halvings, json.ObjectNode("", map[string]*json.Node{
"year": json.StringNode("year", strconv.FormatInt(year, 10)),
"block": json.NumberNode("block", float64(GetHalvingYearBlock(year))),
"block": json.NumberNode("block", float64(GetHalvingYearStartBlock(year))),
"amount": json.NumberNode("amount", float64(GetAmountPerBlockPerHalvingYear(year))),
}))
}
Expand Down
30 changes: 15 additions & 15 deletions _deploy/r/gnoswap/gns/halving_test.gno
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,18 @@ var (
)

var FIRST_BLOCK_OF_YEAR = []int64{
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,
startHeight + (blockPerYear * 0) + 1,
startHeight + (blockPerYear * 1) + 2,
startHeight + (blockPerYear * 2) + 3,
startHeight + (blockPerYear * 3) + 4,
startHeight + (blockPerYear * 4) + 5,
startHeight + (blockPerYear * 5) + 6,
startHeight + (blockPerYear * 6) + 7,
startHeight + (blockPerYear * 7) + 8,
startHeight + (blockPerYear * 8) + 9,
startHeight + (blockPerYear * 9) + 10,
startHeight + (blockPerYear * 10) + 11,
startHeight + (blockPerYear * 11) + 12,
}

var FIRST_TIMESTAMP_OF_YEAR = []int64{
Expand Down Expand Up @@ -97,9 +97,9 @@ func TestGetHalvingYearAndEndTimestamp(t *testing.T) {
})
}

func TestHalvingYearBlock(t *testing.T) {
setHalvingYearBlock(1, 100)
uassert.Equal(t, GetHalvingYearBlock(1), int64(100))
func TestHalvingYearStartBlock(t *testing.T) {
setHalvingYearStartBlock(1, 100)
uassert.Equal(t, GetHalvingYearStartBlock(1), int64(100))
}

func TestHalvingYearTimestamp(t *testing.T) {
Expand Down
12 changes: 5 additions & 7 deletions _deploy/r/gnoswap/gns/tests/gns_calculate_and_mint_test.gnoA
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ func TestCalculateAmountToMint(t *testing.T) {
})

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

// minted all amount for year 01
uassert.Equal(t, GetHalvingYearMaxAmount(1), GetHalvingYearMintAmount(1))
Expand All @@ -37,20 +37,17 @@ func TestCalculateAmountToMint(t *testing.T) {
})

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

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

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

// clear for further test
halvingYearMintAmount = []uint64{}
}

func TestMintGns(t *testing.T) {
Expand Down Expand Up @@ -79,6 +76,7 @@ func TestMintGns(t *testing.T) {
})

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

Expand All @@ -92,7 +90,7 @@ func TestMintGns(t *testing.T) {
})

t.Run("no more emission after it ends", func(t *testing.T) {
std.TestSkipHeights(BLOCK_PER_YEAR)
std.TestSkipHeights(blockPerYear)

std.TestSetRealm(emissionRealm)
mintedAmount := MintGns(a2u(consts.ADMIN))
Expand Down
2 changes: 1 addition & 1 deletion _deploy/r/gnoswap/gns/tests/z2_filetest.gno
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ func blockTime4000ms() {
uassert.Equal(t, uint64(28538812), firstYearAmountPerBlock)

// next halving year start/end block
uassert.Equal(t, int64(7884149), gns.GetHalvingYearBlock(2))
uassert.Equal(t, int64(7884149), gns.GetHalvingYearStartBlock(2))
uassert.Equal(t, int64(15768149), gns.GetHalvingYearEndBlock(2))

// orig year01 start block = 123
Expand Down
2 changes: 1 addition & 1 deletion _deploy/r/gnoswap/gns/tests/z3_filetest.gno
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ func blockTime4000ms() {
uassert.Equal(t, uint64(713470300+28538812), gns.GetMintedEmissionAmount()) // 742009112

// next halving year start/end block
uassert.Equal(t, int64(7884149), gns.GetHalvingYearBlock(2))
uassert.Equal(t, int64(7884149), gns.GetHalvingYearStartBlock(2))
uassert.Equal(t, int64(15768149), gns.GetHalvingYearEndBlock(2))
}

Expand Down

0 comments on commit b98a424

Please sign in to comment.