// SPDX-License-Identifier: GPL-3.0-or-laterpragmasolidity ^0.8.17;/// @title Constant state/// @notice Constant state used by the Universal RouterlibraryConstants{
/// @dev Used for identifying cases when this contract's balance of a token is to be used as an input/// This value is equivalent to 1<<255, i.e. a singular 1 in the most significant bit.uint256internalconstant CONTRACT_BALANCE =0x8000000000000000000000000000000000000000000000000000000000000000;
/// @dev Used for identifying cases when a v2 pair has already received input tokensuint256internalconstant ALREADY_PAID =0;
/// @dev Used as a flag for identifying the transfer of ETH instead of a tokenaddressinternalconstant ETH =address(0);
/// @dev Used for ETH addressaddressinternalconstant ETH_ADDRESS =address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE);
/// @dev Used as a flag for identifying that msg.sender should be used, saves gas by sending more 0 bytesaddressinternalconstant MSG_SENDER =address(1);
/// @dev Used as a flag for identifying address(this) should be used, saves gas by sending more 0 bytesaddressinternalconstant ADDRESS_THIS =address(2);
}
Contract Source Code
File 2 of 17: ERC1155.sol
// SPDX-License-Identifier: AGPL-3.0-onlypragmasolidity >=0.8.0;/// @notice Minimalist and gas efficient standard ERC1155 implementation./// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC1155.sol)abstractcontractERC1155{
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/eventTransferSingle(addressindexed operator,
addressindexedfrom,
addressindexed to,
uint256 id,
uint256 amount
);
eventTransferBatch(addressindexed operator,
addressindexedfrom,
addressindexed to,
uint256[] ids,
uint256[] amounts
);
eventApprovalForAll(addressindexed owner, addressindexed operator, bool approved);
eventURI(string value, uint256indexed id);
/*//////////////////////////////////////////////////////////////
ERC1155 STORAGE
//////////////////////////////////////////////////////////////*/mapping(address=>mapping(uint256=>uint256)) public balanceOf;
mapping(address=>mapping(address=>bool)) public isApprovedForAll;
/*//////////////////////////////////////////////////////////////
METADATA LOGIC
//////////////////////////////////////////////////////////////*/functionuri(uint256 id) publicviewvirtualreturns (stringmemory);
/*//////////////////////////////////////////////////////////////
ERC1155 LOGIC
//////////////////////////////////////////////////////////////*/functionsetApprovalForAll(address operator, bool approved) publicvirtual{
isApprovedForAll[msg.sender][operator] = approved;
emit ApprovalForAll(msg.sender, operator, approved);
}
functionsafeTransferFrom(addressfrom,
address to,
uint256 id,
uint256 amount,
bytescalldata data
) publicvirtual{
require(msg.sender==from|| isApprovedForAll[from][msg.sender], "NOT_AUTHORIZED");
balanceOf[from][id] -= amount;
balanceOf[to][id] += amount;
emit TransferSingle(msg.sender, from, to, id, amount);
require(
to.code.length==0
? to !=address(0)
: ERC1155TokenReceiver(to).onERC1155Received(msg.sender, from, id, amount, data) ==
ERC1155TokenReceiver.onERC1155Received.selector,
"UNSAFE_RECIPIENT"
);
}
functionsafeBatchTransferFrom(addressfrom,
address to,
uint256[] calldata ids,
uint256[] calldata amounts,
bytescalldata data
) publicvirtual{
require(ids.length== amounts.length, "LENGTH_MISMATCH");
require(msg.sender==from|| isApprovedForAll[from][msg.sender], "NOT_AUTHORIZED");
// Storing these outside the loop saves ~15 gas per iteration.uint256 id;
uint256 amount;
for (uint256 i =0; i < ids.length; ) {
id = ids[i];
amount = amounts[i];
balanceOf[from][id] -= amount;
balanceOf[to][id] += amount;
// An array can't have a total length// larger than the max uint256 value.unchecked {
++i;
}
}
emit TransferBatch(msg.sender, from, to, ids, amounts);
require(
to.code.length==0
? to !=address(0)
: ERC1155TokenReceiver(to).onERC1155BatchReceived(msg.sender, from, ids, amounts, data) ==
ERC1155TokenReceiver.onERC1155BatchReceived.selector,
"UNSAFE_RECIPIENT"
);
}
functionbalanceOfBatch(address[] calldata owners, uint256[] calldata ids)
publicviewvirtualreturns (uint256[] memory balances)
{
require(owners.length== ids.length, "LENGTH_MISMATCH");
balances =newuint256[](owners.length);
// Unchecked because the only math done is incrementing// the array index counter which cannot possibly overflow.unchecked {
for (uint256 i =0; i < owners.length; ++i) {
balances[i] = balanceOf[owners[i]][ids[i]];
}
}
}
/*//////////////////////////////////////////////////////////////
ERC165 LOGIC
//////////////////////////////////////////////////////////////*/functionsupportsInterface(bytes4 interfaceId) publicviewvirtualreturns (bool) {
return
interfaceId ==0x01ffc9a7||// ERC165 Interface ID for ERC165
interfaceId ==0xd9b67a26||// ERC165 Interface ID for ERC1155
interfaceId ==0x0e89341c; // ERC165 Interface ID for ERC1155MetadataURI
}
/*//////////////////////////////////////////////////////////////
INTERNAL MINT/BURN LOGIC
//////////////////////////////////////////////////////////////*/function_mint(address to,
uint256 id,
uint256 amount,
bytesmemory data
) internalvirtual{
balanceOf[to][id] += amount;
emit TransferSingle(msg.sender, address(0), to, id, amount);
require(
to.code.length==0
? to !=address(0)
: ERC1155TokenReceiver(to).onERC1155Received(msg.sender, address(0), id, amount, data) ==
ERC1155TokenReceiver.onERC1155Received.selector,
"UNSAFE_RECIPIENT"
);
}
function_batchMint(address to,
uint256[] memory ids,
uint256[] memory amounts,
bytesmemory data
) internalvirtual{
uint256 idsLength = ids.length; // Saves MLOADs.require(idsLength == amounts.length, "LENGTH_MISMATCH");
for (uint256 i =0; i < idsLength; ) {
balanceOf[to][ids[i]] += amounts[i];
// An array can't have a total length// larger than the max uint256 value.unchecked {
++i;
}
}
emit TransferBatch(msg.sender, address(0), to, ids, amounts);
require(
to.code.length==0
? to !=address(0)
: ERC1155TokenReceiver(to).onERC1155BatchReceived(msg.sender, address(0), ids, amounts, data) ==
ERC1155TokenReceiver.onERC1155BatchReceived.selector,
"UNSAFE_RECIPIENT"
);
}
function_batchBurn(addressfrom,
uint256[] memory ids,
uint256[] memory amounts
) internalvirtual{
uint256 idsLength = ids.length; // Saves MLOADs.require(idsLength == amounts.length, "LENGTH_MISMATCH");
for (uint256 i =0; i < idsLength; ) {
balanceOf[from][ids[i]] -= amounts[i];
// An array can't have a total length// larger than the max uint256 value.unchecked {
++i;
}
}
emit TransferBatch(msg.sender, from, address(0), ids, amounts);
}
function_burn(addressfrom,
uint256 id,
uint256 amount
) internalvirtual{
balanceOf[from][id] -= amount;
emit TransferSingle(msg.sender, from, address(0), id, amount);
}
}
/// @notice A generic interface for a contract which properly accepts ERC1155 tokens./// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC1155.sol)abstractcontractERC1155TokenReceiver{
functiononERC1155Received(address,
address,
uint256,
uint256,
bytescalldata) externalvirtualreturns (bytes4) {
return ERC1155TokenReceiver.onERC1155Received.selector;
}
functiononERC1155BatchReceived(address,
address,
uint256[] calldata,
uint256[] calldata,
bytescalldata) externalvirtualreturns (bytes4) {
return ERC1155TokenReceiver.onERC1155BatchReceived.selector;
}
}
Contract Source Code
File 3 of 17: ERC20.sol
// SPDX-License-Identifier: AGPL-3.0-onlypragmasolidity >=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.abstractcontractERC20{
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/eventTransfer(addressindexedfrom, addressindexed to, uint256 amount);
eventApproval(addressindexed owner, addressindexed spender, uint256 amount);
/*//////////////////////////////////////////////////////////////
METADATA STORAGE
//////////////////////////////////////////////////////////////*/stringpublic name;
stringpublic symbol;
uint8publicimmutable decimals;
/*//////////////////////////////////////////////////////////////
ERC20 STORAGE
//////////////////////////////////////////////////////////////*/uint256public totalSupply;
mapping(address=>uint256) public balanceOf;
mapping(address=>mapping(address=>uint256)) public allowance;
/*//////////////////////////////////////////////////////////////
EIP-2612 STORAGE
//////////////////////////////////////////////////////////////*/uint256internalimmutable INITIAL_CHAIN_ID;
bytes32internalimmutable INITIAL_DOMAIN_SEPARATOR;
mapping(address=>uint256) public nonces;
/*//////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/constructor(stringmemory _name,
stringmemory _symbol,
uint8 _decimals
) {
name = _name;
symbol = _symbol;
decimals = _decimals;
INITIAL_CHAIN_ID =block.chainid;
INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
}
/*//////////////////////////////////////////////////////////////
ERC20 LOGIC
//////////////////////////////////////////////////////////////*/functionapprove(address spender, uint256 amount) publicvirtualreturns (bool) {
allowance[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
returntrue;
}
functiontransfer(address to, uint256 amount) publicvirtualreturns (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);
returntrue;
}
functiontransferFrom(addressfrom,
address to,
uint256 amount
) publicvirtualreturns (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);
returntrue;
}
/*//////////////////////////////////////////////////////////////
EIP-2612 LOGIC
//////////////////////////////////////////////////////////////*/functionpermit(address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) publicvirtual{
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);
}
functionDOMAIN_SEPARATOR() publicviewvirtualreturns (bytes32) {
returnblock.chainid== INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
}
functioncomputeDomainSeparator() internalviewvirtualreturns (bytes32) {
returnkeccak256(
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) internalvirtual{
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(addressfrom, uint256 amount) internalvirtual{
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);
}
}
Contract Source Code
File 4 of 17: ERC721.sol
// SPDX-License-Identifier: AGPL-3.0-onlypragmasolidity >=0.8.0;/// @notice Modern, minimalist, and gas efficient ERC-721 implementation./// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC721.sol)abstractcontractERC721{
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/eventTransfer(addressindexedfrom, addressindexed to, uint256indexed id);
eventApproval(addressindexed owner, addressindexed spender, uint256indexed id);
eventApprovalForAll(addressindexed owner, addressindexed operator, bool approved);
/*//////////////////////////////////////////////////////////////
METADATA STORAGE/LOGIC
//////////////////////////////////////////////////////////////*/stringpublic name;
stringpublic symbol;
functiontokenURI(uint256 id) publicviewvirtualreturns (stringmemory);
/*//////////////////////////////////////////////////////////////
ERC721 BALANCE/OWNER STORAGE
//////////////////////////////////////////////////////////////*/mapping(uint256=>address) internal _ownerOf;
mapping(address=>uint256) internal _balanceOf;
functionownerOf(uint256 id) publicviewvirtualreturns (address owner) {
require((owner = _ownerOf[id]) !=address(0), "NOT_MINTED");
}
functionbalanceOf(address owner) publicviewvirtualreturns (uint256) {
require(owner !=address(0), "ZERO_ADDRESS");
return _balanceOf[owner];
}
/*//////////////////////////////////////////////////////////////
ERC721 APPROVAL STORAGE
//////////////////////////////////////////////////////////////*/mapping(uint256=>address) public getApproved;
mapping(address=>mapping(address=>bool)) public isApprovedForAll;
/*//////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/constructor(stringmemory _name, stringmemory _symbol) {
name = _name;
symbol = _symbol;
}
/*//////////////////////////////////////////////////////////////
ERC721 LOGIC
//////////////////////////////////////////////////////////////*/functionapprove(address spender, uint256 id) publicvirtual{
address owner = _ownerOf[id];
require(msg.sender== owner || isApprovedForAll[owner][msg.sender], "NOT_AUTHORIZED");
getApproved[id] = spender;
emit Approval(owner, spender, id);
}
functionsetApprovalForAll(address operator, bool approved) publicvirtual{
isApprovedForAll[msg.sender][operator] = approved;
emit ApprovalForAll(msg.sender, operator, approved);
}
functiontransferFrom(addressfrom,
address to,
uint256 id
) publicvirtual{
require(from== _ownerOf[id], "WRONG_FROM");
require(to !=address(0), "INVALID_RECIPIENT");
require(
msg.sender==from|| isApprovedForAll[from][msg.sender] ||msg.sender== getApproved[id],
"NOT_AUTHORIZED"
);
// Underflow of the sender's balance is impossible because we check for// ownership above and the recipient's balance can't realistically overflow.unchecked {
_balanceOf[from]--;
_balanceOf[to]++;
}
_ownerOf[id] = to;
delete getApproved[id];
emit Transfer(from, to, id);
}
functionsafeTransferFrom(addressfrom,
address to,
uint256 id
) publicvirtual{
transferFrom(from, to, id);
require(
to.code.length==0||
ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, "") ==
ERC721TokenReceiver.onERC721Received.selector,
"UNSAFE_RECIPIENT"
);
}
functionsafeTransferFrom(addressfrom,
address to,
uint256 id,
bytescalldata data
) publicvirtual{
transferFrom(from, to, id);
require(
to.code.length==0||
ERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, data) ==
ERC721TokenReceiver.onERC721Received.selector,
"UNSAFE_RECIPIENT"
);
}
/*//////////////////////////////////////////////////////////////
ERC165 LOGIC
//////////////////////////////////////////////////////////////*/functionsupportsInterface(bytes4 interfaceId) publicviewvirtualreturns (bool) {
return
interfaceId ==0x01ffc9a7||// ERC165 Interface ID for ERC165
interfaceId ==0x80ac58cd||// ERC165 Interface ID for ERC721
interfaceId ==0x5b5e139f; // ERC165 Interface ID for ERC721Metadata
}
/*//////////////////////////////////////////////////////////////
INTERNAL MINT/BURN LOGIC
//////////////////////////////////////////////////////////////*/function_mint(address to, uint256 id) internalvirtual{
require(to !=address(0), "INVALID_RECIPIENT");
require(_ownerOf[id] ==address(0), "ALREADY_MINTED");
// Counter overflow is incredibly unrealistic.unchecked {
_balanceOf[to]++;
}
_ownerOf[id] = to;
emit Transfer(address(0), to, id);
}
function_burn(uint256 id) internalvirtual{
address owner = _ownerOf[id];
require(owner !=address(0), "NOT_MINTED");
// Ownership check above ensures no underflow.unchecked {
_balanceOf[owner]--;
}
delete _ownerOf[id];
delete getApproved[id];
emit Transfer(owner, address(0), id);
}
/*//////////////////////////////////////////////////////////////
INTERNAL SAFE MINT LOGIC
//////////////////////////////////////////////////////////////*/function_safeMint(address to, uint256 id) internalvirtual{
_mint(to, id);
require(
to.code.length==0||
ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, "") ==
ERC721TokenReceiver.onERC721Received.selector,
"UNSAFE_RECIPIENT"
);
}
function_safeMint(address to,
uint256 id,
bytesmemory data
) internalvirtual{
_mint(to, id);
require(
to.code.length==0||
ERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, data) ==
ERC721TokenReceiver.onERC721Received.selector,
"UNSAFE_RECIPIENT"
);
}
}
/// @notice A generic interface for a contract which properly accepts ERC721 tokens./// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC721.sol)abstractcontractERC721TokenReceiver{
functiononERC721Received(address,
address,
uint256,
bytescalldata) externalvirtualreturns (bytes4) {
return ERC721TokenReceiver.onERC721Received.selector;
}
}
Contract Source Code
File 5 of 17: EisenDiamond.sol
// SPDX-License-Identifier: MITpragmasolidity 0.8.19;import {LibDiamond} from"contracts/Libraries/LibDiamond.sol";
import {IDiamondCut} from"contracts/Interfaces/IDiamondCut.sol";
import {LibUtil} from"contracts/Libraries/LibUtil.sol";
import {LibPayments, Recipient, Payer} from"contracts/Libraries/LibPayments.sol";
import {TransferrableOwnership} from"contracts/Helpers/TransferrableOwnership.sol";
import {GenericErrors} from"contracts/Errors/GenericErrors.sol";
/// @title Eisen Diamond/// @author Eisen (https://app.eisenfinance.com)/// @notice Base EIP-2535 Diamond Proxy Contract./// @custom:version 1.0.0contractEisenDiamond{
constructor(address _contractOwner, address _diamondCutFacet, address weth9, address permit2, uint256 fee) payable{
LibDiamond.setContractOwner(_contractOwner);
LibPayments.initializePayment(weth9, permit2, _contractOwner, fee);
// Add the diamondCut external function from the diamondCutFacet
IDiamondCut.FacetCut[] memory cut =new IDiamondCut.FacetCut[](1);
bytes4[] memory functionSelectors =newbytes4[](1);
functionSelectors[0] = IDiamondCut.diamondCut.selector;
cut[0] = IDiamondCut.FacetCut({
facetAddress: _diamondCutFacet,
action: IDiamondCut.FacetCutAction.Add,
functionSelectors: functionSelectors
});
LibDiamond.diamondCut(cut, address(0), "");
}
/// Modifier ///modifieronlyOwner() {
if (msg.sender!= LibDiamond.contractOwner()) {
revert GenericErrors.GenericError(GenericErrors.ONLY_CONTRACT_OWNER);
}
_;
}
/// External Methods ///functionsetFee(uint256 fee) externalonlyOwner{
LibPayments.setFee(fee);
}
functionchangeFeeCollector(address feeCollector) externalonlyOwner{
LibPayments.changeFeeCollector(feeCollector);
}
// Find facet for function that is called and execute the// function if a facet is found and return any value.// solhint-disable-next-line no-complex-fallbackfallback() externalpayable{
LibDiamond.DiamondStorage storage ds;
bytes32 position = LibDiamond.DIAMOND_STORAGE_POSITION;
// get diamond storage// solhint-disable-next-line no-inline-assemblyassembly ("memory-safe") {
ds.slot:= position
}
// get facet from function selectoraddress facet = ds.selectorToFacetAndPosition[msg.sig].facetAddress;
if (facet ==address(0)) {
revert LibDiamond.DiamondError(LibDiamond.FUNCTION_DOES_NOT_EXIST);
}
bytes4 selector =bytes4(msg.sig);
// Execute external function from facet using delegatecall and return any value.// solhint-disable-next-line no-inline-assemblyassembly ("memory-safe") {
// copy function selector and any argumentscalldatacopy(0, 0, calldatasize())
// execute function call using the facetlet result :=delegatecall(gas(), facet, 0, calldatasize(), 0, 0)
// get any return valuereturndatacopy(0, 0, returndatasize())
// return any return value or error back to the callerswitch result
case0 {
// 0xa0d0feeb == function selector of flashloan ["flashloan(address,address[],uint256[],uint256[],bytes)"]ifeq(selector, 0xa0d0feeb) {
// flashloanreturn(0, returndatasize())
}
// Requires success from the function callrevert(0, returndatasize())
}
default {
return(0, returndatasize())
}
}
}
// Able to receive ether// solhint-disable-next-line no-empty-blocksreceive() externalpayable{}
}
// SPDX-License-Identifier: MITpragmasolidity ^0.8.17;/// @title AllowanceTransfer/// @notice Handles ERC20 token permissions through signature based allowance setting and ERC20 token transfers by checking allowed amounts/// @dev Requires user's token approval on the Permit2 contractinterfaceIAllowanceTransfer{
/// @notice Thrown when an allowance on a token has expired./// @param deadline The timestamp at which the allowed amount is no longer validerrorAllowanceExpired(uint256 deadline);
/// @notice Thrown when an allowance on a token has been depleted./// @param amount The maximum amount allowederrorInsufficientAllowance(uint256 amount);
/// @notice Thrown when too many nonces are invalidated.errorExcessiveInvalidation();
/// @notice Emits an event when the owner successfully invalidates an ordered nonce.eventNonceInvalidation(addressindexed owner, addressindexed token, addressindexed spender, uint48 newNonce, uint48 oldNonce
);
/// @notice Emits an event when the owner successfully sets permissions on a token for the spender.eventApproval(addressindexed owner, addressindexed token, addressindexed spender, uint160 amount, uint48 expiration
);
/// @notice Emits an event when the owner successfully sets permissions using a permit signature on a token for the spender.eventPermit(addressindexed owner,
addressindexed token,
addressindexed spender,
uint160 amount,
uint48 expiration,
uint48 nonce
);
/// @notice Emits an event when the owner sets the allowance back to 0 with the lockdown function.eventLockdown(addressindexed owner, address token, address spender);
/// @notice The permit data for a tokenstructPermitDetails {
// ERC20 token addressaddress token;
// the maximum amount allowed to spenduint160 amount;
// timestamp at which a spender's token allowances become invaliduint48 expiration;
// an incrementing value indexed per owner,token,and spender for each signatureuint48 nonce;
}
/// @notice The permit message signed for a single token allowncestructPermitSingle {
// the permit data for a single token alownce
PermitDetails details;
// address permissioned on the allowed tokensaddress spender;
// deadline on the permit signatureuint256 sigDeadline;
}
/// @notice The permit message signed for multiple token allowancesstructPermitBatch {
// the permit data for multiple token allowances
PermitDetails[] details;
// address permissioned on the allowed tokensaddress spender;
// deadline on the permit signatureuint256 sigDeadline;
}
/// @notice The saved permissions/// @dev This info is saved per owner, per token, per spender and all signed over in the permit message/// @dev Setting amount to type(uint160).max sets an unlimited approvalstructPackedAllowance {
// amount alloweduint160 amount;
// permission expiryuint48 expiration;
// an incrementing value indexed per owner,token,and spender for each signatureuint48 nonce;
}
/// @notice A token spender pair.structTokenSpenderPair {
// the token the spender is approvedaddress token;
// the spender addressaddress spender;
}
/// @notice Details for a token transfer.structAllowanceTransferDetails {
// the owner of the tokenaddressfrom;
// the recipient of the tokenaddress to;
// the amount of the tokenuint160 amount;
// the token to be transferredaddress token;
}
/// @notice A mapping from owner address to token address to spender address to PackedAllowance struct, which contains details and conditions of the approval./// @notice The mapping is indexed in the above order see: allowance[ownerAddress][tokenAddress][spenderAddress]/// @dev The packed slot holds the allowed amount, expiration at which the allowed amount is no longer valid, and current nonce thats updated on any signature based approvals.functionallowance(address, address, address) externalviewreturns (uint160, uint48, uint48);
/// @notice Approves the spender to use up to amount of the specified token up until the expiration/// @param token The token to approve/// @param spender The spender address to approve/// @param amount The approved amount of the token/// @param expiration The timestamp at which the approval is no longer valid/// @dev The packed allowance also holds a nonce, which will stay unchanged in approve/// @dev Setting amount to type(uint160).max sets an unlimited approvalfunctionapprove(address token, address spender, uint160 amount, uint48 expiration) external;
/// @notice Permit a spender to a given amount of the owners token via the owner's EIP-712 signature/// @dev May fail if the owner's nonce was invalidated in-flight by invalidateNonce/// @param owner The owner of the tokens being approved/// @param permitSingle Data signed over by the owner specifying the terms of approval/// @param signature The owner's signature over the permit datafunctionpermit(address owner, PermitSingle memory permitSingle, bytescalldata signature) external;
/// @notice Permit a spender to the signed amounts of the owners tokens via the owner's EIP-712 signature/// @dev May fail if the owner's nonce was invalidated in-flight by invalidateNonce/// @param owner The owner of the tokens being approved/// @param permitBatch Data signed over by the owner specifying the terms of approval/// @param signature The owner's signature over the permit datafunctionpermit(address owner, PermitBatch memory permitBatch, bytescalldata signature) external;
/// @notice Transfer approved tokens from one address to another/// @param from The address to transfer from/// @param to The address of the recipient/// @param amount The amount of the token to transfer/// @param token The token address to transfer/// @dev Requires the from address to have approved at least the desired amount/// of tokens to msg.sender.functiontransferFrom(addressfrom, address to, uint160 amount, address token) external;
/// @notice Transfer approved tokens in a batch/// @param transferDetails Array of owners, recipients, amounts, and tokens for the transfers/// @dev Requires the from addresses to have approved at least the desired amount/// of tokens to msg.sender.functiontransferFrom(AllowanceTransferDetails[] calldata transferDetails) external;
/// @notice Enables performing a "lockdown" of the sender's Permit2 identity/// by batch revoking approvals/// @param approvals Array of approvals to revoke.functionlockdown(TokenSpenderPair[] calldata approvals) external;
/// @notice Invalidate nonces for a given (token, spender) pair/// @param token The token to invalidate nonces for/// @param spender The spender to invalidate nonces for/// @param newNonce The new nonce to set. Invalidates all nonces less than it./// @dev Can't invalidate more than 2**16 nonces per transaction.functioninvalidateNonces(address token, address spender, uint48 newNonce) external;
}
Contract Source Code
File 8 of 17: IDiamondCut.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.0;/******************************************************************************\
* Author: Nick Mudge <nick@perfectabstractions.com> (https://twitter.com/mudgen)
* EIP-2535 Diamonds: https://eips.ethereum.org/EIPS/eip-2535
/******************************************************************************/interfaceIDiamondCut{
enumFacetCutAction {
Add,
Replace,
Remove
}
// Add=0, Replace=1, Remove=2structFacetCut {
address facetAddress;
FacetCutAction action;
bytes4[] functionSelectors;
}
/// @notice Add/replace/remove any number of functions and optionally execute/// a function with delegatecall/// @param _diamondCut Contains the facet addresses and function selectors/// @param _init The address of the contract or facet to execute _calldata/// @param _calldata A function call, including function selector and arguments/// _calldata is executed with delegatecall on _initfunctiondiamondCut(FacetCut[] calldata _diamondCut, address _init, bytescalldata _calldata) external;
eventDiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata);
}
// SPDX-License-Identifier: MITpragmasolidity 0.8.19;libraryLibBytes{
// solhint-disable no-inline-assembly// LibBytes specific errorserrorSliceOverflow();
errorSliceOutOfBounds();
errorAddressOutOfBounds();
bytes16privateconstant _SYMBOLS ="0123456789abcdef";
// -------------------------functionslice(bytesmemory _bytes, uint256 _start, uint256 _length) internalpurereturns (bytesmemory) {
if (_length +31< _length) revert SliceOverflow();
if (_bytes.length< _start + _length) revert SliceOutOfBounds();
bytesmemory tempBytes;
assembly ("memory-safe") {
switchiszero(_length)
case0 {
// Get a location of some free memory and store it in tempBytes as// Solidity does for memory variables.
tempBytes :=mload(0x40)
// The first word of the slice result is potentially a partial// word read from the original array. To read it, we calculate// the length of that partial word and start copying that many// bytes into the array. The first word we copy will start with// data we don't care about, but the last `lengthmod` bytes will// land at the beginning of the contents of the new array. When// we're done copying, we overwrite the full first word with// the actual length of the slice.let lengthmod :=and(_length, 31)
// The multiplication in the next line is necessary// because when slicing multiples of 32 bytes (lengthmod == 0)// the following copy loop was copying the origin's length// and then ending prematurely not copying everything it should.let mc :=add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod)))
let end :=add(mc, _length)
for {
// The multiplication in the next line has the same exact purpose// as the one above.let cc :=add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start)
} lt(mc, end) {
mc :=add(mc, 0x20)
cc :=add(cc, 0x20)
} {
mstore(mc, mload(cc))
}
mstore(tempBytes, _length)
//update free-memory pointer//allocating the array padded to 32 bytes like the compiler does nowmstore(0x40, and(add(mc, 31), not(31)))
}
//if we want a zero-length slice let's just return a zero-length arraydefault {
tempBytes :=mload(0x40)
//zero out the 32 bytes slice we are about to return//we need to do it because Solidity does not garbage collectmstore(tempBytes, 0)
mstore(0x40, add(tempBytes, 0x20))
}
}
return tempBytes;
}
functiontoAddress(bytesmemory _bytes, uint256 _start) internalpurereturns (address) {
if (_bytes.length< _start +20) {
revert AddressOutOfBounds();
}
address tempAddress;
assembly ("memory-safe") {
tempAddress :=div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000)
}
return tempAddress;
}
/// Copied from OpenZeppelin's `Strings.sol` utility library./// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/8335676b0e99944eef6a742e16dcd9ff6e68e609/contracts/utils/Strings.solfunctiontoHexString(uint256 value, uint256 length) internalpurereturns (stringmemory) {
bytesmemory buffer =newbytes(2* length +2);
buffer[0] ="0";
buffer[1] ="x";
for (uint256 i =2* length +1; i >1; --i) {
buffer[i] = _SYMBOLS[value &0xf];
value >>=4;
}
require(value ==0, "Strings: hex length insufficient");
returnstring(buffer);
}
}
Contract Source Code
File 11 of 17: LibDiamond.sol
// SPDX-License-Identifier: MITpragmasolidity 0.8.19;import {IDiamondCut} from"contracts/Interfaces/IDiamondCut.sol";
import {LibUtil} from"contracts/Libraries/LibUtil.sol";
import {GenericErrors} from"contracts/Errors/GenericErrors.sol";
/// Implementation of EIP-2535 Diamond Standard/// https://eips.ethereum.org/EIPS/eip-2535libraryLibDiamond{
bytes32internalconstant DIAMOND_STORAGE_POSITION =keccak256("diamond.standard.diamond.storage");
// Diamond specific errorserrorDiamondError(uint256 errorCode);
uint256internalconstant INCORRECT_FACET_CUT_ACTION =1;
uint256internalconstant NO_SELECTORS_IN_FACE =2;
uint256internalconstant FUNCTION_ALREADY_EXISTS =3;
uint256internalconstant FACET_ADDRESS_IS_ZERO =4;
uint256internalconstant FACET_ADDRESS_IS_NOT_ZERO =5;
uint256internalconstant FACET_CONTAINS_NO_CODE =6;
uint256internalconstant FUNCTION_DOES_NOT_EXIST =7;
uint256internalconstant FUNCTION_IS_IMMUTABLE =8;
uint256internalconstant INIT_ZERO_BUT_CALLDATA_NOT_EMPTY =9;
uint256internalconstant CALLDATA_EMPTY_BUT_INIT_NOT_ZERO =10;
uint256internalconstant INIT_REVERTED =11;
// ----------------structFacetAddressAndPosition {
address facetAddress;
uint96 functionSelectorPosition; // position in facetFunctionSelectors.functionSelectors array
}
structFacetFunctionSelectors {
bytes4[] functionSelectors;
uint256 facetAddressPosition; // position of facetAddress in facetAddresses array
}
structDiamondStorage {
// maps function selector to the facet address and// the position of the selector in the facetFunctionSelectors.selectors arraymapping(bytes4=> FacetAddressAndPosition) selectorToFacetAndPosition;
// maps facet addresses to function selectorsmapping(address=> FacetFunctionSelectors) facetFunctionSelectors;
// facet addressesaddress[] facetAddresses;
// Used to query if a contract implements an interface.// Used to implement ERC-165.mapping(bytes4=>bool) supportedInterfaces;
// owner of the contractaddress contractOwner;
}
functiondiamondStorage() internalpurereturns (DiamondStorage storage ds) {
bytes32 position = DIAMOND_STORAGE_POSITION;
// solhint-disable-next-line no-inline-assemblyassembly ("memory-safe") {
ds.slot:= position
}
}
eventOwnershipTransferred(addressindexed previousOwner, addressindexed newOwner);
functionsetContractOwner(address _newOwner) internal{
DiamondStorage storage ds = diamondStorage();
address previousOwner = ds.contractOwner;
ds.contractOwner = _newOwner;
emit OwnershipTransferred(previousOwner, _newOwner);
}
functioncontractOwner() internalviewreturns (address contractOwner_) {
contractOwner_ = diamondStorage().contractOwner;
}
functionenforceIsContractOwner() internalview{
if (msg.sender!= diamondStorage().contractOwner)
revert GenericErrors.GenericError(GenericErrors.ONLY_CONTRACT_OWNER);
}
eventDiamondCut(IDiamondCut.FacetCut[] _diamondCut, address _init, bytes _calldata);
// Internal function version of diamondCutfunctiondiamondCut(IDiamondCut.FacetCut[] memory _diamondCut, address _init, bytesmemory _calldata) internal{
for (uint256 facetIndex; facetIndex < _diamondCut.length; ) {
IDiamondCut.FacetCutAction action = _diamondCut[facetIndex].action;
if (action == IDiamondCut.FacetCutAction.Add) {
addFunctions(_diamondCut[facetIndex].facetAddress, _diamondCut[facetIndex].functionSelectors);
} elseif (action == IDiamondCut.FacetCutAction.Replace) {
replaceFunctions(_diamondCut[facetIndex].facetAddress, _diamondCut[facetIndex].functionSelectors);
} elseif (action == IDiamondCut.FacetCutAction.Remove) {
removeFunctions(_diamondCut[facetIndex].facetAddress, _diamondCut[facetIndex].functionSelectors);
} else {
revert DiamondError(INCORRECT_FACET_CUT_ACTION);
}
unchecked {
++facetIndex;
}
}
emit DiamondCut(_diamondCut, _init, _calldata);
initializeDiamondCut(_init, _calldata);
}
functionaddFunctions(address _facetAddress, bytes4[] memory _functionSelectors) internal{
if (_functionSelectors.length==0) {
revert DiamondError(NO_SELECTORS_IN_FACE);
}
DiamondStorage storage ds = diamondStorage();
if (LibUtil.isZeroAddress(_facetAddress)) {
revert DiamondError(FACET_ADDRESS_IS_ZERO);
}
uint96 selectorPosition =uint96(ds.facetFunctionSelectors[_facetAddress].functionSelectors.length);
// add new facet address if it does not existif (selectorPosition ==0) {
addFacet(ds, _facetAddress);
}
for (uint256 selectorIndex; selectorIndex < _functionSelectors.length; ) {
bytes4 selector = _functionSelectors[selectorIndex];
address oldFacetAddress = ds.selectorToFacetAndPosition[selector].facetAddress;
if (!LibUtil.isZeroAddress(oldFacetAddress)) {
revert DiamondError(FUNCTION_ALREADY_EXISTS);
}
addFunction(ds, selector, selectorPosition, _facetAddress);
unchecked {
++selectorPosition;
++selectorIndex;
}
}
}
functionreplaceFunctions(address _facetAddress, bytes4[] memory _functionSelectors) internal{
if (_functionSelectors.length==0) {
revert DiamondError(NO_SELECTORS_IN_FACE);
}
DiamondStorage storage ds = diamondStorage();
if (LibUtil.isZeroAddress(_facetAddress)) {
revert DiamondError(FACET_ADDRESS_IS_ZERO);
}
uint96 selectorPosition =uint96(ds.facetFunctionSelectors[_facetAddress].functionSelectors.length);
// add new facet address if it does not existif (selectorPosition ==0) {
addFacet(ds, _facetAddress);
}
for (uint256 selectorIndex; selectorIndex < _functionSelectors.length; ) {
bytes4 selector = _functionSelectors[selectorIndex];
address oldFacetAddress = ds.selectorToFacetAndPosition[selector].facetAddress;
if (oldFacetAddress == _facetAddress) {
revert DiamondError(FUNCTION_ALREADY_EXISTS);
}
removeFunction(ds, oldFacetAddress, selector);
addFunction(ds, selector, selectorPosition, _facetAddress);
unchecked {
++selectorPosition;
++selectorIndex;
}
}
}
functionremoveFunctions(address _facetAddress, bytes4[] memory _functionSelectors) internal{
if (_functionSelectors.length==0) {
revert DiamondError(NO_SELECTORS_IN_FACE);
}
DiamondStorage storage ds = diamondStorage();
// if function does not exist then do nothing and returnif (!LibUtil.isZeroAddress(_facetAddress)) {
revert DiamondError(FACET_ADDRESS_IS_NOT_ZERO);
}
for (uint256 selectorIndex; selectorIndex < _functionSelectors.length; ) {
bytes4 selector = _functionSelectors[selectorIndex];
address oldFacetAddress = ds.selectorToFacetAndPosition[selector].facetAddress;
removeFunction(ds, oldFacetAddress, selector);
unchecked {
++selectorIndex;
}
}
}
functionaddFacet(DiamondStorage storage ds, address _facetAddress) internal{
enforceHasContractCode(_facetAddress);
ds.facetFunctionSelectors[_facetAddress].facetAddressPosition = ds.facetAddresses.length;
ds.facetAddresses.push(_facetAddress);
}
functionaddFunction(
DiamondStorage storage ds,
bytes4 _selector,
uint96 _selectorPosition,
address _facetAddress
) internal{
ds.selectorToFacetAndPosition[_selector].functionSelectorPosition = _selectorPosition;
ds.facetFunctionSelectors[_facetAddress].functionSelectors.push(_selector);
ds.selectorToFacetAndPosition[_selector].facetAddress = _facetAddress;
}
functionremoveFunction(DiamondStorage storage ds, address _facetAddress, bytes4 _selector) internal{
if (LibUtil.isZeroAddress(_facetAddress)) {
revert DiamondError(FUNCTION_DOES_NOT_EXIST);
}
// an immutable function is a function defined directly in a diamondif (_facetAddress ==address(this)) {
revert DiamondError(FUNCTION_IS_IMMUTABLE);
}
// replace selector with last selector, then delete last selectoruint256 selectorPosition = ds.selectorToFacetAndPosition[_selector].functionSelectorPosition;
uint256 lastSelectorPosition = ds.facetFunctionSelectors[_facetAddress].functionSelectors.length-1;
// if not the same then replace _selector with lastSelectorif (selectorPosition != lastSelectorPosition) {
bytes4 lastSelector = ds.facetFunctionSelectors[_facetAddress].functionSelectors[lastSelectorPosition];
ds.facetFunctionSelectors[_facetAddress].functionSelectors[selectorPosition] = lastSelector;
ds.selectorToFacetAndPosition[lastSelector].functionSelectorPosition =uint96(selectorPosition);
}
// delete the last selector
ds.facetFunctionSelectors[_facetAddress].functionSelectors.pop();
delete ds.selectorToFacetAndPosition[_selector];
// if no more selectors for facet address then delete the facet addressif (lastSelectorPosition ==0) {
// replace facet address with last facet address and delete last facet addressuint256 lastFacetAddressPosition = ds.facetAddresses.length-1;
uint256 facetAddressPosition = ds.facetFunctionSelectors[_facetAddress].facetAddressPosition;
if (facetAddressPosition != lastFacetAddressPosition) {
address lastFacetAddress = ds.facetAddresses[lastFacetAddressPosition];
ds.facetAddresses[facetAddressPosition] = lastFacetAddress;
ds.facetFunctionSelectors[lastFacetAddress].facetAddressPosition = facetAddressPosition;
}
ds.facetAddresses.pop();
delete ds.facetFunctionSelectors[_facetAddress].facetAddressPosition;
}
}
functioninitializeDiamondCut(address _init, bytesmemory _calldata) internal{
if (LibUtil.isZeroAddress(_init)) {
if (_calldata.length!=0) {
revert DiamondError(INIT_ZERO_BUT_CALLDATA_NOT_EMPTY);
}
} else {
if (_calldata.length==0) {
revert DiamondError(CALLDATA_EMPTY_BUT_INIT_NOT_ZERO);
}
if (_init !=address(this)) {
enforceHasContractCode(_init);
}
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytesmemoryerror) = _init.delegatecall(_calldata);
if (!success) {
if (error.length>0) {
// bubble up error/// @solidity memory-safe-assemblyassembly ("memory-safe") {
let returndata_size :=mload(error)
revert(add(32, error), returndata_size)
}
} else {
revert DiamondError(INIT_REVERTED);
}
}
}
}
functionenforceHasContractCode(address _contract) internalview{
uint256 contractSize;
// solhint-disable-next-line no-inline-assemblyassembly ("memory-safe") {
contractSize :=extcodesize(_contract)
}
if (contractSize ==0) {
revert DiamondError(FACET_CONTAINS_NO_CODE);
}
}
}
Contract Source Code
File 12 of 17: LibPayments.sol
// SPDX-License-Identifier: MITpragmasolidity 0.8.19;import {GenericErrors} from"contracts/Errors/GenericErrors.sol";
import {SafeCast160} from"permit2/src/libraries/SafeCast160.sol";
import {SafeTransferLib} from"contracts/Libraries/SafeTransferLib.sol";
import {Constants} from"contracts/Libraries/Constants.sol";
import {ERC20} from"solmate/tokens/ERC20.sol";
import {ERC721} from"solmate/tokens/ERC721.sol";
import {ERC1155} from"solmate/tokens/ERC1155.sol";
import {IWETH9} from"contracts/Interfaces/IWETH9.sol";
import {IAllowanceTransfer} from"permit2/src/interfaces/IAllowanceTransfer.sol";
import {UniERC20} from"contracts/Libraries/UniERC20.sol";
type Recipient isaddress;
type Payer isaddress;
/// @title Payments Library/// @author Eisen (https://app.eisenfinance.com)/// @notice Provides functionality for transferring assetslibraryLibPayments{
usingSafeTransferLibforaddress;
usingSafeCast160foruint256;
usingUniERC20foraddress;
/// Types ///bytes32internalconstant NAMESPACE =bytes32(0x6a2b33915c87ebbd2e7a47520fe4aaa6f0e18ef6bdebd64915d7aeced08d447e);
// bytes32(uint256(bytes32(keccak256("com.eisen.library.payments")))-1);uint256internalconstant FEE_BIPS_BASE =10_000;
/// Storage ///structPaymentStorage {
uint256 fee;
address feeCollector;
address WETH9;
address PERMIT2;
bool initialized;
}
/// Events ///eventFeeChanged(uint256 feeOld, uint256 feeNew);
eventFeeCollectorChanged(address feeCollectorOld, address feeCollectorNew);
eventPaymentInitialized(addressindexed weth9, addressindexed permit2, addressindexed feeCollector, uint256 fee);
/// @dev Fetch local storagefunctionpaymentStorage() internalpurereturns (PaymentStorage storage payStor) {
bytes32 position = NAMESPACE;
// solhint-disable-next-line no-inline-assemblyassembly ("memory-safe") {
payStor.slot:= position
}
}
/// @notice Set fee bips/// @param fee fee portion in bipsfunctionsetFee(uint256 fee) internal{
PaymentStorage storage payStor = paymentStorage();
if (fee > FEE_BIPS_BASE /10) {
revert GenericErrors.GenericError(GenericErrors.INVALID_FEE_AMOUNT);
}
emit FeeChanged(payStor.fee, fee);
payStor.fee = fee;
}
/// @notice Set fee collector/// @param feeCollector The address of feeCollectorfunctionchangeFeeCollector(address feeCollector) internal{
PaymentStorage storage payStor = paymentStorage();
emit FeeCollectorChanged(payStor.feeCollector, feeCollector);
payStor.feeCollector = feeCollector;
}
/// @notice Initializes parameters for transferring assets/// @param weth9 The address of wrapped native token/// @param permit2 The address of permit2 contractfunctioninitializePayment(address weth9, address permit2, address feeCollector, uint256 fee) internal{
PaymentStorage storage payStor = paymentStorage();
if (!payStor.initialized) {
payStor.WETH9 = weth9;
payStor.PERMIT2 = permit2;
payStor.feeCollector = feeCollector;
payStor.fee = fee;
payStor.initialized =true;
} else {
revert GenericErrors.GenericError(GenericErrors.ALREADY_INITIALIZED);
}
emit PaymentInitialized(weth9, permit2, feeCollector, fee);
}
/// @notice Gets the address of wrapped native token/// @return The address of wrapped native tokenfunctionWETH() internalviewreturns (address) {
return paymentStorage().WETH9;
}
/// @notice Calculates the recipient address for a command/// @param recipient The recipient or recipient-flag for the command/// @return outRecipient The resultant recipient for the commandfunctionmap(Recipient recipient) internalviewreturns (Recipient outRecipient) {
if (Recipient.unwrap(recipient) == Constants.MSG_SENDER) {
outRecipient = Recipient.wrap(msg.sender);
} elseif (Recipient.unwrap(recipient) == Constants.ADDRESS_THIS) {
outRecipient = Recipient.wrap(address(this));
} elseif (UniERC20.isETH(Recipient.unwrap(recipient))) {
// ETH is a special case, it is not a valid recipient(address(0))
outRecipient = Recipient.wrap(msg.sender);
} else {
outRecipient = recipient;
}
}
/// @notice Calculates the payer address for a command/// @param payer The payer-flag for the command/// @return outPayer The resultant payer for the commandfunctionpayerMap(Payer payer) internalviewreturns (Payer outPayer) {
if (Payer.unwrap(payer) == Constants.MSG_SENDER) {
outPayer = Payer.wrap(msg.sender);
} elseif (Payer.unwrap(payer) == Constants.ADDRESS_THIS) {
outPayer = Payer.wrap(address(this));
} else {
revert GenericErrors.GenericError(GenericErrors.INVALID_PARAMS);
}
}
/// @notice Pays an amount of ETH or ERC20 to a recipient/// @param token The token to pay (can be ETH using Constants.ETH)/// @param recipient The Recipient that will receive the payment/// @param value The amount to payfunctionpay(address token, Recipient recipient, uint256 value) internal{
if (Recipient.unwrap(recipient) ==address(this)) return;
if (token.isETH()) {
Recipient.unwrap(recipient).safeTransferETH(value);
} else {
if (value == Constants.CONTRACT_BALANCE) {
value = token.balanceOf(address(this));
}
token.safeTransfer(Recipient.unwrap(recipient), value);
}
}
/// @notice Pays a proportion of the contract's ETH or ERC20 to a recipient/// @param token The token to pay (can be ETH using Constants.ETH)/// @param recipient The Recipient that will receive payment/// @param bips Portion in bips of whole balance of the contractfunctionpayPortion(address token, Recipient recipient, uint256 bips) internal{
if (bips ==0|| bips >10_000) revert GenericErrors.GenericError(GenericErrors.INVALID_BIPS);
if (token.isETH()) {
uint256 balance =address(this).balance;
uint256 amount = (balance * bips) / FEE_BIPS_BASE;
Recipient.unwrap(recipient).safeTransferETH(amount);
} else {
uint256 balance = ERC20(token).balanceOf(address(this));
uint256 amount = (balance * bips) / FEE_BIPS_BASE;
// pay with tokens already in the contract (for the exact input multihop case)
token.safeTransfer(Recipient.unwrap(recipient), amount);
}
}
/// @notice Sweeps all of the contract's ERC20 or ETH to an address/// @param token The token to sweep (can be ETH using Constants.ETH)/// @param recipient The address that will receive payment/// @param amountMinimum The minimum desired amountfunctionsweep(address token, Recipient recipient, uint256 amountMinimum) internal{
uint256 balance;
if (token.isETH()) {
balance =address(this).balance;
if (balance < amountMinimum) revert GenericErrors.GenericError(GenericErrors.INSUFFICIENT_VALUE);
if (balance >0) Recipient.unwrap(recipient).safeTransferETH(balance);
} else {
balance = token.balanceOf(address(this));
if (balance < amountMinimum) revert GenericErrors.GenericError(GenericErrors.INSUFFICIENT_TOKEN);
if (balance >0) token.safeTransfer(Recipient.unwrap(recipient), balance);
}
}
/// @notice Sweeps an ERC721 to a recipient from the contract/// @param token The ERC721 token to sweep/// @param recipient The address that will receive payment/// @param id The ID of the ERC721 to sweepfunctionsweepERC721(address token, Recipient recipient, uint256 id) internal{
ERC721(token).safeTransferFrom(address(this), Recipient.unwrap(recipient), id);
}
/// @notice Sweeps all of the contract's ERC1155 to an address/// @param token The ERC1155 token to sweep/// @param recipient The address that will receive payment/// @param id The ID of the ERC1155 to sweep/// @param amountMinimum The minimum desired amountfunctionsweepERC1155(address token, Recipient recipient, uint256 id, uint256 amountMinimum) internal{
uint256 balance = ERC1155(token).balanceOf(address(this), id);
if (balance < amountMinimum) revert GenericErrors.GenericError(GenericErrors.INSUFFICIENT_TOKEN);
ERC1155(token).safeTransferFrom(address(this), Recipient.unwrap(recipient), id, balance, bytes(""));
}
/// @notice Wraps an amount of ETH into WETH/// @param recipient The recipient of the WETH/// @param amount The amount to wrap (can be CONTRACT_BALANCE)functionwrapETH(Recipient recipient, uint256 amount) internalreturns (uint256 amountOut) {
if (amount == Constants.CONTRACT_BALANCE) {
amount =address(this).balance;
} elseif (amount >address(this).balance) {
revert GenericErrors.GenericError(GenericErrors.INSUFFICIENT_VALUE);
}
if (amount >0) {
PaymentStorage storage ps = paymentStorage();
IWETH9(ps.WETH9).deposit{value: amount}();
if (Recipient.unwrap(recipient) !=address(this)) {
IWETH9(ps.WETH9).transfer(Recipient.unwrap(recipient), amount);
}
}
amountOut = amount;
}
/// @notice Unwraps the amount of the contract's WETH into ETH/// @param recipient The recipient of the ETH/// @param amount The minimum amount of ETH desiredfunctionunwrapWETH9(Recipient recipient, uint256 amount) internalreturns (uint256 amountOut) {
PaymentStorage storage ps = paymentStorage();
if (IWETH9(ps.WETH9).balanceOf(address(this)) < amount) {
revert GenericErrors.GenericError(GenericErrors.INSUFFICIENT_VALUE);
}
IWETH9(ps.WETH9).withdraw(amount);
if (Recipient.unwrap(recipient) !=address(this)) {
Recipient.unwrap(recipient).safeTransferETH(amount);
}
amountOut = amount;
}
/// @notice Performs a approve function on Permit2/// @param token The token address/// @param spender The spender addressfunctionapproveMax(address token, address spender, uint256 amount) internal{
PaymentStorage storage ps = paymentStorage();
(uint256 allowance, , ) = IAllowanceTransfer(ps.PERMIT2).allowance(address(this), token, spender);
if (allowance < amount) {
IAllowanceTransfer(ps.PERMIT2).approve(token, spender, type(uint160).max, type(uint48).max);
}
}
/// @notice Performs a approve function on Permit2/// @param token The token address/// @param spender The spender addressfunctionapproveWithOutExpiration(address token, address spender, uint256 amount) internal{
PaymentStorage storage ps = paymentStorage();
IAllowanceTransfer(ps.PERMIT2).approve(token, spender, amount.toUint160(), type(uint48).max);
}
/// @notice Performs a permit function on Permit2/// @param owner The token owner address/// @param permitSingle A single of permit description/// @param signature A single of permit data with signaturefunctionpermit(address owner,
IAllowanceTransfer.PermitSingle memory permitSingle,
bytesmemory signature
) internal{
PaymentStorage storage ps = paymentStorage();
IAllowanceTransfer(ps.PERMIT2).permit(owner, permitSingle, signature);
}
/// @notice Performs a batch permit function on Permit2/// @param owner The token owner address/// @param permitBatch A batch of permit descriptions/// @param signature A batch of permit data with signaturefunctionpermit(address owner, IAllowanceTransfer.PermitBatch memory permitBatch, bytesmemory signature) internal{
PaymentStorage storage ps = paymentStorage();
IAllowanceTransfer(ps.PERMIT2).permit(owner, permitBatch, signature);
}
/// @notice Performs a transferFrom on Permit2/// @param token The token to transfer/// @param from The address to transfer from/// @param to The recipient of the transfer/// @param amount The amount to transferfunctionpermit2TransferFrom(address token, addressfrom, address to, uint160 amount) internal{
_permit2TransferFrom(token, from, to, amount);
}
/// @notice Performs a batch transferFrom on Permit2/// @param batchDetails An array detailing each of the transfers that should occurfunctionpermit2TransferFrom(IAllowanceTransfer.AllowanceTransferDetails[] memory batchDetails) internal{
address owner =msg.sender;
uint256 batchLength = batchDetails.length;
PaymentStorage storage ps = paymentStorage();
for (uint256 i =0; i < batchLength; ++i) {
if (batchDetails[i].from != owner) revert GenericErrors.GenericError(GenericErrors.FROM_ADDR_IS_NOT_OWNER);
}
for (uint256 i =0; i < batchLength; ++i) {
_permit2TransferFrom(
batchDetails[i].token,
batchDetails[i].from,
batchDetails[i].to,
batchDetails[i].amount
);
}
}
/// @notice Either performs a regular payment or transferFrom on Permit2, depending on the payer address/// @param token The token to transfer/// @param payer The address to pay for the transfer/// @param recipient The recipient of the transfer/// @param amount The amount to transferfunctionpayOrPermit2Transfer(address token, Payer payer, Recipient recipient, uint256 amount) internal{
if (Payer.unwrap(payer) ==address(this)) pay(token, recipient, amount);
else payFrom(token, payer, recipient, amount.toUint160());
}
/// @notice Performs a transferFrom on Permit2/// @param token The token to transfer/// @param payer The address to pay for the transfer/// @param recipient The recipient of the transfer/// @param amount The amount to transferfunctionpayFrom(address token, Payer payer, Recipient recipient, uint256 amount) internal{
if (Payer.unwrap(payer) == Recipient.unwrap(recipient)) return;
if (!token.isETH()) {
_permit2TransferFrom(token, Payer.unwrap(payer), Recipient.unwrap(recipient), amount.toUint160());
}
}
/// @notice Performs a transferFrom on Permit2 internally/// @param token The token to transfer/// @param from The address to transfer from/// @param to The recipient of the transfer/// @param amount The amount to transferfunction_permit2TransferFrom(address token, addressfrom, address to, uint160 amount) internal{
if (from== to) return;
PaymentStorage storage ps = paymentStorage();
IAllowanceTransfer(ps.PERMIT2).transferFrom(from, to, amount, token);
}
}
Contract Source Code
File 13 of 17: LibUtil.sol
// SPDX-License-Identifier: MITpragmasolidity 0.8.19;import"./LibBytes.sol";
libraryLibUtil{
usingLibBytesforbytes;
functiongetRevertMsg(bytesmemory _res) internalpurereturns (stringmemory) {
// If the _res length is less than 68, then the transaction failed silently (without a revert message)if (_res.length<68) return"Transaction reverted silently";
bytesmemory revertData = _res.slice(4, _res.length-4); // Remove the selector which is the first 4 bytesreturnabi.decode(revertData, (string)); // All that remains is the revert string
}
/// @notice Determines whether the given address is the zero address/// @param addr The address to verify/// @return Boolean indicating if the address is the zero addressfunctionisZeroAddress(address addr) internalpurereturns (bool) {
return addr ==address(0);
}
}
Contract Source Code
File 14 of 17: SafeCast160.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.17;librarySafeCast160{
/// @notice Thrown when a valude greater than type(uint160).max is cast to uint160errorUnsafeCast();
/// @notice Safely casts uint256 to uint160/// @param value The uint256 to be castfunctiontoUint160(uint256 value) internalpurereturns (uint160) {
if (value >type(uint160).max) revert UnsafeCast();
returnuint160(value);
}
}
Contract Source Code
File 15 of 17: SafeTransferLib.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.4;/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values./// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SafeTransferLib.sol)/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)////// @dev Note:/// - For ETH transfers, please use `forceSafeTransferETH` for DoS protection./// - For ERC20s, this implementation won't check that a token has code,/// responsibility is delegated to the caller.librarySafeTransferLib{
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*//* CUSTOM ERRORS *//*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*//// @dev The ETH transfer has failed.errorETHTransferFailed();
/// @dev The ERC20 `transferFrom` has failed.errorTransferFromFailed();
/// @dev The ERC20 `transfer` has failed.errorTransferFailed();
/// @dev The ERC20 `approve` has failed.errorApproveFailed();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*//* CONSTANTS *//*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*//// @dev Suggested gas stipend for contract receiving ETH that disallows any storage writes.uint256internalconstant GAS_STIPEND_NO_STORAGE_WRITES =2300;
/// @dev Suggested gas stipend for contract receiving ETH to perform a few/// storage reads and writes, but low enough to prevent griefing.uint256internalconstant GAS_STIPEND_NO_GRIEF =100000;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*//* ETH OPERATIONS *//*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/// If the ETH transfer MUST succeed with a reasonable gas budget, use the force variants.//// The regular variants:// - Forwards all remaining gas to the target.// - Reverts if the target reverts.// - Reverts if the current contract has insufficient balance.//// The force variants:// - Forwards with an optional gas stipend// (defaults to `GAS_STIPEND_NO_GRIEF`, which is sufficient for most cases).// - If the target reverts, or if the gas stipend is exhausted,// creates a temporary contract to force send the ETH via `SELFDESTRUCT`.// Future compatible with `SENDALL`: https://eips.ethereum.org/EIPS/eip-4758.// - Reverts if the current contract has insufficient balance.//// The try variants:// - Forwards with a mandatory gas stipend.// - Instead of reverting, returns whether the transfer succeeded./// @dev Sends `amount` (in wei) ETH to `to`.functionsafeTransferETH(address to, uint256 amount) internal{
/// @solidity memory-safe-assemblyassembly {
ifiszero(call(gas(), to, amount, gas(), 0x00, gas(), 0x00)) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.revert(0x1c, 0x04)
}
}
}
/// @dev Sends all the ETH in the current contract to `to`.functionsafeTransferAllETH(address to) internal{
/// @solidity memory-safe-assemblyassembly {
// Transfer all the ETH and check if it succeeded or not.ifiszero(call(gas(), to, selfbalance(), gas(), 0x00, gas(), 0x00)) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.revert(0x1c, 0x04)
}
}
}
/// @dev Force sends `amount` (in wei) ETH to `to`, with a `gasStipend`.functionforceSafeTransferETH(address to, uint256 amount, uint256 gasStipend) internal{
/// @solidity memory-safe-assemblyassembly {
iflt(selfbalance(), amount) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.revert(0x1c, 0x04)
}
ifiszero(call(gasStipend, to, amount, gas(), 0x00, gas(), 0x00)) {
mstore(0x00, to) // Store the address in scratch space.mstore8(0x0b, 0x73) // Opcode `PUSH20`.mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.ifiszero(create(amount, 0x0b, 0x16)) {
returndatacopy(gas(), returndatasize(), shr(20, gas())) // For gas estimation.
}
}
}
}
/// @dev Force sends all the ETH in the current contract to `to`, with a `gasStipend`.functionforceSafeTransferAllETH(address to, uint256 gasStipend) internal{
/// @solidity memory-safe-assemblyassembly {
ifiszero(call(gasStipend, to, selfbalance(), gas(), 0x00, gas(), 0x00)) {
mstore(0x00, to) // Store the address in scratch space.mstore8(0x0b, 0x73) // Opcode `PUSH20`.mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.ifiszero(create(selfbalance(), 0x0b, 0x16)) {
returndatacopy(gas(), returndatasize(), shr(20, gas())) // For gas estimation.
}
}
}
}
/// @dev Force sends `amount` (in wei) ETH to `to`, with `GAS_STIPEND_NO_GRIEF`.functionforceSafeTransferETH(address to, uint256 amount) internal{
/// @solidity memory-safe-assemblyassembly {
iflt(selfbalance(), amount) {
mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.revert(0x1c, 0x04)
}
ifiszero(call(GAS_STIPEND_NO_GRIEF, to, amount, gas(), 0x00, gas(), 0x00)) {
mstore(0x00, to) // Store the address in scratch space.mstore8(0x0b, 0x73) // Opcode `PUSH20`.mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.ifiszero(create(amount, 0x0b, 0x16)) {
returndatacopy(gas(), returndatasize(), shr(20, gas())) // For gas estimation.
}
}
}
}
/// @dev Force sends all the ETH in the current contract to `to`, with `GAS_STIPEND_NO_GRIEF`.functionforceSafeTransferAllETH(address to) internal{
/// @solidity memory-safe-assemblyassembly {
ifiszero(call(GAS_STIPEND_NO_GRIEF, to, selfbalance(), gas(), 0x00, gas(), 0x00)) {
mstore(0x00, to) // Store the address in scratch space.mstore8(0x0b, 0x73) // Opcode `PUSH20`.mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.ifiszero(create(selfbalance(), 0x0b, 0x16)) {
returndatacopy(gas(), returndatasize(), shr(20, gas())) // For gas estimation.
}
}
}
}
/// @dev Sends `amount` (in wei) ETH to `to`, with a `gasStipend`.functiontrySafeTransferETH(address to, uint256 amount, uint256 gasStipend) internalreturns (bool success) {
/// @solidity memory-safe-assemblyassembly {
success :=call(gasStipend, to, amount, gas(), 0x00, gas(), 0x00)
}
}
/// @dev Sends all the ETH in the current contract to `to`, with a `gasStipend`.functiontrySafeTransferAllETH(address to, uint256 gasStipend) internalreturns (bool success) {
/// @solidity memory-safe-assemblyassembly {
success :=call(gasStipend, to, selfbalance(), gas(), 0x00, gas(), 0x00)
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*//* ERC20 OPERATIONS *//*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*//// @dev Sends `amount` of ERC20 `token` from `from` to `to`./// Reverts upon failure.////// The `from` account must have at least `amount` approved for/// the current contract to manage.functionsafeTransferFrom(address token, addressfrom, address to, uint256 amount) internal{
/// @solidity memory-safe-assemblyassembly {
let m :=mload(0x40) // Cache the free memory pointer.mstore(0x60, amount) // Store the `amount` argument.mstore(0x40, to) // Store the `to` argument.mstore(0x2c, shl(96, from)) // Store the `from` argument.mstore(0x0c, 0x23b872dd000000000000000000000000) // `transferFrom(address,address,uint256)`.// Perform the transfer, reverting upon failure.ifiszero(
and(
// The arguments of `and` are evaluated from right to left.or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
)
) {
mstore(0x00, 0x7939f424) // `TransferFromFailed()`.revert(0x1c, 0x04)
}
mstore(0x60, 0) // Restore the zero slot to zero.mstore(0x40, m) // Restore the free memory pointer.
}
}
/// @dev Sends all of ERC20 `token` from `from` to `to`./// Reverts upon failure.////// The `from` account must have their entire balance approved for/// the current contract to manage.functionsafeTransferAllFrom(address token, addressfrom, address to) internalreturns (uint256 amount) {
/// @solidity memory-safe-assemblyassembly {
let m :=mload(0x40) // Cache the free memory pointer.mstore(0x40, to) // Store the `to` argument.mstore(0x2c, shl(96, from)) // Store the `from` argument.mstore(0x0c, 0x70a08231000000000000000000000000) // `balanceOf(address)`.// Read the balance, reverting upon failure.ifiszero(
and(
// The arguments of `and` are evaluated from right to left.gt(returndatasize(), 0x1f), // At least 32 bytes returned.staticcall(gas(), token, 0x1c, 0x24, 0x60, 0x20)
)
) {
mstore(0x00, 0x7939f424) // `TransferFromFailed()`.revert(0x1c, 0x04)
}
mstore(0x00, 0x23b872dd) // `transferFrom(address,address,uint256)`.
amount :=mload(0x60) // The `amount` is already at 0x60. We'll need to return it.// Perform the transfer, reverting upon failure.ifiszero(
and(
// The arguments of `and` are evaluated from right to left.or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
)
) {
mstore(0x00, 0x7939f424) // `TransferFromFailed()`.revert(0x1c, 0x04)
}
mstore(0x60, 0) // Restore the zero slot to zero.mstore(0x40, m) // Restore the free memory pointer.
}
}
/// @dev Sends `amount` of ERC20 `token` from the current contract to `to`./// Reverts upon failure.functionsafeTransfer(address token, address to, uint256 amount) internal{
/// @solidity memory-safe-assemblyassembly {
mstore(0x14, to) // Store the `to` argument.mstore(0x34, amount) // Store the `amount` argument.mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`.// Perform the transfer, reverting upon failure.ifiszero(
and(
// The arguments of `and` are evaluated from right to left.or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
)
) {
mstore(0x00, 0x90b8ec18) // `TransferFailed()`.revert(0x1c, 0x04)
}
mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
}
}
/// @dev Sends all of ERC20 `token` from the current contract to `to`./// Reverts upon failure.functionsafeTransferAll(address token, address to) internalreturns (uint256 amount) {
/// @solidity memory-safe-assemblyassembly {
mstore(0x00, 0x70a08231) // Store the function selector of `balanceOf(address)`.mstore(0x20, address()) // Store the address of the current contract.// Read the balance, reverting upon failure.ifiszero(
and(
// The arguments of `and` are evaluated from right to left.gt(returndatasize(), 0x1f), // At least 32 bytes returned.staticcall(gas(), token, 0x1c, 0x24, 0x34, 0x20)
)
) {
mstore(0x00, 0x90b8ec18) // `TransferFailed()`.revert(0x1c, 0x04)
}
mstore(0x14, to) // Store the `to` argument.
amount :=mload(0x34) // The `amount` is already at 0x34. We'll need to return it.mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`.// Perform the transfer, reverting upon failure.ifiszero(
and(
// The arguments of `and` are evaluated from right to left.or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
)
) {
mstore(0x00, 0x90b8ec18) // `TransferFailed()`.revert(0x1c, 0x04)
}
mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
}
}
/// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract./// Reverts upon failure.functionsafeApprove(address token, address to, uint256 amount) internal{
/// @solidity memory-safe-assemblyassembly {
mstore(0x14, to) // Store the `to` argument.mstore(0x34, amount) // Store the `amount` argument.mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.// Perform the approval, reverting upon failure.ifiszero(
and(
// The arguments of `and` are evaluated from right to left.or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
)
) {
mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`.revert(0x1c, 0x04)
}
mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
}
}
/// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract./// If the initial attempt to approve fails, attempts to reset the approved amount to zero,/// then retries the approval again (some tokens, e.g. USDT, requires this)./// Reverts upon failure.functionsafeApproveWithRetry(address token, address to, uint256 amount) internal{
/// @solidity memory-safe-assemblyassembly {
mstore(0x14, to) // Store the `to` argument.mstore(0x34, amount) // Store the `amount` argument.mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.// Perform the approval, retrying upon failure.ifiszero(
and(
// The arguments of `and` are evaluated from right to left.or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
)
) {
mstore(0x34, 0) // Store 0 for the `amount`.mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.pop(call(gas(), token, 0, 0x10, 0x44, 0x00, 0x00)) // Reset the approval.mstore(0x34, amount) // Store back the original `amount`.// Retry the approval, reverting upon failure.ifiszero(
and(
or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
)
) {
mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`.revert(0x1c, 0x04)
}
}
mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
}
}
/// @dev Returns the amount of ERC20 `token` owned by `account`./// Returns zero if the `token` does not exist.functionbalanceOf(address token, address account) internalviewreturns (uint256 amount) {
/// @solidity memory-safe-assemblyassembly {
mstore(0x14, account) // Store the `account` argument.mstore(0x00, 0x70a08231000000000000000000000000) // `balanceOf(address)`.
amount :=mul(
mload(0x20),
and(
// The arguments of `and` are evaluated from right to left.gt(returndatasize(), 0x1f), // At least 32 bytes returned.staticcall(gas(), token, 0x10, 0x24, 0x20, 0x20)
)
)
}
}
}
Contract Source Code
File 16 of 17: TransferrableOwnership.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.4;/// @notice Simple single owner authorization mixin./// @author Solady (https://github.com/vectorized/solady/blob/main/src/auth/Ownable.sol)/// @dev While the ownable portion follows/// [EIP-173](https://eips.ethereum.org/EIPS/eip-173) for compatibility,/// the nomenclature for the 2-step ownership handover may be unique to this codebase.contractTransferrableOwnership{
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*//* CUSTOM ERRORS *//*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*//// @dev The caller is not authorized to call the function.errorUnauthorized();
/// @dev The `newOwner` cannot be the zero address.errorNewOwnerIsZeroAddress();
/// @dev The `pendingOwner` does not have a valid handover request.errorNoHandoverRequest();
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*//* EVENTS *//*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*//// @dev The ownership is transferred from `oldOwner` to `newOwner`./// This event is intentionally kept the same as OpenZeppelin's Ownable to be/// compatible with indexers and [EIP-173](https://eips.ethereum.org/EIPS/eip-173),/// despite it not being as lightweight as a single argument event.eventOwnershipTransferred(addressindexed oldOwner, addressindexed newOwner);
/// @dev An ownership handover to `pendingOwner` has been requested.eventOwnershipHandoverRequested(addressindexed pendingOwner);
/// @dev The ownership handover to `pendingOwner` has been canceled.eventOwnershipHandoverCanceled(addressindexed pendingOwner);
/// @dev `keccak256(bytes("OwnershipTransferred(address,address)"))`.uint256privateconstant _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE =0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0;
/// @dev `keccak256(bytes("OwnershipHandoverRequested(address)"))`.uint256privateconstant _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE =0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d;
/// @dev `keccak256(bytes("OwnershipHandoverCanceled(address)"))`.uint256privateconstant _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE =0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92;
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*//* STORAGE *//*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*//// @dev The owner slot is given by: `not(_OWNER_SLOT_NOT)`./// It is intentionally chosen to be a high value/// to avoid collision with lower slots./// The choice of manual storage layout is to enable compatibility/// with both regular and upgradeable contracts.uint256privateconstant _OWNER_SLOT_NOT =0x8b78c6d8;
/// The ownership handover slot of `newOwner` is given by:/// ```/// mstore(0x00, or(shl(96, user), _HANDOVER_SLOT_SEED))/// let handoverSlot := keccak256(0x00, 0x20)/// ```/// It stores the expiry timestamp of the two-step ownership handover.uint256privateconstant _HANDOVER_SLOT_SEED =0x389a75e1;
constructor(address newOwner) {
if (newOwner !=address(0)) {
_initializeOwner(newOwner);
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*//* INTERNAL FUNCTIONS *//*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*//// @dev Initializes the owner directly without authorization guard./// This function must be called upon initialization,/// regardless of whether the contract is upgradeable or not./// This is to enable generalization to both regular and upgradeable contracts,/// and to save gas in case the initial owner is not the caller./// For performance reasons, this function will not check if there/// is an existing owner.function_initializeOwner(address newOwner) internalvirtual{
/// @solidity memory-safe-assemblyassembly ("memory-safe") {
// Clean the upper 96 bits.
newOwner :=shr(96, shl(96, newOwner))
// Store the new value.sstore(not(_OWNER_SLOT_NOT), newOwner)
// Emit the {OwnershipTransferred} event.log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
}
}
/// @dev Sets the owner directly without authorization guard.function_setOwner(address newOwner) internalvirtual{
/// @solidity memory-safe-assemblyassembly ("memory-safe") {
let ownerSlot :=not(_OWNER_SLOT_NOT)
// Clean the upper 96 bits.
newOwner :=shr(96, shl(96, newOwner))
// Emit the {OwnershipTransferred} event.log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
// Store the new value.sstore(ownerSlot, newOwner)
}
}
/// @dev Throws if the sender is not the owner.function_checkOwner() internalviewvirtual{
/// @solidity memory-safe-assemblyassembly ("memory-safe") {
// If the caller is not the stored owner, revert.ifiszero(eq(caller(), sload(not(_OWNER_SLOT_NOT)))) {
mstore(0x00, 0x82b42900) // `Unauthorized()`.revert(0x1c, 0x04)
}
}
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*//* PUBLIC UPDATE FUNCTIONS *//*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*//// @dev Allows the owner to transfer the ownership to `newOwner`.functiontransferOwnership(address newOwner) publicpayablevirtualonlyOwner{
/// @solidity memory-safe-assemblyassembly ("memory-safe") {
ifiszero(shl(96, newOwner)) {
mstore(0x00, 0x7448fbae) // `NewOwnerIsZeroAddress()`.revert(0x1c, 0x04)
}
}
_setOwner(newOwner);
}
/// @dev Request a two-step ownership handover to the caller./// The request will automatically expire in 48 hours (172800 seconds) by default.functionrequestOwnershipHandover() publicpayablevirtual{
unchecked {
uint256 expires =block.timestamp+ ownershipHandoverValidFor();
/// @solidity memory-safe-assemblyassembly ("memory-safe") {
// Compute and set the handover slot to `expires`.mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, caller())
sstore(keccak256(0x0c, 0x20), expires)
// Emit the {OwnershipHandoverRequested} event.log2(0, 0, _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE, caller())
}
}
}
/// @dev Cancels the two-step ownership handover to the caller, if any.functioncancelOwnershipTransfer() publicpayablevirtual{
/// @solidity memory-safe-assemblyassembly ("memory-safe") {
// Compute and set the handover slot to 0.mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, caller())
sstore(keccak256(0x0c, 0x20), 0)
// Emit the {OwnershipHandoverCanceled} event.log2(0, 0, _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE, caller())
}
}
/// @dev Allows the owner to complete the two-step ownership handover to `pendingOwner`./// Reverts if there is no existing ownership handover requested by `pendingOwner`.functionconfirmOwnershipTransfer(address pendingOwner) publicpayablevirtualonlyOwner{
/// @solidity memory-safe-assemblyassembly ("memory-safe") {
// Compute and set the handover slot to 0.mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, pendingOwner)
let handoverSlot :=keccak256(0x0c, 0x20)
// If the handover does not exist, or has expired.ifgt(timestamp(), sload(handoverSlot)) {
mstore(0x00, 0x6f5e8818) // `NoHandoverRequest()`.revert(0x1c, 0x04)
}
// Set the handover slot to 0.sstore(handoverSlot, 0)
}
_setOwner(pendingOwner);
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*//* PUBLIC READ FUNCTIONS *//*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*//// @dev Returns the owner of the contract.functionowner() publicviewvirtualreturns (address result) {
/// @solidity memory-safe-assemblyassembly ("memory-safe") {
result :=sload(not(_OWNER_SLOT_NOT))
}
}
/// @dev Returns the expiry timestamp for the two-step ownership handover to `pendingOwner`.functionownershipHandoverExpiresAt(address pendingOwner) publicviewvirtualreturns (uint256 result) {
/// @solidity memory-safe-assemblyassembly ("memory-safe") {
// Compute the handover slot.mstore(0x0c, _HANDOVER_SLOT_SEED)
mstore(0x00, pendingOwner)
// Load the handover slot.
result :=sload(keccak256(0x0c, 0x20))
}
}
/// @dev Returns how long a two-step ownership handover is valid for in seconds.functionownershipHandoverValidFor() publicviewvirtualreturns (uint64) {
return48*3600;
}
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*//* MODIFIERS *//*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*//// @dev Marks a function as only callable by the owner.modifieronlyOwner() virtual{
_checkOwner();
_;
}
}