Feature Tip: Add private address tag to any address under My Name Tag !
Overview
ETH Balance
0 ETH
Eth Value
$0.00Latest 17 from a total of 17 transactions
| Transaction Hash |
Method
|
Block
|
From
|
|
To
|
||||
|---|---|---|---|---|---|---|---|---|---|
| Redeem Mature | 23668625 | 102 days ago | IN | 0 ETH | 0.00001989 | ||||
| Redeem Mature | 23589188 | 113 days ago | IN | 0 ETH | 0.00013599 | ||||
| Redeem Mature | 23392542 | 140 days ago | IN | 0 ETH | 0.00003952 | ||||
| Redeem Mature | 23384408 | 141 days ago | IN | 0 ETH | 0.00007468 | ||||
| Redeem Mature | 23383720 | 142 days ago | IN | 0 ETH | 0.00011351 | ||||
| Redeem Mature | 23367688 | 144 days ago | IN | 0 ETH | 0.00011184 | ||||
| Redeem Mature | 23347815 | 147 days ago | IN | 0 ETH | 0.00006753 | ||||
| Redeem Mature | 23344913 | 147 days ago | IN | 0 ETH | 0.00009402 | ||||
| Redeem Mature | 23338657 | 148 days ago | IN | 0 ETH | 0.00010254 | ||||
| Redeem Mature | 23337041 | 148 days ago | IN | 0 ETH | 0.00009387 | ||||
| Redeem Mature | 23336368 | 148 days ago | IN | 0 ETH | 0.0000203 | ||||
| Redeem Mature | 23333317 | 149 days ago | IN | 0 ETH | 0.00018638 | ||||
| Redeem Mature | 23332822 | 149 days ago | IN | 0 ETH | 0.0000433 | ||||
| Redeem Mature | 23332598 | 149 days ago | IN | 0 ETH | 0.00005409 | ||||
| Redeem Mature | 23330593 | 149 days ago | IN | 0 ETH | 0.00001127 | ||||
| Redeem Mature | 23330421 | 149 days ago | IN | 0 ETH | 0.0000926 | ||||
| Redeem Mature | 23330098 | 149 days ago | IN | 0 ETH | 0.00000951 |
Latest 1 internal transaction
Advanced mode:
| Parent Transaction Hash | Method | Block |
From
|
|
To
|
||
|---|---|---|---|---|---|---|---|
| 0x3d602d80 | 23129607 | 177 days ago | Contract Creation | 0 ETH |
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
Minimal Proxy Contract for 0x8c624d6a336ede5da3bda01574cf091a938ea906
Contract Name:
BondController
Compiler Version
v0.8.3+commit.8d00100c
Optimization Enabled:
Yes with 999999 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
pragma solidity 0.8.3;
import "@openzeppelin/contracts/utils/math/Math.sol";
import "@openzeppelin/contracts/utils/Context.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@uniswap/lib/contracts/libraries/TransferHelper.sol";
import "./interfaces/IBondController.sol";
import "./interfaces/ITrancheFactory.sol";
import "./interfaces/ITranche.sol";
import "./interfaces/IRebasingERC20.sol";
/**
* @dev Controller for a ButtonTranche bond
*
* Invariants:
* - `totalDebt` should always equal the sum of all tranche tokens' `totalSupply()`
*/
contract BondController is IBondController, OwnableUpgradeable {
uint256 private constant TRANCHE_RATIO_GRANULARITY = 1000;
// One tranche for A-Z
uint256 private constant MAX_TRANCHE_COUNT = 26;
// Denominator for basis points. Used to calculate fees
uint256 private constant BPS = 10_000;
// Maximum fee in terms of basis points
uint256 private constant MAX_FEE_BPS = 50;
// to avoid precision loss and other weird math from a small total debt
// we require the debt to be at least MINIMUM_VALID_DEBT if any
uint256 private constant MINIMUM_VALID_DEBT = 10e9;
address public override collateralToken;
TrancheData[] public override tranches;
uint256 public override trancheCount;
mapping(address => bool) public trancheTokenAddresses;
uint256 public override creationDate;
uint256 public override maturityDate;
bool public override isMature;
uint256 public override totalDebt;
uint256 public lastScaledCollateralBalance;
// Maximum amount of collateral that can be deposited into this bond
// Used as a guardrail for initial launch.
// If set to 0, no deposit limit will be enforced
uint256 public depositLimit;
// Fee taken on deposit in basis points. Can be set by the contract owner
uint256 public override feeBps;
/**
* @dev Constructor for Tranche ERC20 token
* @param _trancheFactory The address of the tranche factory
* @param _collateralToken The address of the ERC20 collateral token
* @param _admin The address of the initial admin for this contract
* @param trancheRatios The tranche ratios for this bond
* @param _maturityDate The date timestamp in seconds at which this bond matures
* @param _depositLimit The maximum amount of collateral that can be deposited. 0 if no limit
*/
function init(
address _trancheFactory,
address _collateralToken,
address _admin,
uint256[] memory trancheRatios,
uint256 _maturityDate,
uint256 _depositLimit
) external initializer {
require(_trancheFactory != address(0), "BondController: invalid trancheFactory address");
require(_collateralToken != address(0), "BondController: invalid collateralToken address");
require(_admin != address(0), "BondController: invalid admin address");
require(trancheRatios.length <= MAX_TRANCHE_COUNT, "BondController: invalid tranche count");
__Ownable_init();
transferOwnership(_admin);
trancheCount = trancheRatios.length;
collateralToken = _collateralToken;
string memory collateralSymbol = IERC20Metadata(collateralToken).symbol();
uint256 totalRatio;
for (uint256 i = 0; i < trancheRatios.length; i++) {
uint256 ratio = trancheRatios[i];
require(ratio <= TRANCHE_RATIO_GRANULARITY, "BondController: Invalid tranche ratio");
totalRatio += ratio;
address trancheTokenAddress = ITrancheFactory(_trancheFactory).createTranche(
getTrancheName(collateralSymbol, i, trancheRatios.length),
getTrancheSymbol(collateralSymbol, i, trancheRatios.length),
_collateralToken
);
tranches.push(TrancheData(ITranche(trancheTokenAddress), ratio));
trancheTokenAddresses[trancheTokenAddress] = true;
}
require(totalRatio == TRANCHE_RATIO_GRANULARITY, "BondController: Invalid tranche ratios");
require(_maturityDate > block.timestamp, "BondController: Invalid maturity date");
creationDate = block.timestamp;
maturityDate = _maturityDate;
depositLimit = _depositLimit;
}
/**
* @dev Skims extraneous collateral that was incorrectly sent to the contract
*/
modifier onSkim() {
uint256 scaledCollateralBalance = IRebasingERC20(collateralToken).scaledBalanceOf(address(this));
// If there is extraneous collateral, transfer to the owner
if (scaledCollateralBalance > lastScaledCollateralBalance) {
uint256 _collateralBalance = IERC20(collateralToken).balanceOf(address(this));
uint256 virtualCollateralBalance = Math.mulDiv(
lastScaledCollateralBalance,
_collateralBalance,
scaledCollateralBalance
);
TransferHelper.safeTransfer(collateralToken, owner(), _collateralBalance - virtualCollateralBalance);
}
_;
// Update the lastScaledCollateralBalance after the function call
lastScaledCollateralBalance = IRebasingERC20(collateralToken).scaledBalanceOf(address(this));
}
/**
* @inheritdoc IBondController
*/
function deposit(uint256 amount) external override onSkim {
require(amount > 0, "BondController: invalid amount");
require(!isMature, "BondController: Already mature");
uint256 _collateralBalance = IERC20(collateralToken).balanceOf(address(this));
require(depositLimit == 0 || _collateralBalance + amount <= depositLimit, "BondController: Deposit limit");
TrancheData[] memory _tranches = tranches;
uint256 newDebt;
uint256[] memory trancheValues = new uint256[](trancheCount);
for (uint256 i = 0; i < _tranches.length; i++) {
// NOTE: solidity 0.8 checks for over/underflow natively so no need for SafeMath
uint256 trancheValue = (amount * _tranches[i].ratio) / TRANCHE_RATIO_GRANULARITY;
// if there is any collateral, we should scale by the debt:collateral ratio
// note: if totalDebt == 0 then we're minting for the first time
// so shouldn't scale even if there is some collateral mistakenly sent in
if (_collateralBalance > 0 && totalDebt > 0) {
trancheValue = Math.mulDiv(trancheValue, totalDebt, _collateralBalance);
}
newDebt += trancheValue;
trancheValues[i] = trancheValue;
}
totalDebt += newDebt;
TransferHelper.safeTransferFrom(collateralToken, _msgSender(), address(this), amount);
// saving feeBps in memory to minimize sloads
uint256 _feeBps = feeBps;
for (uint256 i = 0; i < trancheValues.length; i++) {
uint256 trancheValue = trancheValues[i];
// fee tranche tokens are minted and held by the contract
// upon maturity, they are redeemed and underlying collateral are sent to the owner
uint256 fee = (trancheValue * _feeBps) / BPS;
if (fee > 0) {
_tranches[i].token.mint(address(this), fee);
}
_tranches[i].token.mint(_msgSender(), trancheValue - fee);
}
emit Deposit(_msgSender(), amount, _feeBps);
_enforceTotalDebt();
}
/**
* @inheritdoc IBondController
*/
function mature() external override onSkim {
require(!isMature, "BondController: Already mature");
require(owner() == _msgSender() || maturityDate < block.timestamp, "BondController: Invalid call to mature");
isMature = true;
TrancheData[] memory _tranches = tranches;
uint256 _collateralBalance = IERC20(collateralToken).balanceOf(address(this));
// Go through all tranches A-Y (not Z) delivering collateral if possible
for (uint256 i = 0; i < _tranches.length - 1 && _collateralBalance > 0; i++) {
ITranche _tranche = _tranches[i].token;
// pay out the entire tranche token's owed collateral (equal to the supply of tranche tokens)
// if there is not enough collateral to pay it out, pay as much as we have
uint256 amount = Math.min(_tranche.totalSupply(), _collateralBalance);
_collateralBalance -= amount;
TransferHelper.safeTransfer(collateralToken, address(_tranche), amount);
// redeem fees, sending output tokens to owner
_tranche.redeem(address(this), owner(), IERC20(_tranche).balanceOf(address(this)));
}
// Transfer any remaining collaeral to the Z tranche
if (_collateralBalance > 0) {
ITranche _tranche = _tranches[_tranches.length - 1].token;
TransferHelper.safeTransfer(collateralToken, address(_tranche), _collateralBalance);
_tranche.redeem(address(this), owner(), IERC20(_tranche).balanceOf(address(this)));
}
emit Mature(_msgSender());
}
/**
* @inheritdoc IBondController
*/
function redeemMature(address tranche, uint256 amount) external override {
require(isMature, "BondController: Bond is not mature");
require(trancheTokenAddresses[tranche], "BondController: Invalid tranche address");
ITranche(tranche).redeem(_msgSender(), _msgSender(), amount);
totalDebt -= amount;
emit RedeemMature(_msgSender(), tranche, amount);
}
/**
* @inheritdoc IBondController
*/
function redeem(uint256[] memory amounts) external override onSkim {
require(!isMature, "BondController: Bond is already mature");
TrancheData[] memory _tranches = tranches;
require(amounts.length == _tranches.length, "BondController: Invalid redeem amounts");
uint256 total;
for (uint256 i = 0; i < amounts.length; i++) {
total += amounts[i];
}
for (uint256 i = 0; i < amounts.length; i++) {
require(
(amounts[i] * TRANCHE_RATIO_GRANULARITY) / total == _tranches[i].ratio,
"BondController: Invalid redemption ratio"
);
_tranches[i].token.burn(_msgSender(), amounts[i]);
}
uint256 _collateralBalance = IERC20(collateralToken).balanceOf(address(this));
// return as a proportion of the total debt redeemed
uint256 returnAmount = Math.mulDiv(total, _collateralBalance, totalDebt);
totalDebt -= total;
TransferHelper.safeTransfer(collateralToken, _msgSender(), returnAmount);
emit Redeem(_msgSender(), amounts);
_enforceTotalDebt();
}
/**
* @inheritdoc IBondController
*/
function setFee(uint256 newFeeBps) external override onlyOwner {
require(!isMature, "BondController: Invalid call to setFee");
require(newFeeBps <= MAX_FEE_BPS, "BondController: New fee too high");
feeBps = newFeeBps;
emit FeeUpdate(newFeeBps);
}
/**
* @dev Get the string name for a tranche
* @param collateralSymbol the symbol of the collateral token
* @param index the tranche index
* @param _trancheCount the total number of tranches
* @return the string name of the tranche
*/
function getTrancheName(
string memory collateralSymbol,
uint256 index,
uint256 _trancheCount
) internal pure returns (string memory) {
return
string(abi.encodePacked("ButtonTranche ", collateralSymbol, " ", getTrancheLetter(index, _trancheCount)));
}
/**
* @dev Get the string symbol for a tranche
* @param collateralSymbol the symbol of the collateral token
* @param index the tranche index
* @param _trancheCount the total number of tranches
* @return the string symbol of the tranche
*/
function getTrancheSymbol(
string memory collateralSymbol,
uint256 index,
uint256 _trancheCount
) internal pure returns (string memory) {
return string(abi.encodePacked("TRANCHE-", collateralSymbol, "-", getTrancheLetter(index, _trancheCount)));
}
/**
* @dev Get the string letter for a tranche index
* @param index the tranche index
* @param _trancheCount the total number of tranches
* @return the string letter of the tranche index
*/
function getTrancheLetter(uint256 index, uint256 _trancheCount) internal pure returns (string memory) {
bytes memory trancheLetters = bytes("ABCDEFGHIJKLMNOPQRSTUVWXY");
bytes memory target = new bytes(1);
if (index == _trancheCount - 1) {
target[0] = "Z";
} else {
target[0] = trancheLetters[index];
}
return string(target);
}
// @dev Ensuring total debt isn't too small
function _enforceTotalDebt() internal {
require(totalDebt >= MINIMUM_VALID_DEBT, "BondController: Expected minimum valid debt");
}
/**
* @dev Get the virtual collateral balance of the bond
* @return the virtual collateral balance
*/
function collateralBalance() external view returns (uint256) {
uint256 scaledCollateralBalance = IRebasingERC20(collateralToken).scaledBalanceOf(address(this));
uint256 _collateralBalance = IERC20(collateralToken).balanceOf(address(this));
return
(scaledCollateralBalance > lastScaledCollateralBalance)
? Math.mulDiv(lastScaledCollateralBalance, _collateralBalance, scaledCollateralBalance)
: _collateralBalance;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/Math.sol)
pragma solidity ^0.8.0;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
enum Rounding {
Down, // Toward negative infinity
Up, // Toward infinity
Zero // Toward zero
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return 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 up instead
* of rounding down.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
* @dev 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^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod0 := mul(x, y)
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
require(denominator > prod1);
///////////////////////////////////////////////
// 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.
// Does not overflow because the denominator cannot be zero at this stage in the function.
uint256 twos = denominator & (~denominator + 1);
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^256 / 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^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
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^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// 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^256. Since the preconditions guarantee that the outcome is
// less than 2^256, 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;
}
}
/**
* @notice 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) {
uint256 result = mulDiv(x, y, denominator);
if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
//
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
//
// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
//
// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1 << (log2(a) >> 1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
/**
* @notice 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 + (rounding == Rounding.Up && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 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 + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10, rounded down, of a positive value.
* 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 + (rounding == Rounding.Up && 10**result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256, rounded down, of a positive value.
* 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;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
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 log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (rounding == Rounding.Up && 1 << (result * 8) < value ? 1 : 0);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
pragma solidity ^0.8.0;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
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 amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` 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 amount) 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 `amount` 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 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` 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 amount
) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity ^0.8.0;
import "../IERC20.sol";
/**
* @dev Interface for the optional metadata functions from the ERC20 standard.
*
* _Available since v4.1._
*/
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
pragma solidity ^0.8.0;
import "../utils/ContextUpgradeable.sol";
import "../proxy/utils/Initializable.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract OwnableUpgradeable is Initializable, ContextUpgradeable {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
function __Ownable_init() internal initializer {
__Context_init_unchained();
__Ownable_init_unchained();
}
function __Ownable_init_unchained() internal initializer {
_setOwner(_msgSender());
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
_;
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions anymore. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby removing any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_setOwner(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_setOwner(newOwner);
}
function _setOwner(address newOwner) private {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
uint256[49] private __gap;
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity >=0.6.0;
// helper methods for interacting with ERC20 tokens and sending ETH that do not consistently return true/false
library TransferHelper {
function safeApprove(
address token,
address to,
uint256 value
) internal {
// bytes4(keccak256(bytes('approve(address,uint256)')));
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x095ea7b3, to, value));
require(
success && (data.length == 0 || abi.decode(data, (bool))),
'TransferHelper::safeApprove: approve failed'
);
}
function safeTransfer(
address token,
address to,
uint256 value
) internal {
// bytes4(keccak256(bytes('transfer(address,uint256)')));
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(0xa9059cbb, to, value));
require(
success && (data.length == 0 || abi.decode(data, (bool))),
'TransferHelper::safeTransfer: transfer failed'
);
}
function safeTransferFrom(
address token,
address from,
address to,
uint256 value
) internal {
// bytes4(keccak256(bytes('transferFrom(address,address,uint256)')));
(bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x23b872dd, from, to, value));
require(
success && (data.length == 0 || abi.decode(data, (bool))),
'TransferHelper::transferFrom: transferFrom failed'
);
}
function safeTransferETH(address to, uint256 value) internal {
(bool success, ) = to.call{value: value}(new bytes(0));
require(success, 'TransferHelper::safeTransferETH: ETH transfer failed');
}
}pragma solidity ^0.8.3;
import "@openzeppelin/contracts/utils/Context.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@uniswap/lib/contracts/libraries/TransferHelper.sol";
import "./ITranche.sol";
struct TrancheData {
ITranche token;
uint256 ratio;
}
/**
* @dev Controller for a ButtonTranche bond system
*/
interface IBondController {
event Deposit(address from, uint256 amount, uint256 feeBps);
event Mature(address caller);
event RedeemMature(address user, address tranche, uint256 amount);
event Redeem(address user, uint256[] amounts);
event FeeUpdate(uint256 newFee);
function collateralToken() external view returns (address);
function tranches(uint256 i) external view returns (ITranche token, uint256 ratio);
function trancheCount() external view returns (uint256 count);
function feeBps() external view returns (uint256 fee);
function maturityDate() external view returns (uint256 maturityDate);
function isMature() external view returns (bool isMature);
function creationDate() external view returns (uint256 creationDate);
function totalDebt() external view returns (uint256 totalDebt);
/**
* @dev Deposit `amount` tokens from `msg.sender`, get tranche tokens in return
* Requirements:
* - `msg.sender` must have `approved` `amount` collateral tokens to this contract
*/
function deposit(uint256 amount) external;
/**
* @dev Matures the bond. Disables deposits,
* fixes the redemption ratio, and distributes collateral to redemption pools
* Redeems any fees collected from deposits, sending redeemed funds to the contract owner
* Requirements:
* - The bond is not already mature
* - One of:
* - `msg.sender` is owner
* - `maturityDate` has passed
*/
function mature() external;
/**
* @dev Redeems some tranche tokens
* Requirements:
* - The bond is mature
* - `msg.sender` owns at least `amount` tranche tokens from address `tranche`
* - `tranche` must be a valid tranche token on this bond
*/
function redeemMature(address tranche, uint256 amount) external;
/**
* @dev Redeems a slice of tranche tokens from all tranches.
* Returns collateral to the user proportionally to the amount of debt they are removing
* Requirements
* - The bond is not mature
* - The number of `amounts` is the same as the number of tranches
* - The `amounts` are in equivalent ratio to the tranche order
*/
function redeem(uint256[] memory amounts) external;
/**
* @dev Updates the fee taken on deposit to the given new fee
*
* Requirements
* - `msg.sender` has admin role
* - `newFeeBps` is in range [0, 50]
*/
function setFee(uint256 newFeeBps) external;
}pragma solidity ^0.8.3;
/**
* @dev Factory for Tranche minimal proxy contracts
*/
interface ITrancheFactory {
event TrancheCreated(address newTrancheAddress);
/**
* @dev Deploys a minimal proxy instance for a new tranche ERC20 token with the given parameters.
*/
function createTranche(
string memory name,
string memory symbol,
address _collateralToken
) external returns (address);
}pragma solidity ^0.8.3;
import "@openzeppelin/contracts/utils/Context.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@uniswap/lib/contracts/libraries/TransferHelper.sol";
/**
* @dev ERC20 token to represent a single tranche for a ButtonTranche bond
*
*/
interface ITranche is IERC20 {
/**
* @dev returns the BondController address which owns this Tranche contract
* It should have admin permissions to call mint, burn, and redeem functions
*/
function bond() external view returns (address);
/**
* @dev Mint `amount` tokens to `to`
* Only callable by the owner (bond controller). Used to
* manage bonds, specifically creating tokens upon deposit
* @param to the address to mint tokens to
* @param amount The amount of tokens to mint
*/
function mint(address to, uint256 amount) external;
/**
* @dev Burn `amount` tokens from `from`'s balance
* Only callable by the owner (bond controller). Used to
* manage bonds, specifically burning tokens upon redemption
* @param from The address to burn tokens from
* @param amount The amount of tokens to burn
*/
function burn(address from, uint256 amount) external;
/**
* @dev Burn `amount` tokens from `from` and return the proportional
* value of the collateral token to `to`
* @param from The address to burn tokens from
* @param to The address to send collateral back to
* @param amount The amount of tokens to burn
*/
function redeem(
address from,
address to,
uint256 amount
) external;
}// SPDX-License-Identifier: GPL-3.0-or-later
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
// Interface definition for Rebasing ERC20 tokens which have a "elastic" external
// balance and "fixed" internal balance. Each user's external balance is
// represented as a product of a "scalar" and the user's internal balance.
//
// From time to time the "Rebase" event updates scaler,
// which increases/decreases all user balances proportionally.
//
// The standard ERC-20 methods are denominated in the elastic balance
//
interface IRebasingERC20 is IERC20, IERC20Metadata {
/// @notice Returns the fixed balance of the specified address.
/// @param who The address to query.
function scaledBalanceOf(address who) external view returns (uint256);
/// @notice Returns the total fixed supply.
function scaledTotalSupply() external view returns (uint256);
/// @notice Transfer all of the sender's balance to a specified address.
/// @param to The address to transfer to.
/// @return True on success, false otherwise.
function transferAll(address to) external returns (bool);
/// @notice Transfer all balance tokens from one address to another.
/// @param from The address to send tokens from.
/// @param to The address to transfer to.
function transferAllFrom(address from, address to) external returns (bool);
/// @notice Triggers the next rebase, if applicable.
function rebase() external;
/// @notice Event emitted when the balance scalar is updated.
/// @param epoch The number of rebases since inception.
/// @param newScalar The new scalar.
event Rebase(uint256 indexed epoch, uint256 newScalar);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "../proxy/utils/Initializable.sol";
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract ContextUpgradeable is Initializable {
function __Context_init() internal initializer {
__Context_init_unchained();
}
function __Context_init_unchained() internal initializer {
}
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
uint256[50] private __gap;
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
/**
* @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 a proxied contract can't have 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.
*
* 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.
*/
abstract contract Initializable {
/**
* @dev Indicates that the contract has been initialized.
*/
bool private _initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool private _initializing;
/**
* @dev Modifier to protect an initializer function from being invoked twice.
*/
modifier initializer() {
require(_initializing || !_initialized, "Initializable: contract is already initialized");
bool isTopLevelCall = !_initializing;
if (isTopLevelCall) {
_initializing = true;
_initialized = true;
}
_;
if (isTopLevelCall) {
_initializing = false;
}
}
}{
"metadata": {
"bytecodeHash": "none"
},
"optimizer": {
"enabled": true,
"runs": 999999
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"libraries": {}
}Contract ABI
API[{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"feeBps","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newFee","type":"uint256"}],"name":"FeeUpdate","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"caller","type":"address"}],"name":"Mature","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"name":"Redeem","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"address","name":"tranche","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"RedeemMature","type":"event"},{"inputs":[],"name":"collateralBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"collateralToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"creationDate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"deposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"depositLimit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feeBps","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_trancheFactory","type":"address"},{"internalType":"address","name":"_collateralToken","type":"address"},{"internalType":"address","name":"_admin","type":"address"},{"internalType":"uint256[]","name":"trancheRatios","type":"uint256[]"},{"internalType":"uint256","name":"_maturityDate","type":"uint256"},{"internalType":"uint256","name":"_depositLimit","type":"uint256"}],"name":"init","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"isMature","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lastScaledCollateralBalance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"mature","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"maturityDate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"name":"redeem","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"tranche","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"redeemMature","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"newFeeBps","type":"uint256"}],"name":"setFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"totalDebt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"trancheCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"trancheTokenAddresses","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"tranches","outputs":[{"internalType":"contract ITranche","name":"token","type":"address"},{"internalType":"uint256","name":"ratio","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"}]Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 34 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ 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.