// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)
/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)
/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.
abstract contract ERC20 {
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
event Transfer(address indexed from, address indexed to, uint256 amount);
event Approval(address indexed owner, address indexed spender, uint256 amount);
/*//////////////////////////////////////////////////////////////
METADATA STORAGE
//////////////////////////////////////////////////////////////*/
string public name;
string public symbol;
uint8 public immutable decimals;
/*//////////////////////////////////////////////////////////////
ERC20 STORAGE
//////////////////////////////////////////////////////////////*/
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;
/*//////////////////////////////////////////////////////////////
EIP-2612 STORAGE
//////////////////////////////////////////////////////////////*/
uint256 internal immutable INITIAL_CHAIN_ID;
bytes32 internal immutable INITIAL_DOMAIN_SEPARATOR;
mapping(address => uint256) public nonces;
/*//////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/
constructor(
string memory _name,
string memory _symbol,
uint8 _decimals
) {
name = _name;
symbol = _symbol;
decimals = _decimals;
INITIAL_CHAIN_ID = block.chainid;
INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
}
/*//////////////////////////////////////////////////////////////
ERC20 LOGIC
//////////////////////////////////////////////////////////////*/
function approve(address spender, uint256 amount) public virtual returns (bool) {
allowance[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
return true;
}
function transfer(address to, uint256 amount) public virtual returns (bool) {
balanceOf[msg.sender] -= amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(msg.sender, to, amount);
return true;
}
function transferFrom(
address from,
address to,
uint256 amount
) public virtual returns (bool) {
uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.
if (allowed != type(uint256).max) allowance[from][msg.sender] = allowed - amount;
balanceOf[from] -= amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(from, to, amount);
return true;
}
/*//////////////////////////////////////////////////////////////
EIP-2612 LOGIC
//////////////////////////////////////////////////////////////*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) public virtual {
require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");
// Unchecked because the only math done is incrementing
// the owner's nonce which cannot realistically overflow.
unchecked {
address recoveredAddress = ecrecover(
keccak256(
abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR(),
keccak256(
abi.encode(
keccak256(
"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
),
owner,
spender,
value,
nonces[owner]++,
deadline
)
)
)
),
v,
r,
s
);
require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");
allowance[recoveredAddress][spender] = value;
}
emit Approval(owner, spender, value);
}
function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
return block.chainid == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
}
function computeDomainSeparator() internal view virtual returns (bytes32) {
return
keccak256(
abi.encode(
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
keccak256(bytes(name)),
keccak256("1"),
block.chainid,
address(this)
)
);
}
/*//////////////////////////////////////////////////////////////
INTERNAL MINT/BURN LOGIC
//////////////////////////////////////////////////////////////*/
function _mint(address to, uint256 amount) internal virtual {
totalSupply += amount;
// Cannot overflow because the sum of all user
// balances can't exceed the max uint256 value.
unchecked {
balanceOf[to] += amount;
}
emit Transfer(address(0), to, amount);
}
function _burn(address from, uint256 amount) internal virtual {
balanceOf[from] -= amount;
// Cannot underflow because a user's balance
// will never be larger than the total supply.
unchecked {
totalSupply -= amount;
}
emit Transfer(from, address(0), amount);
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (access/IAccessControl.sol)
pragma solidity ^0.8.20;
/**
* @dev External interface of AccessControl declared to support ERC-165 detection.
*/
interface IAccessControl {
/**
* @dev The `account` is missing a role.
*/
error AccessControlUnauthorizedAccount(address account, bytes32 neededRole);
/**
* @dev The caller of a function is not the expected one.
*
* NOTE: Don't confuse with {AccessControlUnauthorizedAccount}.
*/
error AccessControlBadConfirmation();
/**
* @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
*
* `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
* {RoleAdminChanged} not being emitted signaling this.
*/
event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
/**
* @dev Emitted when `account` is granted `role`.
*
* `sender` is the account that originated the contract call. This account bears the admin role (for the granted role).
* Expected in cases where the role was granted using the internal {AccessControl-_grantRole}.
*/
event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Emitted when `account` is revoked `role`.
*
* `sender` is the account that originated the contract call:
* - if using `revokeRole`, it is the admin role bearer
* - if using `renounceRole`, it is the role bearer (i.e. `account`)
*/
event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) external view returns (bool);
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {AccessControl-_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) external view returns (bytes32);
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function grantRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function revokeRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been granted `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `callerConfirmation`.
*/
function renounceRole(bytes32 role, address callerConfirmation) external;
}
// SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.4;
interface IAerodromeRouter {
struct Route {
address from;
address to;
bool stable;
address factory;
}
function swapExactETHForTokens(
uint256 amountOutMin,
Route[] memory routes,
address to,
uint256 deadline
) external payable returns (uint256[] memory amounts);
function addLiquidity(
address tokenA,
address tokenB,
bool stable,
uint256 amountADesired,
uint256 amountBDesired,
uint256 amountAMin,
uint256 amountBMin,
address to,
uint256 deadline
) external returns (uint256 amountA, uint256 amountB, uint256 liquidity);
function defaultFactory() external view returns (address);
function weth() external view returns (address);
}
// SPDX-License-Identifier: Unlicense
pragma solidity 0.8.28;
import {IBadge, IBadgeRef} from "./IBadgeRef.sol";
import {IERC20} from "@openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
interface IAgent is IERC20, IBadgeRef {
event AgentTransferable();
function makeTransferable() external;
function setBadge(IBadge _badge) external;
function isTransferable() external view returns (bool);
}
// SPDX-License-Identifier: Unlicense
pragma solidity 0.8.28;
import {IAgent} from "./IAgent.sol";
interface IAgentRef {
function agent() external view returns (IAgent);
}
// SPDX-License-Identifier: Unlicense
pragma solidity 0.8.28;
import {IAgent, IAgentRef} from "./IAgentRef.sol";
import {IERC721AQueryable} from "erc721a-4.3.0/contracts/extensions/IERC721AQueryable.sol";
interface IBadge is IERC721AQueryable, IAgentRef {
event Mited(
address indexed to,
uint256 indexed tokenId,
uint256 shares,
uint256 totalDistribiutedShares
);
event Burned(
address indexed from,
uint256 indexed tokenId,
uint256 shares,
uint256 totalDistribiutedShares
);
function mint(address to, uint256 shares) external;
function burn(uint256 tokenId) external;
function makeBurnable(bool burnable) external;
function maxShares() external view returns (uint256);
function contractURI() external view returns (string memory);
function totalDistribiutedShares() external view returns (uint256);
function tokenShares(uint256 tokenId) external view returns (uint256);
}
// SPDX-License-Identifier: Unlicense
pragma solidity 0.8.28;
import {IBadge} from "./IBadge.sol";
interface IBadgeRef {
function badge() external view returns (IBadge);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC-20 standard as defined in the ERC.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 value) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 value) external returns (bool);
}
// SPDX-License-Identifier: MIT
// ERC721A Contracts v4.3.0
// Creator: Chiru Labs
pragma solidity ^0.8.4;
/**
* @dev Interface of ERC721A.
*/
interface IERC721A {
/**
* The caller must own the token or be an approved operator.
*/
error ApprovalCallerNotOwnerNorApproved();
/**
* The token does not exist.
*/
error ApprovalQueryForNonexistentToken();
/**
* Cannot query the balance for the zero address.
*/
error BalanceQueryForZeroAddress();
/**
* Cannot mint to the zero address.
*/
error MintToZeroAddress();
/**
* The quantity of tokens minted must be more than zero.
*/
error MintZeroQuantity();
/**
* The token does not exist.
*/
error OwnerQueryForNonexistentToken();
/**
* The caller must own the token or be an approved operator.
*/
error TransferCallerNotOwnerNorApproved();
/**
* The token must be owned by `from`.
*/
error TransferFromIncorrectOwner();
/**
* Cannot safely transfer to a contract that does not implement the
* ERC721Receiver interface.
*/
error TransferToNonERC721ReceiverImplementer();
/**
* Cannot transfer to the zero address.
*/
error TransferToZeroAddress();
/**
* The token does not exist.
*/
error URIQueryForNonexistentToken();
/**
* The `quantity` minted with ERC2309 exceeds the safety limit.
*/
error MintERC2309QuantityExceedsLimit();
/**
* The `extraData` cannot be set on an unintialized ownership slot.
*/
error OwnershipNotInitializedForExtraData();
/**
* `_sequentialUpTo()` must be greater than `_startTokenId()`.
*/
error SequentialUpToTooSmall();
/**
* The `tokenId` of a sequential mint exceeds `_sequentialUpTo()`.
*/
error SequentialMintExceedsLimit();
/**
* Spot minting requires a `tokenId` greater than `_sequentialUpTo()`.
*/
error SpotMintTokenIdTooSmall();
/**
* Cannot mint over a token that already exists.
*/
error TokenAlreadyExists();
/**
* The feature is not compatible with spot mints.
*/
error NotCompatibleWithSpotMints();
// =============================================================
// STRUCTS
// =============================================================
struct TokenOwnership {
// The address of the owner.
address addr;
// Stores the start time of ownership with minimal overhead for tokenomics.
uint64 startTimestamp;
// Whether the token has been burned.
bool burned;
// Arbitrary data similar to `startTimestamp` that can be set via {_extraData}.
uint24 extraData;
}
// =============================================================
// TOKEN COUNTERS
// =============================================================
/**
* @dev Returns the total number of tokens in existence.
* Burned tokens will reduce the count.
* To get the total number of tokens minted, please see {_totalMinted}.
*/
function totalSupply() external view returns (uint256);
// =============================================================
// IERC165
// =============================================================
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* [EIP section](https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified)
* to learn more about how these ids are created.
*
* This function call must use less than 30000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
// =============================================================
// IERC721
// =============================================================
/**
* @dev Emitted when `tokenId` token is transferred from `from` to `to`.
*/
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
*/
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables or disables
* (`approved`) `operator` to manage all of its assets.
*/
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
/**
* @dev Returns the number of tokens in `owner`'s account.
*/
function balanceOf(address owner) external view returns (uint256 balance);
/**
* @dev Returns the owner of the `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function ownerOf(uint256 tokenId) external view returns (address owner);
/**
* @dev Safely transfers `tokenId` token from `from` to `to`,
* checking first that contract recipients are aware of the ERC721 protocol
* to prevent tokens from being forever locked.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must be have been allowed to move
* this token by either {approve} or {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement
* {IERC721Receiver-onERC721Received}, which is called upon a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(
address from,
address to,
uint256 tokenId,
bytes calldata data
) external payable;
/**
* @dev Equivalent to `safeTransferFrom(from, to, tokenId, '')`.
*/
function safeTransferFrom(
address from,
address to,
uint256 tokenId
) external payable;
/**
* @dev Transfers `tokenId` from `from` to `to`.
*
* WARNING: Usage of this method is discouraged, use {safeTransferFrom}
* whenever possible.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token
* by either {approve} or {setApprovalForAll}.
*
* Emits a {Transfer} event.
*/
function transferFrom(
address from,
address to,
uint256 tokenId
) external payable;
/**
* @dev Gives permission to `to` to transfer `tokenId` token to another account.
* The approval is cleared when the token is transferred.
*
* Only a single account can be approved at a time, so approving the
* zero address clears previous approvals.
*
* Requirements:
*
* - The caller must own the token or be an approved operator.
* - `tokenId` must exist.
*
* Emits an {Approval} event.
*/
function approve(address to, uint256 tokenId) external payable;
/**
* @dev Approve or remove `operator` as an operator for the caller.
* Operators can call {transferFrom} or {safeTransferFrom}
* for any token owned by the caller.
*
* Requirements:
*
* - The `operator` cannot be the caller.
*
* Emits an {ApprovalForAll} event.
*/
function setApprovalForAll(address operator, bool _approved) external;
/**
* @dev Returns the account approved for `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function getApproved(uint256 tokenId) external view returns (address operator);
/**
* @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
*
* See {setApprovalForAll}.
*/
function isApprovedForAll(address owner, address operator) external view returns (bool);
// =============================================================
// IERC721Metadata
// =============================================================
/**
* @dev Returns the token collection name.
*/
function name() external view returns (string memory);
/**
* @dev Returns the token collection symbol.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token.
*/
function tokenURI(uint256 tokenId) external view returns (string memory);
// =============================================================
// IERC2309
// =============================================================
/**
* @dev Emitted when tokens in `fromTokenId` to `toTokenId`
* (inclusive) is transferred from `from` to `to`, as defined in the
* [ERC2309](https://eips.ethereum.org/EIPS/eip-2309) standard.
*
* See {_mintERC2309} for more details.
*/
event ConsecutiveTransfer(uint256 indexed fromTokenId, uint256 toTokenId, address indexed from, address indexed to);
}
// SPDX-License-Identifier: MIT
// ERC721A Contracts v4.3.0
// Creator: Chiru Labs
pragma solidity ^0.8.4;
import '../IERC721A.sol';
/**
* @dev Interface of ERC721AQueryable.
*/
interface IERC721AQueryable is IERC721A {
/**
* Invalid query range (`start` >= `stop`).
*/
error InvalidQueryRange();
/**
* @dev Returns the `TokenOwnership` struct at `tokenId` without reverting.
*
* If the `tokenId` is out of bounds:
*
* - `addr = address(0)`
* - `startTimestamp = 0`
* - `burned = false`
* - `extraData = 0`
*
* If the `tokenId` is burned:
*
* - `addr = <Address of owner before token was burned>`
* - `startTimestamp = <Timestamp when token was burned>`
* - `burned = true`
* - `extraData = <Extra data when token was burned>`
*
* Otherwise:
*
* - `addr = <Address of owner>`
* - `startTimestamp = <Timestamp of start of ownership>`
* - `burned = false`
* - `extraData = <Extra data at start of ownership>`
*/
function explicitOwnershipOf(uint256 tokenId) external view returns (TokenOwnership memory);
/**
* @dev Returns an array of `TokenOwnership` structs at `tokenIds` in order.
* See {ERC721AQueryable-explicitOwnershipOf}
*/
function explicitOwnershipsOf(uint256[] memory tokenIds) external view returns (TokenOwnership[] memory);
/**
* @dev Returns an array of token IDs owned by `owner`,
* in the range [`start`, `stop`)
* (i.e. `start <= tokenId < stop`).
*
* This function allows for tokens to be queried if the collection
* grows too big for a single call of {ERC721AQueryable-tokensOfOwner}.
*
* Requirements:
*
* - `start < stop`
*/
function tokensOfOwnerIn(
address owner,
uint256 start,
uint256 stop
) external view returns (uint256[] memory);
/**
* @dev Returns an array of token IDs owned by `owner`.
*
* This function scans the ownership mapping and is O(`totalSupply`) in complexity.
* It is meant to be called off-chain.
*
* See {ERC721AQueryable-tokensOfOwnerIn} for splitting the scan into
* multiple smaller scans if the collection is large enough to cause
* an out-of-gas error (10K collections should be fine).
*/
function tokensOfOwner(address owner) external view returns (uint256[] memory);
}
// SPDX-License-Identifier: Unlicense
pragma solidity 0.8.28;
import {IAccessControl} from "@openzeppelin-contracts/contracts/access/IAccessControl.sol";
interface ISystemContext {
event ContractRegistered(bytes32 indexed id, address indexed adr);
event FeeSet(uint256 indexed feeNominator, uint256 indexed feeDenominator);
function setContract(bytes32 _id, address _adr) external;
function setContractByName(string calldata _name, address _adr) external;
function acl() external view returns (IAccessControl);
function getContractByName(
string calldata _name
) external view returns (address);
function contractExists(bytes32 _id) external view returns (bool);
function contractExistsByName(
string calldata _name
) external view returns (bool);
function getContarct(bytes32 _id) external view returns (address);
function feeNominator() external view returns (uint256);
function FEE_DENOMINATOR() external view returns (uint256);
}
// SPDX-License-Identifier: Unlicense
pragma solidity 0.8.28;
import {ISystemContext} from "./ISystemContext.sol";
interface ISystemContextHolder {
function systemContext() external view returns (ISystemContext);
}
// SPDX-License-Identifier: Unlicense
pragma solidity 0.8.28;
import {IAgent} from "./IAgent.sol";
interface ITreasury {
error TransferNativeFailed();
error InsufficientAmount();
struct CollectedForCollection {
uint256 native;
uint256 token;
}
function buyFunPack(
bytes32 guid,
IAgent agent,
address to,
uint256 amount
) external payable;
function transfer(address token, address to, uint256 amount) external;
function transferNative(address to, uint256 amount) external;
function returnForFunPack(
bytes32 guid,
uint256 amount,
address to
) external;
function transferBeforeAddingLiquidity(
bytes32 guid,
IAgent agent,
address to
) external returns (CollectedForCollection memory);
}
// SPDX-License-Identifier: Unlicense
pragma solidity ^0.8.0;
library Roles {
// 0x939a24089ef9a2a3684f692aedeb6fd0718b403972efb8e9da1cd3482c725e4e
bytes32 public constant COLLECTION_DATA_SIGNER =
keccak256("COLLECTION_DATA_SIGNER");
// 0x828634d95e775031b9ff576b159a8509d3053581a8c9c4d7d86899e0afcd882f
bytes32 public constant CREATOR_ROLE = keccak256("CREATOR_ROLE");
// 0xc5a01215c40ae393b8dd7ca6c61496b1cc32e1f8c7c4359fd8dae25be62fcd41
bytes32 public constant SUPPLY_MANAGER_ROLE =
keccak256("SUPPLY_MANAGER_ROLE");
// 0x1b05b96c44da1642397b13f9be393e1c7e511337f767be5b49d066a258a05e93
bytes32 public constant BONDING_CURVE_ADMIN_ROLE =
keccak256("BONDING_CURVE_ADMIN_ROLE");
// 0x91b8732a4217bad2aa8c558673916da1214fe5944c933a3aff5c60758ed3368f
bytes32 public constant COLLECTIONS_REGISTRATOR_ROLE =
keccak256("COLLECTIONS_REGISTRATOR_ROLE");
// 0xfbf54e323e51085d9430651bc8cd8ce2260d5350b7ad5ec9924f27bf1f9e761f
bytes32 public constant AI_AGENT_FUN_PACK_CREATOR_ROLE =
keccak256("AI_AGENT_FUN_PACK_CREATOR_ROLE");
// 0x7a05a596cb0ce7fdea8a1e1ec73be300bdb35097c944ce1897202f7a13122eb2
bytes32 public constant ROUTER_ROLE = keccak256("ROUTER_ROLE");
// 0xd9cba60585c9657ebda3da0083d5e02c7e19fcb21be2bbbbad0592b0b4040921
bytes32 public constant SYSTEM_CONTEXT_ADMIN_ROLE =
keccak256("SYSTEM_CONTEXT_ADMIN_ROLE");
// 0x42802a37d17e698ec3d88f7a6917f1f5a6abb4d99a8f4255c389e56d10218a64
bytes32 public constant LIQUIDITY_PROVIDER_ROLE =
keccak256("LIQUIDITY_PROVIDER_ROLE");
// 0xe28507373278a16e91a3c50928460a3461e7d7b9bca969110a305de57aa5916a
bytes32 public constant TRANSFER_CONTROLLER_ROLE =
keccak256("TRANSFER_CONTROLLER_ROLE");
// 0x73e9313463d20ecb48d57e0f6f5d83b7adbe3a3f694bf9358ab1f80d8ebcd90a
bytes32 public constant SYSTEM_ADMIN_ROLE = keccak256("SYSTEM_ADMIN_ROLE");
}
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.0;
import {ERC20} from "../tokens/ERC20.sol";
/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
/// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer.
library SafeTransferLib {
/*//////////////////////////////////////////////////////////////
ETH OPERATIONS
//////////////////////////////////////////////////////////////*/
function safeTransferETH(address to, uint256 amount) internal {
bool success;
/// @solidity memory-safe-assembly
assembly {
// Transfer the ETH and store if it succeeded or not.
success := call(gas(), to, amount, 0, 0, 0, 0)
}
require(success, "ETH_TRANSFER_FAILED");
}
/*//////////////////////////////////////////////////////////////
ERC20 OPERATIONS
//////////////////////////////////////////////////////////////*/
function safeTransferFrom(
ERC20 token,
address from,
address to,
uint256 amount
) internal {
bool success;
/// @solidity memory-safe-assembly
assembly {
// Get a pointer to some free memory.
let freeMemoryPointer := mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.
mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), and(from, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "from" argument.
mstore(add(freeMemoryPointer, 36), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.
// We use 100 because the length of our calldata totals up like so: 4 + 32 * 3.
// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
success := call(gas(), token, 0, freeMemoryPointer, 100, 0, 32)
// Set success to whether the call reverted, if not we check it either
// returned exactly 1 (can't just be non-zero data), or had no return data and token has code.
if and(iszero(and(eq(mload(0), 1), gt(returndatasize(), 31))), success) {
success := iszero(or(iszero(extcodesize(token)), returndatasize()))
}
}
require(success, "TRANSFER_FROM_FAILED");
}
function safeTransfer(
ERC20 token,
address to,
uint256 amount
) internal {
bool success;
/// @solidity memory-safe-assembly
assembly {
// Get a pointer to some free memory.
let freeMemoryPointer := mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.
mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.
// We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
success := call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
// Set success to whether the call reverted, if not we check it either
// returned exactly 1 (can't just be non-zero data), or had no return data and token has code.
if and(iszero(and(eq(mload(0), 1), gt(returndatasize(), 31))), success) {
success := iszero(or(iszero(extcodesize(token)), returndatasize()))
}
}
require(success, "TRANSFER_FAILED");
}
function safeApprove(
ERC20 token,
address to,
uint256 amount
) internal {
bool success;
/// @solidity memory-safe-assembly
assembly {
// Get a pointer to some free memory.
let freeMemoryPointer := mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.
mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), and(to, 0xffffffffffffffffffffffffffffffffffffffff)) // Append and mask the "to" argument.
mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument. Masking not required as it's a full 32 byte type.
// We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.
// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.
success := call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
// Set success to whether the call reverted, if not we check it either
// returned exactly 1 (can't just be non-zero data), or had no return data and token has code.
if and(iszero(and(eq(mload(0), 1), gt(returndatasize(), 31))), success) {
success := iszero(or(iszero(extcodesize(token)), returndatasize()))
}
}
require(success, "APPROVE_FAILED");
}
}
// SPDX-License-Identifier: Unlicense
pragma solidity 0.8.28;
import {ITreasury} from "../interfaces/ITreasury.sol";
import {ISystemContextHolder} from "../interfaces/ISystemContextHolder.sol";
import {ISystemContext} from "../interfaces/ISystemContext.sol";
import {IAgent} from "../interfaces/IAgent.sol";
import {IAerodromeRouter} from "../interfaces/IAerodromeRouter.sol";
import {Roles} from "../lib/Roles.sol";
import {SafeTransferLib} from "solmate-6.8.0/src/utils/SafeTransferLib.sol";
import {ERC20} from "solmate-6.8.0/src/tokens/ERC20.sol";
contract Treasury is ITreasury, ISystemContextHolder {
error OnlyRole(bytes32 role);
event FunPackFundsCollected(
bytes32 indexed guid,
uint256 amount,
uint256 totalAmount
);
event FunPackFundsReturned(
bytes32 indexed guid,
uint256 amount,
uint256 totalAmount
);
event CollectedNativeTranferred(address to, uint256 amount);
ISystemContext public override systemContext;
mapping(bytes32 => uint256) internal _funPackAllocations;
modifier onlyRole(bytes32 role) {
if (!systemContext.acl().hasRole(role, msg.sender)) {
revert OnlyRole(role);
}
_;
}
constructor(ISystemContext systemContext_) {
systemContext = systemContext_;
}
function buyFunPack(
bytes32 guid,
IAgent agent,
address to,
uint256 amount
) external payable onlyRole(Roles.TRANSFER_CONTROLLER_ROLE) {
_funPackAllocations[guid] += msg.value;
SafeTransferLib.safeTransfer(ERC20(address(agent)), to, amount);
emit FunPackFundsCollected(guid, msg.value, _funPackAllocations[guid]);
}
function returnForFunPack(
bytes32 guid,
uint256 amount,
address to
) external override onlyRole(Roles.TRANSFER_CONTROLLER_ROLE) {
uint256 totalForFunPack = _funPackAllocations[guid];
if (amount > totalForFunPack) {
revert InsufficientAmount();
}
_funPackAllocations[guid] -= amount;
_transferNative(to, amount);
emit FunPackFundsReturned(guid, amount, _funPackAllocations[guid]);
}
function transfer(
address token,
address to,
uint256 amount
) external override onlyRole(Roles.SYSTEM_ADMIN_ROLE) {
SafeTransferLib.safeTransfer(ERC20(token), to, amount);
}
function transferBeforeAddingLiquidity(
bytes32 guid,
IAgent agent,
address to
)
external
onlyRole(Roles.LIQUIDITY_PROVIDER_ROLE)
returns (CollectedForCollection memory)
{
CollectedForCollection memory collectedForCollection;
collectedForCollection.native = _funPackAllocations[guid];
collectedForCollection.token = agent.balanceOf(address(this));
_transferNative(to, collectedForCollection.native);
SafeTransferLib.safeTransfer(
ERC20(address(agent)),
to,
collectedForCollection.token
);
_funPackAllocations[guid] = 0;
emit CollectedNativeTranferred(to, collectedForCollection.native);
return collectedForCollection;
}
function transferNative(
address to,
uint256 amount
) external override onlyRole(Roles.SYSTEM_ADMIN_ROLE) {
_transferNative(to, amount);
}
function getAllocation(bytes32 guid) external view returns (uint256) {
return _funPackAllocations[guid];
}
function _transferNative(address to, uint256 amount) internal {
bool success;
/// @solidity memory-safe-assembly
assembly {
// Transfer the ETH and store if it succeeded or not.
success := call(gas(), to, amount, 0, 0, 0, 0)
}
if (!success) {
revert TransferNativeFailed();
}
}
}
{
"compilationTarget": {
"src/implementations/Treasury.sol": "Treasury"
},
"evmVersion": "cancun",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs"
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": [
":@openzeppelin-contracts/contracts/=dependencies/@openzeppelin-contracts-5.1.0/",
":erc721a-4.3.0/=dependencies/erc721a-4.3.0/",
":forge-std/=lib/forge-std/src/",
":solmate-6.8.0/=dependencies/solmate-6.8.0/"
],
"viaIR": true
}
[{"inputs":[{"internalType":"contract ISystemContext","name":"systemContext_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"InsufficientAmount","type":"error"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"OnlyRole","type":"error"},{"inputs":[],"name":"TransferNativeFailed","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"CollectedNativeTranferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"guid","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalAmount","type":"uint256"}],"name":"FunPackFundsCollected","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"guid","type":"bytes32"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"totalAmount","type":"uint256"}],"name":"FunPackFundsReturned","type":"event"},{"inputs":[{"internalType":"bytes32","name":"guid","type":"bytes32"},{"internalType":"contract IAgent","name":"agent","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"buyFunPack","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"guid","type":"bytes32"}],"name":"getAllocation","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"guid","type":"bytes32"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"to","type":"address"}],"name":"returnForFunPack","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"systemContext","outputs":[{"internalType":"contract ISystemContext","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"guid","type":"bytes32"},{"internalType":"contract IAgent","name":"agent","type":"address"},{"internalType":"address","name":"to","type":"address"}],"name":"transferBeforeAddingLiquidity","outputs":[{"components":[{"internalType":"uint256","name":"native","type":"uint256"},{"internalType":"uint256","name":"token","type":"uint256"}],"internalType":"struct ITreasury.CollectedForCollection","name":"","type":"tuple"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferNative","outputs":[],"stateMutability":"nonpayable","type":"function"}]