Contract Overview
Balance:
0 AVAX
AVAX Value:
$0.00
My Name Tag:
Not Available, login to update
[ Download CSV Export ]
Latest 1 internal transaction
| Parent Txn Hash | Block | From | To | Value | |||
|---|---|---|---|---|---|---|---|
| 0xfec980e2671ccc947bf063a5cb3ac41299eacf3729eec9a2d1f7d74b534ee9d7 | 19560264 | 194 days 7 hrs ago | 0x0b1f9c2211f77ec3fa2719671c5646cf6e59b775 | Contract Creation | 0 AVAX |
[ Download CSV Export ]
Minimal Proxy Contract for 0xe767c6c3bf42f550a5a258a379713322b6c4c060
Contract Name:
CauldronV2
Compiler Version
v0.6.12+commit.27d51765
Optimization Enabled:
Yes with 200 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: None
pragma solidity 0.6.12;
pragma experimental ABIEncoderV2;
/// @notice A library for performing overflow-/underflow-safe math,
library BoringMath {
function add(uint256 a, uint256 b) internal pure returns (uint256 c) {
require((c = a + b) >= b, "BoringMath: Add Overflow");
}
function sub(uint256 a, uint256 b) internal pure returns (uint256 c) {
require((c = a - b) <= a, "BoringMath: Underflow");
}
function mul(uint256 a, uint256 b) internal pure returns (uint256 c) {
require(b == 0 || (c = a * b) / b == a, "BoringMath: Mul Overflow");
}
function to128(uint256 a) internal pure returns (uint128 c) {
require(a <= uint128(-1), "BoringMath: uint128 Overflow");
c = uint128(a);
}
function to64(uint256 a) internal pure returns (uint64 c) {
require(a <= uint64(-1), "BoringMath: uint64 Overflow");
c = uint64(a);
}
function to32(uint256 a) internal pure returns (uint32 c) {
require(a <= uint32(-1), "BoringMath: uint32 Overflow");
c = uint32(a);
}
}
/// @notice A library for performing overflow-/underflow-safe addition and subtraction on uint128.
library BoringMath128 {
function add(uint128 a, uint128 b) internal pure returns (uint128 c) {
require((c = a + b) >= b, "BoringMath: Add Overflow");
}
function sub(uint128 a, uint128 b) internal pure returns (uint128 c) {
require((c = a - b) <= a, "BoringMath: Underflow");
}
}
/// @notice A library for performing overflow-/underflow-safe addition and subtraction on uint64.
library BoringMath64 {
function add(uint64 a, uint64 b) internal pure returns (uint64 c) {
require((c = a + b) >= b, "BoringMath: Add Overflow");
}
function sub(uint64 a, uint64 b) internal pure returns (uint64 c) {
require((c = a - b) <= a, "BoringMath: Underflow");
}
}
/// @notice A library for performing overflow-/underflow-safe addition and subtraction on uint32.
library BoringMath32 {
function add(uint32 a, uint32 b) internal pure returns (uint32 c) {
require((c = a + b) >= b, "BoringMath: Add Overflow");
}
function sub(uint32 a, uint32 b) internal pure returns (uint32 c) {
require((c = a - b) <= a, "BoringMath: Underflow");
}
}
contract BoringOwnableData {
address public owner;
address public pendingOwner;
}
contract BoringOwnable is BoringOwnableData {
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/// @notice `owner` defaults to msg.sender on construction.
constructor() public {
owner = msg.sender;
emit OwnershipTransferred(address(0), msg.sender);
}
/// @notice Transfers ownership to `newOwner`. Either directly or claimable by the new pending owner.
/// Can only be invoked by the current `owner`.
/// @param newOwner Address of the new owner.
/// @param direct True if `newOwner` should be set immediately. False if `newOwner` needs to use `claimOwnership`.
/// @param renounce Allows the `newOwner` to be `address(0)` if `direct` and `renounce` is True. Has no effect otherwise.
function transferOwnership(
address newOwner,
bool direct,
bool renounce
) public onlyOwner {
if (direct) {
// Checks
require(newOwner != address(0) || renounce, "Ownable: zero address");
// Effects
emit OwnershipTransferred(owner, newOwner);
owner = newOwner;
pendingOwner = address(0);
} else {
// Effects
pendingOwner = newOwner;
}
}
/// @notice Needs to be called by `pendingOwner` to claim ownership.
function claimOwnership() public {
address _pendingOwner = pendingOwner;
// Checks
require(msg.sender == _pendingOwner, "Ownable: caller != pending owner");
// Effects
emit OwnershipTransferred(owner, _pendingOwner);
owner = _pendingOwner;
pendingOwner = address(0);
}
/// @notice Only allows the `owner` to execute the function.
modifier onlyOwner() {
require(msg.sender == owner, "Ownable: caller is not the owner");
_;
}
}
interface IERC20 {
function totalSupply() external view returns (uint256);
function balanceOf(address account) external view returns (uint256);
function allowance(address owner, address spender) external view returns (uint256);
function approve(address spender, uint256 amount) external returns (bool);
event Transfer(address indexed from, address indexed to, uint256 value);
event Approval(address indexed owner, address indexed spender, uint256 value);
/// @notice EIP 2612
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
}
contract Domain {
bytes32 private constant DOMAIN_SEPARATOR_SIGNATURE_HASH = keccak256("EIP712Domain(uint256 chainId,address verifyingContract)");
// See https://eips.ethereum.org/EIPS/eip-191
string private constant EIP191_PREFIX_FOR_EIP712_STRUCTURED_DATA = "\x19\x01";
// solhint-disable var-name-mixedcase
bytes32 private immutable _DOMAIN_SEPARATOR;
uint256 private immutable DOMAIN_SEPARATOR_CHAIN_ID;
/// @dev Calculate the DOMAIN_SEPARATOR
function _calculateDomainSeparator(uint256 chainId) private view returns (bytes32) {
return keccak256(
abi.encode(
DOMAIN_SEPARATOR_SIGNATURE_HASH,
chainId,
address(this)
)
);
}
constructor() public {
uint256 chainId; assembly {chainId := chainid()}
_DOMAIN_SEPARATOR = _calculateDomainSeparator(DOMAIN_SEPARATOR_CHAIN_ID = chainId);
}
/// @dev Return the DOMAIN_SEPARATOR
// It's named internal to allow making it public from the contract that uses it by creating a simple view function
// with the desired public name, such as DOMAIN_SEPARATOR or domainSeparator.
// solhint-disable-next-line func-name-mixedcase
function _domainSeparator() internal view returns (bytes32) {
uint256 chainId; assembly {chainId := chainid()}
return chainId == DOMAIN_SEPARATOR_CHAIN_ID ? _DOMAIN_SEPARATOR : _calculateDomainSeparator(chainId);
}
function _getDigest(bytes32 dataHash) internal view returns (bytes32 digest) {
digest =
keccak256(
abi.encodePacked(
EIP191_PREFIX_FOR_EIP712_STRUCTURED_DATA,
_domainSeparator(),
dataHash
)
);
}
}
// Data part taken out for building of contracts that receive delegate calls
contract ERC20Data {
/// @notice owner > balance mapping.
mapping(address => uint256) public balanceOf;
/// @notice owner > spender > allowance mapping.
mapping(address => mapping(address => uint256)) public allowance;
/// @notice owner > nonce mapping. Used in `permit`.
mapping(address => uint256) public nonces;
}
abstract contract ERC20 is IERC20, Domain {
/// @notice owner > balance mapping.
mapping(address => uint256) public override balanceOf;
/// @notice owner > spender > allowance mapping.
mapping(address => mapping(address => uint256)) public override allowance;
/// @notice owner > nonce mapping. Used in `permit`.
mapping(address => uint256) public nonces;
event Transfer(address indexed _from, address indexed _to, uint256 _value);
event Approval(address indexed _owner, address indexed _spender, uint256 _value);
/// @notice Transfers `amount` tokens from `msg.sender` to `to`.
/// @param to The address to move the tokens.
/// @param amount of the tokens to move.
/// @return (bool) Returns True if succeeded.
function transfer(address to, uint256 amount) public returns (bool) {
// If `amount` is 0, or `msg.sender` is `to` nothing happens
if (amount != 0 || msg.sender == to) {
uint256 srcBalance = balanceOf[msg.sender];
require(srcBalance >= amount, "ERC20: balance too low");
if (msg.sender != to) {
require(to != address(0), "ERC20: no zero address"); // Moved down so low balance calls safe some gas
balanceOf[msg.sender] = srcBalance - amount; // Underflow is checked
balanceOf[to] += amount;
}
}
emit Transfer(msg.sender, to, amount);
return true;
}
/// @notice Transfers `amount` tokens from `from` to `to`. Caller needs approval for `from`.
/// @param from Address to draw tokens from.
/// @param to The address to move the tokens.
/// @param amount The token amount to move.
/// @return (bool) Returns True if succeeded.
function transferFrom(
address from,
address to,
uint256 amount
) public returns (bool) {
// If `amount` is 0, or `from` is `to` nothing happens
if (amount != 0) {
uint256 srcBalance = balanceOf[from];
require(srcBalance >= amount, "ERC20: balance too low");
if (from != to) {
uint256 spenderAllowance = allowance[from][msg.sender];
// If allowance is infinite, don't decrease it to save on gas (breaks with EIP-20).
if (spenderAllowance != type(uint256).max) {
require(spenderAllowance >= amount, "ERC20: allowance too low");
allowance[from][msg.sender] = spenderAllowance - amount; // Underflow is checked
}
require(to != address(0), "ERC20: no zero address"); // Moved down so other failed calls safe some gas
balanceOf[from] = srcBalance - amount; // Underflow is checked
balanceOf[to] += amount;
}
}
emit Transfer(from, to, amount);
return true;
}
/// @notice Approves `amount` from sender to be spend by `spender`.
/// @param spender Address of the party that can draw from msg.sender's account.
/// @param amount The maximum collective amount that `spender` can draw.
/// @return (bool) Returns True if approved.
function approve(address spender, uint256 amount) public override returns (bool) {
allowance[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
return true;
}
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32) {
return _domainSeparator();
}
// keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
bytes32 private constant PERMIT_SIGNATURE_HASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;
/// @notice Approves `value` from `owner_` to be spend by `spender`.
/// @param owner_ Address of the owner.
/// @param spender The address of the spender that gets approved to draw from `owner_`.
/// @param value The maximum collective amount that `spender` can draw.
/// @param deadline This permit must be redeemed before this deadline (UTC timestamp in seconds).
function permit(
address owner_,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external override {
require(owner_ != address(0), "ERC20: Owner cannot be 0");
require(block.timestamp < deadline, "ERC20: Expired");
require(
ecrecover(_getDigest(keccak256(abi.encode(PERMIT_SIGNATURE_HASH, owner_, spender, value, nonces[owner_]++, deadline))), v, r, s) ==
owner_,
"ERC20: Invalid Signature"
);
allowance[owner_][spender] = value;
emit Approval(owner_, spender, value);
}
}
contract ERC20WithSupply is IERC20, ERC20 {
uint256 public override totalSupply;
function _mint(address user, uint256 amount) private {
uint256 newTotalSupply = totalSupply + amount;
require(newTotalSupply >= totalSupply, "Mint overflow");
totalSupply = newTotalSupply;
balanceOf[user] += amount;
}
function _burn(address user, uint256 amount) private {
require(balanceOf[user] >= amount, "Burn too much");
totalSupply -= amount;
balanceOf[user] -= amount;
}
}
interface IMasterContract {
/// @notice Init function that gets called from `BoringFactory.deploy`.
/// Also kown as the constructor for cloned contracts.
/// Any ETH send to `BoringFactory.deploy` ends up here.
/// @param data Can be abi encoded arguments or anything else.
function init(bytes calldata data) external payable;
}
struct Rebase {
uint128 elastic;
uint128 base;
}
/// @notice A rebasing library using overflow-/underflow-safe math.
library RebaseLibrary {
using BoringMath for uint256;
using BoringMath128 for uint128;
/// @notice Calculates the base value in relationship to `elastic` and `total`.
function toBase(
Rebase memory total,
uint256 elastic,
bool roundUp
) internal pure returns (uint256 base) {
if (total.elastic == 0) {
base = elastic;
} else {
base = elastic.mul(total.base) / total.elastic;
if (roundUp && base.mul(total.elastic) / total.base < elastic) {
base = base.add(1);
}
}
}
/// @notice Calculates the elastic value in relationship to `base` and `total`.
function toElastic(
Rebase memory total,
uint256 base,
bool roundUp
) internal pure returns (uint256 elastic) {
if (total.base == 0) {
elastic = base;
} else {
elastic = base.mul(total.elastic) / total.base;
if (roundUp && elastic.mul(total.base) / total.elastic < base) {
elastic = elastic.add(1);
}
}
}
/// @notice Add `elastic` to `total` and doubles `total.base`.
/// @return (Rebase) The new total.
/// @return base in relationship to `elastic`.
function add(
Rebase memory total,
uint256 elastic,
bool roundUp
) internal pure returns (Rebase memory, uint256 base) {
base = toBase(total, elastic, roundUp);
total.elastic = total.elastic.add(elastic.to128());
total.base = total.base.add(base.to128());
return (total, base);
}
/// @notice Sub `base` from `total` and update `total.elastic`.
/// @return (Rebase) The new total.
/// @return elastic in relationship to `base`.
function sub(
Rebase memory total,
uint256 base,
bool roundUp
) internal pure returns (Rebase memory, uint256 elastic) {
elastic = toElastic(total, base, roundUp);
total.elastic = total.elastic.sub(elastic.to128());
total.base = total.base.sub(base.to128());
return (total, elastic);
}
/// @notice Add `elastic` and `base` to `total`.
function add(
Rebase memory total,
uint256 elastic,
uint256 base
) internal pure returns (Rebase memory) {
total.elastic = total.elastic.add(elastic.to128());
total.base = total.base.add(base.to128());
return total;
}
/// @notice Subtract `elastic` and `base` to `total`.
function sub(
Rebase memory total,
uint256 elastic,
uint256 base
) internal pure returns (Rebase memory) {
total.elastic = total.elastic.sub(elastic.to128());
total.base = total.base.sub(base.to128());
return total;
}
/// @notice Add `elastic` to `total` and update storage.
/// @return newElastic Returns updated `elastic`.
function addElastic(Rebase storage total, uint256 elastic) internal returns (uint256 newElastic) {
newElastic = total.elastic = total.elastic.add(elastic.to128());
}
/// @notice Subtract `elastic` from `total` and update storage.
/// @return newElastic Returns updated `elastic`.
function subElastic(Rebase storage total, uint256 elastic) internal returns (uint256 newElastic) {
newElastic = total.elastic = total.elastic.sub(elastic.to128());
}
}
library BoringERC20 {
bytes4 private constant SIG_SYMBOL = 0x95d89b41; // symbol()
bytes4 private constant SIG_NAME = 0x06fdde03; // name()
bytes4 private constant SIG_DECIMALS = 0x313ce567; // decimals()
bytes4 private constant SIG_TRANSFER = 0xa9059cbb; // transfer(address,uint256)
bytes4 private constant SIG_TRANSFER_FROM = 0x23b872dd; // transferFrom(address,address,uint256)
function returnDataToString(bytes memory data) internal pure returns (string memory) {
if (data.length >= 64) {
return abi.decode(data, (string));
} else if (data.length == 32) {
uint8 i = 0;
while(i < 32 && data[i] != 0) {
i++;
}
bytes memory bytesArray = new bytes(i);
for (i = 0; i < 32 && data[i] != 0; i++) {
bytesArray[i] = data[i];
}
return string(bytesArray);
} else {
return "???";
}
}
/// @notice Provides a safe ERC20.symbol version which returns '???' as fallback string.
/// @param token The address of the ERC-20 token contract.
/// @return (string) Token symbol.
function safeSymbol(IERC20 token) internal view returns (string memory) {
(bool success, bytes memory data) = address(token).staticcall(abi.encodeWithSelector(SIG_SYMBOL));
return success ? returnDataToString(data) : "???";
}
/// @notice Provides a safe ERC20.name version which returns '???' as fallback string.
/// @param token The address of the ERC-20 token contract.
/// @return (string) Token name.
function safeName(IERC20 token) internal view returns (string memory) {
(bool success, bytes memory data) = address(token).staticcall(abi.encodeWithSelector(SIG_NAME));
return success ? returnDataToString(data) : "???";
}
/// @notice Provides a safe ERC20.decimals version which returns '18' as fallback value.
/// @param token The address of the ERC-20 token contract.
/// @return (uint8) Token decimals.
function safeDecimals(IERC20 token) internal view returns (uint8) {
(bool success, bytes memory data) = address(token).staticcall(abi.encodeWithSelector(SIG_DECIMALS));
return success && data.length == 32 ? abi.decode(data, (uint8)) : 18;
}
/// @notice Provides a safe ERC20.transfer version for different ERC-20 implementations.
/// Reverts on a failed transfer.
/// @param token The address of the ERC-20 token.
/// @param to Transfer tokens to.
/// @param amount The token amount.
function safeTransfer(
IERC20 token,
address to,
uint256 amount
) internal {
(bool success, bytes memory data) = address(token).call(abi.encodeWithSelector(SIG_TRANSFER, to, amount));
require(success && (data.length == 0 || abi.decode(data, (bool))), "BoringERC20: Transfer failed");
}
/// @notice Provides a safe ERC20.transferFrom version for different ERC-20 implementations.
/// Reverts on a failed transfer.
/// @param token The address of the ERC-20 token.
/// @param from Transfer tokens from.
/// @param to Transfer tokens to.
/// @param amount The token amount.
function safeTransferFrom(
IERC20 token,
address from,
address to,
uint256 amount
) internal {
(bool success, bytes memory data) = address(token).call(abi.encodeWithSelector(SIG_TRANSFER_FROM, from, to, amount));
require(success && (data.length == 0 || abi.decode(data, (bool))), "BoringERC20: TransferFrom failed");
}
}
interface IBatchFlashBorrower {
function onBatchFlashLoan(
address sender,
IERC20[] calldata tokens,
uint256[] calldata amounts,
uint256[] calldata fees,
bytes calldata data
) external;
}
interface IFlashBorrower {
function onFlashLoan(
address sender,
IERC20 token,
uint256 amount,
uint256 fee,
bytes calldata data
) external;
}
interface IStrategy {
// Send the assets to the Strategy and call skim to invest them
function skim(uint256 amount) external;
// Harvest any profits made converted to the asset and pass them to the caller
function harvest(uint256 balance, address sender) external returns (int256 amountAdded);
// Withdraw assets. The returned amount can differ from the requested amount due to rounding.
// The actualAmount should be very close to the amount. The difference should NOT be used to report a loss. That's what harvest is for.
function withdraw(uint256 amount) external returns (uint256 actualAmount);
// Withdraw all assets in the safest way possible. This shouldn't fail.
function exit(uint256 balance) external returns (int256 amountAdded);
}
interface IBentoBoxV1 {
event LogDeploy(address indexed masterContract, bytes data, address indexed cloneAddress);
event LogDeposit(address indexed token, address indexed from, address indexed to, uint256 amount, uint256 share);
event LogFlashLoan(address indexed borrower, address indexed token, uint256 amount, uint256 feeAmount, address indexed receiver);
event LogRegisterProtocol(address indexed protocol);
event LogSetMasterContractApproval(address indexed masterContract, address indexed user, bool approved);
event LogStrategyDivest(address indexed token, uint256 amount);
event LogStrategyInvest(address indexed token, uint256 amount);
event LogStrategyLoss(address indexed token, uint256 amount);
event LogStrategyProfit(address indexed token, uint256 amount);
event LogStrategyQueued(address indexed token, address indexed strategy);
event LogStrategySet(address indexed token, address indexed strategy);
event LogStrategyTargetPercentage(address indexed token, uint256 targetPercentage);
event LogTransfer(address indexed token, address indexed from, address indexed to, uint256 share);
event LogWhiteListMasterContract(address indexed masterContract, bool approved);
event LogWithdraw(address indexed token, address indexed from, address indexed to, uint256 amount, uint256 share);
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
function balanceOf(IERC20, address) external view returns (uint256);
function batch(bytes[] calldata calls, bool revertOnFail) external payable returns (bool[] memory successes, bytes[] memory results);
function batchFlashLoan(IBatchFlashBorrower borrower, address[] calldata receivers, IERC20[] calldata tokens, uint256[] calldata amounts, bytes calldata data) external;
function claimOwnership() external;
function deploy(address masterContract, bytes calldata data, bool useCreate2) external payable;
function deposit(IERC20 token_, address from, address to, uint256 amount, uint256 share) external payable returns (uint256 amountOut, uint256 shareOut);
function flashLoan(IFlashBorrower borrower, address receiver, IERC20 token, uint256 amount, bytes calldata data) external;
function harvest(IERC20 token, bool balance, uint256 maxChangeAmount) external;
function masterContractApproved(address, address) external view returns (bool);
function masterContractOf(address) external view returns (address);
function nonces(address) external view returns (uint256);
function owner() external view returns (address);
function pendingOwner() external view returns (address);
function pendingStrategy(IERC20) external view returns (IStrategy);
function permitToken(IERC20 token, address from, address to, uint256 amount, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external;
function registerProtocol() external;
function setMasterContractApproval(address user, address masterContract, bool approved, uint8 v, bytes32 r, bytes32 s) external;
function setStrategy(IERC20 token, IStrategy newStrategy) external;
function setStrategyTargetPercentage(IERC20 token, uint64 targetPercentage_) external;
function strategy(IERC20) external view returns (IStrategy);
function strategyData(IERC20) external view returns (uint64 strategyStartDate, uint64 targetPercentage, uint128 balance);
function toAmount(IERC20 token, uint256 share, bool roundUp) external view returns (uint256 amount);
function toShare(IERC20 token, uint256 amount, bool roundUp) external view returns (uint256 share);
function totals(IERC20) external view returns (Rebase memory totals_);
function transfer(IERC20 token, address from, address to, uint256 share) external;
function transferMultiple(IERC20 token, address from, address[] calldata tos, uint256[] calldata shares) external;
function transferOwnership(address newOwner, bool direct, bool renounce) external;
function whitelistMasterContract(address masterContract, bool approved) external;
function whitelistedMasterContracts(address) external view returns (bool);
function withdraw(IERC20 token_, address from, address to, uint256 amount, uint256 share) external returns (uint256 amountOut, uint256 shareOut);
}
/// @title Cauldron
/// @dev This contract allows contract calls to any contract (except BentoBox)
/// from arbitrary callers thus, don't trust calls from this contract in any circumstances.
contract NXUSD is ERC20, BoringOwnable {
using BoringMath for uint256;
// ERC20 'variables'
string public constant symbol = "NXUSD";
string public constant name = "NXUSD";
uint8 public constant decimals = 18;
uint256 public override totalSupply;
struct Minting {
uint128 time;
uint128 amount;
}
Minting public lastMint;
uint256 private constant MINTING_PERIOD = 24 hours;
uint256 private constant MINTING_INCREASE = 15000;
uint256 private constant MINTING_PRECISION = 1e5;
function mint(address to, uint256 amount) public onlyOwner {
require(to != address(0), "NXUSD: no mint to zero address");
// Limits the amount minted per period to a convergence function, with the period duration restarting on every mint
uint256 totalMintedAmount = uint256(lastMint.time < block.timestamp - MINTING_PERIOD ? 0 : lastMint.amount).add(amount);
require(totalSupply == 0 || totalSupply.mul(MINTING_INCREASE) / MINTING_PRECISION >= totalMintedAmount);
lastMint.time = block.timestamp.to128();
lastMint.amount = totalMintedAmount.to128();
totalSupply = totalSupply + amount;
balanceOf[to] += amount;
emit Transfer(address(0), to, amount);
}
function mintToBentoBox(address clone, uint256 amount, IBentoBoxV1 bentoBox) public onlyOwner {
mint(address(bentoBox), amount);
bentoBox.deposit(IERC20(address(this)), address(bentoBox), clone, amount, 0);
}
function burn(uint256 amount) public {
require(amount <= balanceOf[msg.sender], "NXUSD: not enough");
balanceOf[msg.sender] -= amount;
totalSupply -= amount;
emit Transfer(msg.sender, address(0), amount);
}
}
contract PermissionManager is BoringOwnable {
struct PermissionInfo {
uint index;
bool isAllowed;
}
mapping(address => PermissionInfo) public info;
address[] public allowedAccounts;
function permit(address _account) public onlyOwner {
if (info[_account].isAllowed) {
revert("Account is already permitted");
}
info[_account] = PermissionInfo({index: allowedAccounts.length, isAllowed: true});
allowedAccounts.push(_account);
}
function revoke(address _account) public onlyOwner {
PermissionInfo memory accountInfo = info[_account];
if (accountInfo.index != allowedAccounts.length-1) {
address last = allowedAccounts[allowedAccounts.length-1];
PermissionInfo storage infoLast = info[last];
allowedAccounts[accountInfo.index] = last;
infoLast.index = accountInfo.index;
}
delete info[_account];
allowedAccounts.pop();
}
function getAllAccounts() public view returns (address[] memory) {
return allowedAccounts;
}
modifier isAllowed() {
require(info[msg.sender].isAllowed, "sender is not allowed");
_;
}
}
interface IOracle {
/// @notice Get the latest exchange rate.
/// @param data Usually abi encoded, implementation specific data that contains information and arguments to & about the oracle.
/// For example:
/// (string memory collateralSymbol, string memory assetSymbol, uint256 division) = abi.decode(data, (string, string, uint256));
/// @return success if no valid (recent) rate is available, return false else true.
/// @return rate The rate of the requested asset / pair / pool.
function get(bytes calldata data) external returns (bool success, uint256 rate);
/// @notice Check the last exchange rate without any state changes.
/// @param data Usually abi encoded, implementation specific data that contains information and arguments to & about the oracle.
/// For example:
/// (string memory collateralSymbol, string memory assetSymbol, uint256 division) = abi.decode(data, (string, string, uint256));
/// @return success if no valid (recent) rate is available, return false else true.
/// @return rate The rate of the requested asset / pair / pool.
function peek(bytes calldata data) external view returns (bool success, uint256 rate);
/// @notice Check the current spot exchange rate without any state changes. For oracles like TWAP this will be different from peek().
/// @param data Usually abi encoded, implementation specific data that contains information and arguments to & about the oracle.
/// For example:
/// (string memory collateralSymbol, string memory assetSymbol, uint256 division) = abi.decode(data, (string, string, uint256));
/// @return rate The rate of the requested asset / pair / pool.
function peekSpot(bytes calldata data) external view returns (uint256 rate);
/// @notice Returns a human readable (short) name about this oracle.
/// @param data Usually abi encoded, implementation specific data that contains information and arguments to & about the oracle.
/// For example:
/// (string memory collateralSymbol, string memory assetSymbol, uint256 division) = abi.decode(data, (string, string, uint256));
/// @return (string) A human readable symbol name about this oracle.
function symbol(bytes calldata data) external view returns (string memory);
/// @notice Returns a human readable name about this oracle.
/// @param data Usually abi encoded, implementation specific data that contains information and arguments to & about the oracle.
/// For example:
/// (string memory collateralSymbol, string memory assetSymbol, uint256 division) = abi.decode(data, (string, string, uint256));
/// @return (string) A human readable name about this oracle.
function name(bytes calldata data) external view returns (string memory);
}
interface ISwapper {
/// @notice Withdraws 'amountFrom' of token 'from' from the BentoBox account for this swapper.
/// Swaps it for at least 'amountToMin' of token 'to'.
/// Transfers the swapped tokens of 'to' into the BentoBox using a plain ERC20 transfer.
/// Returns the amount of tokens 'to' transferred to BentoBox.
/// (The BentoBox skim function will be used by the caller to get the swapped funds).
function swap(
IERC20 fromToken,
IERC20 toToken,
address recipient,
uint256 shareToMin,
uint256 shareFrom
) external returns (uint256 extraShare, uint256 shareReturned);
/// @notice Calculates the amount of token 'from' needed to complete the swap (amountFrom),
/// this should be less than or equal to amountFromMax.
/// Withdraws 'amountFrom' of token 'from' from the BentoBox account for this swapper.
/// Swaps it for exactly 'exactAmountTo' of token 'to'.
/// Transfers the swapped tokens of 'to' into the BentoBox using a plain ERC20 transfer.
/// Transfers allocated, but unused 'from' tokens within the BentoBox to 'refundTo' (amountFromMax - amountFrom).
/// Returns the amount of 'from' tokens withdrawn from BentoBox (amountFrom).
/// (The BentoBox skim function will be used by the caller to get the swapped funds).
function swapExact(
IERC20 fromToken,
IERC20 toToken,
address recipient,
address refundTo,
uint256 shareFromSupplied,
uint256 shareToExact
) external returns (uint256 shareUsed, uint256 shareReturned);
}
/// @title Cauldron
/// @dev This contract allows contract calls to any contract (except BentoBox)
/// from arbitrary callers thus, don't trust calls from this contract in any circumstances.
contract CauldronV2 is BoringOwnable, IMasterContract {
using BoringMath for uint256;
using BoringMath128 for uint128;
using RebaseLibrary for Rebase;
using BoringERC20 for IERC20;
event LogExchangeRate(uint256 rate);
event LogAccrue(uint128 accruedAmount, address collateral);
event LogAddCollateral(address indexed from, address indexed to, uint256 share, address collateral);
event LogRemoveCollateral(address indexed from, address indexed to, uint256 share, address collateral);
event LogBorrow(address indexed from, address indexed to, uint256 amount, uint256 part, address collateral);
event LogRepay(address indexed from, address indexed to, uint256 amount, uint256 part, address collateral);
event LogFeeTo(address indexed newFeeTo);
event LogWithdrawFees(address indexed feeTo, uint256 feesEarnedFraction);
// Immutables (for MasterContract and all clones)
IBentoBoxV1 public immutable bentoBox;
CauldronV2 public immutable masterContract;
IERC20 public immutable nxusd;
PermissionManager public immutable liquidatorManager;
// MasterContract variables
address public feeTo;
// Per clone variables
// Clone init settings
IERC20 public collateral;
IOracle public oracle;
bytes public oracleData;
// Total amounts
uint256 public totalCollateralShare; // Total collateral supplied
Rebase public totalBorrow; // elastic = Total token amount to be repayed by borrowers, base = Total parts of the debt held by borrowers
// User balances
mapping(address => uint256) public userCollateralShare;
mapping(address => uint256) public userBorrowPart;
/// @notice Exchange and interest rate tracking.
/// This is 'cached' here because calls to Oracles can be very expensive.
uint256 public exchangeRate;
struct AccrueInfo {
uint64 lastAccrued;
uint128 feesEarned;
uint64 INTEREST_PER_SECOND;
}
AccrueInfo public accrueInfo;
// Settings
uint256 public COLLATERIZATION_RATE;
uint256 private constant COLLATERIZATION_RATE_PRECISION = 1e5; // Must be less than EXCHANGE_RATE_PRECISION (due to optimization in math)
uint256 private constant EXCHANGE_RATE_PRECISION = 1e18;
uint256 public LIQUIDATION_MULTIPLIER;
uint256 private constant LIQUIDATION_MULTIPLIER_PRECISION = 1e5;
uint256 public BORROW_OPENING_FEE;
uint256 private constant BORROW_OPENING_FEE_PRECISION = 1e5;
uint256 private constant DISTRIBUTION_PART = 0;
uint256 private constant DISTRIBUTION_PRECISION = 100;
/// @notice The constructor is only used for the initial master contract. Subsequent clones are initialised via `init`.
constructor(IBentoBoxV1 bentoBox_, IERC20 nxusd_, PermissionManager liquidatorManager_) public {
bentoBox = bentoBox_;
nxusd = nxusd_;
masterContract = this;
liquidatorManager = liquidatorManager_;
}
/// @notice Serves as the constructor for clones, as clones can't have a regular constructor
/// @dev `data` is abi encoded in the format: (IERC20 collateral, IERC20 asset, IOracle oracle, bytes oracleData)
function init(bytes calldata data) public payable override {
require(address(collateral) == address(0), "Cauldron: already initialized");
(collateral, oracle, oracleData, accrueInfo.INTEREST_PER_SECOND, LIQUIDATION_MULTIPLIER, COLLATERIZATION_RATE, BORROW_OPENING_FEE) = abi.decode(data, (IERC20, IOracle, bytes, uint64, uint256, uint256, uint256));
require(address(collateral) != address(0), "Cauldron: bad pair");
}
/// @notice Accrues the interest on the borrowed tokens and handles the accumulation of fees.
function accrue() public virtual {
AccrueInfo memory _accrueInfo = accrueInfo;
// Number of seconds since accrue was called
uint256 elapsedTime = block.timestamp - _accrueInfo.lastAccrued;
if (elapsedTime == 0) {
return;
}
_accrueInfo.lastAccrued = uint64(block.timestamp);
Rebase memory _totalBorrow = totalBorrow;
if (_totalBorrow.base == 0) {
accrueInfo = _accrueInfo;
return;
}
// Accrue interest
uint128 extraAmount = (uint256(_totalBorrow.elastic).mul(_accrueInfo.INTEREST_PER_SECOND).mul(elapsedTime) / 1e18).to128();
_totalBorrow.elastic = _totalBorrow.elastic.add(extraAmount);
_accrueInfo.feesEarned = _accrueInfo.feesEarned.add(extraAmount);
totalBorrow = _totalBorrow;
accrueInfo = _accrueInfo;
emit LogAccrue(extraAmount, address(collateral));
}
/// @notice Concrete implementation of `isSolvent`. Includes a third parameter to allow caching `exchangeRate`.
/// @param _exchangeRate The exchange rate. Used to cache the `exchangeRate` between calls.
function _isSolvent(address user, uint256 _exchangeRate) internal view returns (bool) {
// accrue must have already been called!
uint256 borrowPart = userBorrowPart[user];
if (borrowPart == 0) return true;
uint256 collateralShare = userCollateralShare[user];
if (collateralShare == 0) return false;
Rebase memory _totalBorrow = totalBorrow;
return
bentoBox.toAmount(
collateral,
collateralShare.mul(EXCHANGE_RATE_PRECISION / COLLATERIZATION_RATE_PRECISION).mul(COLLATERIZATION_RATE),
false
) >=
// Moved exchangeRate here instead of dividing the other side to preserve more precision
borrowPart.mul(_totalBorrow.elastic).mul(_exchangeRate) / _totalBorrow.base;
}
/// @dev Checks if the user is solvent in the closed liquidation case at the end of the function body.
modifier solvent() {
_;
require(_isSolvent(msg.sender, exchangeRate), "Cauldron: user insolvent");
}
/// @notice Gets the exchange rate. I.e how much collateral to buy 1e18 asset.
/// This function is supposed to be invoked if needed because Oracle queries can be expensive.
/// @return updated True if `exchangeRate` was updated.
/// @return rate The new exchange rate.
function updateExchangeRate() public virtual returns (bool updated, uint256 rate) {
(updated, rate) = oracle.get(oracleData);
if (updated) {
exchangeRate = rate;
emit LogExchangeRate(rate);
} else {
// Return the old rate if fetching wasn't successful
rate = exchangeRate;
}
}
/// @dev Helper function to move tokens.
/// @param token The ERC-20 token.
/// @param share The amount in shares to add.
/// @param total Grand total amount to deduct from this contract's balance. Only applicable if `skim` is True.
/// Only used for accounting checks.
/// @param skim If True, only does a balance check on this contract.
/// False if tokens from msg.sender in `bentoBox` should be transferred.
function _addTokens(
IERC20 token,
uint256 share,
uint256 total,
bool skim
) internal {
if (skim) {
require(share <= bentoBox.balanceOf(token, address(this)).sub(total), "Cauldron: Skim too much");
} else {
bentoBox.transfer(token, msg.sender, address(this), share);
}
}
/// @notice Adds `collateral` from msg.sender to the account `to`.
/// @param to The receiver of the tokens.
/// @param skim True if the amount should be skimmed from the deposit balance of msg.sender.x
/// False if tokens from msg.sender in `bentoBox` should be transferred.
/// @param share The amount of shares to add for `to`.
function addCollateral(
address to,
bool skim,
uint256 share
) public virtual {
userCollateralShare[to] = userCollateralShare[to].add(share);
uint256 oldTotalCollateralShare = totalCollateralShare;
totalCollateralShare = oldTotalCollateralShare.add(share);
_addTokens(collateral, share, oldTotalCollateralShare, skim);
emit LogAddCollateral(skim ? address(bentoBox) : msg.sender, to, share, address(collateral));
}
/// @dev Concrete implementation of `removeCollateral`.
function _removeCollateral(address to, uint256 share) internal {
userCollateralShare[msg.sender] = userCollateralShare[msg.sender].sub(share);
totalCollateralShare = totalCollateralShare.sub(share);
emit LogRemoveCollateral(msg.sender, to, share, address(collateral));
bentoBox.transfer(collateral, address(this), to, share);
}
/// @notice Removes `share` amount of collateral and transfers it to `to`.
/// @param to The receiver of the shares.
/// @param share Amount of shares to remove.
function removeCollateral(address to, uint256 share) public virtual solvent {
// accrue must be called because we check solvency
accrue();
_removeCollateral(to, share);
}
/// @dev Concrete implementation of `borrow`.
function _borrow(address to, uint256 amount) internal returns (uint256 part, uint256 share) {
uint256 feeAmount = amount.mul(BORROW_OPENING_FEE) / BORROW_OPENING_FEE_PRECISION; // A flat % fee is charged for any borrow
(totalBorrow, part) = totalBorrow.add(amount.add(feeAmount), true);
accrueInfo.feesEarned = accrueInfo.feesEarned.add(uint128(feeAmount));
userBorrowPart[msg.sender] = userBorrowPart[msg.sender].add(part);
// As long as there are tokens on this contract you can 'mint'... this enables limiting borrows
share = bentoBox.toShare(nxusd, amount, false);
bentoBox.transfer(nxusd, address(this), to, share);
emit LogBorrow(msg.sender, to, amount.add(feeAmount), part, address(collateral));
}
/// @notice Sender borrows `amount` and transfers it to `to`.
/// @return part Total part of the debt held by borrowers.
/// @return share Total amount in shares borrowed.
function borrow(address to, uint256 amount) public solvent virtual returns (uint256 part, uint256 share) {
accrue();
(part, share) = _borrow(to, amount);
}
/// @dev Concrete implementation of `repay`.
function _repay(
address to,
bool skim,
uint256 part
) internal returns (uint256 amount) {
(totalBorrow, amount) = totalBorrow.sub(part, true);
userBorrowPart[to] = userBorrowPart[to].sub(part);
uint256 share = bentoBox.toShare(nxusd, amount, true);
bentoBox.transfer(nxusd, skim ? address(bentoBox) : msg.sender, address(this), share);
emit LogRepay(skim ? address(bentoBox) : msg.sender, to, amount, part, address(collateral));
}
/// @notice Repays a loan.
/// @param to Address of the user this payment should go.
/// @param skim True if the amount should be skimmed from the deposit balance of msg.sender.
/// False if tokens from msg.sender in `bentoBox` should be transferred.
/// @param part The amount to repay. See `userBorrowPart`.
/// @return amount The total amount repayed.
function repay(
address to,
bool skim,
uint256 part
) public virtual returns (uint256 amount) {
accrue();
amount = _repay(to, skim, part);
}
// Functions that need accrue to be called
uint8 internal constant ACTION_REPAY = 2;
uint8 internal constant ACTION_REMOVE_COLLATERAL = 4;
uint8 internal constant ACTION_BORROW = 5;
uint8 internal constant ACTION_GET_REPAY_SHARE = 6;
uint8 internal constant ACTION_GET_REPAY_PART = 7;
uint8 internal constant ACTION_ACCRUE = 8;
// Functions that don't need accrue to be called
uint8 internal constant ACTION_ADD_COLLATERAL = 10;
uint8 internal constant ACTION_UPDATE_EXCHANGE_RATE = 11;
// Function on BentoBox
uint8 internal constant ACTION_BENTO_DEPOSIT = 20;
uint8 internal constant ACTION_BENTO_WITHDRAW = 21;
uint8 internal constant ACTION_BENTO_TRANSFER = 22;
uint8 internal constant ACTION_BENTO_TRANSFER_MULTIPLE = 23;
uint8 internal constant ACTION_BENTO_SETAPPROVAL = 24;
// Any external call (except to BentoBox)
uint8 internal constant ACTION_CALL = 30;
int256 internal constant USE_VALUE1 = -1;
int256 internal constant USE_VALUE2 = -2;
/// @dev Helper function for choosing the correct value (`value1` or `value2`) depending on `inNum`.
function _num(
int256 inNum,
uint256 value1,
uint256 value2
) internal pure returns (uint256 outNum) {
outNum = inNum >= 0 ? uint256(inNum) : (inNum == USE_VALUE1 ? value1 : value2);
}
/// @dev Helper function for depositing into `bentoBox`.
function _bentoDeposit(
bytes memory data,
uint256 value,
uint256 value1,
uint256 value2
) internal returns (uint256, uint256) {
(IERC20 token, address to, int256 amount, int256 share) = abi.decode(data, (IERC20, address, int256, int256));
amount = int256(_num(amount, value1, value2)); // Done this way to avoid stack too deep errors
share = int256(_num(share, value1, value2));
return bentoBox.deposit{value: value}(token, msg.sender, to, uint256(amount), uint256(share));
}
/// @dev Helper function to withdraw from the `bentoBox`.
function _bentoWithdraw(
bytes memory data,
uint256 value1,
uint256 value2
) internal returns (uint256, uint256) {
(IERC20 token, address to, int256 amount, int256 share) = abi.decode(data, (IERC20, address, int256, int256));
return bentoBox.withdraw(token, msg.sender, to, _num(amount, value1, value2), _num(share, value1, value2));
}
/// @dev Helper function to perform a contract call and eventually extracting revert messages on failure.
/// Calls to `bentoBox` are not allowed for obvious security reasons.
/// This also means that calls made from this contract shall *not* be trusted.
function _call(
uint256 value,
bytes memory data,
uint256 value1,
uint256 value2
) internal returns (bytes memory, uint8) {
(address callee, bytes memory callData, bool useValue1, bool useValue2, uint8 returnValues) =
abi.decode(data, (address, bytes, bool, bool, uint8));
if (useValue1 && !useValue2) {
callData = abi.encodePacked(callData, value1);
} else if (!useValue1 && useValue2) {
callData = abi.encodePacked(callData, value2);
} else if (useValue1 && useValue2) {
callData = abi.encodePacked(callData, value1, value2);
}
require(callee != address(bentoBox) && callee != address(this), "Cauldron: can't call");
(bool success, bytes memory returnData) = callee.call{value: value}(callData);
require(success, "Cauldron: call failed");
return (returnData, returnValues);
}
struct CookStatus {
bool needsSolvencyCheck;
bool hasAccrued;
}
/// @notice Executes a set of actions and allows composability (contract calls) to other contracts.
/// @param actions An array with a sequence of actions to execute (see ACTION_ declarations).
/// @param values A one-to-one mapped array to `actions`. ETH amounts to send along with the actions.
/// Only applicable to `ACTION_CALL`, `ACTION_BENTO_DEPOSIT`.
/// @param datas A one-to-one mapped array to `actions`. Contains abi encoded data of function arguments.
/// @return value1 May contain the first positioned return value of the last executed action (if applicable).
/// @return value2 May contain the second positioned return value of the last executed action which returns 2 values (if applicable).
function cook(
uint8[] calldata actions,
uint256[] calldata values,
bytes[] calldata datas
) public virtual payable returns (uint256 value1, uint256 value2) {
CookStatus memory status;
for (uint256 i = 0; i < actions.length; i++) {
uint8 action = actions[i];
if (!status.hasAccrued && action < 10) {
accrue();
status.hasAccrued = true;
}
if (action == ACTION_ADD_COLLATERAL) {
(int256 share, address to, bool skim) = abi.decode(datas[i], (int256, address, bool));
addCollateral(to, skim, _num(share, value1, value2));
} else if (action == ACTION_REPAY) {
(int256 part, address to, bool skim) = abi.decode(datas[i], (int256, address, bool));
_repay(to, skim, _num(part, value1, value2));
} else if (action == ACTION_REMOVE_COLLATERAL) {
(int256 share, address to) = abi.decode(datas[i], (int256, address));
_removeCollateral(to, _num(share, value1, value2));
status.needsSolvencyCheck = true;
} else if (action == ACTION_BORROW) {
(int256 amount, address to) = abi.decode(datas[i], (int256, address));
(value1, value2) = _borrow(to, _num(amount, value1, value2));
status.needsSolvencyCheck = true;
} else if (action == ACTION_UPDATE_EXCHANGE_RATE) {
(bool must_update, uint256 minRate, uint256 maxRate) = abi.decode(datas[i], (bool, uint256, uint256));
(bool updated, uint256 rate) = updateExchangeRate();
require((!must_update || updated) && rate > minRate && (maxRate == 0 || rate > maxRate), "Cauldron: rate not ok");
} else if (action == ACTION_BENTO_SETAPPROVAL) {
(address user, address _masterContract, bool approved, uint8 v, bytes32 r, bytes32 s) =
abi.decode(datas[i], (address, address, bool, uint8, bytes32, bytes32));
bentoBox.setMasterContractApproval(user, _masterContract, approved, v, r, s);
} else if (action == ACTION_BENTO_DEPOSIT) {
(value1, value2) = _bentoDeposit(datas[i], values[i], value1, value2);
} else if (action == ACTION_BENTO_WITHDRAW) {
(value1, value2) = _bentoWithdraw(datas[i], value1, value2);
} else if (action == ACTION_BENTO_TRANSFER) {
(IERC20 token, address to, int256 share) = abi.decode(datas[i], (IERC20, address, int256));
bentoBox.transfer(token, msg.sender, to, _num(share, value1, value2));
} else if (action == ACTION_BENTO_TRANSFER_MULTIPLE) {
(IERC20 token, address[] memory tos, uint256[] memory shares) = abi.decode(datas[i], (IERC20, address[], uint256[]));
bentoBox.transferMultiple(token, msg.sender, tos, shares);
} else if (action == ACTION_CALL) {
(bytes memory returnData, uint8 returnValues) = _call(values[i], datas[i], value1, value2);
if (returnValues == 1) {
(value1) = abi.decode(returnData, (uint256));
} else if (returnValues == 2) {
(value1, value2) = abi.decode(returnData, (uint256, uint256));
}
} else if (action == ACTION_GET_REPAY_SHARE) {
int256 part = abi.decode(datas[i], (int256));
value1 = bentoBox.toShare(nxusd, totalBorrow.toElastic(_num(part, value1, value2), true), true);
} else if (action == ACTION_GET_REPAY_PART) {
int256 amount = abi.decode(datas[i], (int256));
value1 = totalBorrow.toBase(_num(amount, value1, value2), false);
}
}
if (status.needsSolvencyCheck) {
require(_isSolvent(msg.sender, exchangeRate), "Cauldron: user insolvent");
}
}
/// @notice Handles the liquidation of users' balances, once the users' amount of collateral is too low.
/// @param users An array of user addresses.
/// @param maxBorrowParts A one-to-one mapping to `users`, contains maximum (partial) borrow amounts (to liquidate) of the respective user.
/// @param to Address of the receiver in open liquidations if `swapper` is zero.
function liquidate(
address[] calldata users,
uint256[] calldata maxBorrowParts,
address to,
ISwapper swapper
) public {
(, bool isAllowed) = liquidatorManager.info(msg.sender);
require(isAllowed, "sender is not approved as liquidator");
// Oracle can fail but we still need to allow liquidations
(, uint256 _exchangeRate) = updateExchangeRate();
accrue();
uint256 allCollateralShare;
uint256 allBorrowAmount;
uint256 allBorrowPart;
Rebase memory _totalBorrow = totalBorrow;
Rebase memory bentoBoxTotals = bentoBox.totals(collateral);
for (uint256 i = 0; i < users.length; i++) {
address user = users[i];
if (!_isSolvent(user, _exchangeRate)) {
uint256 borrowPart;
{
uint256 availableBorrowPart = userBorrowPart[user];
borrowPart = maxBorrowParts[i] > availableBorrowPart ? availableBorrowPart : maxBorrowParts[i];
userBorrowPart[user] = availableBorrowPart.sub(borrowPart);
}
uint256 borrowAmount = _totalBorrow.toElastic(borrowPart, false);
uint256 collateralShare =
bentoBoxTotals.toBase(
borrowAmount.mul(LIQUIDATION_MULTIPLIER).mul(_exchangeRate) /
(LIQUIDATION_MULTIPLIER_PRECISION * EXCHANGE_RATE_PRECISION),
false
);
userCollateralShare[user] = userCollateralShare[user].sub(collateralShare);
emit LogRemoveCollateral(user, to, collateralShare, address(collateral));
emit LogRepay(msg.sender, user, borrowAmount, borrowPart, address(collateral));
// Keep totals
allCollateralShare = allCollateralShare.add(collateralShare);
allBorrowAmount = allBorrowAmount.add(borrowAmount);
allBorrowPart = allBorrowPart.add(borrowPart);
}
}
require(allBorrowAmount != 0, "Cauldron: all are solvent");
_totalBorrow.elastic = _totalBorrow.elastic.sub(allBorrowAmount.to128());
_totalBorrow.base = _totalBorrow.base.sub(allBorrowPart.to128());
totalBorrow = _totalBorrow;
totalCollateralShare = totalCollateralShare.sub(allCollateralShare);
// Apply a percentual fee share to sSpell holders
{
uint256 distributionAmount = (allBorrowAmount.mul(LIQUIDATION_MULTIPLIER) / LIQUIDATION_MULTIPLIER_PRECISION).sub(allBorrowAmount).mul(DISTRIBUTION_PART) / DISTRIBUTION_PRECISION; // Distribution Amount
allBorrowAmount = allBorrowAmount.add(distributionAmount);
accrueInfo.feesEarned = accrueInfo.feesEarned.add(distributionAmount.to128());
}
uint256 allBorrowShare = bentoBox.toShare(nxusd, allBorrowAmount, true);
// Swap using a swapper freely chosen by the caller
// Open (flash) liquidation: get proceeds first and provide the borrow after
bentoBox.transfer(collateral, address(this), to, allCollateralShare);
if (swapper != ISwapper(0)) {
swapper.swap(collateral, nxusd, msg.sender, allBorrowShare, allCollateralShare);
}
bentoBox.transfer(nxusd, msg.sender, address(this), allBorrowShare);
}
/// @notice Withdraws the fees accumulated.
function withdrawFees() public virtual {
accrue();
address _feeTo = masterContract.feeTo();
uint256 _feesEarned = accrueInfo.feesEarned;
uint256 share = bentoBox.toShare(nxusd, _feesEarned, false);
bentoBox.transfer(nxusd, address(this), _feeTo, share);
accrueInfo.feesEarned = 0;
emit LogWithdrawFees(_feeTo, _feesEarned);
}
/// @notice Sets the beneficiary of interest accrued.
/// MasterContract Only Admin function.
/// @param newFeeTo The address of the receiver.
function setFeeTo(address newFeeTo) public virtual onlyOwner {
feeTo = newFeeTo;
emit LogFeeTo(newFeeTo);
}
/// @notice reduces the supply of NXUSD
/// @param amount amount to reduce supply by
function reduceSupply(uint256 amount) public virtual {
require(msg.sender == masterContract.owner(), "Caller is not the owner");
bentoBox.withdraw(nxusd, address(this), address(this), amount, 0);
NXUSD(address(nxusd)).burn(amount);
}
}{
"optimizer": {
"enabled": true,
"runs": 200
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"metadata": {
"useLiteralContent": true
},
"libraries": {}
}[{"inputs":[{"internalType":"contract IBentoBoxV1","name":"bentoBox_","type":"address"},{"internalType":"contract IERC20","name":"nxusd_","type":"address"},{"internalType":"contract PermissionManager","name":"liquidatorManager_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint128","name":"accruedAmount","type":"uint128"},{"indexed":false,"internalType":"address","name":"collateral","type":"address"}],"name":"LogAccrue","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"share","type":"uint256"},{"indexed":false,"internalType":"address","name":"collateral","type":"address"}],"name":"LogAddCollateral","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"part","type":"uint256"},{"indexed":false,"internalType":"address","name":"collateral","type":"address"}],"name":"LogBorrow","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"rate","type":"uint256"}],"name":"LogExchangeRate","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"newFeeTo","type":"address"}],"name":"LogFeeTo","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"share","type":"uint256"},{"indexed":false,"internalType":"address","name":"collateral","type":"address"}],"name":"LogRemoveCollateral","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"part","type":"uint256"},{"indexed":false,"internalType":"address","name":"collateral","type":"address"}],"name":"LogRepay","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"feeTo","type":"address"},{"indexed":false,"internalType":"uint256","name":"feesEarnedFraction","type":"uint256"}],"name":"LogWithdrawFees","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"},{"inputs":[],"name":"BORROW_OPENING_FEE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"COLLATERIZATION_RATE","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"LIQUIDATION_MULTIPLIER","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"accrue","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"accrueInfo","outputs":[{"internalType":"uint64","name":"lastAccrued","type":"uint64"},{"internalType":"uint128","name":"feesEarned","type":"uint128"},{"internalType":"uint64","name":"INTEREST_PER_SECOND","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"bool","name":"skim","type":"bool"},{"internalType":"uint256","name":"share","type":"uint256"}],"name":"addCollateral","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"bentoBox","outputs":[{"internalType":"contract IBentoBoxV1","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"borrow","outputs":[{"internalType":"uint256","name":"part","type":"uint256"},{"internalType":"uint256","name":"share","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claimOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"collateral","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint8[]","name":"actions","type":"uint8[]"},{"internalType":"uint256[]","name":"values","type":"uint256[]"},{"internalType":"bytes[]","name":"datas","type":"bytes[]"}],"name":"cook","outputs":[{"internalType":"uint256","name":"value1","type":"uint256"},{"internalType":"uint256","name":"value2","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"exchangeRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feeTo","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"data","type":"bytes"}],"name":"init","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address[]","name":"users","type":"address[]"},{"internalType":"uint256[]","name":"maxBorrowParts","type":"uint256[]"},{"internalType":"address","name":"to","type":"address"},{"internalType":"contract ISwapper","name":"swapper","type":"address"}],"name":"liquidate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"liquidatorManager","outputs":[{"internalType":"contract PermissionManager","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"masterContract","outputs":[{"internalType":"contract CauldronV2","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nxusd","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"oracle","outputs":[{"internalType":"contract IOracle","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"oracleData","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"reduceSupply","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"share","type":"uint256"}],"name":"removeCollateral","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"bool","name":"skim","type":"bool"},{"internalType":"uint256","name":"part","type":"uint256"}],"name":"repay","outputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newFeeTo","type":"address"}],"name":"setFeeTo","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"totalBorrow","outputs":[{"internalType":"uint128","name":"elastic","type":"uint128"},{"internalType":"uint128","name":"base","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalCollateralShare","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"},{"internalType":"bool","name":"direct","type":"bool"},{"internalType":"bool","name":"renounce","type":"bool"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"updateExchangeRate","outputs":[{"internalType":"bool","name":"updated","type":"bool"},{"internalType":"uint256","name":"rate","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"userBorrowPart","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"userCollateralShare","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"withdrawFees","outputs":[],"stateMutability":"nonpayable","type":"function"}]
| Age | Block | Fee Address | BC Fee Address | Voting Power | Jailed | Incoming |
|---|
Make sure to use the "Vote Down" button for any spammy posts, and the "Vote Up" for interesting conversations.
Contract