Skip to content

Commit

Permalink
refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
ltzmaxwell committed Feb 1, 2023
1 parent 76d23ef commit 8fa34a2
Show file tree
Hide file tree
Showing 33 changed files with 2,560 additions and 2 deletions.
5 changes: 5 additions & 0 deletions examples/gno.land/p/demo/governance/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
## Acknowledgment

Based and adapted from:

* https://www.openzeppelin.com/
77 changes: 77 additions & 0 deletions examples/gno.land/p/demo/governance/checkpoints/checkpoints.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package checkpoints

import (
"std"
)

type Checkpoint struct {
blockNumber int64
value uint64
}

type History struct {
checkpoints []Checkpoint
}

// Returns the value in the latest checkpoint, or zero if there are no checkpoints.
func (history *History) Latest() uint64 {
pos := len(history.checkpoints)
if pos == 0 {
return 0
} else {
return history.checkpoints[pos-1].value
}
}

// Returns the value at a given block number. If a checkpoint is not available at that block, the closest one
// before it is returned, or zero otherwise.
func (history *History) GetAtBlock(blockNumber int64) uint64 {
height := std.GetHeight()
if blockNumber >= height {
panic("Checkpoints: block not yet mined")
}
var high int64
var low int64

high = int64(len(history.checkpoints))
low = 0
for low < high {
mid := _average(low, high)
if history.checkpoints[mid].blockNumber > blockNumber {
high = mid
} else {
low = mid + 1
}
}
if high == 0 {
return 0
}
return history.checkpoints[high-1].value
}

// (a + b) / 2 can overflow.
func _average(a, b int64) int64 {
return (a & b) + (a^b)/2
}

// Pushes a value onto a History so that it is stored as the checkpoint for the current block.
// Returns previous value and new value.
func (history *History) Push(value uint64) (uint64, uint64) {
pos := len(history.checkpoints)
old := history.Latest()
height := std.GetHeight()
// same block, update
if pos > 0 && history.checkpoints[pos-1].blockNumber == height {
history.checkpoints[pos-1].value = value
} else {
history.checkpoints = append(history.checkpoints, Checkpoint{blockNumber: height, value: value})
}
return old, value
}

// Pushes a value onto a History, by updating the latest value using binary operation `op`. The new value will
// be set to `op(latest, delta)`.
// Returns previous value and new value.
func (history *History) PushWithOp(op func(uint64, uint64) uint64, delta uint64) (uint64, uint64) {
return history.Push(op(history.Latest(), delta))
}
25 changes: 25 additions & 0 deletions examples/gno.land/p/demo/governance/counters/counters.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package counters

type Counter struct {
value uint64
}

func (counter *Counter) Current() uint64 {
return counter.value
}

func (counter *Counter) Increment() {
counter.value += 1
}

func (counter *Counter) Decrement() {
value := counter.value
if value <= 0 {
panic("Counter: decrement overflow")
}
counter.value = value - 1
}

func (counter *Counter) Reset() {
counter.value = 0
}
164 changes: 164 additions & 0 deletions examples/gno.land/p/demo/governance/governor/deposit.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
package governor

import std "std"
import "gno.land/p/demo/avl"

// update deposit for a proposal
// return when active
// burn when fail or not reaching quorum? NO

// deposits only burned when veto
func (gvr *Governor) Deposit(proposalId string) error {
// std.AssertOriginCall()
// caller is who deposit, will got returned deposit once succeeded
caller := std.GetOrigCaller()
// banker do the transfer work, for native coin
banker := std.GetBanker(std.BankerTypeOrigSend)
pkgaddr := std.GetOrigPkgAddr()
println("pkgaddr: ", pkgaddr)
// get deposit coin
var coinD std.Coin
sentCoins := std.GetOrigSend()
if len(sentCoins) == 1 {
coinD = sentCoins[0]
}
// check if return coin
var proposal *ProposalCore
proposalI, found := gvr.proposals.Get(proposalId)
if !found {
// return deposit
banker.SendCoins(pkgaddr, caller, sentCoins)
return ErrProposalNotExisit
} else {
proposal = proposalI.(*ProposalCore)
}

// check deposit state
if d, ok := gvr.GetDeposit(proposalId); ok {
if d.IsGTE(gvr.gs.minDeposit) {
return ErrDepositEnded
}
}

// if not return, send coins to governor pkg, coins is maintained in banker
to := std.DerivePkgAddr("gno.land/p/demo/governance")
banker.SendCoins(pkgaddr, to, sentCoins)

// maintain deposits: proposalId <=> total
total, ok := gvr.GetDeposit(proposalId)
sum := total.Add(coinD)
gvr.deposits.Set(proposalId, sum)

// manipulate proposal state, via setting voteStart and voteEnd parameter
var snapshot int64
// check if deposit amount reached
// check threshold
if sum.IsGTE(gvr.gs.minDeposit) {
// ok to vote
snapshot = std.GetHeight()
} else {
snapshot = std.GetHeight() + gvr.gs.getVotingDelay()
}

deadline := snapshot + gvr.gs.getVotingPeriod()
proposal.voteStart.SetDeadline(snapshot)
proposal.voteEnd.SetDeadline(deadline)

// maintain depositList: proposalId=>(caller=>amount)
var deposit *avl.Tree
idt, ok := gvr.depositList.Get(proposalId)
if !ok {
// first time deposit for this proposalId
deposit = avl.NewTree()
} else {
deposit = idt.(*avl.Tree) // caller => amount
}
// caller => amount sums
amt := std.Coin{Amount: 0, Denom: "ugnot"}
iamt, ok := deposit.Get(caller.String())
if ok {
amt = iamt.(std.Coin)
}
sum = amt.Add(coinD)
// update
// deposit.Set(caller.String(), sum)
deposit.Set(pkgaddr.String(), sum)
gvr.depositList.Set(proposalId, deposit)

return nil
}

// get deposit for a proposal
func (gvr *Governor) GetDeposit(proposalId string) (std.Coin, bool) {
d, ok := gvr.deposits.Get(proposalId)
if !ok {
return std.Coin{Amount: 0, Denom: "ugnot"}, false
}
deposit := d.(std.Coin)
return deposit, true
}

// trigger
func (gvr *Governor) Burn(proposalId string) {

}

// func (gvr *Governor) Refund(proposalId string, isBurn bool) error {
func (gvr *Governor) unDeposit(proposalId string, isBurn bool) error {
// banker do the transfer work, for native coin
banker := std.GetBanker(std.BankerTypeRealmSend)
vault := std.DerivePkgAddr("gno.land/p/demo/governance")
println(banker.GetCoins(vault))
// iterate depositList, get caller, and amount, update value
idt, ok := gvr.depositList.Get(proposalId)
if !ok {
return ErrProposalNotExisit
}
deposit := idt.(*avl.Tree)

var toAddr std.Address
deposit.Iterate("", "", func(n *avl.Node) bool {
c := n.Key()
a := n.Value().(std.Coin)
println("caller: ", c)
println("amount: ", a)
deposit.Set(c, std.Coins{std.Coin{Denom: "ugnot", Amount: 0}})
if !isBurn {
toAddr = std.Address(c)
} else {
toAddr = std.Address("")
}
// concrete send back coins
banker.SendCoins(vault, toAddr, std.Coins{a})
return false
})

// set every deposit to 0
gvr.depositList.Set(proposalId, deposit)

// set total amount to 0
gvr.deposits.Set(proposalId, std.Coins{std.Coin{Denom: "ugnot", Amount: 0}})
return nil
}

// return coins when succeed, or burn when fail
// fail in two ways: deposit not reach quorum, or 33.4% vote NOWithVeto
func (gvr *Governor) Undeposit(proposalId string) error {

// if passed, iterate depositList, return value, update deposits, and depositList
// return deposit
if gvr.state(proposalId) == Succeeded {
gvr.unDeposit(proposalId, false)
}
// if failed, burn deposit, update deposits, and depositList
// check NoWithVeto percentage, if exeeded 33.4% , burn, or if votes not reaching quorum, burn
if gvr.state(proposalId) == QuorumNotReach {
println("quorum not reach, going to burn")
gvr.unDeposit(proposalId, true)
}
if gvr.state(proposalId) == Defeated {
println("defeated, going to burn")
gvr.unDeposit(proposalId, true)
}
return nil
}
8 changes: 8 additions & 0 deletions examples/gno.land/p/demo/governance/governor/errors.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package governor

import "errors"

var (
ErrDepositEnded = errors.New("deposit ended")
ErrProposalNotExisit = errors.New("proposal not exist")
)
106 changes: 106 additions & 0 deletions examples/gno.land/p/demo/governance/governor/governor.gno
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package governor

import (
"gno.land/p/demo/avl"
"gno.land/p/demo/grc/exts/grc20votes"
"gno.land/p/demo/ufmt"
std "std"
)

type Governor struct {
name string
proposals *avl.Tree // ProposalId => ProposalCore
proposal_ext *avl.Tree
deposits *avl.Tree // proposalId <=> Amount, total amount
depositList *avl.Tree // proposalId <=> (caller <=> amount)
// cs *GovernorCountingSimple
tally Tally
gv *GovernorVotes
gs *GovernorSettings
}

// TODO: default option

func NewGovernor(name string, g20v *grc20votes.Grc20Votes, votingDelay int64, votingPeriod int64, minDeposit std.Coin, proposalThreshold int64, quorumNumerator uint64, vetoNumerator float64) *Governor {
return &Governor{
name: name,
proposals: avl.NewTree(),
proposal_ext: avl.NewTree(),
deposits: avl.NewTree(),
depositList: avl.NewTree(),
tally: NewGovernorCountingSimple(quorumNumerator, vetoNumerator, g20v),
gv: NewGovernorVotes(g20v),
gs: NewGovernorSettings(votingDelay, votingPeriod, minDeposit, proposalThreshold),
}
}

func (gvr *Governor) getName() string {
return gvr.name
}

func (gvr *Governor) getVersion() string {
return "1"
}

type TallyResult struct {
YesCount uint64
NoCount uint64
NoWithVetoCount uint64
AbstainCount uint64
}

// return Tally result
func (gvr *Governor) Tally(proposalId string) (state ProposalState, isBurnDeposit bool, tResult TallyResult) {
state = gvr.state(proposalId)
isBurnDeposit = gvr.tally.isBurnDeposit(proposalId)
tResult = gvr.tally.getProposalVotes(proposalId)
return
}

// Default additional encoded parameters used by castVote methods that don't include them
// Note: Should be overriden by specific implementations to use an appropriate value, the
// meaning of the additional params, in the context of that implementation
func defaultParams() []byte {
return []byte("")
}

// Returns weither `account` has cast a vote on `proposalId`.
// func hasVoted(proposalId uint64, account std.Address) bool{}

func (gvr *Governor) SetVotingDelay(vd int64) {
gvr.gs.setVotingDelay(vd)
}

// Relays a transaction or function call to an arbitrary target. In cases where the governance executor
// is some contract other than the governor itself, like when using a timelock, this function can be invoked
// in a governance proposal to recover tokens or Ether that was sent to the governor contract by mistake.
// Note that if the executor is simply the governor itself, use of `relay` is redundant.
func relay(target std.Address, value uint64, data []byte) {
// TODO: only governance
// TODO: relay
}

// Address through which the governor executes action. Will be overloaded by module that execute actions
// through another contract such as a timelock.
func executor() std.Address {
// TODO: return executor
return ""
}

func (gvr *Governor) RenderHome(proposalId string) string {
state := gvr.state(proposalId)
snapShot := gvr.proposalSnapshot(proposalId)
deadLine := gvr.proposalDeadline(proposalId)
totalDeposit := std.Coin{Amount: 0, Denom: "ugnot"}
itotal, ok := gvr.deposits.Get(proposalId)
if ok {
totalDeposit = itotal.(std.Coin)
}

str := ""
str += ufmt.Sprintf("* **Proposal state is***: %s\n", state.String())
str += ufmt.Sprintf("* **Proposal snapShot is***: %d\n", snapShot)
str += ufmt.Sprintf("* **Proposal deadLine is***: %d\n", deadLine)
str += ufmt.Sprintf("* **Proposal deposit is***: %s\n", totalDeposit.String())
return str
}
Loading

0 comments on commit 8fa34a2

Please sign in to comment.