Source Code
Latest 20 from a total of 20 transactions
| Transaction Hash |
Method
|
Block
|
From
|
|
To
|
||||
|---|---|---|---|---|---|---|---|---|---|
| Claim Tokens | 23489192 | 142 days ago | IN | 0 ETH | 0.00016402 | ||||
| Claim Tokens | 23489128 | 142 days ago | IN | 0 ETH | 0.00015529 | ||||
| Claim Funds | 23468054 | 145 days ago | IN | 0 ETH | 0.00012251 | ||||
| Deploy Token | 23468048 | 145 days ago | IN | 0 ETH | 0.00391238 | ||||
| Commit Funds | 23449026 | 148 days ago | IN | 0 ETH | 0.00030176 | ||||
| Commit Funds | 23447588 | 148 days ago | IN | 0 ETH | 0.00030144 | ||||
| Commit Funds | 23446564 | 148 days ago | IN | 0 ETH | 0.00021004 | ||||
| Commit Funds | 23433378 | 150 days ago | IN | 0 ETH | 0.00012402 | ||||
| Commit Funds | 23432921 | 150 days ago | IN | 0 ETH | 0.00020086 | ||||
| Commit Funds | 23432261 | 150 days ago | IN | 0 ETH | 0.00019614 | ||||
| Cancel Commitmen... | 23431381 | 151 days ago | IN | 0 ETH | 0.00027367 | ||||
| Commit Funds | 23426501 | 151 days ago | IN | 0 ETH | 0.00036751 | ||||
| Commit Funds | 23425896 | 151 days ago | IN | 0 ETH | 0.0002483 | ||||
| Commit Funds | 23420488 | 152 days ago | IN | 0 ETH | 0.00021392 | ||||
| Commit Funds | 23420187 | 152 days ago | IN | 0 ETH | 0.00019855 | ||||
| Commit Funds | 23420153 | 152 days ago | IN | 0 ETH | 0.00020747 | ||||
| Commit Funds | 23420140 | 152 days ago | IN | 0 ETH | 0.00020575 | ||||
| Commit Funds | 23420130 | 152 days ago | IN | 0 ETH | 0.00020866 | ||||
| Commit Funds | 23420118 | 152 days ago | IN | 0 ETH | 0.00020215 | ||||
| Commit Funds | 23419977 | 152 days ago | IN | 0 ETH | 0.00036597 |
Latest 1 internal transaction
Advanced mode:
| Parent Transaction Hash | Method | Block |
From
|
|
To
|
||
|---|---|---|---|---|---|---|---|
| 0x60c06040 | 23419636 | 152 days ago | Contract Creation | 0 ETH |
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
Contract Name:
PermissionedSale
Compiler Version
v0.8.20+commit.a1b79de6
Optimization Enabled:
Yes with 999999 runs
Other Settings:
paris EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "./PermissionedToken.sol";
import "./RedeemablePermissionedTokenFactory.sol";
import "./MathLib.sol";
import "./RBAC.sol";
import "./Ownable.sol";
contract PermissionedSale is Ownable {
using SafeERC20 for IERC20;
using MathLib for uint;
struct SaleParams {
string saleId;
bytes32 investorRole;
address paymentToken;
uint256 price;
uint256 minCommitment;
uint256 maxCommitment;
uint256 hardcap;
uint256 softcap;
uint32 saleStart;
uint32 saleEnd;
bool cancelable;
bool isAddressed;
}
struct Commitment {
address investor;
uint256 amount;
}
SaleParams private params;
address private rwaToken;
address private immutable rwaTokenFactory;
uint256 public totalCommited = 0;
uint32 public nextCommitmentIdx = 0;
bool private isForceStarted;
bool private isForceFinished;
bool private isCanceled;
bool private isFundsClaimed;
mapping(address => uint256) private commitmentsTotal;
mapping(bytes32 => Commitment) private commitments;
mapping(address => bool) private claimed;
modifier started() {require(isStarted(), "Sale is not started"); _;}
modifier notStarted() {require(!isStarted(), "Sale is already started"); _;}
modifier rwaDeployed() {require(isDeployed(), "RWA token is not deployed"); _;}
modifier rwaNotDeployed() {require(!isDeployed(), "RWA token is already deployed"); _;}
modifier finalized() {require(isFinalized(), "Sale is not finalized"); _;}
modifier notFinalized() {require(!isFinalized(), "Sale is already finalized"); _;}
modifier softCapAchieved() {require(isSoftCapAchieved(), "Soft cap was not achieved"); _;}
event FundsCommitted(string saleId, address indexed investor, bytes32 commitmentHash, uint256 amount, bool isSoftCapAchieved);
event CommitmentCanceled(string saleId, address indexed investor, bytes32 commitmentHash, uint256 amount, bool isSoftCapAchieved);
event FundsClaimed(string saleId, address indexed owner, uint256 amount);
event TokensClaimed(string saleId, address indexed investor, uint256 amount);
event TokenDeployed(string saleId, address rwaToken, uint256 rwaAmount);
event SaleStarted(string saleId);
event SaleFinalized(string saleId, bool success);
event SaleCanceled(string saleId);
constructor(address _rbac, address _rwaTokenFactory, SaleParams memory _params) Ownable(_rbac) {
assert(_params.paymentToken != address(0));
assert(_rwaTokenFactory != address(0));
require(_params.price > 0, "Price must be positive number");
require(_params.hardcap >= _params.softcap, "Hardcap must be more than softcap");
require(_params.saleStart < _params.saleEnd, "Start timestamp must be earlier than end timestamp");
require(_params.saleEnd > block.timestamp, "End timestamp must be more than current timestamp");
params = _params;
rwaTokenFactory = _rwaTokenFactory;
if (_params.investorRole == 0) {
params.investorRole = INVESTOR_ROLE;
}
}
function commitFunds(uint256 _amount) external onlyRole(params.investorRole) started notFinalized {
_commitFunds(_amount);
}
function commitFundsWhitelisted(
RBAC.AddingToWhiteListMessage memory message,
bytes calldata signature,
uint256 _amount
) external onlyRole(params.investorRole) started notFinalized {
require(params.isAddressed, "Sale is not addressed");
RBAC rbacContract = RBAC(rbac);
if (!rbacContract.isInWhiteList(address(this), msg.sender)) {
rbacContract.addWalletToSaleWhiteList(message, signature);
}
_commitFunds(_amount);
}
function _commitFunds(uint256 _amount) private {
_checkWhiteListIfNeeded();
totalCommited += _amount;
require(totalCommited <= params.hardcap, "Hardcap reached");
bytes32 hash = keccak256(abi.encodePacked(address(this), nextCommitmentIdx));
_receiveFunds(_amount);
commitmentsTotal[msg.sender] += _amount;
commitments[hash] = Commitment(msg.sender, _amount);
require((params.minCommitment > 0 && params.minCommitment <= commitmentsTotal[msg.sender]) || params.minCommitment == 0, "Commitments amount less than min");
require((params.maxCommitment > 0 && params.maxCommitment >= commitmentsTotal[msg.sender]) || params.maxCommitment == 0, "Commitments amount more than max");
require(_amount % params.price == 0, "Can buy only whole rwa tokens");
nextCommitmentIdx += 1;
if (isHardCapAchieved()) {
isForceFinished = true;
emit SaleFinalized(params.saleId, isSoftCapAchieved());
}
emit FundsCommitted(params.saleId, msg.sender, hash, _amount, isSoftCapAchieved());
}
function cancelSale() external onlyOwner rwaNotDeployed onlyRole(SALE_MANAGER_ROLE) {
require(params.cancelable, "Sale is not cancelable");
require(!isCanceled, "Sale is already canceled");
isForceFinished = true;
isCanceled = true;
emit SaleCanceled(params.saleId);
}
function cancelCommitment(bytes32[] calldata hashes) external rwaNotDeployed {
if (RBAC(rbac).hasRole(SUPERVISOR_ROLE, msg.sender)) {
_cancelCommitment(hashes);
if (isFinalized()) {
emit SaleFinalized(params.saleId, isSoftCapAchieved());
}
} else if (_isAllHashesBySender(hashes)) {
require(isCanceled || !(isFinalized() && isSoftCapAchieved()), "Not allowed");
_cancelCommitment(hashes);
} else {
revert("Not allowed");
}
}
function _isAllHashesBySender(bytes32[] calldata hashes) private view returns (bool) {
for (uint i = 0; i < hashes.length; i++) {
if (commitments[hashes[i]].investor != msg.sender) {
revert("Not allowed");
}
}
return true;
}
function _cancelCommitment(bytes32[] calldata hashes) private {
uint256 canceledAmount = 0;
address investor = commitments[hashes[0]].investor;
for (uint i = 0; i < hashes.length; i++) {
uint256 amount = commitments[hashes[i]].amount;
require(amount > 0, "Commitment not found");
commitments[hashes[i]].amount = 0;
commitmentsTotal[investor] -= amount;
canceledAmount += amount;
totalCommited -= amount;
emit CommitmentCanceled(params.saleId, investor, hashes[i], amount, isSoftCapAchieved());
}
if (canceledAmount > 0) {
_sendFunds(investor, canceledAmount);
}
}
function claimFunds() external onlyOwner onlyRole(SALE_WITHDRAW_ROLE) rwaDeployed finalized {
require(!isFundsClaimed, "Funds already been claimed");
_sendFunds(msg.sender, totalCommited);
isFundsClaimed = true;
emit FundsClaimed(params.saleId, msg.sender, totalCommited);
}
function claimTokens() external rwaDeployed {
_checkWhiteListIfNeeded();
require(!claimed[msg.sender], "Tokens is already claimed");
uint256 amount = commitmentsTotal[msg.sender];
require(amount > 0, "Unknown investor");
PermissionedToken token = PermissionedToken(rwaToken);
token.mint(msg.sender, amount / params.price * 1e18);
claimed[msg.sender] = true;
emit TokensClaimed(params.saleId, msg.sender, amount);
}
function startSale() external onlyOwner notStarted onlyRole(SALE_MANAGER_ROLE) {
isForceStarted = true;
emit SaleStarted(params.saleId);
}
function finalizeSale() external onlyOwner notFinalized onlyRole(SALE_MANAGER_ROLE) {
isForceFinished = true;
emit SaleFinalized(params.saleId, isSoftCapAchieved());
}
function deployToken(
uint32 _redeemDate,
string memory _name,
string memory _ticker
) external onlyOwner rwaNotDeployed finalized softCapAchieved onlyRole(SALE_MANAGER_ROLE) {
RedeemablePermissionedTokenFactory factory = RedeemablePermissionedTokenFactory(rwaTokenFactory);
uint256 amount = totalCommited / params.price;
rwaToken = factory.createToken(_name, _ticker, params.paymentToken, params.price, _redeemDate, amount, owner);
emit TokenDeployed(params.saleId, rwaToken, amount);
}
function withdrawFunds(uint _amount) external onlyRole(SUPERVISOR_ROLE) {
IERC20 paymentToken = IERC20(params.paymentToken);
uint256 balance = paymentToken.balanceOf(address(this));
require(balance >= _amount, "Not enough contract balance");
paymentToken.safeTransfer(msg.sender, _amount);
}
function _receiveFunds(uint256 amount) private {
IERC20 token = IERC20(params.paymentToken);
uint allowed = token.allowance(msg.sender, address(this));
require(allowed >= amount, "Transfer not allowed");
token.safeTransferFrom(msg.sender, address(this), amount);
}
function _sendFunds(address to, uint256 amount) private {
IERC20 token = IERC20(params.paymentToken);
uint256 balance = token.balanceOf(address(this));
require(balance >= amount, "Not enough tokens");
token.safeTransfer(to, amount);
}
function _checkWhiteListIfNeeded() private view {
if (params.isAddressed) {
require(RBAC(rbac).isInWhiteList(address(this), msg.sender), "Not allowed");
}
}
function isDeployed() public view returns (bool) {
return rwaToken != address(0);
}
function isStarted() public view returns (bool) {
return (block.timestamp > params.saleStart) || isForceStarted;
}
function isFinalized() public view returns (bool) {
return (block.timestamp > params.saleEnd) || isForceFinished;
}
function isSoftCapAchieved() public view returns (bool) {
return totalCommited >= params.softcap;
}
function isHardCapAchieved() public view returns (bool) {
return totalCommited == params.hardcap;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/draft-IERC6093.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard ERC20 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC20 tokens.
*/
interface IERC20Errors {
/**
* @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param balance Current balance for the interacting account.
* @param needed Minimum amount required to perform a transfer.
*/
error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC20InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC20InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers.
* @param spender Address that may be allowed to operate on tokens without being their owner.
* @param allowance Amount of tokens a `spender` is allowed to operate with.
* @param needed Minimum amount required to perform a transfer.
*/
error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC20InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `spender` to be approved. Used in approvals.
* @param spender Address that may be allowed to operate on tokens without being their owner.
*/
error ERC20InvalidSpender(address spender);
}
/**
* @dev Standard ERC721 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC721 tokens.
*/
interface IERC721Errors {
/**
* @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in EIP-20.
* Used in balance queries.
* @param owner Address of the current owner of a token.
*/
error ERC721InvalidOwner(address owner);
/**
* @dev Indicates a `tokenId` whose `owner` is the zero address.
* @param tokenId Identifier number of a token.
*/
error ERC721NonexistentToken(uint256 tokenId);
/**
* @dev Indicates an error related to the ownership over a particular token. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param tokenId Identifier number of a token.
* @param owner Address of the current owner of a token.
*/
error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC721InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC721InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `operator`’s approval. Used in transfers.
* @param operator Address that may be allowed to operate on tokens without being their owner.
* @param tokenId Identifier number of a token.
*/
error ERC721InsufficientApproval(address operator, uint256 tokenId);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC721InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `operator` to be approved. Used in approvals.
* @param operator Address that may be allowed to operate on tokens without being their owner.
*/
error ERC721InvalidOperator(address operator);
}
/**
* @dev Standard ERC1155 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC1155 tokens.
*/
interface IERC1155Errors {
/**
* @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
* @param balance Current balance for the interacting account.
* @param needed Minimum amount required to perform a transfer.
* @param tokenId Identifier number of a token.
*/
error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId);
/**
* @dev Indicates a failure with the token `sender`. Used in transfers.
* @param sender Address whose tokens are being transferred.
*/
error ERC1155InvalidSender(address sender);
/**
* @dev Indicates a failure with the token `receiver`. Used in transfers.
* @param receiver Address to which tokens are being transferred.
*/
error ERC1155InvalidReceiver(address receiver);
/**
* @dev Indicates a failure with the `operator`’s approval. Used in transfers.
* @param operator Address that may be allowed to operate on tokens without being their owner.
* @param owner Address of the current owner of a token.
*/
error ERC1155MissingApprovalForAll(address operator, address owner);
/**
* @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
* @param approver Address initiating an approval operation.
*/
error ERC1155InvalidApprover(address approver);
/**
* @dev Indicates a failure with the `operator` to be approved. Used in approvals.
* @param operator Address that may be allowed to operate on tokens without being their owner.
*/
error ERC1155InvalidOperator(address operator);
/**
* @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation.
* Used in batch transfers.
* @param idsLength Length of the array of token identifiers
* @param valuesLength Length of the array of token amounts
*/
error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC5267.sol)
pragma solidity ^0.8.20;
interface IERC5267 {
/**
* @dev MAY be emitted to signal that the domain could have changed.
*/
event EIP712DomainChanged();
/**
* @dev returns the fields and values that describe the domain separator used by this contract for EIP-712
* signature.
*/
function eip712Domain()
external
view
returns (
bytes1 fields,
string memory name,
string memory version,
uint256 chainId,
address verifyingContract,
bytes32 salt,
uint256[] memory extensions
);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/ERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "./IERC20.sol";
import {IERC20Metadata} from "./extensions/IERC20Metadata.sol";
import {Context} from "../../utils/Context.sol";
import {IERC20Errors} from "../../interfaces/draft-IERC6093.sol";
/**
* @dev Implementation of the {IERC20} interface.
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using {_mint}.
*
* TIP: For a detailed writeup see our guide
* https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms].
*
* The default value of {decimals} is 18. To change this, you should override
* this function so it returns a different value.
*
* We have followed general OpenZeppelin Contracts guidelines: functions revert
* instead returning `false` on failure. This behavior is nonetheless
* conventional and does not conflict with the expectations of ERC20
* applications.
*
* Additionally, an {Approval} event is emitted on calls to {transferFrom}.
* This allows applications to reconstruct the allowance for all accounts just
* by listening to said events. Other implementations of the EIP may not emit
* these events, as it isn't required by the specification.
*/
abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors {
mapping(address account => uint256) private _balances;
mapping(address account => mapping(address spender => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
/**
* @dev Sets the values for {name} and {symbol}.
*
* All two of these values are immutable: they can only be set once during
* construction.
*/
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
/**
* @dev Returns the name of the token.
*/
function name() public view virtual returns (string memory) {
return _name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public view virtual returns (string memory) {
return _symbol;
}
/**
* @dev Returns the number of decimals used to get its user representation.
* For example, if `decimals` equals `2`, a balance of `505` tokens should
* be displayed to a user as `5.05` (`505 / 10 ** 2`).
*
* Tokens usually opt for a value of 18, imitating the relationship between
* Ether and Wei. This is the default value returned by this function, unless
* it's overridden.
*
* NOTE: This information is only used for _display_ purposes: it in
* no way affects any of the arithmetic of the contract, including
* {IERC20-balanceOf} and {IERC20-transfer}.
*/
function decimals() public view virtual returns (uint8) {
return 18;
}
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() public view virtual returns (uint256) {
return _totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(address account) public view virtual returns (uint256) {
return _balances[account];
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - the caller must have a balance of at least `value`.
*/
function transfer(address to, uint256 value) public virtual returns (bool) {
address owner = _msgSender();
_transfer(owner, to, value);
return true;
}
/**
* @dev See {IERC20-allowance}.
*/
function allowance(address owner, address spender) public view virtual returns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* NOTE: If `value` is the maximum `uint256`, the allowance is not updated on
* `transferFrom`. This is semantically equivalent to an infinite approval.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function approve(address spender, uint256 value) public virtual returns (bool) {
address owner = _msgSender();
_approve(owner, spender, value);
return true;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Emits an {Approval} event indicating the updated allowance. This is not
* required by the EIP. See the note at the beginning of {ERC20}.
*
* NOTE: Does not update the allowance if the current allowance
* is the maximum `uint256`.
*
* Requirements:
*
* - `from` and `to` cannot be the zero address.
* - `from` must have a balance of at least `value`.
* - the caller must have allowance for ``from``'s tokens of at least
* `value`.
*/
function transferFrom(address from, address to, uint256 value) public virtual returns (bool) {
address spender = _msgSender();
_spendAllowance(from, spender, value);
_transfer(from, to, value);
return true;
}
/**
* @dev Moves a `value` amount of tokens from `from` to `to`.
*
* This internal function is equivalent to {transfer}, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a {Transfer} event.
*
* NOTE: This function is not virtual, {_update} should be overridden instead.
*/
function _transfer(address from, address to, uint256 value) internal {
if (from == address(0)) {
revert ERC20InvalidSender(address(0));
}
if (to == address(0)) {
revert ERC20InvalidReceiver(address(0));
}
_update(from, to, value);
}
/**
* @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from`
* (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding
* this function.
*
* Emits a {Transfer} event.
*/
function _update(address from, address to, uint256 value) internal virtual {
if (from == address(0)) {
// Overflow check required: The rest of the code assumes that totalSupply never overflows
_totalSupply += value;
} else {
uint256 fromBalance = _balances[from];
if (fromBalance < value) {
revert ERC20InsufficientBalance(from, fromBalance, value);
}
unchecked {
// Overflow not possible: value <= fromBalance <= totalSupply.
_balances[from] = fromBalance - value;
}
}
if (to == address(0)) {
unchecked {
// Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply.
_totalSupply -= value;
}
} else {
unchecked {
// Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256.
_balances[to] += value;
}
}
emit Transfer(from, to, value);
}
/**
* @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0).
* Relies on the `_update` mechanism
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* NOTE: This function is not virtual, {_update} should be overridden instead.
*/
function _mint(address account, uint256 value) internal {
if (account == address(0)) {
revert ERC20InvalidReceiver(address(0));
}
_update(address(0), account, value);
}
/**
* @dev Destroys a `value` amount of tokens from `account`, lowering the total supply.
* Relies on the `_update` mechanism.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* NOTE: This function is not virtual, {_update} should be overridden instead
*/
function _burn(address account, uint256 value) internal {
if (account == address(0)) {
revert ERC20InvalidSender(address(0));
}
_update(account, address(0), value);
}
/**
* @dev Sets `value` as the allowance of `spender` over the `owner` s tokens.
*
* This internal function is equivalent to `approve`, and can be used to
* e.g. set automatic allowances for certain subsystems, etc.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*
* Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument.
*/
function _approve(address owner, address spender, uint256 value) internal {
_approve(owner, spender, value, true);
}
/**
* @dev Variant of {_approve} with an optional flag to enable or disable the {Approval} event.
*
* By default (when calling {_approve}) the flag is set to true. On the other hand, approval changes made by
* `_spendAllowance` during the `transferFrom` operation set the flag to false. This saves gas by not emitting any
* `Approval` event during `transferFrom` operations.
*
* Anyone who wishes to continue emitting `Approval` events on the`transferFrom` operation can force the flag to
* true using the following override:
* ```
* function _approve(address owner, address spender, uint256 value, bool) internal virtual override {
* super._approve(owner, spender, value, true);
* }
* ```
*
* Requirements are the same as {_approve}.
*/
function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual {
if (owner == address(0)) {
revert ERC20InvalidApprover(address(0));
}
if (spender == address(0)) {
revert ERC20InvalidSpender(address(0));
}
_allowances[owner][spender] = value;
if (emitEvent) {
emit Approval(owner, spender, value);
}
}
/**
* @dev Updates `owner` s allowance for `spender` based on spent `value`.
*
* Does not update the allowance value in case of infinite allowance.
* Revert if not enough allowance is available.
*
* Does not emit an {Approval} event.
*/
function _spendAllowance(address owner, address spender, uint256 value) internal virtual {
uint256 currentAllowance = allowance(owner, spender);
if (currentAllowance != type(uint256).max) {
if (currentAllowance < value) {
revert ERC20InsufficientAllowance(spender, currentAllowance, value);
}
unchecked {
_approve(owner, spender, currentAllowance - value, false);
}
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.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 ERC20 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.0.0) (token/ERC20/extensions/IERC20Permit.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*
* ==== Security Considerations
*
* There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
* expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
* considered as an intention to spend the allowance in any specific way. The second is that because permits have
* built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
* take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
* generally recommended is:
*
* ```solidity
* function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
* try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
* doThing(..., value);
* }
*
* function doThing(..., uint256 value) public {
* token.safeTransferFrom(msg.sender, address(this), value);
* ...
* }
* ```
*
* Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
* `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
* {SafeERC20-safeTransferFrom}).
*
* Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
* contracts should have entry points that don't rely on permit.
*/
interface IERC20Permit {
/**
* @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
* given ``owner``'s signed approval.
*
* IMPORTANT: The same issues {IERC20-approve} has related to transaction
* ordering also apply here.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `deadline` must be a timestamp in the future.
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
* over the EIP712-formatted function arguments.
* - the signature must use ``owner``'s current nonce (see {nonces}).
*
* For more information on the signature format, see the
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
* section].
*
* CAUTION: See Security Considerations above.
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @dev Returns the current nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/
function nonces(address owner) external view returns (uint256);
/**
* @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the 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.0.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
import {IERC20Permit} from "../extensions/IERC20Permit.sol";
import {Address} from "../../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using Address for address;
/**
* @dev An operation with an ERC20 token failed.
*/
error SafeERC20FailedOperation(address token);
/**
* @dev Indicates a failed `decreaseAllowance` request.
*/
error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
forceApprove(token, spender, oldAllowance + value);
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
* value, non-reverting calls are assumed to be successful.
*/
function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
unchecked {
uint256 currentAllowance = token.allowance(address(this), spender);
if (currentAllowance < requestedDecrease) {
revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
}
forceApprove(token, spender, currentAllowance - requestedDecrease);
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*/
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data);
if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
// and not revert is the subcall reverts.
(bool success, bytes memory returndata) = address(token).call(data);
return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)
pragma solidity ^0.8.20;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev The ETH balance of the account is not enough to perform the operation.
*/
error AddressInsufficientBalance(address account);
/**
* @dev There's no code at `target` (it is not a contract).
*/
error AddressEmptyCode(address target);
/**
* @dev A call to an address target failed. The target may have reverted.
*/
error FailedInnerCall();
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
if (address(this).balance < amount) {
revert AddressInsufficientBalance(address(this));
}
(bool success, ) = recipient.call{value: amount}("");
if (!success) {
revert FailedInnerCall();
}
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason or custom error, it is bubbled
* up by this function (like regular Solidity function calls). However, if
* the call reverted with no returned reason, this function reverts with a
* {FailedInnerCall} error.
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
if (address(this).balance < value) {
revert AddressInsufficientBalance(address(this));
}
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
* was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
* unsuccessful call.
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata
) internal view returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
// only check if target is a contract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
if (returndata.length == 0 && target.code.length == 0) {
revert AddressEmptyCode(target);
}
return returndata;
}
}
/**
* @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
* revert reason or with a default {FailedInnerCall} error.
*/
function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
return returndata;
}
}
/**
* @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
*/
function _revert(bytes memory returndata) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert FailedInnerCall();
}
}
}// 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.0.0) (utils/cryptography/ECDSA.sol)
pragma solidity ^0.8.20;
/**
* @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
*
* These functions can be used to verify that a message was signed by the holder
* of the private keys of a given address.
*/
library ECDSA {
enum RecoverError {
NoError,
InvalidSignature,
InvalidSignatureLength,
InvalidSignatureS
}
/**
* @dev The signature derives the `address(0)`.
*/
error ECDSAInvalidSignature();
/**
* @dev The signature has an invalid length.
*/
error ECDSAInvalidSignatureLength(uint256 length);
/**
* @dev The signature has an S value that is in the upper half order.
*/
error ECDSAInvalidSignatureS(bytes32 s);
/**
* @dev Returns the address that signed a hashed message (`hash`) with `signature` or an error. This will not
* return address(0) without also returning an error description. Errors are documented using an enum (error type)
* and a bytes32 providing additional information about the error.
*
* If no error is returned, then the address can be used for verification purposes.
*
* The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.
*
* Documentation for signature generation:
* - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
* - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
*/
function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError, bytes32) {
if (signature.length == 65) {
bytes32 r;
bytes32 s;
uint8 v;
// ecrecover takes the signature parameters, and the only way to get them
// currently is to use assembly.
/// @solidity memory-safe-assembly
assembly {
r := mload(add(signature, 0x20))
s := mload(add(signature, 0x40))
v := byte(0, mload(add(signature, 0x60)))
}
return tryRecover(hash, v, r, s);
} else {
return (address(0), RecoverError.InvalidSignatureLength, bytes32(signature.length));
}
}
/**
* @dev Returns the address that signed a hashed message (`hash`) with
* `signature`. This address can then be used for verification purposes.
*
* The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:
* this function rejects them by requiring the `s` value to be in the lower
* half order, and the `v` value to be either 27 or 28.
*
* IMPORTANT: `hash` _must_ be the result of a hash operation for the
* verification to be secure: it is possible to craft signatures that
* recover to arbitrary addresses for non-hashed data. A safe way to ensure
* this is by receiving a hash of the original message (which may otherwise
* be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.
*/
function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
(address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, signature);
_throwError(error, errorArg);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
*
* See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
*/
function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError, bytes32) {
unchecked {
bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
// We do not check for an overflow here since the shift operation results in 0 or 1.
uint8 v = uint8((uint256(vs) >> 255) + 27);
return tryRecover(hash, v, r, s);
}
}
/**
* @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
*/
function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
(address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, r, vs);
_throwError(error, errorArg);
return recovered;
}
/**
* @dev Overload of {ECDSA-tryRecover} that receives the `v`,
* `r` and `s` signature fields separately.
*/
function tryRecover(
bytes32 hash,
uint8 v,
bytes32 r,
bytes32 s
) internal pure returns (address, RecoverError, bytes32) {
// EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
// unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
// the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
// signatures from current libraries generate a unique signature with an s-value in the lower half order.
//
// If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
// with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
// vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
// these malleable signatures as well.
if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
return (address(0), RecoverError.InvalidSignatureS, s);
}
// If the signature is valid (and not malleable), return the signer address
address signer = ecrecover(hash, v, r, s);
if (signer == address(0)) {
return (address(0), RecoverError.InvalidSignature, bytes32(0));
}
return (signer, RecoverError.NoError, bytes32(0));
}
/**
* @dev Overload of {ECDSA-recover} that receives the `v`,
* `r` and `s` signature fields separately.
*/
function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
(address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, v, r, s);
_throwError(error, errorArg);
return recovered;
}
/**
* @dev Optionally reverts with the corresponding custom error according to the `error` argument provided.
*/
function _throwError(RecoverError error, bytes32 errorArg) private pure {
if (error == RecoverError.NoError) {
return; // no error: do nothing
} else if (error == RecoverError.InvalidSignature) {
revert ECDSAInvalidSignature();
} else if (error == RecoverError.InvalidSignatureLength) {
revert ECDSAInvalidSignatureLength(uint256(errorArg));
} else if (error == RecoverError.InvalidSignatureS) {
revert ECDSAInvalidSignatureS(errorArg);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/EIP712.sol)
pragma solidity ^0.8.20;
import {MessageHashUtils} from "./MessageHashUtils.sol";
import {ShortStrings, ShortString} from "../ShortStrings.sol";
import {IERC5267} from "../../interfaces/IERC5267.sol";
/**
* @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
*
* The encoding scheme specified in the EIP requires a domain separator and a hash of the typed structured data, whose
* encoding is very generic and therefore its implementation in Solidity is not feasible, thus this contract
* does not implement the encoding itself. Protocols need to implement the type-specific encoding they need in order to
* produce the hash of their typed data using a combination of `abi.encode` and `keccak256`.
*
* This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
* scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
* ({_hashTypedDataV4}).
*
* The implementation of the domain separator was designed to be as efficient as possible while still properly updating
* the chain id to protect against replay attacks on an eventual fork of the chain.
*
* NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
* https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
*
* NOTE: In the upgradeable version of this contract, the cached values will correspond to the address, and the domain
* separator of the implementation contract. This will cause the {_domainSeparatorV4} function to always rebuild the
* separator from the immutable values, which is cheaper than accessing a cached version in cold storage.
*
* @custom:oz-upgrades-unsafe-allow state-variable-immutable
*/
abstract contract EIP712 is IERC5267 {
using ShortStrings for *;
bytes32 private constant TYPE_HASH =
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
// Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to
// invalidate the cached domain separator if the chain id changes.
bytes32 private immutable _cachedDomainSeparator;
uint256 private immutable _cachedChainId;
address private immutable _cachedThis;
bytes32 private immutable _hashedName;
bytes32 private immutable _hashedVersion;
ShortString private immutable _name;
ShortString private immutable _version;
string private _nameFallback;
string private _versionFallback;
/**
* @dev Initializes the domain separator and parameter caches.
*
* The meaning of `name` and `version` is specified in
* https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:
*
* - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
* - `version`: the current major version of the signing domain.
*
* NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
* contract upgrade].
*/
constructor(string memory name, string memory version) {
_name = name.toShortStringWithFallback(_nameFallback);
_version = version.toShortStringWithFallback(_versionFallback);
_hashedName = keccak256(bytes(name));
_hashedVersion = keccak256(bytes(version));
_cachedChainId = block.chainid;
_cachedDomainSeparator = _buildDomainSeparator();
_cachedThis = address(this);
}
/**
* @dev Returns the domain separator for the current chain.
*/
function _domainSeparatorV4() internal view returns (bytes32) {
if (address(this) == _cachedThis && block.chainid == _cachedChainId) {
return _cachedDomainSeparator;
} else {
return _buildDomainSeparator();
}
}
function _buildDomainSeparator() private view returns (bytes32) {
return keccak256(abi.encode(TYPE_HASH, _hashedName, _hashedVersion, block.chainid, address(this)));
}
/**
* @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
* function returns the hash of the fully encoded EIP712 message for this domain.
*
* This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
*
* ```solidity
* bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
* keccak256("Mail(address to,string contents)"),
* mailTo,
* keccak256(bytes(mailContents))
* )));
* address signer = ECDSA.recover(digest, signature);
* ```
*/
function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
return MessageHashUtils.toTypedDataHash(_domainSeparatorV4(), structHash);
}
/**
* @dev See {IERC-5267}.
*/
function eip712Domain()
public
view
virtual
returns (
bytes1 fields,
string memory name,
string memory version,
uint256 chainId,
address verifyingContract,
bytes32 salt,
uint256[] memory extensions
)
{
return (
hex"0f", // 01111
_EIP712Name(),
_EIP712Version(),
block.chainid,
address(this),
bytes32(0),
new uint256[](0)
);
}
/**
* @dev The name parameter for the EIP712 domain.
*
* NOTE: By default this function reads _name which is an immutable value.
* It only reads from storage if necessary (in case the value is too large to fit in a ShortString).
*/
// solhint-disable-next-line func-name-mixedcase
function _EIP712Name() internal view returns (string memory) {
return _name.toStringWithFallback(_nameFallback);
}
/**
* @dev The version parameter for the EIP712 domain.
*
* NOTE: By default this function reads _version which is an immutable value.
* It only reads from storage if necessary (in case the value is too large to fit in a ShortString).
*/
// solhint-disable-next-line func-name-mixedcase
function _EIP712Version() internal view returns (string memory) {
return _version.toStringWithFallback(_versionFallback);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/MessageHashUtils.sol)
pragma solidity ^0.8.20;
import {Strings} from "../Strings.sol";
/**
* @dev Signature message hash utilities for producing digests to be consumed by {ECDSA} recovery or signing.
*
* The library provides methods for generating a hash of a message that conforms to the
* https://eips.ethereum.org/EIPS/eip-191[EIP 191] and https://eips.ethereum.org/EIPS/eip-712[EIP 712]
* specifications.
*/
library MessageHashUtils {
/**
* @dev Returns the keccak256 digest of an EIP-191 signed data with version
* `0x45` (`personal_sign` messages).
*
* The digest is calculated by prefixing a bytes32 `messageHash` with
* `"\x19Ethereum Signed Message:\n32"` and hashing the result. It corresponds with the
* hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method.
*
* NOTE: The `messageHash` parameter is intended to be the result of hashing a raw message with
* keccak256, although any bytes32 value can be safely used because the final digest will
* be re-hashed.
*
* See {ECDSA-recover}.
*/
function toEthSignedMessageHash(bytes32 messageHash) internal pure returns (bytes32 digest) {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, "\x19Ethereum Signed Message:\n32") // 32 is the bytes-length of messageHash
mstore(0x1c, messageHash) // 0x1c (28) is the length of the prefix
digest := keccak256(0x00, 0x3c) // 0x3c is the length of the prefix (0x1c) + messageHash (0x20)
}
}
/**
* @dev Returns the keccak256 digest of an EIP-191 signed data with version
* `0x45` (`personal_sign` messages).
*
* The digest is calculated by prefixing an arbitrary `message` with
* `"\x19Ethereum Signed Message:\n" + len(message)` and hashing the result. It corresponds with the
* hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method.
*
* See {ECDSA-recover}.
*/
function toEthSignedMessageHash(bytes memory message) internal pure returns (bytes32) {
return
keccak256(bytes.concat("\x19Ethereum Signed Message:\n", bytes(Strings.toString(message.length)), message));
}
/**
* @dev Returns the keccak256 digest of an EIP-191 signed data with version
* `0x00` (data with intended validator).
*
* The digest is calculated by prefixing an arbitrary `data` with `"\x19\x00"` and the intended
* `validator` address. Then hashing the result.
*
* See {ECDSA-recover}.
*/
function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) {
return keccak256(abi.encodePacked(hex"19_00", validator, data));
}
/**
* @dev Returns the keccak256 digest of an EIP-712 typed data (EIP-191 version `0x01`).
*
* The digest is calculated from a `domainSeparator` and a `structHash`, by prefixing them with
* `\x19\x01` and hashing the result. It corresponds to the hash signed by the
* https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] JSON-RPC method as part of EIP-712.
*
* See {ECDSA-recover}.
*/
function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 digest) {
/// @solidity memory-safe-assembly
assembly {
let ptr := mload(0x40)
mstore(ptr, hex"19_01")
mstore(add(ptr, 0x02), domainSeparator)
mstore(add(ptr, 0x22), structHash)
digest := keccak256(ptr, 0x42)
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
/**
* @dev Muldiv operation overflow.
*/
error MathOverflowedMulDiv();
enum Rounding {
Floor, // Toward negative infinity
Ceil, // Toward positive infinity
Trunc, // Toward zero
Expand // Away from zero
}
/**
* @dev Returns the addition of two unsigned integers, with an overflow flag.
*/
function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the subtraction of two unsigned integers, with an overflow flag.
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b > a) return (false, 0);
return (true, a - b);
}
}
/**
* @dev Returns the multiplication of two unsigned integers, with an overflow flag.
*/
function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the division of two unsigned integers, with a division by zero flag.
*/
function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a / b);
}
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
*/
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a % b);
}
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds towards infinity instead
* of rounding towards zero.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
if (b == 0) {
// Guarantee the same behavior as in a regular Solidity division.
return a / b;
}
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
* denominator == 0.
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
* Uniswap Labs also under MIT license.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0 = x * y; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.
// The surrounding unchecked block does not change this fact.
// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
if (denominator <= prod1) {
revert MathOverflowedMulDiv();
}
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator.
// Always >= 1. See https://cs.stackexchange.com/q/138556/92363.
uint256 twos = denominator & (0 - denominator);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also
// works in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
* towards zero.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
//
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
//
// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
//
// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1 << (log2(a) >> 1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256 of a positive value rounded towards zero.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 256, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
}
}
/**
* @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
*/
function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
return uint8(rounding) % 2 == 1;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SignedMath.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard signed math utilities missing in the Solidity language.
*/
library SignedMath {
/**
* @dev Returns the largest of two signed numbers.
*/
function max(int256 a, int256 b) internal pure returns (int256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two signed numbers.
*/
function min(int256 a, int256 b) internal pure returns (int256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two signed numbers without overflow.
* The result is rounded towards zero.
*/
function average(int256 a, int256 b) internal pure returns (int256) {
// Formula from the book "Hacker's Delight"
int256 x = (a & b) + ((a ^ b) >> 1);
return x + (int256(uint256(x) >> 255) & (a ^ b));
}
/**
* @dev Returns the absolute unsigned value of a signed value.
*/
function abs(int256 n) internal pure returns (uint256) {
unchecked {
// must be unchecked in order to support `n = type(int256).min`
return uint256(n >= 0 ? n : -n);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/ShortStrings.sol)
pragma solidity ^0.8.20;
import {StorageSlot} from "./StorageSlot.sol";
// | string | 0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA |
// | length | 0x BB |
type ShortString is bytes32;
/**
* @dev This library provides functions to convert short memory strings
* into a `ShortString` type that can be used as an immutable variable.
*
* Strings of arbitrary length can be optimized using this library if
* they are short enough (up to 31 bytes) by packing them with their
* length (1 byte) in a single EVM word (32 bytes). Additionally, a
* fallback mechanism can be used for every other case.
*
* Usage example:
*
* ```solidity
* contract Named {
* using ShortStrings for *;
*
* ShortString private immutable _name;
* string private _nameFallback;
*
* constructor(string memory contractName) {
* _name = contractName.toShortStringWithFallback(_nameFallback);
* }
*
* function name() external view returns (string memory) {
* return _name.toStringWithFallback(_nameFallback);
* }
* }
* ```
*/
library ShortStrings {
// Used as an identifier for strings longer than 31 bytes.
bytes32 private constant FALLBACK_SENTINEL = 0x00000000000000000000000000000000000000000000000000000000000000FF;
error StringTooLong(string str);
error InvalidShortString();
/**
* @dev Encode a string of at most 31 chars into a `ShortString`.
*
* This will trigger a `StringTooLong` error is the input string is too long.
*/
function toShortString(string memory str) internal pure returns (ShortString) {
bytes memory bstr = bytes(str);
if (bstr.length > 31) {
revert StringTooLong(str);
}
return ShortString.wrap(bytes32(uint256(bytes32(bstr)) | bstr.length));
}
/**
* @dev Decode a `ShortString` back to a "normal" string.
*/
function toString(ShortString sstr) internal pure returns (string memory) {
uint256 len = byteLength(sstr);
// using `new string(len)` would work locally but is not memory safe.
string memory str = new string(32);
/// @solidity memory-safe-assembly
assembly {
mstore(str, len)
mstore(add(str, 0x20), sstr)
}
return str;
}
/**
* @dev Return the length of a `ShortString`.
*/
function byteLength(ShortString sstr) internal pure returns (uint256) {
uint256 result = uint256(ShortString.unwrap(sstr)) & 0xFF;
if (result > 31) {
revert InvalidShortString();
}
return result;
}
/**
* @dev Encode a string into a `ShortString`, or write it to storage if it is too long.
*/
function toShortStringWithFallback(string memory value, string storage store) internal returns (ShortString) {
if (bytes(value).length < 32) {
return toShortString(value);
} else {
StorageSlot.getStringSlot(store).value = value;
return ShortString.wrap(FALLBACK_SENTINEL);
}
}
/**
* @dev Decode a string that was encoded to `ShortString` or written to storage using {setWithFallback}.
*/
function toStringWithFallback(ShortString value, string storage store) internal pure returns (string memory) {
if (ShortString.unwrap(value) != FALLBACK_SENTINEL) {
return toString(value);
} else {
return store;
}
}
/**
* @dev Return the length of a string that was encoded to `ShortString` or written to storage using
* {setWithFallback}.
*
* WARNING: This will return the "byte length" of the string. This may not reflect the actual length in terms of
* actual characters as the UTF-8 encoding of a single character can span over multiple bytes.
*/
function byteLengthWithFallback(ShortString value, string storage store) internal view returns (uint256) {
if (ShortString.unwrap(value) != FALLBACK_SENTINEL) {
return byteLength(value);
} else {
return bytes(store).length;
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/StorageSlot.sol)
// This file was procedurally generated from scripts/generate/templates/StorageSlot.js.
pragma solidity ^0.8.20;
/**
* @dev Library for reading and writing primitive types to specific storage slots.
*
* Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
* This library helps with reading and writing to such slots without the need for inline assembly.
*
* The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
*
* Example usage to set ERC1967 implementation slot:
* ```solidity
* contract ERC1967 {
* bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
*
* function _getImplementation() internal view returns (address) {
* return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
* }
*
* function _setImplementation(address newImplementation) internal {
* require(newImplementation.code.length > 0);
* StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
* }
* }
* ```
*/
library StorageSlot {
struct AddressSlot {
address value;
}
struct BooleanSlot {
bool value;
}
struct Bytes32Slot {
bytes32 value;
}
struct Uint256Slot {
uint256 value;
}
struct StringSlot {
string value;
}
struct BytesSlot {
bytes value;
}
/**
* @dev Returns an `AddressSlot` with member `value` located at `slot`.
*/
function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `BooleanSlot` with member `value` located at `slot`.
*/
function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
*/
function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `Uint256Slot` with member `value` located at `slot`.
*/
function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `StringSlot` with member `value` located at `slot`.
*/
function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `StringSlot` representation of the string storage pointer `store`.
*/
function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := store.slot
}
}
/**
* @dev Returns an `BytesSlot` with member `value` located at `slot`.
*/
function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := slot
}
}
/**
* @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
*/
function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
/// @solidity memory-safe-assembly
assembly {
r.slot := store.slot
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Strings.sol)
pragma solidity ^0.8.20;
import {Math} from "./math/Math.sol";
import {SignedMath} from "./math/SignedMath.sol";
/**
* @dev String operations.
*/
library Strings {
bytes16 private constant HEX_DIGITS = "0123456789abcdef";
uint8 private constant ADDRESS_LENGTH = 20;
/**
* @dev The `value` string doesn't fit in the specified `length`.
*/
error StringsInsufficientHexLength(uint256 value, uint256 length);
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/
function toString(uint256 value) internal pure returns (string memory) {
unchecked {
uint256 length = Math.log10(value) + 1;
string memory buffer = new string(length);
uint256 ptr;
/// @solidity memory-safe-assembly
assembly {
ptr := add(buffer, add(32, length))
}
while (true) {
ptr--;
/// @solidity memory-safe-assembly
assembly {
mstore8(ptr, byte(mod(value, 10), HEX_DIGITS))
}
value /= 10;
if (value == 0) break;
}
return buffer;
}
}
/**
* @dev Converts a `int256` to its ASCII `string` decimal representation.
*/
function toStringSigned(int256 value) internal pure returns (string memory) {
return string.concat(value < 0 ? "-" : "", toString(SignedMath.abs(value)));
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
*/
function toHexString(uint256 value) internal pure returns (string memory) {
unchecked {
return toHexString(value, Math.log256(value) + 1);
}
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
*/
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
uint256 localValue = value;
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = HEX_DIGITS[localValue & 0xf];
localValue >>= 4;
}
if (localValue != 0) {
revert StringsInsufficientHexLength(value, length);
}
return string(buffer);
}
/**
* @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal
* representation.
*/
function toHexString(address addr) internal pure returns (string memory) {
return toHexString(uint256(uint160(addr)), ADDRESS_LENGTH);
}
/**
* @dev Returns true if the two strings are equal.
*/
function equal(string memory a, string memory b) internal pure returns (bool) {
return bytes(a).length == bytes(b).length && keccak256(bytes(a)) == keccak256(bytes(b));
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
interface IRBAC {
function grantRole(bytes32 role, address account) external;
function revokeRole(bytes32 role, address account) external;
function hasRole(bytes32 role, address account) external returns (bool);
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
library MathLib {
uint256 private constant PRECISION = 1e18;
function mulp(uint256 a, uint256 b) internal pure returns (uint256) {
return a * b / PRECISION;
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
import "./Roles.sol";
contract Ownable is Roles {
address public owner;
constructor(address _rbac) Roles(_rbac) {
owner = msg.sender;
}
modifier onlyOwner() {
require(isOwner(msg.sender), "Not allowed - only owner can call");
_;
}
function changeOwner(address _newOwner) external {
require(IRBAC(rbac).hasRole(SUPERVISOR_ROLE, msg.sender) || msg.sender == owner, "Not allowed - changing owner");
owner = _newOwner;
}
function isOwner(address _address) public view returns (bool) {
return _address == owner;
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "./RBAC.sol";
import "./Roles.sol";
import "./Ownable.sol";
contract PermissionedToken is ERC20, Ownable {
mapping(address => bool) private frozenAccounts;
event AccountFrozen(address indexed account);
event AccountUnfrozen(address indexed account);
event TokensMinted(address indexed saleId, address indexed to, uint256 amount);
constructor(address _rbac, string memory _name, string memory _token) ERC20(_name, _token) Ownable(_rbac) {}
modifier isNotFrozen(address to) {
require(!frozenAccounts[to], "Account is frozen");
_;
}
modifier isFrozen(address to) {
require(frozenAccounts[to], "Account is not frozen");
_;
}
function mint(address to, uint256 amount) public virtual onlyRole(MINTER_ROLE) isNotFrozen(to) {
assert(msg.sender != address(0));
_mint(to, amount);
emit TokensMinted(address(0), to, amount);
}
function burn(uint256 amount) external isNotFrozen(msg.sender) {
assert(msg.sender != address(0));
_burn(msg.sender, amount);
}
function freeze(address account) external onlyRole(FREEZER_ROLE) isNotFrozen(account) {
assert(msg.sender != address(0));
frozenAccounts[account] = true;
emit AccountFrozen(account);
}
function unfreeze(address account) external onlyRole(FREEZER_ROLE) isFrozen(account) {
assert(msg.sender != address(0));
frozenAccounts[account] = false;
emit AccountUnfrozen(account);
}
function transfer(address to, uint256 amount) public isNotFrozen(msg.sender) isNotFrozen(to) override returns (bool) {
_transfer(msg.sender, to, amount);
return true;
}
function transferFrom(address from, address to, uint256 amount) public isNotFrozen(from) isNotFrozen(to) override returns (bool) {
super.transferFrom(from, to, amount);
return true;
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.20;
import "./Roles.sol";
import "@openzeppelin/contracts/utils/cryptography/EIP712.sol";
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
contract RBAC is Roles, EIP712 {
mapping(address => mapping(bytes32 => bool)) private roles;
mapping(address => mapping(address => bool)) private saleWhiteLists;
mapping(bytes32 => bool) private processedMessages;
bytes32 private constant _GRANTING_ROLE_TYPE_HASH = keccak256("GrantingRoleMessage(address target,bytes32[] roles,uint256 salt)");
bytes32 private constant _ADDING_WALLET_TO_WHITE_LIST_HASH = keccak256("AddingToWhiteListMessage(address sale,address wallet,uint256 salt)");
string private constant SIGNATURE_DOMAIN = "RBACDomain";
string private constant SIGNATURE_VERSION = "1";
event RoleGranted(bytes32 indexed role, address indexed target);
event RoleRevoked(bytes32 indexed role, address indexed target);
event WalletInWhiteList(address indexed sale, address indexed wallet);
event WalletRemovedFromWhiteList(address indexed sale, address indexed wallet);
constructor(address initialOwner) Roles(address(this)) EIP712(SIGNATURE_DOMAIN, SIGNATURE_VERSION) {
_grantRole(SUPERVISOR_ROLE, initialOwner);
}
struct GrantingRoleMessage {
address target;
bytes32[] roles;
uint256 salt;
}
struct AddingToWhiteListMessage {
address sale;
address wallet;
uint256 salt;
}
function claimRoles(GrantingRoleMessage memory message, bytes calldata signature) external {
require(msg.sender == message.target, "Invalid sender");
bytes32 hash = _hashTypedDataV4(
keccak256(
abi.encode(
_GRANTING_ROLE_TYPE_HASH,
message.target,
keccak256(abi.encodePacked(message.roles)),
message.salt
)
)
);
_processMessage(hash, signature);
_grantRoles(message.roles, message.target);
}
function grantRole(bytes32 role, address account) external onlyRole(SUPERVISOR_ROLE) {
require(!hasRole(role, account), "Role is already granted");
_grantRole(role, account);
}
function grantRoles(bytes32[] memory roles, address account) external onlyRole(SUPERVISOR_ROLE) {
_grantRoles(roles, account);
}
function revokeRole(bytes32 role, address account) external onlyRole(SUPERVISOR_ROLE) {
_revokeRole(role, account);
emit RoleRevoked(role, account);
}
function addWalletToSaleWhiteList(AddingToWhiteListMessage memory message, bytes calldata signature) external {
bytes32 hash = _hashTypedDataV4(
keccak256(
abi.encode(
_ADDING_WALLET_TO_WHITE_LIST_HASH,
message.sale,
message.wallet,
message.salt
)
)
);
_processMessage(hash, signature);
_addToSaleWhiteList(message.sale, message.wallet);
}
function removeWalletFromSaleWhiteList(address sale, address wallet) external onlyRole(SUPERVISOR_ROLE) {
_removeFromSaleWhiteList(sale, wallet);
}
function hasRole(bytes32 role, address account) public view returns (bool) {
return roles[account][role];
}
function isInWhiteList(address sale, address wallet) public view returns (bool) {
return saleWhiteLists[sale][wallet];
}
function _revokeRole(bytes32 role, address account) internal {
assert(msg.sender != address(0));
require(hasRole(role, account), "Account doesn't have this role");
roles[account][role] = false;
}
function _grantRole(bytes32 role, address account) internal {
assert(msg.sender != address(0));
roles[account][role] = true;
emit RoleGranted(role, account);
}
function _grantRoles(bytes32[] memory grantingRoles, address account) internal {
for (uint256 i = 0; i < grantingRoles.length; i++) {
_grantRole(grantingRoles[i], account);
}
}
function _addToSaleWhiteList(address sale, address wallet) internal {
saleWhiteLists[sale][wallet] = true;
emit WalletInWhiteList(sale, wallet);
}
function _removeFromSaleWhiteList(address sale, address wallet) internal {
saleWhiteLists[sale][wallet] = false;
emit WalletRemovedFromWhiteList(sale, wallet);
}
function _processMessage(bytes32 hash, bytes calldata signature) private {
require(!processedMessages[hash], "Message already processed");
require(hasRole(SUPERVISOR_ROLE, ECDSA.recover(hash, signature)), "Not allowed");
processedMessages[hash] = true;
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "./PermissionedToken.sol";
import "./MathLib.sol";
import "./Roles.sol";
contract RedeemablePermissionedToken is PermissionedToken {
using SafeERC20 for IERC20;
using MathLib for uint;
uint256 private redeemPrice;
uint256 private redeemDate;
uint256 private totalRedeemed;
uint256 private immutable rwaAmount;
address private immutable token;
event TokensRedeemed(address indexed from, uint256 amount, uint256 stablecoinAmount);
event AllTokensRedeemed(address indexed token);
event RedeemRateChanged(uint256 amount);
event RedeemDateChanged(uint32 date);
event Deposited(address indexed from, uint256 amount, address token, uint256 newRate);
constructor(
address _rbac,
string memory _name,
string memory _ticker,
address _token,
uint256 _redeemPrice,
uint32 _redeemDate,
uint256 _rwaAmount
) PermissionedToken(_rbac, _name, _ticker) {
token = _token;
redeemPrice = _redeemPrice;
redeemDate = _redeemDate;
rwaAmount = _rwaAmount;
}
function mint(address to, uint256 amount) override public {
require((totalSupply() + amount + totalRedeemed) / 1e18 <= rwaAmount, "Max mint amount reached");
super.mint(to, amount);
}
function redeem(uint256 _amount) external isNotFrozen(msg.sender) {
require(redeemDate < block.timestamp, "Redeem is not allowed yet");
uint256 toSend = _amount.mulp(redeemPrice);
totalRedeemed += _amount;
if (isAllTokensRedeemed()) {
emit AllTokensRedeemed(address(this));
}
_burn(msg.sender, _amount);
IERC20 paymentToken = IERC20(token);
paymentToken.safeTransfer(msg.sender, toSend);
emit TokensRedeemed(msg.sender, _amount, toSend);
}
function deposit(uint256 amount, uint256 newRate) external onlyOwner onlyRole(REDEEMER_ROLE) {
require(amount > 0, "Deposit amount must be greater than 0");
require(newRate > 0, "New rate must be greater than 0");
uint256 expectedAmount = ((rwaAmount - (totalRedeemed / 1e18)) * newRate) - IERC20(token).balanceOf(address(this));
require(amount >= expectedAmount, "Insufficient deposit amount");
IERC20 paymentToken = IERC20(token);
paymentToken.safeTransferFrom(msg.sender, address(this), amount);
redeemPrice = newRate;
emit Deposited(msg.sender, amount, token, newRate);
}
function updateRedeemRate(uint256 _newRate) external onlyOwner onlyRole(REDEEMER_ROLE) {
require(_newRate <= redeemPrice, "New rate can not be more than old value");
redeemPrice = _newRate;
emit RedeemRateChanged(_newRate);
}
function updateRedeemDate(uint32 _redeemDate) external {
require((isOwner(msg.sender) && IRBAC(rbac).hasRole(REDEEMER_ROLE, msg.sender)) || IRBAC(rbac).hasRole(SUPERVISOR_ROLE, msg.sender), "Not allowed");
redeemDate = _redeemDate;
emit RedeemDateChanged(_redeemDate);
}
function withdrawFunds(uint _amount) external onlyRole(SUPERVISOR_ROLE) {
IERC20 paymentToken = IERC20(token);
uint256 balance = paymentToken.balanceOf(address(this));
require(balance >= _amount, "Not enough contract balance");
paymentToken.safeTransfer(msg.sender, _amount);
}
function isAllTokensRedeemed() public view returns (bool) {
return totalRedeemed / 1e18 == rwaAmount;
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.20;
import "./RedeemablePermissionedToken.sol";
import "./Roles.sol";
contract RedeemablePermissionedTokenFactory is Roles {
address[] private tokens;
event NewToken(address indexed tokenContract, string name, string ticker, address token, uint256 rate, uint32 date, uint256 amount);
constructor(address _rbac) Roles(_rbac) {}
function createToken(string memory _name, string memory _ticker, address _paymentToken, uint256 _rate, uint32 _date, uint256 _amount, address owner) public onlyRole(MINTER_ROLE) returns (address) {
RedeemablePermissionedToken newToken = new RedeemablePermissionedToken(rbac, _name, _ticker, _paymentToken, _rate, _date, _amount);
address tokenAddress = address(newToken);
newToken.changeOwner(owner);
tokens.push(tokenAddress);
emit NewToken(tokenAddress, _name, _ticker, _paymentToken, _rate, _date, _amount);
return tokenAddress;
}
function getDeployedTokens() public view returns (address[] memory) {
return tokens;
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.20;
import "./IRBAC.sol";
bytes32 constant SUPERVISOR_ROLE = keccak256("SUPERVISOR_ROLE");
bytes32 constant SALE_WITHDRAW_ROLE = keccak256("SALE_WITHDRAW_ROLE");
bytes32 constant SALE_MANAGER_ROLE = keccak256("SALE_MANAGER_ROLE");
bytes32 constant INVESTOR_ROLE = keccak256("INVESTOR_ROLE");
bytes32 constant MINTER_ROLE = keccak256("MINTER_ROLE");
bytes32 constant FREEZER_ROLE = keccak256("FREEZER_ROLE");
bytes32 constant REDEEMER_ROLE = keccak256("REDEEMER_ROLE");
bytes32 constant BRIDGE_ADMIN_ROLE = keccak256("BRIDGE_ADMIN_ROLE");
contract Roles {
address public immutable rbac;
constructor(address _rbacAddress) {
rbac = _rbacAddress;
}
modifier onlyRole(bytes32 role) {
bool isAllowed = IRBAC(rbac).hasRole(role, msg.sender);
require(isAllowed, "Not allowed");
_;
}
}{
"optimizer": {
"enabled": true,
"runs": 999999
},
"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":"address","name":"_rbac","type":"address"},{"internalType":"address","name":"_rwaTokenFactory","type":"address"},{"components":[{"internalType":"string","name":"saleId","type":"string"},{"internalType":"bytes32","name":"investorRole","type":"bytes32"},{"internalType":"address","name":"paymentToken","type":"address"},{"internalType":"uint256","name":"price","type":"uint256"},{"internalType":"uint256","name":"minCommitment","type":"uint256"},{"internalType":"uint256","name":"maxCommitment","type":"uint256"},{"internalType":"uint256","name":"hardcap","type":"uint256"},{"internalType":"uint256","name":"softcap","type":"uint256"},{"internalType":"uint32","name":"saleStart","type":"uint32"},{"internalType":"uint32","name":"saleEnd","type":"uint32"},{"internalType":"bool","name":"cancelable","type":"bool"},{"internalType":"bool","name":"isAddressed","type":"bool"}],"internalType":"struct PermissionedSale.SaleParams","name":"_params","type":"tuple"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"AddressInsufficientBalance","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"saleId","type":"string"},{"indexed":true,"internalType":"address","name":"investor","type":"address"},{"indexed":false,"internalType":"bytes32","name":"commitmentHash","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"bool","name":"isSoftCapAchieved","type":"bool"}],"name":"CommitmentCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"saleId","type":"string"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"FundsClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"saleId","type":"string"},{"indexed":true,"internalType":"address","name":"investor","type":"address"},{"indexed":false,"internalType":"bytes32","name":"commitmentHash","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"bool","name":"isSoftCapAchieved","type":"bool"}],"name":"FundsCommitted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"saleId","type":"string"}],"name":"SaleCanceled","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"saleId","type":"string"},{"indexed":false,"internalType":"bool","name":"success","type":"bool"}],"name":"SaleFinalized","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"saleId","type":"string"}],"name":"SaleStarted","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"saleId","type":"string"},{"indexed":false,"internalType":"address","name":"rwaToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"rwaAmount","type":"uint256"}],"name":"TokenDeployed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"saleId","type":"string"},{"indexed":true,"internalType":"address","name":"investor","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"TokensClaimed","type":"event"},{"inputs":[{"internalType":"bytes32[]","name":"hashes","type":"bytes32[]"}],"name":"cancelCommitment","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"cancelSale","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newOwner","type":"address"}],"name":"changeOwner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claimFunds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claimTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"commitFunds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"sale","type":"address"},{"internalType":"address","name":"wallet","type":"address"},{"internalType":"uint256","name":"salt","type":"uint256"}],"internalType":"struct RBAC.AddingToWhiteListMessage","name":"message","type":"tuple"},{"internalType":"bytes","name":"signature","type":"bytes"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"commitFundsWhitelisted","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"_redeemDate","type":"uint32"},{"internalType":"string","name":"_name","type":"string"},{"internalType":"string","name":"_ticker","type":"string"}],"name":"deployToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"finalizeSale","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"isDeployed","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isFinalized","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isHardCapAchieved","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_address","type":"address"}],"name":"isOwner","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isSoftCapAchieved","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isStarted","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nextCommitmentIdx","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"rbac","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"startSale","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"totalCommited","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"withdrawFunds","outputs":[],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
60c06040526000600b55600c805463ffffffff191690553480156200002357600080fd5b50604051620042ec380380620042ec8339810160408190526200004691620004ca565b6001600160a01b03808416608052600080546001600160a01b031916331790556040820151166200007b576200007b6200060e565b6001600160a01b0382166200009457620000946200060e565b6000816060015111620000ee5760405162461bcd60e51b815260206004820152601d60248201527f5072696365206d75737420626520706f736974697665206e756d62657200000060448201526064015b60405180910390fd5b8060e001518160c001511015620001525760405162461bcd60e51b815260206004820152602160248201527f48617264636170206d757374206265206d6f7265207468616e20736f667463616044820152600760fc1b6064820152608401620000e5565b80610120015163ffffffff1681610100015163ffffffff1610620001d45760405162461bcd60e51b815260206004820152603260248201527f53746172742074696d657374616d70206d757374206265206561726c6965722060448201527107468616e20656e642074696d657374616d760741b6064820152608401620000e5565b4281610120015163ffffffff16116200024a5760405162461bcd60e51b815260206004820152603160248201527f456e642074696d657374616d70206d757374206265206d6f7265207468616e20604482015270063757272656e742074696d657374616d7607c1b6064820152608401620000e5565b8051819060019081906200025f9082620006b3565b50602082810151600183015560408301516002830180546001600160a01b039283166001600160a01b0319909116179055606084015160038401556080840151600484015560a080850151600585015560c0850151600685015560e085015160078501556101008501516008909401805461012087015161014088015161016090980151151569010000000000000000000260ff60481b1998151568010000000000000000029890981661ffff60401b1963ffffffff928316640100000000026001600160401b03199094169290981691909117919091179590951694909417949094179092559084169091528101516000036200037c577fb165298935924f540e4181c03493a5d686c54a0aaeb3f6216de85b7ffbba77386002555b5050506200077f565b80516001600160a01b03811681146200039d57600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b60405161018081016001600160401b0381118282101715620003de57620003de620003a2565b60405290565b604051601f8201601f191681016001600160401b03811182821017156200040f576200040f620003a2565b604052919050565b600082601f8301126200042957600080fd5b81516001600160401b03811115620004455762000445620003a2565b60206200045b601f8301601f19168201620003e4565b82815285828487010111156200047057600080fd5b60005b838110156200049057858101830151828201840152820162000473565b506000928101909101919091529392505050565b805163ffffffff811681146200039d57600080fd5b805180151581146200039d57600080fd5b600080600060608486031215620004e057600080fd5b620004eb8462000385565b9250620004fb6020850162000385565b60408501519092506001600160401b03808211156200051957600080fd5b9085019061018082880312156200052f57600080fd5b62000539620003b8565b8251828111156200054957600080fd5b620005578982860162000417565b82525060208301516020820152620005726040840162000385565b6040820152606083015160608201526080830151608082015260a083015160a082015260c083015160c082015260e083015160e08201526101009150620005bb828401620004a4565b828201526101209150620005d1828401620004a4565b828201526101409150620005e7828401620004b9565b828201526101609150620005fd828401620004b9565b828201528093505050509250925092565b634e487b7160e01b600052600160045260246000fd5b600181811c908216806200063957607f821691505b6020821081036200065a57634e487b7160e01b600052602260045260246000fd5b50919050565b601f821115620006ae57600081815260208120601f850160051c81016020861015620006895750805b601f850160051c820191505b81811015620006aa5782815560010162000695565b5050505b505050565b81516001600160401b03811115620006cf57620006cf620003a2565b620006e781620006e0845462000624565b8462000660565b602080601f8311600181146200071f5760008415620007065750858301515b600019600386901b1c1916600185901b178555620006aa565b600085815260208120601f198616915b8281101562000750578886015182559484019460019091019084016200072f565b50858210156200076f5787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b60805160a051613af3620007f960003960006123700152600081816102cd015281816104260152818161068e01528181610b8401528181610f07015281816110c1015281816113280152818161175c015281816118cd01528181611b4201528181611df90152818161229201526128e30152613af36000f3fe608060405234801561001057600080fd5b50600436106101775760003560e01c80638d4e4083116100d8578063ac3077731161008c578063c8bbd01711610066578063c8bbd0171461031f578063e7e1049014610332578063e8d08b7f1461033a57600080fd5b8063ac307773146102ef578063b66a0e5d146102f7578063c52046de146102ff57600080fd5b8063a1b56914116100bd578063a1b56914146102a9578063a6f9dae1146102b5578063a8ecc7f1146102c857600080fd5b80638d4e40831461025c5780638da5cb5b1461026457600080fd5b806348c54b9d1161012f57806353b9a99f1161011457806353b9a99f14610235578063544736e61461024c57806358a687ec1461025457600080fd5b806348c54b9d1461021a5780634f409b581461022257600080fd5b80632f54bf6e116101605780632f54bf6e146101a457806345bcdbab146101e8578063488cb59b146101f557600080fd5b80630685675b1461017c578063155dd5ee14610191575b600080fd5b61018f61018a3660046132e4565b61034d565b005b61018f61019f366004613359565b61061f565b6101d36101b2366004613394565b60005473ffffffffffffffffffffffffffffffffffffffff91821691161490565b60405190151581526020015b60405180910390f35b600854600b5410156101d3565b600c546102059063ffffffff1681565b60405163ffffffff90911681526020016101df565b61018f61088f565b61018f610230366004613359565b610b49565b61023e600b5481565b6040519081526020016101df565b6101d3610d5a565b61018f610d82565b6101d361103e565b6000546102849073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016101df565b600754600b54146101d3565b61018f6102c3366004613394565b61106d565b6102847f000000000000000000000000000000000000000000000000000000000000000081565b61018f611212565b61018f6115d7565b600a5473ffffffffffffffffffffffffffffffffffffffff1615156101d3565b61018f61032d366004613409565b611892565b61018f611c63565b61018f610348366004613576565b612020565b600a5473ffffffffffffffffffffffffffffffffffffffff16156103d2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f52574120746f6b656e20697320616c7265616479206465706c6f79656400000060448201526064015b60405180910390fd5b6040517f91d148540000000000000000000000000000000000000000000000000000000081527f060c8eced3c6b422fe5573c862b67b9f6e25a3fc7d9543b14f7aee77b138e70d60048201523360248201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906391d1485490604401602060405180830381865afa158015610482573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104a691906135f5565b1561050e576104b582826124fa565b6104bd61103e565b1561050a577fd2f91af2702654485f0845106235c49adbd9b1334d64e16e4f444e62b31aaf5660016104f3600854600b54101590565b6040516105019291906136f2565b60405180910390a15b5050565b610518828261273e565b156105bd57600c546601000000000000900460ff168061054d575061053b61103e565b801561054b5750600854600b5410155b155b6105b3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f4e6f7420616c6c6f77656400000000000000000000000000000000000000000060448201526064016103c9565b61050a82826124fa565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f4e6f7420616c6c6f77656400000000000000000000000000000000000000000060448201526064016103c9565b6040517f91d148540000000000000000000000000000000000000000000000000000000081527f060c8eced3c6b422fe5573c862b67b9f6e25a3fc7d9543b14f7aee77b138e70d600482018190523360248301529060009073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016906391d14854906044016020604051808303816000875af11580156106d7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106fb91906135f5565b905080610764576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f4e6f7420616c6c6f77656400000000000000000000000000000000000000000060448201526064016103c9565b6003546040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff9091169060009082906370a0823190602401602060405180830381865afa1580156107d7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107fb9190613716565b905084811015610867576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f4e6f7420656e6f75676820636f6e74726163742062616c616e6365000000000060448201526064016103c9565b61088873ffffffffffffffffffffffffffffffffffffffff83163387612816565b5050505050565b600a5473ffffffffffffffffffffffffffffffffffffffff1661090e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f52574120746f6b656e206973206e6f74206465706c6f7965640000000000000060448201526064016103c9565b610916612897565b336000908152600f602052604090205460ff1615610990576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f546f6b656e7320697320616c726561647920636c61696d65640000000000000060448201526064016103c9565b336000908152600d602052604090205480610a07576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f556e6b6e6f776e20696e766573746f720000000000000000000000000000000060448201526064016103c9565b600a5460045473ffffffffffffffffffffffffffffffffffffffff9091169081906340c10f19903390610a3a908661378d565b610a4c90670de0b6b3a76400006137a1565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b16815273ffffffffffffffffffffffffffffffffffffffff90921660048301526024820152604401600060405180830381600087803b158015610ab757600080fd5b505af1158015610acb573d6000803e3d6000fd5b5050336000818152600f60205260409081902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600190811790915590519193507fbc3fa44f5bc4e5f6340fc228b4609c4fc3760144310f27788d15433ae42146569250610b3d9186906137b8565b60405180910390a25050565b6002546040517f91d14854000000000000000000000000000000000000000000000000000000008152600481018290523360248201526000907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906391d14854906044016020604051808303816000875af1158015610be2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c0691906135f5565b905080610c6f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f4e6f7420616c6c6f77656400000000000000000000000000000000000000000060448201526064016103c9565b610c77610d5a565b610cdd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f53616c65206973206e6f7420737461727465640000000000000000000000000060448201526064016103c9565b610ce561103e565b15610d4c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f53616c6520697320616c72656164792066696e616c697a65640000000000000060448201526064016103c9565b610d55836129cb565b505050565b60095460009063ffffffff16421180610d7d5750600c54640100000000900460ff165b905090565b60005473ffffffffffffffffffffffffffffffffffffffff163314610e29576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602160248201527f4e6f7420616c6c6f776564202d206f6e6c79206f776e65722063616e2063616c60448201527f6c0000000000000000000000000000000000000000000000000000000000000060648201526084016103c9565b610e3161103e565b15610e98576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f53616c6520697320616c72656164792066696e616c697a65640000000000000060448201526064016103c9565b6040517f91d148540000000000000000000000000000000000000000000000000000000081527f988301af8238f779281a300de031815cd01b48e9f1ae47a0f91ed6584213624a600482018190523360248301529060009073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016906391d14854906044016020604051808303816000875af1158015610f50573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f7491906135f5565b905080610fdd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f4e6f7420616c6c6f77656400000000000000000000000000000000000000000060448201526064016103c9565b600c80547fffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffff16650100000000001790557fd2f91af2702654485f0845106235c49adbd9b1334d64e16e4f444e62b31aaf5660016104f3600854600b54101590565b600954600090640100000000900463ffffffff16421180610d7d575050600c5465010000000000900460ff1690565b6040517f91d148540000000000000000000000000000000000000000000000000000000081527f060c8eced3c6b422fe5573c862b67b9f6e25a3fc7d9543b14f7aee77b138e70d60048201523360248201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906391d14854906044016020604051808303816000875af115801561111f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061114391906135f5565b80611165575060005473ffffffffffffffffffffffffffffffffffffffff1633145b6111cb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601c60248201527f4e6f7420616c6c6f776564202d206368616e67696e67206f776e65720000000060448201526064016103c9565b600080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b60005473ffffffffffffffffffffffffffffffffffffffff1633146112b9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602160248201527f4e6f7420616c6c6f776564202d206f6e6c79206f776e65722063616e2063616c60448201527f6c0000000000000000000000000000000000000000000000000000000000000060648201526084016103c9565b6040517f91d148540000000000000000000000000000000000000000000000000000000081527f29b12d97af3e09871b33156ea28b4c5ef612a3f81c5cf4df4930aa95a3010914600482018190523360248301529060009073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016906391d14854906044016020604051808303816000875af1158015611371573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061139591906135f5565b9050806113fe576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f4e6f7420616c6c6f77656400000000000000000000000000000000000000000060448201526064016103c9565b600a5473ffffffffffffffffffffffffffffffffffffffff1661147d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f52574120746f6b656e206973206e6f74206465706c6f7965640000000000000060448201526064016103c9565b61148561103e565b6114eb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f53616c65206973206e6f742066696e616c697a6564000000000000000000000060448201526064016103c9565b600c54670100000000000000900460ff1615611563576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f46756e647320616c7265616479206265656e20636c61696d656400000000000060448201526064016103c9565b61156f33600b54612e0d565b600c80547fffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffff16670100000000000000179055600b5460405133917f6955e837ab9063947a4eaa5e482b94f0dc861212a212b2b38b29c5a39c7a24e191610b3d916001916137b8565b60005473ffffffffffffffffffffffffffffffffffffffff16331461167e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602160248201527f4e6f7420616c6c6f776564202d206f6e6c79206f776e65722063616e2063616c60448201527f6c0000000000000000000000000000000000000000000000000000000000000060648201526084016103c9565b611686610d5a565b156116ed576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f53616c6520697320616c7265616479207374617274656400000000000000000060448201526064016103c9565b6040517f91d148540000000000000000000000000000000000000000000000000000000081527f988301af8238f779281a300de031815cd01b48e9f1ae47a0f91ed6584213624a600482018190523360248301529060009073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016906391d14854906044016020604051808303816000875af11580156117a5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117c991906135f5565b905080611832576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f4e6f7420616c6c6f77656400000000000000000000000000000000000000000060448201526064016103c9565b600c80547fffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffff166401000000001790556040517f89aeb0dbef97d15c76fca4fa0413f164fb0c47a9a4b987a1cdffec3596ce628590610501906001906137da565b6002546040517f91d14854000000000000000000000000000000000000000000000000000000008152600481018290523360248201526000907f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16906391d14854906044016020604051808303816000875af115801561192b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061194f91906135f5565b9050806119b8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f4e6f7420616c6c6f77656400000000000000000000000000000000000000000060448201526064016103c9565b6119c0610d5a565b611a26576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f53616c65206973206e6f7420737461727465640000000000000000000000000060448201526064016103c9565b611a2e61103e565b15611a95576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f53616c6520697320616c72656164792066696e616c697a65640000000000000060448201526064016103c9565b6009546901000000000000000000900460ff16611b0e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f53616c65206973206e6f7420616464726573736564000000000000000000000060448201526064016103c9565b6040517fadc8f3780000000000000000000000000000000000000000000000000000000081523060048201523360248201527f00000000000000000000000000000000000000000000000000000000000000009073ffffffffffffffffffffffffffffffffffffffff82169063adc8f37890604401602060405180830381865afa158015611ba0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bc491906135f5565b611c51576040517ff1b5606200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82169063f1b5606290611c1e908a908a908a906004016137ed565b600060405180830381600087803b158015611c3857600080fd5b505af1158015611c4c573d6000803e3d6000fd5b505050505b611c5a846129cb565b50505050505050565b60005473ffffffffffffffffffffffffffffffffffffffff163314611d0a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602160248201527f4e6f7420616c6c6f776564202d206f6e6c79206f776e65722063616e2063616c60448201527f6c0000000000000000000000000000000000000000000000000000000000000060648201526084016103c9565b600a5473ffffffffffffffffffffffffffffffffffffffff1615611d8a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f52574120746f6b656e20697320616c7265616479206465706c6f79656400000060448201526064016103c9565b6040517f91d148540000000000000000000000000000000000000000000000000000000081527f988301af8238f779281a300de031815cd01b48e9f1ae47a0f91ed6584213624a600482018190523360248301529060009073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016906391d14854906044016020604051808303816000875af1158015611e42573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e6691906135f5565b905080611ecf576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f4e6f7420616c6c6f77656400000000000000000000000000000000000000000060448201526064016103c9565b60095468010000000000000000900460ff16611f47576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f53616c65206973206e6f742063616e63656c61626c650000000000000000000060448201526064016103c9565b600c546601000000000000900460ff1615611fbe576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f53616c6520697320616c72656164792063616e63656c6564000000000000000060448201526064016103c9565b600c80547fffffffffffffffffffffffffffffffffffffffffffffffffff0000ffffffffff1666010100000000001790556040517ffbc404511d6696b6c633e1b1c0425a4525b59c3b351e713446b4e4ddd909ee3c90610501906001906137da565b60005473ffffffffffffffffffffffffffffffffffffffff1633146120c7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602160248201527f4e6f7420616c6c6f776564202d206f6e6c79206f776e65722063616e2063616c60448201527f6c0000000000000000000000000000000000000000000000000000000000000060648201526084016103c9565b600a5473ffffffffffffffffffffffffffffffffffffffff1615612147576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f52574120746f6b656e20697320616c7265616479206465706c6f79656400000060448201526064016103c9565b61214f61103e565b6121b5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f53616c65206973206e6f742066696e616c697a6564000000000000000000000060448201526064016103c9565b600854600b541015612223576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f536f66742063617020776173206e6f742061636869657665640000000000000060448201526064016103c9565b6040517f91d148540000000000000000000000000000000000000000000000000000000081527f988301af8238f779281a300de031815cd01b48e9f1ae47a0f91ed6584213624a600482018190523360248301529060009073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016906391d14854906044016020604051808303816000875af11580156122db573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122ff91906135f5565b905080612368576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f4e6f7420616c6c6f77656400000000000000000000000000000000000000000060448201526064016103c9565b600454600b547f00000000000000000000000000000000000000000000000000000000000000009160009161239d919061378d565b90508173ffffffffffffffffffffffffffffffffffffffff1663f67c7b318787600160020160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff166001600301548c8760008054906101000a900473ffffffffffffffffffffffffffffffffffffffff166040518863ffffffff1660e01b815260040161242e97969594939291906138e1565b6020604051808303816000875af115801561244d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124719190613953565b600a80547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff9290921691821790556040517f868fe488c48c70f27d9d98f40cfc2ddb161894613ebc05f66f464e463047e54a916124e991600191908590613970565b60405180910390a150505050505050565b600080600e600085856000818110612514576125146139ab565b6020908102929092013583525081019190915260400160009081205473ffffffffffffffffffffffffffffffffffffffff1691505b83811015612727576000600e6000878785818110612569576125696139ab565b905060200201358152602001908152602001600020600101549050600081116125ee576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f436f6d6d69746d656e74206e6f7420666f756e6400000000000000000000000060448201526064016103c9565b6000600e6000888886818110612606576126066139ab565b9050602002013581526020019081526020016000206001018190555080600d60008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825461267191906139da565b90915550612681905081856139ed565b935080600b600082825461269591906139da565b909155505073ffffffffffffffffffffffffffffffffffffffff83167f9fae2f113ed39927bd4cd5a7c096ddb57e2f83d600cffc985f74b584d4b17cf060018888868181106126e6576126e66139ab565b90506020020135846126fc600854600b54101590565b60405161270c9493929190613a00565b60405180910390a2508061271f81613a31565b915050612549565b508115612738576127388183612e0d565b50505050565b6000805b8281101561280a5733600e6000868685818110612761576127616139ab565b602090810292909201358352508101919091526040016000205473ffffffffffffffffffffffffffffffffffffffff16146127f8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f4e6f7420616c6c6f77656400000000000000000000000000000000000000000060448201526064016103c9565b8061280281613a31565b915050612742565b50600190505b92915050565b60405173ffffffffffffffffffffffffffffffffffffffff838116602483015260448201839052610d5591859182169063a9059cbb906064015b604051602081830303815290604052915060e01b6020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050612f31565b6009546901000000000000000000900460ff16156129c9576040517fadc8f3780000000000000000000000000000000000000000000000000000000081523060048201523360248201527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169063adc8f37890604401602060405180830381865afa15801561293f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061296391906135f5565b6129c9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f4e6f7420616c6c6f77656400000000000000000000000000000000000000000060448201526064016103c9565b565b6129d3612897565b80600b60008282546129e591906139ed565b9091555050600754600b541115612a58576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f486172646361702072656163686564000000000000000000000000000000000060448201526064016103c9565b600c546040513060601b7fffffffffffffffffffffffffffffffffffffffff00000000000000000000000016602082015260e09190911b7fffffffff00000000000000000000000000000000000000000000000000000000166034820152600090603801604051602081830303815290604052805190602001209050612add82612fc7565b336000908152600d602052604081208054849290612afc9084906139ed565b909155505060408051808201825233815260208082018581526000858152600e909252929020905181547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff909116178155905160019091015560055415801590612b8d5750336000908152600d602052604090205460055411155b80612b985750600554155b612bfe576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f436f6d6d69746d656e747320616d6f756e74206c657373207468616e206d696e60448201526064016103c9565b60065415801590612c205750336000908152600d602052604090205460065410155b80612c2b5750600654155b612c91576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f436f6d6d69746d656e747320616d6f756e74206d6f7265207468616e206d617860448201526064016103c9565b600454612c9e9083613a69565b15612d05576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f43616e20627579206f6e6c792077686f6c652072776120746f6b656e7300000060448201526064016103c9565b600c805460019190600090612d2190849063ffffffff16613a7d565b92506101000a81548163ffffffff021916908363ffffffff160217905550612d4c600754600b541490565b15612dc957600c80547fffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffff16650100000000001790557fd2f91af2702654485f0845106235c49adbd9b1334d64e16e4f444e62b31aaf566001612db2600854600b54101590565b604051612dc09291906136f2565b60405180910390a15b337f643251631e5934da7f24efe751d897f111d3b0f45db8c310d365599e16180e3560018385612dfd600854600b54101590565b604051610b3d9493929190613a00565b6003546040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff9091169060009082906370a0823190602401602060405180830381865afa158015612e80573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ea49190613716565b905082811015612f10576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f4e6f7420656e6f75676820746f6b656e7300000000000000000000000000000060448201526064016103c9565b61273873ffffffffffffffffffffffffffffffffffffffff83168585612816565b6000612f5373ffffffffffffffffffffffffffffffffffffffff8416836130f2565b90508051600014158015612f78575080806020019051810190612f7691906135f5565b155b15610d55576040517f5274afe700000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff841660048201526024016103c9565b6003546040517fdd62ed3e00000000000000000000000000000000000000000000000000000000815233600482015230602482015273ffffffffffffffffffffffffffffffffffffffff90911690600090829063dd62ed3e90604401602060405180830381865afa158015613040573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130649190613716565b9050828110156130d0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f5472616e73666572206e6f7420616c6c6f77656400000000000000000000000060448201526064016103c9565b610d5573ffffffffffffffffffffffffffffffffffffffff8316333086613107565b60606131008383600061314d565b9392505050565b60405173ffffffffffffffffffffffffffffffffffffffff84811660248301528381166044830152606482018390526127389186918216906323b872dd90608401612850565b60608147101561318b576040517fcd7860590000000000000000000000000000000000000000000000000000000081523060048201526024016103c9565b6000808573ffffffffffffffffffffffffffffffffffffffff1684866040516131b49190613aa1565b60006040518083038185875af1925050503d80600081146131f1576040519150601f19603f3d011682016040523d82523d6000602084013e6131f6565b606091505b5091509150613206868383613210565b9695505050505050565b606082613225576132208261329f565b613100565b8151158015613249575073ffffffffffffffffffffffffffffffffffffffff84163b155b15613298576040517f9996b31500000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff851660048201526024016103c9565b5080613100565b8051156132af5780518082602001fd5b6040517f1425ea4200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50565b600080602083850312156132f757600080fd5b823567ffffffffffffffff8082111561330f57600080fd5b818501915085601f83011261332357600080fd5b81358181111561333257600080fd5b8660208260051b850101111561334757600080fd5b60209290920196919550909350505050565b60006020828403121561336b57600080fd5b5035919050565b73ffffffffffffffffffffffffffffffffffffffff811681146132e157600080fd5b6000602082840312156133a657600080fd5b813561310081613372565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040516060810167ffffffffffffffff81118282101715613403576134036133b1565b60405290565b60008060008084860360a081121561342057600080fd5b606081121561342e57600080fd5b506134376133e0565b853561344281613372565b8152602086013561345281613372565b6020820152604086810135908201529350606085013567ffffffffffffffff8082111561347e57600080fd5b818701915087601f83011261349257600080fd5b8135818111156134a157600080fd5b8860208285010111156134b357600080fd5b95986020929092019750949560800135945092505050565b600082601f8301126134dc57600080fd5b813567ffffffffffffffff808211156134f7576134f76133b1565b604051601f83017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f0116810190828211818310171561353d5761353d6133b1565b8160405283815286602085880101111561355657600080fd5b836020870160208301376000602085830101528094505050505092915050565b60008060006060848603121561358b57600080fd5b833563ffffffff8116811461359f57600080fd5b9250602084013567ffffffffffffffff808211156135bc57600080fd5b6135c8878388016134cb565b935060408601359150808211156135de57600080fd5b506135eb868287016134cb565b9150509250925092565b60006020828403121561360757600080fd5b8151801515811461310057600080fd5b8054600090600181811c908083168061363157607f831692505b6020808410820361366b577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b8388526020880182801561368657600181146136ba576136e5565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008716825285151560051b820197506136e5565b60008981526020902060005b878110156136df578154848201529086019084016136c6565b83019850505b5050505050505092915050565b6040815260006137056040830185613617565b905082151560208301529392505050565b60006020828403121561372857600080fd5b5051919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60008261379c5761379c61372f565b500490565b80820281158282048414176128105761281061375e565b6040815260006137cb6040830185613617565b90508260208301529392505050565b6020815260006131006020830184613617565b600073ffffffffffffffffffffffffffffffffffffffff808651168352806020870151166020840152506040850151604083015260806060830152826080830152828460a0840137600060a0848401015260a07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8501168301019050949350505050565b60005b8381101561388e578181015183820152602001613876565b50506000910152565b600081518084526138af816020860160208601613873565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b60e0815260006138f460e083018a613897565b8281036020840152613906818a613897565b91505073ffffffffffffffffffffffffffffffffffffffff808816604084015286606084015263ffffffff861660808401528460a084015280841660c08401525098975050505050505050565b60006020828403121561396557600080fd5b815161310081613372565b6060815260006139836060830186613617565b73ffffffffffffffffffffffffffffffffffffffff9490941660208301525060400152919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b818103818111156128105761281061375e565b808201808211156128105761281061375e565b608081526000613a136080830187613617565b60208301959095525060408101929092521515606090910152919050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203613a6257613a6261375e565b5060010190565b600082613a7857613a7861372f565b500690565b63ffffffff818116838216019080821115613a9a57613a9a61375e565b5092915050565b60008251613ab3818460208701613873565b919091019291505056fea2646970667358221220f44f63073f0d1748561f9f883f09bdb859b3d6de7a170c108ad4d21d437b260364736f6c63430008140033000000000000000000000000c9251b4f1d4acc535bb3392be257b72739abf0d3000000000000000000000000f8fa8d19d8e5384f9bd48fad67dafeedfea27e6d00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000180b165298935924f540e4181c03493a5d686c54a0aaeb3f6216de85b7ffbba7738000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec700000000000000000000000000000000000000000000000000000000000f424000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000333905980000000000000000000000000000000000000000000000000000000000000f42400000000000000000000000000000000000000000000000000000000068d173ac0000000000000000000000000000000000000000000000000000000068da4a9000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002435316463623037372d623236662d343033662d626634372d34643636613030643333376400000000000000000000000000000000000000000000000000000000
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106101775760003560e01c80638d4e4083116100d8578063ac3077731161008c578063c8bbd01711610066578063c8bbd0171461031f578063e7e1049014610332578063e8d08b7f1461033a57600080fd5b8063ac307773146102ef578063b66a0e5d146102f7578063c52046de146102ff57600080fd5b8063a1b56914116100bd578063a1b56914146102a9578063a6f9dae1146102b5578063a8ecc7f1146102c857600080fd5b80638d4e40831461025c5780638da5cb5b1461026457600080fd5b806348c54b9d1161012f57806353b9a99f1161011457806353b9a99f14610235578063544736e61461024c57806358a687ec1461025457600080fd5b806348c54b9d1461021a5780634f409b581461022257600080fd5b80632f54bf6e116101605780632f54bf6e146101a457806345bcdbab146101e8578063488cb59b146101f557600080fd5b80630685675b1461017c578063155dd5ee14610191575b600080fd5b61018f61018a3660046132e4565b61034d565b005b61018f61019f366004613359565b61061f565b6101d36101b2366004613394565b60005473ffffffffffffffffffffffffffffffffffffffff91821691161490565b60405190151581526020015b60405180910390f35b600854600b5410156101d3565b600c546102059063ffffffff1681565b60405163ffffffff90911681526020016101df565b61018f61088f565b61018f610230366004613359565b610b49565b61023e600b5481565b6040519081526020016101df565b6101d3610d5a565b61018f610d82565b6101d361103e565b6000546102849073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016101df565b600754600b54146101d3565b61018f6102c3366004613394565b61106d565b6102847f000000000000000000000000c9251b4f1d4acc535bb3392be257b72739abf0d381565b61018f611212565b61018f6115d7565b600a5473ffffffffffffffffffffffffffffffffffffffff1615156101d3565b61018f61032d366004613409565b611892565b61018f611c63565b61018f610348366004613576565b612020565b600a5473ffffffffffffffffffffffffffffffffffffffff16156103d2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f52574120746f6b656e20697320616c7265616479206465706c6f79656400000060448201526064015b60405180910390fd5b6040517f91d148540000000000000000000000000000000000000000000000000000000081527f060c8eced3c6b422fe5573c862b67b9f6e25a3fc7d9543b14f7aee77b138e70d60048201523360248201527f000000000000000000000000c9251b4f1d4acc535bb3392be257b72739abf0d373ffffffffffffffffffffffffffffffffffffffff16906391d1485490604401602060405180830381865afa158015610482573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906104a691906135f5565b1561050e576104b582826124fa565b6104bd61103e565b1561050a577fd2f91af2702654485f0845106235c49adbd9b1334d64e16e4f444e62b31aaf5660016104f3600854600b54101590565b6040516105019291906136f2565b60405180910390a15b5050565b610518828261273e565b156105bd57600c546601000000000000900460ff168061054d575061053b61103e565b801561054b5750600854600b5410155b155b6105b3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f4e6f7420616c6c6f77656400000000000000000000000000000000000000000060448201526064016103c9565b61050a82826124fa565b6040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f4e6f7420616c6c6f77656400000000000000000000000000000000000000000060448201526064016103c9565b6040517f91d148540000000000000000000000000000000000000000000000000000000081527f060c8eced3c6b422fe5573c862b67b9f6e25a3fc7d9543b14f7aee77b138e70d600482018190523360248301529060009073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000c9251b4f1d4acc535bb3392be257b72739abf0d316906391d14854906044016020604051808303816000875af11580156106d7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106fb91906135f5565b905080610764576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f4e6f7420616c6c6f77656400000000000000000000000000000000000000000060448201526064016103c9565b6003546040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff9091169060009082906370a0823190602401602060405180830381865afa1580156107d7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107fb9190613716565b905084811015610867576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601b60248201527f4e6f7420656e6f75676820636f6e74726163742062616c616e6365000000000060448201526064016103c9565b61088873ffffffffffffffffffffffffffffffffffffffff83163387612816565b5050505050565b600a5473ffffffffffffffffffffffffffffffffffffffff1661090e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f52574120746f6b656e206973206e6f74206465706c6f7965640000000000000060448201526064016103c9565b610916612897565b336000908152600f602052604090205460ff1615610990576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f546f6b656e7320697320616c726561647920636c61696d65640000000000000060448201526064016103c9565b336000908152600d602052604090205480610a07576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601060248201527f556e6b6e6f776e20696e766573746f720000000000000000000000000000000060448201526064016103c9565b600a5460045473ffffffffffffffffffffffffffffffffffffffff9091169081906340c10f19903390610a3a908661378d565b610a4c90670de0b6b3a76400006137a1565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e085901b16815273ffffffffffffffffffffffffffffffffffffffff90921660048301526024820152604401600060405180830381600087803b158015610ab757600080fd5b505af1158015610acb573d6000803e3d6000fd5b5050336000818152600f60205260409081902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600190811790915590519193507fbc3fa44f5bc4e5f6340fc228b4609c4fc3760144310f27788d15433ae42146569250610b3d9186906137b8565b60405180910390a25050565b6002546040517f91d14854000000000000000000000000000000000000000000000000000000008152600481018290523360248201526000907f000000000000000000000000c9251b4f1d4acc535bb3392be257b72739abf0d373ffffffffffffffffffffffffffffffffffffffff16906391d14854906044016020604051808303816000875af1158015610be2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c0691906135f5565b905080610c6f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f4e6f7420616c6c6f77656400000000000000000000000000000000000000000060448201526064016103c9565b610c77610d5a565b610cdd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f53616c65206973206e6f7420737461727465640000000000000000000000000060448201526064016103c9565b610ce561103e565b15610d4c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f53616c6520697320616c72656164792066696e616c697a65640000000000000060448201526064016103c9565b610d55836129cb565b505050565b60095460009063ffffffff16421180610d7d5750600c54640100000000900460ff165b905090565b60005473ffffffffffffffffffffffffffffffffffffffff163314610e29576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602160248201527f4e6f7420616c6c6f776564202d206f6e6c79206f776e65722063616e2063616c60448201527f6c0000000000000000000000000000000000000000000000000000000000000060648201526084016103c9565b610e3161103e565b15610e98576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f53616c6520697320616c72656164792066696e616c697a65640000000000000060448201526064016103c9565b6040517f91d148540000000000000000000000000000000000000000000000000000000081527f988301af8238f779281a300de031815cd01b48e9f1ae47a0f91ed6584213624a600482018190523360248301529060009073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000c9251b4f1d4acc535bb3392be257b72739abf0d316906391d14854906044016020604051808303816000875af1158015610f50573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610f7491906135f5565b905080610fdd576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f4e6f7420616c6c6f77656400000000000000000000000000000000000000000060448201526064016103c9565b600c80547fffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffff16650100000000001790557fd2f91af2702654485f0845106235c49adbd9b1334d64e16e4f444e62b31aaf5660016104f3600854600b54101590565b600954600090640100000000900463ffffffff16421180610d7d575050600c5465010000000000900460ff1690565b6040517f91d148540000000000000000000000000000000000000000000000000000000081527f060c8eced3c6b422fe5573c862b67b9f6e25a3fc7d9543b14f7aee77b138e70d60048201523360248201527f000000000000000000000000c9251b4f1d4acc535bb3392be257b72739abf0d373ffffffffffffffffffffffffffffffffffffffff16906391d14854906044016020604051808303816000875af115801561111f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061114391906135f5565b80611165575060005473ffffffffffffffffffffffffffffffffffffffff1633145b6111cb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601c60248201527f4e6f7420616c6c6f776564202d206368616e67696e67206f776e65720000000060448201526064016103c9565b600080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b60005473ffffffffffffffffffffffffffffffffffffffff1633146112b9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602160248201527f4e6f7420616c6c6f776564202d206f6e6c79206f776e65722063616e2063616c60448201527f6c0000000000000000000000000000000000000000000000000000000000000060648201526084016103c9565b6040517f91d148540000000000000000000000000000000000000000000000000000000081527f29b12d97af3e09871b33156ea28b4c5ef612a3f81c5cf4df4930aa95a3010914600482018190523360248301529060009073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000c9251b4f1d4acc535bb3392be257b72739abf0d316906391d14854906044016020604051808303816000875af1158015611371573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061139591906135f5565b9050806113fe576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f4e6f7420616c6c6f77656400000000000000000000000000000000000000000060448201526064016103c9565b600a5473ffffffffffffffffffffffffffffffffffffffff1661147d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f52574120746f6b656e206973206e6f74206465706c6f7965640000000000000060448201526064016103c9565b61148561103e565b6114eb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f53616c65206973206e6f742066696e616c697a6564000000000000000000000060448201526064016103c9565b600c54670100000000000000900460ff1615611563576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601a60248201527f46756e647320616c7265616479206265656e20636c61696d656400000000000060448201526064016103c9565b61156f33600b54612e0d565b600c80547fffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffff16670100000000000000179055600b5460405133917f6955e837ab9063947a4eaa5e482b94f0dc861212a212b2b38b29c5a39c7a24e191610b3d916001916137b8565b60005473ffffffffffffffffffffffffffffffffffffffff16331461167e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602160248201527f4e6f7420616c6c6f776564202d206f6e6c79206f776e65722063616e2063616c60448201527f6c0000000000000000000000000000000000000000000000000000000000000060648201526084016103c9565b611686610d5a565b156116ed576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f53616c6520697320616c7265616479207374617274656400000000000000000060448201526064016103c9565b6040517f91d148540000000000000000000000000000000000000000000000000000000081527f988301af8238f779281a300de031815cd01b48e9f1ae47a0f91ed6584213624a600482018190523360248301529060009073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000c9251b4f1d4acc535bb3392be257b72739abf0d316906391d14854906044016020604051808303816000875af11580156117a5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117c991906135f5565b905080611832576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f4e6f7420616c6c6f77656400000000000000000000000000000000000000000060448201526064016103c9565b600c80547fffffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffff166401000000001790556040517f89aeb0dbef97d15c76fca4fa0413f164fb0c47a9a4b987a1cdffec3596ce628590610501906001906137da565b6002546040517f91d14854000000000000000000000000000000000000000000000000000000008152600481018290523360248201526000907f000000000000000000000000c9251b4f1d4acc535bb3392be257b72739abf0d373ffffffffffffffffffffffffffffffffffffffff16906391d14854906044016020604051808303816000875af115801561192b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061194f91906135f5565b9050806119b8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f4e6f7420616c6c6f77656400000000000000000000000000000000000000000060448201526064016103c9565b6119c0610d5a565b611a26576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601360248201527f53616c65206973206e6f7420737461727465640000000000000000000000000060448201526064016103c9565b611a2e61103e565b15611a95576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f53616c6520697320616c72656164792066696e616c697a65640000000000000060448201526064016103c9565b6009546901000000000000000000900460ff16611b0e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f53616c65206973206e6f7420616464726573736564000000000000000000000060448201526064016103c9565b6040517fadc8f3780000000000000000000000000000000000000000000000000000000081523060048201523360248201527f000000000000000000000000c9251b4f1d4acc535bb3392be257b72739abf0d39073ffffffffffffffffffffffffffffffffffffffff82169063adc8f37890604401602060405180830381865afa158015611ba0573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bc491906135f5565b611c51576040517ff1b5606200000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff82169063f1b5606290611c1e908a908a908a906004016137ed565b600060405180830381600087803b158015611c3857600080fd5b505af1158015611c4c573d6000803e3d6000fd5b505050505b611c5a846129cb565b50505050505050565b60005473ffffffffffffffffffffffffffffffffffffffff163314611d0a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602160248201527f4e6f7420616c6c6f776564202d206f6e6c79206f776e65722063616e2063616c60448201527f6c0000000000000000000000000000000000000000000000000000000000000060648201526084016103c9565b600a5473ffffffffffffffffffffffffffffffffffffffff1615611d8a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f52574120746f6b656e20697320616c7265616479206465706c6f79656400000060448201526064016103c9565b6040517f91d148540000000000000000000000000000000000000000000000000000000081527f988301af8238f779281a300de031815cd01b48e9f1ae47a0f91ed6584213624a600482018190523360248301529060009073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000c9251b4f1d4acc535bb3392be257b72739abf0d316906391d14854906044016020604051808303816000875af1158015611e42573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611e6691906135f5565b905080611ecf576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f4e6f7420616c6c6f77656400000000000000000000000000000000000000000060448201526064016103c9565b60095468010000000000000000900460ff16611f47576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f53616c65206973206e6f742063616e63656c61626c650000000000000000000060448201526064016103c9565b600c546601000000000000900460ff1615611fbe576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601860248201527f53616c6520697320616c72656164792063616e63656c6564000000000000000060448201526064016103c9565b600c80547fffffffffffffffffffffffffffffffffffffffffffffffffff0000ffffffffff1666010100000000001790556040517ffbc404511d6696b6c633e1b1c0425a4525b59c3b351e713446b4e4ddd909ee3c90610501906001906137da565b60005473ffffffffffffffffffffffffffffffffffffffff1633146120c7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602160248201527f4e6f7420616c6c6f776564202d206f6e6c79206f776e65722063616e2063616c60448201527f6c0000000000000000000000000000000000000000000000000000000000000060648201526084016103c9565b600a5473ffffffffffffffffffffffffffffffffffffffff1615612147576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f52574120746f6b656e20697320616c7265616479206465706c6f79656400000060448201526064016103c9565b61214f61103e565b6121b5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f53616c65206973206e6f742066696e616c697a6564000000000000000000000060448201526064016103c9565b600854600b541015612223576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601960248201527f536f66742063617020776173206e6f742061636869657665640000000000000060448201526064016103c9565b6040517f91d148540000000000000000000000000000000000000000000000000000000081527f988301af8238f779281a300de031815cd01b48e9f1ae47a0f91ed6584213624a600482018190523360248301529060009073ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000c9251b4f1d4acc535bb3392be257b72739abf0d316906391d14854906044016020604051808303816000875af11580156122db573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906122ff91906135f5565b905080612368576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f4e6f7420616c6c6f77656400000000000000000000000000000000000000000060448201526064016103c9565b600454600b547f000000000000000000000000f8fa8d19d8e5384f9bd48fad67dafeedfea27e6d9160009161239d919061378d565b90508173ffffffffffffffffffffffffffffffffffffffff1663f67c7b318787600160020160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff166001600301548c8760008054906101000a900473ffffffffffffffffffffffffffffffffffffffff166040518863ffffffff1660e01b815260040161242e97969594939291906138e1565b6020604051808303816000875af115801561244d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124719190613953565b600a80547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff9290921691821790556040517f868fe488c48c70f27d9d98f40cfc2ddb161894613ebc05f66f464e463047e54a916124e991600191908590613970565b60405180910390a150505050505050565b600080600e600085856000818110612514576125146139ab565b6020908102929092013583525081019190915260400160009081205473ffffffffffffffffffffffffffffffffffffffff1691505b83811015612727576000600e6000878785818110612569576125696139ab565b905060200201358152602001908152602001600020600101549050600081116125ee576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f436f6d6d69746d656e74206e6f7420666f756e6400000000000000000000000060448201526064016103c9565b6000600e6000888886818110612606576126066139ab565b9050602002013581526020019081526020016000206001018190555080600d60008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825461267191906139da565b90915550612681905081856139ed565b935080600b600082825461269591906139da565b909155505073ffffffffffffffffffffffffffffffffffffffff83167f9fae2f113ed39927bd4cd5a7c096ddb57e2f83d600cffc985f74b584d4b17cf060018888868181106126e6576126e66139ab565b90506020020135846126fc600854600b54101590565b60405161270c9493929190613a00565b60405180910390a2508061271f81613a31565b915050612549565b508115612738576127388183612e0d565b50505050565b6000805b8281101561280a5733600e6000868685818110612761576127616139ab565b602090810292909201358352508101919091526040016000205473ffffffffffffffffffffffffffffffffffffffff16146127f8576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f4e6f7420616c6c6f77656400000000000000000000000000000000000000000060448201526064016103c9565b8061280281613a31565b915050612742565b50600190505b92915050565b60405173ffffffffffffffffffffffffffffffffffffffff838116602483015260448201839052610d5591859182169063a9059cbb906064015b604051602081830303815290604052915060e01b6020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff8381831617835250505050612f31565b6009546901000000000000000000900460ff16156129c9576040517fadc8f3780000000000000000000000000000000000000000000000000000000081523060048201523360248201527f000000000000000000000000c9251b4f1d4acc535bb3392be257b72739abf0d373ffffffffffffffffffffffffffffffffffffffff169063adc8f37890604401602060405180830381865afa15801561293f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061296391906135f5565b6129c9576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600b60248201527f4e6f7420616c6c6f77656400000000000000000000000000000000000000000060448201526064016103c9565b565b6129d3612897565b80600b60008282546129e591906139ed565b9091555050600754600b541115612a58576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f486172646361702072656163686564000000000000000000000000000000000060448201526064016103c9565b600c546040513060601b7fffffffffffffffffffffffffffffffffffffffff00000000000000000000000016602082015260e09190911b7fffffffff00000000000000000000000000000000000000000000000000000000166034820152600090603801604051602081830303815290604052805190602001209050612add82612fc7565b336000908152600d602052604081208054849290612afc9084906139ed565b909155505060408051808201825233815260208082018581526000858152600e909252929020905181547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff909116178155905160019091015560055415801590612b8d5750336000908152600d602052604090205460055411155b80612b985750600554155b612bfe576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f436f6d6d69746d656e747320616d6f756e74206c657373207468616e206d696e60448201526064016103c9565b60065415801590612c205750336000908152600d602052604090205460065410155b80612c2b5750600654155b612c91576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f436f6d6d69746d656e747320616d6f756e74206d6f7265207468616e206d617860448201526064016103c9565b600454612c9e9083613a69565b15612d05576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f43616e20627579206f6e6c792077686f6c652072776120746f6b656e7300000060448201526064016103c9565b600c805460019190600090612d2190849063ffffffff16613a7d565b92506101000a81548163ffffffff021916908363ffffffff160217905550612d4c600754600b541490565b15612dc957600c80547fffffffffffffffffffffffffffffffffffffffffffffffffffff00ffffffffff16650100000000001790557fd2f91af2702654485f0845106235c49adbd9b1334d64e16e4f444e62b31aaf566001612db2600854600b54101590565b604051612dc09291906136f2565b60405180910390a15b337f643251631e5934da7f24efe751d897f111d3b0f45db8c310d365599e16180e3560018385612dfd600854600b54101590565b604051610b3d9493929190613a00565b6003546040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015273ffffffffffffffffffffffffffffffffffffffff9091169060009082906370a0823190602401602060405180830381865afa158015612e80573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612ea49190613716565b905082811015612f10576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f4e6f7420656e6f75676820746f6b656e7300000000000000000000000000000060448201526064016103c9565b61273873ffffffffffffffffffffffffffffffffffffffff83168585612816565b6000612f5373ffffffffffffffffffffffffffffffffffffffff8416836130f2565b90508051600014158015612f78575080806020019051810190612f7691906135f5565b155b15610d55576040517f5274afe700000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff841660048201526024016103c9565b6003546040517fdd62ed3e00000000000000000000000000000000000000000000000000000000815233600482015230602482015273ffffffffffffffffffffffffffffffffffffffff90911690600090829063dd62ed3e90604401602060405180830381865afa158015613040573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906130649190613716565b9050828110156130d0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601460248201527f5472616e73666572206e6f7420616c6c6f77656400000000000000000000000060448201526064016103c9565b610d5573ffffffffffffffffffffffffffffffffffffffff8316333086613107565b60606131008383600061314d565b9392505050565b60405173ffffffffffffffffffffffffffffffffffffffff84811660248301528381166044830152606482018390526127389186918216906323b872dd90608401612850565b60608147101561318b576040517fcd7860590000000000000000000000000000000000000000000000000000000081523060048201526024016103c9565b6000808573ffffffffffffffffffffffffffffffffffffffff1684866040516131b49190613aa1565b60006040518083038185875af1925050503d80600081146131f1576040519150601f19603f3d011682016040523d82523d6000602084013e6131f6565b606091505b5091509150613206868383613210565b9695505050505050565b606082613225576132208261329f565b613100565b8151158015613249575073ffffffffffffffffffffffffffffffffffffffff84163b155b15613298576040517f9996b31500000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff851660048201526024016103c9565b5080613100565b8051156132af5780518082602001fd5b6040517f1425ea4200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50565b600080602083850312156132f757600080fd5b823567ffffffffffffffff8082111561330f57600080fd5b818501915085601f83011261332357600080fd5b81358181111561333257600080fd5b8660208260051b850101111561334757600080fd5b60209290920196919550909350505050565b60006020828403121561336b57600080fd5b5035919050565b73ffffffffffffffffffffffffffffffffffffffff811681146132e157600080fd5b6000602082840312156133a657600080fd5b813561310081613372565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6040516060810167ffffffffffffffff81118282101715613403576134036133b1565b60405290565b60008060008084860360a081121561342057600080fd5b606081121561342e57600080fd5b506134376133e0565b853561344281613372565b8152602086013561345281613372565b6020820152604086810135908201529350606085013567ffffffffffffffff8082111561347e57600080fd5b818701915087601f83011261349257600080fd5b8135818111156134a157600080fd5b8860208285010111156134b357600080fd5b95986020929092019750949560800135945092505050565b600082601f8301126134dc57600080fd5b813567ffffffffffffffff808211156134f7576134f76133b1565b604051601f83017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f0116810190828211818310171561353d5761353d6133b1565b8160405283815286602085880101111561355657600080fd5b836020870160208301376000602085830101528094505050505092915050565b60008060006060848603121561358b57600080fd5b833563ffffffff8116811461359f57600080fd5b9250602084013567ffffffffffffffff808211156135bc57600080fd5b6135c8878388016134cb565b935060408601359150808211156135de57600080fd5b506135eb868287016134cb565b9150509250925092565b60006020828403121561360757600080fd5b8151801515811461310057600080fd5b8054600090600181811c908083168061363157607f831692505b6020808410820361366b577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b8388526020880182801561368657600181146136ba576136e5565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff008716825285151560051b820197506136e5565b60008981526020902060005b878110156136df578154848201529086019084016136c6565b83019850505b5050505050505092915050565b6040815260006137056040830185613617565b905082151560208301529392505050565b60006020828403121561372857600080fd5b5051919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60008261379c5761379c61372f565b500490565b80820281158282048414176128105761281061375e565b6040815260006137cb6040830185613617565b90508260208301529392505050565b6020815260006131006020830184613617565b600073ffffffffffffffffffffffffffffffffffffffff808651168352806020870151166020840152506040850151604083015260806060830152826080830152828460a0840137600060a0848401015260a07fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8501168301019050949350505050565b60005b8381101561388e578181015183820152602001613876565b50506000910152565b600081518084526138af816020860160208601613873565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b60e0815260006138f460e083018a613897565b8281036020840152613906818a613897565b91505073ffffffffffffffffffffffffffffffffffffffff808816604084015286606084015263ffffffff861660808401528460a084015280841660c08401525098975050505050505050565b60006020828403121561396557600080fd5b815161310081613372565b6060815260006139836060830186613617565b73ffffffffffffffffffffffffffffffffffffffff9490941660208301525060400152919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b818103818111156128105761281061375e565b808201808211156128105761281061375e565b608081526000613a136080830187613617565b60208301959095525060408101929092521515606090910152919050565b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203613a6257613a6261375e565b5060010190565b600082613a7857613a7861372f565b500690565b63ffffffff818116838216019080821115613a9a57613a9a61375e565b5092915050565b60008251613ab3818460208701613873565b919091019291505056fea2646970667358221220f44f63073f0d1748561f9f883f09bdb859b3d6de7a170c108ad4d21d437b260364736f6c63430008140033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000c9251b4f1d4acc535bb3392be257b72739abf0d3000000000000000000000000f8fa8d19d8e5384f9bd48fad67dafeedfea27e6d00000000000000000000000000000000000000000000000000000000000000600000000000000000000000000000000000000000000000000000000000000180b165298935924f540e4181c03493a5d686c54a0aaeb3f6216de85b7ffbba7738000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec700000000000000000000000000000000000000000000000000000000000f424000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000333905980000000000000000000000000000000000000000000000000000000000000f42400000000000000000000000000000000000000000000000000000000068d173ac0000000000000000000000000000000000000000000000000000000068da4a9000000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002435316463623037372d623236662d343033662d626634372d34643636613030643333376400000000000000000000000000000000000000000000000000000000
-----Decoded View---------------
Arg [0] : _rbac (address): 0xC9251b4f1D4aCc535Bb3392be257B72739Abf0d3
Arg [1] : _rwaTokenFactory (address): 0xf8fA8d19D8E5384F9bD48FAD67dAfeeDfea27E6d
Arg [2] : _params (tuple):
Arg [1] : saleId (string): 51dcb077-b26f-403f-bf47-4d66a00d337d
Arg [2] : investorRole (bytes32): 0xb165298935924f540e4181c03493a5d686c54a0aaeb3f6216de85b7ffbba7738
Arg [3] : paymentToken (address): 0xdAC17F958D2ee523a2206206994597C13D831ec7
Arg [4] : price (uint256): 1000000
Arg [5] : minCommitment (uint256): 0
Arg [6] : maxCommitment (uint256): 0
Arg [7] : hardcap (uint256): 220000000000
Arg [8] : softcap (uint256): 1000000
Arg [9] : saleStart (uint32): 1758557100
Arg [10] : saleEnd (uint32): 1759136400
Arg [11] : cancelable (bool): True
Arg [12] : isAddressed (bool): False
-----Encoded View---------------
18 Constructor Arguments found :
Arg [0] : 000000000000000000000000c9251b4f1d4acc535bb3392be257b72739abf0d3
Arg [1] : 000000000000000000000000f8fa8d19d8e5384f9bd48fad67dafeedfea27e6d
Arg [2] : 0000000000000000000000000000000000000000000000000000000000000060
Arg [3] : 0000000000000000000000000000000000000000000000000000000000000180
Arg [4] : b165298935924f540e4181c03493a5d686c54a0aaeb3f6216de85b7ffbba7738
Arg [5] : 000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7
Arg [6] : 00000000000000000000000000000000000000000000000000000000000f4240
Arg [7] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [8] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [9] : 0000000000000000000000000000000000000000000000000000003339059800
Arg [10] : 00000000000000000000000000000000000000000000000000000000000f4240
Arg [11] : 0000000000000000000000000000000000000000000000000000000068d173ac
Arg [12] : 0000000000000000000000000000000000000000000000000000000068da4a90
Arg [13] : 0000000000000000000000000000000000000000000000000000000000000001
Arg [14] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [15] : 0000000000000000000000000000000000000000000000000000000000000024
Arg [16] : 35316463623037372d623236662d343033662d626634372d3464363661303064
Arg [17] : 3333376400000000000000000000000000000000000000000000000000000000
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 34 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.