Feature Tip: Add private address tag to any address under My Name Tag !
Latest 25 from a total of 28 transactions
| Transaction Hash |
Method
|
Block
|
From
|
|
To
|
||||
|---|---|---|---|---|---|---|---|---|---|
| Receive Message | 23822529 | 113 days ago | IN | 0 ETH | 0.00007719 | ||||
| Receive Message | 23737415 | 125 days ago | IN | 0 ETH | 0.00013677 | ||||
| Receive Message | 23679273 | 133 days ago | IN | 0 ETH | 0.00003525 | ||||
| Receive Message | 23545672 | 152 days ago | IN | 0 ETH | 0.00001434 | ||||
| Receive Message | 23464932 | 163 days ago | IN | 0 ETH | 0.00002237 | ||||
| Receive Message | 23069177 | 219 days ago | IN | 0 ETH | 0.00009182 | ||||
| Receive Message | 22874381 | 246 days ago | IN | 0 ETH | 0.0000833 | ||||
| Receive Message | 22837017 | 251 days ago | IN | 0 ETH | 0.00004047 | ||||
| Receive Message | 22793951 | 257 days ago | IN | 0 ETH | 0.00004881 | ||||
| Receive Message | 22786078 | 258 days ago | IN | 0 ETH | 0.00008104 | ||||
| Receive Message | 22766629 | 261 days ago | IN | 0 ETH | 0.0005009 | ||||
| Receive Message | 22738142 | 265 days ago | IN | 0 ETH | 0.00036038 | ||||
| Receive Message | 22737264 | 265 days ago | IN | 0 ETH | 0.00017501 | ||||
| Receive Message | 22731318 | 266 days ago | IN | 0 ETH | 0.00017423 | ||||
| Receive Message | 22730524 | 266 days ago | IN | 0 ETH | 0.000189 | ||||
| Receive Message | 22730066 | 266 days ago | IN | 0 ETH | 0.00010447 | ||||
| Receive Message | 22716177 | 268 days ago | IN | 0 ETH | 0.00047344 | ||||
| Receive Message | 22653154 | 277 days ago | IN | 0 ETH | 0.00013845 | ||||
| Receive Message | 22653051 | 277 days ago | IN | 0 ETH | 0.00013106 | ||||
| Receive Message | 22639311 | 279 days ago | IN | 0 ETH | 0.00148004 | ||||
| Receive Message | 22629625 | 280 days ago | IN | 0 ETH | 0.00007504 | ||||
| Receive Message | 22587658 | 286 days ago | IN | 0 ETH | 0.00074924 | ||||
| Receive Message | 22587485 | 286 days ago | IN | 0 ETH | 0.00092525 | ||||
| Receive Message | 22543685 | 292 days ago | IN | 0 ETH | 0.00017197 | ||||
| Receive Message | 22543594 | 292 days ago | IN | 0 ETH | 0.00021295 |
Latest 1 internal transaction
Advanced mode:
| Parent Transaction Hash | Method | Block |
From
|
|
To
|
||
|---|---|---|---|---|---|---|---|
| 0x60806040 | 22517822 | 296 days ago | Contract Creation | 0 ETH |
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
Contract Name:
TransparentUpgradeableProxy
Compiler Version
v0.8.25+commit.b61c2a91
Optimization Enabled:
Yes with 200 runs
Other Settings:
cancun EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.2.0) (proxy/transparent/TransparentUpgradeableProxy.sol)
pragma solidity ^0.8.22;
import {ERC1967Utils} from "../ERC1967/ERC1967Utils.sol";
import {ERC1967Proxy} from "../ERC1967/ERC1967Proxy.sol";
import {IERC1967} from "../../interfaces/IERC1967.sol";
import {ProxyAdmin} from "./ProxyAdmin.sol";
/**
* @dev Interface for {TransparentUpgradeableProxy}. In order to implement transparency, {TransparentUpgradeableProxy}
* does not implement this interface directly, and its upgradeability mechanism is implemented by an internal dispatch
* mechanism. The compiler is unaware that these functions are implemented by {TransparentUpgradeableProxy} and will not
* include them in the ABI so this interface must be used to interact with it.
*/
interface ITransparentUpgradeableProxy is IERC1967 {
/// @dev See {UUPSUpgradeable-upgradeToAndCall}
function upgradeToAndCall(address newImplementation, bytes calldata data) external payable;
}
/**
* @dev This contract implements a proxy that is upgradeable through an associated {ProxyAdmin} instance.
*
* To avoid https://medium.com/nomic-labs-blog/malicious-backdoors-in-ethereum-proxies-62629adf3357[proxy selector
* clashing], which can potentially be used in an attack, this contract uses the
* https://blog.openzeppelin.com/the-transparent-proxy-pattern/[transparent proxy pattern]. This pattern implies two
* things that go hand in hand:
*
* 1. If any account other than the admin calls the proxy, the call will be forwarded to the implementation, even if
* that call matches the {ITransparentUpgradeableProxy-upgradeToAndCall} function exposed by the proxy itself.
* 2. If the admin calls the proxy, it can call the `upgradeToAndCall` function but any other call won't be forwarded to
* the implementation. If the admin tries to call a function on the implementation it will fail with an error indicating
* the proxy admin cannot fallback to the target implementation.
*
* These properties mean that the admin account can only be used for upgrading the proxy, so it's best if it's a
* dedicated account that is not used for anything else. This will avoid headaches due to sudden errors when trying to
* call a function from the proxy implementation. For this reason, the proxy deploys an instance of {ProxyAdmin} and
* allows upgrades only if they come through it. You should think of the `ProxyAdmin` instance as the administrative
* interface of the proxy, including the ability to change who can trigger upgrades by transferring ownership.
*
* NOTE: The real interface of this proxy is that defined in `ITransparentUpgradeableProxy`. This contract does not
* inherit from that interface, and instead `upgradeToAndCall` is implicitly implemented using a custom dispatch
* mechanism in `_fallback`. Consequently, the compiler will not produce an ABI for this contract. This is necessary to
* fully implement transparency without decoding reverts caused by selector clashes between the proxy and the
* implementation.
*
* NOTE: This proxy does not inherit from {Context} deliberately. The {ProxyAdmin} of this contract won't send a
* meta-transaction in any way, and any other meta-transaction setup should be made in the implementation contract.
*
* IMPORTANT: This contract avoids unnecessary storage reads by setting the admin only during construction as an
* immutable variable, preventing any changes thereafter. However, the admin slot defined in ERC-1967 can still be
* overwritten by the implementation logic pointed to by this proxy. In such cases, the contract may end up in an
* undesirable state where the admin slot is different from the actual admin. Relying on the value of the admin slot
* is generally fine if the implementation is trusted.
*
* WARNING: It is not recommended to extend this contract to add additional external functions. If you do so, the
* compiler will not check that there are no selector conflicts, due to the note above. A selector clash between any new
* function and the functions declared in {ITransparentUpgradeableProxy} will be resolved in favor of the new one. This
* could render the `upgradeToAndCall` function inaccessible, preventing upgradeability and compromising transparency.
*/
contract TransparentUpgradeableProxy is ERC1967Proxy {
// An immutable address for the admin to avoid unnecessary SLOADs before each call
// at the expense of removing the ability to change the admin once it's set.
// This is acceptable if the admin is always a ProxyAdmin instance or similar contract
// with its own ability to transfer the permissions to another account.
address private immutable _admin;
/**
* @dev The proxy caller is the current admin, and can't fallback to the proxy target.
*/
error ProxyDeniedAdminAccess();
/**
* @dev Initializes an upgradeable proxy managed by an instance of a {ProxyAdmin} with an `initialOwner`,
* backed by the implementation at `_logic`, and optionally initialized with `_data` as explained in
* {ERC1967Proxy-constructor}.
*/
constructor(address _logic, address initialOwner, bytes memory _data) payable ERC1967Proxy(_logic, _data) {
_admin = address(new ProxyAdmin(initialOwner));
// Set the storage value and emit an event for ERC-1967 compatibility
ERC1967Utils.changeAdmin(_proxyAdmin());
}
/**
* @dev Returns the admin of this proxy.
*/
function _proxyAdmin() internal view virtual returns (address) {
return _admin;
}
/**
* @dev If caller is the admin process the call internally, otherwise transparently fallback to the proxy behavior.
*/
function _fallback() internal virtual override {
if (msg.sender == _proxyAdmin()) {
if (msg.sig != ITransparentUpgradeableProxy.upgradeToAndCall.selector) {
revert ProxyDeniedAdminAccess();
} else {
_dispatchUpgradeToAndCall();
}
} else {
super._fallback();
}
}
/**
* @dev Upgrade the implementation of the proxy. See {ERC1967Utils-upgradeToAndCall}.
*
* Requirements:
*
* - If `data` is empty, `msg.value` must be zero.
*/
function _dispatchUpgradeToAndCall() private {
(address newImplementation, bytes memory data) = abi.decode(msg.data[4:], (address, bytes));
ERC1967Utils.upgradeToAndCall(newImplementation, data);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
pragma solidity ^0.8.20;
import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.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.
*
* The initial owner is set to the address provided by the deployer. 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 OwnableUpgradeable is Initializable, ContextUpgradeable {
/// @custom:storage-location erc7201:openzeppelin.storage.Ownable
struct OwnableStorage {
address _owner;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Ownable")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant OwnableStorageLocation = 0x9016d09d72d40fdae2fd8ceac6b6234c7706214fd39c1cd1e609a0528c199300;
function _getOwnableStorage() private pure returns (OwnableStorage storage $) {
assembly {
$.slot := OwnableStorageLocation
}
}
/**
* @dev The caller account is not authorized to perform an operation.
*/
error OwnableUnauthorizedAccount(address account);
/**
* @dev The owner is not a valid owner account. (eg. `address(0)`)
*/
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the address provided by the deployer as the initial owner.
*/
function __Ownable_init(address initialOwner) internal onlyInitializing {
__Ownable_init_unchained(initialOwner);
}
function __Ownable_init_unchained(address initialOwner) internal onlyInitializing {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
/**
* @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) {
OwnableStorage storage $ = _getOwnableStorage();
return $._owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
/**
* @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 {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
OwnableStorage storage $ = _getOwnableStorage();
address oldOwner = $._owner;
$._owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol)
pragma solidity ^0.8.20;
/**
* @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
* behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
* external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
* function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
*
* The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
* reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
* case an upgrade adds a module that needs to be initialized.
*
* For example:
*
* [.hljs-theme-light.nopadding]
* ```solidity
* contract MyToken is ERC20Upgradeable {
* function initialize() initializer public {
* __ERC20_init("MyToken", "MTK");
* }
* }
*
* contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
* function initializeV2() reinitializer(2) public {
* __ERC20Permit_init("MyToken");
* }
* }
* ```
*
* TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
* possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
*
* CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
* that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
*
* [CAUTION]
* ====
* Avoid leaving a contract uninitialized.
*
* An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
* contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
* the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
*
* [.hljs-theme-light.nopadding]
* ```
* /// @custom:oz-upgrades-unsafe-allow constructor
* constructor() {
* _disableInitializers();
* }
* ```
* ====
*/
abstract contract Initializable {
/**
* @dev Storage of the initializable contract.
*
* It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions
* when using with upgradeable contracts.
*
* @custom:storage-location erc7201:openzeppelin.storage.Initializable
*/
struct InitializableStorage {
/**
* @dev Indicates that the contract has been initialized.
*/
uint64 _initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool _initializing;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;
/**
* @dev The contract is already initialized.
*/
error InvalidInitialization();
/**
* @dev The contract is not initializing.
*/
error NotInitializing();
/**
* @dev Triggered when the contract has been initialized or reinitialized.
*/
event Initialized(uint64 version);
/**
* @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
* `onlyInitializing` functions can be used to initialize parent contracts.
*
* Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any
* number of times. This behavior in the constructor can be useful during testing and is not expected to be used in
* production.
*
* Emits an {Initialized} event.
*/
modifier initializer() {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
// Cache values to avoid duplicated sloads
bool isTopLevelCall = !$._initializing;
uint64 initialized = $._initialized;
// Allowed calls:
// - initialSetup: the contract is not in the initializing state and no previous version was
// initialized
// - construction: the contract is initialized at version 1 (no reininitialization) and the
// current contract is just being deployed
bool initialSetup = initialized == 0 && isTopLevelCall;
bool construction = initialized == 1 && address(this).code.length == 0;
if (!initialSetup && !construction) {
revert InvalidInitialization();
}
$._initialized = 1;
if (isTopLevelCall) {
$._initializing = true;
}
_;
if (isTopLevelCall) {
$._initializing = false;
emit Initialized(1);
}
}
/**
* @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
* contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
* used to initialize parent contracts.
*
* A reinitializer may be used after the original initialization step. This is essential to configure modules that
* are added through upgrades and that require initialization.
*
* When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
* cannot be nested. If one is invoked in the context of another, execution will revert.
*
* Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
* a contract, executing them in the right order is up to the developer or operator.
*
* WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization.
*
* Emits an {Initialized} event.
*/
modifier reinitializer(uint64 version) {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing || $._initialized >= version) {
revert InvalidInitialization();
}
$._initialized = version;
$._initializing = true;
_;
$._initializing = false;
emit Initialized(version);
}
/**
* @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
* {initializer} and {reinitializer} modifiers, directly or indirectly.
*/
modifier onlyInitializing() {
_checkInitializing();
_;
}
/**
* @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.
*/
function _checkInitializing() internal view virtual {
if (!_isInitializing()) {
revert NotInitializing();
}
}
/**
* @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
* Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
* to any version. It is recommended to use this to lock implementation contracts that are designed to be called
* through proxies.
*
* Emits an {Initialized} event the first time it is successfully executed.
*/
function _disableInitializers() internal virtual {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing) {
revert InvalidInitialization();
}
if ($._initialized != type(uint64).max) {
$._initialized = type(uint64).max;
emit Initialized(type(uint64).max);
}
}
/**
* @dev Returns the highest version that has been initialized. See {reinitializer}.
*/
function _getInitializedVersion() internal view returns (uint64) {
return _getInitializableStorage()._initialized;
}
/**
* @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
*/
function _isInitializing() internal view returns (bool) {
return _getInitializableStorage()._initializing;
}
/**
* @dev Returns a pointer to the storage namespace.
*/
// solhint-disable-next-line var-name-mixedcase
function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
assembly {
$.slot := INITIALIZABLE_STORAGE
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @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 ContextUpgradeable is Initializable {
function __Context_init() internal onlyInitializing {
}
function __Context_init_unchained() internal onlyInitializing {
}
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
pragma solidity ^0.8.20;
import {Context} from "../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.
*
* The initial owner is set to the address provided by the deployer. 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;
/**
* @dev The caller account is not authorized to perform an operation.
*/
error OwnableUnauthorizedAccount(address account);
/**
* @dev The owner is not a valid owner account. (eg. `address(0)`)
*/
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the address provided by the deployer as the initial owner.
*/
constructor(address initialOwner) {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
/**
* @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 {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
/**
* @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 {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_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 v5.1.0) (interfaces/IERC1363.sol)
pragma solidity ^0.8.20;
import {IERC20} from "./IERC20.sol";
import {IERC165} from "./IERC165.sol";
/**
* @title IERC1363
* @dev Interface of the ERC-1363 standard as defined in the https://eips.ethereum.org/EIPS/eip-1363[ERC-1363].
*
* Defines an extension interface for ERC-20 tokens that supports executing code on a recipient contract
* after `transfer` or `transferFrom`, or code on a spender contract after `approve`, in a single transaction.
*/
interface IERC1363 is IERC20, IERC165 {
/*
* Note: the ERC-165 identifier for this interface is 0xb0202a11.
* 0xb0202a11 ===
* bytes4(keccak256('transferAndCall(address,uint256)')) ^
* bytes4(keccak256('transferAndCall(address,uint256,bytes)')) ^
* bytes4(keccak256('transferFromAndCall(address,address,uint256)')) ^
* bytes4(keccak256('transferFromAndCall(address,address,uint256,bytes)')) ^
* bytes4(keccak256('approveAndCall(address,uint256)')) ^
* bytes4(keccak256('approveAndCall(address,uint256,bytes)'))
*/
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferAndCall(address to, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @param data Additional data with no specified format, sent in call to `to`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param from The address which you want to send tokens from.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferFromAndCall(address from, address to, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param from The address which you want to send tokens from.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @param data Additional data with no specified format, sent in call to `to`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferFromAndCall(address from, address to, uint256 value, bytes calldata data) external returns (bool);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
* @param spender The address which will spend the funds.
* @param value The amount of tokens to be spent.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function approveAndCall(address spender, uint256 value) external returns (bool);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
* @param spender The address which will spend the funds.
* @param value The amount of tokens to be spent.
* @param data Additional data with no specified format, sent in call to `spender`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function approveAndCall(address spender, uint256 value, bytes calldata data) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC165.sol)
pragma solidity ^0.8.20;
import {IERC165} from "../utils/introspection/IERC165.sol";// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC1967.sol)
pragma solidity ^0.8.20;
/**
* @dev ERC-1967: Proxy Storage Slots. This interface contains the events defined in the ERC.
*/
interface IERC1967 {
/**
* @dev Emitted when the implementation is upgraded.
*/
event Upgraded(address indexed implementation);
/**
* @dev Emitted when the admin account has changed.
*/
event AdminChanged(address previousAdmin, address newAdmin);
/**
* @dev Emitted when the beacon is changed.
*/
event BeaconUpgraded(address indexed beacon);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../token/ERC20/IERC20.sol";// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/beacon/IBeacon.sol)
pragma solidity ^0.8.20;
/**
* @dev This is the interface that {BeaconProxy} expects of its beacon.
*/
interface IBeacon {
/**
* @dev Must return an address that can be used as a delegate call target.
*
* {UpgradeableBeacon} will check that this address is a contract.
*/
function implementation() external view returns (address);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.2.0) (proxy/ERC1967/ERC1967Proxy.sol)
pragma solidity ^0.8.22;
import {Proxy} from "../Proxy.sol";
import {ERC1967Utils} from "./ERC1967Utils.sol";
/**
* @dev This contract implements an upgradeable proxy. It is upgradeable because calls are delegated to an
* implementation address that can be changed. This address is stored in storage in the location specified by
* https://eips.ethereum.org/EIPS/eip-1967[ERC-1967], so that it doesn't conflict with the storage layout of the
* implementation behind the proxy.
*/
contract ERC1967Proxy is Proxy {
/**
* @dev Initializes the upgradeable proxy with an initial implementation specified by `implementation`.
*
* If `_data` is nonempty, it's used as data in a delegate call to `implementation`. This will typically be an
* encoded function call, and allows initializing the storage of the proxy like a Solidity constructor.
*
* Requirements:
*
* - If `data` is empty, `msg.value` must be zero.
*/
constructor(address implementation, bytes memory _data) payable {
ERC1967Utils.upgradeToAndCall(implementation, _data);
}
/**
* @dev Returns the current implementation address.
*
* TIP: To get this value clients can read directly from the storage slot shown below (specified by ERC-1967) using
* the https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
* `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc`
*/
function _implementation() internal view virtual override returns (address) {
return ERC1967Utils.getImplementation();
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.2.0) (proxy/ERC1967/ERC1967Utils.sol)
pragma solidity ^0.8.22;
import {IBeacon} from "../beacon/IBeacon.sol";
import {IERC1967} from "../../interfaces/IERC1967.sol";
import {Address} from "../../utils/Address.sol";
import {StorageSlot} from "../../utils/StorageSlot.sol";
/**
* @dev This library provides getters and event emitting update functions for
* https://eips.ethereum.org/EIPS/eip-1967[ERC-1967] slots.
*/
library ERC1967Utils {
/**
* @dev Storage slot with the address of the current implementation.
* This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1.
*/
// solhint-disable-next-line private-vars-leading-underscore
bytes32 internal constant IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
/**
* @dev The `implementation` of the proxy is invalid.
*/
error ERC1967InvalidImplementation(address implementation);
/**
* @dev The `admin` of the proxy is invalid.
*/
error ERC1967InvalidAdmin(address admin);
/**
* @dev The `beacon` of the proxy is invalid.
*/
error ERC1967InvalidBeacon(address beacon);
/**
* @dev An upgrade function sees `msg.value > 0` that may be lost.
*/
error ERC1967NonPayable();
/**
* @dev Returns the current implementation address.
*/
function getImplementation() internal view returns (address) {
return StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value;
}
/**
* @dev Stores a new address in the ERC-1967 implementation slot.
*/
function _setImplementation(address newImplementation) private {
if (newImplementation.code.length == 0) {
revert ERC1967InvalidImplementation(newImplementation);
}
StorageSlot.getAddressSlot(IMPLEMENTATION_SLOT).value = newImplementation;
}
/**
* @dev Performs implementation upgrade with additional setup call if data is nonempty.
* This function is payable only if the setup call is performed, otherwise `msg.value` is rejected
* to avoid stuck value in the contract.
*
* Emits an {IERC1967-Upgraded} event.
*/
function upgradeToAndCall(address newImplementation, bytes memory data) internal {
_setImplementation(newImplementation);
emit IERC1967.Upgraded(newImplementation);
if (data.length > 0) {
Address.functionDelegateCall(newImplementation, data);
} else {
_checkNonPayable();
}
}
/**
* @dev Storage slot with the admin of the contract.
* This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1.
*/
// solhint-disable-next-line private-vars-leading-underscore
bytes32 internal constant ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
/**
* @dev Returns the current admin.
*
* TIP: To get this value clients can read directly from the storage slot shown below (specified by ERC-1967) using
* the https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
* `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`
*/
function getAdmin() internal view returns (address) {
return StorageSlot.getAddressSlot(ADMIN_SLOT).value;
}
/**
* @dev Stores a new address in the ERC-1967 admin slot.
*/
function _setAdmin(address newAdmin) private {
if (newAdmin == address(0)) {
revert ERC1967InvalidAdmin(address(0));
}
StorageSlot.getAddressSlot(ADMIN_SLOT).value = newAdmin;
}
/**
* @dev Changes the admin of the proxy.
*
* Emits an {IERC1967-AdminChanged} event.
*/
function changeAdmin(address newAdmin) internal {
emit IERC1967.AdminChanged(getAdmin(), newAdmin);
_setAdmin(newAdmin);
}
/**
* @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
* This is the keccak-256 hash of "eip1967.proxy.beacon" subtracted by 1.
*/
// solhint-disable-next-line private-vars-leading-underscore
bytes32 internal constant BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;
/**
* @dev Returns the current beacon.
*/
function getBeacon() internal view returns (address) {
return StorageSlot.getAddressSlot(BEACON_SLOT).value;
}
/**
* @dev Stores a new beacon in the ERC-1967 beacon slot.
*/
function _setBeacon(address newBeacon) private {
if (newBeacon.code.length == 0) {
revert ERC1967InvalidBeacon(newBeacon);
}
StorageSlot.getAddressSlot(BEACON_SLOT).value = newBeacon;
address beaconImplementation = IBeacon(newBeacon).implementation();
if (beaconImplementation.code.length == 0) {
revert ERC1967InvalidImplementation(beaconImplementation);
}
}
/**
* @dev Change the beacon and trigger a setup call if data is nonempty.
* This function is payable only if the setup call is performed, otherwise `msg.value` is rejected
* to avoid stuck value in the contract.
*
* Emits an {IERC1967-BeaconUpgraded} event.
*
* CAUTION: Invoking this function has no effect on an instance of {BeaconProxy} since v5, since
* it uses an immutable beacon without looking at the value of the ERC-1967 beacon slot for
* efficiency.
*/
function upgradeBeaconToAndCall(address newBeacon, bytes memory data) internal {
_setBeacon(newBeacon);
emit IERC1967.BeaconUpgraded(newBeacon);
if (data.length > 0) {
Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);
} else {
_checkNonPayable();
}
}
/**
* @dev Reverts if `msg.value` is not zero. It can be used to avoid `msg.value` stuck in the contract
* if an upgrade doesn't perform an initialization call.
*/
function _checkNonPayable() private {
if (msg.value > 0) {
revert ERC1967NonPayable();
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/Proxy.sol)
pragma solidity ^0.8.20;
/**
* @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM
* instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to
* be specified by overriding the virtual {_implementation} function.
*
* Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a
* different contract through the {_delegate} function.
*
* The success and return data of the delegated call will be returned back to the caller of the proxy.
*/
abstract contract Proxy {
/**
* @dev Delegates the current call to `implementation`.
*
* This function does not return to its internal call site, it will return directly to the external caller.
*/
function _delegate(address implementation) internal virtual {
assembly {
// Copy msg.data. We take full control of memory in this inline assembly
// block because it will not return to Solidity code. We overwrite the
// Solidity scratch pad at memory position 0.
calldatacopy(0, 0, calldatasize())
// Call the implementation.
// out and outsize are 0 because we don't know the size yet.
let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)
// Copy the returned data.
returndatacopy(0, 0, returndatasize())
switch result
// delegatecall returns 0 on error.
case 0 {
revert(0, returndatasize())
}
default {
return(0, returndatasize())
}
}
}
/**
* @dev This is a virtual function that should be overridden so it returns the address to which the fallback
* function and {_fallback} should delegate.
*/
function _implementation() internal view virtual returns (address);
/**
* @dev Delegates the current call to the address returned by `_implementation()`.
*
* This function does not return to its internal call site, it will return directly to the external caller.
*/
function _fallback() internal virtual {
_delegate(_implementation());
}
/**
* @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other
* function in the contract matches the call data.
*/
fallback() external payable virtual {
_fallback();
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.2.0) (proxy/transparent/ProxyAdmin.sol)
pragma solidity ^0.8.22;
import {ITransparentUpgradeableProxy} from "./TransparentUpgradeableProxy.sol";
import {Ownable} from "../../access/Ownable.sol";
/**
* @dev This is an auxiliary contract meant to be assigned as the admin of a {TransparentUpgradeableProxy}. For an
* explanation of why you would want to use this see the documentation for {TransparentUpgradeableProxy}.
*/
contract ProxyAdmin is Ownable {
/**
* @dev The version of the upgrade interface of the contract. If this getter is missing, both `upgrade(address,address)`
* and `upgradeAndCall(address,address,bytes)` are present, and `upgrade` must be used if no function should be called,
* while `upgradeAndCall` will invoke the `receive` function if the third argument is the empty byte string.
* If the getter returns `"5.0.0"`, only `upgradeAndCall(address,address,bytes)` is present, and the third argument must
* be the empty byte string if no function should be called, making it impossible to invoke the `receive` function
* during an upgrade.
*/
string public constant UPGRADE_INTERFACE_VERSION = "5.0.0";
/**
* @dev Sets the initial owner who can perform upgrades.
*/
constructor(address initialOwner) Ownable(initialOwner) {}
/**
* @dev Upgrades `proxy` to `implementation` and calls a function on the new implementation.
* See {TransparentUpgradeableProxy-_dispatchUpgradeToAndCall}.
*
* Requirements:
*
* - This contract must be the admin of `proxy`.
* - If `data` is empty, `msg.value` must be zero.
*/
function upgradeAndCall(
ITransparentUpgradeableProxy proxy,
address implementation,
bytes memory data
) public payable virtual onlyOwner {
proxy.upgradeToAndCall{value: msg.value}(implementation, data);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC1155/IERC1155Receiver.sol)
pragma solidity ^0.8.20;
import {IERC165} from "../../utils/introspection/IERC165.sol";
/**
* @dev Interface that must be implemented by smart contracts in order to receive
* ERC-1155 token transfers.
*/
interface IERC1155Receiver is IERC165 {
/**
* @dev Handles the receipt of a single ERC-1155 token type. This function is
* called at the end of a `safeTransferFrom` after the balance has been updated.
*
* NOTE: To accept the transfer, this must return
* `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))`
* (i.e. 0xf23a6e61, or its own function selector).
*
* @param operator The address which initiated the transfer (i.e. msg.sender)
* @param from The address which previously owned the token
* @param id The ID of the token being transferred
* @param value The amount of tokens being transferred
* @param data Additional data with no specified format
* @return `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)"))` if transfer is allowed
*/
function onERC1155Received(
address operator,
address from,
uint256 id,
uint256 value,
bytes calldata data
) external returns (bytes4);
/**
* @dev Handles the receipt of a multiple ERC-1155 token types. This function
* is called at the end of a `safeBatchTransferFrom` after the balances have
* been updated.
*
* NOTE: To accept the transfer(s), this must return
* `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))`
* (i.e. 0xbc197c81, or its own function selector).
*
* @param operator The address which initiated the batch transfer (i.e. msg.sender)
* @param from The address which previously owned the token
* @param ids An array containing ids of each token being transferred (order and length must match values array)
* @param values An array containing amounts of each token being transferred (order and length must match ids array)
* @param data Additional data with no specified format
* @return `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)"))` if transfer is allowed
*/
function onERC1155BatchReceived(
address operator,
address from,
uint256[] calldata ids,
uint256[] calldata values,
bytes calldata data
) external returns (bytes4);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC-20 standard as defined in the ERC.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 value) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 value) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.2.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
import {IERC1363} from "../../../interfaces/IERC1363.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC-20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
/**
* @dev An operation with an ERC-20 token failed.
*/
error SafeERC20FailedOperation(address token);
/**
* @dev Indicates a failed `decreaseAllowance` request.
*/
error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*
* IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
* smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
* this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
* that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
forceApprove(token, spender, oldAllowance + value);
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
* value, non-reverting calls are assumed to be successful.
*
* IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
* smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
* this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
* that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
*/
function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
unchecked {
uint256 currentAllowance = token.allowance(address(this), spender);
if (currentAllowance < requestedDecrease) {
revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
}
forceApprove(token, spender, currentAllowance - requestedDecrease);
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*
* NOTE: If the token implements ERC-7674, this function will not modify any temporary allowance. This function
* only sets the "standard" allowance. Any temporary allowance will remain active, in addition to the value being
* set here.
*/
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Performs an {ERC1363} transferAndCall, with a fallback to the simple {ERC20} transfer if the target has no
* code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* Reverts if the returned value is other than `true`.
*/
function transferAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
if (to.code.length == 0) {
safeTransfer(token, to, value);
} else if (!token.transferAndCall(to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Performs an {ERC1363} transferFromAndCall, with a fallback to the simple {ERC20} transferFrom if the target
* has no code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* Reverts if the returned value is other than `true`.
*/
function transferFromAndCallRelaxed(
IERC1363 token,
address from,
address to,
uint256 value,
bytes memory data
) internal {
if (to.code.length == 0) {
safeTransferFrom(token, from, to, value);
} else if (!token.transferFromAndCall(from, to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Performs an {ERC1363} approveAndCall, with a fallback to the simple {ERC20} approve if the target has no
* code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* NOTE: When the recipient address (`to`) has no code (i.e. is an EOA), this function behaves as {forceApprove}.
* Opposedly, when the recipient address (`to`) has code, this function only attempts to call {ERC1363-approveAndCall}
* once without retrying, and relies on the returned value to be true.
*
* Reverts if the returned value is other than `true`.
*/
function approveAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
if (to.code.length == 0) {
forceApprove(token, to, value);
} else if (!token.approveAndCall(to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturnBool} that reverts if call fails to meet the requirements.
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
uint256 returnSize;
uint256 returnValue;
assembly ("memory-safe") {
let success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
// bubble errors
if iszero(success) {
let ptr := mload(0x40)
returndatacopy(ptr, 0, returndatasize())
revert(ptr, returndatasize())
}
returnSize := returndatasize()
returnValue := mload(0)
}
if (returnSize == 0 ? address(token).code.length == 0 : returnValue != 1) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturn} that silently catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
bool success;
uint256 returnSize;
uint256 returnValue;
assembly ("memory-safe") {
success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
returnSize := returndatasize()
returnValue := mload(0)
}
return success && (returnSize == 0 ? address(token).code.length > 0 : returnValue == 1);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC721/IERC721Receiver.sol)
pragma solidity ^0.8.20;
/**
* @title ERC-721 token receiver interface
* @dev Interface for any contract that wants to support safeTransfers
* from ERC-721 asset contracts.
*/
interface IERC721Receiver {
/**
* @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
* by `operator` from `from`, this function is called.
*
* It must return its Solidity selector to confirm the token transfer.
* If any other value is returned or the interface is not implemented by the recipient, the transfer will be
* reverted.
*
* The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.
*/
function onERC721Received(
address operator,
address from,
uint256 tokenId,
bytes calldata data
) external returns (bytes4);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.2.0) (utils/Address.sol)
pragma solidity ^0.8.20;
import {Errors} from "./Errors.sol";
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev There's no code at `target` (it is not a contract).
*/
error AddressEmptyCode(address target);
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
if (address(this).balance < amount) {
revert Errors.InsufficientBalance(address(this).balance, amount);
}
(bool success, bytes memory returndata) = recipient.call{value: amount}("");
if (!success) {
_revert(returndata);
}
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason or custom error, it is bubbled
* up by this function (like regular Solidity function calls). However, if
* the call reverted with no returned reason, this function reverts with a
* {Errors.FailedCall} error.
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
if (address(this).balance < value) {
revert Errors.InsufficientBalance(address(this).balance, value);
}
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
* was not a contract or bubbling up the revert reason (falling back to {Errors.FailedCall}) in case
* of an unsuccessful call.
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata
) internal view returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
// only check if target is a contract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
if (returndata.length == 0 && target.code.length == 0) {
revert AddressEmptyCode(target);
}
return returndata;
}
}
/**
* @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
* revert reason or with a default {Errors.FailedCall} error.
*/
function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
return returndata;
}
}
/**
* @dev Reverts with returndata if present. Otherwise reverts with {Errors.FailedCall}.
*/
function _revert(bytes memory returndata) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
assembly ("memory-safe") {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert Errors.FailedCall();
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
/**
* @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;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/Errors.sol)
pragma solidity ^0.8.20;
/**
* @dev Collection of common custom errors used in multiple contracts
*
* IMPORTANT: Backwards compatibility is not guaranteed in future versions of the library.
* It is recommended to avoid relying on the error API for critical functionality.
*
* _Available since v5.1._
*/
library Errors {
/**
* @dev The ETH balance of the account is not enough to perform the operation.
*/
error InsufficientBalance(uint256 balance, uint256 needed);
/**
* @dev A call to an address target failed. The target may have reverted.
*/
error FailedCall();
/**
* @dev The deployment failed.
*/
error FailedDeployment();
/**
* @dev A necessary precompile is missing.
*/
error MissingPrecompile(address);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/IERC165.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC-165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[ERC].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/StorageSlot.sol)
// This file was procedurally generated from scripts/generate/templates/StorageSlot.js.
pragma solidity ^0.8.20;
/**
* @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 ERC-1967 implementation slot:
* ```solidity
* contract ERC1967 {
* // Define the slot. Alternatively, use the SlotDerivation library to derive the slot.
* bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
*
* function _getImplementation() internal view returns (address) {
* return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
* }
*
* function _setImplementation(address newImplementation) internal {
* require(newImplementation.code.length > 0);
* StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
* }
* }
* ```
*
* TIP: Consider using this library along with {SlotDerivation}.
*/
library StorageSlot {
struct AddressSlot {
address value;
}
struct BooleanSlot {
bool value;
}
struct Bytes32Slot {
bytes32 value;
}
struct Uint256Slot {
uint256 value;
}
struct Int256Slot {
int256 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) {
assembly ("memory-safe") {
r.slot := slot
}
}
/**
* @dev Returns a `BooleanSlot` with member `value` located at `slot`.
*/
function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
assembly ("memory-safe") {
r.slot := slot
}
}
/**
* @dev Returns a `Bytes32Slot` with member `value` located at `slot`.
*/
function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
assembly ("memory-safe") {
r.slot := slot
}
}
/**
* @dev Returns a `Uint256Slot` with member `value` located at `slot`.
*/
function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
assembly ("memory-safe") {
r.slot := slot
}
}
/**
* @dev Returns a `Int256Slot` with member `value` located at `slot`.
*/
function getInt256Slot(bytes32 slot) internal pure returns (Int256Slot storage r) {
assembly ("memory-safe") {
r.slot := slot
}
}
/**
* @dev Returns a `StringSlot` with member `value` located at `slot`.
*/
function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
assembly ("memory-safe") {
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) {
assembly ("memory-safe") {
r.slot := store.slot
}
}
/**
* @dev Returns a `BytesSlot` with member `value` located at `slot`.
*/
function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
assembly ("memory-safe") {
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) {
assembly ("memory-safe") {
r.slot := store.slot
}
}
}/*
* Copyright (c) 2022, Circle Internet Financial Limited.
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
pragma solidity >=0.8.12;
import "./TypedMemView.sol";
/**
* @title BurnMessage Library
* @notice Library for formatted BurnMessages used by TokenMessenger.
* @dev BurnMessage format:
* Field Bytes Type Index
* version 4 uint32 0
* burnToken 32 bytes32 4
* mintRecipient 32 bytes32 36
* amount 32 uint256 68
* messageSender 32 bytes32 100
**/
library BurnMessage {
using TypedMemView for bytes;
using TypedMemView for bytes29;
uint8 private constant VERSION_INDEX = 0;
uint8 private constant VERSION_LEN = 4;
uint8 private constant BURN_TOKEN_INDEX = 4;
uint8 private constant BURN_TOKEN_LEN = 32;
uint8 private constant MINT_RECIPIENT_INDEX = 36;
uint8 private constant MINT_RECIPIENT_LEN = 32;
uint8 private constant AMOUNT_INDEX = 68;
uint8 private constant AMOUNT_LEN = 32;
uint8 private constant MSG_SENDER_INDEX = 100;
uint8 private constant MSG_SENDER_LEN = 32;
// 4 byte version + 32 bytes burnToken + 32 bytes mintRecipient + 32 bytes amount + 32 bytes messageSender
uint8 private constant BURN_MESSAGE_LEN = 132;
/**
* @notice Formats Burn message
* @param _version The message body version
* @param _burnToken The burn token address on source domain as bytes32
* @param _mintRecipient The mint recipient address as bytes32
* @param _amount The burn amount
* @param _messageSender The message sender
* @return Burn formatted message.
*/
function _formatMessage(
uint32 _version,
bytes32 _burnToken,
bytes32 _mintRecipient,
uint256 _amount,
bytes32 _messageSender
) internal pure returns (bytes memory) {
return
abi.encodePacked(
_version,
_burnToken,
_mintRecipient,
_amount,
_messageSender
);
}
/**
* @notice Retrieves the burnToken from a DepositForBurn BurnMessage
* @param _message The message
* @return sourceToken address as bytes32
*/
function _getMessageSender(bytes29 _message)
internal
pure
returns (bytes32)
{
return _message.index(MSG_SENDER_INDEX, MSG_SENDER_LEN);
}
/**
* @notice Retrieves the burnToken from a DepositForBurn BurnMessage
* @param _message The message
* @return sourceToken address as bytes32
*/
function _getBurnToken(bytes29 _message) internal pure returns (bytes32) {
return _message.index(BURN_TOKEN_INDEX, BURN_TOKEN_LEN);
}
/**
* @notice Retrieves the mintRecipient from a BurnMessage
* @param _message The message
* @return mintRecipient
*/
function _getMintRecipient(bytes29 _message)
internal
pure
returns (bytes32)
{
return _message.index(MINT_RECIPIENT_INDEX, MINT_RECIPIENT_LEN);
}
/**
* @notice Retrieves the amount from a BurnMessage
* @param _message The message
* @return amount
*/
function _getAmount(bytes29 _message) internal pure returns (uint256) {
return _message.indexUint(AMOUNT_INDEX, AMOUNT_LEN);
}
/**
* @notice Retrieves the version from a Burn message
* @param _message The message
* @return version
*/
function _getVersion(bytes29 _message) internal pure returns (uint32) {
return uint32(_message.indexUint(VERSION_INDEX, VERSION_LEN));
}
/**
* @notice Reverts if burn message is malformed or invalid length
* @param _message The burn message as bytes29
*/
function _validateBurnMessageFormat(bytes29 _message) internal pure {
require(_message.isValid(), "Malformed message");
require(_message.len() == BURN_MESSAGE_LEN, "Invalid message length");
}
}/*
* Copyright 2024 Circle Internet Group, Inc. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
pragma solidity >=0.8.12;
import { TypedMemView } from './TypedMemView.sol';
import { BurnMessage } from './BurnMessage.sol';
/**
* @title BurnMessageV2 Library
* @notice Library for formatted V2 BurnMessages used by TokenMessengerV2.
* @dev BurnMessageV2 format:
* Field Bytes Type Index
* version 4 uint32 0
* burnToken 32 bytes32 4
* mintRecipient 32 bytes32 36
* amount 32 uint256 68
* messageSender 32 bytes32 100
* maxFee 32 uint256 132
* feeExecuted 32 uint256 164
* expirationBlock 32 uint256 196
* hookData dynamic bytes 228
* @dev Additions from v1:
* - maxFee
* - feeExecuted
* - expirationBlock
* - hookData
**/
library BurnMessageV2 {
using TypedMemView for bytes;
using TypedMemView for bytes29;
using BurnMessage for bytes29;
// Field indices
uint8 private constant MAX_FEE_INDEX = 132;
uint8 private constant FEE_EXECUTED_INDEX = 164;
uint8 private constant EXPIRATION_BLOCK_INDEX = 196;
uint8 private constant HOOK_DATA_INDEX = 228;
uint256 private constant EMPTY_FEE_EXECUTED = 0;
uint256 private constant EMPTY_EXPIRATION_BLOCK = 0;
/**
* @notice Formats a V2 burn message
* @param _version The message body version
* @param _burnToken The burn token address on the source domain, as bytes32
* @param _mintRecipient The mint recipient address as bytes32
* @param _amount The burn amount
* @param _messageSender The message sender
* @param _maxFee The maximum fee to be paid on destination domain
* @param _hookData Optional hook data for processing on the destination domain
* @return Formatted message bytes.
*/
function _formatMessageForRelay(
uint32 _version,
bytes32 _burnToken,
bytes32 _mintRecipient,
uint256 _amount,
bytes32 _messageSender,
uint256 _maxFee,
bytes calldata _hookData
) internal pure returns (bytes memory) {
return
abi.encodePacked(
_version,
_burnToken,
_mintRecipient,
_amount,
_messageSender,
_maxFee,
EMPTY_FEE_EXECUTED,
EMPTY_EXPIRATION_BLOCK,
_hookData
);
}
// @notice Returns _message's version field
function _getVersion(bytes29 _message) internal pure returns (uint32) {
return _message._getVersion();
}
// @notice Returns _message's burnToken field
function _getBurnToken(bytes29 _message) internal pure returns (bytes32) {
return _message._getBurnToken();
}
// @notice Returns _message's mintRecipient field
function _getMintRecipient(bytes29 _message) internal pure returns (bytes32) {
return _message._getMintRecipient();
}
// @notice Returns _message's amount field
function _getAmount(bytes29 _message) internal pure returns (uint256) {
return _message._getAmount();
}
// @notice Returns _message's messageSender field
function _getMessageSender(bytes29 _message) internal pure returns (bytes32) {
return _message._getMessageSender();
}
// @notice Returns _message's maxFee field
function _getMaxFee(bytes29 _message) internal pure returns (uint256) {
return _message.indexUint(MAX_FEE_INDEX, 32);
}
// @notice Returns _message's feeExecuted field
function _getFeeExecuted(bytes29 _message) internal pure returns (uint256) {
return _message.indexUint(FEE_EXECUTED_INDEX, 32);
}
// @notice Returns _message's expirationBlock field
function _getExpirationBlock(bytes29 _message) internal pure returns (uint256) {
return _message.indexUint(EXPIRATION_BLOCK_INDEX, 32);
}
// @notice Returns _message's hookData field
function _getHookData(bytes29 _message) internal pure returns (bytes29) {
return _message.slice(HOOK_DATA_INDEX, _message.len() - HOOK_DATA_INDEX, 0);
}
/**
* @notice Reverts if burn message is malformed or invalid length
* @param _message The burn message as bytes29
*/
function _validateBurnMessageFormat(bytes29 _message) internal pure {
require(_message.isValid(), 'Malformed message');
require(_message.len() >= HOOK_DATA_INDEX, 'Invalid burn message: too short');
}
}/*
* Copyright (c) 2022, Circle Internet Financial Limited.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
pragma solidity ^0.8.0;
/**
* @title MessageTransmitter
* @notice Contract responsible for sending and receiving messages across chains.
*/
interface IMessageTransmitter {
// ============ State Variables ============
// Domain of chain on which the contract is deployed
function localDomain() external view returns (uint32);
// Message Format version
function version() external view returns (uint32);
// Maximum size of message body, in bytes.
// This value is set by owner.
function maxMessageBodySize() external view returns (uint256);
// Next available nonce from this source domain
function nextAvailableNonce() external view returns (uint64);
// Maps a bytes32 hash of (sourceDomain, nonce) -> uint256 (0 if unused, 1 if used)
function usedNonces(bytes32 _hash) external view returns (uint256);
}/*
* Copyright (c) 2022, Circle Internet Financial Limited.
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
pragma solidity ^0.8.0;
/**
* @title MessageTransmitterV2
* @notice Contract responsible for sending and receiving messages across chains.
*/
interface IMessageTransmitterV2 {
// ============ Events ============
/**
* @notice Emitted when a new message is dispatched
* @param message Raw bytes of message
*/
event MessageSent(bytes message);
// ============ State Variables ============
// Domain of chain on which the contract is deployed
function localDomain() external view returns (uint32);
// Message Format version
function version() external view returns (uint32);
// Maximum size of message body, in bytes.
// This value is set by owner.
function maxMessageBodySize() external view returns (uint256);
// Maps a bytes32 hash of (sourceDomain, nonce) -> uint256 (0 if unused, 1 if used)
function usedNonces(bytes32 _hash) external view returns (uint256);
}/*
* Copyright (c) 2022, Circle Internet Financial Limited.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;
/**
* @title IReceiver
* @notice Receives messages on destination chain and forwards them to IMessageDestinationHandler
*/
interface IReceiver {
/**
* @notice Receives an incoming message, validating the header and passing
* the body to application-specific handler.
* @param message The message raw bytes
* @param signature The message signature
* @return success bool, true if successful
*/
function receiveMessage(
bytes calldata message,
bytes calldata signature
) external returns (bool success);
}/*
* Copyright (c) 2022, Circle Internet Financial Limited.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;
/**
* @title IReceiverV2
* @notice Receives messages on destination chain and forwards them to IMessageDestinationHandler
*/
interface IReceiverV2 {
/**
* @notice Receives an incoming message, validating the header and passing
* the body to application-specific handler.
* @param message The message raw bytes
* @param signature The message signature
* @return success bool, true if successful
*/
function receiveMessage(bytes calldata message, bytes calldata signature) external returns (bool success);
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
interface ITokenMessenger {
// ============ Events ============
/**
* @notice Emitted when a DepositForBurn message is sent
* @param nonce unique nonce reserved by message
* @param burnToken address of token burnt on source domain
* @param amount deposit amount
* @param depositor address where deposit is transferred from
* @param mintRecipient address receiving minted tokens on destination domain as bytes32
* @param destinationDomain destination domain
* @param destinationTokenMessenger address of TokenMessenger on destination domain as bytes32
* @param destinationCaller authorized caller as bytes32 of receiveMessage() on destination domain, if not equal to bytes32(0).
* If equal to bytes32(0), any address can call receiveMessage().
*/
event DepositForBurn(
uint64 indexed nonce,
address indexed burnToken,
uint256 amount,
address indexed depositor,
bytes32 mintRecipient,
uint32 destinationDomain,
bytes32 destinationTokenMessenger,
bytes32 destinationCaller
);
// ============ State Variables ============
// Local Message Transmitter responsible for sending and receiving messages to/from remote domains
function localMessageTransmitter() external view returns (address);
// Version of message body format
function messageBodyVersion() external view returns (uint32);
// Minter responsible for minting and burning tokens on the local domain
function localMinter() external view returns (address);
// Valid TokenMessengers on remote domains
function remoteTokenMessengers(uint32 domain) external view returns (bytes32);
// ============ External Functions ============
/**
* @notice Deposits and burns tokens from sender to be minted on destination domain.
* Emits a `DepositForBurn` event.
* @dev reverts if:
* - given burnToken is not supported
* - given destinationDomain has no TokenMessenger registered
* - transferFrom() reverts. For example, if sender's burnToken balance or approved allowance
* to this contract is less than `amount`.
* - burn() reverts. For example, if `amount` is 0.
* - MessageTransmitter returns false or reverts.
* @param amount amount of tokens to burn
* @param destinationDomain destination domain
* @param mintRecipient address of mint recipient on destination domain
* @param burnToken address of contract to burn deposited tokens, on local domain
* @return nonce unique nonce reserved by message
*/
function depositForBurn(
uint256 amount,
uint32 destinationDomain,
bytes32 mintRecipient,
address burnToken
) external returns (uint64 nonce);
/**
* @notice Deposits and burns tokens from sender to be minted on destination domain. The mint
* on the destination domain must be called by `destinationCaller`.
* WARNING: if the `destinationCaller` does not represent a valid address as bytes32, then it will not be possible
* to broadcast the message on the destination domain. This is an advanced feature, and the standard
* depositForBurn() should be preferred for use cases where a specific destination caller is not required.
* Emits a `DepositForBurn` event.
* @dev reverts if:
* - given destinationCaller is zero address
* - given burnToken is not supported
* - given destinationDomain has no TokenMessenger registered
* - transferFrom() reverts. For example, if sender's burnToken balance or approved allowance
* to this contract is less than `amount`.
* - burn() reverts. For example, if `amount` is 0.
* - MessageTransmitter returns false or reverts.
* @param amount amount of tokens to burn
* @param destinationDomain destination domain
* @param mintRecipient address of mint recipient on destination domain
* @param burnToken address of contract to burn deposited tokens, on local domain
* @param destinationCaller caller on the destination domain, as bytes32
* @return nonce unique nonce reserved by message
*/
function depositForBurnWithCaller(
uint256 amount,
uint32 destinationDomain,
bytes32 mintRecipient,
address burnToken,
bytes32 destinationCaller
) external returns (uint64 nonce);
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
interface ITokenMessengerV2 {
// ============ Events ============
/**
* @notice Emitted when a DepositForBurn message is sent
* @param burnToken address of token burnt on source domain
* @param amount deposit amount
* @param depositor address where deposit is transferred from
* @param mintRecipient address receiving minted tokens on destination domain as bytes32
* @param destinationDomain destination domain
* @param destinationTokenMessenger address of TokenMessenger on destination domain as bytes32
* @param destinationCaller authorized caller as bytes32 of receiveMessage() on destination domain.
* If equal to bytes32(0), any address can broadcast the message.
* @param maxFee maximum fee to pay on destination domain, in units of burnToken
* @param minFinalityThreshold the minimum finality at which the message should be attested to.
* @param hookData optional hook for execution on destination domain
*/
event DepositForBurn(
address indexed burnToken,
uint256 amount,
address indexed depositor,
bytes32 mintRecipient,
uint32 destinationDomain,
bytes32 destinationTokenMessenger,
bytes32 destinationCaller,
uint256 maxFee,
uint32 indexed minFinalityThreshold,
bytes hookData
);
// ============ State Variables ============
// Local Message Transmitter responsible for sending and receiving messages to/from remote domains
function localMessageTransmitter() external view returns (address);
// Version of message body format
function messageBodyVersion() external view returns (uint32);
// Minter responsible for minting and burning tokens on the local domain
function localMinter() external view returns (address);
// Valid TokenMessengers on remote domains
function remoteTokenMessengers(uint32 domain) external view returns (bytes32);
// ============ External Functions ============
/**
* @notice Deposits and burns tokens from sender to be minted on destination domain.
* Emits a `DepositForBurn` event.
* @dev reverts if:
* - given burnToken is not supported
* - given destinationDomain has no TokenMessenger registered
* - transferFrom() reverts. For example, if sender's burnToken balance or approved allowance
* to this contract is less than `amount`.
* - burn() reverts. For example, if `amount` is 0.
* - maxFee is greater than or equal to `amount`.
* - MessageTransmitterV2#sendMessage reverts.
* @param amount amount of tokens to burn
* @param destinationDomain destination domain to receive message on
* @param mintRecipient address of mint recipient on destination domain
* @param burnToken token to burn `amount` of, on local domain
* @param destinationCaller authorized caller on the destination domain, as bytes32. If equal to bytes32(0),
* any address can broadcast the message.
* @param maxFee maximum fee to pay on the destination domain, specified in units of burnToken
* @param minFinalityThreshold the minimum finality at which a burn message will be attested to.
*/
function depositForBurn(
uint256 amount,
uint32 destinationDomain,
bytes32 mintRecipient,
address burnToken,
bytes32 destinationCaller,
uint256 maxFee,
uint32 minFinalityThreshold
) external;
/**
* @notice Deposits and burns tokens from sender to be minted on destination domain.
* Emits a `DepositForBurn` event.
* @dev reverts if:
* - `hookData` is zero-length
* - `burnToken` is not supported
* - `destinationDomain` has no TokenMessenger registered
* - transferFrom() reverts. For example, if sender's burnToken balance or approved allowance
* to this contract is less than `amount`.
* - burn() reverts. For example, if `amount` is 0.
* - maxFee is greater than or equal to `amount`.
* - MessageTransmitterV2#sendMessage reverts.
* @param amount amount of tokens to burn
* @param destinationDomain destination domain to receive message on
* @param mintRecipient address of mint recipient on destination domain, as bytes32
* @param burnToken token to burn `amount` of, on local domain
* @param destinationCaller authorized caller on the destination domain, as bytes32. If equal to bytes32(0),
* any address can broadcast the message.
* @param maxFee maximum fee to pay on the destination domain, specified in units of burnToken
* @param hookData hook data to append to burn message for interpretation on destination domain
*/
function depositForBurnWithHook(
uint256 amount,
uint32 destinationDomain,
bytes32 mintRecipient,
address burnToken,
bytes32 destinationCaller,
uint256 maxFee,
uint32 minFinalityThreshold,
bytes calldata hookData
) external;
}/*
* Copyright (c) 2022, Circle Internet Financial Limited.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
pragma solidity ^0.8.0;
/**
* @title Message Library
* @notice Library for formatted messages used by Relayer and Receiver.
*
* @dev The message body is dynamically-sized to support custom message body
* formats. Other fields must be fixed-size to avoid hash collisions.
* Each other input value has an explicit type to guarantee fixed-size.
* Padding: uintNN fields are left-padded, and bytesNN fields are right-padded.
*
* Field Bytes Type Index
* version 4 uint32 0
* sourceDomain 4 uint32 4
* destinationDomain 4 uint32 8
* nonce 8 uint64 12
* sender 32 bytes32 20
* recipient 32 bytes32 52
* destinationCaller 32 bytes32 84
* messageBody dynamic bytes 116
*
**/
library Message {
/**
* @notice Returns formatted (packed) message with provided fields
* @param _msgVersion the version of the message format
* @param _msgSourceDomain Domain of home chain
* @param _msgDestinationDomain Domain of destination chain
* @param _msgNonce Destination-specific nonce
* @param _msgSender Address of sender on source chain as bytes32
* @param _msgRecipient Address of recipient on destination chain as bytes32
* @param _msgDestinationCaller Address of caller on destination chain as bytes32
* @param _msgRawBody Raw bytes of message body
* @return Formatted message
**/
function _formatMessage(
uint32 _msgVersion,
uint32 _msgSourceDomain,
uint32 _msgDestinationDomain,
uint64 _msgNonce,
bytes32 _msgSender,
bytes32 _msgRecipient,
bytes32 _msgDestinationCaller,
bytes memory _msgRawBody
) internal pure returns (bytes memory) {
return
abi.encodePacked(
_msgVersion,
_msgSourceDomain,
_msgDestinationDomain,
_msgNonce,
_msgSender,
_msgRecipient,
_msgDestinationCaller,
_msgRawBody
);
}
/**
* @notice converts address to bytes32 (alignment preserving cast.)
* @param addr the address to convert to bytes32
*/
function addressToBytes32(address addr) external pure returns (bytes32) {
return bytes32(uint256(uint160(addr)));
}
/**
* @notice converts bytes32 to address (alignment preserving cast.)
* @dev Warning: it is possible to have different input values _buf map to the same address.
* For use cases where this is not acceptable, validate that the first 12 bytes of _buf are zero-padding.
* @param _buf the bytes32 to convert to address
*/
function bytes32ToAddress(bytes32 _buf) public pure returns (address) {
return address(uint160(uint256(_buf)));
}
}/*
* Copyright 2024 Circle Internet Group, Inc. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
pragma solidity >=0.8.12;
import {TypedMemView} from "./TypedMemView.sol";
/**
* @title MessageV2 Library
* @notice Library for formatted v2 messages used by Relayer and Receiver.
*
* @dev The message body is dynamically-sized to support custom message body
* formats. Other fields must be fixed-size to avoid hash collisions.
* Each other input value has an explicit type to guarantee fixed-size.
* Padding: uintNN fields are left-padded, and bytesNN fields are right-padded.
*
* Field Bytes Type Index
* version 4 uint32 0
* sourceDomain 4 uint32 4
* destinationDomain 4 uint32 8
* nonce 32 bytes32 12
* sender 32 bytes32 44
* recipient 32 bytes32 76
* destinationCaller 32 bytes32 108
* minFinalityThreshold 4 uint32 140
* finalityThresholdExecuted 4 uint32 144
* messageBody dynamic bytes 148
* @dev Differences from v1:
* - Nonce is now bytes32 (vs. uint64)
* - minFinalityThreshold added
* - finalityThresholdExecuted added
**/
library MessageV2 {
using TypedMemView for bytes;
using TypedMemView for bytes29;
// Indices of each field in message
uint8 private constant VERSION_INDEX = 0;
uint8 private constant SOURCE_DOMAIN_INDEX = 4;
uint8 private constant DESTINATION_DOMAIN_INDEX = 8;
uint8 private constant NONCE_INDEX = 12;
uint8 private constant SENDER_INDEX = 44;
uint8 private constant RECIPIENT_INDEX = 76;
uint8 private constant DESTINATION_CALLER_INDEX = 108;
uint8 private constant MIN_FINALITY_THRESHOLD_INDEX = 140;
uint8 private constant FINALITY_THRESHOLD_EXECUTED_INDEX = 144;
uint8 private constant MESSAGE_BODY_INDEX = 148;
bytes32 private constant EMPTY_NONCE = bytes32(0);
uint32 private constant EMPTY_FINALITY_THRESHOLD_EXECUTED = 0;
/**
* @notice Returns formatted (packed) message with provided fields
* @param _version the version of the message format
* @param _sourceDomain Domain of home chain
* @param _destinationDomain Domain of destination chain
* @param _sender Address of sender on source chain as bytes32
* @param _recipient Address of recipient on destination chain as bytes32
* @param _destinationCaller Address of caller on destination chain as bytes32
* @param _minFinalityThreshold the minimum finality at which the message should be attested to
* @param _messageBody Raw bytes of message body
* @return Formatted message
**/
function _formatMessageForRelay(
uint32 _version,
uint32 _sourceDomain,
uint32 _destinationDomain,
bytes32 _sender,
bytes32 _recipient,
bytes32 _destinationCaller,
uint32 _minFinalityThreshold,
bytes calldata _messageBody
) internal pure returns (bytes memory) {
return
abi.encodePacked(
_version,
_sourceDomain,
_destinationDomain,
EMPTY_NONCE,
_sender,
_recipient,
_destinationCaller,
_minFinalityThreshold,
EMPTY_FINALITY_THRESHOLD_EXECUTED,
_messageBody
);
}
// @notice Returns _message's version field
function _getVersion(bytes29 _message) internal pure returns (uint32) {
return uint32(_message.indexUint(VERSION_INDEX, 4));
}
// @notice Returns _message's sourceDomain field
function _getSourceDomain(bytes29 _message) internal pure returns (uint32) {
return uint32(_message.indexUint(SOURCE_DOMAIN_INDEX, 4));
}
// @notice Returns _message's destinationDomain field
function _getDestinationDomain(
bytes29 _message
) internal pure returns (uint32) {
return uint32(_message.indexUint(DESTINATION_DOMAIN_INDEX, 4));
}
// @notice Returns _message's nonce field
function _getNonce(bytes29 _message) internal pure returns (bytes32) {
return _message.index(NONCE_INDEX, 32);
}
// @notice Returns _message's sender field
function _getSender(bytes29 _message) internal pure returns (bytes32) {
return _message.index(SENDER_INDEX, 32);
}
// @notice Returns _message's recipient field
function _getRecipient(bytes29 _message) internal pure returns (bytes32) {
return _message.index(RECIPIENT_INDEX, 32);
}
// @notice Returns _message's destinationCaller field
function _getDestinationCaller(
bytes29 _message
) internal pure returns (bytes32) {
return _message.index(DESTINATION_CALLER_INDEX, 32);
}
// @notice Returns _message's minFinalityThreshold field
function _getMinFinalityThreshold(
bytes29 _message
) internal pure returns (uint32) {
return uint32(_message.indexUint(MIN_FINALITY_THRESHOLD_INDEX, 4));
}
// @notice Returns _message's finalityThresholdExecuted field
function _getFinalityThresholdExecuted(
bytes29 _message
) internal pure returns (uint32) {
return uint32(_message.indexUint(FINALITY_THRESHOLD_EXECUTED_INDEX, 4));
}
// @notice Returns _message's messageBody field
function _getMessageBody(bytes29 _message) internal pure returns (bytes29) {
return
_message.slice(
MESSAGE_BODY_INDEX,
_message.len() - MESSAGE_BODY_INDEX,
0
);
}
/**
* @notice Reverts if message is malformed or too short
* @param _message The message as bytes29
*/
function _validateMessageFormat(bytes29 _message) internal pure {
require(_message.isValid(), "Malformed message");
require(
_message.len() >= MESSAGE_BODY_INDEX,
"Invalid message: too short"
);
}
}// SPDX-License-Identifier: MIT OR Apache-2.0
pragma solidity >=0.8.12;
library TypedMemView {
// Why does this exist?
// the solidity `bytes memory` type has a few weaknesses.
// 1. You can't index ranges effectively
// 2. You can't slice without copying
// 3. The underlying data may represent any type
// 4. Solidity never deallocates memory, and memory costs grow
// superlinearly
// By using a memory view instead of a `bytes memory` we get the following
// advantages:
// 1. Slices are done on the stack, by manipulating the pointer
// 2. We can index arbitrary ranges and quickly convert them to stack types
// 3. We can insert type info into the pointer, and typecheck at runtime
// This makes `TypedMemView` a useful tool for efficient zero-copy
// algorithms.
// Why bytes29?
// We want to avoid confusion between views, digests, and other common
// types so we chose a large and uncommonly used odd number of bytes
//
// Note that while bytes are left-aligned in a word, integers and addresses
// are right-aligned. This means when working in assembly we have to
// account for the 3 unused bytes on the righthand side
//
// First 5 bytes are a type flag.
// - ff_ffff_fffe is reserved for unknown type.
// - ff_ffff_ffff is reserved for invalid types/errors.
// next 12 are memory address
// next 12 are len
// bottom 3 bytes are empty
// Assumptions:
// - non-modification of memory.
// - No Solidity updates
// - - wrt free mem point
// - - wrt bytes representation in memory
// - - wrt memory addressing in general
// Usage:
// - create type constants
// - use `assertType` for runtime type assertions
// - - unfortunately we can't do this at compile time yet :(
// - recommended: implement modifiers that perform type checking
// - - e.g.
// - - `uint40 constant MY_TYPE = 3;`
// - - ` modifer onlyMyType(bytes29 myView) { myView.assertType(MY_TYPE); }`
// - instantiate a typed view from a bytearray using `ref`
// - use `index` to inspect the contents of the view
// - use `slice` to create smaller views into the same memory
// - - `slice` can increase the offset
// - - `slice can decrease the length`
// - - must specify the output type of `slice`
// - - `slice` will return a null view if you try to overrun
// - - make sure to explicitly check for this with `notNull` or `assertType`
// - use `equal` for typed comparisons.
// The null view
bytes29 public constant NULL = hex"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffff";
// Mask a low uint96
uint256 constant LOW_12_MASK = 0xffffffffffffffffffffffff;
// Shift constants
uint8 constant SHIFT_TO_LEN = 24;
uint8 constant SHIFT_TO_LOC = 96 + 24;
uint8 constant SHIFT_TO_TYPE = 96 + 96 + 24;
// For nibble encoding
bytes private constant NIBBLE_LOOKUP = "0123456789abcdef";
/**
* @notice Returns the encoded hex character that represents the lower 4 bits of the argument.
* @param _byte The byte
* @return _char The encoded hex character
*/
function nibbleHex(uint8 _byte) internal pure returns (uint8 _char) {
uint8 _nibble = _byte & 0x0f; // keep bottom 4, 0 top 4
_char = uint8(NIBBLE_LOOKUP[_nibble]);
}
/**
* @notice Returns a uint16 containing the hex-encoded byte.
* @param _b The byte
* @return encoded - The hex-encoded byte
*/
function byteHex(uint8 _b) internal pure returns (uint16 encoded) {
encoded |= nibbleHex(_b >> 4); // top 4 bits
encoded <<= 8;
encoded |= nibbleHex(_b); // lower 4 bits
}
/**
* @notice Encodes the uint256 to hex. `first` contains the encoded top 16 bytes.
* `second` contains the encoded lower 16 bytes.
*
* @param _b The 32 bytes as uint256
* @return first - The top 16 bytes
* @return second - The bottom 16 bytes
*/
function encodeHex(uint256 _b) internal pure returns (uint256 first, uint256 second) {
for (uint8 i = 31; i > 15;) {
uint8 _byte = uint8(_b >> (i * 8));
first |= byteHex(_byte);
if (i != 16) {
first <<= 16;
}
unchecked {
i -= 1;
}
}
// abusing underflow here =_=
for (uint8 i = 15; i < 255;) {
uint8 _byte = uint8(_b >> (i * 8));
second |= byteHex(_byte);
if (i != 0) {
second <<= 16;
}
unchecked {
i -= 1;
}
}
}
/**
* @notice Changes the endianness of a uint256.
* @dev https://graphics.stanford.edu/~seander/bithacks.html#ReverseParallel
* @param _b The unsigned integer to reverse
* @return v - The reversed value
*/
function reverseUint256(uint256 _b) internal pure returns (uint256 v) {
v = _b;
// swap bytes
v = ((v >> 8) & 0x00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF)
| ((v & 0x00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF00FF) << 8);
// swap 2-byte long pairs
v = ((v >> 16) & 0x0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF)
| ((v & 0x0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF0000FFFF) << 16);
// swap 4-byte long pairs
v = ((v >> 32) & 0x00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF)
| ((v & 0x00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF00000000FFFFFFFF) << 32);
// swap 8-byte long pairs
v = ((v >> 64) & 0x0000000000000000FFFFFFFFFFFFFFFF0000000000000000FFFFFFFFFFFFFFFF)
| ((v & 0x0000000000000000FFFFFFFFFFFFFFFF0000000000000000FFFFFFFFFFFFFFFF) << 64);
// swap 16-byte long pairs
v = (v >> 128) | (v << 128);
}
/**
* @notice Create a mask with the highest `_len` bits set.
* @param _len The length
* @return mask - The mask
*/
function leftMask(uint8 _len) private pure returns (uint256 mask) {
// ugly. redo without assembly?
assembly {
// solhint-disable-previous-line no-inline-assembly
mask := sar(sub(_len, 1), 0x8000000000000000000000000000000000000000000000000000000000000000)
}
}
/**
* @notice Return the null view.
* @return bytes29 - The null view
*/
function nullView() internal pure returns (bytes29) {
return NULL;
}
/**
* @notice Check if the view is null.
* @return bool - True if the view is null
*/
function isNull(bytes29 memView) internal pure returns (bool) {
return memView == NULL;
}
/**
* @notice Check if the view is not null.
* @return bool - True if the view is not null
*/
function notNull(bytes29 memView) internal pure returns (bool) {
return !isNull(memView);
}
/**
* @notice Check if the view is of a valid type and points to a valid location
* in memory.
* @dev We perform this check by examining solidity's unallocated memory
* pointer and ensuring that the view's upper bound is less than that.
* @param memView The view
* @return ret - True if the view is valid
*/
function isValid(bytes29 memView) internal pure returns (bool ret) {
if (typeOf(memView) == 0xffffffffff) return false;
uint256 _end = end(memView);
assembly {
// solhint-disable-previous-line no-inline-assembly
ret := iszero(gt(_end, mload(0x40)))
}
}
/**
* @notice Require that a typed memory view be valid.
* @dev Returns the view for easy chaining.
* @param memView The view
* @return bytes29 - The validated view
*/
function assertValid(bytes29 memView) internal pure returns (bytes29) {
require(isValid(memView), "Validity assertion failed");
return memView;
}
/**
* @notice Return true if the memview is of the expected type. Otherwise false.
* @param memView The view
* @param _expected The expected type
* @return bool - True if the memview is of the expected type
*/
function isType(bytes29 memView, uint40 _expected) internal pure returns (bool) {
return typeOf(memView) == _expected;
}
/**
* @notice Require that a typed memory view has a specific type.
* @dev Returns the view for easy chaining.
* @param memView The view
* @param _expected The expected type
* @return bytes29 - The view with validated type
*/
function assertType(bytes29 memView, uint40 _expected) internal pure returns (bytes29) {
if (!isType(memView, _expected)) {
(, uint256 g) = encodeHex(uint256(typeOf(memView)));
(, uint256 e) = encodeHex(uint256(_expected));
string memory err =
string(abi.encodePacked("Type assertion failed. Got 0x", uint80(g), ". Expected 0x", uint80(e)));
revert(err);
}
return memView;
}
/**
* @notice Return an identical view with a different type.
* @param memView The view
* @param _newType The new type
* @return newView - The new view with the specified type
*/
function castTo(bytes29 memView, uint40 _newType) internal pure returns (bytes29 newView) {
// then | in the new type
uint256 _typeShift = SHIFT_TO_TYPE;
uint256 _typeBits = 40;
assembly {
// solhint-disable-previous-line no-inline-assembly
// shift off the top 5 bytes
newView := or(newView, shr(_typeBits, shl(_typeBits, memView)))
newView := or(newView, shl(_typeShift, _newType))
}
}
/**
* @notice Unsafe raw pointer construction. This should generally not be called
* directly. Prefer `ref` wherever possible.
* @dev Unsafe raw pointer construction. This should generally not be called
* directly. Prefer `ref` wherever possible.
* @param _type The type
* @param _loc The memory address
* @param _len The length
* @return newView - The new view with the specified type, location and length
*/
function unsafeBuildUnchecked(uint256 _type, uint256 _loc, uint256 _len) private pure returns (bytes29 newView) {
uint256 _uint96Bits = 96;
uint256 _emptyBits = 24;
assembly {
// solium-disable-previous-line security/no-inline-assembly
newView := shl(_uint96Bits, or(newView, _type)) // insert type
newView := shl(_uint96Bits, or(newView, _loc)) // insert loc
newView := shl(_emptyBits, or(newView, _len)) // empty bottom 3 bytes
}
}
/**
* @notice Instantiate a new memory view. This should generally not be called
* directly. Prefer `ref` wherever possible.
* @dev Instantiate a new memory view. This should generally not be called
* directly. Prefer `ref` wherever possible.
* @param _type The type
* @param _loc The memory address
* @param _len The length
* @return newView - The new view with the specified type, location and length
*/
function build(uint256 _type, uint256 _loc, uint256 _len) internal pure returns (bytes29 newView) {
uint256 _end = _loc + _len;
assembly {
// solhint-disable-previous-line no-inline-assembly
if gt(_end, mload(0x40)) { _end := 0 }
}
if (_end == 0) {
return NULL;
}
newView = unsafeBuildUnchecked(_type, _loc, _len);
}
/**
* @notice Instantiate a memory view from a byte array.
* @dev Note that due to Solidity memory representation, it is not possible to
* implement a deref, as the `bytes` type stores its len in memory.
* @param arr The byte array
* @param newType The type
* @return bytes29 - The memory view
*/
function ref(bytes memory arr, uint40 newType) internal pure returns (bytes29) {
uint256 _len = arr.length;
uint256 _loc;
assembly {
// solhint-disable-previous-line no-inline-assembly
_loc := add(arr, 0x20) // our view is of the data, not the struct
}
return build(newType, _loc, _len);
}
/**
* @notice Return the associated type information.
* @param memView The memory view
* @return _type - The type associated with the view
*/
function typeOf(bytes29 memView) internal pure returns (uint40 _type) {
uint256 _shift = SHIFT_TO_TYPE;
assembly {
// solium-disable-previous-line security/no-inline-assembly
_type := shr(_shift, memView) // shift out lower 27 bytes
}
}
/**
* @notice Optimized type comparison. Checks that the 5-byte type flag is equal.
* @param left The first view
* @param right The second view
* @return bool - True if the 5-byte type flag is equal
*/
function sameType(bytes29 left, bytes29 right) internal pure returns (bool) {
return (left ^ right) >> SHIFT_TO_TYPE == 0;
}
/**
* @notice Return the memory address of the underlying bytes.
* @param memView The view
* @return _loc - The memory address
*/
function loc(bytes29 memView) internal pure returns (uint96 _loc) {
uint256 _mask = LOW_12_MASK; // assembly can't use globals
uint256 _shift = SHIFT_TO_LOC;
assembly {
// solium-disable-previous-line security/no-inline-assembly
_loc := and(shr(_shift, memView), _mask)
}
}
/**
* @notice The number of memory words this memory view occupies, rounded up.
* @param memView The view
* @return uint256 - The number of memory words
*/
function words(bytes29 memView) internal pure returns (uint256) {
return (uint256(len(memView)) + 31) / 32;
}
/**
* @notice The in-memory footprint of a fresh copy of the view.
* @param memView The view
* @return uint256 - The in-memory footprint of a fresh copy of the view.
*/
function footprint(bytes29 memView) internal pure returns (uint256) {
return words(memView) * 32;
}
/**
* @notice The number of bytes of the view.
* @param memView The view
* @return _len - The length of the view
*/
function len(bytes29 memView) internal pure returns (uint96 _len) {
uint256 _mask = LOW_12_MASK; // assembly can't use globals
uint256 _emptyBits = 24;
assembly {
// solium-disable-previous-line security/no-inline-assembly
_len := and(shr(_emptyBits, memView), _mask)
}
}
/**
* @notice Returns the endpoint of `memView`.
* @param memView The view
* @return uint256 - The endpoint of `memView`
*/
function end(bytes29 memView) internal pure returns (uint256) {
unchecked {
return loc(memView) + len(memView);
}
}
/**
* @notice Safe slicing without memory modification.
* @param memView The view
* @param _index The start index
* @param _len The length
* @param newType The new type
* @return bytes29 - The new view
*/
function slice(bytes29 memView, uint256 _index, uint256 _len, uint40 newType) internal pure returns (bytes29) {
uint256 _loc = loc(memView);
// Ensure it doesn't overrun the view
if (_loc + _index + _len > end(memView)) {
return NULL;
}
_loc = _loc + _index;
return build(newType, _loc, _len);
}
/**
* @notice Shortcut to `slice`. Gets a view representing the first `_len` bytes.
* @param memView The view
* @param _len The length
* @param newType The new type
* @return bytes29 - The new view
*/
function prefix(bytes29 memView, uint256 _len, uint40 newType) internal pure returns (bytes29) {
return slice(memView, 0, _len, newType);
}
/**
* @notice Shortcut to `slice`. Gets a view representing the last `_len` byte.
* @param memView The view
* @param _len The length
* @param newType The new type
* @return bytes29 - The new view
*/
function postfix(bytes29 memView, uint256 _len, uint40 newType) internal pure returns (bytes29) {
return slice(memView, uint256(len(memView)) - _len, _len, newType);
}
/**
* @notice Construct an error message for an indexing overrun.
* @param _loc The memory address
* @param _len The length
* @param _index The index
* @param _slice The slice where the overrun occurred
* @return err - The err
*/
function indexErrOverrun(uint256 _loc, uint256 _len, uint256 _index, uint256 _slice)
internal
pure
returns (string memory err)
{
(, uint256 a) = encodeHex(_loc);
(, uint256 b) = encodeHex(_len);
(, uint256 c) = encodeHex(_index);
(, uint256 d) = encodeHex(_slice);
err = string(
abi.encodePacked(
"TypedMemView/index - Overran the view. Slice is at 0x",
uint48(a),
" with length 0x",
uint48(b),
". Attempted to index at offset 0x",
uint48(c),
" with length 0x",
uint48(d),
"."
)
);
}
/**
* @notice Load up to 32 bytes from the view onto the stack.
* @dev Returns a bytes32 with only the `_bytes` highest bytes set.
* This can be immediately cast to a smaller fixed-length byte array.
* To automatically cast to an integer, use `indexUint`.
* @param memView The view
* @param _index The index
* @param _bytes The bytes
* @return result - The 32 byte result
*/
function index(bytes29 memView, uint256 _index, uint8 _bytes) internal pure returns (bytes32 result) {
if (_bytes == 0) return bytes32(0);
if (_index + _bytes > len(memView)) {
revert(indexErrOverrun(loc(memView), len(memView), _index, uint256(_bytes)));
}
require(_bytes <= 32, "TypedMemView/index - Attempted to index more than 32 bytes");
uint8 bitLength;
unchecked {
bitLength = _bytes * 8;
}
uint256 _loc = loc(memView);
uint256 _mask = leftMask(bitLength);
assembly {
// solhint-disable-previous-line no-inline-assembly
result := and(mload(add(_loc, _index)), _mask)
}
}
/**
* @notice Parse an unsigned integer from the view at `_index`.
* @dev Requires that the view have >= `_bytes` bytes following that index.
* @param memView The view
* @param _index The index
* @param _bytes The bytes
* @return result - The unsigned integer
*/
function indexUint(bytes29 memView, uint256 _index, uint8 _bytes) internal pure returns (uint256 result) {
return uint256(index(memView, _index, _bytes)) >> ((32 - _bytes) * 8);
}
/**
* @notice Parse an unsigned integer from LE bytes.
* @param memView The view
* @param _index The index
* @param _bytes The bytes
* @return result - The unsigned integer
*/
function indexLEUint(bytes29 memView, uint256 _index, uint8 _bytes) internal pure returns (uint256 result) {
return reverseUint256(uint256(index(memView, _index, _bytes)));
}
/**
* @notice Parse an address from the view at `_index`. Requires that the view have >= 20 bytes
* following that index.
* @param memView The view
* @param _index The index
* @return address - The address
*/
function indexAddress(bytes29 memView, uint256 _index) internal pure returns (address) {
return address(uint160(indexUint(memView, _index, 20)));
}
/**
* @notice Return the keccak256 hash of the underlying memory
* @param memView The view
* @return digest - The keccak256 hash of the underlying memory
*/
function keccak(bytes29 memView) internal pure returns (bytes32 digest) {
uint256 _loc = loc(memView);
uint256 _len = len(memView);
assembly {
// solhint-disable-previous-line no-inline-assembly
digest := keccak256(_loc, _len)
}
}
/**
* @notice Return the sha2 digest of the underlying memory.
* @dev We explicitly deallocate memory afterwards.
* @param memView The view
* @return digest - The sha2 hash of the underlying memory
*/
function sha2(bytes29 memView) internal view returns (bytes32 digest) {
uint256 _loc = loc(memView);
uint256 _len = len(memView);
bool res;
assembly {
// solhint-disable-previous-line no-inline-assembly
let ptr := mload(0x40)
res := staticcall(gas(), 2, _loc, _len, ptr, 0x20) // sha2 #1
digest := mload(ptr)
}
require(res, "sha2 OOG");
}
/**
* @notice Implements bitcoin's hash160 (rmd160(sha2()))
* @param memView The pre-image
* @return digest - the Digest
*/
function hash160(bytes29 memView) internal view returns (bytes20 digest) {
uint256 _loc = loc(memView);
uint256 _len = len(memView);
bool res;
assembly {
// solhint-disable-previous-line no-inline-assembly
let ptr := mload(0x40)
res := staticcall(gas(), 2, _loc, _len, ptr, 0x20) // sha2
res := and(res, staticcall(gas(), 3, ptr, 0x20, ptr, 0x20)) // rmd160
digest := mload(add(ptr, 0xc)) // return value is 0-prefixed.
}
require(res, "hash160 OOG");
}
/**
* @notice Implements bitcoin's hash256 (double sha2)
* @param memView A view of the preimage
* @return digest - the Digest
*/
function hash256(bytes29 memView) internal view returns (bytes32 digest) {
uint256 _loc = loc(memView);
uint256 _len = len(memView);
bool res;
assembly {
// solhint-disable-previous-line no-inline-assembly
let ptr := mload(0x40)
res := staticcall(gas(), 2, _loc, _len, ptr, 0x20) // sha2 #1
res := and(res, staticcall(gas(), 2, ptr, 0x20, ptr, 0x20)) // sha2 #2
digest := mload(ptr)
}
require(res, "hash256 OOG");
}
/**
* @notice Return true if the underlying memory is equal. Else false.
* @param left The first view
* @param right The second view
* @return bool - True if the underlying memory is equal
*/
function untypedEqual(bytes29 left, bytes29 right) internal pure returns (bool) {
return (loc(left) == loc(right) && len(left) == len(right)) || keccak(left) == keccak(right);
}
/**
* @notice Return false if the underlying memory is equal. Else true.
* @param left The first view
* @param right The second view
* @return bool - False if the underlying memory is equal
*/
function untypedNotEqual(bytes29 left, bytes29 right) internal pure returns (bool) {
return !untypedEqual(left, right);
}
/**
* @notice Compares type equality.
* @dev Shortcuts if the pointers are identical, otherwise compares type and digest.
* @param left The first view
* @param right The second view
* @return bool - True if the types are the same
*/
function equal(bytes29 left, bytes29 right) internal pure returns (bool) {
return left == right || (typeOf(left) == typeOf(right) && keccak(left) == keccak(right));
}
/**
* @notice Compares type inequality.
* @dev Shortcuts if the pointers are identical, otherwise compares type and digest.
* @param left The first view
* @param right The second view
* @return bool - True if the types are not the same
*/
function notEqual(bytes29 left, bytes29 right) internal pure returns (bool) {
return !equal(left, right);
}
/**
* @notice Copy the view to a location, return an unsafe memory reference
* @dev Super Dangerous direct memory access.
*
* This reference can be overwritten if anything else modifies memory (!!!).
* As such it MUST be consumed IMMEDIATELY.
* This function is private to prevent unsafe usage by callers.
* @param memView The view
* @param _newLoc The new location
* @return written - the unsafe memory reference
*/
function unsafeCopyTo(bytes29 memView, uint256 _newLoc) private view returns (bytes29 written) {
require(notNull(memView), "TypedMemView/copyTo - Null pointer deref");
require(isValid(memView), "TypedMemView/copyTo - Invalid pointer deref");
uint256 _len = len(memView);
uint256 _oldLoc = loc(memView);
uint256 ptr;
bool res;
assembly {
// solhint-disable-previous-line no-inline-assembly
ptr := mload(0x40)
// revert if we're writing in occupied memory
if gt(ptr, _newLoc) { revert(0x60, 0x20) } // empty revert message
// use the identity precompile to copy
res := staticcall(gas(), 4, _oldLoc, _len, _newLoc, _len)
}
require(res, "identity OOG");
written = unsafeBuildUnchecked(typeOf(memView), _newLoc, _len);
}
/**
* @notice Copies the referenced memory to a new loc in memory, returning a `bytes` pointing to
* the new memory
* @dev Shortcuts if the pointers are identical, otherwise compares type and digest.
* @param memView The view
* @return ret - The view pointing to the new memory
*/
function clone(bytes29 memView) internal view returns (bytes memory ret) {
uint256 ptr;
uint256 _len = len(memView);
assembly {
// solhint-disable-previous-line no-inline-assembly
ptr := mload(0x40) // load unused memory pointer
ret := ptr
}
unchecked {
unsafeCopyTo(memView, ptr + 0x20);
}
assembly {
// solhint-disable-previous-line no-inline-assembly
mstore(0x40, add(add(ptr, _len), 0x20)) // write new unused pointer
mstore(ptr, _len) // write len of new array (in bytes)
}
}
/**
* @notice Join the views in memory, return an unsafe reference to the memory.
* @dev Super Dangerous direct memory access.
*
* This reference can be overwritten if anything else modifies memory (!!!).
* As such it MUST be consumed IMMEDIATELY.
* This function is private to prevent unsafe usage by callers.
* @param memViews The views
* @param _location The location in memory to which to copy & concatenate
* @return unsafeView - The conjoined view pointing to the new memory
*/
function unsafeJoin(bytes29[] memory memViews, uint256 _location) private view returns (bytes29 unsafeView) {
assembly {
// solhint-disable-previous-line no-inline-assembly
let ptr := mload(0x40)
// revert if we're writing in occupied memory
if gt(ptr, _location) { revert(0x60, 0x20) } // empty revert message
}
uint256 _offset = 0;
for (uint256 i = 0; i < memViews.length; i++) {
bytes29 memView = memViews[i];
unchecked {
unsafeCopyTo(memView, _location + _offset);
_offset += len(memView);
}
}
unsafeView = unsafeBuildUnchecked(0, _location, _offset);
}
/**
* @notice Produce the keccak256 digest of the concatenated contents of multiple views.
* @param memViews The views
* @return bytes32 - The keccak256 digest
*/
function joinKeccak(bytes29[] memory memViews) internal view returns (bytes32) {
uint256 ptr;
assembly {
// solhint-disable-previous-line no-inline-assembly
ptr := mload(0x40) // load unused memory pointer
}
return keccak(unsafeJoin(memViews, ptr));
}
/**
* @notice Produce the sha256 digest of the concatenated contents of multiple views.
* @param memViews The views
* @return bytes32 - The sha256 digest
*/
function joinSha2(bytes29[] memory memViews) internal view returns (bytes32) {
uint256 ptr;
assembly {
// solhint-disable-previous-line no-inline-assembly
ptr := mload(0x40) // load unused memory pointer
}
return sha2(unsafeJoin(memViews, ptr));
}
/**
* @notice copies all views, joins them into a new bytearray.
* @param memViews The views
* @return ret - The new byte array
*/
function join(bytes29[] memory memViews) internal view returns (bytes memory ret) {
uint256 ptr;
assembly {
// solhint-disable-previous-line no-inline-assembly
ptr := mload(0x40) // load unused memory pointer
}
bytes29 _newView;
unchecked {
_newView = unsafeJoin(memViews, ptr + 0x20);
}
uint256 _written = len(_newView);
uint256 _footprint = footprint(_newView);
assembly {
// solhint-disable-previous-line no-inline-assembly
// store the legnth
mstore(ptr, _written)
// new pointer is old + 0x20 + the footprint of the body
mstore(0x40, add(add(ptr, _footprint), 0x20))
ret := ptr
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
/**
* @title IPlugin
* @notice Transfer tokens from source to destination chain
*/
interface IPlugin {
/**
* @notice Transfer tokens from source to destination chain
* @param pluginParams Plugin parameters as raw bytes
* @param extraData Parameters which should be passed to receiver on destination chain
* @param amount Amount of tokens to transfer
*/
function submit(bytes calldata pluginParams, bytes calldata extraData, uint256 amount) external payable;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
/**
* @title IReceiver
* @notice Handles message from another chain
*/
interface IReceiver {
/**
* @notice Receives tokens from source chain and complete swap.
* @param params Swap params as raw bytes
* @param amount Amount of tokens transferred
*/
function onReceive(bytes calldata params, uint256 amount) external;
}// SPDX-License-Identifier: MIT pragma solidity ^0.8.24; import '@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol'; import '@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol';
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import { SafeERC20 } from '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';
import { IERC20 } from '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import { IPermit2 } from '../lib/permit2/src/interfaces/IPermit2.sol';
import { IUniversalRouter } from '../lib/universal-router/contracts/interfaces/IUniversalRouter.sol';
error ErrorSwapFailed();
abstract contract UniswapWrapper {
using SafeERC20 for IERC20;
IUniversalRouter public immutable universalRouter;
IPermit2 public immutable permit2;
struct SwapParams {
address tokenIn;
address tokenOut;
uint256 deadline;
bytes commands;
bytes[] inputs;
}
constructor(address _universalRouter, address _permit2) {
universalRouter = IUniversalRouter(_universalRouter);
permit2 = IPermit2(_permit2);
}
/**
* @notice Performs a Uniswap swap via the Universal Router.
* @param params Struct containing swap details (tokens, commands, deadlines, etc.).
* @param amountIn Amount of `tokenIn` to swap.
* @return amountOut Amount of `tokenOut` received after the swap.
*/
function _swap(SwapParams memory params, uint256 amountIn) internal returns (uint256 amountOut) {
if (params.tokenIn == params.tokenOut) return amountIn;
// Approve the Universal Router to spend tokens
uint256 preBalance;
if (params.tokenOut == address(0)) {
preBalance = address(this).balance;
} else {
preBalance = IERC20(params.tokenOut).balanceOf(address(this));
}
uint256 value;
if (params.tokenIn == address(0)) {
value = amountIn;
} else {
value = 0;
IERC20(params.tokenIn).safeIncreaseAllowance(address(permit2), amountIn);
permit2.approve(params.tokenIn, address(universalRouter), uint160(amountIn), uint48(params.deadline));
}
// Execute the swap through Universal Router
universalRouter.execute{ value: value }(params.commands, params.inputs, params.deadline);
uint256 afterBalance;
if (params.tokenOut == address(0)) {
afterBalance = address(this).balance;
} else {
afterBalance = IERC20(params.tokenOut).balanceOf(address(this));
}
if (afterBalance < preBalance) {
revert ErrorSwapFailed();
} else {
amountOut = afterBalance - preBalance;
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import { IERC20 } from '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import { SafeERC20 } from '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';
import { ITokenMessenger } from './interfaces/circle-cctp/ITokenMessenger.sol';
import { UniswapWrapper } from './UniswapWrapper.sol';
import { IPlugin } from './interfaces/zenswap/IPlugin.sol';
import { IReceiver } from './interfaces/zenswap/IReceiver.sol';
error ErrorInvalidIntermediateToken();
error ErrorNotEnoughNativeToken();
contract ZenSwap is UniswapWrapper, IReceiver {
using SafeERC20 for IERC20;
event Received(address tokenIn, uint256 amountIn, address tokenOut, uint256 amountOut);
event Sent(address tokenIn, uint256 amountIn, address tokenOut, uint256 amountOut);
event SamechainSwap(address tokenIn, uint256 amountIn, address tokenOut, uint256 amountOut);
struct ExtraData {
SwapParams params;
address payable recipient;
}
/**
* @notice Sets the addresses of the Universal Router and Permit2 contract.
* @param _universalRouter Address of the Uniswap Universal Router.
* @param _permit2 Address of the Uniswap Permit2 contract.
*/
constructor(address _universalRouter, address _permit2) UniswapWrapper(_universalRouter, _permit2) {}
receive() external payable {}
/**
* @notice Swaps `amountIn` of `sourceParams.tokenIn` locally, then sends the resulting tokens (sourceParams.tokenOut)
* to a plugin for bridging (or further handling) on another chain
* @param pluginParams Encoded parameters for the plugin (e.g., bridging config)
* @param sourceParams Swap parameters for the source-chain swap (from tokenIn to tokenOut)
* @param destParams Swap parameters that will be used on the destination chain (packed into plugin’s extraData)
* @param recipient The final recipient of the tokens after the swap on the destination chain
* @param plugin The address of the plugin to which we forward tokens after the local swap
* @param amountIn The amount of `sourceParams.tokenIn` to swap
*/
function swapSend(
bytes calldata pluginParams,
SwapParams calldata sourceParams,
SwapParams calldata destParams,
address payable recipient,
address plugin,
uint256 amountIn
) external payable {
if (sourceParams.tokenOut == address(0)) revert ErrorInvalidIntermediateToken();
uint256 value;
if (sourceParams.tokenIn != address(0)) {
IERC20(sourceParams.tokenIn).safeTransferFrom(msg.sender, address(this), amountIn);
value = msg.value;
} else {
if (msg.value < amountIn) revert ErrorNotEnoughNativeToken();
value = msg.value - amountIn;
}
uint256 amountOut = _swap(sourceParams, amountIn);
IERC20(sourceParams.tokenOut).forceApprove(plugin, amountOut);
IPlugin(plugin).submit{ value: value }(
pluginParams,
abi.encode(ExtraData({ params: destParams, recipient: recipient })),
amountOut
);
emit Sent(sourceParams.tokenIn, amountIn, sourceParams.tokenOut, amountOut);
}
/**
* @notice Performs a one chain swap of `amountIn` using `sourceParams`,
* and transfers the swapped tokens to `recipient`.
* @param sourceParams Swap parameters for the local swap (from tokenIn to tokenOut)
* @param recipient Address to receive the tokens after the swap
* @param amountIn Amount of `sourceParams.tokenIn` to swap
*/
function swap(SwapParams calldata sourceParams, address payable recipient, uint256 amountIn) external payable {
if (sourceParams.tokenIn != address(0)) {
IERC20(sourceParams.tokenIn).safeTransferFrom(msg.sender, address(this), amountIn);
}
uint256 amountOut = _swap(sourceParams, amountIn);
if (sourceParams.tokenOut != address(0)) {
IERC20(sourceParams.tokenOut).safeTransfer(recipient, amountOut);
} else {
recipient.transfer(amountOut);
}
emit SamechainSwap(sourceParams.tokenIn, amountIn, sourceParams.tokenOut, amountOut);
}
/**
* @notice Called by the plugin with `amountIn` of tokens just delivered to this contract.
* Executes a swap and sends the result to user
* @param rawParams Encoded `ExtraData` specifying swap parameters and recipient
* @param amountIn The amount of tokens transferred from the plugin
*/
function onReceive(bytes calldata rawParams, uint256 amountIn) external {
ExtraData memory params = abi.decode(rawParams, (ExtraData));
// ETH input token is not allowed
IERC20(params.params.tokenIn).safeTransferFrom(msg.sender, address(this), amountIn);
uint256 amountOut = _swap(params.params, amountIn);
if (params.params.tokenOut != address(0)) {
IERC20(params.params.tokenOut).safeTransfer(params.recipient, amountOut);
} else {
params.recipient.transfer(amountOut);
}
emit Received(params.params.tokenIn, amountIn, params.params.tokenOut, amountOut);
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import { ITokenMessengerV2 } from './interfaces/circle-cctp/ITokenMessengerV2.sol';
import { IMessageTransmitterV2 } from './interfaces/circle-cctp/IMessageTransmitterV2.sol';
import { IReceiverV2 } from './interfaces/circle-cctp/IReceiverV2.sol';
import { IReceiver as IZenSwapReceiver } from './interfaces/zenswap/IReceiver.sol';
import { IPlugin as IZenSwapPlugin } from './interfaces/zenswap/IPlugin.sol';
import { IERC20 } from '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import { SafeERC20 } from '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';
import { OwnableUpgradeable } from '@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol';
import { TypedMemView } from './interfaces/circle-cctp/TypedMemView.sol';
import { MessageV2 } from './interfaces/circle-cctp/MessageV2.sol';
import { BurnMessageV2 } from './interfaces/circle-cctp/BurnMessageV2.sol';
error ErrorInvalidRecipient();
error ErrorInvalidFallbackRecipient();
error ErrorNotEnoughForFee();
error ErrorCCTPReceiveFailure();
error ErrorInvalidMessageVersion();
error ErrorInvalidMessageBodyVersion();
error ErrorHookParsingFailed();
error ErrorInvalidPlugin();
error ErrorInvalidSender();
error ErrorNoUSDC();
error ErrorInvalidRewardAmount();
contract ZenSwapCCTPv2 is IZenSwapPlugin, OwnableUpgradeable {
using SafeERC20 for IERC20;
using TypedMemView for bytes;
using TypedMemView for bytes29;
event PluginMessageSent(
uint32 cctpDestinationDomain,
address destPlugin,
address recipient,
address fallbackRecipient,
uint256 amount,
uint256 fee,
uint256 rewardAmount
);
event Executed(address recipient, uint256 amount, bool isFallback);
// The supported Message Format version
uint32 public constant supportedMessageVersion = 1;
// The supported Message Body version
uint32 public constant supportedMessageBodyVersion = 1;
// Byte-length of an address
uint256 internal constant ADDRESS_BYTE_LENGTH = 20;
struct PluginParams {
// Plugin address on destination chain
address destPlugin;
// Extra data recipient (ZenSwap contract)
address recipient;
// USDC recipient in case of onReceived fail
address fallbackRecipient;
// CCTP destination domain
uint32 cctpDestinationDomain;
// Maximum fee to pay on destination domain
uint256 maxFee;
// Minimum finality threshold for attestation
uint32 minFinalityThreshold;
// Reward amount for receiveMessage caller
uint256 rewardAmount;
}
struct PluginExtraData {
address recipient;
address fallbackRecipient;
bytes extraData;
uint256 rewardAmount;
}
ITokenMessengerV2 public cctpTokenMessenger;
IMessageTransmitterV2 public cctpMessageTransmitter;
IReceiverV2 public cctpReceiver;
IERC20 public usdc;
uint public fee;
/**
* @param _cctpMessenger Address of the Circle CCTP Token Messenger.
* @param _cctpReceiver Address of the Circle CCTP receiver contract.
* @param _usdc Address of the USDC token.
* @param _fee Fee amount to retain on bridging.
*/
function initialize(address _cctpMessenger, address _cctpReceiver, address _usdc, uint _fee) public initializer {
__Ownable_init_unchained(msg.sender);
cctpTokenMessenger = ITokenMessengerV2(_cctpMessenger);
cctpMessageTransmitter = IMessageTransmitterV2(cctpTokenMessenger.localMessageTransmitter());
cctpReceiver = IReceiverV2(_cctpReceiver);
usdc = IERC20(_usdc);
fee = _fee;
}
/**
* @notice Submits a cross-chain USDC transfer with extra data
* @param pluginParams Encoded `PluginParams`
* @param extraData Additional data used by the destination chain after bridging
* @param amount USDC amount to bridge
*/
function submit(bytes calldata pluginParams, bytes calldata extraData, uint256 amount) external payable {
usdc.safeTransferFrom(msg.sender, address(this), amount);
PluginParams memory params = abi.decode(pluginParams, (PluginParams));
if (params.destPlugin == address(0)) revert ErrorInvalidPlugin();
if (params.recipient == address(0)) revert ErrorInvalidRecipient();
if (params.fallbackRecipient == address(0)) revert ErrorInvalidFallbackRecipient();
if (amount <= fee) revert ErrorNotEnoughForFee();
bytes memory hookData = abi.encode(
PluginExtraData({
recipient: params.recipient,
fallbackRecipient: params.fallbackRecipient,
extraData: extraData,
rewardAmount: params.rewardAmount
})
);
usdc.safeIncreaseAllowance(address(cctpTokenMessenger), amount - fee);
cctpTokenMessenger.depositForBurnWithHook(
amount - fee,
params.cctpDestinationDomain,
bytes32(uint256(uint160(params.destPlugin))), // Mint to destination plugin
address(usdc),
bytes32(0), // Allow any address to call receiveMessage
params.maxFee,
params.minFinalityThreshold,
hookData
);
emit PluginMessageSent(
params.cctpDestinationDomain,
params.destPlugin,
params.recipient,
params.fallbackRecipient,
amount - fee,
fee,
params.rewardAmount
);
}
/**
* @notice Called by anyone to receive CCTP tokens and execute the hook
* @param message CCTP message payload
* @param attestation CCTP attestation
*/
function receiveMessage(bytes calldata message, bytes calldata attestation) external {
// Process the CCTP message and receive USDC
uint256 receivedAmount = _cctpReceive(message, attestation);
// Try to parse and execute hook
_parseAndExecuteHook(message, receivedAmount);
}
/**
* @notice Internal function to parse and execute hook
* @param message CCTP message payload
*/
function _parseAndExecuteHook(bytes calldata message, uint256 receivedAmount) internal {
// Validate message format
bytes29 _msg = message.ref(0);
MessageV2._validateMessageFormat(_msg);
if (MessageV2._getVersion(_msg) != supportedMessageVersion) {
revert ErrorInvalidMessageVersion();
}
// Validate burn message format
bytes29 _msgBody = MessageV2._getMessageBody(_msg);
BurnMessageV2._validateBurnMessageFormat(_msgBody);
if (BurnMessageV2._getVersion(_msgBody) != supportedMessageBodyVersion) {
revert ErrorInvalidMessageBodyVersion();
}
// Handle hook if present
bytes29 _hookData = BurnMessageV2._getHookData(_msgBody);
if (!_hookData.isValid()) {
revert ErrorHookParsingFailed();
}
PluginExtraData memory params = abi.decode(_hookData.clone(), (PluginExtraData));
executeOnReceive(params, receivedAmount, params.extraData);
}
/**
* @notice Force receives CCTP tokens (for emergency/maintenance)
* @param recipient Address to receive USDC
* @param message CCTP message payload
* @param attestation CCTP attestation
*/
function forceCctpReceive(
address recipient,
bytes memory message,
bytes memory attestation
) external onlyOwner {
uint256 receivedAmount = _cctpReceive(message, attestation);
usdc.safeTransfer(recipient, receivedAmount);
}
function updateParams(address _cctpMessenger, address _cctpReceiver, address _usdc, uint _fee) public onlyOwner {
cctpTokenMessenger = ITokenMessengerV2(_cctpMessenger);
cctpMessageTransmitter = IMessageTransmitterV2(cctpTokenMessenger.localMessageTransmitter());
cctpReceiver = IReceiverV2(_cctpReceiver);
usdc = IERC20(_usdc);
fee = _fee;
}
/**
* @notice Updates the bridging fee amount.
* @param newFee New fee amount.
*/
function updateFee(uint newFee) external onlyOwner {
fee = newFee;
}
/**
* @notice Withdraws the collected fees from the contract.
* @param recipient Address to receive the entire USDC balance.
*/
function takeFee(address recipient) external onlyOwner {
usdc.safeTransfer(recipient, usdc.balanceOf(address(this)));
}
function executeOnReceive(PluginExtraData memory params, uint256 receivedAmount, bytes memory extraData) internal {
if (params.recipient == address(0)) revert ErrorInvalidRecipient();
if (params.fallbackRecipient == address(0)) revert ErrorInvalidFallbackRecipient();
if (params.rewardAmount > receivedAmount) revert ErrorInvalidRewardAmount();
if (params.rewardAmount > 0) {
usdc.safeTransfer(msg.sender, params.rewardAmount);
receivedAmount -= params.rewardAmount;
}
usdc.forceApprove(params.recipient, receivedAmount);
try IZenSwapReceiver(params.recipient).onReceive(extraData, receivedAmount) {
// Success: No further action needed
usdc.forceApprove(params.recipient, 0);
emit Executed(params.recipient, receivedAmount, false);
} catch {
// If the call fails, send USDC to fallback recipient
usdc.forceApprove(params.recipient, 0);
usdc.safeTransfer(params.fallbackRecipient, receivedAmount);
emit Executed(params.recipient, receivedAmount, true);
}
}
function _cctpReceive(bytes memory message, bytes memory attestation) internal returns (uint256 amount) {
uint256 balanceBefore = usdc.balanceOf(address(this));
if (!cctpReceiver.receiveMessage(message, attestation)) revert ErrorCCTPReceiveFailure();
amount = usdc.balanceOf(address(this)) - balanceBefore;
if (amount == 0) revert ErrorNoUSDC();
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import { IGmpReceiver } from '../lib/analog-gmp/src/interfaces/IGmpReceiver.sol';
import { IGateway as IGmpGateway } from '../lib/analog-gmp/src/interfaces/IGateway.sol';
import { BranchlessMath } from '../lib/analog-gmp/src/utils/BranchlessMath.sol';
import { Message as CctpMessage } from './interfaces/circle-cctp/Message.sol';
import { BurnMessage as CctpBurnMessage } from './interfaces/circle-cctp/BurnMessage.sol';
import { ITokenMessenger as ICctpTokenMessenger } from './interfaces/circle-cctp/ITokenMessenger.sol';
import { IReceiver as ICctpReceiver } from './interfaces/circle-cctp/IReceiver.sol';
import { IReceiver as IZenSwapReceiver } from './interfaces/zenswap/IReceiver.sol';
import { IPlugin as IZenSwapPlugin } from './interfaces/zenswap/IPlugin.sol';
import { IMessageTransmitter as ICctpMessageTransmitter } from './interfaces/circle-cctp/IMessageTransmitter.sol';
import { IERC20 } from '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import { SafeERC20 } from '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';
import { OwnableUpgradeable } from '@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol';
error ErrorCCTPReceiveFailure();
error ErrorInvalidCCTPRecipient();
error ErrorInvalidRecipient();
error ErrorInvalidSender();
error ErrorInvalidFallbackRecipient();
error ErrorInvalidGateway();
error ErrorNotEnoughForFee();
error ErrorNoUSDC();
contract ZenSwapGmpPlugin is IGmpReceiver, IZenSwapPlugin, OwnableUpgradeable {
using BranchlessMath for uint256;
using SafeERC20 for IERC20;
event GmpReceived(address sender, bytes32 id, bytes payload);
event Executed(address recipient, uint256 amount, bool isFallback);
event MessageSent(bytes message);
event CCTPReceiveFailed(bytes32 id);
event PluginMessageSent(
uint32 cctpDestinationDomain,
address destPlugin,
address recipient,
address fallbackRecipient,
uint256 amount,
uint256 fee,
uint256 rewardAmount
);
struct PluginParams {
// Plugin address on destination chain
address destPlugin;
// Extra data recipient (ZenSwap contract)
address recipient;
// USDC recipient in case of onReceived fail
address fallbackRecipient;
// CCTP destination domain
uint32 cctpDestinationDomain;
// GMP destination network id
uint16 gmpDestNetwork;
// GMP gas limit
uint64 gmpGasLimit;
}
struct PluginExtraData {
address recipient;
address fallbackRecipient;
bytes extraData;
}
struct CCTP {
bytes attestation;
bytes message;
bytes extraData;
}
IGmpGateway public gmpGateway;
ICctpTokenMessenger public cctpTokenMessenger;
ICctpMessageTransmitter public cctpMessageTransmitter;
ICctpReceiver public cctpReceiver;
IERC20 public usdc;
uint public fee;
mapping(bytes32 => PluginExtraData) public failedTxs;
/**
* @param _gmpGateway Address of the GMP gateway contract.
* @param _cctpMessenger Address of the Circle CCTP Token Messenger.
* @param _cctpReceiver Address of the Circle CCTP receiver contract.
* @param _usdc Address of the USDC token.
* @param _fee Fee amount to retain on bridging.
*/
function initialize(
address _gmpGateway,
address _cctpMessenger,
address _cctpReceiver,
address _usdc,
uint _fee
) public initializer {
__Ownable_init_unchained(msg.sender);
gmpGateway = IGmpGateway(_gmpGateway);
cctpTokenMessenger = ICctpTokenMessenger(_cctpMessenger);
cctpMessageTransmitter = ICctpMessageTransmitter(cctpTokenMessenger.localMessageTransmitter());
cctpReceiver = ICctpReceiver(_cctpReceiver);
usdc = IERC20(_usdc);
fee = _fee;
}
function estimateMessageCost(
uint16 networkId,
uint256 messageSize,
uint256 gasLimit
) external view returns (uint256) {
return gmpGateway.estimateMessageCost(networkId, messageSize, gasLimit);
}
/**
* @notice Called by the GMP gateway when a message arrives on this chain.
* @param id Message ID assigned by the gateway.
* @param payload Encoded CCTP data containing attestation, message, and extra data.
* @return The message ID.
*/
function onGmpReceived(
bytes32 id,
uint128,
bytes32,
uint64,
bytes calldata payload
) external payable returns (bytes32) {
if (msg.sender != address(gmpGateway)) revert ErrorInvalidGateway();
CCTP memory message = abi.decode(payload, (CCTP));
// Decode extraData to extract recipient and fallback
PluginExtraData memory params = abi.decode(message.extraData, (PluginExtraData));
// Process the CCTP message and receive USDC
(uint256 receivedAmount, bool success) = _cctpReceive(message.message, message.attestation);
if (!success) {
failedTxs[id] = params;
emit CCTPReceiveFailed(id);
return id;
}
executeOnReceive(params, params.extraData, receivedAmount);
return id;
}
/**
* @notice Restores a failed transaction
* @param id Message ID
* @param message CCTP message payload
* @param attestation CCTP attestation
*/
function restoreFailedTx(bytes32 id, bytes calldata message, bytes calldata attestation) external {
PluginExtraData memory params = failedTxs[id];
if (params.fallbackRecipient != msg.sender && params.recipient != msg.sender) revert ErrorInvalidSender();
(uint256 receivedAmount, bool success) = _cctpReceive(message, attestation);
if (!success) revert ErrorCCTPReceiveFailure();
executeOnReceive(params, params.extraData, receivedAmount);
delete failedTxs[id];
}
/**
* @notice Submits a cross-chain USDC transfer with extra data
* @param pluginParams Encoded `PluginParams`
* @param extraData Additional data used by the destination chain after bridging
* @param amount USDC amount to bridge
*/
function submit(bytes calldata pluginParams, bytes calldata extraData, uint256 amount) external payable {
usdc.safeTransferFrom(msg.sender, address(this), amount);
PluginParams memory params = abi.decode(pluginParams, (PluginParams));
if (params.recipient == address(0)) revert ErrorInvalidRecipient();
if (params.fallbackRecipient == address(0)) revert ErrorInvalidFallbackRecipient();
if (amount <= fee) revert ErrorNotEnoughForFee();
bytes memory extraPluginData = abi.encode(
PluginExtraData({
recipient: params.recipient,
fallbackRecipient: params.fallbackRecipient,
extraData: extraData
})
);
bytes memory cctpMessage = _cctpSend(params, extraPluginData, amount - fee);
gmpGateway.submitMessage{ value: msg.value }(
params.destPlugin,
params.gmpDestNetwork,
params.gmpGasLimit,
cctpMessage
);
emit PluginMessageSent(
params.cctpDestinationDomain,
params.destPlugin,
params.recipient,
params.fallbackRecipient,
amount - fee,
fee,
0
);
}
/**
* @notice Force receives CCTP tokens (for emergency/maintenance)
* @param recipient Address to receive USDC
* @param message CCTP message payload
* @param attestation CCTP attestation
*/
function forceCctpReceive(
address recipient,
bytes memory message,
bytes memory attestation
) external onlyOwner {
(uint256 receivedAmount, bool success) = _cctpReceive(message, attestation);
if (!success) revert ErrorCCTPReceiveFailure();
usdc.safeTransfer(recipient, receivedAmount);
}
/**
* @notice Updates the contract parameters.
* @param _gmpGateway Address of the GMP gateway contract.
* @param _cctpMessenger Address of the Circle CCTP Token Messenger.
* @param _cctpReceiver Address of the Circle CCTP receiver contract.
* @param _usdc Address of the USDC token.
* @param _fee Fee amount to retain on bridging.
*/
function updateParams(
address _gmpGateway,
address _cctpMessenger,
address _cctpReceiver,
address _usdc,
uint _fee
) public onlyOwner {
gmpGateway = IGmpGateway(_gmpGateway);
cctpTokenMessenger = ICctpTokenMessenger(_cctpMessenger);
cctpMessageTransmitter = ICctpMessageTransmitter(cctpTokenMessenger.localMessageTransmitter());
cctpReceiver = ICctpReceiver(_cctpReceiver);
usdc = IERC20(_usdc);
fee = _fee;
}
/**
* @notice Updates the bridging fee amount.
* @param newFee New fee amount.
*/
function updateFee(uint newFee) external onlyOwner {
fee = newFee;
}
/**
* @notice Withdraws the collected fees from the contract.
* @param recipient Address to receive the entire USDC balance.
*/
function takeFee(address recipient) external onlyOwner {
usdc.safeTransfer(recipient, usdc.balanceOf(address(this)));
}
function executeOnReceive(PluginExtraData memory params, bytes memory extraData, uint256 amount) internal {
// Validate ZenSwap params
if (params.recipient == address(0)) revert ErrorInvalidRecipient();
if (params.fallbackRecipient == address(0)) revert ErrorInvalidFallbackRecipient();
// Set recipient allowance
usdc.forceApprove(params.recipient, amount);
// Try to call recipient contract's onReceive function
try IZenSwapReceiver(params.recipient).onReceive(extraData, amount) {
emit Executed(params.recipient, amount, false);
} catch {
usdc.safeTransfer(params.fallbackRecipient, amount);
emit Executed(params.recipient, amount, true);
}
// Unset recipient allowance
usdc.forceApprove(params.recipient, 0);
}
function _cctpSend(
PluginParams memory params,
bytes memory extraData,
uint256 amount
) internal returns (bytes memory) {
usdc.safeIncreaseAllowance(address(cctpTokenMessenger), amount);
bytes32 destPluginBytes32 = CctpMessage.addressToBytes32(params.destPlugin);
uint64 nonce = cctpTokenMessenger.depositForBurnWithCaller(
amount,
params.cctpDestinationDomain,
destPluginBytes32,
address(usdc),
destPluginBytes32
);
bytes memory burnMessage = CctpBurnMessage._formatMessage(
cctpTokenMessenger.messageBodyVersion(),
CctpMessage.addressToBytes32(address(usdc)),
destPluginBytes32,
amount,
CctpMessage.addressToBytes32(address(this))
);
bytes memory messageData = CctpMessage._formatMessage(
cctpMessageTransmitter.version(),
cctpMessageTransmitter.localDomain(),
params.cctpDestinationDomain,
nonce,
CctpMessage.addressToBytes32(address(cctpTokenMessenger)),
cctpTokenMessenger.remoteTokenMessengers(params.cctpDestinationDomain),
destPluginBytes32,
burnMessage
);
emit MessageSent(messageData);
return abi.encode(CCTP({ message: messageData, extraData: extraData, attestation: '' }));
}
function _cctpReceive(bytes memory message, bytes memory attestation) internal returns (uint256 amount, bool success) {
uint256 balanceBefore = usdc.balanceOf(address(this));
if (attestation.length == 0) {
return (0, false);
}
success = cctpReceiver.receiveMessage(message, attestation);
amount = usdc.balanceOf(address(this)) - balanceBefore;
if (amount == 0) revert ErrorNoUSDC();
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
import { OwnableUpgradeable } from '@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol';
contract ZenSwapGmpPluginEmpty is OwnableUpgradeable {
function initialize() public initializer {
__Ownable_init_unchained(msg.sender);
}
}// SPDX-License-Identifier: MIT
// Analog's Contracts (last updated v0.1.0) (src/interfaces/IGateway.sol)
pragma solidity >=0.8.0;
import {GmpSender} from "../Primitives.sol";
/**
* @dev Required interface of an Gateway compliant contract
*/
interface IGateway {
/**
* @dev New GMP submitted by calling the `submitMessage` method.
* @param id EIP-712 hash of the `GmpPayload`, which is it's unique identifier
* @param source sender account, with an extra flag indicating if it is a contract or an EOA
* @param destinationAddress the target address on the destination chain.
* @param destinationNetwork the target chain where the contract call will be made.
* @param executionGasLimit the gas limit available for the contract call
* @param gasCost the gas limit available for the contract call
* @param nonce Sequence number per sender, used to guarantee each message is unique.
* @param data message data with no specified format
*/
event GmpCreated(
bytes32 indexed id,
bytes32 indexed source,
address indexed destinationAddress,
uint16 destinationNetwork,
uint64 executionGasLimit,
uint64 gasCost,
uint64 nonce,
bytes data
);
function networkId() external view returns (uint16);
/**
* @notice Estimate the gas cost of execute a GMP message.
* @dev This function is called on the destination chain before calling the gateway to execute a source contract.
* @param networkid The target chain where the contract call will be made
* @param messageSize Message size
* @param messageSize Message gas limit
*/
function estimateMessageCost(uint16 networkid, uint256 messageSize, uint256 gasLimit)
external
view
returns (uint256);
/**
* @dev Send message from chain A to chain B
* @param destinationAddress the target address on the destination chain
* @param destinationNetwork the target chain where the contract call will be made
* @param executionGasLimit the gas limit available for the contract call
* @param data message data with no specified format
*/
function submitMessage(
address destinationAddress,
uint16 destinationNetwork,
uint256 executionGasLimit,
bytes calldata data
) external payable returns (bytes32);
}// SPDX-License-Identifier: MIT
// Analog's Contracts (last updated v0.1.0) (src/interfaces/IGmpReceiver.sol)
pragma solidity >=0.8.0;
/**
* @dev Required interface of an GMP compliant contract
*/
interface IGmpReceiver {
/**
* @dev Handles the receipt of a single GMP message.
* The contract must verify the msg.sender, it must be the Gateway Contract address.
*
* @param id The EIP-712 hash of the message payload, used as GMP unique identifier
* @param network The chain_id of the source chain who send the message
* @param source The pubkey/address which sent the GMP message
* @param payload The message payload with no specified format
* @return 32 byte result which will be stored together with GMP message
*/
function onGmpReceived(bytes32 id, uint128 network, bytes32 source, uint64 nonce, bytes calldata payload)
external
payable
returns (bytes32);
}// SPDX-License-Identifier: MIT
// Analog's Contracts (last updated v0.1.0) (src/NetworkID.sol)
pragma solidity ^0.8.0;
import {BranchlessMath} from "./utils/BranchlessMath.sol";
type NetworkID is uint16;
library NetworkIDHelpers {
NetworkID internal constant MAINNET = NetworkID.wrap(0);
NetworkID internal constant ASTAR = NetworkID.wrap(1);
NetworkID internal constant POLYGON_POS = NetworkID.wrap(2);
NetworkID internal constant ETHEREUM_LOCAL_DEV = NetworkID.wrap(3);
NetworkID internal constant GOERLI = NetworkID.wrap(4);
NetworkID internal constant SEPOLIA = NetworkID.wrap(5);
NetworkID internal constant ASTAR_LOCAL_DEV = NetworkID.wrap(6);
NetworkID internal constant SHIBUYA = NetworkID.wrap(7);
NetworkID internal constant POLYGON_AMOY = NetworkID.wrap(8);
NetworkID internal constant BINANCE_SMART_CHAIN_TESTNET = NetworkID.wrap(9);
NetworkID internal constant ARBITRUM_SEPOLIA = NetworkID.wrap(10);
/**
* @dev Converts a `NetworkID` into a `uint16`.
*/
function asUint(NetworkID networkId) internal pure returns (uint16) {
return NetworkID.unwrap(networkId);
}
/**
* @dev Get the EIP-150 chain id from the network id.
*/
function chainId(NetworkID networkId) internal pure returns (uint64 chainID) {
assembly {
switch networkId
case 0 {
// Ethereum Mainnet
chainID := 0
}
case 1 {
// Astar
chainID := 592
}
case 2 {
// Polygon PoS
chainID := 137
}
case 3 {
// Ethereum local testnet
chainID := 1337
}
case 4 {
// Goerli
chainID := 5
}
case 5 {
// Sepolia
chainID := 11155111
}
case 6 {
// Astar local testnet
chainID := 592
}
case 7 {
// Shibuya
chainID := 81
}
case 8 {
// Polygon Amoy
chainID := 80002
}
case 9 {
// Binance Smart Chain
chainID := 97
}
case 10 {
// Arbitrum Sepolia
chainID := 421614
}
default {
// Unknown network id
chainID := 0xffffffffffffffff
}
}
require(chainID > 2 ** 24, "the provided network id doesn't exists");
return uint64(chainID);
}
/**
* @dev Try to get the network id from the chain id.
*/
function tryFromChainID(uint256 chainid) internal pure returns (bool, NetworkID) {
uint256 networkId = type(uint256).max;
// Ethereum Mainnet
networkId = BranchlessMath.ternary(chainid == 0, asUint(MAINNET), networkId);
// Astar
networkId = BranchlessMath.ternary(chainid == 592, asUint(ASTAR), networkId);
// Polygon PoS
networkId = BranchlessMath.ternary(chainid == 137, asUint(POLYGON_POS), networkId);
// Ethereum local testnet
networkId = BranchlessMath.ternary(chainid == 1337, asUint(ETHEREUM_LOCAL_DEV), networkId);
// Goerli
networkId = BranchlessMath.ternary(chainid == 5, asUint(GOERLI), networkId);
// Sepolia
networkId = BranchlessMath.ternary(chainid == 11155111, asUint(SEPOLIA), networkId);
// Astar local testnet
networkId = BranchlessMath.ternary(chainid == 592, asUint(ASTAR_LOCAL_DEV), networkId);
// Shibuya
networkId = BranchlessMath.ternary(chainid == 81, asUint(SHIBUYA), networkId);
// Polygon Amoy
networkId = BranchlessMath.ternary(chainid == 80002, asUint(POLYGON_AMOY), networkId);
// Binance Smart Chain
networkId = BranchlessMath.ternary(chainid == 97, asUint(BINANCE_SMART_CHAIN_TESTNET), networkId);
// Arbitrum Sepolia
networkId = BranchlessMath.ternary(chainid == 421614, asUint(ARBITRUM_SEPOLIA), networkId);
bool exists = networkId != type(uint256).max;
return (exists, NetworkID.wrap(uint16(networkId)));
}
/**
* @dev Converts a EIP-155 chain id into a `NetworkID`, reverts if the network id doesn't exists.
*/
function fromChainID(uint256 chainid) internal pure returns (NetworkID) {
(bool exists, NetworkID networkId) = tryFromChainID(chainid);
require(exists, "network id doesn't exists for the given chain id");
return networkId;
}
}// SPDX-License-Identifier: MIT
// Analog's Contracts (last updated v0.1.0) (src/Primitives.sol)
pragma solidity >=0.8.0;
import {BranchlessMath} from "./utils/BranchlessMath.sol";
import {UFloatMath, UFloat9x56} from "./utils/Float9x56.sol";
import {NetworkID} from "./NetworkID.sol";
/**
* @dev GMP message EIP-712 Type Hash.
* Declared as raw value to enable it to be used in inline assembly
* keccak256("GmpMessage(bytes32 source,uint16 srcNetwork,address dest,uint16 destNetwork,uint64 gasLimit,uint64 gasCost,uint32 nonce,bytes data)")
*/
uint256 constant GMP_VERSION = 0;
/**
* @dev Maximum size of the GMP payload
*/
uint256 constant MAX_PAYLOAD_SIZE = 0x6000;
/**
* @dev GmpSender is the sender of a GMP message
*/
type GmpSender is bytes32;
/**
* @dev Tss public key
* @param yParity public key y-coord parity, the contract converts it to 27/28
* @param xCoord affine x-coordinate
*/
struct TssKey {
uint8 yParity;
uint256 xCoord;
}
/**
* @dev Schnorr signature.
* OBS: what is actually signed is: keccak256(abi.encodePacked(R, parity, px, nonce, message))
* Where `parity` is the public key y coordinate stored in the contract, and `R` is computed from `e` and `s` parameters.
* @param xCoord public key x coordinates, y-parity is stored in the contract
* @param e Schnorr signature e component
* @param s Schnorr signature s component
*/
struct Signature {
uint256 xCoord;
uint256 e;
uint256 s;
}
/**
* @dev GMP payload, this is what the timechain creates as task payload
* @param source Pubkey/Address of who send the GMP message
* @param srcNetwork Source chain identifier (for ethereum networks it is the EIP-155 chain id)
* @param dest Destination/Recipient contract address
* @param destNetwork Destination chain identifier (it's the EIP-155 chain_id for ethereum networks)
* @param gasLimit gas limit of the GMP call
* @param nonce Sequence nonce per sender, allows sending two messages with same content
* @param data message data with no specified format
*/
struct GmpMessage {
GmpSender source;
uint16 srcNetwork;
address dest;
uint16 destNetwork;
uint64 gasLimit;
uint64 nonce;
bytes data;
}
/**
* @dev Message payload used to revoke or/and register new shards
* @param revoke Shard's keys to revoke
* @param register Shard's keys to register
*/
struct UpdateKeysMessage {
TssKey[] revoke;
TssKey[] register;
}
/**
* @dev Messages from Timechain take the form of these commands.
*/
enum Command {
Invalid,
GMP,
RegisterShard,
UnregisterShard,
SetRoute
}
/**
* @dev Inbound message from a Timechain
* @param command Command identifier.
* @param params Encoded command.
*/
struct GatewayOp {
/// @dev The command to execute
Command command;
/// @dev The Parameters for the command
bytes params;
}
/**
* @dev Inbound message from a Timechain
* @param version Message version, will change if the message format changes.
* @param batchID Sequence number representing the batch order.
* @param ops List of operations to execute.
*/
struct InboundMessage {
uint8 version;
/// @dev The batch ID
uint64 batchID;
/// @dev
GatewayOp[] ops;
}
/**
* @dev A Route represents a communication channel between two networks.
* @param networkId The id of the provided network.
* @param gasLimit The maximum amount of gas we allow on this particular network.
* @param gateway Destination chain gateway address.
* @param relativeGasPriceNumerator Gas price numerator in terms of the source chain token.
* @param relativeGasPriceDenominator Gas price denominator in terms of the source chain token.
*/
struct Route {
NetworkID networkId;
uint64 gasLimit;
uint128 baseFee;
bytes32 gateway;
uint128 relativeGasPriceNumerator;
uint128 relativeGasPriceDenominator;
}
/**
* @dev Message payload used to revoke or/and register new shards
* @param revoke Shard's keys to revoke
* @param register Shard's keys to register
*/
struct Network {
uint16 id;
address gateway;
}
/**
* @dev Status of a GMP message
*/
enum GmpStatus {
NOT_FOUND,
SUCCESS,
REVERT,
INSUFFICIENT_FUNDS,
PENDING
}
/**
* @dev GmpMessage with EIP-712 GMP ID and callback function encoded.
* @param eip712hash EIP-712 hash of the `GmpMessage`, which is it's unique identifier
* @param source Pubkey/Address of who send the GMP message
* @param srcNetwork Source chain identifier (for ethereum networks it is the EIP-155 chain id)
* @param dest Destination/Recipient contract address
* @param destNetwork Destination chain identifier (it's the EIP-155 chain_id for ethereum networks)
* @param gasLimit gas limit of the GMP call
* @param nonce Sequence nonce per sender, allows sending two messages with same content
* @param callback encoded callback of `IGmpRecipient` interface, see `IGateway.sol` for more details.
*/
struct GmpCallback {
bytes32 opHash;
GmpSender source;
uint16 srcNetwork;
address dest;
uint16 destNetwork;
uint64 gasLimit;
uint64 nonce;
bytes callback;
}
/**
* @dev EIP-712 utility functions for primitives
*/
library PrimitiveUtils {
function toAddress(GmpSender sender) internal pure returns (address) {
return address(uint160(uint256(GmpSender.unwrap(sender))));
}
function toSender(address addr, bool isContract) internal pure returns (GmpSender) {
uint256 sender = BranchlessMath.toUint(isContract) << 160 | uint256(uint160(addr));
return GmpSender.wrap(bytes32(sender));
}
// computes the hash of an array of tss keys
function eip712hash(TssKey memory tssKey) internal pure returns (bytes32) {
return keccak256(abi.encode(keccak256("TssKey(uint8 yParity,uint256 xCoord)"), tssKey.yParity, tssKey.xCoord));
}
// computes the hash of an array of tss keys
function eip712hash(TssKey[] memory tssKeys) internal pure returns (bytes32) {
bytes memory keysHashed = new bytes(tssKeys.length * 32);
uint256 ptr;
assembly {
ptr := keysHashed
}
for (uint256 i = 0; i < tssKeys.length; i++) {
bytes32 hash = eip712hash(tssKeys[i]);
assembly {
ptr := add(ptr, 32)
mstore(ptr, hash)
}
}
return keccak256(keysHashed);
}
// computes the hash of the fully encoded EIP-712 message for the domain, which can be used to recover the signer
function eip712hash(UpdateKeysMessage memory message) internal pure returns (bytes32) {
return keccak256(
abi.encode(
keccak256("UpdateKeysMessage(TssKey[] revoke,TssKey[] register)TssKey(uint8 yParity,uint256 xCoord)"),
eip712hash(message.revoke),
eip712hash(message.register)
)
);
}
function messageId(GmpMessage memory message) internal pure returns (bytes32 id) {
assembly ("memory-safe") {
// now compute the GmpMessage Type Hash without memory copying
let offset1 := sub(message, 32)
let backup1 := mload(offset1)
mstore(offset1, GMP_VERSION)
id := keccak256(offset1, 0xe0)
mstore(offset1, backup1)
}
}
function opHash(GmpMessage memory message) internal pure returns (bytes32 id) {
bytes32 dataHash = keccak256(message.data);
assembly ("memory-safe") {
// now compute the GmpMessage Type Hash without memory copying
let offset1 := sub(message, 32)
let backup1 := mload(offset1)
let backup2 := mload(message)
mstore(offset1, GMP_VERSION)
id := keccak256(offset1, 0xe0)
mstore(offset1, id)
mstore(message, dataHash)
id := keccak256(offset1, 64)
mstore(offset1, backup1)
mstore(message, backup2)
}
}
type MessagePtr is uint256;
function _intoMemoryPointer(MessagePtr ptr) private pure returns (GmpMessage memory r) {
assembly {
r := ptr
}
}
function _intoCalldataPointer(MessagePtr ptr) private pure returns (GmpMessage calldata r) {
assembly {
r := ptr
}
}
function memToCallback(GmpMessage memory message) internal pure returns (GmpCallback memory callback) {
MessagePtr ptr;
assembly {
ptr := message
}
_intoCallback(ptr, false, callback);
}
function intoCallback(GmpMessage calldata message) internal pure returns (GmpCallback memory callback) {
MessagePtr ptr;
assembly {
ptr := message
}
_intoCallback(ptr, true, callback);
}
/**
* @dev Computes the message ID from the provided `GmpCallback` struct.
*/
function _computeMessageID(GmpCallback memory callback) private pure {
bytes memory onGmpReceived = callback.callback;
callback.opHash = bytes32(GMP_VERSION);
bytes32 msgId;
assembly ("memory-safe") {
// Compute `keccak256(abi.encode(GMP_VERSION, message.source, ...))`
msgId := keccak256(callback, 0x00e0)
// Replace the `id` in `onGmpReceived(uint256 id,...)` in the callback.
mstore(add(onGmpReceived, 0x24), msgId)
}
bytes memory data;
assembly ("memory-safe") {
data := add(onGmpReceived, 0xc4)
}
bytes32 dataHash = keccak256(data);
callback.opHash = msgId;
GmpSender backup = callback.source;
callback.source = GmpSender.wrap(dataHash);
assembly ("memory-safe") {
dataHash := keccak256(callback, 0x40)
}
callback.opHash = dataHash;
callback.source = backup;
}
function messageId(GmpCallback memory callback) internal pure returns (bytes32 msgId) {
bytes memory onGmpReceived = callback.callback;
assembly ("memory-safe") {
msgId := mload(add(onGmpReceived, 0x24))
}
}
/**
* @dev Converts the `GmpMessage` into a `GmpCallback` struct, which contains all fields from
* `GmpMessage`, plus the EIP-712 hash and `IGmpReceiver.onGmpReceived` callback.
*
* This method also prevents copying the `message.data` to memory twice, which is expensive if
* the data is large.
* Example: using solidity high-level `abi.encode` method does the following.
* 1. Copy the `message.data` to memory to compute the `GmpMessage` EIP-712 hash.
* 2. Copy again to encode the `IGmpReceiver.onGmpReceived` callback.
*
* Instead we copy it once and use the same memory location to compute the EIP-712 hash and
* create he `IGmpReceiver.onGmpReceived` callback, unfortunately this requires inline assembly.
*
* @param message GmpMessage from calldata to be encoded
* @param callback `GmpCallback` struct
*/
function _intoCallback(MessagePtr message, bool isCalldata, GmpCallback memory callback) private pure {
// | MEMORY OFFSET | RESERVED FIELD |
// | 0x0000..0x0020 <- GmpCallback.opHash
// | 0x0020..0x0040 <- GmpCallback.source
// | 0x0040..0x0060 <- GmpCallback.srcNetwork
// | 0x0060..0x0080 <- GmpCallback.dest
// | 0x0080..0x00a0 <- GmpCallback.destNetwork
// | 0x00a0..0x00c0 <- GmpCallback.gasLimit
// | 0x00c0..0x00e0 <- GmpCallback.nonce
// | 0x00e0..0x0100 <- GmpCallback.callback.offset
// | 0x0100..0x0120 <- GmpCallback.callback.length
// | 0x0120..0x0124 <- onGmpReceived.selector (4 bytes)
// | 0x0124..0x0144 <- onGmpReceived.id
// | 0x0144..0x0164 <- onGmpReceived.network
// | 0x0164..0x0184 <- onGmpReceived.source
// | 0x0184..0x01a4 <- onGmpReceived.nonce
// | 0x01a4..0x01c4 <- onGmpReceived.data.offset
// | 0x01c4..0x01e4 <- onGmpReceived.data.length
// | 0x01e4........ <- onGmpReceived.data
if (isCalldata) {
GmpMessage calldata m = _intoCalldataPointer(message);
callback.source = m.source;
callback.srcNetwork = m.srcNetwork;
callback.dest = m.dest;
callback.destNetwork = m.destNetwork;
callback.gasLimit = m.gasLimit;
callback.nonce = m.nonce;
bytes calldata data = m.data;
callback.callback = abi.encodeWithSignature(
"onGmpReceived(bytes32,uint128,bytes32,uint64,bytes)",
callback.opHash,
callback.srcNetwork,
callback.source,
callback.nonce,
data
);
} else {
GmpMessage memory m = _intoMemoryPointer(message);
callback.source = m.source;
callback.srcNetwork = m.srcNetwork;
callback.dest = m.dest;
callback.destNetwork = m.destNetwork;
callback.gasLimit = m.gasLimit;
callback.nonce = m.nonce;
callback.callback = abi.encodeWithSignature(
"onGmpReceived(bytes32,uint128,bytes32,uint64,bytes)",
callback.opHash,
callback.srcNetwork,
callback.source,
callback.nonce,
m.data
);
}
// Compute the message ID
_computeMessageID(callback);
}
}// SPDX-License-Identifier: MIT
// Analog's Contracts (last updated v0.1.0) (src/utils/BranchlessMath.sol)
pragma solidity >=0.8.20;
/**
* Rounding mode used when divide an integer.
*/
enum Rounding {
// Rounds towards zero
Floor,
// Rounds to the nearest value; if the number falls midway,
// it is rounded to the value above.
Nearest,
// Rounds towards positive infinite
Ceil
}
/**
* @dev Utilities for branchless operations, useful when a constant gas cost is required.
*/
library BranchlessMath {
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 x, uint256 y) internal pure returns (uint256) {
return ternary(x < y, x, y);
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 x, uint256 y) internal pure returns (uint256) {
return ternary(x > y, x, y);
}
/**
* @dev If `condition` is true returns `a`, otherwise returns `b`.
*/
function ternary(bool condition, uint256 a, uint256 b) internal pure returns (uint256) {
unchecked {
// branchless select, works because:
// b ^ (a ^ b) == a
// b ^ 0 == b
//
// This is better than doing `condition ? a : b` because:
// - Consumes less gas
// - Constant gas cost regardless the inputs
// - Reduces the final bytecode size
return b ^ ((a ^ b) * toUint(condition));
}
}
/**
* @dev If `condition` is true returns `a`, otherwise returns `b`.
* see `BranchlessMath.ternary`
*/
function ternary(bool condition, int256 a, int256 b) internal pure returns (int256 r) {
assembly {
r := xor(b, mul(xor(a, b), condition))
}
}
/**
* @dev If `condition` is true returns `a`, otherwise returns `b`.
* see `BranchlessMath.ternary`
*/
function ternary(bool condition, address a, address b) internal pure returns (address r) {
assembly {
r := xor(b, mul(xor(a, b), condition))
}
}
/**
* @dev If `condition` is true returns `a`, otherwise returns `b`.
* see `BranchlessMath.ternary`
*/
function ternary(bool condition, bytes32 a, bytes32 b) internal pure returns (bytes32 r) {
assembly {
r := xor(b, mul(xor(a, b), condition))
}
}
/**
* @dev If `condition` is true returns `a`, otherwise returns `b`.
* see `BranchlessMath.ternary`
*/
function ternaryU128(bool condition, uint128 a, uint128 b) internal pure returns (uint128 r) {
assembly {
r := xor(b, mul(xor(a, b), condition))
}
}
/**
* @dev If `condition` is true returns `a`, otherwise returns `b`.
* see `BranchlessMath.ternary`
*/
function ternaryU64(bool condition, uint64 a, uint64 b) internal pure returns (uint64 r) {
assembly {
r := xor(b, mul(xor(a, b), condition))
}
}
/**
* @dev If `condition` is true returns `a`, otherwise returns `b`.
* see `BranchlessMath.ternary`
*/
function ternaryU32(bool condition, uint32 a, uint32 b) internal pure returns (uint32 r) {
assembly {
r := xor(b, mul(xor(a, b), condition))
}
}
/**
* @dev If `condition` is true returns `a`, otherwise returns `b`.
* see `BranchlessMath.ternary`
*/
function ternaryU8(bool condition, uint8 a, uint8 b) internal pure returns (uint8 r) {
assembly {
r := xor(b, mul(xor(a, b), condition))
}
}
/**
* @dev If `condition` is true return `value`, otherwise return zero.
* see `BranchlessMath.ternary`
*/
function selectIf(bool condition, uint256 value) internal pure returns (uint256) {
unchecked {
return value * toUint(condition);
}
}
/**
* @dev Unsigned saturating addition, bounds to UINT256 MAX instead of overflowing.
* equivalent to:
* uint256 r = x + y;
* return r >= x ? r : UINT256_MAX;
*/
function saturatingAdd(uint256 x, uint256 y) internal pure returns (uint256) {
unchecked {
x = x + y;
y = 0 - toUint(x < y);
return x | y;
}
}
/**
* @dev Unsigned saturating subtraction, bounds to zero instead of overflowing.
* equivalent to: x > y ? x - y : 0
*/
function saturatingSub(uint256 a, uint256 b) internal pure returns (uint256) {
unchecked {
// equivalent to: a > b ? a - b : 0
return (a - b) * toUint(a > b);
}
}
/**
* @dev Unsigned saturating multiplication, bounds to `2 ** 256 - 1` instead of overflowing.
*/
function saturatingMul(uint256 a, uint256 b) internal pure returns (uint256) {
unchecked {
uint256 c = a * b;
bool success;
assembly {
// Only true when the multiplication doesn't overflow
// (c / a == b) || (a == 0)
success := or(eq(div(c, a), b), iszero(a))
}
return c | (toUint(success) - 1);
}
}
/**
* @dev Unsigned saturating division, bounds to UINT256 MAX instead of overflowing.
*/
function saturatingDiv(uint256 x, uint256 y) internal pure returns (uint256 r) {
assembly {
// Solidity reverts with a division by zero error, while using inline assembly division does
// not revert, it returns zero.
// Reference: https://github.com/ethereum/solidity/issues/15200
r := div(x, y)
}
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds towards infinity instead
* of rounding towards zero.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
unchecked {
// The following calculation ensures accurate ceiling division without overflow.
// Since a is non-zero, (a - 1) / b will not overflow.
// The largest possible result occurs when (a - 1) / b is type(uint256).max,
// but the largest value we can obtain is type(uint256).max - 1, which happens
// when a = type(uint256).max and b = 1.
return selectIf(a > 0, ((a - 1) / b + 1));
}
}
/**
* @dev Unsigned saturating left shift, bounds to `2 ** 256 - 1` instead of overflowing.
*/
function saturatingShl(uint256 x, uint8 shift) internal pure returns (uint256 r) {
assembly {
// Detect overflow by checking if (x >> (256 - shift)) > 0
r := gt(shr(sub(256, shift), x), 0)
// Bounds to `type(uint256).max` if an overflow happened
r := or(shl(shift, x), sub(0, r))
}
}
/**
* @dev Returns the absolute unsigned value of a signed value.
*
* Ref: https://graphics.stanford.edu/~seander/bithacks.html#IntegerAbs
*/
function abs(int256 a) internal pure returns (uint256 r) {
assembly {
// Formula from the "Bit Twiddling Hacks" by Sean Eron Anderson.
// Since `n` is a signed integer, the generated bytecode will use the SAR opcode to perform the right shift,
// taking advantage of the most significant (or "sign" bit) in two's complement representation.
// This opcode adds new most significant bits set to the value of the previous most significant bit. As a result,
// the mask will either be `bytes(0)` (if n is positive) or `~bytes32(0)` (if n is negative).
let mask := sar(255, a)
// A `bytes(0)` mask leaves the input unchanged, while a `~bytes32(0)` mask complements it.
r := xor(add(a, mask), mask)
}
}
/**
* @dev Computes the absolute difference between x and y.
*/
function absDiff(uint256 x, uint256 y) internal pure returns (uint256) {
return abs(int256(x) - int256(y));
}
/**
* @dev Computes the absolute difference between x and y.
*/
function absDiff(int256 x, int256 y) internal pure returns (uint256) {
return abs(x - y);
}
/**
* @dev Cast a boolean (false or true) to a uint256 (0 or 1) with no jump.
*/
function toUint(bool b) internal pure returns (uint256 u) {
assembly ("memory-safe") {
u := iszero(iszero(b))
}
}
/**
* @dev Cast a boolean (false or true) to a int256 (0 or 1) with no jump.
*/
function toInt(bool b) internal pure returns (int256 i) {
assembly ("memory-safe") {
i := iszero(iszero(b))
}
}
/**
* @dev Cast an address to uint256
*/
function toUint(address addr) internal pure returns (uint256) {
return uint256(uint160(addr));
}
/**
* @dev Count the consecutive zero bits (trailing) on the right.
*/
function trailingZeros(uint256 x) internal pure returns (uint256 r) {
assembly {
// Compute largest power of two divisor of `x`.
x := and(x, sub(0, x))
// Use De Bruijn lookups to convert the power of 2 to log2(x).
// Reference: https://graphics.stanford.edu/~seander/bithacks.html#IntegerLogDeBruijn
r := byte(and(div(80, mod(x, 255)), 31), 0x0706050000040000010003000000000000000000020000000000000000000000)
r := add(byte(31, div(0xf8f0e8e0d8d0c8c0b8b0a8a09890888078706860585048403830282018100800, shr(r, x))), r)
}
}
/**
* @dev Count the consecutive zero bits (trailing) on the right.
*/
function leadingZeros(uint256 x) internal pure returns (uint256 r) {
return 255 - log2(x) + toUint(x == 0);
}
/**
* @dev Return the log in base 2 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log2(uint256 x) internal pure returns (uint256 r) {
unchecked {
// Round down to the closest power of 2
// Reference: https://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2
x |= x >> 1;
x |= x >> 2;
x |= x >> 4;
x |= x >> 8;
x |= x >> 16;
x |= x >> 32;
x |= x >> 64;
x |= x >> 128;
x = (x >> 1) + 1;
// Use De Bruijn lookups to convert the power of 2 to floor(log2(x)).
// Reference: https://graphics.stanford.edu/~seander/bithacks.html#IntegerLogDeBruijn
assembly {
r :=
byte(and(div(80, mod(x, 255)), 31), 0x0706050000040000010003000000000000000000020000000000000000000000)
r :=
add(byte(31, div(0xf8f0e8e0d8d0c8c0b8b0a8a09890888078706860585048403830282018100800, shr(r, x))), r)
}
}
}
/**
* @dev Aligns `x` to 32 bytes.
*/
function align32(uint256 x) internal pure returns (uint256 r) {
unchecked {
r = saturatingAdd(x, 31) & 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0;
}
}
/**
* @dev Computes `x * 2**exponent`, essentially shifting the value to the left when
* `exp` is positive, or shift to the right when `exp` is negative.
*/
function mul2pow(uint256 x, int256 exponent) internal pure returns (uint256) {
unchecked {
// Rationale:
// - When the exponent is negative, then `x << exp` is zero.
// - When the exponent is positive, then `x >> -exp` is zero.
// Then we can use the `or` operation to get the correct result.
// result = (x << exp) | (x >> -exp)
return (x << uint256(exponent)) | (x >> uint256(-exponent));
}
}
/**
* @dev Computes `x * 2**exponent`, bounds to `2 ** 256 - 1` instead overflowing.
*/
function saturatingMul2pow(uint256 x, int256 exponent) internal pure returns (uint256 result) {
unchecked {
result = mul2pow(x, exponent);
// An overflow happens when exponent is positive and (x << exp) >> exp != x.
bool success = (result >> uint256(exponent)) == (x * toUint(exponent > 0));
// Bounds to `type(uint256).max` if `success` is false.
return result | (toUint(success) - 1);
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
* Throws if result overflows a uint256 or denominator == 0.
*
* @dev This this an modified version of the original implementation by OpenZeppelin SDK, which is released under MIT.
* original: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v5.0.2/contracts/utils/math/Math.sol#L117-L202
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
unchecked {
// Compute remainder.
// - Rounding.Floor then remainder is 0
// - Rounding.Nearest then remainder is denominator / 2
// - Rounding.Ceil then remainder is denominator - 1
uint256 remainder = denominator;
remainder *= toUint(rounding != Rounding.Floor);
remainder >>= toUint(rounding == Rounding.Nearest);
remainder -= toUint(rounding == Rounding.Ceil);
// 512-bit multiply [prod1 prod0] = x * y + remainder.
// Compute the product mod 2²⁵⁶ and mod 2²⁵⁶ - 1, then use the Chinese Remainder Theorem to reconstruct
// the 512 bit result. The result is stored in two 256 variables such that product = prod1 * 2²⁵⁶ + prod0.
uint256 prod0 = x * y; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
// Only round up if the final result is less than 2²⁵⁶.
remainder := mul(remainder, lt(prod1, denominator))
// Add 256 bit remainder to 512 bit number.
// Cannot overflow once (2²⁵⁶ - 1)² + 2²⁵⁶ - 1 < 2⁵¹².
mm := add(prod0, remainder)
prod1 := add(prod1, lt(mm, prod0))
prod0 := mm
}
// Make sure the result is less than 2**256. Also prevents denominator == 0.
require(prod1 < denominator, "muldiv overflow");
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
assembly {
// Compute remainder using addmod and mulmod.
remainder := addmod(remainder, mulmod(x, y, denominator), 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.
uint256 twos = denominator & (0 - denominator);
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²⁵⁶ / 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²⁵⁶. Now that denominator is an odd number, it has an inverse modulo 2²⁵⁶ such
// that denominator * inv ≡ 1 mod 2²⁵⁶. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv ≡ 1 mod 2⁴.
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⁸
inverse *= 2 - denominator * inverse; // inverse mod 2¹⁶
inverse *= 2 - denominator * inverse; // inverse mod 2³²
inverse *= 2 - denominator * inverse; // inverse mod 2⁶⁴
inverse *= 2 - denominator * inverse; // inverse mod 2¹²⁸
inverse *= 2 - denominator * inverse; // inverse mod 2²⁵⁶
// 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²⁵⁶. Since the preconditions guarantee that the outcome is
// less than 2²⁵⁶, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
return prod0 * inverse;
}
}
}// SPDX-License-Identifier: MIT
// Analog's Contracts (last updated v0.1.0) (src/utils/Float9x56.sol)
pragma solidity >=0.8.0;
import {BranchlessMath, Rounding} from "./BranchlessMath.sol";
/**
* @dev Unsigned Float with 9-bit exponent and 56-bit significand precision (55 explicitly stored).
*
* UFloat9x56 values are described by `2**exponent * (1 + fraction)`, where the `exponent` is a signed
* integer between -255 and 255, and the `fraction` is the next 55 binary digits, which translates to
* 15~17 decimal digits. Zero and values below 2**-255 have a special encoding format.
*
* # Exponent Encoding
* The exponent is encoded using offset-binary representation, with the zero offset being 256, example:
* - 2**-255 is encoded as -255 + 256 == 1. (smallest exponent for normal numbers)
* - 2**0 is encoded as 0 + 256 == 256 (zero offset)
* - 2**6 is encoded as 6 + 256 == 262
* - 2**255 is encoded as 255 + 256 == 511 (highest exponent)
*
* # Subnormal Numbers
* The smallest possible exponent -256 have a special meaning, it represents subnormal numbers, where the
* exponent is -255 and the +1 is removed, this is useful to represent zero and values below 2**-255.
*
* Assume `e` is an 9-bit encoded exponent between 0~511:
* - When `e > 0`, the number is described by: 2**(e - 256) * (1 + fraction)
* - When `e == 0`, the number is described by: 2**-255 * fraction
*/
type UFloat9x56 is uint64;
library UFloatMath {
using BranchlessMath for uint256;
/**
* @dev Constant representing 0.0 in UFloat9x56.
*/
UFloat9x56 internal constant ZERO = UFloat9x56.wrap(0x0000000000000000);
/**
* @dev Constant representing 1.0 in UFloat9x56.
*/
UFloat9x56 internal constant ONE = UFloat9x56.wrap(0x8000000000000000);
/**
* @dev Maximum value representable in UFloat9x56, i.e., 2**200 * (2**56 - 1).
*/
UFloat9x56 internal constant MAX = UFloat9x56.wrap(0xffffffffffffffff);
/**
* @dev Default rounding mode for conversion functions.
*/
Rounding internal constant DEFAULT_ROUNDING = Rounding.Nearest;
/**
* @dev Number of bits used to represent the mantissa.
*/
uint256 internal constant MANTISSA_DIGITS = 56;
/**
* @dev Maximum value the mantissa can assume.
*/
uint256 internal constant MANTISSA_MAX = (2 ** MANTISSA_DIGITS) - 1;
/**
* @dev Minimum value the mantissa can assume, lower than this value is considered a subnormal number.
*/
uint256 internal constant MANTISSA_MIN = 1 << (MANTISSA_DIGITS - 1);
/**
* @dev The maximum value that can be represented by `UFloat9x56`.
*/
uint256 internal constant MAX_VALUE = MANTISSA_MAX << (256 - MANTISSA_DIGITS);
/**
* @dev Mask to extract the mantissa from raw `UFloat9x56`.
*/
uint256 private constant MANTISSA_MASK = MANTISSA_MAX >> 1;
/**
* @dev Bit offset used to extract the exponent from raw `UFloat9x56`.
* This value also represents the number of signficand bits explicitly stored.
*/
uint256 private constant EXPONENT_OFFSET = MANTISSA_DIGITS - 1;
/**
* @dev Position of the carry bit when converting uint256 to `UFloat9x56`.
*/
uint256 private constant CARRY_BIT = 2 ** (256 - MANTISSA_DIGITS);
/**
* @dev multiply an UFloat9x56 by an uint256 in constant gas.
*/
function mul(UFloat9x56 x, uint256 y) internal pure returns (uint256 result) {
assembly {
// Extract exponent and fraction
let exponent := shr(55, x)
let fraction := or(and(x, 0x007fffffffffffff), shl(55, gt(exponent, 0)))
exponent := sub(exponent, sub(311, iszero(exponent)))
fraction := shl(mul(exponent, sgt(exponent, 0)), fraction)
// 512-bit multiply [high low] = x * y. Compute the product mod 2²⁵⁶ and mod 2²⁵⁶ - 1
let mm := mulmod(y, fraction, not(0))
let low := mul(y, fraction)
let high := sub(sub(mm, low), lt(mm, low))
// Shift low and high if exponent >= 256
let shift := mul(sub(0, exponent), slt(exponent, 0))
mm := gt(shift, 255)
low := xor(low, mul(xor(low, high), mm))
high := mul(high, iszero(mm))
// make sure shift is between 0 and 255
shift := mod(shift, 256)
// Combine high and low
high := shl(sub(256, shift), high)
low := shr(shift, low)
result := or(high, low)
}
}
/**
* @dev Saturating multiplication, bounds to `2 ** 256 - 1` instead of overflowing.
*/
function saturatingMul(UFloat9x56 x, uint256 y) internal pure returns (uint256 result) {
assembly {
// Extract exponent and fraction
let exponent := shr(55, x)
let fraction := or(and(x, 0x007fffffffffffff), shl(55, gt(exponent, 0)))
exponent := sub(exponent, sub(311, iszero(exponent)))
fraction := shl(mul(exponent, sgt(exponent, 0)), fraction)
// 512-bit multiply [high low] = x * y. Compute the product mod 2²⁵⁶ and mod 2²⁵⁶ - 1
let mm := mulmod(y, fraction, not(0))
let low := mul(y, fraction)
let high := sub(sub(mm, low), lt(mm, low))
// Shift low and high if exponent >= 256
let shift := mul(sub(0, exponent), slt(exponent, 0))
mm := gt(shift, 255)
low := xor(low, mul(xor(low, high), mm))
high := mul(high, iszero(mm))
// make sure shift is between 0 and 255
shift := mod(shift, 256)
// Combine high and low
mm := iszero(shr(shift, high)) // detect overflow
high := shl(sub(256, shift), high)
low := shr(shift, low)
result := or(high, low)
result := or(result, sub(mm, 1)) // saturate if overflow
}
}
/**
* @dev Returns the mantissa and base 2 exponent as integers, respectively.
* The original number can be recovered by `mantissa * 2 ** exponent`.
* Returns (0, -311) if the value is zero.
*/
function decode(UFloat9x56 value) internal pure returns (uint56, int16) {
unchecked {
// Extract the exponent
int256 exponent = int256(uint256(UFloat9x56.unwrap(value)) >> EXPONENT_OFFSET);
// Extract the mantissa
uint256 mantissa = uint56(UFloat9x56.unwrap(value) & MANTISSA_MASK);
// If the value is subnormal, then the exponent is -310 and mantissa msb is not set.
bool isSubnormal = exponent == 0;
mantissa |= BranchlessMath.toUint(!isSubnormal) << EXPONENT_OFFSET;
exponent += BranchlessMath.toInt(isSubnormal && mantissa > 0);
// Exponent bias + mantissa shift
exponent -= 256 + int256(EXPONENT_OFFSET);
return (uint56(mantissa), int16(exponent));
}
}
/**
* @dev Encode the provided `mantissa` and `exponent` into `UFloat9x56`, this method assumes the
* mantissa msb is set when the number is normal, and unset when the number is subnormal.
*/
function encode(uint256 mantissa, int256 exponent) internal pure returns (UFloat9x56) {
unchecked {
// Minimum exponent is -310 when the mantissa is greater than zero.
int256 minExponent = -311 + BranchlessMath.toInt(mantissa > 0);
require(exponent >= minExponent && exponent <= 200, "UFloat9x56: exponent out of bounds");
// If the mantissa is zero, then the exponent must be -311.
exponent = BranchlessMath.ternary(mantissa == 0, -311, exponent);
// For subnormal numbers the mantissa msb is not set.
bool isSubnormal = mantissa < MANTISSA_MIN && exponent == minExponent;
require(
isSubnormal || (mantissa >= MANTISSA_MIN && mantissa <= MANTISSA_MAX), "UFloat9x56: invalid mantissa"
);
isSubnormal = isSubnormal && mantissa > 0;
// Remove mantissa most significant bit.
mantissa &= MANTISSA_MASK;
// Encode the exponent as an 9-bit unsigned integer.
exponent += 311 - BranchlessMath.toInt(isSubnormal);
// Shift the exponent to the correct position
exponent <<= EXPONENT_OFFSET;
// Encode the exponent and mantissa into `UFloat9x56`
return UFloat9x56.wrap(uint64(mantissa) | uint64(uint256(exponent)));
}
}
/**
* @dev Compare if `UFloat9x56` is equal to another integer, considering only the mantissa bits.
*/
function _integerMask(UFloat9x56 x) private pure returns (uint256, uint256) {
(uint256 mantissa, int256 exponent) = decode(x);
unchecked {
// Shift y if the exponent is negative
mantissa >>= BranchlessMath.abs(exponent) * BranchlessMath.toUint(exponent < 0);
uint256 shift = uint256(exponent) * BranchlessMath.toUint(exponent > 0);
return (type(uint256).max << shift, mantissa << shift);
}
}
/**
* @dev Compare if `UFloat9x56` is equal to another integer, considering only the mantissa bits.
*/
function eq(UFloat9x56 x, uint256 y) internal pure returns (bool r) {
(uint256 mask, uint256 integer) = _integerMask(x);
return (y & mask) == integer;
}
/**
* @dev Compare if `UFloat9x56` is equal to another integer, considering only the mantissa bits.
*/
function eq(UFloat9x56 a, UFloat9x56 b) internal pure returns (bool) {
return UFloat9x56.unwrap(a) == UFloat9x56.unwrap(b);
}
/**
* @dev Compare if `UFloat9x56` is equal to another integer, considering only the mantissa bits.
*/
function gt(UFloat9x56 a, UFloat9x56 b) internal pure returns (bool) {
return UFloat9x56.unwrap(a) > UFloat9x56.unwrap(b);
}
/**
* @dev Compare if `UFloat9x56` is equal to another integer, considering only the mantissa bits.
*/
function gt(UFloat9x56 x, uint256 y) internal pure returns (bool r) {
return truncate(x) > y;
}
/**
* @dev Compare if `UFloat9x56` is equal to another integer, considering only the mantissa bits.
*/
function ge(UFloat9x56 a, UFloat9x56 b) internal pure returns (bool) {
return UFloat9x56.unwrap(a) >= UFloat9x56.unwrap(b);
}
/**
* @dev Compare if `UFloat9x56` is equal to another integer, considering only the mantissa bits.
*/
function lt(UFloat9x56 a, UFloat9x56 b) internal pure returns (bool) {
return UFloat9x56.unwrap(a) < UFloat9x56.unwrap(b);
}
/**
* @dev Compare if `UFloat9x56` is equal to another integer, considering only the mantissa bits.
*/
function le(UFloat9x56 a, UFloat9x56 b) internal pure returns (bool) {
return UFloat9x56.unwrap(a) <= UFloat9x56.unwrap(b);
}
/**
* @dev Returns the integer part. This means that non-integer numbers are always truncated towards zero
* This function always returns the precise result.
*/
function truncate(UFloat9x56 value) internal pure returns (uint256) {
(uint256 mantissa, int256 exponent) = decode(value);
return mantissa.mul2pow(exponent);
}
/**
* @dev Converts uint256 to `UFloat9x56`, following the selected rounding direction.
* By default, it rounds to the nearest value.
*/
function fromUint(uint256 value) internal pure returns (UFloat9x56) {
return fromUint(value, DEFAULT_ROUNDING);
}
/**
* @dev Converts uint256 to `UFloat9x56`, following the selected rounding direction.
* IMPORTANT: Always round down if the value is greater than `MAX_VALUE`.
*/
function fromUint(uint256 value, Rounding rounding) internal pure returns (UFloat9x56) {
unchecked {
// Compute the exponent, if `value > 0` then the exponent cannot be less than 2**-55.
uint256 exponent = BranchlessMath.log2(value) + 256;
// Normalize mantissa by removing leading zeros, this step make sure the `CARRY_BIT`
// is always in the same position for any given value.
uint256 mantissa = value << (511 - exponent);
// Set carry bit based on selected rouding direction.
uint256 carry = CARRY_BIT;
carry *= BranchlessMath.toUint(rounding != Rounding.Floor && value < MAX_VALUE);
carry -= BranchlessMath.toUint(rounding == Rounding.Ceil && carry > 0);
carry >>= BranchlessMath.toUint(rounding == Rounding.Nearest);
carry += mantissa & (CARRY_BIT - 1);
carry = BranchlessMath.toUint(carry >= CARRY_BIT);
// Shift mantissa to a 56 bit integer then add the carry bit.
mantissa >>= 256 - MANTISSA_DIGITS;
mantissa += carry;
// Increment the exponent if mantissa overflow after adding the carry bit.
carry = BranchlessMath.toUint(mantissa > MANTISSA_MAX);
mantissa >>= carry;
exponent += carry;
// If the value is zero, then the exponent must be -311.
exponent *= BranchlessMath.toUint(value > 0);
// Encode mantissa and exponent into `UFloat9x56`
mantissa &= MANTISSA_MASK;
exponent <<= EXPONENT_OFFSET;
return UFloat9x56.wrap(uint64(mantissa) | uint64(exponent));
}
}
/**
* @dev Converts numerator / denominator to `UFloat9x56`, following the selected rounding direction.
*/
function fromRational(uint256 numerator, uint256 denominator) internal pure returns (UFloat9x56) {
return fromRational(numerator, denominator, DEFAULT_ROUNDING);
}
/**
* @dev Converts numerator / denominator to `UFloat9x56`, following the selected rounding direction.
*/
function fromRational(uint256 numerator, uint256 denominator, Rounding rounding)
internal
pure
returns (UFloat9x56)
{
unchecked {
int256 exponent;
{
// Remove leading zeros from numerator and denominator
uint256 numbits = BranchlessMath.log2(numerator);
uint256 denbits = BranchlessMath.log2(denominator);
numerator <<= 255 - numbits;
denominator <<= 255 - denbits;
// Compute exponent
exponent = int256(numbits) - int256(denbits);
}
// If `(numerator / denominator) <= 2**-255` then it is subnormal number
bool isSubnormal = numerator > 0 && exponent <= -255;
// Adjust the exponent to guarantee the mantissa most significant bit is set
uint256 shift = MANTISSA_DIGITS;
shift -= BranchlessMath.toUint(numerator >= denominator || isSubnormal);
exponent -= int256(shift);
// Compute (numerator * 2**exponent) / denominator
uint256 mantissa = BranchlessMath.mulDiv(numerator, 1 << shift, denominator, rounding);
// Adjust mantissa and exponent when it exceeds 56 bits, this is only possible when
// all mantissa bits are set and the value is rounded up, as described below.
// ```solidity
// UFloatMath.fromRational(0x2ffffffffffffff, 3, Rounding.Floor) == (2**0 * 0xffffffffffffff)
// UFloatMath.fromRational(0x2ffffffffffffff, 3, Rounding.Ceil) == (2**1 * 0x80000000000000)
// ```
shift = BranchlessMath.toUint(mantissa > MANTISSA_MAX);
mantissa >>= shift;
exponent += int256(shift);
// If the mantissa is zero, then the exponent is the minimum value.
exponent = BranchlessMath.ternary(mantissa == 0, -311, exponent);
// Adjust exponent to fit in 9 bits
exponent += 311 - int256(BranchlessMath.toUint(isSubnormal));
exponent <<= EXPONENT_OFFSET;
// Remove mantissa most significant bit
mantissa &= MANTISSA_MASK;
return UFloat9x56.wrap(uint64(mantissa) | uint64(uint256(exponent)));
}
}
/**
* @dev Convert `UFloat9x56` to a rational number, expressed as numerator / denominator.
* Obs: Values above 2**-256 are represented precisely, values below are approximated or round down to zero.
*/
function toRational(UFloat9x56 value) internal pure returns (uint256 numerator, uint256 denominator) {
unchecked {
if (UFloat9x56.unwrap(value) == 0) {
return (0, 1);
}
int256 exponent;
(numerator, exponent) = decode(value);
// Remove trailing zeros from mantissa.
{
uint256 trailingZeros = BranchlessMath.trailingZeros(numerator);
trailingZeros *= BranchlessMath.toUint(exponent < 0);
exponent += int256(trailingZeros);
numerator >>= trailingZeros;
}
if (exponent > 0) {
// The exponent is positive, cannot overflow once the maximum exponent is 200.
// Calculates: (mantissa * 2**exponent) / 1
numerator <<= uint256(exponent);
denominator = 1;
} else if (exponent > -256) {
// The exponent is negative, so we shift the denominator and keep the numerator.
// Calculates: mantissa / 2**-exponent
denominator = 1 << uint256(-exponent);
} else {
// Is not possible to represent such tiny values accurately given the denominator has more than 256 bit,
// but is still possible to get a good aproximation if we set the numerator to one:
// Calculates: 1 / (2**-exponent / mantissa)
//
// The final exponent is computed as a product of two exponents:
// 2**-exponent == 2**exp0 * 2*exp1
uint256 exp0 = 255;
uint256 exp1 = BranchlessMath.abs(exponent) - exp0;
// If numerator is less or equal to `2**exp1`, then the denominator has more than 256bit, so return zero.
if (exp1 >= numerator) {
return (0, 1);
}
// Compute full 512 bit multiplication and division as (2**exp0 * 2**exp1) / mantissa.
denominator = BranchlessMath.mulDiv(1 << exp0, 1 << exp1, numerator, Rounding.Nearest);
// Handle the case where the denominator is round towards zero.
numerator = BranchlessMath.toUint(denominator > 0);
denominator |= BranchlessMath.toUint(denominator == 0);
}
}
}
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {IEIP712} from "./IEIP712.sol";
/// @title AllowanceTransfer
/// @notice Handles ERC20 token permissions through signature based allowance setting and ERC20 token transfers by checking allowed amounts
/// @dev Requires user's token approval on the Permit2 contract
interface IAllowanceTransfer is IEIP712 {
/// @notice Thrown when an allowance on a token has expired.
/// @param deadline The timestamp at which the allowed amount is no longer valid
error AllowanceExpired(uint256 deadline);
/// @notice Thrown when an allowance on a token has been depleted.
/// @param amount The maximum amount allowed
error InsufficientAllowance(uint256 amount);
/// @notice Thrown when too many nonces are invalidated.
error ExcessiveInvalidation();
/// @notice Emits an event when the owner successfully invalidates an ordered nonce.
event NonceInvalidation(
address indexed owner, address indexed token, address indexed spender, uint48 newNonce, uint48 oldNonce
);
/// @notice Emits an event when the owner successfully sets permissions on a token for the spender.
event Approval(
address indexed owner, address indexed token, address indexed spender, uint160 amount, uint48 expiration
);
/// @notice Emits an event when the owner successfully sets permissions using a permit signature on a token for the spender.
event Permit(
address indexed owner,
address indexed token,
address indexed spender,
uint160 amount,
uint48 expiration,
uint48 nonce
);
/// @notice Emits an event when the owner sets the allowance back to 0 with the lockdown function.
event Lockdown(address indexed owner, address token, address spender);
/// @notice The permit data for a token
struct PermitDetails {
// ERC20 token address
address token;
// the maximum amount allowed to spend
uint160 amount;
// timestamp at which a spender's token allowances become invalid
uint48 expiration;
// an incrementing value indexed per owner,token,and spender for each signature
uint48 nonce;
}
/// @notice The permit message signed for a single token allowance
struct PermitSingle {
// the permit data for a single token alownce
PermitDetails details;
// address permissioned on the allowed tokens
address spender;
// deadline on the permit signature
uint256 sigDeadline;
}
/// @notice The permit message signed for multiple token allowances
struct PermitBatch {
// the permit data for multiple token allowances
PermitDetails[] details;
// address permissioned on the allowed tokens
address spender;
// deadline on the permit signature
uint256 sigDeadline;
}
/// @notice The saved permissions
/// @dev This info is saved per owner, per token, per spender and all signed over in the permit message
/// @dev Setting amount to type(uint160).max sets an unlimited approval
struct PackedAllowance {
// amount allowed
uint160 amount;
// permission expiry
uint48 expiration;
// an incrementing value indexed per owner,token,and spender for each signature
uint48 nonce;
}
/// @notice A token spender pair.
struct TokenSpenderPair {
// the token the spender is approved
address token;
// the spender address
address spender;
}
/// @notice Details for a token transfer.
struct AllowanceTransferDetails {
// the owner of the token
address from;
// the recipient of the token
address to;
// the amount of the token
uint160 amount;
// the token to be transferred
address token;
}
/// @notice A mapping from owner address to token address to spender address to PackedAllowance struct, which contains details and conditions of the approval.
/// @notice The mapping is indexed in the above order see: allowance[ownerAddress][tokenAddress][spenderAddress]
/// @dev The packed slot holds the allowed amount, expiration at which the allowed amount is no longer valid, and current nonce thats updated on any signature based approvals.
function allowance(address user, address token, address spender)
external
view
returns (uint160 amount, uint48 expiration, uint48 nonce);
/// @notice Approves the spender to use up to amount of the specified token up until the expiration
/// @param token The token to approve
/// @param spender The spender address to approve
/// @param amount The approved amount of the token
/// @param expiration The timestamp at which the approval is no longer valid
/// @dev The packed allowance also holds a nonce, which will stay unchanged in approve
/// @dev Setting amount to type(uint160).max sets an unlimited approval
function approve(address token, address spender, uint160 amount, uint48 expiration) external;
/// @notice Permit a spender to a given amount of the owners token via the owner's EIP-712 signature
/// @dev May fail if the owner's nonce was invalidated in-flight by invalidateNonce
/// @param owner The owner of the tokens being approved
/// @param permitSingle Data signed over by the owner specifying the terms of approval
/// @param signature The owner's signature over the permit data
function permit(address owner, PermitSingle memory permitSingle, bytes calldata signature) external;
/// @notice Permit a spender to the signed amounts of the owners tokens via the owner's EIP-712 signature
/// @dev May fail if the owner's nonce was invalidated in-flight by invalidateNonce
/// @param owner The owner of the tokens being approved
/// @param permitBatch Data signed over by the owner specifying the terms of approval
/// @param signature The owner's signature over the permit data
function permit(address owner, PermitBatch memory permitBatch, bytes calldata signature) external;
/// @notice Transfer approved tokens from one address to another
/// @param from The address to transfer from
/// @param to The address of the recipient
/// @param amount The amount of the token to transfer
/// @param token The token address to transfer
/// @dev Requires the from address to have approved at least the desired amount
/// of tokens to msg.sender.
function transferFrom(address from, address to, uint160 amount, address token) external;
/// @notice Transfer approved tokens in a batch
/// @param transferDetails Array of owners, recipients, amounts, and tokens for the transfers
/// @dev Requires the from addresses to have approved at least the desired amount
/// of tokens to msg.sender.
function transferFrom(AllowanceTransferDetails[] calldata transferDetails) external;
/// @notice Enables performing a "lockdown" of the sender's Permit2 identity
/// by batch revoking approvals
/// @param approvals Array of approvals to revoke.
function lockdown(TokenSpenderPair[] calldata approvals) external;
/// @notice Invalidate nonces for a given (token, spender) pair
/// @param token The token to invalidate nonces for
/// @param spender The spender to invalidate nonces for
/// @param newNonce The new nonce to set. Invalidates all nonces less than it.
/// @dev Can't invalidate more than 2**16 nonces per transaction.
function invalidateNonces(address token, address spender, uint48 newNonce) external;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IEIP712 {
function DOMAIN_SEPARATOR() external view returns (bytes32);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {ISignatureTransfer} from "./ISignatureTransfer.sol";
import {IAllowanceTransfer} from "./IAllowanceTransfer.sol";
/// @notice Permit2 handles signature-based transfers in SignatureTransfer and allowance-based transfers in AllowanceTransfer.
/// @dev Users must approve Permit2 before calling any of the transfer functions.
interface IPermit2 is ISignatureTransfer, IAllowanceTransfer {
// IPermit2 unifies the two interfaces so users have maximal flexibility with their approval.
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {IEIP712} from "./IEIP712.sol";
/// @title SignatureTransfer
/// @notice Handles ERC20 token transfers through signature based actions
/// @dev Requires user's token approval on the Permit2 contract
interface ISignatureTransfer is IEIP712 {
/// @notice Thrown when the requested amount for a transfer is larger than the permissioned amount
/// @param maxAmount The maximum amount a spender can request to transfer
error InvalidAmount(uint256 maxAmount);
/// @notice Thrown when the number of tokens permissioned to a spender does not match the number of tokens being transferred
/// @dev If the spender does not need to transfer the number of tokens permitted, the spender can request amount 0 to be transferred
error LengthMismatch();
/// @notice Emits an event when the owner successfully invalidates an unordered nonce.
event UnorderedNonceInvalidation(address indexed owner, uint256 word, uint256 mask);
/// @notice The token and amount details for a transfer signed in the permit transfer signature
struct TokenPermissions {
// ERC20 token address
address token;
// the maximum amount that can be spent
uint256 amount;
}
/// @notice The signed permit message for a single token transfer
struct PermitTransferFrom {
TokenPermissions permitted;
// a unique value for every token owner's signature to prevent signature replays
uint256 nonce;
// deadline on the permit signature
uint256 deadline;
}
/// @notice Specifies the recipient address and amount for batched transfers.
/// @dev Recipients and amounts correspond to the index of the signed token permissions array.
/// @dev Reverts if the requested amount is greater than the permitted signed amount.
struct SignatureTransferDetails {
// recipient address
address to;
// spender requested amount
uint256 requestedAmount;
}
/// @notice Used to reconstruct the signed permit message for multiple token transfers
/// @dev Do not need to pass in spender address as it is required that it is msg.sender
/// @dev Note that a user still signs over a spender address
struct PermitBatchTransferFrom {
// the tokens and corresponding amounts permitted for a transfer
TokenPermissions[] permitted;
// a unique value for every token owner's signature to prevent signature replays
uint256 nonce;
// deadline on the permit signature
uint256 deadline;
}
/// @notice A map from token owner address and a caller specified word index to a bitmap. Used to set bits in the bitmap to prevent against signature replay protection
/// @dev Uses unordered nonces so that permit messages do not need to be spent in a certain order
/// @dev The mapping is indexed first by the token owner, then by an index specified in the nonce
/// @dev It returns a uint256 bitmap
/// @dev The index, or wordPosition is capped at type(uint248).max
function nonceBitmap(address, uint256) external view returns (uint256);
/// @notice Transfers a token using a signed permit message
/// @dev Reverts if the requested amount is greater than the permitted signed amount
/// @param permit The permit data signed over by the owner
/// @param owner The owner of the tokens to transfer
/// @param transferDetails The spender's requested transfer details for the permitted token
/// @param signature The signature to verify
function permitTransferFrom(
PermitTransferFrom memory permit,
SignatureTransferDetails calldata transferDetails,
address owner,
bytes calldata signature
) external;
/// @notice Transfers a token using a signed permit message
/// @notice Includes extra data provided by the caller to verify signature over
/// @dev The witness type string must follow EIP712 ordering of nested structs and must include the TokenPermissions type definition
/// @dev Reverts if the requested amount is greater than the permitted signed amount
/// @param permit The permit data signed over by the owner
/// @param owner The owner of the tokens to transfer
/// @param transferDetails The spender's requested transfer details for the permitted token
/// @param witness Extra data to include when checking the user signature
/// @param witnessTypeString The EIP-712 type definition for remaining string stub of the typehash
/// @param signature The signature to verify
function permitWitnessTransferFrom(
PermitTransferFrom memory permit,
SignatureTransferDetails calldata transferDetails,
address owner,
bytes32 witness,
string calldata witnessTypeString,
bytes calldata signature
) external;
/// @notice Transfers multiple tokens using a signed permit message
/// @param permit The permit data signed over by the owner
/// @param owner The owner of the tokens to transfer
/// @param transferDetails Specifies the recipient and requested amount for the token transfer
/// @param signature The signature to verify
function permitTransferFrom(
PermitBatchTransferFrom memory permit,
SignatureTransferDetails[] calldata transferDetails,
address owner,
bytes calldata signature
) external;
/// @notice Transfers multiple tokens using a signed permit message
/// @dev The witness type string must follow EIP712 ordering of nested structs and must include the TokenPermissions type definition
/// @notice Includes extra data provided by the caller to verify signature over
/// @param permit The permit data signed over by the owner
/// @param owner The owner of the tokens to transfer
/// @param transferDetails Specifies the recipient and requested amount for the token transfer
/// @param witness Extra data to include when checking the user signature
/// @param witnessTypeString The EIP-712 type definition for remaining string stub of the typehash
/// @param signature The signature to verify
function permitWitnessTransferFrom(
PermitBatchTransferFrom memory permit,
SignatureTransferDetails[] calldata transferDetails,
address owner,
bytes32 witness,
string calldata witnessTypeString,
bytes calldata signature
) external;
/// @notice Invalidates the bits specified in mask for the bitmap at the word position
/// @dev The wordPos is maxed at type(uint248).max
/// @param wordPos A number to index the nonceBitmap at
/// @param mask A bitmap masked against msg.sender's current bitmap at the word position
function invalidateUnorderedNonces(uint256 wordPos, uint256 mask) external;
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.15;
import {ERC20} from 'solmate/src/tokens/ERC20.sol';
/// @title LooksRare Rewards Collector
/// @notice Implements a permissionless call to fetch LooksRare rewards earned by Universal Router users
/// and transfers them to an external rewards distributor contract
interface IRewardsCollector {
/// @notice Fetches users' LooksRare rewards and sends them to the distributor contract
/// @param looksRareClaim The data required by LooksRare to claim reward tokens
function collectRewards(bytes calldata looksRareClaim) external;
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.17;
import {IERC721Receiver} from '@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol';
import {IERC1155Receiver} from '@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol';
import {IRewardsCollector} from './IRewardsCollector.sol';
interface IUniversalRouter is IRewardsCollector, IERC721Receiver, IERC1155Receiver {
/// @notice Thrown when a required command has failed
error ExecutionFailed(uint256 commandIndex, bytes message);
/// @notice Thrown when attempting to send ETH directly to the contract
error ETHNotAccepted();
/// @notice Thrown when executing commands with an expired deadline
error TransactionDeadlinePassed();
/// @notice Thrown when attempting to execute commands and an incorrect number of inputs are provided
error LengthMismatch();
/// @notice Executes encoded commands along with provided inputs. Reverts if deadline has expired.
/// @param commands A set of concatenated commands, each 1 byte in length
/// @param inputs An array of byte strings containing abi encoded inputs for each command
/// @param deadline The deadline by which the transaction must be executed
function execute(bytes calldata commands, bytes[] calldata inputs, uint256 deadline) external payable;
}// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)
/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)
/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.
abstract contract ERC20 {
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
event Transfer(address indexed from, address indexed to, uint256 amount);
event Approval(address indexed owner, address indexed spender, uint256 amount);
/*//////////////////////////////////////////////////////////////
METADATA STORAGE
//////////////////////////////////////////////////////////////*/
string public name;
string public symbol;
uint8 public immutable decimals;
/*//////////////////////////////////////////////////////////////
ERC20 STORAGE
//////////////////////////////////////////////////////////////*/
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;
/*//////////////////////////////////////////////////////////////
EIP-2612 STORAGE
//////////////////////////////////////////////////////////////*/
uint256 internal immutable INITIAL_CHAIN_ID;
bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;
mapping(address => uint256) public nonces;
/*//////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/
constructor(
string memory _name,
string memory _symbol,
uint8 _decimals
) {
name = _name;
symbol = _symbol;
decimals = _decimals;
INITIAL_CHAIN_ID = block.chainid;
INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
}
/*//////////////////////////////////////////////////////////////
ERC20 LOGIC
//////////////////////////////////////////////////////////////*/
function approve(address spender, uint256 amount) public virtual returns (bool) {
allowance[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
return true;
}
function transfer(address to, uint256 amount) public virtual returns (bool) {
balanceOf[msg.sender] -= amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(msg.sender, to, amount);
return true;
}
function transferFrom(
address from,
address to,
uint256 amount
) public virtual returns (bool) {
uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.
if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;
balanceOf[from] -= amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(from, to, amount);
return true;
}
/*//////////////////////////////////////////////////////////////
EIP-2612 LOGIC
//////////////////////////////////////////////////////////////*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) public virtual {
require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");
// Unchecked because the only math done is incrementing
// the owner's nonce which cannot realistically overflow.
unchecked {
address recoveredAddress = ecrecover(
keccak256(
abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR(),
keccak256(
abi.encode(
keccak256(
"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
),
owner,
spender,
value,
nonces[owner]++,
deadline
)
)
)
),
v,
r,
s
);
require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");
allowance[recoveredAddress][spender] = value;
}
emit Approval(owner, spender, value);
}
function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
}
function computeDomainSeparator() internal view virtual returns (bytes32) {
return
keccak256(
abi.encode(
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
keccak256(bytes(name)),
keccak256("1"),
block.chainid,
address(this)
)
);
}
/*//////////////////////////////////////////////////////////////
INTERNAL MINT/BURN LOGIC
//////////////////////////////////////////////////////////////*/
function _mint(address to, uint256 amount) internal virtual {
totalSupply += amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(address(0), to, amount);
}
function _burn(address from, uint256 amount) internal virtual {
balanceOf[from] -= amount;
// Cannot underflow because a user's balance
// will never be larger than the total supply.
unchecked {
totalSupply -= amount;
}
emit Transfer(from, address(0), amount);
}
}{
"evmVersion": "cancun",
"optimizer": {
"enabled": true,
"runs": 200
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"_logic","type":"address"},{"internalType":"address","name":"initialOwner","type":"address"},{"internalType":"bytes","name":"_data","type":"bytes"}],"stateMutability":"payable","type":"constructor"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[{"internalType":"address","name":"admin","type":"address"}],"name":"ERC1967InvalidAdmin","type":"error"},{"inputs":[{"internalType":"address","name":"implementation","type":"address"}],"name":"ERC1967InvalidImplementation","type":"error"},{"inputs":[],"name":"ERC1967NonPayable","type":"error"},{"inputs":[],"name":"FailedCall","type":"error"},{"inputs":[],"name":"ProxyDeniedAdminAccess","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"previousAdmin","type":"address"},{"indexed":false,"internalType":"address","name":"newAdmin","type":"address"}],"name":"AdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"implementation","type":"address"}],"name":"Upgraded","type":"event"},{"stateMutability":"payable","type":"fallback"}]Contract Creation Code
60a0604052604051610dbc380380610dbc8339810160408190526100229161036a565b828161002e828261008c565b50508160405161003d9061032e565b6001600160a01b039091168152602001604051809103905ff080158015610066573d5f803e3d5ffd5b506001600160a01b031660805261008461007f60805190565b6100ea565b50505061044b565b61009582610157565b6040516001600160a01b038316907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b905f90a28051156100de576100d982826101d5565b505050565b6100e6610248565b5050565b7f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f6101295f80516020610d9c833981519152546001600160a01b031690565b604080516001600160a01b03928316815291841660208301520160405180910390a161015481610269565b50565b806001600160a01b03163b5f0361019157604051634c9c8ce360e01b81526001600160a01b03821660048201526024015b60405180910390fd5b807f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5b80546001600160a01b0319166001600160a01b039290921691909117905550565b60605f80846001600160a01b0316846040516101f19190610435565b5f60405180830381855af49150503d805f8114610229576040519150601f19603f3d011682016040523d82523d5f602084013e61022e565b606091505b50909250905061023f8583836102a6565b95945050505050565b34156102675760405163b398979f60e01b815260040160405180910390fd5b565b6001600160a01b03811661029257604051633173bdd160e11b81525f6004820152602401610188565b805f80516020610d9c8339815191526101b4565b6060826102bb576102b682610305565b6102fe565b81511580156102d257506001600160a01b0384163b155b156102fb57604051639996b31560e01b81526001600160a01b0385166004820152602401610188565b50805b9392505050565b8051156103155780518082602001fd5b60405163d6bda27560e01b815260040160405180910390fd5b6104e7806108b583390190565b80516001600160a01b0381168114610351575f80fd5b919050565b634e487b7160e01b5f52604160045260245ffd5b5f805f6060848603121561037c575f80fd5b6103858461033b565b92506103936020850161033b565b60408501519092506001600160401b03808211156103af575f80fd5b818601915086601f8301126103c2575f80fd5b8151818111156103d4576103d4610356565b604051601f8201601f19908116603f011681019083821181831017156103fc576103fc610356565b81604052828152896020848701011115610414575f80fd5b8260208601602083015e5f6020848301015280955050505050509250925092565b5f82518060208501845e5f920191825250919050565b6080516104536104625f395f601001526104535ff3fe608060405261000c61000e565b005b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316330361007a575f356001600160e01b03191663278f794360e11b14610070576040516334ad5dbb60e21b815260040160405180910390fd5b610078610082565b565b6100786100b0565b5f806100913660048184610303565b81019061009e919061033e565b915091506100ac82826100c0565b5050565b6100786100bb61011a565b610151565b6100c98261016f565b6040516001600160a01b038316907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b905f90a28051156101125761010d82826101ea565b505050565b6100ac61025c565b5f61014c7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc546001600160a01b031690565b905090565b365f80375f80365f845af43d5f803e80801561016b573d5ff35b3d5ffd5b806001600160a01b03163b5f036101a957604051634c9c8ce360e01b81526001600160a01b03821660048201526024015b60405180910390fd5b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80546001600160a01b0319166001600160a01b0392909216919091179055565b60605f80846001600160a01b0316846040516102069190610407565b5f60405180830381855af49150503d805f811461023e576040519150601f19603f3d011682016040523d82523d5f602084013e610243565b606091505b509150915061025385838361027b565b95945050505050565b34156100785760405163b398979f60e01b815260040160405180910390fd5b6060826102905761028b826102da565b6102d3565b81511580156102a757506001600160a01b0384163b155b156102d057604051639996b31560e01b81526001600160a01b03851660048201526024016101a0565b50805b9392505050565b8051156102ea5780518082602001fd5b60405163d6bda27560e01b815260040160405180910390fd5b5f8085851115610311575f80fd5b8386111561031d575f80fd5b5050820193919092039150565b634e487b7160e01b5f52604160045260245ffd5b5f806040838503121561034f575f80fd5b82356001600160a01b0381168114610365575f80fd5b9150602083013567ffffffffffffffff80821115610381575f80fd5b818501915085601f830112610394575f80fd5b8135818111156103a6576103a661032a565b604051601f8201601f19908116603f011681019083821181831017156103ce576103ce61032a565b816040528281528860208487010111156103e6575f80fd5b826020860160208301375f6020848301015280955050505050509250929050565b5f82518060208501845e5f92019182525091905056fea26469706673582212205edd4125111abfd0ad13d77470f15ad7f7a7bbf2d74488fecfc374902b4e60bb64736f6c63430008190033608060405234801561000f575f80fd5b506040516104e73803806104e783398101604081905261002e916100bb565b806001600160a01b03811661005c57604051631e4fbdf760e01b81525f600482015260240160405180910390fd5b6100658161006c565b50506100e8565b5f80546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b5f602082840312156100cb575f80fd5b81516001600160a01b03811681146100e1575f80fd5b9392505050565b6103f2806100f55f395ff3fe608060405260043610610049575f3560e01c8063715018a61461004d5780638da5cb5b146100635780639623609d1461008e578063ad3cb1cc146100a1578063f2fde38b146100de575b5f80fd5b348015610058575f80fd5b506100616100fd565b005b34801561006e575f80fd5b505f546040516001600160a01b0390911681526020015b60405180910390f35b61006161009c366004610260565b610110565b3480156100ac575f80fd5b506100d1604051806040016040528060058152602001640352e302e360dc1b81525081565b604051610085919061035d565b3480156100e9575f80fd5b506100616100f8366004610376565b61017b565b6101056101bd565b61010e5f6101e9565b565b6101186101bd565b60405163278f794360e11b81526001600160a01b03841690634f1ef2869034906101489086908690600401610391565b5f604051808303818588803b15801561015f575f80fd5b505af1158015610171573d5f803e3d5ffd5b5050505050505050565b6101836101bd565b6001600160a01b0381166101b157604051631e4fbdf760e01b81525f60048201526024015b60405180910390fd5b6101ba816101e9565b50565b5f546001600160a01b0316331461010e5760405163118cdaa760e01b81523360048201526024016101a8565b5f80546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6001600160a01b03811681146101ba575f80fd5b634e487b7160e01b5f52604160045260245ffd5b5f805f60608486031215610272575f80fd5b833561027d81610238565b9250602084013561028d81610238565b9150604084013567ffffffffffffffff808211156102a9575f80fd5b818601915086601f8301126102bc575f80fd5b8135818111156102ce576102ce61024c565b604051601f8201601f19908116603f011681019083821181831017156102f6576102f661024c565b8160405282815289602084870101111561030e575f80fd5b826020860160208301375f6020848301015280955050505050509250925092565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b602081525f61036f602083018461032f565b9392505050565b5f60208284031215610386575f80fd5b813561036f81610238565b6001600160a01b03831681526040602082018190525f906103b49083018461032f565b94935050505056fea2646970667358221220a1394a13174b53959943aa96987679477af6c7bf6e288cf992f2a480ca3b4cb864736f6c63430008190033b53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103000000000000000000000000f2b0b267858ce85c281f821ac04057d7b419256800000000000000000000000007431988c98011de073615877e44198e42b2050400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000084cf756fdf00000000000000000000000028b5a0e9c621a5badaa536219b3a228c8168cf5d00000000000000000000000081d40f21f12a8f0e3252bccb954d722d4c464b64000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000000f424000000000000000000000000000000000000000000000000000000000
Deployed Bytecode
0x608060405261000c61000e565b005b7f000000000000000000000000c9e2aa19b8a280f8ab56ee851ebd505117c5a4e46001600160a01b0316330361007a575f356001600160e01b03191663278f794360e11b14610070576040516334ad5dbb60e21b815260040160405180910390fd5b610078610082565b565b6100786100b0565b5f806100913660048184610303565b81019061009e919061033e565b915091506100ac82826100c0565b5050565b6100786100bb61011a565b610151565b6100c98261016f565b6040516001600160a01b038316907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b905f90a28051156101125761010d82826101ea565b505050565b6100ac61025c565b5f61014c7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc546001600160a01b031690565b905090565b365f80375f80365f845af43d5f803e80801561016b573d5ff35b3d5ffd5b806001600160a01b03163b5f036101a957604051634c9c8ce360e01b81526001600160a01b03821660048201526024015b60405180910390fd5b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80546001600160a01b0319166001600160a01b0392909216919091179055565b60605f80846001600160a01b0316846040516102069190610407565b5f60405180830381855af49150503d805f811461023e576040519150601f19603f3d011682016040523d82523d5f602084013e610243565b606091505b509150915061025385838361027b565b95945050505050565b34156100785760405163b398979f60e01b815260040160405180910390fd5b6060826102905761028b826102da565b6102d3565b81511580156102a757506001600160a01b0384163b155b156102d057604051639996b31560e01b81526001600160a01b03851660048201526024016101a0565b50805b9392505050565b8051156102ea5780518082602001fd5b60405163d6bda27560e01b815260040160405180910390fd5b5f8085851115610311575f80fd5b8386111561031d575f80fd5b5050820193919092039150565b634e487b7160e01b5f52604160045260245ffd5b5f806040838503121561034f575f80fd5b82356001600160a01b0381168114610365575f80fd5b9150602083013567ffffffffffffffff80821115610381575f80fd5b818501915085601f830112610394575f80fd5b8135818111156103a6576103a661032a565b604051601f8201601f19908116603f011681019083821181831017156103ce576103ce61032a565b816040528281528860208487010111156103e6575f80fd5b826020860160208301375f6020848301015280955050505050509250929050565b5f82518060208501845e5f92019182525091905056fea26469706673582212205edd4125111abfd0ad13d77470f15ad7f7a7bbf2d74488fecfc374902b4e60bb64736f6c63430008190033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000f2b0b267858ce85c281f821ac04057d7b419256800000000000000000000000007431988c98011de073615877e44198e42b2050400000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000084cf756fdf00000000000000000000000028b5a0e9c621a5badaa536219b3a228c8168cf5d00000000000000000000000081d40f21f12a8f0e3252bccb954d722d4c464b64000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000000f424000000000000000000000000000000000000000000000000000000000
-----Decoded View---------------
Arg [0] : _logic (address): 0xF2b0b267858ce85c281f821aC04057D7B4192568
Arg [1] : initialOwner (address): 0x07431988C98011de073615877e44198E42b20504
Arg [2] : _data (bytes): 0xcf756fdf00000000000000000000000028b5a0e9c621a5badaa536219b3a228c8168cf5d00000000000000000000000081d40f21f12a8f0e3252bccb954d722d4c464b64000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000000000000000000000000000000000000000f4240
-----Encoded View---------------
9 Constructor Arguments found :
Arg [0] : 000000000000000000000000f2b0b267858ce85c281f821ac04057d7b4192568
Arg [1] : 00000000000000000000000007431988c98011de073615877e44198e42b20504
Arg [2] : 0000000000000000000000000000000000000000000000000000000000000060
Arg [3] : 0000000000000000000000000000000000000000000000000000000000000084
Arg [4] : cf756fdf00000000000000000000000028b5a0e9c621a5badaa536219b3a228c
Arg [5] : 8168cf5d00000000000000000000000081d40f21f12a8f0e3252bccb954d722d
Arg [6] : 4c464b64000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce
Arg [7] : 3606eb4800000000000000000000000000000000000000000000000000000000
Arg [8] : 000f424000000000000000000000000000000000000000000000000000000000
Loading...
Loading
Loading...
Loading
Net Worth in USD
$1.00
Net Worth in ETH
0.000483
Token Allocations
USDC
100.00%
Multichain Portfolio | 33 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|---|---|---|---|---|
| ETH | 100.00% | $0.999909 | 1 | $0.9999 |
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.