ERC-20
Overview
Max Total Supply
185,201,226,334,568,637.309637 ERC20 ***
Holders
0
Transfers
-
0
Market
Price
$0.00 @ 0.000000 ETH
Onchain Market Cap
-
Circulating Supply Market Cap
-
Other Info
Token Contract (WITH 6 Decimals)
Loading...
Loading
Loading...
Loading
Loading...
Loading
Contract Source Code Verified (Exact Match)
Contract Name:
UniswapV2DynamicERC20
Compiler Version
v0.8.24+commit.e11b9ed9
Optimization Enabled:
Yes with 200 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {Ownable2Step} from "@openzeppelin/contracts/access/Ownable2Step.sol";
import {
IERC20Metadata
} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {
IUniswapV2DynamicPriceRouter
} from "../router/IUniswapV2DynamicPriceRouter.sol";
import {DynamicERC20} from "../abstract/DynamicERC20.sol";
/*
____ _ _ __ __ _ _
| _ \ _ __ ___ __| |_ _ ___| |_| \/ (_)_ __ | |_
| |_) | '__/ _ \ / _` | | | |/ __| __| |\/| | | '_ \| __|
| __/| | | (_) | (_| | |_| | (__| |_| | | | | | | | |_
|_| |_| \___/ \__,_|\__,_|\___|\__|_| |_|_|_| |_|\__|
NFT based payment system to mint products onchain with one-time payments and
recurring permissionless subscriptions.
https://productmint.io
*/
/**
* @title UniswapV2DynamicERC20
* @notice A dynamic ERC20 token that uses Uniswap V2 to get the current swap price.
* @dev A UniswapV2DynamicERC20 cannot be minted, burned, or transferred
*
* Used within the ProductMint system to act as a proxy for the base token against the quote token.
* The base token is used to charge for payment.
* The quote token is used for price targeting.
* A dynamic price router is used to get the current swap price at a dex such as Uniswap.
*
* For example, assume the base token is WETH and the quote token is USDC.
* An organization can use the DynamicERC20 to create a pricing model that targets a price of 100 USDC.
* Then, when a user purchases a product, 100 USDC worth of WETH will be transferred to the organization.
*/
contract UniswapV2DynamicERC20 is DynamicERC20, Ownable2Step {
constructor(
string memory _name,
string memory _symbol,
address _baseToken,
address _quoteToken,
address _dynamicPriceRouter,
address[] memory _baseToQuotePath,
address[] memory _quoteToBasePath
)
DynamicERC20(
_name,
_symbol,
_baseToken,
_quoteToken,
_dynamicPriceRouter
)
Ownable(_msgSender())
{
_setBaseToQuotePath(_baseToQuotePath);
_setQuoteToBasePath(_quoteToBasePath);
}
/**
* IDynamicERC20
*/
function getBaseTokenPrice() external view returns (uint256) {
return _getQuoteTokenAmount(10 ** IERC20Metadata(baseToken).decimals());
}
function balanceOfQuote(address _account) external view returns (uint256) {
return _getQuoteTokenAmount(IERC20(baseToken).balanceOf(_account));
}
function allowanceQuote(
address owner,
address spender
) external view returns (uint256) {
return
_getQuoteTokenAmount(IERC20(baseToken).allowance(owner, spender));
}
function getBaseTokenAmount(
uint256 quoteTokenAmount
) external view returns (address, uint256) {
return (baseToken, _getBaseTokenAmount(quoteTokenAmount));
}
function getQuoteTokenAmount(
uint256 baseTokenAmount
) external view returns (address, uint256) {
return (quoteToken, _getQuoteTokenAmount(baseTokenAmount));
}
function _getBaseTokenAmount(
uint256 amount
) internal view returns (uint256) {
if (amount == 0) return 0;
return
IUniswapV2DynamicPriceRouter(dynamicPriceRouter)
.getPriceFeesRemoved(amount, quoteToBasePath);
}
function _getQuoteTokenAmount(
uint256 amount
) internal view returns (uint256) {
if (amount == 0) return 0;
return
IUniswapV2DynamicPriceRouter(dynamicPriceRouter)
.getPriceFeesRemoved(amount, baseToQuotePath);
}
/**
* Base to quote path
*/
/**
* @notice Emitted when the base to quote path is set
* @param dynamicERC20 The address of the current dynamic ERC20 contract
* @param baseToken The address of the base token
* @param quoteToken The address of the quote token
* @param baseToQuotePath The path used to convert the base token to the quote token
*/
event UniswapV2BaseToQuotePathSet(
address indexed dynamicERC20,
address indexed baseToken,
address indexed quoteToken,
address[] baseToQuotePath
);
function setBaseToQuotePath(
address[] calldata _baseToQuotePath
) external onlyOwner {
_setBaseToQuotePath(_baseToQuotePath);
}
function _setBaseToQuotePath(address[] memory _baseToQuotePath) internal {
_checkBaseToQuotePath(_baseToQuotePath);
_checkPriceValid(_baseToQuotePath);
baseToQuotePath = _baseToQuotePath;
emit UniswapV2BaseToQuotePathSet(
address(this),
baseToken,
quoteToken,
_baseToQuotePath
);
}
/**
* Quote to base path
*/
/**
* @notice Emitted when the quote to base path is set
* @param dynamicERC20 The address of the current dynamic ERC20 contract
* @param quoteToBasePath The path used to convert the quote token to the base token
*/
event UniswapV2QuoteToBasePathSet(
address indexed dynamicERC20,
address indexed baseToken,
address indexed quoteToken,
address[] quoteToBasePath
);
function setQuoteToBasePath(
address[] calldata _quoteToBasePath
) external onlyOwner {
_setQuoteToBasePath(_quoteToBasePath);
}
function _setQuoteToBasePath(address[] memory _quoteToBasePath) internal {
_checkQuoteToBasePath(_quoteToBasePath);
_checkPriceValid(_quoteToBasePath);
quoteToBasePath = _quoteToBasePath;
emit UniswapV2QuoteToBasePathSet(
address(this),
baseToken,
quoteToken,
_quoteToBasePath
);
}
/**
* Path updates
*/
/**
* @dev Error when attempting to set an invalid path
*/
error InvalidPath(address[] _path);
function _checkPriceValid(address[] memory _path) internal view {
try
IUniswapV2DynamicPriceRouter(dynamicPriceRouter)
.getPriceFeesRemoved(
10 ** IERC20Metadata(_path[0]).decimals(),
_path
)
{} catch {
revert InvalidPath(_path);
}
}
/**
* Dynamic price router updates
*/
function setDynamicPriceRouter(
address _dynamicPriceRouter
) external onlyOwner {
_setDynamicPriceRouter(_dynamicPriceRouter);
}
function _setDynamicPriceRouter(
address _dynamicPriceRouter
) internal override {
require(
IERC165(_dynamicPriceRouter).supportsInterface(
type(IUniswapV2DynamicPriceRouter).interfaceId
),
"Does not implement IUniswapV2DynamicPriceRouter"
);
super._setDynamicPriceRouter(_dynamicPriceRouter);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/AccessControl.sol)
pragma solidity ^0.8.20;
import {IAccessControl} from "./IAccessControl.sol";
import {Context} from "../utils/Context.sol";
import {ERC165} from "../utils/introspection/ERC165.sol";
/**
* @dev Contract module that allows children to implement role-based access
* control mechanisms. This is a lightweight version that doesn't allow enumerating role
* members except through off-chain means by accessing the contract event logs. Some
* applications may benefit from on-chain enumerability, for those cases see
* {AccessControlEnumerable}.
*
* Roles are referred to by their `bytes32` identifier. These should be exposed
* in the external API and be unique. The best way to achieve this is by
* using `public constant` hash digests:
*
* ```solidity
* bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
* ```
*
* Roles can be used to represent a set of permissions. To restrict access to a
* function call, use {hasRole}:
*
* ```solidity
* function foo() public {
* require(hasRole(MY_ROLE, msg.sender));
* ...
* }
* ```
*
* Roles can be granted and revoked dynamically via the {grantRole} and
* {revokeRole} functions. Each role has an associated admin role, and only
* accounts that have a role's admin role can call {grantRole} and {revokeRole}.
*
* By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
* that only accounts with this role will be able to grant or revoke other
* roles. More complex role relationships can be created by using
* {_setRoleAdmin}.
*
* WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
* grant and revoke this role. Extra precautions should be taken to secure
* accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}
* to enforce additional security measures for this role.
*/
abstract contract AccessControl is Context, IAccessControl, ERC165 {
struct RoleData {
mapping(address account => bool) hasRole;
bytes32 adminRole;
}
mapping(bytes32 role => RoleData) private _roles;
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
/**
* @dev Modifier that checks that an account has a specific role. Reverts
* with an {AccessControlUnauthorizedAccount} error including the required role.
*/
modifier onlyRole(bytes32 role) {
_checkRole(role);
_;
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
}
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) public view virtual returns (bool) {
return _roles[role].hasRole[account];
}
/**
* @dev Reverts with an {AccessControlUnauthorizedAccount} error if `_msgSender()`
* is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier.
*/
function _checkRole(bytes32 role) internal view virtual {
_checkRole(role, _msgSender());
}
/**
* @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account`
* is missing `role`.
*/
function _checkRole(bytes32 role, address account) internal view virtual {
if (!hasRole(role, account)) {
revert AccessControlUnauthorizedAccount(account, role);
}
}
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) public view virtual returns (bytes32) {
return _roles[role].adminRole;
}
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*
* May emit a {RoleGranted} event.
*/
function grantRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
_grantRole(role, account);
}
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*
* May emit a {RoleRevoked} event.
*/
function revokeRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
_revokeRole(role, account);
}
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been revoked `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `callerConfirmation`.
*
* May emit a {RoleRevoked} event.
*/
function renounceRole(bytes32 role, address callerConfirmation) public virtual {
if (callerConfirmation != _msgSender()) {
revert AccessControlBadConfirmation();
}
_revokeRole(role, callerConfirmation);
}
/**
* @dev Sets `adminRole` as ``role``'s admin role.
*
* Emits a {RoleAdminChanged} event.
*/
function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
bytes32 previousAdminRole = getRoleAdmin(role);
_roles[role].adminRole = adminRole;
emit RoleAdminChanged(role, previousAdminRole, adminRole);
}
/**
* @dev Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted.
*
* Internal function without access restriction.
*
* May emit a {RoleGranted} event.
*/
function _grantRole(bytes32 role, address account) internal virtual returns (bool) {
if (!hasRole(role, account)) {
_roles[role].hasRole[account] = true;
emit RoleGranted(role, account, _msgSender());
return true;
} else {
return false;
}
}
/**
* @dev Attempts to revoke `role` to `account` and returns a boolean indicating if `role` was revoked.
*
* Internal function without access restriction.
*
* May emit a {RoleRevoked} event.
*/
function _revokeRole(bytes32 role, address account) internal virtual returns (bool) {
if (hasRole(role, account)) {
_roles[role].hasRole[account] = false;
emit RoleRevoked(role, account, _msgSender());
return true;
} else {
return false;
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (access/IAccessControl.sol)
pragma solidity ^0.8.20;
/**
* @dev External interface of AccessControl declared to support ERC-165 detection.
*/
interface IAccessControl {
/**
* @dev The `account` is missing a role.
*/
error AccessControlUnauthorizedAccount(address account, bytes32 neededRole);
/**
* @dev The caller of a function is not the expected one.
*
* NOTE: Don't confuse with {AccessControlUnauthorizedAccount}.
*/
error AccessControlBadConfirmation();
/**
* @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
*
* `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
* {RoleAdminChanged} not being emitted signaling this.
*/
event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
/**
* @dev Emitted when `account` is granted `role`.
*
* `sender` is the account that originated the contract call. This account bears the admin role (for the granted role).
* Expected in cases where the role was granted using the internal {AccessControl-_grantRole}.
*/
event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Emitted when `account` is revoked `role`.
*
* `sender` is the account that originated the contract call:
* - if using `revokeRole`, it is the admin role bearer
* - if using `renounceRole`, it is the role bearer (i.e. `account`)
*/
event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) external view returns (bool);
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {AccessControl-_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) external view returns (bytes32);
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function grantRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function revokeRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been granted `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `callerConfirmation`.
*/
function renounceRole(bytes32 role, address callerConfirmation) external;
}// 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) (access/Ownable2Step.sol)
pragma solidity ^0.8.20;
import {Ownable} from "./Ownable.sol";
/**
* @dev Contract module which provides access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* This extension of the {Ownable} contract includes a two-step mechanism to transfer
* ownership, where the new owner must call {acceptOwnership} in order to replace the
* old one. This can help prevent common mistakes, such as transfers of ownership to
* incorrect accounts, or to contracts that are unable to interact with the
* permission system.
*
* The initial owner is specified at deployment time in the constructor for `Ownable`. This
* can later be changed with {transferOwnership} and {acceptOwnership}.
*
* This module is used through inheritance. It will make available all functions
* from parent (Ownable).
*/
abstract contract Ownable2Step is Ownable {
address private _pendingOwner;
event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);
/**
* @dev Returns the address of the pending owner.
*/
function pendingOwner() public view virtual returns (address) {
return _pendingOwner;
}
/**
* @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.
* Can only be called by the current owner.
*
* Setting `newOwner` to the zero address is allowed; this can be used to cancel an initiated ownership transfer.
*/
function transferOwnership(address newOwner) public virtual override onlyOwner {
_pendingOwner = newOwner;
emit OwnershipTransferStarted(owner(), newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual override {
delete _pendingOwner;
super._transferOwnership(newOwner);
}
/**
* @dev The new owner accepts the ownership transfer.
*/
function acceptOwnership() public virtual {
address sender = _msgSender();
if (pendingOwner() != sender) {
revert OwnableUnauthorizedAccount(sender);
}
_transferOwnership(sender);
}
}// 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.1.0) (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
/**
* @dev Interface for the optional metadata functions from the ERC-20 standard.
*/
interface IERC20Metadata is IERC20 {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}// 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.1.0) (token/ERC721/IERC721.sol)
pragma solidity ^0.8.20;
import {IERC165} from "../../utils/introspection/IERC165.sol";
/**
* @dev Required interface of an ERC-721 compliant contract.
*/
interface IERC721 is IERC165 {
/**
* @dev Emitted when `tokenId` token is transferred from `from` to `to`.
*/
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
*/
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
*/
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
/**
* @dev Returns the number of tokens in ``owner``'s account.
*/
function balanceOf(address owner) external view returns (uint256 balance);
/**
* @dev Returns the owner of the `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function ownerOf(uint256 tokenId) external view returns (address owner);
/**
* @dev Safely transfers `tokenId` token from `from` to `to`.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon
* a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
/**
* @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
* are aware of the ERC-721 protocol to prevent tokens from being forever locked.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must have been allowed to move this token by either {approve} or
* {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon
* a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(address from, address to, uint256 tokenId) external;
/**
* @dev Transfers `tokenId` token from `from` to `to`.
*
* WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC-721
* or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
* understand this adds an external call which potentially creates a reentrancy vulnerability.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 tokenId) external;
/**
* @dev Gives permission to `to` to transfer `tokenId` token to another account.
* The approval is cleared when the token is transferred.
*
* Only a single account can be approved at a time, so approving the zero address clears previous approvals.
*
* Requirements:
*
* - The caller must own the token or be an approved operator.
* - `tokenId` must exist.
*
* Emits an {Approval} event.
*/
function approve(address to, uint256 tokenId) external;
/**
* @dev Approve or remove `operator` as an operator for the caller.
* Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
*
* Requirements:
*
* - The `operator` cannot be the address zero.
*
* Emits an {ApprovalForAll} event.
*/
function setApprovalForAll(address operator, bool approved) external;
/**
* @dev Returns the account approved for `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function getApproved(uint256 tokenId) external view returns (address operator);
/**
* @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
*
* See {setApprovalForAll}
*/
function isApprovedForAll(address owner, address operator) external view returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated 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/introspection/ERC165.sol)
pragma solidity ^0.8.20;
import {IERC165} from "./IERC165.sol";
/**
* @dev Implementation of the {IERC165} interface.
*
* Contracts that want to implement ERC-165 should inherit from this contract and override {supportsInterface} to check
* for the additional interface id that will be supported. For example:
*
* ```solidity
* function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
* return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
* }
* ```
*/
abstract contract ERC165 is IERC165 {
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}// 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.0.0) (utils/Pausable.sol)
pragma solidity ^0.8.20;
import {Context} from "../utils/Context.sol";
/**
* @dev Contract module which allows children to implement an emergency stop
* mechanism that can be triggered by an authorized account.
*
* This module is used through inheritance. It will make available the
* modifiers `whenNotPaused` and `whenPaused`, which can be applied to
* the functions of your contract. Note that they will not be pausable by
* simply including this module, only once the modifiers are put in place.
*/
abstract contract Pausable is Context {
bool private _paused;
/**
* @dev Emitted when the pause is triggered by `account`.
*/
event Paused(address account);
/**
* @dev Emitted when the pause is lifted by `account`.
*/
event Unpaused(address account);
/**
* @dev The operation failed because the contract is paused.
*/
error EnforcedPause();
/**
* @dev The operation failed because the contract is not paused.
*/
error ExpectedPause();
/**
* @dev Initializes the contract in unpaused state.
*/
constructor() {
_paused = false;
}
/**
* @dev Modifier to make a function callable only when the contract is not paused.
*
* Requirements:
*
* - The contract must not be paused.
*/
modifier whenNotPaused() {
_requireNotPaused();
_;
}
/**
* @dev Modifier to make a function callable only when the contract is paused.
*
* Requirements:
*
* - The contract must be paused.
*/
modifier whenPaused() {
_requirePaused();
_;
}
/**
* @dev Returns true if the contract is paused, and false otherwise.
*/
function paused() public view virtual returns (bool) {
return _paused;
}
/**
* @dev Throws if the contract is paused.
*/
function _requireNotPaused() internal view virtual {
if (paused()) {
revert EnforcedPause();
}
}
/**
* @dev Throws if the contract is not paused.
*/
function _requirePaused() internal view virtual {
if (!paused()) {
revert ExpectedPause();
}
}
/**
* @dev Triggers stopped state.
*
* Requirements:
*
* - The contract must not be paused.
*/
function _pause() internal virtual whenNotPaused {
_paused = true;
emit Paused(_msgSender());
}
/**
* @dev Returns to normal state.
*
* Requirements:
*
* - The contract must be paused.
*/
function _unpause() internal virtual whenPaused {
_paused = false;
emit Unpaused(_msgSender());
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/ReentrancyGuard.sol)
pragma solidity ^0.8.20;
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If EIP-1153 (transient storage) is available on the chain you're deploying at,
* consider using {ReentrancyGuardTransient} instead.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuard {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant NOT_ENTERED = 1;
uint256 private constant ENTERED = 2;
uint256 private _status;
/**
* @dev Unauthorized reentrant call.
*/
error ReentrancyGuardReentrantCall();
constructor() {
_status = NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
// On the first call to nonReentrant, _status will be NOT_ENTERED
if (_status == ENTERED) {
revert ReentrancyGuardReentrantCall();
}
// Any calls to nonReentrant after this point will fail
_status = ENTERED;
}
function _nonReentrantAfter() private {
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = NOT_ENTERED;
}
/**
* @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
* `nonReentrant` function in the call stack.
*/
function _reentrancyGuardEntered() internal view returns (bool) {
return _status == ENTERED;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.
pragma solidity ^0.8.20;
/**
* @dev Library for managing
* https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
* types.
*
* Sets have the following properties:
*
* - Elements are added, removed, and checked for existence in constant time
* (O(1)).
* - Elements are enumerated in O(n). No guarantees are made on the ordering.
*
* ```solidity
* contract Example {
* // Add the library methods
* using EnumerableSet for EnumerableSet.AddressSet;
*
* // Declare a set state variable
* EnumerableSet.AddressSet private mySet;
* }
* ```
*
* As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
* and `uint256` (`UintSet`) are supported.
*
* [WARNING]
* ====
* Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
* unusable.
* See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
*
* In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
* array of EnumerableSet.
* ====
*/
library EnumerableSet {
// To implement this library for multiple types with as little code
// repetition as possible, we write it in terms of a generic Set type with
// bytes32 values.
// The Set implementation uses private functions, and user-facing
// implementations (such as AddressSet) are just wrappers around the
// underlying Set.
// This means that we can only create new EnumerableSets for types that fit
// in bytes32.
struct Set {
// Storage of set values
bytes32[] _values;
// Position is the index of the value in the `values` array plus 1.
// Position 0 is used to mean a value is not in the set.
mapping(bytes32 value => uint256) _positions;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function _add(Set storage set, bytes32 value) private returns (bool) {
if (!_contains(set, value)) {
set._values.push(value);
// The value is stored at length-1, but we add 1 to all indexes
// and use 0 as a sentinel value
set._positions[value] = set._values.length;
return true;
} else {
return false;
}
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function _remove(Set storage set, bytes32 value) private returns (bool) {
// We cache the value's position to prevent multiple reads from the same storage slot
uint256 position = set._positions[value];
if (position != 0) {
// Equivalent to contains(set, value)
// To delete an element from the _values array in O(1), we swap the element to delete with the last one in
// the array, and then remove the last element (sometimes called as 'swap and pop').
// This modifies the order of the array, as noted in {at}.
uint256 valueIndex = position - 1;
uint256 lastIndex = set._values.length - 1;
if (valueIndex != lastIndex) {
bytes32 lastValue = set._values[lastIndex];
// Move the lastValue to the index where the value to delete is
set._values[valueIndex] = lastValue;
// Update the tracked position of the lastValue (that was just moved)
set._positions[lastValue] = position;
}
// Delete the slot where the moved value was stored
set._values.pop();
// Delete the tracked position for the deleted slot
delete set._positions[value];
return true;
} else {
return false;
}
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function _contains(Set storage set, bytes32 value) private view returns (bool) {
return set._positions[value] != 0;
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function _length(Set storage set) private view returns (uint256) {
return set._values.length;
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function _at(Set storage set, uint256 index) private view returns (bytes32) {
return set._values[index];
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function _values(Set storage set) private view returns (bytes32[] memory) {
return set._values;
}
// Bytes32Set
struct Bytes32Set {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _add(set._inner, value);
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _remove(set._inner, value);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
return _contains(set._inner, value);
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(Bytes32Set storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
return _at(set._inner, index);
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
bytes32[] memory store = _values(set._inner);
bytes32[] memory result;
assembly ("memory-safe") {
result := store
}
return result;
}
// AddressSet
struct AddressSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(AddressSet storage set, address value) internal returns (bool) {
return _add(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(AddressSet storage set, address value) internal returns (bool) {
return _remove(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(AddressSet storage set, address value) internal view returns (bool) {
return _contains(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(AddressSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(AddressSet storage set, uint256 index) internal view returns (address) {
return address(uint160(uint256(_at(set._inner, index))));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(AddressSet storage set) internal view returns (address[] memory) {
bytes32[] memory store = _values(set._inner);
address[] memory result;
assembly ("memory-safe") {
result := store
}
return result;
}
// UintSet
struct UintSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(UintSet storage set, uint256 value) internal returns (bool) {
return _add(set._inner, bytes32(value));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(UintSet storage set, uint256 value) internal returns (bool) {
return _remove(set._inner, bytes32(value));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(UintSet storage set, uint256 value) internal view returns (bool) {
return _contains(set._inner, bytes32(value));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(UintSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(UintSet storage set, uint256 index) internal view returns (uint256) {
return uint256(_at(set._inner, index));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(UintSet storage set) internal view returns (uint256[] memory) {
bytes32[] memory store = _values(set._inner);
uint256[] memory result;
assembly ("memory-safe") {
result := store
}
return result;
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {ERC165} from "@openzeppelin/contracts/utils/introspection/ERC165.sol";
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import {
IERC20Metadata
} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {IDynamicERC20} from "../tokens/IDynamicERC20.sol";
import {IDynamicPriceRouter} from "../router/IDynamicPriceRouter.sol";
/**
* @title DynamicERC20
* @notice DynamicERC20 is an abstract contract that implements the IDynamicERC20 interface.
* It is used to create dynamic ERC20 tokens that can be used to pay for products.
*
* This contract should be used to create Dynamic tokens using dex routers.
*/
abstract contract DynamicERC20 is
ERC165,
IERC20,
IERC20Metadata,
IDynamicERC20
{
// Name for the token
string private _name;
// Symbol for the token
string private _symbol;
// Token used for payment
address public immutable baseToken;
// Token used for price targeting
address public immutable quoteToken;
// Path used to convert the base token to the quote token
address[] internal baseToQuotePath;
// Path used to convert the quote token to the base token
address[] internal quoteToBasePath;
// Dynamic price router to interact with the dex
address public dynamicPriceRouter;
constructor(
string memory name_,
string memory symbol_,
address _baseToken,
address _quoteToken,
address _dynamicPriceRouter
) {
require(_baseToken != address(0), "Base token cannot be zero address");
require(
_quoteToken != address(0),
"Quote token cannot be zero address"
);
require(
_baseToken != _quoteToken,
"Base and quote token cannot be the same"
);
_name = name_;
_symbol = symbol_;
baseToken = _baseToken;
quoteToken = _quoteToken;
_setDynamicPriceRouter(_dynamicPriceRouter);
}
/**
* IDynamicERC20
*/
function routerName() external view virtual returns (string memory) {
return IDynamicPriceRouter(dynamicPriceRouter).ROUTER_NAME();
}
function getBaseToQuotePath()
external
view
virtual
returns (address[] memory)
{
return baseToQuotePath;
}
function getQuoteToBasePath()
external
view
virtual
returns (address[] memory)
{
return quoteToBasePath;
}
/**
* IERC20Metadata
*/
function name() external view virtual returns (string memory) {
return _name;
}
function symbol() external view virtual returns (string memory) {
return _symbol;
}
function decimals() external view virtual returns (uint8) {
return IERC20Metadata(quoteToken).decimals();
}
/**
* IERC20
*/
error TransferNotAllowed();
error ApproveNotAllowed();
function totalSupply() external view virtual returns (uint256) {
return IERC20(baseToken).totalSupply();
}
function balanceOf(
address account
) external view virtual returns (uint256) {
return IERC20(baseToken).balanceOf(account);
}
/**
* @dev Not allowed to transfer the token.
*/
function transfer(address, uint256) external pure returns (bool) {
revert TransferNotAllowed();
}
function allowance(
address owner,
address spender
) external view virtual returns (uint256) {
return IERC20(baseToken).allowance(owner, spender);
}
/**
* @dev Not allowed to approve the token.
*/
function approve(address, uint256) external pure returns (bool) {
revert ApproveNotAllowed();
}
/**
* @dev Not allowed to transfer the token.
*/
function transferFrom(
address,
address,
uint256
) external pure returns (bool) {
revert TransferNotAllowed();
}
/**
* Dynamic price router updates
*/
/**
* @notice Emitted when the dynamic price router is set
* @param dynamicERC20 The address of the current dynamic ERC20 contract
* @param dynamicPriceRouter The address of the dynamic price router
*/
event DynamicPriceRouterSet(
address indexed dynamicERC20,
address indexed dynamicPriceRouter
);
function _setDynamicPriceRouter(
address _dynamicPriceRouter
) internal virtual {
dynamicPriceRouter = _dynamicPriceRouter;
emit DynamicPriceRouterSet(address(this), _dynamicPriceRouter);
}
/**
* Checks
*/
function _checkBaseToQuotePath(address[] memory _path) internal virtual {
require(_path.length > 1, "Path must have at least 2 tokens");
require(_path[0] == baseToken, "Base token must be first in path");
require(
_path[_path.length - 1] == quoteToken,
"Quote token must be last in path"
);
}
function _checkQuoteToBasePath(address[] memory _path) internal virtual {
require(_path.length > 1, "Path must have at least 2 tokens");
require(_path[0] == quoteToken, "Quote token must be first in path");
require(
_path[_path.length - 1] == baseToken,
"Base token must be last in path"
);
}
/**
* ERC165
*/
function supportsInterface(
bytes4 interfaceId
) public view virtual override returns (bool) {
return
interfaceId == type(IDynamicERC20).interfaceId ||
interfaceId == type(IERC20).interfaceId ||
interfaceId == type(IERC20Metadata).interfaceId ||
super.supportsInterface(interfaceId);
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;
import {IERC165} from "@openzeppelin/contracts/interfaces/IERC165.sol";
import {IDynamicERC20} from "../tokens/IDynamicERC20.sol";
import {IDynamicPriceRegistry} from "../registry/IDynamicPriceRegistry.sol";
/**
* @title DynamicPriceEnabled
* @author ProductMint
* @notice Abstract contract that enables dynamic price functionality to translate purchase prices
* for dynamic tokens to base tokens and quote tokens.
*/
abstract contract DynamicPriceEnabled {
// The registry to check if a token is a DynamicERC20
IDynamicPriceRegistry public dynamicPriceRegistry;
/**
* @notice Emitted when the dynamic price registry is updated
* @param _dynamicPriceRegistry The address of the new dynamic price registry
*/
event DynamicPriceRegistryUpdated(address indexed _dynamicPriceRegistry);
constructor(address _dynamicPriceRegistry) {
_setDynamicPriceRegistry(_dynamicPriceRegistry);
}
/**
* @dev Translate the purchase price of a dynamic token to the base token
*/
function _translateBaseTokenPurchasePrice(
address token,
uint256 amount
) internal virtual returns (address, uint256) {
if (dynamicPriceRegistry.isTokenRegistered(token)) {
return IDynamicERC20(token).getBaseTokenAmount(amount);
}
return (token, amount);
}
/**
* @dev Set the dynamic price registry
*/
function _setDynamicPriceRegistry(
address _dynamicPriceRegistry
) internal virtual {
require(
IERC165(_dynamicPriceRegistry).supportsInterface(
type(IDynamicPriceRegistry).interfaceId
),
"IDynamicPriceRegistry not supported"
);
dynamicPriceRegistry = IDynamicPriceRegistry(_dynamicPriceRegistry);
emit DynamicPriceRegistryUpdated(_dynamicPriceRegistry);
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;
import {IERC165} from "@openzeppelin/contracts/interfaces/IERC165.sol";
import {IPermissionRegistry} from "../registry/IPermissionRegistry.sol";
import {PermissionUtils} from "../libs/PermissionUtils.sol";
/**
* @title PermissionChecker
* @notice Assert that a permission exists for an organization and owner using the permission registry.
*/
abstract contract PermissionChecker {
using PermissionUtils for string;
// @notice Revert if the owner does not have the permission
error PermissionNotFound(address _owner, bytes32 _id);
IPermissionRegistry public permissionRegistry;
constructor(address _registry) {
_setPermissionRegistry(_registry);
}
/**
* @dev Assert a permission
*/
/**
* @notice Assert a permission
* @dev Reverts if the owner does not have the permission or the permission is inactive
* @param _permission The permission to check
* @param _orgId The organization ID
* @param _owner The owner to check
*/
function _checkPermission(
bytes32 _permission,
uint256 _orgId,
address _owner
) internal view virtual {
if (
!permissionRegistry.hasOwnerPermission(_orgId, _owner, _permission)
) {
revert PermissionNotFound(_owner, _permission);
}
}
/**
* @notice Assert a permission by name
* @dev Reverts if the owner does not have the permission or the permission is inactive
* @param _permissionName The permission name to check
* @param _orgId The organization ID
* @param _owner The owner to check
*/
function _checkPermissionName(
string memory _permissionName,
uint256 _orgId,
address _owner
) internal view virtual {
_checkPermission(_permissionName.id(), _orgId, _owner);
}
/**
* @dev Set the permission registry
*/
/**
* @notice Set the permission registry
* @dev Reverts if the registry does not support the IPermissionRegistry interface
* @param _registry The address of the new permission registry
*/
function _setPermissionRegistry(address _registry) internal virtual {
require(
IERC165(_registry).supportsInterface(
type(IPermissionRegistry).interfaceId
),
"Invalid permission registry"
);
permissionRegistry = IPermissionRegistry(_registry);
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;
import {Context} from "@openzeppelin/contracts/utils/Context.sol";
import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import {IContractRegistry} from "../registry/IContractRegistry.sol";
import {IOrganizationAdmin} from "../admin/IOrganizationAdmin.sol";
/**
* @title RegistryEnabled
* @notice A base contract for contracts that use the central registry to interact with other contracts within the ProductMint system.
*/
abstract contract RegistryEnabled is Context {
/**
* @notice The registry contract
*/
IContractRegistry public registry;
uint256[50] private __gap;
constructor(address _registry) {
registry = IContractRegistry(_registry);
}
// Registry
modifier onlyRegistry(address expectedContract) {
_checkRegistry(expectedContract);
_;
}
function _checkRegistry(address expectedContract) internal view {
require(_msgSender() == expectedContract, "Caller not authorized");
}
// Org Admin
modifier onlyOrgAdmin(uint256 organizationId) {
_checkOrgAdmin(organizationId);
_;
}
function _isOrgAdmin(uint256 organizationId) internal view returns (bool) {
return _isOrgAdminAddress(organizationId, _msgSender());
}
function _isOrgAdminAddress(
uint256 organizationId,
address orgAdmin
) internal view returns (bool) {
return
_isOrgOwnerAddress(organizationId, orgAdmin) ||
IOrganizationAdmin(registry.orgAdmin()).isAdmin(
organizationId,
orgAdmin
);
}
function _checkOrgAdmin(uint256 organizationId) internal view {
require(
_isOrgAdmin(organizationId),
"Not an admin of the organization"
);
}
// Product Pass NFT
modifier onlyPassOwner(uint256 _productPassId) {
_checkPassOwner(_productPassId);
_;
}
function _checkPassOwner(uint256 _productPassId) internal view {
require(
_isPassOwner(_productPassId),
"Not the owner of the ProductPassNFT"
);
}
function _isPassOwner(uint256 _productPassId) internal view returns (bool) {
return _passOwner(_productPassId) == _msgSender();
}
function _passOwner(
uint256 _productPassId
) internal view returns (address) {
return IERC721(registry.productPassNFT()).ownerOf(_productPassId);
}
// Organization NFT
modifier onlyOrgOwner(uint256 organizationId) {
_checkOrgOwner(organizationId);
_;
}
function _checkOrgOwner(uint256 organizationId) internal view {
require(
_isOrgOwner(organizationId),
"Not the owner of the OrganizationNFT"
);
}
function _isOrgOwner(uint256 organizationId) internal view returns (bool) {
return _isOrgOwnerAddress(organizationId, _msgSender());
}
function _isOrgOwnerAddress(
uint256 organizationId,
address orgOwner
) internal view returns (bool) {
return _orgOwner(organizationId) == orgOwner;
}
function _orgOwner(uint256 organizationId) internal view returns (address) {
return IERC721(registry.organizationNFT()).ownerOf(organizationId);
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;
interface IOrganizationAdmin {
/**
* Organization Admins
*/
/**
* @notice Emitted when an admin is added or removed from an organization.
* @param orgId The ID of the organization.
* @param admin The address of the admin.
* @param status The status of the admin. True if added, false if removed.
*/
event OrgAdminUpdate(
uint256 indexed orgId,
address indexed admin,
bool status
);
/**
* @notice Adds an admin to the organization.
* Admins are great for delegating the responsibilities of the organization.
* @dev Only the owner of the organization can add admins.
* @param organizationId The ID of the organization.
* @param admin The address of the admin to add.
*/
function addAdmin(uint256 organizationId, address admin) external;
/**
* @notice Removes an admin from the organization.
* @param organizationId The ID of the organization.
* @param admin The address of the admin to remove.
*/
function removeAdmin(uint256 organizationId, address admin) external;
/**
* @notice Removes all admins from the organization.
* @param organizationId The ID of the organization.
*/
function removeAllAdmins(uint256 organizationId) external;
/**
* @notice Returns all admins for an organization.
* @param organizationId The ID of the organization.
* @return admins The addresses of the admins for the organization.
*/
function getAdmins(
uint256 organizationId
) external view returns (address[] memory);
/**
* @notice Checks if an address is an admin for an organization.
* @param organizationId The ID of the organization.
* @param admin The address of the admin to check.
* @return status True if the address is an admin, false otherwise.
*/
function isAdmin(
uint256 organizationId,
address admin
) external view returns (bool);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;
import {PricingUtils} from "../libs/PricingUtils.sol";
interface IPricingCalculator {
/**
* Checkout Cost
*/
/**
* @notice The parameters to calculate the checkout total cost
* @dev Uses the same validation as the purchase manager so it will revert if the parameters are not valid
* @param organizationId The id of the organization
* @param productPassOwner The address of the product pass owner
* @param productIds The ids of the products
* @param pricingIds The ids of the pricing
* @param quantities The quantities of the pricing
* @param discountIds The ids of the discounts
* @param couponId The id of the coupon
*/
struct CheckoutTotalCostParams {
uint256 organizationId;
address productPassOwner;
uint256[] productIds;
uint256[] pricingIds;
uint256[] quantities;
uint256[] discountIds;
uint256 couponId;
}
/**
* @notice The result of the checkout total cost calculation
* @param pricingIds The ids of the pricing
* @param token The token of the pricing
* @param costs The costs for each pricing id
* @param couponCost The cost after applying the coupon
* @param couponDiscount The discount percentage applied by the coupon in basis points
* @param couponSavings The savings from the coupon
* @param permanentCost The cost after applying the permanent discounts
* @param permanentDiscount The discount percentage applied by the permanent discounts in basis points
* @param permanentSavings The savings from the permanent discounts
* @param subTotalCost The sub total cost of the checkout before applying the coupon and permanent discounts
* @param checkoutTotalCost The total cost of the checkout after applying the coupon and permanent discounts
*/
struct CheckoutTotalCost {
uint256[] pricingIds;
address token;
uint256[] costs;
uint256 couponCost;
uint256 couponDiscount;
uint256 couponSavings;
uint256 permanentCost;
uint256 permanentDiscount;
uint256 permanentSavings;
uint256 subTotalCost;
uint256 checkoutTotalCost;
}
/**
* @notice Get the checkout total cost
* @param params The parameters for the checkout total cost calculation
* @return checkout The result of the checkout total cost calculation
*/
function getCheckoutTotalCost(
CheckoutTotalCostParams memory params
) external view returns (CheckoutTotalCost memory checkout);
/**
* Pricing Total Cost
*/
/**
* @notice Get the total cost for a pricing id
* @param pricingId The id of the pricing
* @param quantity The quantity of the pricing. Ignored if not tiered or usage based pricing.
* @return The total cost of the pricing
*/
function getPricingTotalCost(
uint256 pricingId,
uint256 quantity
) external view returns (uint256);
/**
* @notice Get the initial purchase cost for a list of pricing ids
* @param pricingIds The ids of the pricing
* @param quantities The quantities of the pricing. Ignored if not tiered or usage based pricing.
* @return cost The initial purchase cost of the pricing
*/
function getInitialPurchaseCost(
uint256[] memory pricingIds,
uint256[] memory quantities
) external view returns (uint256 cost);
/**
* @notice Get the total cost for a pricing model
* @param chargeStyle The charge style of the pricing
* @param tiers The tiers of the pricing
* @param flatPrice The flat price of the pricing
* @param quantity The quantity of the pricing. Ignored if not tiered or usage based pricing.
* @return The total cost of the pricing
*/
function getTotalCost(
PricingUtils.ChargeStyle chargeStyle,
PricingUtils.PricingTier[] memory tiers,
uint256 flatPrice,
uint256 quantity
) external pure returns (uint256);
/**
* @notice Get the total cost for a pricing model
* @param tiers The tiers of the pricing
* @param quantity The quantity of the pricing.
* @return The total cost of the pricing
*/
function totalVolumeCost(
PricingUtils.PricingTier[] memory tiers,
uint256 quantity
) external pure returns (uint256);
/**
* @notice Get the total cost for a pricing model
* @param tiers The tiers of the pricing
* @param quantity The quantity of the pricing.
* @return The total cost of the pricing
*/
function totalGraduatedCost(
PricingUtils.PricingTier[] memory tiers,
uint256 quantity
) external pure returns (uint256);
/**
* Change Subscription Pricing
*/
/**
* @notice Reverts if the charge style is not valid when changing subscription pricing
*/
error InvalidChargeStyle();
/**
* @notice Reverts if the charge style is not usage based when changing subscription pricing for another usage based pricing
* @dev You cannot change to a non-usage based pricing model from a usage based pricing model and vice versa
*/
error UsageBasedChargeStyleInconsistency();
/**
* @notice Reverts if the charge style is not tiered when changing subscription pricing for another tiered pricing
* @dev You cannot change to a non-tiered pricing model from a tiered pricing model and vice versa
*/
error TieredChargeStyleInconsistency();
/**
* @notice Get the new end date, token, and amount to change a subscription pricing
* @dev Reverts if the charge style or date range is not valid
* @param oldPricingId The id of the old pricing
* @param newPricingId The id of the new pricing
* @param currentStartDate The start date of the current subscription
* @param currentEndDate The end date of the current subscription
* @param quantity The quantity of the pricing
* @return newEndDate The new end date of the subscription
* @return token The token of the pricing
* @return amount The amount of the pricing
*/
function getChangeSubscriptionCost(
uint256 oldPricingId,
uint256 newPricingId,
uint256 currentStartDate,
uint256 currentEndDate,
uint256 quantity
) external view returns (uint256 newEndDate, address token, uint256 amount);
/**
* Change Tiered Subscription Unit Quantity
*/
/**
* @notice Reverts if the old quantity is the same as the new quantity
*/
error UnitQuantityIsTheSame();
/**
* @notice Get the new end date, token, and amount to change a tiered subscription unit quantity
* @dev Reverts if the charge style or date range is not valid
* @param pricingId The id of the pricing
* @param currentStartDate The start date of the current subscription
* @param currentEndDate The end date of the current subscription
* @param oldQuantity The old quantity of the pricing
* @param newQuantity The new quantity of the pricing
* @return The pricing token and amount to charge for the change in quantity
*/
function getChangeUnitQuantityCost(
uint256 pricingId,
uint256 currentStartDate,
uint256 currentEndDate,
uint256 oldQuantity,
uint256 newQuantity
) external view returns (address, uint256);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;
interface IPaymentEscrow {
/**
* Roles
*/
/**
* @notice Get the fee setter role for setting fees
* @return Fee setter role
*/
function FEE_SETTER_ROLE() external view returns (bytes32);
/**
* @notice Get the fee exempt role for setting fee exemptions for organizations
* @return Fee exempt role
*/
function FEE_EXEMPT_ROLE() external view returns (bytes32);
/**
* @notice Get the fee enabled role for setting if fees are enabled
* @return Fee enabled role
*/
function FEE_ENABLED_ROLE() external view returns (bytes32);
/**
* @notice Get the fee withdraw role for withdrawing fees required to withdraw fees
* @return Fee withdraw role
*/
function FEE_WITHDRAW_ROLE() external view returns (bytes32);
/**
* @notice Get the whitelist role for whitelisting ERC20 tokens
* @return Whitelist role
*/
function WHITELIST_ROLE() external view returns (bytes32);
/**
* @notice Get the revoke charge role for revoking organization charge ability
* @dev It is important to allow the org charging to be revoked in case control over the org is lost
* @return Revoke charge role
*/
function REVOKE_CHARGE_ROLE() external view returns (bytes32);
/**
* Payment Processing
*/
/**
* @notice Emitted when a transfer is recorded that transfers funds to an org.
* @param orgId Organization ID
* @param from Payer (User wallet)
* @param token Token address. Address(0) for native tokens.
* @param totalAmount Total amount transferred to the escrow contract
* @param orgAmount Amount transferred to the org minus fees
*/
event TransferAmount(
uint256 indexed orgId,
address indexed from,
address indexed token,
uint256 totalAmount,
uint256 orgAmount
);
/**
* @notice Transfer directly from the user wallet to the contract and set the balance for the organization
* @dev Only callable by the purchase manager
* @param orgId Organization ID
* @param from Payer (User wallet)
* @param token Token address. Address(0) for native tokens.
* @param amount Amount to transfer
*/
function transferDirect(
uint256 orgId,
address payable from,
address token,
uint256 amount
) external payable;
/**
* Token Whitelisting
*/
/**
* @notice Emitted when a token is whitelisted.
* @param token Token address
* @param isWhitelisted True if the token is whitelisted, false otherwise
*/
event WhitelistedTokenSet(address indexed token, bool isWhitelisted);
/**
* @notice Get if a token is whitelisted.
* @param token Token address
* @return True if the token is whitelisted, false otherwise
*/
function whitelistedTokens(address token) external view returns (bool);
/**
* @notice Set if a token is whitelisted.
* @dev Only whitelisted tokens can be used to purchase products
* @param token Token address
* @param isWhitelisted True if the token is whitelisted, false otherwise
*/
function setWhitelistedToken(address token, bool isWhitelisted) external;
/**
* Balance Management
*/
/**
* @notice Emitted when an organization balance is withdrawn.
* @param orgId Organization ID
* @param token Token address. Address(0) for native tokens.
* @param amount Amount withdrawn
*/
event OrgBalanceWithdrawn(
uint256 indexed orgId,
address indexed token,
uint256 amount
);
/**
* @notice Get the balance for a specific token for an organization
* @param orgId Organization ID
* @param token Token address. Address(0) for native tokens.
* @return Balance
*/
function orgBalances(
uint256 orgId,
address token
) external view returns (uint256);
/**
* @notice Get the total balance for a specific token for all organizations combined
* @param token Token address. Address(0) for native tokens.
* @return Balance
*/
function totalBalances(address token) external view returns (uint256);
/**
* @notice Withdraw an organization balance. Org balances are only available to the organization token owner.
* Organization balances can be withdrawn by the owner at any time. They can never be restricted. Your revenue is yours to keep.
* @param orgId Organization ID
* @param token Token address. Address(0) for native tokens.
* @param amount Amount to withdraw
*/
function withdrawOrgBalance(
uint256 orgId,
address token,
uint256 amount
) external;
/**
* Fee Management
*/
/**
* @notice Emitted when fees are toggled on or off.
* @param isEnabled True if fees are enabled, false otherwise
*/
event FeeEnabled(bool isEnabled);
/**
* @notice Emitted when a fee is set for a specific token.
* @param token Token address. Address(0) for native tokens.
* @param newFee Fee amount in basis points (100 = 1%, 1000 = 10%, etc.)
*/
event FeeSet(address indexed token, uint256 newFee);
/**
* @notice Emitted when the exotic fee is set.
* @param fee Fee amount in basis points (100 = 1%, 1000 = 10%, etc.)
*/
event ExoticFeeSet(uint256 fee);
/**
* @notice Emitted when a fee exemption is set for an organization.
* @param orgId Organization ID
* @param isExempt True if the organization is fee exempt, false otherwise
*/
event FeeExemptSet(uint256 indexed orgId, bool isExempt);
/**
* @notice Emitted when a fee is withdrawn.
* @param token Token address. Address(0) for native tokens.
* @param amount Amount withdrawn
*/
event FeeWithdraw(address indexed token, uint256 amount);
/**
* @notice Get the fee denominator
* @return Fee denominator
*/
function FEE_DENOMINATOR() external view returns (uint256);
/**
* @notice Check if fees are enabled
* @return True if fees are enabled, false otherwise
*/
function isFeeEnabled() external view returns (bool);
/**
* @notice Get the fee for a specific token
* @param token Token address. Address(0) for native tokens.
* @return Fee amount in basis points (100 = 1%, 1000 = 10%, etc.)
*/
function fees(address token) external view returns (uint256);
/**
* @notice Get the exotic fee. This fee is applied to any ERC20 token that does not have a specific fee set.
* @return Fee amount in basis points (100 = 1%, 1000 = 10%, etc.)
*/
function exoticFee() external view returns (uint256);
/**
* @notice Set if fees are enabled
* @param _isFeeEnabled True if fees are enabled, false otherwise
*/
function setFeeEnabled(bool _isFeeEnabled) external;
/**
* @notice Set the exotic fee.
* @param newFee Fee amount in basis points (100 = 1%, 1000 = 10%, etc.)
*/
function setExoticFee(uint256 newFee) external;
/**
* @notice Check if an organization is fee exempt
* @param orgId Organization token ID
* @return True if the organization token ID is fee exempt, false otherwise
*/
function feeExempt(uint256 orgId) external view returns (bool);
/**
* @notice Set if an organization is fee exempt
* @param orgId Organization token ID
* @param isExempt True if the organization token ID is fee exempt, false otherwise
*/
function setFeeExempt(uint256 orgId, bool isExempt) external;
/**
* @notice Calculate the fee for a specific token
* @param orgId The organization ID to calculate the fee for
* @param token Token address. Address(0) for native tokens.
* @param amount Amount to calculate the fee for
* @return Fee amount in basis points (100 = 1%, 1000 = 10%, etc.)
*/
function calculateFee(
uint256 orgId,
address token,
uint256 amount
) external view returns (uint256);
/**
* @notice Withdraw the fee for a specific token
* @param token Token address. Address(0) for native tokens.
* @return balance Total amount of fees withdrawn
*/
function withdrawFee(address token) external returns (uint256 balance);
/**
* @notice Get the fee balance for a specific token
* @param token Token address. Address(0) for native tokens.
* @return Fee balance
*/
function getFeeBalance(address token) external view returns (uint256);
/**
* Fee Reducer
*/
/**
* @notice Get the fee reducer
* @return Fee reducer contract address
*/
function feeReducer() external view returns (address);
/**
* @notice Emitted when the fee reducer contract is set
* @param feeReducer Fee reducer address
*/
event FeeReducerSet(address feeReducer);
/**
* @notice Set the fee reducer
* @dev Must implement the IFeeReducer interface
* @param _feeReducer Fee reducer address
*/
function setFeeReducer(address _feeReducer) external;
/**
* Revoking Organization Charge Ability
*/
/**
* @notice Emitted when an organization's payment charge ability is updated.
* @param orgId Organization ID
* @param isRevoked True if the organization's payment charge ability is revoked, false otherwise
*/
event OrgChargeAbilityUpdate(uint256 indexed orgId, bool isRevoked);
/**
* @notice Get if an organization's payment charge ability is revoked
* @param orgId Organization ID
* @return True if the organization's payment charge ability is revoked, false otherwise
*/
function revokedOrgs(uint256 orgId) external view returns (bool);
/**
* @notice Revoke an organization's payment charge ability
* @dev WARNING: Once an organization's payment charge ability is revoked, it cannot be restored without reaching out to the team.
* If you accidentally revoke an organization's payment charge ability, please reach out to the team to restore it.
* This will not prevent funds from being withdrawn but it will prevent your customers from being able to purchase products
* preventing them from having their wallet funds used.
* @param orgId Organization ID
*/
function revokeOrgChargeAbility(uint256 orgId) external;
/**
* @notice Restore an organization's payment charge ability
* @param orgId Organization ID
*/
function restoreOrgChargeAbility(uint256 orgId) external;
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;
import {PricingUtils} from "../libs/PricingUtils.sol";
interface ISubscriptionEscrow {
/**
* @notice Subscription struct relating to a product on a product pass.
* @param orgId Organization token ID
* @param pricingId Pricing model ID
* @param startDate Start date of the subscription
* @param endDate End date of the subscription
* @param timeRemaining Time remaining in the subscription
* @param isCancelled Whether the subscription has been cancelled
* @param isPaused Whether the subscription has been paused
*/
struct Subscription {
uint256 orgId;
uint256 pricingId;
uint256 startDate;
uint256 endDate;
uint256 timeRemaining;
bool isCancelled;
bool isPaused;
}
/**
* @notice Statuses derived from subscription state.
* @param ACTIVE Activated and product access should be granted.
* @param CANCELLED Will not renew at the end of the current period but product access should remain granted.
* @param PAST_DUE Attempted to renew at the end of the current period but failed. Product access should be revoked.
* @param PAUSED Deactivated and product access should be revoked.
*/
enum SubscriptionStatus {
ACTIVE,
CANCELLED,
PAST_DUE,
PAUSED
}
/**
* @notice Get a subscription with its status.
* @param productPassId The ID of the product pass.
* @param productId The ID of the product.
* @return subscription The subscription details.
* @return status The status of the subscription.
*/
function getSubscription(
uint256 productPassId,
uint256 productId
) external view returns (Subscription memory, SubscriptionStatus);
/**
* @notice Get multiple subscriptions with their statuses.
* @param productPassId The ID of the product pass.
* @param productIds The IDs of the products.
* @return _subs The subscription details.
* @return _statuses The statuses of the subscriptions.
*/
function getSubscriptionBatch(
uint256 productPassId,
uint256[] calldata productIds
)
external
view
returns (
Subscription[] memory _subs,
SubscriptionStatus[] memory _statuses
);
/**
* @notice Get the product IDs for a product pass that have subscriptions.
* @param productPassId The ID of the product pass.
* @return productIds The product IDs.
*/
function getPassSubs(
uint256 productPassId
) external view returns (uint256[] memory);
/**
* @notice Emitted when a subscription cycle is updated.
* @param organizationId The ID of the organization.
* @param productPassId The ID of the product pass.
* @param productId The ID of the product.
* @param status The status of the subscription.
* @param startDate The start date of the subscription.
* @param endDate The end date of the subscription.
*/
event SubscriptionCycleUpdated(
uint256 indexed organizationId,
uint256 indexed productPassId,
uint256 indexed productId,
SubscriptionStatus status,
uint256 startDate,
uint256 endDate
);
/**
* Creation
*/
/**
* @notice Reverts when a subscription already exists for the product pass and product.
* @param orgId The ID of the organization.
* @param productPassId The ID of the product pass.
* @param productId The ID of the product.
*/
error SubscriptionAlreadyExists(
uint256 orgId,
uint256 productPassId,
uint256 productId
);
/**
* @notice Reverts when the organization is not pausable when trying to create a subscription in a paused state.
*/
error OrganizationIsNotPausable();
/**
* @notice Creates a new subscription for a product pass and link it to the products.
* @param _organizationId The ID of the organization.
* @param _productPassId The ID of the product pass.
* @param _productIds The IDs of the products.
* @param _pricingIds The IDs of the pricing models.
* @param _cycleDurations The durations of the cycles.
* @param _unitQuantities The unit quantities of the products.
* @param _pause Whether the subscription is paused.
*/
function createSubscriptions(
uint256 _organizationId,
uint256 _productPassId,
uint256[] calldata _productIds,
uint256[] calldata _pricingIds,
uint256[] calldata _cycleDurations,
uint256[] calldata _unitQuantities,
bool _pause
) external;
/**
* Renewal
*/
/**
* @notice Reverts when the charge style is invalid during a renewal.
* @param chargeStyle The charge style of the pricing model.
*/
error InvalidChargeStyle(PricingUtils.ChargeStyle chargeStyle);
/**
* @notice Reverts when the unit quantity is invalid.
* @param orgId The ID of the organization.
* @param productPassId The ID of the product pass.
* @param productId The ID of the product.
*/
error InvalidUnitQuantity(
uint256 orgId,
uint256 productPassId,
uint256 productId
);
/**
* @notice Renews an existing subscription.
* @param productPassId The ID of the product pass.
* @param productId The ID of the product.
* @return orgId The ID of the organization.
* @return token The token used to pay for the renewal.
* @return price The price of the renewal.
*/
function renewSubscription(
uint256 productPassId,
uint256 productId
) external returns (uint256 orgId, address token, uint256 price);
/**
* @notice Returns the cost of renewing a specific subscription.
* @param productPassId The ID of the product pass.
* @param productId The ID of the product.
* @return orgId The ID of the organization.
* @return token The token used to pay for the renewal.
* @return price The price of the renewal.
*/
function getRenewalCost(
uint256 productPassId,
uint256 productId
) external view returns (uint256 orgId, address token, uint256 price);
/**
* @notice Returns the cost of renewing multiple subscriptions on a product pass.
* @param productPassId The ID of the product pass.
* @param productIds The IDs of the products.
* @return orgId The ID of the organization that the subscriptions belong to.
* @return tokens The tokens used to pay for the renewals.
* @return prices The prices of the renewals.
*/
function getRenewalCostBatch(
uint256 productPassId,
uint256[] calldata productIds
)
external
view
returns (
uint256 orgId,
address[] memory tokens,
uint256[] memory prices
);
/**
* Change Pricing
*/
/**
* @notice Emitted when a subscription pricing is changed.
* @param organizationId The ID of the organization.
* @param productPassId The ID of the product pass.
* @param productId The ID of the product.
* @param newPricingId The ID of the new pricing model.
*/
event SubscriptionPricingChanged(
uint256 indexed organizationId,
uint256 indexed productPassId,
uint256 indexed productId,
uint256 newPricingId
);
/**
* @notice Emitted when the owner change pricing is set.
* @dev Only the org admin can set this.
* @param organizationId The ID of the organization.
* @param canChange Whether the owner can change the pricing.
*/
event OwnerChangePricingSet(uint256 indexed organizationId, bool canChange);
/**
* @notice Changes the pricing of a subscription.
* @param productPassId The ID of the product pass.
* @param productId The ID of the product.
* @param newPricingId The ID of the new pricing model.
* @param isPassOwner Whether the pass owner is changing the pricing.
* @return token The token of the pricing model.
* @return amount The amount to charge for the pricing model change.
*/
function changeSubscriptionPricing(
uint256 productPassId,
uint256 productId,
uint256 newPricingId,
bool isPassOwner
) external returns (address token, uint256 amount);
/**
* @notice Returns whether the product pass owner can change the pricing.
* If not, then only the org admin can change the pricing.
* @param orgId The ID of the organization.
* @return canChange Whether the owner can change the pricing.
*/
function ownerChangePricing(uint256 orgId) external view returns (bool);
/**
* @notice Sets whether the product pass owner can change the pricing.
* @param orgId The ID of the organization.
* @param canChange Whether the owner can change the pricing.
*/
function setOwnerChangePricing(uint256 orgId, bool canChange) external;
/**
* Activation
*/
/**
* @notice Emitted when the subscription pausable is set.
* @param organizationId The ID of the organization.
* @param pausable Whether subscriptions can be paused.
*/
event SubscriptionPausableSet(
uint256 indexed organizationId,
bool pausable
);
/**
* @notice Reverts when the pause state is invalid. It must be updated.
*/
error InvalidPauseState();
/**
* @notice Pauses a subscription.
* @param productPassId The ID of the product pass.
* @param productId The ID of the product.
* @param pause Whether to pause the subscription.
* @return isPastDue Whether the subscription is past due.
*/
function pauseSubscription(
uint256 productPassId,
uint256 productId,
bool pause
) external returns (bool);
/**
* @notice Returns whether subscriptions can be paused.
* @param orgId The ID of the organization.
* @return pausable Whether subscriptions can be paused.
*/
function subscriptionsPauseable(uint256 orgId) external view returns (bool);
/**
* @notice Sets whether subscriptions can be paused.
* @dev Only the org admin can set this.
* @param orgId The ID of the organization.
* @param _pausable Whether subscriptions can be paused.
*/
function setSubscriptionsPausable(uint256 orgId, bool _pausable) external;
/**
* Unit Quantities
*/
/**
* @notice The unit quantity for a product pass used for tiered charge styles.
* @param quantity The quantity of units for tiered charge styles.
* @param maxQuantity The maximum quantity of units that have been purchased for the current cycle. Resets at the end of each cycle.
*/
struct UnitQuantity {
uint256 orgId;
uint256 quantity;
uint256 maxQuantity;
}
/**
* Emitted when a unit quantity is set for a product pass.
* @param productPassId The ID of the product pass.
* @param productId The ID of the product.
* @param quantity The quantity of the unit.
* @param maxQuantity The maximum quantity of units that have been purchased for the current cycle. Resets at the end of each cycle.
*/
event UnitQuantitySet(
uint256 indexed productPassId,
uint256 indexed productId,
uint256 quantity,
uint256 maxQuantity
);
/**
* @notice Returns the current quantity for a product pass.
* @param productPassId The ID of the product pass.
* @param productId The ID of the product.
* @return quantity The quantity of the unit.
*/
function getUnitQuantity(
uint256 productPassId,
uint256 productId
) external view returns (uint256);
/**
* @notice Returns the full unit quantity for a product pass.
* @param productPassId The ID of the product pass.
* @param productId The ID of the product.
* @return unitQuantity The full unit quantity struct.
*/
function getUnitQuantityFull(
uint256 productPassId,
uint256 productId
) external view returns (UnitQuantity memory);
/**
* @notice Changes the unit quantity for a product pass.
* @dev Only the purchase manager can call this.
* @param productPassId The ID of the product pass.
* @param productId The ID of the product.
* @param quantity The quantity of the unit.
* @return orgId The ID of the organization which the subscription belongs to.
* @return token The token of the pricing model.
* @return amount The amount to charge for the unit quantity change.
*/
function changeSubscriptionUnitQuantity(
uint256 productPassId,
uint256 productId,
uint256 quantity
) external returns (uint256 orgId, address token, uint256 amount);
/**
* Cancellation
*/
/**
* @notice Cancels a subscription.
* @param productPassId The ID of the product pass.
* @param productId The ID of the product.
* @param cancel Whether to cancel the subscription.
* @return isPastDue True if the subscription is past due, else false.
*/
function cancelSubscription(
uint256 productPassId,
uint256 productId,
bool cancel
) external returns (bool);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;
/**
* @title PermissionUtils
* @notice Utility library for working with permissions
*/
library PermissionUtils {
/**
* @notice Generate a permission ID from a name
* @param _name The name of the permission
* @return The ID of the permission
*/
function id(string memory _name) internal pure returns (bytes32) {
return keccak256(abi.encode(_name));
}
/**
* @dev Initial default pass permissions for each organization and pass owner
* that were originally deployed with the permissions system.
*/
/**
* @notice Can the org spend tokens from the owner wallet
*/
string constant PASS_WALLET_SPEND = "pass.wallet.spend";
/**
* @notice Purchase additional products for an existing product pass
*/
string constant PASS_PURCHASE_ADDITIONAL = "pass.purchase.additional";
/**
* @notice Renew an existing expired subscription
*/
string constant PASS_SUBSCRIPTION_RENEWAL = "pass.subscription.renewal";
/**
* @notice Change the pricing model for an existing subscription
*/
string constant PASS_SUBSCRIPTION_PRICING = "pass.subscription.pricing";
/**
* @notice Change the unit quantity for an existing TIERED subscription
*/
string constant PASS_SUBSCRIPTION_QUANTITY = "pass.subscription.quantity";
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;
/**
* @notice Utility library for managing pricing configurations.
*/
library PricingUtils {
/**
* @notice A pricing configuration option for a product.
* @param orgId The organization ID that the pricing configuration belongs to.
* @param chargeStyle How the product should be charged.
* @param chargeFrequency How often the subscription is charged.
* @param tiers The tiers of pricing used in TIER and USAGE_BASED models.
* @param token The ERC20 token that is used for all tiers. address(0) means use the native token of the chain.
* @param flatPrice The price of the product if the pricing model is FLAT_RATE or chargeStyle is ONE_TIME.
* @param usageMeterId The usage meter ID that is used to record the usage of the product. 0 if not used.
* @param isActive If true, then the pricing configuration can be used in new purchases, else it is not available for purchase.
* @param isRestricted If true, then the pricing configuration is restricted to wallets that have been granted restricted access.
*/
struct Pricing {
uint256 orgId;
ChargeStyle chargeStyle;
ChargeFrequency chargeFrequency;
PricingTier[] tiers;
address token;
uint256 flatPrice;
uint256 usageMeterId;
bool isActive;
bool isRestricted;
}
/**
* @notice Returns true if the pricing is usage based.
* @param pricing The pricing configuration.
* @return true if the pricing is usage based, false otherwise.
*/
function isUsage(Pricing memory pricing) internal pure returns (bool) {
return
pricing.chargeStyle == ChargeStyle.USAGE_BASED_VOLUME ||
pricing.chargeStyle == ChargeStyle.USAGE_BASED_GRADUATED;
}
/**
* @notice Returns true if the pricing is tiered.
* @param pricing The pricing configuration.
* @return true if the pricing is tiered, false otherwise.
*/
function isTiered(Pricing memory pricing) internal pure returns (bool) {
return
pricing.chargeStyle == ChargeStyle.TIERED_VOLUME ||
pricing.chargeStyle == ChargeStyle.TIERED_GRADUATED;
}
/**
* @notice ChargeStyle is how the product should be charged.
* @param ONE_TIME The product is charged once for a one-time purchase.
* @param FLAT_RATE The price for a recurring subscription.
* @param TIERED_VOLUME The price is different for different quantity of units.
* Final tier reached is used for all units. Billed at the start of the period.
* @param TIERED_GRADUATED The price is different for different quantity of units.
* Tiers apply progressively. Billed at the start of the period.
* @param USAGE_BASED_VOLUME The price is based on the final usage recorded in the billing period.
* Final tier reached is used. Billed at the end of the period.
* @param USAGE_BASED_GRADUATED The price is based on the usage recorded in the billing period.
* Tiers apply progressively. Billed at the end of the period.
*/
enum ChargeStyle {
ONE_TIME,
FLAT_RATE,
TIERED_VOLUME,
TIERED_GRADUATED,
USAGE_BASED_VOLUME,
USAGE_BASED_GRADUATED
}
/**
* Subscription based products
*/
/**
* @notice ChargeFrequency is how often the subscription is charged.
* @param DAILY Every 1 day
* @param WEEKLY Every 7 days
* @param MONTHLY Every 30 days
* @param QUARTERLY Every 90 days
* @param YEARLY Every 365 days
*/
enum ChargeFrequency {
DAILY,
WEEKLY,
MONTHLY,
QUARTERLY,
YEARLY
}
/**
* @notice PricingTier is a tier of pricing.
* @param lowerBound The lower number of units for the tier.
* @param upperBound The upper number of units for the tier.
* @param pricePerUnit The price per unit.
* @param priceFlatRate The flat rate that is added to the price.
*/
struct PricingTier {
uint256 lowerBound;
uint256 upperBound;
uint256 pricePerUnit;
uint256 priceFlatRate;
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;
interface IPurchaseManager {
/**
* @notice Get the total number of product pass tokens minted.
* @return The total number of product pass tokens minted.
*/
function passSupply() external view returns (uint256);
/**
* @notice Emitted when the funds are transferred from the user to the payment escrow.
* During the initial purchase, additional purchases, changing/renewing subscription pricing,
* the amount paid is recorded.
* @param orgId The ID of the organization that the products are purchased for.
* @param passOwner The address of the owner of the product pass that paid for the products.
* @param purchaser The address of the purchaser of the products.
* @param token The token used for the purchase.
* @param amountPaid The total amount paid for the products including any discounts from coupons.
*/
event PerformPurchase(
uint256 indexed orgId,
address indexed passOwner,
address indexed purchaser,
address token,
uint256 amountPaid
);
/**
* @notice Revert when the provided coupon code is invalid.
*/
error InvalidCouponCode();
/**
* @notice Revert when no products are provided for a purchase or renewal during batch operations.
*/
error NoProductsProvided();
/**
* @notice Revert when the number of product IDs and statuses do not match during batch operations.
*/
error ProductIdsAndStatusesLengthMismatch();
/**
* @notice Revert when the caller is not authorized to perform the action.
*/
error NotAuthorized();
/**
* Purchase Products
*/
/**
* @notice Emitted when products are purchased for a product pass.
* @param orgId The ID of the organization that the products are purchased for.
* @param productPassId The ID of the product pass that the products are purchased for.
* @param passOwner The address of the owner of the product pass.
* @param productIds The IDs of the products that are purchased.
* @param pricingIds The IDs of the pricing options for the products.
* @param quantities The quantities of the products that are purchased.
* @param token The token that is used to purchase the products.
* @param amountPaid The total amount paid for the products before any discounts.
*/
event ProductsPurchased(
uint256 indexed orgId,
uint256 indexed productPassId,
address indexed passOwner,
uint256[] productIds,
uint256[] pricingIds,
uint256[] quantities,
address token,
uint256 amountPaid
);
/**
* @notice The parameters for an initial purchase.
*
* @param to The address of the user to purchase the products for and mint the product pass to.
* @param organizationId The ID of the organization to purchase the products for.
* @param productIds The IDs of the products to purchase.
* @param pricingIds The IDs of the pricing options for the products.
* @param quantities The quantities of the products to purchase.
* Only relevant for products for tiered pricing. 0 must be provided for all other pricing models.
* @param discountIds The IDs of the discounts to be minted onto the product pass.
* @param couponCode The coupon code to apply to the purchase.
* @param airdrop Whether to airdrop the products to the user.
* Can only be called by the org admin.
* @param pause Whether to pause any subscriptions that are purchased during the purchase.
* The org must have this feature enabled to pause subscriptions.
*/
struct InitialPurchaseParams {
address to;
uint256 organizationId;
uint256[] productIds;
uint256[] pricingIds;
uint256[] quantities;
uint256[] discountIds;
string couponCode;
bool airdrop;
bool pause;
}
/**
* @notice Purchase products by minting a new product pass.
* @param params The parameters for the purchase.
*/
function purchaseProducts(
InitialPurchaseParams calldata params
) external payable;
/**
* @notice The parameters needed for an additional purchase to add products to an existing product pass.
*
* @param productPassId The ID of the product pass to add the products to.
* @param productIds The IDs of the products to purchase.
* @param pricingIds The IDs of the pricing options for the products.
* @param quantities The quantities of the products to purchase.
* Only relevant for products for tiered pricing. 0 must be provided for all other pricing models.
* @param couponCode The coupon code to apply to the purchase.
* @param airdrop Whether to airdrop the products to the user.
* Can only be called by the pass owner.
* @param pause Whether to pause any subscriptions that are purchased during the purchase.
* The org must have this feature enabled to pause subscriptions.
*/
struct AdditionalPurchaseParams {
uint256 productPassId;
uint256[] productIds;
uint256[] pricingIds;
uint256[] quantities;
string couponCode;
bool airdrop;
bool pause;
}
/**
* @notice Purchase additional products by adding them to an existing product pass.
* @param params The parameters for the purchase.
*/
function purchaseAdditionalProducts(
AdditionalPurchaseParams calldata params
) external payable;
/**
* @dev Used internally by the PurchaseManager during the product purchase.
*/
struct PurchaseProductsParams {
address passOwner;
address purchaser;
uint256 orgId;
uint256 productPassId;
uint256[] productIds;
uint256[] pricingIds;
uint256[] quantities;
string couponCode;
bool airdrop;
bool pause;
bool isInitialPurchase;
}
/**
* @dev Used internally by the PurchaseManager during the product purchase.
*/
struct PerformPurchaseParams {
uint256 orgId;
uint256 productPassId;
address passOwner;
address purchaser;
uint256 totalAmount;
address token;
bool airdrop;
bool isInitialPurchase;
bool forceCoupon;
}
/**
* Change subscription pricing
*/
/**
* @notice The parameters for changing the pricing for a subscription.
*
* @custom:field orgId The ID of the organization that the subscription is purchased for.
* @custom:field productPassId The ID of the product pass that the subscription is purchased for.
* @custom:field productId The ID of the product to change the pricing for.
* @custom:field newPricingId The ID of the new pricing option for the product.
* @custom:field airdrop Whether to airdrop the change to the user.
*/
struct ChangeSubscriptionPricingParams {
uint256 orgId;
uint256 productPassId;
uint256 productId;
uint256 newPricingId;
bool airdrop;
}
/**
* @notice Change the pricing for an existing subscription.
* Can only change between pricing models that are the same charge style i.e. FLAT_RATE, TIERED, or USAGE
* @dev Only the pass owner or an org admin can change the pricing model for a subscription.
* @param params The parameters for the change.
*/
function changeSubscriptionPricing(
ChangeSubscriptionPricingParams calldata params
) external;
/**
* Renew subscription
*/
/**
* @notice Renew a subscription for a product.
* @param productPassId The ID of the product pass to renew.
* @param productId The ID of the product to renew.
* @param airdrop Whether to airdrop the renewed subscription to the user. Can only be used by an org admin.
*/
function renewSubscription(
uint256 productPassId,
uint256 productId,
bool airdrop
) external;
/**
* @notice Batch renew multiple subscriptions on a product pass in a single transaction.
* @param productPassId The ID of the product pass to renew.
* @param productIds The IDs of the products to renew.
* @param airdrop Whether to airdrop the products to the user. Can only be used by an org admin.
*/
function renewSubscriptionBatch(
uint256 productPassId,
uint256[] calldata productIds,
bool airdrop
) external;
/**
* Tiered Subscription Unit Quantity
*/
/**
* @notice Change the unit quantity for a tiered subscription.
* @dev Subscription must be active to change the unit quantity and a TIERED pricing model must be set.
* @param productPassId The ID of the product pass that the subscription is purchased for.
* @param productId The ID of the product to change the unit quantity for.
* @param quantity The new unit quantity for the subscription.
* @param airdrop Whether to airdrop the change to the user. Can only be used by an org admin.
*/
function changeTieredSubscriptionUnitQuantity(
uint256 productPassId,
uint256 productId,
uint256 quantity,
bool airdrop
) external;
/**
* Pause subscription
*/
/**
* @notice Pause a subscription for a product.
* @dev The org must have this feature enabled to pause subscriptions.
* @param productPassId The ID of the product pass that the subscription is purchased for.
* @param productId The ID of the product to pause the subscription for.
* @param _pause Whether to pause the subscription.
*/
function pauseSubscription(
uint256 productPassId,
uint256 productId,
bool _pause
) external;
/**
* @notice Batch pause multiple subscriptions on a product pass in a single transaction.
* @param productPassId The ID of the product pass that the subscriptions are purchased for.
* @param productIds The IDs of the products to pause the subscriptions for.
* @param pause Whether to pause the subscriptions.
*/
function pauseSubscriptionBatch(
uint256 productPassId,
uint256[] calldata productIds,
bool[] calldata pause
) external;
/**
* Cancel subscription
*/
/**
* @notice Cancel a subscription for a product.
* When a subscription is cancelled it cannot be renewed.
* The subscription will automatically be renewed when un-cancelled.
* @param productPassId The ID of the product pass that the subscription is purchased for.
* @param productId The ID of the product to cancel the subscription for.
* @param cancel Whether to cancel the subscription.
*/
function cancelSubscription(
uint256 productPassId,
uint256 productId,
bool cancel
) external;
/**
* @notice Batch cancel multiple subscriptions on a product pass in a single transaction.
* @param productPassId The ID of the product pass that the subscriptions are purchased for.
* @param productIds The IDs of the products to cancel the subscriptions for.
* @param cancel Whether to cancel the subscriptions.
*/
function cancelSubscriptionBatch(
uint256 productPassId,
uint256[] calldata productIds,
bool[] calldata cancel
) external;
/**
* Purchase Pause
*/
/**
* @notice Pause all activity within the purchase manager.
* @dev This will prevent any purchases, renewals, cancellations, or pausing of subscriptions.
* Can be used by the owner when doing upgrades or maintenance.
*/
function pausePurchases() external;
/**
* @notice Unpause activity within the purchase manager.
* @dev This will resume any paused activity within the purchase manager.
*/
function unpausePurchases() external;
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;
import {RegistryEnabled} from "../abstract/RegistryEnabled.sol";
import {
ReentrancyGuard
} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import {Pausable} from "@openzeppelin/contracts/utils/Pausable.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {Ownable2Step} from "@openzeppelin/contracts/access/Ownable2Step.sol";
import {IPurchaseManager} from "./IPurchaseManager.sol";
import {IProductPassNFT} from "../tokens/IProductPassNFT.sol";
import {IProductRegistry} from "../registry/IProductRegistry.sol";
import {IPricingRegistry} from "../registry/IPricingRegistry.sol";
import {IPaymentEscrow} from "../escrow/IPaymentEscrow.sol";
import {ISubscriptionEscrow} from "../escrow/ISubscriptionEscrow.sol";
import {IPurchaseRegistry} from "../registry/IPurchaseRegistry.sol";
import {IPricingCalculator} from "../calculator/IPricingCalculator.sol";
import {ICouponRegistry} from "../registry/ICouponRegistry.sol";
import {IDiscountRegistry} from "../registry/IDiscountRegistry.sol";
import {PermissionChecker} from "../abstract/PermissionChecker.sol";
import {PermissionUtils} from "../libs/PermissionUtils.sol";
import {DynamicPriceEnabled} from "../abstract/DynamicPriceEnabled.sol";
/*
____ _ _ __ __ _ _
| _ \ _ __ ___ __| |_ _ ___| |_| \/ (_)_ __ | |_
| |_) | '__/ _ \ / _` | | | |/ __| __| |\/| | | '_ \| __|
| __/| | | (_) | (_| | |_| | (__| |_| | | | | | | | |_
|_| |_| \___/ \__,_|\__,_|\___|\__|_| |_|_|_| |_|\__|
NFT based payment system to mint products onchain with one-time payments and
recurring permissionless subscriptions.
https://productmint.io
*/
/**
* @title PurchaseManager
* @notice The PurchaseManager is responsible for purchasing products and managing subscriptions for a user.
* When a product is purchased, a Product Pass NFT is minted to the user.
*
* The PurchaseManager is also responsible for renewing subscriptions, pausing and cancelling subscriptions, and
* changing the pricing model for an existing subscription.
*
* Upon minting a product pass, the user is granted initial owner permissions to the organization enabling the
* organization to charge renewals, change pricing, and pause and cancel subscriptions.
*/
contract PurchaseManager is
RegistryEnabled,
DynamicPriceEnabled,
ReentrancyGuard,
Pausable,
IERC165,
IPurchaseManager,
PermissionChecker,
Ownable2Step
{
// Total number of product pass tokens minted
uint256 public passSupply;
constructor(
address _contractRegistry,
address _permissionRegistry,
address _oldPurchaseManager,
address _dynamicPriceRegistry
)
Ownable(_msgSender())
RegistryEnabled(_contractRegistry)
ReentrancyGuard()
Pausable()
PermissionChecker(_permissionRegistry)
DynamicPriceEnabled(_dynamicPriceRegistry)
{
if (_oldPurchaseManager != address(0)) {
passSupply = IPurchaseManager(_oldPurchaseManager).passSupply();
}
}
/**
* Purchase Products
*/
function purchaseProducts(
InitialPurchaseParams calldata params
) external payable nonReentrant whenNotPaused {
passSupply++;
address purchaser = _msgSender();
permissionRegistry.grantInitialOwnerPermissions(
params.organizationId,
purchaser
);
if (params.discountIds.length > 0) {
IDiscountRegistry(registry.discountRegistry()).mintDiscountsToPass(
params.organizationId,
passSupply,
purchaser,
params.discountIds
);
}
_purchaseProducts(
PurchaseProductsParams({
passOwner: params.to,
purchaser: purchaser,
orgId: params.organizationId,
productPassId: passSupply,
productIds: params.productIds,
pricingIds: params.pricingIds,
quantities: params.quantities,
couponCode: params.couponCode,
airdrop: params.airdrop,
pause: params.pause ||
(purchaser != params.to &&
!_isOrgAdmin(params.organizationId)),
isInitialPurchase: true
})
);
IProductPassNFT(registry.productPassNFT()).mint(params.to, passSupply);
}
function purchaseAdditionalProducts(
AdditionalPurchaseParams calldata params
) external payable onlyPassOwnerOrAdmin(params.productPassId) nonReentrant {
address passOwner = _passOwner(params.productPassId);
uint256 orgId = IPurchaseRegistry(registry.purchaseRegistry())
.passOrganization(params.productPassId);
_checkPermissionName(
PermissionUtils.PASS_PURCHASE_ADDITIONAL,
orgId,
passOwner
);
_purchaseProducts(
PurchaseProductsParams({
passOwner: passOwner,
purchaser: passOwner,
orgId: orgId,
productPassId: params.productPassId,
productIds: params.productIds,
pricingIds: params.pricingIds,
quantities: params.quantities,
couponCode: params.couponCode,
airdrop: params.airdrop,
pause: params.pause,
isInitialPurchase: false
})
);
}
function _purchaseProducts(PurchaseProductsParams memory params) internal {
IProductRegistry(registry.productRegistry()).canPurchaseProducts(
params.orgId,
params.productIds,
params.pricingIds
);
(address token, uint256[] memory cycleDurations) = IPricingRegistry(
registry.pricingRegistry()
).validateCheckoutBatch(
params.orgId,
params.passOwner,
params.pricingIds,
params.quantities
);
if (bytes(params.couponCode).length > 0) {
_setCoupon(params.orgId, params.purchaser, params.couponCode);
}
IPurchaseRegistry(registry.purchaseRegistry()).recordProductPurchase(
params.orgId,
params.productPassId,
params.passOwner,
params.purchaser,
params.productIds,
params.pricingIds
);
ISubscriptionEscrow(registry.subscriptionEscrow()).createSubscriptions(
params.orgId,
params.productPassId,
params.productIds,
params.pricingIds,
cycleDurations,
params.quantities,
params.pause
);
uint256 totalAmount = IPricingCalculator(registry.pricingCalculator())
.getInitialPurchaseCost(params.pricingIds, params.quantities);
(token, totalAmount) = _translateBaseTokenPurchasePrice(
token,
totalAmount
);
if (totalAmount > 0) {
_performPurchase(
PerformPurchaseParams({
orgId: params.orgId,
productPassId: params.productPassId,
purchaser: params.purchaser,
passOwner: params.passOwner,
totalAmount: totalAmount,
token: token,
airdrop: params.airdrop,
isInitialPurchase: params.isInitialPurchase,
forceCoupon: true
})
);
}
emit ProductsPurchased(
params.orgId,
params.productPassId,
params.passOwner,
params.productIds,
params.pricingIds,
params.quantities,
token,
totalAmount
);
}
/**
* Change subscription pricing
*/
function changeSubscriptionPricing(
ChangeSubscriptionPricingParams calldata params
) external onlyPassOwnerOrAdmin(params.productPassId) nonReentrant {
IProductRegistry(registry.productRegistry()).canPurchaseProduct(
params.orgId,
params.productId,
params.newPricingId
);
address passOwner = _passOwner(params.productPassId);
_checkPermissionName(
PermissionUtils.PASS_SUBSCRIPTION_PRICING,
params.orgId,
passOwner
);
IPricingRegistry(registry.pricingRegistry()).validateCheckout(
params.orgId,
passOwner,
params.newPricingId,
ISubscriptionEscrow(registry.subscriptionEscrow()).getUnitQuantity(
params.productPassId,
params.productId
)
);
(address token, uint256 amount) = ISubscriptionEscrow(
registry.subscriptionEscrow()
).changeSubscriptionPricing(
params.productPassId,
params.productId,
params.newPricingId,
_isPassOwner(params.productPassId)
);
if (amount > 0) {
(token, amount) = _translateBaseTokenPurchasePrice(token, amount);
_performPurchase(
PerformPurchaseParams({
orgId: params.orgId,
productPassId: params.productPassId,
passOwner: passOwner,
purchaser: passOwner,
totalAmount: amount,
token: token,
airdrop: params.airdrop,
isInitialPurchase: false,
forceCoupon: false
})
);
}
}
/**
* Renew subscription
*/
function renewSubscription(
uint256 productPassId,
uint256 productId,
bool airdrop
) external nonReentrant whenNotPaused {
_renewSubscription(productPassId, productId, airdrop);
}
function renewSubscriptionBatch(
uint256 productPassId,
uint256[] calldata productIds,
bool airdrop
) external nonReentrant whenNotPaused {
if (productIds.length == 0) {
revert NoProductsProvided();
}
for (uint256 i = 0; i < productIds.length; i++) {
_renewSubscription(productPassId, productIds[i], airdrop);
}
}
function _renewSubscription(
uint256 productPassId,
uint256 productId,
bool airdrop
) internal {
(uint256 orgId, address token, uint256 price) = ISubscriptionEscrow(
registry.subscriptionEscrow()
).renewSubscription(productPassId, productId);
address passOwner = _passOwner(productPassId);
_checkPermissionName(
PermissionUtils.PASS_SUBSCRIPTION_RENEWAL,
orgId,
passOwner
);
(token, price) = _translateBaseTokenPurchasePrice(token, price);
if (price > 0) {
_performPurchase(
PerformPurchaseParams({
orgId: orgId,
productPassId: productPassId,
passOwner: passOwner,
purchaser: passOwner,
totalAmount: price,
token: token,
airdrop: airdrop,
isInitialPurchase: false,
forceCoupon: false
})
);
}
emit SubscriptionRenewed(
orgId,
productPassId,
productId,
passOwner,
token,
price
);
}
/**
* @notice Emitted when a subscription is renewed via the purchase manager
* @param orgId The organization ID
* @param productPassId The product pass ID
* @param productId The product ID
* @param purchaser The purchaser address
* @param token The token address used for the renewal purchase
* @param subtotalAmount The subtotal amount
*/
event SubscriptionRenewed(
uint256 indexed orgId,
uint256 indexed productPassId,
uint256 indexed productId,
address purchaser,
address token,
uint256 subtotalAmount
);
/**
* Tiered Subscription Unit Quantity
*/
function changeTieredSubscriptionUnitQuantity(
uint256 productPassId,
uint256 productId,
uint256 quantity,
bool airdrop
) external onlyPassOwnerOrAdmin(productPassId) nonReentrant {
(uint256 orgId, address token, uint256 amount) = ISubscriptionEscrow(
registry.subscriptionEscrow()
).changeSubscriptionUnitQuantity(productPassId, productId, quantity);
address passOwner = _passOwner(productPassId);
_checkPermissionName(
PermissionUtils.PASS_SUBSCRIPTION_QUANTITY,
orgId,
passOwner
);
if (amount > 0) {
(token, amount) = _translateBaseTokenPurchasePrice(token, amount);
_performPurchase(
PerformPurchaseParams({
orgId: orgId,
productPassId: productPassId,
passOwner: passOwner,
purchaser: passOwner,
totalAmount: amount,
token: token,
airdrop: airdrop,
isInitialPurchase: false,
forceCoupon: false
})
);
}
}
/**
* Pause subscription
*/
function pauseSubscription(
uint256 productPassId,
uint256 productId,
bool _pause
) external onlyPassOwnerOrAdmin(productPassId) nonReentrant {
_pauseSubscription(productPassId, productId, _pause);
}
function pauseSubscriptionBatch(
uint256 productPassId,
uint256[] calldata productIds,
bool[] calldata pause
) external onlyPassOwnerOrAdmin(productPassId) nonReentrant {
_checkProductStatuses(productIds, pause);
for (uint256 i = 0; i < productIds.length; i++) {
_pauseSubscription(productPassId, productIds[i], pause[i]);
}
}
function _pauseSubscription(
uint256 productPassId,
uint256 productId,
bool _pause
) internal {
bool needsRenewal = ISubscriptionEscrow(registry.subscriptionEscrow())
.pauseSubscription(productPassId, productId, _pause);
if (!_pause && needsRenewal) {
_renewSubscription(productPassId, productId, false);
}
}
/**
* Cancel subscription
*/
function cancelSubscription(
uint256 productPassId,
uint256 productId,
bool cancel
) external onlyPassOwnerOrAdmin(productPassId) nonReentrant {
_cancelSubscription(productPassId, productId, cancel);
}
function cancelSubscriptionBatch(
uint256 productPassId,
uint256[] calldata productIds,
bool[] calldata cancel
) external onlyPassOwnerOrAdmin(productPassId) nonReentrant {
_checkProductStatuses(productIds, cancel);
for (uint256 i = 0; i < productIds.length; i++) {
_cancelSubscription(productPassId, productIds[i], cancel[i]);
}
}
function _cancelSubscription(
uint256 productPassId,
uint256 productId,
bool cancel
) internal {
bool needsRenewal = ISubscriptionEscrow(registry.subscriptionEscrow())
.cancelSubscription(productPassId, productId, cancel);
if (!cancel && needsRenewal) {
_renewSubscription(productPassId, productId, false);
}
}
/**
* Payment Processing
*/
function _performPurchase(PerformPurchaseParams memory params) internal {
if (params.airdrop) {
_checkOrgAdmin(params.orgId);
return;
}
// Apply coupon discount first
if (
ICouponRegistry(registry.couponRegistry()).hasPassCouponCode(
params.orgId,
params.purchaser
)
) {
params.totalAmount = _redeemCoupon(
params.orgId,
params.purchaser,
params.totalAmount,
params.isInitialPurchase,
params.forceCoupon
);
}
// Apply permanent pass discounts
params.totalAmount = IDiscountRegistry(registry.discountRegistry())
.calculateTotalPassDiscountedAmount(
params.productPassId,
params.totalAmount
);
// Transfer funds
if (params.totalAmount > 0) {
if (!params.isInitialPurchase) {
_checkPermissionName(
PermissionUtils.PASS_WALLET_SPEND,
params.orgId,
params.purchaser
);
}
IPaymentEscrow(registry.paymentEscrow()).transferDirect{
value: msg.value
}(
params.orgId,
payable(params.purchaser),
params.token,
params.totalAmount
);
}
emit PerformPurchase(
params.orgId,
params.passOwner,
params.purchaser,
params.token,
params.totalAmount
);
}
receive() external payable {}
/**
* Coupons
*/
function _redeemCoupon(
uint256 orgId,
address passOwner,
uint256 totalAmount,
bool isInitialPurchase,
bool forceCoupon
) internal returns (uint256) {
uint256 amount = totalAmount;
try
ICouponRegistry(registry.couponRegistry()).redeemCoupon(
orgId,
passOwner,
isInitialPurchase,
totalAmount
)
returns (uint256 discountedAmount) {
amount = discountedAmount;
} catch {
if (forceCoupon) {
revert InvalidCouponCode();
} else {
ICouponRegistry(registry.couponRegistry()).removePassCouponCode(
orgId,
passOwner
);
}
}
return amount;
}
function _setCoupon(
uint256 orgId,
address passOwner,
string memory code
) internal {
ICouponRegistry(registry.couponRegistry()).setPassCouponCode(
orgId,
passOwner,
code
);
}
/**
* Purchase Pause
*/
function pausePurchases() external onlyOwner {
_pause();
}
function unpausePurchases() external onlyOwner {
_unpause();
}
/**
* Permission Registry
*/
function setPermissionRegistry(
address _permissionRegistry
) external onlyOwner {
_setPermissionRegistry(_permissionRegistry);
}
/**
* Dynamic Price Registry
*/
function setDynamicPriceRegistry(
address _dynamicPriceRegistry
) external onlyOwner {
_setDynamicPriceRegistry(_dynamicPriceRegistry);
}
/**
* Checks
*/
function _checkProductStatuses(
uint256[] calldata productIds,
bool[] calldata statuses
) internal pure {
if (productIds.length == 0) {
revert NoProductsProvided();
}
if (productIds.length != statuses.length) {
revert ProductIdsAndStatusesLengthMismatch();
}
}
function _checkPassOwnerOrAdmin(uint256 productPassId) internal view {
if (
!_isPassOwner(productPassId) &&
!_isOrgAdmin(
IPurchaseRegistry(registry.purchaseRegistry()).passOrganization(
productPassId
)
)
) {
revert NotAuthorized();
}
}
/**
* Modifiers
*/
modifier onlyPassOwnerOrAdmin(uint256 productPassId) {
_requireNotPaused();
_checkPassOwnerOrAdmin(productPassId);
_;
}
/**
* ERC165
*/
function supportsInterface(
bytes4 interfaceId
) external pure returns (bool) {
return
interfaceId == type(IERC165).interfaceId ||
interfaceId == type(IPurchaseManager).interfaceId;
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;
import {
EnumerableSet
} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol";
import {IERC165} from "@openzeppelin/contracts/interfaces/IERC165.sol";
import {IDynamicERC20} from "../tokens/IDynamicERC20.sol";
import {IDynamicPriceRegistry} from "./IDynamicPriceRegistry.sol";
import {IPaymentEscrow} from "../escrow/IPaymentEscrow.sol";
import {RegistryEnabled} from "../abstract/RegistryEnabled.sol";
/*
____ _ _ __ __ _ _
| _ \ _ __ ___ __| |_ _ ___| |_| \/ (_)_ __ | |_
| |_) | '__/ _ \ / _` | | | |/ __| __| |\/| | | '_ \| __|
| __/| | | (_) | (_| | |_| | (__| |_| | | | | | | | |_
|_| |_| \___/ \__,_|\__,_|\___|\__|_| |_|_|_| |_|\__|
NFT based payment system to mint products onchain with one-time payments and
recurring permissionless subscriptions.
https://productmint.io
*/
/**
* @title Dynamic Price Registry
* @notice Manages the dynamic price tokens for the ProductMint system.
* The registry is used to store the dynamic token contract addresses.
*/
contract DynamicPriceRegistry is
AccessControl,
RegistryEnabled,
IDynamicPriceRegistry
{
using EnumerableSet for EnumerableSet.AddressSet;
// The set of all dynamic tokens
EnumerableSet.AddressSet private tokens;
// The role required to register a new dynamic token
bytes32 public constant REGISTER_TOKEN_ROLE =
keccak256("REGISTER_TOKEN_ROLE");
// The role required to unregister and remove a dynamic token
bytes32 public constant UNREGISTER_TOKEN_ROLE =
keccak256("UNREGISTER_TOKEN_ROLE");
constructor(
address _contractRegistry
) AccessControl() RegistryEnabled(_contractRegistry) {
_grantRole(DEFAULT_ADMIN_ROLE, _msgSender());
_grantRole(REGISTER_TOKEN_ROLE, _msgSender());
_grantRole(UNREGISTER_TOKEN_ROLE, _msgSender());
}
/**
* View functions
*/
function getTokenCount() external view returns (uint256) {
return tokens.length();
}
function getTokens() external view returns (address[] memory) {
return tokens.values();
}
function isTokenRegistered(address _token) external view returns (bool) {
return tokens.contains(_token);
}
function isTokenRegisteredBatch(
address[] calldata _tokens
) external view returns (bool) {
require(_tokens.length > 0, "No tokens provided");
for (uint256 i = 0; i < _tokens.length; i++) {
if (!tokens.contains(_tokens[i])) {
return false;
}
}
return true;
}
/**
* Update the registry
*/
function registerToken(
address _token
) external onlyRole(REGISTER_TOKEN_ROLE) {
require(!tokens.contains(_token), "Token already registered");
require(
IERC165(_token).supportsInterface(type(IDynamicERC20).interfaceId),
"Token does not support IDynamicERC20"
);
require(
IPaymentEscrow(registry.paymentEscrow()).whitelistedTokens(_token),
"Dynamic token is not whitelisted"
);
require(
IPaymentEscrow(registry.paymentEscrow()).whitelistedTokens(
IDynamicERC20(_token).baseToken()
),
"Base token is not whitelisted"
);
tokens.add(_token);
emit DynamicTokenRegistrationUpdated(_token, true);
}
function unregisterToken(
address _token
) external onlyRole(UNREGISTER_TOKEN_ROLE) {
require(tokens.contains(_token), "Token not registered");
tokens.remove(_token);
emit DynamicTokenRegistrationUpdated(_token, false);
}
/**
* IERC165
*/
function supportsInterface(
bytes4 interfaceId
) public view override returns (bool) {
return
interfaceId == type(IDynamicPriceRegistry).interfaceId ||
super.supportsInterface(interfaceId);
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;
interface IContractRegistry {
/**
* @notice Emitted when a contract is updated.
* @param contractName The name of the contract.
* @param newAddress The new address of the contract.
*/
event ContractUpdated(
string indexed contractName,
address indexed newAddress
);
/**
* Manager
*/
/**
* @notice Purchase manager responsible for facilitating product purchases and managing subscriptions.
* @return The address of the purchase manager.
*/
function purchaseManager() external view returns (address);
/**
* Admin
*/
/**
* @notice Organization admin that can delegate other addresses to help manage the organization and make calls on behalf of the organization.
* @return The address of the organization admin.
*/
function orgAdmin() external view returns (address);
/**
* NFTs
*/
/**
* @notice Product Pass NFT that represents a user's membership to an organization and allows them to purchase products.
* @return The address of the product pass NFT.
*/
function productPassNFT() external view returns (address);
/**
* @notice Organization NFT that represents a product owner and allows for the creation of products that can be purchased by users via Product Passes.
* @return The address of the organization NFT.
*/
function organizationNFT() external view returns (address);
/**
* Registry
*/
/**
* @notice Product registry that allows for the creation of products that can be purchased by users via Product Passes.
* @return The address of the product registry.
*/
function productRegistry() external view returns (address);
/**
* @notice Pricing registry that allows for the creation of pricing models that can be linked to products for purchase.
* @return The address of the pricing registry.
*/
function pricingRegistry() external view returns (address);
/**
* @notice Purchase registry records all purchases made by users via Product Passes.
* @return The address of the purchase registry.
*/
function purchaseRegistry() external view returns (address);
/**
* @notice Coupon registry that allows for the creation of coupons that can be applied to purchases and subscriptions.
* @return The address of the coupon registry.
*/
function couponRegistry() external view returns (address);
/**
* @notice Permanent discount registry that allows for the creation of permanent discounts that are minted onto product passes.
* Discounts minted onto product passes are permanent and used in all future purchases including any subscription renewals.
* @return The address of the discount registry.
*/
function discountRegistry() external view returns (address);
/**
* Calculator
*/
/**
* @notice Pricing calculator that allows performs all the pricing calculations for different pricing models created in the pricing registry.
* @return The address of the pricing calculator.
*/
function pricingCalculator() external view returns (address);
/**
* Oracles
*/
/**
* @notice Responsible for knowing whether the product pass NFT can be transferred from one user to another.
* Products created in the registry must be set to transferable to be able to be transferred.
* @return The address of the product transfer oracle.
*/
function productTransferOracle() external view returns (address);
/**
* @notice Responsible for knowing whether the subscription can be transferred from one user to another.
* Subs must be in a paused state to be transferred.
* Organizations must enable this feature for subscriptions to be paused.
* @return The address of the subscription transfer oracle.
*/
function subscriptionTransferOracle() external view returns (address);
/**
* Escrow
*/
/**
* @notice Escrow that that holds all the subscriptions that have been created for product passes
* @return The address of the subscription escrow.
*/
function subscriptionEscrow() external view returns (address);
/**
* @notice Escrow that holds all the payments that have been made for purchases that are withdrawable by the organization.
* @return The address of the payment escrow.
*/
function paymentEscrow() external view returns (address);
/**
* Usage Recorder
*/
/**
* @notice Usage recorder where usage meters can be created for pricing models set to usage based billing.
* Once a meter has been created and is active, organizations can begin recording usage for their products.
* @return The address of the usage recorder.
*/
function usageRecorder() external view returns (address);
/**
* Locks
*/
/**
* @notice Lock for the product pass NFT.
* This lock prevents the product pass NFT from being changed once it has been set.
* @return The address of the product pass NFT.
*/
function PASS_LOCK() external view returns (bytes32);
/**
* @notice Lock for the organization NFT.
* This lock prevents the organization NFT from being changed once it has been set.
* @return The address of the organization NFT.
*/
function ORG_LOCK() external view returns (bytes32);
/**
* Batch Setup
*/
struct BatchSetupContracts {
// Manager
address purchaseManager;
// Admin
address orgAdmin;
// NFTs
address productPassNFT;
address organizationNFT;
// Registry
address productRegistry;
address pricingRegistry;
address purchaseRegistry;
address couponRegistry;
address discountRegistry;
// Calculator
address pricingCalculator;
// Oracles
address productTransferOracle;
address subscriptionTransferOracle;
// Escrow
address subscriptionEscrow;
address paymentEscrow;
// Usage recorder
address usageRecorder;
}
/**
* @notice Batch set all the contracts in the registry.
* @param _contracts The contracts to be set.
*/
function batchSetContracts(BatchSetupContracts memory _contracts) external;
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;
interface ICouponRegistry {
/**
* @notice Coupon struct
* @param orgId Organization ID that the coupon belongs to
* @param code Coupon code
* @param discount Discount percentage. 10 = 0.1%, 100 = 1%, 250 = 2.5%, 1000 = 10%, 10000 = 100%, etc.
* @param expiration Expiration timestamp when the coupon will no longer be valid. 0 = never expires.
* @param totalRedemptions Total number of redemptions for the coupon
* @param maxTotalRedemptions Maximum total redemptions for the coupon. 0 = unlimited.
* @param isInitialPurchaseOnly True then only initial product pass mint purchases should be able to use the coupon
* @param isActive True then the coupon is currently active and can be redeemed, else it is inactive and cannot be redeemed
* @param isRestricted True then the coupon is restricted to a specific group of users that have been granted access
* @param isOneTimeUse True then the coupon can only be used once per customer
*/
struct Coupon {
uint256 orgId;
string code;
uint256 discount;
uint256 expiration;
uint256 totalRedemptions;
uint256 maxTotalRedemptions;
bool isInitialPurchaseOnly;
bool isActive;
bool isRestricted;
bool isOneTimeUse;
}
/**
* @notice Create coupon parameters
* @param orgId Organization ID that the coupon belongs to
* @param code Coupon code
* @param discount Discount percentage. 10 = 0.1%, 100 = 1%, 250 = 2.5%, 1000 = 10%, 10000 = 100%, etc.
* @param expiration Expiration timestamp when the coupon will no longer be valid. 0 = never expires.
* @param maxTotalRedemptions Maximum total redemptions for the coupon. 0 = unlimited.
* @param isInitialPurchaseOnly True then only initial product pass mint purchases should be able to use the coupon
* @param isActive True then the coupon is currently active and can be redeemed
* @param isRestricted True then the coupon is restricted to a specific group of users that have been granted access
* @param isOneTimeUse True then the coupon can only be used once per customer
*/
struct CreateCouponParams {
uint256 orgId;
string code;
uint256 discount;
uint256 expiration;
uint256 maxTotalRedemptions;
bool isInitialPurchaseOnly;
bool isActive;
bool isRestricted;
bool isOneTimeUse;
}
/**
* @notice Get the total number of coupons that have been created
* @return The total number of coupons
*/
function totalCoupons() external view returns (uint256);
/**
* Redemption
*/
/**
* @notice Invalid coupon code error when redeeming a coupon
*/
error InvalidCouponCode();
/**
* @notice Coupon code not found error when redeeming a coupon
* @param code Coupon code
*/
error CouponCodeNotFound(string code);
/**
* @notice Coupon not active error when redeeming a coupon
* @param couponId Coupon ID
*/
error CouponNotActive(uint256 couponId);
/**
* @notice Coupon expired error when redeeming a coupon
* @param couponId Coupon ID
*/
error CouponExpired(uint256 couponId);
/**
* @notice Coupon max redemptions reached error when redeeming a coupon
* @param couponId Coupon ID
* @param maxRedemptions Maximum redemptions
*/
error CouponMaxRedemptionsReached(uint256 couponId, uint256 maxRedemptions);
/**
* @notice Coupon initial purchase only error when redeeming a coupon
* @param couponId Coupon ID
*/
error CouponInitialPurchaseOnly(uint256 couponId);
/**
* @notice Coupon already redeemed error when redeeming a coupon
* @param couponId Coupon ID
* @param passOwner Pass owner address
*/
error CouponAlreadyRedeemed(uint256 couponId, address passOwner);
/**
* @notice Coupon restricted error when redeeming a coupon
* @param couponId Coupon ID
* @param passOwner Pass owner address
*/
error CouponRestricted(uint256 couponId, address passOwner);
/**
* @notice Emitted when a coupon is redeemed
*/
event CouponRedeemed(
uint256 indexed orgId,
uint256 indexed couponId,
address indexed passOwner
);
/**
* @notice Attempt to redeem the current pass coupon code that is set by recording the redemption and return the discounted amount
* @dev Will revert if the coupon is not redeemable
* @param orgId Organization ID that the coupon belongs to
* @param passOwner Pass owner address
* @param isInitialPurchase True then the coupon is for an initial product pass mint purchase
* @param amount Amount to redeem the coupon for
* @return The discounted total amount
*/
function redeemCoupon(
uint256 orgId,
address passOwner,
bool isInitialPurchase,
uint256 amount
) external returns (uint256);
/**
* Queries
*/
/**
* @notice Get a coupon by ID
* @param couponId Coupon ID
* @return The coupon
*/
function getCoupon(uint256 couponId) external view returns (Coupon memory);
/**
* @notice Get all the coupon IDs for an organization
* @param orgId Organization ID
* @return The coupon IDs
*/
function getOrgCouponIds(
uint256 orgId
) external view returns (uint256[] memory);
/**
* @notice Get all the coupons for an organization
* @param orgId Organization ID
* @return The coupons
*/
function getOrgCoupons(
uint256 orgId
) external view returns (Coupon[] memory);
/**
* @notice Check if a coupon exists for an organization
* @param orgId Organization ID
* @param code Coupon code
* @return True if the coupon exists, else false
*/
function orgCouponExists(
uint256 orgId,
string calldata code
) external view returns (bool);
/**
* @notice Get the coupon ID for an organization and code
* @param orgId Organization ID
* @param code Coupon code
* @return The coupon ID
*/
function orgCouponCodes(
uint256 orgId,
string calldata code
) external view returns (uint256);
/**
* @notice Get the redeemed coupons for a pass owner
* @param orgId Organization ID
* @param passOwner Pass owner address
* @return The coupons that the pass owner has redeemed
*/
function getRedeemedCoupons(
uint256 orgId,
address passOwner
) external view returns (uint256[] memory);
/**
* @notice Check if a pass owner has redeemed a coupon
* @param orgId Organization ID
* @param passOwner Pass owner address
* @param couponId Coupon ID
* @return True if the pass owner has redeemed the coupon, else false
*/
function hasRedeemedCoupon(
uint256 orgId,
address passOwner,
uint256 couponId
) external view returns (bool);
/**
* @notice Get the discounted amount for a coupon
* @param couponId Coupon ID
* @param amount Amount to get the discounted amount for
* @return The discounted amount
*/
function discountedAmount(
uint256 couponId,
uint256 amount
) external view returns (uint256);
/**
* @notice Check if a coupon code is redeemable
* @dev Will revert if the coupon is not redeemable
* @param orgId Organization ID
* @param passOwner Pass owner address
* @param code Coupon code
* @param isInitialPurchase True then the coupon is for an initial product pass mint purchase
* @return The coupon ID if the coupon is redeemable, else 0
*/
function isCodeRedeemable(
uint256 orgId,
address passOwner,
string memory code,
bool isInitialPurchase
) external view returns (uint256);
/**
* Creation
*/
/**
* @notice Emitted when a coupon is created
*/
event CouponCreated(uint256 indexed orgId, uint256 indexed couponId);
/**
* @notice Creates a new coupon for an organization
* @param params Coupon parameters
*/
function createCoupon(CreateCouponParams calldata params) external;
/**
* Management
*/
/**
* @notice Emitted when a coupon is updated
*/
event CouponUpdated(uint256 indexed orgId, uint256 indexed couponId);
/**
* @notice Emitted when a coupon status is updated
*/
event CouponStatusUpdated(
uint256 indexed orgId,
uint256 indexed couponId,
bool isActive
);
/**
* @notice Set the discount for a coupon
* @param couponId Coupon ID
* @param discount Discount percentage. 10 = 0.1%, 100 = 1%, 250 = 2.5%, 1000 = 10%, 10000 = 100%, etc.
*/
function setCouponDiscount(uint256 couponId, uint256 discount) external;
/**
* @notice Set the expiration for a coupon
* @dev Will revert if the expiration is in the past
* @param couponId Coupon ID
* @param expiration Expiration timestamp when the coupon will no longer be valid. 0 = never expires.
*/
function setCouponExpiration(uint256 couponId, uint256 expiration) external;
/**
* @notice Set the maximum total redemptions for a coupon
* @param couponId Coupon ID
* @param maxTotalRedemptions Maximum total redemptions for the coupon. 0 = unlimited.
*/
function setCouponMaxRedemptions(
uint256 couponId,
uint256 maxTotalRedemptions
) external;
/**
* @notice Set if a coupon is only for initial product pass mint purchases
* @param couponId Coupon ID
* @param isInitialPurchaseOnly True then only initial product pass mint purchases should be able to use the coupon
*/
function setCouponNewCustomers(
uint256 couponId,
bool isInitialPurchaseOnly
) external;
/**
* @notice Set if a coupon is active
* @param couponId Coupon ID
* @param active True then the coupon is currently active and can be redeemed, else it is inactive and cannot be redeemed
*/
function setCouponActive(uint256 couponId, bool active) external;
/**
* @notice Set if a coupon is restricted to a specific group of users that have been granted access
* @param couponId Coupon ID
* @param restricted True then the coupon is restricted to a specific group of users that have been granted access
*/
function setCouponRestricted(uint256 couponId, bool restricted) external;
/**
* Restricted Access
*/
/**
* @notice Batch set the restricted access for a pass owners
* @param couponId Coupon ID
* @param passOwners Pass owners addresses
* @param restricted True then the pass owner has restricted access to the coupon, else false
*/
function setRestrictedAccess(
uint256 couponId,
address[] calldata passOwners,
bool[] calldata restricted
) external;
/**
* Pass Owner Codes
*/
/**
* @notice Emitted when a pass owner coupon code is set or removed. If removed, the code will be an empty string.
*/
event PassCouponCodeSet(
uint256 indexed orgId,
address indexed passOwner,
string code
);
/**
* @notice Get the coupon code for a pass owner
* @param orgId Organization ID
* @param passOwner Pass owner address
* @return The coupon code
*/
function passOwnerCodes(
uint256 orgId,
address passOwner
) external view returns (string memory);
/**
* @notice Check if a pass owner has a coupon code
* @param orgId Organization ID
* @param passOwner Pass owner address
* @return True if the pass owner has a coupon code set, else false
*/
function hasPassCouponCode(
uint256 orgId,
address passOwner
) external view returns (bool);
/**
* @notice Set the coupon code for a pass owner
* @param orgId Organization ID
* @param passOwner Pass owner address
* @param code Coupon code
*/
function setPassCouponCode(
uint256 orgId,
address passOwner,
string calldata code
) external;
/**
* @notice Batch set the coupon code for multiple pass owners
* @dev Only org admins can call this function
* @param orgId Organization ID
* @param passOwners Pass owners addresses
* @param codes Coupon codes
*/
function setPassCouponCodeBatch(
uint256 orgId,
address[] calldata passOwners,
string[] calldata codes
) external;
/**
* @notice Remove the coupon code for a pass owner
* @param orgId Organization ID
* @param passOwner Pass owner address
*/
function removePassCouponCode(uint256 orgId, address passOwner) external;
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;
interface IDiscountRegistry {
/**
* Discount
*/
/**
* @notice Discount struct
* @param id The id of the discount
* @param orgId The organization ID
* @param name The name of the discount. Must be unique within the organization.
* @param discount The discount amount
* @param totalMints The total number of mints for the discount
* @param maxMints The maximum number of times the discount can be minted
* @param isActive Whether the discount is active
* @param isRestricted Whether the discount is restricted
*/
struct Discount {
uint256 id;
uint256 orgId;
string name;
uint256 discount;
uint256 totalMints;
uint256 maxMints;
bool isActive;
bool isRestricted;
}
/**
* @notice Total number of discounts created
* @return The total number of discounts created
*/
function totalDiscounts() external view returns (uint256);
/**
* Get Discounts
*/
/**
* @notice Revert when discount does not exist
* @param discountId The discount ID
*/
error DiscountDoesNotExist(uint256 discountId);
/**
* @notice Get discount details
* @param discountId The discount ID
* @return The discount
*/
function getDiscount(
uint256 discountId
) external view returns (Discount memory);
/**
* @notice Get discount details for multiple discounts
* @param discountIds The discount IDs
* @return The discounts
*/
function getDiscountBatch(
uint256[] calldata discountIds
) external view returns (Discount[] memory);
/**
* @notice Get discount names
* @param discountIds The discount IDs
* @return The discount names
*/
function getDiscountNames(
uint256[] calldata discountIds
) external view returns (string[] memory);
/**
* @notice Get organization discount IDs for discounts created by the organization
* @param orgId The organization ID
* @return The discount IDs
*/
function getOrgDiscountIds(
uint256 orgId
) external view returns (uint256[] memory);
/**
* @notice Get all discount IDs minted onto a pass
* @param passId The pass ID
* @return The discount IDs
*/
function getPassDiscountIds(
uint256 passId
) external view returns (uint256[] memory);
/**
* @notice Check if a pass has a discount
* @param passId The pass ID
* @param discountId The discount ID
* @return True if the pass has the discount, false otherwise
*/
function hasPassDiscount(
uint256 passId,
uint256 discountId
) external view returns (bool);
/**
* @notice Get the total discount for a list of discounts
* @param discountIds The discount IDs
* @return The total discount
*/
function getTotalDiscount(
uint256[] memory discountIds
) external view returns (uint256);
/**
* @notice Get the total discount for a pass by summing all discounts minted onto the pass
* @param passId The pass ID
* @return The total discount
*/
function getTotalPassDiscount(
uint256 passId
) external view returns (uint256);
/**
* @notice Get the discount ID for a discount name
* @param orgId The organization ID
* @param name The discount name
* @return The discount ID. 0 if the discount name does not exist.
*/
function discountNames(
uint256 orgId,
string memory name
) external view returns (uint256);
/**
* Mint Discounts
*/
/**
* @notice Emitted when a discount is minted onto a pass
* @param orgId The organization ID
* @param passId The pass ID
* @param discountId The discount ID
* @param minter The minter for the discount
*/
event DiscountMinted(
uint256 indexed orgId,
uint256 indexed passId,
uint256 indexed discountId,
address minter
);
/**
* @notice Revert when discount is not found
* @param orgId The organization ID
* @param name The discount name
*/
error DiscountNotFound(uint256 orgId, string name);
/**
* @notice Revert when discount is not for organization during minting
* @param orgId The organization ID
* @param discountId The discount ID
*/
error DiscountNotForOrg(uint256 orgId, uint256 discountId);
/**
* @notice Revert when discount is not active during minting and cannot be minted
* @param discountId The discount ID
*/
error DiscountNotActive(uint256 discountId);
/**
* @notice Revert when discount is restricted and access is not granted during minting
* @param discountId The discount ID
* @param minter The minter
*/
error DiscountAccessRestricted(uint256 discountId, address minter);
/**
* @notice Revert when discount max mints is reached during minting
* @param discountId The discount ID
* @param maxMints The maximum number of mints
*/
error DiscountMaxMintsReached(uint256 discountId, uint256 maxMints);
/**
* @notice Revert when discount is already minted onto a pass
* @param discountId The discount ID
* @param passId The pass ID
*/
error DiscountAlreadyMinted(uint256 discountId, uint256 passId);
/**
* @notice Revert when pass is not a member of the organization
* @param orgId The organization ID
* @param passId The pass ID
*/
error PassNotOrgMember(uint256 orgId, uint256 passId);
/**
* @notice Check if a discount can be minted by a pass owner
* @dev will revert if any of the checks fail
* @param orgId The organization ID
* @param passId The pass ID
* @param minter The minter
* @param discountId The discount ID
*/
function canMintDiscount(
uint256 orgId,
uint256 passId,
address minter,
uint256 discountId
) external view;
/**
* Check if a discount can be minted by a pass owner with the name
* @param orgId The organization ID
* @param passId The pass ID
* @param minter The minter
* @param name The discount name
* @return The discount ID
*/
function canMintDiscountByName(
uint256 orgId,
uint256 passId,
address minter,
string memory name
) external view returns (uint256);
/**
* Check if a list of discounts can be minted by a pass owner with the name
* @param orgId The organization ID
* @param passId The pass ID
* @param minter The minter
* @param names The discount names
* @return The discount IDs
*/
function canMintDiscountByNameBatch(
uint256 orgId,
uint256 passId,
address minter,
string[] memory names
) external view returns (uint256[] memory);
/**
* @notice Mint discounts onto a pass
* @dev used by purchase manager
* @param orgId The organization ID
* @param passId The pass ID
* @param minter The minter for the discount
* @param discountIds The discount IDs
*/
function mintDiscountsToPass(
uint256 orgId,
uint256 passId,
address minter,
uint256[] calldata discountIds
) external;
/**
* @notice Mint discounts onto a pass by a pass owner
* @dev used by pass owner
* @param passId The pass ID
* @param discountIds The discount IDs
*/
function mintDiscountsToPassByOwner(
uint256 passId,
uint256[] calldata discountIds
) external;
/**
* @notice Mint discounts onto a pass by an organization
* @dev used by organization
* @param orgId The organization ID
* @param passIds The pass IDs
* @param discountIds The discount IDs
*/
function mintDiscountsToPassByOrg(
uint256 orgId,
uint256[] calldata passIds,
uint256[] calldata discountIds
) external;
/**
* Calculations
*/
/**
* @notice Calculate the total discounted amount for a list of discounts
* @param discountIds The discount IDs
* @param amount The amount to be discounted
* @return The total discounted amount
*/
function calculateTotalDiscountedAmount(
uint256[] memory discountIds,
uint256 amount
) external view returns (uint256);
/**
* @notice Calculate the total discounted amount for a pass by summing all discounts minted onto the pass
* @param passId The pass ID
* @param amount The amount to be discounted
* @return The total discounted amount
*/
function calculateTotalPassDiscountedAmount(
uint256 passId,
uint256 amount
) external view returns (uint256);
/**
* Create Discount
*/
/**
* @notice Emitted when a discount is created
* @param orgId The organization ID
* @param discountId The discount ID
* @param name The name of the discount
* @param discount The discount in basis points up to 10000
*/
event DiscountCreated(
uint256 indexed orgId,
uint256 indexed discountId,
string name,
uint256 discount
);
/**
* @notice Create discount params
* @param orgId The organization ID
* @param name The name of the discount
* @param discount The discount in basis points up to 10000
* @param maxMints The maximum number of times the discount can be minted. 0 means unlimited.
* @param isActive Whether the discount is active. Must be true to be minted.
* @param isRestricted Whether the discount is restricted. If true, only specific pass owners can mint the discount.
*/
struct CreateDiscountParams {
uint256 orgId;
string name;
uint256 discount;
uint256 maxMints;
bool isActive;
bool isRestricted;
}
/**
* @notice Create a new discount
* @param params The create discount params
*/
function createDiscount(CreateDiscountParams calldata params) external;
/**
* Update Discount
*/
/**
* @notice Emitted when a discount is updated
* @param orgId The organization ID
* @param discountId The discount ID
*/
event DiscountUpdated(uint256 indexed orgId, uint256 indexed discountId);
/**
* @notice Set discount name
* @param discountId The discount ID
* @param name The new name of the discount
*/
function setDiscountName(uint256 discountId, string calldata name) external;
/**
* @notice Set discount
* @param discountId The discount ID
* @param discount The new discount in basis points up to 10000
*/
function setDiscount(uint256 discountId, uint256 discount) external;
/**
* @notice Set discount max mints
* @param discountId The discount ID
* @param maxMints The new maximum number of times the discount can be minted. 0 means unlimited.
*/
function setDiscountMaxMints(uint256 discountId, uint256 maxMints) external;
/**
* @notice Set discount active
* @param discountId The discount ID
* @param isActive Whether the discount is active and can be minted.
*/
function setDiscountActive(uint256 discountId, bool isActive) external;
/**
* @notice Set discount restricted
* @param discountId The discount ID
* @param isRestricted Whether the discount is restricted. If true, only specific pass owners can mint the discount.
*/
function setDiscountRestricted(
uint256 discountId,
bool isRestricted
) external;
/**
* Restricted Access
*/
/**
* @notice Set restricted access
* @param discountId The discount ID
* @param passOwners The pass owners
* @param restricted Whether the pass owners can mint the discount.
*/
function setRestrictedAccess(
uint256 discountId,
address[] calldata passOwners,
bool[] calldata restricted
) external;
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;
interface IDynamicPriceRegistry {
/**
* Roles
*/
/**
* @notice The role required to register a new dynamic token
*/
function REGISTER_TOKEN_ROLE() external view returns (bytes32);
/**
* @notice The role required to unregister and remove a dynamic token
*/
function UNREGISTER_TOKEN_ROLE() external view returns (bytes32);
/**
* View functions
*/
/**
* @notice Get the number of tokens registered in the registry
* @return The number of tokens registered in the registry
*/
function getTokenCount() external view returns (uint256);
/**
* @notice Get the list of tokens registered in the registry
* @return The list of tokens registered in the registry
*/
function getTokens() external view returns (address[] memory);
/**
* @notice Check if a token is registered in the registry
* @param token The address of the token to check
* @return True if the token is registered, false otherwise
*/
function isTokenRegistered(address token) external view returns (bool);
/**
* @notice Check if a list of tokens are registered in the registry
* @param tokens The list of tokens to check
* @return True if all tokens are registered, false otherwise
*/
function isTokenRegisteredBatch(
address[] calldata tokens
) external view returns (bool);
/**
* Update the registry
*/
/**
* @notice Emitted when a token is registered or unregistered
* @param token The address of the token that was registered or unregistered
* @param registered True if the token was registered, false if it was unregistered
*/
event DynamicTokenRegistrationUpdated(
address indexed token,
bool registered
);
/**
* @notice Register a token in the registry
* @param token The address of the token to register
*/
function registerToken(address token) external;
/**
* @notice Unregister a token in the registry
* @param token The address of the token to unregister
*/
function unregisterToken(address token) external;
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;
interface IPermissionRegistry {
/**
* @dev Errors
*/
/**
* @notice Revert if a permission is inactive
*
* @param _permission The ID of the inactive permission
*/
error InactivePermission(bytes32 _permission);
/**
* @notice Revert if any of the permissions are inactive
*
* @param _permissions The IDs of the potentially inactive permissions
*/
error InactivePermissionBatch(bytes32[] _permissions);
/**
* @dev View owner permissions
*/
/**
* @notice Has the owner had their initial permissions set for the org?
*
* @param _orgId The ID of the organization
* @param _owner The address of the owner
* @return True if the owner permissions have been set for the org, false otherwise
*/
function ownerPermissionsSet(
uint256 _orgId,
address _owner
) external view returns (bool);
/**
* @notice Does the owner have a permission for the org?
*
* @param _orgId The ID of the organization
* @param _owner The address of the owner
* @param _permission The ID of the permission
* @return True if the owner has the permission, false otherwise
*/
function hasOwnerPermission(
uint256 _orgId,
address _owner,
bytes32 _permission
) external view returns (bool);
/**
* @notice Does the owner have a batch of permissions for the org?
*
* @param _orgId The ID of the organization
* @param _owner The address of the owner
* @param _permissions The IDs of the permissions
* @return _hasPermissions Resulting array of booleans indicating if the owner has each permission
*/
function hasOwnerPermissionBatch(
uint256 _orgId,
address _owner,
bytes32[] memory _permissions
) external view returns (bool[] memory _hasPermissions);
/**
* @notice Get all the permissions for the owner
*
* @param _orgId The ID of the organization
* @param _owner The address of the owner
* @return _permissions The IDs of the permissions
*/
function getOwnerPermissions(
uint256 _orgId,
address _owner
) external view returns (bytes32[] memory);
/**
* @notice Get all the permissions for a batch of owners
*
* @param _orgIds The IDs of the organizations
* @param _owners The addresses of the owners
* @return _permissions The IDs of the permissions
*/
function getOwnerPermissionsBatch(
uint256[] memory _orgIds,
address[] memory _owners
) external view returns (bytes32[][] memory _permissions);
/**
* @dev Grant and revoke owner permissions
*/
/**
* @notice Emitted when owner permissions are updated
*
* @param _orgId The ID of the organization
* @param _owner The address of the owner
* @param _grantAccess True if the permissions are being granted, false if they are being revoked
* @param _permissions The IDs of the permissions
*/
event OwnerPermissionsUpdated(
uint256 indexed _orgId,
address indexed _owner,
bool indexed _grantAccess,
bytes32[] _permissions
);
/**
* @notice Grant permissions to the owner
*
* @dev caller can only update their own permissions
* @param _orgId The ID of the organization
* @param _permissions The IDs of the permissions
*/
function addOwnerPermissions(
uint256 _orgId,
bytes32[] memory _permissions
) external;
/**
* @notice Revoke permissions from the owner
*
* @dev caller can only update their own permissions
* @param _orgId The ID of the organization
* @param _permissions The IDs of the permissions
*/
function removeOwnerPermissions(
uint256 _orgId,
bytes32[] memory _permissions
) external;
/**
* @dev Organization permissions
*/
/**
* @notice Is the default permissions excluded for the org?
*
* If true then new pass owners will not be granted the default permissions.
*
* @param _orgId The ID of the organization
* @return True if the default permissions are excluded, false otherwise
*/
function excludeDefaultPermissions(
uint256 _orgId
) external view returns (bool);
/**
* @notice Emitted when the default permissions exclusion is updated for the org
*
* @param _orgId The ID of the organization
* @param _exclude True if the default permissions are excluded, false otherwise
*/
event ExcludeDefaultPermissionsUpdated(
uint256 indexed _orgId,
bool _exclude
);
/**
* @notice Set if the default permissions are excluded for the org during initial purchase
*
* @param _orgId The ID of the organization
* @param _exclude True if the default permissions are excluded, false otherwise
*/
function setExcludeDefaultPermissions(
uint256 _orgId,
bool _exclude
) external;
/**
* @notice Get all the permissions for the organization.
*
* These additional permissions are granted to the pass owner during the initial purchase.
*
* @param _orgId The ID of the organization
* @return _permissions The IDs of the permissions
*/
function getOrgPermissions(
uint256 _orgId
) external view returns (bytes32[] memory);
/**
* @notice Emitted when an organization permission is updated
*
* @param _orgId The ID of the organization
* @param _permission The ID of the permission
* @param _add True if the permission is being added, false if it is being removed
*/
event OrgPermissionUpdated(
uint256 indexed _orgId,
bytes32 _permission,
bool _add
);
/**
* @notice Update the permissions for the organization
*
* @param _orgId The ID of the organization
* @param _permissions The IDs of the permissions
* @param _add True if the permissions are being added, false if they are being removed
*/
function updateOrgPermissions(
uint256 _orgId,
bytes32[] memory _permissions,
bool[] memory _add
) external;
/**
* @notice Grant the initial permissions to the owner
*
* @dev Only the purchase manager can call this function
* @param _orgId The ID of the organization
* @param _owner The address of the owner
*/
function grantInitialOwnerPermissions(
uint256 _orgId,
address _owner
) external;
/**
* @dev Admin functions
*/
/**
* @notice Parameters for updating owner permissions
*
* @param owner The address of the owner
* @param orgId The ID of the organization
* @param permissions The IDs of the permissions
* @param grantAccess True if the permissions are being granted, false if they are being revoked
*/
struct AdminPermissionParams {
address owner;
uint256 orgId;
bytes32[] permissions;
bool grantAccess;
}
/**
* @notice Update the owner permissions
*
* @dev Only the owner can call this function
* @param _params The parameters for updating the owner permissions
*/
function adminUpdateOwnerPermissions(
AdminPermissionParams[] calldata _params
) external;
/**
* @notice Grant the initial permissions to the owners of a batch of passes
*
* This can be used to backfill the initial permissions for all pass owners
* who have already purchased passes.
*
* @dev Only the owner can call this function
* @param _passIds The IDs of the passes
*/
function adminGrantInitialOwnerPermissions(
uint256[] calldata _passIds
) external;
/**
* @notice Set the permission factory
*
* @dev Only the owner can call this function
* @param _permissionFactory The address of the permission factory
*/
function setPermissionFactory(address _permissionFactory) external;
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;
import {PricingUtils} from "../libs/PricingUtils.sol";
interface IPricingRegistry {
/**
* Errors
*/
/**
* @notice Thrown when a pricing configuration is not owned by the organization.
*/
error PricingNotAuthorized();
/**
* @notice Thrown when a pricing configuration is inactive.
*/
error PricingInactive();
/**
* @notice Thrown during checkout validation when multiple pricing configurations have different tokens.
*/
error PricingTokensMismatch();
/**
* @notice Thrown when a pricing configuration is restricted and the product pass owner does not have access.
*/
error PricingRestrictedAccess();
/**
* @notice Thrown when a pricing configuration with usage based charge style required.
*/
error RequiresUsageChargeStyle();
/**
* @notice Thrown when a pricing configuration is not a one time or flat rate charge style.
*/
error RequiresOneTimeOrFlatRateChargeStyle();
/**
* @notice Thrown when a pricing configuration is not an ERC20 token.
*/
error RequiresERC20Token();
/**
* @notice Thrown when a pricing configuration is not whitelisted.
*/
error TokenNotWhitelisted();
/**
* @notice Thrown during checkout validation when a pricing configuration has an invalid quantity for a non tiered pricing configuration.
* @dev All pricing charge styles except TIERED_VOLUME and TIERED_GRADUATED must have a quantity of 0.
*/
error InvalidQuantity();
/**
* Checkout
*/
/**
* Confirm that the pricing configuration can be used in a checkout.
* @param _organizationId The organization ID to validate the pricing configuration for.
* @param _productPassOwner The product pass owner to validate the pricing configuration for.
* @param _pricingId The pricing ID to validate.
* @param _quantity The quantity to validate. Must be 0 unless the pricing configuration is a TIERED_VOLUME or TIERED_GRADUATED charge style.
* @return cycleDuration The cycle duration for the pricing configuration.
*/
function validateCheckout(
uint256 _organizationId,
address _productPassOwner,
uint256 _pricingId,
uint256 _quantity
) external view returns (uint256 cycleDuration);
/**
* @notice Confirm that all the pricing configurations exist, are active, and have the same token for an organization.
* @dev Reverts if the pricing configuration is not valid to be used in a checkout.
* @param _organizationId The organization ID to validate the pricing configuration for.
* @param _productPassOwner The product pass owner to validate the pricing configuration for.
* @param _pricingIds The pricing IDs to validate.
* @param _quantities The quantities to validate. Must be 0 unless the pricing configuration is a TIERED_VOLUME or TIERED_GRADUATED charge style.
* @return token The token to use for the checkout.
* @return cycleDurations The cycle durations for the pricing configurations.
*/
function validateCheckoutBatch(
uint256 _organizationId,
address _productPassOwner,
uint256[] calldata _pricingIds,
uint256[] calldata _quantities
) external view returns (address token, uint256[] memory cycleDurations);
/**
* @notice Validate the pricing configurations exist for an organization.
* @dev Reverts if any of the pricing configurations do not exist for the organization.
* @param _organizationId The organization ID to validate the pricing configurations for.
* @param _pricingIds The pricing IDs to validate.
*/
function validateOrgPricing(
uint256 _organizationId,
uint256[] calldata _pricingIds
) external view;
/**
* Get Pricing Details
*/
/**
* @notice Get the total number of pricing configurations that have been created.
* @return The total number of pricing configurations.
*/
function pricingSupply() external view returns (uint256);
/**
* @notice Get the pricing details for a pricing configuration.
* @dev Reverts if the pricing configuration does not exist.
* @param pricingId The pricing ID to get the pricing details for.
* @return pricing The pricing details for the pricing configuration.
*/
function getPricing(
uint256 pricingId
) external view returns (PricingUtils.Pricing memory);
/**
* @notice Get the pricing details for a batch of pricing configurations.
* @param _pricingIds The pricing IDs to get the pricing details for.
* @return _pricing The pricing details for the pricing configurations.
*/
function getPricingBatch(
uint256[] memory _pricingIds
) external view returns (PricingUtils.Pricing[] memory _pricing);
/**
* @notice Get all pricing IDs for an organization.
* @param organizationId The organization ID to get the pricing IDs for.
* @return pricingIds The pricing IDs for the organization.
*/
function getOrgPricingIds(
uint256 organizationId
) external view returns (uint256[] memory);
/**
* @notice Get all pricing details for an organization.
* @param organizationId The organization ID to get the pricing details for.
* @return pricingIds The pricing IDs for the organization.
* @return pricing The pricing details for the pricing configurations.
*/
function getOrgPricing(
uint256 organizationId
) external view returns (uint256[] memory, PricingUtils.Pricing[] memory);
/**
* Restricted Access
*/
/**
* @notice Get the restricted access for a pricing configuration.
* @param pricingId The pricing ID to get the restricted access for.
* @param productPassOwner The product pass owner to get the restricted access for.
* @return isRestricted True if the address has restricted access, false otherwise.
*/
function restrictedAccess(
uint256 pricingId,
address productPassOwner
) external view returns (bool);
/**
* @notice Grant access to a pricing configuration.
* @param pricingId The pricing ID to grant access to.
* @param productPassOwners The product pass owners to grant access to.
* @param isRestricted True if the address has access, false otherwise.
*/
function setRestrictedAccess(
uint256 pricingId,
address[] calldata productPassOwners,
bool[] calldata isRestricted
) external;
/**
* Pricing Creation
*/
/**
* @notice Emitted when a pricing configuration is created.
* @param organizationId The organization ID that the pricing configuration belongs to.
* @param pricingId The pricing ID that was created.
*/
event PricingCreated(
uint256 indexed organizationId,
uint256 indexed pricingId
);
/**
* @notice Parameters for creating a one time pricing configuration.
* @param organizationId The organization ID that the pricing configuration belongs to.
* @param flatPrice The price for the one time purchase.
* @param token The token to use for the one time purchase. address(0) to use the native token.
* @param isRestricted Whether the pricing configuration is restricted to only addresses with restricted access.
*/
struct CreateOneTimeParams {
uint256 organizationId;
uint256 flatPrice;
address token;
bool isRestricted;
}
/**
* @notice Create a one time pricing configuration.
* @param params The parameters for the one time pricing configuration.
*/
function createOneTimePricing(CreateOneTimeParams calldata params) external;
/**
* @notice Parameters for creating a flat rate recurring subscription pricing configuration.
* @param organizationId The organization ID that the pricing configuration belongs to.
* @param flatPrice The price for the subscription.
* @param token The token to use for the subscription. address(0) to use the native token.
* @param chargeFrequency The frequency of the subscription that it is charged.
* @param isRestricted Whether the pricing configuration is restricted to only addresses with restricted access.
*/
struct CreateFlatRateSubscriptionParams {
uint256 organizationId;
uint256 flatPrice;
address token;
PricingUtils.ChargeFrequency chargeFrequency;
bool isRestricted;
}
/**
* @notice Create a flat rate recurring subscription pricing configuration.
* @param params The parameters for the flat rate recurring subscription pricing configuration.
*/
function createFlatRateSubscriptionPricing(
CreateFlatRateSubscriptionParams calldata params
) external;
/**
* @notice Parameters for creating a tiered subscription pricing configuration.
* @param organizationId The organization ID that the pricing configuration belongs to.
* @param token The token to use for the subscription. address(0) to use the native token.
* @param chargeFrequency The frequency of the subscription that it is charged.
* @param tiers The tiers of the subscription.
* @param isVolume Whether the tiers are volume tiers or graduated tiers.
* @param isRestricted Whether the pricing configuration is restricted to only addresses with restricted access.
*/
struct CreateTieredSubscriptionParams {
uint256 organizationId;
address token;
PricingUtils.ChargeFrequency chargeFrequency;
PricingUtils.PricingTier[] tiers;
bool isVolume;
bool isRestricted;
}
/**
* @notice Create a tiered subscription pricing configuration.
* @param params The parameters for the tiered subscription pricing configuration.
*/
function createTieredSubscriptionPricing(
CreateTieredSubscriptionParams calldata params
) external;
/**
* @notice Parameters for creating a usage based subscription pricing configuration.
* @dev The usage meter ID is the ID of the usage meter that the subscription is based on.
* @param organizationId The organization ID that the pricing configuration belongs to.
* @param token The token to use for the subscription. address(0) to use the native token.
* @param chargeFrequency The frequency of the subscription that it is charged.
* @param tiers The tiers of the subscription.
* @param usageMeterId The usage meter ID that the subscription is based on.
* @param isVolume Whether the tiers are volume tiers or graduated tiers.
* @param isRestricted Whether the pricing configuration is restricted to only addresses with restricted access.
*/
struct CreateUsageBasedSubscriptionParams {
uint256 organizationId;
address token;
PricingUtils.ChargeFrequency chargeFrequency;
PricingUtils.PricingTier[] tiers;
uint256 usageMeterId;
bool isVolume;
bool isRestricted;
}
/**
* @notice Create a usage based subscription pricing configuration.
* @param params The parameters for the usage based subscription pricing configuration.
*/
function createUsageBasedSubscriptionPricing(
CreateUsageBasedSubscriptionParams calldata params
) external;
/**
* Pricing Updates
*/
/**
* @notice Emitted when a pricing configuration is updated.
* @param organizationId The organization ID that the pricing configuration belongs to.
* @param pricingId The pricing ID that was updated.
*/
event PricingUpdated(
uint256 indexed organizationId,
uint256 indexed pricingId
);
/**
* @notice Emitted when a pricing status is changed.
* @param organizationId The organization ID that the pricing configuration belongs to.
* @param pricingId The pricing ID that was updated.
* @param isActive The new status of the pricing configuration.
*/
event PricingStatusChanged(
uint256 indexed organizationId,
uint256 indexed pricingId,
bool isActive
);
/**
* @notice Emitted when restricted access is granted or revoked to a pricing configuration.
* @param organizationId The organization ID that the pricing configuration belongs to.
* @param pricingId The pricing ID that the restricted access was granted to.
* @param productPassOwner The product pass owner that the restricted access was granted to.
* @param isRestricted True if the address has restricted access, false otherwise.
*/
event RestrictedAccessGranted(
uint256 indexed organizationId,
uint256 indexed pricingId,
address indexed productPassOwner,
bool isRestricted
);
/**
* @notice Set the tiers for TIERED or USAGE_BASED pricing configuration.
* @dev Reverts if the pricing configuration is not a TIERED or USAGE_BASED charge style.
* @param pricingId The pricing ID that the tiers are being set for.
* @param tiers The tiers to set for the pricing configuration.
*/
function setPricingTiers(
uint256 pricingId,
PricingUtils.PricingTier[] calldata tiers
) external;
/**
* @notice Reverts when no tiers are found during validation.
*/
error NoTiersFound();
/**
* @notice Reverts when the lower bound is not 1 for a volume tier.
*/
error VolumeLowerBoundMustBeOne();
/**
* @notice Reverts when the lower bound is not 0 for a graduated tier.
*/
error GraduatedLowerBoundMustBeZero();
/**
* @notice Reverts when the lower bound is not one greater than the previous upper bound.
*/
error LowerBoundMustBeOneGreaterThanPreviousUpperBound();
/**
* @notice Reverts when the last tier upper bound is not 0 to represent infinity.
*/
error LastTierUpperBoundMustBeZeroToRepresentInfinity();
/**
* @notice Reverts when the lower bound is greater than the upper bound.
*/
error LowerBoundGreaterThanUpperBound();
/**
* @notice Reverts when the charge style is not valid.
*/
error InvalidChargeStyle();
/**
* @notice Validate the tiers for a pricing configuration.
* @dev Reverts if the tiers are not valid.
* @param tiers The tiers to validate.
* @param chargeStyle The charge style of the pricing configuration.
*/
function validateTiers(
PricingUtils.PricingTier[] calldata tiers,
PricingUtils.ChargeStyle chargeStyle
) external pure;
/**
* @notice Set the token for a pricing configuration.
* @param pricingId The pricing ID that the token is being set for.
* @param token The token to set for the pricing configuration. address(0) to switch back to native token.
*/
function setPricingToken(uint256 pricingId, address token) external;
/**
* @notice Set the flat price for a pricing configuration.
* @dev Reverts if the pricing configuration is not a ONE_TIME or FLAT_RATE charge style.
* @param pricingId The pricing ID that the flat price is being set for.
* @param flatPrice The flat price to set for the pricing configuration.
*/
function setPricingFlatPrice(uint256 pricingId, uint256 flatPrice) external;
/**
* @notice Set the usage meter ID for a pricing configuration.
* @dev Reverts if the pricing configuration is not a USAGE_BASED_VOLUME or USAGE_BASED_GRADUATED charge style.
* @param pricingId The pricing ID that the usage meter ID is being set for.
* @param usageMeterId The usage meter ID to set for the pricing configuration.
*/
function setPricingUsageMeterId(
uint256 pricingId,
uint256 usageMeterId
) external;
/**
* @notice Set the active status for a pricing configuration.
* @dev Pricing must be active to be used in a checkout.
* @param pricingId The pricing ID that the active status is being set for.
* @param isActive The new active status of the pricing configuration.
*/
function setPricingActive(uint256 pricingId, bool isActive) external;
/**
* @notice Set the restricted status for a pricing configuration.
* @dev If pricing is restricted, then only addresses with restricted access can purchase using pricing configuration.
* @param pricingId The pricing ID that the restricted status is being set for.
* @param isRestricted True if the pricing configuration is restricted, false otherwise.
*/
function setPricingRestricted(
uint256 pricingId,
bool isRestricted
) external;
/**
* Pricing Cycle Duration
*/
/**
* @notice Get the cycle duration for a pricing configuration.
* @param pricingId The pricing ID to get the cycle duration for.
* @return cycleDuration The cycle duration for the pricing configuration.
*/
function getCycleDuration(
uint256 pricingId
) external view returns (uint256);
/**
* @notice Get the cycle duration for a batch of pricing configurations.
* @param _pricingIds The pricing IDs to get the cycle duration for.
* @return cycleDurations The cycle durations for the pricing configurations.
*/
function getCycleDurationBatch(
uint256[] calldata _pricingIds
) external view returns (uint256[] memory cycleDurations);
/**
* @notice Get the cycle duration for a charge frequency.
* @param chargeFrequency The charge frequency to get the cycle duration for.
* @return cycleDuration The cycle duration for the charge frequency.
*/
function getChargeFrequencyCycleDuration(
PricingUtils.ChargeFrequency chargeFrequency
) external pure returns (uint256);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;
import {PricingUtils} from "../libs/PricingUtils.sol";
interface IProductRegistry {
/**
* @notice ProductInfo is the information about the product.
* @param orgId The organization ID that the product belongs to.
* @param name The name of the product.
* @param description The description of the product.
* @param imageUrl An image URL for the product.
* @param externalUrl An external URL of the product. (e.g. a link to a product page on a website)
* @param isTransferable True if the product is transferable, else False and the product is soulbound to the NFT owner.
* @param isActive If true, then the product is purchasable, else it is not available for purchase.
*/
struct Product {
uint256 orgId;
string name;
string description;
string imageUrl;
string externalUrl;
bool isTransferable;
bool isActive;
}
/**
* @notice CreateProductParams are the parameters for creating a new product.
* @param orgId The organization ID that the product belongs to.
* @param name The name of the product.
* @param description The description of the product.
* @param imageUrl An image URL for the product. (optional)
* @param externalUrl An external URL of the product. (e.g. a link to a product page on a website) (optional)
* @param isTransferable True if the product is transferable, else False and the product is soulbound to the NFT owner.
*/
struct CreateProductParams {
uint256 orgId;
string name;
string description;
string imageUrl;
string externalUrl;
bool isTransferable;
}
/**
* @notice Get the total number of products that have been created.
* @return The total number of products.
*/
function totalProductSupply() external view returns (uint256);
/**
* @notice ProductNotFoundForOrganization is an error that is reverted when a product is not found for an organization.
*/
error ProductNotFoundForOrganization(
uint256 organizationId,
uint256 productId
);
/**
* @notice PricingNotLinkedToProduct is an error that is reverted when a pricing is not linked to a product.
*/
error PricingNotLinkedToProduct(uint256 productId, uint256 pricingId);
/**
* @notice ProductIsNotActive is an error that is reverted when a product is not active.
*/
error ProductIsNotActive(uint256 productId);
/**
* @notice Check if a product can be purchased.
* @dev Will revert if the product cannot be purchased.
* @param organizationId The organization ID that the product belongs to.
* @param productId The product ID to check if it can be purchased.
* @param pricingId The pricing ID to check if it can be used with the product purchase.
*/
function canPurchaseProduct(
uint256 organizationId,
uint256 productId,
uint256 pricingId
) external view;
/**
* @notice Batch check if multiple products can be purchased.
* @dev Will revert if any of the products cannot be purchased.
* @param _organizationId The organization ID that the products belong to.
* @param _productIds The product IDs to check if they can be purchased.
* @param _pricingIds The pricing IDs to check if they can be used with the product purchases.
*/
function canPurchaseProducts(
uint256 _organizationId,
uint256[] calldata _productIds,
uint256[] calldata _pricingIds
) external view;
/**
* Get Product Details
*/
/**
* @notice Get the product info for a product.
* @param productId The product ID to get the info for.
* @return productInfo The info for the product.
*/
function getProduct(
uint256 productId
) external view returns (Product memory);
/**
* @notice Get the product info for a batch of products.
* @param _productIds The product IDs to get the info for.
* @return _products The info for the products.
*/
function getProductsBatch(
uint256[] memory _productIds
) external view returns (Product[] memory _products);
/**
* @notice Get the names for a batch of products.
* @param _productIds The product IDs to get the names for.
* @return _productNames The names for the products.
*/
function getProductNames(
uint256[] memory _productIds
) external view returns (string[] memory _productNames);
/**
* @notice Get the product IDs for an organization.
* @param _organizationId The organization ID to get the product IDs for.
* @return productIds The product IDs that belong to the organization.
*/
function getOrgProductIds(
uint256 _organizationId
) external view returns (uint256[] memory);
/**
* @notice Get the product info for all products for an organization.
* @param _organizationId The organization ID to get the product info for.
* @return productIds The product IDs that belong to the organization.
* @return products The info for the products.
*/
function getOrgProducts(
uint256 _organizationId
) external view returns (uint256[] memory, Product[] memory);
/**
* @notice Check if a product belongs to an organization.
* @param organizationId The organization ID that the product belongs to.
* @param productId The product ID to check if it belongs to the organization.
* @return isOrgProduct True if the product belongs to the organization, else False.
*/
function isOrgProduct(
uint256 organizationId,
uint256 productId
) external view returns (bool);
/**
* Product Creation
*/
/**
* @notice ValueCannotBeEmpty is an error that is thrown when a value is found to be empty during validation.
*/
error ValueCannotBeEmpty();
/**
* @notice ValueTooLong is an error that is thrown when a value is found to be too long during validation.
*/
error ValueTooLong();
/**
* @notice ProductCreated is an event that is emitted when a product is created.
* @param organizationId The organization ID that the product belongs to.
* @param productId The product ID that was created.
*/
event ProductCreated(
uint256 indexed organizationId,
uint256 indexed productId
);
/**
* @notice Create a new product for an organization.
* @dev Will revert if not an org admin.
* @param params The parameters for creating a new product.
*/
function createProduct(CreateProductParams calldata params) external;
/**
* Product Management
*/
/**
* @notice ProductUpdated is an event that is emitted when a product is updated.
* @param organizationId The organization ID that the product belongs to.
* @param productId The product ID that was updated.
*/
event ProductUpdated(
uint256 indexed organizationId,
uint256 indexed productId
);
/**
* @notice ProductStatusChanged is an event that is emitted when a product's status is changed.
* @param organizationId The organization ID that the product belongs to.
* @param productId The product ID that was updated.
* @param isActive The new status of the product.
*/
event ProductStatusChanged(
uint256 indexed organizationId,
uint256 indexed productId,
bool isActive
);
/**
* @notice Set a new name for the product.
* @dev Will revert if not an org admin or product does not exist.
* @param productId The product ID to set the name for.
* @param name The new name for the product.
*/
function setProductName(uint256 productId, string calldata name) external;
/**
* @notice Set a new description of a product.
* @dev Will revert if not an org admin or product does not exist.
* @param productId The product ID to set the description for.
* @param description The new description for the product.
*/
function setProductDescription(
uint256 productId,
string calldata description
) external;
/**
* @notice Set a new image URL of a product.
* @dev Will revert if not an org admin or product does not exist.
* @param productId The product ID to set the image URL for.
* @param imageUrl The new image URL for the product. Can be empty.
*/
function setProductImageUrl(
uint256 productId,
string calldata imageUrl
) external;
/**
* @notice Set a new external URL of a product.
* @dev Will revert if not an org admin or product does not exist.
* @param productId The product ID to set the external URL for.
* @param externalUrl The new external URL for the product. Can be empty.
*/
function setProductExternalUrl(
uint256 productId,
string calldata externalUrl
) external;
/**
* @notice Set a new transferable status for a product.
* @dev Will revert if not an org admin or product does not exist.
* @param productId The product ID to set the transferable status for.
* @param _isTransferable The new transferable status for the product.
*/
function setProductTransferable(
uint256 productId,
bool _isTransferable
) external;
/**
* @notice Set a new active status for a product. True if the product can be purchased during new checkouts, else it is not available for purchase.
* @dev Will revert if not an org admin or product does not exist.
* @param productId The product ID to set the active status for.
* @param _isActive The new active status for the product.
*/
function setProductActive(uint256 productId, bool _isActive) external;
/**
* Link Products with Pricing
*/
/**
* @notice ProductPricingLinkUpdate is an event that is emitted when a product's pricing is linked or unlinked.
* @param organizationId The organization ID that the product belongs to.
* @param productId The product ID that was updated.
* @param pricingId The pricing ID that was linked or unlinked.
* @param isLinked True if the pricing is linked, else False.
*/
event ProductPricingLinkUpdate(
uint256 indexed organizationId,
uint256 indexed productId,
uint256 pricingId,
bool isLinked
);
/**
* @notice Link a pricing to a product. When a pricing is linked to a product, it can be used in checkouts for the product.
* @dev Will revert if not an org admin, product does not exist, or pricing is not authorized.
* @param productId The product ID to link the pricing to.
* @param pricingIds The pricing IDs to link to the product.
*/
function linkPricing(
uint256 productId,
uint256[] calldata pricingIds
) external;
/**
* @notice Unlink a pricing from a product.
* @dev Will revert if not an org admin, product does not exist, or pricing is not linked to the product.
* NOTE: Even if a pricing is unlinked from a product, subscription renewals using the product and pricing model will still continue to renew.
* @param productId The product ID to unlink the pricing from.
* @param pricingIds The pricing IDs to unlink from the product.
*/
function unlinkPricing(
uint256 productId,
uint256[] calldata pricingIds
) external;
/**
* @notice Get the pricing IDs linked to a product.
* @param productId The product ID to get the pricing IDs for.
* @return pricingIds The pricing IDs that are linked to the product.
*/
function getProductPricingIds(
uint256 productId
) external view returns (uint256[] memory);
/**
* @notice Get all the pricing options for a product.
* @param productId The product ID to get the pricing options for.
* @return pricingIds The pricing IDs that are linked to the product.
* @return pricingOptions The pricing options for the product.
*/
function getProductPricing(
uint256 productId
) external view returns (uint256[] memory, PricingUtils.Pricing[] memory);
/**
* @notice Get all the pricing options for a batch of products.
* @dev This will be expensive to call and should be used with view only.
* @param _productIds The product IDs to get the pricing options for.
* @return pricingIds The pricing IDs that are linked to the products.
* @return pricingOptions The pricing options for the products.
*/
function getProductPricingBatch(
uint256[] memory _productIds
)
external
view
returns (
uint256[][] memory pricingIds,
PricingUtils.Pricing[][] memory pricingOptions
);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;
interface IPurchaseRegistry {
/**
* @notice Get the organization ID that the pass token belongs to.
* @param tokenId The Product Pass Token ID to get the organization ID for.
* @return The organization ID for the pass.
*/
function passOrganization(uint256 tokenId) external view returns (uint256);
/**
* @notice Get the the count for the number of products purchased.
* @param productId The product ID to get the supply for.
* @return The supply of the product across all passes.
*/
function productSupply(uint256 productId) external view returns (uint256);
/**
* @notice Get the max supply for a product. Used to create scarcity.
* @param productId The product ID to get the max supply for.
* @return The max supply for the product. 0 if no limit.
*/
function productMaxSupply(
uint256 productId
) external view returns (uint256);
/**
* @notice Get the total number of products sold for an organization.
* @param organizationId The organization ID to get the total products sold for.
* @return The total products sold across all passes for the organization.
*/
function totalProductsSold(
uint256 organizationId
) external view returns (uint256);
/**
* @notice Get the total number of product passes minted for an organization.
* @param organizationId The organization ID to get the total pass mints for.
* @return The total pass mints for the organization.
*/
function totalPassMints(
uint256 organizationId
) external view returns (uint256);
/**
* @notice Get the product IDs that have been purchased for a pass.
* @param tokenId The Product Pass Token ID to get the product IDs for.
* @return The product IDs for the pass.
*/
function getPassProductIds(
uint256 tokenId
) external view returns (uint256[] memory);
/**
* @notice Check if a pass has purchased a list of products.
* @param tokenId The Product Pass Token ID to check.
* @param productIds The product IDs to check.
* @return True if the pass has purchased all the products, false otherwise.
*/
function hasPassPurchasedProducts(
uint256 tokenId,
uint256[] calldata productIds
) external view returns (bool);
/**
* @notice Get the number of mints for an organization by a single wallet.
* @param organizationId The organization ID that the pass belongs to.
* @param purchaser The wallet that is purchasing the products.
* @return The number of mints by the wallet for the organization.
*/
function passMintCount(
uint256 organizationId,
address purchaser
) external view returns (uint256);
/**
* @notice Get the max mints for an organization.
* @param organizationId The organization ID to get the max mints for.
* @return The max amount of product passes that can be minted by a single wallet for the organization.
*/
function maxMints(uint256 organizationId) external view returns (uint256);
/**
* @notice Get the whitelist status for an organization.
* @param organizationId The organization ID to get the whitelist status for.
* @return True if the organization is whitelist only, false otherwise.
*/
function isWhitelist(uint256 organizationId) external view returns (bool);
/**
* @notice Get the mint closed status for an organization.
* @dev When the mint is closed, no passes can be purchased for the organization regardless of whitelist status.
* @param organizationId The organization ID to get the mint closed status for.
* @return True if the mint is closed, false otherwise.
*/
function isMintClosed(uint256 organizationId) external view returns (bool);
/**
* Record Purchase
*/
/**
* @notice Revert for when a product is already added to the pass. Prevent duplicate purchases.
*/
error ProductAlreadyAdded();
/**
* @notice Revert for when the max supply is reached.
*/
error MaxSupplyReached();
/**
* @notice Revert for when the organization is invalid for the pass.
*/
error InvalidOrganization();
/**
* @notice Revert for when the max mints are reached.
*/
error MaxMintsReached();
/**
* @notice Revert for when the address is not whitelisted when attempting to purchase a pass in a whitelist only organization.
*/
error AddressNotWhitelisted();
/**
* @notice Revert for when the mint is closed and no passes can be minted or additional products can be added to the pass.
*/
error MintClosed();
/**
* @notice Revert for when the gifting is disabled for an organization.
*/
error GiftingIsDisabled(uint256 organizationId);
/**
* @notice Record a product purchase and link the products to the pass.
* @dev Only the purchase manager can record a product purchase.
* @param _organizationId The organization ID that the product belongs to.
* @param _passId The Product Pass ID to be used in the purchase.
* @param _passOwner The owner of the pass.
* @param _purchaser The wallet that is purchasing the products.
* @param _productIds The product IDs to be used in the purchase.
* @param _pricingIds The pricing IDs to be used in the purchase.
*/
function recordProductPurchase(
uint256 _organizationId,
uint256 _passId,
address _passOwner,
address _purchaser,
uint256[] calldata _productIds,
uint256[] calldata _pricingIds
) external;
/**
* Max Supply
*/
/**
* @notice Emitted for when the max supply is updated.
* @param organizationId The organization ID that the product belongs to.
* @param productId The product ID to set the max supply for.
* @param maxSupply The new max supply for the product. 0 if no limit.
*/
event ProductMaxSupplyUpdated(
uint256 indexed organizationId,
uint256 indexed productId,
uint256 maxSupply
);
/**
* @notice Set a new max supply for a product.
* @dev Will revert if not an org admin or product does not exist or max supply is less than current supply.
* @param organizationId The organization ID that the product belongs to.
* @param productId The product ID to set the max supply for.
* @param _maxSupply The new max supply for the product. 0 if no limit.
*/
function setProductMaxSupply(
uint256 organizationId,
uint256 productId,
uint256 _maxSupply
) external;
/**
* Max Mints
*/
/**
* @notice Emitted when max mints are updated for an organization.
* @param organizationId The organization ID to update the max mints for.
* @param maxMints The new max mints for the organization. 0 if no limit.
*/
event MaxMintsUpdated(uint256 indexed organizationId, uint256 maxMints);
/**
* @notice Set a new max mint amount for an organization.
* @dev NOTE: When lowering the limit, it will not affect existing wallets that have already minted.
* @param organizationId The organization ID to set the max mints for.
* @param _maxMints The new max mints for the organization. 0 if no limit.
*/
function setMaxMints(uint256 organizationId, uint256 _maxMints) external;
/**
* Whitelist
*/
/**
* @notice Emitted when the whitelist status for an organization is updated.
* @param organizationId The organization ID to update the whitelist for.
* @param isWhitelist True if the organization is whitelist only, false otherwise.
*/
event WhitelistStatusChanged(
uint256 indexed organizationId,
bool isWhitelist
);
/**
* @notice Set a new whitelist status for an organization.
* @param organizationId The organization ID to set the whitelist for.
* @param _isWhitelist True if the organization is whitelist only, false otherwise.
*/
function setWhitelist(uint256 organizationId, bool _isWhitelist) external;
/**
* @notice Emitted when the whitelist is updated for an address.
* @param organizationId The organization ID to update the whitelist for.
* @param passOwner The address to update the whitelist for.
* @param isWhitelisted The new whitelist status for the address.
*/
event WhitelistPassOwnerUpdated(
uint256 indexed organizationId,
address indexed passOwner,
bool isWhitelisted
);
/**
* @notice Whitelist addresses for an organization.
* @dev When an org level whitelist is enabled, only whitelisted addresses can purchase passes.
* @param organizationId The organization ID to update the whitelist for.
* @param _addresses The addresses to update the whitelist for.
* @param _isWhitelisted The new whitelist status for the addresses.
*/
function whitelistPassOwners(
uint256 organizationId,
address[] calldata _addresses,
bool[] calldata _isWhitelisted
) external;
/**
* @notice Get the whitelist status for an organization and a purchaser.
* @param orgId The organization ID to get the whitelist status for.
* @param purchaser The purchaser to get the whitelist status for.
* @return True if the purchaser is whitelisted, false otherwise.
*/
function whitelisted(
uint256 orgId,
address purchaser
) external view returns (bool);
/**
* Mint Closed
*/
/**
* @notice Emitted when the mint closed status for an organization is updated.
* @param organizationId The organization ID to update the mint closed status for.
* @param isMintClosed True if the mint is closed, false otherwise.
*/
event MintClosedStatusChanged(
uint256 indexed organizationId,
bool isMintClosed
);
/**
* @notice Set a new mint closed status for an organization.
* @param organizationId The organization ID to set the mint closed status for.
* @param _isMintClosed True if the mint is closed, false otherwise.
*/
function setMintClosed(uint256 organizationId, bool _isMintClosed) external;
/**
* Gifting
*/
/**
* @notice Emitted when the gifting status for an organization is updated.
* @param organizationId The organization ID to update the gifting status for.
* @param isGifting True if the organization allows product passes to be gifted to other addresses, false otherwise.
*/
event GiftingStatusChanged(uint256 indexed organizationId, bool isGifting);
/**
* @notice Get the gifting status for an organization.
* @param orgId The organization ID to get the gifting status for.
* @return True if the organization allows product passes to be gifted to other addresses, false otherwise.
*/
function isGiftingEnabled(uint256 orgId) external view returns (bool);
/**
* @notice Set a new gifting status for an organization.
* @param organizationId The organization ID to set the gifting status for.
* @param _isGifting True if the organization allows product passes to be gifted to other addresses, false otherwise.
*/
function setGiftingEnabled(
uint256 organizationId,
bool _isGifting
) external;
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;
interface IDynamicPriceRouter {
/**
* @notice Get the name of the underlying price router.
* @return The name of the underlying price router. i.e. "uniswap-v2" or "uniswap-v3"
*/
function ROUTER_NAME() external view returns (string memory);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;
import {IDynamicPriceRouter} from "./IDynamicPriceRouter.sol";
/**
* @title IUniswapV2DynamicPriceRouter
* @notice Interface for a dynamic price router that uses Uniswap V2.
*/
interface IUniswapV2DynamicPriceRouter is IDynamicPriceRouter {
/**
* @notice Get the direct swap price for the final token in the given path with Uniswap fees included.
* @param amountIn The amount of token to convert.
* @param path The path to use for the conversion.
* @return The amount of token at the end of the path received.
*/
function getPrice(
uint256 amountIn,
address[] calldata path
) external view returns (uint256);
/**
* @notice Get the direct swap price for the final token in the given path with Uniswap fees excluded.
* @dev We do a best approximation of the price without fees.
* @param amountIn The amount of token to convert.
* @param path The path to use for the conversion.
* @return The amount of token at the end of the path received.
*/
function getPriceFeesRemoved(
uint256 amountIn,
address[] calldata path
) external view returns (uint256);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;
import {IDynamicPriceRouter} from "./IDynamicPriceRouter.sol";
/**
* @title IUniswapV3DynamicPriceRouter
* @notice Interface for a dynamic price router that uses Uniswap V3.
*/
interface IUniswapV3DynamicPriceRouter is IDynamicPriceRouter {
/**
* Pricing
*/
/**
* @notice Get the swap price for the final token in the given path with Uniswap fees included.
* @param amountIn The amount of token to convert.
* @param path The path to use for the conversion.
* @return The amount of token at the end of the path received.
*/
function getPrice(
uint256 amountIn,
bytes calldata path
) external returns (uint256);
/**
* @notice Get the swap price for the final token in the given path with Uniswap fees excluded.
* @dev We do a best approximation of the price without fees.
* @param amountIn The amount of token to convert.
* @param path The path to use for the conversion.
* @param fees The fees for each pool in the path. Used to calculate the fee-free price.
* @return The amount of token at the end of the path received.
*/
function getPriceFeesRemoved(
uint256 amountIn,
bytes calldata path,
Fee[] calldata fees
) external returns (uint256);
/**
* Fees
*/
/**
* @notice The Uniswap V3 fee options.
*/
enum Fee {
LOWEST_001, // 0.01%
LOW_005, // 0.05%
MEDIUM_03, // 0.3%
HIGH_1 // 1%
}
/**
* @notice Get the Uniswap V3 fee for a given fee option.
* @param fee The fee option.
* @return The fee as a percentage of the amount out.
* @dev 0.01% = 100, 0.05% = 500, 0.3% = 3000, 1% = 10000.
*/
function getFee(Fee fee) external pure returns (uint256);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;
/**
* @title IDynamicERC20
* @notice An interface for a DynamicERC20 contract allowing for targeted pricing against a quote token.
*/
interface IDynamicERC20 {
/**
* @notice Get the name of the dynamic price router
* @return The name of the dynamic price router
*/
function routerName() external view returns (string memory);
/**
* @notice Get the address of the dynamic price router
* @return The address of the dynamic price router
*/
function dynamicPriceRouter() external view returns (address);
/**
* @notice The ERC20 token used to charge for payment
* @return The contract address of the base token
*/
function baseToken() external view returns (address);
/**
* @dev The ERC20 token used for price targeting
* @return The contract address of the quote token
*/
function quoteToken() external view returns (address);
/**
* @notice Get the path used to convert the base token to the quote token
* @return The path used to pass through the dex
*/
function getBaseToQuotePath() external view returns (address[] memory);
/**
* @notice Get the path used to convert the quote token to the base token
* @return The path used to pass through the dex
*/
function getQuoteToBasePath() external view returns (address[] memory);
/**
* @notice Get the current swap price of the base token in terms of the quote token
* @return The amount of quote token per 1 base token
*/
function getBaseTokenPrice() external returns (uint256);
/**
* @notice Get the balance of the base token in terms of the quote token pricing
* @param account The address to get the balance of
* @return The balance of the base token in quote token terms
*/
function balanceOfQuote(address account) external returns (uint256);
/**
* @notice Get the allowance of the base token in terms of the quote token pricing
* @param owner The address to get the allowance of
* @return The allowance of the base token in quote token terms
*/
function allowanceQuote(
address owner,
address spender
) external returns (uint256);
/**
* @notice Get the amount of base tokens that would be received for a given amount of quote tokens
* @param quoteTokenAmount The amount of quote tokens to convert to base tokens
* @return baseToken The address of the base token
* @return baseTokenAmount The amount of base tokens that would be received
*/
function getBaseTokenAmount(
uint256 quoteTokenAmount
) external returns (address baseToken, uint256 baseTokenAmount);
/**
* @notice Get the amount of quote tokens that would be received for a given amount of base tokens
* @param baseTokenAmount The amount of base tokens to convert to quote tokens
* @return quoteToken The address of the quote token
* @return quoteTokenAmount The amount of quote tokens that would be received
*/
function getQuoteTokenAmount(
uint256 baseTokenAmount
) external returns (address quoteToken, uint256 quoteTokenAmount);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;
interface IProductPassNFT {
/**
* Revert when attempting to transfer a product pass with products that are not transferable.
*/
error ProductsNotTransferable();
/**
* Revert when attempting to transfer a product pass with subscriptions that are not transferable.
*/
error SubscriptionsNotTransferable();
/**
* Mint a product pass to an address.
* @dev Only the purchase manager can mint a product pass.
* @param to The address to mint the product pass to.
* @param tokenId The token ID of the product pass to mint.
*/
function mint(address to, uint256 tokenId) external;
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.24;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {Ownable2Step} from "@openzeppelin/contracts/access/Ownable2Step.sol";
import {
IERC20Metadata
} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {DynamicERC20} from "../abstract/DynamicERC20.sol";
import {
IUniswapV3DynamicPriceRouter
} from "../router/IUniswapV3DynamicPriceRouter.sol";
/*
____ _ _ __ __ _ _
| _ \ _ __ ___ __| |_ _ ___| |_| \/ (_)_ __ | |_
| |_) | '__/ _ \ / _` | | | |/ __| __| |\/| | | '_ \| __|
| __/| | | (_) | (_| | |_| | (__| |_| | | | | | | | |_
|_| |_| \___/ \__,_|\__,_|\___|\__|_| |_|_|_| |_|\__|
NFT based payment system to mint products onchain with one-time payments and
recurring permissionless subscriptions.
https://productmint.io
*/
/**
* @title UniswapV3DynamicERC20
* @notice A dynamic ERC20 token that uses Uniswap V3 to get the current swap price.
* @dev A UniswapV3DynamicERC20 cannot be minted, burned, or transferred
*
* Used within the ProductMint system to act as a proxy for the base token against the quote token.
* The base token is used to charge for payment.
* The quote token is used for price targeting.
* A dynamic price router is used to get the current swap price at a dex such as Uniswap.
*
* For example, assume the base token is WETH and the quote token is USDC.
* An organization can use the DynamicERC20 to create a pricing model that targets a price of 100 USDC.
* Then, when a user purchases a product, 100 USDC worth of WETH will be transferred to the organization.
*/
contract UniswapV3DynamicERC20 is DynamicERC20, Ownable2Step {
// Path used to convert the base token to the quote token
bytes public baseToQuotePathEncoded;
// Path used to convert the quote token to the base token
bytes public quoteToBasePathEncoded;
// Fees for each pool in the base to quote path
IUniswapV3DynamicPriceRouter.Fee[] private baseToQuoteFees;
// Fees for each pool in the quote to base path
IUniswapV3DynamicPriceRouter.Fee[] private quoteToBaseFees;
constructor(
string memory _name,
string memory _symbol,
address _baseToken,
address _quoteToken,
address _dynamicPriceRouter,
address[] memory _baseToQuotePath,
address[] memory _quoteToBasePath,
IUniswapV3DynamicPriceRouter.Fee[] memory _baseToQuoteFees,
IUniswapV3DynamicPriceRouter.Fee[] memory _quoteToBaseFees
)
DynamicERC20(
_name,
_symbol,
_baseToken,
_quoteToken,
_dynamicPriceRouter
)
Ownable(_msgSender())
{
_setBaseToQuotePath(_baseToQuotePath, _baseToQuoteFees);
_setQuoteToBasePath(_quoteToBasePath, _quoteToBaseFees);
}
/**
* IDynamicERC20
*/
function getBaseTokenPrice() external returns (uint256) {
return _getQuoteTokenAmount(10 ** IERC20Metadata(baseToken).decimals());
}
function balanceOfQuote(address account) external returns (uint256) {
return _getQuoteTokenAmount(IERC20(baseToken).balanceOf(account));
}
function allowanceQuote(
address owner,
address spender
) external returns (uint256) {
return
_getQuoteTokenAmount(IERC20(baseToken).allowance(owner, spender));
}
function getBaseTokenAmount(
uint256 quoteTokenAmount
) external returns (address, uint256) {
return (baseToken, _getBaseTokenAmount(quoteTokenAmount));
}
function getQuoteTokenAmount(
uint256 baseTokenAmount
) external returns (address, uint256) {
return (quoteToken, _getQuoteTokenAmount(baseTokenAmount));
}
function _getBaseTokenAmount(uint256 amount) internal returns (uint256) {
if (amount == 0) return 0;
return
IUniswapV3DynamicPriceRouter(dynamicPriceRouter)
.getPriceFeesRemoved(
amount,
quoteToBasePathEncoded,
quoteToBaseFees
);
}
function _getQuoteTokenAmount(uint256 amount) internal returns (uint256) {
if (amount == 0) return 0;
return
IUniswapV3DynamicPriceRouter(dynamicPriceRouter)
.getPriceFeesRemoved(
amount,
baseToQuotePathEncoded,
baseToQuoteFees
);
}
/**
* Base to quote path
*/
function getBaseToQuoteFees()
external
view
returns (IUniswapV3DynamicPriceRouter.Fee[] memory)
{
return baseToQuoteFees;
}
/**
* @notice Emitted when the base to quote path is set
* @param dynamicERC20 The address of the current dynamic ERC20 contract
* @param baseToken The address of the base token
* @param quoteToken The address of the quote token
* @param path The path used to convert the base token to the quote token
* @param pathEncoded The encoded path
* @param fees The fees for each pool in the path
*/
event UniswapV3BaseToQuotePathSet(
address indexed dynamicERC20,
address indexed baseToken,
address indexed quoteToken,
address[] path,
bytes pathEncoded,
IUniswapV3DynamicPriceRouter.Fee[] fees
);
function setBaseToQuotePath(
address[] memory _path,
IUniswapV3DynamicPriceRouter.Fee[] memory _fees
) external onlyOwner {
_setBaseToQuotePath(_path, _fees);
}
function _setBaseToQuotePath(
address[] memory _path,
IUniswapV3DynamicPriceRouter.Fee[] memory _fees
) internal {
_checkBaseToQuotePath(_path);
_checkFees(_path, _fees);
bytes memory pathEncoded = _encodePath(_path, _fees);
baseToQuotePathEncoded = pathEncoded;
baseToQuotePath = _path;
baseToQuoteFees = _fees;
emit UniswapV3BaseToQuotePathSet(
address(this),
baseToken,
quoteToken,
_path,
pathEncoded,
_fees
);
}
/**
* Quote to base path
*/
function getQuoteToBaseFees()
external
view
returns (IUniswapV3DynamicPriceRouter.Fee[] memory)
{
return quoteToBaseFees;
}
/**
* @notice Emitted when the quote to base path is set
* @param dynamicERC20 The address of the current dynamic ERC20 contract
* @param baseToken The address of the base token
* @param quoteToken The address of the quote token
* @param path The path used to convert the quote token to the base token
* @param pathEncoded The encoded path
* @param fees The fees for each pool in the path
*/
event UniswapV3QuoteToBasePathSet(
address indexed dynamicERC20,
address indexed baseToken,
address indexed quoteToken,
address[] path,
bytes pathEncoded,
IUniswapV3DynamicPriceRouter.Fee[] fees
);
function setQuoteToBasePath(
address[] memory _path,
IUniswapV3DynamicPriceRouter.Fee[] memory _fees
) external onlyOwner {
_setQuoteToBasePath(_path, _fees);
}
function _setQuoteToBasePath(
address[] memory _path,
IUniswapV3DynamicPriceRouter.Fee[] memory _fees
) internal {
_checkQuoteToBasePath(_path);
_checkFees(_path, _fees);
bytes memory pathEncoded = _encodePath(_path, _fees);
quoteToBasePathEncoded = pathEncoded;
quoteToBasePath = _path;
quoteToBaseFees = _fees;
emit UniswapV3QuoteToBasePathSet(
address(this),
baseToken,
quoteToken,
_path,
pathEncoded,
_fees
);
}
/**
* Path updates
*/
/**
* @dev Error when attempting to set an invalid path
*/
error InvalidPath(address[] _path);
function _encodePath(
address[] memory _path,
IUniswapV3DynamicPriceRouter.Fee[] memory _fees
) internal returns (bytes memory pathEncoded) {
IUniswapV3DynamicPriceRouter _router = IUniswapV3DynamicPriceRouter(
dynamicPriceRouter
);
for (uint256 i = 0; i < _path.length - 1; i++) {
pathEncoded = abi.encodePacked(
pathEncoded,
_path[i],
uint24(_router.getFee(_fees[i]))
);
}
pathEncoded = abi.encodePacked(pathEncoded, _path[_path.length - 1]);
try
_router.getPriceFeesRemoved(
10 ** IERC20Metadata(_path[0]).decimals(),
pathEncoded,
_fees
)
{} catch {
revert InvalidPath(_path);
}
}
function _checkFees(
address[] memory _path,
IUniswapV3DynamicPriceRouter.Fee[] memory _fees
) internal pure {
require(
_fees.length == _path.length - 1,
"Fees length must match hops"
);
}
/**
* Dynamic price router updates
*/
function setDynamicPriceRouter(
address _dynamicPriceRouter
) external onlyOwner {
_setDynamicPriceRouter(_dynamicPriceRouter);
}
function _setDynamicPriceRouter(
address _dynamicPriceRouter
) internal override {
require(
IERC165(_dynamicPriceRouter).supportsInterface(
type(IUniswapV3DynamicPriceRouter).interfaceId
),
"Does not implement IUniswapV3DynamicPriceRouter"
);
super._setDynamicPriceRouter(_dynamicPriceRouter);
}
}{
"optimizer": {
"enabled": true,
"runs": 200
},
"evmVersion": "paris",
"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":"string","name":"_name","type":"string"},{"internalType":"string","name":"_symbol","type":"string"},{"internalType":"address","name":"_baseToken","type":"address"},{"internalType":"address","name":"_quoteToken","type":"address"},{"internalType":"address","name":"_dynamicPriceRouter","type":"address"},{"internalType":"address[]","name":"_baseToQuotePath","type":"address[]"},{"internalType":"address[]","name":"_quoteToBasePath","type":"address[]"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ApproveNotAllowed","type":"error"},{"inputs":[{"internalType":"address[]","name":"_path","type":"address[]"}],"name":"InvalidPath","type":"error"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"OwnableInvalidOwner","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"OwnableUnauthorizedAccount","type":"error"},{"inputs":[],"name":"TransferNotAllowed","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"dynamicERC20","type":"address"},{"indexed":true,"internalType":"address","name":"dynamicPriceRouter","type":"address"}],"name":"DynamicPriceRouterSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferStarted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"dynamicERC20","type":"address"},{"indexed":true,"internalType":"address","name":"baseToken","type":"address"},{"indexed":true,"internalType":"address","name":"quoteToken","type":"address"},{"indexed":false,"internalType":"address[]","name":"baseToQuotePath","type":"address[]"}],"name":"UniswapV2BaseToQuotePathSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"dynamicERC20","type":"address"},{"indexed":true,"internalType":"address","name":"baseToken","type":"address"},{"indexed":true,"internalType":"address","name":"quoteToken","type":"address"},{"indexed":false,"internalType":"address[]","name":"quoteToBasePath","type":"address[]"}],"name":"UniswapV2QuoteToBasePathSet","type":"event"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowanceQuote","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"balanceOfQuote","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"baseToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"dynamicPriceRouter","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getBaseToQuotePath","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"quoteTokenAmount","type":"uint256"}],"name":"getBaseTokenAmount","outputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getBaseTokenPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getQuoteToBasePath","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"baseTokenAmount","type":"uint256"}],"name":"getQuoteTokenAmount","outputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"quoteToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"routerName","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"_baseToQuotePath","type":"address[]"}],"name":"setBaseToQuotePath","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_dynamicPriceRouter","type":"address"}],"name":"setDynamicPriceRouter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_quoteToBasePath","type":"address[]"}],"name":"setQuoteToBasePath","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
60c06040523480156200001157600080fd5b5060405162002a1638038062002a16833981016040819052620000349162000a99565b3387878787876001600160a01b038316620000a05760405162461bcd60e51b815260206004820152602160248201527f4261736520746f6b656e2063616e6e6f74206265207a65726f206164647265736044820152607360f81b60648201526084015b60405180910390fd5b6001600160a01b038216620001035760405162461bcd60e51b815260206004820152602260248201527f51756f746520746f6b656e2063616e6e6f74206265207a65726f206164647265604482015261737360f01b606482015260840162000097565b816001600160a01b0316836001600160a01b031603620001765760405162461bcd60e51b815260206004820152602760248201527f4261736520616e642071756f746520746f6b656e2063616e6e6f74206265207460448201526668652073616d6560c81b606482015260840162000097565b600062000184868262000c1b565b50600162000193858262000c1b565b506001600160a01b03808416608052821660a052620001b28162000214565b5050506001600160a01b0383169150620001e5905057604051631e4fbdf760e01b81526000600482015260240162000097565b620001f081620002fa565b50620001fc8262000315565b62000207816200039f565b5050505050505062000f26565b6040516301ffc9a760e01b815263b130abe360e01b60048201526001600160a01b038216906301ffc9a790602401602060405180830381865afa15801562000260573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019062000286919062000ce7565b620002ec5760405162461bcd60e51b815260206004820152602f60248201527f446f6573206e6f7420696d706c656d656e742049556e6973776170563244796e60448201526e30b6b4b1a83934b1b2a937baba32b960891b606482015260840162000097565b620002f7816200041e565b50565b600680546001600160a01b0319169055620002f7816200046a565b6200032081620004bc565b6200032b8162000610565b80516200034090600290602084019062000894565b5060a0516001600160a01b03166080516001600160a01b0316306001600160a01b03167f25b4c0059a7a45c3fb2e9a613393e8a0b8339d669153d7add5c83611d89074808460405162000394919062000d59565b60405180910390a450565b620003aa8162000736565b620003b58162000610565b8051620003ca90600390602084019062000894565b5060a0516001600160a01b03166080516001600160a01b0316306001600160a01b03167f3105ebf8cfe0d8803d5b103773a21806a6c43fa57501763b771be3db158bfcd58460405162000394919062000d59565b600480546001600160a01b0319166001600160a01b03831690811790915560405130907fd208b3f2137ba34e4b2ea29e6e0558a1395796409f6b8328f2cbd5ff9159bc1590600090a350565b600580546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b6001815111620004fe5760405162461bcd60e51b81526020600482018190526024820152600080516020620029f6833981519152604482015260640162000097565b6080516001600160a01b03168160008151811062000520576200052062000d6e565b60200260200101516001600160a01b031614620005805760405162461bcd60e51b815260206004820181905260248201527f4261736520746f6b656e206d75737420626520666972737420696e2070617468604482015260640162000097565b60a0516001600160a01b031681600183516200059d919062000d9a565b81518110620005b057620005b062000d6e565b60200260200101516001600160a01b031614620002f75760405162461bcd60e51b815260206004820181905260248201527f51756f746520746f6b656e206d757374206265206c61737420696e2070617468604482015260640162000097565b60045481516001600160a01b039091169063bd3f03f99083906000906200063b576200063b62000d6e565b60200260200101516001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa15801562000681573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190620006a7919062000db6565b620006b490600a62000ed8565b836040518363ffffffff1660e01b8152600401620006d492919062000ee9565b602060405180830381865afa92505050801562000710575060408051601f3d908101601f191682019092526200070d9181019062000f0c565b60015b6200073257806040516354da4f1960e01b815260040162000097919062000d59565b5050565b6001815111620007785760405162461bcd60e51b81526020600482018190526024820152600080516020620029f6833981519152604482015260640162000097565b60a0516001600160a01b0316816000815181106200079a576200079a62000d6e565b60200260200101516001600160a01b031614620008045760405162461bcd60e51b815260206004820152602160248201527f51756f746520746f6b656e206d75737420626520666972737420696e207061746044820152600d60fb1b606482015260840162000097565b6080516001600160a01b0316816001835162000821919062000d9a565b8151811062000834576200083462000d6e565b60200260200101516001600160a01b031614620002f75760405162461bcd60e51b815260206004820152601f60248201527f4261736520746f6b656e206d757374206265206c61737420696e207061746800604482015260640162000097565b828054828255906000526020600020908101928215620008ec579160200282015b82811115620008ec57825182546001600160a01b0319166001600160a01b03909116178255602090920191600190910190620008b5565b50620008fa929150620008fe565b5090565b5b80821115620008fa5760008155600101620008ff565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b038111828210171562000956576200095662000915565b604052919050565b600082601f8301126200097057600080fd5b81516001600160401b038111156200098c576200098c62000915565b6020620009a2601f8301601f191682016200092b565b8281528582848701011115620009b757600080fd5b60005b83811015620009d7578581018301518282018401528201620009ba565b506000928101909101919091529392505050565b80516001600160a01b038116811462000a0357600080fd5b919050565b600082601f83011262000a1a57600080fd5b815160206001600160401b0382111562000a385762000a3862000915565b8160051b62000a498282016200092b565b928352848101820192828101908785111562000a6457600080fd5b83870192505b8483101562000a8e5762000a7e83620009eb565b8252918301919083019062000a6a565b979650505050505050565b600080600080600080600060e0888a03121562000ab557600080fd5b87516001600160401b038082111562000acd57600080fd5b62000adb8b838c016200095e565b985060208a015191508082111562000af257600080fd5b62000b008b838c016200095e565b975062000b1060408b01620009eb565b965062000b2060608b01620009eb565b955062000b3060808b01620009eb565b945060a08a015191508082111562000b4757600080fd5b62000b558b838c0162000a08565b935060c08a015191508082111562000b6c57600080fd5b5062000b7b8a828b0162000a08565b91505092959891949750929550565b600181811c9082168062000b9f57607f821691505b60208210810362000bc057634e487b7160e01b600052602260045260246000fd5b50919050565b601f82111562000c16576000816000526020600020601f850160051c8101602086101562000bf15750805b601f850160051c820191505b8181101562000c125782815560010162000bfd565b5050505b505050565b81516001600160401b0381111562000c375762000c3762000915565b62000c4f8162000c48845462000b8a565b8462000bc6565b602080601f83116001811462000c87576000841562000c6e5750858301515b600019600386901b1c1916600185901b17855562000c12565b600085815260208120601f198616915b8281101562000cb85788860151825594840194600190910190840162000c97565b508582101562000cd75787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b60006020828403121562000cfa57600080fd5b8151801515811462000d0b57600080fd5b9392505050565b60008151808452602080850194506020840160005b8381101562000d4e5781516001600160a01b03168752958201959082019060010162000d27565b509495945050505050565b60208152600062000d0b602083018462000d12565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b8181038181111562000db05762000db062000d84565b92915050565b60006020828403121562000dc957600080fd5b815160ff8116811462000d0b57600080fd5b600181815b8085111562000e1c57816000190482111562000e005762000e0062000d84565b8085161562000e0e57918102915b93841c939080029062000de0565b509250929050565b60008262000e355750600162000db0565b8162000e445750600062000db0565b816001811462000e5d576002811462000e685762000e88565b600191505062000db0565b60ff84111562000e7c5762000e7c62000d84565b50506001821b62000db0565b5060208310610133831016604e8410600b841016171562000ead575081810a62000db0565b62000eb9838362000ddb565b806000190482111562000ed05762000ed062000d84565b029392505050565b600062000d0b60ff84168362000e24565b82815260406020820152600062000f04604083018462000d12565b949350505050565b60006020828403121562000f1f57600080fd5b5051919050565b60805160a051611a3362000fc36000396000818161023f015281816105f6015281816107d701528181610cde01528181610db50152818161104e01526112aa0152600081816103c001528181610552015281816106fa0152818161088b0152818161097c01528181610a1601528181610b3a01528181610bac01528181610d0801528181610ddf01528181610fb3015261134f0152611a336000f3fe608060405234801561001057600080fd5b50600436106101cf5760003560e01c806379ba509711610104578063aebab157116100a2578063dd62ed3e11610071578063dd62ed3e146103ea578063e30c3978146103fd578063e71dc5e51461040e578063f2fde38b1461042157600080fd5b8063aebab15714610395578063be41d5bc146103a8578063c55dae63146103bb578063cb27a434146103e257600080fd5b80638da5cb5b116100de5780638da5cb5b1461035b57806395d89b411461036c5780639bd230f314610374578063a9059cbb1461038757600080fd5b806379ba50971461032d5780637a19c83d146103355780638caecde51461034857600080fd5b806339ff2da2116101715780636702fcac1161014b5780636702fcac146102cb5780636bb26dba146102fd57806370a0823114610312578063715018a61461032557600080fd5b806339ff2da2146102a65780634971c79a146102ae5780634b230342146102b657600080fd5b806318160ddd116101ad57806318160ddd14610224578063217a4b701461023a57806323b872dd14610279578063313ce5671461028c57600080fd5b806301ffc9a7146101d457806306fdde03146101fc578063095ea7b314610211575b600080fd5b6101e76101e23660046114ba565b610434565b60405190151581526020015b60405180910390f35b6102046104a1565b6040516101f39190611508565b6101e761021f366004611557565b610533565b61022c61054e565b6040519081526020016101f3565b6102617f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020016101f3565b6101e7610287366004611581565b6105d7565b6102946105f2565b60405160ff90911681526020016101f3565b610204610676565b61022c6106f3565b6102c96102c43660046115bd565b61078a565b005b6102de6102d9366004611632565b6107d2565b604080516001600160a01b0390931683526020830191909152016101f3565b610305610808565b6040516101f39190611690565b61022c6103203660046116a3565b610869565b6102c96108f9565b6102c961090d565b61022c6103433660046116a3565b610956565b61022c6103563660046116be565b6109e8565b6005546001600160a01b0316610261565b610204610a4c565b6102c96103823660046115bd565b610a5b565b6101e7610287366004611557565b6102c96103a33660046116a3565b610a9f565b600454610261906001600160a01b031681565b6102617f000000000000000000000000000000000000000000000000000000000000000081565b610305610ab0565b61022c6103f83660046116be565b610b10565b6006546001600160a01b0316610261565b6102de61041c366004611632565b610ba7565b6102c961042f3660046116a3565b610bd4565b60006001600160e01b03198216631f9aa5f160e31b148061046557506001600160e01b031982166336372b0760e01b145b8061048057506001600160e01b0319821663a219a02560e01b145b8061049b57506301ffc9a760e01b6001600160e01b03198316145b92915050565b6060600080546104b0906116f1565b80601f01602080910402602001604051908101604052809291908181526020018280546104dc906116f1565b80156105295780601f106104fe57610100808354040283529160200191610529565b820191906000526020600020905b81548152906001019060200180831161050c57829003601f168201915b5050505050905090565b6000604051632028747160e01b815260040160405180910390fd5b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156105ae573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105d2919061172b565b905090565b6000604051638cd22d1960e01b815260040160405180910390fd5b60007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015610652573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105d29190611744565b6060600460009054906101000a90046001600160a01b03166001600160a01b0316630e05f6766040518163ffffffff1660e01b8152600401600060405180830381865afa1580156106cb573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526105d2919081019061177d565b60006105d27f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015610756573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061077a9190611744565b61078590600a611924565b610c45565b610792610c89565b6107ce828280806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250610cb692505050565b5050565b6000807f00000000000000000000000000000000000000000000000000000000000000006107ff84610c45565b91509150915091565b6060600380548060200260200160405190810160405280929190818152602001828054801561052957602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610842575050505050905090565b6040516370a0823160e01b81526001600160a01b0382811660048301526000917f0000000000000000000000000000000000000000000000000000000000000000909116906370a08231906024015b602060405180830381865afa1580156108d5573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061049b919061172b565b610901610c89565b61090b6000610d74565b565b60065433906001600160a01b0316811461094a5760405163118cdaa760e01b81526001600160a01b03821660048201526024015b60405180910390fd5b61095381610d74565b50565b6040516370a0823160e01b81526001600160a01b03828116600483015260009161049b917f000000000000000000000000000000000000000000000000000000000000000016906370a08231906024015b602060405180830381865afa1580156109c4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610785919061172b565b604051636eb1769f60e11b81526001600160a01b0383811660048301528281166024830152600091610a45917f0000000000000000000000000000000000000000000000000000000000000000169063dd62ed3e906044016109a7565b9392505050565b6060600180546104b0906116f1565b610a63610c89565b6107ce828280806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250610d8d92505050565b610aa7610c89565b61095381610e40565b60606002805480602002602001604051908101604052809291908181526020018280548015610529576020028201919060005260206000209081546001600160a01b03168152600190910190602001808311610842575050505050905090565b604051636eb1769f60e11b81526001600160a01b03838116600483015282811660248301526000917f00000000000000000000000000000000000000000000000000000000000000009091169063dd62ed3e90604401602060405180830381865afa158015610b83573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a45919061172b565b6000807f00000000000000000000000000000000000000000000000000000000000000006107ff84610f1c565b610bdc610c89565b600680546001600160a01b0383166001600160a01b03199091168117909155610c0d6005546001600160a01b031690565b6001600160a01b03167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a350565b600081600003610c5757506000919050565b6004805460405163bd3f03f960e01b81526001600160a01b039091169163bd3f03f9916108b891869160029101611933565b6005546001600160a01b0316331461090b5760405163118cdaa760e01b8152336004820152602401610941565b610cbf81610f60565b610cc8816110f3565b8051610cdb906002906020840190611440565b507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03167f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316306001600160a01b03167f25b4c0059a7a45c3fb2e9a613393e8a0b8339d669153d7add5c83611d890748084604051610d699190611690565b60405180910390a450565b600680546001600160a01b031916905561095381611205565b610d9681611257565b610d9f816110f3565b8051610db2906003906020840190611440565b507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03167f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316306001600160a01b03167f3105ebf8cfe0d8803d5b103773a21806a6c43fa57501763b771be3db158bfcd584604051610d699190611690565b6040516301ffc9a760e01b815263b130abe360e01b60048201526001600160a01b038216906301ffc9a790602401602060405180830381865afa158015610e8b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610eaf9190611991565b610f135760405162461bcd60e51b815260206004820152602f60248201527f446f6573206e6f7420696d706c656d656e742049556e6973776170563244796e60448201526e30b6b4b1a83934b1b2a937baba32b960891b6064820152608401610941565b610953816113f4565b600081600003610f2e57506000919050565b6004805460405163bd3f03f960e01b81526001600160a01b039091169163bd3f03f9916108b891869160039101611933565b6001815111610fb15760405162461bcd60e51b815260206004820181905260248201527f50617468206d7573742068617665206174206c65617374203220746f6b656e736044820152606401610941565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031681600081518110610fee57610fee6119b3565b60200260200101516001600160a01b03161461104c5760405162461bcd60e51b815260206004820181905260248201527f4261736520746f6b656e206d75737420626520666972737420696e20706174686044820152606401610941565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316816001835161108591906119c9565b81518110611095576110956119b3565b60200260200101516001600160a01b0316146109535760405162461bcd60e51b815260206004820181905260248201527f51756f746520746f6b656e206d757374206265206c61737420696e20706174686044820152606401610941565b60045481516001600160a01b039091169063bd3f03f990839060009061111b5761111b6119b3565b60200260200101516001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015611160573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111849190611744565b61118f90600a611924565b836040518363ffffffff1660e01b81526004016111ad9291906119dc565b602060405180830381865afa9250505080156111e6575060408051601f3d908101601f191682019092526111e39181019061172b565b60015b6107ce57806040516354da4f1960e01b81526004016109419190611690565b600580546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b60018151116112a85760405162461bcd60e51b815260206004820181905260248201527f50617468206d7573742068617665206174206c65617374203220746f6b656e736044820152606401610941565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316816000815181106112e5576112e56119b3565b60200260200101516001600160a01b03161461134d5760405162461bcd60e51b815260206004820152602160248201527f51756f746520746f6b656e206d75737420626520666972737420696e207061746044820152600d60fb1b6064820152608401610941565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316816001835161138691906119c9565b81518110611396576113966119b3565b60200260200101516001600160a01b0316146109535760405162461bcd60e51b815260206004820152601f60248201527f4261736520746f6b656e206d757374206265206c61737420696e2070617468006044820152606401610941565b600480546001600160a01b0319166001600160a01b03831690811790915560405130907fd208b3f2137ba34e4b2ea29e6e0558a1395796409f6b8328f2cbd5ff9159bc1590600090a350565b828054828255906000526020600020908101928215611495579160200282015b8281111561149557825182546001600160a01b0319166001600160a01b03909116178255602090920191600190910190611460565b506114a19291506114a5565b5090565b5b808211156114a157600081556001016114a6565b6000602082840312156114cc57600080fd5b81356001600160e01b031981168114610a4557600080fd5b60005b838110156114ff5781810151838201526020016114e7565b50506000910152565b60208152600082518060208401526115278160408501602087016114e4565b601f01601f19169190910160400192915050565b80356001600160a01b038116811461155257600080fd5b919050565b6000806040838503121561156a57600080fd5b6115738361153b565b946020939093013593505050565b60008060006060848603121561159657600080fd5b61159f8461153b565b92506115ad6020850161153b565b9150604084013590509250925092565b600080602083850312156115d057600080fd5b823567ffffffffffffffff808211156115e857600080fd5b818501915085601f8301126115fc57600080fd5b81358181111561160b57600080fd5b8660208260051b850101111561162057600080fd5b60209290920196919550909350505050565b60006020828403121561164457600080fd5b5035919050565b60008151808452602080850194506020840160005b838110156116855781516001600160a01b031687529582019590820190600101611660565b509495945050505050565b602081526000610a45602083018461164b565b6000602082840312156116b557600080fd5b610a458261153b565b600080604083850312156116d157600080fd5b6116da8361153b565b91506116e86020840161153b565b90509250929050565b600181811c9082168061170557607f821691505b60208210810361172557634e487b7160e01b600052602260045260246000fd5b50919050565b60006020828403121561173d57600080fd5b5051919050565b60006020828403121561175657600080fd5b815160ff81168114610a4557600080fd5b634e487b7160e01b600052604160045260246000fd5b60006020828403121561178f57600080fd5b815167ffffffffffffffff808211156117a757600080fd5b818401915084601f8301126117bb57600080fd5b8151818111156117cd576117cd611767565b604051601f8201601f19908116603f011681019083821181831017156117f5576117f5611767565b8160405282815287602084870101111561180e57600080fd5b61181f8360208301602088016114e4565b979650505050505050565b634e487b7160e01b600052601160045260246000fd5b600181815b8085111561187b5781600019048211156118615761186161182a565b8085161561186e57918102915b93841c9390800290611845565b509250929050565b6000826118925750600161049b565b8161189f5750600061049b565b81600181146118b557600281146118bf576118db565b600191505061049b565b60ff8411156118d0576118d061182a565b50506001821b61049b565b5060208310610133831016604e8410600b84101617156118fe575081810a61049b565b6119088383611840565b806000190482111561191c5761191c61182a565b029392505050565b6000610a4560ff841683611883565b600060408201848352602060406020850152818554808452606086019150866000526020600020935060005b818110156119845784546001600160a01b03168352600194850194928401920161195f565b5090979650505050505050565b6000602082840312156119a357600080fd5b81518015158114610a4557600080fd5b634e487b7160e01b600052603260045260246000fd5b8181038181111561049b5761049b61182a565b8281526040602082015260006119f5604083018461164b565b94935050505056fea2646970667358221220010d22d06d443982ab6747b918a4f147524563fb657637be8943b6752884d25564736f6c6343000818003350617468206d7573742068617665206174206c65617374203220746f6b656e7300000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001200000000000000000000000004200000000000000000000000000000000000006000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda0291300000000000000000000000017b0e768cd785fcaad2729650714fbf57e8f4ddc000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000000000000000000000000000000000000000001c556e69737761702056322044796e616d696320574554482f5553444300000000000000000000000000000000000000000000000000000000000000000000000a64574554482d555344430000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000004200000000000000000000000000000000000006000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda029130000000000000000000000000000000000000000000000000000000000000002000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda029130000000000000000000000004200000000000000000000000000000000000006
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106101cf5760003560e01c806379ba509711610104578063aebab157116100a2578063dd62ed3e11610071578063dd62ed3e146103ea578063e30c3978146103fd578063e71dc5e51461040e578063f2fde38b1461042157600080fd5b8063aebab15714610395578063be41d5bc146103a8578063c55dae63146103bb578063cb27a434146103e257600080fd5b80638da5cb5b116100de5780638da5cb5b1461035b57806395d89b411461036c5780639bd230f314610374578063a9059cbb1461038757600080fd5b806379ba50971461032d5780637a19c83d146103355780638caecde51461034857600080fd5b806339ff2da2116101715780636702fcac1161014b5780636702fcac146102cb5780636bb26dba146102fd57806370a0823114610312578063715018a61461032557600080fd5b806339ff2da2146102a65780634971c79a146102ae5780634b230342146102b657600080fd5b806318160ddd116101ad57806318160ddd14610224578063217a4b701461023a57806323b872dd14610279578063313ce5671461028c57600080fd5b806301ffc9a7146101d457806306fdde03146101fc578063095ea7b314610211575b600080fd5b6101e76101e23660046114ba565b610434565b60405190151581526020015b60405180910390f35b6102046104a1565b6040516101f39190611508565b6101e761021f366004611557565b610533565b61022c61054e565b6040519081526020016101f3565b6102617f000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda0291381565b6040516001600160a01b0390911681526020016101f3565b6101e7610287366004611581565b6105d7565b6102946105f2565b60405160ff90911681526020016101f3565b610204610676565b61022c6106f3565b6102c96102c43660046115bd565b61078a565b005b6102de6102d9366004611632565b6107d2565b604080516001600160a01b0390931683526020830191909152016101f3565b610305610808565b6040516101f39190611690565b61022c6103203660046116a3565b610869565b6102c96108f9565b6102c961090d565b61022c6103433660046116a3565b610956565b61022c6103563660046116be565b6109e8565b6005546001600160a01b0316610261565b610204610a4c565b6102c96103823660046115bd565b610a5b565b6101e7610287366004611557565b6102c96103a33660046116a3565b610a9f565b600454610261906001600160a01b031681565b6102617f000000000000000000000000420000000000000000000000000000000000000681565b610305610ab0565b61022c6103f83660046116be565b610b10565b6006546001600160a01b0316610261565b6102de61041c366004611632565b610ba7565b6102c961042f3660046116a3565b610bd4565b60006001600160e01b03198216631f9aa5f160e31b148061046557506001600160e01b031982166336372b0760e01b145b8061048057506001600160e01b0319821663a219a02560e01b145b8061049b57506301ffc9a760e01b6001600160e01b03198316145b92915050565b6060600080546104b0906116f1565b80601f01602080910402602001604051908101604052809291908181526020018280546104dc906116f1565b80156105295780601f106104fe57610100808354040283529160200191610529565b820191906000526020600020905b81548152906001019060200180831161050c57829003601f168201915b5050505050905090565b6000604051632028747160e01b815260040160405180910390fd5b60007f00000000000000000000000042000000000000000000000000000000000000066001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156105ae573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105d2919061172b565b905090565b6000604051638cd22d1960e01b815260040160405180910390fd5b60007f000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda029136001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015610652573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906105d29190611744565b6060600460009054906101000a90046001600160a01b03166001600160a01b0316630e05f6766040518163ffffffff1660e01b8152600401600060405180830381865afa1580156106cb573d6000803e3d6000fd5b505050506040513d6000823e601f3d908101601f191682016040526105d2919081019061177d565b60006105d27f00000000000000000000000042000000000000000000000000000000000000066001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015610756573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061077a9190611744565b61078590600a611924565b610c45565b610792610c89565b6107ce828280806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250610cb692505050565b5050565b6000807f000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda029136107ff84610c45565b91509150915091565b6060600380548060200260200160405190810160405280929190818152602001828054801561052957602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311610842575050505050905090565b6040516370a0823160e01b81526001600160a01b0382811660048301526000917f0000000000000000000000004200000000000000000000000000000000000006909116906370a08231906024015b602060405180830381865afa1580156108d5573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061049b919061172b565b610901610c89565b61090b6000610d74565b565b60065433906001600160a01b0316811461094a5760405163118cdaa760e01b81526001600160a01b03821660048201526024015b60405180910390fd5b61095381610d74565b50565b6040516370a0823160e01b81526001600160a01b03828116600483015260009161049b917f000000000000000000000000420000000000000000000000000000000000000616906370a08231906024015b602060405180830381865afa1580156109c4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610785919061172b565b604051636eb1769f60e11b81526001600160a01b0383811660048301528281166024830152600091610a45917f0000000000000000000000004200000000000000000000000000000000000006169063dd62ed3e906044016109a7565b9392505050565b6060600180546104b0906116f1565b610a63610c89565b6107ce828280806020026020016040519081016040528093929190818152602001838360200280828437600092019190915250610d8d92505050565b610aa7610c89565b61095381610e40565b60606002805480602002602001604051908101604052809291908181526020018280548015610529576020028201919060005260206000209081546001600160a01b03168152600190910190602001808311610842575050505050905090565b604051636eb1769f60e11b81526001600160a01b03838116600483015282811660248301526000917f00000000000000000000000042000000000000000000000000000000000000069091169063dd62ed3e90604401602060405180830381865afa158015610b83573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a45919061172b565b6000807f00000000000000000000000042000000000000000000000000000000000000066107ff84610f1c565b610bdc610c89565b600680546001600160a01b0383166001600160a01b03199091168117909155610c0d6005546001600160a01b031690565b6001600160a01b03167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a350565b600081600003610c5757506000919050565b6004805460405163bd3f03f960e01b81526001600160a01b039091169163bd3f03f9916108b891869160029101611933565b6005546001600160a01b0316331461090b5760405163118cdaa760e01b8152336004820152602401610941565b610cbf81610f60565b610cc8816110f3565b8051610cdb906002906020840190611440565b507f000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda029136001600160a01b03167f00000000000000000000000042000000000000000000000000000000000000066001600160a01b0316306001600160a01b03167f25b4c0059a7a45c3fb2e9a613393e8a0b8339d669153d7add5c83611d890748084604051610d699190611690565b60405180910390a450565b600680546001600160a01b031916905561095381611205565b610d9681611257565b610d9f816110f3565b8051610db2906003906020840190611440565b507f000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda029136001600160a01b03167f00000000000000000000000042000000000000000000000000000000000000066001600160a01b0316306001600160a01b03167f3105ebf8cfe0d8803d5b103773a21806a6c43fa57501763b771be3db158bfcd584604051610d699190611690565b6040516301ffc9a760e01b815263b130abe360e01b60048201526001600160a01b038216906301ffc9a790602401602060405180830381865afa158015610e8b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610eaf9190611991565b610f135760405162461bcd60e51b815260206004820152602f60248201527f446f6573206e6f7420696d706c656d656e742049556e6973776170563244796e60448201526e30b6b4b1a83934b1b2a937baba32b960891b6064820152608401610941565b610953816113f4565b600081600003610f2e57506000919050565b6004805460405163bd3f03f960e01b81526001600160a01b039091169163bd3f03f9916108b891869160039101611933565b6001815111610fb15760405162461bcd60e51b815260206004820181905260248201527f50617468206d7573742068617665206174206c65617374203220746f6b656e736044820152606401610941565b7f00000000000000000000000042000000000000000000000000000000000000066001600160a01b031681600081518110610fee57610fee6119b3565b60200260200101516001600160a01b03161461104c5760405162461bcd60e51b815260206004820181905260248201527f4261736520746f6b656e206d75737420626520666972737420696e20706174686044820152606401610941565b7f000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda029136001600160a01b0316816001835161108591906119c9565b81518110611095576110956119b3565b60200260200101516001600160a01b0316146109535760405162461bcd60e51b815260206004820181905260248201527f51756f746520746f6b656e206d757374206265206c61737420696e20706174686044820152606401610941565b60045481516001600160a01b039091169063bd3f03f990839060009061111b5761111b6119b3565b60200260200101516001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa158015611160573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111849190611744565b61118f90600a611924565b836040518363ffffffff1660e01b81526004016111ad9291906119dc565b602060405180830381865afa9250505080156111e6575060408051601f3d908101601f191682019092526111e39181019061172b565b60015b6107ce57806040516354da4f1960e01b81526004016109419190611690565b600580546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b60018151116112a85760405162461bcd60e51b815260206004820181905260248201527f50617468206d7573742068617665206174206c65617374203220746f6b656e736044820152606401610941565b7f000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda029136001600160a01b0316816000815181106112e5576112e56119b3565b60200260200101516001600160a01b03161461134d5760405162461bcd60e51b815260206004820152602160248201527f51756f746520746f6b656e206d75737420626520666972737420696e207061746044820152600d60fb1b6064820152608401610941565b7f00000000000000000000000042000000000000000000000000000000000000066001600160a01b0316816001835161138691906119c9565b81518110611396576113966119b3565b60200260200101516001600160a01b0316146109535760405162461bcd60e51b815260206004820152601f60248201527f4261736520746f6b656e206d757374206265206c61737420696e2070617468006044820152606401610941565b600480546001600160a01b0319166001600160a01b03831690811790915560405130907fd208b3f2137ba34e4b2ea29e6e0558a1395796409f6b8328f2cbd5ff9159bc1590600090a350565b828054828255906000526020600020908101928215611495579160200282015b8281111561149557825182546001600160a01b0319166001600160a01b03909116178255602090920191600190910190611460565b506114a19291506114a5565b5090565b5b808211156114a157600081556001016114a6565b6000602082840312156114cc57600080fd5b81356001600160e01b031981168114610a4557600080fd5b60005b838110156114ff5781810151838201526020016114e7565b50506000910152565b60208152600082518060208401526115278160408501602087016114e4565b601f01601f19169190910160400192915050565b80356001600160a01b038116811461155257600080fd5b919050565b6000806040838503121561156a57600080fd5b6115738361153b565b946020939093013593505050565b60008060006060848603121561159657600080fd5b61159f8461153b565b92506115ad6020850161153b565b9150604084013590509250925092565b600080602083850312156115d057600080fd5b823567ffffffffffffffff808211156115e857600080fd5b818501915085601f8301126115fc57600080fd5b81358181111561160b57600080fd5b8660208260051b850101111561162057600080fd5b60209290920196919550909350505050565b60006020828403121561164457600080fd5b5035919050565b60008151808452602080850194506020840160005b838110156116855781516001600160a01b031687529582019590820190600101611660565b509495945050505050565b602081526000610a45602083018461164b565b6000602082840312156116b557600080fd5b610a458261153b565b600080604083850312156116d157600080fd5b6116da8361153b565b91506116e86020840161153b565b90509250929050565b600181811c9082168061170557607f821691505b60208210810361172557634e487b7160e01b600052602260045260246000fd5b50919050565b60006020828403121561173d57600080fd5b5051919050565b60006020828403121561175657600080fd5b815160ff81168114610a4557600080fd5b634e487b7160e01b600052604160045260246000fd5b60006020828403121561178f57600080fd5b815167ffffffffffffffff808211156117a757600080fd5b818401915084601f8301126117bb57600080fd5b8151818111156117cd576117cd611767565b604051601f8201601f19908116603f011681019083821181831017156117f5576117f5611767565b8160405282815287602084870101111561180e57600080fd5b61181f8360208301602088016114e4565b979650505050505050565b634e487b7160e01b600052601160045260246000fd5b600181815b8085111561187b5781600019048211156118615761186161182a565b8085161561186e57918102915b93841c9390800290611845565b509250929050565b6000826118925750600161049b565b8161189f5750600061049b565b81600181146118b557600281146118bf576118db565b600191505061049b565b60ff8411156118d0576118d061182a565b50506001821b61049b565b5060208310610133831016604e8410600b84101617156118fe575081810a61049b565b6119088383611840565b806000190482111561191c5761191c61182a565b029392505050565b6000610a4560ff841683611883565b600060408201848352602060406020850152818554808452606086019150866000526020600020935060005b818110156119845784546001600160a01b03168352600194850194928401920161195f565b5090979650505050505050565b6000602082840312156119a357600080fd5b81518015158114610a4557600080fd5b634e487b7160e01b600052603260045260246000fd5b8181038181111561049b5761049b61182a565b8281526040602082015260006119f5604083018461164b565b94935050505056fea2646970667358221220010d22d06d443982ab6747b918a4f147524563fb657637be8943b6752884d25564736f6c63430008180033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
00000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001200000000000000000000000004200000000000000000000000000000000000006000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda0291300000000000000000000000017b0e768cd785fcaad2729650714fbf57e8f4ddc000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000001c0000000000000000000000000000000000000000000000000000000000000001c556e69737761702056322044796e616d696320574554482f5553444300000000000000000000000000000000000000000000000000000000000000000000000a64574554482d555344430000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000004200000000000000000000000000000000000006000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda029130000000000000000000000000000000000000000000000000000000000000002000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda029130000000000000000000000004200000000000000000000000000000000000006
-----Decoded View---------------
Arg [0] : _name (string): Uniswap V2 Dynamic WETH/USDC
Arg [1] : _symbol (string): dWETH-USDC
Arg [2] : _baseToken (address): 0x4200000000000000000000000000000000000006
Arg [3] : _quoteToken (address): 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913
Arg [4] : _dynamicPriceRouter (address): 0x17B0e768Cd785fCAad2729650714fbF57E8F4DdC
Arg [5] : _baseToQuotePath (address[]): 0x4200000000000000000000000000000000000006,0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913
Arg [6] : _quoteToBasePath (address[]): 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913,0x4200000000000000000000000000000000000006
-----Encoded View---------------
17 Constructor Arguments found :
Arg [0] : 00000000000000000000000000000000000000000000000000000000000000e0
Arg [1] : 0000000000000000000000000000000000000000000000000000000000000120
Arg [2] : 0000000000000000000000004200000000000000000000000000000000000006
Arg [3] : 000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda02913
Arg [4] : 00000000000000000000000017b0e768cd785fcaad2729650714fbf57e8f4ddc
Arg [5] : 0000000000000000000000000000000000000000000000000000000000000160
Arg [6] : 00000000000000000000000000000000000000000000000000000000000001c0
Arg [7] : 000000000000000000000000000000000000000000000000000000000000001c
Arg [8] : 556e69737761702056322044796e616d696320574554482f5553444300000000
Arg [9] : 000000000000000000000000000000000000000000000000000000000000000a
Arg [10] : 64574554482d5553444300000000000000000000000000000000000000000000
Arg [11] : 0000000000000000000000000000000000000000000000000000000000000002
Arg [12] : 0000000000000000000000004200000000000000000000000000000000000006
Arg [13] : 000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda02913
Arg [14] : 0000000000000000000000000000000000000000000000000000000000000002
Arg [15] : 000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda02913
Arg [16] : 0000000000000000000000004200000000000000000000000000000000000006
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
A token is a representation of an on-chain or off-chain asset. The token page shows information such as price, total supply, holders, transfers and social links. Learn more about this page in our Knowledge Base.
Add Token to MetaMask (Web3)