Feature Tip: Add private address tag to any address under My Name Tag !
Source Code
Overview
ETH Balance
0 ETH
Eth Value
$0.00Latest 12 from a total of 12 transactions
| Transaction Hash |
Method
|
Block
|
From
|
|
To
|
||||
|---|---|---|---|---|---|---|---|---|---|
| Deploy | 22443474 | 310 days ago | IN | 0 ETH | 0.00308557 | ||||
| Deploy | 20918503 | 523 days ago | IN | 0 ETH | 0.019477 | ||||
| Deploy | 20853926 | 532 days ago | IN | 0 ETH | 0.00739604 | ||||
| Deploy | 20690025 | 555 days ago | IN | 0 ETH | 0.00789613 | ||||
| Deploy | 20575897 | 571 days ago | IN | 0 ETH | 0.00158988 | ||||
| Deploy | 20386902 | 598 days ago | IN | 0 ETH | 0.00313904 | ||||
| Deploy | 20107182 | 637 days ago | IN | 0 ETH | 0.00558586 | ||||
| Deploy | 20032049 | 647 days ago | IN | 0 ETH | 0.0223729 | ||||
| Deploy | 20004679 | 651 days ago | IN | 0 ETH | 0.01773286 | ||||
| Deploy | 19602534 | 707 days ago | IN | 0 ETH | 0.02477219 | ||||
| Deploy | 19600968 | 707 days ago | IN | 0 ETH | 0.01635695 | ||||
| Deploy | 19580266 | 710 days ago | IN | 0 ETH | 0.02466224 |
Latest 25 internal transactions (View All)
Advanced mode:
| Parent Transaction Hash | Method | Block |
From
|
|
To
|
||
|---|---|---|---|---|---|---|---|
| 0x60806040 | 22443474 | 310 days ago | Contract Creation | 0 ETH | |||
| 0x60806040 | 22443474 | 310 days ago | Contract Creation | 0 ETH | |||
| 0x60806040 | 22443474 | 310 days ago | Contract Creation | 0 ETH | |||
| 0x60806040 | 20918503 | 523 days ago | Contract Creation | 0 ETH | |||
| 0x60806040 | 20918503 | 523 days ago | Contract Creation | 0 ETH | |||
| 0x60806040 | 20918503 | 523 days ago | Contract Creation | 0 ETH | |||
| 0x60806040 | 20853926 | 532 days ago | Contract Creation | 0 ETH | |||
| 0x60806040 | 20853926 | 532 days ago | Contract Creation | 0 ETH | |||
| 0x60806040 | 20853926 | 532 days ago | Contract Creation | 0 ETH | |||
| 0x60806040 | 20690025 | 555 days ago | Contract Creation | 0 ETH | |||
| 0x60806040 | 20690025 | 555 days ago | Contract Creation | 0 ETH | |||
| 0x60806040 | 20690025 | 555 days ago | Contract Creation | 0 ETH | |||
| 0x60806040 | 20575897 | 571 days ago | Contract Creation | 0 ETH | |||
| 0x60806040 | 20575897 | 571 days ago | Contract Creation | 0 ETH | |||
| 0x60806040 | 20575897 | 571 days ago | Contract Creation | 0 ETH | |||
| 0x60806040 | 20386902 | 598 days ago | Contract Creation | 0 ETH | |||
| 0x60806040 | 20386902 | 598 days ago | Contract Creation | 0 ETH | |||
| 0x60806040 | 20386902 | 598 days ago | Contract Creation | 0 ETH | |||
| 0x60806040 | 20107182 | 637 days ago | Contract Creation | 0 ETH | |||
| 0x60806040 | 20107182 | 637 days ago | Contract Creation | 0 ETH | |||
| 0x60806040 | 20107182 | 637 days ago | Contract Creation | 0 ETH | |||
| 0x60806040 | 20032049 | 647 days ago | Contract Creation | 0 ETH | |||
| 0x60806040 | 20032049 | 647 days ago | Contract Creation | 0 ETH | |||
| 0x60806040 | 20032049 | 647 days ago | Contract Creation | 0 ETH | |||
| 0x60806040 | 20004679 | 651 days ago | Contract Creation | 0 ETH |
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
DelegationWalletFactory
Compiler Version
v0.8.19+commit.7dd6d404
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.19;
import { Enum } from "@gnosis.pm/safe-contracts/contracts/common/Enum.sol";
import { GnosisSafeProxyFactory, GnosisSafeProxy } from "@gnosis.pm/safe-contracts/contracts/proxies/GnosisSafeProxyFactory.sol";
import { UpgradeableBeacon } from "@openzeppelin/contracts/proxy/beacon/UpgradeableBeacon.sol";
import { BeaconProxy } from "@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol";
import { IERC721 } from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import { Address } from "@openzeppelin/contracts/utils/Address.sol";
import { IGnosisSafe } from "./interfaces/IGnosisSafe.sol";
import { TransactionGuard } from "./libs/guards/TransactionGuard.sol";
import { IDelegationWalletRegistry } from "./interfaces/IDelegationWalletRegistry.sol";
import { GnosisSafe } from "@gnosis.pm/safe-contracts/contracts/GnosisSafe.sol";
import { GuardOwner } from "./libs/owners/GuardOwner.sol";
import { DelegationOwner } from "./libs/owners/DelegationOwner.sol";
import { ProtocolOwner } from "./libs/owners/ProtocolOwner.sol";
/**
* @title DelegationWalletFactory
* @author BootNode
* @dev Factory contract for deploying and configuring a new Delegation Wallet
* Deploys a GnosisSafe, a DelegationOwner and a DelegationGuard, sets Safe wallet threshold to 1, the DelegationOwner
* contract as owner together with the deployer and the DelegationGuard as the Safe's guard.
*/
contract DelegationWalletFactory {
/**
* @notice Stores the Safe proxy factory address.
*/
address public immutable gnosisSafeProxyFactory;
/**
* @notice Stores the Safe implementation address.
*/
address public immutable singleton;
/**
* @notice Stores the Safe CompatibilityFallbackHandler address.
*/
address public immutable compatibilityFallbackHandler;
/**
* @notice Stores the TransactionGuard beacon contract address.
*/
address public immutable guardBeacon;
/**
* @notice Stores the GuardOwner beacon contract address.
*/
address public immutable guardOwnerBeacon;
/**
* @notice Stores the DelegationOwner beacon contract address.
*/
address public immutable delegationOwnerBeacon;
/**
* @notice Stores the DelegationOwner beacon contract address.
*/
address public immutable protocolOwnerBeacon;
/**
* @notice Stores the DelegationWalletRegistry contract address.
*/
address public immutable registry;
event WalletDeployed(
address indexed safe,
address indexed owner,
address indexed guard,
address delegationOwner,
address protocolOwner,
address sender
);
constructor(
address _gnosisSafeProxyFactory,
address _singleton,
address _compatibilityFallbackHandler,
address _guardBeacon,
address _guardOwnerBeacon,
address _delegationOwnerBeacon,
address _protocolOwnerBeacon,
address _registry
) {
gnosisSafeProxyFactory = _gnosisSafeProxyFactory;
singleton = _singleton;
compatibilityFallbackHandler = _compatibilityFallbackHandler;
guardBeacon = _guardBeacon;
guardOwnerBeacon = _guardOwnerBeacon;
delegationOwnerBeacon = _delegationOwnerBeacon;
protocolOwnerBeacon = _protocolOwnerBeacon;
registry = _registry;
}
/**
* @notice Deploys a new DelegationWallet with the msg.sender as the owner.
*/
function deploy(address _delegationController) external returns (address, address, address, address) {
return deployFor(msg.sender, _delegationController);
}
/**
* @notice Deploys a new DelegationWallet for a given owner.
* @param _owner - The owner's address.
* @param _delegationController - Delegation controller owner
*/
function deployFor(
address _owner,
address _delegationController
) public returns (address, address, address, address) {
address safeProxy = address(
GnosisSafeProxyFactory(gnosisSafeProxyFactory).createProxy(singleton, new bytes(0))
);
// Proxy creation
address guardOwnerProxy = address(new BeaconProxy(guardOwnerBeacon, new bytes(0)));
address delegationOwnerProxy = address(new BeaconProxy(delegationOwnerBeacon, new bytes(0)));
address protocolOwnerProxy = address(new BeaconProxy(protocolOwnerBeacon, new bytes(0)));
// Set owners
address[] memory owners = new address[](4);
owners[0] = _owner;
owners[1] = guardOwnerProxy;
owners[2] = delegationOwnerProxy;
owners[3] = protocolOwnerProxy;
// setup owners and threshold, this should be done before delegationOwner.initialize because DelegationOwners
// has to be an owner to be able to set the guard
GnosisSafe(payable(safeProxy)).setup(
owners,
1,
address(0),
new bytes(0),
compatibilityFallbackHandler,
address(0),
0,
payable(address(0))
);
// Setup logic of the GUARD
//////////////////////////////////////////
// Initialize Owners Manager
//////////////////////////////////////////
// Guard OWNER
GuardOwner(guardOwnerProxy).initialize(
guardBeacon,
address(safeProxy),
_owner,
delegationOwnerProxy,
protocolOwnerProxy
);
address guard = address(GuardOwner(guardOwnerProxy).guard());
// Delegation OWNER
DelegationOwner(delegationOwnerProxy).initialize(
guard,
safeProxy,
_owner,
_delegationController,
protocolOwnerProxy
);
// Protocol OWNER
ProtocolOwner(protocolOwnerProxy).initialize(guard, address(safeProxy), _owner, delegationOwnerProxy);
// Save wallet
IDelegationWalletRegistry(registry).setWallet(
safeProxy,
_owner,
guard,
guardOwnerProxy,
delegationOwnerProxy,
protocolOwnerProxy
);
emit WalletDeployed(safeProxy, _owner, guard, delegationOwnerProxy, protocolOwnerProxy, msg.sender);
return (safeProxy, delegationOwnerProxy, protocolOwnerProxy, guardOwnerProxy);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)
pragma solidity ^0.8.0;
/**
* @dev External interface of AccessControl declared to support ERC165 detection.
*/
interface IAccessControl {
/**
* @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
*
* `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
* {RoleAdminChanged} not being emitted signaling this.
*
* _Available since v3.1._
*/
event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
/**
* @dev Emitted when `account` is granted `role`.
*
* `sender` is the account that originated the contract call, an admin role
* bearer except when using {AccessControl-_setupRole}.
*/
event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Emitted when `account` is revoked `role`.
*
* `sender` is the account that originated the contract call:
* - if using `revokeRole`, it is the admin role bearer
* - if using `renounceRole`, it is the role bearer (i.e. `account`)
*/
event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) external view returns (bool);
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {AccessControl-_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) external view returns (bytes32);
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function grantRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function revokeRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been granted `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `account`.
*/
function renounceRole(bytes32 role, address account) external;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)
pragma solidity ^0.8.0;
import "../utils/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
constructor() {
_transferOwnership(_msgSender());
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (interfaces/IERC1967.sol)
pragma solidity ^0.8.0;
/**
* @dev ERC-1967: Proxy Storage Slots. This interface contains the events defined in the ERC.
*
* _Available since v4.8.3._
*/
interface IERC1967 {
/**
* @dev Emitted when the implementation is upgraded.
*/
event Upgraded(address indexed implementation);
/**
* @dev Emitted when the admin account has changed.
*/
event AdminChanged(address previousAdmin, address newAdmin);
/**
* @dev Emitted when the beacon is changed.
*/
event BeaconUpgraded(address indexed beacon);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (interfaces/draft-IERC1822.sol)
pragma solidity ^0.8.0;
/**
* @dev ERC1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified
* proxy whose upgrades are fully controlled by the current implementation.
*/
interface IERC1822Proxiable {
/**
* @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation
* address.
*
* IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
* bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
* function revert if invoked through a proxy.
*/
function proxiableUUID() external view returns (bytes32);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (proxy/ERC1967/ERC1967Upgrade.sol)
pragma solidity ^0.8.2;
import "../beacon/IBeacon.sol";
import "../../interfaces/IERC1967.sol";
import "../../interfaces/draft-IERC1822.sol";
import "../../utils/Address.sol";
import "../../utils/StorageSlot.sol";
/**
* @dev This abstract contract provides getters and event emitting update functions for
* https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.
*
* _Available since v4.1._
*/
abstract contract ERC1967Upgrade is IERC1967 {
// This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1
bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;
/**
* @dev Storage slot with the address of the current implementation.
* This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
* validated in the constructor.
*/
bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
/**
* @dev Returns the current implementation address.
*/
function _getImplementation() internal view returns (address) {
return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
}
/**
* @dev Stores a new address in the EIP1967 implementation slot.
*/
function _setImplementation(address newImplementation) private {
require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
}
/**
* @dev Perform implementation upgrade
*
* Emits an {Upgraded} event.
*/
function _upgradeTo(address newImplementation) internal {
_setImplementation(newImplementation);
emit Upgraded(newImplementation);
}
/**
* @dev Perform implementation upgrade with additional setup call.
*
* Emits an {Upgraded} event.
*/
function _upgradeToAndCall(address newImplementation, bytes memory data, bool forceCall) internal {
_upgradeTo(newImplementation);
if (data.length > 0 || forceCall) {
Address.functionDelegateCall(newImplementation, data);
}
}
/**
* @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call.
*
* Emits an {Upgraded} event.
*/
function _upgradeToAndCallUUPS(address newImplementation, bytes memory data, bool forceCall) internal {
// Upgrades from old implementations will perform a rollback test. This test requires the new
// implementation to upgrade back to the old, non-ERC1822 compliant, implementation. Removing
// this special case will break upgrade paths from old UUPS implementation to new ones.
if (StorageSlot.getBooleanSlot(_ROLLBACK_SLOT).value) {
_setImplementation(newImplementation);
} else {
try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) {
require(slot == _IMPLEMENTATION_SLOT, "ERC1967Upgrade: unsupported proxiableUUID");
} catch {
revert("ERC1967Upgrade: new implementation is not UUPS");
}
_upgradeToAndCall(newImplementation, data, forceCall);
}
}
/**
* @dev Storage slot with the admin of the contract.
* This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
* validated in the constructor.
*/
bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
/**
* @dev Returns the current admin.
*/
function _getAdmin() internal view returns (address) {
return StorageSlot.getAddressSlot(_ADMIN_SLOT).value;
}
/**
* @dev Stores a new address in the EIP1967 admin slot.
*/
function _setAdmin(address newAdmin) private {
require(newAdmin != address(0), "ERC1967: new admin is the zero address");
StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin;
}
/**
* @dev Changes the admin of the proxy.
*
* Emits an {AdminChanged} event.
*/
function _changeAdmin(address newAdmin) internal {
emit AdminChanged(_getAdmin(), newAdmin);
_setAdmin(newAdmin);
}
/**
* @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
* This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor.
*/
bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;
/**
* @dev Returns the current beacon.
*/
function _getBeacon() internal view returns (address) {
return StorageSlot.getAddressSlot(_BEACON_SLOT).value;
}
/**
* @dev Stores a new beacon in the EIP1967 beacon slot.
*/
function _setBeacon(address newBeacon) private {
require(Address.isContract(newBeacon), "ERC1967: new beacon is not a contract");
require(
Address.isContract(IBeacon(newBeacon).implementation()),
"ERC1967: beacon implementation is not a contract"
);
StorageSlot.getAddressSlot(_BEACON_SLOT).value = newBeacon;
}
/**
* @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does
* not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that).
*
* Emits a {BeaconUpgraded} event.
*/
function _upgradeBeaconToAndCall(address newBeacon, bytes memory data, bool forceCall) internal {
_setBeacon(newBeacon);
emit BeaconUpgraded(newBeacon);
if (data.length > 0 || forceCall) {
Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (proxy/Proxy.sol)
pragma solidity ^0.8.0;
/**
* @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM
* instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to
* be specified by overriding the virtual {_implementation} function.
*
* Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a
* different contract through the {_delegate} function.
*
* The success and return data of the delegated call will be returned back to the caller of the proxy.
*/
abstract contract Proxy {
/**
* @dev Delegates the current call to `implementation`.
*
* This function does not return to its internal call site, it will return directly to the external caller.
*/
function _delegate(address implementation) internal virtual {
assembly {
// Copy msg.data. We take full control of memory in this inline assembly
// block because it will not return to Solidity code. We overwrite the
// Solidity scratch pad at memory position 0.
calldatacopy(0, 0, calldatasize())
// Call the implementation.
// out and outsize are 0 because we don't know the size yet.
let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)
// Copy the returned data.
returndatacopy(0, 0, returndatasize())
switch result
// delegatecall returns 0 on error.
case 0 {
revert(0, returndatasize())
}
default {
return(0, returndatasize())
}
}
}
/**
* @dev This is a virtual function that should be overridden so it returns the address to which the fallback function
* and {_fallback} should delegate.
*/
function _implementation() internal view virtual returns (address);
/**
* @dev Delegates the current call to the address returned by `_implementation()`.
*
* This function does not return to its internal call site, it will return directly to the external caller.
*/
function _fallback() internal virtual {
_beforeFallback();
_delegate(_implementation());
}
/**
* @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other
* function in the contract matches the call data.
*/
fallback() external payable virtual {
_fallback();
}
/**
* @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data
* is empty.
*/
receive() external payable virtual {
_fallback();
}
/**
* @dev Hook that is called before falling back to the implementation. Can happen as part of a manual `_fallback`
* call, or as part of the Solidity `fallback` or `receive` functions.
*
* If overridden should call `super._beforeFallback()`.
*/
function _beforeFallback() internal virtual {}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (proxy/beacon/BeaconProxy.sol)
pragma solidity ^0.8.0;
import "./IBeacon.sol";
import "../Proxy.sol";
import "../ERC1967/ERC1967Upgrade.sol";
/**
* @dev This contract implements a proxy that gets the implementation address for each call from an {UpgradeableBeacon}.
*
* The beacon address is stored in storage slot `uint256(keccak256('eip1967.proxy.beacon')) - 1`, so that it doesn't
* conflict with the storage layout of the implementation behind the proxy.
*
* _Available since v3.4._
*/
contract BeaconProxy is Proxy, ERC1967Upgrade {
/**
* @dev Initializes the proxy with `beacon`.
*
* If `data` is nonempty, it's used as data in a delegate call to the implementation returned by the beacon. This
* will typically be an encoded function call, and allows initializing the storage of the proxy like a Solidity
* constructor.
*
* Requirements:
*
* - `beacon` must be a contract with the interface {IBeacon}.
*/
constructor(address beacon, bytes memory data) payable {
_upgradeBeaconToAndCall(beacon, data, false);
}
/**
* @dev Returns the current beacon address.
*/
function _beacon() internal view virtual returns (address) {
return _getBeacon();
}
/**
* @dev Returns the current implementation address of the associated beacon.
*/
function _implementation() internal view virtual override returns (address) {
return IBeacon(_getBeacon()).implementation();
}
/**
* @dev Changes the proxy to use a new beacon. Deprecated: see {_upgradeBeaconToAndCall}.
*
* If `data` is nonempty, it's used as data in a delegate call to the implementation returned by the beacon.
*
* Requirements:
*
* - `beacon` must be a contract.
* - The implementation returned by `beacon` must be a contract.
*/
function _setBeacon(address beacon, bytes memory data) internal virtual {
_upgradeBeaconToAndCall(beacon, data, false);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (proxy/beacon/IBeacon.sol)
pragma solidity ^0.8.0;
/**
* @dev This is the interface that {BeaconProxy} expects of its beacon.
*/
interface IBeacon {
/**
* @dev Must return an address that can be used as a delegate call target.
*
* {BeaconProxy} will check that this address is a contract.
*/
function implementation() external view returns (address);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (proxy/beacon/UpgradeableBeacon.sol)
pragma solidity ^0.8.0;
import "./IBeacon.sol";
import "../../access/Ownable.sol";
import "../../utils/Address.sol";
/**
* @dev This contract is used in conjunction with one or more instances of {BeaconProxy} to determine their
* implementation contract, which is where they will delegate all function calls.
*
* An owner is able to change the implementation the beacon points to, thus upgrading the proxies that use this beacon.
*/
contract UpgradeableBeacon is IBeacon, Ownable {
address private _implementation;
/**
* @dev Emitted when the implementation returned by the beacon is changed.
*/
event Upgraded(address indexed implementation);
/**
* @dev Sets the address of the initial implementation, and the deployer account as the owner who can upgrade the
* beacon.
*/
constructor(address implementation_) {
_setImplementation(implementation_);
}
/**
* @dev Returns the current implementation address.
*/
function implementation() public view virtual override returns (address) {
return _implementation;
}
/**
* @dev Upgrades the beacon to a new implementation.
*
* Emits an {Upgraded} event.
*
* Requirements:
*
* - msg.sender must be the owner of the contract.
* - `newImplementation` must be a contract.
*/
function upgradeTo(address newImplementation) public virtual onlyOwner {
_setImplementation(newImplementation);
emit Upgraded(newImplementation);
}
/**
* @dev Sets the implementation contract address for this beacon
*
* Requirements:
*
* - `newImplementation` must be a contract.
*/
function _setImplementation(address newImplementation) private {
require(Address.isContract(newImplementation), "UpgradeableBeacon: implementation is not a contract");
_implementation = newImplementation;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (proxy/utils/Initializable.sol)
pragma solidity ^0.8.2;
import "../../utils/Address.sol";
/**
* @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
* behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
* external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
* function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
*
* The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
* reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
* case an upgrade adds a module that needs to be initialized.
*
* For example:
*
* [.hljs-theme-light.nopadding]
* ```solidity
* contract MyToken is ERC20Upgradeable {
* function initialize() initializer public {
* __ERC20_init("MyToken", "MTK");
* }
* }
*
* contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
* function initializeV2() reinitializer(2) public {
* __ERC20Permit_init("MyToken");
* }
* }
* ```
*
* TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
* possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
*
* CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
* that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
*
* [CAUTION]
* ====
* Avoid leaving a contract uninitialized.
*
* An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
* contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
* the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
*
* [.hljs-theme-light.nopadding]
* ```
* /// @custom:oz-upgrades-unsafe-allow constructor
* constructor() {
* _disableInitializers();
* }
* ```
* ====
*/
abstract contract Initializable {
/**
* @dev Indicates that the contract has been initialized.
* @custom:oz-retyped-from bool
*/
uint8 private _initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool private _initializing;
/**
* @dev Triggered when the contract has been initialized or reinitialized.
*/
event Initialized(uint8 version);
/**
* @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
* `onlyInitializing` functions can be used to initialize parent contracts.
*
* Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a
* constructor.
*
* Emits an {Initialized} event.
*/
modifier initializer() {
bool isTopLevelCall = !_initializing;
require(
(isTopLevelCall && _initialized < 1) || (!Address.isContract(address(this)) && _initialized == 1),
"Initializable: contract is already initialized"
);
_initialized = 1;
if (isTopLevelCall) {
_initializing = true;
}
_;
if (isTopLevelCall) {
_initializing = false;
emit Initialized(1);
}
}
/**
* @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
* contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
* used to initialize parent contracts.
*
* A reinitializer may be used after the original initialization step. This is essential to configure modules that
* are added through upgrades and that require initialization.
*
* When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
* cannot be nested. If one is invoked in the context of another, execution will revert.
*
* Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
* a contract, executing them in the right order is up to the developer or operator.
*
* WARNING: setting the version to 255 will prevent any future reinitialization.
*
* Emits an {Initialized} event.
*/
modifier reinitializer(uint8 version) {
require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
_initialized = version;
_initializing = true;
_;
_initializing = false;
emit Initialized(version);
}
/**
* @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
* {initializer} and {reinitializer} modifiers, directly or indirectly.
*/
modifier onlyInitializing() {
require(_initializing, "Initializable: contract is not initializing");
_;
}
/**
* @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
* Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
* to any version. It is recommended to use this to lock implementation contracts that are designed to be called
* through proxies.
*
* Emits an {Initialized} event the first time it is successfully executed.
*/
function _disableInitializers() internal virtual {
require(!_initializing, "Initializable: contract is initializing");
if (_initialized != type(uint8).max) {
_initialized = type(uint8).max;
emit Initialized(type(uint8).max);
}
}
/**
* @dev Returns the highest version that has been initialized. See {reinitializer}.
*/
function _getInitializedVersion() internal view returns (uint8) {
return _initialized;
}
/**
* @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
*/
function _isInitializing() internal view returns (bool) {
return _initializing;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 amount) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC721/IERC721.sol)
pragma solidity ^0.8.0;
import "../../utils/introspection/IERC165.sol";
/**
* @dev Required interface of an ERC721 compliant contract.
*/
interface IERC721 is IERC165 {
/**
* @dev Emitted when `tokenId` token is transferred from `from` to `to`.
*/
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
*/
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
*/
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
/**
* @dev Returns the number of tokens in ``owner``'s account.
*/
function balanceOf(address owner) external view returns (uint256 balance);
/**
* @dev Returns the owner of the `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function ownerOf(uint256 tokenId) external view returns (address owner);
/**
* @dev Safely transfers `tokenId` token from `from` to `to`.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
/**
* @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
* are aware of the ERC721 protocol to prevent tokens from being forever locked.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must have been allowed to move this token by either {approve} or {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(address from, address to, uint256 tokenId) external;
/**
* @dev Transfers `tokenId` token from `from` to `to`.
*
* WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721
* or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
* understand this adds an external call which potentially creates a reentrancy vulnerability.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 tokenId) external;
/**
* @dev Gives permission to `to` to transfer `tokenId` token to another account.
* The approval is cleared when the token is transferred.
*
* Only a single account can be approved at a time, so approving the zero address clears previous approvals.
*
* Requirements:
*
* - The caller must own the token or be an approved operator.
* - `tokenId` must exist.
*
* Emits an {Approval} event.
*/
function approve(address to, uint256 tokenId) external;
/**
* @dev Approve or remove `operator` as an operator for the caller.
* Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
*
* Requirements:
*
* - The `operator` cannot be the caller.
*
* Emits an {ApprovalForAll} event.
*/
function setApprovalForAll(address operator, bool approved) external;
/**
* @dev Returns the account approved for `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function getApproved(uint256 tokenId) external view returns (address operator);
/**
* @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
*
* See {setApprovalForAll}
*/
function isApprovedForAll(address owner, address operator) external view returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
pragma solidity ^0.8.1;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
*
* Furthermore, `isContract` will also return true if the target contract within
* the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
* which only has an effect at the end of a transaction.
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0
// for contracts in construction, since the code is only stored at the end
// of the constructor execution.
return account.code.length > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
* the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
*
* _Available since v4.8._
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
// only check isContract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
/**
* @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason or using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
pragma solidity ^0.8.0;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/StorageSlot.sol)
// This file was procedurally generated from scripts/generate/templates/StorageSlot.js.
pragma solidity ^0.8.0;
/**
* @dev Library for reading and writing primitive types to specific storage slots.
*
* Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
* This library helps with reading and writing to such slots without the need for inline assembly.
*
* The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
*
* Example usage to set ERC1967 implementation slot:
* ```solidity
* contract ERC1967 {
* bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
*
* function _getImplementation() internal view returns (address) {
* return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
* }
*
* function _setImplementation(address newImplementation) internal {
* require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
* StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
* }
* }
* ```
*
* _Available since v4.1 for `address`, `bool`, `bytes32`, `uint256`._
* _Available since v4.9 for `string`, `bytes`._
*/
library StorageSlot {
struct AddressSlot {
address value;
}
struct BooleanSlot {
bool value;
}
struct Bytes32Slot {
bytes32 value;
}
struct Uint256Slot {
uint256 value;
}
struct StringSlot {
string value;
}
struct BytesSlot {
bytes value;
}
/**
* @dev Returns an `AddressSlot` with member `value` located at `slot`.
*/
function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `BooleanSlot` with member `value` located at `slot`.
*/
function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
*/
function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `Uint256Slot` with member `value` located at `slot`.
*/
function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `StringSlot` with member `value` located at `slot`.
*/
function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `StringSlot` representation of the string storage pointer `store`.
*/
function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := store.slot
}
}
/**
* @dev Returns an `BytesSlot` with member `value` located at `slot`.
*/
function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
*/
function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := store.slot
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol)
pragma solidity ^0.8.0;
import "./math/Math.sol";
import "./math/SignedMath.sol";
/**
* @dev String operations.
*/
library Strings {
bytes16 private constant _SYMBOLS = "0123456789abcdef";
uint8 private constant _ADDRESS_LENGTH = 20;
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/
function toString(uint256 value) internal pure returns (string memory) {
unchecked {
uint256 length = Math.log10(value) + 1;
string memory buffer = new string(length);
uint256 ptr;
/// @solidity memory-safe-assembly
assembly {
ptr := add(buffer, add(32, length))
}
while (true) {
ptr--;
/// @solidity memory-safe-assembly
assembly {
mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
}
value /= 10;
if (value == 0) break;
}
return buffer;
}
}
/**
* @dev Converts a `int256` to its ASCII `string` decimal representation.
*/
function toString(int256 value) internal pure returns (string memory) {
return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMath.abs(value))));
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
*/
function toHexString(uint256 value) internal pure returns (string memory) {
unchecked {
return toHexString(value, Math.log256(value) + 1);
}
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
*/
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = _SYMBOLS[value & 0xf];
value >>= 4;
}
require(value == 0, "Strings: hex length insufficient");
return string(buffer);
}
/**
* @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
*/
function toHexString(address addr) internal pure returns (string memory) {
return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
}
/**
* @dev Returns true if the two strings are equal.
*/
function equal(string memory a, string memory b) internal pure returns (bool) {
return keccak256(bytes(a)) == keccak256(bytes(b));
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/cryptography/ECDSA.sol)
pragma solidity ^0.8.0;
import "../Strings.sol";
/**
* @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
*
* These functions can be used to verify that a message was signed by the holder
* of the private keys of a given address.
*/
library ECDSA {
enum RecoverError {
NoError,
InvalidSignature,
InvalidSignatureLength,
InvalidSignatureS,
InvalidSignatureV // Deprecated in v4.8
}
function _throwError(RecoverError error) private pure {
if (error == RecoverError.NoError) {
return; // no error: do nothing
} else if (error == RecoverError.InvalidSignature) {
revert("ECDSA: invalid signature");
} else if (error == RecoverError.InvalidSignatureLength) {
revert("ECDSA: invalid signature length");
} else if (error == RecoverError.InvalidSignatureS) {
revert("ECDSA: invalid signature 's' value");
}
}
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature` or error string. This address can then be used for verification purposes.
*
* The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {toEthSignedMessageHash} on it.
*
* Documentation for signature generation:
* - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
* - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
*
* _Available since v4.3._
*/
function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) {
if (signature.length == 65) {
bytes32 r;
bytes32 s;
uint8 v;
// ecrecover takes the signature parameters, and the only way to get them
// currently is to use assembly.
/// @solidity memory-safe-assembly
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
return tryRecover(hash, v, r, s);
} else {
return (address(0), RecoverError.InvalidSignatureLength);
}
}
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature`. This address can then be used for verification purposes.
*
* The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {toEthSignedMessageHash} on it.
*/
function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, signature);
_throwError(error);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
*
* See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
*
* _Available since v4.3._
*/
function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError) {
bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
uint8 v = uint8((uint256(vs) >> 255) + 27);
return tryRecover(hash, v, r, s);
}
/**
* @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
*
* _Available since v4.2._
*/
function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, r, vs);
_throwError(error);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `v`,
* `r` and `s` signature fields separately.
*
* _Available since v4.3._
*/
function tryRecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address, RecoverError) {
// EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
// unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
// the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
// signatures from current libraries generate a unique signature with an s-value in the lower half order.
//
// If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
// with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
// vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
// these malleable signatures as well.
if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
return (address(0), RecoverError.InvalidSignatureS);
}
// If the signature is valid (and not malleable), return the signer address
address signer = ecrecover(hash, v, r, s);
if (signer == address(0)) {
return (address(0), RecoverError.InvalidSignature);
}
return (signer, RecoverError.NoError);
}
/**
* @dev Overload of {ECDSA-recover} that receives the `v`,
* `r` and `s` signature fields separately.
*/
function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
(address recovered, RecoverError error) = tryRecover(hash, v, r, s);
_throwError(error);
return recovered;
}
/**
* @dev Returns an Ethereum Signed Message, created from a `hash`. This
* produces hash corresponding to the one signed with the
* https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
* JSON-RPC method as part of EIP-191.
*
* See {recover}.
*/
function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 message) {
// 32 is the length in bytes of hash,
// enforced by the type signature above
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, "\x19Ethereum Signed Message:\n32")
mstore(0x1c, hash)
message := keccak256(0x00, 0x3c)
}
}
/**
* @dev Returns an Ethereum Signed Message, created from `s`. This
* produces hash corresponding to the one signed with the
* https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`]
* JSON-RPC method as part of EIP-191.
*
* See {recover}.
*/
function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) {
return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", Strings.toString(s.length), s));
}
/**
* @dev Returns an Ethereum Signed Typed Data, created from a
* `domainSeparator` and a `structHash`. This produces hash corresponding
* to the one signed with the
* https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`]
* JSON-RPC method as part of EIP-712.
*
* See {recover}.
*/
function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 data) {
/// @solidity memory-safe-assembly
assembly {
let ptr := mload(0x40)
mstore(ptr, "\x19\x01")
mstore(add(ptr, 0x02), domainSeparator)
mstore(add(ptr, 0x22), structHash)
data := keccak256(ptr, 0x42)
}
}
/**
* @dev Returns an Ethereum Signed Data with intended validator, created from a
* `validator` and `data` according to the version 0 of EIP-191.
*
* See {recover}.
*/
function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) {
return keccak256(abi.encodePacked("\x19\x00", validator, data));
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[EIP].
*
* 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[EIP 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);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)
pragma solidity ^0.8.0;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
enum Rounding {
Down, // Toward negative infinity
Up, // Toward infinity
Zero // Toward zero
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds up instead
* of rounding down.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
* with further edits by Uniswap Labs also under MIT license.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// 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(x, y, not(0))
prod0 := mul(x, y)
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.
// The surrounding unchecked block does not change this fact.
// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
require(denominator > prod1, "Math: mulDiv overflow");
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
// See https://cs.stackexchange.com/q/138556/92363.
// Does not overflow because the denominator cannot be zero at this stage in the function.
uint256 twos = denominator & (~denominator + 1);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
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 for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the 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.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // 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 preconditions 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 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
//
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
//
// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
//
// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1 << (log2(a) >> 1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256, rounded down, of a positive value.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 256, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol)
pragma solidity ^0.8.0;
/**
* @dev Standard signed math utilities missing in the Solidity language.
*/
library SignedMath {
/**
* @dev Returns the largest of two signed numbers.
*/
function max(int256 a, int256 b) internal pure returns (int256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two signed numbers.
*/
function min(int256 a, int256 b) internal pure returns (int256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two signed numbers without overflow.
* The result is rounded towards zero.
*/
function average(int256 a, int256 b) internal pure returns (int256) {
// Formula from the book "Hacker's Delight"
int256 x = (a & b) + ((a ^ b) >> 1);
return x + (int256(uint256(x) >> 255) & (a ^ b));
}
/**
* @dev Returns the absolute unsigned value of a signed value.
*/
function abs(int256 n) internal pure returns (uint256) {
unchecked {
// must be unchecked in order to support `n = type(int256).min`
return uint256(n >= 0 ? n : -n);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.
pragma solidity ^0.8.0;
/**
* @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.
*
* ```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 of the value in the `values` array, plus 1 because index 0
// means a value is not in the set.
mapping(bytes32 => uint256) _indexes;
}
/**
* @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._indexes[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 read and store the value's index to prevent multiple reads from the same storage slot
uint256 valueIndex = set._indexes[value];
if (valueIndex != 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 toDeleteIndex = valueIndex - 1;
uint256 lastIndex = set._values.length - 1;
if (lastIndex != toDeleteIndex) {
bytes32 lastValue = set._values[lastIndex];
// Move the last value to the index where the value to delete is
set._values[toDeleteIndex] = lastValue;
// Update the index for the moved value
set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex
}
// Delete the slot where the moved value was stored
set._values.pop();
// Delete the index for the deleted slot
delete set._indexes[value];
return true;
} else {
return false;
}
}
/**
* @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._indexes[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 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;
/// @solidity memory-safe-assembly
assembly {
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 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;
/// @solidity memory-safe-assembly
assembly {
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 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;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
}// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity >=0.7.0 <0.9.0;
import "./base/ModuleManager.sol";
import "./base/OwnerManager.sol";
import "./base/FallbackManager.sol";
import "./base/GuardManager.sol";
import "./common/EtherPaymentFallback.sol";
import "./common/Singleton.sol";
import "./common/SignatureDecoder.sol";
import "./common/SecuredTokenTransfer.sol";
import "./common/StorageAccessible.sol";
import "./interfaces/ISignatureValidator.sol";
import "./external/GnosisSafeMath.sol";
/// @title Gnosis Safe - A multisignature wallet with support for confirmations using signed messages based on ERC191.
/// @author Stefan George - <stefan@gnosis.io>
/// @author Richard Meissner - <richard@gnosis.io>
contract GnosisSafe is
EtherPaymentFallback,
Singleton,
ModuleManager,
OwnerManager,
SignatureDecoder,
SecuredTokenTransfer,
ISignatureValidatorConstants,
FallbackManager,
StorageAccessible,
GuardManager
{
using GnosisSafeMath for uint256;
string public constant VERSION = "1.3.0";
// keccak256(
// "EIP712Domain(uint256 chainId,address verifyingContract)"
// );
bytes32 private constant DOMAIN_SEPARATOR_TYPEHASH = 0x47e79534a245952e8b16893a336b85a3d9ea9fa8c573f3d803afb92a79469218;
// keccak256(
// "SafeTx(address to,uint256 value,bytes data,uint8 operation,uint256 safeTxGas,uint256 baseGas,uint256 gasPrice,address gasToken,address refundReceiver,uint256 nonce)"
// );
bytes32 private constant SAFE_TX_TYPEHASH = 0xbb8310d486368db6bd6f849402fdd73ad53d316b5a4b2644ad6efe0f941286d8;
event SafeSetup(address indexed initiator, address[] owners, uint256 threshold, address initializer, address fallbackHandler);
event ApproveHash(bytes32 indexed approvedHash, address indexed owner);
event SignMsg(bytes32 indexed msgHash);
event ExecutionFailure(bytes32 txHash, uint256 payment);
event ExecutionSuccess(bytes32 txHash, uint256 payment);
uint256 public nonce;
bytes32 private _deprecatedDomainSeparator;
// Mapping to keep track of all message hashes that have been approved by ALL REQUIRED owners
mapping(bytes32 => uint256) public signedMessages;
// Mapping to keep track of all hashes (message or transaction) that have been approved by ANY owners
mapping(address => mapping(bytes32 => uint256)) public approvedHashes;
// This constructor ensures that this contract can only be used as a master copy for Proxy contracts
constructor() {
// By setting the threshold it is not possible to call setup anymore,
// so we create a Safe with 0 owners and threshold 1.
// This is an unusable Safe, perfect for the singleton
threshold = 1;
}
/// @dev Setup function sets initial storage of contract.
/// @param _owners List of Safe owners.
/// @param _threshold Number of required confirmations for a Safe transaction.
/// @param to Contract address for optional delegate call.
/// @param data Data payload for optional delegate call.
/// @param fallbackHandler Handler for fallback calls to this contract
/// @param paymentToken Token that should be used for the payment (0 is ETH)
/// @param payment Value that should be paid
/// @param paymentReceiver Address that should receive the payment (or 0 if tx.origin)
function setup(
address[] calldata _owners,
uint256 _threshold,
address to,
bytes calldata data,
address fallbackHandler,
address paymentToken,
uint256 payment,
address payable paymentReceiver
) external {
// setupOwners checks if the Threshold is already set, therefore preventing that this method is called twice
setupOwners(_owners, _threshold);
if (fallbackHandler != address(0)) internalSetFallbackHandler(fallbackHandler);
// As setupOwners can only be called if the contract has not been initialized we don't need a check for setupModules
setupModules(to, data);
if (payment > 0) {
// To avoid running into issues with EIP-170 we reuse the handlePayment function (to avoid adjusting code of that has been verified we do not adjust the method itself)
// baseGas = 0, gasPrice = 1 and gas = payment => amount = (payment + 0) * 1 = payment
handlePayment(payment, 0, 1, paymentToken, paymentReceiver);
}
emit SafeSetup(msg.sender, _owners, _threshold, to, fallbackHandler);
}
/// @dev Allows to execute a Safe transaction confirmed by required number of owners and then pays the account that submitted the transaction.
/// Note: The fees are always transferred, even if the user transaction fails.
/// @param to Destination address of Safe transaction.
/// @param value Ether value of Safe transaction.
/// @param data Data payload of Safe transaction.
/// @param operation Operation type of Safe transaction.
/// @param safeTxGas Gas that should be used for the Safe transaction.
/// @param baseGas Gas costs that are independent of the transaction execution(e.g. base transaction fee, signature check, payment of the refund)
/// @param gasPrice Gas price that should be used for the payment calculation.
/// @param gasToken Token address (or 0 if ETH) that is used for the payment.
/// @param refundReceiver Address of receiver of gas payment (or 0 if tx.origin).
/// @param signatures Packed signature data ({bytes32 r}{bytes32 s}{uint8 v})
function execTransaction(
address to,
uint256 value,
bytes calldata data,
Enum.Operation operation,
uint256 safeTxGas,
uint256 baseGas,
uint256 gasPrice,
address gasToken,
address payable refundReceiver,
bytes memory signatures
) public payable virtual returns (bool success) {
bytes32 txHash;
// Use scope here to limit variable lifetime and prevent `stack too deep` errors
{
bytes memory txHashData =
encodeTransactionData(
// Transaction info
to,
value,
data,
operation,
safeTxGas,
// Payment info
baseGas,
gasPrice,
gasToken,
refundReceiver,
// Signature info
nonce
);
// Increase nonce and execute transaction.
nonce++;
txHash = keccak256(txHashData);
checkSignatures(txHash, txHashData, signatures);
}
address guard = getGuard();
{
if (guard != address(0)) {
Guard(guard).checkTransaction(
// Transaction info
to,
value,
data,
operation,
safeTxGas,
// Payment info
baseGas,
gasPrice,
gasToken,
refundReceiver,
// Signature info
signatures,
msg.sender
);
}
}
// We require some gas to emit the events (at least 2500) after the execution and some to perform code until the execution (500)
// We also include the 1/64 in the check that is not send along with a call to counteract potential shortings because of EIP-150
require(gasleft() >= ((safeTxGas * 64) / 63).max(safeTxGas + 2500) + 500, "GS010");
// Use scope here to limit variable lifetime and prevent `stack too deep` errors
{
uint256 gasUsed = gasleft();
// If the gasPrice is 0 we assume that nearly all available gas can be used (it is always more than safeTxGas)
// We only substract 2500 (compared to the 3000 before) to ensure that the amount passed is still higher than safeTxGas
success = execute(to, value, data, operation, gasPrice == 0 ? (gasleft() - 2500) : safeTxGas);
gasUsed = gasUsed.sub(gasleft());
// If no safeTxGas and no gasPrice was set (e.g. both are 0), then the internal tx is required to be successful
// This makes it possible to use `estimateGas` without issues, as it searches for the minimum gas where the tx doesn't revert
require(success || safeTxGas != 0 || gasPrice != 0, "GS013");
// We transfer the calculated tx costs to the tx.origin to avoid sending it to intermediate contracts that have made calls
uint256 payment = 0;
if (gasPrice > 0) {
payment = handlePayment(gasUsed, baseGas, gasPrice, gasToken, refundReceiver);
}
if (success) emit ExecutionSuccess(txHash, payment);
else emit ExecutionFailure(txHash, payment);
}
{
if (guard != address(0)) {
Guard(guard).checkAfterExecution(txHash, success);
}
}
}
function handlePayment(
uint256 gasUsed,
uint256 baseGas,
uint256 gasPrice,
address gasToken,
address payable refundReceiver
) private returns (uint256 payment) {
// solhint-disable-next-line avoid-tx-origin
address payable receiver = refundReceiver == address(0) ? payable(tx.origin) : refundReceiver;
if (gasToken == address(0)) {
// For ETH we will only adjust the gas price to not be higher than the actual used gas price
payment = gasUsed.add(baseGas).mul(gasPrice < tx.gasprice ? gasPrice : tx.gasprice);
require(receiver.send(payment), "GS011");
} else {
payment = gasUsed.add(baseGas).mul(gasPrice);
require(transferToken(gasToken, receiver, payment), "GS012");
}
}
/**
* @dev Checks whether the signature provided is valid for the provided data, hash. Will revert otherwise.
* @param dataHash Hash of the data (could be either a message hash or transaction hash)
* @param data That should be signed (this is passed to an external validator contract)
* @param signatures Signature data that should be verified. Can be ECDSA signature, contract signature (EIP-1271) or approved hash.
*/
function checkSignatures(
bytes32 dataHash,
bytes memory data,
bytes memory signatures
) public view {
// Load threshold to avoid multiple storage loads
uint256 _threshold = threshold;
// Check that a threshold is set
require(_threshold > 0, "GS001");
checkNSignatures(dataHash, data, signatures, _threshold);
}
/**
* @dev Checks whether the signature provided is valid for the provided data, hash. Will revert otherwise.
* @param dataHash Hash of the data (could be either a message hash or transaction hash)
* @param data That should be signed (this is passed to an external validator contract)
* @param signatures Signature data that should be verified. Can be ECDSA signature, contract signature (EIP-1271) or approved hash.
* @param requiredSignatures Amount of required valid signatures.
*/
function checkNSignatures(
bytes32 dataHash,
bytes memory data,
bytes memory signatures,
uint256 requiredSignatures
) public view {
// Check that the provided signature data is not too short
require(signatures.length >= requiredSignatures.mul(65), "GS020");
// There cannot be an owner with address 0.
address lastOwner = address(0);
address currentOwner;
uint8 v;
bytes32 r;
bytes32 s;
uint256 i;
for (i = 0; i < requiredSignatures; i++) {
(v, r, s) = signatureSplit(signatures, i);
if (v == 0) {
// If v is 0 then it is a contract signature
// When handling contract signatures the address of the contract is encoded into r
currentOwner = address(uint160(uint256(r)));
// Check that signature data pointer (s) is not pointing inside the static part of the signatures bytes
// This check is not completely accurate, since it is possible that more signatures than the threshold are send.
// Here we only check that the pointer is not pointing inside the part that is being processed
require(uint256(s) >= requiredSignatures.mul(65), "GS021");
// Check that signature data pointer (s) is in bounds (points to the length of data -> 32 bytes)
require(uint256(s).add(32) <= signatures.length, "GS022");
// Check if the contract signature is in bounds: start of data is s + 32 and end is start + signature length
uint256 contractSignatureLen;
// solhint-disable-next-line no-inline-assembly
assembly {
contractSignatureLen := mload(add(add(signatures, s), 0x20))
}
require(uint256(s).add(32).add(contractSignatureLen) <= signatures.length, "GS023");
// Check signature
bytes memory contractSignature;
// solhint-disable-next-line no-inline-assembly
assembly {
// The signature data for contract signatures is appended to the concatenated signatures and the offset is stored in s
contractSignature := add(add(signatures, s), 0x20)
}
require(ISignatureValidator(currentOwner).isValidSignature(data, contractSignature) == EIP1271_MAGIC_VALUE, "GS024");
} else if (v == 1) {
// If v is 1 then it is an approved hash
// When handling approved hashes the address of the approver is encoded into r
currentOwner = address(uint160(uint256(r)));
// Hashes are automatically approved by the sender of the message or when they have been pre-approved via a separate transaction
require(msg.sender == currentOwner || approvedHashes[currentOwner][dataHash] != 0, "GS025");
} else if (v > 30) {
// If v > 30 then default va (27,28) has been adjusted for eth_sign flow
// To support eth_sign and similar we adjust v and hash the messageHash with the Ethereum message prefix before applying ecrecover
currentOwner = ecrecover(keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", dataHash)), v - 4, r, s);
} else {
// Default is the ecrecover flow with the provided data hash
// Use ecrecover with the messageHash for EOA signatures
currentOwner = ecrecover(dataHash, v, r, s);
}
require(currentOwner > lastOwner && owners[currentOwner] != address(0) && currentOwner != SENTINEL_OWNERS, "GS026");
lastOwner = currentOwner;
}
}
/// @dev Allows to estimate a Safe transaction.
/// This method is only meant for estimation purpose, therefore the call will always revert and encode the result in the revert data.
/// Since the `estimateGas` function includes refunds, call this method to get an estimated of the costs that are deducted from the safe with `execTransaction`
/// @param to Destination address of Safe transaction.
/// @param value Ether value of Safe transaction.
/// @param data Data payload of Safe transaction.
/// @param operation Operation type of Safe transaction.
/// @return Estimate without refunds and overhead fees (base transaction and payload data gas costs).
/// @notice Deprecated in favor of common/StorageAccessible.sol and will be removed in next version.
function requiredTxGas(
address to,
uint256 value,
bytes calldata data,
Enum.Operation operation
) external returns (uint256) {
uint256 startGas = gasleft();
// We don't provide an error message here, as we use it to return the estimate
require(execute(to, value, data, operation, gasleft()));
uint256 requiredGas = startGas - gasleft();
// Convert response to string and return via error message
revert(string(abi.encodePacked(requiredGas)));
}
/**
* @dev Marks a hash as approved. This can be used to validate a hash that is used by a signature.
* @param hashToApprove The hash that should be marked as approved for signatures that are verified by this contract.
*/
function approveHash(bytes32 hashToApprove) external {
require(owners[msg.sender] != address(0), "GS030");
approvedHashes[msg.sender][hashToApprove] = 1;
emit ApproveHash(hashToApprove, msg.sender);
}
/// @dev Returns the chain id used by this contract.
function getChainId() public view returns (uint256) {
uint256 id;
// solhint-disable-next-line no-inline-assembly
assembly {
id := chainid()
}
return id;
}
function domainSeparator() public view returns (bytes32) {
return keccak256(abi.encode(DOMAIN_SEPARATOR_TYPEHASH, getChainId(), this));
}
/// @dev Returns the bytes that are hashed to be signed by owners.
/// @param to Destination address.
/// @param value Ether value.
/// @param data Data payload.
/// @param operation Operation type.
/// @param safeTxGas Gas that should be used for the safe transaction.
/// @param baseGas Gas costs for that are independent of the transaction execution(e.g. base transaction fee, signature check, payment of the refund)
/// @param gasPrice Maximum gas price that should be used for this transaction.
/// @param gasToken Token address (or 0 if ETH) that is used for the payment.
/// @param refundReceiver Address of receiver of gas payment (or 0 if tx.origin).
/// @param _nonce Transaction nonce.
/// @return Transaction hash bytes.
function encodeTransactionData(
address to,
uint256 value,
bytes calldata data,
Enum.Operation operation,
uint256 safeTxGas,
uint256 baseGas,
uint256 gasPrice,
address gasToken,
address refundReceiver,
uint256 _nonce
) public view returns (bytes memory) {
bytes32 safeTxHash =
keccak256(
abi.encode(
SAFE_TX_TYPEHASH,
to,
value,
keccak256(data),
operation,
safeTxGas,
baseGas,
gasPrice,
gasToken,
refundReceiver,
_nonce
)
);
return abi.encodePacked(bytes1(0x19), bytes1(0x01), domainSeparator(), safeTxHash);
}
/// @dev Returns hash to be signed by owners.
/// @param to Destination address.
/// @param value Ether value.
/// @param data Data payload.
/// @param operation Operation type.
/// @param safeTxGas Fas that should be used for the safe transaction.
/// @param baseGas Gas costs for data used to trigger the safe transaction.
/// @param gasPrice Maximum gas price that should be used for this transaction.
/// @param gasToken Token address (or 0 if ETH) that is used for the payment.
/// @param refundReceiver Address of receiver of gas payment (or 0 if tx.origin).
/// @param _nonce Transaction nonce.
/// @return Transaction hash.
function getTransactionHash(
address to,
uint256 value,
bytes calldata data,
Enum.Operation operation,
uint256 safeTxGas,
uint256 baseGas,
uint256 gasPrice,
address gasToken,
address refundReceiver,
uint256 _nonce
) public view returns (bytes32) {
return keccak256(encodeTransactionData(to, value, data, operation, safeTxGas, baseGas, gasPrice, gasToken, refundReceiver, _nonce));
}
}// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity >=0.7.0 <0.9.0;
import "../common/Enum.sol";
/// @title Executor - A contract that can execute transactions
/// @author Richard Meissner - <richard@gnosis.pm>
contract Executor {
function execute(
address to,
uint256 value,
bytes memory data,
Enum.Operation operation,
uint256 txGas
) internal returns (bool success) {
if (operation == Enum.Operation.DelegateCall) {
// solhint-disable-next-line no-inline-assembly
assembly {
success := delegatecall(txGas, to, add(data, 0x20), mload(data), 0, 0)
}
} else {
// solhint-disable-next-line no-inline-assembly
assembly {
success := call(txGas, to, value, add(data, 0x20), mload(data), 0, 0)
}
}
}
}// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity >=0.7.0 <0.9.0;
import "../common/SelfAuthorized.sol";
/// @title Fallback Manager - A contract that manages fallback calls made to this contract
/// @author Richard Meissner - <richard@gnosis.pm>
contract FallbackManager is SelfAuthorized {
event ChangedFallbackHandler(address handler);
// keccak256("fallback_manager.handler.address")
bytes32 internal constant FALLBACK_HANDLER_STORAGE_SLOT = 0x6c9a6c4a39284e37ed1cf53d337577d14212a4870fb976a4366c693b939918d5;
function internalSetFallbackHandler(address handler) internal {
bytes32 slot = FALLBACK_HANDLER_STORAGE_SLOT;
// solhint-disable-next-line no-inline-assembly
assembly {
sstore(slot, handler)
}
}
/// @dev Allows to add a contract to handle fallback calls.
/// Only fallback calls without value and with data will be forwarded.
/// This can only be done via a Safe transaction.
/// @param handler contract to handle fallback calls.
function setFallbackHandler(address handler) public authorized {
internalSetFallbackHandler(handler);
emit ChangedFallbackHandler(handler);
}
// solhint-disable-next-line payable-fallback,no-complex-fallback
fallback() external {
bytes32 slot = FALLBACK_HANDLER_STORAGE_SLOT;
// solhint-disable-next-line no-inline-assembly
assembly {
let handler := sload(slot)
if iszero(handler) {
return(0, 0)
}
calldatacopy(0, 0, calldatasize())
// The msg.sender address is shifted to the left by 12 bytes to remove the padding
// Then the address without padding is stored right after the calldata
mstore(calldatasize(), shl(96, caller()))
// Add 20 bytes for the address appended add the end
let success := call(gas(), handler, 0, 0, add(calldatasize(), 20), 0, 0)
returndatacopy(0, 0, returndatasize())
if iszero(success) {
revert(0, returndatasize())
}
return(0, returndatasize())
}
}
}// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity >=0.7.0 <0.9.0;
import "../common/Enum.sol";
import "../common/SelfAuthorized.sol";
import "../interfaces/IERC165.sol";
interface Guard is IERC165 {
function checkTransaction(
address to,
uint256 value,
bytes memory data,
Enum.Operation operation,
uint256 safeTxGas,
uint256 baseGas,
uint256 gasPrice,
address gasToken,
address payable refundReceiver,
bytes memory signatures,
address msgSender
) external;
function checkAfterExecution(bytes32 txHash, bool success) external;
}
abstract contract BaseGuard is Guard {
function supportsInterface(bytes4 interfaceId) external view virtual override returns (bool) {
return
interfaceId == type(Guard).interfaceId || // 0xe6d7a83a
interfaceId == type(IERC165).interfaceId; // 0x01ffc9a7
}
}
/// @title Fallback Manager - A contract that manages fallback calls made to this contract
/// @author Richard Meissner - <richard@gnosis.pm>
contract GuardManager is SelfAuthorized {
event ChangedGuard(address guard);
// keccak256("guard_manager.guard.address")
bytes32 internal constant GUARD_STORAGE_SLOT = 0x4a204f620c8c5ccdca3fd54d003badd85ba500436a431f0cbda4f558c93c34c8;
/// @dev Set a guard that checks transactions before execution
/// @param guard The address of the guard to be used or the 0 address to disable the guard
function setGuard(address guard) external authorized {
if (guard != address(0)) {
require(Guard(guard).supportsInterface(type(Guard).interfaceId), "GS300");
}
bytes32 slot = GUARD_STORAGE_SLOT;
// solhint-disable-next-line no-inline-assembly
assembly {
sstore(slot, guard)
}
emit ChangedGuard(guard);
}
function getGuard() internal view returns (address guard) {
bytes32 slot = GUARD_STORAGE_SLOT;
// solhint-disable-next-line no-inline-assembly
assembly {
guard := sload(slot)
}
}
}// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity >=0.7.0 <0.9.0;
import "../common/Enum.sol";
import "../common/SelfAuthorized.sol";
import "./Executor.sol";
/// @title Module Manager - A contract that manages modules that can execute transactions via this contract
/// @author Stefan George - <stefan@gnosis.pm>
/// @author Richard Meissner - <richard@gnosis.pm>
contract ModuleManager is SelfAuthorized, Executor {
event EnabledModule(address module);
event DisabledModule(address module);
event ExecutionFromModuleSuccess(address indexed module);
event ExecutionFromModuleFailure(address indexed module);
address internal constant SENTINEL_MODULES = address(0x1);
mapping(address => address) internal modules;
function setupModules(address to, bytes memory data) internal {
require(modules[SENTINEL_MODULES] == address(0), "GS100");
modules[SENTINEL_MODULES] = SENTINEL_MODULES;
if (to != address(0))
// Setup has to complete successfully or transaction fails.
require(execute(to, 0, data, Enum.Operation.DelegateCall, gasleft()), "GS000");
}
/// @dev Allows to add a module to the whitelist.
/// This can only be done via a Safe transaction.
/// @notice Enables the module `module` for the Safe.
/// @param module Module to be whitelisted.
function enableModule(address module) public authorized {
// Module address cannot be null or sentinel.
require(module != address(0) && module != SENTINEL_MODULES, "GS101");
// Module cannot be added twice.
require(modules[module] == address(0), "GS102");
modules[module] = modules[SENTINEL_MODULES];
modules[SENTINEL_MODULES] = module;
emit EnabledModule(module);
}
/// @dev Allows to remove a module from the whitelist.
/// This can only be done via a Safe transaction.
/// @notice Disables the module `module` for the Safe.
/// @param prevModule Module that pointed to the module to be removed in the linked list
/// @param module Module to be removed.
function disableModule(address prevModule, address module) public authorized {
// Validate module address and check that it corresponds to module index.
require(module != address(0) && module != SENTINEL_MODULES, "GS101");
require(modules[prevModule] == module, "GS103");
modules[prevModule] = modules[module];
modules[module] = address(0);
emit DisabledModule(module);
}
/// @dev Allows a Module to execute a Safe transaction without any further confirmations.
/// @param to Destination address of module transaction.
/// @param value Ether value of module transaction.
/// @param data Data payload of module transaction.
/// @param operation Operation type of module transaction.
function execTransactionFromModule(
address to,
uint256 value,
bytes memory data,
Enum.Operation operation
) public virtual returns (bool success) {
// Only whitelisted modules are allowed.
require(msg.sender != SENTINEL_MODULES && modules[msg.sender] != address(0), "GS104");
// Execute transaction without further confirmations.
success = execute(to, value, data, operation, gasleft());
if (success) emit ExecutionFromModuleSuccess(msg.sender);
else emit ExecutionFromModuleFailure(msg.sender);
}
/// @dev Allows a Module to execute a Safe transaction without any further confirmations and return data
/// @param to Destination address of module transaction.
/// @param value Ether value of module transaction.
/// @param data Data payload of module transaction.
/// @param operation Operation type of module transaction.
function execTransactionFromModuleReturnData(
address to,
uint256 value,
bytes memory data,
Enum.Operation operation
) public returns (bool success, bytes memory returnData) {
success = execTransactionFromModule(to, value, data, operation);
// solhint-disable-next-line no-inline-assembly
assembly {
// Load free memory location
let ptr := mload(0x40)
// We allocate memory for the return data by setting the free memory location to
// current free memory location + data size + 32 bytes for data size value
mstore(0x40, add(ptr, add(returndatasize(), 0x20)))
// Store the size
mstore(ptr, returndatasize())
// Store the data
returndatacopy(add(ptr, 0x20), 0, returndatasize())
// Point the return data to the correct memory location
returnData := ptr
}
}
/// @dev Returns if an module is enabled
/// @return True if the module is enabled
function isModuleEnabled(address module) public view returns (bool) {
return SENTINEL_MODULES != module && modules[module] != address(0);
}
/// @dev Returns array of modules.
/// @param start Start of the page.
/// @param pageSize Maximum number of modules that should be returned.
/// @return array Array of modules.
/// @return next Start of the next page.
function getModulesPaginated(address start, uint256 pageSize) external view returns (address[] memory array, address next) {
// Init array with max page size
array = new address[](pageSize);
// Populate return array
uint256 moduleCount = 0;
address currentModule = modules[start];
while (currentModule != address(0x0) && currentModule != SENTINEL_MODULES && moduleCount < pageSize) {
array[moduleCount] = currentModule;
currentModule = modules[currentModule];
moduleCount++;
}
next = currentModule;
// Set correct size of returned array
// solhint-disable-next-line no-inline-assembly
assembly {
mstore(array, moduleCount)
}
}
}// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity >=0.7.0 <0.9.0;
import "../common/SelfAuthorized.sol";
/// @title OwnerManager - Manages a set of owners and a threshold to perform actions.
/// @author Stefan George - <stefan@gnosis.pm>
/// @author Richard Meissner - <richard@gnosis.pm>
contract OwnerManager is SelfAuthorized {
event AddedOwner(address owner);
event RemovedOwner(address owner);
event ChangedThreshold(uint256 threshold);
address internal constant SENTINEL_OWNERS = address(0x1);
mapping(address => address) internal owners;
uint256 internal ownerCount;
uint256 internal threshold;
/// @dev Setup function sets initial storage of contract.
/// @param _owners List of Safe owners.
/// @param _threshold Number of required confirmations for a Safe transaction.
function setupOwners(address[] memory _owners, uint256 _threshold) internal {
// Threshold can only be 0 at initialization.
// Check ensures that setup function can only be called once.
require(threshold == 0, "GS200");
// Validate that threshold is smaller than number of added owners.
require(_threshold <= _owners.length, "GS201");
// There has to be at least one Safe owner.
require(_threshold >= 1, "GS202");
// Initializing Safe owners.
address currentOwner = SENTINEL_OWNERS;
for (uint256 i = 0; i < _owners.length; i++) {
// Owner address cannot be null.
address owner = _owners[i];
require(owner != address(0) && owner != SENTINEL_OWNERS && owner != address(this) && currentOwner != owner, "GS203");
// No duplicate owners allowed.
require(owners[owner] == address(0), "GS204");
owners[currentOwner] = owner;
currentOwner = owner;
}
owners[currentOwner] = SENTINEL_OWNERS;
ownerCount = _owners.length;
threshold = _threshold;
}
/// @dev Allows to add a new owner to the Safe and update the threshold at the same time.
/// This can only be done via a Safe transaction.
/// @notice Adds the owner `owner` to the Safe and updates the threshold to `_threshold`.
/// @param owner New owner address.
/// @param _threshold New threshold.
function addOwnerWithThreshold(address owner, uint256 _threshold) public authorized {
// Owner address cannot be null, the sentinel or the Safe itself.
require(owner != address(0) && owner != SENTINEL_OWNERS && owner != address(this), "GS203");
// No duplicate owners allowed.
require(owners[owner] == address(0), "GS204");
owners[owner] = owners[SENTINEL_OWNERS];
owners[SENTINEL_OWNERS] = owner;
ownerCount++;
emit AddedOwner(owner);
// Change threshold if threshold was changed.
if (threshold != _threshold) changeThreshold(_threshold);
}
/// @dev Allows to remove an owner from the Safe and update the threshold at the same time.
/// This can only be done via a Safe transaction.
/// @notice Removes the owner `owner` from the Safe and updates the threshold to `_threshold`.
/// @param prevOwner Owner that pointed to the owner to be removed in the linked list
/// @param owner Owner address to be removed.
/// @param _threshold New threshold.
function removeOwner(
address prevOwner,
address owner,
uint256 _threshold
) public authorized {
// Only allow to remove an owner, if threshold can still be reached.
require(ownerCount - 1 >= _threshold, "GS201");
// Validate owner address and check that it corresponds to owner index.
require(owner != address(0) && owner != SENTINEL_OWNERS, "GS203");
require(owners[prevOwner] == owner, "GS205");
owners[prevOwner] = owners[owner];
owners[owner] = address(0);
ownerCount--;
emit RemovedOwner(owner);
// Change threshold if threshold was changed.
if (threshold != _threshold) changeThreshold(_threshold);
}
/// @dev Allows to swap/replace an owner from the Safe with another address.
/// This can only be done via a Safe transaction.
/// @notice Replaces the owner `oldOwner` in the Safe with `newOwner`.
/// @param prevOwner Owner that pointed to the owner to be replaced in the linked list
/// @param oldOwner Owner address to be replaced.
/// @param newOwner New owner address.
function swapOwner(
address prevOwner,
address oldOwner,
address newOwner
) public authorized {
// Owner address cannot be null, the sentinel or the Safe itself.
require(newOwner != address(0) && newOwner != SENTINEL_OWNERS && newOwner != address(this), "GS203");
// No duplicate owners allowed.
require(owners[newOwner] == address(0), "GS204");
// Validate oldOwner address and check that it corresponds to owner index.
require(oldOwner != address(0) && oldOwner != SENTINEL_OWNERS, "GS203");
require(owners[prevOwner] == oldOwner, "GS205");
owners[newOwner] = owners[oldOwner];
owners[prevOwner] = newOwner;
owners[oldOwner] = address(0);
emit RemovedOwner(oldOwner);
emit AddedOwner(newOwner);
}
/// @dev Allows to update the number of required confirmations by Safe owners.
/// This can only be done via a Safe transaction.
/// @notice Changes the threshold of the Safe to `_threshold`.
/// @param _threshold New threshold.
function changeThreshold(uint256 _threshold) public authorized {
// Validate that threshold is smaller than number of owners.
require(_threshold <= ownerCount, "GS201");
// There has to be at least one Safe owner.
require(_threshold >= 1, "GS202");
threshold = _threshold;
emit ChangedThreshold(threshold);
}
function getThreshold() public view returns (uint256) {
return threshold;
}
function isOwner(address owner) public view returns (bool) {
return owner != SENTINEL_OWNERS && owners[owner] != address(0);
}
/// @dev Returns array of owners.
/// @return Array of Safe owners.
function getOwners() public view returns (address[] memory) {
address[] memory array = new address[](ownerCount);
// populate return array
uint256 index = 0;
address currentOwner = owners[SENTINEL_OWNERS];
while (currentOwner != SENTINEL_OWNERS) {
array[index] = currentOwner;
currentOwner = owners[currentOwner];
index++;
}
return array;
}
}// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity >=0.7.0 <0.9.0;
/// @title Enum - Collection of enums
/// @author Richard Meissner - <richard@gnosis.pm>
contract Enum {
enum Operation {Call, DelegateCall}
}// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity >=0.7.0 <0.9.0;
/// @title EtherPaymentFallback - A contract that has a fallback to accept ether payments
/// @author Richard Meissner - <richard@gnosis.pm>
contract EtherPaymentFallback {
event SafeReceived(address indexed sender, uint256 value);
/// @dev Fallback function accepts Ether transactions.
receive() external payable {
emit SafeReceived(msg.sender, msg.value);
}
}// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity >=0.7.0 <0.9.0;
/// @title SecuredTokenTransfer - Secure token transfer
/// @author Richard Meissner - <richard@gnosis.pm>
contract SecuredTokenTransfer {
/// @dev Transfers a token and returns if it was a success
/// @param token Token that should be transferred
/// @param receiver Receiver to whom the token should be transferred
/// @param amount The amount of tokens that should be transferred
function transferToken(
address token,
address receiver,
uint256 amount
) internal returns (bool transferred) {
// 0xa9059cbb - keccack("transfer(address,uint256)")
bytes memory data = abi.encodeWithSelector(0xa9059cbb, receiver, amount);
// solhint-disable-next-line no-inline-assembly
assembly {
// We write the return value to scratch space.
// See https://docs.soliditylang.org/en/v0.7.6/internals/layout_in_memory.html#layout-in-memory
let success := call(sub(gas(), 10000), token, 0, add(data, 0x20), mload(data), 0, 0x20)
switch returndatasize()
case 0 {
transferred := success
}
case 0x20 {
transferred := iszero(or(iszero(success), iszero(mload(0))))
}
default {
transferred := 0
}
}
}
}// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity >=0.7.0 <0.9.0;
/// @title SelfAuthorized - authorizes current contract to perform actions
/// @author Richard Meissner - <richard@gnosis.pm>
contract SelfAuthorized {
function requireSelfCall() private view {
require(msg.sender == address(this), "GS031");
}
modifier authorized() {
// This is a function call as it minimized the bytecode size
requireSelfCall();
_;
}
}// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity >=0.7.0 <0.9.0;
/// @title SignatureDecoder - Decodes signatures that a encoded as bytes
/// @author Richard Meissner - <richard@gnosis.pm>
contract SignatureDecoder {
/// @dev divides bytes signature into `uint8 v, bytes32 r, bytes32 s`.
/// @notice Make sure to perform a bounds check for @param pos, to avoid out of bounds access on @param signatures
/// @param pos which signature to read. A prior bounds check of this parameter should be performed, to avoid out of bounds access
/// @param signatures concatenated rsv signatures
function signatureSplit(bytes memory signatures, uint256 pos)
internal
pure
returns (
uint8 v,
bytes32 r,
bytes32 s
)
{
// The signature format is a compact form of:
// {bytes32 r}{bytes32 s}{uint8 v}
// Compact means, uint8 is not padded to 32 bytes.
// solhint-disable-next-line no-inline-assembly
assembly {
let signaturePos := mul(0x41, pos)
r := mload(add(signatures, add(signaturePos, 0x20)))
s := mload(add(signatures, add(signaturePos, 0x40)))
// Here we are loading the last 32 bytes, including 31 bytes
// of 's'. There is no 'mload8' to do this.
//
// 'byte' is not working due to the Solidity parser, so lets
// use the second best option, 'and'
v := and(mload(add(signatures, add(signaturePos, 0x41))), 0xff)
}
}
}// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity >=0.7.0 <0.9.0;
/// @title Singleton - Base for singleton contracts (should always be first super contract)
/// This contract is tightly coupled to our proxy contract (see `proxies/GnosisSafeProxy.sol`)
/// @author Richard Meissner - <richard@gnosis.io>
contract Singleton {
// singleton always needs to be first declared variable, to ensure that it is at the same location as in the Proxy contract.
// It should also always be ensured that the address is stored alone (uses a full word)
address private singleton;
}// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity >=0.7.0 <0.9.0;
/// @title StorageAccessible - generic base contract that allows callers to access all internal storage.
/// @notice See https://github.com/gnosis/util-contracts/blob/bb5fe5fb5df6d8400998094fb1b32a178a47c3a1/contracts/StorageAccessible.sol
contract StorageAccessible {
/**
* @dev Reads `length` bytes of storage in the currents contract
* @param offset - the offset in the current contract's storage in words to start reading from
* @param length - the number of words (32 bytes) of data to read
* @return the bytes that were read.
*/
function getStorageAt(uint256 offset, uint256 length) public view returns (bytes memory) {
bytes memory result = new bytes(length * 32);
for (uint256 index = 0; index < length; index++) {
// solhint-disable-next-line no-inline-assembly
assembly {
let word := sload(add(offset, index))
mstore(add(add(result, 0x20), mul(index, 0x20)), word)
}
}
return result;
}
/**
* @dev Performs a delegatecall on a targetContract in the context of self.
* Internally reverts execution to avoid side effects (making it static).
*
* This method reverts with data equal to `abi.encode(bool(success), bytes(response))`.
* Specifically, the `returndata` after a call to this method will be:
* `success:bool || response.length:uint256 || response:bytes`.
*
* @param targetContract Address of the contract containing the code to execute.
* @param calldataPayload Calldata that should be sent to the target contract (encoded method name and arguments).
*/
function simulateAndRevert(address targetContract, bytes memory calldataPayload) external {
// solhint-disable-next-line no-inline-assembly
assembly {
let success := delegatecall(gas(), targetContract, add(calldataPayload, 0x20), mload(calldataPayload), 0, 0)
mstore(0x00, success)
mstore(0x20, returndatasize())
returndatacopy(0x40, 0, returndatasize())
revert(0, add(returndatasize(), 0x40))
}
}
}// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity >=0.7.0 <0.9.0;
/**
* @title GnosisSafeMath
* @dev Math operations with safety checks that revert on error
* Renamed from SafeMath to GnosisSafeMath to avoid conflicts
* TODO: remove once open zeppelin update to solc 0.5.0
*/
library GnosisSafeMath {
/**
* @dev Multiplies two numbers, reverts on overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b);
return c;
}
/**
* @dev Subtracts two numbers, reverts on overflow (i.e. if subtrahend is greater than minuend).
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(b <= a);
uint256 c = a - b;
return c;
}
/**
* @dev Adds two numbers, reverts on overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a);
return c;
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a >= b ? a : b;
}
}// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity >=0.7.0 <0.9.0;
/// @notice More details at https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/introspection/IERC165.sol
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[EIP 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);
}// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity >=0.7.0 <0.9.0;
contract ISignatureValidatorConstants {
// bytes4(keccak256("isValidSignature(bytes,bytes)")
bytes4 internal constant EIP1271_MAGIC_VALUE = 0x20c13b0b;
}
abstract contract ISignatureValidator is ISignatureValidatorConstants {
/**
* @dev Should return whether the signature provided is valid for the provided data
* @param _data Arbitrary length data signed on the behalf of address(this)
* @param _signature Signature byte array associated with _data
*
* MUST return the bytes4 magic value 0x20c13b0b when function passes.
* MUST NOT modify state (using STATICCALL for solc < 0.5, view modifier for solc > 0.5)
* MUST allow external calls
*/
function isValidSignature(bytes memory _data, bytes memory _signature) public view virtual returns (bytes4);
}// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity >=0.7.0 <0.9.0;
/// @title IProxy - Helper interface to access masterCopy of the Proxy on-chain
/// @author Richard Meissner - <richard@gnosis.io>
interface IProxy {
function masterCopy() external view returns (address);
}
/// @title GnosisSafeProxy - Generic proxy contract allows to execute all transactions applying the code of a master contract.
/// @author Stefan George - <stefan@gnosis.io>
/// @author Richard Meissner - <richard@gnosis.io>
contract GnosisSafeProxy {
// singleton always needs to be first declared variable, to ensure that it is at the same location in the contracts to which calls are delegated.
// To reduce deployment costs this variable is internal and needs to be retrieved via `getStorageAt`
address internal singleton;
/// @dev Constructor function sets address of singleton contract.
/// @param _singleton Singleton address.
constructor(address _singleton) {
require(_singleton != address(0), "Invalid singleton address provided");
singleton = _singleton;
}
/// @dev Fallback function forwards all transactions and returns all received return data.
fallback() external payable {
// solhint-disable-next-line no-inline-assembly
assembly {
let _singleton := and(sload(0), 0xffffffffffffffffffffffffffffffffffffffff)
// 0xa619486e == keccak("masterCopy()"). The value is right padded to 32-bytes with 0s
if eq(calldataload(0), 0xa619486e00000000000000000000000000000000000000000000000000000000) {
mstore(0, _singleton)
return(0, 0x20)
}
calldatacopy(0, 0, calldatasize())
let success := delegatecall(gas(), _singleton, 0, calldatasize(), 0, 0)
returndatacopy(0, 0, returndatasize())
if eq(success, 0) {
revert(0, returndatasize())
}
return(0, returndatasize())
}
}
}// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity >=0.7.0 <0.9.0;
import "./GnosisSafeProxy.sol";
import "./IProxyCreationCallback.sol";
/// @title Proxy Factory - Allows to create new proxy contact and execute a message call to the new proxy within one transaction.
/// @author Stefan George - <stefan@gnosis.pm>
contract GnosisSafeProxyFactory {
event ProxyCreation(GnosisSafeProxy proxy, address singleton);
/// @dev Allows to create new proxy contact and execute a message call to the new proxy within one transaction.
/// @param singleton Address of singleton contract.
/// @param data Payload for message call sent to new proxy contract.
function createProxy(address singleton, bytes memory data) public returns (GnosisSafeProxy proxy) {
proxy = new GnosisSafeProxy(singleton);
if (data.length > 0)
// solhint-disable-next-line no-inline-assembly
assembly {
if eq(call(gas(), proxy, 0, add(data, 0x20), mload(data), 0, 0), 0) {
revert(0, 0)
}
}
emit ProxyCreation(proxy, singleton);
}
/// @dev Allows to retrieve the runtime code of a deployed Proxy. This can be used to check that the expected Proxy was deployed.
function proxyRuntimeCode() public pure returns (bytes memory) {
return type(GnosisSafeProxy).runtimeCode;
}
/// @dev Allows to retrieve the creation code used for the Proxy deployment. With this it is easily possible to calculate predicted address.
function proxyCreationCode() public pure returns (bytes memory) {
return type(GnosisSafeProxy).creationCode;
}
/// @dev Allows to create new proxy contact using CREATE2 but it doesn't run the initializer.
/// This method is only meant as an utility to be called from other methods
/// @param _singleton Address of singleton contract.
/// @param initializer Payload for message call sent to new proxy contract.
/// @param saltNonce Nonce that will be used to generate the salt to calculate the address of the new proxy contract.
function deployProxyWithNonce(
address _singleton,
bytes memory initializer,
uint256 saltNonce
) internal returns (GnosisSafeProxy proxy) {
// If the initializer changes the proxy address should change too. Hashing the initializer data is cheaper than just concatinating it
bytes32 salt = keccak256(abi.encodePacked(keccak256(initializer), saltNonce));
bytes memory deploymentData = abi.encodePacked(type(GnosisSafeProxy).creationCode, uint256(uint160(_singleton)));
// solhint-disable-next-line no-inline-assembly
assembly {
proxy := create2(0x0, add(0x20, deploymentData), mload(deploymentData), salt)
}
require(address(proxy) != address(0), "Create2 call failed");
}
/// @dev Allows to create new proxy contact and execute a message call to the new proxy within one transaction.
/// @param _singleton Address of singleton contract.
/// @param initializer Payload for message call sent to new proxy contract.
/// @param saltNonce Nonce that will be used to generate the salt to calculate the address of the new proxy contract.
function createProxyWithNonce(
address _singleton,
bytes memory initializer,
uint256 saltNonce
) public returns (GnosisSafeProxy proxy) {
proxy = deployProxyWithNonce(_singleton, initializer, saltNonce);
if (initializer.length > 0)
// solhint-disable-next-line no-inline-assembly
assembly {
if eq(call(gas(), proxy, 0, add(initializer, 0x20), mload(initializer), 0, 0), 0) {
revert(0, 0)
}
}
emit ProxyCreation(proxy, _singleton);
}
/// @dev Allows to create new proxy contact, execute a message call to the new proxy and call a specified callback within one transaction
/// @param _singleton Address of singleton contract.
/// @param initializer Payload for message call sent to new proxy contract.
/// @param saltNonce Nonce that will be used to generate the salt to calculate the address of the new proxy contract.
/// @param callback Callback that will be invoked after the new proxy contract has been successfully deployed and initialized.
function createProxyWithCallback(
address _singleton,
bytes memory initializer,
uint256 saltNonce,
IProxyCreationCallback callback
) public returns (GnosisSafeProxy proxy) {
uint256 saltNonceWithCallback = uint256(keccak256(abi.encodePacked(saltNonce, callback)));
proxy = createProxyWithNonce(_singleton, initializer, saltNonceWithCallback);
if (address(callback) != address(0)) callback.proxyCreated(proxy, _singleton, initializer, saltNonce);
}
/// @dev Allows to get the address for a new proxy contact created via `createProxyWithNonce`
/// This method is only meant for address calculation purpose when you use an initializer that would revert,
/// therefore the response is returned with a revert. When calling this method set `from` to the address of the proxy factory.
/// @param _singleton Address of singleton contract.
/// @param initializer Payload for message call sent to new proxy contract.
/// @param saltNonce Nonce that will be used to generate the salt to calculate the address of the new proxy contract.
function calculateCreateProxyWithNonceAddress(
address _singleton,
bytes calldata initializer,
uint256 saltNonce
) external returns (GnosisSafeProxy proxy) {
proxy = deployProxyWithNonce(_singleton, initializer, saltNonce);
revert(string(abi.encodePacked(proxy)));
}
}// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity >=0.7.0 <0.9.0;
import "./GnosisSafeProxy.sol";
interface IProxyCreationCallback {
function proxyCreated(
GnosisSafeProxy proxy,
address _singleton,
bytes calldata initializer,
uint256 saltNonce
) external;
}// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.19;
import { IAccessControl } from "@openzeppelin/contracts/access/IAccessControl.sol";
/**
* @title IACLManager
* @author Unlockd
* @notice Defines the basic interface for the ACL Manager
*/
interface IACLManager is IAccessControl {
function UNLOCK_PROTOCOL() external view returns (address);
/**
* @notice Returns the identifier of the UtokenAdmin role
* @return The id of the UtokenAdmin role
*/
function UTOKEN_ADMIN() external view returns (bytes32);
/**
* @notice Returns the identifier of the Protocol Admin role
* @return The id of the Protocol Admin role
*/
function PROTOCOL_ADMIN() external view returns (bytes32);
/**
* @notice Returns the identifier of the PriceUpdater role
* @return The id of the PriceUpdater role
*/
function PRICE_UPDATER() external view returns (bytes32);
/**
* @notice Returns the identifier of the EmergencyAdmin role
* @return The id of the EmergencyAdmin role
*/
function AUCTION_ADMIN() external view returns (bytes32);
/**
* @notice Returns the identifier of the EmergencyAdmin role
* @return The id of the EmergencyAdmin role
*/
function EMERGENCY_ADMIN() external view returns (bytes32);
/**
* @notice Returns the identifier of the Governance Admin role
* @return The id of the PriceUpdater role
*/
function GOVERNANCE_ADMIN() external view returns (bytes32);
/**
* @notice Set the address of the protocol
* @dev Is the main address of the protocol.Only can be updated by the ADMIN.
* @param protocol address of the protocol
*/
function setProtocol(address protocol) external;
/**
* @notice Returns true if the address is the protocol, false otherwise
* @param protocol The address to check
* @return True if the given address is the protocol, false otherwise
*/
function isProtocol(address protocol) external view returns (bool);
/**
* @notice Set the role as admin of a specific role.
* @dev By default the admin role for all roles is `DEFAULT_ADMIN_ROLE`.
* @param role The role to be managed by the admin role
* @param adminRole The admin role
*/
function setRoleAdmin(bytes32 role, bytes32 adminRole) external;
// UTOKEN
/**
* @notice Adds a new admin as Utoken Admin
* @param admin The address of the new admin
*/
function addUTokenAdmin(address admin) external;
/**
* @notice Removes an admin as Utoken Admin
* @param admin The address of the admin to remove
*/
function removeUTokenAdmin(address admin) external;
/**
* @notice Returns true if the address is Utoken Admin, false otherwise
* @param admin The address to check
* @return True if the given address is Utoken Admin, false otherwise
*/
function isUTokenAdmin(address admin) external view returns (bool);
// PROTOCOL
/**
* @notice Adds a new admin as Protocol Admin
* @param admin The address of the new admin
*/
function addProtocolAdmin(address admin) external;
/**
* @notice Removes an admin as Protocol Admin
* @param admin The address of the admin to remove
*/
function removeProtocolAdmin(address admin) external;
/**
* @notice Returns true if the address is Protocol Admin, false otherwise
* @param admin The address to check
* @return True if the given address is Protocol Admin, false otherwise
*/
function isProtocolAdmin(address admin) external view returns (bool);
// EMERGENCY
/**
* @notice Adds a new admin as EmergencyAdmin
* @param admin The address of the new admin
*/
function addEmergencyAdmin(address admin) external;
/**
* @notice Removes an admin as EmergencyAdmin
* @param admin The address of the admin to remove
*/
function removeEmergencyAdmin(address admin) external;
/**
* @notice Returns true if the address is EmergencyAdmin, false otherwise
* @param admin The address to check
* @return True if the given address is EmergencyAdmin, false otherwise
*/
function isEmergencyAdmin(address admin) external view returns (bool);
// PRICE UPDATER
/**
* @notice Adds a new admin as PriceUpdater
* @param admin The address of the new PriceUpdater
*/
function addPriceUpdater(address admin) external;
/**
* @notice Removes an admin as PriceUpdater
* @param admin The address of the PriceUpdater to remove
*/
function removePriceUpdater(address admin) external;
/**
* @notice Returns true if the address is PriceUpdater, false otherwise
* @param admin The address to check
* @return True if the given address is PriceUpdater, false otherwise
*/
function isPriceUpdater(address admin) external view returns (bool);
// Governance admin
/**
* @notice Adds a new admin as Govnernance admin
* @param admin The address of the new Governance admin
*/
function addGovernanceAdmin(address admin) external;
/**
* @notice Removes an admin as Governance Admin
* @param admin The address of the Governance Admin to remove
*/
function removeGovernanceAdmin(address admin) external;
/**
* @notice Returns true if the address is Governance Admin, false otherwise
* @param admin The address to check
* @return True if the given address is Governance Admin, false otherwise
*/
function isGovernanceAdmin(address admin) external view returns (bool);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.19;
interface IAllowedControllers {
event Collections(address indexed collections, bool isAllowed);
event DelegationController(address indexed delegationController, bool isAllowed);
function isAllowedDelegationController(address _controller) external view returns (bool);
function isAllowedCollection(address _collection) external view returns (bool);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.19;
interface ICryptoPunks {
struct Offer {
bool isForSale;
uint256 punkIndex;
address seller;
uint256 minValue; // in ether
address onlySellTo; // specify to sell only to a specific person
}
// A record of punks that are offered for sale at a specific minimum value, and perhaps to a specific person
function punksOfferedForSale(uint256 punkIndex) external view returns (Offer memory);
function balanceOf(address owner) external view returns (uint256);
function punkIndexToAddress(uint256 punkIndex) external view returns (address);
function transferPunk(address to, uint256 punkIndex) external;
function enterBidForPunk(uint punkIndex) external payable;
function acceptBidForPunk(uint punkIndex, uint minPrice) external;
function offerPunkForSale(uint punkIndex, uint minSalePriceInWei) external;
function offerPunkForSaleToAddress(uint256 punkIndex, uint256 minSalePriceInWei, address toAddress) external;
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.19;
interface IDelegationOwner {
////////////////////////////////////////////////////////////////////////////////
// Events
////////////////////////////////////////////////////////////////////////////////
event SetDelegationController(address indexed delegationController, bool allowed);
event SetLockController(address indexed lockController, bool allowed);
event NewDelegation(
address indexed asset,
uint256 indexed assetId,
uint256 from,
uint256 to,
address indexed delegatee,
address delegationController
);
event EndDelegation(address indexed asset, uint256 indexed assetId, address delegationController);
event ChangeOwner(address indexed asset, uint256 indexed assetId, address newOwner);
event DelegatedSignature(
uint256 from,
uint256 to,
address indexed delegatee,
address[] assets,
uint256[] assetIds,
address delegationController
);
event EndDelegatedSignature(address[] assets, uint256[] assetIds, address delegationController);
event ClaimedAsset(address indexed asset, uint256 indexed assetId, address indexed receiver);
event TransferredAsset(address indexed asset, uint256 indexed assetId, address indexed receiver);
////////////////////////////////////////////////////////////////////////////////
// Structs
////////////////////////////////////////////////////////////////////////////////
struct SignatureAssets {
address[] assets;
uint256[] ids;
}
/**
* @notice Delegation information, it is used for assets and signatures.
*
* @param controller - The delegation controller address that created the delegations.
* @param delegatee - The delegatee address.
* @param from - The date (seconds timestamp) when the delegation starts.
* @param to - The date (seconds timestamp) when the delegation ends.
*/
struct Delegation {
address controller;
address delegatee;
uint256 from;
uint256 to;
}
////////////////////////////////////////////////////////////////////////////////
// Functions
////////////////////////////////////////////////////////////////////////////////
function delegate(address _asset, uint256 _assetId, address _delegatee, uint256 _duration) external;
function endDelegate(address _asset, uint256 _assetId) external;
function delegateSignature(
address[] calldata _assets,
uint256[] calldata _assetIds,
address _delegatee,
uint256 _duration
) external;
// Lock Controller Functions
function claimAsset(address _asset, uint256 _assetId, address _receiver) external;
// Delegatee Functions
function execTransaction(
address _asset,
uint256 _assetId,
address _to,
uint256 _value,
bytes calldata _data,
uint256 _safeTxGas,
uint256 _baseGas,
uint256 _gasPrice,
address _gasToken,
address payable _refundReceiver
) external returns (bool success);
function isAllowedFunction(address _asset, address _contract, bytes4 _selector) external view returns (bool);
function isAssetDelegated(address _asset, uint256 _assetId) external view returns (bool);
function isSignatureDelegated() external view returns (bool);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.19;
interface IDelegationWalletRegistry {
struct Wallet {
address wallet;
address owner;
address guard;
address guardOwner;
address delegationOwner;
address protocolOwner;
}
function setFactory(address _delegationWalletFactory) external;
function setWallet(
address _wallet,
address _owner,
address _guard,
address _guardOwner,
address _delegationGuard,
address _protocolOwner
) external;
function getWallet(address _wallet) external view returns (Wallet memory);
function getOwnerWalletAddresses(address _owner) external view returns (address[] memory);
function getOwnerWalletAt(address _owner, uint256 _index) external view returns (Wallet memory);
}import { IERC721 } from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
interface IERC721Extended is IERC721 {
function burn(uint256 tokenId) external;
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.19;
import { Enum } from "@gnosis.pm/safe-contracts/contracts/common/Enum.sol";
interface IGnosisSafe {
function nonce() external view returns (uint256);
/// @dev Allows a Module to execute a Safe transaction without any further confirmations.
/// @param to Destination address of module transaction.
/// @param value Ether value of module transaction.
/// @param data Data payload of module transaction.
/// @param operation Operation type of module transaction.
function execTransactionFromModule(
address to,
uint256 value,
bytes calldata data,
Enum.Operation operation
) external returns (bool success);
function execTransactionFromModuleReturnData(
address to,
uint256 value,
bytes memory data,
Enum.Operation operation
) external returns (bool success, bytes memory returnData);
/// @dev Setup function sets initial storage of contract.
/// @param _owners List of Safe owners.
/// @param _threshold Number of required confirmations for a Safe transaction.
/// @param to Contract address for optional delegate call.
/// @param data Data payload for optional delegate call.
/// @param fallbackHandler Handler for fallback calls to this contract
/// @param paymentToken Token that should be used for the payment (0 is ETH)
/// @param payment Value that should be paid
/// @param paymentReceiver Address that should receive the payment (or 0 if tx.origin)
function setup(
address[] calldata _owners,
uint256 _threshold,
address to,
bytes calldata data,
address fallbackHandler,
address paymentToken,
uint256 payment,
address payable paymentReceiver
) external;
/// @dev Allows to execute a Safe transaction confirmed by required number of owners and then pays the account that
/// submitted the transaction.
/// Note: The fees are always transferred, even if the user transaction fails.
/// @param to Destination address of Safe transaction.
/// @param value Ether value of Safe transaction.
/// @param data Data payload of Safe transaction.
/// @param operation Operation type of Safe transaction.
/// @param safeTxGas Gas that should be used for the Safe transaction.
/// @param baseGas Gas costs that are independent of the transaction execution(e.g. base transaction fee, signature
/// check, payment of the refund)
/// @param gasPrice Gas price that should be used for the payment calculation.
/// @param gasToken Token address (or 0 if ETH) that is used for the payment.
/// @param refundReceiver Address of receiver of gas payment (or 0 if tx.origin).
/// @param signatures Packed signature data ({bytes32 r}{bytes32 s}{uint8 v})
function execTransaction(
address to,
uint256 value,
bytes calldata data,
Enum.Operation operation,
uint256 safeTxGas,
uint256 baseGas,
uint256 gasPrice,
address gasToken,
address payable refundReceiver,
bytes memory signatures
) external payable returns (bool success);
function getTransactionHash(
address to,
uint256 value,
bytes calldata data,
Enum.Operation operation,
uint256 safeTxGas,
uint256 baseGas,
uint256 gasPrice,
address gasToken,
address refundReceiver,
uint256 _nonce
) external view returns (bytes32);
function isModuleEnabled(address module) external view returns (bool);
function isValidSignature(bytes calldata _data, bytes calldata _signature) external view returns (bytes4);
function isValidSignature(bytes32 _dataHash, bytes calldata _signature) external view returns (bytes4);
function signedMessages(bytes32 message) external view returns (uint256);
function getMessageHash(bytes memory message) external view returns (bytes32);
function domainSeparator() external view returns (bytes32);
function enableModule(address module) external;
function setFallbackHandler(address handler) external;
function setGuard(address guard) external;
function getStorageAt(uint256 offset, uint256 length) external view returns (bytes memory);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.19;
interface IProtocolOwner {
////////////////////////////////////////////////////////////////////////////////
// Events
////////////////////////////////////////////////////////////////////////////////
event SetLockController(address indexed lockController, bool allowed);
event ChangeOwner(address indexed asset, uint256 indexed assetId, address newOwner);
event LockedAsset(
address indexed asset,
uint256 indexed assetId,
uint256 claimDate,
address indexed lockController
);
event UnlockedAsset(address indexed asset, uint256 indexed assetId, address indexed lockController);
event ClaimedAsset(address indexed asset, uint256 indexed assetId, address indexed receiver);
event TransferredAsset(address indexed asset, uint256 indexed assetId, address indexed receiver);
event SetLoanId(bytes32 index, bytes32 loanId);
event SetBatchLoanId(bytes32[] indexed assets, bytes32 indexed loanId);
////////////////////////////////////////////////////////////////////////////////
// Functions
////////////////////////////////////////////////////////////////////////////////
function approveSale(
address _collection,
uint256 _tokenId,
address _underlyingAsset,
uint256 _amount,
address _marketApproval,
bytes32 _loanId
) external;
// Delegatee Functions
function execTransaction(
address _to,
uint256 _value,
bytes calldata _data,
uint256 _safeTxGas,
uint256 _baseGas,
uint256 _gasPrice,
address _gasToken,
address payable _refundReceiver
) external returns (bool success);
function delegateOneExecution(address to, bool value) external;
function isDelegatedExecution(address to) external view returns (bool);
function isAssetLocked(bytes32 _id) external view returns (bool);
function batchSetLoanId(bytes32[] calldata _assets, bytes32 _loanId) external;
function batchSetToZeroLoanId(bytes32[] calldata _assets) external;
function changeOwner(address _asset, uint256 _id, address _newOwner) external;
function getLoanId(bytes32 _assetId) external view returns (bytes32);
function setLoanId(bytes32 _assetId, bytes32 _loanId) external;
function safeSetLoanId(address _asset, uint256 _id, bytes32 _loanId) external;
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.19;
import { Guard } from "@gnosis.pm/safe-contracts/contracts/base/GuardManager.sol";
import { Enum } from "@gnosis.pm/safe-contracts/contracts/common/Enum.sol";
import { IGnosisSafe } from "../../interfaces/IGnosisSafe.sol";
import { IACLManager } from "../../interfaces/IACLManager.sol";
import { AssetLogic } from "../logic/AssetLogic.sol";
import { SafeLogic } from "../logic/SafeLogic.sol";
import { Errors } from "../helpers/Errors.sol";
import { ISignatureValidator } from "@gnosis.pm/safe-contracts/contracts/interfaces/ISignatureValidator.sol";
contract BaseSafeOwner is ISignatureValidator {
/**
* @notice Execution protect
*/
bool internal isExecuting;
bytes32 internal currentTxHash;
/**
* @notice Address of cryptoPunks
*/
address public immutable cryptoPunks;
/**
* @notice The ACLManager address implementatiuon.
*/
address public immutable aclManager;
/**
* @notice Safe wallet address.
*/
address public safe;
/**
* @notice The owner of the DelegationWallet, it is set only once upon initialization. Since this contract works
* in tandem with DelegationGuard which do not allow to change the Safe owners, this owner can't change neither.
*/
address public owner;
constructor(address _cryptoPunks, address _aclManager) {
cryptoPunks = _cryptoPunks;
aclManager = _aclManager;
}
////////////////////////////////////////////////////////////////////////////////
// Modifiers
////////////////////////////////////////////////////////////////////////////////
/**
* @notice This modifier indicates that only the Delegation Controller can execute a given function.
*/
modifier onlyOwner() {
if (owner != msg.sender) revert Errors.DelegationOwner__onlyOwner();
_;
}
modifier onlyProtocol() {
if (IACLManager(aclManager).isProtocol(msg.sender) == false) revert Errors.Caller_notProtocol();
_;
}
modifier onlyGov() {
if (IACLManager(aclManager).isGovernanceAdmin(msg.sender) == false) revert Errors.Caller_notGovernanceAdmin();
_;
}
////////////////////////////////////////////////////////////////////////////////
// Public
////////////////////////////////////////////////////////////////////////////////
/**
* @notice Returns the hash of the NFTs.
*/
function assetId(address _asset, uint256 _id) external pure returns (bytes32) {
return AssetLogic.assetId(_asset, _id);
}
////////////////////////////////////////////////////////////////////////////////
// Private
////////////////////////////////////////////////////////////////////////////////
/**
* @notice Transfer an asset owned by the safe.
*/
function _transferAsset(address _asset, uint256 _id, address _receiver) internal returns (bool) {
bytes memory payload = _asset == cryptoPunks
? SafeLogic._transferPunksPayload(_asset, _id, _receiver, safe)
: SafeLogic._transferERC721Payload(_asset, _id, _receiver, safe);
isExecuting = true;
currentTxHash = IGnosisSafe(payable(safe)).getTransactionHash(
_asset,
0,
payload,
Enum.Operation.Call,
0,
0,
0,
address(0),
payable(0),
IGnosisSafe(payable(safe)).nonce()
);
// https://docs.gnosis-safe.io/contracts/signatures#contract-signature-eip-1271
bytes memory signature = abi.encodePacked(
abi.encode(address(this)), // r
abi.encode(uint256(65)), // s
bytes1(0), // v
abi.encode(currentTxHash.length),
currentTxHash
);
bool success = IGnosisSafe(safe).execTransaction(
_asset,
0,
payload,
Enum.Operation.Call,
0,
0,
0,
address(0),
payable(0),
signature
);
isExecuting = false;
currentTxHash = bytes32(0);
return success;
}
/**
* @notice Approve an asset owned by the safe wallet.
*/
function _approveAsset(address _asset, uint256 _id, address _receiver) internal returns (bool) {
bytes memory payload = _asset == cryptoPunks
? SafeLogic._approvePunksPayload(_asset, _id, _receiver, safe)
: SafeLogic._approveERC721Payload(_asset, _id, _receiver, safe);
isExecuting = true;
currentTxHash = IGnosisSafe(payable(safe)).getTransactionHash(
_asset,
0,
payload,
Enum.Operation.Call,
0,
0,
0,
address(0),
payable(0),
IGnosisSafe(payable(safe)).nonce()
);
// https://docs.gnosis-safe.io/contracts/signatures#contract-signature-eip-1271
bytes memory signature = abi.encodePacked(
abi.encode(address(this)), // r
abi.encode(uint256(65)), // s
bytes1(0), // v
abi.encode(currentTxHash.length),
currentTxHash
);
bool success = IGnosisSafe(safe).execTransaction(
_asset,
0,
payload,
Enum.Operation.Call,
0,
0,
0,
address(0),
payable(0),
signature
);
isExecuting = false;
currentTxHash = bytes32(0);
return success;
}
/**
* @notice Approve an asset owned by the safe wallet.
*/
function _approveERC20(address _asset, uint256 _amount, address _receiver) internal returns (bool) {
bytes memory payload = SafeLogic._approveERC20Payload(_asset, _amount, _receiver, safe);
isExecuting = true;
currentTxHash = IGnosisSafe(payable(safe)).getTransactionHash(
_asset,
0,
payload,
Enum.Operation.Call,
0,
0,
0,
address(0),
payable(0),
IGnosisSafe(payable(safe)).nonce()
);
// https://docs.gnosis-safe.io/contracts/signatures#contract-signature-eip-1271
bytes memory signature = abi.encodePacked(
abi.encode(address(this)), // r
abi.encode(uint256(65)), // s
bytes1(0), // v
abi.encode(currentTxHash.length),
currentTxHash
);
bool success = IGnosisSafe(safe).execTransaction(
_asset,
0,
payload,
Enum.Operation.Call,
0,
0,
0,
address(0),
payable(0),
signature
);
isExecuting = false;
currentTxHash = bytes32(0);
return success;
}
/**
* @notice Validates that the signer is the current signature delegatee, or a valid transaction executed by a asset
* delegatee.
* @param _data Hash of the data signed on the behalf of address(msg.sender) which must be encoded as bytes,
* necessary to make it compatible how the Safe calls the function.
* @param _signature Signature byte array associated with _dataHash
*/
function isValidSignature(
bytes memory _data,
bytes memory _signature
) public view virtual override returns (bytes4) {
_data;
bytes32 txHash = abi.decode(_signature, (bytes32));
if (txHash != currentTxHash) revert Errors.DelegationOwner__isValidSignature_invalidExecSig();
return EIP1271_MAGIC_VALUE;
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.19;
import { Enum } from "@gnosis.pm/safe-contracts/contracts/common/Enum.sol";
import { IERC165 } from "@gnosis.pm/safe-contracts/contracts/interfaces/IERC165.sol";
import { Guard } from "@gnosis.pm/safe-contracts/contracts/base/GuardManager.sol";
import { OwnerManager, GuardManager } from "@gnosis.pm/safe-contracts/contracts/GnosisSafe.sol";
import { Initializable } from "@openzeppelin/contracts/proxy/utils/Initializable.sol";
import { DelegationOwner } from "../owners/DelegationOwner.sol";
import { ICryptoPunks } from "../../interfaces/ICryptoPunks.sol";
import { IGnosisSafe } from "../../interfaces/IGnosisSafe.sol";
import { IERC721Extended, IERC721 } from "../../interfaces/IERC721Extended.sol";
import { AssetLogic } from "../logic/AssetLogic.sol";
import { Errors } from "../helpers/Errors.sol";
/**
* @title TransactionGuard
* @author Unlockd
* @dev This contract protects the Wallet. Is attached to DelegationOwner but manager by ProtocolOwner and DelegationOwner
* - Prevents delegated o locked assets from being transferred.
* - Prevents the approval of delegated or locked assets.
* - Prevents all approveForAll.
* - Prevents change in the configuration of the DelegationWallet.
* - Prevents the remotion a this contract as the Guard of the DelegationWallet.
*/
contract TransactionGuard is Guard, Initializable {
bytes4 internal constant ERC721_SAFE_TRANSFER_FROM =
bytes4(keccak256(bytes("safeTransferFrom(address,address,uint256)")));
bytes4 internal constant ERC721_SAFE_TRANSFER_FROM_DATA =
bytes4(keccak256(bytes("safeTransferFrom(address,address,uint256,bytes)")));
address public immutable cryptoPunks;
// Owners Manager
address public delegationOwner;
address public protocolOwner;
// Mapping managers
mapping(address => bool) public managerOwners;
// any time an asset is locked or delegated, the address is saved here so any address present in this mapping
// will be checked when a transfer is performed
// nft address => true/false
mapping(address => bool) internal checkAsset;
// keccak256(address, nft id) => true/false
mapping(bytes32 => bool) internal lockedAssets; // this locks assets until unlock
// keccak256(address, nft id) => delegation expiry
mapping(bytes32 => uint256) internal delegatedAssets; // this locks assets base on a date
/**
* @notice This modifier indicates that only the DelegationOwner contract can execute a given function.
*/
modifier onlyManagersOwner() {
if (managerOwners[msg.sender] == false) revert Errors.TransactionGuard__onlyManagersOwner();
_;
}
constructor(address _cryptoPunks) {
cryptoPunks = _cryptoPunks;
_disableInitializers();
}
function initialize(address _delegationOwner, address _protocolOwner) public initializer {
if (_delegationOwner == address(0)) revert Errors.TransactionGuard__initialize_invalidDelegationOwner();
if (_protocolOwner == address(0)) revert Errors.TransactionGuard__initialize_invalidProtocolOwner();
delegationOwner = _delegationOwner;
protocolOwner = _protocolOwner;
// Set manager Owners
managerOwners[_delegationOwner] = true;
managerOwners[_protocolOwner] = true;
}
// solhint-disable-next-line payable-fallback
fallback() external {
// We don't revert on fallback to avoid issues in case of a Safe upgrade
// E.g. The expected check method might change and then the Safe would be locked.
}
/**
* @notice This function is called from Safe.execTransaction to perform checks before executing the transaction.
*/
function checkTransaction(
address _to,
uint256,
bytes calldata _data,
Enum.Operation operation,
uint256,
uint256,
uint256,
address,
address payable,
bytes memory,
address _msgSender
) external view override {
// malicious owner can execute transactions to smart contracts by using Enum.Operation.DelegateCall in order to
// manipulate the Safe's internal storage and even transfer locked NFTs out of the delegation wallet
if (operation == Enum.Operation.DelegateCall) revert Errors.TransactionGuard__checkTransaction_noDelegateCall();
// Transactions coming from DelegationOwner are already blocked/allowed there.
// The delegatee calls execTransaction on DelegationOwner, it checks allowance then calls execTransaction
// from Safe.
if (managerOwners[_msgSender] == false) {
_checkLocked(_to, _data);
}
// Ignore this check when is Protocol OWNER
if (_msgSender != protocolOwner) {
// approveForAll should be never allowed since can't be checked before delegating or locking
_checkApproveForAll(_data);
_checkConfiguration(_to, _data);
}
}
/**
* @notice This function is called from Safe.execTransaction to perform checks after executing the transaction.
*/
function checkAfterExecution(bytes32 txHash, bool success) external view override {}
/**
* @notice Returns if an asset is locked.
* @param _id - The asset id.
*/
function isLocked(bytes32 _id) external view returns (bool) {
return _isLocked(_id);
}
/**
* @notice Returns asset delegation expiry.
* @param _id - The asset id.
*/
function getExpiry(bytes32 _id) external view returns (uint256) {
return delegatedAssets[_id];
}
/**
* @notice Sets the delegation expiry for a group of assets.
* @param _assets - The assets addresses.
* @param _ids - The assets ids.
* @param _expiry - The delegation expiry.
*/
function setDelegationExpiries(
address[] calldata _assets,
uint256[] calldata _ids,
uint256 _expiry
) external onlyManagersOwner {
uint256 length = _assets.length;
for (uint256 j; j < length; ) {
delegatedAssets[AssetLogic.assetId(_assets[j], _ids[j])] = _expiry;
unchecked {
++j;
}
}
}
/**
* @notice Sets the delegation expiry for an assets.
* @param _asset - The asset address.
* @param _id - The asset id.
* @param _expiry - The delegation expiry.
*/
function setDelegationExpiry(address _asset, uint256 _id, uint256 _expiry) external onlyManagersOwner {
delegatedAssets[AssetLogic.assetId(_asset, _id)] = _expiry;
}
/**
* @notice Sets an asset as locked.
* @param _id - The asset id.
*/
function lockAsset(bytes32 _id) external onlyManagersOwner {
if (!_isLocked(_id)) {
lockedAssets[_id] = true;
}
}
/**
* @notice Sets an asset as unlocked.
* @param _id - The asset id.
*/
function unlockAsset(bytes32 _id) external onlyManagersOwner {
if (_isLocked(_id)) {
lockedAssets[_id] = false;
}
}
/**
* @notice This function prevents the execution of some functions when the destination contract is a locked or
* delegated asset.
* @param _to - Destination address of Safe transaction.
* @param _data - Data payload of Safe transaction.
*/
function _checkLocked(address _to, bytes calldata _data) internal view {
bytes4 selector = AssetLogic.getSelector(_data);
if (_to == cryptoPunks) {
if (selector == ICryptoPunks.transferPunk.selector) {
(, uint256 assetId) = abi.decode(_data[4:], (address, uint256));
if (_isDelegating(_to, assetId) || _isLocked(AssetLogic.assetId(_to, assetId)))
revert Errors.TransactionGuard__checkLocked_noTransfer();
} else if (selector == ICryptoPunks.offerPunkForSale.selector) {
(uint256 assetId, ) = abi.decode(_data[4:], (uint256, uint256));
if (_isDelegating(_to, assetId) || _isLocked(AssetLogic.assetId(_to, assetId)))
revert Errors.TransactionGuard__checkLocked_noApproval();
} else if (selector == ICryptoPunks.offerPunkForSaleToAddress.selector) {
(uint256 assetId, , ) = abi.decode(_data[4:], (uint256, uint256, address));
if (_isDelegating(_to, assetId) || _isLocked(AssetLogic.assetId(_to, assetId)))
revert Errors.TransactionGuard__checkLocked_noApproval();
} else if (selector == ICryptoPunks.acceptBidForPunk.selector) {
(uint256 assetId, ) = abi.decode(_data[4:], (uint256, uint256));
if (_isDelegating(_to, assetId) || _isLocked(AssetLogic.assetId(_to, assetId)))
revert Errors.TransactionGuard__checkLocked_noTransfer();
}
} else {
if (_isTransfer(selector)) {
(, , uint256 assetId) = abi.decode(_data[4:], (address, address, uint256));
if (_isDelegating(_to, assetId) || _isLocked(AssetLogic.assetId(_to, assetId)))
revert Errors.TransactionGuard__checkLocked_noTransfer();
} else if (_isApproving(selector)) {
(, uint256 assetId) = abi.decode(_data[4:], (address, uint256));
if (_isDelegating(_to, assetId) || _isLocked(AssetLogic.assetId(_to, assetId)))
revert Errors.TransactionGuard__checkLocked_noApproval();
} else if (_isBurning(selector)) {
uint256 assetId = abi.decode(_data[4:], (uint256));
if (_isDelegating(_to, assetId) || _isLocked(AssetLogic.assetId(_to, assetId))) {
revert Errors.TransactionGuard__checkLocked_noBurn();
}
}
}
}
/**
* @notice This function prevents the execution of the IERC721 setApprovalForAll function.
* @param _data - Data payload of Safe transaction.
*/
function _checkApproveForAll(bytes calldata _data) internal pure {
bytes4 selector = AssetLogic.getSelector(_data);
if (selector == IERC721.setApprovalForAll.selector)
revert Errors.TransactionGuard__checkApproveForAll_noApprovalForAll();
}
/**
* @notice This function prevents changes in the configuration of the Safe.
* @param _to - Destination address of Safe transaction.
* @param _data - Data payload of Safe transaction.
*/
function _checkConfiguration(address _to, bytes calldata _data) internal view {
bytes4 selector = AssetLogic.getSelector(_data);
if (_to == DelegationOwner(delegationOwner).safe()) {
// ownership change not allowed while this guard is configured
if (
selector == OwnerManager.addOwnerWithThreshold.selector ||
selector == OwnerManager.removeOwner.selector ||
selector == OwnerManager.swapOwner.selector ||
selector == OwnerManager.changeThreshold.selector
) revert Errors.TransactionGuard__checkConfiguration_ownershipChangesNotAllowed();
// Guard change not allowed
if (selector == GuardManager.setGuard.selector)
revert Errors.TransactionGuard__checkConfiguration_guardChangeNotAllowed();
// Adding modules is not allowed
if (selector == IGnosisSafe.enableModule.selector)
revert Errors.TransactionGuard__checkConfiguration_enableModuleNotAllowed();
// Changing FallbackHandler is not allowed
if (selector == IGnosisSafe.setFallbackHandler.selector)
revert Errors.TransactionGuard__checkConfiguration_setFallbackHandlerNotAllowed();
}
}
/**
* @notice Checks if an asset is delegated.
* @param _asset - The asset addresses.
* @param _id - The asset id.
*/
function _isDelegating(address _asset, uint256 _id) internal view returns (bool) {
return (block.timestamp <= delegatedAssets[AssetLogic.assetId(_asset, _id)]);
}
/**
* @notice Checks if an asset is locked.
* @param _id - Asset id
*/
function _isLocked(bytes32 _id) internal view returns (bool) {
return lockedAssets[_id];
}
/**
* @notice Checks if `_selector` is one of the ERC721 possible transfers.
* @param _selector - Function selector.
*/
function _isTransfer(bytes4 _selector) internal pure returns (bool) {
return (_selector == IERC721.transferFrom.selector ||
_selector == ERC721_SAFE_TRANSFER_FROM ||
_selector == ERC721_SAFE_TRANSFER_FROM_DATA);
}
function _isBurning(bytes4 _selector) internal pure returns (bool) {
return (_selector == IERC721Extended.burn.selector);
}
function _isApproving(bytes4 _selector) internal pure returns (bool) {
return (_selector == IERC721.approve.selector);
}
function supportsInterface(
bytes4 _interfaceId
)
external
view
virtual
returns (
// override
bool
)
{
return
_interfaceId == type(Guard).interfaceId || // 0xe6d7a83a
_interfaceId == type(IERC165).interfaceId; // 0x01ffc9a7
}
}// SPDX-License-Identifier: agpl-3.0
pragma solidity 0.8.19;
library Errors {
// ========== General ===========
error Caller_notProtocol();
error Caller_notGovernanceAdmin();
error Caller_notAdmin();
// ========== Delegation Recipes ===========
error DelegationRecipes__add_arityMismatch();
error DelegationRecipes__remove_arityMismatch();
// ========== Delegation Owner ===========
error DelegationGuard__initialize_invalidGuardBeacon();
error DelegationGuard__initialize_invalidRecipes();
error DelegationGuard__initialize_invalidSafe();
error DelegationGuard__initialize_invalidOwner();
error DelegationGuard__initialize_aclManager();
error DelegationOwner__assetNotLocked();
error DelegationOwner__wrongLoanId();
error DelegationOwner__assetAlreadyLocked();
error DelegationOwner__collectionNotAllowed();
error DelegationOwner__onlyOwner();
error DelegationOwner__onlyDelegationController();
error DelegationOwner__onlyLockController();
error DelegationOwner__onlyDelegationCreator();
error DelegationOwner__onlySignatureDelegationCreator();
error DelegationOwner__onlyLockCreator();
error DelegationOwner__delegate_currentlyDelegated();
error DelegationOwner__delegate_invalidDelegatee();
error DelegationOwner__delegate_invalidDuration();
error DelegationOwner__delegate_assetLocked();
error DelegationOwner__deposit_collectionNotAllowed();
error DelegationOwner__delegateSignature_invalidArity();
error DelegationOwner__delegateSignature_currentlyDelegated();
error DelegationOwner__delegateSignature_invalidDelegatee();
error DelegationOwner__delegateSignature_invalidDuration();
error DelegationOwner__endDelegateSignature_invalidArity();
error DelegationOwner__isValidSignature_notDelegated();
error DelegationOwner__isValidSignature_invalidSigner();
error DelegationOwner__isValidSignature_invalidExecSig();
error DelegationOwner__execTransaction_notDelegated();
error DelegationOwner__execTransaction_invalidDelegatee();
error DelegationOwner__execTransaction_notAllowedFunction();
error DelegationOwner__execTransaction_notSuccess();
error DelegationOwner__lockAsset_assetLocked();
error DelegationOwner__lockAsset_invalidClaimDate();
error DelegationOwner__changeClaimDate_invalidClaimDate();
error DelegationOwner__claimAsset_assetNotClaimable();
error DelegationOwner__claimAsset_assetLocked();
error DelegationOwner__claimAsset_notSuccess();
error DelegationOwner__changeOwner_notSuccess();
error DelegationOwner__transferAsset_assetNotOwned();
error DelegationOwner__approveAsset_assetNotOwned();
error DelegationOwner__checkOwnedAndNotApproved_assetNotOwned();
error DelegationOwner__checkOwnedAndNotApproved_assetApproved();
error DelegationOwner__checkClaimDate_assetDelegatedLonger();
error DelegationOwner__checkClaimDate_signatureDelegatedLonger();
error DelegationOwner__lockCreatorChecks_assetNotLocked();
error DelegationOwner__lockCreatorChecks_onlyLockCreator();
error DelegationOwner__delegationCreatorChecks_notDelegated();
error DelegationOwner__delegationCreatorChecks_onlyDelegationCreator();
error DelegationOwner__setDelegationController_notAllowedController();
error DelegationOwner__setLockController_notAllowedController();
error DelegationOwner__batchSetLoanId_arityMismatch();
// ========== Guard Owner ===========
error GuardOwner__initialize_aclManager();
error GuardOwner__initialize_invalidGuardBeacon();
error GuardOwner__initialize_invalidSafe();
error GuardOwner__initialize_invalidOwner();
error GuardOwner__initialize_invalidDelegationOwner();
error GuardOwner__initialize_invalidProtocolOwner();
// ========== Transaction Guard ===========
error TransactionGuard__onlyManagersOwner();
error TransactionGuard__initialize_invalidDelegationOwner();
error TransactionGuard__initialize_invalidProtocolOwner();
error TransactionGuard__checkTransaction_noDelegateCall();
error TransactionGuard__checkApproveForAll_noApprovalForAll();
error TransactionGuard__checkLocked_noTransfer();
error TransactionGuard__checkLocked_noApproval();
error TransactionGuard__checkLocked_noBurn();
error TransactionGuard__checkConfiguration_ownershipChangesNotAllowed();
error TransactionGuard__checkConfiguration_guardChangeNotAllowed();
error TransactionGuard__checkConfiguration_enableModuleNotAllowed();
error TransactionGuard__checkConfiguration_setFallbackHandlerNotAllowed();
// ========== Allowed Collection ===========
error AllowedCollections__setCollectionsAllowances_invalidAddress();
error AllowedCollections__setCollectionsAllowances_arityMismatch();
error AllowedControllers__setLockControllerAllowances_arityMismatch();
error AllowedControllers__setDelegationControllerAllowances_arityMismatch();
error AllowedControllers__setDelegationControllerAllowance_invalidAddress();
// ========== Delegation Wallet Registry ===========
error DelegationWalletRegistry__onlyFactoryOrOwner();
error DelegationWalletRegistry__setFactory_invalidAddress();
error DelegationWalletRegistry__setWallet_invalidWalletAddress();
error DelegationWalletRegistry__setWallet_invalidOwnerAddress();
error DelegationWalletRegistry__setWallet_invalidDelegationOwnerAddress();
error DelegationWalletRegistry__setWallet_invalidGuardAddress();
error DelegationWalletRegistry__setWallet_invalidProtocolOwnerAddress();
// ========== Protocol OWNER ===========
error ProtocolOwner__invalidDelegatedAddressAddress();
error ProtocolOwner__execTransaction_notSuccess();
}// SPDX-License-Identifier: agpl-3.0
pragma solidity 0.8.19;
library AssetLogic {
function assetId(address _asset, uint256 _id) internal pure returns (bytes32) {
return keccak256(abi.encodePacked(_asset, _id));
}
function getSelector(bytes memory _data) internal pure returns (bytes4 selector) {
// solhint-disable-next-line no-inline-assembly
assembly {
selector := mload(add(_data, 32))
}
}
}// SPDX-License-Identifier: agpl-3.0
pragma solidity 0.8.19;
import { IGnosisSafe } from "../../interfaces/IGnosisSafe.sol";
import { Enum } from "@gnosis.pm/safe-contracts/contracts/common/Enum.sol";
import { IERC721 } from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { ICryptoPunks } from "../../interfaces/ICryptoPunks.sol";
import { Errors } from "../helpers/Errors.sol";
library SafeLogic {
function _transferERC721Payload(
address _asset,
uint256 _id,
address _receiver,
address _safe
) internal view returns (bytes memory) {
// safe should be owner
if (IERC721(_asset).ownerOf(_id) != _safe) revert Errors.DelegationOwner__transferAsset_assetNotOwned();
return abi.encodeWithSelector(IERC721.transferFrom.selector, _safe, _receiver, _id);
}
function _transferPunksPayload(
address _asset,
uint256 _id,
address _receiver,
address _safe
) internal view returns (bytes memory) {
// safe should be owner
if (ICryptoPunks(_asset).punkIndexToAddress(_id) != _safe)
revert Errors.DelegationOwner__transferAsset_assetNotOwned();
return abi.encodeWithSelector(ICryptoPunks.transferPunk.selector, _receiver, _id);
}
// TODO: Check if this is safe
function _approvePunksPayload(
address _asset,
uint256 _id,
address _receiver,
address _safe
) internal view returns (bytes memory) {
// safe should be owner
if (ICryptoPunks(_asset).punkIndexToAddress(_id) != _safe)
revert Errors.DelegationOwner__approveAsset_assetNotOwned();
return abi.encodeWithSelector(ICryptoPunks.offerPunkForSaleToAddress.selector, _receiver, 0, _id);
}
function _approveERC721Payload(
address _asset,
uint256 _id,
address _receiver,
address _safe
) internal view returns (bytes memory) {
// safe should be owner
if (IERC721(_asset).ownerOf(_id) != _safe) revert Errors.DelegationOwner__approveAsset_assetNotOwned();
return abi.encodeWithSelector(IERC721.approve.selector, _receiver, _id);
}
function _approveERC20Payload(
address _asset,
uint256 _amount,
address _receiver,
address _safe
) internal pure returns (bytes memory) {
_asset;
_safe;
return abi.encodeWithSelector(IERC20.approve.selector, _receiver, _amount);
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.19;
import { IGnosisSafe } from "../../interfaces/IGnosisSafe.sol";
import { ICryptoPunks } from "../../interfaces/ICryptoPunks.sol";
import { IAllowedControllers } from "../../interfaces/IAllowedControllers.sol";
import { IACLManager } from "../../interfaces/IACLManager.sol";
import { DelegationRecipes } from "../recipes/DelegationRecipes.sol";
import { TransactionGuard } from "../guards/TransactionGuard.sol";
import { AssetLogic } from "../logic/AssetLogic.sol";
import { SafeLogic } from "../logic/SafeLogic.sol";
import { Errors } from "../helpers/Errors.sol";
import { IDelegationOwner } from "../../interfaces/IDelegationOwner.sol";
import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import { IERC721 } from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import { Initializable } from "@openzeppelin/contracts/proxy/utils/Initializable.sol";
import { BeaconProxy } from "@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol";
import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import { Enum } from "@gnosis.pm/safe-contracts/contracts/common/Enum.sol";
import { GnosisSafe } from "@gnosis.pm/safe-contracts/contracts/GnosisSafe.sol";
import { BaseSafeOwner } from "../base/BaseSafeOwner.sol";
/**
* @title DelegationOwner
* @author Unlockd
* @dev This contract contains the logic that enables asset/signature delegates to interact with a Gnosis Safe wallet.
* In the case of assets delegates, it will allow them to execute functions though the Safe, only those registered
* as allowed on the DelegationRecipes contract.
* In the case of signatures it validates that a signature was made by the current delegatee.
* It is also used by the delegation controller to set delegations and the lock controller to lock, unlock and claim
* assets.
*
* It should be use a proxy's implementation.
*/
contract DelegationOwner is Initializable, IDelegationOwner, BaseSafeOwner {
using EnumerableSet for EnumerableSet.Bytes32Set;
bytes32 public constant GUARD_STORAGE_SLOT = 0x4a204f620c8c5ccdca3fd54d003badd85ba500436a431f0cbda4f558c93c34c8;
/**
* @notice The DelegationRecipes address.
*/
DelegationRecipes public immutable recipes;
/**
* @notice The AllowedControllers address.
*/
IAllowedControllers public immutable allowedControllers;
/**
* @notice The delegation controller address. Allowed to execute delegation related functions.
*/
mapping(address => bool) public delegationControllers;
/**
* @notice The DelegationGuard address.
*/
TransactionGuard public guard;
/**
* @notice Stores the list of asset delegations. keccak256(address, nft id) => Delegation
*/
mapping(bytes32 => Delegation) public delegations;
/**
* @notice Stores the current signature delegation.
*/
Delegation public signatureDelegation;
/**
* @notice List of assetIds affected by last signatureDelegation. This is used for cheap checking if asset in
* included in current signature delegation.
*/
mapping(uint256 => EnumerableSet.Bytes32Set) private signatureDelegationAssetsIds;
/**
* @notice List of assets and ids affected by last signatureDelegation. This is used mainly when ending the
* signature delegation to be able to set the expiry of each asset on the guard. Using EnumerableSet.values from
* signatureDelegationAssetsIds 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.
*/
mapping(uint256 => SignatureAssets) private signatureDelegationAssets;
/**
* @notice Current signature delegation id
*/
uint256 private currentSignatureDelegationAssets;
address private protocolOwner;
////////////////////////////////////////////////////////////////////////////////
// Modifiers
////////////////////////////////////////////////////////////////////////////////
/**
* @notice This modifier indicates that only the Delegation Controller can execute a given function.
*/
modifier onlyDelegationController() {
if (!delegationControllers[msg.sender]) revert Errors.DelegationOwner__onlyDelegationController();
_;
}
modifier onlyProtocolOwner() {
if (protocolOwner != msg.sender) revert Errors.DelegationOwner__onlyDelegationController();
_;
}
/**
* @dev Disables the initializer in order to prevent implementation initialization.
*/
constructor(
address _cryptoPunks,
address _recipes,
address _allowedControllers,
address _aclManager
) BaseSafeOwner(_cryptoPunks, _aclManager) {
if (_aclManager == address(0)) revert Errors.DelegationGuard__initialize_aclManager();
recipes = DelegationRecipes(_recipes);
allowedControllers = IAllowedControllers(_allowedControllers);
_disableInitializers();
}
/**
* @notice Initializes the proxy state.
* @param _guard - The deployed guard
* @param _safe - The DelegationWallet address, the GnosisSafe.
* @param _owner - The owner of the DelegationWallet.
* @param _delegationController - The address that acts as the delegation controller.
* @param _protocolOwner - The address that acts as the delegation controller.
*/
function initialize(
address _guard,
address _safe,
address _owner,
address _delegationController,
address _protocolOwner
) public initializer {
if (_guard == address(0)) revert Errors.DelegationGuard__initialize_invalidGuardBeacon();
if (_safe == address(0)) revert Errors.DelegationGuard__initialize_invalidSafe();
if (_owner == address(0)) revert Errors.DelegationGuard__initialize_invalidOwner();
protocolOwner = _protocolOwner;
safe = _safe;
owner = _owner;
if (_delegationController != address(0)) {
_setDelegationController(_delegationController, true);
}
guard = TransactionGuard(_guard);
}
/**
* @notice Sets a delegation controller address as allowed or not.
* @param _delegationController - The new delegation controller address.
* @param _allowed - Allowance status.
*/
function setDelegationController(address _delegationController, bool _allowed) external onlyGov {
_setDelegationController(_delegationController, _allowed);
}
/**
* @notice Delegates the usage of an asset to the `_delegatee` for a `_duration` of time.
* @param _asset - The asset address.
* @param _id - The asset id.
* @param _delegatee - The delegatee address.
* @param _duration - The duration of the delegation expressed in seconds.
*/
function delegate(
address _asset,
uint256 _id,
address _delegatee,
uint256 _duration
) external onlyDelegationController {
_checkOwnedAndNotApproved(_asset, _id);
bytes32 id = AssetLogic.assetId(_asset, _id);
Delegation storage delegation = delegations[id];
if (_isDelegating(delegation)) revert Errors.DelegationOwner__delegate_currentlyDelegated();
if (_delegatee == address(0)) revert Errors.DelegationOwner__delegate_invalidDelegatee();
if (_duration == 0) revert Errors.DelegationOwner__delegate_invalidDuration();
// @dev is the asset is locked you can't delegate
if (guard.isLocked(id)) revert Errors.DelegationOwner__delegate_assetLocked();
delegation.controller = msg.sender;
delegation.delegatee = _delegatee;
uint256 from = block.timestamp;
uint256 to = block.timestamp + _duration;
delegation.from = from;
delegation.to = to;
emit NewDelegation(_asset, _id, from, to, _delegatee, msg.sender);
guard.setDelegationExpiry(_asset, _id, to);
}
/**
* @notice Ends asset usage delegation.
* @param _asset - The asset address.
* @param _id - The asset id.
*/
function endDelegate(address _asset, uint256 _id) external {
Delegation storage delegation = delegations[AssetLogic.assetId(_asset, _id)];
_delegationCreatorChecks(delegation);
delegation.to = 0;
emit EndDelegation(_asset, _id, msg.sender);
guard.setDelegationExpiry(_asset, _id, 0);
}
function forceEndDelegation(address _asset, uint256 _id) external onlyProtocolOwner {
Delegation storage delegation = delegations[AssetLogic.assetId(_asset, _id)];
delegation.to = 0;
emit EndDelegation(_asset, _id, msg.sender);
guard.setDelegationExpiry(_asset, _id, 0);
}
/**
* @notice Delegates the usage of the signature to the `_delegatee` for a `_duration` of time. Locking a group of
* assets in the wallet.
* @param _assets - The asset addresses.
* @param _ids - The asset ids.
* @param _delegatee - The delegatee address.
* @param _duration - The duration of the delegation expressed in seconds.
*/
function delegateSignature(
address[] calldata _assets,
uint256[] calldata _ids,
address _delegatee,
uint256 _duration
) external onlyDelegationController {
if (_assets.length != _ids.length) revert Errors.DelegationOwner__delegateSignature_invalidArity();
if (_isDelegating(signatureDelegation)) revert Errors.DelegationOwner__delegateSignature_currentlyDelegated();
if (_delegatee == address(0)) revert Errors.DelegationOwner__delegateSignature_invalidDelegatee();
if (_duration == 0) revert Errors.DelegationOwner__delegateSignature_invalidDuration();
uint256 delegationExpiry = block.timestamp + _duration;
currentSignatureDelegationAssets += 1;
uint256 length = _assets.length;
for (uint256 j; j < length; ) {
_checkOwnedAndNotApproved(_assets[j], _ids[j]);
bytes32 id = AssetLogic.assetId(_assets[j], _ids[j]);
// @dev is the asset is locked you can't delegate
if (guard.isLocked(id)) revert Errors.DelegationOwner__delegate_assetLocked();
signatureDelegationAssets[currentSignatureDelegationAssets].assets.push(_assets[j]);
signatureDelegationAssets[currentSignatureDelegationAssets].ids.push(_ids[j]);
signatureDelegationAssetsIds[currentSignatureDelegationAssets].add(id);
unchecked {
++j;
}
}
Delegation memory newDelegation = Delegation(msg.sender, _delegatee, block.timestamp, delegationExpiry);
signatureDelegation = newDelegation;
emit DelegatedSignature(newDelegation.from, newDelegation.to, _delegatee, _assets, _ids, msg.sender);
guard.setDelegationExpiries(_assets, _ids, delegationExpiry);
}
/**
* @notice Ends the delegation of the usage of the signature for the `_delegatee`. Unlocking a group of assets.
*/
function endDelegateSignature() external {
_delegationCreatorChecks(signatureDelegation);
signatureDelegation.to = 0;
emit EndDelegatedSignature(
signatureDelegationAssets[currentSignatureDelegationAssets].assets,
signatureDelegationAssets[currentSignatureDelegationAssets].ids,
msg.sender
);
guard.setDelegationExpiries(
signatureDelegationAssets[currentSignatureDelegationAssets].assets,
signatureDelegationAssets[currentSignatureDelegationAssets].ids,
0
);
}
/**
* @notice Execute a transaction through the GnosisSafe wallet.
* The sender should be the delegatee of the given asset and the function should be allowed for the collection.
* @param _asset - The delegated asset addresses.
* @param _id - The delegated asset ids.
* @param _to - Destination address of Safe transaction.
* @param _value - Ether value of Safe transaction.
* @param _data - Data payload of Safe transaction.
* @param _safeTxGas - Gas that should be used for the Safe transaction.
* @param _baseGas Gas costs that are independent of the transaction execution(e.g. base transaction fee, signature
* check, payment of the refund)
* @param _gasPrice - Gas price that should be used for the payment calculation.
* @param _gasToken - Token address (or 0 if ETH) that is used for the payment.
* @param _refundReceiver - Address of receiver of gas payment (or 0 if tx.origin).
*/
function execTransaction(
address _asset,
uint256 _id,
address _to,
uint256 _value,
bytes calldata _data,
uint256 _safeTxGas,
uint256 _baseGas,
uint256 _gasPrice,
address _gasToken,
address payable _refundReceiver
) external returns (bool success) {
Delegation storage delegation = delegations[AssetLogic.assetId(_asset, _id)];
if (!_isDelegating(delegation)) revert Errors.DelegationOwner__execTransaction_notDelegated();
if (delegation.delegatee != msg.sender) revert Errors.DelegationOwner__execTransaction_invalidDelegatee();
if (!isAllowedFunction(_asset, _to, AssetLogic.getSelector(_data)))
revert Errors.DelegationOwner__execTransaction_notAllowedFunction();
isExecuting = true;
currentTxHash = IGnosisSafe(payable(safe)).getTransactionHash(
// Transaction info
_to,
_value,
_data,
Enum.Operation.Call,
_safeTxGas,
// Payment info
_baseGas,
_gasPrice,
_gasToken,
_refundReceiver,
// Signature info
IGnosisSafe(payable(safe)).nonce()
);
// https://docs.gnosis-safe.io/contracts/signatures#contract-signature-eip-1271
bytes memory signature = abi.encodePacked(
abi.encode(address(this)), // r
abi.encode(uint256(65)), // s
bytes1(0), // v
abi.encode(currentTxHash.length),
currentTxHash
);
(success) = IGnosisSafe(safe).execTransaction(
_to,
_value,
_data,
Enum.Operation.Call,
_safeTxGas,
_baseGas,
_gasPrice,
_gasToken,
_refundReceiver,
signature
);
isExecuting = false;
currentTxHash = bytes32(0);
if (!success) revert Errors.DelegationOwner__execTransaction_notSuccess();
}
/**
* @notice Validates that the signer is the current signature delegatee, or a valid transaction executed by a asset
* delegatee.
* @param _data Hash of the data signed on the behalf of address(msg.sender) which must be encoded as bytes,
* necessary to make it compatible how the Safe calls the function.
* @param _signature Signature byte array associated with _dataHash
*/
function isValidSignature(bytes memory _data, bytes memory _signature) public view override returns (bytes4) {
if (!isExecuting) {
if (!_isDelegating(signatureDelegation)) revert Errors.DelegationOwner__isValidSignature_notDelegated();
// CompatibilityFallbackHandler encodes the bytes32 dataHash before calling the old version of i
// sValidSignature
bytes32 dataHash = abi.decode(_data, (bytes32));
address signer = ECDSA.recover(dataHash, _signature);
if (signatureDelegation.delegatee != signer)
revert Errors.DelegationOwner__isValidSignature_invalidSigner();
} else {
bytes32 txHash = abi.decode(_signature, (bytes32));
if (txHash != currentTxHash) revert Errors.DelegationOwner__isValidSignature_invalidExecSig();
}
return EIP1271_MAGIC_VALUE;
}
/**
* @notice Checks that a function is allowed to be executed by a delegatee of a given asset.
* @param _asset - The delegated asset addresses.
* @param _contract - The addresses of the destination contract.
* @param _selector - The selector of the destination function.
*/
function isAllowedFunction(address _asset, address _contract, bytes4 _selector) public view returns (bool) {
return recipes.isAllowedFunction(_asset, _contract, _selector);
}
/**
* @notice Sends a asset to the `receiver`.
* @param _asset - The locked asset addresses.
* @param _id - The locked asset id.
* @param _receiver - The receiving address.
*/
function claimAsset(address _asset, uint256 _id, address _receiver) external onlyOwner {
bytes32 id = AssetLogic.assetId(_asset, _id);
if (_isAssetDelegated(id)) revert Errors.DelegationOwner__claimAsset_assetNotClaimable();
if (guard.isLocked(id)) revert Errors.DelegationOwner__claimAsset_assetLocked();
bool success = _transferAsset(_asset, _id, _receiver);
if (!success) revert Errors.DelegationOwner__claimAsset_notSuccess();
emit ClaimedAsset(_asset, _id, _receiver);
}
/**
* @notice Returns if an asset is delegated or included in current signature delegation.
* @param _asset - The asset addresses.
* @param _id - The asset id.
*/
function isAssetDelegated(address _asset, uint256 _id) external view returns (bool) {
return _isAssetDelegated(AssetLogic.assetId(_asset, _id));
}
/**
* @notice Returns if the signature is delegated.
*/
function isSignatureDelegated() external view returns (bool) {
return _isDelegating(signatureDelegation);
}
//////////////////////////////////////////////
// Internal functions
//////////////////////////////////////////////
function _setDelegationController(address _delegationController, bool _allowed) internal {
if (_allowed && !allowedControllers.isAllowedDelegationController(_delegationController))
revert Errors.DelegationOwner__setDelegationController_notAllowedController();
delegationControllers[_delegationController] = _allowed;
emit SetDelegationController(_delegationController, _allowed);
}
function _isAssetDelegated(bytes32 _id) internal view returns (bool) {
Delegation storage delegation = delegations[_id];
return (_isDelegating(delegation) ||
(_isDelegating(signatureDelegation) &&
signatureDelegationAssetsIds[currentSignatureDelegationAssets].contains(_id)));
}
function _isDelegating(Delegation storage _delegation) internal view returns (bool) {
return (_delegation.from <= block.timestamp && block.timestamp <= _delegation.to);
}
function _checkOwnedAndNotApproved(address _asset, uint256 _id) internal view {
if (_asset == cryptoPunks) {
// safe should be owner
if (ICryptoPunks(_asset).punkIndexToAddress(_id) != safe)
revert Errors.DelegationOwner__checkOwnedAndNotApproved_assetNotOwned();
// asset shouldn't be approved, it won't be possible to prevent the approved address to move the asset out
// of the safe
if (ICryptoPunks(_asset).punksOfferedForSale(_id).isForSale)
revert Errors.DelegationOwner__checkOwnedAndNotApproved_assetApproved();
} else {
// safe should be owner
if (IERC721(_asset).ownerOf(_id) != safe)
revert Errors.DelegationOwner__checkOwnedAndNotApproved_assetNotOwned();
// asset shouldn't be approved, it won't be possible to prevent the approved address to move the asset out
// f the safe
if (IERC721(_asset).getApproved(_id) != address(0))
revert Errors.DelegationOwner__checkOwnedAndNotApproved_assetApproved();
}
}
function _delegationCreatorChecks(Delegation storage _delegation) internal view {
if (!_isDelegating(_delegation)) revert Errors.DelegationOwner__delegationCreatorChecks_notDelegated();
if (_delegation.controller != msg.sender)
revert Errors.DelegationOwner__delegationCreatorChecks_onlyDelegationCreator();
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.19;
import { IGnosisSafe } from "../../interfaces/IGnosisSafe.sol";
import { ICryptoPunks } from "../../interfaces/ICryptoPunks.sol";
import { IAllowedControllers } from "../../interfaces/IAllowedControllers.sol";
import { IACLManager } from "../../interfaces/IACLManager.sol";
import { DelegationRecipes } from "../recipes/DelegationRecipes.sol";
import { TransactionGuard } from "../guards/TransactionGuard.sol";
import { AssetLogic } from "../logic/AssetLogic.sol";
import { SafeLogic } from "../logic/SafeLogic.sol";
import { Errors } from "../helpers/Errors.sol";
import { IDelegationOwner } from "../../interfaces/IDelegationOwner.sol";
import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import { IERC721 } from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import { Initializable } from "@openzeppelin/contracts/proxy/utils/Initializable.sol";
import { BeaconProxy } from "@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol";
import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import { Enum } from "@gnosis.pm/safe-contracts/contracts/common/Enum.sol";
import { GnosisSafe } from "@gnosis.pm/safe-contracts/contracts/GnosisSafe.sol";
import { BaseSafeOwner } from "../base/BaseSafeOwner.sol";
contract GuardOwner is Initializable, BaseSafeOwner {
using EnumerableSet for EnumerableSet.Bytes32Set;
bytes32 public constant GUARD_STORAGE_SLOT = 0x4a204f620c8c5ccdca3fd54d003badd85ba500436a431f0cbda4f558c93c34c8;
TransactionGuard public guard;
constructor(address _cryptoPunks, address _aclManager) BaseSafeOwner(_cryptoPunks, _aclManager) {
if (_aclManager == address(0)) revert Errors.GuardOwner__initialize_aclManager();
_disableInitializers();
}
function initialize(
address _guardBeacon,
address _safe,
address _owner,
address _delegationOnwer,
address _protocolOwner
) public initializer {
if (_guardBeacon == address(0)) revert Errors.GuardOwner__initialize_invalidGuardBeacon();
if (_safe == address(0)) revert Errors.GuardOwner__initialize_invalidSafe();
if (_owner == address(0)) revert Errors.GuardOwner__initialize_invalidOwner();
if (_delegationOnwer == address(0)) revert Errors.GuardOwner__initialize_invalidDelegationOwner();
if (_protocolOwner == address(0)) revert Errors.GuardOwner__initialize_invalidProtocolOwner();
safe = _safe;
owner = _owner;
address guardProxy = address(
new BeaconProxy(
_guardBeacon,
abi.encodeWithSelector(TransactionGuard.initialize.selector, _delegationOnwer, _protocolOwner)
)
);
guard = TransactionGuard(guardProxy);
// Set up guard
_setupGuard(_safe, guard);
}
function _setupGuard(address _safe, TransactionGuard _guard) internal {
// this requires this address to be a owner of the safe already
isExecuting = true;
bytes memory payload = abi.encodeWithSelector(IGnosisSafe.setGuard.selector, _guard);
currentTxHash = IGnosisSafe(payable(_safe)).getTransactionHash(
// Transaction info
safe,
0,
payload,
Enum.Operation.Call,
0,
// Payment info
0,
0,
address(0),
payable(0),
// Signature info
IGnosisSafe(payable(_safe)).nonce()
);
// https://docs.gnosis-safe.io/contracts/signatures#contract-signature-eip-1271
bytes memory signature = abi.encodePacked(
abi.encode(address(this)), // r
abi.encode(uint256(65)), // s
bytes1(0), // v
abi.encode(currentTxHash.length),
currentTxHash
);
IGnosisSafe(_safe).execTransaction(
safe,
0,
payload,
Enum.Operation.Call,
0,
0,
0,
address(0),
payable(0),
signature
);
isExecuting = false;
currentTxHash = bytes32(0);
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.19;
import { IGnosisSafe } from "../../interfaces/IGnosisSafe.sol";
import { ICryptoPunks } from "../../interfaces/ICryptoPunks.sol";
import { IACLManager } from "../../interfaces/IACLManager.sol";
import { IProtocolOwner } from "../../interfaces/IProtocolOwner.sol";
import { TransactionGuard } from "../guards/TransactionGuard.sol";
import { DelegationOwner } from "./DelegationOwner.sol";
import { AssetLogic } from "../logic/AssetLogic.sol";
import { SafeLogic } from "../logic/SafeLogic.sol";
import { Errors } from "../helpers/Errors.sol";
import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import { IERC721 } from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import { Initializable } from "@openzeppelin/contracts/proxy/utils/Initializable.sol";
import { BeaconProxy } from "@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol";
import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import { Enum } from "@gnosis.pm/safe-contracts/contracts/common/Enum.sol";
import { GnosisSafe } from "@gnosis.pm/safe-contracts/contracts/GnosisSafe.sol";
import { ISignatureValidator } from "@gnosis.pm/safe-contracts/contracts/interfaces/ISignatureValidator.sol";
import { BaseSafeOwner } from "../base/BaseSafeOwner.sol";
/**
* @title ProtocolOwner
* @author Unlockd
* @dev This contract contains the logic that enables asset/signature delegates to interact with a Gnosis Safe wallet.
* In the case of assets delegates, it will allow them to execute functions though the Safe, only those registered
* as allowed on the DelegationRecipes contract.
* In the case of signatures it validates that a signature was made by the current delegate.
* It is also used by the delegation controller to set delegations and the lock controller to lock, unlock and claim
* assets.
*
* It should be use a proxy's implementation.
*/
contract ProtocolOwner is Initializable, BaseSafeOwner, IProtocolOwner {
// bytes32 public constant GUARD_STORAGE_SLOT = 0x4a204f620c8c5ccdca3fd54d003badd85ba500436a431f0cbda4f558c93c34c8;
DelegationOwner public delegationOwner;
mapping(bytes32 => bytes32) loansIds;
mapping(address => bool) oneTimeDelegation;
/**
* @notice The DelegationGuard address.
*/
TransactionGuard public guard;
////////////////////////////////////////////////////////////////////////////////
// Modifiers
////////////////////////////////////////////////////////////////////////////////
modifier onlyOneTimeDelegation() {
if (oneTimeDelegation[msg.sender] == false) revert Errors.ProtocolOwner__invalidDelegatedAddressAddress();
_;
}
/**
* @dev Disables the initializer in order to prevent implementation initialization.
*/
constructor(address _cryptoPunks, address _aclManager) BaseSafeOwner(_cryptoPunks, _aclManager) {
_disableInitializers();
}
/**
* @notice Initializes the proxy state.
* @param _safe - The DelegationWallet address, the GnosisSafe.
* @param _owner - The owner of the DelegationWallet.
* @param _delegationOwner - Use delegation owner
*/
function initialize(address _guard, address _safe, address _owner, address _delegationOwner) public initializer {
if (_guard == address(0)) revert Errors.DelegationGuard__initialize_invalidGuardBeacon();
if (_safe == address(0)) revert Errors.DelegationGuard__initialize_invalidSafe();
if (_owner == address(0)) revert Errors.DelegationGuard__initialize_invalidOwner();
delegationOwner = DelegationOwner(_delegationOwner);
safe = _safe;
owner = _owner;
guard = TransactionGuard(_guard);
}
////////////////////////////////////////////////////////////////////////////////
// Public Functions
////////////////////////////////////////////////////////////////////////////////
function approveSale(
address _collection,
uint256 _tokenId,
address _underlyingAsset,
uint256 _amount,
address _marketApproval,
bytes32 _loanId
) external onlyOneTimeDelegation {
// Doesnt' matter if fails, it need to delegate again.
oneTimeDelegation[msg.sender] = false;
if (loansIds[AssetLogic.assetId(_collection, _tokenId)] != _loanId) {
revert Errors.DelegationOwner__wrongLoanId();
}
// Asset approval to the adapter to perform the sell
_approveAsset(_collection, _tokenId, _marketApproval);
// Approval of the ERC20 to repay the debs
_approveERC20(_underlyingAsset, _amount, msg.sender);
}
/**
* @notice Execute a transaction through the GnosisSafe wallet.
* The sender should be the delegate of the given asset and the function should be allowed for the collection.
* @param _to - Destination address of Safe transaction.
* @param _value - Ether value of Safe transaction.
* @param _data - Data payload of Safe transaction.
* @param _safeTxGas - Gas that should be used for the Safe transaction.
* @param _baseGas Gas costs that are independent of the transaction execution(e.g. base transaction fee, signature
* check, payment of the refund)
* @param _gasPrice - Gas price that should be used for the payment calculation.
* @param _gasToken - Token address (or 0 if ETH) that is used for the payment.
* @param _refundReceiver - Address of receiver of gas payment (or 0 if tx.origin).
*/
function execTransaction(
address _to,
uint256 _value,
bytes calldata _data,
uint256 _safeTxGas,
uint256 _baseGas,
uint256 _gasPrice,
address _gasToken,
address payable _refundReceiver
) external onlyOneTimeDelegation returns (bool success) {
// Doesnt' matter if fails, it need to delegate again.
oneTimeDelegation[msg.sender] = false;
// Admin execution no safe guard
isExecuting = true;
currentTxHash = IGnosisSafe(payable(safe)).getTransactionHash(
// Transaction info
_to,
_value,
_data,
Enum.Operation.Call,
_safeTxGas,
// Payment info
_baseGas,
_gasPrice,
_gasToken,
_refundReceiver,
// Signature info
IGnosisSafe(payable(safe)).nonce()
);
// https://docs.gnosis-safe.io/contracts/signatures#contract-signature-eip-1271
bytes memory signature = abi.encodePacked(
abi.encode(address(this)), // r
abi.encode(uint256(65)), // s
bytes1(0), // v
abi.encode(currentTxHash.length),
currentTxHash
);
(success) = IGnosisSafe(safe).execTransaction(
_to,
_value,
_data,
Enum.Operation.Call,
_safeTxGas,
_baseGas,
_gasPrice,
_gasToken,
_refundReceiver,
signature
);
isExecuting = false;
currentTxHash = bytes32(0);
if (!success) revert Errors.ProtocolOwner__execTransaction_notSuccess();
}
function delegateOneExecution(address to, bool value) external onlyProtocol {
if (to == address(0)) revert Errors.ProtocolOwner__invalidDelegatedAddressAddress();
oneTimeDelegation[to] = value;
}
function isDelegatedExecution(address to) external view returns (bool) {
return oneTimeDelegation[to];
}
/**
* @notice Returns if an asset is locked.
* @param _id - The asset id.
*/
function isAssetLocked(bytes32 _id) external view returns (bool) {
return delegationOwner.guard().isLocked(_id);
}
/**
* @notice Return the LoanId assigned to a asset
* 0 means the asset is locked
*/
function getLoanId(bytes32 index) external view returns (bytes32) {
return loansIds[index];
}
/**
* @notice set loan id assigned to a specific assetId
*/
function setLoanId(bytes32 _index, bytes32 _loanId) external onlyProtocol {
_setLoanId(_index, _loanId);
emit SetLoanId(_index, _loanId);
}
/**
* @notice set loan id assigned to a specific assetId
*/
function safeSetLoanId(address _asset, uint256 _id, bytes32 _loanId) external onlyProtocol {
bytes32 id = AssetLogic.assetId(_asset, _id);
// Reset approve
_approveAsset(_asset, _id, address(0));
// Lock asset
_setLoanId(id, _loanId);
emit SetLoanId(id, _loanId);
}
/**
* @notice change the current ownership of a asset
*/
function changeOwner(address _asset, uint256 _id, address _newOwner) external onlyProtocol {
bytes32 id = AssetLogic.assetId(_asset, _id);
// We unlock the current asset
_setLoanId(id, 0);
// Force end delegation
delegationOwner.forceEndDelegation(_asset, _id);
bool success = _transferAsset(_asset, _id, _newOwner);
if (!success) revert Errors.DelegationOwner__changeOwner_notSuccess();
emit ChangeOwner(_asset, _id, _newOwner);
}
/**
* @notice batch function to set to 0 a group of assets
*/
function batchSetToZeroLoanId(bytes32[] calldata _assets) external onlyProtocol {
uint256 cachedAssets = _assets.length;
for (uint256 i = 0; i < cachedAssets; ) {
if (loansIds[_assets[i]] == 0) revert Errors.DelegationOwner__assetNotLocked();
_setLoanId(_assets[i], 0);
unchecked {
i++;
}
}
emit SetBatchLoanId(_assets, 0);
}
/**
* @notice batch function to set to different from 0 a group of assets
*/
function batchSetLoanId(bytes32[] calldata _assets, bytes32 _loanId) external onlyProtocol {
uint256 cachedAssets = _assets.length;
for (uint256 i = 0; i < cachedAssets; ) {
if (loansIds[_assets[i]] != 0) revert Errors.DelegationOwner__assetAlreadyLocked();
_setLoanId(_assets[i], _loanId);
unchecked {
i++;
}
}
emit SetBatchLoanId(_assets, _loanId);
}
//////////////////////////////////////////////
// Internal functions
//////////////////////////////////////////////
function _setLoanId(bytes32 _assetId, bytes32 _loanId) internal {
loansIds[_assetId] = _loanId;
// We update the guard from DelegationOwner
if (_loanId == 0) {
guard.unlockAsset(_assetId);
} else {
guard.lockAsset(_assetId);
}
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.19;
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import { Errors } from "../helpers/Errors.sol";
/**
* @title DelegationRecipes
* @author BootNode
* @dev Registers the functions that will be allowed to be executed by assets delegates.
* Functions are grouped by target contract and asset collection.
*/
contract DelegationRecipes is Ownable {
using EnumerableSet for EnumerableSet.Bytes32Set;
// collection address -> keccak256(collection, contract, selector)
mapping(address => EnumerableSet.Bytes32Set) internal functionByCollection;
// keccak256(collection, contract, selector) -> description
mapping(bytes32 => string) public functionDescriptions;
event AddRecipe(address indexed collection, address[] contracts, bytes4[] selectors, string[] description);
event RemoveRecipe(address indexed collection, address[] contracts, bytes4[] selectors);
/**
* @notice Adds a group of allowed functions to a collection.
* @param _collection - The asset collection address.
* @param _contracts - The target contract addresses.
* @param _selectors - The allowed function selectors.
*/
function add(
address _collection,
address[] calldata _contracts,
bytes4[] calldata _selectors,
string[] calldata _descriptions
) external onlyOwner {
if (_contracts.length != _selectors.length || _selectors.length != _descriptions.length)
revert Errors.DelegationRecipes__add_arityMismatch();
bytes32 functionId;
uint256 length = _contracts.length;
for (uint256 i; i < length; ) {
functionId = keccak256(abi.encodePacked(_collection, _contracts[i], _selectors[i]));
functionByCollection[_collection].add(functionId);
functionDescriptions[functionId] = _descriptions[i];
emit AddRecipe(_collection, _contracts, _selectors, _descriptions);
unchecked {
++i;
}
}
}
/**
* @notice Removes a group of allowed functions from a collection.
* @param _collection - The owner's address.
* @param _contracts - The owner's address.
* @param _selectors - The owner's address.
*/
function remove(
address _collection,
address[] calldata _contracts,
bytes4[] calldata _selectors
) external onlyOwner {
if (_contracts.length != _selectors.length) revert Errors.DelegationRecipes__remove_arityMismatch();
bytes32 functionId;
uint256 length = _contracts.length;
for (uint256 i; i < length; ) {
functionId = keccak256(abi.encodePacked(_collection, _contracts[i], _selectors[i]));
functionByCollection[_collection].remove(functionId);
delete functionDescriptions[functionId];
emit RemoveRecipe(_collection, _contracts, _selectors);
unchecked {
++i;
}
}
}
/**
* @notice Checks if a function is allowed for a collection.
* @param _collection - The owner's address.
* @param _contract - The owner's address.
* @param _selector - The owner's address.
*/
function isAllowedFunction(address _collection, address _contract, bytes4 _selector) external view returns (bool) {
bytes32 functionId = keccak256(abi.encodePacked(_collection, _contract, _selector));
return functionByCollection[_collection].contains(functionId);
}
}{
"remappings": [
"ds-test/=lib/forge-std/lib/ds-test/src/",
"forge-std/=lib/forge-std/src/",
"solady/=lib/solady/src/",
"@openzeppelin/=lib/openzeppelin-contracts/",
"@openzeppelin-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
"@chainlink/=lib/chainlink/",
"@unlockd-wallet/=lib/unlockdv2-wallet/",
"@solady/=lib/solady/src/",
"@maxapy/=lib/maxapy/src/",
"@gnosis.pm/safe-contracts/=lib/unlockdv2-wallet/lib/safe-contracts/",
"chainlink/=lib/chainlink/",
"erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
"maxapy/=lib/maxapy/",
"openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
"openzeppelin-contracts/=lib/openzeppelin-contracts/",
"openzeppelin/=lib/openzeppelin-contracts/contracts/",
"safe-contracts/=lib/unlockdv2-wallet/lib/safe-contracts/contracts/",
"unlockdv2-wallet/=lib/unlockdv2-wallet/src/"
],
"optimizer": {
"enabled": true,
"runs": 200
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "ipfs",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "paris",
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"_gnosisSafeProxyFactory","type":"address"},{"internalType":"address","name":"_singleton","type":"address"},{"internalType":"address","name":"_compatibilityFallbackHandler","type":"address"},{"internalType":"address","name":"_guardBeacon","type":"address"},{"internalType":"address","name":"_guardOwnerBeacon","type":"address"},{"internalType":"address","name":"_delegationOwnerBeacon","type":"address"},{"internalType":"address","name":"_protocolOwnerBeacon","type":"address"},{"internalType":"address","name":"_registry","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"safe","type":"address"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"guard","type":"address"},{"indexed":false,"internalType":"address","name":"delegationOwner","type":"address"},{"indexed":false,"internalType":"address","name":"protocolOwner","type":"address"},{"indexed":false,"internalType":"address","name":"sender","type":"address"}],"name":"WalletDeployed","type":"event"},{"inputs":[],"name":"compatibilityFallbackHandler","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"delegationOwnerBeacon","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_delegationController","type":"address"}],"name":"deploy","outputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"},{"internalType":"address","name":"_delegationController","type":"address"}],"name":"deployFor","outputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"gnosisSafeProxyFactory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"guardBeacon","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"guardOwnerBeacon","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"protocolOwnerBeacon","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"registry","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"singleton","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}]Contract Creation Code
6101806040523480156200001257600080fd5b506040516200142b3803806200142b83398101604081905262000035916200008f565b6001600160a01b0397881660805295871660a05293861660c05291851660e052841661010052831661012052821661014052166101605262000137565b80516001600160a01b03811681146200008a57600080fd5b919050565b600080600080600080600080610100898b031215620000ad57600080fd5b620000b88962000072565b9750620000c860208a0162000072565b9650620000d860408a0162000072565b9550620000e860608a0162000072565b9450620000f860808a0162000072565b93506200010860a08a0162000072565b92506200011860c08a0162000072565b91506200012860e08a0162000072565b90509295985092959890939650565b60805160a05160c05160e05161010051610120516101405161016051611258620001d36000396000818161019601526108900152600081816101be015261044a01526000818160fb01526103de01526000818161024d015261037201526000818160b6015261066601526000818161020e01526105d40152600081816101e601526102ec01526000818161012301526102be01526112586000f3fe60806040523480156200001157600080fd5b5060043610620000ab5760003560e01c80638ff3a15f116200006e5780638ff3a15f14620001b8578063b62654fb14620001e0578063e4438d281462000208578063f328ec8e1462000230578063f35c0191146200024757600080fd5b806316f8995514620000b0578063314e526214620000f5578063490b7a79146200011d5780634c96a38914620001455780637b1039991462000190575b600080fd5b620000d87f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020015b60405180910390f35b620000d87f000000000000000000000000000000000000000000000000000000000000000081565b620000d87f000000000000000000000000000000000000000000000000000000000000000081565b6200015c6200015636600462000983565b6200026f565b604080516001600160a01b0395861681529385166020850152918416918301919091529091166060820152608001620000ec565b620000d87f000000000000000000000000000000000000000000000000000000000000000081565b620000d87f000000000000000000000000000000000000000000000000000000000000000081565b620000d87f000000000000000000000000000000000000000000000000000000000000000081565b620000d87f000000000000000000000000000000000000000000000000000000000000000081565b6200015c62000241366004620009aa565b62000290565b620000d87f000000000000000000000000000000000000000000000000000000000000000081565b60008060008062000281338662000290565b93509350935093509193509193565b60408051600080825260208201928390526361b69abd60e01b90925281908190819081906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906361b69abd9062000316907f0000000000000000000000000000000000000000000000000000000000000000906024810162000a30565b6020604051808303816000875af115801562000336573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200035c919062000a5e565b60408051600080825260208201928390529293507f0000000000000000000000000000000000000000000000000000000000000000916200039d906200095c565b620003aa92919062000a30565b604051809103906000f080158015620003c7573d6000803e3d6000fd5b5060408051600080825260208201928390529293507f00000000000000000000000000000000000000000000000000000000000000009162000409906200095c565b6200041692919062000a30565b604051809103906000f08015801562000433573d6000803e3d6000fd5b5060408051600080825260208201928390529293507f00000000000000000000000000000000000000000000000000000000000000009162000475906200095c565b6200048292919062000a30565b604051809103906000f0801580156200049f573d6000803e3d6000fd5b5060408051600480825260a0820190925291925060009190602082016080803683370190505090508a81600081518110620004de57620004de62000a7e565b60200260200101906001600160a01b031690816001600160a01b031681525050838160018151811062000515576200051562000a7e565b60200260200101906001600160a01b031690816001600160a01b03168152505082816002815181106200054c576200054c62000a7e565b60200260200101906001600160a01b031690816001600160a01b031681525050818160038151811062000583576200058362000a7e565b6001600160a01b039283166020918202929092010152851663b63e800d8260016000806040519080825280601f01601f191660200182016040528015620005d1576020820181803683370190505b507f000000000000000000000000000000000000000000000000000000000000000060008060006040518963ffffffff1660e01b81526004016200061d98979695949392919062000a94565b600060405180830381600087803b1580156200063857600080fd5b505af11580156200064d573d6000803e3d6000fd5b5050604051630a2ca2bd60e11b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116600483015288811660248301528e811660448301528681166064830152858116608483015287169250631459457a915060a401600060405180830381600087803b158015620006d557600080fd5b505af1158015620006ea573d6000803e3d6000fd5b505050506000846001600160a01b0316637ceab3b16040518163ffffffff1660e01b8152600401602060405180830381865afa1580156200072f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000755919062000a5e565b604051630a2ca2bd60e11b81526001600160a01b03808316600483015288811660248301528e811660448301528d81166064830152858116608483015291925090851690631459457a9060a401600060405180830381600087803b158015620007bd57600080fd5b505af1158015620007d2573d6000803e3d6000fd5b5050604051637c643b2f60e11b81526001600160a01b03848116600483015289811660248301528f8116604483015287811660648301528616925063f8c8765e9150608401600060405180830381600087803b1580156200083257600080fd5b505af115801562000847573d6000803e3d6000fd5b505060405163be1de99560e01b81526001600160a01b0389811660048301528f8116602483015284811660448301528881166064830152878116608483015286811660a48301527f000000000000000000000000000000000000000000000000000000000000000016925063be1de995915060c401600060405180830381600087803b158015620008d757600080fd5b505af1158015620008ec573d6000803e3d6000fd5b5050604080516001600160a01b0388811682528781166020830152339282019290925281851693508f82169250908916907fe95b63f0aa455dec0ba14fbbd8dc16347fa4f8faf551ed85044057fd249e59f99060600160405180910390a450939a91995097509095509350505050565b6106cc8062000b5783390190565b6001600160a01b03811681146200098057600080fd5b50565b6000602082840312156200099657600080fd5b8135620009a3816200096a565b9392505050565b60008060408385031215620009be57600080fd5b8235620009cb816200096a565b91506020830135620009dd816200096a565b809150509250929050565b6000815180845260005b8181101562000a1057602081850181015186830182015201620009f2565b506000602082860101526020601f19601f83011685010191505092915050565b6001600160a01b038316815260406020820181905260009062000a5690830184620009e8565b949350505050565b60006020828403121562000a7157600080fd5b8151620009a3816200096a565b634e487b7160e01b600052603260045260246000fd5b6101008082528951908201819052600090610120830190602090818d01845b8281101562000ada5781516001600160a01b03168552938301939083019060010162000ab3565b50505083018a90526001600160a01b0389166040840152828103606084015262000b058189620009e8565b91505062000b1e60808301876001600160a01b03169052565b6001600160a01b03851660a08301528360c083015262000b4960e08301846001600160a01b03169052565b999850505050505050505056fe60806040526040516106cc3803806106cc83398101604081905261002291610420565b61002e82826000610035565b505061054a565b61003e836100f6565b6040516001600160a01b038416907f1cf3b03a6cf19fa2baba4df148e9dcabedea7f8a5c07840e207e5c089be95d3e90600090a260008251118061007f5750805b156100f1576100ef836001600160a01b0316635c60da1b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156100c5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100e991906104e0565b8361027a565b505b505050565b6001600160a01b0381163b6101605760405162461bcd60e51b815260206004820152602560248201527f455243313936373a206e657720626561636f6e206973206e6f74206120636f6e6044820152641d1c9858dd60da1b60648201526084015b60405180910390fd5b6101d4816001600160a01b0316635c60da1b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156101a1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101c591906104e0565b6001600160a01b03163b151590565b6102395760405162461bcd60e51b815260206004820152603060248201527f455243313936373a20626561636f6e20696d706c656d656e746174696f6e206960448201526f1cc81b9bdd08184818dbdb9d1c9858dd60821b6064820152608401610157565b7fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d5080546001600160a01b0319166001600160a01b0392909216919091179055565b606061029f83836040518060600160405280602781526020016106a5602791396102a6565b9392505050565b6060600080856001600160a01b0316856040516102c391906104fb565b600060405180830381855af49150503d80600081146102fe576040519150601f19603f3d011682016040523d82523d6000602084013e610303565b606091505b5090925090506103158683838761031f565b9695505050505050565b6060831561038e578251600003610387576001600160a01b0385163b6103875760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610157565b5081610398565b61039883836103a0565b949350505050565b8151156103b05781518083602001fd5b8060405162461bcd60e51b81526004016101579190610517565b80516001600160a01b03811681146103e157600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b60005b838110156104175781810151838201526020016103ff565b50506000910152565b6000806040838503121561043357600080fd5b61043c836103ca565b60208401519092506001600160401b038082111561045957600080fd5b818501915085601f83011261046d57600080fd5b81518181111561047f5761047f6103e6565b604051601f8201601f19908116603f011681019083821181831017156104a7576104a76103e6565b816040528281528860208487010111156104c057600080fd5b6104d18360208301602088016103fc565b80955050505050509250929050565b6000602082840312156104f257600080fd5b61029f826103ca565b6000825161050d8184602087016103fc565b9190910192915050565b60208152600082518060208401526105368160408501602087016103fc565b601f01601f19169190910160400192915050565b61014c806105596000396000f3fe60806040523661001357610011610017565b005b6100115b610027610022610029565b6100c2565b565b600061005c7fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50546001600160a01b031690565b6001600160a01b0316635c60da1b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610099573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100bd91906100e6565b905090565b3660008037600080366000845af43d6000803e8080156100e1573d6000f35b3d6000fd5b6000602082840312156100f857600080fd5b81516001600160a01b038116811461010f57600080fd5b939250505056fea264697066735822122039a90dd002d3b65206a5ff52a4d85e0219bbafe5ee1e985606674ec72c36b7e864736f6c63430008130033416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a26469706673582212200a4ba47971e7d9a59a196e9114ea8000172d24b1f557718e5f6c5917db590d2d64736f6c63430008130033000000000000000000000000a6b71e26c5e0845f74c812102ca7114b6a896ab2000000000000000000000000d9db270c1b5e3bd161e8c8503c55ceabee709552000000000000000000000000f48f2b2d2a534e402487b3ee7c18c33aec0fe5e40000000000000000000000006ff7817b3bc8c601828664c1fa77d78e34c12e5e000000000000000000000000a794b528cfadb0e93d3327b07a7682b60d41e7ab0000000000000000000000006b08156b1b491171d25dead9435a5cd00d1bc432000000000000000000000000054f3480439516c248176eecdca6980ede884628000000000000000000000000715988afcbaef9f99a6796f6d6699eeddb48eb20
Deployed Bytecode
0x60806040523480156200001157600080fd5b5060043610620000ab5760003560e01c80638ff3a15f116200006e5780638ff3a15f14620001b8578063b62654fb14620001e0578063e4438d281462000208578063f328ec8e1462000230578063f35c0191146200024757600080fd5b806316f8995514620000b0578063314e526214620000f5578063490b7a79146200011d5780634c96a38914620001455780637b1039991462000190575b600080fd5b620000d87f0000000000000000000000006ff7817b3bc8c601828664c1fa77d78e34c12e5e81565b6040516001600160a01b0390911681526020015b60405180910390f35b620000d87f0000000000000000000000006b08156b1b491171d25dead9435a5cd00d1bc43281565b620000d87f000000000000000000000000a6b71e26c5e0845f74c812102ca7114b6a896ab281565b6200015c6200015636600462000983565b6200026f565b604080516001600160a01b0395861681529385166020850152918416918301919091529091166060820152608001620000ec565b620000d87f000000000000000000000000715988afcbaef9f99a6796f6d6699eeddb48eb2081565b620000d87f000000000000000000000000054f3480439516c248176eecdca6980ede88462881565b620000d87f000000000000000000000000d9db270c1b5e3bd161e8c8503c55ceabee70955281565b620000d87f000000000000000000000000f48f2b2d2a534e402487b3ee7c18c33aec0fe5e481565b6200015c62000241366004620009aa565b62000290565b620000d87f000000000000000000000000a794b528cfadb0e93d3327b07a7682b60d41e7ab81565b60008060008062000281338662000290565b93509350935093509193509193565b60408051600080825260208201928390526361b69abd60e01b90925281908190819081906001600160a01b037f000000000000000000000000a6b71e26c5e0845f74c812102ca7114b6a896ab216906361b69abd9062000316907f000000000000000000000000d9db270c1b5e3bd161e8c8503c55ceabee709552906024810162000a30565b6020604051808303816000875af115801562000336573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906200035c919062000a5e565b60408051600080825260208201928390529293507f000000000000000000000000a794b528cfadb0e93d3327b07a7682b60d41e7ab916200039d906200095c565b620003aa92919062000a30565b604051809103906000f080158015620003c7573d6000803e3d6000fd5b5060408051600080825260208201928390529293507f0000000000000000000000006b08156b1b491171d25dead9435a5cd00d1bc4329162000409906200095c565b6200041692919062000a30565b604051809103906000f08015801562000433573d6000803e3d6000fd5b5060408051600080825260208201928390529293507f000000000000000000000000054f3480439516c248176eecdca6980ede8846289162000475906200095c565b6200048292919062000a30565b604051809103906000f0801580156200049f573d6000803e3d6000fd5b5060408051600480825260a0820190925291925060009190602082016080803683370190505090508a81600081518110620004de57620004de62000a7e565b60200260200101906001600160a01b031690816001600160a01b031681525050838160018151811062000515576200051562000a7e565b60200260200101906001600160a01b031690816001600160a01b03168152505082816002815181106200054c576200054c62000a7e565b60200260200101906001600160a01b031690816001600160a01b031681525050818160038151811062000583576200058362000a7e565b6001600160a01b039283166020918202929092010152851663b63e800d8260016000806040519080825280601f01601f191660200182016040528015620005d1576020820181803683370190505b507f000000000000000000000000f48f2b2d2a534e402487b3ee7c18c33aec0fe5e460008060006040518963ffffffff1660e01b81526004016200061d98979695949392919062000a94565b600060405180830381600087803b1580156200063857600080fd5b505af11580156200064d573d6000803e3d6000fd5b5050604051630a2ca2bd60e11b81526001600160a01b037f0000000000000000000000006ff7817b3bc8c601828664c1fa77d78e34c12e5e8116600483015288811660248301528e811660448301528681166064830152858116608483015287169250631459457a915060a401600060405180830381600087803b158015620006d557600080fd5b505af1158015620006ea573d6000803e3d6000fd5b505050506000846001600160a01b0316637ceab3b16040518163ffffffff1660e01b8152600401602060405180830381865afa1580156200072f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000755919062000a5e565b604051630a2ca2bd60e11b81526001600160a01b03808316600483015288811660248301528e811660448301528d81166064830152858116608483015291925090851690631459457a9060a401600060405180830381600087803b158015620007bd57600080fd5b505af1158015620007d2573d6000803e3d6000fd5b5050604051637c643b2f60e11b81526001600160a01b03848116600483015289811660248301528f8116604483015287811660648301528616925063f8c8765e9150608401600060405180830381600087803b1580156200083257600080fd5b505af115801562000847573d6000803e3d6000fd5b505060405163be1de99560e01b81526001600160a01b0389811660048301528f8116602483015284811660448301528881166064830152878116608483015286811660a48301527f000000000000000000000000715988afcbaef9f99a6796f6d6699eeddb48eb2016925063be1de995915060c401600060405180830381600087803b158015620008d757600080fd5b505af1158015620008ec573d6000803e3d6000fd5b5050604080516001600160a01b0388811682528781166020830152339282019290925281851693508f82169250908916907fe95b63f0aa455dec0ba14fbbd8dc16347fa4f8faf551ed85044057fd249e59f99060600160405180910390a450939a91995097509095509350505050565b6106cc8062000b5783390190565b6001600160a01b03811681146200098057600080fd5b50565b6000602082840312156200099657600080fd5b8135620009a3816200096a565b9392505050565b60008060408385031215620009be57600080fd5b8235620009cb816200096a565b91506020830135620009dd816200096a565b809150509250929050565b6000815180845260005b8181101562000a1057602081850181015186830182015201620009f2565b506000602082860101526020601f19601f83011685010191505092915050565b6001600160a01b038316815260406020820181905260009062000a5690830184620009e8565b949350505050565b60006020828403121562000a7157600080fd5b8151620009a3816200096a565b634e487b7160e01b600052603260045260246000fd5b6101008082528951908201819052600090610120830190602090818d01845b8281101562000ada5781516001600160a01b03168552938301939083019060010162000ab3565b50505083018a90526001600160a01b0389166040840152828103606084015262000b058189620009e8565b91505062000b1e60808301876001600160a01b03169052565b6001600160a01b03851660a08301528360c083015262000b4960e08301846001600160a01b03169052565b999850505050505050505056fe60806040526040516106cc3803806106cc83398101604081905261002291610420565b61002e82826000610035565b505061054a565b61003e836100f6565b6040516001600160a01b038416907f1cf3b03a6cf19fa2baba4df148e9dcabedea7f8a5c07840e207e5c089be95d3e90600090a260008251118061007f5750805b156100f1576100ef836001600160a01b0316635c60da1b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156100c5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100e991906104e0565b8361027a565b505b505050565b6001600160a01b0381163b6101605760405162461bcd60e51b815260206004820152602560248201527f455243313936373a206e657720626561636f6e206973206e6f74206120636f6e6044820152641d1c9858dd60da1b60648201526084015b60405180910390fd5b6101d4816001600160a01b0316635c60da1b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156101a1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906101c591906104e0565b6001600160a01b03163b151590565b6102395760405162461bcd60e51b815260206004820152603060248201527f455243313936373a20626561636f6e20696d706c656d656e746174696f6e206960448201526f1cc81b9bdd08184818dbdb9d1c9858dd60821b6064820152608401610157565b7fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d5080546001600160a01b0319166001600160a01b0392909216919091179055565b606061029f83836040518060600160405280602781526020016106a5602791396102a6565b9392505050565b6060600080856001600160a01b0316856040516102c391906104fb565b600060405180830381855af49150503d80600081146102fe576040519150601f19603f3d011682016040523d82523d6000602084013e610303565b606091505b5090925090506103158683838761031f565b9695505050505050565b6060831561038e578251600003610387576001600160a01b0385163b6103875760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610157565b5081610398565b61039883836103a0565b949350505050565b8151156103b05781518083602001fd5b8060405162461bcd60e51b81526004016101579190610517565b80516001600160a01b03811681146103e157600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b60005b838110156104175781810151838201526020016103ff565b50506000910152565b6000806040838503121561043357600080fd5b61043c836103ca565b60208401519092506001600160401b038082111561045957600080fd5b818501915085601f83011261046d57600080fd5b81518181111561047f5761047f6103e6565b604051601f8201601f19908116603f011681019083821181831017156104a7576104a76103e6565b816040528281528860208487010111156104c057600080fd5b6104d18360208301602088016103fc565b80955050505050509250929050565b6000602082840312156104f257600080fd5b61029f826103ca565b6000825161050d8184602087016103fc565b9190910192915050565b60208152600082518060208401526105368160408501602087016103fc565b601f01601f19169190910160400192915050565b61014c806105596000396000f3fe60806040523661001357610011610017565b005b6100115b610027610022610029565b6100c2565b565b600061005c7fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50546001600160a01b031690565b6001600160a01b0316635c60da1b6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610099573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100bd91906100e6565b905090565b3660008037600080366000845af43d6000803e8080156100e1573d6000f35b3d6000fd5b6000602082840312156100f857600080fd5b81516001600160a01b038116811461010f57600080fd5b939250505056fea264697066735822122039a90dd002d3b65206a5ff52a4d85e0219bbafe5ee1e985606674ec72c36b7e864736f6c63430008130033416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a26469706673582212200a4ba47971e7d9a59a196e9114ea8000172d24b1f557718e5f6c5917db590d2d64736f6c63430008130033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000a6b71e26c5e0845f74c812102ca7114b6a896ab2000000000000000000000000d9db270c1b5e3bd161e8c8503c55ceabee709552000000000000000000000000f48f2b2d2a534e402487b3ee7c18c33aec0fe5e40000000000000000000000006ff7817b3bc8c601828664c1fa77d78e34c12e5e000000000000000000000000a794b528cfadb0e93d3327b07a7682b60d41e7ab0000000000000000000000006b08156b1b491171d25dead9435a5cd00d1bc432000000000000000000000000054f3480439516c248176eecdca6980ede884628000000000000000000000000715988afcbaef9f99a6796f6d6699eeddb48eb20
-----Decoded View---------------
Arg [0] : _gnosisSafeProxyFactory (address): 0xa6B71E26C5e0845f74c812102Ca7114b6a896AB2
Arg [1] : _singleton (address): 0xd9Db270c1B5E3Bd161E8c8503c55cEABeE709552
Arg [2] : _compatibilityFallbackHandler (address): 0xf48f2B2d2a534e402487b3ee7C18c33Aec0Fe5e4
Arg [3] : _guardBeacon (address): 0x6Ff7817B3Bc8c601828664c1Fa77d78E34C12E5E
Arg [4] : _guardOwnerBeacon (address): 0xA794b528CfadB0e93d3327B07A7682b60D41e7aB
Arg [5] : _delegationOwnerBeacon (address): 0x6B08156B1B491171D25DeAd9435A5cd00D1Bc432
Arg [6] : _protocolOwnerBeacon (address): 0x054F3480439516c248176EeCdCa6980EDe884628
Arg [7] : _registry (address): 0x715988AfCbAEF9F99A6796f6d6699EeDDB48EB20
-----Encoded View---------------
8 Constructor Arguments found :
Arg [0] : 000000000000000000000000a6b71e26c5e0845f74c812102ca7114b6a896ab2
Arg [1] : 000000000000000000000000d9db270c1b5e3bd161e8c8503c55ceabee709552
Arg [2] : 000000000000000000000000f48f2b2d2a534e402487b3ee7c18c33aec0fe5e4
Arg [3] : 0000000000000000000000006ff7817b3bc8c601828664c1fa77d78e34c12e5e
Arg [4] : 000000000000000000000000a794b528cfadb0e93d3327b07a7682b60d41e7ab
Arg [5] : 0000000000000000000000006b08156b1b491171d25dead9435a5cd00d1bc432
Arg [6] : 000000000000000000000000054f3480439516c248176eecdca6980ede884628
Arg [7] : 000000000000000000000000715988afcbaef9f99a6796f6d6699eeddb48eb20
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 33 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.