Description:
Governance contract for decentralized decision-making.
Blockchain: Ethereum
Source Code: View Code On The Blockchain
Solidity Source Code:
{{
"language": "Solidity",
"sources": {
"src/NounsPassVotingResolver.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;
/* ──────────────────────────────────────────────────────────────────────────
Nouns “Pass Voting?” resolver for PAMM (YES/NO)
• Governor: 0x6f3E6272A167e8AcCb32072d08E0957F9c79223d (NounsDAOLogicV4)
• PAMM: 0x000000000071176401AdA1f2CD7748e28E173FCa
• One market per Nouns proposal:
YES if the proposal *passed voting*:
state ∈ {Succeeded, Queued, Executed, Expired}
(checked right after endBlock when no objection, or at objection end, or at/after evalBlock)
NO if {Defeated, Vetoed}.
Other states are non-terminal and don’t resolve.
• Auto-sync across many proposals:
- syncNewProposals(max) → discover/adopt/create
- syncOne(id) / poke(id) → keep eval fresh & resolve ASAP
• Adopt-or-create: if a market with the same description/resolver already
exists (even if created by someone else), we adopt when closable or time-closed;
otherwise create our own.
• Objection refresh: updates evalBlock/closeAt once the DAO sets it.
• Emergency resolve: owner-only after a buffer AND only when Nouns state is
terminal (YES/NO). Will skip dead markets.
• Pausable creation/resolution, withdraw tips, ERC20 sweep.
───────────────────────────────────────────────────────────────────────── */
interface INounsGovernor {
enum ProposalState {
Pending, // 0
Active, // 1
Canceled, // 2
Defeated, // 3
Succeeded, // 4
Queued, // 5
Expired, // 6
Executed, // 7
Vetoed, // 8
ObjectionPeriod, // 9 (added in V3/V4)
Updatable // 10 (added in V3/V4)
}
struct ProposalCondensedV3 {
uint256 id;
address proposer;
uint256 proposalThreshold;
uint256 quorumVotes;
uint256 eta;
uint256 startBlock;
uint256 endBlock;
uint256 forVotes;
uint256 againstVotes;
uint256 abstainVotes;
bool canceled;
bool vetoed;
bool executed;
uint256 totalSupply;
uint256 creationBlock;
address[] signers;
uint256 updatePeriodEndBlock;
uint256 objectionPeriodEndBlock;
bool executeOnTimelockV1;
}
function proposalCount() external view returns (uint256);
function proposalsV3(uint256 id) external view returns (ProposalCondensedV3 memory);
function state(uint256 id) external view returns (ProposalState);
function objectionPeriodDurationInBlocks() external view returns (uint256);
}
interface IPAMM {
struct PMTuning {
uint32 lateRampStart; // seconds before close to start ramp (0 = off)
uint16 lateRampMaxBps; // +bps at/after close (EV charge)
uint16 extremeMaxBps; // +bps near p≈0/1
}
function createMarketWithPMTuning(
string calldata description,
address resolver,
uint72 close,
bool canClose,
uint256 seedYes,
uint256 seedNo,
PMTuning calldata t
) external returns (uint256 marketId, uint256 noId);
function closeMarket(uint256 marketId) external;
function resolve(uint256 marketId, bool outcome) external;
}
interface IPAMMExtra is IPAMM {
function getMarket(uint256 marketId)
external
view
returns (
uint256 yesSupply,
uint256 noSupply,
address resolver,
bool resolved,
bool outcome,
uint256 pot,
uint256 payoutPerShare,
string memory desc,
uint72 closeTs,
bool canClose,
uint256 rYes,
uint256 rNo,
uint256 pYes_num,
uint256 pYes_den
);
function balanceOf(address owner, uint256 id) external view returns (uint256);
function getNoId(uint256 marketId) external pure returns (uint256);
function impliedYesProb(uint256 marketId) external view returns (uint256 num, uint256 den);
}
contract NounsPassVotingResolver {
/* ───────────────────────── constants / addrs ───────────────────────── */
INounsGovernor public constant GOV = INounsGovernor(0x6f3E6272A167e8AcCb32072d08E0957F9c79223d);
IPAMMExtra public constant PAMM = IPAMMExtra(0x000000000071176401AdA1f2CD7748e28E173FCa);
// ZAMM address excluded from “circulating” (matches PAMM’s).
address public constant ZAMM = 0x000000000000040470635EB91b7CE4D132D616eD;
// For estimating PAMM close timestamp; resolution is block-gated.
uint256 public secsPerBlock = 12;
/* ───────────────────────── owner / admin ───────────────────────────── */
address public owner;
modifier onlyOwner() {
require(msg.sender == owner, "OWNER");
_;
}
event OwnershipTransferred(address indexed from, address indexed to);
function transferOwnership(address to) public payable onlyOwner {
emit OwnershipTransferred(msg.sender, owner = to);
}
/* ───────────────────────── tips / liveness ─────────────────────────── */
uint256 public tipPerAction = 0.001 ether;
event Funded(address indexed from, uint256 amount);
event TipPaid(address indexed to, uint256 amount);
receive() external payable {
emit Funded(msg.sender, msg.value);
}
function fundTips() public payable {
emit Funded(msg.sender, msg.value);
}
function setTipPerAction(uint256 tip) public payable onlyOwner {
tipPerAction = tip;
}
function setSecsPerBlock(uint256 s) public payable onlyOwner {
secsPerBlock = s;
}
// Owner may withdraw leftover ETH used for tips.
function withdrawTips(address payable to, uint256 amount) public payable onlyOwner {
if (amount == 0 || amount > address(this).balance) amount = address(this).balance;
(bool ok,) = to.call{value: amount}("");
require(ok, "WITHDRAW_FAIL");
}
// Owner can sweep arbitrary ERC20s accidentally sent here.
function sweepERC20(address token, address to, uint256 amount) public payable onlyOwner {
(bool ok, bytes memory d) =
token.call(abi.encodeWithSignature("transfer(address,uint256)", to, amount));
require(ok && (d.length == 0 || abi.decode(d, (bool))), "SWEEP_FAIL");
}
/* ───────────────────────── PM tuning / seeds ───────────────────────── */
IPAMM.PMTuning public pmDefaults = IPAMM.PMTuning({
lateRampStart: 15 minutes,
lateRampMaxBps: 100, // +1.00% EV charge at/after close
extremeMaxBps: 50 // +0.50% near extremes
});
uint256 public seedYes = 1e18; // initial symmetric seeds
uint256 public seedNo = 1e18;
uint256 public adoptMaxCloseDrift = 2 days;
function setPMTuning(IPAMM.PMTuning calldata t) public payable onlyOwner {
require(t.lateRampMaxBps <= 2000 && t.extremeMaxBps <= 2000, "TUNING_CAP");
pmDefaults = t;
}
function setSeeds(uint256 _yes, uint256 _no) public payable onlyOwner {
require((_yes == 0) == (_no == 0), "SEEDS_BOTH_ZERO_OR_BOTH_NONZERO");
seedYes = _yes;
seedNo = _no;
}
function setAdoptMaxCloseDrift(uint256 _adoptMaxCloseDrift) public payable onlyOwner {
adoptMaxCloseDrift = _adoptMaxCloseDrift;
}
/* ───────────────────────── markets / storage ───────────────────────── */
struct MarketInfo {
uint256 marketId;
uint64 evalBlock; // target evaluation block
uint72 closeAt; // PAMM close timestamp estimate (for early-close)
bool created;
bool settled;
bool deadLogged;
}
uint256 public lastScannedProposalId;
mapping(uint256 proposalId => MarketInfo) public marketOf;
event MarketCreated(
uint256 indexed proposalId, uint256 marketId, uint64 evalBlock, uint72 closeAt
);
event MarketAdopted(
uint256 indexed proposalId, uint256 marketId, uint64 evalBlock, uint72 closeAt
);
event MarketResolved(uint256 indexed proposalId, uint256 marketId, bool yes);
event DeadMarketSkipped(uint256 indexed proposalId, uint256 marketId);
event EvalUpdated(uint256 indexed proposalId, uint64 newEvalBlock, uint72 newCloseAt);
/* ───────────────────────── emergency backstop ──────────────────────── */
// Buffer (blocks) beyond evalBlock after which owner can emergency-resolve,
// but ONLY if Nouns state is terminal YES/NO.
uint256 public emergencyDelayBlocks = 7200; // ~1 day
event EmergencyResolved(uint256 indexed proposalId, uint256 marketId, bool outcome);
function setEmergencyDelayBlocks(uint256 d) public payable onlyOwner {
require(d >= 300 && d <= 200_000, "BAD_EMERGENCY_DELAY"); // ~1h .. ~4w
emergencyDelayBlocks = d;
}
function _deadSkip(MarketInfo storage m, uint256 proposalId) internal {
if (!m.deadLogged) {
m.deadLogged = true;
emit DeadMarketSkipped(proposalId, m.marketId);
}
}
/* ───────────────────────── switches ────────────────────────────────── */
bool public creationPaused;
bool public resolutionPaused;
function setCreationPaused(bool p) public payable onlyOwner {
creationPaused = p;
}
function setResolutionPaused(bool p) public payable onlyOwner {
resolutionPaused = p;
}
/* ───────────────────────── reentrancy guard ────────────────────────── */
error Reentrancy();
uint256 constant REENTRANCY_GUARD_SLOT = 0x929eee149b4bd21268;
modifier nonReentrant() {
assembly ("memory-safe") {
if tload(REENTRANCY_GUARD_SLOT) {
mstore(0x00, 0xab143c06)
revert(0x1c, 0x04)
}
tstore(REENTRANCY_GUARD_SLOT, address())
}
_;
assembly ("memory-safe") {
tstore(REENTRANCY_GUARD_SLOT, 0)
}
}
/* ───────────────────────── constructor ─────────────────────────────── */
constructor() payable {
emit OwnershipTransferred(address(0), owner = msg.sender);
}
/* ───────────────────────── public sync API ─────────────────────────── */
/// @notice Discover/adopt/create markets for newly-created proposals up to `maxScan`.
function syncNewProposals(uint256 maxScan) public payable nonReentrant {
if (creationPaused) return;
uint256 latest = GOV.proposalCount();
uint256 hi = lastScannedProposalId + maxScan;
if (hi > latest) hi = latest;
for (uint256 id = lastScannedProposalId + 1; id <= hi; ++id) {
_maybeCreateOrAdopt(id);
}
lastScannedProposalId = hi;
}
/// @notice Idempotent per-proposal sync: adopt/create if needed, then try to resolve if ready.
function syncOne(uint256 proposalId) public payable nonReentrant {
if (!creationPaused) {
_maybeCreateOrAdopt(proposalId);
}
_refreshEval(proposalId); // always refresh eval/close
if (!resolutionPaused) {
_maybeResolve(proposalId, true);
}
}
/// @notice Fast path: attempt to early-close & resolve now if possible.
function poke(uint256 proposalId) public payable nonReentrant {
require(!resolutionPaused, "RESOLUTION_PAUSED");
_refreshEval(proposalId);
bool ok = _maybeResolve(proposalId, true);
require(ok, "NOTHING_TO_DO");
}
error NotReady();
function emergencyResolve(uint256 proposalId) public payable onlyOwner nonReentrant {
require(!resolutionPaused, "RESOLUTION_PAUSED");
MarketInfo storage m = marketOf[proposalId];
require(m.created && !m.settled, "NO_MARKET_OR_DONE");
require(block.number >= uint256(m.evalBlock) + emergencyDelayBlocks, "TOO_SOON");
INounsGovernor.ProposalState s = GOV.state(proposalId);
bool yesTerminal = _isYesTerminalState(s);
bool noTerminal = _isNoTerminalState(s);
require(yesTerminal || noTerminal, "NON_TERMINAL");
// Start with the DAO-indicated terminal side.
bool outcome = yesTerminal; // true = YES, false = NO
// If chosen side has no winners, only skip if BOTH sides have no winners.
if (!_hasWinners(m.marketId, outcome)) {
if (!_hasWinners(m.marketId, !outcome)) {
_deadSkip(m, proposalId);
return;
}
// Auto-flip to the side that does have circulating winners.
outcome = !outcome;
}
if (!_closeIfNeeded(m)) revert NotReady();
PAMM.resolve(m.marketId, outcome);
m.settled = true;
emit EmergencyResolved(proposalId, m.marketId, outcome);
}
/* ───────────────────────── core logic ──────────────────────────────── */
function _maybeCreateOrAdopt(uint256 proposalId) internal {
MarketInfo storage m = marketOf[proposalId];
if (m.created) return;
INounsGovernor.ProposalCondensedV3 memory p = GOV.proposalsV3(proposalId);
uint256 objectionDur = GOV.objectionPeriodDurationInBlocks();
// current eval (with objection if set):
uint256 evalBlk =
p.objectionPeriodEndBlock != 0 ? p.objectionPeriodEndBlock : p.endBlock + objectionDur;
uint72 closeTsEstimate = _estimateClose(uint64(evalBlk));
// two candidate descriptions: current + “no-objection” fallback:
string memory descNow =
_descPassVoting(proposalId, p.endBlock, p.objectionPeriodEndBlock, evalBlk);
string memory descNoObj =
_descPassVoting(proposalId, p.endBlock, 0, p.endBlock + objectionDur);
// --- try adopt (current) ---
uint256 midNow = uint256(keccak256(abi.encodePacked("PMARKET:YES", descNow, address(this))));
(,, address resNow,,,,,, uint72 closeNow, bool canCloseNow,,,,) = PAMM.getMarket(midNow);
bool adoptNow = (resNow != address(0));
if (adoptNow) {
// Require either early-closable OR already time-closed:
if (!canCloseNow) {
adoptNow = (closeNow != 0 && block.timestamp >= closeNow);
}
// Refuse adoption if close is griefy-far in the future vs our estimate:
if (adoptNow && closeNow != 0 && closeNow > closeTsEstimate) {
if (uint256(closeNow) - uint256(closeTsEstimate) > adoptMaxCloseDrift) {
adoptNow = false;
}
}
}
bool refusedCurrentAdoption = (resNow != address(0) && !adoptNow);
if (adoptNow) {
m.marketId = midNow;
m.evalBlock = uint64(evalBlk);
m.closeAt = (closeNow != 0) ? closeNow : closeTsEstimate;
m.created = true;
emit MarketAdopted(proposalId, midNow, uint64(evalBlk), m.closeAt);
_payTip(msg.sender);
return;
}
// --- try adopt (no-objection) ---
uint256 midNoObj =
uint256(keccak256(abi.encodePacked("PMARKET:YES", descNoObj, address(this))));
(,, address resNoObj,,,,,, uint72 closeNoObj, bool canCloseNoObj,,,,) =
PAMM.getMarket(midNoObj);
bool adoptNoObj = (resNoObj != address(0));
if (adoptNoObj) {
// Require either early-closable or already time-closed:
if (!canCloseNoObj) {
adoptNoObj = (closeNoObj != 0 && block.timestamp >= closeNoObj);
}
// Refuse adoption if close is griefy-far in the future vs our estimate:
if (adoptNoObj && closeNoObj != 0 && closeNoObj > closeTsEstimate) {
if (uint256(closeNoObj) - uint256(closeTsEstimate) > adoptMaxCloseDrift) {
adoptNoObj = false;
}
}
}
bool refusedNoObjAdoption = (resNoObj != address(0) && !adoptNoObj);
if (adoptNoObj) {
m.marketId = midNoObj;
m.evalBlock = uint64(evalBlk);
m.closeAt = (closeNoObj != 0) ? closeNoObj : closeTsEstimate;
m.created = true;
emit MarketAdopted(proposalId, midNoObj, uint64(evalBlk), m.closeAt);
_payTip(msg.sender);
return;
}
// --- otherwise create using (possibly suffixed) desc to avoid ID collision if we refused adoption ---
string memory descCreate = descNow;
if (refusedCurrentAdoption || refusedNoObjAdoption) {
descCreate = string(abi.encodePacked(descNow, " ;resolver=closable-v1"));
}
(uint256 newMarketId,) = PAMM.createMarketWithPMTuning(
descCreate, address(this), closeTsEstimate, true, seedYes, seedNo, pmDefaults
);
m.marketId = newMarketId;
m.evalBlock = uint64(evalBlk);
m.closeAt = closeTsEstimate;
m.created = true;
emit MarketCreated(proposalId, newMarketId, uint64(evalBlk), closeTsEstimate);
_payTip(msg.sender);
}
/// Keep evalBlock/current closeAt fresh if objection is set after creation.
function _refreshEval(uint256 proposalId) internal {
MarketInfo storage m = marketOf[proposalId];
if (!m.created || m.settled) return;
INounsGovernor.ProposalCondensedV3 memory p = GOV.proposalsV3(proposalId);
if (p.objectionPeriodEndBlock != 0 && uint64(p.objectionPeriodEndBlock) != m.evalBlock) {
m.evalBlock = uint64(p.objectionPeriodEndBlock);
m.closeAt = _estimateClose(m.evalBlock);
emit EvalUpdated(proposalId, m.evalBlock, m.closeAt);
}
}
/// Try to resolve; returns true if it resolved or skipped (dead-market).
function _maybeResolve(uint256 proposalId, bool payTip) internal returns (bool) {
MarketInfo storage m = marketOf[proposalId];
if (!m.created || m.settled) return false;
INounsGovernor.ProposalState s = GOV.state(proposalId);
INounsGovernor.ProposalCondensedV3 memory p = GOV.proposalsV3(proposalId);
// --- CANCELED: resolve by price at/after eval (not automatic NO) ---
if (s == INounsGovernor.ProposalState.Canceled) {
if (block.number < m.evalBlock) return false;
bool ready = _closeIfNeeded(m);
if (!ready) return false; // wait until time-based close
// decide by current implied price:
(uint256 pYesBps, bool ok) = _impliedYesBps(m.marketId);
bool yesWins;
if (!ok) {
(yesWins, ok) = _circulatingSideIsYes(m.marketId);
if (!ok) yesWins = true; // tie defaults to YES
} else if (pYesBps == 5000) {
(bool cYes, bool decided) = _circulatingSideIsYes(m.marketId);
yesWins = decided ? cYes : true;
} else {
yesWins = (pYesBps > 5000);
}
if (!_hasWinners(m.marketId, yesWins)) {
bool opp = _hasWinners(m.marketId, !yesWins);
if (!opp) {
_deadSkip(m, proposalId);
return false;
}
yesWins = !yesWins;
}
PAMM.resolve(m.marketId, yesWins);
m.settled = true;
emit MarketResolved(proposalId, m.marketId, yesWins);
if (payTip) _payTip(msg.sender);
return true;
}
// Early NO for veto at any time:
if (_isNoImmediatelyResolvable(s)) {
bool outcome = false; // veto → NO
if (!_hasWinners(m.marketId, outcome)) {
if (!_hasWinners(m.marketId, !outcome)) {
_deadSkip(m, proposalId);
return false;
}
outcome = !outcome; // let PAMM flip to the winning side
}
if (!_closeIfNeeded(m)) return false;
PAMM.resolve(m.marketId, outcome);
m.settled = true;
emit MarketResolved(proposalId, m.marketId, outcome);
if (payTip) _payTip(msg.sender);
return true;
}
// After end with no objection set: quick terminal check:
if (block.number >= p.endBlock && p.objectionPeriodEndBlock == 0) {
if (_isYesTerminalState(s)) {
bool outcome = true;
if (!_hasWinners(m.marketId, outcome)) {
if (!_hasWinners(m.marketId, !outcome)) {
_deadSkip(m, proposalId);
return false;
}
outcome = !outcome;
}
if (!_closeIfNeeded(m)) return false;
PAMM.resolve(m.marketId, outcome);
m.settled = true;
emit MarketResolved(proposalId, m.marketId, outcome);
if (payTip) _payTip(msg.sender);
return true;
} else if (s == INounsGovernor.ProposalState.Defeated) {
bool outcome = false;
if (!_hasWinners(m.marketId, outcome)) {
if (!_hasWinners(m.marketId, !outcome)) {
_deadSkip(m, proposalId);
return false;
}
outcome = !outcome;
}
if (!_closeIfNeeded(m)) return false;
PAMM.resolve(m.marketId, outcome);
m.settled = true;
emit MarketResolved(proposalId, m.marketId, outcome);
if (payTip) _payTip(msg.sender);
return true;
}
}
// Gate by evalBlock (covers objection window or padded end):
if (block.number >= m.evalBlock) {
bool yesTerm = _isYesTerminalState(s);
bool noTerm = _isNoTerminalState(s);
if (yesTerm || noTerm) {
bool outcome = yesTerm; // true = YES, false = NO
if (!_hasWinners(m.marketId, outcome)) {
if (!_hasWinners(m.marketId, !outcome)) {
_deadSkip(m, proposalId);
return false;
}
outcome = !outcome;
}
if (!_closeIfNeeded(m)) return false;
PAMM.resolve(m.marketId, outcome);
m.settled = true;
emit MarketResolved(proposalId, m.marketId, outcome);
if (payTip) _payTip(msg.sender);
return true;
}
}
return false;
}
function _readyToResolveView(uint256 marketId, uint72 estCloseAt)
internal
view
returns (bool)
{
(,,,,,,,, uint72 closeTs, bool canClose,,,,) = PAMM.getMarket(marketId);
uint72 ref = closeTs != 0 ? closeTs : estCloseAt;
return (block.timestamp >= ref) || canClose;
}
function _closeIfNeeded(MarketInfo storage m) internal returns (bool ready) {
// Sync on-chain close params:
(,,,,,,,, uint72 closeTs, bool canClose,,,,) = PAMM.getMarket(m.marketId);
if (closeTs != 0) m.closeAt = closeTs;
// Already closed by time:
if (block.timestamp >= m.closeAt) return true;
// If not closable early, we are not ready yet:
if (!canClose) return false;
// Try to early-close; if it fails, treat as not-ready:
try PAMM.closeMarket(m.marketId) {
return true;
} catch {
return false;
}
}
function _estimateClose(uint64 evalBlock) internal view returns (uint72) {
uint256 bn = block.number;
if (evalBlock <= bn) return uint72(block.timestamp + 1); // next second
unchecked {
uint256 delta = evalBlock - bn;
uint256 est = block.timestamp + delta * secsPerBlock;
return uint72(est > type(uint72).max ? type(uint72).max : est);
}
}
/* ───────────────────────── helpers / views ─────────────────────────── */
function canResolveNow(uint256 proposalId)
public
view
returns (bool ready, bool deadMarket, bool yesWins)
{
MarketInfo storage m = marketOf[proposalId];
if (!m.created || m.settled) return (false, false, false);
INounsGovernor.ProposalState s = GOV.state(proposalId);
INounsGovernor.ProposalCondensedV3 memory p = GOV.proposalsV3(proposalId);
if (_isNoImmediatelyResolvable(s)) {
if (!_readyToResolveView(m.marketId, m.closeAt)) return (false, false, false);
bool dead = !_hasWinnersView(m.marketId, false) && !_hasWinnersView(m.marketId, true);
return (true, dead, false);
}
if (s == INounsGovernor.ProposalState.Canceled) {
if (block.number < m.evalBlock) return (false, false, false);
(,,,,,,,, uint72 closeTs, bool canClose,,,,) = PAMM.getMarket(m.marketId);
uint72 ref = closeTs != 0 ? closeTs : m.closeAt;
bool readyNow = (block.timestamp >= ref) || canClose; // can close immediately or already closed by time
if (!readyNow) return (false, false, false);
(uint256 bps, bool ok) = _impliedYesBps(m.marketId);
bool y;
if (!ok) {
(y, ok) = _circulatingSideIsYes(m.marketId);
if (!ok) y = true;
} else if (bps == 5000) {
(bool c, bool d) = _circulatingSideIsYes(m.marketId);
y = d ? c : true;
} else {
y = (bps > 5000);
}
bool dead = !_hasWinnersView(m.marketId, y) && !_hasWinnersView(m.marketId, !y);
return (true, dead, y);
}
bool afterEndNoObj = (block.number >= p.endBlock && p.objectionPeriodEndBlock == 0);
bool afterEval = (block.number >= m.evalBlock);
if (afterEndNoObj || afterEval) {
bool yes = _isYesTerminalState(s);
bool no = _isNoTerminalState(s);
if (yes || no) {
if (!_readyToResolveView(m.marketId, m.closeAt)) return (false, false, false);
bool dead = !_hasWinnersView(m.marketId, yes) && !_hasWinnersView(m.marketId, !yes);
return (true, dead, yes);
}
}
return (false, false, false);
}
function marketIdFor(uint256 proposalId) public view returns (uint256 id, bool created) {
MarketInfo storage m = marketOf[proposalId];
return (m.marketId, m.created);
}
function _isYesTerminalState(INounsGovernor.ProposalState s) internal pure returns (bool) {
return (
s == INounsGovernor.ProposalState.Succeeded || s == INounsGovernor.ProposalState.Queued
|| s == INounsGovernor.ProposalState.Executed
|| s == INounsGovernor.ProposalState.Expired
);
}
function _isNoTerminalState(INounsGovernor.ProposalState s) internal pure returns (bool) {
return
(s == INounsGovernor.ProposalState.Defeated || s == INounsGovernor.ProposalState.Vetoed);
}
function _isNoImmediatelyResolvable(INounsGovernor.ProposalState s)
internal
pure
returns (bool)
{
return s == INounsGovernor.ProposalState.Vetoed;
}
function _impliedYesBps(uint256 marketId) internal view returns (uint256 bps, bool ok) {
(,,,,,,,,,,,, uint256 pYes_num, uint256 pYes_den) = PAMM.getMarket(marketId);
if (pYes_den == 0) return (0, false);
return ((pYes_num * 10_000) / pYes_den, true);
}
function _circulatingSideIsYes(uint256 marketId)
internal
view
returns (bool yesWins, bool decided)
{
(uint256 yesSupply, uint256 noSupply,,,,,,,,,,,,) = PAMM.getMarket(marketId);
uint256 nid = _noId(marketId);
uint256 yesCirc =
yesSupply - PAMM.balanceOf(address(PAMM), marketId) - PAMM.balanceOf(ZAMM, marketId);
uint256 noCirc = noSupply - PAMM.balanceOf(address(PAMM), nid) - PAMM.balanceOf(ZAMM, nid);
if (yesCirc == noCirc) return (false, false);
return (yesCirc > noCirc, true);
}
function _hasWinners(uint256 marketId, bool yesWins) internal view returns (bool) {
(uint256 yesSupply, uint256 noSupply,,,,,,,,,,,,) = PAMM.getMarket(marketId);
uint256 yesCirc = yesSupply - PAMM.balanceOf(address(PAMM), marketId) // exclude PM
- PAMM.balanceOf(ZAMM, marketId); // exclude ZAMM
uint256 nid = _noId(marketId);
uint256 noCirc = noSupply - PAMM.balanceOf(address(PAMM), nid) - PAMM.balanceOf(ZAMM, nid);
return yesWins ? (yesCirc != 0) : (noCirc != 0);
}
function _hasWinnersView(uint256 marketId, bool yesWins) internal view returns (bool) {
return _hasWinners(marketId, yesWins);
}
function _noId(uint256 marketId) internal pure returns (uint256) {
// Match PAMM.getNoId:
return uint256(keccak256(abi.encodePacked("PMARKET:NO", marketId)));
}
function _descPassVoting(
uint256 pid,
uint256 endBlock,
uint256 objectionEndBlock,
uint256 evalBlock
) internal pure returns (string memory) {
return string(
abi.encodePacked(
"Nouns #",
_u2s(pid),
" - Pass Voting? YES if Succeeded/Queued/Executed/Expired at/after voting end (or objection end). ",
"If Canceled: settle to side with >=50% implied YES probability when the market is closed; ",
"50/50 ties break by circulating supply, else YES. ",
"endBlock=",
_u2s(endBlock),
", objectionEndBlock=",
_u2s(objectionEndBlock),
", evalBlock=",
_u2s(evalBlock)
)
);
}
function _payTip(address to) internal {
uint256 tip = tipPerAction;
if (tip == 0) return;
uint256 bal = address(this).balance;
if (bal == 0) return;
if (tip > bal) tip = bal;
(bool ok,) = to.call{value: tip}("");
emit TipPaid(to, ok ? tip : 0);
}
function _u2s(uint256 x) internal pure returns (string memory s) {
if (x == 0) return "0";
uint256 j = x;
uint256 len;
while (j != 0) {
len++;
j /= 10;
}
bytes memory b = new bytes(len);
uint256 k = len;
while (x != 0) {
k--;
b[k] = bytes1(uint8(48 + x % 10));
x /= 10;
}
s = string(b);
}
}
"
}
},
"settings": {
"remappings": [
"@solady/=lib/solady/",
"@soledge/=lib/soledge/",
"@forge/=lib/forge-std/src/",
"forge-std/=lib/forge-std/src/",
"solady/=lib/solady/src/"
],
"optimizer": {
"enabled": true,
"runs": 9999999
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "ipfs",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "prague",
"viaIR": true
}
}}
Submitted on: 2025-10-14 09:17:58
Comments
Log in to comment.
No comments yet.