Description:
Multi-signature wallet contract requiring multiple confirmations for transaction execution.
Blockchain: Ethereum
Source Code: View Code On The Blockchain
Solidity Source Code:
{{
"language": "Solidity",
"sources": {
"contracts/v1/router/MarketViewer.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {IERC721Enumerable} from "@openzeppelin/contracts/interfaces/IERC721Enumerable.sol";
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
import {ITermMaxMarket} from "../ITermMaxMarket.sol";
import {ITermMaxOrder} from "../ITermMaxOrder.sol";
import {IMintableERC20} from "../tokens/IMintableERC20.sol";
import {IGearingToken} from "../tokens/IGearingToken.sol";
import {OrderConfig, CurveCuts, FeeConfig, GtConfig} from "../storage/TermMaxStorage.sol";
import {ITermMaxVault} from "../vault/ITermMaxVault.sol";
import {OrderInfo} from "../vault/VaultStorage.sol";
import {PendingAddress, PendingUint192} from "../lib/PendingLib.sol";
import {OracleAggregator} from "../oracle/OracleAggregator.sol";
interface IPausable {
function paused() external view returns (bool);
}
contract MarketViewer {
using Math for uint256;
struct LoanPosition {
uint256 loanId;
uint256 collateralAmt;
uint256 debtAmt;
}
struct LoanPositionV2 {
address owner;
uint256 loanId;
uint256 collateralAmt;
uint256 debtAmt;
uint128 ltv;
bool isHealthy;
bool isLiquidable;
uint128 maxRepayAmt;
}
struct Position {
uint256 underlyingBalance;
uint256 collateralBalance;
uint256 ftBalance;
uint256 xtBalance;
LoanPosition[] gtInfo;
}
struct VaultPosition {
uint256 balance;
uint256 toAssetBalance;
uint256 usdValue;
}
struct OrderState {
uint256 collateralReserve;
uint256 debtReserve;
uint256 ftReserve;
uint256 xtReserve;
uint256 maxXtReserve;
uint256 gtId;
CurveCuts curveCuts;
FeeConfig feeConfig;
}
struct VaultInfo {
string name;
string symbol;
address assetAddress;
// Basic vault metrics
uint256 totalAssets;
uint256 totalSupply;
uint256 apr;
// Governance settings
address guardian;
address curator;
uint256 timelock;
uint256 maxDeposit;
uint64 performanceFeeRate;
uint256 idleFunds; // asset.balanceOf(address(this))
// Financial metrics
uint256 totalFt;
uint256 accretingPrincipal;
uint256 annualizedInterest;
uint256 performanceFee;
// Queue information
address[] supplyQueue;
address[] withdrawQueue;
// Pending governance updates
PendingAddress pendingGuardian;
PendingUint192 pendingTimelock;
PendingUint192 pendingPerformanceFeeRate;
uint256 maxMint; // maxMint(address(0))
uint256 convertToSharesPrice; // convertToShares(One)
bool isPaused;
}
function getPositionDetail(ITermMaxMarket market, address owner) public view returns (Position memory position) {
(IMintableERC20 ft, IMintableERC20 xt, IGearingToken gt, address collateral, IERC20 underlying) =
market.tokens();
position.underlyingBalance = underlying.balanceOf(owner);
position.collateralBalance = IERC20(collateral).balanceOf(owner);
position.ftBalance = ft.balanceOf(owner);
position.xtBalance = xt.balanceOf(owner);
IERC721Enumerable gtNft = IERC721Enumerable(address(gt));
uint256 balance = gtNft.balanceOf(owner);
LoanPosition[] memory gtInfos = new LoanPosition[](balance);
uint256 validPositions = 0;
for (uint256 i = 0; i < balance; ++i) {
uint256 loanId = gtNft.tokenOfOwnerByIndex(owner, i);
try gt.loanInfo(loanId) returns (address, uint128 debtAmt, bytes memory collateralData) {
gtInfos[validPositions].loanId = loanId;
gtInfos[validPositions].debtAmt = debtAmt;
gtInfos[validPositions].collateralAmt = _decodeAmount(collateralData);
validPositions++;
} catch {
// Skip this loan ID if loanInfo call fails
}
}
position.gtInfo = new LoanPosition[](validPositions);
for (uint256 i = 0; i < validPositions; i++) {
position.gtInfo[i] = gtInfos[i];
}
}
function getPositionDetails(ITermMaxMarket[] memory market, address owner)
external
view
returns (Position[] memory)
{
Position[] memory positions = new Position[](market.length);
for (uint256 i = 0; i < market.length; ++i) {
positions[i] = getPositionDetail(market[i], owner);
}
return positions;
}
function getAllLoanPosition(ITermMaxMarket market, address owner) external view returns (LoanPosition[] memory) {
(,, IGearingToken gt,,) = market.tokens();
uint256 balance = gt.balanceOf(owner);
LoanPosition[] memory loanPositionsTmp = new LoanPosition[](balance);
uint256 validPositions = 0;
for (uint256 i = 0; i < balance; ++i) {
uint256 loanId = gt.tokenOfOwnerByIndex(owner, i);
try gt.loanInfo(loanId) returns (address, uint128 debtAmt, bytes memory collateralData) {
loanPositionsTmp[validPositions].loanId = loanId;
loanPositionsTmp[validPositions].debtAmt = debtAmt;
loanPositionsTmp[validPositions].collateralAmt = _decodeAmount(collateralData);
validPositions++;
} catch {
// Skip this loan ID if loanInfo call fails
}
}
LoanPosition[] memory loanPositions = new LoanPosition[](validPositions);
for (uint256 i = 0; i < validPositions; i++) {
loanPositions[i] = loanPositionsTmp[i];
}
return loanPositions;
}
function getAllLoanPositionV2(ITermMaxMarket market) external view returns (LoanPositionV2[] memory) {
(,, IGearingToken gtNft,,) = market.tokens();
GtConfig memory config = gtNft.getGtConfig();
uint256 supply = gtNft.totalSupply();
LoanPositionV2[] memory loanPositionsTmp = new LoanPositionV2[](supply);
uint256 validPositions = 0;
for (uint256 i = 0; i < supply; ++i) {
uint256 loanId = gtNft.tokenByIndex(i);
try gtNft.loanInfo(loanId) returns (address owner, uint128 debtAmt, bytes memory collateralData) {
loanPositionsTmp[validPositions].loanId = loanId;
loanPositionsTmp[validPositions].debtAmt = debtAmt;
loanPositionsTmp[validPositions].collateralAmt = _decodeAmount(collateralData);
loanPositionsTmp[validPositions].owner = owner;
try gtNft.getLiquidationInfo(loanId) returns (bool isLiquidable, uint128 ltv, uint128 maxRepayAmt) {
loanPositionsTmp[validPositions].ltv = ltv;
loanPositionsTmp[validPositions].isHealthy = ltv >= config.loanConfig.liquidationLtv;
loanPositionsTmp[validPositions].isLiquidable = isLiquidable;
loanPositionsTmp[validPositions].maxRepayAmt = maxRepayAmt;
} catch {
// Skip this loan ID if getLiquidationInfo call fails
}
validPositions++;
} catch {
// Skip this loan ID if loanInfo call fails
continue;
}
}
LoanPositionV2[] memory loanPositions = new LoanPositionV2[](validPositions);
for (uint256 i = 0; i < validPositions; i++) {
loanPositions[i] = loanPositionsTmp[i];
}
return loanPositions;
}
function getVaultBalance(address user, ITermMaxVault[] memory vaults, OracleAggregator oracleAggregator)
external
view
returns (VaultPosition[] memory)
{
VaultPosition[] memory vaultPositions = new VaultPosition[](vaults.length);
for (uint256 i = 0; i < vaults.length; i++) {
address asset = vaults[i].asset();
uint256 balance = vaults[i].balanceOf(user);
vaultPositions[i].balance = balance;
vaultPositions[i].toAssetBalance = vaults[i].convertToAssets(balance);
try oracleAggregator.getPrice(asset) returns (uint256 price, uint8) {
uint8 assetDecimals = IERC20Metadata(asset).decimals();
vaultPositions[i].usdValue = vaultPositions[i].toAssetBalance.mulDiv(price, 10 ** assetDecimals);
} catch {
vaultPositions[i].usdValue = 0;
}
}
return vaultPositions;
}
function getOrderState(ITermMaxOrder order) external view returns (OrderState memory orderState) {
ITermMaxMarket market = order.market();
(,, IGearingToken gt,,) = market.tokens();
(OrderConfig memory orderConfig) = order.orderConfig();
(uint256 ftReserve, uint256 xtReserve) = order.tokenReserves();
if (orderConfig.gtId != 0) {
try gt.loanInfo(orderConfig.gtId) returns (address, uint128 debtAmt, bytes memory collateralData) {
orderState.collateralReserve = _decodeAmount(collateralData);
orderState.debtReserve = debtAmt;
} catch {
// If loan info is unavailable, set defaults
orderState.collateralReserve = 0;
orderState.debtReserve = 0;
}
}
orderState.ftReserve = ftReserve;
orderState.xtReserve = xtReserve;
orderState.maxXtReserve = orderConfig.maxXtReserve;
orderState.gtId = orderConfig.gtId;
orderState.curveCuts = orderConfig.curveCuts;
orderState.feeConfig = orderConfig.feeConfig;
return orderState;
}
/**
* @notice Get comprehensive information about a TermMaxVault
* @param vault The TermMaxVault to query
* @return vaultInfo The vault information
*/
function getVaultInfo(ITermMaxVault vault) external view returns (VaultInfo memory vaultInfo) {
IERC20 asset = IERC20(vault.asset());
// Basic vault metrics
vaultInfo.name = vault.name();
vaultInfo.symbol = vault.symbol();
vaultInfo.assetAddress = address(asset);
vaultInfo.totalAssets = vault.totalAssets();
vaultInfo.totalSupply = vault.totalSupply();
vaultInfo.apr = vault.apr();
// Governance settings
vaultInfo.guardian = vault.guardian();
vaultInfo.curator = vault.curator();
vaultInfo.timelock = vault.timelock();
vaultInfo.maxDeposit = vault.maxDeposit(address(0));
vaultInfo.idleFunds = asset.balanceOf(address(vault));
// Financial metrics
vaultInfo.totalFt = vault.totalFt();
vaultInfo.accretingPrincipal = vault.accretingPrincipal();
vaultInfo.annualizedInterest = vault.annualizedInterest();
vaultInfo.performanceFeeRate = vault.performanceFeeRate();
vaultInfo.performanceFee = vault.performanceFee();
// Queue information
uint256 supplyQueueLength = vault.supplyQueueLength();
vaultInfo.supplyQueue = new address[](supplyQueueLength);
for (uint256 i = 0; i < supplyQueueLength; i++) {
vaultInfo.supplyQueue[i] = vault.supplyQueue(i);
}
uint256 withdrawQueueLength = vault.withdrawQueueLength();
vaultInfo.withdrawQueue = new address[](withdrawQueueLength);
for (uint256 i = 0; i < withdrawQueueLength; i++) {
vaultInfo.withdrawQueue[i] = vault.withdrawQueue(i);
}
// Pending governance updates
vaultInfo.pendingGuardian = vault.pendingGuardian();
vaultInfo.pendingTimelock = vault.pendingTimelock();
vaultInfo.pendingPerformanceFeeRate = vault.pendingPerformanceFeeRate();
vaultInfo.maxMint = vault.maxMint(address(0));
uint256 one = 10 ** vault.decimals();
vaultInfo.convertToSharesPrice = vault.convertToShares(one);
vaultInfo.isPaused = IPausable(address(vault)).paused();
}
/**
* @notice Get information about all orders in a vault
* @param vault The TermMaxVault to query
* @return orderInfos Array of information about each order in the vault
*/
function getVaultOrdersInfo(ITermMaxVault vault) external view returns (OrderState[] memory) {
uint256 supplyQueueLength = vault.supplyQueueLength();
OrderState[] memory orderInfos = new OrderState[](supplyQueueLength);
for (uint256 i = 0; i < supplyQueueLength; i++) {
address orderAddress = vault.supplyQueue(i);
ITermMaxOrder order = ITermMaxOrder(orderAddress);
orderInfos[i] = this.getOrderState(order);
}
return orderInfos;
}
function getVaultPendingMarkets(ITermMaxVault vault, address[] calldata markets)
external
view
returns (PendingUint192[] memory)
{
PendingUint192[] memory pendingMarkets = new PendingUint192[](markets.length);
for (uint256 i = 0; i < markets.length; i++) {
pendingMarkets[i] = vault.pendingMarkets(markets[i]);
}
return pendingMarkets;
}
function _decodeAmount(bytes memory collateralData) internal pure returns (uint256) {
return abi.decode(collateralData, (uint256));
}
}
"
},
"dependencies/@openzeppelin-contracts-5.2.0/token/ERC20/IERC20.sol": {
"content": "// 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);
}
"
},
"dependencies/@openzeppelin-contracts-5.2.0/token/ERC20/extensions/IERC20Metadata.sol": {
"content": "// 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);
}
"
},
"dependencies/@openzeppelin-contracts-5.2.0/interfaces/IERC721Enumerable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC721Enumerable.sol)
pragma solidity ^0.8.20;
import {IERC721Enumerable} from "../token/ERC721/extensions/IERC721Enumerable.sol";
"
},
"dependencies/@openzeppelin-contracts-5.2.0/utils/math/Math.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/math/Math.sol)
pragma solidity ^0.8.20;
import {Panic} from "../Panic.sol";
import {SafeCast} from "./SafeCast.sol";
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
enum Rounding {
Floor, // Toward negative infinity
Ceil, // Toward positive infinity
Trunc, // Toward zero
Expand // Away from zero
}
/**
* @dev Returns the addition of two unsigned integers, with an success flag (no overflow).
*/
function tryAdd(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the subtraction of two unsigned integers, with an success flag (no overflow).
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
if (b > a) return (false, 0);
return (true, a - b);
}
}
/**
* @dev Returns the multiplication of two unsigned integers, with an success flag (no overflow).
*/
function tryMul(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the division of two unsigned integers, with a success flag (no division by zero).
*/
function tryDiv(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
if (b == 0) return (false, 0);
return (true, a / b);
}
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a success flag (no division by zero).
*/
function tryMod(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
if (b == 0) return (false, 0);
return (true, a % b);
}
}
/**
* @dev Branchless ternary evaluation for `a ? b : c`. Gas costs are constant.
*
* IMPORTANT: This function may reduce bytecode size and consume less gas when used standalone.
* However, the compiler may optimize Solidity ternary operations (i.e. `a ? b : c`) to only compute
* one branch when needed, making this function more expensive.
*/
function ternary(bool condition, uint256 a, uint256 b) internal pure returns (uint256) {
unchecked {
// branchless ternary works because:
// b ^ (a ^ b) == a
// b ^ 0 == b
return b ^ ((a ^ b) * SafeCast.toUint(condition));
}
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return ternary(a > b, a, b);
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return ternary(a < b, a, b);
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds towards infinity instead
* of rounding towards zero.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
if (b == 0) {
// Guarantee the same behavior as in a regular Solidity division.
Panic.panic(Panic.DIVISION_BY_ZERO);
}
// The following calculation ensures accurate ceiling division without overflow.
// Since a is non-zero, (a - 1) / b will not overflow.
// The largest possible result occurs when (a - 1) / b is type(uint256).max,
// but the largest value we can obtain is type(uint256).max - 1, which happens
// when a = type(uint256).max and b = 1.
unchecked {
return SafeCast.toUint(a > 0) * ((a - 1) / b + 1);
}
}
/**
* @dev Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
* denominator == 0.
*
* Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
* Uniswap Labs also under MIT license.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2²⁵⁶ and mod 2²⁵⁶ - 1, then use
// the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2²⁵⁶ + prod0.
uint256 prod0 = x * y; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.
// The surrounding unchecked block does not change this fact.
// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
return prod0 / denominator;
}
// Make sure the result is less than 2²⁵⁶. Also prevents denominator == 0.
if (denominator <= prod1) {
Panic.panic(ternary(denominator == 0, Panic.DIVISION_BY_ZERO, Panic.UNDER_OVERFLOW));
}
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator.
// Always >= 1. See https://cs.stackexchange.com/q/138556/92363.
uint256 twos = denominator & (0 - denominator);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2²⁵⁶ / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2²⁵⁶. Now that denominator is an odd number, it has an inverse modulo 2²⁵⁶ such
// that denominator * inv ≡ 1 mod 2²⁵⁶. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv ≡ 1 mod 2⁴.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also
// works in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2⁸
inverse *= 2 - denominator * inverse; // inverse mod 2¹⁶
inverse *= 2 - denominator * inverse; // inverse mod 2³²
inverse *= 2 - denominator * inverse; // inverse mod 2⁶⁴
inverse *= 2 - denominator * inverse; // inverse mod 2¹²⁸
inverse *= 2 - denominator * inverse; // inverse mod 2²⁵⁶
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2²⁵⁶. Since the preconditions guarantee that the outcome is
// less than 2²⁵⁶, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @dev Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
return mulDiv(x, y, denominator) + SafeCast.toUint(unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0);
}
/**
* @dev Calculate the modular multiplicative inverse of a number in Z/nZ.
*
* If n is a prime, then Z/nZ is a field. In that case all elements are inversible, except 0.
* If n is not a prime, then Z/nZ is not a field, and some elements might not be inversible.
*
* If the input value is not inversible, 0 is returned.
*
* NOTE: If you know for sure that n is (big) a prime, it may be cheaper to use Fermat's little theorem and get the
* inverse using `Math.modExp(a, n - 2, n)`. See {invModPrime}.
*/
function invMod(uint256 a, uint256 n) internal pure returns (uint256) {
unchecked {
if (n == 0) return 0;
// The inverse modulo is calculated using the Extended Euclidean Algorithm (iterative version)
// Used to compute integers x and y such that: ax + ny = gcd(a, n).
// When the gcd is 1, then the inverse of a modulo n exists and it's x.
// ax + ny = 1
// ax = 1 + (-y)n
// ax ≡ 1 (mod n) # x is the inverse of a modulo n
// If the remainder is 0 the gcd is n right away.
uint256 remainder = a % n;
uint256 gcd = n;
// Therefore the initial coefficients are:
// ax + ny = gcd(a, n) = n
// 0a + 1n = n
int256 x = 0;
int256 y = 1;
while (remainder != 0) {
uint256 quotient = gcd / remainder;
(gcd, remainder) = (
// The old remainder is the next gcd to try.
remainder,
// Compute the next remainder.
// Can't overflow given that (a % gcd) * (gcd // (a % gcd)) <= gcd
// where gcd is at most n (capped to type(uint256).max)
gcd - remainder * quotient
);
(x, y) = (
// Increment the coefficient of a.
y,
// Decrement the coefficient of n.
// Can overflow, but the result is casted to uint256 so that the
// next value of y is "wrapped around" to a value between 0 and n - 1.
x - y * int256(quotient)
);
}
if (gcd != 1) return 0; // No inverse exists.
return ternary(x < 0, n - uint256(-x), uint256(x)); // Wrap the result if it's negative.
}
}
/**
* @dev Variant of {invMod}. More efficient, but only works if `p` is known to be a prime greater than `2`.
*
* From https://en.wikipedia.org/wiki/Fermat%27s_little_theorem[Fermat's little theorem], we know that if p is
* prime, then `a**(p-1) ≡ 1 mod p`. As a consequence, we have `a * a**(p-2) ≡ 1 mod p`, which means that
* `a**(p-2)` is the modular multiplicative inverse of a in Fp.
*
* NOTE: this function does NOT check that `p` is a prime greater than `2`.
*/
function invModPrime(uint256 a, uint256 p) internal view returns (uint256) {
unchecked {
return Math.modExp(a, p - 2, p);
}
}
/**
* @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m)
*
* Requirements:
* - modulus can't be zero
* - underlying staticcall to precompile must succeed
*
* IMPORTANT: The result is only valid if the underlying call succeeds. When using this function, make
* sure the chain you're using it on supports the precompiled contract for modular exponentiation
* at address 0x05 as specified in https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise,
* the underlying function will succeed given the lack of a revert, but the result may be incorrectly
* interpreted as 0.
*/
function modExp(uint256 b, uint256 e, uint256 m) internal view returns (uint256) {
(bool success, uint256 result) = tryModExp(b, e, m);
if (!success) {
Panic.panic(Panic.DIVISION_BY_ZERO);
}
return result;
}
/**
* @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m).
* It includes a success flag indicating if the operation succeeded. Operation will be marked as failed if trying
* to operate modulo 0 or if the underlying precompile reverted.
*
* IMPORTANT: The result is only valid if the success flag is true. When using this function, make sure the chain
* you're using it on supports the precompiled contract for modular exponentiation at address 0x05 as specified in
* https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise, the underlying function will succeed given the lack
* of a revert, but the result may be incorrectly interpreted as 0.
*/
function tryModExp(uint256 b, uint256 e, uint256 m) internal view returns (bool success, uint256 result) {
if (m == 0) return (false, 0);
assembly ("memory-safe") {
let ptr := mload(0x40)
// | Offset | Content | Content (Hex) |
// |-----------|------------|--------------------------------------------------------------------|
// | 0x00:0x1f | size of b | 0x0000000000000000000000000000000000000000000000000000000000000020 |
// | 0x20:0x3f | size of e | 0x0000000000000000000000000000000000000000000000000000000000000020 |
// | 0x40:0x5f | size of m | 0x0000000000000000000000000000000000000000000000000000000000000020 |
// | 0x60:0x7f | value of b | 0x<.............................................................b> |
// | 0x80:0x9f | value of e | 0x<.............................................................e> |
// | 0xa0:0xbf | value of m | 0x<.............................................................m> |
mstore(ptr, 0x20)
mstore(add(ptr, 0x20), 0x20)
mstore(add(ptr, 0x40), 0x20)
mstore(add(ptr, 0x60), b)
mstore(add(ptr, 0x80), e)
mstore(add(ptr, 0xa0), m)
// Given the result < m, it's guaranteed to fit in 32 bytes,
// so we can use the memory scratch space located at offset 0.
success := staticcall(gas(), 0x05, ptr, 0xc0, 0x00, 0x20)
result := mload(0x00)
}
}
/**
* @dev Variant of {modExp} that supports inputs of arbitrary length.
*/
function modExp(bytes memory b, bytes memory e, bytes memory m) internal view returns (bytes memory) {
(bool success, bytes memory result) = tryModExp(b, e, m);
if (!success) {
Panic.panic(Panic.DIVISION_BY_ZERO);
}
return result;
}
/**
* @dev Variant of {tryModExp} that supports inputs of arbitrary length.
*/
function tryModExp(
bytes memory b,
bytes memory e,
bytes memory m
) internal view returns (bool success, bytes memory result) {
if (_zeroBytes(m)) return (false, new bytes(0));
uint256 mLen = m.length;
// Encode call args in result and move the free memory pointer
result = abi.encodePacked(b.length, e.length, mLen, b, e, m);
assembly ("memory-safe") {
let dataPtr := add(result, 0x20)
// Write result on top of args to avoid allocating extra memory.
success := staticcall(gas(), 0x05, dataPtr, mload(result), dataPtr, mLen)
// Overwrite the length.
// result.length > returndatasize() is guaranteed because returndatasize() == m.length
mstore(result, mLen)
// Set the memory pointer after the returned data.
mstore(0x40, add(dataPtr, mLen))
}
}
/**
* @dev Returns whether the provided byte array is zero.
*/
function _zeroBytes(bytes memory byteArray) private pure returns (bool) {
for (uint256 i = 0; i < byteArray.length; ++i) {
if (byteArray[i] != 0) {
return false;
}
}
return true;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
* towards zero.
*
* This method is based on Newton's method for computing square roots; the algorithm is restricted to only
* using integer operations.
*/
function sqrt(uint256 a) internal pure returns (uint256) {
unchecked {
// Take care of easy edge cases when a == 0 or a == 1
if (a <= 1) {
return a;
}
// In this function, we use Newton's method to get a root of `f(x) := x² - a`. It involves building a
// sequence x_n that converges toward sqrt(a). For each iteration x_n, we also define the error between
// the current value as `ε_n = | x_n - sqrt(a) |`.
//
// For our first estimation, we consider `e` the smallest power of 2 which is bigger than the square root
// of the target. (i.e. `2**(e-1) ≤ sqrt(a) < 2**e`). We know that `e ≤ 128` because `(2¹²⁸)² = 2²⁵⁶` is
// bigger than any uint256.
//
// By noticing that
// `2**(e-1) ≤ sqrt(a) < 2**e → (2**(e-1))² ≤ a < (2**e)² → 2**(2*e-2) ≤ a < 2**(2*e)`
// we can deduce that `e - 1` is `log2(a) / 2`. We can thus compute `x_n = 2**(e-1)` using a method similar
// to the msb function.
uint256 aa = a;
uint256 xn = 1;
if (aa >= (1 << 128)) {
aa >>= 128;
xn <<= 64;
}
if (aa >= (1 << 64)) {
aa >>= 64;
xn <<= 32;
}
if (aa >= (1 << 32)) {
aa >>= 32;
xn <<= 16;
}
if (aa >= (1 << 16)) {
aa >>= 16;
xn <<= 8;
}
if (aa >= (1 << 8)) {
aa >>= 8;
xn <<= 4;
}
if (aa >= (1 << 4)) {
aa >>= 4;
xn <<= 2;
}
if (aa >= (1 << 2)) {
xn <<= 1;
}
// We now have x_n such that `x_n = 2**(e-1) ≤ sqrt(a) < 2**e = 2 * x_n`. This implies ε_n ≤ 2**(e-1).
//
// We can refine our estimation by noticing that the middle of that interval minimizes the error.
// If we move x_n to equal 2**(e-1) + 2**(e-2), then we reduce the error to ε_n ≤ 2**(e-2).
// This is going to be our x_0 (and ε_0)
xn = (3 * xn) >> 1; // ε_0 := | x_0 - sqrt(a) | ≤ 2**(e-2)
// From here, Newton's method give us:
// x_{n+1} = (x_n + a / x_n) / 2
//
// One should note that:
// x_{n+1}² - a = ((x_n + a / x_n) / 2)² - a
// = ((x_n² + a) / (2 * x_n))² - a
// = (x_n⁴ + 2 * a * x_n² + a²) / (4 * x_n²) - a
// = (x_n⁴ + 2 * a * x_n² + a² - 4 * a * x_n²) / (4 * x_n²)
// = (x_n⁴ - 2 * a * x_n² + a²) / (4 * x_n²)
// = (x_n² - a)² / (2 * x_n)²
// = ((x_n² - a) / (2 * x_n))²
// ≥ 0
// Which proves that for all n ≥ 1, sqrt(a) ≤ x_n
//
// This gives us the proof of quadratic convergence of the sequence:
// ε_{n+1} = | x_{n+1} - sqrt(a) |
// = | (x_n + a / x_n) / 2 - sqrt(a) |
// = | (x_n² + a - 2*x_n*sqrt(a)) / (2 * x_n) |
// = | (x_n - sqrt(a))² / (2 * x_n) |
// = | ε_n² / (2 * x_n) |
// = ε_n² / | (2 * x_n) |
//
// For the first iteration, we have a special case where x_0 is known:
// ε_1 = ε_0² / | (2 * x_0) |
// ≤ (2**(e-2))² / (2 * (2**(e-1) + 2**(e-2)))
// ≤ 2**(2*e-4) / (3 * 2**(e-1))
// ≤ 2**(e-3) / 3
// ≤ 2**(e-3-log2(3))
// ≤ 2**(e-4.5)
//
// For the following iterations, we use the fact that, 2**(e-1) ≤ sqrt(a) ≤ x_n:
// ε_{n+1} = ε_n² / | (2 * x_n) |
// ≤ (2**(e-k))² / (2 * 2**(e-1))
// ≤ 2**(2*e-2*k) / 2**e
// ≤ 2**(e-2*k)
xn = (xn + a / xn) >> 1; // ε_1 := | x_1 - sqrt(a) | ≤ 2**(e-4.5) -- special case, see above
xn = (xn + a / xn) >> 1; // ε_2 := | x_2 - sqrt(a) | ≤ 2**(e-9) -- general case with k = 4.5
xn = (xn + a / xn) >> 1; // ε_3 := | x_3 - sqrt(a) | ≤ 2**(e-18) -- general case with k = 9
xn = (xn + a / xn) >> 1; // ε_4 := | x_4 - sqrt(a) | ≤ 2**(e-36) -- general case with k = 18
xn = (xn + a / xn) >> 1; // ε_5 := | x_5 - sqrt(a) | ≤ 2**(e-72) -- general case with k = 36
xn = (xn + a / xn) >> 1; // ε_6 := | x_6 - sqrt(a) | ≤ 2**(e-144) -- general case with k = 72
// Because e ≤ 128 (as discussed during the first estimation phase), we know have reached a precision
// ε_6 ≤ 2**(e-144) < 1. Given we're operating on integers, then we can ensure that xn is now either
// sqrt(a) or sqrt(a) + 1.
return xn - SafeCast.toUint(xn > a / xn);
}
}
/**
* @dev Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + SafeCast.toUint(unsignedRoundsUp(rounding) && result * result < a);
}
}
/**
* @dev Return the log in base 2 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
uint256 exp;
unchecked {
exp = 128 * SafeCast.toUint(value > (1 << 128) - 1);
value >>= exp;
result += exp;
exp = 64 * SafeCast.toUint(value > (1 << 64) - 1);
value >>= exp;
result += exp;
exp = 32 * SafeCast.toUint(value > (1 << 32) - 1);
value >>= exp;
result += exp;
exp = 16 * SafeCast.toUint(value > (1 << 16) - 1);
value >>= exp;
result += exp;
exp = 8 * SafeCast.toUint(value > (1 << 8) - 1);
value >>= exp;
result += exp;
exp = 4 * SafeCast.toUint(value > (1 << 4) - 1);
value >>= exp;
result += exp;
exp = 2 * SafeCast.toUint(value > (1 << 2) - 1);
value >>= exp;
result += exp;
result += SafeCast.toUint(value > 1);
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + SafeCast.toUint(unsignedRoundsUp(rounding) && 1 << result < value);
}
}
/**
* @dev Return the log in base 10 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + SafeCast.toUint(unsignedRoundsUp(rounding) && 10 ** result < value);
}
}
/**
* @dev Return the log in base 256 of a positive value rounded towards zero.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
uint256 isGt;
unchecked {
isGt = SafeCast.toUint(value > (1 << 128) - 1);
value >>= isGt * 128;
result += isGt * 16;
isGt = SafeCast.toUint(value > (1 << 64) - 1);
value >>= isGt * 64;
result += isGt * 8;
isGt = SafeCast.toUint(value > (1 << 32) - 1);
value >>= isGt * 32;
result += isGt * 4;
isGt = SafeCast.toUint(value > (1 << 16) - 1);
value >>= isGt * 16;
result += isGt * 2;
result += SafeCast.toUint(value > (1 << 8) - 1);
}
return result;
}
/**
* @dev Return the log in base 256, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + SafeCast.toUint(unsignedRoundsUp(rounding) && 1 << (result << 3) < value);
}
}
/**
* @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
*/
function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
return uint8(rounding) % 2 == 1;
}
}
"
},
"contracts/v1/ITermMaxMarket.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {IMintableERC20, IERC20} from "./tokens/IMintableERC20.sol";
import {IGearingToken} from "./tokens/IGearingToken.sol";
import {ITermMaxOrder} from "./ITermMaxOrder.sol";
import {MarketConfig, MarketInitialParams, CurveCuts, FeeConfig} from "./storage/TermMaxStorage.sol";
import {Clones} from "@openzeppelin/contracts/proxy/Clones.sol";
import {ISwapCallback} from "./ISwapCallback.sol";
/**
* @title TermMax Market interface
* @author Term Structure Labs
*/
interface ITermMaxMarket {
/// @notice Initialize the token and configuration of the market
function initialize(MarketInitialParams memory params) external;
/// @notice Return the configuration
function config() external view returns (MarketConfig memory);
/// @notice Set the market configuration
function updateMarketConfig(MarketConfig calldata newConfig) external;
/// @notice Return the tokens in TermMax Market
/// @return ft Fixed-rate Token(bond token). Earning Fixed Income with High Certainty
/// @return xt Intermediary Token for Collateralization and Leveragin
/// @return gt Gearing Token
/// @return collateral Collateral token
/// @return underlying Underlying Token(debt)
function tokens()
external
view
returns (IMintableERC20 ft, IMintableERC20 xt, IGearingToken gt, address collateral, IERC20 underlying);
/// @notice Mint FT and XT tokens by underlying token.
/// No price slippage or handling fees.
/// @param debtTokenAmt Amount of underlying token want to lock
function mint(address recipient, uint256 debtTokenAmt) external;
/// @notice Burn FT and XT to get underlying token.
/// No price slippage or handling fees.
/// @param debtTokenAmt Amount of underlying token want to get
function burn(address recipient, uint256 debtTokenAmt) external;
/// @notice Using collateral to issue FT tokens.
/// Caller will get FT(bond) tokens equal to the debt amount subtract issue fee
/// @param debt The amount of debt, unit by underlying token
/// @param collateralData The encoded data of collateral
/// @return gtId The id of Gearing Token
///
function issueFt(address recipient, uint128 debt, bytes calldata collateralData)
external
returns (uint256 gtId, uint128 ftOutAmt);
/// @notice Return the issue fee ratio
function mintGtFeeRatio() external view returns (uint256);
/// @notice Using collateral to issue FT tokens.
/// Caller will get FT(bond) tokens equal to the debt amount subtract issue fee
/// @param recipient Who will receive Gearing Token
/// @param debt The amount of debt, unit by underlying token
/// @param gtId The id of Gearing Token
/// @return ftOutAmt The amount of FT issued
///
function issueFtByExistedGt(address recipient, uint128 debt, uint256 gtId) external returns (uint128 ftOutAmt);
/// @notice Flash loan underlying token for leverage
/// @param recipient Who will receive Gearing Token
/// @param xtAmt The amount of XT token.
/// The caller will receive an equal amount of underlying token by flash loan.
/// @param callbackData The data of flash loan callback
/// @return gtId The id of Gearing Token
function leverageByXt(address recipient, uint128 xtAmt, bytes calldata callbackData)
external
returns (uint256 gtId);
/// @notice Preview the redeem amount and delivery data
/// @param ftAmount The amount of FT want to redeem
/// @return debtTokenAmt The amount of debt token
/// @return deliveryData The delivery data
function previewRedeem(uint256 ftAmount) external view returns (uint256 debtTokenAmt, bytes memory deliveryData);
/// @notice Redeem underlying tokens after maturity
/// @param ftAmount The amount of FT want to redeem
/// @param recipient Who will receive the underlying tokens
/// @return debtTokenAmt The amount of debt token
/// @return deliveryData The delivery data
function redeem(uint256 ftAmount, address recipient)
external
returns (uint256 debtTokenAmt, bytes memory deliveryData);
/// @notice Set the configuration of Gearing Token
function updateGtConfig(bytes memory configData) external;
/// @notice Set the fee rate of order
function updateOrderFeeRate(ITermMaxOrder order, FeeConfig memory newFeeConfig) external;
/// @notice Create a new order
function createOrder(address maker, uint256 maxXtReserve, ISwapCallback swapTrigger, CurveCuts memory curveCuts)
external
returns (ITermMaxOrder order);
}
"
},
"contracts/v1/ITermMaxOrder.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;
import {IMintableERC20, IERC20} from "./tokens/IMintableERC20.sol";
import {IGearingToken} from "./tokens/IGearingToken.sol";
import {ITermMaxMarket} from "./ITermMaxMarket.sol";
import {OrderConfig, MarketConfig, CurveCuts, FeeConfig} from "./storage/TermMaxStorage.sol";
import {ISwapCallback} from "./ISwapCallback.sol";
/**
* @title TermMax Order interface
* @author Term Structure Labs
*/
interface ITermMaxOrder {
/// @notice Initialize the token and configuration of the order
/// @param maker The maker
/// @param tokens The tokens
/// @param gt The Gearing Token
/// @param maxXtReserve The maximum reserve of XT token
/// @param curveCuts The curve cuts
/// @param marketConfig The market configuration
/// @dev Only factory will call this function once when deploying new market
function initialize(
address maker,
IERC20[3] memory tokens,
IGearingToken gt,
uint256 maxXtReserve,
ISwapCallback trigger,
CurveCuts memory curveCuts,
MarketConfig memory marketConfig
) external;
/// @notice Return the configuration
function orderConfig() external view returns (OrderConfig memory);
/// @notice Return the maker
function maker() external view returns (address);
/// @notice Set the market configuration
/// @param newOrderConfig New order configuration
/// @param ftChangeAmt Change amount of FT reserve
/// @param xtChangeAmt Change amount of XT reserve
function updateOrder(OrderConfig memory newOrderConfig, int256 ftChangeAmt, int256 xtChangeAmt) external;
function withdrawAssets(IERC20 token, address recipient, uint256 amount) external;
function updateFeeConfig(FeeConfig memory newFeeConfig) external;
/// @notice Return the token reserves
function tokenReserves() external view returns (uint256 ftReserve, uint256 xtReserve);
/// @notice Return the tokens in TermMax Market
/// @return market The market
function market() external view returns (ITermMaxMarket market);
/// @notice Return the current apr of the amm order book
/// @return lendApr Lend APR
/// @return borrowApr Borrow APR
function apr() external view returns (uint256 lendApr, uint256 borrowApr);
/// @notice Swap exact token to token
/// @param tokenIn The token want to swap
/// @param tokenOut The token want to receive
/// @param recipient Who receive output tokens
/// @param tokenAmtIn The number of tokenIn tokens input
/// @param minTokenOut Minimum number of tokenOut token outputs required
/// @param deadline The timestamp after which the transaction will revert
/// @return netOut The actual number of tokenOut tokens received
function swapExactTokenToToken(
IERC20 tokenIn,
IERC20 tokenOut,
address recipient,
uint128 tokenAmtIn,
uint128 minTokenOut,
uint256 deadline
) external returns (uint256 netOut);
/// @notice Swap token to exact token
/// @param tokenIn The token want to swap
/// @param tokenOut The token want to receive
/// @param recipient Who receive output tokens
/// @param tokenAmtOut The number of tokenOut tokens output
/// @param maxTokenIn Maximum number of tokenIn token inputs required
/// @param deadline The timestamp after which the transaction will revert
/// @return netIn The actual number of tokenIn tokens input
function swapTokenToExactToken(
IERC20 tokenIn,
IERC20 tokenOut,
address recipient,
uint128 tokenAmtOut,
uint128 maxTokenIn,
uint256 deadline
) external returns (uint256 netIn);
/// @notice Suspension of market trading
function pause() external;
/// @notice Open Market Trading
function unpause() external;
}
"
},
"contracts/v1/tokens/IMintableERC20.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
/**
* @title TermMax ERC20 token interface
* @author Term Structure Labs
*/
interface IMintableERC20 is IERC20 {
/// @notice Error when using offline signature but spender is not the maerket
error SpenderIsNotMarket(address spender);
// @notice Initial function
/// @param name The token's name
/// @param symbol The token's symbol
/// @param _decimals The token's decimals
function initialize(string memory name, string memory symbol, uint8 _decimals) external;
/// @notice Mint this token to an address
/// @param to The address receiving token
/// @param amount The amount of token minted
/// @dev Only the market can mint TermMax tokens
function mint(address to, uint256 amount) external;
/// @notice Return the market's address
function marketAddr() external view returns (address);
/// @notice Burn tokens from sender
/// @param amount The number of tokens to be burned
/// @dev Only the market can burn TermMax tokens
function burn(uint256 amount) external;
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}
"
},
"contracts/v1/tokens/IGearingToken.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.27;
import {IERC20Metadata, IERC20} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {IERC721Enumerable} from "@openzeppelin/contracts/token/ERC721/extensions/IERC721Enumerable.sol";
import {GtConfig} from "../storage/TermMaxStorage.sol";
/**
* @title TermMax Gearing token interface
* @author Term Structure Labs
*/
interface IGearingToken is IERC721Enumerable {
// @notice Initial function
/// @param name The token's name
/// @param symbol The token's symbol
/// @param config Configuration of GT
/// @param initalParams The initilization parameters of implementation
function initialize(string memory name, string memory symbol, GtConfig memory config, bytes memory initalParams)
external;
/// @notice Set the treasurer address
/// @param treasurer New address of treasurer
/// @dev Only the market can call this function
function setTreasurer(address treasurer) external;
/// @notice Set the configuration of Gearing Token
function updateConfig(bytes memory configData) external;
/// @notice Return the configuration of Gearing Token
function getGtConfig() external view returns (GtConfig memory);
/// @notice Return the flag to indicate debt is liquidatable or not
function liquidatable() external view returns (bool);
/// @notice Return the market address
function marketAddr() external view returns (address);
/// @notice Mint this token to an address
/// @param collateralProvider Who provide collateral token
/// @param to The address receiving token
/// @param debtAmt The amount of debt, unit by debtToken token
/// @param collateralData The encoded data of collateral
/// @return id The id of Gearing Token
/// @dev Only the market can mint Gearing Token
function mint(address collateralProvider, address to, uint128 debtAmt, bytes memory collateralData)
external
returns (uint256 id);
/// @notice Augment the debt of Gearing Token
/// @param id The id of Gearing Token
/// @param ftAmt The amount of debt, unit by debtToken token
function augmentDebt(address caller, uint256 id, uint256 ftAmt) external;
/// @notice Return the loan information of Gearing Token
/// @param id The id of Gearing Token
/// @return owner The owner of Gearing Token
/// @return debtAmt The amount of debt, unit by debtToken token
/// @return collateralData The encoded data of collateral
function loanInfo(uint256 id) external view returns (address owner, uint128 debtAmt, bytes memory collateralData);
/// @notice Merge multiple Gearing Tokens into one
/// @param ids The array of Gearing Tokens to be merged
/// @return newId The id of new Gearing Token
function merge(uint256[] memory ids) external returns (uint256 newId);
/// @notice Repay the debt of Gearing Token.
/// If repay amount equals the debt amount, Gearing Token's owner will get his collateral.
/// @param id The id of Gearing Token
/// @param repayAmt The amount of debt you want to repay
/// @param byDebtToken Repay using debtToken token or bonds token
function repay(uint256 id, uint128 repayAmt, bool byDebtToken) external;
/// @notice Repay the debt of Gearing Token,
/// the collateral will send by flashloan first.
/// @param id The id of Gearing Token
/// @param byDebtToken Repay using debtToken token or bonds token
function flashRepay(uint256 id, bool byDebtToken, bytes calldata callbackData) external;
/// @notice Remove collateral from the loan.
/// Require the loan to value bigger than maxLtv after this action.
/// @param id The id of Gearing Token
/// @param collateralData Collateral data to be removed
function removeCollateral(uint256 id, bytes memory collateralData) external;
/// @notice Add collateral to the loan
/// @param id The id of Gearing Token
/// @param collateralData Collateral data to be added
function addCollateral(uint256 id, bytes memory collateralData) external;
/// @notice Return the liquidation info of the loan
/// @param id The id of the G-token
/// @return isLiquidable Whether the loan is liquidable
/// @return ltv The loan to collateral
/// @return maxRepayAmt The maximum amount of the debt to be repaid
function getLiquidationInfo(uint256 id)
external
view
returns (bool isLiquidable, uint128 ltv, uint128 maxRepayAmt);
/// @notice Liquidate the loan when its ltv bigger than liquidationLtv or expired.
/// The ltv can not inscrease after liquidation.
/// A maximum of 10% of the repayment amount of collateral is given as a
/// reward to the protocol and liquidator,
/// The proportion of collateral liquidated will not exceed the debt liquidation ratio.
/// @param id The id of the G-token
/// @param repayAmt The amount of the debt to be liquidate
/// @param byDebtToken Repay using debtToken token or bonds token
function liquidate(uint256 id, uint128 repayAmt, bool byDebtToken) external;
/// @notice Preview the delivery data
/// @param proportion The proportion of collateral that should be obtained
/// @return deliveryData The delivery data
function previewDelivery(uint256 proportion) external view returns (bytes memory deliveryData);
/// @notice Deilivery outstanding debts after maturity
/// @param proportion The proportion of collateral that should be obtained
/// @param to The address receiving collateral token
/// @dev Only the market can delivery collateral
function delivery(uint256 proportion, address to) external returns (bytes memory deliveryData);
/// @notice Return the value of collateral in USD with base decimals
/// @param collateralData encoded collateral data
/// @return collateralValue collateral's value in USD
function getCollateralValue(bytes memory collateralData) external view returns (uint256 collateralValue);
}
"
},
"contracts/v1/storage/TermMaxStorage.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IOracle} from "../oracle/IOracle.sol";
import {ISwapCallback} from "../ISwapCallback.sol";
/**
* @title The data struct of token pair
* @author Term Structure Labs
*/
struct CurveCut {
uint256 xtReserve;
uint256 liqSquare;
int256 offset;
}
struct FeeConfig {
/// @notice The lending fee ratio taker
/// i.e. 0.01e8 means 1%
uint32 lendTakerFeeRatio;
/// @notice The lending fee ratio for maker
/// i.e. 0.01e8 means 1%
uint32 lendMakerFeeRatio;
/// @notice The borrowing fee ratio for taker
/// i.e. 0.01e8 means 1%
uint32 borrowTakerFeeRatio;
/// @notice The borrowing fee ratio for maker
/// i.e. 0.01e8 means 1%
uint32 borrowMakerFeeRatio;
/// @notice The fee ratio when minting GT tokens by collateral
/// i.e. 0.01e8 means 1%
uint32 mintGtFeeRatio;
/// @notice The fee ref when minting GT tokens by collateral
/// i.e. 0.01e8 means 1%
uint32 mintGtFeeRef;
}
struct CurveCuts {
/// @notice The curve cuts of the market to lend
CurveCut[] lendCurveCuts;
/// @notice The curve cuts of the market to borrow
Cu
Submitted on: 2025-09-26 11:17:23
Comments
Log in to comment.
No comments yet.