Description:
Multi-signature wallet contract requiring multiple confirmations for transaction execution.
Blockchain: Ethereum
Source Code: View Code On The Blockchain
Solidity Source Code:
{{
"language": "Solidity",
"sources": {
"@openzeppelin/contracts/access/Ownable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol)
pragma solidity ^0.8.20;
import {Context} from "../utils/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* The initial owner is set to the address provided by the deployer. This can
* later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract Ownable is Context {
address private _owner;
/**
* @dev The caller account is not authorized to perform an operation.
*/
error OwnableUnauthorizedAccount(address account);
/**
* @dev The owner is not a valid owner account. (eg. `address(0)`)
*/
error OwnableInvalidOwner(address owner);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the address provided by the deployer as the initial owner.
*/
constructor(address initialOwner) {
if (initialOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(initialOwner);
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
if (owner() != _msgSender()) {
revert OwnableUnauthorizedAccount(_msgSender());
}
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby disabling any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
if (newOwner == address(0)) {
revert OwnableInvalidOwner(address(0));
}
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
"
},
"@openzeppelin/contracts/interfaces/draft-IERC6093.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (interfaces/draft-IERC6093.sol)
pragma solidity >=0.8.4;
/**
* @dev Standard ERC-20 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-20 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 ERC-721 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-721 tokens.
*/
interface IERC721Errors {
/**
* @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in ERC-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 ERC-1155 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-1155 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);
}
"
},
"@openzeppelin/contracts/token/ERC20/ERC20.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.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 ERC-20
* applications.
*/
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}.
*
* Both 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;
}
/// @inheritdoc IERC20
function totalSupply() public view virtual returns (uint256) {
return _totalSupply;
}
/// @inheritdoc IERC20
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;
}
/// @inheritdoc IERC20
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}.
*
* Skips emitting an {Approval} event indicating an allowance update. This is not
* required by the ERC. See {xref-ERC20-_approve-address-address-uint256-bool-}[_approve].
*
* 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:
*
* ```solidity
* 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);
}
}
}
}
"
},
"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity >=0.6.2;
import {IERC20} from "../IERC20.sol";
/**
* @dev Interface for the optional metadata functions from the ERC-20 standard.
*/
interface IERC20Metadata is IERC20 {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}
"
},
"@openzeppelin/contracts/token/ERC20/IERC20.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC20/IERC20.sol)
pragma solidity >=0.4.16;
/**
* @dev Interface of the ERC-20 standard as defined in the ERC.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 value) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 value) external returns (bool);
}
"
},
"@openzeppelin/contracts/utils/Context.sol": {
"content": "// 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;
}
}
"
},
"@openzeppelin/contracts/utils/ReentrancyGuard.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/ReentrancyGuard.sol)
pragma solidity ^0.8.20;
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If EIP-1153 (transient storage) is available on the chain you're deploying at,
* consider using {ReentrancyGuardTransient} instead.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
abstract contract ReentrancyGuard {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being non-zero value makes deployment a bit more expensive,
// but in exchange the refund on every call to nonReentrant will be lower in
// amount. Since refunds are capped to a percentage of the total
// transaction's gas, it is best to keep them low in cases like this one, to
// increase the likelihood of the full refund coming into effect.
uint256 private constant NOT_ENTERED = 1;
uint256 private constant ENTERED = 2;
uint256 private _status;
/**
* @dev Unauthorized reentrant call.
*/
error ReentrancyGuardReentrantCall();
constructor() {
_status = NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function _nonReentrantBefore() private {
// On the first call to nonReentrant, _status will be NOT_ENTERED
if (_status == ENTERED) {
revert ReentrancyGuardReentrantCall();
}
// Any calls to nonReentrant after this point will fail
_status = ENTERED;
}
function _nonReentrantAfter() private {
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = NOT_ENTERED;
}
/**
* @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
* `nonReentrant` function in the call stack.
*/
function _reentrancyGuardEntered() internal view returns (bool) {
return _status == ENTERED;
}
}
"
},
"@uniswap/universal-router/contracts/interfaces/IUniversalRouter.sol": {
"content": "// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.24;
interface IUniversalRouter {
/// @notice Thrown when a required command has failed
error ExecutionFailed(uint256 commandIndex, bytes message);
/// @notice Thrown when attempting to send ETH directly to the contract
error ETHNotAccepted();
/// @notice Thrown when executing commands with an expired deadline
error TransactionDeadlinePassed();
/// @notice Thrown when attempting to execute commands and an incorrect number of inputs are provided
error LengthMismatch();
// @notice Thrown when an address that isn't WETH tries to send ETH to the router without calldata
error InvalidEthSender();
/// @notice Executes encoded commands along with provided inputs. Reverts if deadline has expired.
/// @param commands A set of concatenated commands, each 1 byte in length
/// @param inputs An array of byte strings containing abi encoded inputs for each command
/// @param deadline The deadline by which the transaction must be executed
function execute(bytes calldata commands, bytes[] calldata inputs, uint256 deadline) external payable;
}
"
},
"@uniswap/v2-core/contracts/interfaces/IUniswapV2Factory.sol": {
"content": "pragma solidity >=0.5.0;
interface IUniswapV2Factory {
event PairCreated(address indexed token0, address indexed token1, address pair, uint);
function feeTo() external view returns (address);
function feeToSetter() external view returns (address);
function getPair(address tokenA, address tokenB) external view returns (address pair);
function allPairs(uint) external view returns (address pair);
function allPairsLength() external view returns (uint);
function createPair(address tokenA, address tokenB) external returns (address pair);
function setFeeTo(address) external;
function setFeeToSetter(address) external;
}
"
},
"contracts/OpepenStrategy.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "@uniswap/universal-router/contracts/interfaces/IUniversalRouter.sol";
import "@uniswap/v2-core/contracts/interfaces/IUniswapV2Factory.sol";
// OpenSea integration interfaces
interface IOpenSeaSeaport {
function fulfillOrder(
Order calldata order,
bytes32 fulfillerConduitKey,
address recipient,
uint256 maximumFulfilled
) external payable returns (bool fulfilled);
function createOrder(
OrderParameters calldata parameters,
bytes calldata signature
) external returns (bytes32 orderHash);
function getOrderHash(OrderParameters calldata parameters) external view returns (bytes32);
}
interface IERC721 {
function safeTransferFrom(address from, address to, uint256 tokenId) external;
function ownerOf(uint256 tokenId) external view returns (address);
function approve(address to, uint256 tokenId) external;
function getApproved(uint256 tokenId) external view returns (address);
function setApprovalForAll(address operator, bool approved) external;
function isApprovedForAll(address owner, address operator) external view returns (bool);
}
interface IERC1155 {
function safeTransferFrom(address from, address to, uint256 id, uint256 amount, bytes calldata data) external;
function balanceOf(address account, uint256 id) external view returns (uint256);
function setApprovalForAll(address operator, bool approved) external;
function isApprovedForAll(address account, address operator) external view returns (bool);
}
// OpenSea Seaport Order structure
struct Order {
address offerer;
address zone;
OfferItem[] offer;
ConsiderationItem[] consideration;
OrderType orderType;
uint256 startTime;
uint256 endTime;
bytes32 zoneHash;
uint256 salt;
bytes32 conduitKey;
uint256 totalOriginalConsiderationItems;
}
// OpenSea Seaport OrderParameters structure
struct OrderParameters {
address offerer;
address zone;
OfferItem[] offer;
ConsiderationItem[] consideration;
OrderType orderType;
uint256 startTime;
uint256 endTime;
bytes32 zoneHash;
uint256 salt;
bytes32 conduitKey;
uint256 totalOriginalConsiderationItems;
}
struct OfferItem {
ItemType itemType;
address token;
uint256 identifierOrCriteria;
uint256 startAmount;
uint256 endAmount;
address recipient;
}
struct ConsiderationItem {
ItemType itemType;
address token;
uint256 identifierOrCriteria;
uint256 startAmount;
uint256 endAmount;
address recipient;
}
enum OrderType {
FULL_OPEN,
PARTIAL_OPEN,
FULL_RESTRICTED,
PARTIAL_RESTRICTED
}
enum ItemType {
NATIVE,
ERC20,
ERC721,
ERC1155,
ERC721_WITH_CRITERIA,
ERC1155_WITH_CRITERIA
}
contract OpepenStrategy is ERC20, Ownable, ReentrancyGuard {
// State variables
bool public isPresaleActive;
bool public isUnlocked;
// Price in wei per whole token (1e18 base units)
uint256 public presalePrice;
uint256 public constant TREASURY_FEE_PERCENT = 8;
uint256 public constant DEV_FEE_PERCENT = 1;
uint256 public constant BURN_FEE_PERCENT = 1;
uint256 public constant TOTAL_FEE_PERCENT = 10;
address public treasury;
address public constant BURN_ADDRESS = 0x000000000000000000000000000000000000dEaD;
address public devWallet;
// OpenSea integration
address public openseaSeaport;
address public opepenContract;
address public backendAPI;
// Opepen floor configuration
bool public isOpepenFloorEnabled;
uint256 public opepenFloorSlippage;
// Uniswap Universal Router integration
IUniversalRouter public immutable universalRouter;
IUniswapV2Factory public immutable uniswapV2Factory;
address public immutable uniswapV2Pair;
address public uniswapFeeReceiver; // Wallet to receive swapped ETH
uint256 public swapThreshold; // Minimum tokens to trigger swap
uint256 public sellFeePercent; // Fee percentage on sells
bool private inSwap; // Prevent recursion during swaps
// Enhanced Opepen functionality
mapping(uint256 => uint256) public buyPrices;
mapping(uint256 => uint256) public purchaseTimes;
mapping(uint256 => address) public purchasers;
mapping(uint256 => bytes32) public purchaseOrderHashes;
mapping(uint256 => bool) public isTokenOwned;
mapping(uint256 => bool) public isListed;
mapping(uint256 => uint256) public listingPrices;
mapping(uint256 => bytes32) public listingOrderHashes;
mapping(uint256 => uint256) public listingStartTimes;
mapping(uint256 => uint256) public listingEndTimes;
mapping(address => bool) public authorizedCallers;
uint256 public totalPurchased;
uint256 public totalSpent;
uint256 public averageBuyPrice;
// Events
event PresaleActivated(uint256 price);
event PresaleDeactivated();
event TokensUnlocked();
event TokensPurchased(address indexed buyer, uint256 amount, uint256 cost);
event FeesCollected(uint256 treasuryAmount, uint256 devAmount, uint256 burnAmount);
event BackendAPICalled(address indexed buyer, uint256 tokenId, bool success);
event BuyPriceSet(uint256 indexed tokenId, uint256 buyPrice, uint256 timestamp);
event PurchaseValidated(uint256 indexed tokenId, uint256 price, bool isValid, uint256 timestamp);
event PurchaseOrderCreated(uint256 indexed tokenId, uint256 price, bytes32 orderHash, uint256 timestamp);
event ListingCreated(uint256 indexed tokenId, uint256 buyPrice, uint256 listingPrice, uint256 startTime, uint256 endTime, bytes32 orderHash);
event ListingFulfilled(uint256 indexed tokenId, address indexed buyer, uint256 price, uint256 timestamp);
event NFTReceived(address indexed from, uint256 indexed tokenId, uint256 amount, uint256 timestamp);
event ETHReceived(address indexed from, uint256 amount, uint256 timestamp);
event AuthorizedCallerAdded(address indexed caller);
event AuthorizedCallerRemoved(address indexed caller);
event DevWalletUpdated(address indexed oldWallet, address indexed newWallet);
event OpepenFloorBought(address indexed to, uint256 amount, uint256 timestamp);
event TokensSwappedForETH(uint256 tokenAmount, uint256 ethAmount, uint256 timestamp);
event SellFeeCollected(address indexed from, uint256 feeAmount, uint256 timestamp);
event UniswapConfigUpdated(address indexed feeReceiver, uint256 swapThreshold, uint256 sellFeePercent);
// Modifiers
modifier presaleActive() {
require(isPresaleActive, "Presale is not active");
_;
}
modifier tokensUnlocked() {
require(isUnlocked, "Tokens are still locked");
_;
}
modifier onlyAuthorized() {
require(authorizedCallers[msg.sender] || msg.sender == owner(), "Not authorized");
_;
}
constructor(
string memory name,
string memory symbol,
uint256 initialSupply,
address _treasury,
address _devWallet,
address _openseaSeaport,
address _opepenContract,
address _backendAPI,
address _universalRouter,
address _uniswapV2Factory,
uint256 _swapThreshold,
uint256 _sellFeePercent
) ERC20(name, symbol) Ownable(msg.sender) {
require(_treasury != address(0), "Treasury address cannot be zero");
require(_devWallet != address(0), "Dev wallet address cannot be zero");
require(_openseaSeaport != address(0), "OpenSea Seaport address cannot be zero");
require(_opepenContract != address(0), "Opepen contract address cannot be zero");
require(_backendAPI != address(0), "Backend API address cannot be zero");
require(_universalRouter != address(0), "Universal router address cannot be zero");
require(_uniswapV2Factory != address(0), "Uniswap V2 factory address cannot be zero");
require(_swapThreshold > 0, "Swap threshold must be greater than zero");
require(_sellFeePercent <= 100, "Sell fee percent cannot exceed 100%");
treasury = _treasury;
devWallet = _devWallet;
openseaSeaport = _openseaSeaport;
opepenContract = _opepenContract;
backendAPI = _backendAPI;
// Initialize Uniswap Universal Router
universalRouter = IUniversalRouter(_universalRouter);
uniswapV2Factory = IUniswapV2Factory(_uniswapV2Factory);
// Create V2 pair for this token
uniswapV2Pair = uniswapV2Factory.createPair(address(this), 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2); // WETH address
uniswapFeeReceiver = msg.sender; // Set owner as initial fee receiver
swapThreshold = _swapThreshold;
sellFeePercent = _sellFeePercent;
// Set default presale price to 0.000000011 ETH (11 gwei)
presalePrice = 11000000000; // 0.000000011 ETH in wei
// Initialize Opepen floor configuration
isOpepenFloorEnabled = false;
opepenFloorSlippage = 500; // 5% default slippage (500 basis points)
// Mint initial supply to owner (admin)
_mint(msg.sender, initialSupply);
// Pre-approve Universal Router for maximum token allowance
_approve(address(this), _universalRouter, type(uint256).max);
}
/**
* @dev Activate presale with specified price per whole token (wei)
* @param priceWei Price in wei per 1 whole token (1e18 units)
*/
function activatePresale(uint256 priceWei) external onlyOwner {
require(priceWei > 0, "Price must be greater than zero");
presalePrice = priceWei;
isPresaleActive = true;
emit PresaleActivated(priceWei);
}
/**
* @dev Deactivate presale
*/
function deactivatePresale() external onlyOwner {
isPresaleActive = false;
emit PresaleDeactivated();
}
/**
* @dev Unlock tokens to allow transfers
*/
function unlockTokens() external onlyOwner {
isUnlocked = true;
emit TokensUnlocked();
}
/**
* @dev Purchase tokens during presale
* @param amount Number of tokens to purchase (in base units, 18 decimals)
*/
function purchaseTokens(uint256 amount) external payable presaleActive nonReentrant {
require(amount > 0, "Amount must be greater than zero");
// requiredETH = amount * presalePrice / 1e18 (price is per whole token)
uint256 requiredETH = (amount * presalePrice) / 1e18;
require(msg.value >= requiredETH, "Insufficient ETH sent");
// Check if owner has enough tokens
require(balanceOf(owner()) >= amount, "Insufficient tokens available for sale");
// Transfer tokens from owner to buyer
_transfer(owner(), msg.sender, amount);
// Refund excess ETH
if (msg.value > requiredETH) {
payable(msg.sender).transfer(msg.value - requiredETH);
}
emit TokensPurchased(msg.sender, amount, requiredETH);
}
/**
* @dev Override transfer function to add fees and check unlock status
*/
function _update(address from, address to, uint256 amount) internal override {
// Allow minting (from == address(0)) and burning (to == address(0))
if (from == address(0) || to == address(0)) {
super._update(from, to, amount);
return;
}
// During presale, only allow transfers from owner (for purchases)
if (!isUnlocked && from != owner()) {
revert("Tokens are locked and cannot be transferred");
}
// Check if this is a sell to Uniswap pair
bool isSellToUniswap = !inSwap && isUnlocked && to == uniswapV2Pair && from != owner();
// Apply regular fees for all transfers (not presale purchases)
if (isUnlocked && from != owner() && to != owner()) {
uint256 feeAmount = (amount * TOTAL_FEE_PERCENT) / 100;
uint256 treasuryFee = (amount * TREASURY_FEE_PERCENT) / 100;
uint256 devFee = (amount * DEV_FEE_PERCENT) / 100;
uint256 burnFee = (amount * BURN_FEE_PERCENT) / 100;
// Transfer tokens minus fees
super._update(from, to, amount - feeAmount);
// Transfer treasury fee (goes to treasury wallet)
super._update(from, treasury, treasuryFee);
// Transfer dev fee (goes to dev wallet)
super._update(from, devWallet, devFee);
// Burn tokens (transfer to burn address)
super._update(from, BURN_ADDRESS, burnFee);
emit FeesCollected(treasuryFee, devFee, burnFee);
// Trigger swap if this is a sell to Uniswap pair and threshold is met
if (isSellToUniswap) {
uint256 contractBalance = balanceOf(address(this));
if (contractBalance >= swapThreshold && !inSwap) {
inSwap = true;
_swapTokensForETH(contractBalance);
inSwap = false;
}
}
} else {
// No fees for presale purchases or owner transfers
super._update(from, to, amount);
}
}
/**
* @dev Update treasury address
*/
function setTreasury(address _treasury) external onlyOwner {
require(_treasury != address(0), "Treasury address cannot be zero");
treasury = _treasury;
}
/**
* @dev Update dev wallet address
*/
function setDevWallet(address _devWallet) external onlyOwner {
require(_devWallet != address(0), "Dev wallet address cannot be zero");
address oldWallet = devWallet;
devWallet = _devWallet;
emit DevWalletUpdated(oldWallet, _devWallet);
}
/**
* @dev Buy Opepen floor with specific amount of ETH (only owner)
* @param amount Amount of ETH to use for buying Opepen floor in wei
*/
function BuyOpepenFloor(uint256 amount) external onlyOwner {
require(amount > 0, "Amount must be greater than zero");
require(address(this).balance >= amount, "Insufficient ETH balance");
payable(owner()).transfer(amount);
emit OpepenFloorBought(owner(), amount, block.timestamp);
}
/**
* @dev Buy Opepen floor with all ETH from contract (only owner) - emergency function
*/
function BuyOpepenFloorAll() external onlyOwner {
uint256 balance = address(this).balance;
require(balance > 0, "No ETH to buy Opepen floor");
payable(owner()).transfer(balance);
emit OpepenFloorBought(owner(), balance, block.timestamp);
}
/**
* @dev Get presale status
*/
function getPresaleStatus() external view returns (bool active, bool unlocked, uint256 price) {
return (isPresaleActive, isUnlocked, presalePrice);
}
/**
* @dev Get available tokens for presale
*/
function getAvailableTokensForPresale() external view returns (uint256) {
return balanceOf(owner());
}
/**
* @dev Update OpenSea Seaport address
*/
function setOpenSeaSeaport(address _seaport) external onlyOwner {
require(_seaport != address(0), "Seaport address cannot be zero");
openseaSeaport = _seaport;
}
/**
* @dev Update Opepen contract address
*/
function setOpepenContract(address _opepen) external onlyOwner {
require(_opepen != address(0), "Opepen contract address cannot be zero");
opepenContract = _opepen;
}
/**
* @dev Update backend API address
*/
function setBackendAPI(address _api) external onlyOwner {
require(_api != address(0), "Backend API address cannot be zero");
backendAPI = _api;
}
/**
* @dev Enable or disable Opepen floor functionality
*/
function setOpepenFloorEnabled(bool _enabled) external onlyOwner {
isOpepenFloorEnabled = _enabled;
}
/**
* @dev Set Opepen floor slippage (in basis points, e.g., 500 = 5%)
*/
function setOpepenFloorSlippage(uint256 _slippage) external onlyOwner {
require(_slippage <= 10000, "Slippage cannot exceed 100%");
opepenFloorSlippage = _slippage;
}
/**
* @dev Update Uniswap fee receiver address
*/
function setUniswapFeeReceiver(address _feeReceiver) external onlyOwner {
require(_feeReceiver != address(0), "Fee receiver address cannot be zero");
uniswapFeeReceiver = _feeReceiver;
emit UniswapConfigUpdated(_feeReceiver, swapThreshold, sellFeePercent);
}
/**
* @dev Update swap threshold
*/
function setSwapThreshold(uint256 _swapThreshold) external onlyOwner {
require(_swapThreshold > 0, "Swap threshold must be greater than zero");
swapThreshold = _swapThreshold;
emit UniswapConfigUpdated(uniswapFeeReceiver, _swapThreshold, sellFeePercent);
}
/**
* @dev Update sell fee percentage
*/
function setSellFeePercent(uint256 _sellFeePercent) external onlyOwner {
require(_sellFeePercent <= 100, "Sell fee percent cannot exceed 100%");
sellFeePercent = _sellFeePercent;
emit UniswapConfigUpdated(uniswapFeeReceiver, swapThreshold, _sellFeePercent);
}
/**
* @dev Manually trigger token swap (emergency function)
*/
function manualSwap() external onlyOwner {
uint256 contractBalance = balanceOf(address(this));
require(contractBalance > 0, "No tokens to swap");
require(!inSwap, "Swap already in progress");
inSwap = true;
_swapTokensForETH(contractBalance);
inSwap = false;
}
/**
* @dev Add authorized caller (backend)
*/
function addAuthorizedCaller(address caller) external onlyOwner {
require(caller != address(0), "Caller address cannot be zero");
authorizedCallers[caller] = true;
emit AuthorizedCallerAdded(caller);
}
/**
* @dev Remove authorized caller
*/
function removeAuthorizedCaller(address caller) external onlyOwner {
authorizedCallers[caller] = false;
emit AuthorizedCallerRemoved(caller);
}
/**
* @dev Receive ETH (for sales)
*/
receive() external payable {
emit ETHReceived(msg.sender, msg.value, block.timestamp);
}
/**
* @dev Fallback function
*/
fallback() external payable {
emit ETHReceived(msg.sender, msg.value, block.timestamp);
}
/**
* @dev Get listing information for a token
*/
function getListingInfo(uint256 tokenId) external view returns (
bool listed,
uint256 price,
uint256 startTime,
uint256 endTime,
bytes32 orderHash
) {
return (
isListed[tokenId],
listingPrices[tokenId],
listingStartTimes[tokenId],
listingEndTimes[tokenId],
listingOrderHashes[tokenId]
);
}
/**
* @dev Check if address is authorized caller
*/
function isAuthorizedCaller(address caller) external view returns (bool) {
return authorizedCallers[caller] || caller == owner();
}
/**
* @dev Get contract's ETH balance
*/
function getETHBalance() external view returns (uint256) {
return address(this).balance;
}
/**
* @dev Emergency function to cancel a listing
*/
function cancelListing(uint256 tokenId) external onlyOwner {
require(isListed[tokenId], "Token not listed");
isListed[tokenId] = false;
emit ListingCreated(tokenId, buyPrices[tokenId], 0, 0, 0, bytes32(0)); // Emit with 0 values to indicate cancellation
}
/**
* @dev Create Opepen listing (admin only)
* This function creates a listing on OpenSea for an NFT owned by the contract
*/
function createListing(
uint256 tokenId,
uint256 listingPrice,
uint256 startTime,
uint256 endTime,
uint256 salt
) external onlyOwner returns (bytes32 orderHash) {
require(isTokenOwned[tokenId], "Token not owned by contract");
require(buyPrices[tokenId] > 0, "Buy price not set");
require(!isListed[tokenId], "Token already listed");
require(listingPrice > 0, "Listing price must be greater than zero");
require(endTime > startTime, "End time must be after start time");
require(startTime >= block.timestamp, "Start time must be in the future");
// Create OpenSea Seaport order parameters
OrderParameters memory orderParams = OrderParameters({
offerer: address(this),
zone: address(0), // No zone restriction
offer: _createOfferItems(tokenId),
consideration: _createConsiderationItems(listingPrice),
orderType: OrderType.FULL_OPEN,
startTime: startTime,
endTime: endTime,
zoneHash: bytes32(0),
salt: salt,
conduitKey: bytes32(0),
totalOriginalConsiderationItems: 1
});
// Get order hash from OpenSea Seaport
orderHash = IOpenSeaSeaport(openseaSeaport).getOrderHash(orderParams);
// Update listing status
isListed[tokenId] = true;
listingPrices[tokenId] = listingPrice;
listingOrderHashes[tokenId] = orderHash;
listingStartTimes[tokenId] = startTime;
listingEndTimes[tokenId] = endTime;
emit ListingCreated(tokenId, buyPrices[tokenId], listingPrice, startTime, endTime, orderHash);
}
/**
* @dev Fulfill a listing (when someone buys the NFT)
*/
function fulfillListing(
uint256 tokenId,
address buyer
) external payable onlyAuthorized nonReentrant {
require(isListed[tokenId], "Token not listed");
require(block.timestamp >= listingStartTimes[tokenId], "Listing not started");
require(block.timestamp <= listingEndTimes[tokenId], "Listing expired");
require(msg.value >= listingPrices[tokenId], "Insufficient payment");
// Transfer NFT to buyer
IERC721(opepenContract).safeTransferFrom(address(this), buyer, tokenId);
// Update ownership status
isTokenOwned[tokenId] = false;
isListed[tokenId] = false;
// Distribute payment
uint256 listingPrice = listingPrices[tokenId];
uint256 excess = msg.value - listingPrice;
// Send excess back to buyer
if (excess > 0) {
payable(buyer).transfer(excess);
}
emit ListingFulfilled(tokenId, buyer, listingPrice, block.timestamp);
}
/**
* @dev Receive NFT from user (for listing)
*/
function receiveNFT(
uint256 tokenId,
uint256 buyPrice
) external {
require(IERC721(opepenContract).ownerOf(tokenId) == address(this), "Contract doesn't own token");
require(buyPrice > 0, "Buy price must be greater than zero");
isTokenOwned[tokenId] = true;
buyPrices[tokenId] = buyPrice;
purchaseTimes[tokenId] = block.timestamp;
purchasers[tokenId] = msg.sender;
emit NFTReceived(msg.sender, tokenId, 1, block.timestamp);
}
/**
* @dev Create offer items for OpenSea order
*/
function _createOfferItems(uint256 tokenId) internal view returns (OfferItem[] memory) {
OfferItem[] memory offer = new OfferItem[](1);
offer[0] = OfferItem({
itemType: ItemType.ERC721,
token: opepenContract,
identifierOrCriteria: tokenId,
startAmount: 1,
endAmount: 1,
recipient: address(0)
});
return offer;
}
/**
* @dev Create consideration items for OpenSea order
*/
function _createConsiderationItems(uint256 price) internal view returns (ConsiderationItem[] memory) {
ConsiderationItem[] memory consideration = new ConsiderationItem[](1);
consideration[0] = ConsiderationItem({
itemType: ItemType.NATIVE,
token: address(0),
identifierOrCriteria: 0,
startAmount: price,
endAmount: price,
recipient: payable(address(this))
});
return consideration;
}
/**
* @dev Get fee configuration
*/
function getFeeConfig() external view returns (
uint256 treasuryFeePercent,
uint256 devFeePercent,
uint256 burnFeePercent,
uint256 totalFeePercent,
address treasuryAddress,
address devWalletAddress
) {
return (
TREASURY_FEE_PERCENT,
DEV_FEE_PERCENT,
BURN_FEE_PERCENT,
TOTAL_FEE_PERCENT,
treasury,
devWallet
);
}
/**
* @dev Get Opepen floor configuration
*/
function getOpepenFloorConfig() external view returns (
bool enabled,
address seaport,
address opepen,
address api,
uint256 slippage
) {
return (
isOpepenFloorEnabled,
openseaSeaport,
opepenContract,
backendAPI,
opepenFloorSlippage
);
}
/**
* @dev Get purchase statistics
*/
function getPurchaseStats() external view returns (
uint256 total,
uint256 spent,
uint256 average
) {
return (
totalPurchased,
totalSpent,
averageBuyPrice
);
}
/**
* @dev Get purchase history for a token
*/
function getPurchaseHistory(uint256 tokenId) external view returns (
address purchaser,
uint256 purchaseTime,
bool owned
) {
return (
purchasers[tokenId],
purchaseTimes[tokenId],
isTokenOwned[tokenId]
);
}
/**
* @dev Get Uniswap Universal Router configuration
*/
function getUniswapConfig() external view returns (
address router,
address factory,
address pair,
address feeReceiver,
uint256 currentSwapThreshold,
uint256 currentSellFeePercent,
bool inSwapStatus
) {
return (
address(universalRouter),
address(uniswapV2Factory),
uniswapV2Pair,
uniswapFeeReceiver,
swapThreshold,
sellFeePercent,
inSwap
);
}
/**
* @dev Get contract's token balance available for swapping
*/
function getSwapableTokenBalance() external view returns (uint256) {
return balanceOf(address(this));
}
/**
* @dev Check if swap threshold is met
*/
function isSwapThresholdMet() external view returns (bool) {
return balanceOf(address(this)) >= swapThreshold;
}
/**
* @dev Swap accumulated tokens for ETH using Universal Router
* @param tokenAmount Amount of tokens to swap
*/
function _swapTokensForETH(uint256 tokenAmount) private nonReentrant {
if (tokenAmount == 0) return;
uint256 initialETHBalance = address(this).balance;
// Universal Router command for V2 swap
// Command 0 = V2_SWAP_EXACT_IN
bytes memory commands = abi.encodePacked(uint8(0));
// Encode the V2 swap parameters
bytes memory input = abi.encode(
address(this), // tokenIn
0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2, // tokenOut (WETH)
uniswapV2Pair, // pair
tokenAmount, // amountIn
0, // amountOutMin (accept any amount)
uniswapFeeReceiver // recipient
);
// Create inputs array
bytes[] memory inputs = new bytes[](1);
inputs[0] = input;
// Execute the swap
universalRouter.execute(commands, inputs, block.timestamp);
uint256 ethReceived = address(this).balance - initialETHBalance;
emit TokensSwappedForETH(tokenAmount, ethReceived, block.timestamp);
}
}"
}
},
"settings": {
"optimizer": {
"enabled": true,
"runs": 200
},
"viaIR": true,
"evmVersion": "paris",
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
}
}
}}
Submitted on: 2025-09-19 20:23:47
Comments
Log in to comment.
No comments yet.