Skip to content

Commit

Permalink
Applying modifications to the L2 logic on creation
Browse files Browse the repository at this point in the history
  • Loading branch information
brickpop committed May 15, 2024
1 parent 3ae989e commit 42718d8
Show file tree
Hide file tree
Showing 4 changed files with 158 additions and 55 deletions.
88 changes: 54 additions & 34 deletions src/OptimisticTokenVotingPlugin.sol
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@ import {ProposalUpgradeable} from "@aragon/osx/core/plugin/proposal/ProposalUpgr
import {PluginUUPSUpgradeable} from "@aragon/osx/core/plugin/PluginUUPSUpgradeable.sol";
import {RATIO_BASE, _applyRatioCeiled, RatioOutOfBounds} from "@aragon/osx/plugins/utils/Ratio.sol";
import {IDAO} from "@aragon/osx/core/dao/IDAO.sol";
import {ITaikoEssentialContract} from "./interfaces/ITaikoEssentialContract.sol";
import {TaikoL1, TaikoData} from "./adapted-dependencies/TaikoL1.sol";
// import {EssentialContract as TaikoEssentialContract} from "@taikoxyz/taiko-mono/common/EssentialContract.sol";

uint64 constant L2_AGGREGATION_PERIOD = 3 days;
uint64 constant L2_INACTIVITY_PERIOD = 10 minutes;

/// @title OptimisticTokenVotingPlugin
/// @author Aragon Association - 2023-2024
Expand Down Expand Up @@ -63,15 +64,15 @@ contract OptimisticTokenVotingPlugin is
/// @notice A container for the proposal parameters at the time of proposal creation.
/// @param startDate The start date of the proposal vote.
/// @param endDate The end date of the proposal vote.
/// @param snapshotTimestamp The number of the block prior to the proposal creation.
/// @param snapshotTimestamp The timestamp prior to the proposal creation.
/// @param minVetoRatio The minimum veto ratio needed to defeat the proposal, as a fraction of 1_000_000.
/// @param taikoL1PausedOnCreation Whether the Taiko L1 was paused when the proposal was created.
/// @param unavailableL2WhenCreated Whether the L2 was unavailable when the proposal was created.
struct ProposalParameters {
uint64 startDate;
uint64 endDate;
uint64 snapshotTimestamp;
uint32 minVetoRatio;
bool taikoL1PausedOnCreation;
bool unavailableL2WhenCreated;
}

/// @notice The ID of the permission required to create a proposal.
Expand All @@ -90,8 +91,8 @@ contract OptimisticTokenVotingPlugin is
/// @notice The struct storing the governance settings.
OptimisticGovernanceSettings public governanceSettings;

/// @notice Taiko L1 contract to check for paused() status when a proposal is created.
ITaikoEssentialContract public taikoL1;
/// @notice Taiko L1 contract to check the status from.
TaikoL1 public taikoL1;

/// @notice A mapping between proposal IDs and proposal information.
mapping(uint256 => Proposal) internal proposals;
Expand Down Expand Up @@ -146,7 +147,7 @@ contract OptimisticTokenVotingPlugin is
IDAO _dao,
OptimisticGovernanceSettings calldata _governanceSettings,
IVotesUpgradeable _token,
ITaikoEssentialContract _taikoL1,
TaikoL1 _taikoL1,
address _taikoBridge
) external initializer {
__PluginUUPSUpgradeable_init(_dao);
Expand All @@ -173,13 +174,37 @@ contract OptimisticTokenVotingPlugin is
}

/// @inheritdoc IOptimisticTokenVoting
function getVotingToken() public view returns (IVotesUpgradeable) {
return votingToken;
function totalVotingPower(uint256 _timestamp) public view returns (uint256) {
return votingToken.getPastTotalSupply(_timestamp);
}

/// @inheritdoc IOptimisticTokenVoting
function totalVotingPower(uint256 _blockNumber) public view returns (uint256) {
return votingToken.getPastTotalSupply(_blockNumber);
function bridgedVotingPower(uint256 _timestamp) public view returns (uint256) {
return votingToken.getPastVotes(taikoBridge, _timestamp);
}

/// @inheritdoc IOptimisticTokenVoting
function effectiveVotingPower(uint256 _timestamp, bool _isL2Available) public view returns (uint256) {
uint256 _totalVotingPower = totalVotingPower(_timestamp);
if (!_isL2Available) {
return _totalVotingPower - bridgedVotingPower(_timestamp);
}
return _totalVotingPower;
}

/// @notice Determines whether the L2 is currently available
function isL2Available() public view returns (bool) {
if (taikoL1.paused()) return false;

uint64 _id = taikoL1.slotB().numBlocks;
// No L2 blocks yet
if (_id == 0) return false;

// The last L2 block is too old
TaikoData.Block memory _block = taikoL1.getBlock(_id - 1);
if (_block.proposedAt < (block.timestamp - L2_INACTIVITY_PERIOD)) return false;

return true;
}

/// @inheritdoc IMembership
Expand Down Expand Up @@ -228,7 +253,7 @@ contract OptimisticTokenVotingPlugin is
return false;
}
// Check if L2 bridged vetoes are still possible
else if (_canReceiveL2Vetoes(proposal_)) {
else if (_proposalL2VetoingOpen(proposal_)) {
return false;
}
// Check that not enough voters have vetoed the proposal
Expand All @@ -242,12 +267,9 @@ contract OptimisticTokenVotingPlugin is
/// @inheritdoc IOptimisticTokenVoting
function isMinVetoRatioReached(uint256 _proposalId) public view virtual returns (bool) {
Proposal storage proposal_ = proposals[_proposalId];
uint256 totalVotingPower_ = totalVotingPower(proposal_.parameters.snapshotTimestamp);
if (_isOnlyL1(proposal_)) {
// L2 tokens won't be fully accountable. Subtracting the bridged supply to compute relative to L1 only.
totalVotingPower_ -= IERC20Upgradeable(address(votingToken)).balanceOf(taikoBridge);
}

uint256 totalVotingPower_ =
effectiveVotingPower(proposal_.parameters.snapshotTimestamp, _proposalCanUseL2VotingPower(proposal_));
uint256 _minVetoPower = _applyRatioCeiled(totalVotingPower_, proposal_.parameters.minVetoRatio);
return proposal_.vetoTally >= _minVetoPower;
}
Expand Down Expand Up @@ -306,14 +328,8 @@ contract OptimisticTokenVotingPlugin is
snapshotTimestamp = block.timestamp - 1; // The snapshot timestamp must in the past to protect the transaction against backrunning transactions causing census changes.
}

uint256 totalVotingPower_ = totalVotingPower(snapshotTimestamp);
bool isTaikoL1Paused = taikoL1.paused();

if (isTaikoL1Paused) {
totalVotingPower_ -= IERC20Upgradeable(address(votingToken)).balanceOf(taikoBridge);
}

if (totalVotingPower_ == 0) {
bool _isL2Available = isL2Available();
if (effectiveVotingPower(snapshotTimestamp, _isL2Available) == 0) {
revert NoVotingPower();
}

Expand All @@ -335,7 +351,7 @@ contract OptimisticTokenVotingPlugin is
proposal_.parameters.endDate = _endDate;
proposal_.parameters.snapshotTimestamp = snapshotTimestamp.toUint64();
proposal_.parameters.minVetoRatio = minVetoRatio();
proposal_.parameters.taikoL1PausedOnCreation = isTaikoL1Paused;
proposal_.parameters.unavailableL2WhenCreated = !_isL2Available;

// Save gas
if (_allowFailureMap != 0) {
Expand Down Expand Up @@ -491,23 +507,27 @@ contract OptimisticTokenVotingPlugin is
return currentTime >= proposal_.parameters.endDate;
}

/// @notice Determines whether the proposal is considered L1 only or not
/// @notice Determines whether the proposal has L2 voting enabled or not.
/// @param proposal_ The proposal
function _isOnlyL1(Proposal storage proposal_) internal view returns (bool) {
if (proposal_.parameters.taikoL1PausedOnCreation) {
function _proposalCanUseL2VotingPower(Proposal storage proposal_) internal view returns (bool) {
if (_proposalL2VetoingOpen(proposal_)) {
return true;
} else if (!_canReceiveL2Vetoes(proposal_)) {
return proposal_.aggregatedL2Balance == 0;
}
return false;

// No more L2 vetoes can be registered
// return false if no L2 votes have been aggregated until now
return proposal_.aggregatedL2Balance > 0;
}

/// @notice Internal function to check if a proposal may still receive L2 vetoes.
/// @param proposal_ The proposal struct.
/// @return True if the proposal may still receive L2 bridged votes, false otherwise.
function _canReceiveL2Vetoes(Proposal storage proposal_) internal view virtual returns (bool) {
uint64 currentTime = block.timestamp.toUint64();
function _proposalL2VetoingOpen(Proposal storage proposal_) internal view virtual returns (bool) {
if (proposal_.parameters.unavailableL2WhenCreated) {
return false;
}

uint64 currentTime = block.timestamp.toUint64();
return currentTime >= proposal_.parameters.startDate
&& currentTime < proposal_.parameters.endDate + L2_AGGREGATION_PERIOD;
}
Expand Down
85 changes: 85 additions & 0 deletions src/adapted-dependencies/TaikoL1.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

/// @dev Manual copy of lib/taiko-mono/packages/protocol/contracts/L1/TaikoL1.sol and its dependent structs
/// This is in order to overcome the conflicting compiler versions between 0.8.17 (OSx) and 0.8.24 (Taiko)
abstract contract TaikoL1 {
TaikoData.State public state;

/// @notice Returns true if the contract is paused, and false otherwise.
/// @return true if paused, false otherwise.
function paused() external view virtual returns (bool);

/// @notice Gets SlotB
/// @return State variables stored at SlotB.
function slotB() public view virtual returns (TaikoData.SlotB memory);

function getBlock(uint64 _blockId) public view virtual returns (TaikoData.Block memory);
}

library TaikoData {
/// @dev Struct containing data required for verifying a block.
/// 3 slots used.
struct Block {
bytes32 metaHash; // slot 1
address assignedProver; // slot 2
uint96 livenessBond;
uint64 blockId; // slot 3
uint64 proposedAt; // timestamp
uint64 proposedIn; // L1 block number, required/used by node/client.
uint32 nextTransitionId;
uint32 verifiedTransitionId;
}

/// @dev Struct representing state transition data.
/// 10 slots reserved for upgradability, 6 slots used.
struct TransitionState {
bytes32 key; // slot 1, only written/read for the 1st state transition.
bytes32 blockHash; // slot 2
bytes32 stateRoot; // slot 3
address prover; // slot 4
uint96 validityBond;
address contester; // slot 5
uint96 contestBond;
uint64 timestamp; // slot 6 (90 bits)
uint16 tier;
uint8 __reserved1;
}

/// @dev Forge is only able to run coverage in case the contracts by default
/// capable of compiling without any optimization (neither optimizer runs,
/// no compiling --via-ir flag).
/// In order to resolve stack too deep without optimizations, we needed to
/// introduce outsourcing vars into structs below.
struct SlotA {
uint64 genesisHeight;
uint64 genesisTimestamp;
uint64 lastSyncedBlockId;
uint64 lastSynecdAt; // typo!
}

struct SlotB {
uint64 numBlocks;
uint64 lastVerifiedBlockId;
bool provingPaused;
uint8 __reservedB1;
uint16 __reservedB2;
uint32 __reservedB3;
uint64 lastUnpausedAt;
}

/// @dev Struct holding the state variables for the {TaikoL1} contract.
struct State {
// Ring buffer for proposed blocks and a some recent verified blocks.
mapping(uint64 => Block) blocks;
// Indexing to transition ids (ring buffer not possible)
mapping(uint64 => mapping(bytes32 => uint32)) transitionIds;
// Ring buffer for transitions
mapping(uint64 => mapping(uint32 => TransitionState)) transitions;
// Ring buffer for Ether deposits
bytes32 __reserve1;
SlotA slotA; // slot 5
SlotB slotB; // slot 6
uint256[44] __gap;
}
}
22 changes: 19 additions & 3 deletions src/interfaces/IOptimisticTokenVoting.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,28 @@ interface IOptimisticTokenVoting {
/// @notice getter function for the voting token.
/// @dev public function also useful for registering interfaceId and for distinguishing from majority voting interface.
/// @return The token used for voting.
function getVotingToken() external view returns (IVotesUpgradeable);
function votingToken() external view returns (IVotesUpgradeable);

/// @notice Returns the total voting power checkpointed for a specific block number.
/// @param _blockNumber The block number.
/// @param _timestamp The target timestamp.
/// @return The total voting power.
function totalVotingPower(uint256 _blockNumber) external view returns (uint256);
function totalVotingPower(uint256 _timestamp) external view returns (uint256);

/// @notice Returns the total voting power bridged to the L2 at the given timestamp.
/// @dev This assumes that the Taiko Bridge is delegating to itself.
/// @param _timestamp The target timestamp.
/// @return The total bridged voting power.
function bridgedVotingPower(uint256 _timestamp) external view returns (uint256);

/// @notice Returns the effective voting power that can vote at the given timestamp.
/// @dev This assumes that the Taiko Bridge is delegating to itself.
/// @param _timestamp The target timestamp.
/// @param _isL2Available Whether the L2 can be part of the voting power or not.
/// @return The total bridged voting power.
function effectiveVotingPower(uint256 _timestamp, bool _isL2Available) external view returns (uint256);

/// @notice Returns true is the L2 is currently available.
function isL2Available() external view returns (bool);

/// @notice Returns the veto ratio parameter stored in the optimistic governance settings.
/// @return The veto ratio parameter.
Expand Down
18 changes: 0 additions & 18 deletions src/interfaces/ITaikoEssentialContract.sol

This file was deleted.

0 comments on commit 42718d8

Please sign in to comment.