Skip to content

Commit

Permalink
GSW-1845 refactor: gns
Browse files Browse the repository at this point in the history
  • Loading branch information
r3v4s committed Dec 11, 2024
1 parent 2e7f70f commit 729ef5a
Show file tree
Hide file tree
Showing 5 changed files with 145 additions and 218 deletions.
156 changes: 68 additions & 88 deletions _deploy/r/gnoswap/gns/gns.gno
Original file line number Diff line number Diff line change
Expand Up @@ -16,39 +16,78 @@ import (
"gno.land/r/gnoswap/v1/consts"
)

const MAXIMUM_SUPPLY = uint64(1_000_000_000_000_000) // 1B
const (
MAXIMUM_SUPPLY = uint64(1_000_000_000_000_000)
INITIAL_MINT_AMOUNT = uint64(100_000_000_000_000)
MAX_EMISSION_AMOUNT = MAXIMUM_SUPPLY - INITIAL_MINT_AMOUNT
)

var (
lastMintedHeight = std.GetHeight()
)

var (
lastMintedHeight int64
amountToEmission uint64
leftEmissionAmount = MAX_EMISSION_AMOUNT
)

var (
Token, privateLedger = grc20.NewToken("Gnoswap", "GNS", 6)
UserTeller = Token.CallerTeller()
owner = ownable.NewWithAddress("g17290cwvmrapvp869xfnhhawa8sm9edpufzat7d") // ADMIN
owner = ownable.NewWithAddress(consts.ADMIN)
)

func init() {
privateLedger.Mint(owner.Owner(), 100_000_000_000_000) // 100_000_000 GNS ≈ 0.1B
privateLedger.Mint(owner.Owner(), INITIAL_MINT_AMOUNT)
getter := func() *grc20.Token { return Token }
grc20reg.Register(getter, "")
}

amountToEmission = MAXIMUM_SUPPLY - uint64(100_000_000_000_000)
// MintedEmissionAmount returns the amount of GNS that has been minted by the emission contract.
// It does not include initial minted amount.
func MintedEmissionAmount() uint64 {
return TotalSupply() - INITIAL_MINT_AMOUNT
}

lastMintedHeight = std.GetHeight()
func Mint(address pusers.AddressOrName) uint64 {
common.IsHalted()

caller := std.PrevRealm().Addr()
if err := common.EmissionOnly(caller); err != nil {
panic(err)
}

lastMintedHeight := GetLastMintedHeight()
currentHeight := std.GetHeight()

// skip minting process if gns for current block is already minted
if skipIfSameHeight(lastMintedHeight, currentHeight) {
return 0
}

// calculate gns amount to mint, and the mint to the target address
amountToMint := calculateAmountToMint(lastMintedHeight+1, currentHeight)
err := privateLedger.Mint(users.Resolve(address), amountToMint)
if err != nil {
panic(err.Error())
}

// update
setLastMintedHeight(currentHeight)
setLeftEmissionAmount(leftEmissionAmount - amountToMint)

return amountToMint
}

func GetAmountToEmission() uint64 { return amountToEmission }
func Burn(from pusers.AddressOrName, amount uint64) {
owner.AssertCallerIsOwner()
fromAddr := users.Resolve(from)
checkErr(privateLedger.Burn(fromAddr, amount))
}

func TotalSupply() uint64 {
return UserTeller.TotalSupply()
}

func TotalMinted() uint64 {
return UserTeller.TotalSupply() - uint64(100_000_000_000_000)
}

func BalanceOf(owner pusers.AddressOrName) uint64 {
ownerAddr := users.Resolve(owner)
return UserTeller.BalanceOf(ownerAddr)
Expand Down Expand Up @@ -93,93 +132,34 @@ func Render(path string) string {
}
}

// Mint mints GNS to the address.
// Only emission contract can call Mint.
func Mint(address pusers.AddressOrName) uint64 {
common.IsHalted()

caller := std.PrevRealm().Addr()
if caller != consts.EMISSION_ADDR {
panic(addDetailToError(
errNoPermission,
ufmt.Sprintf("only emission contract(%s) can call Mint, called from %s", consts.EMISSION_ADDR, caller.String()),
))
}

// if not yet initialized, mint 0 amount
if initialized == false {
return 0
}

// calculate gns emission amount for every block, and send by single call
// for this case, we assume that inside of block range gnoswap state hasn't changed.
nowHeight := std.GetHeight()
amountToMint := uint64(0)

if lastMintedHeight >= nowHeight {
return 0
}

// If from, to block is at same halving year, no need iterate
fromYear := GetHalvingYearByHeight(lastMintedHeight + 1)
toYear := GetHalvingYearByHeight(nowHeight)

if fromYear == toYear {
numBlock := uint64(nowHeight - lastMintedHeight)
singleBlockAmount := GetAmountByHeight(nowHeight)
totalBlockAmount := singleBlockAmount * numBlock

amountToMint += totalBlockAmount
amountToMint = checkAndHandleIfLastBlockOfHalvingYear(nowHeight, amountToMint)

halvingYearMintAmount[fromYear] += totalBlockAmount
} else {
for i := lastMintedHeight + 1; i <= nowHeight; i++ {
amount := GetAmountByHeight(i)
amount = checkAndHandleIfLastBlockOfHalvingYear(i, amount)
year := GetHalvingYearByHeight(i)
halvingYearMintAmount[year] += amount
amountToMint += amount
}
}

err := privateLedger.Mint(users.Resolve(address), amountToMint)
func checkErr(err error) {
if err != nil {
panic(err.Error())
}
}

lastMintedHeight = nowHeight

return amountToMint
func isInitialized() bool {
return initialized
}

func Burn(from pusers.AddressOrName, amount uint64) {
owner.AssertCallerIsOwner()
fromAddr := users.Resolve(from)
checkErr(privateLedger.Burn(fromAddr, amount))
func GetLastMintedHeight() int64 {
return lastMintedHeight
}

func checkAndHandleIfLastBlockOfHalvingYear(height int64, amount uint64) uint64 {
year := GetHalvingYearByHeight(height)
lastBlock := halvingYearBlock[year]
if height == lastBlock {
leftForThisYear := halvingYearAmount[year] - halvingYearMintAmount[year]
amount = leftForThisYear
return amount
}
func setLastMintedHeight(height int64) {
lastMintedHeight = height
}

return amount
func GetLeftEmissionAmount() uint64 {
return leftEmissionAmount
}

func checkErr(err error) {
if err != nil {
panic(err.Error())
// skipIfSameHeight returns true if the current block height is the same as the last minted height.
// This prevents multiple gns minting inside the same block.
func skipIfSameHeight(lastMintedHeight, currentHeight int64) bool {
if lastMintedHeight == currentHeight {
return true
}
}

// TODO:
// 1. when emission contract mint gns reward, last executed height should be get from gns contract.
// mint function of gns contract and mintGns function of emission contract should be synchronized.
func GetLastMintedHeight() int64 {
return lastMintedHeight
return false
}
70 changes: 70 additions & 0 deletions _deploy/r/gnoswap/gns/gns_test.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package gns

import (
"std"
"testing"

"gno.land/p/demo/testutils"
"gno.land/p/demo/uassert"

"gno.land/r/gnoswap/v1/consts"

"gno.land/r/gnoswap/v1/gns"
)

const (
GNO_VM_DEFAULT_HEIGHT = int64(123)
)

var (
addr01 = testutils.TestAddress("addr01")
addr01Realm = std.NewUserRealm(addr01)
emissionRealm = std.NewCodeRealm(consts.EMISSION_PATH)
)

func TestInitData(t *testing.T) {
t.Run("total supply", func(t *testing.T) {
uassert.Equal(t, INITIAL_MINT_AMOUNT, TotalSupply())
})

t.Run("balance of admin", func(t *testing.T) {
uassert.Equal(t, INITIAL_MINT_AMOUNT, BalanceOf(a2u(consts.ADMIN)))
})

t.Run("last minted height", func(t *testing.T) {
uassert.Equal(t, GNO_VM_DEFAULT_HEIGHT, lastMintedHeight) // gnoVM(in test) default height
})
}

func TestMint(t *testing.T) {
t.Run("panic if caller is not emission", func(t *testing.T) {
std.TestSetRealm(addr01Realm)
uassert.PanicsWithMessage(t,
`caller(g1v9jxgu3sx9047h6lta047h6lta047h6l0js7st) has no permission`,
func() { Mint(a2u(addr01)) })
})

t.Run("success mint", func(t *testing.T) {
std.TestSkipHeights(1)

uassert.Equal(t, uint64(0), gns.BalanceOf(a2u(consts.EMISSION_ADDR)))

std.TestSetRealm(emissionRealm)
uassert.Equal(t, amountPerBlockPerHalvingYear[1], Mint(a2u(consts.EMISSION_ADDR))) // 1 block for year 01

uassert.Equal(t, std.GetHeight(), GetLastMintedHeight())
uassert.Equal(t, MAX_EMISSION_AMOUNT-amountPerBlockPerHalvingYear[1], GetLeftEmissionAmount())

uassert.Equal(t, amountPerBlockPerHalvingYear[1], gns.BalanceOf(a2u(consts.EMISSION_ADDR)))
})
}

func TestSkipIfSameHeight(t *testing.T) {
t.Run("should skip if height is same", func(t *testing.T) {
uassert.True(t, skipIfSameHeight(1, 1))
})

t.Run("should not skip if height is different", func(t *testing.T) {
uassert.False(t, skipIfSameHeight(1, 2))
})
}
2 changes: 1 addition & 1 deletion _deploy/r/gnoswap/gns/halving.gno
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ func setAvgBlockTimeInMs(ms int64) {
blockLeft := timeLeftMs / avgBlockTimeMs

// how many reward left to next halving
minted := TotalMinted()
minted := MintedEmissionAmount()
amountLeft := halvingYearAccuAmount[year] - minted

// how much reward per block
Expand Down
Loading

0 comments on commit 729ef5a

Please sign in to comment.