Description:
Multi-signature wallet contract requiring multiple confirmations for transaction execution.
Blockchain: Ethereum
Source Code: View Code On The Blockchain
Solidity Source Code:
{{
"language": "Solidity",
"sources": {
"src/modules/DEPOS/OlympusDepositPositionManager.sol": {
"content": "// SPDX-License-Identifier: AGPL-3.0-only
/// forge-lint: disable-start(mixed-case-function, mixed-case-variable)
pragma solidity >=0.8.15;
// Interfaces
import {IERC165} from "@openzeppelin-5.3.0/utils/introspection/IERC165.sol";
// Libraries
import {ERC721} from "@solmate-6.2.0/tokens/ERC721.sol";
import {EnumerableSet} from "@openzeppelin-5.3.0/utils/structs/EnumerableSet.sol";
import {FullMath} from "src/libraries/FullMath.sol";
// Bophades
import {DEPOSv1} from "src/modules/DEPOS/DEPOS.v1.sol";
import {Kernel, Module, Keycode, toKeycode} from "src/Kernel.sol";
import {IDepositPositionManager} from "src/modules/DEPOS/IDepositPositionManager.sol";
import {IPositionTokenRenderer} from "src/modules/DEPOS/IPositionTokenRenderer.sol";
/// @title Olympus Deposit Position Manager
/// @notice Implementation of the {DEPOSv1} interface
/// This contract is used to create, manage, and wrap/unwrap deposit positions. Positions are optionally convertible.
contract OlympusDepositPositionManager is DEPOSv1 {
using EnumerableSet for EnumerableSet.UintSet;
using FullMath for uint256;
// ========== STATE VARIABLES ========== //
/// @notice The address of the token renderer contract
/// @dev If set, tokenURI() will delegate to this contract. If not set, tokenURI() returns an empty string.
address internal _tokenRenderer;
uint256 internal constant _OHM_SCALE = 1e9;
// ========== CONSTRUCTOR ========== //
constructor(
address kernel_,
address tokenRenderer_
) Module(Kernel(kernel_)) ERC721("Olympus Deposit Position", "ODP") {
_setTokenRenderer(tokenRenderer_);
}
// ========== MODULE FUNCTIONS ========== //
/// @inheritdoc Module
function KEYCODE() public pure override returns (Keycode) {
return toKeycode("DEPOS");
}
/// @inheritdoc Module
function VERSION() public pure override returns (uint8 major, uint8 minor) {
major = 1;
minor = 0;
}
// ========== WRAPPING ========== //
/// @inheritdoc IDepositPositionManager
/// @dev This function reverts if:
/// - The position ID is invalid
/// - The caller is not the owner of the position
/// - The position is already wrapped
///
/// This is a public function that can be called by any address holding a position
function wrap(
uint256 positionId_
) external virtual override onlyValidPosition(positionId_) onlyPositionOwner(positionId_) {
// Does not need to check for invalid position ID because the modifier already ensures that
Position storage position = _positions[positionId_];
// Validate that the position is not already wrapped
if (position.wrapped) revert DEPOS_AlreadyWrapped(positionId_);
// Mark the position as wrapped
position.wrapped = true;
// Mint the ERC721 token
_safeMint(msg.sender, positionId_);
emit PositionWrapped(positionId_);
}
/// @inheritdoc IDepositPositionManager
/// @dev This function reverts if:
/// - The position ID is invalid
/// - The caller is not the owner of the position
/// - The position is not wrapped
///
/// This is a public function that can be called by any address holding a position
function unwrap(
uint256 positionId_
) external virtual override onlyValidPosition(positionId_) onlyPositionOwner(positionId_) {
// Does not need to check for invalid position ID because the modifier already ensures that
Position storage position = _positions[positionId_];
// Validate that the position is wrapped
if (!position.wrapped) revert DEPOS_NotWrapped(positionId_);
// Mark the position as unwrapped
position.wrapped = false;
// Burn the ERC721 token
_burn(positionId_);
emit PositionUnwrapped(positionId_);
}
// ========== POSITION MANAGEMENT =========== //
function _create(
address operator_,
IDepositPositionManager.MintParams memory params_
) internal returns (uint256 positionId) {
// Create the position record
positionId = _positionCount++;
_positions[positionId] = Position({
operator: operator_,
owner: params_.owner,
asset: params_.asset,
periodMonths: params_.periodMonths,
remainingDeposit: params_.remainingDeposit,
conversionPrice: params_.conversionPrice,
expiry: params_.expiry,
wrapped: params_.wrapPosition,
additionalData: params_.additionalData
});
// Add the position ID to the user's list of positions
_userPositions[params_.owner].add(positionId);
// If specified, wrap the position
if (params_.wrapPosition) _safeMint(params_.owner, positionId);
// Emit the event
emit PositionCreated(
positionId,
params_.owner,
params_.asset,
params_.periodMonths,
params_.remainingDeposit,
params_.conversionPrice,
params_.expiry,
params_.wrapPosition
);
return positionId;
}
/// @inheritdoc IDepositPositionManager
/// @dev This function reverts if:
/// - The caller is not permissioned
/// - The owner is the zero address
/// - The convertible deposit token is the zero address
/// - The remaining deposit is 0
/// - The conversion price is 0
/// - The conversion expiry is in the past
///
/// This is a permissioned function that can only be called by approved policies
function mint(
IDepositPositionManager.MintParams calldata params_
) external virtual override permissioned returns (uint256 positionId) {
// Validate that the owner is not the zero address
if (params_.owner == address(0)) revert DEPOS_InvalidParams("owner");
// Validate that the asset is not the zero address
if (params_.asset == address(0)) revert DEPOS_InvalidParams("asset");
// Validate that the period is greater than 0
if (params_.periodMonths == 0) revert DEPOS_InvalidParams("period");
// Validate that the remaining deposit is greater than 0
if (params_.remainingDeposit == 0) revert DEPOS_InvalidParams("deposit");
// Validate that the conversion price is greater than 0
if (params_.conversionPrice == 0) revert DEPOS_InvalidParams("conversion price");
// Validate that the conversion expiry is in the future
if (params_.expiry <= block.timestamp) revert DEPOS_InvalidParams("conversion expiry");
return
_create(
msg.sender, // Calling policy is the operator
params_
);
}
/// @inheritdoc IDepositPositionManager
/// @dev This function reverts if:
/// - The caller is not permissioned
/// - The position ID is invalid
/// - The caller is not the operator that created the position
///
/// This is a permissioned function that can only be called by approved policies
function setRemainingDeposit(
uint256 positionId_,
uint256 amount_
)
external
virtual
override
permissioned
onlyValidPosition(positionId_)
onlyPositionOperator(positionId_)
{
// Update the remaining deposit of the position
Position storage position = _positions[positionId_];
position.remainingDeposit = amount_;
// Emit the event
emit PositionRemainingDepositUpdated(positionId_, amount_);
}
/// @inheritdoc IDepositPositionManager
/// @dev This function reverts if:
/// - The caller is not permissioned
/// - The position ID is invalid
/// - The caller is not the operator that created the position
///
/// This is a permissioned function that can only be called by approved policies
function setAdditionalData(
uint256 positionId_,
bytes calldata additionalData_
)
external
virtual
override
permissioned
onlyValidPosition(positionId_)
onlyPositionOperator(positionId_)
{
// Update the additional data of the position
Position storage position = _positions[positionId_];
position.additionalData = additionalData_;
// Emit the event
emit PositionAdditionalDataUpdated(positionId_, additionalData_);
}
/// @inheritdoc IDepositPositionManager
/// @dev This function reverts if:
/// - The caller is not permissioned
/// - The caller is not the operator that created the position
/// - The amount is 0
/// - The amount is greater than the remaining deposit
/// - `to_` is the zero address
///
/// This is a permissioned function that can only be called by approved policies
function split(
uint256 positionId_,
uint256 amount_,
address to_,
bool wrap_
)
external
virtual
override
permissioned
onlyValidPosition(positionId_)
onlyPositionOperator(positionId_)
returns (uint256 newPositionId)
{
Position storage position = _positions[positionId_];
// Validate that the amount is greater than 0
if (amount_ == 0) revert DEPOS_InvalidParams("amount");
// Validate that the amount is less than or equal to the remaining deposit
if (amount_ > position.remainingDeposit) revert DEPOS_InvalidParams("amount");
// Validate that the to address is not the zero address
if (to_ == address(0)) revert DEPOS_InvalidParams("to");
// Calculate the remaining deposit of the existing position
uint256 remainingDeposit = position.remainingDeposit - amount_;
// Update the remaining deposit of the existing position
position.remainingDeposit = remainingDeposit;
// Create the new position
newPositionId = _create(
position.operator, // Operator is the same as the existing position
IDepositPositionManager.MintParams({
owner: to_,
asset: position.asset,
periodMonths: position.periodMonths,
remainingDeposit: amount_,
conversionPrice: position.conversionPrice,
expiry: position.expiry,
wrapPosition: wrap_,
additionalData: position.additionalData
})
);
// Emit the event
emit PositionSplit(
positionId_,
newPositionId,
position.asset,
position.periodMonths,
amount_,
to_,
wrap_
);
return newPositionId;
}
// ========== ERC721 OVERRIDES ========== //
/// @inheritdoc ERC721
function tokenURI(uint256 id_) public view virtual override returns (string memory) {
if (_tokenRenderer == address(0)) return "";
return IPositionTokenRenderer(_tokenRenderer).tokenURI(address(this), id_);
}
/// @inheritdoc ERC721
/// @dev This function performs the following:
/// - Updates the owner of the position
/// - Calls `transferFrom` on the parent contract
function transferFrom(address from_, address to_, uint256 tokenId_) public override {
Position storage position = _positions[tokenId_];
// Validate that the position is valid
if (position.conversionPrice == 0) revert DEPOS_InvalidPositionId(tokenId_);
// Validate that the position is wrapped/minted
if (!position.wrapped) revert DEPOS_NotWrapped(tokenId_);
// Additional validation performed in super.transferFrom():
// - Approvals
// - Ownership
// - Destination address
// Remove from user positions on the source address
// This is done first, so that a self-transfer does not result in removal
if (!_userPositions[from_].remove(tokenId_)) revert DEPOS_InvalidPositionId(tokenId_);
// Update the position record
position.owner = to_;
// Add to user positions on the destination address
_userPositions[to_].add(tokenId_);
// Call `transferFrom` on the parent contract
super.transferFrom(from_, to_, tokenId_);
}
// ========== TERM INFORMATION ========== //
/// @inheritdoc IDepositPositionManager
function getPositionCount() external view virtual override returns (uint256) {
return _positionCount;
}
function _getPosition(uint256 positionId_) internal view returns (Position memory) {
Position memory position = _positions[positionId_];
// `mint()` blocks a 0 conversion price, so this should never happen on a valid position
if (position.conversionPrice == 0) revert DEPOS_InvalidPositionId(positionId_);
return position;
}
/// @inheritdoc IDepositPositionManager
function getUserPositionIds(
address user_
) external view virtual override returns (uint256[] memory) {
return _userPositions[user_].values();
}
/// @inheritdoc IDepositPositionManager
/// @dev This function reverts if:
/// - The position ID is invalid
function getPosition(
uint256 positionId_
) external view virtual override returns (Position memory) {
return _getPosition(positionId_);
}
/// @inheritdoc IDepositPositionManager
/// @dev This function reverts if:
/// - The position ID is invalid
///
/// @return isExpired_ Returns true if the conversion expiry timestamp is now or in the past
function isExpired(uint256 positionId_) external view virtual override returns (bool) {
return _getPosition(positionId_).expiry <= block.timestamp;
}
function _isConvertible(Position memory position_) internal pure returns (bool) {
return
position_.conversionPrice != NON_CONVERSION_PRICE &&
position_.expiry != NON_CONVERSION_EXPIRY;
}
/// @inheritdoc IDepositPositionManager
/// @dev This function reverts if:
/// - The position ID is invalid
///
/// @return isConvertible_ Returns true if the conversion price is not the maximum value
function isConvertible(uint256 positionId_) external view virtual override returns (bool) {
return _isConvertible(_getPosition(positionId_));
}
function _previewConvert(
uint256 amount_,
uint256 conversionPrice_
) internal pure returns (uint256) {
// amount_ and conversionPrice_ are in the same decimals and cancel each other out
// The output needs to be in OHM, so we multiply by 1e9
// This also deliberately rounds down
return amount_.mulDiv(_OHM_SCALE, conversionPrice_);
}
/// @inheritdoc IDepositPositionManager
function previewConvert(
uint256 positionId_,
uint256 amount_
) public view virtual override onlyValidPosition(positionId_) returns (uint256) {
Position memory position = _getPosition(positionId_);
// If expired, conversion output is 0
if (position.expiry <= block.timestamp) return 0;
// If the amount is greater than the remaining deposit, revert
if (amount_ > position.remainingDeposit) revert DEPOS_InvalidParams("amount");
// If conversion is not supported, revert
if (!_isConvertible(position)) revert DEPOS_NotConvertible(positionId_);
return _previewConvert(amount_, position.conversionPrice);
}
// ========== TOKEN URI RENDERER ========== //
function _setTokenRenderer(address renderer_) internal {
// If setting to zero address, just clear the renderer
if (renderer_ == address(0)) {
_tokenRenderer = address(0);
emit TokenRendererSet(address(0));
return;
}
// Validate that the renderer contract supports the required interface
if (!IERC165(renderer_).supportsInterface(type(IPositionTokenRenderer).interfaceId)) {
revert DEPOS_InvalidRenderer(renderer_);
}
// Set the renderer
_tokenRenderer = renderer_;
emit TokenRendererSet(renderer_);
}
/// @inheritdoc IDepositPositionManager
/// @dev This function reverts if:
/// - The caller is not permissioned
/// - The renderer contract does not implement the required interface
function setTokenRenderer(address renderer_) external virtual override permissioned {
_setTokenRenderer(renderer_);
}
/// @inheritdoc IDepositPositionManager
function getTokenRenderer() external view virtual override returns (address) {
return _tokenRenderer;
}
// ========== ERC165 SUPPORT ========== //
function supportsInterface(bytes4 interfaceId_) public view virtual override returns (bool) {
return
interfaceId_ == type(IERC165).interfaceId ||
interfaceId_ == type(IDepositPositionManager).interfaceId ||
super.supportsInterface(interfaceId_);
}
// ========== MODIFIERS ========== //
function _onlyValidPosition(uint256 positionId_) internal view {
if (_getPosition(positionId_).conversionPrice == 0)
revert DEPOS_InvalidPositionId(positionId_);
}
modifier onlyValidPosition(uint256 positionId_) {
_onlyValidPosition(positionId_);
_;
}
function _onlyPositionOperator(uint256 positionId_) internal view {
// This validates that the caller is the operator of the position
if (_getPosition(positionId_).operator != msg.sender) revert DEPOS_NotOperator(positionId_);
}
modifier onlyPositionOperator(uint256 positionId_) {
_onlyPositionOperator(positionId_);
_;
}
function _onlyPositionOwner(uint256 positionId_) internal view {
// This validates that the caller is the owner of the position
if (_getPosition(positionId_).owner != msg.sender) revert DEPOS_NotOwner(positionId_);
}
modifier onlyPositionOwner(uint256 positionId_) {
_onlyPositionOwner(positionId_);
_;
}
}
/// forge-lint: disable-end(mixed-case-function, mixed-case-variable)
"
},
"dependencies/openzeppelin-new-5.3.0/contracts/utils/introspection/IERC165.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/IERC165.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC-165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[ERC].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
"
},
"dependencies/solmate-6.2.0/src/tokens/ERC721.sol": {
"content": "// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// @notice Modern, minimalist, and gas efficient ERC-721 implementation.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC721.sol)
abstract contract ERC721 {
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
event Transfer(address indexed from, address indexed to, uint256 indexed id);
event Approval(address indexed owner, address indexed spender, uint256 indexed id);
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
/*//////////////////////////////////////////////////////////////
METADATA STORAGE/LOGIC
//////////////////////////////////////////////////////////////*/
string public name;
string public symbol;
function tokenURI(uint256 id) public view virtual returns (string memory);
/*//////////////////////////////////////////////////////////////
ERC721 BALANCE/OWNER STORAGE
//////////////////////////////////////////////////////////////*/
mapping(uint256 => address) internal _ownerOf;
mapping(address => uint256) internal _balanceOf;
function ownerOf(uint256 id) public view virtual returns (address owner) {
require((owner = _ownerOf[id]) != address(0), "NOT_MINTED");
}
function balanceOf(address owner) public view virtual returns (uint256) {
require(owner != address(0), "ZERO_ADDRESS");
return _balanceOf[owner];
}
/*//////////////////////////////////////////////////////////////
ERC721 APPROVAL STORAGE
//////////////////////////////////////////////////////////////*/
mapping(uint256 => address) public getApproved;
mapping(address => mapping(address => bool)) public isApprovedForAll;
/*//////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/
constructor(string memory _name, string memory _symbol) {
name = _name;
symbol = _symbol;
}
/*//////////////////////////////////////////////////////////////
ERC721 LOGIC
//////////////////////////////////////////////////////////////*/
function approve(address spender, uint256 id) public virtual {
address owner = _ownerOf[id];
require(msg.sender == owner || isApprovedForAll[owner][msg.sender], "NOT_AUTHORIZED");
getApproved[id] = spender;
emit Approval(owner, spender, id);
}
function setApprovalForAll(address operator, bool approved) public virtual {
isApprovedForAll[msg.sender][operator] = approved;
emit ApprovalForAll(msg.sender, operator, approved);
}
function transferFrom(
address from,
address to,
uint256 id
) public virtual {
require(from == _ownerOf[id], "WRONG_FROM");
require(to != address(0), "INVALID_RECIPIENT");
require(
msg.sender == from || isApprovedForAll[from][msg.sender] || msg.sender == getApproved[id],
"NOT_AUTHORIZED"
);
// Underflow of the sender's balance is impossible because we check for
// ownership above and the recipient's balance can't realistically overflow.
unchecked {
_balanceOf[from]--;
_balanceOf[to]++;
}
_ownerOf[id] = to;
delete getApproved[id];
emit Transfer(from, to, id);
}
function safeTransferFrom(
address from,
address to,
uint256 id
) public virtual {
transferFrom(from, to, id);
require(
to.code.length == 0 ||
ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, "") ==
ERC721TokenReceiver.onERC721Received.selector,
"UNSAFE_RECIPIENT"
);
}
function safeTransferFrom(
address from,
address to,
uint256 id,
bytes calldata data
) public virtual {
transferFrom(from, to, id);
require(
to.code.length == 0 ||
ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, data) ==
ERC721TokenReceiver.onERC721Received.selector,
"UNSAFE_RECIPIENT"
);
}
/*//////////////////////////////////////////////////////////////
ERC165 LOGIC
//////////////////////////////////////////////////////////////*/
function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
return
interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165
interfaceId == 0x80ac58cd || // ERC165 Interface ID for ERC721
interfaceId == 0x5b5e139f; // ERC165 Interface ID for ERC721Metadata
}
/*//////////////////////////////////////////////////////////////
INTERNAL MINT/BURN LOGIC
//////////////////////////////////////////////////////////////*/
function _mint(address to, uint256 id) internal virtual {
require(to != address(0), "INVALID_RECIPIENT");
require(_ownerOf[id] == address(0), "ALREADY_MINTED");
// Counter overflow is incredibly unrealistic.
unchecked {
_balanceOf[to]++;
}
_ownerOf[id] = to;
emit Transfer(address(0), to, id);
}
function _burn(uint256 id) internal virtual {
address owner = _ownerOf[id];
require(owner != address(0), "NOT_MINTED");
// Ownership check above ensures no underflow.
unchecked {
_balanceOf[owner]--;
}
delete _ownerOf[id];
delete getApproved[id];
emit Transfer(owner, address(0), id);
}
/*//////////////////////////////////////////////////////////////
INTERNAL SAFE MINT LOGIC
//////////////////////////////////////////////////////////////*/
function _safeMint(address to, uint256 id) internal virtual {
_mint(to, id);
require(
to.code.length == 0 ||
ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, "") ==
ERC721TokenReceiver.onERC721Received.selector,
"UNSAFE_RECIPIENT"
);
}
function _safeMint(
address to,
uint256 id,
bytes memory data
) internal virtual {
_mint(to, id);
require(
to.code.length == 0 ||
ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, data) ==
ERC721TokenReceiver.onERC721Received.selector,
"UNSAFE_RECIPIENT"
);
}
}
/// @notice A generic interface for a contract which properly accepts ERC721 tokens.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC721.sol)
abstract contract ERC721TokenReceiver {
function onERC721Received(
address,
address,
uint256,
bytes calldata
) external virtual returns (bytes4) {
return ERC721TokenReceiver.onERC721Received.selector;
}
}
"
},
"dependencies/openzeppelin-new-5.3.0/contracts/utils/structs/EnumerableSet.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.
pragma solidity ^0.8.20;
import {Arrays} from "../Arrays.sol";
/**
* @dev Library for managing
* https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
* types.
*
* Sets have the following properties:
*
* - Elements are added, removed, and checked for existence in constant time
* (O(1)).
* - Elements are enumerated in O(n). No guarantees are made on the ordering.
* - Set can be cleared (all elements removed) in O(n).
*
* ```solidity
* contract Example {
* // Add the library methods
* using EnumerableSet for EnumerableSet.AddressSet;
*
* // Declare a set state variable
* EnumerableSet.AddressSet private mySet;
* }
* ```
*
* As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
* and `uint256` (`UintSet`) are supported.
*
* [WARNING]
* ====
* Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
* unusable.
* See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
*
* In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
* array of EnumerableSet.
* ====
*/
library EnumerableSet {
// To implement this library for multiple types with as little code
// repetition as possible, we write it in terms of a generic Set type with
// bytes32 values.
// The Set implementation uses private functions, and user-facing
// implementations (such as AddressSet) are just wrappers around the
// underlying Set.
// This means that we can only create new EnumerableSets for types that fit
// in bytes32.
struct Set {
// Storage of set values
bytes32[] _values;
// Position is the index of the value in the `values` array plus 1.
// Position 0 is used to mean a value is not in the set.
mapping(bytes32 value => uint256) _positions;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function _add(Set storage set, bytes32 value) private returns (bool) {
if (!_contains(set, value)) {
set._values.push(value);
// The value is stored at length-1, but we add 1 to all indexes
// and use 0 as a sentinel value
set._positions[value] = set._values.length;
return true;
} else {
return false;
}
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function _remove(Set storage set, bytes32 value) private returns (bool) {
// We cache the value's position to prevent multiple reads from the same storage slot
uint256 position = set._positions[value];
if (position != 0) {
// Equivalent to contains(set, value)
// To delete an element from the _values array in O(1), we swap the element to delete with the last one in
// the array, and then remove the last element (sometimes called as 'swap and pop').
// This modifies the order of the array, as noted in {at}.
uint256 valueIndex = position - 1;
uint256 lastIndex = set._values.length - 1;
if (valueIndex != lastIndex) {
bytes32 lastValue = set._values[lastIndex];
// Move the lastValue to the index where the value to delete is
set._values[valueIndex] = lastValue;
// Update the tracked position of the lastValue (that was just moved)
set._positions[lastValue] = position;
}
// Delete the slot where the moved value was stored
set._values.pop();
// Delete the tracked position for the deleted slot
delete set._positions[value];
return true;
} else {
return false;
}
}
/**
* @dev Removes all the values from a set. O(n).
*
* WARNING: Developers should keep in mind that this function has an unbounded cost and using it may render the
* function uncallable if the set grows to the point where clearing it consumes too much gas to fit in a block.
*/
function _clear(Set storage set) private {
uint256 len = _length(set);
for (uint256 i = 0; i < len; ++i) {
delete set._positions[set._values[i]];
}
Arrays.unsafeSetLength(set._values, 0);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function _contains(Set storage set, bytes32 value) private view returns (bool) {
return set._positions[value] != 0;
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function _length(Set storage set) private view returns (uint256) {
return set._values.length;
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function _at(Set storage set, uint256 index) private view returns (bytes32) {
return set._values[index];
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function _values(Set storage set) private view returns (bytes32[] memory) {
return set._values;
}
// Bytes32Set
struct Bytes32Set {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _add(set._inner, value);
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _remove(set._inner, value);
}
/**
* @dev Removes all the values from a set. O(n).
*
* WARNING: Developers should keep in mind that this function has an unbounded cost and using it may render the
* function uncallable if the set grows to the point where clearing it consumes too much gas to fit in a block.
*/
function clear(Bytes32Set storage set) internal {
_clear(set._inner);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
return _contains(set._inner, value);
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(Bytes32Set storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
return _at(set._inner, index);
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
bytes32[] memory store = _values(set._inner);
bytes32[] memory result;
assembly ("memory-safe") {
result := store
}
return result;
}
// AddressSet
struct AddressSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(AddressSet storage set, address value) internal returns (bool) {
return _add(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(AddressSet storage set, address value) internal returns (bool) {
return _remove(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Removes all the values from a set. O(n).
*
* WARNING: Developers should keep in mind that this function has an unbounded cost and using it may render the
* function uncallable if the set grows to the point where clearing it consumes too much gas to fit in a block.
*/
function clear(AddressSet storage set) internal {
_clear(set._inner);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(AddressSet storage set, address value) internal view returns (bool) {
return _contains(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(AddressSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(AddressSet storage set, uint256 index) internal view returns (address) {
return address(uint160(uint256(_at(set._inner, index))));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(AddressSet storage set) internal view returns (address[] memory) {
bytes32[] memory store = _values(set._inner);
address[] memory result;
assembly ("memory-safe") {
result := store
}
return result;
}
// UintSet
struct UintSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(UintSet storage set, uint256 value) internal returns (bool) {
return _add(set._inner, bytes32(value));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(UintSet storage set, uint256 value) internal returns (bool) {
return _remove(set._inner, bytes32(value));
}
/**
* @dev Removes all the values from a set. O(n).
*
* WARNING: Developers should keep in mind that this function has an unbounded cost and using it may render the
* function uncallable if the set grows to the point where clearing it consumes too much gas to fit in a block.
*/
function clear(UintSet storage set) internal {
_clear(set._inner);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(UintSet storage set, uint256 value) internal view returns (bool) {
return _contains(set._inner, bytes32(value));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(UintSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(UintSet storage set, uint256 index) internal view returns (uint256) {
return uint256(_at(set._inner, index));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(UintSet storage set) internal view returns (uint256[] memory) {
bytes32[] memory store = _values(set._inner);
uint256[] memory result;
assembly ("memory-safe") {
result := store
}
return result;
}
}
"
},
"src/libraries/FullMath.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
/// @title Contains 512-bit math functions
/// @notice Facilitates multiplication and division that can have overflow of an intermediate value without any loss of precision
/// @dev Handles "phantom overflow" i.e., allows multiplication and division where an intermediate value overflows 256 bits
library FullMath {
/// @notice Calculates floor(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
/// @param a The multiplicand
/// @param b The multiplier
/// @param denominator The divisor
/// @return result The 256-bit result
/// @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv
function mulDiv(
uint256 a,
uint256 b,
uint256 denominator
) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = a * b
// Compute the product mod 2**256 and mod 2**256 - 1
// then use the Chinese Remainder Theorem to reconstruct
// the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2**256 + prod0
uint256 prod0; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(a, b, not(0))
prod0 := mul(a, b)
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division
if (prod1 == 0) {
require(denominator > 0);
assembly {
result := div(prod0, denominator)
}
return result;
}
// Make sure the result is less than 2**256.
// Also prevents denominator == 0
require(denominator > prod1);
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0]
// Compute remainder using mulmod
uint256 remainder;
assembly {
remainder := mulmod(a, b, denominator)
}
// Subtract 256 bit number from 512 bit number
assembly {
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator
// Compute largest power of two divisor of denominator.
// Always >= 1.
uint256 twos = (type(uint256).max - denominator + 1) & denominator;
// Divide denominator by power of two
assembly {
denominator := div(denominator, twos)
}
// Divide [prod1 prod0] by the factors of two
assembly {
prod0 := div(prod0, twos)
}
// Shift in bits from prod1 into prod0. For this we need
// to flip `twos` such that it is 2**256 / twos.
// If twos is zero, then it becomes one
assembly {
twos := add(div(sub(0, twos), twos), 1)
}
prod0 |= prod1 * twos;
// Invert denominator mod 2**256
// Now that denominator is an odd number, it has an inverse
// modulo 2**256 such that denominator * inv = 1 mod 2**256.
// Compute the inverse by starting with a seed that is correct
// correct for four bits. That is, denominator * inv = 1 mod 2**4
uint256 inv = (3 * denominator) ^ 2;
// Now use Newton-Raphson iteration to improve the precision.
// Thanks to Hensel's lifting lemma, this also works in modular
// arithmetic, doubling the correct bits in each step.
inv *= 2 - denominator * inv; // inverse mod 2**8
inv *= 2 - denominator * inv; // inverse mod 2**16
inv *= 2 - denominator * inv; // inverse mod 2**32
inv *= 2 - denominator * inv; // inverse mod 2**64
inv *= 2 - denominator * inv; // inverse mod 2**128
inv *= 2 - denominator * inv; // inverse mod 2**256
// Because the division is now exact we can divide by multiplying
// with the modular inverse of denominator. This will give us the
// correct result modulo 2**256. Since the precoditions guarantee
// that the outcome is less than 2**256, this is the final result.
// We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inv;
return result;
}
}
/// @notice Calculates ceil(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
/// @param a The multiplicand
/// @param b The multiplier
/// @param denominator The divisor
/// @return result The 256-bit result
function mulDivUp(
uint256 a,
uint256 b,
uint256 denominator
) internal pure returns (uint256 result) {
result = mulDiv(a, b, denominator);
unchecked {
if (mulmod(a, b, denominator) > 0) {
require(result < type(uint256).max);
result++;
}
}
}
}
"
},
"src/modules/DEPOS/DEPOS.v1.sol": {
"content": "// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity >=0.8.15;
// Interfaces
import {IDepositPositionManager} from "src/modules/DEPOS/IDepositPositionManager.sol";
// Libraries
import {ERC721} from "@solmate-6.2.0/tokens/ERC721.sol";
import {EnumerableSet} from "@openzeppelin-5.3.0/utils/structs/EnumerableSet.sol";
// Bophades
import {Module} from "src/Kernel.sol";
/// @title DEPOSv1
/// @notice This defines the interface for the DEPOS module.
/// The objective of this module is to track the terms of a deposit position.
abstract contract DEPOSv1 is Module, ERC721, IDepositPositionManager {
// ========== CONSTANTS ========== //
/// @notice The value used for the conversion price if conversion is not supported
uint256 public constant NON_CONVERSION_PRICE = type(uint256).max;
/// @notice The value used for the conversion expiry if conversion is not supported
uint48 public constant NON_CONVERSION_EXPIRY = type(uint48).max;
// ========== STATE VARIABLES ========== //
/// @notice The number of positions created
uint256 internal _positionCount;
/// @notice Mapping of position records to an ID
/// @dev IDs are assigned sequentially starting from 0
/// Mapping entries should not be deleted, but can be overwritten
mapping(uint256 => Position) internal _positions;
/// @notice Mapping of user addresses to their position IDs
mapping(address => EnumerableSet.UintSet) internal _userPositions;
}
"
},
"src/Kernel.sol": {
"content": "// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.15;
// ███████ █████ █████ █████ ██████ ██████ ███████████ █████ █████ █████████
// ███░░░░░███ ░░███ ░░███ ░░███ ░░██████ ██████ ░░███░░░░░███░░███ ░░███ ███░░░░░███
// ███ ░░███ ░███ ░░███ ███ ░███░█████░███ ░███ ░███ ░███ ░███ ░███ ░░░
// ░███ ░███ ░███ ░░█████ ░███░░███ ░███ ░██████████ ░███ ░███ ░░█████████
// ░███ ░███ ░███ ░░███ ░███ ░░░ ░███ ░███░░░░░░ ░███ ░███ ░░░░░░░░███
// ░░███ ███ ░███ █ ░███ ░███ ░███ ░███ ░███ ░███ ███ ░███
// ░░░███████░ ███████████ █████ █████ █████ █████ ░░████████ ░░█████████
// ░░░░░░░ ░░░░░░░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░ ░░░░░░░░ ░░░░░░░░░
//============================================================================================//
// GLOBAL TYPES //
//============================================================================================//
/// @notice Actions to trigger state changes in the kernel. Passed by the executor
enum Actions {
InstallModule,
UpgradeModule,
ActivatePolicy,
DeactivatePolicy,
ChangeExecutor,
MigrateKernel
}
/// @notice Used by executor to select an action and a target contract for a kernel action
struct Instruction {
Actions action;
address target;
}
/// @notice Used to define which module functions a policy needs access to
struct Permissions {
Keycode keycode;
bytes4 funcSelector;
}
type Keycode is bytes5;
//============================================================================================//
// UTIL FUNCTIONS //
//============================================================================================//
error TargetNotAContract(address target_);
error InvalidKeycode(Keycode keycode_);
// solhint-disable-next-line func-visibility
function toKeycode(bytes5 keycode_) pure returns (Keycode) {
return Keycode.wrap(keycode_);
}
// solhint-disable-next-line func-visibility
function fromKeycode(Keycode keycode_) pure returns (bytes5) {
return Keycode.unwrap(keycode_);
}
// solhint-disable-next-line func-visibility
function ensureContract(address target_) view {
if (target_.code.length == 0) revert TargetNotAContract(target_);
}
// solhint-disable-next-line func-visibility
function ensureValidKeycode(Keycode keycode_) pure {
bytes5 unwrapped = Keycode.unwrap(keycode_);
for (uint256 i = 0; i < 5; ) {
bytes1 char = unwrapped[i];
if (char < 0x41 || char > 0x5A) revert InvalidKeycode(keycode_); // A-Z only
unchecked {
i++;
}
}
}
//============================================================================================//
// COMPONENTS //
//============================================================================================//
/// @notice Generic adapter interface for kernel access in modules and policies.
abstract contract KernelAdapter {
error KernelAdapter_OnlyKernel(address caller_);
Kernel public kernel;
constructor(Kernel kernel_) {
kernel = kernel_;
}
/// @notice Modifier to restrict functions to be called only by kernel.
modifier onlyKernel() {
if (msg.sender != address(kernel)) revert KernelAdapter_OnlyKernel(msg.sender);
_;
}
/// @notice Function used by kernel when migrating to a new kernel.
function changeKernel(Kernel newKernel_) external onlyKernel {
kernel = newKernel_;
}
}
/// @notice Base level extension of the kernel. Modules act as independent state components to be
/// interacted with and mutated through policies.
/// @dev Modules are installed and uninstalled via the executor.
abstract contract Module is KernelAdapter {
error Module_PolicyNotPermitted(address policy_);
constructor(Kernel kernel_) KernelAdapter(kernel_) {}
/// @notice Modifier to restrict which policies have access to module functions.
modifier permissioned() {
if (
msg.sender == address(kernel) ||
!kernel.modulePermissions(KEYCODE(), Policy(msg.sender), msg.sig)
) revert Module_PolicyNotPermitted(msg.sender);
_;
}
/// @notice 5 byte identifier for a module.
function KEYCODE() public pure virtual returns (Keycode) {}
/// @notice Returns which semantic version of a module is being implemented.
/// @return major - Major version upgrade indicates breaking change to the interface.
/// @return minor - Minor version change retains backward-compatible interface.
function VERSION() external pure virtual returns (uint8 major, uint8 minor) {}
/// @notice Initialization function for the module
/// @dev This function is called when the module is installed or upgraded by the kernel.
/// @dev MUST BE GATED BY onlyKernel. Used to encompass any initialization or upgrade logic.
function INIT() external virtual onlyKernel {}
}
/// @notice Policies are application logic and external interface for the kernel and installed modules.
/// @dev Policies are activated and deactivated in the kernel by the executor.
/// @dev Module dependencies and function permissions must be defined in appropriate functions.
abstract contract Policy is KernelAdapter {
error Policy_ModuleDoesNotExist(Keycode keycode_);
error Policy_WrongModuleVersion(bytes expected_);
constructor(Kernel kernel_) KernelAdapter(kernel_) {}
/// @notice Easily accessible indicator for if a policy is activated or not.
function isActive() external view returns (bool) {
return kernel.isPolicyActive(this);
}
/// @notice Function to grab module address from a given keycode.
function getModuleAddress(Keycode keycode_) internal view returns (address) {
address moduleForKeycode = address(kernel.getModuleForKeycode(keycode_));
if (moduleForKeycode == address(0)) revert Policy_ModuleDoesNotExist(keycode_);
return moduleForKeycode;
}
/// @notice Define module dependencies for this policy.
/// @return dependencies - Keycode array of module dependencies.
function configureDependencies() external virtual returns (Keycode[] memory dependencies) {}
/// @notice Function called by kernel to set module function permissions.
/// @return requests - Array of keycodes and function selectors for requested permissions.
function requestPermissions() external view virtual returns (Permissions[] memory requests) {}
}
/// @notice Main contract that acts as a central component registry for the protocol.
/// @dev The kernel manages modules and policies. The kernel is mutated via predefined Actions,
/// @dev which are input from any address assigned as the executor. The executor can be changed as needed.
contract Kernel {
// ========= EVENTS ========= //
event PermissionsUpdated(
Keycode indexed keycode_,
Policy indexed policy_,
bytes4 funcSelector_,
bool granted_
);
event ActionExecuted(Actions indexed action_, address indexed target_);
// ========= ERRORS ========= //
error Kernel_OnlyExecutor(address caller_);
error Kernel_ModuleAlreadyInstalled(Keycode module_);
error Kernel_InvalidModuleUpgrade(Keycode module_);
error Kernel_PolicyAlreadyActivated(address policy_);
error Kernel_PolicyNotActivated(address policy_);
// ========= PRIVILEGED ADDRESSES ========= //
/// @notice Address that is able to initiate Actions in the kernel. Can be assigned to a multisig or governance contract.
address public executor;
// ========= MODULE MANAGEMENT ========= //
/// @notice Array of all modules currently installed.
Keycode[] public allKeycodes;
/// @notice Mapping of module address to keycode.
mapping(Keycode => Module) public getModuleForKeycode;
/// @notice Mapping of keycode to module address.
mapping(Module => Keycode) public getKeycodeForModule;
/// @notice Mapping of a keycode to all of its policy dependents. Used to efficiently reconfigure policy dependencies.
mapping(Keycode => Policy[]) public moduleDependents;
/// @notice Helper for module dependent arrays. Prevents the need to loop through array.
mapping(Keycode => mapping(Policy => uint256)) public getDependentIndex;
/// @notice Module <> Policy Permissions.
/// @dev Keycode -> Policy -> Function Selector -> bool for permission
mapping(Keycode => mapping(Policy => mapping(bytes4 => bool))) public modulePermissions;
// ========= POLICY MANAGEMENT ========= //
/// @notice List of all active policies
Policy[] public activePolicies;
/// @notice Helper to get active policy quickly. Prevents need to loop through array.
mapping(Policy => uint256) public getPolicyIndex;
//============================================================================================//
// CORE FUNCTIONS //
//============================================================================================//
constructor() {
executor = msg.sender;
}
/// @notice Modifier to check if caller is the executor.
modifier onlyExecutor() {
if (msg.sender != executor) revert Kernel_OnlyExecutor(msg.sender);
_;
}
function isPolicyActive(Policy policy_) public view returns (bool) {
return activePolicies.length > 0 && activePolicies[getPolicyIndex[policy_]] == policy_;
}
/// @notice Main kernel function. Initiates state changes to kernel depending on Action passed in.
function executeAction(Actions action_, address target_) external onlyExecutor {
if (action_ == Actions.InstallModule) {
ensureContract(target_);
ensureValidKeycode(Module(target_).KEYCODE());
_installModule(Module(target_));
} else if (action_ == Actions.UpgradeModule) {
ensureContract(target_);
ensureValidKeycode(Module(target_).KEYCODE());
_upgradeModule(Module(target_));
} else if (action_ == Actions.ActivatePolicy) {
ensureContract(target_);
_activatePolicy(Policy(target_));
} else if (action_ == Actions.DeactivatePolicy) {
ensureContract(target_);
_deactivatePolicy(Policy(target_));
} else if (action_ == Actions.ChangeExecutor) {
executor = target_;
} else if (action_ == Actions.MigrateKernel) {
ensureContract(target_);
_migrateKernel(Kernel(target_));
}
emit ActionExecuted(action_, target_);
}
function _installModule(Module newModule_) internal {
Keycode keycode = newModule_.KEYCODE();
if (address(getModuleForKeycode[keycode]) != address(0))
revert Kernel_ModuleAlreadyInstalled(keycode);
getModuleForKeycode[keycode] = newModule_;
getKeycodeForModule[newModule_] = keycode;
allKeycodes.push(keycode);
newModule_.INIT();
}
function _upgradeModule(Module newModule_) internal {
Keycode keycode = newModule_.KEYCODE();
Module oldModule = getModuleForKeycode[keycode];
if (address(oldModule) == address(0) || oldModule == newModule_)
revert Kernel_InvalidModuleUpgrade(keycode);
getKeycodeForModule[oldModule] = Keycode.wrap(bytes5(0));
getKeycodeForModule[newModule_] = keycode;
getModuleForKeycode[keycode] = newModule_;
newModule_.INIT();
_reconfigurePolicies(keycode);
}
function _activatePolicy(Policy policy_) internal {
if (isPolicyActive(policy_)) revert Kernel_PolicyAlreadyActivated(address(policy_));
// Add policy to list of active policies
activePolicies.push(policy_);
getPolicyIndex[policy_] = activePolicies.length - 1;
// Record module dependencies
Keycode[] memory dependencies = policy_.configureDependencies();
uint256 depLength = dependencies.length;
for (uint256 i; i < depLength; ) {
Keycode keycode = dependencies[i];
moduleDependents[keycode].push(policy_);
getDependentIndex[keycode][policy_] = moduleDependents[keycode].length - 1;
unchecked {
++i;
}
}
// Grant permissions for policy to access restricted module functions
Permissions[] memory requests = policy_.requestPermissions();
_setPolicyPermissions(policy_, requests, true);
}
function _deactivatePolicy(Policy policy_) internal {
if (!isPolicyActive(policy_)) revert Kernel_PolicyNotActivated(address(policy_));
// Revoke permissions
Permissions[] memory requests = policy_.requestPermissions();
_setPolicyPermissions(policy_, requests, false);
// Remove policy from all policy data structures
uint256 idx = getPolicyIndex[policy_];
Submitted on: 2025-11-07 16:14:21
Comments
Log in to comment.
No comments yet.