Source Code
Latest 25 from a total of 8,599 transactions
| Transaction Hash |
Method
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
| Register | 111887950 | 772 days ago | IN | 0.003700824861122 ETH | 0.000264658604 | ||||
| Register | 111887776 | 772 days ago | IN | 0.003700824861122 ETH | 0.000298026399 | ||||
| Register | 111885882 | 772 days ago | IN | 0.003700824861122 ETH | 0.000287583649 | ||||
| Register | 111885746 | 772 days ago | IN | 0.003700824861122 ETH | 0.000226990852 | ||||
| Register | 111885679 | 772 days ago | IN | 0.003700824861122 ETH | 0.000220438278 | ||||
| Register | 111885177 | 772 days ago | IN | 0.003700824861122 ETH | 0.000301451618 | ||||
| Register | 111884666 | 772 days ago | IN | 0.003700824861122 ETH | 0.000269560776 | ||||
| Register | 111884467 | 772 days ago | IN | 0.003700824861122 ETH | 0.000210903432 | ||||
| Register | 111884222 | 772 days ago | IN | 0.003700824861122 ETH | 0.000255421936 | ||||
| Register | 111883577 | 772 days ago | IN | 0.003700824861122 ETH | 0.000216722938 | ||||
| Register | 111883550 | 772 days ago | IN | 0.003700824861122 ETH | 0.000227427848 | ||||
| Register | 111883031 | 772 days ago | IN | 0.003700824861122 ETH | 0.000327602192 | ||||
| Register | 111882658 | 772 days ago | IN | 0.003700824861122 ETH | 0.000281602152 | ||||
| Register | 111882224 | 772 days ago | IN | 0.003700824861122 ETH | 0.000201491775 | ||||
| Register | 111882218 | 772 days ago | IN | 0.003700824861122 ETH | 0.00020567498 | ||||
| Register | 111880927 | 772 days ago | IN | 0.003700824861122 ETH | 0.000169493148 | ||||
| Register | 111880403 | 772 days ago | IN | 0.003700824861122 ETH | 0.000161701213 | ||||
| Register | 111879528 | 772 days ago | IN | 0.003700824861122 ETH | 0.000191635735 | ||||
| Register | 111879465 | 772 days ago | IN | 0.003700824861122 ETH | 0.000176391321 | ||||
| Register | 111879020 | 772 days ago | IN | 0.003700824861122 ETH | 0.00018096599 | ||||
| Register | 111878907 | 772 days ago | IN | 0.003700824861122 ETH | 0.00016543779 | ||||
| Register | 111878625 | 772 days ago | IN | 0.003700824861122 ETH | 0.000157188169 | ||||
| Register | 111878298 | 772 days ago | IN | 0.003700824861122 ETH | 0.000199970499 | ||||
| Register | 111878156 | 772 days ago | IN | 0.003700824861122 ETH | 0.000174059919 | ||||
| Register | 111878137 | 772 days ago | IN | 0.003700824861122 ETH | 0.000180916466 |
Latest 25 internal transactions (View All)
Advanced mode:
| Parent Transaction Hash | Block | From | To | |||
|---|---|---|---|---|---|---|
| 111887950 | 772 days ago | 0.003700824861122 ETH | ||||
| 111887776 | 772 days ago | 0.003700824861122 ETH | ||||
| 111885882 | 772 days ago | 0.003700824861122 ETH | ||||
| 111885746 | 772 days ago | 0.003700824861122 ETH | ||||
| 111885679 | 772 days ago | 0.003700824861122 ETH | ||||
| 111885177 | 772 days ago | 0.003700824861122 ETH | ||||
| 111884666 | 772 days ago | 0.003700824861122 ETH | ||||
| 111884467 | 772 days ago | 0.003700824861122 ETH | ||||
| 111884222 | 772 days ago | 0.003700824861122 ETH | ||||
| 111883577 | 772 days ago | 0.003700824861122 ETH | ||||
| 111883550 | 772 days ago | 0.003700824861122 ETH | ||||
| 111883031 | 772 days ago | 0.003700824861122 ETH | ||||
| 111882658 | 772 days ago | 0.003700824861122 ETH | ||||
| 111882224 | 772 days ago | 0.003700824861122 ETH | ||||
| 111882218 | 772 days ago | 0.003700824861122 ETH | ||||
| 111880927 | 772 days ago | 0.003700824861122 ETH | ||||
| 111880403 | 772 days ago | 0.003700824861122 ETH | ||||
| 111879528 | 772 days ago | 0.003700824861122 ETH | ||||
| 111879465 | 772 days ago | 0.003700824861122 ETH | ||||
| 111879020 | 772 days ago | 0.003700824861122 ETH | ||||
| 111878907 | 772 days ago | 0.003700824861122 ETH | ||||
| 111878625 | 772 days ago | 0.003700824861122 ETH | ||||
| 111878298 | 772 days ago | 0.003700824861122 ETH | ||||
| 111878156 | 772 days ago | 0.003700824861122 ETH | ||||
| 111878137 | 772 days ago | 0.003700824861122 ETH |
Cross-Chain Transactions
Loading...
Loading
Contract Name:
Bundler
Compiler Version
v0.8.21+commit.d9974bed
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;
import {IdRegistry} from "./IdRegistry.sol";
import {StorageRegistry} from "./StorageRegistry.sol";
import {KeyRegistry} from "./KeyRegistry.sol";
import {IBundler} from "./interfaces/IBundler.sol";
import {TrustedCaller} from "./lib/TrustedCaller.sol";
import {TransferHelper} from "./lib/TransferHelper.sol";
/**
* @title Farcaster Bundler
*
* @notice See https://github.com/farcasterxyz/contracts/blob/v3.0.0/docs/docs.md for an overview.
*
* @custom:security-contact [email protected]
*/
contract Bundler is IBundler, TrustedCaller {
using TransferHelper for address;
/*//////////////////////////////////////////////////////////////
ERRORS
//////////////////////////////////////////////////////////////*/
/// @dev Revert if the caller does not have the authority to perform the action.
error Unauthorized();
/// @dev Revert if the caller attempts to rent zero storage units.
error InvalidAmount();
/*//////////////////////////////////////////////////////////////
CONSTANTS
//////////////////////////////////////////////////////////////*/
/**
* @dev Contract version specified using Farcaster protocol version scheme.
*/
string public constant VERSION = "2023.08.23";
/*//////////////////////////////////////////////////////////////
IMMUTABLES
//////////////////////////////////////////////////////////////*/
/**
* @dev Address of the IdRegistry contract
*/
IdRegistry public immutable idRegistry;
/**
* @dev Address of the StorageRegistry contract
*/
StorageRegistry public immutable storageRegistry;
/**
* @dev Address of the KeyRegistry contract
*/
KeyRegistry public immutable keyRegistry;
/*//////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/
/**
* @notice Configure the addresses of the Registry contracts and the trusted caller, which is
* allowed to register users during the bootstrap phase.
*
* @param _idRegistry Address of the IdRegistry contract
* @param _storageRegistry Address of the StorageRegistry contract
* @param _keyRegistry Address of the KeyRegistry contract
* @param _trustedCaller Address that can call trustedRegister and trustedBatchRegister
* @param _initialOwner Address that can set the trusted caller
*/
constructor(
address _idRegistry,
address _storageRegistry,
address _keyRegistry,
address _trustedCaller,
address _initialOwner
) TrustedCaller(_initialOwner) {
idRegistry = IdRegistry(_idRegistry);
storageRegistry = StorageRegistry(_storageRegistry);
keyRegistry = KeyRegistry(_keyRegistry);
_setTrustedCaller(_trustedCaller);
}
/**
* @notice Register an fid, multiple signers, and rent storage to an address in a single transaction.
*
* @param registration Struct containing registration parameters: to, recovery, deadline, and signature.
* @param signers Array of structs containing signer parameters: keyType, key, metadataType,
* metadata, deadline, and signature.
* @param storageUnits Number of storage units to rent
*
*/
function register(
RegistrationParams calldata registration,
SignerParams[] calldata signers,
uint256 storageUnits
) external payable {
if (storageUnits == 0) revert InvalidAmount();
uint256 fid =
idRegistry.registerFor(registration.to, registration.recovery, registration.deadline, registration.sig);
uint256 signersLen = signers.length;
for (uint256 i; i < signersLen;) {
SignerParams calldata signer = signers[i];
keyRegistry.addFor(
registration.to,
signer.keyType,
signer.key,
signer.metadataType,
signer.metadata,
signer.deadline,
signer.sig
);
// We know this will not overflow because it's less than the length of the array, which is a `uint256`.
unchecked {
++i;
}
}
uint256 overpayment = storageRegistry.rent{value: msg.value}(fid, storageUnits);
if (overpayment > 0) {
msg.sender.sendNative(overpayment);
}
}
/**
* @notice Register an fid, add a signer, and credit storage to an address in a single transaction. Can only
* be called by the trustedCaller during the Seedable phase.
*
* @param user UserData struct including to/recovery address, key params, and number of storage units.
*/
function trustedRegister(UserData calldata user) external onlyTrustedCaller {
// Will revert unless IdRegistry is in the Seedable phase
uint256 fid = idRegistry.trustedRegister(user.to, user.recovery);
uint256 signersLen = user.signers.length;
for (uint256 i; i < signersLen;) {
SignerData calldata signer = user.signers[i];
keyRegistry.trustedAdd(user.to, signer.keyType, signer.key, signer.metadataType, signer.metadata);
unchecked {
++i;
}
}
storageRegistry.credit(fid, user.units);
}
/**
* @notice Register fids, keys, and credit storage for multiple users in a single transaction. Can
* only be called by the trustedCaller during the Seedable phase. Will be used when
* migrating across Ethereum networks to bootstrap a new contract with existing data.
*
* @param users Array of UserData structs to register
*/
function trustedBatchRegister(UserData[] calldata users) external onlyTrustedCaller {
// Safety: calls inside a loop are safe since caller is trusted
uint256 usersLen = users.length;
for (uint256 i; i < usersLen;) {
UserData calldata user = users[i];
uint256 fid = idRegistry.trustedRegister(user.to, user.recovery);
uint256 signersLen = user.signers.length;
for (uint256 j; j < signersLen;) {
SignerData calldata signer = user.signers[j];
keyRegistry.trustedAdd(user.to, signer.keyType, signer.key, signer.metadataType, signer.metadata);
unchecked {
++j;
}
}
storageRegistry.credit(fid, user.units);
// We know this will not overflow because it's less than the length of the array, which is a `uint256`.
unchecked {
++i;
}
}
}
// solhint-disable-next-line no-empty-blocks
receive() external payable {
if (msg.sender != address(storageRegistry)) revert Unauthorized();
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;
import {SignatureChecker} from "openzeppelin/contracts/utils/cryptography/SignatureChecker.sol";
import {Nonces} from "openzeppelin-latest/contracts/utils/Nonces.sol";
import {Pausable} from "openzeppelin/contracts/security/Pausable.sol";
import {IIdRegistry} from "./interfaces/IIdRegistry.sol";
import {EIP712} from "./lib/EIP712.sol";
import {Signatures} from "./lib/Signatures.sol";
import {TrustedCaller} from "./lib/TrustedCaller.sol";
/**
* @title Farcaster IdRegistry
*
* @notice See https://github.com/farcasterxyz/contracts/blob/v3.0.0/docs/docs.md for an overview.
*
* @custom:security-contact [email protected]
*/
contract IdRegistry is IIdRegistry, TrustedCaller, Signatures, Pausable, EIP712, Nonces {
/*//////////////////////////////////////////////////////////////
ERRORS
//////////////////////////////////////////////////////////////*/
/// @dev Revert when the caller does not have the authority to perform the action.
error Unauthorized();
/// @dev Revert when the caller must have an fid but does not have one.
error HasNoId();
/// @dev Revert when the destination must be empty but has an fid.
error HasId();
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
/**
* @dev Emit an event when a new Farcaster ID is registered.
*
* Hubs listen for this and update their address-to-fid mapping by adding `to` as the
* current owner of `id`. Hubs assume the invariants:
*
* 1. Two Register events can never emit with the same `id`
*
* 2. Two Register(alice, ..., ...) cannot emit unless a Transfer(alice, bob, ...) emits
* in between, where bob != alice.
*
* @param to The custody address that owns the fid
* @param id The fid that was registered.
* @param recovery The address that can initiate a recovery request for the fid.
*/
event Register(address indexed to, uint256 indexed id, address recovery);
/**
* @dev Emit an event when an fid is transferred to a new custody address.
*
* Hubs listen to this event and atomically change the current owner of `id`
* from `from` to `to` in their address-to-fid mapping. Hubs assume the invariants:
*
* 1. A Transfer(..., alice, ...) cannot emit if the most recent event for alice is
* Register (alice, ..., ...)
*
* 2. A Transfer(alice, ..., id) cannot emit unless the most recent event with that id is
* Transfer(..., alice, id) or Register(alice, id, ...)
*
* @param from The custody address that previously owned the fid.
* @param to The custody address that now owns the fid.
* @param id The fid that was transferred.
*/
event Transfer(address indexed from, address indexed to, uint256 indexed id);
/**
* @dev Emit an event when an fid is recovered.
*
* @param from The custody address that previously owned the fid.
* @param to The custody address that now owns the fid.
* @param id The fid that was recovered.
*/
event Recover(address indexed from, address indexed to, uint256 indexed id);
/**
* @dev Emit an event when a Farcaster ID's recovery address changes. It is possible for this
* event to emit multiple times in a row with the same recovery address.
*
* @param id The fid whose recovery address was changed.
* @param recovery The new recovery address.
*/
event ChangeRecoveryAddress(uint256 indexed id, address indexed recovery);
/*//////////////////////////////////////////////////////////////
CONSTANTS
//////////////////////////////////////////////////////////////*/
/**
* @inheritdoc IIdRegistry
*/
string public constant name = "Farcaster FID";
/**
* @inheritdoc IIdRegistry
*/
string public constant VERSION = "2023.08.23";
/**
* @inheritdoc IIdRegistry
*/
bytes32 public constant REGISTER_TYPEHASH =
keccak256("Register(address to,address recovery,uint256 nonce,uint256 deadline)");
/**
* @inheritdoc IIdRegistry
*/
bytes32 public constant TRANSFER_TYPEHASH =
keccak256("Transfer(uint256 fid,address to,uint256 nonce,uint256 deadline)");
/**
* @inheritdoc IIdRegistry
*/
bytes32 public constant CHANGE_RECOVERY_ADDRESS_TYPEHASH =
keccak256("ChangeRecoveryAddress(uint256 fid,address recovery,uint256 nonce,uint256 deadline)");
/*//////////////////////////////////////////////////////////////
STORAGE
//////////////////////////////////////////////////////////////*/
/**
* @inheritdoc IIdRegistry
*/
uint256 public idCounter;
/**
* @inheritdoc IIdRegistry
*/
mapping(address owner => uint256 fid) public idOf;
/**
* @inheritdoc IIdRegistry
*/
mapping(uint256 fid => address recovery) public recoveryOf;
/*//////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/
/**
* @notice Set the owner of the contract to the provided _owner.
*
* @param _initialOwner Initial owner address.
*
*/
// solhint-disable-next-line no-empty-blocks
constructor(address _initialOwner) TrustedCaller(_initialOwner) EIP712("Farcaster IdRegistry", "1") {}
/*//////////////////////////////////////////////////////////////
REGISTRATION LOGIC
//////////////////////////////////////////////////////////////*/
/**
* @inheritdoc IIdRegistry
*/
function register(address recovery) external returns (uint256 fid) {
return _register(msg.sender, recovery);
}
/**
* @inheritdoc IIdRegistry
*/
function registerFor(
address to,
address recovery,
uint256 deadline,
bytes calldata sig
) external returns (uint256 fid) {
/* Revert if signature is invalid */
_verifyRegisterSig({to: to, recovery: recovery, deadline: deadline, sig: sig});
return _register(to, recovery);
}
/**
* @inheritdoc IIdRegistry
*/
function trustedRegister(address to, address recovery) external onlyTrustedCaller returns (uint256 fid) {
fid = _unsafeRegister(to, recovery);
emit Register(to, idCounter, recovery);
}
/**
* @dev Registers an fid and sets up a recovery address for a target. The contract must not be
* in the Seedable (trustedOnly = 1) state and target must not have an fid.
*/
function _register(address to, address recovery) internal whenNotTrusted returns (uint256 fid) {
fid = _unsafeRegister(to, recovery);
emit Register(to, idCounter, recovery);
}
/**
* @dev Registers an fid and sets up a recovery address for a target. Does not check all
* invariants or emit events.
*/
function _unsafeRegister(address to, address recovery) internal whenNotPaused returns (uint256 fid) {
/* Revert if the target(to) has an fid */
if (idOf[to] != 0) revert HasId();
/* Safety: idCounter won't realistically overflow. */
unchecked {
/* Incrementing before assignment ensures that no one gets the 0 fid. */
fid = ++idCounter;
}
idOf[to] = fid;
recoveryOf[fid] = recovery;
}
/*//////////////////////////////////////////////////////////////
TRANSFER LOGIC
//////////////////////////////////////////////////////////////*/
/**
* @inheritdoc IIdRegistry
*/
function transfer(address to, uint256 deadline, bytes calldata sig) external {
address from = msg.sender;
uint256 fromId = idOf[from];
/* Revert if the sender has no id */
if (fromId == 0) revert HasNoId();
/* Revert if recipient has an id */
if (idOf[to] != 0) revert HasId();
/* Revert if signature is invalid */
_verifyTransferSig({fid: fromId, to: to, deadline: deadline, signer: to, sig: sig});
_unsafeTransfer(fromId, from, to);
}
/**
* @inheritdoc IIdRegistry
*/
function transferFor(
address from,
address to,
uint256 fromDeadline,
bytes calldata fromSig,
uint256 toDeadline,
bytes calldata toSig
) external {
uint256 fromId = idOf[from];
/* Revert if the sender has no id */
if (fromId == 0) revert HasNoId();
/* Revert if recipient has an id */
if (idOf[to] != 0) revert HasId();
/* Revert if either signature is invalid */
_verifyTransferSig({fid: fromId, to: to, deadline: fromDeadline, signer: from, sig: fromSig});
_verifyTransferSig({fid: fromId, to: to, deadline: toDeadline, signer: to, sig: toSig});
_unsafeTransfer(fromId, from, to);
}
/**
* @dev Transfer the fid to another address without checking invariants.
*/
function _unsafeTransfer(uint256 id, address from, address to) internal whenNotPaused {
idOf[to] = id;
delete idOf[from];
emit Transfer(from, to, id);
}
/*//////////////////////////////////////////////////////////////
RECOVERY LOGIC
//////////////////////////////////////////////////////////////*/
/**
* @inheritdoc IIdRegistry
*/
function changeRecoveryAddress(address recovery) external whenNotPaused {
/* Revert if the caller does not own an fid */
uint256 ownerId = idOf[msg.sender];
if (ownerId == 0) revert HasNoId();
/* Change the recovery address */
recoveryOf[ownerId] = recovery;
emit ChangeRecoveryAddress(ownerId, recovery);
}
/**
* @inheritdoc IIdRegistry
*/
function changeRecoveryAddressFor(
address owner,
address recovery,
uint256 deadline,
bytes calldata sig
) external whenNotPaused {
/* Revert if the caller does not own an fid */
uint256 ownerId = idOf[owner];
if (ownerId == 0) revert HasNoId();
_verifyChangeRecoveryAddressSig({fid: ownerId, recovery: recovery, deadline: deadline, signer: owner, sig: sig});
/* Change the recovery address */
recoveryOf[ownerId] = recovery;
emit ChangeRecoveryAddress(ownerId, recovery);
}
/**
* @inheritdoc IIdRegistry
*/
function recover(address from, address to, uint256 deadline, bytes calldata sig) external {
/* Revert if from does not own an fid */
uint256 fromId = idOf[from];
if (fromId == 0) revert HasNoId();
/* Revert if the caller is not the recovery address */
address caller = msg.sender;
if (recoveryOf[fromId] != caller) revert Unauthorized();
/* Revert if destination(to) already has an fid */
if (idOf[to] != 0) revert HasId();
/* Revert if signature is invalid */
_verifyTransferSig({fid: fromId, to: to, deadline: deadline, signer: to, sig: sig});
emit Recover(from, to, fromId);
_unsafeTransfer(fromId, from, to);
}
/**
* @inheritdoc IIdRegistry
*/
function recoverFor(
address from,
address to,
uint256 recoveryDeadline,
bytes calldata recoverySig,
uint256 toDeadline,
bytes calldata toSig
) external {
/* Revert if from does not own an fid */
uint256 fromId = idOf[from];
if (fromId == 0) revert HasNoId();
/* Revert if destination(to) already has an fid */
if (idOf[to] != 0) revert HasId();
/* Revert if either signature is invalid */
_verifyTransferSig({
fid: fromId,
to: to,
deadline: recoveryDeadline,
signer: recoveryOf[fromId],
sig: recoverySig
});
_verifyTransferSig({fid: fromId, to: to, deadline: toDeadline, signer: to, sig: toSig});
emit Recover(from, to, fromId);
_unsafeTransfer(fromId, from, to);
}
/*//////////////////////////////////////////////////////////////
PERMISSIONED ACTIONS
//////////////////////////////////////////////////////////////*/
/**
* @inheritdoc IIdRegistry
*/
function pause() external onlyOwner {
_pause();
}
/**
* @inheritdoc IIdRegistry
*/
function unpause() external onlyOwner {
_unpause();
}
/*//////////////////////////////////////////////////////////////
VIEWS
//////////////////////////////////////////////////////////////*/
/**
* @inheritdoc IIdRegistry
*/
function verifyFidSignature(
address custodyAddress,
uint256 fid,
bytes32 digest,
bytes calldata sig
) external view returns (bool isValid) {
isValid = idOf[custodyAddress] == fid && SignatureChecker.isValidSignatureNow(custodyAddress, digest, sig);
}
/*//////////////////////////////////////////////////////////////
SIGNATURE VERIFICATION HELPERS
//////////////////////////////////////////////////////////////*/
function _verifyRegisterSig(address to, address recovery, uint256 deadline, bytes memory sig) internal {
_verifySig(
_hashTypedDataV4(keccak256(abi.encode(REGISTER_TYPEHASH, to, recovery, _useNonce(to), deadline))),
to,
deadline,
sig
);
}
function _verifyTransferSig(uint256 fid, address to, uint256 deadline, address signer, bytes memory sig) internal {
_verifySig(
_hashTypedDataV4(keccak256(abi.encode(TRANSFER_TYPEHASH, fid, to, _useNonce(signer), deadline))),
signer,
deadline,
sig
);
}
function _verifyChangeRecoveryAddressSig(
uint256 fid,
address recovery,
uint256 deadline,
address signer,
bytes memory sig
) internal {
_verifySig(
_hashTypedDataV4(
keccak256(abi.encode(CHANGE_RECOVERY_ADDRESS_TYPEHASH, fid, recovery, _useNonce(signer), deadline))
),
signer,
deadline,
sig
);
}
}// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.21;
import {AggregatorV3Interface} from "chainlink/v0.8/interfaces/AggregatorV3Interface.sol";
import {AccessControlEnumerable} from "openzeppelin/contracts/access/AccessControlEnumerable.sol";
import {Pausable} from "openzeppelin/contracts/security/Pausable.sol";
import {FixedPointMathLib} from "solmate/src/utils/FixedPointMathLib.sol";
import {IStorageRegistry} from "./interfaces/IStorageRegistry.sol";
import {TransferHelper} from "./lib/TransferHelper.sol";
/**
* @title Farcaster StorageRegistry
*
* @notice See https://github.com/farcasterxyz/contracts/blob/v3.0.0/docs/docs.md for an overview.
*
* @custom:security-contact [email protected]
*/
contract StorageRegistry is IStorageRegistry, AccessControlEnumerable, Pausable {
using FixedPointMathLib for uint256;
using TransferHelper for address;
/*//////////////////////////////////////////////////////////////
ERRORS
//////////////////////////////////////////////////////////////*/
/// @dev Revert if the caller attempts to rent storage after the contract is deprecated.
error ContractDeprecated();
/// @dev Revert if the caller attempts to rent more storage than is available.
error ExceedsCapacity();
/// @dev Revert if the caller attempts to rent zero units.
error InvalidAmount();
/// @dev Revert if the caller attempts a batch rent with mismatched input array lengths or an empty array.
error InvalidBatchInput();
/// @dev Revert if the caller provides the wrong payment amount.
error InvalidPayment();
/// @dev Revert if the price feed returns a stale answer.
error StaleAnswer();
/// @dev Revert if any data feed round is incomplete and has not yet generated an answer.
error IncompleteRound();
/// @dev Revert if any data feed returns a timestamp in the future.
error InvalidRoundTimestamp();
/// @dev Revert if the price feed returns a value greater than the min/max bound.
error PriceOutOfBounds();
/// @dev Revert if the price feed returns a zero or negative price.
error InvalidPrice();
/// @dev Revert if the sequencer uptime feed detects that the L2 sequencer is unavailable.
error SequencerDown();
/// @dev Revert if the L2 sequencer restarted less than L2_DOWNTIME_GRACE_PERIOD seconds ago.
error GracePeriodNotOver();
/// @dev Revert if the deprecation timestamp parameter is in the past.
error InvalidDeprecationTimestamp();
/// @dev Revert if the priceFeedMinAnswer parameter is greater than or equal to priceFeedMaxAnswer.
error InvalidMinAnswer();
/// @dev Revert if the priceFeedMaxAnswer parameter is less than or equal to priceFeedMinAnswer.
error InvalidMaxAnswer();
/// @dev Revert if the fixedEthUsdPrice is outside the configured price bounds.
error InvalidFixedPrice();
/// @dev Revert if the caller is not an owner.
error NotOwner();
/// @dev Revert if the caller is not an operator.
error NotOperator();
/// @dev Revert if the caller is not a treasurer.
error NotTreasurer();
/// @dev Revert if the caller does not have an authorized role.
error Unauthorized();
/// @dev Revert if transferred to the zero address.
error InvalidAddress();
/// @dev Revert if the caller attempts a continuous credit withan invalid range.
error InvalidRangeInput();
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
/**
* @dev Emit an event when a caller pays rent for an fid's storage.
*
* Hubs increment the units assigned to the fid for exactly 395 days (1 year + grace) from
* the event timestamp. Hubs track this for unregistered fids and will assign units when
* the fid is registered. Storage credited to fid 0 is a no-op.
*
* @param payer Address of the account paying the storage rent.
* @param fid The fid that will receive the storage units.
* @param units The number of storage units being rented.
*/
event Rent(address indexed payer, uint256 indexed fid, uint256 units);
/**
* @dev Emit an event when an owner changes the price feed address.
*
* @param oldFeed The previous price feed address.
* @param newFeed The new price feed address.
*/
event SetPriceFeed(address oldFeed, address newFeed);
/**
* @dev Emit an event when an owner changes the uptime feed address.
*
* @param oldFeed The previous uptime feed address.
* @param newFeed The new uptime feed address.
*/
event SetUptimeFeed(address oldFeed, address newFeed);
/**
* @dev Emit an event when an owner changes the price of storage units.
*
* @param oldPrice The previous unit price in USD. Fixed point value with 8 decimals.
* @param newPrice The new unit price in USD. Fixed point value with 8 decimals.
*/
event SetPrice(uint256 oldPrice, uint256 newPrice);
/**
* @dev Emit an event when an owner changes the fixed ETH/USD price.
* Setting this value to zero means the fixed price is disabled.
*
* @param oldPrice The previous ETH price in USD. Fixed point value with 8 decimals.
* @param newPrice The new ETH price in USD. Fixed point value with 8 decimals.
*/
event SetFixedEthUsdPrice(uint256 oldPrice, uint256 newPrice);
/**
* @dev Emit an event when an owner changes the maximum supply of storage units.
*
* Hub operators should be made aware of changes to storage requirements before they occur
* since it may cause Hubs to fail if they do not allocate sufficient storage.
*
* @param oldMax The previous maximum amount.
* @param newMax The new maximum amount.
*/
event SetMaxUnits(uint256 oldMax, uint256 newMax);
/**
* @dev Emit an event when an owner changes the deprecationTimestamp.
*
* Hubs will stop listening to events after the deprecationTimestamp. This can be used
* when cutting over to a new contract. Hubs assume the following invariants:
*
* 1. SetDeprecationTimestamp() is only emitted once.
*
* @param oldTimestamp The previous deprecationTimestamp.
* @param newTimestamp The new deprecationTimestamp.
*/
event SetDeprecationTimestamp(uint256 oldTimestamp, uint256 newTimestamp);
/**
* @dev Emit an event when an owner changes the priceFeedCacheDuration.
*
* @param oldDuration The previous priceFeedCacheDuration.
* @param newDuration The new priceFeedCacheDuration.
*/
event SetCacheDuration(uint256 oldDuration, uint256 newDuration);
/**
* @dev Emit an event when an owner changes the priceFeedMaxAge.
*
* @param oldAge The previous priceFeedMaxAge.
* @param newAge The new priceFeedMaxAge.
*/
event SetMaxAge(uint256 oldAge, uint256 newAge);
/**
* @dev Emit an event when an owner changes the priceFeedMinAnswer.
*
* @param oldPrice The previous priceFeedMinAnswer.
* @param newPrice The new priceFeedMaxAnswer.
*/
event SetMinAnswer(uint256 oldPrice, uint256 newPrice);
/**
* @dev Emit an event when an owner changes the priceFeedMaxAnswer.
*
* @param oldPrice The previous priceFeedMaxAnswer.
* @param newPrice The new priceFeedMaxAnswer.
*/
event SetMaxAnswer(uint256 oldPrice, uint256 newPrice);
/**
* @dev Emit an event when an owner changes the uptimeFeedGracePeriod.
*
* @param oldPeriod The previous uptimeFeedGracePeriod.
* @param newPeriod The new uptimeFeedGracePeriod.
*/
event SetGracePeriod(uint256 oldPeriod, uint256 newPeriod);
/**
* @dev Emit an event when an owner changes the vault.
*
* @param oldVault The previous vault.
* @param newVault The new vault.
*/
event SetVault(address oldVault, address newVault);
/**
* @dev Emit an event when a treasurer withdraws any contract balance to the vault.
*
* @param to Address of recipient.
* @param amount The amount of ether withdrawn.
*/
event Withdraw(address indexed to, uint256 amount);
/*//////////////////////////////////////////////////////////////
CONSTANTS
//////////////////////////////////////////////////////////////*/
/**
* @inheritdoc IStorageRegistry
*/
string public constant VERSION = "2023.08.23";
bytes32 internal constant OWNER_ROLE = keccak256("OWNER_ROLE");
bytes32 internal constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE");
bytes32 internal constant TREASURER_ROLE = keccak256("TREASURER_ROLE");
/*//////////////////////////////////////////////////////////////
PARAMETERS
//////////////////////////////////////////////////////////////*/
/**
* @inheritdoc IStorageRegistry
*/
AggregatorV3Interface public priceFeed;
/**
* @inheritdoc IStorageRegistry
*/
AggregatorV3Interface public uptimeFeed;
/**
* @inheritdoc IStorageRegistry
*/
uint256 public deprecationTimestamp;
/**
* @inheritdoc IStorageRegistry
*/
uint256 public usdUnitPrice;
/**
* @inheritdoc IStorageRegistry
*/
uint256 public fixedEthUsdPrice;
/**
* @inheritdoc IStorageRegistry
*/
uint256 public maxUnits;
/**
* @inheritdoc IStorageRegistry
*/
uint256 public priceFeedCacheDuration;
/**
* @inheritdoc IStorageRegistry
*/
uint256 public priceFeedMaxAge;
/**
* @inheritdoc IStorageRegistry
*/
uint256 public priceFeedMinAnswer;
/**
* @inheritdoc IStorageRegistry
*/
uint256 public priceFeedMaxAnswer;
/**
* @inheritdoc IStorageRegistry
*/
uint256 public uptimeFeedGracePeriod;
/**
* @inheritdoc IStorageRegistry
*/
address public vault;
/*//////////////////////////////////////////////////////////////
STORAGE
//////////////////////////////////////////////////////////////*/
/**
* @inheritdoc IStorageRegistry
*/
uint256 public rentedUnits;
/**
* @inheritdoc IStorageRegistry
*/
uint256 public ethUsdPrice;
/**
* @inheritdoc IStorageRegistry
*/
uint256 public prevEthUsdPrice;
/**
* @inheritdoc IStorageRegistry
*/
uint256 public lastPriceFeedUpdateTime;
/**
* @inheritdoc IStorageRegistry
*/
uint256 public lastPriceFeedUpdateBlock;
/*//////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/
/**
* @notice Set the price feed, uptime feed, and initial parameters.
*
* @param _priceFeed Chainlink ETH/USD price feed.
* @param _uptimeFeed Chainlink L2 sequencer uptime feed.
* @param _initialUsdUnitPrice Initial unit price in USD. Fixed point 8 decimal value.
* @param _initialMaxUnits Initial maximum capacity in storage units.
* @param _initialVault Initial vault address.
* @param _initialRoleAdmin Initial role admin address.
* @param _initialOwner Initial owner address.
* @param _initialOperator Initial operator address.
* @param _initialTreasurer Initial treasurer address.
*/
constructor(
AggregatorV3Interface _priceFeed,
AggregatorV3Interface _uptimeFeed,
uint256 _initialUsdUnitPrice,
uint256 _initialMaxUnits,
address _initialVault,
address _initialRoleAdmin,
address _initialOwner,
address _initialOperator,
address _initialTreasurer
) {
priceFeed = _priceFeed;
emit SetPriceFeed(address(0), address(_priceFeed));
uptimeFeed = _uptimeFeed;
emit SetUptimeFeed(address(0), address(_uptimeFeed));
deprecationTimestamp = block.timestamp + 365 days;
emit SetDeprecationTimestamp(0, deprecationTimestamp);
usdUnitPrice = _initialUsdUnitPrice;
emit SetPrice(0, _initialUsdUnitPrice);
maxUnits = _initialMaxUnits;
emit SetMaxUnits(0, _initialMaxUnits);
priceFeedCacheDuration = 1 days;
emit SetCacheDuration(0, 1 days);
priceFeedMaxAge = 2 hours;
emit SetMaxAge(0, 2 hours);
uptimeFeedGracePeriod = 1 hours;
emit SetGracePeriod(0, 1 hours);
priceFeedMinAnswer = 100e8; // 100 USD / ETH
emit SetMinAnswer(0, 100e8);
priceFeedMaxAnswer = 10_000e8; // 10_000 USD / ETH
emit SetMaxAnswer(0, 10_000e8);
vault = _initialVault;
emit SetVault(address(0), _initialVault);
_grantRole(DEFAULT_ADMIN_ROLE, _initialRoleAdmin);
_grantRole(OWNER_ROLE, _initialOwner);
_grantRole(OPERATOR_ROLE, _initialOperator);
_grantRole(TREASURER_ROLE, _initialTreasurer);
_refreshPrice();
}
/*//////////////////////////////////////////////////////////////
MODIFIERS
//////////////////////////////////////////////////////////////*/
modifier whenNotDeprecated() {
if (block.timestamp >= deprecationTimestamp) {
revert ContractDeprecated();
}
_;
}
modifier onlyOwner() {
if (!hasRole(OWNER_ROLE, msg.sender)) revert NotOwner();
_;
}
modifier onlyOperator() {
if (!hasRole(OPERATOR_ROLE, msg.sender)) revert NotOperator();
_;
}
modifier onlyTreasurer() {
if (!hasRole(TREASURER_ROLE, msg.sender)) revert NotTreasurer();
_;
}
/*//////////////////////////////////////////////////////////////
STORAGE RENTAL LOGIC
//////////////////////////////////////////////////////////////*/
/**
* @inheritdoc IStorageRegistry
*/
function rent(
uint256 fid,
uint256 units
) external payable whenNotDeprecated whenNotPaused returns (uint256 overpayment) {
// Checks
if (units == 0) revert InvalidAmount();
if (rentedUnits + units > maxUnits) revert ExceedsCapacity();
uint256 totalPrice = _price(units);
if (msg.value < totalPrice) revert InvalidPayment();
// Effects
rentedUnits += units;
emit Rent(msg.sender, fid, units);
// Interactions
// Safety: overpayment is guaranteed to be >=0 because of checks
overpayment = msg.value - totalPrice;
if (overpayment > 0) {
msg.sender.sendNative(overpayment);
}
}
/**
* @inheritdoc IStorageRegistry
*/
function batchRent(
uint256[] calldata fids,
uint256[] calldata units
) external payable whenNotDeprecated whenNotPaused {
// Pre-checks
if (fids.length == 0 || units.length == 0) revert InvalidBatchInput();
if (fids.length != units.length) revert InvalidBatchInput();
// Effects
uint256 _usdPrice = usdUnitPrice;
uint256 _ethPrice = _ethUsdPrice();
uint256 totalQty;
for (uint256 i; i < fids.length; ++i) {
uint256 qty = units[i];
if (qty == 0) continue;
totalQty += qty;
emit Rent(msg.sender, fids[i], qty);
}
uint256 totalPrice = _price(totalQty, _usdPrice, _ethPrice);
// Post-checks
if (msg.value < totalPrice) revert InvalidPayment();
if (rentedUnits + totalQty > maxUnits) revert ExceedsCapacity();
// Effects
rentedUnits += totalQty;
// Interactions
if (msg.value > totalPrice) {
msg.sender.sendNative(msg.value - totalPrice);
}
}
/*//////////////////////////////////////////////////////////////
PRICE VIEWS
//////////////////////////////////////////////////////////////*/
/**
* @inheritdoc IStorageRegistry
*/
function unitPrice() external view returns (uint256) {
return price(1);
}
/**
* @inheritdoc IStorageRegistry
*/
function price(uint256 units) public view returns (uint256) {
uint256 ethPrice;
if (fixedEthUsdPrice != 0) {
ethPrice = fixedEthUsdPrice;
/**
* Slither flags the following line as a dangerous strict equality, but we want to
* make an exact comparison here and are not using this value in the context
* this detector rule describes.
*/
// slither-disable-next-line incorrect-equality
} else if (lastPriceFeedUpdateBlock == block.number) {
ethPrice = prevEthUsdPrice;
} else {
ethPrice = ethUsdPrice;
}
return _price(units, usdUnitPrice, ethPrice);
}
/**
* @dev Return the fixed price if present and the cached ethUsdPrice if it is not. If cached
* price is no longer valid, refresh the cache from the price feed but return the cached
* price for the rest of this block to avoid unexpected price changes.
*/
function _ethUsdPrice() internal returns (uint256) {
/**
* If a fixed ETH/USD price is set, use it. This disables external calls
* to the price feed in case of emergency.
*/
if (fixedEthUsdPrice != 0) return fixedEthUsdPrice;
/**
* If cache duration has expired, get the latest price from the price feed.
* This updates prevEthUsdPrice, ethUsdPrice, lastPriceFeedUpdateTime, and
* lastPriceFeedUpdateBlock.
*/
if (block.timestamp - lastPriceFeedUpdateTime > priceFeedCacheDuration) {
_refreshPrice();
}
/**
* We want price changes to take effect in the first block after the price
* refresh, rather than immediately, to keep the price from changing intra
* block. If we update the price in this block, use the previous price
* until the next block. Otherwise, use the latest price.
*
* Slither flags this line as a dangerous strict equality, but we want to
* make an exact comparison here and are not using this value in the context
* this detector rule describes.
*/
// slither-disable-next-line incorrect-equality
return (lastPriceFeedUpdateBlock == block.number) ? prevEthUsdPrice : ethUsdPrice;
}
/**
* @dev Get the latest ETH/USD price from the price feed and update the cached price.
*/
function _refreshPrice() internal {
/**
* Get and validate the L2 sequencer status.
* We ignore the deprecated answeredInRound value.
*/
// slither-disable-next-line unused-return
(uint80 uptimeRoundId, int256 sequencerUp, uint256 uptimeStartedAt, uint256 uptimeUpdatedAt,) =
uptimeFeed.latestRoundData();
if (sequencerUp != 0) revert SequencerDown();
if (uptimeRoundId == 0) revert IncompleteRound();
if (uptimeUpdatedAt == 0) revert IncompleteRound();
if (uptimeUpdatedAt > block.timestamp) revert InvalidRoundTimestamp();
/* If the L2 sequencer recently restarted, ensure the grace period has elapsed. */
uint256 timeSinceUp = block.timestamp - uptimeStartedAt;
if (timeSinceUp < uptimeFeedGracePeriod) revert GracePeriodNotOver();
/**
* Get and validate the Chainlink ETH/USD price. Validate that the answer is a positive
* value, the round is complete, and the answer is not stale by round.
*
* Ignore the deprecated answeredInRound value.
*
* Ignore the price feed startedAt value, which isn't used in validations, since the
* priceUpdatedAt timestamp is more meaningful.
*
* Slither flags this as an unused return value error, but this is safe since
* we use priceUpdatedAt and are interested in the latest value.
*/
// slither-disable-next-line unused-return
(uint80 priceRoundId, int256 answer,, uint256 priceUpdatedAt,) = priceFeed.latestRoundData();
if (answer <= 0) revert InvalidPrice();
if (priceRoundId == 0) revert IncompleteRound();
if (priceUpdatedAt == 0) revert IncompleteRound();
if (priceUpdatedAt > block.timestamp) revert InvalidRoundTimestamp();
if (block.timestamp - priceUpdatedAt > priceFeedMaxAge) {
revert StaleAnswer();
}
if (uint256(answer) < priceFeedMinAnswer || uint256(answer) > priceFeedMaxAnswer) revert PriceOutOfBounds();
/* Set the last update timestamp and block. */
lastPriceFeedUpdateTime = block.timestamp;
lastPriceFeedUpdateBlock = block.number;
if (prevEthUsdPrice == 0 && ethUsdPrice == 0) {
/* If this is the very first price update, set previous equal to latest. */
prevEthUsdPrice = ethUsdPrice = uint256(answer);
} else {
prevEthUsdPrice = ethUsdPrice;
ethUsdPrice = uint256(answer);
}
}
/**
* @dev Calculate the cost in wei to rent storage units.
*/
function _price(uint256 units) internal returns (uint256) {
return _price(units, usdUnitPrice, _ethUsdPrice());
}
/**
* @dev Calculate the cost in wei to rent storage units.
*
* @param units Number of storage units. Integer, no decimals.
* @param usdPerUnit Unit price in USD. Fixed point with 8 decimals.
* @param usdPerEth ETH/USD price. Fixed point with 8 decimals.
*
* @return uint256 price in wei, i.e. 18 decimals.
*/
function _price(uint256 units, uint256 usdPerUnit, uint256 usdPerEth) internal pure returns (uint256) {
return (units * usdPerUnit).divWadUp(usdPerEth);
}
/*//////////////////////////////////////////////////////////////
PERMISSIONED ACTIONS
//////////////////////////////////////////////////////////////*/
/**
* @inheritdoc IStorageRegistry
*/
function credit(uint256 fid, uint256 units) external onlyOperator whenNotDeprecated whenNotPaused {
if (units == 0) revert InvalidAmount();
if (rentedUnits + units > maxUnits) revert ExceedsCapacity();
rentedUnits += units;
emit Rent(msg.sender, fid, units);
}
/**
* @inheritdoc IStorageRegistry
*/
function batchCredit(
uint256[] calldata fids,
uint256 units
) external onlyOperator whenNotDeprecated whenNotPaused {
if (units == 0) revert InvalidAmount();
uint256 totalUnits = fids.length * units;
if (rentedUnits + totalUnits > maxUnits) revert ExceedsCapacity();
rentedUnits += totalUnits;
for (uint256 i; i < fids.length; ++i) {
emit Rent(msg.sender, fids[i], units);
}
}
/**
* @inheritdoc IStorageRegistry
*/
function continuousCredit(
uint256 start,
uint256 end,
uint256 units
) external onlyOperator whenNotDeprecated whenNotPaused {
if (units == 0) revert InvalidAmount();
if (start >= end) revert InvalidRangeInput();
uint256 len = end - start + 1;
uint256 totalUnits = len * units;
if (rentedUnits + totalUnits > maxUnits) revert ExceedsCapacity();
rentedUnits += totalUnits;
for (uint256 i; i < len; ++i) {
emit Rent(msg.sender, start + i, units);
}
}
/**
* @inheritdoc IStorageRegistry
*/
function refreshPrice() external {
if (!hasRole(OWNER_ROLE, msg.sender) && !hasRole(TREASURER_ROLE, msg.sender)) revert Unauthorized();
_refreshPrice();
}
/**
* @inheritdoc IStorageRegistry
*/
function setPriceFeed(AggregatorV3Interface feed) external onlyOwner {
emit SetPriceFeed(address(priceFeed), address(feed));
priceFeed = feed;
}
/**
* @inheritdoc IStorageRegistry
*/
function setUptimeFeed(AggregatorV3Interface feed) external onlyOwner {
emit SetUptimeFeed(address(uptimeFeed), address(feed));
uptimeFeed = feed;
}
/**
* @inheritdoc IStorageRegistry
*/
function setPrice(uint256 usdPrice) external onlyOwner {
emit SetPrice(usdUnitPrice, usdPrice);
usdUnitPrice = usdPrice;
}
/**
* @inheritdoc IStorageRegistry
*/
function setFixedEthUsdPrice(uint256 fixedPrice) external onlyOwner {
if (fixedPrice != 0) {
if (fixedPrice < priceFeedMinAnswer || fixedPrice > priceFeedMaxAnswer) revert InvalidFixedPrice();
}
emit SetFixedEthUsdPrice(fixedEthUsdPrice, fixedPrice);
fixedEthUsdPrice = fixedPrice;
}
/**
* @inheritdoc IStorageRegistry
*/
function setMaxUnits(uint256 max) external onlyOwner {
emit SetMaxUnits(maxUnits, max);
maxUnits = max;
}
/**
* @inheritdoc IStorageRegistry
*/
function setDeprecationTimestamp(uint256 timestamp) external onlyOwner {
if (timestamp < block.timestamp) revert InvalidDeprecationTimestamp();
emit SetDeprecationTimestamp(deprecationTimestamp, timestamp);
deprecationTimestamp = timestamp;
}
/**
* @inheritdoc IStorageRegistry
*/
function setCacheDuration(uint256 duration) external onlyOwner {
emit SetCacheDuration(priceFeedCacheDuration, duration);
priceFeedCacheDuration = duration;
}
/**
* @inheritdoc IStorageRegistry
*/
function setMaxAge(uint256 age) external onlyOwner {
emit SetMaxAge(priceFeedMaxAge, age);
priceFeedMaxAge = age;
}
/**
* @inheritdoc IStorageRegistry
*/
function setMinAnswer(uint256 minPrice) external onlyOwner {
if (minPrice >= priceFeedMaxAnswer) revert InvalidMinAnswer();
emit SetMinAnswer(priceFeedMinAnswer, minPrice);
priceFeedMinAnswer = minPrice;
}
/**
* @inheritdoc IStorageRegistry
*/
function setMaxAnswer(uint256 maxPrice) external onlyOwner {
if (maxPrice <= priceFeedMinAnswer) revert InvalidMaxAnswer();
emit SetMaxAnswer(priceFeedMaxAnswer, maxPrice);
priceFeedMaxAnswer = maxPrice;
}
/**
* @inheritdoc IStorageRegistry
*/
function setGracePeriod(uint256 period) external onlyOwner {
emit SetGracePeriod(uptimeFeedGracePeriod, period);
uptimeFeedGracePeriod = period;
}
/**
* @inheritdoc IStorageRegistry
*/
function setVault(address vaultAddr) external onlyOwner {
if (vaultAddr == address(0)) revert InvalidAddress();
emit SetVault(vault, vaultAddr);
vault = vaultAddr;
}
/**
* @inheritdoc IStorageRegistry
*/
function withdraw(uint256 amount) external onlyTreasurer {
emit Withdraw(vault, amount);
vault.sendNative(amount);
}
/**
* @inheritdoc IStorageRegistry
*/
function pause() external onlyOwner {
_pause();
}
/**
* @inheritdoc IStorageRegistry
*/
function unpause() external onlyOwner {
_unpause();
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;
import {Nonces} from "openzeppelin-latest/contracts/utils/Nonces.sol";
import {Pausable} from "openzeppelin/contracts/security/Pausable.sol";
import {IdRegistry} from "./IdRegistry.sol";
import {IdRegistryLike} from "./interfaces/IdRegistryLike.sol";
import {IKeyRegistry} from "./interfaces/IKeyRegistry.sol";
import {IMetadataValidator} from "./interfaces/IMetadataValidator.sol";
import {EIP712} from "./lib/EIP712.sol";
import {Signatures} from "./lib/Signatures.sol";
import {TrustedCaller} from "./lib/TrustedCaller.sol";
/**
* @title Farcaster KeyRegistry
*
* @notice See https://github.com/farcasterxyz/contracts/blob/v3.0.0/docs/docs.md for an overview.
*
* @custom:security-contact [email protected]
*/
contract KeyRegistry is IKeyRegistry, TrustedCaller, Signatures, Pausable, EIP712, Nonces {
/*//////////////////////////////////////////////////////////////
ERRORS
//////////////////////////////////////////////////////////////*/
/// @dev Revert if a key violates KeyState transition rules.
error InvalidState();
/// @dev Revert if a validator has not been registered for this keyType and metadataType.
error ValidatorNotFound(uint32 keyType, uint8 metadataType);
/// @dev Revert if metadata validation failed.
error InvalidMetadata();
/// @dev Revert if the admin sets a validator for keyType 0.
error InvalidKeyType();
/// @dev Revert if the admin sets a validator for metadataType 0.
error InvalidMetadataType();
/// @dev Revert if the caller does not have the authority to perform the action.
error Unauthorized();
/// @dev Revert if the owner calls migrateKeys more than once.
error AlreadyMigrated();
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
/**
* @dev Emit an event when an admin or fid adds a new key.
*
* Hubs listen for this, validate that keyBytes is an EdDSA pub key and keyType == 1 and
* add keyBytes to its SignerStore. Messages signed by keyBytes with `fid` are now valid
* and accepted over gossip, sync and client apis. Hubs assume the invariants:
*
* 1. Add(fid, ..., key, keyBytes, ...) cannot emit if there is an earlier emit with
* Add(fid, ..., key, keyBytes, ...) and no AdminReset(fid, key, keyBytes) inbetween.
*
* 2. Add(fid, ..., key, keyBytes, ...) cannot emit if there is an earlier emit with
* Remove(fid, key, keyBytes).
*
* 3. For all Add(..., ..., key, keyBytes, ...), key = keccak(keyBytes)
*
* @param fid The fid associated with the key.
* @param keyType The type of the key.
* @param key The key being registered. (indexed as hash)
* @param keyBytes The bytes of the key being registered.
* @param metadataType The type of the metadata.
* @param metadata Metadata about the key.
*/
event Add(
uint256 indexed fid,
uint32 indexed keyType,
bytes indexed key,
bytes keyBytes,
uint8 metadataType,
bytes metadata
);
/**
* @dev Emit an event when an fid removes an added key.
*
* Hubs listen for this, validate that keyType == 1 and keyBytes exists in its SignerStore.
* keyBytes is marked as removed, messages signed by keyBytes with `fid` are invalid,
* dropped immediately and no longer accepted. Hubs assume the invariants:
*
* 1. Remove(fid, key, keyBytes) cannot emit if there is no earlier emit with
* Add(fid, ..., key, keyBytes, ...)
*
* 2. Remove(fid, key, keyBytes) cannot emit if there is an earlier emit with
* Remove(fid, key, keyBytes)
*
* 3. For all Remove(..., key, keyBytes), key = keccak(keyBytes)
*
* @param fid The fid associated with the key.
* @param key The key being registered. (indexed as hash)
* @param keyBytes The bytes of the key being registered.
*/
event Remove(uint256 indexed fid, bytes indexed key, bytes keyBytes);
/**
* @dev Emit an event when an admin resets an added key.
*
* Hubs listen for this, validate that keyType == 1 and that keyBytes exists in its SignerStore.
* keyBytes is no longer tracked, messages signed by keyBytes with `fid` are invalid, dropped
* immediately and not accepted. Hubs assume the following invariants:
*
* 1. AdminReset(fid, key, keyBytes) cannot emit unless the most recent event for the fid
* was Add(fid, ..., key, keyBytes, ...).
*
* 2. For all AdminReset(..., key, keyBytes), key = keccak(keyBytes).
*
* 3. AdminReset() cannot emit after Migrated().
*
* @param fid The fid associated with the key.
* @param key The key being reset. (indexed as hash)
* @param keyBytes The bytes of the key being registered.
*/
event AdminReset(uint256 indexed fid, bytes indexed key, bytes keyBytes);
/**
* @dev Emit an event when the admin calls migrateKeys. Used to migrate Hubs from using
* offchain signers to onchain signers.
*
* Hubs listen for this and:
* 1. Stop accepting Farcaster Signer messages with a timestamp >= keysMigratedAt.
* 2. After the grace period (24 hours), stop accepting all Farcaster Signer messages.
* 3. Drop any messages created by off-chain Farcaster Signers whose pub key was
* not emitted as an Add event.
*
* If SignerMessages are not correctly migrated by an admin during the migration,
* there is a chance that there is some data loss, which is considered an acceptable
* risk for this migration.
*
* If this event is emitted incorrectly ahead of schedule, new users cannot not post
* and existing users cannot add new apps. A protocol upgrade will be necessary
* which could take up to 6 weeks to roll out correctly.
*
* @param keysMigratedAt The timestamp at which the migration occurred.
*/
event Migrated(uint256 indexed keysMigratedAt);
/**
* @dev Emit an event when the admin sets a metadata validator contract for a given
* keyType and metadataType.
*
* @param keyType The numeric keyType associated with this validator.
* @param metadataType The metadataType associated with this validator.
* @param oldValidator The previous validator contract address.
* @param newValidator The new validator contract address.
*/
event SetValidator(uint32 keyType, uint8 metadataType, address oldValidator, address newValidator);
/**
* @dev Emit an event when the admin sets a new IdRegistry contract address.
*
* @param oldIdRegistry The previous IdRegistry address.
* @param newIdRegistry The ne IdRegistry address.
*/
event SetIdRegistry(address oldIdRegistry, address newIdRegistry);
/*//////////////////////////////////////////////////////////////
CONSTANTS
//////////////////////////////////////////////////////////////*/
/**
* @inheritdoc IKeyRegistry
*/
string public constant VERSION = "2023.08.23";
/**
* @inheritdoc IKeyRegistry
*/
uint24 public constant gracePeriod = uint24(24 hours);
/**
* @inheritdoc IKeyRegistry
*/
bytes32 public constant ADD_TYPEHASH = keccak256(
"Add(address owner,uint32 keyType,bytes key,uint8 metadataType,bytes metadata,uint256 nonce,uint256 deadline)"
);
/**
* @inheritdoc IKeyRegistry
*/
bytes32 public constant REMOVE_TYPEHASH =
keccak256("Remove(address owner,bytes key,uint256 nonce,uint256 deadline)");
/*//////////////////////////////////////////////////////////////
STORAGE
//////////////////////////////////////////////////////////////*/
/**
* @inheritdoc IKeyRegistry
*/
IdRegistryLike public idRegistry;
/**
* @inheritdoc IKeyRegistry
*/
uint40 public keysMigratedAt;
/**
* @dev Mapping of fid to a key to the key's data.
*
* @custom:param fid The fid associated with the key.
* @custom:param key Bytes of the key.
* @custom:param data Struct with the state and key type. In the initial migration
* all keys will have data.keyType == 1.
*/
mapping(uint256 fid => mapping(bytes key => KeyData data)) public keys;
/**
* @dev Mapping of keyType to metadataType to validator contract.
*
* @custom:param keyType Numeric keyType.
* @custom:param metadataType Metadata metadataType.
* @custom:param validator Validator contract implementing IMetadataValidator.
*/
mapping(uint32 keyType => mapping(uint8 metadataType => IMetadataValidator validator)) public validators;
/*//////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/
/**
* @notice Set the IdRegistry and owner.
*
* @param _idRegistry IdRegistry contract address.
* @param _initialOwner Initial contract owner address.
*/
constructor(
address _idRegistry,
address _initialOwner
) TrustedCaller(_initialOwner) EIP712("Farcaster KeyRegistry", "1") {
idRegistry = IdRegistryLike(_idRegistry);
}
/*//////////////////////////////////////////////////////////////
VIEWS
//////////////////////////////////////////////////////////////*/
/**
* @inheritdoc IKeyRegistry
*/
function keyDataOf(uint256 fid, bytes calldata key) external view returns (KeyData memory) {
return keys[fid][key];
}
/**
* @inheritdoc IKeyRegistry
*/
function isMigrated() public view returns (bool) {
return keysMigratedAt != 0;
}
/*//////////////////////////////////////////////////////////////
REGISTRATION
//////////////////////////////////////////////////////////////*/
/**
* @inheritdoc IKeyRegistry
*/
function add(uint32 keyType, bytes calldata key, uint8 metadataType, bytes calldata metadata) external {
_add(_fidOf(msg.sender), keyType, key, metadataType, metadata);
}
/**
* @inheritdoc IKeyRegistry
*/
function addFor(
address fidOwner,
uint32 keyType,
bytes calldata key,
uint8 metadataType,
bytes calldata metadata,
uint256 deadline,
bytes calldata sig
) external {
_verifyAddSig(fidOwner, keyType, key, metadataType, metadata, deadline, sig);
_add(_fidOf(fidOwner), keyType, key, metadataType, metadata);
}
/**
* @inheritdoc IKeyRegistry
*/
function trustedAdd(
address fidOwner,
uint32 keyType,
bytes calldata key,
uint8 metadataType,
bytes calldata metadata
) external onlyTrustedCaller {
_add(_fidOf(fidOwner), keyType, key, metadataType, metadata);
}
/**
* @inheritdoc IKeyRegistry
*/
function remove(bytes calldata key) external {
_remove(_fidOf(msg.sender), key);
}
/**
* @inheritdoc IKeyRegistry
*/
function removeFor(address fidOwner, bytes calldata key, uint256 deadline, bytes calldata sig) external {
_verifyRemoveSig(fidOwner, key, deadline, sig);
_remove(_fidOf(fidOwner), key);
}
/*//////////////////////////////////////////////////////////////
MIGRATION
//////////////////////////////////////////////////////////////*/
/**
* @inheritdoc IKeyRegistry
*/
function migrateKeys() external onlyOwner {
if (isMigrated()) revert AlreadyMigrated();
keysMigratedAt = uint40(block.timestamp);
emit Migrated(keysMigratedAt);
}
/**
* @inheritdoc IKeyRegistry
*/
function bulkAddKeysForMigration(BulkAddData[] calldata items) external onlyOwner {
if (isMigrated() && block.timestamp > keysMigratedAt + gracePeriod) {
revert Unauthorized();
}
// Safety: i and j can be incremented unchecked since they are bound by items.length and
// items[i].keys.length respectively.
unchecked {
for (uint256 i = 0; i < items.length; i++) {
BulkAddData calldata item = items[i];
for (uint256 j = 0; j < item.keys.length; j++) {
// TODO: add note about griefing during migration
_add(item.fid, 1, item.keys[j].key, 1, item.keys[j].metadata);
}
}
}
}
/**
* @inheritdoc IKeyRegistry
*/
function bulkResetKeysForMigration(BulkResetData[] calldata items) external onlyOwner {
if (isMigrated() && block.timestamp > keysMigratedAt + gracePeriod) {
revert Unauthorized();
}
// Safety: i and j can be incremented unchecked since they are bound by items.length and
// items[i].keys.length respectively.
unchecked {
for (uint256 i = 0; i < items.length; i++) {
BulkResetData calldata item = items[i];
for (uint256 j = 0; j < item.keys.length; j++) {
// TODO: add note about griefing during migration
_reset(item.fid, item.keys[j]);
}
}
}
}
/*//////////////////////////////////////////////////////////////
ADMIN
//////////////////////////////////////////////////////////////*/
/**
* @inheritdoc IKeyRegistry
*/
function setValidator(uint32 keyType, uint8 metadataType, IMetadataValidator validator) external onlyOwner {
if (keyType == 0) revert InvalidKeyType();
if (metadataType == 0) revert InvalidMetadataType();
emit SetValidator(keyType, metadataType, address(validators[keyType][metadataType]), address(validator));
validators[keyType][metadataType] = validator;
}
/**
* @inheritdoc IKeyRegistry
*/
function setIdRegistry(address _idRegistry) external onlyOwner {
emit SetIdRegistry(address(idRegistry), _idRegistry);
idRegistry = IdRegistryLike(_idRegistry);
}
/**
* @inheritdoc IKeyRegistry
*/
function pause() external onlyOwner {
_pause();
}
/**
* @inheritdoc IKeyRegistry
*/
function unpause() external onlyOwner {
_unpause();
}
/*//////////////////////////////////////////////////////////////
HELPERS
//////////////////////////////////////////////////////////////*/
function _add(
uint256 fid,
uint32 keyType,
bytes calldata key,
uint8 metadataType,
bytes calldata metadata
) internal whenNotPaused {
KeyData storage keyData = keys[fid][key];
if (keyData.state != KeyState.NULL) revert InvalidState();
IMetadataValidator validator = validators[keyType][metadataType];
if (validator == IMetadataValidator(address(0))) {
revert ValidatorNotFound(keyType, metadataType);
}
bool isValid = validator.validate(fid, key, metadata);
if (!isValid) revert InvalidMetadata();
keyData.state = KeyState.ADDED;
keyData.keyType = keyType;
emit Add(fid, keyType, key, key, metadataType, metadata);
}
function _remove(uint256 fid, bytes calldata key) internal whenNotPaused {
KeyData storage keyData = keys[fid][key];
if (keyData.state != KeyState.ADDED) revert InvalidState();
keyData.state = KeyState.REMOVED;
emit Remove(fid, key, key);
}
function _reset(uint256 fid, bytes calldata key) internal whenNotPaused {
KeyData storage keyData = keys[fid][key];
if (keyData.state != KeyState.ADDED) revert InvalidState();
keyData.state = KeyState.NULL;
delete keyData.keyType;
emit AdminReset(fid, key, key);
}
/*//////////////////////////////////////////////////////////////
SIGNATURE VERIFICATION HELPERS
//////////////////////////////////////////////////////////////*/
function _verifyAddSig(
address fidOwner,
uint32 keyType,
bytes memory key,
uint8 metadataType,
bytes memory metadata,
uint256 deadline,
bytes memory sig
) internal {
_verifySig(
_hashTypedDataV4(
keccak256(
abi.encode(
ADD_TYPEHASH,
fidOwner,
keyType,
keccak256(key),
metadataType,
keccak256(metadata),
_useNonce(fidOwner),
deadline
)
)
),
fidOwner,
deadline,
sig
);
}
function _verifyRemoveSig(address fidOwner, bytes memory key, uint256 deadline, bytes memory sig) internal {
_verifySig(
_hashTypedDataV4(
keccak256(abi.encode(REMOVE_TYPEHASH, fidOwner, keccak256(key), _useNonce(fidOwner), deadline))
),
fidOwner,
deadline,
sig
);
}
/*//////////////////////////////////////////////////////////////
FID HELPERS
//////////////////////////////////////////////////////////////*/
function _fidOf(address fidOwner) internal view returns (uint256 fid) {
fid = idRegistry.idOf(fidOwner);
if (fid == 0) revert Unauthorized();
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
interface IBundler {
/*//////////////////////////////////////////////////////////////
STRUCTS
//////////////////////////////////////////////////////////////*/
/// @notice Data needed to trusted register a user with the fid and storage contracts.
struct UserData {
address to;
address recovery;
SignerData[] signers;
uint256 units;
}
/// @notice Data needed to trusted register a signer with the key registry
struct SignerData {
uint32 keyType;
bytes key;
uint8 metadataType;
bytes metadata;
}
/// @notice Data needed to register an fid with signature.
struct RegistrationParams {
address to;
address recovery;
uint256 deadline;
bytes sig;
}
/// @notice Data needed to add a signer with signature.
struct SignerParams {
uint32 keyType;
bytes key;
uint8 metadataType;
bytes metadata;
uint256 deadline;
bytes sig;
}
/*//////////////////////////////////////////////////////////////
CONSTANTS
//////////////////////////////////////////////////////////////*/
/**
* @notice Contract version specified in the Farcaster protocol version scheme.
*/
function VERSION() external view returns (string memory);
/*//////////////////////////////////////////////////////////////
REGISTRATION
//////////////////////////////////////////////////////////////*/
/**
* @notice Register an fid, multiple signers, and rent storage to an address in a single transaction.
*
* @param registration Struct containing registration parameters: to, recovery, deadline, and signature.
* @param signers Array of structs containing signer parameters: keyType, key, metadataType, metadata, deadline, and signature.
* @param storageUnits Number of storage units to rent
*
*/
function register(
RegistrationParams calldata registration,
SignerParams[] calldata signers,
uint256 storageUnits
) external payable;
/*//////////////////////////////////////////////////////////////
PERMISSIONED ACTIONS
//////////////////////////////////////////////////////////////*/
/**
* @notice Register an fid, add a signer, and credit storage to an address in a single transaction. Can only
* be called by the trustedCaller during the Seedable phase.
*
* @param user UserData struct including to/recovery address, key params, and number of storage units.
*/
function trustedRegister(UserData calldata user) external;
/**
* @notice Register fids, keys, and credit storage for multiple users in a single transaction. Can
* only be called by the trustedCaller during the Seedable phase. Will be used when
* migrating across Ethereum networks to bootstrap a new contract with existing data.
*
* @param users Array of UserData structs to register
*/
function trustedBatchRegister(UserData[] calldata users) external;
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;
import {Ownable2Step} from "openzeppelin/contracts/access/Ownable2Step.sol";
abstract contract TrustedCaller is Ownable2Step {
/*//////////////////////////////////////////////////////////////
ERRORS
//////////////////////////////////////////////////////////////*/
/// @dev Revert when an unauthorized caller calls a trusted function.
error OnlyTrustedCaller();
/// @dev Revert if trustedRegister is invoked after trustedCallerOnly is disabled.
error Registrable();
/// @dev Revert if register is invoked before trustedCallerOnly is disabled.
error Seedable();
/// @dev Revert when an invalid address is provided as input.
error InvalidAddress();
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
/**
* @dev Emit an event when the trusted caller is modified.
*
* @param oldCaller The address of the old trusted caller.
* @param newCaller The address of the new trusted caller.
* @param owner The address of the owner setting the new caller.
*/
event SetTrustedCaller(address indexed oldCaller, address indexed newCaller, address owner);
/**
* @dev Emit an event when the trustedOnly state is disabled.
*/
event DisableTrustedOnly();
/*//////////////////////////////////////////////////////////////
STORAGE
//////////////////////////////////////////////////////////////*/
/**
* @dev The privileged address that is allowed to call trusted functions.
*/
address public trustedCaller;
/**
* @dev Allows calling trusted functions when set 1, and disables trusted
* functions when set to 0. The value is set to 1 and can be changed to 0,
* but never back to 1.
*/
uint256 public trustedOnly = 1;
/*//////////////////////////////////////////////////////////////
MODIFIERS
//////////////////////////////////////////////////////////////*/
/**
* @dev Allow only the trusted caller to call the modified function.
*/
modifier onlyTrustedCaller() {
if (trustedOnly == 0) revert Registrable();
if (msg.sender != trustedCaller) revert OnlyTrustedCaller();
_;
}
/**
* @dev Prevent calling the modified function in trustedOnly mode.
*/
modifier whenNotTrusted() {
if (trustedOnly == 1) revert Seedable();
_;
}
/*//////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/
/**
* @param _initialOwner Initial contract owner address.
*/
constructor(address _initialOwner) {
_transferOwnership(_initialOwner);
}
/*//////////////////////////////////////////////////////////////
PERMISSIONED ACTIONS
//////////////////////////////////////////////////////////////*/
/**
* @notice Change the trusted caller by calling this from the contract's owner.
*
* @param _trustedCaller The address of the new trusted caller
*/
function setTrustedCaller(address _trustedCaller) public onlyOwner {
_setTrustedCaller(_trustedCaller);
}
/**
* @notice Disable trustedOnly mode. Must be called by the contract's owner.
*/
function disableTrustedOnly() external onlyOwner {
delete trustedOnly;
emit DisableTrustedOnly();
}
/*//////////////////////////////////////////////////////////////
INTERNAL FUNCTIONS
//////////////////////////////////////////////////////////////*/
/**
* @dev Internal helper to set trusted caller. Can be used internally
* to set the trusted caller at construction time.
*/
function _setTrustedCaller(address _trustedCaller) internal {
if (_trustedCaller == address(0)) revert InvalidAddress();
emit SetTrustedCaller(trustedCaller, _trustedCaller, msg.sender);
trustedCaller = _trustedCaller;
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;
library TransferHelper {
/// @dev Revert when a native token transfer fails.
error CallFailed();
/**
* @dev Native token transfer helper.
*/
function sendNative(address to, uint256 amount) internal {
bool success;
// solhint-disable-next-line no-inline-assembly
assembly {
// Transfer the native token and store if it succeeded or not.
success := call(gas(), to, amount, 0, 0, 0, 0)
}
if (!success) revert CallFailed();
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/cryptography/SignatureChecker.sol)
pragma solidity ^0.8.0;
import "./ECDSA.sol";
import "../../interfaces/IERC1271.sol";
/**
* @dev Signature verification helper that can be used instead of `ECDSA.recover` to seamlessly support both ECDSA
* signatures from externally owned accounts (EOAs) as well as ERC1271 signatures from smart contract wallets like
* Argent and Gnosis Safe.
*
* _Available since v4.1._
*/
library SignatureChecker {
/**
* @dev Checks if a signature is valid for a given signer and data hash. If the signer is a smart contract, the
* signature is validated against that smart contract using ERC1271, otherwise it's validated using `ECDSA.recover`.
*
* NOTE: Unlike ECDSA signatures, contract signatures are revocable, and the outcome of this function can thus
* change through time. It could return true at block N and false at block N+1 (or the opposite).
*/
function isValidSignatureNow(address signer, bytes32 hash, bytes memory signature) internal view returns (bool) {
(address recovered, ECDSA.RecoverError error) = ECDSA.tryRecover(hash, signature);
return
(error == ECDSA.RecoverError.NoError && recovered == signer) ||
isValidERC1271SignatureNow(signer, hash, signature);
}
/**
* @dev Checks if a signature is valid for a given signer and data hash. The signature is validated
* against the signer smart contract using ERC1271.
*
* NOTE: Unlike ECDSA signatures, contract signatures are revocable, and the outcome of this function can thus
* change through time. It could return true at block N and false at block N+1 (or the opposite).
*/
function isValidERC1271SignatureNow(
address signer,
bytes32 hash,
bytes memory signature
) internal view returns (bool) {
(bool success, bytes memory result) = signer.staticcall(
abi.encodeWithSelector(IERC1271.isValidSignature.selector, hash, signature)
);
return (success &&
result.length >= 32 &&
abi.decode(result, (bytes32)) == bytes32(IERC1271.isValidSignature.selector));
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
/**
* @dev Provides tracking nonces for addresses. Nonces will only increment.
*/
abstract contract Nonces {
/**
* @dev The nonce used for an `account` is not the expected current nonce.
*/
error InvalidAccountNonce(address account, uint256 currentNonce);
mapping(address => uint256) private _nonces;
/**
* @dev Returns an the next unused nonce for an address.
*/
function nonces(address owner) public view virtual returns (uint256) {
return _nonces[owner];
}
/**
* @dev Consumes a nonce.
*
* Returns the current value and increments nonce.
*/
function _useNonce(address owner) internal virtual returns (uint256) {
// For each account, the nonce has an initial value of 0, can only be incremented by one, and cannot be
// decremented or reset. This guarantees that the nonce never overflows.
unchecked {
// It is important to do x++ and not ++x here.
return _nonces[owner]++;
}
}
/**
* @dev Same as {_useNonce} but checking that `nonce` is the next valid for `owner`.
*/
function _useCheckedNonce(address owner, uint256 nonce) internal virtual returns (uint256) {
uint256 current = _useNonce(owner);
if (nonce != current) {
revert InvalidAccountNonce(owner, current);
}
return current;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol)
pragma solidity ^0.8.0;
import "../utils/Context.sol";
/**
* @dev Contract module which allows children to implement an emergency stop
* mechanism that can be triggered by an authorized account.
*
* This module is used through inheritance. It will make available the
* modifiers `whenNotPaused` and `whenPaused`, which can be applied to
* the functions of your contract. Note that they will not be pausable by
* simply including this module, only once the modifiers are put in place.
*/
abstract contract Pausable is Context {
/**
* @dev Emitted when the pause is triggered by `account`.
*/
event Paused(address account);
/**
* @dev Emitted when the pause is lifted by `account`.
*/
event Unpaused(address account);
bool private _paused;
/**
* @dev Initializes the contract in unpaused state.
*/
constructor() {
_paused = false;
}
/**
* @dev Modifier to make a function callable only when the contract is not paused.
*
* Requirements:
*
* - The contract must not be paused.
*/
modifier whenNotPaused() {
_requireNotPaused();
_;
}
/**
* @dev Modifier to make a function callable only when the contract is paused.
*
* Requirements:
*
* - The contract must be paused.
*/
modifier whenPaused() {
_requirePaused();
_;
}
/**
* @dev Returns true if the contract is paused, and false otherwise.
*/
function paused() public view virtual returns (bool) {
return _paused;
}
/**
* @dev Throws if the contract is paused.
*/
function _requireNotPaused() internal view virtual {
require(!paused(), "Pausable: paused");
}
/**
* @dev Throws if the contract is not paused.
*/
function _requirePaused() internal view virtual {
require(paused(), "Pausable: not paused");
}
/**
* @dev Triggers stopped state.
*
* Requirements:
*
* - The contract must not be paused.
*/
function _pause() internal virtual whenNotPaused {
_paused = true;
emit Paused(_msgSender());
}
/**
* @dev Returns to normal state.
*
* Requirements:
*
* - The contract must be paused.
*/
function _unpause() internal virtual whenPaused {
_paused = false;
emit Unpaused(_msgSender());
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
interface IIdRegistry {
/*//////////////////////////////////////////////////////////////
CONSTANTS
//////////////////////////////////////////////////////////////*/
/**
* @notice Defined for compatibility with tools like Etherscan that detect fid
* transfers as token transfers. This is intentionally lowercased.
*/
function name() external view returns (string memory);
/**
* @notice Contract version specified in the Farcaster protocol version scheme.
*/
function VERSION() external view returns (string memory);
/**
* @notice EIP-712 typehash for Register signatures.
*/
function REGISTER_TYPEHASH() external view returns (bytes32);
/**
* @notice EIP-712 typehash for Transfer signatures.
*/
function TRANSFER_TYPEHASH() external view returns (bytes32);
/**
* @notice EIP-712 typehash for ChangeRecoveryAddress signatures.
*/
function CHANGE_RECOVERY_ADDRESS_TYPEHASH() external view returns (bytes32);
/*//////////////////////////////////////////////////////////////
STORAGE
//////////////////////////////////////////////////////////////*/
/**
* @notice The last Farcaster id that was issued.
*/
function idCounter() external view returns (uint256);
/**
* @notice Maps each address to an fid, or zero if it does not own an fid.
*/
function idOf(address owner) external view returns (uint256 fid);
/**
* @notice Maps each fid to an address that can initiate a recovery.
*/
function recoveryOf(uint256 fid) external view returns (address recovery);
/*//////////////////////////////////////////////////////////////
REGISTRATION LOGIC
//////////////////////////////////////////////////////////////*/
/**
* @notice Register a new Farcaster ID (fid) to the caller. The caller must not have an fid.
* The contract must not be in the Registrable (trustedOnly = 0) state.
*
* @param recovery Address which can recover the fid. Set to zero to disable recovery.
*
* @return fid registered FID.
*/
function register(address recovery) external returns (uint256 fid);
/**
* @notice Register a new Farcaster ID (fid) to any address. A signed message from the address
* must be provided which approves both the to and the recovery. The address must not
* have an fid. The contract must be in the Registrable (trustedOnly = 0) state.
*
* @param to Address which will own the fid.
* @param recovery Address which can recover the fid. Set to zero to disable recovery.
* @param deadline Expiration timestamp of the signature.
* @param sig EIP-712 Register signature signed by the to address.
*
* @return fid registered FID.
*/
function registerFor(
address to,
address recovery,
uint256 deadline,
bytes calldata sig
) external returns (uint256 fid);
/*//////////////////////////////////////////////////////////////
TRANSFER LOGIC
//////////////////////////////////////////////////////////////*/
/**
* @notice Transfer the fid owned by this address to another address that does not have an fid.
* A signed Transfer message from the destination address must be provided.
*
* @param to The address to transfer the fid to.
* @param deadline Expiration timestamp of the signature.
* @param sig EIP-712 Transfer signature signed by the to address.
*/
function transfer(address to, uint256 deadline, bytes calldata sig) external;
/**
* @notice Transfer the fid owned by the from address to another address that does not
* have an fid. Caller must provide two signed Transfer messages: one signed by
* the from address and one signed by the to address.
*
* @param from The owner address of the fid to transfer.
* @param to The address to transfer the fid to.
* @param fromDeadline Expiration timestamp of the from signature.
* @param fromSig EIP-712 Transfer signature signed by the from address.
* @param toDeadline Expiration timestamp of the to signature.
* @param toSig EIP-712 Transfer signature signed by the to address.
*/
function transferFor(
address from,
address to,
uint256 fromDeadline,
bytes calldata fromSig,
uint256 toDeadline,
bytes calldata toSig
) external;
/*//////////////////////////////////////////////////////////////
RECOVERY LOGIC
//////////////////////////////////////////////////////////////*/
/**
* @notice Change the recovery address of the fid owned by the caller.
*
* @param recovery The address which can recover the fid. Set to 0x0 to disable recovery.
*/
function changeRecoveryAddress(address recovery) external;
/**
* @notice Change the recovery address of fid owned by the owner. Caller must provide an
* EIP-712 ChangeRecoveryAddress message signed by the owner.
*
* @param owner Custody address of the fid whose recovery address will be changed.
* @param recovery The address which can recover the fid. Set to 0x0 to disable recovery.
* @param deadline Expiration timestamp of the ChangeRecoveryAddress signature.
* @param sig EIP-712 ChangeRecoveryAddress message signed by the owner address.
*/
function changeRecoveryAddressFor(address owner, address recovery, uint256 deadline, bytes calldata sig) external;
/**
* @notice Transfer the fid from the from address to the to address. Must be called by the
* recovery address. A signed message from the to address must be provided.
*
* @param from The address that currently owns the fid.
* @param to The address to transfer the fid to.
* @param deadline Expiration timestamp of the signature.
* @param sig EIP-712 Transfer signature signed by the to address.
*/
function recover(address from, address to, uint256 deadline, bytes calldata sig) external;
/**
* @notice Transfer the fid owned by the from address to another address that does not
* have an fid. Caller must provide two signed Transfer messages: one signed by
* the recovery address and one signed by the to address.
*
* @param from The owner address of the fid to transfer.
* @param to The address to transfer the fid to.
* @param recoveryDeadline Expiration timestamp of the recovery signature.
* @param recoverySig EIP-712 Transfer signature signed by the recovery address.
* @param toDeadline Expiration timestamp of the to signature.
* @param toSig EIP-712 Transfer signature signed by the to address.
*/
function recoverFor(
address from,
address to,
uint256 recoveryDeadline,
bytes calldata recoverySig,
uint256 toDeadline,
bytes calldata toSig
) external;
/*//////////////////////////////////////////////////////////////
VIEWS
//////////////////////////////////////////////////////////////*/
/**
* @notice Verify that a signature was produced by the custody address that owns an fid.
*
* @param custodyAddress The address to check the signature of.
* @param fid The fid to check the signature of.
* @param digest The digest that was signed.
* @param sig The signature to check.
*
* @return isValid Whether provided signature is valid.
*/
function verifyFidSignature(
address custodyAddress,
uint256 fid,
bytes32 digest,
bytes calldata sig
) external view returns (bool isValid);
/*//////////////////////////////////////////////////////////////
PERMISSIONED ACTIONS
//////////////////////////////////////////////////////////////*/
/**
* @notice Register a new Farcaster ID (fid) to any address. The address must not have an fid.
* The contract must be in the Seedable (trustedOnly = 1) state.
* Can only be called by the trustedCaller.
*
* @param to The address which will own the fid.
* @param recovery The address which can recover the fid.
*
* @return fid registered FID.
*/
function trustedRegister(address to, address recovery) external returns (uint256 fid);
/**
* @notice Pause registration, transfer, and recovery.
* Must be called by the owner.
*/
function pause() external;
/**
* @notice Unpause registration, transfer, and recovery.
* Must be called by the owner.
*/
function unpause() external;
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;
import {EIP712 as EIP712Base} from "openzeppelin/contracts/utils/cryptography/EIP712.sol";
abstract contract EIP712 is EIP712Base {
constructor(string memory name, string memory version) EIP712Base(name, version) {}
/*//////////////////////////////////////////////////////////////
EIP-712 HELPERS
//////////////////////////////////////////////////////////////*/
/**
* @notice Helper view to read EIP-712 domain separator.
*
* @return bytes32 domain separator hash.
*/
function domainSeparatorV4() external view returns (bytes32) {
return _domainSeparatorV4();
}
/**
* @notice Helper view to hash EIP-712 typed data onchain.
*
* @param structHash EIP-712 typed data hash.
*
* @return bytes32 EIP-712 message digest.
*/
function hashTypedDataV4(bytes32 structHash) external view returns (bytes32) {
return _hashTypedDataV4(structHash);
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;
import {SignatureChecker} from "openzeppelin/contracts/utils/cryptography/SignatureChecker.sol";
abstract contract Signatures {
/*//////////////////////////////////////////////////////////////
ERRORS
//////////////////////////////////////////////////////////////*/
/// @dev Revert when the signature provided is invalid.
error InvalidSignature();
/// @dev Revert when the block.timestamp is ahead of the signature deadline.
error SignatureExpired();
/*//////////////////////////////////////////////////////////////
SIGNATURE VERIFICATION HELPERS
//////////////////////////////////////////////////////////////*/
function _verifySig(bytes32 digest, address signer, uint256 deadline, bytes memory sig) internal view {
if (block.timestamp >= deadline) revert SignatureExpired();
if (!SignatureChecker.isValidSignatureNow(signer, digest, sig)) {
revert InvalidSignature();
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface AggregatorV3Interface {
function decimals()
external
view
returns (
uint8
);
function description()
external
view
returns (
string memory
);
function version()
external
view
returns (
uint256
);
// getRoundData and latestRoundData should both raise "No data present"
// if they do not have data to report, instead of returning unset values
// which could be misinterpreted as actual reported values.
function getRoundData(
uint80 _roundId
)
external
view
returns (
uint80 roundId,
int256 answer,
uint256 startedAt,
uint256 updatedAt,
uint80 answeredInRound
);
function latestRoundData()
external
view
returns (
uint80 roundId,
int256 answer,
uint256 startedAt,
uint256 updatedAt,
uint80 answeredInRound
);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (access/AccessControlEnumerable.sol)
pragma solidity ^0.8.0;
import "./IAccessControlEnumerable.sol";
import "./AccessControl.sol";
import "../utils/structs/EnumerableSet.sol";
/**
* @dev Extension of {AccessControl} that allows enumerating the members of each role.
*/
abstract contract AccessControlEnumerable is IAccessControlEnumerable, AccessControl {
using EnumerableSet for EnumerableSet.AddressSet;
mapping(bytes32 => EnumerableSet.AddressSet) private _roleMembers;
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IAccessControlEnumerable).interfaceId || super.supportsInterface(interfaceId);
}
/**
* @dev Returns one of the accounts that have `role`. `index` must be a
* value between 0 and {getRoleMemberCount}, non-inclusive.
*
* Role bearers are not sorted in any particular way, and their ordering may
* change at any point.
*
* WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure
* you perform all queries on the same block. See the following
* https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]
* for more information.
*/
function getRoleMember(bytes32 role, uint256 index) public view virtual override returns (address) {
return _roleMembers[role].at(index);
}
/**
* @dev Returns the number of accounts that have `role`. Can be used
* together with {getRoleMember} to enumerate all bearers of a role.
*/
function getRoleMemberCount(bytes32 role) public view virtual override returns (uint256) {
return _roleMembers[role].length();
}
/**
* @dev Overload {_grantRole} to track enumerable memberships
*/
function _grantRole(bytes32 role, address account) internal virtual override {
super._grantRole(role, account);
_roleMembers[role].add(account);
}
/**
* @dev Overload {_revokeRole} to track enumerable memberships
*/
function _revokeRole(bytes32 role, address account) internal virtual override {
super._revokeRole(role, account);
_roleMembers[role].remove(account);
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;
/// @notice Arithmetic library with operations for fixed-point numbers.
/// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/FixedPointMathLib.sol)
library FixedPointMathLib {
/*//////////////////////////////////////////////////////////////
SIMPLIFIED FIXED POINT OPERATIONS
//////////////////////////////////////////////////////////////*/
uint256 internal constant WAD = 1e18; // The scalar of ETH and most ERC20s.
function mulWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivDown(x, y, WAD); // Equivalent to (x * y) / WAD rounded down.
}
function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivUp(x, y, WAD); // Equivalent to (x * y) / WAD rounded up.
}
function divWadDown(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivDown(x, WAD, y); // Equivalent to (x * WAD) / y rounded down.
}
function divWadUp(uint256 x, uint256 y) internal pure returns (uint256) {
return mulDivUp(x, WAD, y); // Equivalent to (x * WAD) / y rounded up.
}
function powWad(int256 x, int256 y) internal pure returns (int256) {
// Equivalent to x to the power of y because x ** y = (e ** ln(x)) ** y = e ** (ln(x) * y)
return expWad((lnWad(x) * y) / int256(WAD)); // Using ln(x) means x must be greater than 0.
}
function expWad(int256 x) internal pure returns (int256 r) {
unchecked {
// When the result is < 0.5 we return zero. This happens when
// x <= floor(log(0.5e18) * 1e18) ~ -42e18
if (x <= -42139678854452767551) return 0;
// When the result is > (2**255 - 1) / 1e18 we can not represent it as an
// int. This happens when x >= floor(log((2**255 - 1) / 1e18) * 1e18) ~ 135.
if (x >= 135305999368893231589) revert("EXP_OVERFLOW");
// x is now in the range (-42, 136) * 1e18. Convert to (-42, 136) * 2**96
// for more intermediate precision and a binary basis. This base conversion
// is a multiplication by 1e18 / 2**96 = 5**18 / 2**78.
x = (x << 78) / 5**18;
// Reduce range of x to (-½ ln 2, ½ ln 2) * 2**96 by factoring out powers
// of two such that exp(x) = exp(x') * 2**k, where k is an integer.
// Solving this gives k = round(x / log(2)) and x' = x - k * log(2).
int256 k = ((x << 96) / 54916777467707473351141471128 + 2**95) >> 96;
x = x - k * 54916777467707473351141471128;
// k is in the range [-61, 195].
// Evaluate using a (6, 7)-term rational approximation.
// p is made monic, we'll multiply by a scale factor later.
int256 y = x + 1346386616545796478920950773328;
y = ((y * x) >> 96) + 57155421227552351082224309758442;
int256 p = y + x - 94201549194550492254356042504812;
p = ((p * y) >> 96) + 28719021644029726153956944680412240;
p = p * x + (4385272521454847904659076985693276 << 96);
// We leave p in 2**192 basis so we don't need to scale it back up for the division.
int256 q = x - 2855989394907223263936484059900;
q = ((q * x) >> 96) + 50020603652535783019961831881945;
q = ((q * x) >> 96) - 533845033583426703283633433725380;
q = ((q * x) >> 96) + 3604857256930695427073651918091429;
q = ((q * x) >> 96) - 14423608567350463180887372962807573;
q = ((q * x) >> 96) + 26449188498355588339934803723976023;
assembly {
// Div in assembly because solidity adds a zero check despite the unchecked.
// The q polynomial won't have zeros in the domain as all its roots are complex.
// No scaling is necessary because p is already 2**96 too large.
r := sdiv(p, q)
}
// r should be in the range (0.09, 0.25) * 2**96.
// We now need to multiply r by:
// * the scale factor s = ~6.031367120.
// * the 2**k factor from the range reduction.
// * the 1e18 / 2**96 factor for base conversion.
// We do this all at once, with an intermediate result in 2**213
// basis, so the final right shift is always by a positive amount.
r = int256((uint256(r) * 3822833074963236453042738258902158003155416615667) >> uint256(195 - k));
}
}
function lnWad(int256 x) internal pure returns (int256 r) {
unchecked {
require(x > 0, "UNDEFINED");
// We want to convert x from 10**18 fixed point to 2**96 fixed point.
// We do this by multiplying by 2**96 / 10**18. But since
// ln(x * C) = ln(x) + ln(C), we can simply do nothing here
// and add ln(2**96 / 10**18) at the end.
// Reduce range of x to (1, 2) * 2**96
// ln(2^k * x) = k * ln(2) + ln(x)
int256 k = int256(log2(uint256(x))) - 96;
x <<= uint256(159 - k);
x = int256(uint256(x) >> 159);
// Evaluate using a (8, 8)-term rational approximation.
// p is made monic, we will multiply by a scale factor later.
int256 p = x + 3273285459638523848632254066296;
p = ((p * x) >> 96) + 24828157081833163892658089445524;
p = ((p * x) >> 96) + 43456485725739037958740375743393;
p = ((p * x) >> 96) - 11111509109440967052023855526967;
p = ((p * x) >> 96) - 45023709667254063763336534515857;
p = ((p * x) >> 96) - 14706773417378608786704636184526;
p = p * x - (795164235651350426258249787498 << 96);
// We leave p in 2**192 basis so we don't need to scale it back up for the division.
// q is monic by convention.
int256 q = x + 5573035233440673466300451813936;
q = ((q * x) >> 96) + 71694874799317883764090561454958;
q = ((q * x) >> 96) + 283447036172924575727196451306956;
q = ((q * x) >> 96) + 401686690394027663651624208769553;
q = ((q * x) >> 96) + 204048457590392012362485061816622;
q = ((q * x) >> 96) + 31853899698501571402653359427138;
q = ((q * x) >> 96) + 909429971244387300277376558375;
assembly {
// Div in assembly because solidity adds a zero check despite the unchecked.
// The q polynomial is known not to have zeros in the domain.
// No scaling required because p is already 2**96 too large.
r := sdiv(p, q)
}
// r is in the range (0, 0.125) * 2**96
// Finalization, we need to:
// * multiply by the scale factor s = 5.549…
// * add ln(2**96 / 10**18)
// * add k * ln(2)
// * multiply by 10**18 / 2**96 = 5**18 >> 78
// mul s * 5e18 * 2**96, base is now 5**18 * 2**192
r *= 1677202110996718588342820967067443963516166;
// add ln(2) * k * 5e18 * 2**192
r += 16597577552685614221487285958193947469193820559219878177908093499208371 * k;
// add ln(2**96 / 10**18) * 5e18 * 2**192
r += 600920179829731861736702779321621459595472258049074101567377883020018308;
// base conversion: mul 2**18 / 2**192
r >>= 174;
}
}
/*//////////////////////////////////////////////////////////////
LOW LEVEL FIXED POINT OPERATIONS
//////////////////////////////////////////////////////////////*/
function mulDivDown(
uint256 x,
uint256 y,
uint256 denominator
) internal pure returns (uint256 z) {
assembly {
// Store x * y in z for now.
z := mul(x, y)
// Equivalent to require(denominator != 0 && (x == 0 || (x * y) / x == y))
if iszero(and(iszero(iszero(denominator)), or(iszero(x), eq(div(z, x), y)))) {
revert(0, 0)
}
// Divide z by the denominator.
z := div(z, denominator)
}
}
function mulDivUp(
uint256 x,
uint256 y,
uint256 denominator
) internal pure returns (uint256 z) {
assembly {
// Store x * y in z for now.
z := mul(x, y)
// Equivalent to require(denominator != 0 && (x == 0 || (x * y) / x == y))
if iszero(and(iszero(iszero(denominator)), or(iszero(x), eq(div(z, x), y)))) {
revert(0, 0)
}
// First, divide z - 1 by the denominator and add 1.
// We allow z - 1 to underflow if z is 0, because we multiply the
// end result by 0 if z is zero, ensuring we return 0 if z is zero.
z := mul(iszero(iszero(z)), add(div(sub(z, 1), denominator), 1))
}
}
function rpow(
uint256 x,
uint256 n,
uint256 scalar
) internal pure returns (uint256 z) {
assembly {
switch x
case 0 {
switch n
case 0 {
// 0 ** 0 = 1
z := scalar
}
default {
// 0 ** n = 0
z := 0
}
}
default {
switch mod(n, 2)
case 0 {
// If n is even, store scalar in z for now.
z := scalar
}
default {
// If n is odd, store x in z for now.
z := x
}
// Shifting right by 1 is like dividing by 2.
let half := shr(1, scalar)
for {
// Shift n right by 1 before looping to halve it.
n := shr(1, n)
} n {
// Shift n right by 1 each iteration to halve it.
n := shr(1, n)
} {
// Revert immediately if x ** 2 would overflow.
// Equivalent to iszero(eq(div(xx, x), x)) here.
if shr(128, x) {
revert(0, 0)
}
// Store x squared.
let xx := mul(x, x)
// Round to the nearest number.
let xxRound := add(xx, half)
// Revert if xx + half overflowed.
if lt(xxRound, xx) {
revert(0, 0)
}
// Set x to scaled xxRound.
x := div(xxRound, scalar)
// If n is even:
if mod(n, 2) {
// Compute z * x.
let zx := mul(z, x)
// If z * x overflowed:
if iszero(eq(div(zx, x), z)) {
// Revert if x is non-zero.
if iszero(iszero(x)) {
revert(0, 0)
}
}
// Round to the nearest number.
let zxRound := add(zx, half)
// Revert if zx + half overflowed.
if lt(zxRound, zx) {
revert(0, 0)
}
// Return properly scaled zxRound.
z := div(zxRound, scalar)
}
}
}
}
}
/*//////////////////////////////////////////////////////////////
GENERAL NUMBER UTILITIES
//////////////////////////////////////////////////////////////*/
function sqrt(uint256 x) internal pure returns (uint256 z) {
assembly {
let y := x // We start y at x, which will help us make our initial estimate.
z := 181 // The "correct" value is 1, but this saves a multiplication later.
// This segment is to get a reasonable initial estimate for the Babylonian method. With a bad
// start, the correct # of bits increases ~linearly each iteration instead of ~quadratically.
// We check y >= 2^(k + 8) but shift right by k bits
// each branch to ensure that if x >= 256, then y >= 256.
if iszero(lt(y, 0x10000000000000000000000000000000000)) {
y := shr(128, y)
z := shl(64, z)
}
if iszero(lt(y, 0x1000000000000000000)) {
y := shr(64, y)
z := shl(32, z)
}
if iszero(lt(y, 0x10000000000)) {
y := shr(32, y)
z := shl(16, z)
}
if iszero(lt(y, 0x1000000)) {
y := shr(16, y)
z := shl(8, z)
}
// Goal was to get z*z*y within a small factor of x. More iterations could
// get y in a tighter range. Currently, we will have y in [256, 256*2^16).
// We ensured y >= 256 so that the relative difference between y and y+1 is small.
// That's not possible if x < 256 but we can just verify those cases exhaustively.
// Now, z*z*y <= x < z*z*(y+1), and y <= 2^(16+8), and either y >= 256, or x < 256.
// Correctness can be checked exhaustively for x < 256, so we assume y >= 256.
// Then z*sqrt(y) is within sqrt(257)/sqrt(256) of sqrt(x), or about 20bps.
// For s in the range [1/256, 256], the estimate f(s) = (181/1024) * (s+1) is in the range
// (1/2.84 * sqrt(s), 2.84 * sqrt(s)), with largest error when s = 1 and when s = 256 or 1/256.
// Since y is in [256, 256*2^16), let a = y/65536, so that a is in [1/256, 256). Then we can estimate
// sqrt(y) using sqrt(65536) * 181/1024 * (a + 1) = 181/4 * (y + 65536)/65536 = 181 * (y + 65536)/2^18.
// There is no overflow risk here since y < 2^136 after the first branch above.
z := shr(18, mul(z, add(y, 65536))) // A mul() is saved from starting z at 181.
// Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough.
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
z := shr(1, add(z, div(x, z)))
// If x+1 is a perfect square, the Babylonian method cycles between
// floor(sqrt(x)) and ceil(sqrt(x)). This statement ensures we return floor.
// See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division
// Since the ceil is rare, we save gas on the assignment and repeat division in the rare case.
// If you don't care whether the floor or ceil square root is returned, you can remove this statement.
z := sub(z, lt(div(x, z), z))
}
}
function log2(uint256 x) internal pure returns (uint256 r) {
require(x > 0, "UNDEFINED");
assembly {
r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
r := or(r, shl(4, lt(0xffff, shr(r, x))))
r := or(r, shl(3, lt(0xff, shr(r, x))))
r := or(r, shl(2, lt(0xf, shr(r, x))))
r := or(r, shl(1, lt(0x3, shr(r, x))))
r := or(r, lt(0x1, shr(r, x)))
}
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;
import {AggregatorV3Interface} from "chainlink/v0.8/interfaces/AggregatorV3Interface.sol";
interface IStorageRegistry {
/*//////////////////////////////////////////////////////////////
CONSTANTS
//////////////////////////////////////////////////////////////*/
/**
* @notice Contract version specified in the Farcaster protocol version scheme.
*/
function VERSION() external view returns (string memory);
/*//////////////////////////////////////////////////////////////
PARAMETERS
//////////////////////////////////////////////////////////////*/
/**
* @notice Chainlink ETH/USD price feed.
*/
function priceFeed() external view returns (AggregatorV3Interface);
/**
* @notice Chainlink L2 sequencer uptime feed.
*/
function uptimeFeed() external view returns (AggregatorV3Interface);
/**
* @notice Block timestamp at which this contract will no longer accept storage rent payments.
* Changeable by owner.
*/
function deprecationTimestamp() external view returns (uint256);
/**
* @notice Price per storage unit in USD. Fixed point value with 8 decimals, e.g. 5e8 = $5 USD.
* Changeable by owner.
*/
function usdUnitPrice() external view returns (uint256);
/**
* @notice A fixed ETH/USD price which overrides the Chainlink feed. If this value is nonzero,
* we disable external calls to the price feed and use this price. Changeable by owner.
* To be used in the event of a price feed failure.
*/
function fixedEthUsdPrice() external view returns (uint256);
/**
* @notice Total capacity of storage units. Changeable by owner.
*/
function maxUnits() external view returns (uint256);
/**
* @notice Duration to cache ethUsdPrice before updating from the price feed. Changeable by owner.
*/
function priceFeedCacheDuration() external view returns (uint256);
/**
* @notice Max age of a price feed answer before it is considered stale. Changeable by owner.
*/
function priceFeedMaxAge() external view returns (uint256);
/**
* @notice Lower bound on acceptable price feed answer. Changeable by owner.
*/
function priceFeedMinAnswer() external view returns (uint256);
/**
* @notice Upper bound on acceptable price feed answer. Changeable by owner.
*/
function priceFeedMaxAnswer() external view returns (uint256);
/**
* @notice Period in seconds to wait after the L2 sequencer restarts before resuming rentals.
* See: https://docs.chain.link/data-feeds/l2-sequencer-feeds. Changeable by owner.
*/
function uptimeFeedGracePeriod() external view returns (uint256);
/**
* @notice Address to which the treasurer role can withdraw funds. Changeable by owner.
*/
function vault() external view returns (address);
/**
* @notice Total number of storage units that have been rented.
*/
function rentedUnits() external view returns (uint256);
/**
* @notice Cached Chainlink ETH/USD price.
*/
function ethUsdPrice() external view returns (uint256);
/**
* @notice Previously cached Chainlink ETH/USD price.
*/
function prevEthUsdPrice() external view returns (uint256);
/**
* @notice Timestamp of the last update to ethUsdPrice.
*/
function lastPriceFeedUpdateTime() external view returns (uint256);
/**
* @notice Block number of the last update to ethUsdPrice.
*/
function lastPriceFeedUpdateBlock() external view returns (uint256);
/*//////////////////////////////////////////////////////////////
STORAGE RENTAL LOGIC
//////////////////////////////////////////////////////////////*/
/**
* @notice Rent storage for a given fid for a year. The caller must provide at least
* price(units) wei of payment. Any excess payment will be refunded to the caller. Hubs
* will issue storage for 365 days + 30 day grace period after which it expires.
*
* RentedUnits is never decremented on the contract even as the assigned storage expires
* on the hubs. This is done to keep the contract simple since we expect to launch a new
* storage contract within the year and deprecate this one. Even if that does not occur,
* the existing maxUnits parameter can be tweaked to account for expired units.
*
* @param fid The fid that will receive the storage units.
* @param units Number of storage units to rent.
*/
function rent(uint256 fid, uint256 units) external payable returns (uint256 overpayment);
/**
* @notice Rent storage for multiple fids for a year. The caller must provide at least
* price(units) wei of payment where units is the sum of storage units requested across
* the fids. See comments on rent() for additional details.
*
* @param fids An array of fids.
* @param units An array of storage unit quantities. Must be the same length as the fids array.
*/
function batchRent(uint256[] calldata fids, uint256[] calldata units) external payable;
/*//////////////////////////////////////////////////////////////
PRICE VIEWS
//////////////////////////////////////////////////////////////*/
/**
* @notice Calculate the cost in wei to rent one storage unit.
*
* @return uint256 cost in wei.
*/
function unitPrice() external view returns (uint256);
/**
* @notice Calculate the cost in wei to rent the given number of storage units.
*
* @param units Number of storage units.
* @return uint256 cost in wei.
*/
function price(uint256 units) external view returns (uint256);
/*//////////////////////////////////////////////////////////////
PERMISSIONED ACTIONS
//////////////////////////////////////////////////////////////*/
/**
* @notice Credit a single fid with free storage units. Only callable by operator.
*
* @param fid The fid that will receive the credit.
* @param units Number of storage units to credit.
*/
function credit(uint256 fid, uint256 units) external;
/**
* @notice Credit multiple fids with free storage units. Only callable by operator.
*
* @param fids An array of fids.
* @param units Number of storage units per fid.
*/
function batchCredit(uint256[] calldata fids, uint256 units) external;
/**
* @notice Credit a continuous sequence of fids with free storage units. Only callable by operator.
*
* @param start Lowest fid in sequence (inclusive).
* @param end Highest fid in sequence (inclusive).
* @param units Number of storage units per fid.
*/
function continuousCredit(uint256 start, uint256 end, uint256 units) external;
/**
* @notice Force refresh the cached Chainlink ETH/USD price. Callable by owner and treasurer.
*/
function refreshPrice() external;
/**
* @notice Change the price feed addresss. Callable by owner.
*
* @param feed The new price feed.
*/
function setPriceFeed(AggregatorV3Interface feed) external;
/**
* @notice Change the uptime feed addresss. Callable by owner.
*
* @param feed The new uptime feed.
*/
function setUptimeFeed(AggregatorV3Interface feed) external;
/**
* @notice Change the USD price per storage unit. Callable by owner.
*
* @param usdPrice The new unit price in USD. Fixed point value with 8 decimals.
*/
function setPrice(uint256 usdPrice) external;
/**
* @notice Set the fixed ETH/USD price, disabling the price feed if the value is
* nonzero. This is an emergency fallback in case of a price feed failure.
* Only callable by owner.
*
* @param fixedPrice The new fixed ETH/USD price. Fixed point value with 8 decimals.
* Setting this value back to zero from a nonzero value will
* re-enable the price feed.
*/
function setFixedEthUsdPrice(uint256 fixedPrice) external;
/**
* @notice Change the maximum supply of storage units. Only callable by owner.
*
* @param max The new maximum supply of storage units.
*/
function setMaxUnits(uint256 max) external;
/**
* @notice Change the deprecationTimestamp. Only callable by owner.
*
* @param timestamp The new deprecationTimestamp. Must be at least equal to block.timestamp.
*/
function setDeprecationTimestamp(uint256 timestamp) external;
/**
* @notice Change the priceFeedCacheDuration. Only callable by owner.
*
* @param duration The new priceFeedCacheDuration.
*/
function setCacheDuration(uint256 duration) external;
/**
* @notice Change the priceFeedMaxAge. Only callable by owner.
*
* @param age The new priceFeedMaxAge.
*/
function setMaxAge(uint256 age) external;
/**
* @notice Change the priceFeedMinAnswer. Only callable by owner.
*
* @param minPrice The new priceFeedMinAnswer. Must be less than current priceFeedMaxAnswer.
*/
function setMinAnswer(uint256 minPrice) external;
/**
* @notice Change the priceFeedMaxAnswer. Only callable by owner.
*
* @param maxPrice The new priceFeedMaxAnswer. Must be greater than current priceFeedMinAnswer.
*/
function setMaxAnswer(uint256 maxPrice) external;
/**
* @notice Change the uptimeFeedGracePeriod. Only callable by owner.
*
* @param period The new uptimeFeedGracePeriod.
*/
function setGracePeriod(uint256 period) external;
/**
* @notice Change the vault address that can receive funds from this contract.
* Only callable by owner.
*
* @param vaultAddr The new vault address.
*/
function setVault(address vaultAddr) external;
/**
* @notice Withdraw a specified amount of ether from the contract balance to the vault.
* Only callable by treasurer.
*
* @param amount The amount of ether to withdraw.
*/
function withdraw(uint256 amount) external;
/**
* @notice Pause, disabling rentals and credits.
* Only callable by owner.
*/
function pause() external;
/**
* @notice Unpause, enabling rentals and credits.
* Only callable by owner.
*/
function unpause() external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
interface IdRegistryLike {
function idOf(address fidOwner) external view returns (uint256);
function verifyFidSignature(
address custodyAddress,
uint256 fid,
bytes32 digest,
bytes calldata sig
) external view returns (bool isValid);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;
import {IMetadataValidator} from "./IMetadataValidator.sol";
import {IdRegistryLike} from "./IdRegistryLike.sol";
interface IKeyRegistry {
/*//////////////////////////////////////////////////////////////
STRUCTS
//////////////////////////////////////////////////////////////*/
/**
* @notice State enumeration for a key in the registry. During migration, an admin can change
* the state of any fids key from NULL to ADDED or ADDED to NULL. After migration, an
* fid can change the state of a key from NULL to ADDED or ADDED to REMOVED only.
*
* - NULL: The key is not in the registry.
* - ADDED: The key has been added to the registry.
* - REMOVED: The key was added to the registry but is now removed.
*/
enum KeyState {
NULL,
ADDED,
REMOVED
}
/**
* @notice Data about a key.
*
* @param state The current state of the key.
* @param keyType Numeric ID representing the manner in which the key should be used.
*/
struct KeyData {
KeyState state;
uint32 keyType;
}
/**
* @dev Struct argument for bulk add function, representing an FID
* and its associated keys.
*
* @param fid Fid associated with provided keys to add.
* @param keys Array of BulkAddKey structs, including key and metadata.
*/
struct BulkAddData {
uint256 fid;
BulkAddKey[] keys;
}
/**
* @dev Struct argument for bulk add function, representing a key
* and its associated metadata.
*
* @param key Bytes of the signer key.
* @param keys Metadata metadata of the signer key.
*/
struct BulkAddKey {
bytes key;
bytes metadata;
}
/**
* @dev Struct argument for bulk reset function, representing an FID
* and its associated keys.
*
* @param fid Fid associated with provided keys to reset.
* @param keys Array of keys to reset.
*/
struct BulkResetData {
uint256 fid;
bytes[] keys;
}
/*//////////////////////////////////////////////////////////////
CONSTANTS
//////////////////////////////////////////////////////////////*/
/**
* @notice Contract version specified in the Farcaster protocol version scheme.
*/
function VERSION() external view returns (string memory);
/**
* @notice Period in seconds after migration during which admin can bulk add/reset keys.
* Admins can make corrections to the migrated data during the grace period if necessary,
* but cannot make changes after it expires.
*/
function gracePeriod() external view returns (uint24);
/**
* @notice EIP-712 typehash for Add signatures.
*/
function ADD_TYPEHASH() external view returns (bytes32);
/**
* @notice EIP-712 typehash for Remove signatures.
*/
function REMOVE_TYPEHASH() external view returns (bytes32);
/*//////////////////////////////////////////////////////////////
STORAGE
//////////////////////////////////////////////////////////////*/
/**
* @notice The IdRegistry contract.
*/
function idRegistry() external view returns (IdRegistryLike);
/**
* @notice Timestamp at which keys migrated. Hubs will cut over to use this KeyRegistry as their
* source of truth after this timestamp.
*/
function keysMigratedAt() external view returns (uint40);
/*//////////////////////////////////////////////////////////////
VIEWS
//////////////////////////////////////////////////////////////*/
/**
* @notice Retrieve state and type data for a given key.
*
* @param fid The fid associated with the key.
* @param key Bytes of the key.
*
* @return KeyData struct that contains the state and keyType.
*/
function keyDataOf(uint256 fid, bytes calldata key) external view returns (KeyData memory);
/**
* @notice Check if the contract has been migrated.
*
* @return true if the contract has been migrated, false otherwise.
*/
function isMigrated() external view returns (bool);
/*//////////////////////////////////////////////////////////////
REGISTRATION
//////////////////////////////////////////////////////////////*/
/**
* @notice Add a key to the caller's fid, setting the key state to ADDED.
*
* @param keyType The key's numeric keyType.
* @param key Bytes of the key to add.
* @param metadataType Metadata type ID.
* @param metadata Metadata about the key, which is not stored and only emitted in an event.
*/
function add(uint32 keyType, bytes calldata key, uint8 metadataType, bytes calldata metadata) external;
/**
* @notice Add a key on behalf of another fid owner, setting the key state to ADDED.
* caller must supply a valid EIP-712 Add signature from the fid owner.
*
* @param fidOwner The fid owner address.
* @param keyType The key's numeric keyType.
* @param key Bytes of the key to add.
* @param metadataType Metadata type ID.
* @param metadata Metadata about the key, which is not stored and only emitted in an event.
* @param deadline Deadline after which the signature expires.
* @param sig EIP-712 Add signature generated by fid owner.
*/
function addFor(
address fidOwner,
uint32 keyType,
bytes calldata key,
uint8 metadataType,
bytes calldata metadata,
uint256 deadline,
bytes calldata sig
) external;
/**
* @notice Remove a key associated with the caller's fid, setting the key state to REMOVED.
* The key must be in the ADDED state.
*
* @param key Bytes of the key to remove.
*/
function remove(bytes calldata key) external;
/**
* @notice Remove a key on behalf of another fid owner, setting the key state to REMOVED.
* caller must supply a valid EIP-712 Remove signature from the fid owner.
*
* @param fidOwner The fid owner address.
* @param key Bytes of the key to remove.
* @param deadline Deadline after which the signature expires.
* @param sig EIP-712 Remove signature generated by fid owner.
*/
function removeFor(address fidOwner, bytes calldata key, uint256 deadline, bytes calldata sig) external;
/*//////////////////////////////////////////////////////////////
PERMISSIONED ACTIONS
//////////////////////////////////////////////////////////////*/
/**
* @notice Add a key on behalf of another fid owner, setting the key state to ADDED.
* Can only be called by the trustedCaller.
*
* @param fidOwner The fid owner address.
* @param keyType The key's numeric keyType.
* @param key Bytes of the key to add.
* @param metadataType Metadata type ID.
* @param metadata Metadata about the key, which is not stored and only emitted in an event.
*/
function trustedAdd(
address fidOwner,
uint32 keyType,
bytes calldata key,
uint8 metadataType,
bytes calldata metadata
) external;
/**
* @notice Set the time of the key migration and emit an event. Hubs will watch this event and
* cut over to use the onchain registry as their source of truth after this timestamp.
* Only callable by the contract owner.
*/
function migrateKeys() external;
/**
* @notice Add multiple keys as part of the initial migration. Only callable by the contract owner.
*
* @param items An array of BulkAddData structs including fid and array of BulkAddKey structs.
*/
function bulkAddKeysForMigration(BulkAddData[] calldata items) external;
/**
* @notice Reset multiple keys as part of the initial migration. Only callable by the contract owner.
* Reset is not the same as removal: this function sets the key state back to NULL,
* rather than REMOVED. This allows the owner to correct any errors in the initial migration until
* the grace period expires.
*
* @param items A list of BulkResetData structs including an fid and array of keys.
*/
function bulkResetKeysForMigration(BulkResetData[] calldata items) external;
/**
* @notice Set a metadata validator contract for the given keyType and metadataType. Only callable by owner.
*
* @param keyType The numeric key type ID associated with this validator.
* @param metadataType The numeric metadata type ID associated with this validator.
* @param validator Contract implementing IMetadataValidator.
*/
function setValidator(uint32 keyType, uint8 metadataType, IMetadataValidator validator) external;
/**
* @notice Set the IdRegistry contract address. Only callable by owner.
*
* @param _idRegistry The new IdRegistry address.
*/
function setIdRegistry(address _idRegistry) external;
/**
* @notice Pause add, remove, and reset.
* Must be called by the owner.
*/
function pause() external;
/**
* @notice Unpause add, remove, and reset.
* Must be called by the owner.
*/
function unpause() external;
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.21;
interface IMetadataValidator {
function validate(uint256 userFid, bytes memory key, bytes memory metadata) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable2Step.sol)
pragma solidity ^0.8.0;
import "./Ownable.sol";
/**
* @dev Contract module which provides 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} and {acceptOwnership}.
*
* This module is used through inheritance. It will make available all functions
* from parent (Ownable).
*/
abstract contract Ownable2Step is Ownable {
address private _pendingOwner;
event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);
/**
* @dev Returns the address of the pending owner.
*/
function pendingOwner() public view virtual returns (address) {
return _pendingOwner;
}
/**
* @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual override onlyOwner {
_pendingOwner = newOwner;
emit OwnershipTransferStarted(owner(), newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual override {
delete _pendingOwner;
super._transferOwnership(newOwner);
}
/**
* @dev The new owner accepts the ownership transfer.
*/
function acceptOwnership() public virtual {
address sender = _msgSender();
require(pendingOwner() == sender, "Ownable2Step: caller is not the new owner");
_transferOwnership(sender);
}
}// 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 (interfaces/IERC1271.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC1271 standard signature validation method for
* contracts as defined in https://eips.ethereum.org/EIPS/eip-1271[ERC-1271].
*
* _Available since v4.1._
*/
interface IERC1271 {
/**
* @dev Should return whether the signature provided is valid for the provided data
* @param hash Hash of the data to be signed
* @param signature Signature byte array associated with _data
*/
function isValidSignature(bytes32 hash, bytes memory signature) external view returns (bytes4 magicValue);
}// 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/cryptography/EIP712.sol)
pragma solidity ^0.8.8;
import "./ECDSA.sol";
import "../ShortStrings.sol";
import "../../interfaces/IERC5267.sol";
/**
* @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
*
* The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible,
* thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding
* they need in their contracts using a combination of `abi.encode` and `keccak256`.
*
* This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
* scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
* ({_hashTypedDataV4}).
*
* The implementation of the domain separator was designed to be as efficient as possible while still properly updating
* the chain id to protect against replay attacks on an eventual fork of the chain.
*
* NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
* https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
*
* NOTE: In the upgradeable version of this contract, the cached values will correspond to the address, and the domain
* separator of the implementation contract. This will cause the `_domainSeparatorV4` function to always rebuild the
* separator from the immutable values, which is cheaper than accessing a cached version in cold storage.
*
* _Available since v3.4._
*
* @custom:oz-upgrades-unsafe-allow state-variable-immutable state-variable-assignment
*/
abstract contract EIP712 is IERC5267 {
using ShortStrings for *;
bytes32 private constant _TYPE_HASH =
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
// Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to
// invalidate the cached domain separator if the chain id changes.
bytes32 private immutable _cachedDomainSeparator;
uint256 private immutable _cachedChainId;
address private immutable _cachedThis;
bytes32 private immutable _hashedName;
bytes32 private immutable _hashedVersion;
ShortString private immutable _name;
ShortString private immutable _version;
string private _nameFallback;
string private _versionFallback;
/**
* @dev Initializes the domain separator and parameter caches.
*
* The meaning of `name` and `version` is specified in
* https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:
*
* - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
* - `version`: the current major version of the signing domain.
*
* NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
* contract upgrade].
*/
constructor(string memory name, string memory version) {
_name = name.toShortStringWithFallback(_nameFallback);
_version = version.toShortStringWithFallback(_versionFallback);
_hashedName = keccak256(bytes(name));
_hashedVersion = keccak256(bytes(version));
_cachedChainId = block.chainid;
_cachedDomainSeparator = _buildDomainSeparator();
_cachedThis = address(this);
}
/**
* @dev Returns the domain separator for the current chain.
*/
function _domainSeparatorV4() internal view returns (bytes32) {
if (address(this) == _cachedThis && block.chainid == _cachedChainId) {
return _cachedDomainSeparator;
} else {
return _buildDomainSeparator();
}
}
function _buildDomainSeparator() private view returns (bytes32) {
return keccak256(abi.encode(_TYPE_HASH, _hashedName, _hashedVersion, block.chainid, address(this)));
}
/**
* @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
* function returns the hash of the fully encoded EIP712 message for this domain.
*
* This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
*
* ```solidity
* bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
* keccak256("Mail(address to,string contents)"),
* mailTo,
* keccak256(bytes(mailContents))
* )));
* address signer = ECDSA.recover(digest, signature);
* ```
*/
function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
return ECDSA.toTypedDataHash(_domainSeparatorV4(), structHash);
}
/**
* @dev See {EIP-5267}.
*
* _Available since v4.9._
*/
function eip712Domain()
public
view
virtual
override
returns (
bytes1 fields,
string memory name,
string memory version,
uint256 chainId,
address verifyingContract,
bytes32 salt,
uint256[] memory extensions
)
{
return (
hex"0f", // 01111
_name.toStringWithFallback(_nameFallback),
_version.toStringWithFallback(_versionFallback),
block.chainid,
address(this),
bytes32(0),
new uint256[](0)
);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/IAccessControlEnumerable.sol)
pragma solidity ^0.8.0;
import "./IAccessControl.sol";
/**
* @dev External interface of AccessControlEnumerable declared to support ERC165 detection.
*/
interface IAccessControlEnumerable is IAccessControl {
/**
* @dev Returns one of the accounts that have `role`. `index` must be a
* value between 0 and {getRoleMemberCount}, non-inclusive.
*
* Role bearers are not sorted in any particular way, and their ordering may
* change at any point.
*
* WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure
* you perform all queries on the same block. See the following
* https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]
* for more information.
*/
function getRoleMember(bytes32 role, uint256 index) external view returns (address);
/**
* @dev Returns the number of accounts that have `role`. Can be used
* together with {getRoleMember} to enumerate all bearers of a role.
*/
function getRoleMemberCount(bytes32 role) external view returns (uint256);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/AccessControl.sol)
pragma solidity ^0.8.0;
import "./IAccessControl.sol";
import "../utils/Context.sol";
import "../utils/Strings.sol";
import "../utils/introspection/ERC165.sol";
/**
* @dev Contract module that allows children to implement role-based access
* control mechanisms. This is a lightweight version that doesn't allow enumerating role
* members except through off-chain means by accessing the contract event logs. Some
* applications may benefit from on-chain enumerability, for those cases see
* {AccessControlEnumerable}.
*
* Roles are referred to by their `bytes32` identifier. These should be exposed
* in the external API and be unique. The best way to achieve this is by
* using `public constant` hash digests:
*
* ```solidity
* bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
* ```
*
* Roles can be used to represent a set of permissions. To restrict access to a
* function call, use {hasRole}:
*
* ```solidity
* function foo() public {
* require(hasRole(MY_ROLE, msg.sender));
* ...
* }
* ```
*
* Roles can be granted and revoked dynamically via the {grantRole} and
* {revokeRole} functions. Each role has an associated admin role, and only
* accounts that have a role's admin role can call {grantRole} and {revokeRole}.
*
* By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
* that only accounts with this role will be able to grant or revoke other
* roles. More complex role relationships can be created by using
* {_setRoleAdmin}.
*
* WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
* grant and revoke this role. Extra precautions should be taken to secure
* accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}
* to enforce additional security measures for this role.
*/
abstract contract AccessControl is Context, IAccessControl, ERC165 {
struct RoleData {
mapping(address => bool) members;
bytes32 adminRole;
}
mapping(bytes32 => RoleData) private _roles;
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
/**
* @dev Modifier that checks that an account has a specific role. Reverts
* with a standardized message including the required role.
*
* The format of the revert reason is given by the following regular expression:
*
* /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
*
* _Available since v4.1._
*/
modifier onlyRole(bytes32 role) {
_checkRole(role);
_;
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
}
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) public view virtual override returns (bool) {
return _roles[role].members[account];
}
/**
* @dev Revert with a standard message if `_msgSender()` is missing `role`.
* Overriding this function changes the behavior of the {onlyRole} modifier.
*
* Format of the revert message is described in {_checkRole}.
*
* _Available since v4.6._
*/
function _checkRole(bytes32 role) internal view virtual {
_checkRole(role, _msgSender());
}
/**
* @dev Revert with a standard message if `account` is missing `role`.
*
* The format of the revert reason is given by the following regular expression:
*
* /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
*/
function _checkRole(bytes32 role, address account) internal view virtual {
if (!hasRole(role, account)) {
revert(
string(
abi.encodePacked(
"AccessControl: account ",
Strings.toHexString(account),
" is missing role ",
Strings.toHexString(uint256(role), 32)
)
)
);
}
}
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) {
return _roles[role].adminRole;
}
/**
* @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.
*
* May emit a {RoleGranted} event.
*/
function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
_grantRole(role, account);
}
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*
* May emit a {RoleRevoked} event.
*/
function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
_revokeRole(role, account);
}
/**
* @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 revoked `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `account`.
*
* May emit a {RoleRevoked} event.
*/
function renounceRole(bytes32 role, address account) public virtual override {
require(account == _msgSender(), "AccessControl: can only renounce roles for self");
_revokeRole(role, account);
}
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event. Note that unlike {grantRole}, this function doesn't perform any
* checks on the calling account.
*
* May emit a {RoleGranted} event.
*
* [WARNING]
* ====
* This function should only be called from the constructor when setting
* up the initial roles for the system.
*
* Using this function in any other way is effectively circumventing the admin
* system imposed by {AccessControl}.
* ====
*
* NOTE: This function is deprecated in favor of {_grantRole}.
*/
function _setupRole(bytes32 role, address account) internal virtual {
_grantRole(role, account);
}
/**
* @dev Sets `adminRole` as ``role``'s admin role.
*
* Emits a {RoleAdminChanged} event.
*/
function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
bytes32 previousAdminRole = getRoleAdmin(role);
_roles[role].adminRole = adminRole;
emit RoleAdminChanged(role, previousAdminRole, adminRole);
}
/**
* @dev Grants `role` to `account`.
*
* Internal function without access restriction.
*
* May emit a {RoleGranted} event.
*/
function _grantRole(bytes32 role, address account) internal virtual {
if (!hasRole(role, account)) {
_roles[role].members[account] = true;
emit RoleGranted(role, account, _msgSender());
}
}
/**
* @dev Revokes `role` from `account`.
*
* Internal function without access restriction.
*
* May emit a {RoleRevoked} event.
*/
function _revokeRole(bytes32 role, address account) internal virtual {
if (hasRole(role, account)) {
_roles[role].members[account] = false;
emit RoleRevoked(role, account, _msgSender());
}
}
}// 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: 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) (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/ShortStrings.sol)
pragma solidity ^0.8.8;
import "./StorageSlot.sol";
// | string | 0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA |
// | length | 0x BB |
type ShortString is bytes32;
/**
* @dev This library provides functions to convert short memory strings
* into a `ShortString` type that can be used as an immutable variable.
*
* Strings of arbitrary length can be optimized using this library if
* they are short enough (up to 31 bytes) by packing them with their
* length (1 byte) in a single EVM word (32 bytes). Additionally, a
* fallback mechanism can be used for every other case.
*
* Usage example:
*
* ```solidity
* contract Named {
* using ShortStrings for *;
*
* ShortString private immutable _name;
* string private _nameFallback;
*
* constructor(string memory contractName) {
* _name = contractName.toShortStringWithFallback(_nameFallback);
* }
*
* function name() external view returns (string memory) {
* return _name.toStringWithFallback(_nameFallback);
* }
* }
* ```
*/
library ShortStrings {
// Used as an identifier for strings longer than 31 bytes.
bytes32 private constant _FALLBACK_SENTINEL = 0x00000000000000000000000000000000000000000000000000000000000000FF;
error StringTooLong(string str);
error InvalidShortString();
/**
* @dev Encode a string of at most 31 chars into a `ShortString`.
*
* This will trigger a `StringTooLong` error is the input string is too long.
*/
function toShortString(string memory str) internal pure returns (ShortString) {
bytes memory bstr = bytes(str);
if (bstr.length > 31) {
revert StringTooLong(str);
}
return ShortString.wrap(bytes32(uint256(bytes32(bstr)) | bstr.length));
}
/**
* @dev Decode a `ShortString` back to a "normal" string.
*/
function toString(ShortString sstr) internal pure returns (string memory) {
uint256 len = byteLength(sstr);
// using `new string(len)` would work locally but is not memory safe.
string memory str = new string(32);
/// @solidity memory-safe-assembly
assembly {
mstore(str, len)
mstore(add(str, 0x20), sstr)
}
return str;
}
/**
* @dev Return the length of a `ShortString`.
*/
function byteLength(ShortString sstr) internal pure returns (uint256) {
uint256 result = uint256(ShortString.unwrap(sstr)) & 0xFF;
if (result > 31) {
revert InvalidShortString();
}
return result;
}
/**
* @dev Encode a string into a `ShortString`, or write it to storage if it is too long.
*/
function toShortStringWithFallback(string memory value, string storage store) internal returns (ShortString) {
if (bytes(value).length < 32) {
return toShortString(value);
} else {
StorageSlot.getStringSlot(store).value = value;
return ShortString.wrap(_FALLBACK_SENTINEL);
}
}
/**
* @dev Decode a string that was encoded to `ShortString` or written to storage using {setWithFallback}.
*/
function toStringWithFallback(ShortString value, string storage store) internal pure returns (string memory) {
if (ShortString.unwrap(value) != _FALLBACK_SENTINEL) {
return toString(value);
} else {
return store;
}
}
/**
* @dev Return the length of a string that was encoded to `ShortString` or written to storage using {setWithFallback}.
*
* WARNING: This will return the "byte length" of the string. This may not reflect the actual length in terms of
* actual characters as the UTF-8 encoding of a single character can span over multiple bytes.
*/
function byteLengthWithFallback(ShortString value, string storage store) internal view returns (uint256) {
if (ShortString.unwrap(value) != _FALLBACK_SENTINEL) {
return byteLength(value);
} else {
return bytes(store).length;
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (interfaces/IERC5267.sol)
pragma solidity ^0.8.0;
interface IERC5267 {
/**
* @dev MAY be emitted to signal that the domain could have changed.
*/
event EIP712DomainChanged();
/**
* @dev returns the fields and values that describe the domain separator used by this contract for EIP-712
* signature.
*/
function eip712Domain()
external
view
returns (
bytes1 fields,
string memory name,
string memory version,
uint256 chainId,
address verifyingContract,
bytes32 salt,
uint256[] memory extensions
);
}// 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 v4.4.1 (utils/introspection/ERC165.sol)
pragma solidity ^0.8.0;
import "./IERC165.sol";
/**
* @dev Implementation of the {IERC165} interface.
*
* Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
* for the additional interface id that will be supported. For example:
*
* ```solidity
* function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
* return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
* }
* ```
*
* Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
*/
abstract contract ERC165 is IERC165 {
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}// 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/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 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);
}{
"remappings": [
"solmate/=lib/solmate/",
"openzeppelin/=lib/openzeppelin-contracts/",
"openzeppelin-latest/=lib/openzeppelin-latest/",
"chainlink/=lib/chainlink-brownie-contracts/contracts/src/",
"chainlink-brownie-contracts/=lib/chainlink-brownie-contracts/",
"ds-test/=lib/forge-std/lib/ds-test/src/",
"erc4626-tests/=lib/openzeppelin-latest/lib/erc4626-tests/",
"forge-std/=lib/forge-std/src/",
"openzeppelin-contracts/=lib/openzeppelin-contracts/"
],
"optimizer": {
"enabled": true,
"runs": 100000
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "none",
"appendCBOR": true
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"abi"
]
}
},
"evmVersion": "paris",
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"_idRegistry","type":"address"},{"internalType":"address","name":"_storageRegistry","type":"address"},{"internalType":"address","name":"_keyRegistry","type":"address"},{"internalType":"address","name":"_trustedCaller","type":"address"},{"internalType":"address","name":"_initialOwner","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"CallFailed","type":"error"},{"inputs":[],"name":"InvalidAddress","type":"error"},{"inputs":[],"name":"InvalidAmount","type":"error"},{"inputs":[],"name":"OnlyTrustedCaller","type":"error"},{"inputs":[],"name":"Registrable","type":"error"},{"inputs":[],"name":"Seedable","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"anonymous":false,"inputs":[],"name":"DisableTrustedOnly","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferStarted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldCaller","type":"address"},{"indexed":true,"internalType":"address","name":"newCaller","type":"address"},{"indexed":false,"internalType":"address","name":"owner","type":"address"}],"name":"SetTrustedCaller","type":"event"},{"inputs":[],"name":"VERSION","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"disableTrustedOnly","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"idRegistry","outputs":[{"internalType":"contract IdRegistry","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"keyRegistry","outputs":[{"internalType":"contract KeyRegistry","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"to","type":"address"},{"internalType":"address","name":"recovery","type":"address"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"bytes","name":"sig","type":"bytes"}],"internalType":"struct IBundler.RegistrationParams","name":"registration","type":"tuple"},{"components":[{"internalType":"uint32","name":"keyType","type":"uint32"},{"internalType":"bytes","name":"key","type":"bytes"},{"internalType":"uint8","name":"metadataType","type":"uint8"},{"internalType":"bytes","name":"metadata","type":"bytes"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"bytes","name":"sig","type":"bytes"}],"internalType":"struct IBundler.SignerParams[]","name":"signers","type":"tuple[]"},{"internalType":"uint256","name":"storageUnits","type":"uint256"}],"name":"register","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_trustedCaller","type":"address"}],"name":"setTrustedCaller","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"storageRegistry","outputs":[{"internalType":"contract StorageRegistry","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"to","type":"address"},{"internalType":"address","name":"recovery","type":"address"},{"components":[{"internalType":"uint32","name":"keyType","type":"uint32"},{"internalType":"bytes","name":"key","type":"bytes"},{"internalType":"uint8","name":"metadataType","type":"uint8"},{"internalType":"bytes","name":"metadata","type":"bytes"}],"internalType":"struct IBundler.SignerData[]","name":"signers","type":"tuple[]"},{"internalType":"uint256","name":"units","type":"uint256"}],"internalType":"struct IBundler.UserData[]","name":"users","type":"tuple[]"}],"name":"trustedBatchRegister","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"trustedCaller","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"trustedOnly","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"to","type":"address"},{"internalType":"address","name":"recovery","type":"address"},{"components":[{"internalType":"uint32","name":"keyType","type":"uint32"},{"internalType":"bytes","name":"key","type":"bytes"},{"internalType":"uint8","name":"metadataType","type":"uint8"},{"internalType":"bytes","name":"metadata","type":"bytes"}],"internalType":"struct IBundler.SignerData[]","name":"signers","type":"tuple[]"},{"internalType":"uint256","name":"units","type":"uint256"}],"internalType":"struct IBundler.UserData","name":"user","type":"tuple"}],"name":"trustedRegister","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]Contract Creation Code
60e060405260016003553480156200001657600080fd5b5060405162001a0038038062001a00833981016040819052620000399162000199565b80620000453362000080565b620000508162000080565b506001600160a01b0380861660805284811660a052831660c05262000075826200009e565b505050505062000209565b600180546001600160a01b03191690556200009b816200012c565b50565b6001600160a01b038116620000c65760405163e6c4247b60e01b815260040160405180910390fd5b6002546040513381526001600160a01b038381169216907ff49aa3de84c4496d3a942511e6d2319eb43aff3afa8072a0cb18f9cc55a0359f9060200160405180910390a3600280546001600160a01b0319166001600160a01b0392909216919091179055565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b80516001600160a01b03811681146200019457600080fd5b919050565b600080600080600060a08688031215620001b257600080fd5b620001bd866200017c565b9450620001cd602087016200017c565b9350620001dd604087016200017c565b9250620001ed606087016200017c565b9150620001fd608087016200017c565b90509295509295909350565b60805160a05160c0516117816200027f6000396000818161018401528181610621015281816109900152610d65015260008181610114015281816102830152818161072401528181610a930152610e920152600081816101e2015281816104e00152818161084f0152610c6201526117816000f3fe6080604052600436106100f75760003560e01c8063715018a61161008a578063e30c397811610059578063e30c397814610346578063ea40ae1414610371578063f2fde38b14610391578063ffa1ad74146103b157600080fd5b8063715018a6146102de57806379ba5097146102f35780638da5cb5b14610308578063a44c9ce71461033357600080fd5b80634121e73a116100c65780634121e73a146102515780634ec77b45146102715780636b2ddd4e146102a55780636e9bde49146102c957600080fd5b8063086b5198146101725780630aa13b8c146101d0578063268f0760146102045780632a79713f1461023157600080fd5b3661016d573373ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000161461016b576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b005b600080fd5b34801561017e57600080fd5b506101a67f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b3480156101dc57600080fd5b506101a67f000000000000000000000000000000000000000000000000000000000000000081565b34801561021057600080fd5b506002546101a69073ffffffffffffffffffffffffffffffffffffffff1681565b34801561023d57600080fd5b5061016b61024c366004611276565b610407565b34801561025d57600080fd5b5061016b61026c3660046112d0565b6107a9565b34801561027d57600080fd5b506101a67f000000000000000000000000000000000000000000000000000000000000000081565b3480156102b157600080fd5b506102bb60035481565b6040519081526020016101c7565b3480156102d557600080fd5b5061016b610b09565b3480156102ea57600080fd5b5061016b610b41565b3480156102ff57600080fd5b5061016b610b55565b34801561031457600080fd5b5060005473ffffffffffffffffffffffffffffffffffffffff166101a6565b61016b61034136600461130d565b610c0f565b34801561035257600080fd5b5060015473ffffffffffffffffffffffffffffffffffffffff166101a6565b34801561037d57600080fd5b5061016b61038c36600461137d565b610f13565b34801561039d57600080fd5b5061016b6103ac36600461137d565b610f24565b3480156103bd57600080fd5b506103fa6040518060400160405280600a81526020017f323032332e30382e32330000000000000000000000000000000000000000000081525081565b6040516101c791906113ba565b600354600003610443576040517f7cb40a3900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60025473ffffffffffffffffffffffffffffffffffffffff163314610494576040517f7033971c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8060005b818110156107a357368484838181106104b3576104b3611426565b90506020028101906104c59190611455565b9050600073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001663b12da9aa610512602085018561137d565b610522604086016020870161137d565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b16815273ffffffffffffffffffffffffffffffffffffffff9283166004820152911660248201526044016020604051808303816000875af1158015610594573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105b89190611493565b905060006105c960408401846114ac565b9050905060005b818110156106ea57366105e660408601866114ac565b838181106105f6576105f6611426565b90506020028101906106089190611455565b905073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016632cd840f0610653602088018861137d565b6106606020850185611514565b61066d602086018661153a565b61067d606088016040890161159f565b61068a606089018961153a565b6040518863ffffffff1660e01b81526004016106ac979695949392919061160b565b600060405180830381600087803b1580156106c657600080fd5b505af11580156106da573d6000803e3d6000fd5b50505050816001019150506105d0565b506040517fab7ccc1c00000000000000000000000000000000000000000000000000000000815260048101839052606084013560248201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063ab7ccc1c90604401600060405180830381600087803b15801561077d57600080fd5b505af1158015610791573d6000803e3d6000fd5b50505050836001019350505050610498565b50505050565b6003546000036107e5576040517f7cb40a3900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60025473ffffffffffffffffffffffffffffffffffffffff163314610836576040517f7033971c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001663b12da9aa610881602085018561137d565b610891604086016020870161137d565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b16815273ffffffffffffffffffffffffffffffffffffffff9283166004820152911660248201526044016020604051808303816000875af1158015610903573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109279190611493565b9050600061093860408401846114ac565b9050905060005b81811015610a59573661095560408601866114ac565b8381811061096557610965611426565b90506020028101906109779190611455565b905073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016632cd840f06109c2602088018861137d565b6109cf6020850185611514565b6109dc602086018661153a565b6109ec606088016040890161159f565b6109f9606089018961153a565b6040518863ffffffff1660e01b8152600401610a1b979695949392919061160b565b600060405180830381600087803b158015610a3557600080fd5b505af1158015610a49573d6000803e3d6000fd5b505050508160010191505061093f565b506040517fab7ccc1c00000000000000000000000000000000000000000000000000000000815260048101839052606084013560248201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063ab7ccc1c90604401600060405180830381600087803b158015610aec57600080fd5b505af1158015610b00573d6000803e3d6000fd5b50505050505050565b610b11610fd4565b600060038190556040517f03732e5295a5bd18e6ef95b03b41aa8bcadae292a7ef40468144c7a727dfa8b59190a1565b610b49610fd4565b610b536000611055565b565b600154339073ffffffffffffffffffffffffffffffffffffffff168114610c03576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602960248201527f4f776e61626c6532537465703a2063616c6c6572206973206e6f74207468652060448201527f6e6577206f776e6572000000000000000000000000000000000000000000000060648201526084015b60405180910390fd5b610c0c81611055565b50565b80600003610c49576040517f2c5211c600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016633efa0b02610c94602088018861137d565b610ca46040890160208a0161137d565b6040890135610cb660608b018b61153a565b6040518663ffffffff1660e01b8152600401610cd6959493929190611671565b6020604051808303816000875af1158015610cf5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d199190611493565b90508260005b81811015610e435736868683818110610d3a57610d3a611426565b9050602002810190610d4c91906116bc565b905073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001663a005d3d2610d9760208b018b61137d565b610da46020850185611514565b610db1602086018661153a565b610dc1606088016040890161159f565b610dce606089018961153a565b60808a0135610de060a08c018c61153a565b6040518b63ffffffff1660e01b8152600401610e059a999897969594939291906116f0565b600060405180830381600087803b158015610e1f57600080fd5b505af1158015610e33573d6000803e3d6000fd5b5050505081600101915050610d1f565b506040517f783a112b000000000000000000000000000000000000000000000000000000008152600481018390526024810184905260009073ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000169063783a112b90349060440160206040518083038185885af1158015610edc573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190610f019190611493565b90508015610b0057610b003382611086565b610f1b610fd4565b610c0c816110d0565b610f2c610fd4565b6001805473ffffffffffffffffffffffffffffffffffffffff83167fffffffffffffffffffffffff00000000000000000000000000000000000000009091168117909155610f8f60005473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a350565b60005473ffffffffffffffffffffffffffffffffffffffff163314610b53576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610bfa565b600180547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055610c0c816111b5565b600080600080600085875af19050806110cb576040517f3204506f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050565b73ffffffffffffffffffffffffffffffffffffffff811661111d576040517fe6c4247b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60025460405133815273ffffffffffffffffffffffffffffffffffffffff8381169216907ff49aa3de84c4496d3a942511e6d2319eb43aff3afa8072a0cb18f9cc55a0359f9060200160405180910390a3600280547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b6000805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b60008083601f84011261123c57600080fd5b50813567ffffffffffffffff81111561125457600080fd5b6020830191508360208260051b850101111561126f57600080fd5b9250929050565b6000806020838503121561128957600080fd5b823567ffffffffffffffff8111156112a057600080fd5b6112ac8582860161122a565b90969095509350505050565b6000608082840312156112ca57600080fd5b50919050565b6000602082840312156112e257600080fd5b813567ffffffffffffffff8111156112f957600080fd5b611305848285016112b8565b949350505050565b6000806000806060858703121561132357600080fd5b843567ffffffffffffffff8082111561133b57600080fd5b611347888389016112b8565b9550602087013591508082111561135d57600080fd5b5061136a8782880161122a565b9598909750949560400135949350505050565b60006020828403121561138f57600080fd5b813573ffffffffffffffffffffffffffffffffffffffff811681146113b357600080fd5b9392505050565b600060208083528351808285015260005b818110156113e7578581018301518582016040015282016113cb565b5060006040828601015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8301168501019250505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8183360301811261148957600080fd5b9190910192915050565b6000602082840312156114a557600080fd5b5051919050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126114e157600080fd5b83018035915067ffffffffffffffff8211156114fc57600080fd5b6020019150600581901b360382131561126f57600080fd5b60006020828403121561152657600080fd5b813563ffffffff811681146113b357600080fd5b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261156f57600080fd5b83018035915067ffffffffffffffff82111561158a57600080fd5b60200191503681900382131561126f57600080fd5b6000602082840312156115b157600080fd5b813560ff811681146113b357600080fd5b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b73ffffffffffffffffffffffffffffffffffffffff8816815263ffffffff8716602082015260a06040820152600061164760a0830187896115c2565b60ff8616606084015282810360808401526116638185876115c2565b9a9950505050505050505050565b600073ffffffffffffffffffffffffffffffffffffffff8088168352808716602084015250846040830152608060608301526116b16080830184866115c2565b979650505050505050565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff4183360301811261148957600080fd5b73ffffffffffffffffffffffffffffffffffffffff8b16815263ffffffff8a16602082015260e06040820152600061172c60e083018a8c6115c2565b60ff89166060840152828103608084015261174881888a6115c2565b90508560a084015282810360c08401526117638185876115c2565b9d9c5050505050505050505050505056fea164736f6c6343000815000a00000000000000000000000000000000fcaf86937e41ba038b4fa40baa4b780a00000000000000000000000000000000fcce7f938e7ae6d3c335bd6a1a7c593d00000000000000000000000000000000fc9e66f1c6d86d750b4af47ff0cc343d0000000000000000000000002d93c2f74b2c4697f9ea85d0450148aa45d4d5a200000000000000000000000053c6da835c777ad11159198fbe11f95e5ee6b692
Deployed Bytecode
0x6080604052600436106100f75760003560e01c8063715018a61161008a578063e30c397811610059578063e30c397814610346578063ea40ae1414610371578063f2fde38b14610391578063ffa1ad74146103b157600080fd5b8063715018a6146102de57806379ba5097146102f35780638da5cb5b14610308578063a44c9ce71461033357600080fd5b80634121e73a116100c65780634121e73a146102515780634ec77b45146102715780636b2ddd4e146102a55780636e9bde49146102c957600080fd5b8063086b5198146101725780630aa13b8c146101d0578063268f0760146102045780632a79713f1461023157600080fd5b3661016d573373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000fcce7f938e7ae6d3c335bd6a1a7c593d161461016b576040517f82b4290000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b005b600080fd5b34801561017e57600080fd5b506101a67f00000000000000000000000000000000fc9e66f1c6d86d750b4af47ff0cc343d81565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020015b60405180910390f35b3480156101dc57600080fd5b506101a67f00000000000000000000000000000000fcaf86937e41ba038b4fa40baa4b780a81565b34801561021057600080fd5b506002546101a69073ffffffffffffffffffffffffffffffffffffffff1681565b34801561023d57600080fd5b5061016b61024c366004611276565b610407565b34801561025d57600080fd5b5061016b61026c3660046112d0565b6107a9565b34801561027d57600080fd5b506101a67f00000000000000000000000000000000fcce7f938e7ae6d3c335bd6a1a7c593d81565b3480156102b157600080fd5b506102bb60035481565b6040519081526020016101c7565b3480156102d557600080fd5b5061016b610b09565b3480156102ea57600080fd5b5061016b610b41565b3480156102ff57600080fd5b5061016b610b55565b34801561031457600080fd5b5060005473ffffffffffffffffffffffffffffffffffffffff166101a6565b61016b61034136600461130d565b610c0f565b34801561035257600080fd5b5060015473ffffffffffffffffffffffffffffffffffffffff166101a6565b34801561037d57600080fd5b5061016b61038c36600461137d565b610f13565b34801561039d57600080fd5b5061016b6103ac36600461137d565b610f24565b3480156103bd57600080fd5b506103fa6040518060400160405280600a81526020017f323032332e30382e32330000000000000000000000000000000000000000000081525081565b6040516101c791906113ba565b600354600003610443576040517f7cb40a3900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60025473ffffffffffffffffffffffffffffffffffffffff163314610494576040517f7033971c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8060005b818110156107a357368484838181106104b3576104b3611426565b90506020028101906104c59190611455565b9050600073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000fcaf86937e41ba038b4fa40baa4b780a1663b12da9aa610512602085018561137d565b610522604086016020870161137d565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b16815273ffffffffffffffffffffffffffffffffffffffff9283166004820152911660248201526044016020604051808303816000875af1158015610594573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105b89190611493565b905060006105c960408401846114ac565b9050905060005b818110156106ea57366105e660408601866114ac565b838181106105f6576105f6611426565b90506020028101906106089190611455565b905073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000fc9e66f1c6d86d750b4af47ff0cc343d16632cd840f0610653602088018861137d565b6106606020850185611514565b61066d602086018661153a565b61067d606088016040890161159f565b61068a606089018961153a565b6040518863ffffffff1660e01b81526004016106ac979695949392919061160b565b600060405180830381600087803b1580156106c657600080fd5b505af11580156106da573d6000803e3d6000fd5b50505050816001019150506105d0565b506040517fab7ccc1c00000000000000000000000000000000000000000000000000000000815260048101839052606084013560248201527f00000000000000000000000000000000fcce7f938e7ae6d3c335bd6a1a7c593d73ffffffffffffffffffffffffffffffffffffffff169063ab7ccc1c90604401600060405180830381600087803b15801561077d57600080fd5b505af1158015610791573d6000803e3d6000fd5b50505050836001019350505050610498565b50505050565b6003546000036107e5576040517f7cb40a3900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60025473ffffffffffffffffffffffffffffffffffffffff163314610836576040517f7033971c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000fcaf86937e41ba038b4fa40baa4b780a1663b12da9aa610881602085018561137d565b610891604086016020870161137d565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b16815273ffffffffffffffffffffffffffffffffffffffff9283166004820152911660248201526044016020604051808303816000875af1158015610903573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109279190611493565b9050600061093860408401846114ac565b9050905060005b81811015610a59573661095560408601866114ac565b8381811061096557610965611426565b90506020028101906109779190611455565b905073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000fc9e66f1c6d86d750b4af47ff0cc343d16632cd840f06109c2602088018861137d565b6109cf6020850185611514565b6109dc602086018661153a565b6109ec606088016040890161159f565b6109f9606089018961153a565b6040518863ffffffff1660e01b8152600401610a1b979695949392919061160b565b600060405180830381600087803b158015610a3557600080fd5b505af1158015610a49573d6000803e3d6000fd5b505050508160010191505061093f565b506040517fab7ccc1c00000000000000000000000000000000000000000000000000000000815260048101839052606084013560248201527f00000000000000000000000000000000fcce7f938e7ae6d3c335bd6a1a7c593d73ffffffffffffffffffffffffffffffffffffffff169063ab7ccc1c90604401600060405180830381600087803b158015610aec57600080fd5b505af1158015610b00573d6000803e3d6000fd5b50505050505050565b610b11610fd4565b600060038190556040517f03732e5295a5bd18e6ef95b03b41aa8bcadae292a7ef40468144c7a727dfa8b59190a1565b610b49610fd4565b610b536000611055565b565b600154339073ffffffffffffffffffffffffffffffffffffffff168114610c03576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602960248201527f4f776e61626c6532537465703a2063616c6c6572206973206e6f74207468652060448201527f6e6577206f776e6572000000000000000000000000000000000000000000000060648201526084015b60405180910390fd5b610c0c81611055565b50565b80600003610c49576040517f2c5211c600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000fcaf86937e41ba038b4fa40baa4b780a16633efa0b02610c94602088018861137d565b610ca46040890160208a0161137d565b6040890135610cb660608b018b61153a565b6040518663ffffffff1660e01b8152600401610cd6959493929190611671565b6020604051808303816000875af1158015610cf5573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610d199190611493565b90508260005b81811015610e435736868683818110610d3a57610d3a611426565b9050602002810190610d4c91906116bc565b905073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000fc9e66f1c6d86d750b4af47ff0cc343d1663a005d3d2610d9760208b018b61137d565b610da46020850185611514565b610db1602086018661153a565b610dc1606088016040890161159f565b610dce606089018961153a565b60808a0135610de060a08c018c61153a565b6040518b63ffffffff1660e01b8152600401610e059a999897969594939291906116f0565b600060405180830381600087803b158015610e1f57600080fd5b505af1158015610e33573d6000803e3d6000fd5b5050505081600101915050610d1f565b506040517f783a112b000000000000000000000000000000000000000000000000000000008152600481018390526024810184905260009073ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000fcce7f938e7ae6d3c335bd6a1a7c593d169063783a112b90349060440160206040518083038185885af1158015610edc573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190610f019190611493565b90508015610b0057610b003382611086565b610f1b610fd4565b610c0c816110d0565b610f2c610fd4565b6001805473ffffffffffffffffffffffffffffffffffffffff83167fffffffffffffffffffffffff00000000000000000000000000000000000000009091168117909155610f8f60005473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a350565b60005473ffffffffffffffffffffffffffffffffffffffff163314610b53576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610bfa565b600180547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055610c0c816111b5565b600080600080600085875af19050806110cb576040517f3204506f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050565b73ffffffffffffffffffffffffffffffffffffffff811661111d576040517fe6c4247b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60025460405133815273ffffffffffffffffffffffffffffffffffffffff8381169216907ff49aa3de84c4496d3a942511e6d2319eb43aff3afa8072a0cb18f9cc55a0359f9060200160405180910390a3600280547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b6000805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b60008083601f84011261123c57600080fd5b50813567ffffffffffffffff81111561125457600080fd5b6020830191508360208260051b850101111561126f57600080fd5b9250929050565b6000806020838503121561128957600080fd5b823567ffffffffffffffff8111156112a057600080fd5b6112ac8582860161122a565b90969095509350505050565b6000608082840312156112ca57600080fd5b50919050565b6000602082840312156112e257600080fd5b813567ffffffffffffffff8111156112f957600080fd5b611305848285016112b8565b949350505050565b6000806000806060858703121561132357600080fd5b843567ffffffffffffffff8082111561133b57600080fd5b611347888389016112b8565b9550602087013591508082111561135d57600080fd5b5061136a8782880161122a565b9598909750949560400135949350505050565b60006020828403121561138f57600080fd5b813573ffffffffffffffffffffffffffffffffffffffff811681146113b357600080fd5b9392505050565b600060208083528351808285015260005b818110156113e7578581018301518582016040015282016113cb565b5060006040828601015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8301168501019250505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8183360301811261148957600080fd5b9190910192915050565b6000602082840312156114a557600080fd5b5051919050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126114e157600080fd5b83018035915067ffffffffffffffff8211156114fc57600080fd5b6020019150600581901b360382131561126f57600080fd5b60006020828403121561152657600080fd5b813563ffffffff811681146113b357600080fd5b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe184360301811261156f57600080fd5b83018035915067ffffffffffffffff82111561158a57600080fd5b60200191503681900382131561126f57600080fd5b6000602082840312156115b157600080fd5b813560ff811681146113b357600080fd5b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b73ffffffffffffffffffffffffffffffffffffffff8816815263ffffffff8716602082015260a06040820152600061164760a0830187896115c2565b60ff8616606084015282810360808401526116638185876115c2565b9a9950505050505050505050565b600073ffffffffffffffffffffffffffffffffffffffff8088168352808716602084015250846040830152608060608301526116b16080830184866115c2565b979650505050505050565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff4183360301811261148957600080fd5b73ffffffffffffffffffffffffffffffffffffffff8b16815263ffffffff8a16602082015260e06040820152600061172c60e083018a8c6115c2565b60ff89166060840152828103608084015261174881888a6115c2565b90508560a084015282810360c08401526117638185876115c2565b9d9c5050505050505050505050505056fea164736f6c6343000815000a
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
00000000000000000000000000000000fcaf86937e41ba038b4fa40baa4b780a00000000000000000000000000000000fcce7f938e7ae6d3c335bd6a1a7c593d00000000000000000000000000000000fc9e66f1c6d86d750b4af47ff0cc343d0000000000000000000000002d93c2f74b2c4697f9ea85d0450148aa45d4d5a200000000000000000000000053c6da835c777ad11159198fbe11f95e5ee6b692
-----Decoded View---------------
Arg [0] : _idRegistry (address): 0x00000000FcAf86937e41bA038B4fA40BAA4B780A
Arg [1] : _storageRegistry (address): 0x00000000fcCe7f938e7aE6D3c335bD6a1a7c593D
Arg [2] : _keyRegistry (address): 0x00000000fC9e66f1c6d86D750B4af47fF0Cc343d
Arg [3] : _trustedCaller (address): 0x2D93c2F74b2C4697f9ea85D0450148AA45D4D5a2
Arg [4] : _initialOwner (address): 0x53c6dA835c777AD11159198FBe11f95E5eE6B692
-----Encoded View---------------
5 Constructor Arguments found :
Arg [0] : 00000000000000000000000000000000fcaf86937e41ba038b4fa40baa4b780a
Arg [1] : 00000000000000000000000000000000fcce7f938e7ae6d3c335bd6a1a7c593d
Arg [2] : 00000000000000000000000000000000fc9e66f1c6d86d750b4af47ff0cc343d
Arg [3] : 0000000000000000000000002d93c2f74b2c4697f9ea85d0450148aa45d4d5a2
Arg [4] : 00000000000000000000000053c6da835c777ad11159198fbe11f95e5ee6b692
Deployed Bytecode Sourcemap
561:6552:24:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;7043:10;:38;7065:15;7043:38;;7039:65;;7090:14;;;;;;;;;;;;;;7039:65;561:6552;;;;;1846:40;;;;;;;;;;;;;;;;;;210:42:38;198:55;;;180:74;;168:2;153:18;1846:40:24;;;;;;;;1614:38;;;;;;;;;;;;;;;1734:28:37;;;;;;;;;;-1:-1:-1;1734:28:37;;;;;;;;5973:974:24;;;;;;;;;;-1:-1:-1;5973:974:24;;;;;:::i;:::-;;:::i;4985:603::-;;;;;;;;;;-1:-1:-1;4985:603:24;;;;;:::i;:::-;;:::i;1727:48::-;;;;;;;;;;;;;;;1980:30:37;;;;;;;;;;;;;;;;;;;2548:25:38;;;2536:2;2521:18;1980:30:37;2402:177:38;3574:119:37;;;;;;;;;;;;;:::i;1824:101:5:-;;;;;;;;;;;;;:::i;1734:212:6:-;;;;;;;;;;;;;:::i;1201:85:5:-;;;;;;;;;;-1:-1:-1;1247:7:5;1273:6;;;1201:85;;3520:1143:24;;;;;;:::i;:::-;;:::i;847:99:6:-;;;;;;;;;;-1:-1:-1;926:13:6;;;;847:99;;3354:117:37;;;;;;;;;;-1:-1:-1;3354:117:37;;;;;:::i;:::-;;:::i;1139:178:6:-;;;;;;;;;;-1:-1:-1;1139:178:6;;;;;:::i;:::-;;:::i;1317:45:24:-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;5973:974::-;2329:11:37;;2344:1;2329:16;2325:42;;2354:13;;;;;;;;;;;;;;2325:42;2395:13;;;;2381:10;:27;2377:59;;2417:19;;;;;;;;;;;;;;2377:59;6158:5:24;6139:16:::1;6180:761;6200:8;6196:1;:12;6180:761;;;6225:22;6250:5;;6256:1;6250:8;;;;;;;:::i;:::-;;;;;;;;;;;;:::i;:::-;6225:33:::0;-1:-1:-1;6272:11:24::1;6286:26;:10;:26;;6313:7;;::::0;::::1;6225:33:::0;6313:7:::1;:::i;:::-;6322:13;::::0;;;::::1;::::0;::::1;;:::i;:::-;6286:50;::::0;;::::1;::::0;;;;;;5098:42:38;5167:15;;;6286:50:24::1;::::0;::::1;5149:34:38::0;5219:15;;5199:18;;;5192:43;5061:18;;6286:50:24::1;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;6272:64:::0;-1:-1:-1;6350:18:24::1;6371:12;;::::0;::::1;:4:::0;:12:::1;:::i;:::-;:19;;6350:40;;6410:9;6405:296;6425:10;6421:1;:14;6405:296;;;6456:26;6485:12;;::::0;::::1;:4:::0;:12:::1;:::i;:::-;6498:1;6485:15;;;;;;;:::i;:::-;;;;;;;;;;;;:::i;:::-;6456:44:::0;-1:-1:-1;6518:22:24::1;:11;:22;;6541:7;;::::0;::::1;:4:::0;:7:::1;:::i;:::-;6550:14;;::::0;::::1;:6:::0;:14:::1;:::i;:::-;6566:10;;::::0;::::1;:6:::0;:10:::1;:::i;:::-;6578:19;::::0;;;::::1;::::0;::::1;;:::i;:::-;6599:15;;::::0;::::1;:6:::0;:15:::1;:::i;:::-;6518:97;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;6665:3;;;;;6438:263;6405:296;;;-1:-1:-1::0;6715:39:24::1;::::0;;;;::::1;::::0;::::1;8832:25:38::0;;;6743:10:24::1;::::0;::::1;;8873:18:38::0;;;8866:34;6715:15:24::1;:22;;::::0;::::1;::::0;8805:18:38;;6715:39:24::1;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;6913:3;;;;;6211:730;;;6180:761;;;;6057:890;5973:974:::0;;:::o;4985:603::-;2329:11:37;;2344:1;2329:16;2325:42;;2354:13;;;;;;;;;;;;;;2325:42;2395:13;;;;2381:10;:27;2377:59;;2417:19;;;;;;;;;;;;;;2377:59;5137:11:24::1;5151:26;:10;:26;;5178:7;;::::0;::::1;:4:::0;:7:::1;:::i;:::-;5187:13;::::0;;;::::1;::::0;::::1;;:::i;:::-;5151:50;::::0;;::::1;::::0;;;;;;5098:42:38;5167:15;;;5151:50:24::1;::::0;::::1;5149:34:38::0;5219:15;;5199:18;;;5192:43;5061:18;;5151:50:24::1;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;5137:64:::0;-1:-1:-1;5211:18:24::1;5232:12;;::::0;::::1;:4:::0;:12:::1;:::i;:::-;:19;;5211:40;;5266:9;5261:272;5281:10;5277:1;:14;5261:272;;;5308:26;5337:12;;::::0;::::1;:4:::0;:12:::1;:::i;:::-;5350:1;5337:15;;;;;;;:::i;:::-;;;;;;;;;;;;:::i;:::-;5308:44:::0;-1:-1:-1;5366:22:24::1;:11;:22;;5389:7;;::::0;::::1;:4:::0;:7:::1;:::i;:::-;5398:14;;::::0;::::1;:6:::0;:14:::1;:::i;:::-;5414:10;;::::0;::::1;:6:::0;:10:::1;:::i;:::-;5426:19;::::0;;;::::1;::::0;::::1;;:::i;:::-;5447:15;;::::0;::::1;:6:::0;:15:::1;:::i;:::-;5366:97;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;5505:3;;;;;5294:239;5261:272;;;-1:-1:-1::0;5542:39:24::1;::::0;;;;::::1;::::0;::::1;8832:25:38::0;;;5570:10:24::1;::::0;::::1;;8873:18:38::0;;;8866:34;5542:15:24::1;:22;;::::0;::::1;::::0;8805:18:38;;5542:39:24::1;;;;;;;;;;;;;;;;;::::0;::::1;;;;;;;;;;;;::::0;::::1;;;;;;;;;5061:527;;4985:603:::0;:::o;3574:119:37:-;1094:13:5;:11;:13::i;:::-;3633:18:37::1;3640:11;3633:18:::0;;;3666:20:::1;::::0;::::1;::::0;3633:18;3666:20:::1;3574:119::o:0;1824:101:5:-;1094:13;:11;:13::i;:::-;1888:30:::1;1915:1;1888:18;:30::i;:::-;1824:101::o:0;1734:212:6:-;926:13;;719:10:10;;1833:24:6;926:13;1833:24;;1825:78;;;;;;;9113:2:38;1825:78:6;;;9095:21:38;9152:2;9132:18;;;9125:30;9191:34;9171:18;;;9164:62;9262:11;9242:18;;;9235:39;9291:19;;1825:78:6;;;;;;;;;1913:26;1932:6;1913:18;:26::i;:::-;1776:170;1734:212::o;3520:1143:24:-;3696:12;3712:1;3696:17;3692:45;;3722:15;;;;;;;;;;;;;;3692:45;3747:11;3773:22;:10;:22;;3796:15;;;;:12;:15;:::i;:::-;3813:21;;;;;;;;:::i;:::-;3836;;;;3859:16;;;;3836:12;3859:16;:::i;:::-;3773:103;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;3747:129;-1:-1:-1;3908:7:24;3887:18;3932:545;3952:10;3948:1;:14;3932:545;;;3979:28;4010:7;;4018:1;4010:10;;;;;;;:::i;:::-;;;;;;;;;;;;:::i;:::-;3979:41;-1:-1:-1;4034:18:24;:11;:18;;4070:15;;;;:12;:15;:::i;:::-;4103:14;;;;:6;:14;:::i;:::-;4135:10;;;;:6;:10;:::i;:::-;4163:19;;;;;;;;:::i;:::-;4200:15;;;;:6;:15;:::i;:::-;4233;;;;4266:10;;;;4233:6;4266:10;:::i;:::-;4034:256;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4449:3;;;;;3965:512;3932:545;;;-1:-1:-1;4509:57:24;;;;;;;;8832:25:38;;;8873:18;;;8866:34;;;4487:19:24;;4509:20;:15;:20;;;;4537:9;;8805:18:38;;4509:57:24;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;4487:79;-1:-1:-1;4581:15:24;;4577:80;;4612:34;:10;4634:11;4612:21;:34::i;3354:117:37:-;1094:13:5;:11;:13::i;:::-;3431:33:37::1;3449:14;3431:17;:33::i;1139:178:6:-:0;1094:13:5;:11;:13::i;:::-;1228::6::1;:24:::0;;::::1;::::0;::::1;::::0;;;::::1;::::0;::::1;::::0;;;1292:7:::1;1247::5::0;1273:6;;;;1201:85;1292:7:6::1;1267:43;;;;;;;;;;;;1139:178:::0;:::o;1359:130:5:-;1247:7;1273:6;1422:23;1273:6;719:10:10;1422:23:5;1414:68;;;;;;;11442:2:38;1414:68:5;;;11424:21:38;;;11461:18;;;11454:30;11520:34;11500:18;;;11493:62;11572:18;;1414:68:5;11240:356:38;1501:153:6;1590:13;1583:20;;;;;;1613:34;1638:8;1613:24;:34::i;225:350:36:-;292:12;513:1;510;507;504;496:6;492:2;485:5;480:35;469:46;;540:7;535:33;;556:12;;;;;;;;;;;;;;535:33;282:293;225:350;;:::o;4032:249:37:-;4106:28;;;4102:57;;4143:16;;;;;;;;;;;;;;4102:57;4192:13;;4175:59;;4223:10;180:74:38;;4175:59:37;;;;;4192:13;;4175:59;;168:2:38;153:18;4175:59:37;;;;;;;4244:13;:30;;;;;;;;;;;;;;;4032:249::o;2426:187:5:-;2499:16;2518:6;;;2534:17;;;;;;;;;;2566:40;;2518:6;;;;;;;2566:40;;2499:16;2566:40;2489:124;2426:187;:::o;746:384:38:-;826:8;836:6;890:3;883:4;875:6;871:17;867:27;857:55;;908:1;905;898:12;857:55;-1:-1:-1;931:20:38;;974:18;963:30;;960:50;;;1006:1;1003;996:12;960:50;1043:4;1035:6;1031:17;1019:29;;1103:3;1096:4;1086:6;1083:1;1079:14;1071:6;1067:27;1063:38;1060:47;1057:67;;;1120:1;1117;1110:12;1057:67;746:384;;;;;:::o;1135:482::-;1249:6;1257;1310:2;1298:9;1289:7;1285:23;1281:32;1278:52;;;1326:1;1323;1316:12;1278:52;1366:9;1353:23;1399:18;1391:6;1388:30;1385:50;;;1431:1;1428;1421:12;1385:50;1470:87;1549:7;1540:6;1529:9;1525:22;1470:87;:::i;:::-;1576:8;;1444:113;;-1:-1:-1;1135:482:38;-1:-1:-1;;;;1135:482:38:o;1622:157::-;1683:5;1728:3;1719:6;1714:3;1710:16;1706:26;1703:46;;;1745:1;1742;1735:12;1703:46;-1:-1:-1;1767:6:38;1622:157;-1:-1:-1;1622:157:38:o;1784:358::-;1871:6;1924:2;1912:9;1903:7;1899:23;1895:32;1892:52;;;1940:1;1937;1930:12;1892:52;1980:9;1967:23;2013:18;2005:6;2002:30;1999:50;;;2045:1;2042;2035:12;1999:50;2068:68;2128:7;2119:6;2108:9;2104:22;2068:68;:::i;:::-;2058:78;1784:358;-1:-1:-1;;;;1784:358:38:o;2584:821::-;2758:6;2766;2774;2782;2835:2;2823:9;2814:7;2810:23;2806:32;2803:52;;;2851:1;2848;2841:12;2803:52;2891:9;2878:23;2920:18;2961:2;2953:6;2950:14;2947:34;;;2977:1;2974;2967:12;2947:34;3000:68;3060:7;3051:6;3040:9;3036:22;3000:68;:::i;:::-;2990:78;;3121:2;3110:9;3106:18;3093:32;3077:48;;3150:2;3140:8;3137:16;3134:36;;;3166:1;3163;3156:12;3134:36;;3205:89;3286:7;3275:8;3264:9;3260:24;3205:89;:::i;:::-;2584:821;;3313:8;;-1:-1:-1;3179:115:38;;3395:2;3380:18;3367:32;;2584:821;-1:-1:-1;;;;2584:821:38:o;3410:309::-;3469:6;3522:2;3510:9;3501:7;3497:23;3493:32;3490:52;;;3538:1;3535;3528:12;3490:52;3577:9;3564:23;3627:42;3620:5;3616:54;3609:5;3606:65;3596:93;;3685:1;3682;3675:12;3596:93;3708:5;3410:309;-1:-1:-1;;;3410:309:38:o;3724:607::-;3836:4;3865:2;3894;3883:9;3876:21;3926:6;3920:13;3969:6;3964:2;3953:9;3949:18;3942:34;3994:1;4004:140;4018:6;4015:1;4012:13;4004:140;;;4113:14;;;4109:23;;4103:30;4079:17;;;4098:2;4075:26;4068:66;4033:10;;4004:140;;;4008:3;4193:1;4188:2;4179:6;4168:9;4164:22;4160:31;4153:42;4322:2;4252:66;4247:2;4239:6;4235:15;4231:88;4220:9;4216:104;4212:113;4204:121;;;;3724:607;;;;:::o;4336:184::-;4388:77;4385:1;4378:88;4485:4;4482:1;4475:15;4509:4;4506:1;4499:15;4525:384;4619:4;4677:11;4664:25;4767:66;4756:8;4740:14;4736:29;4732:102;4712:18;4708:127;4698:155;;4849:1;4846;4839:12;4698:155;4870:33;;;;;4525:384;-1:-1:-1;;4525:384:38:o;5246:184::-;5316:6;5369:2;5357:9;5348:7;5344:23;5340:32;5337:52;;;5385:1;5382;5375:12;5337:52;-1:-1:-1;5408:16:38;;5246:184;-1:-1:-1;5246:184:38:o;5435:634::-;5558:4;5564:6;5624:11;5611:25;5714:66;5703:8;5687:14;5683:29;5679:102;5659:18;5655:127;5645:155;;5796:1;5793;5786:12;5645:155;5823:33;;5875:20;;;-1:-1:-1;5918:18:38;5907:30;;5904:50;;;5950:1;5947;5940:12;5904:50;5983:4;5971:17;;-1:-1:-1;6034:1:38;6030:14;;;6014;6010:35;6000:46;;5997:66;;;6059:1;6056;6049:12;6465:276;6523:6;6576:2;6564:9;6555:7;6551:23;6547:32;6544:52;;;6592:1;6589;6582:12;6544:52;6631:9;6618:23;6681:10;6674:5;6670:22;6663:5;6660:33;6650:61;;6707:1;6704;6697:12;6746:580;6823:4;6829:6;6889:11;6876:25;6979:66;6968:8;6952:14;6948:29;6944:102;6924:18;6920:127;6910:155;;7061:1;7058;7051:12;6910:155;7088:33;;7140:20;;;-1:-1:-1;7183:18:38;7172:30;;7169:50;;;7215:1;7212;7205:12;7169:50;7248:4;7236:17;;-1:-1:-1;7279:14:38;7275:27;;;7265:38;;7262:58;;;7316:1;7313;7306:12;7331:269;7388:6;7441:2;7429:9;7420:7;7416:23;7412:32;7409:52;;;7457:1;7454;7447:12;7409:52;7496:9;7483:23;7546:4;7539:5;7535:16;7528:5;7525:27;7515:55;;7566:1;7563;7556:12;7605:325;7693:6;7688:3;7681:19;7745:6;7738:5;7731:4;7726:3;7722:14;7709:43;;7797:1;7790:4;7781:6;7776:3;7772:16;7768:27;7761:38;7663:3;7919:4;7849:66;7844:2;7836:6;7832:15;7828:88;7823:3;7819:98;7815:109;7808:116;;7605:325;;;;:::o;7935:718::-;8238:42;8230:6;8226:55;8215:9;8208:74;8330:10;8322:6;8318:23;8313:2;8302:9;8298:18;8291:51;8378:3;8373:2;8362:9;8358:18;8351:31;8189:4;8405:62;8462:3;8451:9;8447:19;8439:6;8431;8405:62;:::i;:::-;8515:4;8507:6;8503:17;8498:2;8487:9;8483:18;8476:45;8570:9;8562:6;8558:22;8552:3;8541:9;8537:19;8530:51;8598:49;8640:6;8632;8624;8598:49;:::i;:::-;8590:57;7935:718;-1:-1:-1;;;;;;;;;;7935:718:38:o;9321:538::-;9525:4;9554:42;9635:2;9627:6;9623:15;9612:9;9605:34;9687:2;9679:6;9675:15;9670:2;9659:9;9655:18;9648:43;;9727:6;9722:2;9711:9;9707:18;9700:34;9770:3;9765:2;9754:9;9750:18;9743:31;9791:62;9848:3;9837:9;9833:19;9825:6;9817;9791:62;:::i;:::-;9783:70;9321:538;-1:-1:-1;;;;;;;9321:538:38:o;9864:388::-;9962:4;10020:11;10007:25;10110:66;10099:8;10083:14;10079:29;10075:102;10055:18;10051:127;10041:155;;10192:1;10189;10182:12;10257:978;10644:42;10636:6;10632:55;10621:9;10614:74;10736:10;10728:6;10724:23;10719:2;10708:9;10704:18;10697:51;10784:3;10779:2;10768:9;10764:18;10757:31;10595:4;10811:62;10868:3;10857:9;10853:19;10845:6;10837;10811:62;:::i;:::-;10921:4;10913:6;10909:17;10904:2;10893:9;10889:18;10882:45;10976:9;10968:6;10964:22;10958:3;10947:9;10943:19;10936:51;11010:49;11052:6;11044;11036;11010:49;:::i;:::-;10996:63;;11096:6;11090:3;11079:9;11075:19;11068:35;11152:9;11144:6;11140:22;11134:3;11123:9;11119:19;11112:51;11180:49;11222:6;11214;11206;11180:49;:::i;:::-;11172:57;10257:978;-1:-1:-1;;;;;;;;;;;;;10257:978:38:o
Swarm Source
none://164736f6c6343000815000a
Loading...
Loading
Loading...
Loading
0x00000000fc94856F3967b047325F88d47Bc225d0
Multichain Portfolio | 34 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.