Skip to main content

veNOTA

The veNOTA contract (Solidity name NotareumVeNOTA) implements vote-escrowed NOTA. Holders lock NOTA for a chosen duration (up to 4 years) and receive non-transferable veNOTA balances that decay linearly to zero as the unlock date approaches. Governance weight and validator reward boosts are computed from the current balanceOf read on the contract. Longer locks yield more governance weight and deeper reward boosts per NOTA committed, at the cost of illiquidity.

Design origin

The mechanic follows the ve(3,3) lineage pioneered by Curve’s veCRV: time-weighted voting power with linear decay, no early withdrawal, governance-controlled boost mechanics. Notareum’s variant sheds the gauge system and stays focused on protocol governance and validator reward multipliers.

Parameters

Voting power curve

For a lock of amount NOTA expiring at end, the veNOTA balance at time t:
where MAXTIME = 4 * 365 * 86400. A 1000-NOTA lock for 4 years starts at 1000 veNOTA and decays to 0 over 4 years. A 1000-NOTA lock for 1 year starts at 250 veNOTA.

Data structures

Per-user point history and a global point history are maintained, mirroring the canonical veCRV layout. Global history enables historical voting-weight queries for proposals that snapshot at a block.

Functions

createLock

Transfers amount NOTA from the caller and creates a lock ending at unlockTime (rounded down to the nearest weekly boundary). Reverts if:
  • The caller already has a lock.
  • amount == 0.
  • unlockTime <= block.timestamp.
  • unlockTime - block.timestamp > MAXTIME.
  • unlockTime - block.timestamp < MIN_LOCK_TIME.
Emits LockCreated(user, amount, unlockTime).

increaseLockAmount

Adds amount NOTA to the caller’s existing lock. Unlock time is unchanged. Reverts if the lock has expired. Emits LockIncreased(user, amount, newTotal).

extendLock

Pushes the unlock time forward. Reverts if:
  • newUnlockTime <= currentLock.end.
  • newUnlockTime - block.timestamp > MAXTIME.
Both the voting power at current time and at subsequent times are recomputed. Emits LockExtended(user, oldEnd, newEnd).

withdraw

After the lock has expired, withdraws the full locked amount to the caller. Emits LockWithdrawn(user, amount).

balanceOf

balanceOf returns the current veNOTA balance. balanceOfAt returns the veNOTA balance at a historical block, used by governance for snapshot voting.

totalSupply

Returns the aggregate veNOTA supply, used as the denominator for quorum computations.

Non-transferability

The contract rejects all transfers:
Governance power cannot be bought or sold; it must be earned by locking.

Integration points

  • Governance. Proposal voting weight is veNOTA.balanceOfAt(voter, proposalBlock).
  • Validator reward boost. Validators who lock additional NOTA receive a boost on attestation rewards proportional to their veNOTA balance.
  • Dispute bond scaling. Disputants’ required bond can scale inversely with veNOTA balance to reward long-term commitment.
  • Staking synergy. Validators’ own staked NOTA in ValidatorStaking does not count toward veNOTA; the two contracts are independent.

Boost formula (illustrative)

A validator’s effective attestation reward:
where R is a governance parameter (default 1.0). A validator with equal veNOTA and stake receives the full 2.5× boost; one with no lock receives 1.0×. The exact boost curve and ceiling are set at initialization and changeable by governance.

Checkpointing

Every state change (createLock, increaseLockAmount, extendLock, withdraw) writes a user point and a global point. Points form a piecewise-linear representation of the decay curve. The contract maintains slopeChanges[week] to handle lock expiries efficiently when updating the global supply.
The internal checkpoint is identical to Curve’s canonical implementation with gas micro-optimizations.

Events

SDK usage

Invariants

  1. lock.amount for an existing lock is monotonically non-decreasing until withdrawal.
  2. lock.end is monotonically non-decreasing until withdrawal; it can never be pulled forward.
  3. end - startedAt <= MAXTIME at lock creation or extension.
  4. balanceOf(user) == 0 after the lock end has passed and withdrawal has occurred.
  5. The contract’s NOTA balance equals the sum of all outstanding lock.amount.