Transaction

B253DC2D2402F2…3FC548589918

Block 26,406 · index 0 · indexed

Summary

Hash
B253DC2D2402F2AB3E2FDBED06D9F505B51A12E9609FD2B50AFD3FC548589918
Block
26,406
Size
7659 bytes
Gas used
12,263,893 / 14,716,731
Fee
1000000 ugnot
Memo
gnopublish
Status
success

Messages

Arguments · 9

  1. #1timelock
  2. #2README.md
  3. #3# Timelock > ⚠️ **Experimental — generated with no human supervision** by the daily MCP pipeline (Solidity→Gno port). Not audited. See [r/moul/x/daily](https://github.com/moul/gno-contracts/blob/main/r/moul/x/daily/README.md). --- A simplified gno.land port of OpenZeppelin's **TimelockController**. Actions are scheduled with a block delay and can only be executed once the chain reaches the operation's ready height; before that they can be cancelled. It ports the core delay-enforcement idea idiomatically — block height (`runtime.ChainHeight()`) replaces `block.number`, `panic` replaces `require`, `chain.Emit` replaces events, and an `avl.Tree` replaces the Solidity `mapping` (role-based access control is omitted for simplicity). Operations are terminal once executed or cancelled, and `Render` lists every operation with its live state (Pending / Ready / Executed / Cancelled) and the number of blocks remaining until it becomes ready. ## Example calls ``` # Schedule an action to run 20 blocks from now; returns the operation id. gnokey maketx call -pkgpath gno.land/r/moul/x/daily/timelock/v0 \ -func Schedule -args "upgrade contract" -args 20 ... # Once ChainHeight() >= readyHeight, execute it. gnokey maketx call -pkgpath gno.land/r/moul/x/daily/timelock/v0 \ -func Execute -args 1 ... # Or cancel it beforehand. gnokey maketx call -pkgpath gno.land/r/moul/x/daily/timelock/v0 \ -func Cancel -args 1 ... ``` View the queue by rendering the realm (`gno.land/r/moul/x/daily/timelock/v0`). <!-- BEGIN GNOCONTRACTS FOOTER (generated by `make readmes`; do not edit below) --> --- Part of **[moul/gno-contracts](https://github.com/moul/gno-contracts)** — moul's versioned gno.land contracts. See the repository for the full catalog, build/test tooling, and usage. **Dependency graph:** ![gno.land/r/moul/x/daily/timelock/v0 dependency graph](https://raw.githubusercontent.com/moul/gno-contracts/main/_assets/gno.land/r/moul/x/daily/timelock/v0/deps.png) > 🧪 **Highly experimental — potentially vibe-coded.** Not audited; may break, change, or be removed at any time. Do not use with anything of value. Full disclaimer: [DISCLAIMER](https://github.com/moul/gno-contracts/blob/main/DISCLAIMER.md). <!-- END GNOCONTRACTS FOOTER -->
  4. #4gnomod.toml
  5. #5module = "gno.land/r/moul/x/daily/timelock/v0" gno = "0.9"
  6. #6timelock.gno
  7. #7// Package timelock is a simplified port of OpenZeppelin's TimelockController. // // Operations are scheduled with a delay (measured in blocks). A scheduled // operation becomes executable only once the chain height reaches the // operation's ready height. Until then it can be cancelled by anyone (this // simplified port omits role-based access control). Once executed or // cancelled, an operation is terminal. // // Solidity mapping: // - block.number -> runtime.ChainHeight() // - msg.sender -> unsafe.PreviousRealm().Address() // - require(...) -> panic(...) // - events -> chain.Emit(...) // - mapping(id => op) -> avl.Tree keyed by zero-padded id package timelock import ( "strconv" "chain" "chain/runtime" "chain/runtime/unsafe" "gno.land/p/nt/avl/v0" "gno.land/p/nt/ufmt/v0" ) // State labels for an operation. const ( StatePending = "Pending" // scheduled, not yet ready StateReady = "Ready" // ready height reached, awaiting execution StateExecuted = "Executed" // terminal StateCancelled = "Cancelled" // terminal ) // Operation is a single scheduled action. type Operation struct { ID int Action string Proposer address Scheduled int64 // chain height at scheduling ReadyHeight int64 // executable once ChainHeight() >= ReadyHeight Done bool // executed Cancelled bool } var ( ops = avl.NewTree() // key: paddedKey(id) -> *Operation nextID = 1 ) // Schedule registers a new operation to run after delayBlocks blocks and // returns its id. delayBlocks must be >= 0. func Schedule(cur realm, action string, delayBlocks int) int { if action == "" { panic("timelock: empty action") } if delayBlocks < 0 { panic("timelock: negative delay") } h := runtime.ChainHeight() id := nextID nextID++ op := &Operation{ ID: id, Action: action, Proposer: unsafe.PreviousRealm().Address(), Scheduled: h, ReadyHeight: h + int64(delayBlocks), } ops.Set(paddedKey(id), op) chain.Emit("Scheduled", "id", strconv.Itoa(id), "action", action, "readyHeight", strconv.FormatInt(op.ReadyHeight, 10), ) return id } // Execute runs a ready operation. It panics if the operation is unknown, // already terminal, or not yet ready. func Execute(cur realm, id int) { op := mustGet(id) if op.Done { panic("timelock: already executed") } if op.Cancelled { panic("timelock: operation cancelled") } if runtime.ChainHeight() < op.ReadyHeight { panic("timelock: operation not ready") } op.Done = true chain.Emit("Executed", "id", strconv.Itoa(id), "action", op.Action) } // Cancel drops a pending or ready operation before it executes. It panics if // the operation is unknown or already terminal. func Cancel(cur realm, id int) { op := mustGet(id) if op.Done { panic("timelock: already executed") } if op.Cancelled { panic("timelock: already cancelled") } op.Cancelled = true chain.Emit("Cancelled", "id", strconv.Itoa(id), "action", op.Action) } // mustGet returns the operation or panics if absent. func mustGet(id int) *Operation { v := ops.Get(paddedKey(id)) if v == nil { panic("timelock: unknown operation") } return v.(*Operation) } // --- pure/read-only helpers (safe to unit-test) --- // paddedKey returns a fixed-width, lexicographically sortable key for an id so // avl iteration yields operations in id order. func paddedKey(id int) string { s := strconv.Itoa(id) for len(s) < 12 { s = "0" + s } return s } // StateOf returns the display state of op at the given chain height. func StateOf(op *Operation, height int64) string { switch { case op.Cancelled: return StateCancelled case op.Done: return StateExecuted case height >= op.ReadyHeight: return StateReady default: return StatePending } } // BlocksRemaining returns how many blocks remain before op is ready (0 if // already ready or past). func BlocksRemaining(op *Operation, height int64) int64 { if height >= op.ReadyHeight { return 0 } return op.ReadyHeight - height } // Render implements the standard realm markdown view. func Render(path string) string { h := runtime.ChainHeight() out := "# Timelock Controller\n\n" out += ufmt.Sprintf("Current chain height: **%d**\n\n", h) if ops.Size() == 0 { out += "_No operations scheduled yet._\n\n" out += "Call `Schedule(action, delayBlocks)` to queue one.\n" return out } out += ufmt.Sprintf("Total operations: **%d**\n\n", ops.Size()) out += "| ID | Action | State | Ready @ | Blocks left | Proposer |\n" out += "|---:|--------|-------|--------:|------------:|----------|\n" ops.Iterate("", "", func(_ string, v any) bool { op := v.(*Operation) state := StateOf(op, h) left := BlocksRemaining(op, h) out += ufmt.Sprintf("| %d | %s | %s | %d | %d | %s |\n", op.ID, op.Action, state, op.ReadyHeight, left, op.Proposer.String()) return false }) return out }
  8. #8/gno.MemPackageType
  9. #9 MPUserProd

Result log

msg:0,success:true,log:,events:[]