Overview
ETH Balance
0.00000001 ETH
Eth Value
Less Than $0.01 (@ $1,943.38/ETH)| Transaction Hash |
Method
|
Block
|
From
|
|
To
|
||||
|---|---|---|---|---|---|---|---|---|---|
Latest 11 internal transactions
Advanced mode:
| Parent Transaction Hash | Method | Block |
From
|
|
To
|
||
|---|---|---|---|---|---|---|---|
| Transfer | 24291592 | 31 days ago | 0 ETH | ||||
| Transfer | 24291590 | 31 days ago | 0 ETH | ||||
| Transfer | 24291589 | 31 days ago | 0 ETH | ||||
| Transfer | 24291588 | 31 days ago | 0 ETH | ||||
| Transfer | 24291587 | 31 days ago | 0 ETH | ||||
| Transfer | 24291586 | 31 days ago | 0 ETH | ||||
| Transfer | 24291585 | 31 days ago | 0 ETH | ||||
| Transfer | 24291584 | 31 days ago | 0 ETH | ||||
| Transfer | 24291583 | 31 days ago | 0 ETH | ||||
| Transfer | 24291582 | 31 days ago | 0 ETH | ||||
| 0x3d602d80 | 24291581 | 31 days ago | Contract Creation | 0 ETH |
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
Minimal Proxy Contract for 0xf755c383b31f28dbdf4a6a03da4f007abdaf6261
Contract Name:
GlueERC721
Compiler Version
v0.8.28+commit.7893614a
Optimization Enabled:
Yes with 200 runs
Other Settings:
cancun EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: BUSL-1.1
// https://github.com/glue-finance/glue/blob/main/LICENCE.txt
/**
██████╗ ██╗ ██╗ ██╗███████╗
██╔════╝ ██║ ██║ ██║██╔════╝
██║ ███╗██║ ██║ ██║█████╗
██║ ██║██║ ██║ ██║██╔══╝
╚██████╔╝███████╗╚██████╔╝███████╗
╚═════╝ ╚══════╝ ╚═════╝ ╚══════╝
██╗ ██╗ ██████╗ ██╗ ██╗██████╗
╚██╗ ██╔╝██╔═══██╗██║ ██║██╔══██╗
╚████╔╝ ██║ ██║██║ ██║██████╔╝
╚██╔╝ ██║ ██║██║ ██║██╔══██╗
██║ ╚██████╔╝╚██████╔╝██║ ██║
╚═╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝
███╗ ██╗███████╗████████╗
████╗ ██║██╔════╝╚══██╔══╝
██╔██╗ ██║█████╗ ██║
██║╚██╗██║██╔══╝ ██║
██║ ╚████║██║ ██║
╚═╝ ╚═══╝╚═╝ ╚═╝
@title Glue V1 for Enumerable ERC721s
@author @BasedToschi
@notice A comprehensive protocol for making enumerable ERC721 NFT collections "sticky" through the Glue Protocol infrastructure
@dev This contract implements the core functionality of the Glue Protocol for NFT collections. The system consists of two primary components:
1. GlueStickERC721: Factory contract that creates and manages individual Glue instances for NFT collections
2. GlueERC721: Implementation contract that gets cloned for each sticky NFT collection
The protocol enables NFT collections to have backing assets by:
- Associating the collection with a unique glue address that can hold collateral (any ERC20 or ETH)
- Allowing users to "unglue" by burning NFTs from the collection to withdraw a proportional amount of collateral
- Supporting batch operations, flash loans, and advanced hook mechanisms for extended functionality
Lore:
-* "Glue Stick" is the factory contract that glues ERC721 tokens.
-* "Sticky Asset" is an asset fueled by glue.
-* "Glue Address" is the address of the glue that is linked to a Sticky Token.
-* "Glued Collaterals" are the collaterals glued to a Sticky Token.
-* "Apply the Glue" is the action of infusing a NFT Collection with glue, making it sticky by creating its Glue Address.
-* "Unglue" is the action of burning the supply of a Sticky Asset to withdraw the corresponding percentage of the collateral.
-* "Glued Loan" is the action of borrowing collateral from multiple glues.
-* "Glued Hook" is a tool to expand the functionality of the protocol, via integrating the Sticky Asset Standard in your contract.
-* "Sticky Asset Standard" A common tools to implenet in your contract to expand the Glue functions and simplifying the development process.
-* "Sticky Asset Native" SAN is an asset that is natively compatible with the Sticky Asset Standard.
*/
pragma solidity ^0.8.28;
/**
* @dev Imports standard OpenZeppelin implementation, interfaces, and extensions for secure functionalities
*/
import {Clones} from "@openzeppelin/contracts/proxy/Clones.sol";
import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import {ERC721Holder} from "@openzeppelin/contracts/token/ERC721/utils/ERC721Holder.sol";
import {IERC721Enumerable} from "@openzeppelin/contracts/token/ERC721/extensions/IERC721Enumerable.sol";
import {Initializable} from "@openzeppelin/contracts/proxy/utils/Initializable.sol";
import {SafeERC20, IERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
/**
* @dev Interfaces for GlueERC20
*/
import {IGlueERC721, IGlueStickERC721, IERC721Burnable} from "./interfaces/IGlueERC721.sol";
import {IGluedLoanReceiver} from "./interfaces/IGluedLoanReceiver.sol";
import {IGluedSettings} from "./interfaces/IGluedSettings.sol";
import {IGluedHooks} from "./interfaces/IGluedHooks.sol";
/**
* @dev Library providing high-precision mathematical operations, decimal conversion, and rounding utilities for token calculations
*/
import {GluedMath} from "./libraries/GluedMath.sol";
/**
█████╗█████╗█████╗█████╗█████╗█████╗█████╗█████╗█████╗█████╗█████╗█████╗█████╗█████╗█████╗█████╗█████╗
╚════╝╚════╝╚════╝╚════╝╚════╝╚════╝╚════╝╚════╝╚════╝╚════╝╚════╝╚════╝╚════╝╚════╝╚════╝╚════╝╚════╝
██████╗ ██╗ ██╗ ██╗███████╗ ███████╗████████╗██╗ ██████╗██╗ ██╗
██╔════╝ ██║ ██║ ██║██╔════╝ ██╔════╝╚══██╔══╝██║██╔════╝██║ ██╔╝
██║ ███╗██║ ██║ ██║█████╗ ███████╗ ██║ ██║██║ █████╔╝
██║ ██║██║ ██║ ██║██╔══╝ ╚════██║ ██║ ██║██║ ██╔═██╗
╚██████╔╝███████╗╚██████╔╝███████╗ ███████║ ██║ ██║╚██████╗██║ ██╗
╚═════╝ ╚══════╝ ╚═════╝ ╚══════╝ ╚══════╝ ╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝
* @title GlueStickERC721
* @notice Factory contract for deploying and managing glue instances for ERC721 NFT collections
* @dev This contract serves as the entry point for making NFT collections sticky in the Glue Protocol.
* It validates collections, deploys minimal proxies for individual glue instances, and provides
* batch operations across multiple collections. It also coordinates cross-glue flash loans.
*/
contract GlueStickERC721 is IGlueStickERC721 {
/**
--------------------------------------------------------------------------------------------------------
▗▄▄▖▗▄▄▄▖▗▄▄▄▖▗▖ ▗▖▗▄▄▖
▐▌ ▐▌ █ ▐▌ ▐▌▐▌ ▐▌
▝▀▚▖▐▛▀▀▘ █ ▐▌ ▐▌▐▛▀▘
▗▄▄▞▘▐▙▄▄▖ █ ▝▚▄▞▘▐▌
01010011 01100101 01110100
01110101 01110000
*/
// Import SafeERC20 for ERC20 operations
using SafeERC20 for IERC20;
// Registry of NFT collections to their glue addresses
mapping(address => address) private _getGlueAddress;
// Array of all deployed glue addresses for enumeration
address[] private _allGlues;
// Implementation contract address that gets cloned for each collection
address private immutable _THE_GLUE;
/**
* @notice Deploys the implementation contract and initializes the factory
* @dev Sets up the factory by deploying the implementation contract that will be cloned
* for each NFT collection that gets glued in the protocol
*
* Use case: One-time deployment of the GlueStickERC721 factory to establish
* the NFT branch of the Glue protocol on a blockchain network
*/
constructor () {
// Deploy the implementation contract
_THE_GLUE = deployTheGlue();
}
/**
* @notice Prevents reentrancy attacks using transient storage
* @dev Custom implementation of reentrancy protection using transient storage
* This approach optimizes gas costs by using tstore/tload instead of state variables
* while maintaining robust security guarantees for critical functions
*
* Use case: Protecting critical functions against potential reentrancy exploits,
* particularly during NFT and collateral transfers which could contain callbacks
*/
modifier nnrtnt() {
// Check if the slot is already set
bytes32 slot = keccak256(abi.encodePacked(address(this), "ReentrancyGuard"));
// If the slot is already set, revert with a specific error signature
assembly {
// If the slot is already set, revert with a specific error signature
if tload(slot) {
mstore(0x00, 0x3ee5aeb5)
revert(0x1c, 0x04)
}
// Set the slot to 1 to indicate the function is being executed
tstore(slot, 1)
}
// Execute the function
_;
// Reset the slot to 0 after the function execution is complete
assembly {
tstore(slot, 0)
}
}
/**
--------------------------------------------------------------------------------------------------------
▗▄▄▄▖▗▖ ▗▖▗▖ ▗▖ ▗▄▄▖▗▄▄▄▖▗▄▄▄▖ ▗▄▖ ▗▖ ▗▖ ▗▄▄▖
▐▌ ▐▌ ▐▌▐▛▚▖▐▌▐▌ █ █ ▐▌ ▐▌▐▛▚▖▐▌▐▌
▐▛▀▀▘▐▌ ▐▌▐▌ ▝▜▌▐▌ █ █ ▐▌ ▐▌▐▌ ▝▜▌ ▝▀▚▖
▐▌ ▝▚▄▞▘▐▌ ▐▌▝▚▄▄▖ █ ▗▄█▄▖▝▚▄▞▘▐▌ ▐▌▗▄▄▞▘
01000110 01110101 01101110 01100011 01110100
01101001 01101111 01101110 01110011
*/
/**
* @notice Creates a new GlueERC721 contract for a specified NFT collection
* @dev Validates the NFT collection for compatibility, creates a deterministic clone
* of the implementation contract, initializes it with the collection address, and
* registers it in the protocol registry. The created glue instance becomes the
* collateral vault for the NFT collection.
*
* @param asset The address of the ERC721 collection to be glued
* @return glueAddress The address of the newly created glue instance
*
* Use cases:
* - Adding asset backing capabilities to existing NFT collections
* - Creating collateralization mechanisms for NFTs
* - Establishing new NFT economic models with withdrawal mechanisms
* - Supporting floor price protection for collections through backing
*/
function applyTheGlue(address asset) external override returns (address glueAddress) {
// Validate inputs
if(asset == address(0)) revert InvalidAsset(asset);
// Check if the token is valid
(bool isAllowed) = checkAsset(asset);
// If the token is not valid, revert
if(!isAllowed) revert InvalidAsset(asset);
// Check if the token is already glued
if(_getGlueAddress[asset] != address(0)) revert DuplicateGlue(asset);
// Generate a salt for the deterministic clone
bytes32 salt = keccak256(abi.encodePacked(asset));
// Clone the implementation contract
glueAddress = Clones.cloneDeterministic(_THE_GLUE, salt);
// Initialize the glue contract
IGlueERC721(glueAddress).initialize(asset);
// Store the glue address for the token
_getGlueAddress[asset] = glueAddress;
// Add the glue address to the array of all glued addresses
_allGlues.push(glueAddress);
// Emit an event to signal the addition of a new glue
emit GlueAdded(asset, glueAddress, _allGlues.length);
// Return the glue address
return glueAddress;
}
/**
* @notice Processes ungluing operations for multiple NFT collections in a single transaction
* @dev Efficiently batches unglue operations across multiple NFT collections, managing the
* transfer of NFTs from caller to glue contracts, and execution of unglue operations.
* Supports both single and multiple recipient configurations.
*
* @param stickyAssets Array of NFT collection addresses to unglue from
* @param tokenIds Two-dimensional array of token IDs to unglue for each collection
* @param collaterals Array of collateral addresses to withdraw (common across all unglue operations)
* @param recipients Array of recipient addresses to receive the unglued collateral
*
* Use cases:
* - Unglue collaterals across multiple sticky NFT collections
* - Efficient withdrawal of collaterals from multiple sticky NFT collections
* - Consolidated position exits for complex NFT strategies
* - Multi-collection redemption in a single transaction
*/
function batchUnglue(address[] calldata stickyAssets,uint256[][] calldata tokenIds,address[] calldata collaterals,address[] calldata recipients) external override nnrtnt {
// Validate inputs
if(stickyAssets.length == 0 || stickyAssets.length != tokenIds.length || recipients.length == 0)
revert InvalidInputs();
// Process each sticky token in the batch
for(uint256 i; i < stickyAssets.length;) {
// Get the sticky token
address stickyAsset = stickyAssets[i];
// Get the token IDs
uint256[] calldata tokenIdBatch = tokenIds[i];
// Transfer each token ID individually
for (uint256 j = 0; j < tokenIdBatch.length; j++) {
// Transfer the token ID from the caller to this contract
IERC721(stickyAsset).transferFrom(msg.sender, address(this), tokenIdBatch[j]);
}
// If there are no token IDs, skip to the next sticky token
if (tokenIdBatch.length == 0) continue;
// Get the glue address for this sticky token
address glueAddress = _getGlueAddress[stickyAsset];
// If the glue address is not set, skip to the next sticky token
if(glueAddress == address(0) ) continue;
// Approve each token ID individually
for (uint256 j = 0; j < tokenIdBatch.length; j++) {
// Approve the token ID to the glue address
IERC721(stickyAsset).approve(glueAddress, tokenIdBatch[j]);
}
// If there are multiple recipients, validate inputs
if(recipients.length > 1) {
// Validate inputs
if (recipients.length != stickyAssets.length || recipients[i] == address(0)) revert InvalidInputs();
// Execute unglue for this sticky token
IGlueERC721(glueAddress).unglue(
collaterals,
tokenIdBatch,
recipients[i]
);
// If there is only one recipient, validate inputs
} else {
// Validate inputs
if (recipients[0] == address(0)) revert InvalidInputs();
// Execute unglue for this sticky token
IGlueERC721(glueAddress).unglue(
collaterals,
tokenIdBatch,
recipients[0]
);
}
// Increment the index
unchecked { ++i; }
}
// Emit an event to signal the completion of the batch ungluing
emit BatchUnglueExecuted(stickyAssets, tokenIds, collaterals, recipients);
}
/**
* @notice Executes multiple flash loans across multiple glues.
* @dev This function calculates the loans, executes them, and verifies the repayments.
*
* @param glues The addresses of the glues to borrow from.
* @param collateral The address of the collateral to borrow.
* @param loanAmount The total amount of collaterals to borrow.
* @param receiver The address of the receiver.
* @param params Additional parameters for the receiver.
*
* Use cases:
* - Flash Loans across multiple glues
* - Capital-efficient arbitrage across DEXes
* - Liquidation operations in lending protocols
* - Complex cross-protocol interactions requiring upfront capital
* - Temporary liquidity for atomic multi-step operations
* - Collateral swaps without requiring pre-owned capital
*/
function gluedLoan(address[] calldata glues,address collateral,uint256 loanAmount,address receiver,bytes calldata params) external override nnrtnt {
// Validate inputs
if(receiver == address(0)) revert InvalidAddress();
if(loanAmount == 0) revert InvalidInputs();
if(glues.length == 0) revert InvalidInputs();
// Calculate the loans
LoanData memory loanData = _calculateLoans(glues, collateral, loanAmount);
// Execute the loans
_executeLoans(loanData, glues, collateral, receiver);
// Execute the receiver's callback
if (!IGluedLoanReceiver(receiver).executeOperation(
glues[0:loanData.count],
collateral,
loanData.expectedAmounts,
params
)) revert FlashLoanFailed();
// Verify the balances
_verifyBalances(loanData, glues, collateral);
}
/**
* @notice Calculates the flash loans for each glue.
* @dev This function calculates the loans, executes them, and verifies the repayments.
* @param glues The addresses of the glues to borrow from.
* @param collateral The address of the collateral to borrow.
* @param loanAmount The total amount of collateral to borrow.
* @return loanData The data for the loans.
*
* Use cases:
* - Calculate the ammount to borrow from each glue
*/
function _calculateLoans(address[] calldata glues, address collateral, uint256 loanAmount) private view returns (LoanData memory loanData) {
// Initialize the arrays for the loans
loanData.toBorrow = new uint256[](glues.length);
loanData.expectedAmounts = new uint256[](glues.length);
loanData.expectedBalances = new uint256[](glues.length);
// Initialize the total collected amount
uint256 totalCollected;
// Initialize the index for the loans
uint256 j;
// Process each glue
for (uint256 i; i < glues.length;) {
// If the total collected amount is greater than or equal to the total amount, break
if (totalCollected >= loanAmount) break;
// Get the glue address
address glue = glues[i];
// If the glue address is invalid, revert
if(glue == address(0)) revert InvalidAddress();
// Get the available balance of the glue
uint256 available = getGlueBalance(glue, collateral);
// If the available balance is 0, revert
if(available == 0) revert InvalidGlueBalance(glue, available, collateral);
// If the available balance is greater than 0, calculate the loans
if (available > 0) {
// Calculate the amount to borrow
uint256 toBorrow = loanAmount - totalCollected;
// If the amount to borrow is greater than the available balance, set the amount to borrow to the available balance
if (toBorrow > available) toBorrow = available;
// If the amount to borrow is 0, skip to the next glue
if(toBorrow == 0) continue;
// Get the flash loan fee
uint256 fee = IGlueERC721(glue).getFlashLoanFeeCalculated(toBorrow);
// Store the loan data
loanData.toBorrow[j] = toBorrow;
loanData.expectedAmounts[j] = toBorrow + fee;
loanData.expectedBalances[j] = available + fee;
totalCollected += toBorrow;
j++;
}
// Increment the index
unchecked { ++i; }
}
// Set the count of the loans
loanData.count = j;
// If the total collected amount is less than the total amount, revert
if (totalCollected < loanAmount)
revert InsufficientLiquidity(totalCollected, loanAmount);
// Return the loan data
return loanData;
}
/**
* @notice Executes the flash loans for each glue.
* @dev This function executes the loans and verifies the repayments.
* @param loanData The data for the loans.
* @param glues The addresses of the glues to borrow from.
* @param collateral The address of the collateral to borrow.
* @param receiver The address of the receiver.
*
* Use cases:
* - Execute the flash loans
*/
function _executeLoans(LoanData memory loanData,address[] calldata glues,address collateral,address receiver) private {
// Process each glue
for (uint256 i; i < loanData.count;) {
// Execute the loan
if(!IGlueERC721(glues[i]).loanHandler(
receiver,
collateral,
loanData.toBorrow[i]
)) revert FlashLoanFailed();
// Increment the index
unchecked { ++i; }
}
}
/**
* @notice Verifies the balances for each glue.
* @dev This function verifies the balances for each glue.
* @param loanData The data for the loans.
* @param glues The addresses of the glues to borrow from.
* @param collateral The address of the collateral to borrow.
*
* Use cases:
* - Verify the balances for each glue after loans are executed
*/
function _verifyBalances(LoanData memory loanData,address[] calldata glues,address collateral) private view {
// Verify the balances
for (uint256 i; i < loanData.count;) {
// Get the glue address
address glue = glues[i];
// If the glue address is invalid, revert
if(glue == address(0)) revert InvalidAddress();
// If the balance is less than the expected balance, revert
if (getGlueBalance(glue, collateral) < loanData.expectedBalances[i])
revert RepaymentFailed(glue);
// Increment the index
unchecked { ++i; }
}
}
/**
* @notice Deploys the _THE_GLUE contract.
* @dev This function is only called once when deploying the implementation contract
* Actual glue instances are created as clones and initialized via initialize()
* @return address The address of the deployed GlueERC721 contract
*
* Use cases:
* - One-time deployment of the implementation contract for NFT collections
*/
function deployTheGlue() internal returns (address) {
// Deploy the implementation contract
GlueERC721 glueContract = new GlueERC721(address(this));
// Get the address of the deployed implementation contract
address glueAddress = address(glueContract);
// If the address is 0, revert
if(glueAddress == address(0)) revert FailedToDeployGlue();
// Return the address of the deployed implementation contract
return glueAddress;
}
/**
--------------------------------------------------------------------------------------------------------
▗▄▄▖ ▗▄▄▄▖ ▗▄▖ ▗▄▄▄
▐▌ ▐▌▐▌ ▐▌ ▐▌▐▌ █
▐▛▀▚▖▐▛▀▀▘▐▛▀▜▌▐▌ █
▐▌ ▐▌▐▙▄▄▖▐▌ ▐▌▐▙▄▄▀
01010010 01100101
01100001 01100100
*/
/**
* @notice Retrieves expected collateral amounts from batch ungluing operations for NFTs
* @dev View function to calculate expected collateral returns for multiple NFT collections.
* This is essential for front-end applications and integrations to estimate expected
* returns before executing batch unglue operations.
*
* @param stickyAssets Array of NFT collection addresses
* @param stickyAmounts Array of NFT counts to simulate ungluing (number of NFTs, not IDs)
* @param collaterals Array of collateral addresses to check
* @return collateralAmounts 2D array of corresponding collateral amounts [glueIndex][collateralIndex]
*
* Use cases:
* - Pre-transaction estimation for front-end applications
* - Strategy optimization based on expected returns
* - User interface displays showing potential redemption values
*/
function getBatchCollaterals(address[] calldata stickyAssets,uint256[] calldata stickyAmounts,address[] calldata collaterals) external view override returns (uint256[][] memory collateralAmounts) {
// Validate inputs
if(stickyAssets.length != stickyAmounts.length) revert InvalidInputs();
// Initialize the memory array for the collateral amounts
collateralAmounts = new uint256[][](stickyAssets.length);
// Process each sticky token
for(uint256 i; i < stickyAssets.length;) {
// Get the glue address for this sticky token
address glueAddress = _getGlueAddress[stickyAssets[i]];
// If the glue address is not set, create an empty array for the collateral amounts
if(glueAddress == address(0)) {
// Create empty array for invalid glue addresses
collateralAmounts[i] = new uint256[](collaterals.length);
} else {
// Get collateral amounts for this sticky token
(uint256[] memory tokenCollateralAmounts) = IGlueERC721(glueAddress).collateralByAmount(stickyAmounts[i], collaterals);
// Store the collateral amounts
collateralAmounts[i] = tokenCollateralAmounts;
}
// Increment the index
unchecked { ++i; }
}
// Return the sticky tokens and the collateral amounts
return collateralAmounts;
}
/**
* @notice Checks if the given ERC721 address has valid totalSupply and no decimals
* @dev This function performs static calls to check if token is a valid NFT
* Token validation is critical for ensuring only compatible collections can be glued,
* preventing issues with non-enumerable NFT collections.
*
* @param asset The address of the ERC721 asset to check
* @return isValid Indicates whether the token is valid
*
* Use cases:
* - Pre-glue verification to prevent incompatible token issues
* - Protocol security to maintain compatibility standards
* - Front-end validation before attempting glue operations
*/
function checkAsset(address asset) public view override returns (bool isValid) {
// First check if it supports ERC721 interface
bytes4 ERC721InterfaceId = 0x80ac58cd;
// Try to check if it supports the ERC721 interface
try IERC165(asset).supportsInterface(ERC721InterfaceId) returns (bool supports721) {
// If it doesn't support the ERC721 interface, return false
if (!supports721) {
return false;
}
} catch {
// If it doesn't support the ERC721 interface, return false
return false;
}
// Then check for totalSupply
(bool hasTotalSupply, ) = asset.staticcall(abi.encodeWithSignature("totalSupply()"));
// If it doesn't have a totalSupply, return false
if (!hasTotalSupply) {
return false;
}
// Return true if it supports the ERC721 interface and has a totalSupply
return true;
}
/**
* @notice Computes the address of the GlueERC721 contract for the given ERC721 address.
* @dev Uses the Clones library to predict the address of the minimal proxy.
*
* @param asset The address of the ERC721 contract.
* @return predictedGlueAddress The computed address of the GlueERC721 contract.
*
* Use cases:
* - Complex integrations requiring pre-knowledge of glue addresses
* - Front-end preparation before actual glue deployment
* - Cross-contract interactions that reference glue addresses
* - Security verification of expected deployment addresses
*/
function computeGlueAddress(address asset) public view override returns (address predictedGlueAddress) {
// Validate inputs
if(asset == address(0)) revert InvalidAsset(asset);
// Compute the glue address
bytes32 salt = keccak256(abi.encodePacked(asset));
// Return the predicted address
return Clones.predictDeterministicAddress(_THE_GLUE, salt, address(this));
}
/**
* @notice Checks if a given token is sticky and returns its glue address
* @dev Utility function for external contracts and front-ends to verify token status
* in the Glue protocol and retrieve the associated glue address if it exists.
*
* @param asset The address of the NFT Collection to check
* @return isSticky bool Indicates whether the token is sticky.
* @return glueAddress The glue address for the token if it's sticky, otherwise address(0).
*
* Use cases:
* - UI elements showing token glue status
* - Protocol integrations needing to verify glue existence
* - Smart contracts checking if a token can be unglued
* - External protocols building on top of the Glue protocol
*/
function isStickyAsset(address asset) public view override returns (bool isSticky, address glueAddress) {
// Return a boolean, true if the token is sticky and the glue address
return (_getGlueAddress[asset] != address(0), _getGlueAddress[asset]);
}
/**
* @notice Retrieves the balance of a given collateral in a glue.
* @dev Handles both ERC20 collaterals and native ETH (when collateral address is address(0)),
* providing a unified interface for balance queries that's used throughout the protocol.
*
* @param glue The address of the glue.
* @param collateral The address of the collateral.
* @return uint256 The balance of the collateral in the glue.
*
* Use cases:
* - Collateral availability verification for flash loans
* - Used in getGluesBalances to track the balance of each glue for each collateral
*/
function getGlueBalance(address glue,address collateral) internal view returns (uint256) {
// If the collateral is 0, return the balance of the glue
if(collateral == address(0)) {
// Return the balance of the collateral
return glue.balance;
} else {
// Return the balance of the collateral
return IERC20(collateral).balanceOf(glue);
}
}
/**
* @notice Retrieves the balances of multiple collaterals across multiple glues
* @dev Returns a 2D array where each row represents a glue and each column represents a collateral
* @dev This function is used to get the balances of multiple collaterals across multiple glues
*
* @param glues The addresses of the glues to check
* @param collaterals The addresses of the collaterals to check for each glue
* @return balances a 2D array of balances [glueIndex][collateralIndex]
*
* Use cases:
* - Batch querying collateral positions across multiple glues
* - Dashboard displays showing complete portfolio positions
* - Cross-glue analytics and reporting
*/
function getGluesBalances(address[] calldata glues, address[] calldata collaterals) external view override returns (uint256[][] memory balances) {
// Initialize the 2D balances array
balances = new uint256[][](glues.length);
// Process each glue
for (uint256 i; i < glues.length;) {
// Initialize the balances array for this glue
balances[i] = new uint256[](collaterals.length);
// Process each collateral for this glue
for (uint256 j; j < collaterals.length;) {
// Get the balance of this collateral in this glue
balances[i][j] = getGlueBalance(glues[i], collaterals[j]);
// Increment the collateral index
unchecked { ++j; }
}
// Increment the glue index
unchecked { ++i; }
}
// Return the 2D balances array
return balances;
}
/**
* @notice Returns the total number of deployed glues.
* @return existingGlues The length of the _allGlues array.
*
* Use cases:
* - Informational queries about the total number of deployed glues
*/
function allGluesLength() external view override returns (uint256 existingGlues) {
// Return the length of the allGlues array
return _allGlues.length;
}
/**
* @notice Retrieves the glue address for a given token
* @dev Returns the glue address for the given token
*
* @param asset The address of the NFT collection to get the glue address for
* @return glueAddress The glue address for the given token, if it exists, otherwise address(0)
*
* Use cases:
* - Retrieving the glue address for a given token
*/
function getGlueAddress(address asset) external view override returns (address glueAddress) {
// Return the glue address for the given token
return _getGlueAddress[asset];
}
/**
* @notice Retrieves a glue address by its index in the registry
* @dev Returns the address of a deployed glue at the specified index
* This provides indexed access to the array of all deployed glues
*
* @param index The index in the allGlues array to query
* @return glueAddress The address of the glue at the specified index
*
* Use cases:
* - Enumeration of all deployed glues in the protocol
* - Accessing specific glues by index for reporting or integration
* - Batch operations on sequential glue addresses
*/
function getGlueAtIndex(uint256 index) external view override returns (address glueAddress) {
// Revert if the index is out of bounds
if (index >= _allGlues.length) {
return address(0);
}
// Return the glue address at the specified index
return _allGlues[index];
}
}
/**
█████╗█████╗█████╗█████╗█████╗█████╗█████╗█████╗█████╗█████╗█████╗█████╗█████╗█████╗█████╗█████╗█████╗
╚════╝╚════╝╚════╝╚════╝╚════╝╚════╝╚════╝╚════╝╚════╝╚════╝╚════╝╚════╝╚════╝╚════╝╚════╝╚════╝╚════╝
████████╗██╗ ██╗███████╗ ██████╗ ██╗ ██╗ ██╗███████╗
╚══██╔══╝██║ ██║██╔════╝ ██╔════╝ ██║ ██║ ██║██╔════╝
██║ ███████║█████╗ ██║ ███╗██║ ██║ ██║█████╗
██║ ██╔══██║██╔══╝ ██║ ██║██║ ██║ ██║██╔══╝
██║ ██║ ██║███████╗ ╚██████╔╝███████╗╚██████╔╝███████╗
╚═╝ ╚═╝ ╚═╝╚══════╝ ╚═════╝ ╚══════╝ ╚═════╝ ╚══════╝
* @title GlueERC721
* @notice Implementation contract for individual NFT collection glue instances
* @dev This contract is deployed once and then cloned using minimal proxies for each glued NFT collection.
* It manages collateral holdings, processes NFT ungluing operations, calculates proportional
* withdrawals based on NFT count vs total supply, and facilitates flash loans. The contract
* implements ERC721Holder for safe NFT receipt during ungluing operations.
*/
contract GlueERC721 is Initializable, ERC721Holder, IGlueERC721 {
/**
--------------------------------------------------------------------------------------------------------
▗▄▄▖▗▄▄▄▖▗▄▄▄▖▗▖ ▗▖▗▄▄▖
▐▌ ▐▌ █ ▐▌ ▐▌▐▌ ▐▌
▝▀▚▖▐▛▀▀▘ █ ▐▌ ▐▌▐▛▀▘
▗▄▄▞▘▐▙▄▄▖ █ ▝▚▄▞▘▐▌
01010011 01100101 01110100
01110101 01110000
*/
// Address for address payable (ETH)
using Address for address payable;
// SafeERC20 for IERC20
using SafeERC20 for IERC20;
// GluedMath for uint256
using GluedMath for uint256;
// Protocol constants
/// @notice Precision factor used for fractional calculations (10^18)
uint256 private constant PRECISION = 1e18;
/// @notice Protocol fee percentage in PRECISION units (0.1%)
uint256 private constant PROTOCOL_FEE = 1e15;
/// @notice Flash loan fee percentage in PRECISION units (0.01%)
uint256 private constant LOAN_FEE = 1e14;
/// @notice Special address used to represent native ETH in the protocol
address private constant ETH_ADDRESS = address(0);
/// @notice Dead address used for NFTs that don't support burning
address private constant DEAD_ADDRESS = 0x000000000000000000000000000000000000dEaD;
/// @notice Address of the protocol-wide settings contract
address private constant SETTINGS = 0x9976457c0C646710827bE1E36139C2b73DA6d2f3;
// Immutable reference to factory
/// @notice Address of the GlueStick factory that created this glue
address private immutable GLUE_STICK;
// Glue instance state
/// @notice Address of the ERC721 collection this glue is associated with
address private STICKY_ASSET;
/// @notice Flag indicating if the NFT collection doesn't support burning
bool private notBurnable;
/// @notice Flag indicating if NFTs are stored in this contract rather than burned/transferred
bool private stickySupplyStored;
/// @notice Enum tracking hook capability status (UNCHECKED, NO_HOOK, or HOOK)
BIO private bio;
/**
* @notice Constructor sets the factory address and initializes core variables
* @dev This constructor is only called once when deploying the implementation contract
* Actual glue instances are created as clones and initialized via initialize()
*
* @param _glueStickAddress Address of the factory contract that deploys glue instances
*
* Use case: One-time deployment of the implementation contract for NFT collections
*/
constructor(address _glueStickAddress) {
// If the glue stick address is 0, revert
if(_glueStickAddress == address(0)) revert InvalidGlueStickAddress();
// Set the glue stick address
GLUE_STICK = _glueStickAddress;
}
/**
* @notice Guards against reentrancy attacks using transient storage
* @dev Custom implementation of reentrancy protection using transient storage (tstore/tload)
* instead of a standard state variable, optimizing gas costs while maintaining security
*
* Use case: Securing all external functions against reentrancy attacks,
* particularly important for functions handling NFT and collateral transfers
*/
modifier nnrtnt() {
// Create a slot for the reentrancy guard
bytes32 slot = keccak256(abi.encodePacked(address(this), "ReentrancyGuard"));
// If the slot is already set, revert
assembly {
// If the slot is already set, revert with a specific error signature
if tload(slot) {
mstore(0x00, 0x3ee5aeb5)
revert(0x1c, 0x04)
}
// Set the slot to 1 to indicate the function is being executed
tstore(slot, 1)
}
// Execute the function
_;
// Reset the slot to 0 after the function execution is complete
assembly {
tstore(slot, 0)
}
}
/**
* @notice Initializes a newly deployed glue clone for an NFT collection
* @dev Called by the factory when creating a new glue instance through cloning
* Sets up the core state variables and establishes the relationship between
* this glue instance and its associated NFT collection
*
* @param asset Address of the ERC721 collection to be linked with this glue
*
* Use cases:
* - Creating a new glue address for a NFT collection (now Sticky Token) in which attach collateral
* - Establishing the collection-glue relationship in the protocol
*/
function initialize(address asset) external nnrtnt initializer {
// If the sender is not the glue stick, revert
if(msg.sender != GLUE_STICK) revert Unauthorized();
// If the token address to glue is 0, revert
if(asset == address(0)) revert InvalidAsset(asset);
// Set the sticky token
STICKY_ASSET = asset;
// Set inital boolean values
stickySupplyStored = false;
notBurnable = false;
bio = BIO.UNCHECKED;
}
/**
* @notice Allows the contract to receive ETH.
*/
receive() external payable {}
/**
* @notice Override ERC721Holder's onERC721Received to only accept STICKY_ASSET
* @dev This implementation ensures only the STICKY_ASSET can be received during unglue
*
* @param operator The address which called `safeTransferFrom` function
* @param from The address which previously owned the token
* @param tokenId The NFT identifier which is being transferred
* @param data Additional data with no specified format
* @return bytes4 The function selector
*
* Use cases:
* - Ensuring the ONLY ERC721 that can be received is the STICKY_ASSET.
*/
function onERC721Received(
address operator,
address from,
uint256 tokenId,
bytes memory data
) public virtual override returns (bytes4) {
// Only allow receiving the sticky token
if (msg.sender != STICKY_ASSET) {
revert NoAssetsTransferred();
}
// Call parent implementation
return super.onERC721Received(operator, from, tokenId, data);
}
/**
--------------------------------------------------------------------------------------------------------
▗▄▄▄▖▗▖ ▗▖▗▖ ▗▖ ▗▄▄▖▗▄▄▄▖▗▄▄▄▖ ▗▄▖ ▗▖ ▗▖ ▗▄▄▖
▐▌ ▐▌ ▐▌▐▛▚▖▐▌▐▌ █ █ ▐▌ ▐▌▐▛▚▖▐▌▐▌
▐▛▀▀▘▐▌ ▐▌▐▌ ▝▜▌▐▌ █ █ ▐▌ ▐▌▐▌ ▝▜▌ ▝▀▚▖
▐▌ ▝▚▄▞▘▐▌ ▐▌▝▚▄▄▖ █ ▗▄█▄▖▝▚▄▞▘▐▌ ▐▌▗▄▄▞▘
01000110 01110101 01101110 01100011 01110100
01101001 01101111 01101110 01110011
*/
/**
* @notice Core function that processes NFT ungluing operations to release collateral
* @dev Handles the complete ungluing workflow for NFTs: verifying ownership,
* managing transfers, calculating proportional collateral amounts, applying fees,
* executing hook logic if enabled, and distributing collateral to the recipient.
*
* @param collaterals Array of collateral token addresses to withdraw
* @param tokenIds Array of NFT token IDs to burn for collateral withdrawal
* @param recipient Address to receive the withdrawn collateral
* @return supplyDelta Calculated proportion of total NFT supply (in PRECISION units)
* @return realAmount Number of NFTs processed (after removing duplicates)
* @return beforeTotalSupply NFT collection supply before the unglue operation
* @return afterTotalSupply NFT collection supply after the unglue operation
*
* Use cases:
* - Redeeming collateral from the protocol by burning NFTs
* - Converting sticky NFTs back to their collaterals
*/
function unglue(address[] calldata collaterals, uint256[] calldata tokenIds, address recipient) external override nnrtnt returns (uint256 supplyDelta, uint256 realAmount, uint256 beforeTotalSupply, uint256 afterTotalSupply) {
// If no collateral is selected, revert
if(collaterals.length == 0) revert NoAssetsSelected();
// If the recipient is 0, set it to the sender
if (recipient == address(0)) {recipient = msg.sender;}
// Process the unique token IDs
realAmount = processUniqueTokenIds(tokenIds, recipient);
// Get the real total supply
(beforeTotalSupply, afterTotalSupply) = getRealTotalSupply(realAmount);
// Calculate the supply delta
supplyDelta = calculateSupplyDelta(realAmount, beforeTotalSupply);
// Compute the collateral
computeCollateral(collaterals, supplyDelta, recipient);
// Emit the unglued event
emit unglued(recipient, realAmount, beforeTotalSupply, afterTotalSupply, supplyDelta);
// Return the values
return (supplyDelta, realAmount, beforeTotalSupply, afterTotalSupply);
}
/**
* @notice Processes token IDs array to remove duplicates and verify ownership
* @dev Creates a new array with unique token IDs and verifies ownership
*
* @param tokenIds Array of token IDs to process
* @param recipient The address of the recipient of the unglue operation
* @return uniqueCount Number of unique token IDs
*
* Use cases:
* - Removing duplicates from the token IDs array
* - Verifying ownership of the token IDs
*/
function processUniqueTokenIds(uint256[] calldata tokenIds, address recipient) private returns (uint256) {
// If no token IDs are selected, revert
if(tokenIds.length == 0) revert NoAssetsSelected();
// Create a slot for the duplicate token ID check
bytes32 duplicateSlot = keccak256(abi.encodePacked(address(this), "DuplicateTokenIdCheck"));
// Initialize the count
uint256 count = 0;
// Process each token ID
for (uint256 i = 0; i < tokenIds.length; i++) {
// Get the token ID
uint256 tokenId = tokenIds[i];
// Create a slot for the duplicate token ID check
bytes32 slot = keccak256(abi.encodePacked(duplicateSlot, tokenId));
// Check if the token ID is a duplicate
bool isDuplicate;
assembly {
// Get the duplicate status
isDuplicate := tload(slot)
// Set the duplicate status to true
tstore(slot, 1)
}
// If the token ID is not a duplicate
if (!isDuplicate) {
// Check if the sender owns the token ID
if(IERC721(STICKY_ASSET).ownerOf(tokenId) != msg.sender) revert NoAssetsTransferred();
// Increment the count
count++;
}
}
// If no token IDs are processed, revert
if (count == 0) revert NoAssetsTransferred();
// Create a new array with unique token IDs
uint256[] memory uniqueTokenIds = new uint256[](count);
// Initialize the unique count
uint256 uniqueCount = 0;
// Process each token ID
for (uint256 i = 0; i < tokenIds.length; i++) {
// Get the token ID
uint256 tokenId = tokenIds[i];
// Create a slot for the duplicate token ID check
bytes32 slot = keccak256(abi.encodePacked(duplicateSlot, tokenId));
// Check if the token ID is processed
bool isProcessed;
assembly {
// Get the processed status
isProcessed := tload(slot)
// Set the processed status to false
tstore(slot, 0)
}
// If the token ID is processed
if (isProcessed) {
// Add the token ID to the unique token IDs array
uniqueTokenIds[uniqueCount] = tokenId;
// Increment the unique count
uniqueCount++;
}
}
// Burn the main tokens
burnMain(uniqueTokenIds);
// Execute the hook
tryHook(address(this), uniqueCount, uniqueTokenIds, recipient);
// If no tokens are transferred, revert
if (uniqueCount == 0) {
revert NoAssetsTransferred();
}
// Return the unique count
return uniqueCount;
}
/**
* @notice Calculates the real total supply of the sticky token by excluding balances in dead and burn addresses.
* This function is used to calculate the total supply before and after the unglue operation.
*
* @param realAmount The amount of sticky tokens being unglued
* @return beforeTotalSupply The total supply before ungluing
* @return afterTotalSupply The total supply after ungluing
*
* Use cases:
* - Calculating the total supply before and after ungluing
* - Ensuring accurate supply metrics for fair collateral distribution
*/
function getRealTotalSupply(uint256 realAmount) private view returns (uint256, uint256) {
// Get the before total supply
uint256 beforeTotalSupply = (getNFTTotalSupply() + realAmount) - getNFTBalance(DEAD_ADDRESS);
// Subtract the balance of the glue
beforeTotalSupply -= getNFTBalance(address(this));
// Get the after total supply
uint256 afterTotalSupply = beforeTotalSupply - realAmount;
// Return the values
return (beforeTotalSupply, afterTotalSupply);
}
/**
* @notice Calculates the supply delta based on the real amount and real total supply.
* This function is used to calculate the supply delta based on the real amount and real total supply.
*
* @param realAmount The real amount of supply.
* @param beforeTotalSupply The real total supply.
* @return The calculated supply delta.
*
* Use cases:
* - Calculating the supply delta based on the real amount and real total supply.
*/
function calculateSupplyDelta(uint256 realAmount, uint256 beforeTotalSupply) private pure returns (uint256) {
// Calculate the supply delta
return GluedMath.md512(realAmount, PRECISION, beforeTotalSupply);
}
/**
* @notice Burns the main tokens held by the glue or transfers them to the dead address if burning fails.
* This function is used to burn the main tokens held by the glue or transfer them to the dead address if burning fails.
*
* @param _tokenIds The token IDs to burn or transfer.
*
* Use cases:
* - Burning the main tokens held by the glue.
* - Transferring the main tokens to the dead address if burning fails.
*/
function burnMain(uint256[] memory _tokenIds) private {
// Process each token ID
for (uint256 i = 0; i < _tokenIds.length; i++) {
// Get the token ID
uint256 tokenId = _tokenIds[i];
// If the token is not burnable, try to burn it
if (!notBurnable) {
// Try to burn the token
try IERC721Burnable(STICKY_ASSET).burn(tokenId) {
// Burn successful, continue to next iteration
continue;
} catch {
// Set the not burnable flag to true
notBurnable = true;
}
}
// If the token is not burnable and the token is not stored, try to transfer it to the dead address
if (notBurnable && !stickySupplyStored) {
// Try to transfer the token to the dead address
try IERC721(STICKY_ASSET).transferFrom(msg.sender, DEAD_ADDRESS, tokenId) {
// Transfer successful, continue to next iteration
continue;
} catch {
// Set the sticky token stored flag to true
stickySupplyStored = true;
}
}
// If the token is not burnable and the token is stored, try to transfer it to the glue
if (notBurnable && stickySupplyStored) {
// Try to transfer the token to the glue
try IERC721(STICKY_ASSET).transferFrom(msg.sender, address(this), tokenId) {
// Transfer successful, continue to next iteration
continue;
} catch {
// Revert
revert FailedToProcessCollection();
}
}
}
}
/**
* @dev Processes the withdrawals for the given token addresses and amounts.
* It also checks for duplicates and calculates the asset availability.
* It also calculates the protocol fee and the recipient amount.
* It also executes the hook if enabled.
* It also sends the glue fee and the protocol fee to the glue fee address and the team address respectively.
* It also sends the recipient amount to the recipient.
*
* @param collaterals The addresses of the collateral tokens to withdraw.
* @param supplyDelta The change in the token supply.
* @param recipient The address of the recipient.
*
* Use cases:
* - Ungluing assets from the glue.
* - Sending the unglued assets to the recipient.
* - Calculating the protocol fee and the recipient amount.
* - Executing the hook if enabled.
* - Sending the glue fee and the protocol fee to the glue fee address and the team address respectively.
*/
function computeCollateral(address[] calldata collaterals, uint256 supplyDelta, address recipient) private {
// Create a slot for the duplicate address check
bytes32 duplicateSlot = keccak256(abi.encodePacked(address(this), "DuplicateAddressCheck"));
// Fetch fee information directly from SETTINGS
(uint256 glueFee, address glueFeeAddress, address teamAddress) = IGluedSettings(SETTINGS).getProtocolFeeInfo();
// Process each collateral
for (uint256 i = 0; i < collaterals.length; i++) {
// Get the collateral
address gluedCollateral = collaterals[i];
// If the collateral is the sticky token, continue
if(gluedCollateral == STICKY_ASSET) continue;
// Check if the collateral is a duplicate
bytes32 slot = keccak256(abi.encodePacked(duplicateSlot, gluedCollateral));
// Check if the collateral is a duplicate
bool isDuplicate;
assembly {
// Get the duplicate flag
isDuplicate := tload(slot)
// Set the duplicate flag to true
tstore(slot, 1)
}
// If the collateral is a duplicate, continue
if (isDuplicate) continue;
// Calculate the asset availability
uint256 assetAvailability = GluedMath.md512(getAssetBalance(gluedCollateral, address(this)), supplyDelta, PRECISION);
// If the asset availability is 0, continue
if (assetAvailability == 0) continue;
// Calculate fees
uint256 protocolFeeAmount = GluedMath.md512Up(assetAvailability, PROTOCOL_FEE, PRECISION);
// Calculate the recipient amount
uint256 recipientAmount = assetAvailability - protocolFeeAmount;
// If the recipient amount is 0, continue
if(recipientAmount == 0) continue;
// Check if out hook is enabled (bit 1, 0x2) in BIO
if (bio == BIO.UNCHECKED || bio == BIO.HOOK) {
// Execute the hook
recipientAmount = tryHook(gluedCollateral, recipientAmount, new uint256[](0), recipient);
}
// Calculate the glue fee amount
uint256 glueFeeAmount = GluedMath.md512Up(protocolFeeAmount, glueFee, PRECISION);
// If the glue fee amount is greater than the protocol fee amount, set the glue fee amount to the protocol fee amount
if (glueFeeAmount > protocolFeeAmount) glueFeeAmount = protocolFeeAmount;
// For ETH transfers
if (gluedCollateral == ETH_ADDRESS) {
// Send the glue fee to the glue fee address
payable(glueFeeAddress).sendValue(glueFeeAmount);
// If the glue fee amount is less than the protocol fee amount, send the protocol fee to the team address
if (glueFeeAmount < protocolFeeAmount) {
// Send the protocol fee to the team address
payable(teamAddress).sendValue(protocolFeeAmount - glueFeeAmount);
}
// Send the recipient amount to the recipient
payable(recipient).sendValue(recipientAmount);
} else {
// Send the glue fee to the glue fee address
IERC20(gluedCollateral).safeTransfer(glueFeeAddress, glueFeeAmount);
// If the glue fee amount is less than the protocol fee amount, send the protocol fee to the team address
if (glueFeeAmount < protocolFeeAmount) {
// Send the protocol fee to the team address
IERC20(gluedCollateral).safeTransfer(teamAddress, protocolFeeAmount - glueFeeAmount);
}
// Send the recipient amount to the recipient
IERC20(gluedCollateral).safeTransfer(recipient, recipientAmount);
}
}
// Reset the duplicate flags
for (uint256 i = 0; i < collaterals.length; i++) {
// Get the collateral
address gluedCollateral = collaterals[i];
// Reset the duplicate flag
bytes32 slot = keccak256(abi.encodePacked(duplicateSlot, gluedCollateral));
assembly {
// Reset the duplicate flag
tstore(slot, 0)
}
}
}
/**
* @notice Executes a hook based on the token address and returns the hook amount
* @dev This function assumes all checks are done outside and just executes the hook
*
* @param asset The address of the asset
* @param amount The amount of the asset
* @param tokenIds The token IDs to execute the hook on
* @param recipient The address of the recipient of the unglue operation
* @return The amount of tokens consumed by the hook operation
*
* Use cases:
* - Executing the hook if enabled.
* - Sending the hook amount to the sticky token.
* - Returning the amount minus the hook amount.
*/
function tryHook(address asset, uint256 amount, uint256[] memory tokenIds, address recipient) private returns (uint256) {
// If the token is the sticky token, execute the hook
// This hook dont send ammount to the sticky token, but is designed to track for expanded integration the burned IDs.
if (asset == STICKY_ASSET) {
if (bio == BIO.HOOK || bio == BIO.UNCHECKED) {
try IGluedHooks(STICKY_ASSET).executeHook(asset, amount, tokenIds, recipient) {
// Hook executed successfully
} catch {
// Hook execution failed, but we continue processing
// The assets have already been transferred
}
// Return 0
return 0;
}
// Return 0
return 0;
} else {
// Initialize the hook amount
uint256 hookAmount;
// If the hook is unchecked, try to get the hook size
if (bio == BIO.UNCHECKED) {
// Try to get the hook size
try IGluedHooks(STICKY_ASSET).hasHook() returns (bool hasHook) {
// If the hook is enabled, set the bio to hook
if (hasHook) {
// Set the bio to hook
bio = BIO.HOOK;
} else {
// Set the bio to no hook
bio = BIO.NO_HOOK;
}
} catch {
// Set the bio to no hook
bio = BIO.NO_HOOK;
}
}
// If the hook is enabled, try to get the hook size
if (bio == BIO.HOOK) {
// Try to get the hook size
try IGluedHooks(STICKY_ASSET).hookSize(asset, amount) returns (uint256 hookSize) {
// If the hook size is greater than the precision, set the hook size to the precision
if (hookSize > PRECISION) {
hookSize = PRECISION;
}
// Calculate the hook amount
hookAmount = GluedMath.md512(amount, hookSize, PRECISION);
} catch {
// If hook size retrieval fails, default to 0
return amount;
}
} else {
// No hook enabled
return amount;
}
// Ensure hook amount doesn't exceed available amount
hookAmount = hookAmount > amount ? amount : hookAmount;
// Only when there's actually an amount to transfer
if (hookAmount > 0) {
// If the token is not ETH, transfer the hook amount to the sticky token
if (asset != ETH_ADDRESS) {
// Get the balance before
uint256 balanceBefore = IERC20(asset).balanceOf(STICKY_ASSET);
// Transfer the hook amount to the sticky token
IERC20(asset).safeTransfer(STICKY_ASSET, hookAmount);
// Get the balance after
uint256 balanceAfter = IERC20(asset).balanceOf(STICKY_ASSET);
// If the balance after is less than or equal to the balance before, set the hook amount to 0
if (balanceAfter <= balanceBefore) {
// Set the hook amount to 0
hookAmount = 0;
} else {
// Set the hook amount to the balance after minus the balance before
hookAmount = balanceAfter - balanceBefore;
}
} else {
// Send the hook amount to the sticky token
payable(STICKY_ASSET).sendValue(hookAmount);
}
}
// Call appropriate hook function with try-catch to handle potential failures
try IGluedHooks(STICKY_ASSET).executeHook(asset, hookAmount, tokenIds, recipient) {
// Hook executed successfully
} catch {
// Hook execution failed, but we continue processing
// The assets have already been transferred
}
// Return the amount minus the hook amount
return amount - hookAmount;
}
}
/**
* @notice Retrieves the balance of the specified token held by the glue.
* @dev This function is used to get the balance of the specified token for the given account.
*
* @param asset The address of the token contract.
* @param account The address of the account.
* @return The balance of the token held by the account.
*
* Use cases:
* - Retrieving the balance of the specified token for the given account.
*/
function getAssetBalance(address asset, address account) private view returns (uint256) {
// If the token is ETH, return the balance of the account
if (asset == ETH_ADDRESS) {
// Return the balance of the account
return account.balance;
} else {
// Return the balance of the token
return IERC20(asset).balanceOf(account);
}
}
/**
* @dev Retrieves the balance of a given ERC721 token for a specific account.
* @param account The address of the account to check the balance for.
* @return The balance of the ERC721 token held by the account.
*
* Use cases:
* - Retrieving the balance of the ERC721 token for the given account.
*/
function getNFTBalance(address account) private view returns (uint256) {
// If the account is the zero address, return 0
if (account == address(0)) {
// Return 0
return 0;
}
// Try to get the balance of the ERC721 token
try IERC721(STICKY_ASSET).balanceOf(account) returns (uint256 balance) {
// Return the balance
return balance;
} catch {
// Return 0
return 0;
}
}
/**
* @notice Retrieves the total supply of the specified token.
* @dev This function is used to get the total supply of the specified token.
*
* @return The total supply of the token.
*
* Use cases:
* - Retrieving the total supply of the specified token.
*/
function getNFTTotalSupply() private view returns (uint256) {
// Try to get the total supply of the ERC721 token
uint256 totalSupply = IERC721Enumerable(STICKY_ASSET).totalSupply();
// Return the total supply
return totalSupply;
}
/**
* @notice Calculates the asset availability based on the asset balance and supply delta.
* @dev This function is used to calculate the asset availability based on the asset balance and supply delta.
*
* @param assetBalance The balance of the asset.
* @param supplyDelta The supply delta.
* @return The calculated asset availability.
*
* Use cases:
* - Calculating the asset availability based on the asset balance and supply delta.
*/
function calculateAssetAvailability(uint256 assetBalance, uint256 supplyDelta) private pure returns (uint256) {
// Return the calculated asset availability
return GluedMath.md512(assetBalance, supplyDelta, PRECISION);
}
/**
* @notice Initiates a flash loan.
* @dev This function is used to initiate a flash loan.
*
* @param collateral The address of the collateral token.
* @param amount The amount of tokens to flash loan.
* @param receiver The address of the receiver.
* @param params The parameters for the flash loan.
* @return success boolean indicating success
*
* Use cases:
* - Initiating a simplified Glued loan from this Glue.
* - Initiating a flash loan with simpler integration.
*/
function flashLoan(address collateral,uint256 amount,address receiver,bytes calldata params) external override returns (bool success) {
// Create an array with just this glue address
address[] memory glues = new address[](1);
// Set the glue address
glues[0] = address(this);
// Call the GlueStick's gluedLoan function
try IGlueStickERC721(GLUE_STICK).gluedLoan(glues,collateral,amount,receiver,params) {
// Set the success to true
success = true;
// If the loan operation failed
} catch {
// Set the success to false
success = false;
}
}
/**
* @notice Initiates a minimal flash loan.
* @dev This function is used for the Glue Stick to handle collateral in a Glued Loan.
* @dev Only the Glue Stick can call this function.
*
* @param receiver The address of the receiver.
* @param collateral The address of the token to flash loan.
* @param amount The amount of tokens to flash loan.
* @return loanSent boolean indicating success
*
* Use cases:
* - Handle collateral in a Glued Loan.
*/
function loanHandler(address receiver, address collateral, uint256 amount) external override nnrtnt returns (bool loanSent) {
// If the sender is not the glue stick, revert
if(msg.sender != GLUE_STICK) revert Unauthorized();
// If the token is the sticky token, revert
if(collateral == STICKY_ASSET) revert InvalidAsset(collateral);
// If the token is ETH, send the amount to the receiver
if(collateral == ETH_ADDRESS) {
// Send the amount to the receiver
payable(receiver).sendValue(amount);
} else {
// If the token is not ETH, transfer the amount to the receiver
IERC20(collateral).safeTransfer(receiver, amount);
}
// Emit the GlueLoan event
emit GlueLoan(collateral, amount, receiver);
// Return Status
return true;
}
/**
--------------------------------------------------------------------------------------------------------
▗▄▄▖ ▗▄▄▄▖ ▗▄▖ ▗▄▄▄
▐▌ ▐▌▐▌ ▐▌ ▐▌▐▌ █
▐▛▀▚▖▐▛▀▀▘▐▛▀▜▌▐▌ █
▐▌ ▐▌▐▙▄▄▖▐▌ ▐▌▐▙▄▄▀
01010010 01100101
01100001 01100100
*/
/**
* @notice Calculates the supply delta based on the sticky NFT amount and total supply.
* @dev This function is used to calculate the supply delta based on the sticky NFT amount and total supply.
*
* @param stickyAmount The amount of sticky NFTs.
* @return supplyDelta The calculated supply delta.
*
* Use cases:
* - Calculating the supply delta based on the sticky NFT amount.
*
*/
function getSupplyDelta(uint256 stickyAmount) public view override returns (uint256 supplyDelta) {
// Get the real total supply
(uint256 beforeTotalSupply, ) = getRealTotalSupply(stickyAmount);
// Return the calculated supply delta
return calculateSupplyDelta(stickyAmount, beforeTotalSupply);
}
/**
* @notice Retrieves the adjusted total supply of the Sticky NFT Collection.
* @dev This function is used to get the adjusted total supply of the Sticky NFT Collection.
*
* @return adjustedTotalSupply The adjusted total supply of the Sticky NFT Collection.
*
* Use cases:
* - Retrieving the adjusted and actual total supply of the Sticky NFT Collection.
*/
function getAdjustedTotalSupply() external view override returns (uint256 adjustedTotalSupply) {
// Get the real total supply
(uint256 beforeTotalSupply, ) = getRealTotalSupply(0);
// Return the adjusted total supply
return beforeTotalSupply;
}
/**
* @notice Retrieves the protocol fee percentage.
* @dev This function is used to get the protocol fee percentage.
*
* @return protocolFee The protocol fee as a fixed-point number with 18 decimal places.
*
* Use cases:
* - Retrieving the protocol fee percentage fixed to 1e15 = 0.1% | 1e18 = 100%.
*/
function getProtocolFee() external pure override returns (uint256 protocolFee) {
// Return the protocol fee
return (PROTOCOL_FEE);
}
/**
* @notice Retrieves the flash loan fee percentage.
* @dev This function is used to get the flash loan fee percentage.
* @dev The flash loan fee is fully paid to the Glue
*
* @return flashLoanFee The flash loan fee as a fixed-point number with 18 decimal places.
*
* Use cases:
* - Retrieving the flash loan fee percentage fixed to 1e14 = 0.01% | 1e18 = 100%.
*/
function getFlashLoanFee() external pure override returns (uint256 flashLoanFee) {
// Return the flash loan fee
return (LOAN_FEE);
}
/**
* @notice Retrieves the flash loan fee for a given amount.
* @dev This function is used to get the flash loan fee for a given amount.
*
* @param amount The amount to calculate the flash loan fee for.
* @return fee The flash loan fee applied to a given amount.
*
* Use cases:
* - Retrieving the flash loan fee applied to a given amount.
*/
function getFlashLoanFeeCalculated(uint256 amount) external pure override returns (uint256 fee) {
// Return the flash loan fee applied to a given amount
return (GluedMath.md512Up(amount, LOAN_FEE, PRECISION));
}
/**
* @notice Retrieves the total hook size for a sepecific collateral.
* @dev This function is used to get the total hook size for a sepecific collateral or sticky token.
*
* @param collateral The address of the collateral token.
* @param collateralAmount The amount of tokens to calculate the hook size for.
* @return hookSize The total hook size.
*
* Use cases:
* - Retrieving the total hook size for a specific collateral.
*/
function getTotalHookSize(address collateral, uint256 collateralAmount) public view override returns (uint256 hookSize) {
// If the collateral is the sticky token, return 0
if (collateral == STICKY_ASSET) {
// Return 0
return 0;
}
// Try to get inHookSize if the hook is enabled
if (bio == BIO.HOOK) {
// Try to get the hook size
try IGluedHooks(STICKY_ASSET).hooksImpact(collateral, collateralAmount, 0) returns (uint256 size) {
// Return the hook size
return size;
} catch {
// Return 0
return 0;
}
}
// Return 0
return 0;
}
/**
* @notice Calculates the amount of collateral tokens that can be unglued for a given amount of sticky tokens.
* @dev This function is used to calculate the amount of collateral tokens that can be unglued for a given amount of sticky tokens.
*
* @param stickyAmount The amount of sticky tokens to be burned.
* @param collaterals An array of addresses representing the collateral tokens to unglue.
* @return amounts An array containing the corresponding amounts that can be unglued.
* @dev This function accounts for the protocol fee in its calculations.
*
* Use cases:
* - Calculating the amount of collateral tokens that can be unglued for a given amount of sticky tokens.
*/
function collateralByAmount (uint256 stickyAmount, address[] calldata collaterals) external view override returns (uint256[] memory amounts) {
// If the collaterals array is empty, revert
if(collaterals.length == 0) revert NoCollateralSelected();
// If the amount is 0, revert
if(stickyAmount == 0) revert ZeroAmount();
// Calculate the supply delta based on the sticky token amount
uint256 supplyDelta = getSupplyDelta(stickyAmount);
// Create array for final unglue amounts
uint256[] memory finalUnglueAmounts = new uint256[](collaterals.length);
// Process each collateral and calculate available amounts with hooks
for (uint256 i = 0; i < collaterals.length; i++) {
// Get the collateral address
address gluedCollateral = collaterals[i];
// If the collateral is the sticky token, set the unglue amount to 0
if(gluedCollateral == STICKY_ASSET) {
// Set the unglue amount to 0
finalUnglueAmounts[i] = 0;
// Continue to the next collateral
continue;
}
// Get asset balance and calculate initial availability
uint256 assetBalance = getAssetBalance(gluedCollateral, address(this));
// If the asset balance is greater than 0
if (assetBalance > 0) {
// Calculate asset availability based on supply delta
uint256 assetAvailability = calculateAssetAvailability(assetBalance, supplyDelta);
// Apply protocol fee
uint256 afterFeeAmount = assetAvailability - GluedMath.md512(assetAvailability, PROTOCOL_FEE, PRECISION);
// Apply hooks if enabled
uint256 hookSize = getTotalHookSize(gluedCollateral, afterFeeAmount);
// If the hook size is greater than 0
if (hookSize > 0) {
// Calculate the hook amount
uint256 hookAmount = GluedMath.md512(afterFeeAmount, hookSize, PRECISION);
// If the hook amount is greater than the after fee amount, set the hook amount to the after fee amount
if (hookAmount > afterFeeAmount) {
hookAmount = afterFeeAmount;
}
// Set the unglue amount to the after fee amount minus the hook amount
finalUnglueAmounts[i] = afterFeeAmount - hookAmount;
} else {
// Set the unglue amount to the after fee amount
finalUnglueAmounts[i] = afterFeeAmount;
}
} else {
// Set the unglue amount to 0
finalUnglueAmounts[i] = 0;
}
}
// Return the collaterals and the final unglue amounts
return (finalUnglueAmounts);
}
/**
* @notice Retrieves the balance of an array of specified collateral tokens for the glue contract.
* @dev This function is used to get the balance of an array of specified collateral tokens for the glue contract.
*
* @param collaterals An array of addresses representing the collateral tokens.
* @return balances An array containing the corresponding balances.
*
* Use cases:
* - Retrieving the balance of an array of specified collateral tokens for the glue contract.
*/
function getBalances(address[] calldata collaterals) external view override returns (uint256[] memory balances) {
// Create an array for the balances
balances = new uint256[](collaterals.length);
// Process each collateral and get the balance
for (uint256 i = 0; i < collaterals.length; i++) {
// Get the balance of the collateral
balances[i] = getAssetBalance(collaterals[i], address(this));
}
// Return the collateral addresses and the balances
return (balances);
}
/**
* @notice Retrieves the balance of the sticky NFTs for the glue contract.
* @dev This function is used to get the balance of the sticky NFTs for the glue contract.
*
* @return stickyAmount The balance of the sticky NFTs.
*
* Use cases:
* - Retrieving the balance of the sticky NFTs for the glue contract.
*/
function getStickySupplyStored() external view override returns (uint256 stickyAmount) {
// Return the balance of the sticky token
return getNFTBalance(address(this));
}
/**
* @notice Retrieves the settings contract address.
* @dev This function is used to get the settings contract address.
*
* @return settings The address of the settings contract.
*
* Use cases:
* - Retrieving the settings contract address.
*/
function getSettings() external pure override returns (address settings) {
// Return the settings contract address
return SETTINGS;
}
/**
* @notice Retrieves the address of the GlueStick factory contract.
* @dev This function is used to get the address of the GlueStick factory contract.
*
* @return glueStick The address of the GlueStick factory contract.
*
* Use cases:
* - Retrieving the address of the GlueStick factory contract.
*/
function getGlueStick() external view override returns (address glueStick) {
// Return the glue stick address
return GLUE_STICK;
}
/**
* @notice Retrieves the address of the sticky token.
* @dev This function is used to get the address of the sticky token.
*
* @return stickyAsset The address of the sticky NFT collection.
*
* Use cases:
* - Retrieving the address of the sticky token.
*/
function getStickyAsset() external view override returns (address stickyAsset) {
// Return the sticky token address
return STICKY_ASSET;
}
/**
* @notice Retrieves if the glue is expanded with active Hooks.
* @dev This function is used to get if the glue is expanded with active Hooks:
* - BIO.HOOK: The glue is expanded with active Hooks.
* - BIO.NO_HOOK: The glue is not expanded with active Hooks.
* - BIO.UNCHECKED: The glue didn't have learned yet (before the first unglue interaction).
*
* @return hooksStatus The bio of the hooks status.
*
* Use cases:
* - Knowing if the glue is expanded with active Hooks for external interactions.
*/
function isExpanded() external view override returns (BIO hooksStatus) {
// Return the hooks status
return bio;
}
/**
* @notice Retrieves if the Sticky Asset is natively not burnable and
* if the sticky token is permanently stored in the contract.
* @dev This function is used to get if the Sticky Asset is natively not burnable,
* and if the sticky token is permanently stored in the contract.
*
* @return noNativeBurn A boolean representing if the sticky asset is natively not burnable.
* @return stickySupplyGlued A boolean representing if the sticky token is permanently stored in the contract.
*
* Use cases:
* - Knowing if the Sticky Asset is natively not burnable and if the sticky token is permanently stored in the contract.
*/
function getSelfLearning() external view override returns (bool noNativeBurn, bool stickySupplyGlued) {
// Return if not burnable and sticky supply stored flags
return (notBurnable, stickySupplyStored);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (interfaces/IERC1363.sol)
pragma solidity ^0.8.20;
import {IERC20} from "./IERC20.sol";
import {IERC165} from "./IERC165.sol";
/**
* @title IERC1363
* @dev Interface of the ERC-1363 standard as defined in the https://eips.ethereum.org/EIPS/eip-1363[ERC-1363].
*
* Defines an extension interface for ERC-20 tokens that supports executing code on a recipient contract
* after `transfer` or `transferFrom`, or code on a spender contract after `approve`, in a single transaction.
*/
interface IERC1363 is IERC20, IERC165 {
/*
* Note: the ERC-165 identifier for this interface is 0xb0202a11.
* 0xb0202a11 ===
* bytes4(keccak256('transferAndCall(address,uint256)')) ^
* bytes4(keccak256('transferAndCall(address,uint256,bytes)')) ^
* bytes4(keccak256('transferFromAndCall(address,address,uint256)')) ^
* bytes4(keccak256('transferFromAndCall(address,address,uint256,bytes)')) ^
* bytes4(keccak256('approveAndCall(address,uint256)')) ^
* bytes4(keccak256('approveAndCall(address,uint256,bytes)'))
*/
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferAndCall(address to, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @param data Additional data with no specified format, sent in call to `to`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param from The address which you want to send tokens from.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferFromAndCall(address from, address to, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param from The address which you want to send tokens from.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @param data Additional data with no specified format, sent in call to `to`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferFromAndCall(address from, address to, uint256 value, bytes calldata data) external returns (bool);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
* @param spender The address which will spend the funds.
* @param value The amount of tokens to be spent.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function approveAndCall(address spender, uint256 value) external returns (bool);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
* @param spender The address which will spend the funds.
* @param value The amount of tokens to be spent.
* @param data Additional data with no specified format, sent in call to `spender`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function approveAndCall(address spender, uint256 value, bytes calldata data) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC165.sol)
pragma solidity ^0.8.20;
import {IERC165} from "../utils/introspection/IERC165.sol";// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../token/ERC20/IERC20.sol";// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.2.0) (proxy/Clones.sol)
pragma solidity ^0.8.20;
import {Create2} from "../utils/Create2.sol";
import {Errors} from "../utils/Errors.sol";
/**
* @dev https://eips.ethereum.org/EIPS/eip-1167[ERC-1167] is a standard for
* deploying minimal proxy contracts, also known as "clones".
*
* > To simply and cheaply clone contract functionality in an immutable way, this standard specifies
* > a minimal bytecode implementation that delegates all calls to a known, fixed address.
*
* The library includes functions to deploy a proxy using either `create` (traditional deployment) or `create2`
* (salted deterministic deployment). It also includes functions to predict the addresses of clones deployed using the
* deterministic method.
*/
library Clones {
error CloneArgumentsTooLong();
/**
* @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
*
* This function uses the create opcode, which should never revert.
*/
function clone(address implementation) internal returns (address instance) {
return clone(implementation, 0);
}
/**
* @dev Same as {xref-Clones-clone-address-}[clone], but with a `value` parameter to send native currency
* to the new contract.
*
* NOTE: Using a non-zero value at creation will require the contract using this function (e.g. a factory)
* to always have enough balance for new deployments. Consider exposing this function under a payable method.
*/
function clone(address implementation, uint256 value) internal returns (address instance) {
if (address(this).balance < value) {
revert Errors.InsufficientBalance(address(this).balance, value);
}
assembly ("memory-safe") {
// Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes
// of the `implementation` address with the bytecode before the address.
mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))
// Packs the remaining 17 bytes of `implementation` with the bytecode after the address.
mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3))
instance := create(value, 0x09, 0x37)
}
if (instance == address(0)) {
revert Errors.FailedDeployment();
}
}
/**
* @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation`.
*
* This function uses the create2 opcode and a `salt` to deterministically deploy
* the clone. Using the same `implementation` and `salt` multiple times will revert, since
* the clones cannot be deployed twice at the same address.
*/
function cloneDeterministic(address implementation, bytes32 salt) internal returns (address instance) {
return cloneDeterministic(implementation, salt, 0);
}
/**
* @dev Same as {xref-Clones-cloneDeterministic-address-bytes32-}[cloneDeterministic], but with
* a `value` parameter to send native currency to the new contract.
*
* NOTE: Using a non-zero value at creation will require the contract using this function (e.g. a factory)
* to always have enough balance for new deployments. Consider exposing this function under a payable method.
*/
function cloneDeterministic(
address implementation,
bytes32 salt,
uint256 value
) internal returns (address instance) {
if (address(this).balance < value) {
revert Errors.InsufficientBalance(address(this).balance, value);
}
assembly ("memory-safe") {
// Cleans the upper 96 bits of the `implementation` word, then packs the first 3 bytes
// of the `implementation` address with the bytecode before the address.
mstore(0x00, or(shr(0xe8, shl(0x60, implementation)), 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000))
// Packs the remaining 17 bytes of `implementation` with the bytecode after the address.
mstore(0x20, or(shl(0x78, implementation), 0x5af43d82803e903d91602b57fd5bf3))
instance := create2(value, 0x09, 0x37, salt)
}
if (instance == address(0)) {
revert Errors.FailedDeployment();
}
}
/**
* @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
*/
function predictDeterministicAddress(
address implementation,
bytes32 salt,
address deployer
) internal pure returns (address predicted) {
assembly ("memory-safe") {
let ptr := mload(0x40)
mstore(add(ptr, 0x38), deployer)
mstore(add(ptr, 0x24), 0x5af43d82803e903d91602b57fd5bf3ff)
mstore(add(ptr, 0x14), implementation)
mstore(ptr, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73)
mstore(add(ptr, 0x58), salt)
mstore(add(ptr, 0x78), keccak256(add(ptr, 0x0c), 0x37))
predicted := and(keccak256(add(ptr, 0x43), 0x55), 0xffffffffffffffffffffffffffffffffffffffff)
}
}
/**
* @dev Computes the address of a clone deployed using {Clones-cloneDeterministic}.
*/
function predictDeterministicAddress(
address implementation,
bytes32 salt
) internal view returns (address predicted) {
return predictDeterministicAddress(implementation, salt, address(this));
}
/**
* @dev Deploys and returns the address of a clone that mimics the behavior of `implementation` with custom
* immutable arguments. These are provided through `args` and cannot be changed after deployment. To
* access the arguments within the implementation, use {fetchCloneArgs}.
*
* This function uses the create opcode, which should never revert.
*/
function cloneWithImmutableArgs(address implementation, bytes memory args) internal returns (address instance) {
return cloneWithImmutableArgs(implementation, args, 0);
}
/**
* @dev Same as {xref-Clones-cloneWithImmutableArgs-address-bytes-}[cloneWithImmutableArgs], but with a `value`
* parameter to send native currency to the new contract.
*
* NOTE: Using a non-zero value at creation will require the contract using this function (e.g. a factory)
* to always have enough balance for new deployments. Consider exposing this function under a payable method.
*/
function cloneWithImmutableArgs(
address implementation,
bytes memory args,
uint256 value
) internal returns (address instance) {
if (address(this).balance < value) {
revert Errors.InsufficientBalance(address(this).balance, value);
}
bytes memory bytecode = _cloneCodeWithImmutableArgs(implementation, args);
assembly ("memory-safe") {
instance := create(value, add(bytecode, 0x20), mload(bytecode))
}
if (instance == address(0)) {
revert Errors.FailedDeployment();
}
}
/**
* @dev Deploys and returns the address of a clone that mimics the behaviour of `implementation` with custom
* immutable arguments. These are provided through `args` and cannot be changed after deployment. To
* access the arguments within the implementation, use {fetchCloneArgs}.
*
* This function uses the create2 opcode and a `salt` to deterministically deploy the clone. Using the same
* `implementation`, `args` and `salt` multiple times will revert, since the clones cannot be deployed twice
* at the same address.
*/
function cloneDeterministicWithImmutableArgs(
address implementation,
bytes memory args,
bytes32 salt
) internal returns (address instance) {
return cloneDeterministicWithImmutableArgs(implementation, args, salt, 0);
}
/**
* @dev Same as {xref-Clones-cloneDeterministicWithImmutableArgs-address-bytes-bytes32-}[cloneDeterministicWithImmutableArgs],
* but with a `value` parameter to send native currency to the new contract.
*
* NOTE: Using a non-zero value at creation will require the contract using this function (e.g. a factory)
* to always have enough balance for new deployments. Consider exposing this function under a payable method.
*/
function cloneDeterministicWithImmutableArgs(
address implementation,
bytes memory args,
bytes32 salt,
uint256 value
) internal returns (address instance) {
bytes memory bytecode = _cloneCodeWithImmutableArgs(implementation, args);
return Create2.deploy(value, salt, bytecode);
}
/**
* @dev Computes the address of a clone deployed using {Clones-cloneDeterministicWithImmutableArgs}.
*/
function predictDeterministicAddressWithImmutableArgs(
address implementation,
bytes memory args,
bytes32 salt,
address deployer
) internal pure returns (address predicted) {
bytes memory bytecode = _cloneCodeWithImmutableArgs(implementation, args);
return Create2.computeAddress(salt, keccak256(bytecode), deployer);
}
/**
* @dev Computes the address of a clone deployed using {Clones-cloneDeterministicWithImmutableArgs}.
*/
function predictDeterministicAddressWithImmutableArgs(
address implementation,
bytes memory args,
bytes32 salt
) internal view returns (address predicted) {
return predictDeterministicAddressWithImmutableArgs(implementation, args, salt, address(this));
}
/**
* @dev Get the immutable args attached to a clone.
*
* - If `instance` is a clone that was deployed using `clone` or `cloneDeterministic`, this
* function will return an empty array.
* - If `instance` is a clone that was deployed using `cloneWithImmutableArgs` or
* `cloneDeterministicWithImmutableArgs`, this function will return the args array used at
* creation.
* - If `instance` is NOT a clone deployed using this library, the behavior is undefined. This
* function should only be used to check addresses that are known to be clones.
*/
function fetchCloneArgs(address instance) internal view returns (bytes memory) {
bytes memory result = new bytes(instance.code.length - 45); // revert if length is too short
assembly ("memory-safe") {
extcodecopy(instance, add(result, 32), 45, mload(result))
}
return result;
}
/**
* @dev Helper that prepares the initcode of the proxy with immutable args.
*
* An assembly variant of this function requires copying the `args` array, which can be efficiently done using
* `mcopy`. Unfortunately, that opcode is not available before cancun. A pure solidity implementation using
* abi.encodePacked is more expensive but also more portable and easier to review.
*
* NOTE: https://eips.ethereum.org/EIPS/eip-170[EIP-170] limits the length of the contract code to 24576 bytes.
* With the proxy code taking 45 bytes, that limits the length of the immutable args to 24531 bytes.
*/
function _cloneCodeWithImmutableArgs(
address implementation,
bytes memory args
) private pure returns (bytes memory) {
if (args.length > 24531) revert CloneArgumentsTooLong();
return
abi.encodePacked(
hex"61",
uint16(args.length + 45),
hex"3d81600a3d39f3363d3d373d3d3d363d73",
implementation,
hex"5af43d82803e903d91602b57fd5bf3",
args
);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol)
pragma solidity ^0.8.20;
/**
* @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
* behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
* external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
* function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
*
* The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
* reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
* case an upgrade adds a module that needs to be initialized.
*
* For example:
*
* [.hljs-theme-light.nopadding]
* ```solidity
* contract MyToken is ERC20Upgradeable {
* function initialize() initializer public {
* __ERC20_init("MyToken", "MTK");
* }
* }
*
* contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
* function initializeV2() reinitializer(2) public {
* __ERC20Permit_init("MyToken");
* }
* }
* ```
*
* TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
* possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
*
* CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
* that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
*
* [CAUTION]
* ====
* Avoid leaving a contract uninitialized.
*
* An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
* contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
* the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
*
* [.hljs-theme-light.nopadding]
* ```
* /// @custom:oz-upgrades-unsafe-allow constructor
* constructor() {
* _disableInitializers();
* }
* ```
* ====
*/
abstract contract Initializable {
/**
* @dev Storage of the initializable contract.
*
* It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions
* when using with upgradeable contracts.
*
* @custom:storage-location erc7201:openzeppelin.storage.Initializable
*/
struct InitializableStorage {
/**
* @dev Indicates that the contract has been initialized.
*/
uint64 _initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool _initializing;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;
/**
* @dev The contract is already initialized.
*/
error InvalidInitialization();
/**
* @dev The contract is not initializing.
*/
error NotInitializing();
/**
* @dev Triggered when the contract has been initialized or reinitialized.
*/
event Initialized(uint64 version);
/**
* @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
* `onlyInitializing` functions can be used to initialize parent contracts.
*
* Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any
* number of times. This behavior in the constructor can be useful during testing and is not expected to be used in
* production.
*
* Emits an {Initialized} event.
*/
modifier initializer() {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
// Cache values to avoid duplicated sloads
bool isTopLevelCall = !$._initializing;
uint64 initialized = $._initialized;
// Allowed calls:
// - initialSetup: the contract is not in the initializing state and no previous version was
// initialized
// - construction: the contract is initialized at version 1 (no reininitialization) and the
// current contract is just being deployed
bool initialSetup = initialized == 0 && isTopLevelCall;
bool construction = initialized == 1 && address(this).code.length == 0;
if (!initialSetup && !construction) {
revert InvalidInitialization();
}
$._initialized = 1;
if (isTopLevelCall) {
$._initializing = true;
}
_;
if (isTopLevelCall) {
$._initializing = false;
emit Initialized(1);
}
}
/**
* @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
* contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
* used to initialize parent contracts.
*
* A reinitializer may be used after the original initialization step. This is essential to configure modules that
* are added through upgrades and that require initialization.
*
* When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
* cannot be nested. If one is invoked in the context of another, execution will revert.
*
* Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
* a contract, executing them in the right order is up to the developer or operator.
*
* WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization.
*
* Emits an {Initialized} event.
*/
modifier reinitializer(uint64 version) {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing || $._initialized >= version) {
revert InvalidInitialization();
}
$._initialized = version;
$._initializing = true;
_;
$._initializing = false;
emit Initialized(version);
}
/**
* @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
* {initializer} and {reinitializer} modifiers, directly or indirectly.
*/
modifier onlyInitializing() {
_checkInitializing();
_;
}
/**
* @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.
*/
function _checkInitializing() internal view virtual {
if (!_isInitializing()) {
revert NotInitializing();
}
}
/**
* @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
* Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
* to any version. It is recommended to use this to lock implementation contracts that are designed to be called
* through proxies.
*
* Emits an {Initialized} event the first time it is successfully executed.
*/
function _disableInitializers() internal virtual {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing) {
revert InvalidInitialization();
}
if ($._initialized != type(uint64).max) {
$._initialized = type(uint64).max;
emit Initialized(type(uint64).max);
}
}
/**
* @dev Returns the highest version that has been initialized. See {reinitializer}.
*/
function _getInitializedVersion() internal view returns (uint64) {
return _getInitializableStorage()._initialized;
}
/**
* @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
*/
function _isInitializing() internal view returns (bool) {
return _getInitializableStorage()._initializing;
}
/**
* @dev Returns a pointer to the storage namespace.
*/
// solhint-disable-next-line var-name-mixedcase
function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
assembly {
$.slot := INITIALIZABLE_STORAGE
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
/**
* @dev Interface for the optional metadata functions from the ERC-20 standard.
*/
interface IERC20Metadata is IERC20 {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC-20 standard as defined in the ERC.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 value) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 value) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.2.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
import {IERC1363} from "../../../interfaces/IERC1363.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC-20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
/**
* @dev An operation with an ERC-20 token failed.
*/
error SafeERC20FailedOperation(address token);
/**
* @dev Indicates a failed `decreaseAllowance` request.
*/
error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*
* IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
* smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
* this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
* that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
forceApprove(token, spender, oldAllowance + value);
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
* value, non-reverting calls are assumed to be successful.
*
* IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
* smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
* this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
* that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
*/
function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
unchecked {
uint256 currentAllowance = token.allowance(address(this), spender);
if (currentAllowance < requestedDecrease) {
revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
}
forceApprove(token, spender, currentAllowance - requestedDecrease);
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*
* NOTE: If the token implements ERC-7674, this function will not modify any temporary allowance. This function
* only sets the "standard" allowance. Any temporary allowance will remain active, in addition to the value being
* set here.
*/
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Performs an {ERC1363} transferAndCall, with a fallback to the simple {ERC20} transfer if the target has no
* code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* Reverts if the returned value is other than `true`.
*/
function transferAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
if (to.code.length == 0) {
safeTransfer(token, to, value);
} else if (!token.transferAndCall(to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Performs an {ERC1363} transferFromAndCall, with a fallback to the simple {ERC20} transferFrom if the target
* has no code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* Reverts if the returned value is other than `true`.
*/
function transferFromAndCallRelaxed(
IERC1363 token,
address from,
address to,
uint256 value,
bytes memory data
) internal {
if (to.code.length == 0) {
safeTransferFrom(token, from, to, value);
} else if (!token.transferFromAndCall(from, to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Performs an {ERC1363} approveAndCall, with a fallback to the simple {ERC20} approve if the target has no
* code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* NOTE: When the recipient address (`to`) has no code (i.e. is an EOA), this function behaves as {forceApprove}.
* Opposedly, when the recipient address (`to`) has code, this function only attempts to call {ERC1363-approveAndCall}
* once without retrying, and relies on the returned value to be true.
*
* Reverts if the returned value is other than `true`.
*/
function approveAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
if (to.code.length == 0) {
forceApprove(token, to, value);
} else if (!token.approveAndCall(to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturnBool} that reverts if call fails to meet the requirements.
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
uint256 returnSize;
uint256 returnValue;
assembly ("memory-safe") {
let success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
// bubble errors
if iszero(success) {
let ptr := mload(0x40)
returndatacopy(ptr, 0, returndatasize())
revert(ptr, returndatasize())
}
returnSize := returndatasize()
returnValue := mload(0)
}
if (returnSize == 0 ? address(token).code.length == 0 : returnValue != 1) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturn} that silently catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
bool success;
uint256 returnSize;
uint256 returnValue;
assembly ("memory-safe") {
success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
returnSize := returndatasize()
returnValue := mload(0)
}
return success && (returnSize == 0 ? address(token).code.length > 0 : returnValue == 1);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/extensions/IERC721Enumerable.sol)
pragma solidity ^0.8.20;
import {IERC721} from "../IERC721.sol";
/**
* @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
* @dev See https://eips.ethereum.org/EIPS/eip-721
*/
interface IERC721Enumerable is IERC721 {
/**
* @dev Returns the total amount of tokens stored by the contract.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns a token ID owned by `owner` at a given `index` of its token list.
* Use along with {balanceOf} to enumerate all of ``owner``'s tokens.
*/
function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256);
/**
* @dev Returns a token ID at a given `index` of all the tokens stored by the contract.
* Use along with {totalSupply} to enumerate all tokens.
*/
function tokenByIndex(uint256 index) external view returns (uint256);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC721/IERC721.sol)
pragma solidity ^0.8.20;
import {IERC165} from "../../utils/introspection/IERC165.sol";
/**
* @dev Required interface of an ERC-721 compliant contract.
*/
interface IERC721 is IERC165 {
/**
* @dev Emitted when `tokenId` token is transferred from `from` to `to`.
*/
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
*/
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
*/
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
/**
* @dev Returns the number of tokens in ``owner``'s account.
*/
function balanceOf(address owner) external view returns (uint256 balance);
/**
* @dev Returns the owner of the `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function ownerOf(uint256 tokenId) external view returns (address owner);
/**
* @dev Safely transfers `tokenId` token from `from` to `to`.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon
* a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
/**
* @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
* are aware of the ERC-721 protocol to prevent tokens from being forever locked.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must have been allowed to move this token by either {approve} or
* {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon
* a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(address from, address to, uint256 tokenId) external;
/**
* @dev Transfers `tokenId` token from `from` to `to`.
*
* WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC-721
* or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
* understand this adds an external call which potentially creates a reentrancy vulnerability.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 tokenId) external;
/**
* @dev Gives permission to `to` to transfer `tokenId` token to another account.
* The approval is cleared when the token is transferred.
*
* Only a single account can be approved at a time, so approving the zero address clears previous approvals.
*
* Requirements:
*
* - The caller must own the token or be an approved operator.
* - `tokenId` must exist.
*
* Emits an {Approval} event.
*/
function approve(address to, uint256 tokenId) external;
/**
* @dev Approve or remove `operator` as an operator for the caller.
* Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
*
* Requirements:
*
* - The `operator` cannot be the address zero.
*
* Emits an {ApprovalForAll} event.
*/
function setApprovalForAll(address operator, bool approved) external;
/**
* @dev Returns the account approved for `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function getApproved(uint256 tokenId) external view returns (address operator);
/**
* @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
*
* See {setApprovalForAll}
*/
function isApprovedForAll(address owner, address operator) external view returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC721/IERC721Receiver.sol)
pragma solidity ^0.8.20;
/**
* @title ERC-721 token receiver interface
* @dev Interface for any contract that wants to support safeTransfers
* from ERC-721 asset contracts.
*/
interface IERC721Receiver {
/**
* @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
* by `operator` from `from`, this function is called.
*
* It must return its Solidity selector to confirm the token transfer.
* If any other value is returned or the interface is not implemented by the recipient, the transfer will be
* reverted.
*
* The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.
*/
function onERC721Received(
address operator,
address from,
uint256 tokenId,
bytes calldata data
) external returns (bytes4);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/utils/ERC721Holder.sol)
pragma solidity ^0.8.20;
import {IERC721Receiver} from "../IERC721Receiver.sol";
/**
* @dev Implementation of the {IERC721Receiver} interface.
*
* Accepts all token transfers.
* Make sure the contract is able to use its token with {IERC721-safeTransferFrom}, {IERC721-approve} or
* {IERC721-setApprovalForAll}.
*/
abstract contract ERC721Holder is IERC721Receiver {
/**
* @dev See {IERC721Receiver-onERC721Received}.
*
* Always returns `IERC721Receiver.onERC721Received.selector`.
*/
function onERC721Received(address, address, uint256, bytes memory) public virtual returns (bytes4) {
return this.onERC721Received.selector;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.2.0) (utils/Address.sol)
pragma solidity ^0.8.20;
import {Errors} from "./Errors.sol";
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev There's no code at `target` (it is not a contract).
*/
error AddressEmptyCode(address target);
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
if (address(this).balance < amount) {
revert Errors.InsufficientBalance(address(this).balance, amount);
}
(bool success, bytes memory returndata) = recipient.call{value: amount}("");
if (!success) {
_revert(returndata);
}
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason or custom error, it is bubbled
* up by this function (like regular Solidity function calls). However, if
* the call reverted with no returned reason, this function reverts with a
* {Errors.FailedCall} error.
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
if (address(this).balance < value) {
revert Errors.InsufficientBalance(address(this).balance, value);
}
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
* was not a contract or bubbling up the revert reason (falling back to {Errors.FailedCall}) in case
* of an unsuccessful call.
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata
) internal view returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
// only check if target is a contract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
if (returndata.length == 0 && target.code.length == 0) {
revert AddressEmptyCode(target);
}
return returndata;
}
}
/**
* @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
* revert reason or with a default {Errors.FailedCall} error.
*/
function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
return returndata;
}
}
/**
* @dev Reverts with returndata if present. Otherwise reverts with {Errors.FailedCall}.
*/
function _revert(bytes memory returndata) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
assembly ("memory-safe") {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert Errors.FailedCall();
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/Create2.sol)
pragma solidity ^0.8.20;
import {Errors} from "./Errors.sol";
/**
* @dev Helper to make usage of the `CREATE2` EVM opcode easier and safer.
* `CREATE2` can be used to compute in advance the address where a smart
* contract will be deployed, which allows for interesting new mechanisms known
* as 'counterfactual interactions'.
*
* See the https://eips.ethereum.org/EIPS/eip-1014#motivation[EIP] for more
* information.
*/
library Create2 {
/**
* @dev There's no code to deploy.
*/
error Create2EmptyBytecode();
/**
* @dev Deploys a contract using `CREATE2`. The address where the contract
* will be deployed can be known in advance via {computeAddress}.
*
* The bytecode for a contract can be obtained from Solidity with
* `type(contractName).creationCode`.
*
* Requirements:
*
* - `bytecode` must not be empty.
* - `salt` must have not been used for `bytecode` already.
* - the factory must have a balance of at least `amount`.
* - if `amount` is non-zero, `bytecode` must have a `payable` constructor.
*/
function deploy(uint256 amount, bytes32 salt, bytes memory bytecode) internal returns (address addr) {
if (address(this).balance < amount) {
revert Errors.InsufficientBalance(address(this).balance, amount);
}
if (bytecode.length == 0) {
revert Create2EmptyBytecode();
}
assembly ("memory-safe") {
addr := create2(amount, add(bytecode, 0x20), mload(bytecode), salt)
// if no address was created, and returndata is not empty, bubble revert
if and(iszero(addr), not(iszero(returndatasize()))) {
let p := mload(0x40)
returndatacopy(p, 0, returndatasize())
revert(p, returndatasize())
}
}
if (addr == address(0)) {
revert Errors.FailedDeployment();
}
}
/**
* @dev Returns the address where a contract will be stored if deployed via {deploy}. Any change in the
* `bytecodeHash` or `salt` will result in a new destination address.
*/
function computeAddress(bytes32 salt, bytes32 bytecodeHash) internal view returns (address) {
return computeAddress(salt, bytecodeHash, address(this));
}
/**
* @dev Returns the address where a contract will be stored if deployed via {deploy} from a contract located at
* `deployer`. If `deployer` is this contract's address, returns the same value as {computeAddress}.
*/
function computeAddress(bytes32 salt, bytes32 bytecodeHash, address deployer) internal pure returns (address addr) {
assembly ("memory-safe") {
let ptr := mload(0x40) // Get free memory pointer
// | | ↓ ptr ... ↓ ptr + 0x0B (start) ... ↓ ptr + 0x20 ... ↓ ptr + 0x40 ... |
// |-------------------|---------------------------------------------------------------------------|
// | bytecodeHash | CCCCCCCCCCCCC...CC |
// | salt | BBBBBBBBBBBBB...BB |
// | deployer | 000000...0000AAAAAAAAAAAAAAAAAAA...AA |
// | 0xFF | FF |
// |-------------------|---------------------------------------------------------------------------|
// | memory | 000000...00FFAAAAAAAAAAAAAAAAAAA...AABBBBBBBBBBBBB...BBCCCCCCCCCCCCC...CC |
// | keccak(start, 85) | ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ |
mstore(add(ptr, 0x40), bytecodeHash)
mstore(add(ptr, 0x20), salt)
mstore(ptr, deployer) // Right-aligned with 12 preceding garbage bytes
let start := add(ptr, 0x0b) // The hashed data starts at the final garbage byte which we will set to 0xff
mstore8(start, 0xff)
addr := and(keccak256(start, 85), 0xffffffffffffffffffffffffffffffffffffffff)
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/Errors.sol)
pragma solidity ^0.8.20;
/**
* @dev Collection of common custom errors used in multiple contracts
*
* IMPORTANT: Backwards compatibility is not guaranteed in future versions of the library.
* It is recommended to avoid relying on the error API for critical functionality.
*
* _Available since v5.1._
*/
library Errors {
/**
* @dev The ETH balance of the account is not enough to perform the operation.
*/
error InsufficientBalance(uint256 balance, uint256 needed);
/**
* @dev A call to an address target failed. The target may have reverted.
*/
error FailedCall();
/**
* @dev The deployment failed.
*/
error FailedDeployment();
/**
* @dev A necessary precompile is missing.
*/
error MissingPrecompile(address);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/IERC165.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC-165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[ERC].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[ERC section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}// SPDX-License-Identifier: BUSL-1.1
// https://github.com/glue-finance/glue/blob/main/LICENCE.txt
/**
██████╗ ██╗ ██╗ ██╗███████╗██████╗
██╔════╝ ██║ ██║ ██║██╔════╝██╔══██╗
██║ ███╗██║ ██║ ██║█████╗ ██║ ██║
██║ ██║██║ ██║ ██║██╔══╝ ██║ ██║
╚██████╔╝███████╗╚██████╔╝███████╗██████╔╝
╚═════╝ ╚══════╝ ╚═════╝ ╚══════╝╚═════╝
██╗ ██╗ ██████╗ ██████╗ ██╗ ██╗███████╗
██║ ██║██╔═══██╗██╔═══██╗██║ ██╔╝██╔════╝
███████║██║ ██║██║ ██║█████╔╝ ███████╗
██╔══██║██║ ██║██║ ██║██╔═██╗ ╚════██║
██║ ██║╚██████╔╝╚██████╔╝██║ ██╗███████║
╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝
██╗███╗ ██╗████████╗███████╗██████╗ ███████╗ █████╗ ██████╗███████╗
██║████╗ ██║╚══██╔══╝██╔════╝██╔══██╗██╔════╝██╔══██╗██╔════╝██╔════╝
██║██╔██╗ ██║ ██║ █████╗ ██████╔╝█████╗ ███████║██║ █████╗
██║██║╚██╗██║ ██║ ██╔══╝ ██╔══██╗██╔══╝ ██╔══██║██║ ██╔══╝
██║██║ ╚████║ ██║ ███████╗██║ ██║██║ ██║ ██║╚██████╗███████╗
╚═╝╚═╝ ╚═══╝ ╚═╝ ╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚═════╝╚══════╝
*/
pragma solidity ^0.8.28;
/**
* @title IGluedHooks
* @author BasedToschi
* @notice Extension interface that defines callback mechanisms for Sticky Assets to interact with the Glue Protocol
* @dev Provides a standardized set of methods that Sticky Assets must implement to receive and process callbacks
* during critical operations like collateral ungluing and token burning. This interface enables advanced features
* like fee collection, rebasing mechanisms, and custom logic execution during protocol interactions.
*/
interface IGluedHooks {
/**
* @notice Calculates the appropriate hook size for token operations
* @dev Called by the Glue Protocol to determine what percentage of tokens should be redirected to the token contract
* during ungluing operations. The return value represents a percentage in PRECISION units (1e18 = 100%)
*
* @param asset The address of the token being processed (collateral or sticky token)
* @param amount The amount of tokens being processed by the hook
* @return size The hook size as a percentage in PRECISION units (e.g. 1e18 = 100%, 5e17 = 50%)
*
* Use case: Implementing dynamic fee models, rebasing mechanisms, or treasury allocations during ungluing
*/
function hookSize(address asset, uint256 amount) external view returns (uint256 size);
/**
* @notice Main hook execution function called during ungluing operations
* @dev This function is invoked after tokens have been transferred to the sticky asset contract
* and gives the asset an opportunity to execute custom logic with the received tokens
*
* @param asset The address of the token transferred to the contract (ETH = address(0))
* @param amount The precise amount of tokens transferred to the contract
* @param tokenIds Array of token IDs for ERC721 operations (empty for ERC20 tokens)
* @param recipient The address of the recipient of the hook
*
* Use case: Implementing automatic token buybacks, liquidity provision, or reward distribution
*/
function executeHook(address asset, uint256 amount, uint256[] memory tokenIds, address recipient) external;
/**
* @notice Calculates the total impact of all hooks during ungluing operations
* @dev Provides an aggregated view of how hooks will affect the token distribution
* for a specific collateral and amount combination
*
* @param collateral The address of the collateral token being unglued
* @param collateralAmount The amount of collateral token being withdrawn from the glue
* @param stickyAmount The amount of sticky tokens being burned (for ERC20 only)
* @return size The combined impact of all hooks as a percentage in PRECISION units
*
* Use case: Client-side prediction of expected output amounts accounting for hooks
*/
function hooksImpact(address collateral, uint256 collateralAmount, uint256 stickyAmount) external view returns (uint256 size);
/**
* @notice Global hook enablement flag
* @dev Constant that indicates whether hook functionality is active for this token
*
* @return hook Boolean flag: true if hooks are enabled, false if disabled
*
* Use case: Protocol optimizations to skip hook processing entirely for tokens without hooks
*/
function hasHook() external view returns (bool hook);
}// SPDX-License-Identifier: BUSL-1.1
// https://github.com/glue-finance/glue/blob/main/LICENCE.txt
/**
██████╗ ██╗ ██╗ ██╗███████╗██████╗ ██╗ ██████╗ █████╗ ███╗ ██╗
██╔════╝ ██║ ██║ ██║██╔════╝██╔══██╗██║ ██╔═══██╗██╔══██╗████╗ ██║
██║ ███╗██║ ██║ ██║█████╗ ██║ ██║██║ ██║ ██║███████║██╔██╗ ██║
██║ ██║██║ ██║ ██║██╔══╝ ██║ ██║██║ ██║ ██║██╔══██║██║╚██╗██║
╚██████╔╝███████╗╚██████╔╝███████╗██████╔╝███████╗╚██████╔╝██║ ██║██║ ╚████║
╚═════╝ ╚══════╝ ╚═════╝ ╚══════╝╚═════╝ ╚══════╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═══╝
██████╗ ███████╗ ██████╗███████╗██╗██╗ ██╗███████╗██████╗
██╔══██╗██╔════╝██╔════╝██╔════╝██║██║ ██║██╔════╝██╔══██╗
██████╔╝█████╗ ██║ █████╗ ██║██║ ██║█████╗ ██████╔╝
██╔══██╗██╔══╝ ██║ ██╔══╝ ██║╚██╗ ██╔╝██╔══╝ ██╔══██╗
██║ ██║███████╗╚██████╗███████╗██║ ╚████╔╝ ███████╗██║ ██║
╚═╝ ╚═╝╚══════╝ ╚═════╝╚══════╝╚═╝ ╚═══╝ ╚══════╝╚═╝ ╚═╝
██╗███╗ ██╗████████╗███████╗██████╗ ███████╗ █████╗ ██████╗███████╗
██║████╗ ██║╚══██╔══╝██╔════╝██╔══██╗██╔════╝██╔══██╗██╔════╝██╔════╝
██║██╔██╗ ██║ ██║ █████╗ ██████╔╝█████╗ ███████║██║ █████╗
██║██║╚██╗██║ ██║ ██╔══╝ ██╔══██╗██╔══╝ ██╔══██║██║ ██╔══╝
██║██║ ╚████║ ██║ ███████╗██║ ██║██║ ██║ ██║╚██████╗███████╗
╚═╝╚═╝ ╚═══╝ ╚═╝ ╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚═════╝╚══════╝
*/
pragma solidity ^0.8.28;
/**
* @title IGluedLoanReceiver
* @author BasedToschi
* @notice Interface that must be implemented by contracts receiving glued loans from the Glue Protocol
* @dev This interface standardizes the callback mechanism used by the Glue Protocol's flash loan feature,
* allowing seamless integration with DeFi protocols and custom applications
*/
interface IGluedLoanReceiver {
/**
* @notice Executes custom logic after receiving a flash loan from the Glue Protocol
* @dev This function is called by the protocol after transferring borrowed assets to the receiver contract
* The receiver must ensure that the borrowed assets plus fees are returned to the glue contracts before this
* function completes execution, otherwise the transaction will revert
*
* @param glues Array of glue contract addresses that provided the loaned assets
* @param collateral Address of the borrowed token (address(0) for ETH)
* @param expectedAmounts Array of amounts expected to be repaid to each glue contract
* @param params Arbitrary data passed by the loan initiator for custom execution
* @return loanSuccess Boolean indicating whether the operation was successful
*
* Use cases:
* - Flash Loans across multiple glues
* - Capital-efficient arbitrage across DEXes
* - Liquidation operations in lending protocols
* - Complex cross-protocol interactions requiring upfront capital
* - Temporary liquidity for atomic multi-step operations
* - Collateral swaps without requiring pre-owned capital
*/
function executeOperation(
address[] memory glues,
address collateral,
uint256[] memory expectedAmounts,
bytes memory params
) external returns (bool loanSuccess);
}// SPDX-License-Identifier: BUSL-1.1
// https://github.com/glue-finance/glue/blob/main/LICENCE.txt
/**
██████╗ ██╗ ██╗ ██╗███████╗██████╗
██╔════╝ ██║ ██║ ██║██╔════╝██╔══██╗
██║ ███╗██║ ██║ ██║█████╗ ██║ ██║
██║ ██║██║ ██║ ██║██╔══╝ ██║ ██║
╚██████╔╝███████╗╚██████╔╝███████╗██████╔╝
╚═════╝ ╚══════╝ ╚═════╝ ╚══════╝╚═════╝
███████╗███████╗████████╗████████╗██╗███╗ ██╗ ██████╗ ███████╗
██╔════╝██╔════╝╚══██╔══╝╚══██╔══╝██║████╗ ██║██╔════╝ ██╔════╝
███████╗█████╗ ██║ ██║ ██║██╔██╗ ██║██║ ███╗███████╗
╚════██║██╔══╝ ██║ ██║ ██║██║╚██╗██║██║ ██║╚════██║
███████║███████╗ ██║ ██║ ██║██║ ╚████║╚██████╔╝███████║
╚══════╝╚══════╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═══╝ ╚═════╝ ╚══════╝
██╗███╗ ██╗████████╗███████╗██████╗ ███████╗ █████╗ ██████╗███████╗
██║████╗ ██║╚══██╔══╝██╔════╝██╔══██╗██╔════╝██╔══██╗██╔════╝██╔════╝
██║██╔██╗ ██║ ██║ █████╗ ██████╔╝█████╗ ███████║██║ █████╗
██║██║╚██╗██║ ██║ ██╔══╝ ██╔══██╗██╔══╝ ██╔══██║██║ ██╔══╝
██║██║ ╚████║ ██║ ███████╗██║ ██║██║ ██║ ██║╚██████╗███████╗
╚═╝╚═╝ ╚═══╝ ╚═╝ ╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚═════╝╚══════╝
*/
pragma solidity ^0.8.28;
/**
* @title IGluedSettings
* @dev Interface for managing protocol configuration parameters, fee structures, and administrative controls.
* This contract serves as the central configuration hub for the Glued protocol, allowing for
* dynamic adjustment of fees, designation of fee recipients, and governance of protocol parameters.
* The interface includes functions for ownership management, fee updates, address assignments,
* and provides various status checks for protocol governance.
*/
interface IGluedSettings {
/**
--------------------------------------------------------------------------------------------------------
▗▄▄▄▖▗▖ ▗▖▗▖ ▗▖ ▗▄▄▖▗▄▄▄▖▗▄▄▄▖ ▗▄▖ ▗▖ ▗▖ ▗▄▄▖
▐▌ ▐▌ ▐▌▐▛▚▖▐▌▐▌ █ █ ▐▌ ▐▌▐▛▚▖▐▌▐▌
▐▛▀▀▘▐▌ ▐▌▐▌ ▝▜▌▐▌ █ █ ▐▌ ▐▌▐▌ ▝▜▌ ▝▀▚▖
▐▌ ▝▚▄▞▘▐▌ ▐▌▝▚▄▄▖ █ ▗▄█▄▖▝▚▄▞▘▐▌ ▐▌▗▄▄▞▘
01000110 01110101 01101110 01100011 01110100
01101001 01101111 01101110 01110011
*/
// █████╗ Management Addresses
// ╚════╝ Owner: Can update most of the parameters
// Team: Can receive portion of protocol fees
// Glue: Can receive glue protocol fees
/**
* @notice Transfers governance control to a new owner address
* @dev Critical operation that shifts all admin capabilities to the provided address
*
* @param newOwner The address of the new contract owner that will receive full administrative control
*
* Use case: Transferring protocol control during governance transitions or to multisig/DAO control
*/
function transferOwnership(address newOwner) external;
/**
* @notice Sets the team address that receives remaining protocol fee portion
* @dev Only the current team address can update this to ensure secure transitions
*
* @param newTeamAddress The new address to receive the team's share of protocol fees
*
* Use case: Transitioning team treasury management or updating protocol revenue destinations
*/
function setTeamAddress(address newTeamAddress) external;
/**
* @notice Sets the address that receives the main glue fee portion
* @dev Updates the destination for glue protocol fees, impacting protocol revenue distribution
*
* @param newGlueFeeAddress The new address to receive the glue fee revenue share
*
* Use case: Redirecting protocol revenue to new treasury contracts or revenue management systems
*/
function setGlueFeeAddress(address newGlueFeeAddress) external;
/**
* @notice Permanently renounces ownership of the contract
* @dev Irreversibly sets owner to address(0), removing all governance capabilities
*
* Use case: Protocol decentralization milestone or transitioning to fully immutable operations
*/
function renounceOwnership() external;
// █████╗ Protocol Fees
// ╚════╝ The total fee that affect expanded glue products
// The Glue V1 protocol fee is not affected by this options, that one is fixed at 0.1%
/**
* @notice Updates the expanded protocol fee percentage
* @dev Controls the fee applied to expanded glue protocol operations, bounded by min/max safety limits
*
* @param newExpProtocolFee The new expanded protocol fee percentage (in PRECISION units)
*
* Use case: Fee adjustment in response to market conditions or protocol revenue requirements
*/
function updateExpProtocolFee(uint256 newExpProtocolFee) external;
/**
* @notice Updates the swap protocol fee percentage
* @dev Controls the fee applied to swap operations, bounded by min/max safety limits
*
* @param newSwapProtocolFee The new swap protocol fee percentage (in PRECISION units)
*
* Use case: Fee adjustment in response to market conditions or protocol revenue requirements
*/
function updateSwapProtocolFee(uint256 newSwapProtocolFee) external;
// █████╗ Glue Cut
// ╚════╝ The portion of the protocol fees that are distributed to the glue fee address
/**
* @notice Updates the glue fee percentage for the Glue V1 protocol
* @dev Sets the percentage of protocol fees distributed to the glue fee receiver
*
* @param newGlueFee The new main glue fee percentage (in PRECISION units)
*
* Use case: Adjusting fee distribution between protocol stakeholders based on governance decisions
*/
function updateGlueFee(uint256 newGlueFee) external;
/**
* @notice Updates the glue expansions fee percentage
* @dev Controls the fee percentage applied specifically to protocol expansion operations
*
* @param newGlueExpFee The new glue expansion fee percentage (in PRECISION units)
*
* Use case: Fine-tuning economics for protocol expansion
*/
function updateGlueExpFee(uint256 newGlueExpFee) external;
/**
* @notice Updates the glue swap fee percentage
* @dev Controls the fee percentage applied specifically to protocol swap operations
*
* @param newGlueSwapFee The new glue swap fee percentage (in PRECISION units)
*
* Use case: Fine-tuning economics for protocol swap operations
*/
function updateGlueSwapFee(uint256 newGlueSwapFee) external;
// █████╗ Granular Permissions
// ╚════╝ The permissions that can be permanently removed to lock the protocol parameters
/**
* @notice Permanently removes the ability to modify expanded protocol fees
* @dev Irreversibly locks the expanded protocol fee parameter
*
* Use case: Granular control over protocol parameters
*/
function removeExpProtocolFeeOwnership() external;
/**
* @notice Permanently removes the ability to modify the swap protocol fee
* @dev Irreversibly locks the swap protocol fee parameter
*
* Use case: Granular control over protocol parameters
*/
function removeSwapProtocolFeeOwnership() external;
/**
* @notice Permanently removes the ability to change the glue fee address
* @dev Irreversibly locks the glue fee recipient address
*
* Use case: Granular control over protocol parameters
*/
function removeGlueOwnership() external;
/**
* @notice Permanently removes the ability to modify the glue fee percentage
* @dev Irreversibly locks the glue fee parameter
*
* Use case: Granular control over protocol parameters
*/
function removeGlueFeeOwnership() external;
/**
* @notice Permanently removes the ability to modify the glue expansion fee
* @dev Irreversibly locks the glue expansion fee parameter
*
* Use case: Granular control over protocol parameters
*/
function removeGlueExpFeeOwnership() external;
/**
* @notice Permanently removes the ability to modify the glue swap fee
* @dev Irreversibly locks the glue swap fee parameter
*
* Use case: Granular control over protocol parameters
*/
function removeGlueSwapFeeOwnership() external;
/**
--------------------------------------------------------------------------------------------------------
▗▄▄▖ ▗▄▄▄▖ ▗▄▖ ▗▄▄▄
▐▌ ▐▌▐▌ ▐▌ ▐▌▐▌ █
▐▛▀▚▖▐▛▀▀▘▐▛▀▜▌▐▌ █
▐▌ ▐▌▐▙▄▄▖▐▌ ▐▌▐▙▄▄▀
01010010 01100101
01100001 01100100
*/
// █████╗ Management Addresses
// ╚════╝ Owner: Can update most of the parameters
// Team: Can receive portion of protocol fees
// Glue: Can receive glue protocol fees
/**
* @notice Retrieves the current owner address
* @dev Provides public access to the address of the contract owner
*
* @return owner The current owner address
*
* Use case: Integration with protocol dashboards or verification of governance authority
*/
function getOwner() external view returns (address owner);
/**
* @notice Retrieves the current team address
* @dev Provides public access to the address receiving the team's protocol fee share
*
* @return team The current team address configured to receive protocol fees
*
* Use case: Integration with protocol dashboards or verification of fee destinations
*/
function getTeamAddress() external view returns (address team);
/**
* @notice Retrieves the current glue fee address
* @dev Provides public access to the address receiving the glue portion of protocol fees
*
* @return glue The current glue fee address configured to receive protocol fees
*
* Use case: Protocol analytics, fee flow verification, or integration with monitoring systems
*/
function getGlue() external view returns (address glue);
// █████╗ Protocol Fees
// ╚════╝ The total fee that affect expanded glue products
// The Glue V1 protocol fee is not affected by this options, that one is fixed at 0.1%
/**
* @notice Retrieves the current expanded protocol fee percentage
* @dev Provides public access to the fee rate applied to expanded protocol operations
*
* @return expansionsFee The current expansions protocol fee percentage (in PRECISION units)
*
* Use case: Fee calculation in expansion modules or client-side fee estimations
*/
function getExpansionsFee() external view returns (uint256 expansionsFee);
/**
* @notice Retrieves the current swap protocol fee percentage
* @dev Provides public access to the fee rate applied to swap operations
*
* @return tradingFee The current swap protocol fee percentage (in PRECISION units)
*
* Use case: Fee calculation in swap modules or client-side fee estimations
*/
function getTradingFee() external view returns (uint256 tradingFee);
// █████╗ Glue Cut
// ╚════╝ The portion of the protocol fees that are distributed to the glue fee address
/**
* @notice Retrieves the current glue fee percentage
* @dev Provides the percentage of protocol fees allocated to the glue fee address
*
* @return glueProtocolCut The current glue fee percentage (in PRECISION units)
*
* Use case: Fee distribution calculations or protocol revenue projections
*/
function getGlueProtocolCut() external view returns (uint256 glueProtocolCut);
/**
* @notice Retrieves the current glue expansion fee percentage
* @dev Provides the percentage applied specifically to expansion operations
*
* @return glueExpansionsCut The current glue expansion fee percentage (in PRECISION units)
*
* Use case: Protocol expansion planning or economic impact analysis
*/
function getGlueExpansionsCut() external view returns (uint256 glueExpansionsCut);
/**
* @notice Retrieves the current glue swap fee percentage
* @dev Provides the percentage applied specifically to swap operations
*
* @return glueTradingCut The current glue swap fee percentage (in PRECISION units)
*
* Use case: Protocol swap planning or economic impact analysis
*/
function getGlueTradingCut() external view returns (uint256 glueTradingCut);
// █████╗ Batch Info
// ╚════╝ Functions designed to batch information retrieval for protocols interactions and UX
/**
* @notice Retrieves complete protocol fee configuration information
* @dev Consolidated getter for core fee distribution parameters in a single call
*
* @return glueProtocolCut The portion of the protocol fees that are distributed to the glue fee address
* @return glue The address receiving the glue portion of fees
* @return team The address receiving the team portion of fees
*
* Use case: Efficient fee data retrieval for protocol operations and integrations
*/
function getProtocolFeeInfo() external view returns (uint256 glueProtocolCut, address glue, address team);
/**
* @notice Retrieves extended protocol fee configuration including expansion parameters
* @dev Consolidated getter for all fee parameters in a single call
*
* @return expansionsFee The current expanded protocol fee percentage
* @return glueExpansionsCut The current glue expansion fee percentage
* @return glue The address receiving the glue portion of fees
* @return team The address receiving the team portion of fees
*
* Use case: Complete fee data retrieval for expansion modules and advanced protocol integrations
*/
function getExpansionsFeeInfo() external view returns (uint256 expansionsFee, uint256 glueExpansionsCut, address glue, address team);
/**
* @notice Retrieves extended protocol fee configuration including swap parameters
* @dev Consolidated getter for all fee parameters in a single call
*
* @return tradingFee The current swap protocol fee percentage
* @return glueTradingCut The current glue swap fee percentage
* @return glue The address receiving the glue portion of fees
* @return team The address receiving the team portion of fees
*
* Use case: Complete fee data retrieval for swap modules and advanced protocol integrations
*/
function getTradingFeeInfo() external view returns (uint256 tradingFee, uint256 glueTradingCut, address glue, address team);
/**
* @notice Retrieves the current state of all protocol governance permissions
* @dev Provides a consolidated view of which protocol parameters can still be modified
*
* @return expProtocolFeeRenounced The changeable status of expanded protocol fee
* @return swapProtocolFeeRenounced The changeable status of swap protocol fee
* @return glueProtocolCutRenounced The changeable status of glue protocol cut
* @return glueExpansionsCutRenounced The changeable status of glue expansion cut
* @return glueTradingCutRenounced The changeable status of glue trading cut
* @return glueAddressRenounced The changeable status of glue fee receiver
*
* Use case: Protocol governance dashboards or immutability verification for integrators
*/
function getGlueOwnershipStatus() external view returns (bool expProtocolFeeRenounced, bool swapProtocolFeeRenounced, bool glueProtocolCutRenounced, bool glueExpansionsCutRenounced, bool glueTradingCutRenounced, bool glueAddressRenounced);
// █████╗ Fee Ranges
// ╚════╝ Information about the minimum and maximum values for each fees and glue cuts
/**
* @notice Retrieves the current expanded protocol fee range
* @dev Provides public access to the minimum and maximum expanded protocol fee values
*
* @return minFee The minimum expansions fee percentage
* @return maxFee The maximum expansions fee percentage
*
* Use case: Fee calculation in expansion modules or client-side fee estimations
*/
function getExpansionsFeeRange() external pure returns (uint256 minFee, uint256 maxFee);
/**
* @notice Retrieves the current swap protocol fee range
* @dev Provides public access to the minimum and maximum swap protocol fee values
*
* @return minFee The minimum trading fee percentage
* @return maxFee The maximum trading fee percentage
*
* Use case: Fee calculation in swap modules or client-side fee estimations
*/
function getTradingFeeRange() external pure returns (uint256 minFee, uint256 maxFee);
/**
* @notice Retrieves the current glue protocol cut range
* @dev Provides public access to the minimum and maximum glue protocol cut values
*
* @return minCut The minimum glue cut from protocol fee percentage
* @return maxCut The maximum glue cut from protocol fee percentage
*
* Use case: Fee distribution calculations or protocol revenue projections
*/
function getGlueProtocolCutRange() external pure returns (uint256 minCut, uint256 maxCut);
/**
* @notice Retrieves the current glue expansion fee range
* @dev Provides public access to the minimum and maximum glue expansion fee values
*
* @return minCut The minimum glue cut from expansion fee percentage
* @return maxCut The maximum glue cut from expansion fee percentage
*
* Use case: Fee distribution calculations or protocol revenue projections
*/
function getGlueExpansionsCutRange() external pure returns (uint256 minCut, uint256 maxCut);
/**
* @notice Retrieves the current glue trading fee range
* @dev Provides public access to the minimum and maximum glue trading fee values
*
* @return minCut The minimum glue cut from trading fee percentage
* @return maxCut The maximum glue cut from trading fee percentage
*
* Use case: Fee distribution calculations or protocol revenue projections
*/
function getGlueTradingCutRange() external pure returns (uint256 minCut, uint256 maxCut);
/**
--------------------------------------------------------------------------------------------------------
▗▄▄▄▖▗▄▄▖ ▗▄▄▖ ▗▄▖ ▗▄▄▖ ▗▄▄▖
▐▌ ▐▌ ▐▌▐▌ ▐▌▐▌ ▐▌▐▌ ▐▌▐▌
▐▛▀▀▘▐▛▀▚▖▐▛▀▚▖▐▌ ▐▌▐▛▀▚▖ ▝▀▚▖
▐▙▄▄▖▐▌ ▐▌▐▌ ▐▌▝▚▄▞▘▐▌ ▐▌▗▄▄▞▘
01100101 01110010 01110010
01101111 01110010 01110011
*/
/**
* @dev Error thrown when the caller does not have the permission to process the request
*/
error OwnershipNotGranted();
/**
* @dev Error thrown when the inputs are invalid
*/
error InvalidInputs();
/**
--------------------------------------------------------------------------------------------------------
▗▄▄▄▖▗▖ ▗▖▗▄▄▄▖▗▖ ▗▖▗▄▄▄▖▗▄▄▖
▐▌ ▐▌ ▐▌▐▌ ▐▛▚▖▐▌ █ ▐▌
▐▛▀▀▘▐▌ ▐▌▐▛▀▀▘▐▌ ▝▜▌ █ ▝▀▚▖
▐▙▄▄▖ ▝▚▞▘ ▐▙▄▄▖▐▌ ▐▌ █ ▗▄▄▞▘
01000101 01010110 01000101
01001110 01010100 01010011
*/
/**
* @dev Emitted when ownership of the contract is transferred.
* @param previousOwner Address of the former owner.
* @param newOwner Address of the new owner.
*/
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Emitted when the team treasury address is changed.
* @param previousTeamAddress The former team treasury address.
* @param newTeamAddress The new team treasury address.
*/
event TeamAddressUpdated(address indexed previousTeamAddress, address indexed newTeamAddress);
/**
* @dev Emitted when the Glue fee recipient address is changed.
* @param previousGlueFeeAddress The former fee recipient address.
* @param newGlueFeeAddress The new fee recipient address.
*/
event GlueFeeAddressUpdated(address indexed previousGlueFeeAddress, address indexed newGlueFeeAddress);
/**
* @dev Emitted when the exponential protocol fee rate is updated.
* @param newExpProtocolFee The new exponential protocol fee value.
*/
event ExpProtocolFeeUpdated(uint256 newExpProtocolFee);
/**
* @dev Emitted when the swap protocol fee rate is updated.
* @param newSwapProtocolFee The new swap protocol fee value.
*/
event SwapProtocolFeeUpdated(uint256 newSwapProtocolFee);
/**
* @dev Emitted when the base Glue fee rate is updated.
* @param newGlueFee The new base Glue fee value.
*/
event GlueFeeUpdated(uint256 newGlueFee);
/**
* @dev Emitted when the exponential Glue fee rate is updated.
* @param newGlueExpFee The new exponential Glue fee value.
*/
event GlueExpFeeUpdated(uint256 newGlueExpFee);
/**
* @dev Emitted when the swap Glue fee rate is updated.
* @param newGlueSwapFee The new swap Glue fee value.
*/
event GlueSwapFeeUpdated(uint256 newGlueSwapFee);
/**
* @dev Emitted when the ability to modify exponential protocol fees is permanently removed.
*/
event ExpProtocolFeeOwnershipRemoved();
/**
* @dev Emitted when the ability to modify swap protocol fees is permanently removed.
*/
event SwapProtocolFeeOwnershipRemoved();
/**
* @dev Emitted when the ability to modify Glue ownership parameters is permanently removed.
*/
event GlueOwnershipRemoved();
/**
* @dev Emitted when the ability to modify Glue fee parameters is permanently removed.
*/
event GlueFeeOwnershipRemoved();
/**
* @dev Emitted when the ability to modify exponential Glue fee parameters is permanently removed.
*/
event GlueExpFeeOwnershipRemoved();
/**
* @dev Emitted when the ability to modify swap Glue fee parameters is permanently removed.
*/
event GlueSwapFeeOwnershipRemoved();
}// SPDX-License-Identifier: BUSL-1.1
// https://github.com/glue-finance/glue/blob/main/LICENCE.txt
/**
██████╗ ██╗ ██╗ ██╗███████╗
██╔════╝ ██║ ██║ ██║██╔════╝
██║ ███╗██║ ██║ ██║█████╗
██║ ██║██║ ██║ ██║██╔══╝
╚██████╔╝███████╗╚██████╔╝███████╗
╚═════╝ ╚══════╝ ╚═════╝ ╚══════╝
██╗███╗ ██╗████████╗███████╗██████╗ ███████╗ █████╗ ██████╗███████╗
██║████╗ ██║╚══██╔══╝██╔════╝██╔══██╗██╔════╝██╔══██╗██╔════╝██╔════╝
██║██╔██╗ ██║ ██║ █████╗ ██████╔╝█████╗ ███████║██║ █████╗
██║██║╚██╗██║ ██║ ██╔══╝ ██╔══██╗██╔══╝ ██╔══██║██║ ██╔══╝
██║██║ ╚████║ ██║ ███████╗██║ ██║██║ ██║ ██║╚██████╗███████╗
╚═╝╚═╝ ╚═══╝ ╚═╝ ╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚═════╝╚══════╝
███████╗ ██████╗ ██████╗ ███╗ ██╗███████╗████████╗███████╗
██╔════╝██╔═══██╗██╔══██╗ ████╗ ██║██╔════╝╚══██╔══╝██╔════╝
█████╗ ██║ ██║██████╔╝ ██╔██╗ ██║█████╗ ██║ ███████╗
██╔══╝ ██║ ██║██╔══██╗ ██║╚██╗██║██╔══╝ ██║ ╚════██║
██║ ╚██████╔╝██║ ██║ ██║ ╚████║██║ ██║ ███████║
╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═══╝╚═╝ ╚═╝ ╚══════╝
*/
pragma solidity ^0.8.28;
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
/**
██████╗ ██╗ ██╗██████╗ ███╗ ██╗ █████╗ ██████╗ ██╗ ███████╗
██╔══██╗██║ ██║██╔══██╗████╗ ██║██╔══██╗██╔══██╗██║ ██╔════╝
██████╔╝██║ ██║██████╔╝██╔██╗ ██║███████║██████╔╝██║ █████╗
██╔══██╗██║ ██║██╔══██╗██║╚██╗██║██╔══██║██╔══██╗██║ ██╔══╝
██████╔╝╚██████╔╝██║ ██║██║ ╚████║██║ ██║██████╔╝███████╗███████╗
╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═══╝╚═╝ ╚═╝╚═════╝ ╚══════╝╚══════╝
███╗ ██╗███████╗████████╗███████╗
████╗ ██║██╔════╝╚══██╔══╝██╔════╝
██╔██╗ ██║█████╗ ██║ ███████╗
██║╚██╗██║██╔══╝ ██║ ╚════██║
██║ ╚████║██║ ██║ ███████║
╚═╝ ╚═══╝╚═╝ ╚═╝ ╚══════╝
* @title IERC721Burnable
* @author @BasedToschi
* @notice Interface for ERC721 tokens that support burning functionality
* @dev Extends the standard ERC721 interface with a burn function to destroy tokens
*/
interface IERC721Burnable is IERC721 {
/**
* @notice Burns (destroys) the token with the given ID
* @dev The caller must own the token or be an approved operator
* @param tokenId The ID of the token to burn
*/
function burn(uint256 tokenId) external;
}
/**
██████╗ ██╗ ██╗ ██╗███████╗
██╔════╝ ██║ ██║ ██║██╔════╝
██║ ███╗██║ ██║ ██║█████╗
██║ ██║██║ ██║ ██║██╔══╝
╚██████╔╝███████╗╚██████╔╝███████╗
╚═════╝ ╚══════╝ ╚═════╝ ╚══════╝
███████╗████████╗██╗ ██████╗██╗ ██╗
██╔════╝╚══██╔══╝██║██╔════╝██║ ██╔╝
███████╗ ██║ ██║██║ █████╔╝
╚════██║ ██║ ██║██║ ██╔═██╗
███████║ ██║ ██║╚██████╗██║ ██╗
╚══════╝ ╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝
* @title IGlueStickERC721
* @author @BasedToschi
* @notice Interface defining the factory contract API for creating and managing ERC721 NFT collection glue instances
* @dev This interface establishes the contract API for the GlueStickERC721 factory,
* which handles creation of glue contracts for NFT collections, batch operations,
* and cross-glue flash loans in the NFT branch of the Glue Protocol
*/
interface IGlueStickERC721 {
/**
--------------------------------------------------------------------------------------------------------
▗▄▄▖▗▄▄▄▖▗▄▄▄▖▗▖ ▗▖▗▄▄▖
▐▌ ▐▌ █ ▐▌ ▐▌▐▌ ▐▌
▝▀▚▖▐▛▀▀▘ █ ▐▌ ▐▌▐▛▀▘
▗▄▄▞▘▐▙▄▄▖ █ ▝▚▄▞▘▐▌
01010011 01100101 01110100
01110101 01110000
*/
/**
* @dev Struct for managing flash loan data across multiple glue contracts
* @notice Encapsulates all data required for multi-glue flash loan operations
*/
struct LoanData {
uint256 count; /// @notice Number of glue contracts participating in the loan
uint256[] toBorrow; /// @notice Amount to borrow from each glue contract
uint256[] expectedAmounts; /// @notice Expected repayment amount for each loan (including fees)
uint256[] expectedBalances; /// @notice Expected final balance in each glue after repayment
}
/**
--------------------------------------------------------------------------------------------------------
▗▄▄▄▖▗▖ ▗▖▗▖ ▗▖ ▗▄▄▖▗▄▄▄▖▗▄▄▄▖ ▗▄▖ ▗▖ ▗▖ ▗▄▄▖
▐▌ ▐▌ ▐▌▐▛▚▖▐▌▐▌ █ █ ▐▌ ▐▌▐▛▚▖▐▌▐▌
▐▛▀▀▘▐▌ ▐▌▐▌ ▝▜▌▐▌ █ █ ▐▌ ▐▌▐▌ ▝▜▌ ▝▀▚▖
▐▌ ▝▚▄▞▘▐▌ ▐▌▝▚▄▄▖ █ ▗▄█▄▖▝▚▄▞▘▐▌ ▐▌▗▄▄▞▘
01000110 01110101 01101110 01100011 01110100
01101001 01101111 01101110 01110011
*/
/**
* @notice Creates a new GlueERC721 contract for a specified NFT collection
* @dev Validates the NFT collection for compatibility, creates a deterministic clone
* of the implementation contract, initializes it with the collection address, and
* registers it in the protocol registry. The created glue instance becomes the
* collateral vault for the NFT collection.
*
* @param asset The address of the ERC721 collection to be glued
* @return glueAddress The address of the newly created glue instance
*
* Use cases:
* - Adding asset backing capabilities to existing NFT collections
* - Creating collateralization mechanisms for NFTs
* - Establishing new NFT economic models with withdrawal mechanisms
* - Supporting floor price protection for collections through backing
*/
function applyTheGlue(address asset) external returns (address glueAddress);
/**
* @notice Processes ungluing operations for multiple NFT collections in a single transaction
* @dev Efficiently batches unglue operations across multiple NFT collections, managing the
* transfer of NFTs from caller to glue contracts, and execution of unglue operations.
* Supports both single and multiple recipient configurations.
*
* @param stickyAssets Array of NFT collection addresses to unglue from
* @param tokenIds Two-dimensional array of token IDs to unglue for each collection
* @param collaterals Array of collateral addresses to withdraw (common across all unglue operations)
* @param recipients Array of recipient addresses to receive the unglued collateral
*
* Use cases:
* - Unglue collaterals across multiple sticky NFT collections
* - Efficient withdrawal of collaterals from multiple sticky NFT collections
* - Consolidated position exits for complex NFT strategies
* - Multi-collection redemption in a single transaction
*/
function batchUnglue(address[] calldata stickyAssets,uint256[][] calldata tokenIds,address[] calldata collaterals,address[] calldata recipients) external;
/**
* @notice Executes multiple flash loans across multiple glues.
* @dev This function calculates the loans, executes them, and verifies the repayments.
*
* @param glues The addresses of the glues to borrow from.
* @param collateral The address of the collateral to borrow.
* @param loanAmount The total amount of collaterals to borrow.
* @param receiver The address of the receiver.
* @param params Additional parameters for the receiver.
*
* Use cases:
* - Flash Loans across multiple glues
* - Capital-efficient arbitrage across DEXes
* - Liquidation operations in lending protocols
* - Complex cross-protocol interactions requiring upfront capital
* - Temporary liquidity for atomic multi-step operations
* - Collateral swaps without requiring pre-owned capital
*/
function gluedLoan(address[] calldata glues, address collateral, uint256 loanAmount, address receiver, bytes calldata params) external;
/**
--------------------------------------------------------------------------------------------------------
▗▄▄▖ ▗▄▄▄▖ ▗▄▖ ▗▄▄▄
▐▌ ▐▌▐▌ ▐▌ ▐▌▐▌ █
▐▛▀▚▖▐▛▀▀▘▐▛▀▜▌▐▌ █
▐▌ ▐▌▐▙▄▄▖▐▌ ▐▌▐▙▄▄▀
01010010 01100101
01100001 01100100
*/
/**
* @notice Retrieves expected collateral amounts from batch ungluing operations for NFTs
* @dev View function to calculate expected collateral returns for multiple NFT collections.
* This is essential for front-end applications and integrations to estimate expected
* returns before executing batch unglue operations.
*
* @param stickyAssets Array of NFT collection addresses
* @param stickyAmounts Array of NFT counts to simulate ungluing (number of NFTs, not IDs)
* @param collaterals Array of collateral addresses to check
* @return collateralAmounts 2D array of corresponding collateral amounts [glueIndex][collateralIndex]
*
* Use cases:
* - Pre-transaction estimation for front-end applications
* - Strategy optimization based on expected returns
* - User interface displays showing potential redemption values
*/
function getBatchCollaterals(address[] calldata stickyAssets,uint256[] calldata stickyAmounts,address[] calldata collaterals) external view returns (uint256[][] memory collateralAmounts);
/**
* @notice Checks if the given ERC721 address has valid totalSupply and no decimals
* @dev This function performs static calls to check if token is a valid NFT
* Token validation is critical for ensuring only compatible collections can be glued,
* preventing issues with non-enumerable NFT collections.
*
* @param asset The address of the ERC721 asset to check
* @return isValid Indicates whether the token is valid
*
* Use cases:
* - Pre-glue verification to prevent incompatible token issues
* - Protocol security to maintain compatibility standards
* - Front-end validation before attempting glue operations
*/
function checkAsset(address asset) external view returns (bool isValid);
/**
* @notice Computes the address of the GlueERC721 contract for the given ERC721 address.
* @dev Uses the Clones library to predict the address of the minimal proxy.
*
* @param asset The address of the ERC721 contract.
* @return predictedGlueAddress The computed address of the GlueERC721 contract.
*
* Use cases:
* - Complex integrations requiring pre-knowledge of glue addresses
* - Front-end preparation before actual glue deployment
* - Cross-contract interactions that reference glue addresses
* - Security verification of expected deployment addresses
*/
function computeGlueAddress(address asset) external view returns (address predictedGlueAddress);
/**
* @notice Checks if a given token is sticky and returns its glue address
* @dev Utility function for external contracts and front-ends to verify token status
* in the Glue protocol and retrieve the associated glue address if it exists.
*
* @param asset The address of the NFT Collection to check
* @return isSticky bool Indicates whether the token is sticky.
* @return glueAddress The glue address for the token if it's sticky, otherwise address(0).
*
* Use cases:
* - UI elements showing token glue status
* - Protocol integrations needing to verify glue existence
* - Smart contracts checking if a token can be unglued
* - External protocols building on top of the Glue protocol
*/
function isStickyAsset(address asset) external view returns (bool isSticky, address glueAddress);
/**
* @notice Retrieves the balances of multiple collaterals across multiple glues
* @dev Returns a 2D array where each row represents a glue and each column represents a collateral
* @dev This function is used to get the balances of multiple collaterals across multiple glues
*
* @param glues The addresses of the glues to check
* @param collaterals The addresses of the collaterals to check for each glue
* @return balances a 2D array of balances [glueIndex][collateralIndex]
*
* Use cases:
* - Batch querying collateral positions across multiple glues
* - Dashboard displays showing complete portfolio positions
* - Cross-glue analytics and reporting
*/
function getGluesBalances(address[] calldata glues, address[] calldata collaterals) external view returns (uint256[][] memory balances);
/**
* @notice Returns the total number of deployed glues.
* @return existingGlues The length of the allGlues array.
*
* Use cases:
* - Informational queries about the total number of deployed glues
*/
function allGluesLength() external view returns (uint256 existingGlues);
/**
* @notice Retrieves the glue address for a given token
* @dev Returns the glue address for the given token
*
* @param asset The address of the NFT collection to get the glue address for
* @return glueAddress The glue address for the given token, if it exists, otherwise address(0)
*
* Use cases:
* - Retrieving the glue address for a given token
*/
function getGlueAddress(address asset) external view returns (address glueAddress);
/**
* @notice Retrieves a glue address by its index in the registry
* @dev Returns the address of a deployed glue at the specified index
* This provides indexed access to the array of all deployed glues
*
* @param index The index in the allGlues array to query
* @return glueAddress The address of the glue at the specified index
*
* Use cases:
* - Enumeration of all deployed glues in the protocol
* - Accessing specific glues by index for reporting or integration
* - Batch operations on sequential glue addresses
*/
function getGlueAtIndex(uint256 index) external view returns (address glueAddress);
/**
--------------------------------------------------------------------------------------------------------
▗▄▄▄▖▗▄▄▖ ▗▄▄▖ ▗▄▖ ▗▄▄▖ ▗▄▄▖
▐▌ ▐▌ ▐▌▐▌ ▐▌▐▌ ▐▌▐▌ ▐▌▐▌
▐▛▀▀▘▐▛▀▚▖▐▛▀▚▖▐▌ ▐▌▐▛▀▚▖ ▝▀▚▖
▐▙▄▄▖▐▌ ▐▌▐▌ ▐▌▝▚▄▞▘▐▌ ▐▌▗▄▄▞▘
01100101 01110010 01110010
01101111 01110010 01110011
*/
/**
* @dev Error thrown when an invalid asset address is provided
* @param asset The address of the invalid asset
*/
error InvalidAsset(address asset);
/**
* @dev Error thrown when attempting to glue a collection that's already glued
* @param asset The address of the duplicate collection
*/
error DuplicateGlue(address asset);
/**
* @dev Error thrown when an invalid address (typically zero address) is provided
*/
error InvalidAddress();
/**
* @dev Error thrown when function inputs are invalid or inconsistent
*/
error InvalidInputs();
/**
* @dev Error thrown when a glue contract has insufficient balance
* @param glue The address of the glue contract
* @param balance The actual balance found
* @param collateral The collateral being checked
*/
error InvalidGlueBalance(address glue, uint256 balance, address collateral);
/**
* @dev Error thrown when there is insufficient liquidity for a flash loan
* @param totalCollected The amount of liquidity that was collected
* @param loanAmount The amount of liquidity that was required
*/
error InsufficientLiquidity(uint256 totalCollected, uint256 loanAmount);
/**
* @dev Error thrown when a flash loan operation fails
*/
error FlashLoanFailed();
/**
* @dev Error thrown when a flash loan repayment fails
* @param glue The glue contract that failed to receive repayment
*/
error RepaymentFailed(address glue);
/**
* @dev Error thrown when the deployment of a glue contract fails
*/
error FailedToDeployGlue();
/**
* @dev Error thrown when no assets are selected for an operation
*/
error NoAssetsSelected();
/**
--------------------------------------------------------------------------------------------------------
▗▄▄▄▖▗▖ ▗▖▗▄▄▄▖▗▖ ▗▖▗▄▄▄▖▗▄▄▖
▐▌ ▐▌ ▐▌▐▌ ▐▛▚▖▐▌ █ ▐▌
▐▛▀▀▘▐▌ ▐▌▐▛▀▀▘▐▌ ▝▜▌ █ ▝▀▚▖
▐▙▄▄▖ ▝▚▞▘ ▐▙▄▄▖▐▌ ▐▌ █ ▗▄▄▞▘
01000101 01010110 01000101
01001110 01010100 01010011
*/
/**
* @notice Emitted when a new NFT collection is glued
* @param asset The address of the collection that was glued
* @param glueAddress The address of the created glue contract
* @param glueIndex The index of the glue in the allGlues array
*/
event GlueAdded(address indexed asset, address glueAddress, uint256 glueIndex);
/**
* @notice Emitted when a batch unglue operation is executed for NFTs
* @param stickyAssets Array of NFT collections that were unglued
* @param tokenIds Two-dimensional array of token IDs that were processed
* @param collaterals Array of collateral addresses that were withdrawn
* @param recipients Array of addresses that received the assets
*/
event BatchUnglueExecuted(address[] stickyAssets,uint256[][] tokenIds,address[] collaterals,address[] recipients);
}
/**
████████╗██╗ ██╗███████╗
╚══██╔══╝██║ ██║██╔════╝
██║ ███████║█████╗
██║ ██╔══██║██╔══╝
██║ ██║ ██║███████╗
╚═╝ ╚═╝ ╚═╝╚══════╝
██████╗ ██╗ ██╗ ██╗███████╗
██╔════╝ ██║ ██║ ██║██╔════╝
██║ ███╗██║ ██║ ██║█████╗
██║ ██║██║ ██║ ██║██╔══╝
╚██████╔╝███████╗╚██████╔╝███████╗
╚═════╝ ╚══════╝ ╚═════╝ ╚══════╝
* @title IGlueERC721
* @author @BasedToschi
* @notice Interface for individual glue contract instances managing ERC721 NFT collection collateralization
* @dev This interface defines the API for GlueERC721 contracts that are created by the factory.
* These contracts manage collateral for NFT collections, process ungluing operations based on
* proportional NFT count vs. total supply, and provide flash loan functionality.
*/
interface IGlueERC721 {
/**
--------------------------------------------------------------------------------------------------------
▗▄▄▖▗▄▄▄▖▗▄▄▄▖▗▖ ▗▖▗▄▄▖
▐▌ ▐▌ █ ▐▌ ▐▌▐▌ ▐▌
▝▀▚▖▐▛▀▀▘ █ ▐▌ ▐▌▐▛▀▘
▗▄▄▞▘▐▙▄▄▖ █ ▝▚▄▞▘▐▌
01010011 01100101 01110100
01110101 01110000
*/
/**
* @notice Enum representing hook detection status
* @dev Used to track if the collection implements and supports hooks
* @dev UNCHECKED: The collection has not been checked for hooks. The check happens at the first unglue operation.
* @dev NO_HOOK: The collection does not implement hooks
* @dev HOOK: The collection implements hooks
*/
enum BIO {UNCHECKED,NO_HOOK,HOOK}
/**
* @notice Initializes a newly deployed glue clone for an NFT collection
* @dev Called by the factory when creating a new glue instance through cloning
* Sets up the core state variables and establishes the relationship between
* this glue instance and its associated NFT collection
*
* @param asset Address of the ERC721 collection to be linked with this glue
*
* Use cases:
* - Creating a new glue address for a NFT collection (now Sticky Token) in which attach collateral
* - Establishing the collection-glue relationship in the protocol
*/
function initialize(address asset) external;
/**
--------------------------------------------------------------------------------------------------------
▗▄▄▄▖▗▖ ▗▖▗▖ ▗▖ ▗▄▄▖▗▄▄▄▖▗▄▄▄▖ ▗▄▖ ▗▖ ▗▖ ▗▄▄▖
▐▌ ▐▌ ▐▌▐▛▚▖▐▌▐▌ █ █ ▐▌ ▐▌▐▛▚▖▐▌▐▌
▐▛▀▀▘▐▌ ▐▌▐▌ ▝▜▌▐▌ █ █ ▐▌ ▐▌▐▌ ▝▜▌ ▝▀▚▖
▐▌ ▝▚▄▞▘▐▌ ▐▌▝▚▄▄▖ █ ▗▄█▄▖▝▚▄▞▘▐▌ ▐▌▗▄▄▞▘
01000110 01110101 01101110 01100011 01110100
01101001 01101111 01101110 01110011
*/
/**
* @notice Core function that processes NFT ungluing operations to release collateral
* @dev Handles the complete ungluing workflow for NFTs: verifying ownership,
* managing transfers, calculating proportional collateral amounts, applying fees,
* executing hook logic if enabled, and distributing collateral to the recipient.
*
* @param collaterals Array of collateral token addresses to withdraw
* @param tokenIds Array of NFT token IDs to burn for collateral withdrawal
* @param recipient Address to receive the withdrawn collateral
* @return supplyDelta Calculated proportion of total NFT supply (in PRECISION units)
* @return realAmount Number of NFTs processed (after removing duplicates)
* @return beforeTotalSupply NFT collection supply before the unglue operation
* @return afterTotalSupply NFT collection supply after the unglue operation
*
* Use cases:
* - Redeeming collateral from the protocol by burning NFTs
* - Converting sticky NFTs back to their collaterals
*/
function unglue(address[] calldata collaterals, uint256[] calldata tokenIds, address recipient) external returns (uint256 supplyDelta, uint256 realAmount, uint256 beforeTotalSupply, uint256 afterTotalSupply);
/**
* @notice Initiates a flash loan.
* @dev This function is used to initiate a flash loan.
*
* @param collateral The address of the collateral token.
* @param amount The amount of tokens to flash loan.
* @param receiver The address of the receiver.
* @param params The parameters for the flash loan.
* @return success boolean indicating success
*
* Use cases:
* - Initiating a simplified Glued loan from this Glue.
* - Initiating a flash loan with simpler integration.
*/
function flashLoan(address collateral, uint256 amount, address receiver, bytes calldata params) external returns (bool success);
/**
* @notice Initiates a minimal flash loan.
* @dev This function is used for the Glue Stick to handle collateral in a Glued Loan.
* @dev Only the Glue Stick can call this function.
*
* @param receiver The address of the receiver.
* @param collateral The address of the token to flash loan.
* @param amount The amount of tokens to flash loan.
* @return loanSent boolean indicating success
*
* Use cases:
* - Handle collateral in a Glued Loan.
*/
function loanHandler(address receiver, address collateral, uint256 amount) external returns (bool loanSent);
/**
--------------------------------------------------------------------------------------------------------
▗▄▄▖ ▗▄▄▄▖ ▗▄▖ ▗▄▄▄
▐▌ ▐▌▐▌ ▐▌ ▐▌▐▌ █
▐▛▀▚▖▐▛▀▀▘▐▛▀▜▌▐▌ █
▐▌ ▐▌▐▙▄▄▖▐▌ ▐▌▐▙▄▄▀
01010010 01100101
01100001 01100100
*/
/**
* @notice Calculates the supply delta based on the sticky NFT amount and total supply.
* @dev This function is used to calculate the supply delta based on the sticky NFT amount and total supply.
*
* @param stickyAmount The amount of sticky NFTs.
* @return supplyDelta The calculated supply delta.
*
* Use cases:
* - Calculating the supply delta based on the sticky NFT amount.
*
*/
function getSupplyDelta(uint256 stickyAmount) external view returns (uint256 supplyDelta);
/**
* @notice Retrieves the adjusted total supply of the Sticky NFT Collection.
* @dev This function is used to get the adjusted total supply of the Sticky NFT Collection.
*
* @return adjustedTotalSupply The adjusted and actual total supply of the Sticky NFT Collection.
*
* Use cases:
* - Retrieving the adjusted total supply of the Sticky NFT Collection.
*/
function getAdjustedTotalSupply() external view returns (uint256 adjustedTotalSupply);
/**
* @notice Retrieves the protocol fee percentage.
* @dev This function is used to get the protocol fee percentage.
*
* @return protocolFee The protocol fee as a fixed-point number with 18 decimal places.
*
* Use cases:
* - Retrieving the protocol fee percentage fixed to 1e15 = 0.1% | 1e18 = 100%.
*/
function getProtocolFee() external pure returns (uint256 protocolFee);
/**
* @notice Retrieves the flash loan fee percentage.
* @dev This function is used to get the flash loan fee percentage.
* @dev The flash loan fee is fully paid to the Glue
*
* @return flashLoanFee The flash loan fee as a fixed-point number with 18 decimal places.
*
* Use cases:
* - Retrieving the flash loan fee percentage fixed to 1e14 = 0.01% | 1e18 = 100%.
*/
function getFlashLoanFee() external pure returns (uint256 flashLoanFee);
/**
* @notice Retrieves the flash loan fee for a given amount.
* @dev This function is used to get the flash loan fee for a given amount.
*
* @param amount The amount to calculate the flash loan fee for.
* @return fee The flash loan fee applied to a given amount.
*
* Use cases:
* - Retrieving the flash loan fee applied to a given amount.
*/
function getFlashLoanFeeCalculated(uint256 amount) external pure returns (uint256 fee);
/**
* @notice Retrieves the total hook size for a sepecific collateral.
* @dev This function is used to get the total hook size for a sepecific collateral or sticky token.
*
* @param collateral The address of the collateral token.
* @param collateralAmount The amount of tokens to calculate the hook size for.
* @return hookSize The total hook size.
*
* Use cases:
* - Retrieving the total hook size for a specific collateral.
*/
function getTotalHookSize(address collateral, uint256 collateralAmount) external view returns (uint256 hookSize);
/**
* @notice Calculates the amount of collateral tokens that can be unglued for a given amount of sticky tokens.
* @dev This function is used to calculate the amount of collateral tokens that can be unglued for a given amount of sticky tokens.
*
* @param stickyAmount The amount of sticky tokens to be burned.
* @param collaterals An array of addresses representing the collateral tokens to unglue.
* @return amounts An array containing the corresponding amounts that can be unglued.
* @dev This function accounts for the protocol fee in its calculations.
*
* Use cases:
* - Calculating the amount of collateral tokens that can be unglued for a given amount of sticky tokens.
*/
function collateralByAmount(uint256 stickyAmount, address[] calldata collaterals) external view returns (uint256[] memory amounts);
/**
* @notice Retrieves the balance of an array of specified collateral tokens for the glue contract.
* @dev This function is used to get the balance of an array of specified collateral tokens for the glue contract.
*
* @param collaterals An array of addresses representing the collateral tokens.
* @return balances An array containing the corresponding balances.
*
* Use cases:
* - Retrieving the balance of an array of specified collateral tokens for the glue contract.
*/
function getBalances(address[] calldata collaterals) external view returns (uint256[] memory balances);
/**
* @notice Retrieves the balance of the sticky NFTs for the glue contract.
* @dev This function is used to get the balance of the sticky NFTs for the glue contract.
*
* @return stickyAmount The balance of the sticky NFTs.
*
* Use cases:
* - Retrieving the balance of the sticky NFTs for the glue contract.
*/
function getStickySupplyStored() external view returns (uint256 stickyAmount);
/**
* @notice Retrieves the settings contract address.
* @dev This function is used to get the settings contract address.
*
* @return settings The address of the settings contract.
*
* Use cases:
* - Retrieving the settings contract address.
*/
function getSettings() external pure returns (address settings);
/**
* @notice Retrieves the address of the GlueStick factory contract.
* @dev This function is used to get the address of the GlueStick factory contract.
*
* @return glueStick The address of the GlueStick factory contract.
*
* Use cases:
* - Retrieving the address of the GlueStick factory contract.
*/
function getGlueStick() external view returns (address glueStick);
/**
* @notice Retrieves the address of the sticky token.
* @dev This function is used to get the address of the sticky token.
*
* @return stickyAsset The address of the sticky NFT collection.
*
* Use cases:
* - Retrieving the address of the sticky token.
*/
function getStickyAsset() external view returns (address stickyAsset);
/**
* @notice Retrieves if the glue is expanded with active Hooks.
* @dev This function is used to get if the glue is expanded with active Hooks:
* - BIO.HOOK: The glue is expanded with active Hooks.
* - BIO.NO_HOOK: The glue is not expanded with active Hooks.
* - BIO.UNCHECKED: The glue didn't have learned yet (before the first unglue interaction).
*
* @return hooksStatus The bio of the hooks status.
*
* Use cases:
* - Knowing if the glue is expanded with active Hooks for external interactions.
*/
function isExpanded() external view returns (BIO hooksStatus);
/**
* @notice Retrieves if the Sticky Asset is natively not burnable and
* if the sticky token is permanently stored in the contract.
* @dev This function is used to get if the Sticky Asset is natively not burnable,
* and if the sticky token is permanently stored in the contract.
*
* @return noNativeBurn A boolean representing if the sticky asset is natively not burnable.
* @return stickySupplyGlued A boolean representing if the sticky token is permanently stored in the contract.
*
* Use cases:
* - Knowing if the Sticky Asset is natively not burnable and if the sticky token is permanently stored in the contract.
*/
function getSelfLearning() external view returns (bool noNativeBurn, bool stickySupplyGlued);
/**
--------------------------------------------------------------------------------------------------------
▗▄▄▄▖▗▄▄▖ ▗▄▄▖ ▗▄▖ ▗▄▄▖ ▗▄▄▖
▐▌ ▐▌ ▐▌▐▌ ▐▌▐▌ ▐▌▐▌ ▐▌▐▌
▐▛▀▀▘▐▛▀▚▖▐▛▀▚▖▐▌ ▐▌▐▛▀▚▖ ▝▀▚▖
▐▙▄▄▖▐▌ ▐▌▐▌ ▐▌▝▚▄▞▘▐▌ ▐▌▗▄▄▞▘
01100101 01110010 01110010
01101111 01110010 01110011
*/
/**
* @dev Error thrown when no assets are selected for ungluing
*/
error NoAssetsSelected();
/**
* @dev Error thrown when an invalid GlueStick factory address is provided
*/
error InvalidGlueStickAddress();
/**
* @dev Error thrown when an invalid asset address is provided
* @param asset The address of the invalid asset
*/
error InvalidAsset(address asset);
/**
* @dev Error thrown when no collateral is selected for ungluing
*/
error NoCollateralSelected();
/**
* @dev Error thrown when no tokens are transferred during an operation
*/
error NoAssetsTransferred();
/**
* @dev Error thrown when the contract fails to process an NFT collection operation
*/
error FailedToProcessCollection();
/**
* @dev Error thrown when an unauthorized caller attempts an operation
*/
error Unauthorized();
/**
* @dev Error thrown when a zero amount is provided where a non-zero amount is required
*/
error ZeroAmount();
/**
--------------------------------------------------------------------------------------------------------
▗▄▄▄▖▗▖ ▗▖▗▄▄▄▖▗▖ ▗▖▗▄▄▄▖▗▄▄▖
▐▌ ▐▌ ▐▌▐▌ ▐▛▚▖▐▌ █ ▐▌
▐▛▀▀▘▐▌ ▐▌▐▛▀▀▘▐▌ ▝▜▌ █ ▝▀▚▖
▐▙▄▄▖ ▝▚▞▘ ▐▙▄▄▖▐▌ ▐▌ █ ▗▄▄▞▘
01000101 01010110 01000101
01001110 01010100 01010011
*/
/**
* @notice Emitted when NFTs are unglued and collateral is withdrawn
* @param recipient The address receiving the withdrawn collateral
* @param realAmount The number of NFTs that were processed
* @param beforeTotalSupply The total supply before the operation
* @param afterTotalSupply The total supply after the operation
* @param supplyDelta The supply delta
*/
event unglued(address indexed recipient, uint256 realAmount, uint256 beforeTotalSupply, uint256 afterTotalSupply, uint256 supplyDelta);
/**
* @notice Emitted when a flash loan is executed
* @param collateral The address of the borrowed asset
* @param amount The amount that was borrowed
* @param receiver The address that received the loan
*/
event GlueLoan(address indexed collateral, uint256 amount, address receiver);
}// SPDX-License-Identifier: MIT
/**
███╗ ███╗ █████╗ ████████╗██╗ ██╗
████╗ ████║██╔══██╗╚══██╔══╝██║ ██║
██╔████╔██║███████║ ██║ ███████║
██║╚██╔╝██║██╔══██║ ██║ ██╔══██║
██║ ╚═╝ ██║██║ ██║ ██║ ██║ ██║
╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝
███████╗ ██████╗ ██████╗
██╔════╝██╔═══██╗██╔══██╗
█████╗ ██║ ██║██████╔╝
██╔══╝ ██║ ██║██╔══██╗
██║ ╚██████╔╝██║ ██║
╚═╝ ╚═════╝ ╚═╝ ╚═╝
██████╗ ██╗ ██╗ ██╗███████╗
██╔════╝ ██║ ██║ ██║██╔════╝
██║ ███╗██║ ██║ ██║█████╗
██║ ██║██║ ██║ ██║██╔══╝
╚██████╔╝███████╗╚██████╔╝███████╗
╚═════╝ ╚══════╝ ╚═════╝ ╚══════╝
@title GluedMath
@author Implementation by @BasedToschi
@notice Glued Math Basics is a library for advanced fixed-point math operations (Original version by Uniswap Labs).
@notice Glued Math Expanded introduces a new function to adjust the decimal places between different tokens with different decimals or ETH.
@dev Implements multiplication and division with overflow protection and precision retention.
@dev This library is used to handle the decimal places of the tokens.
*/
pragma solidity ^0.8.28;
library GluedMath {
/**
██████╗ █████╗ ███████╗██╗ ██████╗ ███╗ ███╗ █████╗ ████████╗██╗ ██╗
██╔══██╗██╔══██╗██╔════╝██║██╔════╝ ████╗ ████║██╔══██╗╚══██╔══╝██║ ██║
██████╔╝███████║███████╗██║██║ ██╔████╔██║███████║ ██║ ███████║
██╔══██╗██╔══██║╚════██║██║██║ ██║╚██╔╝██║██╔══██║ ██║ ██╔══██║
██████╔╝██║ ██║███████║██║╚██████╗ ██║ ╚═╝ ██║██║ ██║ ██║ ██║ ██║
╚═════╝ ╚═╝ ╚═╝╚══════╝╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝
*/
/**
* @notice Performs a multiply-divide operation with full precision.
* @dev Calculates floor(a * b / denominator) with full precision, using 512-bit intermediate values.
* Throws if the result overflows a uint256 or if the denominator is zero.
*
* @param a The multiplicand.
* @param b The multiplier.
* @param denominator The divisor.
* @return result The result of the operation.
*
* Use case: When you need to calculate the result of a multiply-divide operation with full precision.
*/
function md512(uint256 a, uint256 b, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
// Calculate the product of a and b
uint256 prod0;
uint256 prod1;
// Calculate the product of a and b with overflow protection
assembly {
let mm := mulmod(a, b, not(0))
prod0 := mul(a, b)
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// If the denominator is zero or the result overflows, revert
require(denominator > prod1, "GluedMath: denominator is zero or result overflows");
// If the product of a and b is less than the denominator, return the result
if (prod1 == 0) {
assembly {
result := div(prod0, denominator)
}
// Return the result
return result;
}
// Calculate the remainder of the product of a and b divided by the denominator
uint256 remainder;
assembly {
remainder := mulmod(a, b, denominator)
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Calculate the twos of the denominator
uint256 twos = (0 - denominator) & denominator;
// Calculate the inverse of the denominator
assembly {
denominator := div(denominator, twos)
prod0 := div(prod0, twos)
}
// Calculate the inverse of the denominator
uint256 inv = (3 * denominator) ^ 2;
inv *= 2 - denominator * inv; // inverse mod 2^8
inv *= 2 - denominator * inv; // inverse mod 2^16
inv *= 2 - denominator * inv; // inverse mod 2^32
inv *= 2 - denominator * inv; // inverse mod 2^64
inv *= 2 - denominator * inv; // inverse mod 2^128
inv *= 2 - denominator * inv; // inverse mod 2^256
// Calculate the result
result = prod0 * inv;
// Return the result
return result;
}
}
/**
* @notice Performs a multiply-divide operation with full precision and rounding up.
* @dev Calculates ceil(a * b / denominator) with full precision, using 512-bit intermediate values.
* Throws if the result overflows a uint256 or if the denominator is zero.
*
* @param a The multiplicand.
* @param b The multiplier.
* @param denominator The divisor.
* @return result The result of the operation, rounded up to the nearest integer.
*
* Use case: When you need to calculate the result of a multiply-divide operation with full precision and rounding up.
*/
function md512Up(uint256 a, uint256 b, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
// Calculate the result
result = md512(a, b, denominator);
// If the remainder of the product of a and b divided by the denominator is greater than 0, increment the result
if (mulmod(a, b, denominator) > 0) {
require(result < type(uint256).max, "GluedMath: result overflows");
result++;
}
}
}
/**
███████╗██╗ ██╗██████╗ █████╗ ███╗ ██╗██████╗ ███████╗██████╗ ███╗ ███╗ █████╗ ████████╗██╗ ██╗
██╔════╝╚██╗██╔╝██╔══██╗██╔══██╗████╗ ██║██╔══██╗██╔════╝██╔══██╗ ████╗ ████║██╔══██╗╚══██╔══╝██║ ██║
█████╗ ╚███╔╝ ██████╔╝███████║██╔██╗ ██║██║ ██║█████╗ ██║ ██║ ██╔████╔██║███████║ ██║ ███████║
██╔══╝ ██╔██╗ ██╔═══╝ ██╔══██║██║╚██╗██║██║ ██║██╔══╝ ██║ ██║ ██║╚██╔╝██║██╔══██║ ██║ ██╔══██║
███████╗██╔╝ ██╗██║ ██║ ██║██║ ╚████║██████╔╝███████╗██████╔╝ ██║ ╚═╝ ██║██║ ██║ ██║ ██║ ██║
╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝╚═╝ ╚═══╝╚═════╝ ╚══════╝╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝
*/
/**
* @notice Gets the decimals of a token
* @dev If the token is ETH, you can use the address(0) as the token address.
*
* @param token The address of the token
* @return decimals The number of decimals of the token
*
* Use case: When you need to get the decimals of a token
*/
function getDecimals(address token) internal view returns (uint256 decimals) {
// If the token is ETH, return 18
if (token == address(0)) {
return 18;
}
// Get the decimals of the token
(bool success, bytes memory data) = token.staticcall(abi.encodeWithSignature("decimals()"));
// If the call failed, revert
require(success && data.length >= 32, "decimals() call failed");
// Return the decimals of the token
return uint256(uint8(bytes1(data)));
}
/**
* @notice Adjusts decimal places between different token decimals. With this function,
* you can get the right ammount of tokenOut from a given tokenIn address and amount
* espressed in tokenIn's decimals.
* @dev If one of the tokens is ETH, you can use the address(0) as the token address.
*
* @param amount The amount to adjust
* @param tokenIn The address of the input token
* @param tokenOut The address of the output token
* @return adjustedAmount The adjusted amount with correct decimal places
*
* Use case: When you need to adjust the decimal places operating with two different tokens
*/
function adjustDecimals(uint256 amount, address tokenIn, address tokenOut) internal view returns (uint256 adjustedAmount) {
// Get the decimals of the input and output tokens
uint256 decimalsIn = tokenIn == address(0) ? 18 : getDecimals(tokenIn);
uint256 decimalsOut = tokenOut == address(0) ? 18 : getDecimals(tokenOut);
// If the decimals are the same, return the amount
if (decimalsIn == decimalsOut) return amount;
// If input token is 0 decimal and output has decimals, special handling
if (decimalsIn == 0 && decimalsOut > 0) {
// Mltiply by 10^decimalsOut to ensure proper scaling
return amount * (10 ** decimalsOut);
}
// If output token is 0 decimal and input has decimals, handle precision loss
if (decimalsOut == 0 && decimalsIn > 0) {
// Round up if there's any fractional part to avoid returning 0 for small amounts
uint256 divisor = 10 ** decimalsIn;
// Ceiling division
return (amount + divisor - 1) / divisor;
}
// If the decimals of the input token are greater than the decimals of the output token, divide the amount
return decimalsIn > decimalsOut
? amount / (10 ** (decimalsIn - decimalsOut))
: amount * (10 ** (decimalsOut - decimalsIn));
}
/**
* @dev You can combine md512 + adjustDecimals
*
* Example:
*
* uint256 result = adjustDecimals(md512(a, b, denominator), tokenIn, tokenOut);
*
*/
}{
"optimizer": {
"enabled": true,
"runs": 200
},
"evmVersion": "cancun",
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"libraries": {}
}Contract ABI
API[{"inputs":[{"internalType":"address","name":"_glueStickAddress","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"FailedCall","type":"error"},{"inputs":[],"name":"FailedToProcessCollection","type":"error"},{"inputs":[{"internalType":"uint256","name":"balance","type":"uint256"},{"internalType":"uint256","name":"needed","type":"uint256"}],"name":"InsufficientBalance","type":"error"},{"inputs":[{"internalType":"address","name":"asset","type":"address"}],"name":"InvalidAsset","type":"error"},{"inputs":[],"name":"InvalidGlueStickAddress","type":"error"},{"inputs":[],"name":"InvalidInitialization","type":"error"},{"inputs":[],"name":"NoAssetsSelected","type":"error"},{"inputs":[],"name":"NoAssetsTransferred","type":"error"},{"inputs":[],"name":"NoCollateralSelected","type":"error"},{"inputs":[],"name":"NotInitializing","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"inputs":[],"name":"Unauthorized","type":"error"},{"inputs":[],"name":"ZeroAmount","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"collateral","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"address","name":"receiver","type":"address"}],"name":"GlueLoan","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"version","type":"uint64"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"realAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"beforeTotalSupply","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"afterTotalSupply","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"supplyDelta","type":"uint256"}],"name":"unglued","type":"event"},{"inputs":[{"internalType":"uint256","name":"stickyAmount","type":"uint256"},{"internalType":"address[]","name":"collaterals","type":"address[]"}],"name":"collateralByAmount","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"collateral","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"bytes","name":"params","type":"bytes"}],"name":"flashLoan","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getAdjustedTotalSupply","outputs":[{"internalType":"uint256","name":"adjustedTotalSupply","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"collaterals","type":"address[]"}],"name":"getBalances","outputs":[{"internalType":"uint256[]","name":"balances","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getFlashLoanFee","outputs":[{"internalType":"uint256","name":"flashLoanFee","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"getFlashLoanFeeCalculated","outputs":[{"internalType":"uint256","name":"fee","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"getGlueStick","outputs":[{"internalType":"address","name":"glueStick","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getProtocolFee","outputs":[{"internalType":"uint256","name":"protocolFee","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"getSelfLearning","outputs":[{"internalType":"bool","name":"noNativeBurn","type":"bool"},{"internalType":"bool","name":"stickySupplyGlued","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getSettings","outputs":[{"internalType":"address","name":"settings","type":"address"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"getStickyAsset","outputs":[{"internalType":"address","name":"stickyAsset","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getStickySupplyStored","outputs":[{"internalType":"uint256","name":"stickyAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"stickyAmount","type":"uint256"}],"name":"getSupplyDelta","outputs":[{"internalType":"uint256","name":"supplyDelta","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"collateral","type":"address"},{"internalType":"uint256","name":"collateralAmount","type":"uint256"}],"name":"getTotalHookSize","outputs":[{"internalType":"uint256","name":"hookSize","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"asset","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"isExpanded","outputs":[{"internalType":"enum IGlueERC721.BIO","name":"hooksStatus","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"},{"internalType":"address","name":"collateral","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"loanHandler","outputs":[{"internalType":"bool","name":"loanSent","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"operator","type":"address"},{"internalType":"address","name":"from","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"onERC721Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"collaterals","type":"address[]"},{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"},{"internalType":"address","name":"recipient","type":"address"}],"name":"unglue","outputs":[{"internalType":"uint256","name":"supplyDelta","type":"uint256"},{"internalType":"uint256","name":"realAmount","type":"uint256"},{"internalType":"uint256","name":"beforeTotalSupply","type":"uint256"},{"internalType":"uint256","name":"afterTotalSupply","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Token Allocations
ETH
100.00%
Multichain Portfolio | 34 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|---|---|---|---|---|
| ETH | 100.00% | $1,944.24 | 0.00000001 | $0.000019 |
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.