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": {
"contracts/l1/Treasury.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;
import {ERC6909} from "../../lib/solmate/src/tokens/ERC6909.sol";
import {Implementation, OwnerOnly} from "../Implementation.sol";
import {IToken} from "../interfaces/IToken.sol";
interface IDepository {
/// @dev Calculates amounts and initiates cross-chain unstake request from specified models.
/// @param unstakeAmount Total amount to unstake.
/// @param chainIds Set of chain Ids with staking proxies.
/// @param stakingProxies Set staking proxies corresponding to each chain Id.
/// @param bridgePayloads Bridge payloads corresponding to each chain Id.
/// @param values Value amounts for each bridge interaction, if applicable.
/// @param sender Sender account.
/// @return amounts Corresponding OLAS amounts for each staking proxy.
function unstake(
uint256 unstakeAmount,
uint256[] memory chainIds,
address[] memory stakingProxies,
bytes[] memory bridgePayloads,
uint256[] memory values,
address sender
) external payable returns (uint256[] memory amounts);
}
interface IST {
/// @dev Redeems OLAS in exchange for stOLAS tokens.
/// @param shares stOLAS amount.
/// @param receiver Receiver account address.
/// @param owner Token owner account address.
/// @return assets OLAS amount.
function redeem(uint256 shares, address receiver, address owner) external returns (uint256 assets);
/// @dev Gets stOLAS staked balance.
/// @return stOLAS staked balance.
function stakedBalance() external returns (uint256);
}
/// @dev Only `depository` has a privilege, but the `sender` was provided.
/// @param sender Sender address.
/// @param depository Required depository address.
error DepositoryOnly(address sender, address depository);
/// @dev Zero value.
error ZeroValue();
/// @dev The contract is already initialized.
error AlreadyInitialized();
/// @dev Value overflow.
/// @param provided Overflow value.
/// @param max Maximum possible value.
error Overflow(uint256 provided, uint256 max);
/// @dev Caught reentrancy violation.
error ReentrancyGuard();
/// @title Treasury - Smart contract for treasury
contract Treasury is Implementation, ERC6909 {
event WithdrawDelayUpdates(uint256 withdrawDelay);
event WithdrawRequestInitiated(
address indexed requester, uint256 indexed requestId, uint256 stAmount, uint256 olasAmount, uint256 withdrawTime
);
event WithdrawRequestExecuted(uint256 requestId, uint256 amount);
event WithdrawAmountRequestedUpdated(uint256 withdrawAmountRequested);
// Treasury version
string public constant VERSION = "0.1.0";
// OLAS token address
address public immutable olas;
// stOLAS token address
address public immutable st;
// Depository address
address public immutable depository;
// Total withdraw amount requested
uint256 public withdrawAmountRequested;
// Withdraw time delay
uint256 public withdrawDelay;
// Number of withdraw requests
uint256 public numWithdrawRequests;
// Reentrancy lock
bool transient _locked;
/// @dev Treasury constructor.
/// @param _olas OLAS address.
/// @param _st stOLAS address.
/// @param _depository Depository address.
constructor(address _olas, address _st, address _depository) {
olas = _olas;
st = _st;
depository = _depository;
}
/// @dev Treasury initializer.
function initialize(uint256 _withdrawDelay) external {
// Check for already initialized
if (owner != address(0)) {
revert AlreadyInitialized();
}
withdrawDelay = _withdrawDelay;
owner = msg.sender;
}
/// @dev Changes withdraw delay value.
/// @param newWithdrawDelay New withdraw delay value in seconds.
function changeWithdrawDelay(uint256 newWithdrawDelay) external {
// Check for ownership
if (msg.sender != owner) {
revert OwnerOnly(msg.sender, owner);
}
withdrawDelay = newWithdrawDelay;
emit WithdrawDelayUpdates(newWithdrawDelay);
}
/// @dev Requests withdraw of OLAS in exchange of provided stOLAS.
/// @notice Vault reserves are used first. If there is a lack of OLAS reserves, the backup amount is requested
/// to be unstaked from other models.
/// @param stAmount Provided stAmount to burn in favor of OLAS tokens.
/// @param chainIds Set of chain Ids with staking proxies.
/// @param stakingProxies Set of staking proxies corresponding to each chain Id.
/// @param bridgePayloads Bridge payloads corresponding to each chain Id.
/// @param values Value amounts for each bridge interaction, if applicable.
/// @return requestId Withdraw request ERC-1155 token.
/// @return olasAmount Calculated OLAS amount.
function requestToWithdraw(
uint256 stAmount,
uint256[] memory chainIds,
address[] memory stakingProxies,
bytes[] memory bridgePayloads,
uint256[] memory values
) external payable returns (uint256 requestId, uint256 olasAmount) {
// Reentrancy guard
if (_locked) {
revert ReentrancyGuard();
}
_locked = true;
// Check for zero value
if (stAmount == 0) {
revert ZeroValue();
}
// Get current staked balance
uint256 stakedBalanceBefore = IST(st).stakedBalance();
// Redeem OLAS and burn stOLAS tokens
olasAmount = IST(st).redeem(stAmount, address(this), msg.sender);
// Get updated staked balance
uint256 stakedBalanceAfter = IST(st).stakedBalance();
// Calculate withdraw time
uint256 withdrawTime = block.timestamp + withdrawDelay;
// Get withdraw request Id
requestId = numWithdrawRequests;
numWithdrawRequests = requestId + 1;
// Push a pair of key defining variables into one key: withdrawTime | requestId
// requestId occupies first 64 bits, withdrawTime occupies next bits as they both fit well in uint256
requestId |= withdrawTime << 64;
// Mint request tokens
_mint(msg.sender, requestId, olasAmount);
// Update total withdraw amount requested
uint256 curWithdrawAmountRequested = withdrawAmountRequested;
curWithdrawAmountRequested += olasAmount;
withdrawAmountRequested = curWithdrawAmountRequested;
// If withdraw amount is bigger than the current one, need to unstake
if (stakedBalanceBefore > stakedBalanceAfter) {
uint256 withdrawDiff = stakedBalanceBefore - stakedBalanceAfter;
IDepository(depository).unstake(withdrawDiff, chainIds, stakingProxies, bridgePayloads, values, msg.sender);
}
emit WithdrawRequestInitiated(msg.sender, requestId, stAmount, olasAmount, withdrawTime);
emit WithdrawAmountRequestedUpdated(curWithdrawAmountRequested);
}
/// @dev Finalizes withdraw requests.
/// @param requestIds Withdraw request Ids.
/// @param amounts Token amounts corresponding to request Ids.
function finalizeWithdrawRequests(uint256[] calldata requestIds, uint256[] calldata amounts) external {
// Reentrancy guard
if (_locked) {
revert ReentrancyGuard();
}
_locked = true;
uint256 totalAmount;
// Traverse all withdraw requests
for (uint256 i = 0; i < requestIds.length; ++i) {
// Decode a pair of key defining variables from one key: withdrawTime | requestId
// requestId occupies first 64 bits, withdrawTime occupies next bits as they both fit well in uint256
uint256 requestId = requestIds[i] & type(uint64).max;
uint256 numRequests = numWithdrawRequests;
// This must never happen as otherwise token would not exist and none of it would be transferFrom-ed
if (requestId >= numRequests) {
revert Overflow(requestId, numRequests - 1);
}
// It is safe to just move 64 bits as there is a single withdrawTime value after that
uint256 withdrawTime = requestIds[i] >> 64;
// Check for earliest possible withdraw time
if (withdrawTime > block.timestamp) {
revert Overflow(withdrawTime, block.timestamp);
}
// Burn withdraw tokens
_burn(msg.sender, requestIds[i], amounts[i]);
totalAmount += amounts[i];
emit WithdrawRequestExecuted(requestIds[i], amounts[i]);
}
// This must never happen
uint256 curWithdrawAmountRequested = withdrawAmountRequested;
if (totalAmount > curWithdrawAmountRequested) {
revert Overflow(totalAmount, curWithdrawAmountRequested);
}
// Update total withdraw amount requested
curWithdrawAmountRequested -= totalAmount;
withdrawAmountRequested = curWithdrawAmountRequested;
// Transfer total amount of OLAS
// The transfer overflow check is not needed since balances are in sync
// OLAS has been redeemed when withdraw request was posted
IToken(olas).transfer(msg.sender, totalAmount);
emit WithdrawAmountRequestedUpdated(curWithdrawAmountRequested);
}
/// @dev Gets withdraw request Id by request Id and withdraw time.
/// @param requestId Withdraw request Id.
/// @param withdrawTime Withdraw time.
function getWithdrawRequestId(uint256 requestId, uint256 withdrawTime) external pure returns (uint256) {
return requestId | (withdrawTime << 64);
}
/// @dev Gets withdraw request Id and time.
/// @param withdrawRequestId Combined withdrawRequestId value.
function getWithdrawIdAndTime(uint256 withdrawRequestId) external pure returns (uint256, uint256) {
return ((withdrawRequestId & type(uint64).max), (withdrawRequestId >> 64));
}
}
"
},
"lib/solmate/src/tokens/ERC6909.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
/// @notice Minimalist and gas efficient standard ERC6909 implementation.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC6909.sol)
abstract contract ERC6909 {
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
event OperatorSet(address indexed owner, address indexed operator, bool approved);
event Approval(address indexed owner, address indexed spender, uint256 indexed id, uint256 amount);
event Transfer(address caller, address indexed from, address indexed to, uint256 indexed id, uint256 amount);
/*//////////////////////////////////////////////////////////////
ERC6909 STORAGE
//////////////////////////////////////////////////////////////*/
mapping(address => mapping(address => bool)) public isOperator;
mapping(address => mapping(uint256 => uint256)) public balanceOf;
mapping(address => mapping(address => mapping(uint256 => uint256))) public allowance;
/*//////////////////////////////////////////////////////////////
ERC6909 LOGIC
//////////////////////////////////////////////////////////////*/
function transfer(
address receiver,
uint256 id,
uint256 amount
) public virtual returns (bool) {
balanceOf[msg.sender][id] -= amount;
balanceOf[receiver][id] += amount;
emit Transfer(msg.sender, msg.sender, receiver, id, amount);
return true;
}
function transferFrom(
address sender,
address receiver,
uint256 id,
uint256 amount
) public virtual returns (bool) {
if (msg.sender != sender && !isOperator[sender][msg.sender]) {
uint256 allowed = allowance[sender][msg.sender][id];
if (allowed != type(uint256).max) allowance[sender][msg.sender][id] = allowed - amount;
}
balanceOf[sender][id] -= amount;
balanceOf[receiver][id] += amount;
emit Transfer(msg.sender, sender, receiver, id, amount);
return true;
}
function approve(
address spender,
uint256 id,
uint256 amount
) public virtual returns (bool) {
allowance[msg.sender][spender][id] = amount;
emit Approval(msg.sender, spender, id, amount);
return true;
}
function setOperator(address operator, bool approved) public virtual returns (bool) {
isOperator[msg.sender][operator] = approved;
emit OperatorSet(msg.sender, operator, approved);
return true;
}
/*//////////////////////////////////////////////////////////////
ERC165 LOGIC
//////////////////////////////////////////////////////////////*/
function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
return
interfaceId == 0x01ffc9a7 || // ERC165 Interface ID for ERC165
interfaceId == 0x0f632fb3; // ERC165 Interface ID for ERC6909
}
/*//////////////////////////////////////////////////////////////
INTERNAL MINT/BURN LOGIC
//////////////////////////////////////////////////////////////*/
function _mint(
address receiver,
uint256 id,
uint256 amount
) internal virtual {
balanceOf[receiver][id] += amount;
emit Transfer(msg.sender, address(0), receiver, id, amount);
}
function _burn(
address sender,
uint256 id,
uint256 amount
) internal virtual {
balanceOf[sender][id] -= amount;
emit Transfer(msg.sender, sender, address(0), id, amount);
}
}
"
},
"contracts/Implementation.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;
/// @dev Only `owner` has a privilege, but the `sender` was provided.
/// @param sender Sender address.
/// @param owner Required sender address as an owner.
error OwnerOnly(address sender, address owner);
/// @dev Zero address.
error ZeroAddress();
/// @title Implementation - Smart contract for default minimal implementation
contract Implementation {
event OwnerUpdated(address indexed owner);
event ImplementationUpdated(address indexed implementation);
// Code position in storage is bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1) = "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc"
bytes32 public constant PROXY_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
// Contract owner address
address public owner;
/// @dev Changes contract owner address.
/// @param newOwner Address of a new owner.
function changeOwner(address newOwner) external {
// Check for ownership
if (msg.sender != owner) {
revert OwnerOnly(msg.sender, owner);
}
// Check for zero address
if (newOwner == address(0)) {
revert ZeroAddress();
}
owner = newOwner;
emit OwnerUpdated(newOwner);
}
/// @dev Changes depository implementation contract address.
/// @param newImplementation New implementation contract address.
function changeImplementation(address newImplementation) external {
// Check for ownership
if (msg.sender != owner) {
revert OwnerOnly(msg.sender, owner);
}
// Check for zero address
if (newImplementation == address(0)) {
revert ZeroAddress();
}
// Store depository implementation address
assembly {
sstore(PROXY_SLOT, newImplementation)
}
emit ImplementationUpdated(newImplementation);
}
}
"
},
"contracts/interfaces/IToken.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;
// ERC20 token interface
interface IToken {
/// @dev Transfers the token amount.
/// @param to Address to transfer to.
/// @param amount The amount to transfer.
/// @return True if the function execution is successful.
function transfer(address to, uint256 amount) external returns (bool);
/// @dev Transfers the token amount that was previously approved up until the maximum allowance.
/// @param from Account address to transfer from.
/// @param to Account address to transfer to.
/// @param amount Amount to transfer to.
/// @return True if the function execution is successful.
function transferFrom(address from, address to, uint256 amount) external returns (bool);
/// @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
/// @param spender Account address that will be able to transfer tokens on behalf of the caller.
/// @param amount Token amount.
/// @return True if the function execution is successful.
function approve(address spender, uint256 amount) external returns (bool);
/// @dev Mints tokens.
/// @param account Account address.
/// @param amount Token amount.
function mint(address account, uint256 amount) external;
/// @dev Burns tokens.
/// @param amount Token amount.
function burn(uint256 amount) external;
/// @dev Gets the amount of tokens owned by a specified account.
/// @param account Account address.
/// @return Amount of tokens owned.
function balanceOf(address account) external view returns (uint256);
}
// ERC721 token interface
interface INFToken {
/// @dev Sets token `id` as the allowance of `spender` over the caller's tokens.
/// @param spender Account address that will be able to transfer the token on behalf of the caller.
/// @param id Token id.
function approve(address spender, uint256 id) external;
/// @dev Transfers a specified token Id.
/// @param from Account address to transfer from.
/// @param to Account address to transfer to.
/// @param id Token id.
function transferFrom(address from, address to, uint256 id) external;
/// @dev Transfers a specified token Id with a callback.
/// @param from Account address to transfer from.
/// @param to Account address to transfer to.
/// @param id Token id.
function safeTransferFrom(address from, address to, uint256 id) external;
}
"
}
},
"settings": {
"remappings": [
"@gnosis.pm/=node_modules/@gnosis.pm/",
"@layerzerolabs/oapp-evm/=lib/devtools/packages/oapp-evm/",
"@layerzerolabs/lz-evm-protocol-v2/=lib/layerzero-v2/packages/layerzero-v2/evm/protocol/",
"@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
"@registries/=lib/autonolas-registries/",
"@solmate/=lib/solmate/",
"autonolas-registries/=lib/autonolas-registries/",
"devtools/=lib/devtools/packages/toolbox-foundry/src/",
"ds-test/=lib/autonolas-registries/lib/forge-std/lib/ds-test/src/",
"erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
"forge-std/=lib/autonolas-registries/lib/forge-std/src/",
"halmos-cheatcodes/=lib/openzeppelin-contracts/lib/halmos-cheatcodes/src/",
"layerzero-v2/=lib/layerzero-v2/",
"openzeppelin-contracts/=lib/openzeppelin-contracts/",
"solmate/=lib/solmate/src/"
],
"optimizer": {
"enabled": true,
"runs": 1000000
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "ipfs",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "prague",
"viaIR": true
}
}}
Submitted on: 2025-10-24 09:54:40
Comments
Log in to comment.
No comments yet.