Latest 25 from a total of 1,138 transactions
| Transaction Hash |
Method
|
Block
|
From
|
|
To
|
||||
|---|---|---|---|---|---|---|---|---|---|
| Withdraw Multipl... | 24390362 | 29 hrs ago | IN | 0 ETH | 0.00019782 | ||||
| Withdraw Multipl... | 24379276 | 2 days ago | IN | 0 ETH | 0.00036765 | ||||
| Withdraw Multipl... | 24378570 | 2 days ago | IN | 0 ETH | 0.00040771 | ||||
| Withdraw Multipl... | 24339340 | 8 days ago | IN | 0 ETH | 0.00015365 | ||||
| Withdraw Multipl... | 24276584 | 17 days ago | IN | 0 ETH | 0.00017621 | ||||
| Withdraw Multipl... | 24271143 | 17 days ago | IN | 0 ETH | 0.00034036 | ||||
| Withdraw Multipl... | 24233410 | 23 days ago | IN | 0 ETH | 0.00033902 | ||||
| Withdraw Multipl... | 24191573 | 28 days ago | IN | 0 ETH | 0.00035234 | ||||
| Withdraw Multipl... | 24185726 | 29 days ago | IN | 0 ETH | 0.00028815 | ||||
| Withdraw Multipl... | 24173528 | 31 days ago | IN | 0 ETH | 0.00000493 | ||||
| Withdraw Multipl... | 24151947 | 34 days ago | IN | 0 ETH | 0.00000781 | ||||
| Withdraw Multipl... | 24148298 | 35 days ago | IN | 0 ETH | 0.00032585 | ||||
| Withdraw Multipl... | 24141749 | 35 days ago | IN | 0 ETH | 0.00033666 | ||||
| Withdraw Multipl... | 24089840 | 43 days ago | IN | 0 ETH | 0.00033312 | ||||
| Withdraw Multipl... | 24089657 | 43 days ago | IN | 0 ETH | 0.00008563 | ||||
| Withdraw Multipl... | 24051179 | 48 days ago | IN | 0 ETH | 0.0002868 | ||||
| Withdraw Multipl... | 24048411 | 48 days ago | IN | 0 ETH | 0.00000797 | ||||
| Withdraw Multipl... | 24047471 | 49 days ago | IN | 0 ETH | 0.00024922 | ||||
| Withdraw Multipl... | 24047304 | 49 days ago | IN | 0 ETH | 0.00018033 | ||||
| Withdraw Multipl... | 24045674 | 49 days ago | IN | 0 ETH | 0.00003195 | ||||
| Withdraw Multipl... | 24041385 | 49 days ago | IN | 0 ETH | 0.00000474 | ||||
| Withdraw Multipl... | 24039168 | 50 days ago | IN | 0 ETH | 0.00032185 | ||||
| Withdraw Multipl... | 24028574 | 51 days ago | IN | 0 ETH | 0.00014643 | ||||
| Withdraw Multipl... | 24025468 | 52 days ago | IN | 0 ETH | 0.00029193 | ||||
| Withdraw Multipl... | 24010160 | 54 days ago | IN | 0 ETH | 0.00000992 |
Latest 1 internal transaction
Advanced mode:
| Parent Transaction Hash | Method | Block |
From
|
|
To
|
||
|---|---|---|---|---|---|---|---|
| - | 13704904 | 1530 days ago | Contract Creation | 0 ETH |
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
Minimal Proxy Contract for 0xcd123dd7b07e164bf1b33fc639a8f7a04c3370b2
Contract Name:
TranchedPool
Compiler Version
v0.6.12+commit.27d51765
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
pragma experimental ABIEncoderV2;
import "@openzeppelin/contracts/drafts/IERC20Permit.sol";
import "@openzeppelin/contracts-ethereum-package/contracts/math/Math.sol";
import "@openzeppelin/contracts-ethereum-package/contracts/math/SafeMath.sol";
import "../../interfaces/ITranchedPool.sol";
import "../../interfaces/IERC20withDec.sol";
import "../../interfaces/IV2CreditLine.sol";
import "../../interfaces/IPoolTokens.sol";
import "./GoldfinchConfig.sol";
import "./BaseUpgradeablePausable.sol";
import "./ConfigHelper.sol";
import "../../external/FixedPoint.sol";
import "../../library/SafeERC20Transfer.sol";
contract TranchedPool is BaseUpgradeablePausable, ITranchedPool, SafeERC20Transfer {
GoldfinchConfig public config;
using ConfigHelper for GoldfinchConfig;
using FixedPoint for FixedPoint.Unsigned;
using FixedPoint for uint256;
bytes32 public constant LOCKER_ROLE = keccak256("LOCKER_ROLE");
uint256 public constant FP_SCALING_FACTOR = 1e18;
uint256 public constant SECONDS_PER_DAY = 60 * 60 * 24;
uint256 public constant ONE_HUNDRED = 100; // Need this because we cannot call .div on a literal 100
uint256 public juniorFeePercent;
bool public drawdownsPaused;
TrancheInfo internal seniorTranche;
TrancheInfo internal juniorTranche;
event DepositMade(address indexed owner, uint256 indexed tranche, uint256 indexed tokenId, uint256 amount);
event WithdrawalMade(
address indexed owner,
uint256 indexed tranche,
uint256 indexed tokenId,
uint256 interestWithdrawn,
uint256 principalWithdrawn
);
event PaymentApplied(
address indexed payer,
address indexed pool,
uint256 interestAmount,
uint256 principalAmount,
uint256 remainingAmount,
uint256 reserveAmount
);
event SharePriceUpdated(
address indexed pool,
uint256 indexed tranche,
uint256 principalSharePrice,
int256 principalDelta,
uint256 interestSharePrice,
int256 interestDelta
);
event ReserveFundsCollected(address indexed from, uint256 amount);
event CreditLineMigrated(address indexed oldCreditLine, address indexed newCreditLine);
event DrawdownMade(address indexed borrower, uint256 amount);
event DrawdownsPaused(address indexed pool);
event DrawdownsUnpaused(address indexed pool);
event EmergencyShutdown(address indexed pool);
function initialize(
address _config,
address _borrower,
uint256 _juniorFeePercent,
uint256 _limit,
uint256 _interestApr,
uint256 _paymentPeriodInDays,
uint256 _termInDays,
uint256 _lateFeeApr
) public override initializer {
require(
address(_config) != address(0) && address(_borrower) != address(0),
"Config and borrower addresses cannot be empty"
);
config = GoldfinchConfig(_config);
address owner = config.protocolAdminAddress();
require(owner != address(0), "Owner address cannot be empty");
__BaseUpgradeablePausable__init(owner);
seniorTranche = TrancheInfo({
id: uint256(ITranchedPool.Tranches.Senior),
principalSharePrice: usdcToSharePrice(1, 1),
interestSharePrice: 0,
principalDeposited: 0,
lockedUntil: 0
});
juniorTranche = TrancheInfo({
id: uint256(ITranchedPool.Tranches.Junior),
principalSharePrice: usdcToSharePrice(1, 1),
interestSharePrice: 0,
principalDeposited: 0,
lockedUntil: 0
});
createAndSetCreditLine(_borrower, _limit, _interestApr, _paymentPeriodInDays, _termInDays, _lateFeeApr);
createdAt = block.timestamp;
juniorFeePercent = _juniorFeePercent;
_setupRole(LOCKER_ROLE, _borrower);
_setupRole(LOCKER_ROLE, owner);
_setRoleAdmin(LOCKER_ROLE, OWNER_ROLE);
// Unlock self for infinite amount
bool success = config.getUSDC().approve(address(this), uint256(-1));
require(success, "Failed to approve USDC");
}
/**
* @notice Deposit a USDC amount into the pool for a tranche. Mints an NFT to the caller representing the position
* @param tranche The number representing the tranche to deposit into
* @param amount The USDC amount to tranfer from the caller to the pool
* @return tokenId The tokenId of the NFT
*/
function deposit(uint256 tranche, uint256 amount)
public
override
nonReentrant
whenNotPaused
returns (uint256 tokenId)
{
TrancheInfo storage trancheInfo = getTrancheInfo(tranche);
require(trancheInfo.lockedUntil == 0, "Tranche has been locked");
trancheInfo.principalDeposited = trancheInfo.principalDeposited.add(amount);
IPoolTokens.MintParams memory params = IPoolTokens.MintParams({tranche: tranche, principalAmount: amount});
tokenId = config.getPoolTokens().mint(params, msg.sender);
safeERC20TransferFrom(config.getUSDC(), msg.sender, address(this), amount);
emit DepositMade(msg.sender, tranche, tokenId, amount);
return tokenId;
}
function depositWithPermit(
uint256 tranche,
uint256 amount,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) public override returns (uint256 tokenId) {
IERC20Permit(config.usdcAddress()).permit(msg.sender, address(this), amount, deadline, v, r, s);
return deposit(tranche, amount);
}
/**
* @notice Withdraw an already deposited amount if the funds are available
* @param tokenId The NFT representing the position
* @param amount The amount to withdraw (must be <= interest+principal currently available to withdraw)
* @return interestWithdrawn The interest amount that was withdrawn
* @return principalWithdrawn The principal amount that was withdrawn
*/
function withdraw(uint256 tokenId, uint256 amount)
public
override
onlyTokenHolder(tokenId)
nonReentrant
whenNotPaused
returns (uint256 interestWithdrawn, uint256 principalWithdrawn)
{
IPoolTokens.TokenInfo memory tokenInfo = config.getPoolTokens().getTokenInfo(tokenId);
TrancheInfo storage trancheInfo = getTrancheInfo(tokenInfo.tranche);
return _withdraw(trancheInfo, tokenInfo, tokenId, amount);
}
/**
* @notice Withdraw from many tokens (that the sender owns) in a single transaction
* @param tokenIds An array of tokens ids representing the position
* @param amounts An array of amounts to withdraw from the corresponding tokenIds
*/
function withdrawMultiple(uint256[] calldata tokenIds, uint256[] calldata amounts) public override {
require(tokenIds.length == amounts.length, "TokensIds and Amounts must be the same length");
for (uint256 i = 0; i < amounts.length; i++) {
withdraw(tokenIds[i], amounts[i]);
}
}
/**
* @notice Similar to withdraw but will withdraw all available funds
* @param tokenId The NFT representing the position
* @return interestWithdrawn The interest amount that was withdrawn
* @return principalWithdrawn The principal amount that was withdrawn
*/
function withdrawMax(uint256 tokenId)
external
override
onlyTokenHolder(tokenId)
nonReentrant
whenNotPaused
returns (uint256 interestWithdrawn, uint256 principalWithdrawn)
{
IPoolTokens.TokenInfo memory tokenInfo = config.getPoolTokens().getTokenInfo(tokenId);
TrancheInfo storage trancheInfo = getTrancheInfo(tokenInfo.tranche);
(uint256 interestRedeemable, uint256 principalRedeemable) = redeemableInterestAndPrincipal(trancheInfo, tokenInfo);
uint256 amount = interestRedeemable.add(principalRedeemable);
return _withdraw(trancheInfo, tokenInfo, tokenId, amount);
}
/**
* @notice Draws down the funds (and locks the pool) to the borrower address. Can only be called by the borrower
* @param amount The amount to drawdown from the creditline (must be < limit)
*/
function drawdown(uint256 amount) external override onlyLocker whenNotPaused {
require(!drawdownsPaused, "Drawdowns are currently paused");
if (!locked()) {
// Assumes the senior fund has invested already (saves the borrower a separate transaction to lock the pool)
_lockPool();
}
creditLine.drawdown(amount);
// Update the share price to reflect the amount remaining in the pool
uint256 amountRemaining = totalDeposited().sub(creditLine.balance());
uint256 oldJuniorPrincipalSharePrice = juniorTranche.principalSharePrice;
uint256 oldSeniorPrincipalSharePrice = seniorTranche.principalSharePrice;
juniorTranche.principalSharePrice = calculateExpectedSharePrice(amountRemaining, juniorTranche);
seniorTranche.principalSharePrice = calculateExpectedSharePrice(amountRemaining, seniorTranche);
address borrower = creditLine.borrower();
safeERC20TransferFrom(config.getUSDC(), address(this), borrower, amount);
emit DrawdownMade(borrower, amount);
emit SharePriceUpdated(
address(this),
juniorTranche.id,
juniorTranche.principalSharePrice,
int256(oldJuniorPrincipalSharePrice.sub(juniorTranche.principalSharePrice)) * -1,
juniorTranche.interestSharePrice,
0
);
emit SharePriceUpdated(
address(this),
seniorTranche.id,
seniorTranche.principalSharePrice,
int256(oldSeniorPrincipalSharePrice.sub(seniorTranche.principalSharePrice)) * -1,
seniorTranche.interestSharePrice,
0
);
}
/**
* @notice Locks the junior tranche, preventing more junior deposits. Gives time for the senior to determine how
* much to invest (ensure leverage ratio cannot change for the period)
*/
function lockJuniorCapital() external override onlyLocker whenNotPaused {
_lockJuniorCapital();
}
/**
* @notice Locks the pool (locks both senior and junior tranches and starts the drawdown period). Beyond the drawdown
* period, any unused capital is available to withdraw by all depositors
*/
function lockPool() external override onlyLocker whenNotPaused {
_lockPool();
}
/**
* @notice Triggers an assessment of the creditline and the applies the payments according the tranche waterfall
*/
function assess() external override whenNotPaused {
_assess();
}
/**
* @notice Allows repaying the creditline. Collects the USDC amount from the sender and triggers an assess
* @param amount The amount to repay
*/
function pay(uint256 amount) external override whenNotPaused {
require(amount > 0, "Must pay more than zero");
collectPayment(amount);
_assess();
}
/**
* @notice Migrates to a new goldfinch config address
*/
function updateGoldfinchConfig() external onlyAdmin {
config = GoldfinchConfig(config.configAddress());
creditLine.updateGoldfinchConfig();
}
/**
* @notice Pauses the pool and sweeps any remaining funds to the treasury reserve.
*/
function emergencyShutdown() public onlyAdmin {
if (!paused()) {
pause();
}
IERC20withDec usdc = config.getUSDC();
address reserveAddress = config.reserveAddress();
// Sweep any funds to community reserve
uint256 poolBalance = usdc.balanceOf(address(this));
if (poolBalance > 0) {
safeERC20Transfer(usdc, reserveAddress, poolBalance);
}
uint256 clBalance = usdc.balanceOf(address(creditLine));
if (clBalance > 0) {
safeERC20TransferFrom(usdc, address(creditLine), reserveAddress, clBalance);
}
emit EmergencyShutdown(address(this));
}
/**
* @notice Pauses all drawdowns (but not deposits/withdraws)
*/
function pauseDrawdowns() public onlyAdmin {
drawdownsPaused = true;
emit DrawdownsPaused(address(this));
}
/**
* @notice Unpause drawdowns
*/
function unpauseDrawdowns() public onlyAdmin {
drawdownsPaused = false;
emit DrawdownsUnpaused(address(this));
}
/**
* @notice Migrates the accounting variables from the current creditline to a brand new one
* @param _borrower The borrower address
* @param _limit The new limit
* @param _interestApr The new interest APR
* @param _paymentPeriodInDays The new payment period in days
* @param _termInDays The new term in days
* @param _lateFeeApr The new late fee APR
*/
function migrateCreditLine(
address _borrower,
uint256 _limit,
uint256 _interestApr,
uint256 _paymentPeriodInDays,
uint256 _termInDays,
uint256 _lateFeeApr
) public onlyAdmin {
require(_borrower != address(0), "Borrower must not be empty");
require(_paymentPeriodInDays != 0, "Payment period must not be empty");
require(_termInDays != 0, "Term must not be empty");
address originalClAddr = address(creditLine);
IV2CreditLine originalCl = IV2CreditLine(originalClAddr);
createAndSetCreditLine(_borrower, _limit, _interestApr, _paymentPeriodInDays, _termInDays, _lateFeeApr);
IV2CreditLine newCl = creditLine;
address newClAddr = address(newCl);
emit CreditLineMigrated(originalClAddr, newClAddr);
// Copy over all accounting variables
newCl.setBalance(originalCl.balance());
newCl.setInterestOwed(originalCl.interestOwed());
newCl.setPrincipalOwed(originalCl.principalOwed());
newCl.setTermEndTime(originalCl.termEndTime());
newCl.setNextDueTime(originalCl.nextDueTime());
newCl.setInterestAccruedAsOf(originalCl.interestAccruedAsOf());
newCl.setLastFullPaymentTime(originalCl.lastFullPaymentTime());
newCl.setTotalInterestAccrued(originalCl.totalInterestAccrued());
// Transfer any funds to new CL
uint256 clBalance = config.getUSDC().balanceOf(originalClAddr);
if (clBalance > 0) {
safeERC20TransferFrom(config.getUSDC(), originalClAddr, newClAddr, clBalance);
}
// Close out old CL
originalCl.setBalance(0);
originalCl.setLimit(0);
}
/**
* @notice Migrates to a new creditline without copying the accounting variables
*/
function migrateAndSetNewCreditLine(address newCl) public onlyAdmin {
require(newCl != address(0), "Creditline cannot be empty");
address originalClAddr = address(creditLine);
// Transfer any funds to new CL
uint256 clBalance = config.getUSDC().balanceOf(originalClAddr);
if (clBalance > 0) {
safeERC20TransferFrom(config.getUSDC(), originalClAddr, newCl, clBalance);
}
// Close out old CL
creditLine.setBalance(0);
creditLine.setLimit(0);
// set new CL
creditLine = IV2CreditLine(newCl);
// sanity check that the new address is in fact a creditline
creditLine.limit();
emit CreditLineMigrated(originalClAddr, address(creditLine));
}
// CreditLine proxy methods, for convenience
function limit() public view returns (uint256) {
return creditLine.limit();
}
function borrower() public view returns (address) {
return creditLine.borrower();
}
function interestApr() public view returns (uint256) {
return creditLine.interestApr();
}
function paymentPeriodInDays() public view returns (uint256) {
return creditLine.paymentPeriodInDays();
}
function termInDays() public view returns (uint256) {
return creditLine.termInDays();
}
function lateFeeApr() public view returns (uint256) {
return creditLine.lateFeeApr();
}
function getTranche(uint256 tranche) public view override returns (TrancheInfo memory) {
return getTrancheInfo(tranche);
}
/**
* @notice Converts USDC amounts to share price
* @param amount The USDC amount to convert
* @param totalShares The total shares outstanding
* @return The share price of the input amount
*/
function usdcToSharePrice(uint256 amount, uint256 totalShares) public pure returns (uint256) {
return totalShares == 0 ? 0 : amount.mul(FP_SCALING_FACTOR).div(totalShares);
}
/**
* @notice Converts share price to USDC amounts
* @param sharePrice The share price to convert
* @param totalShares The total shares outstanding
* @return The USDC amount of the input share price
*/
function sharePriceToUsdc(uint256 sharePrice, uint256 totalShares) public pure returns (uint256) {
return sharePrice.mul(totalShares).div(FP_SCALING_FACTOR);
}
/**
* @notice Determines the amount of interest and principal redeemable by a particular tokenId
* @param tokenId The token representing the position
* @return interestRedeemable The interest available to redeem
* @return principalRedeemable The principal available to redeem
*/
function availableToWithdraw(uint256 tokenId)
public
view
override
returns (uint256 interestRedeemable, uint256 principalRedeemable)
{
IPoolTokens.TokenInfo memory tokenInfo = config.getPoolTokens().getTokenInfo(tokenId);
TrancheInfo storage trancheInfo = getTrancheInfo(tokenInfo.tranche);
if (currentTime() > trancheInfo.lockedUntil) {
return redeemableInterestAndPrincipal(trancheInfo, tokenInfo);
} else {
return (0, 0);
}
}
/* Internal functions */
function _withdraw(
TrancheInfo storage trancheInfo,
IPoolTokens.TokenInfo memory tokenInfo,
uint256 tokenId,
uint256 amount
) internal returns (uint256 interestWithdrawn, uint256 principalWithdrawn) {
(uint256 interestRedeemable, uint256 principalRedeemable) = redeemableInterestAndPrincipal(trancheInfo, tokenInfo);
uint256 netRedeemable = interestRedeemable.add(principalRedeemable);
require(amount <= netRedeemable, "Invalid redeem amount");
require(currentTime() > trancheInfo.lockedUntil, "Tranche is locked");
// If the tranche has not been locked, ensure the deposited amount is correct
if (trancheInfo.lockedUntil == 0) {
trancheInfo.principalDeposited = trancheInfo.principalDeposited.sub(amount);
}
uint256 interestToRedeem = Math.min(interestRedeemable, amount);
uint256 principalToRedeem = Math.min(principalRedeemable, amount.sub(interestToRedeem));
config.getPoolTokens().redeem(tokenId, principalToRedeem, interestToRedeem);
safeERC20TransferFrom(config.getUSDC(), address(this), msg.sender, principalToRedeem.add(interestToRedeem));
emit WithdrawalMade(msg.sender, tokenInfo.tranche, tokenId, interestToRedeem, principalToRedeem);
return (interestToRedeem, principalToRedeem);
}
function redeemableInterestAndPrincipal(TrancheInfo storage trancheInfo, IPoolTokens.TokenInfo memory tokenInfo)
internal
view
returns (uint256 interestRedeemable, uint256 principalRedeemable)
{
// This supports withdrawing before or after locking because principal share price starts at 1
// and is set to 0 on lock. Interest share price is always 0 until interest payments come back, when it increases
uint256 maxPrincipalRedeemable = sharePriceToUsdc(trancheInfo.principalSharePrice, tokenInfo.principalAmount);
// The principalAmount is used as the totalShares because we want the interestSharePrice to be expressed as a
// percent of total loan value e.g. if the interest is 10% APR, the interestSharePrice should approach a max of 0.1.
uint256 maxInterestRedeemable = sharePriceToUsdc(trancheInfo.interestSharePrice, tokenInfo.principalAmount);
interestRedeemable = maxInterestRedeemable.sub(tokenInfo.interestRedeemed);
principalRedeemable = maxPrincipalRedeemable.sub(tokenInfo.principalRedeemed);
return (interestRedeemable, principalRedeemable);
}
function _lockJuniorCapital() internal {
require(!locked(), "Pool already locked");
require(juniorTranche.lockedUntil == 0, "Junior tranche already locked");
juniorTranche.lockedUntil = currentTime().add(config.getDrawdownPeriodInSeconds());
}
function _lockPool() internal {
require(juniorTranche.lockedUntil > 0, "Junior tranche must be locked first");
creditLine.setLimit(Math.min(totalDeposited(), creditLine.limit()));
// We start the drawdown period, so backers can withdraw unused capital after borrower draws down
uint256 lockPeriod = config.getDrawdownPeriodInSeconds();
seniorTranche.lockedUntil = currentTime().add(lockPeriod);
juniorTranche.lockedUntil = currentTime().add(lockPeriod);
}
function collectInterestAndPrincipal(
address from,
uint256 interest,
uint256 principal
) internal returns (uint256 totalReserveAmount) {
safeERC20TransferFrom(config.getUSDC(), from, address(this), principal.add(interest), "Failed to collect payment");
(uint256 interestAccrued, uint256 principalAccrued) = getTotalInterestAndPrincipal();
uint256 reserveFeePercent = ONE_HUNDRED.div(config.getReserveDenominator()); // Convert the denonminator to percent
uint256 interestRemaining = interest;
uint256 principalRemaining = principal;
// First determine the expected share price for the senior tranche. This is the gross amount the senior
// tranche should receive.
uint256 expectedInterestSharePrice = calculateExpectedSharePrice(interestAccrued, seniorTranche);
uint256 expectedPrincipalSharePrice = calculateExpectedSharePrice(principalAccrued, seniorTranche);
// Deduct the junior fee and the protocol reserve
uint256 desiredNetInterestSharePrice = scaleByFraction(
expectedInterestSharePrice,
ONE_HUNDRED.sub(juniorFeePercent.add(reserveFeePercent)),
ONE_HUNDRED
);
// Collect protocol fee interest received (we've subtracted this from the senior portion above)
uint256 reserveDeduction = scaleByFraction(interestRemaining, reserveFeePercent, ONE_HUNDRED);
totalReserveAmount = totalReserveAmount.add(reserveDeduction); // protocol fee
interestRemaining = interestRemaining.sub(reserveDeduction);
// Apply the interest remaining so we get up to the netInterestSharePrice
(interestRemaining, principalRemaining) = applyToTrancheBySharePrice(
interestRemaining,
principalRemaining,
desiredNetInterestSharePrice,
expectedPrincipalSharePrice,
seniorTranche
);
// Then fill up the junior tranche with all the interest remaining, upto the principal share price
expectedInterestSharePrice = juniorTranche.interestSharePrice.add(
usdcToSharePrice(interestRemaining, juniorTranche.principalDeposited)
);
expectedPrincipalSharePrice = calculateExpectedSharePrice(principalAccrued, juniorTranche);
(interestRemaining, principalRemaining) = applyToTrancheBySharePrice(
interestRemaining,
principalRemaining,
expectedInterestSharePrice,
expectedPrincipalSharePrice,
juniorTranche
);
// All remaining interest and principal is applied towards the junior tranche as interest
interestRemaining = interestRemaining.add(principalRemaining);
// Since any principal remaining is treated as interest (there is "extra" interest to be distributed)
// we need to make sure to collect the protocol fee on the additional interest (we only deducted the
// fee on the original interest portion)
reserveDeduction = scaleByFraction(principalRemaining, reserveFeePercent, ONE_HUNDRED);
totalReserveAmount = totalReserveAmount.add(reserveDeduction);
interestRemaining = interestRemaining.sub(reserveDeduction);
principalRemaining = 0;
(interestRemaining, principalRemaining) = applyToTrancheByAmount(
interestRemaining.add(principalRemaining),
0,
interestRemaining.add(principalRemaining),
0,
juniorTranche
);
sendToReserve(totalReserveAmount);
return totalReserveAmount;
}
function getTotalInterestAndPrincipal() internal view returns (uint256 interestAccrued, uint256 principalAccrued) {
interestAccrued = creditLine.totalInterestAccrued();
principalAccrued = creditLine.principalOwed();
// Add any remaining balance we have to the principal accrued so expected share price will reflect partial
// drawdowns appropriately. (e.g. if 300K was drawndown from a 1M loan, current and expected share price should
// be 0.7 and not 0)
principalAccrued = principalAccrued.add(totalDeposited().sub(creditLine.balance()));
return (interestAccrued, principalAccrued);
}
function calculateExpectedSharePrice(uint256 amount, TrancheInfo memory tranche) internal view returns (uint256) {
uint256 sharePrice = usdcToSharePrice(amount, tranche.principalDeposited);
return scaleByPercentOwnership(sharePrice, tranche);
}
// If the senior tranche is locked, then the pool is not open to any more deposits (could throw off leverage ratio)
function locked() internal view returns (bool) {
return seniorTranche.lockedUntil > 0;
}
function createAndSetCreditLine(
address _borrower,
uint256 _limit,
uint256 _interestApr,
uint256 _paymentPeriodInDays,
uint256 _termInDays,
uint256 _lateFeeApr
) internal {
address _creditLine = config.getGoldfinchFactory().createCreditLine();
creditLine = IV2CreditLine(_creditLine);
creditLine.initialize(
address(config),
address(this), // Set self as the owner
_borrower,
_limit,
_interestApr,
_paymentPeriodInDays,
_termInDays,
_lateFeeApr
);
}
function getTrancheInfo(uint256 tranche) internal view returns (TrancheInfo storage) {
require(
tranche == uint256(ITranchedPool.Tranches.Senior) || tranche == uint256(ITranchedPool.Tranches.Junior),
"Unsupported tranche"
);
TrancheInfo storage trancheInfo = tranche == uint256(ITranchedPool.Tranches.Senior) ? seniorTranche : juniorTranche;
return trancheInfo;
}
function scaleByPercentOwnership(uint256 amount, TrancheInfo memory tranche) internal view returns (uint256) {
uint256 totalDeposited = juniorTranche.principalDeposited.add(seniorTranche.principalDeposited);
return scaleByFraction(amount, tranche.principalDeposited, totalDeposited);
}
function scaleByFraction(
uint256 amount,
uint256 fraction,
uint256 total
) internal pure returns (uint256) {
FixedPoint.Unsigned memory totalAsFixedPoint = FixedPoint.fromUnscaledUint(total);
FixedPoint.Unsigned memory fractionAsFixedPoint = FixedPoint.fromUnscaledUint(fraction);
return fractionAsFixedPoint.div(totalAsFixedPoint).mul(amount).div(FP_SCALING_FACTOR).rawValue;
}
function totalDeposited() internal view returns (uint256) {
return juniorTranche.principalDeposited.add(seniorTranche.principalDeposited);
}
function currentTime() internal view virtual returns (uint256) {
return block.timestamp;
}
function applyToTrancheBySharePrice(
uint256 interestRemaining,
uint256 principalRemaining,
uint256 desiredInterestSharePrice,
uint256 desiredPrincipalSharePrice,
TrancheInfo storage tranche
) internal returns (uint256, uint256) {
uint256 totalShares = tranche.principalDeposited;
// If the desired share price is lower, then ignore it, and leave it unchanged
uint256 principalSharePrice = tranche.principalSharePrice;
if (desiredPrincipalSharePrice < principalSharePrice) {
desiredPrincipalSharePrice = principalSharePrice;
}
uint256 interestSharePrice = tranche.interestSharePrice;
if (desiredInterestSharePrice < interestSharePrice) {
desiredInterestSharePrice = interestSharePrice;
}
uint256 interestSharePriceDifference = desiredInterestSharePrice.sub(interestSharePrice);
uint256 desiredInterestAmount = sharePriceToUsdc(interestSharePriceDifference, totalShares);
uint256 principalSharePriceDifference = desiredPrincipalSharePrice.sub(principalSharePrice);
uint256 desiredPrincipalAmount = sharePriceToUsdc(principalSharePriceDifference, totalShares);
(interestRemaining, principalRemaining) = applyToTrancheByAmount(
interestRemaining,
principalRemaining,
desiredInterestAmount,
desiredPrincipalAmount,
tranche
);
return (interestRemaining, principalRemaining);
}
function applyToTrancheByAmount(
uint256 interestRemaining,
uint256 principalRemaining,
uint256 desiredInterestAmount,
uint256 desiredPrincipalAmount,
TrancheInfo storage tranche
) internal returns (uint256, uint256) {
uint256 totalShares = tranche.principalDeposited;
uint256 newSharePrice;
(interestRemaining, newSharePrice) = applyToSharePrice(
interestRemaining,
tranche.interestSharePrice,
desiredInterestAmount,
totalShares
);
uint256 oldInterestSharePrice = tranche.interestSharePrice;
tranche.interestSharePrice = newSharePrice;
(principalRemaining, newSharePrice) = applyToSharePrice(
principalRemaining,
tranche.principalSharePrice,
desiredPrincipalAmount,
totalShares
);
uint256 oldPrincipalSharePrice = tranche.principalSharePrice;
tranche.principalSharePrice = newSharePrice;
emit SharePriceUpdated(
address(this),
tranche.id,
tranche.principalSharePrice,
int256(tranche.principalSharePrice.sub(oldPrincipalSharePrice)),
tranche.interestSharePrice,
int256(tranche.interestSharePrice.sub(oldInterestSharePrice))
);
return (interestRemaining, principalRemaining);
}
function applyToSharePrice(
uint256 amountRemaining,
uint256 currentSharePrice,
uint256 desiredAmount,
uint256 totalShares
) internal pure returns (uint256, uint256) {
// If no money left to apply, or don't need any changes, return the original amounts
if (amountRemaining == 0 || desiredAmount == 0) {
return (amountRemaining, currentSharePrice);
}
if (amountRemaining < desiredAmount) {
// We don't have enough money to adjust share price to the desired level. So just use whatever amount is left
desiredAmount = amountRemaining;
}
uint256 sharePriceDifference = usdcToSharePrice(desiredAmount, totalShares);
return (amountRemaining.sub(desiredAmount), currentSharePrice.add(sharePriceDifference));
}
function sendToReserve(uint256 amount) internal {
emit ReserveFundsCollected(address(this), amount);
safeERC20TransferFrom(
config.getUSDC(),
address(this),
config.reserveAddress(),
amount,
"Failed to send to reserve"
);
}
function collectPayment(uint256 amount) internal {
safeERC20TransferFrom(config.getUSDC(), msg.sender, address(creditLine), amount, "Failed to collect payment");
}
function _assess() internal {
(uint256 paymentRemaining, uint256 interestPayment, uint256 principalPayment) = creditLine.assess();
if (interestPayment > 0 || principalPayment > 0) {
uint256 reserveAmount = collectInterestAndPrincipal(
address(creditLine),
interestPayment,
principalPayment.add(paymentRemaining)
);
emit PaymentApplied(
creditLine.borrower(),
address(this),
interestPayment,
principalPayment,
paymentRemaining,
reserveAmount
);
}
}
modifier onlyLocker() {
require(hasRole(LOCKER_ROLE, msg.sender), "Must have locker role to perform this action");
_;
}
modifier onlyTokenHolder(uint256 tokenId) {
require(
config.getPoolTokens().isApprovedOrOwner(msg.sender, tokenId),
"Only the token owner is allowed to call this function"
);
_;
}
}pragma solidity ^0.6.0;
import "../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 GSN 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.
*/
contract ContextUpgradeSafe is Initializable {
// Empty internal constructor, to prevent people from mistakenly deploying
// an instance of this contract, which should be used via inheritance.
function __Context_init() internal initializer {
__Context_init_unchained();
}
function __Context_init_unchained() internal initializer {
}
function _msgSender() internal view virtual returns (address payable) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes memory) {
this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691
return msg.data;
}
uint256[50] private __gap;
}pragma solidity >=0.4.24 <0.7.0;
/**
* @title Initializable
*
* @dev Helper contract to support initializer functions. To use it, replace
* the constructor with a function that has the `initializer` modifier.
* WARNING: Unlike constructors, initializer functions must be manually
* invoked. This applies both to deploying an Initializable contract, as well
* as extending an Initializable contract via inheritance.
* WARNING: When used with inheritance, manual care must be taken to not invoke
* a parent initializer twice, or ensure that all initializers are idempotent,
* because this is not dealt with automatically as with constructors.
*/
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 use in the initializer function of a contract.
*/
modifier initializer() {
require(initializing || isConstructor() || !initialized, "Contract instance has already been initialized");
bool isTopLevelCall = !initializing;
if (isTopLevelCall) {
initializing = true;
initialized = true;
}
_;
if (isTopLevelCall) {
initializing = false;
}
}
/// @dev Returns true if and only if the function is running in the constructor
function isConstructor() private view returns (bool) {
// extcodesize checks the size of the code stored in an address, and
// address returns the current address. Since the code is still not
// deployed when running a constructor, any checks on its code size will
// yield zero, making it an effective way to detect if a contract is
// under construction or not.
address self = address(this);
uint256 cs;
assembly { cs := extcodesize(self) }
return cs == 0;
}
// Reserved storage space to allow for layout changes in the future.
uint256[50] private ______gap;
}pragma solidity ^0.6.0;
import "../utils/EnumerableSet.sol";
import "../utils/Address.sol";
import "../GSN/Context.sol";
import "../Initializable.sol";
/**
* @dev Contract module that allows children to implement role-based access
* control mechanisms.
*
* Roles are referred to by their `bytes32` identifier. These should be exposed
* in the external API and be unique. The best way to achieve this is by
* using `public constant` hash digests:
*
* ```
* bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
* ```
*
* Roles can be used to represent a set of permissions. To restrict access to a
* function call, use {hasRole}:
*
* ```
* function foo() public {
* require(hasRole(MY_ROLE, _msgSender()));
* ...
* }
* ```
*
* Roles can be granted and revoked dynamically via the {grantRole} and
* {revokeRole} functions. Each role has an associated admin role, and only
* accounts that have a role's admin role can call {grantRole} and {revokeRole}.
*
* By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
* that only accounts with this role will be able to grant or revoke other
* roles. More complex role relationships can be created by using
* {_setRoleAdmin}.
*/
abstract contract AccessControlUpgradeSafe is Initializable, ContextUpgradeSafe {
function __AccessControl_init() internal initializer {
__Context_init_unchained();
__AccessControl_init_unchained();
}
function __AccessControl_init_unchained() internal initializer {
}
using EnumerableSet for EnumerableSet.AddressSet;
using Address for address;
struct RoleData {
EnumerableSet.AddressSet members;
bytes32 adminRole;
}
mapping (bytes32 => RoleData) private _roles;
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
/**
* @dev Emitted when `account` is granted `role`.
*
* `sender` is the account that originated the contract call, an admin role
* bearer except when using {_setupRole}.
*/
event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Emitted when `account` is revoked `role`.
*
* `sender` is the account that originated the contract call:
* - if using `revokeRole`, it is the admin role bearer
* - if using `renounceRole`, it is the role bearer (i.e. `account`)
*/
event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) public view returns (bool) {
return _roles[role].members.contains(account);
}
/**
* @dev Returns the number of accounts that have `role`. Can be used
* together with {getRoleMember} to enumerate all bearers of a role.
*/
function getRoleMemberCount(bytes32 role) public view returns (uint256) {
return _roles[role].members.length();
}
/**
* @dev Returns one of the accounts that have `role`. `index` must be a
* value between 0 and {getRoleMemberCount}, non-inclusive.
*
* Role bearers are not sorted in any particular way, and their ordering may
* change at any point.
*
* WARNING: When using {getRoleMember} and {getRoleMemberCount}, make sure
* you perform all queries on the same block. See the following
* https://forum.openzeppelin.com/t/iterating-over-elements-on-enumerableset-in-openzeppelin-contracts/2296[forum post]
* for more information.
*/
function getRoleMember(bytes32 role, uint256 index) public view returns (address) {
return _roles[role].members.at(index);
}
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) public view returns (bytes32) {
return _roles[role].adminRole;
}
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function grantRole(bytes32 role, address account) public virtual {
require(hasRole(_roles[role].adminRole, _msgSender()), "AccessControl: sender must be an admin to grant");
_grantRole(role, account);
}
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function revokeRole(bytes32 role, address account) public virtual {
require(hasRole(_roles[role].adminRole, _msgSender()), "AccessControl: sender must be an admin to revoke");
_revokeRole(role, account);
}
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been granted `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `account`.
*/
function renounceRole(bytes32 role, address account) public virtual {
require(account == _msgSender(), "AccessControl: can only renounce roles for self");
_revokeRole(role, account);
}
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event. Note that unlike {grantRole}, this function doesn't perform any
* checks on the calling account.
*
* [WARNING]
* ====
* This function should only be called from the constructor when setting
* up the initial roles for the system.
*
* Using this function in any other way is effectively circumventing the admin
* system imposed by {AccessControl}.
* ====
*/
function _setupRole(bytes32 role, address account) internal virtual {
_grantRole(role, account);
}
/**
* @dev Sets `adminRole` as ``role``'s admin role.
*/
function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
_roles[role].adminRole = adminRole;
}
function _grantRole(bytes32 role, address account) private {
if (_roles[role].members.add(account)) {
emit RoleGranted(role, account, _msgSender());
}
}
function _revokeRole(bytes32 role, address account) private {
if (_roles[role].members.remove(account)) {
emit RoleRevoked(role, account, _msgSender());
}
}
uint256[49] private __gap;
}pragma solidity ^0.6.0;
/**
* @dev Interface of the ERC165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[EIP].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}pragma solidity ^0.6.0;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
/**
* @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, so we distribute
return (a / 2) + (b / 2) + ((a % 2 + b % 2) / 2);
}
}pragma solidity ^0.6.0;
/**
* @dev Wrappers over Solidity's arithmetic operations with added overflow
* checks.
*
* Arithmetic operations in Solidity wrap on overflow. This can easily result
* in bugs, because programmers usually assume that an overflow raises an
* error, which is the standard behavior in high level programming languages.
* `SafeMath` restores this intuition by reverting the transaction when an
* operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*/
library SafeMath {
/**
* @dev Returns the addition of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
* - Addition cannot overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "SafeMath: addition overflow");
return c;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
return sub(a, b, "SafeMath: subtraction overflow");
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting with custom message on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b <= a, errorMessage);
uint256 c = a - b;
return c;
}
/**
* @dev Returns the multiplication of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
* - Multiplication cannot overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) {
return 0;
}
uint256 c = a * b;
require(c / a == b, "SafeMath: multiplication overflow");
return c;
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
return div(a, b, "SafeMath: division by zero");
}
/**
* @dev Returns the integer division of two unsigned integers. Reverts with custom message on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
// Solidity only automatically asserts when dividing by 0
require(b > 0, errorMessage);
uint256 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
return mod(a, b, "SafeMath: modulo by zero");
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* Reverts with custom message when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
require(b != 0, errorMessage);
return a % b;
}
}pragma solidity ^0.6.0;
/**
* @title SignedSafeMath
* @dev Signed math operations with safety checks that revert on error.
*/
library SignedSafeMath {
int256 constant private _INT256_MIN = -2**255;
/**
* @dev Multiplies two signed integers, reverts on overflow.
*/
function mul(int256 a, int256 b) internal pure returns (int256) {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) {
return 0;
}
require(!(a == -1 && b == _INT256_MIN), "SignedSafeMath: multiplication overflow");
int256 c = a * b;
require(c / a == b, "SignedSafeMath: multiplication overflow");
return c;
}
/**
* @dev Integer division of two signed integers truncating the quotient, reverts on division by zero.
*/
function div(int256 a, int256 b) internal pure returns (int256) {
require(b != 0, "SignedSafeMath: division by zero");
require(!(b == -1 && a == _INT256_MIN), "SignedSafeMath: division overflow");
int256 c = a / b;
return c;
}
/**
* @dev Subtracts two signed integers, reverts on overflow.
*/
function sub(int256 a, int256 b) internal pure returns (int256) {
int256 c = a - b;
require((b >= 0 && c <= a) || (b < 0 && c > a), "SignedSafeMath: subtraction overflow");
return c;
}
/**
* @dev Adds two signed integers, reverts on overflow.
*/
function add(int256 a, int256 b) internal pure returns (int256) {
int256 c = a + b;
require((b >= 0 && c >= a) || (b < 0 && c < a), "SignedSafeMath: addition overflow");
return c;
}
}pragma solidity ^0.6.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @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 `recipient`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address recipient, 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 `sender` to `recipient` 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 sender, address recipient, uint256 amount) external returns (bool);
/**
* @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);
}pragma solidity ^0.6.2;
import "../../introspection/IERC165.sol";
/**
* @dev Required interface of an ERC721 compliant contract.
*/
interface IERC721 is IERC165 {
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
/**
* @dev Returns the number of NFTs in ``owner``'s account.
*/
function balanceOf(address owner) external view returns (uint256 balance);
/**
* @dev Returns the owner of the NFT specified by `tokenId`.
*/
function ownerOf(uint256 tokenId) external view returns (address owner);
/**
* @dev Transfers a specific NFT (`tokenId`) from one account (`from`) to
* another (`to`).
*
*
*
* Requirements:
* - `from`, `to` cannot be zero.
* - `tokenId` must be owned by `from`.
* - If the caller is not `from`, it must be have been allowed to move this
* NFT by either {approve} or {setApprovalForAll}.
*/
function safeTransferFrom(address from, address to, uint256 tokenId) external;
/**
* @dev Transfers a specific NFT (`tokenId`) from one account (`from`) to
* another (`to`).
*
* Requirements:
* - If the caller is not `from`, it must be approved to move this NFT by
* either {approve} or {setApprovalForAll}.
*/
function transferFrom(address from, address to, uint256 tokenId) external;
function approve(address to, uint256 tokenId) external;
function getApproved(uint256 tokenId) external view returns (address operator);
function setApprovalForAll(address operator, bool _approved) external;
function isApprovedForAll(address owner, address operator) external view returns (bool);
function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
}pragma solidity ^0.6.2;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
* ====
*/
function isContract(address account) internal view returns (bool) {
// According to EIP-1052, 0x0 is the value returned for not-yet created accounts
// and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned
// for accounts without code, i.e. `keccak256('')`
bytes32 codehash;
bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
// solhint-disable-next-line no-inline-assembly
assembly { codehash := extcodehash(account) }
return (codehash != accountHash && codehash != 0x0);
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
// solhint-disable-next-line avoid-low-level-calls, avoid-call-value
(bool success, ) = recipient.call{ value: amount }("");
require(success, "Address: unable to send value, recipient may have reverted");
}
}pragma solidity ^0.6.0;
/**
* @dev Library for managing
* https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
* types.
*
* Sets have the following properties:
*
* - Elements are added, removed, and checked for existence in constant time
* (O(1)).
* - Elements are enumerated in O(n). No guarantees are made on the ordering.
*
* ```
* contract Example {
* // Add the library methods
* using EnumerableSet for EnumerableSet.AddressSet;
*
* // Declare a set state variable
* EnumerableSet.AddressSet private mySet;
* }
* ```
*
* As of v3.0.0, only sets of type `address` (`AddressSet`) and `uint256`
* (`UintSet`) are supported.
*/
library EnumerableSet {
// To implement this library for multiple types with as little code
// repetition as possible, we write it in terms of a generic Set type with
// bytes32 values.
// The Set implementation uses private functions, and user-facing
// implementations (such as AddressSet) are just wrappers around the
// underlying Set.
// This means that we can only create new EnumerableSets for types that fit
// in bytes32.
struct Set {
// Storage of set values
bytes32[] _values;
// Position of the value in the `values` array, plus 1 because index 0
// means a value is not in the set.
mapping (bytes32 => uint256) _indexes;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function _add(Set storage set, bytes32 value) private returns (bool) {
if (!_contains(set, value)) {
set._values.push(value);
// The value is stored at length-1, but we add 1 to all indexes
// and use 0 as a sentinel value
set._indexes[value] = set._values.length;
return true;
} else {
return false;
}
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function _remove(Set storage set, bytes32 value) private returns (bool) {
// We read and store the value's index to prevent multiple reads from the same storage slot
uint256 valueIndex = set._indexes[value];
if (valueIndex != 0) { // Equivalent to contains(set, value)
// To delete an element from the _values array in O(1), we swap the element to delete with the last one in
// the array, and then remove the last element (sometimes called as 'swap and pop').
// This modifies the order of the array, as noted in {at}.
uint256 toDeleteIndex = valueIndex - 1;
uint256 lastIndex = set._values.length - 1;
// When the value to delete is the last one, the swap operation is unnecessary. However, since this occurs
// so rarely, we still do the swap anyway to avoid the gas cost of adding an 'if' statement.
bytes32 lastvalue = set._values[lastIndex];
// Move the last value to the index where the value to delete is
set._values[toDeleteIndex] = lastvalue;
// Update the index for the moved value
set._indexes[lastvalue] = toDeleteIndex + 1; // All indexes are 1-based
// Delete the slot where the moved value was stored
set._values.pop();
// Delete the index for the deleted slot
delete set._indexes[value];
return true;
} else {
return false;
}
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function _contains(Set storage set, bytes32 value) private view returns (bool) {
return set._indexes[value] != 0;
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function _length(Set storage set) private view returns (uint256) {
return set._values.length;
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function _at(Set storage set, uint256 index) private view returns (bytes32) {
require(set._values.length > index, "EnumerableSet: index out of bounds");
return set._values[index];
}
// AddressSet
struct AddressSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(AddressSet storage set, address value) internal returns (bool) {
return _add(set._inner, bytes32(uint256(value)));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(AddressSet storage set, address value) internal returns (bool) {
return _remove(set._inner, bytes32(uint256(value)));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(AddressSet storage set, address value) internal view returns (bool) {
return _contains(set._inner, bytes32(uint256(value)));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(AddressSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(AddressSet storage set, uint256 index) internal view returns (address) {
return address(uint256(_at(set._inner, index)));
}
// UintSet
struct UintSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(UintSet storage set, uint256 value) internal returns (bool) {
return _add(set._inner, bytes32(value));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(UintSet storage set, uint256 value) internal returns (bool) {
return _remove(set._inner, bytes32(value));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(UintSet storage set, uint256 value) internal view returns (bool) {
return _contains(set._inner, bytes32(value));
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function length(UintSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(UintSet storage set, uint256 index) internal view returns (uint256) {
return uint256(_at(set._inner, index));
}
}pragma solidity ^0.6.0;
import "../GSN/Context.sol";
import "../Initializable.sol";
/**
* @dev Contract module which allows children to implement an emergency stop
* mechanism that can be triggered by an authorized account.
*
* This module is used through inheritance. It will make available the
* modifiers `whenNotPaused` and `whenPaused`, which can be applied to
* the functions of your contract. Note that they will not be pausable by
* simply including this module, only once the modifiers are put in place.
*/
contract PausableUpgradeSafe is Initializable, ContextUpgradeSafe {
/**
* @dev Emitted when the pause is triggered by `account`.
*/
event Paused(address account);
/**
* @dev Emitted when the pause is lifted by `account`.
*/
event Unpaused(address account);
bool private _paused;
/**
* @dev Initializes the contract in unpaused state.
*/
function __Pausable_init() internal initializer {
__Context_init_unchained();
__Pausable_init_unchained();
}
function __Pausable_init_unchained() internal initializer {
_paused = false;
}
/**
* @dev Returns true if the contract is paused, and false otherwise.
*/
function paused() public view returns (bool) {
return _paused;
}
/**
* @dev Modifier to make a function callable only when the contract is not paused.
*/
modifier whenNotPaused() {
require(!_paused, "Pausable: paused");
_;
}
/**
* @dev Modifier to make a function callable only when the contract is paused.
*/
modifier whenPaused() {
require(_paused, "Pausable: not paused");
_;
}
/**
* @dev Triggers stopped state.
*/
function _pause() internal virtual whenNotPaused {
_paused = true;
emit Paused(_msgSender());
}
/**
* @dev Returns to normal state.
*/
function _unpause() internal virtual whenPaused {
_paused = false;
emit Unpaused(_msgSender());
}
uint256[49] private __gap;
}pragma solidity ^0.6.0;
import "../Initializable.sol";
/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/
contract ReentrancyGuardUpgradeSafe is Initializable {
bool private _notEntered;
function __ReentrancyGuard_init() internal initializer {
__ReentrancyGuard_init_unchained();
}
function __ReentrancyGuard_init_unchained() internal initializer {
// Storing an initial non-zero value makes deployment a bit more
// expensive, but in exchange the refund on every call to nonReentrant
// will be lower in amount. Since refunds are capped to a percetange of
// the total transaction's gas, it is best to keep them low in cases
// like this one, to increase the likelihood of the full refund coming
// into effect.
_notEntered = true;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and make it call a
* `private` function that does the actual work.
*/
modifier nonReentrant() {
// On the first call to nonReentrant, _notEntered will be true
require(_notEntered, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
_notEntered = false;
_;
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_notEntered = true;
}
uint256[49] private __gap;
}// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.8.0;
/**
* @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on `{IERC20-approve}`, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*/
interface IERC20Permit {
/**
* @dev Sets `value` as the allowance of `spender` over `owner`'s tokens,
* given `owner`'s signed approval.
*
* IMPORTANT: The same issues {IERC20-approve} has related to transaction
* ordering also apply here.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `deadline` must be a timestamp in the future.
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
* over the EIP712-formatted function arguments.
* - the signature must use ``owner``'s current nonce (see {nonces}).
*
* For more information on the signature format, see the
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
* section].
*/
function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external;
/**
* @dev Returns the current nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/
function nonces(address owner) external view returns (uint256);
/**
* @dev Returns the domain separator used in the encoding of the signature for `permit`, as defined by {EIP712}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32);
}// SPDX-License-Identifier: AGPL-3.0-only
// solhint-disable
// Imported from https://github.com/UMAprotocol/protocol/blob/4d1c8cc47a4df5e79f978cb05647a7432e111a3d/packages/core/contracts/common/implementation/FixedPoint.sol
pragma solidity 0.6.12;
pragma experimental ABIEncoderV2;
import "@openzeppelin/contracts-ethereum-package/contracts/math/SafeMath.sol";
import "@openzeppelin/contracts-ethereum-package/contracts/math/SignedSafeMath.sol";
/**
* @title Library for fixed point arithmetic on uints
*/
library FixedPoint {
using SafeMath for uint256;
using SignedSafeMath for int256;
// Supports 18 decimals. E.g., 1e18 represents "1", 5e17 represents "0.5".
// For unsigned values:
// This can represent a value up to (2^256 - 1)/10^18 = ~10^59. 10^59 will be stored internally as uint256 10^77.
uint256 private constant FP_SCALING_FACTOR = 10**18;
// --------------------------------------- UNSIGNED -----------------------------------------------------------------------------
struct Unsigned {
uint256 rawValue;
}
/**
* @notice Constructs an `Unsigned` from an unscaled uint, e.g., `b=5` gets stored internally as `5**18`.
* @param a uint to convert into a FixedPoint.
* @return the converted FixedPoint.
*/
function fromUnscaledUint(uint256 a) internal pure returns (Unsigned memory) {
return Unsigned(a.mul(FP_SCALING_FACTOR));
}
/**
* @notice Whether `a` is equal to `b`.
* @param a a FixedPoint.
* @param b a uint256.
* @return True if equal, or False.
*/
function isEqual(Unsigned memory a, uint256 b) internal pure returns (bool) {
return a.rawValue == fromUnscaledUint(b).rawValue;
}
/**
* @notice Whether `a` is equal to `b`.
* @param a a FixedPoint.
* @param b a FixedPoint.
* @return True if equal, or False.
*/
function isEqual(Unsigned memory a, Unsigned memory b) internal pure returns (bool) {
return a.rawValue == b.rawValue;
}
/**
* @notice Whether `a` is greater than `b`.
* @param a a FixedPoint.
* @param b a FixedPoint.
* @return True if `a > b`, or False.
*/
function isGreaterThan(Unsigned memory a, Unsigned memory b) internal pure returns (bool) {
return a.rawValue > b.rawValue;
}
/**
* @notice Whether `a` is greater than `b`.
* @param a a FixedPoint.
* @param b a uint256.
* @return True if `a > b`, or False.
*/
function isGreaterThan(Unsigned memory a, uint256 b) internal pure returns (bool) {
return a.rawValue > fromUnscaledUint(b).rawValue;
}
/**
* @notice Whether `a` is greater than `b`.
* @param a a uint256.
* @param b a FixedPoint.
* @return True if `a > b`, or False.
*/
function isGreaterThan(uint256 a, Unsigned memory b) internal pure returns (bool) {
return fromUnscaledUint(a).rawValue > b.rawValue;
}
/**
* @notice Whether `a` is greater than or equal to `b`.
* @param a a FixedPoint.
* @param b a FixedPoint.
* @return True if `a >= b`, or False.
*/
function isGreaterThanOrEqual(Unsigned memory a, Unsigned memory b) internal pure returns (bool) {
return a.rawValue >= b.rawValue;
}
/**
* @notice Whether `a` is greater than or equal to `b`.
* @param a a FixedPoint.
* @param b a uint256.
* @return True if `a >= b`, or False.
*/
function isGreaterThanOrEqual(Unsigned memory a, uint256 b) internal pure returns (bool) {
return a.rawValue >= fromUnscaledUint(b).rawValue;
}
/**
* @notice Whether `a` is greater than or equal to `b`.
* @param a a uint256.
* @param b a FixedPoint.
* @return True if `a >= b`, or False.
*/
function isGreaterThanOrEqual(uint256 a, Unsigned memory b) internal pure returns (bool) {
return fromUnscaledUint(a).rawValue >= b.rawValue;
}
/**
* @notice Whether `a` is less than `b`.
* @param a a FixedPoint.
* @param b a FixedPoint.
* @return True if `a < b`, or False.
*/
function isLessThan(Unsigned memory a, Unsigned memory b) internal pure returns (bool) {
return a.rawValue < b.rawValue;
}
/**
* @notice Whether `a` is less than `b`.
* @param a a FixedPoint.
* @param b a uint256.
* @return True if `a < b`, or False.
*/
function isLessThan(Unsigned memory a, uint256 b) internal pure returns (bool) {
return a.rawValue < fromUnscaledUint(b).rawValue;
}
/**
* @notice Whether `a` is less than `b`.
* @param a a uint256.
* @param b a FixedPoint.
* @return True if `a < b`, or False.
*/
function isLessThan(uint256 a, Unsigned memory b) internal pure returns (bool) {
return fromUnscaledUint(a).rawValue < b.rawValue;
}
/**
* @notice Whether `a` is less than or equal to `b`.
* @param a a FixedPoint.
* @param b a FixedPoint.
* @return True if `a <= b`, or False.
*/
function isLessThanOrEqual(Unsigned memory a, Unsigned memory b) internal pure returns (bool) {
return a.rawValue <= b.rawValue;
}
/**
* @notice Whether `a` is less than or equal to `b`.
* @param a a FixedPoint.
* @param b a uint256.
* @return True if `a <= b`, or False.
*/
function isLessThanOrEqual(Unsigned memory a, uint256 b) internal pure returns (bool) {
return a.rawValue <= fromUnscaledUint(b).rawValue;
}
/**
* @notice Whether `a` is less than or equal to `b`.
* @param a a uint256.
* @param b a FixedPoint.
* @return True if `a <= b`, or False.
*/
function isLessThanOrEqual(uint256 a, Unsigned memory b) internal pure returns (bool) {
return fromUnscaledUint(a).rawValue <= b.rawValue;
}
/**
* @notice The minimum of `a` and `b`.
* @param a a FixedPoint.
* @param b a FixedPoint.
* @return the minimum of `a` and `b`.
*/
function min(Unsigned memory a, Unsigned memory b) internal pure returns (Unsigned memory) {
return a.rawValue < b.rawValue ? a : b;
}
/**
* @notice The maximum of `a` and `b`.
* @param a a FixedPoint.
* @param b a FixedPoint.
* @return the maximum of `a` and `b`.
*/
function max(Unsigned memory a, Unsigned memory b) internal pure returns (Unsigned memory) {
return a.rawValue > b.rawValue ? a : b;
}
/**
* @notice Adds two `Unsigned`s, reverting on overflow.
* @param a a FixedPoint.
* @param b a FixedPoint.
* @return the sum of `a` and `b`.
*/
function add(Unsigned memory a, Unsigned memory b) internal pure returns (Unsigned memory) {
return Unsigned(a.rawValue.add(b.rawValue));
}
/**
* @notice Adds an `Unsigned` to an unscaled uint, reverting on overflow.
* @param a a FixedPoint.
* @param b a uint256.
* @return the sum of `a` and `b`.
*/
function add(Unsigned memory a, uint256 b) internal pure returns (Unsigned memory) {
return add(a, fromUnscaledUint(b));
}
/**
* @notice Subtracts two `Unsigned`s, reverting on overflow.
* @param a a FixedPoint.
* @param b a FixedPoint.
* @return the difference of `a` and `b`.
*/
function sub(Unsigned memory a, Unsigned memory b) internal pure returns (Unsigned memory) {
return Unsigned(a.rawValue.sub(b.rawValue));
}
/**
* @notice Subtracts an unscaled uint256 from an `Unsigned`, reverting on overflow.
* @param a a FixedPoint.
* @param b a uint256.
* @return the difference of `a` and `b`.
*/
function sub(Unsigned memory a, uint256 b) internal pure returns (Unsigned memory) {
return sub(a, fromUnscaledUint(b));
}
/**
* @notice Subtracts an `Unsigned` from an unscaled uint256, reverting on overflow.
* @param a a uint256.
* @param b a FixedPoint.
* @return the difference of `a` and `b`.
*/
function sub(uint256 a, Unsigned memory b) internal pure returns (Unsigned memory) {
return sub(fromUnscaledUint(a), b);
}
/**
* @notice Multiplies two `Unsigned`s, reverting on overflow.
* @dev This will "floor" the product.
* @param a a FixedPoint.
* @param b a FixedPoint.
* @return the product of `a` and `b`.
*/
function mul(Unsigned memory a, Unsigned memory b) internal pure returns (Unsigned memory) {
// There are two caveats with this computation:
// 1. Max output for the represented number is ~10^41, otherwise an intermediate value overflows. 10^41 is
// stored internally as a uint256 ~10^59.
// 2. Results that can't be represented exactly are truncated not rounded. E.g., 1.4 * 2e-18 = 2.8e-18, which
// would round to 3, but this computation produces the result 2.
// No need to use SafeMath because FP_SCALING_FACTOR != 0.
return Unsigned(a.rawValue.mul(b.rawValue) / FP_SCALING_FACTOR);
}
/**
* @notice Multiplies an `Unsigned` and an unscaled uint256, reverting on overflow.
* @dev This will "floor" the product.
* @param a a FixedPoint.
* @param b a uint256.
* @return the product of `a` and `b`.
*/
function mul(Unsigned memory a, uint256 b) internal pure returns (Unsigned memory) {
return Unsigned(a.rawValue.mul(b));
}
/**
* @notice Multiplies two `Unsigned`s and "ceil's" the product, reverting on overflow.
* @param a a FixedPoint.
* @param b a FixedPoint.
* @return the product of `a` and `b`.
*/
function mulCeil(Unsigned memory a, Unsigned memory b) internal pure returns (Unsigned memory) {
uint256 mulRaw = a.rawValue.mul(b.rawValue);
uint256 mulFloor = mulRaw / FP_SCALING_FACTOR;
uint256 mod = mulRaw.mod(FP_SCALING_FACTOR);
if (mod != 0) {
return Unsigned(mulFloor.add(1));
} else {
return Unsigned(mulFloor);
}
}
/**
* @notice Multiplies an `Unsigned` and an unscaled uint256 and "ceil's" the product, reverting on overflow.
* @param a a FixedPoint.
* @param b a FixedPoint.
* @return the product of `a` and `b`.
*/
function mulCeil(Unsigned memory a, uint256 b) internal pure returns (Unsigned memory) {
// Since b is an int, there is no risk of truncation and we can just mul it normally
return Unsigned(a.rawValue.mul(b));
}
/**
* @notice Divides one `Unsigned` by an `Unsigned`, reverting on overflow or division by 0.
* @dev This will "floor" the quotient.
* @param a a FixedPoint numerator.
* @param b a FixedPoint denominator.
* @return the quotient of `a` divided by `b`.
*/
function div(Unsigned memory a, Unsigned memory b) internal pure returns (Unsigned memory) {
// There are two caveats with this computation:
// 1. Max value for the number dividend `a` represents is ~10^41, otherwise an intermediate value overflows.
// 10^41 is stored internally as a uint256 10^59.
// 2. Results that can't be represented exactly are truncated not rounded. E.g., 2 / 3 = 0.6 repeating, which
// would round to 0.666666666666666667, but this computation produces the result 0.666666666666666666.
return Unsigned(a.rawValue.mul(FP_SCALING_FACTOR).div(b.rawValue));
}
/**
* @notice Divides one `Unsigned` by an unscaled uint256, reverting on overflow or division by 0.
* @dev This will "floor" the quotient.
* @param a a FixedPoint numerator.
* @param b a uint256 denominator.
* @return the quotient of `a` divided by `b`.
*/
function div(Unsigned memory a, uint256 b) internal pure returns (Unsigned memory) {
return Unsigned(a.rawValue.div(b));
}
/**
* @notice Divides one unscaled uint256 by an `Unsigned`, reverting on overflow or division by 0.
* @dev This will "floor" the quotient.
* @param a a uint256 numerator.
* @param b a FixedPoint denominator.
* @return the quotient of `a` divided by `b`.
*/
function div(uint256 a, Unsigned memory b) internal pure returns (Unsigned memory) {
return div(fromUnscaledUint(a), b);
}
/**
* @notice Divides one `Unsigned` by an `Unsigned` and "ceil's" the quotient, reverting on overflow or division by 0.
* @param a a FixedPoint numerator.
* @param b a FixedPoint denominator.
* @return the quotient of `a` divided by `b`.
*/
function divCeil(Unsigned memory a, Unsigned memory b) internal pure returns (Unsigned memory) {
uint256 aScaled = a.rawValue.mul(FP_SCALING_FACTOR);
uint256 divFloor = aScaled.div(b.rawValue);
uint256 mod = aScaled.mod(b.rawValue);
if (mod != 0) {
return Unsigned(divFloor.add(1));
} else {
return Unsigned(divFloor);
}
}
/**
* @notice Divides one `Unsigned` by an unscaled uint256 and "ceil's" the quotient, reverting on overflow or division by 0.
* @param a a FixedPoint numerator.
* @param b a uint256 denominator.
* @return the quotient of `a` divided by `b`.
*/
function divCeil(Unsigned memory a, uint256 b) internal pure returns (Unsigned memory) {
// Because it is possible that a quotient gets truncated, we can't just call "Unsigned(a.rawValue.div(b))"
// similarly to mulCeil with a uint256 as the second parameter. Therefore we need to convert b into an Unsigned.
// This creates the possibility of overflow if b is very large.
return divCeil(a, fromUnscaledUint(b));
}
/**
* @notice Raises an `Unsigned` to the power of an unscaled uint256, reverting on overflow. E.g., `b=2` squares `a`.
* @dev This will "floor" the result.
* @param a a FixedPoint numerator.
* @param b a uint256 denominator.
* @return output is `a` to the power of `b`.
*/
function pow(Unsigned memory a, uint256 b) internal pure returns (Unsigned memory output) {
output = fromUnscaledUint(1);
for (uint256 i = 0; i < b; i = i.add(1)) {
output = mul(output, a);
}
}
// ------------------------------------------------- SIGNED -------------------------------------------------------------
// Supports 18 decimals. E.g., 1e18 represents "1", 5e17 represents "0.5".
// For signed values:
// This can represent a value up (or down) to +-(2^255 - 1)/10^18 = ~10^58. 10^58 will be stored internally as int256 10^76.
int256 private constant SFP_SCALING_FACTOR = 10**18;
struct Signed {
int256 rawValue;
}
function fromSigned(Signed memory a) internal pure returns (Unsigned memory) {
require(a.rawValue >= 0, "Negative value provided");
return Unsigned(uint256(a.rawValue));
}
function fromUnsigned(Unsigned memory a) internal pure returns (Signed memory) {
require(a.rawValue <= uint256(type(int256).max), "Unsigned too large");
return Signed(int256(a.rawValue));
}
/**
* @notice Constructs a `Signed` from an unscaled int, e.g., `b=5` gets stored internally as `5**18`.
* @param a int to convert into a FixedPoint.Signed.
* @return the converted FixedPoint.Signed.
*/
function fromUnscaledInt(int256 a) internal pure returns (Signed memory) {
return Signed(a.mul(SFP_SCALING_FACTOR));
}
/**
* @notice Whether `a` is equal to `b`.
* @param a a FixedPoint.Signed.
* @param b a int256.
* @return True if equal, or False.
*/
function isEqual(Signed memory a, int256 b) internal pure returns (bool) {
return a.rawValue == fromUnscaledInt(b).rawValue;
}
/**
* @notice Whether `a` is equal to `b`.
* @param a a FixedPoint.Signed.
* @param b a FixedPoint.Signed.
* @return True if equal, or False.
*/
function isEqual(Signed memory a, Signed memory b) internal pure returns (bool) {
return a.rawValue == b.rawValue;
}
/**
* @notice Whether `a` is greater than `b`.
* @param a a FixedPoint.Signed.
* @param b a FixedPoint.Signed.
* @return True if `a > b`, or False.
*/
function isGreaterThan(Signed memory a, Signed memory b) internal pure returns (bool) {
return a.rawValue > b.rawValue;
}
/**
* @notice Whether `a` is greater than `b`.
* @param a a FixedPoint.Signed.
* @param b an int256.
* @return True if `a > b`, or False.
*/
function isGreaterThan(Signed memory a, int256 b) internal pure returns (bool) {
return a.rawValue > fromUnscaledInt(b).rawValue;
}
/**
* @notice Whether `a` is greater than `b`.
* @param a an int256.
* @param b a FixedPoint.Signed.
* @return True if `a > b`, or False.
*/
function isGreaterThan(int256 a, Signed memory b) internal pure returns (bool) {
return fromUnscaledInt(a).rawValue > b.rawValue;
}
/**
* @notice Whether `a` is greater than or equal to `b`.
* @param a a FixedPoint.Signed.
* @param b a FixedPoint.Signed.
* @return True if `a >= b`, or False.
*/
function isGreaterThanOrEqual(Signed memory a, Signed memory b) internal pure returns (bool) {
return a.rawValue >= b.rawValue;
}
/**
* @notice Whether `a` is greater than or equal to `b`.
* @param a a FixedPoint.Signed.
* @param b an int256.
* @return True if `a >= b`, or False.
*/
function isGreaterThanOrEqual(Signed memory a, int256 b) internal pure returns (bool) {
return a.rawValue >= fromUnscaledInt(b).rawValue;
}
/**
* @notice Whether `a` is greater than or equal to `b`.
* @param a an int256.
* @param b a FixedPoint.Signed.
* @return True if `a >= b`, or False.
*/
function isGreaterThanOrEqual(int256 a, Signed memory b) internal pure returns (bool) {
return fromUnscaledInt(a).rawValue >= b.rawValue;
}
/**
* @notice Whether `a` is less than `b`.
* @param a a FixedPoint.Signed.
* @param b a FixedPoint.Signed.
* @return True if `a < b`, or False.
*/
function isLessThan(Signed memory a, Signed memory b) internal pure returns (bool) {
return a.rawValue < b.rawValue;
}
/**
* @notice Whether `a` is less than `b`.
* @param a a FixedPoint.Signed.
* @param b an int256.
* @return True if `a < b`, or False.
*/
function isLessThan(Signed memory a, int256 b) internal pure returns (bool) {
return a.rawValue < fromUnscaledInt(b).rawValue;
}
/**
* @notice Whether `a` is less than `b`.
* @param a an int256.
* @param b a FixedPoint.Signed.
* @return True if `a < b`, or False.
*/
function isLessThan(int256 a, Signed memory b) internal pure returns (bool) {
return fromUnscaledInt(a).rawValue < b.rawValue;
}
/**
* @notice Whether `a` is less than or equal to `b`.
* @param a a FixedPoint.Signed.
* @param b a FixedPoint.Signed.
* @return True if `a <= b`, or False.
*/
function isLessThanOrEqual(Signed memory a, Signed memory b) internal pure returns (bool) {
return a.rawValue <= b.rawValue;
}
/**
* @notice Whether `a` is less than or equal to `b`.
* @param a a FixedPoint.Signed.
* @param b an int256.
* @return True if `a <= b`, or False.
*/
function isLessThanOrEqual(Signed memory a, int256 b) internal pure returns (bool) {
return a.rawValue <= fromUnscaledInt(b).rawValue;
}
/**
* @notice Whether `a` is less than or equal to `b`.
* @param a an int256.
* @param b a FixedPoint.Signed.
* @return True if `a <= b`, or False.
*/
function isLessThanOrEqual(int256 a, Signed memory b) internal pure returns (bool) {
return fromUnscaledInt(a).rawValue <= b.rawValue;
}
/**
* @notice The minimum of `a` and `b`.
* @param a a FixedPoint.Signed.
* @param b a FixedPoint.Signed.
* @return the minimum of `a` and `b`.
*/
function min(Signed memory a, Signed memory b) internal pure returns (Signed memory) {
return a.rawValue < b.rawValue ? a : b;
}
/**
* @notice The maximum of `a` and `b`.
* @param a a FixedPoint.Signed.
* @param b a FixedPoint.Signed.
* @return the maximum of `a` and `b`.
*/
function max(Signed memory a, Signed memory b) internal pure returns (Signed memory) {
return a.rawValue > b.rawValue ? a : b;
}
/**
* @notice Adds two `Signed`s, reverting on overflow.
* @param a a FixedPoint.Signed.
* @param b a FixedPoint.Signed.
* @return the sum of `a` and `b`.
*/
function add(Signed memory a, Signed memory b) internal pure returns (Signed memory) {
return Signed(a.rawValue.add(b.rawValue));
}
/**
* @notice Adds an `Signed` to an unscaled int, reverting on overflow.
* @param a a FixedPoint.Signed.
* @param b an int256.
* @return the sum of `a` and `b`.
*/
function add(Signed memory a, int256 b) internal pure returns (Signed memory) {
return add(a, fromUnscaledInt(b));
}
/**
* @notice Subtracts two `Signed`s, reverting on overflow.
* @param a a FixedPoint.Signed.
* @param b a FixedPoint.Signed.
* @return the difference of `a` and `b`.
*/
function sub(Signed memory a, Signed memory b) internal pure returns (Signed memory) {
return Signed(a.rawValue.sub(b.rawValue));
}
/**
* @notice Subtracts an unscaled int256 from an `Signed`, reverting on overflow.
* @param a a FixedPoint.Signed.
* @param b an int256.
* @return the difference of `a` and `b`.
*/
function sub(Signed memory a, int256 b) internal pure returns (Signed memory) {
return sub(a, fromUnscaledInt(b));
}
/**
* @notice Subtracts an `Signed` from an unscaled int256, reverting on overflow.
* @param a an int256.
* @param b a FixedPoint.Signed.
* @return the difference of `a` and `b`.
*/
function sub(int256 a, Signed memory b) internal pure returns (Signed memory) {
return sub(fromUnscaledInt(a), b);
}
/**
* @notice Multiplies two `Signed`s, reverting on overflow.
* @dev This will "floor" the product.
* @param a a FixedPoint.Signed.
* @param b a FixedPoint.Signed.
* @return the product of `a` and `b`.
*/
function mul(Signed memory a, Signed memory b) internal pure returns (Signed memory) {
// There are two caveats with this computation:
// 1. Max output for the represented number is ~10^41, otherwise an intermediate value overflows. 10^41 is
// stored internally as an int256 ~10^59.
// 2. Results that can't be represented exactly are truncated not rounded. E.g., 1.4 * 2e-18 = 2.8e-18, which
// would round to 3, but this computation produces the result 2.
// No need to use SafeMath because SFP_SCALING_FACTOR != 0.
return Signed(a.rawValue.mul(b.rawValue) / SFP_SCALING_FACTOR);
}
/**
* @notice Multiplies an `Signed` and an unscaled int256, reverting on overflow.
* @dev This will "floor" the product.
* @param a a FixedPoint.Signed.
* @param b an int256.
* @return the product of `a` and `b`.
*/
function mul(Signed memory a, int256 b) internal pure returns (Signed memory) {
return Signed(a.rawValue.mul(b));
}
/**
* @notice Multiplies two `Signed`s and "ceil's" the product, reverting on overflow.
* @param a a FixedPoint.Signed.
* @param b a FixedPoint.Signed.
* @return the product of `a` and `b`.
*/
function mulAwayFromZero(Signed memory a, Signed memory b) internal pure returns (Signed memory) {
int256 mulRaw = a.rawValue.mul(b.rawValue);
int256 mulTowardsZero = mulRaw / SFP_SCALING_FACTOR;
// Manual mod because SignedSafeMath doesn't support it.
int256 mod = mulRaw % SFP_SCALING_FACTOR;
if (mod != 0) {
bool isResultPositive = isLessThan(a, 0) == isLessThan(b, 0);
int256 valueToAdd = isResultPositive ? int256(1) : int256(-1);
return Signed(mulTowardsZero.add(valueToAdd));
} else {
return Signed(mulTowardsZero);
}
}
/**
* @notice Multiplies an `Signed` and an unscaled int256 and "ceil's" the product, reverting on overflow.
* @param a a FixedPoint.Signed.
* @param b a FixedPoint.Signed.
* @return the product of `a` and `b`.
*/
function mulAwayFromZero(Signed memory a, int256 b) internal pure returns (Signed memory) {
// Since b is an int, there is no risk of truncation and we can just mul it normally
return Signed(a.rawValue.mul(b));
}
/**
* @notice Divides one `Signed` by an `Signed`, reverting on overflow or division by 0.
* @dev This will "floor" the quotient.
* @param a a FixedPoint numerator.
* @param b a FixedPoint denominator.
* @return the quotient of `a` divided by `b`.
*/
function div(Signed memory a, Signed memory b) internal pure returns (Signed memory) {
// There are two caveats with this computation:
// 1. Max value for the number dividend `a` represents is ~10^41, otherwise an intermediate value overflows.
// 10^41 is stored internally as an int256 10^59.
// 2. Results that can't be represented exactly are truncated not rounded. E.g., 2 / 3 = 0.6 repeating, which
// would round to 0.666666666666666667, but this computation produces the result 0.666666666666666666.
return Signed(a.rawValue.mul(SFP_SCALING_FACTOR).div(b.rawValue));
}
/**
* @notice Divides one `Signed` by an unscaled int256, reverting on overflow or division by 0.
* @dev This will "floor" the quotient.
* @param a a FixedPoint numerator.
* @param b an int256 denominator.
* @return the quotient of `a` divided by `b`.
*/
function div(Signed memory a, int256 b) internal pure returns (Signed memory) {
return Signed(a.rawValue.div(b));
}
/**
* @notice Divides one unscaled int256 by an `Signed`, reverting on overflow or division by 0.
* @dev This will "floor" the quotient.
* @param a an int256 numerator.
* @param b a FixedPoint denominator.
* @return the quotient of `a` divided by `b`.
*/
function div(int256 a, Signed memory b) internal pure returns (Signed memory) {
return div(fromUnscaledInt(a), b);
}
/**
* @notice Divides one `Signed` by an `Signed` and "ceil's" the quotient, reverting on overflow or division by 0.
* @param a a FixedPoint numerator.
* @param b a FixedPoint denominator.
* @return the quotient of `a` divided by `b`.
*/
function divAwayFromZero(Signed memory a, Signed memory b) internal pure returns (Signed memory) {
int256 aScaled = a.rawValue.mul(SFP_SCALING_FACTOR);
int256 divTowardsZero = aScaled.div(b.rawValue);
// Manual mod because SignedSafeMath doesn't support it.
int256 mod = aScaled % b.rawValue;
if (mod != 0) {
bool isResultPositive = isLessThan(a, 0) == isLessThan(b, 0);
int256 valueToAdd = isResultPositive ? int256(1) : int256(-1);
return Signed(divTowardsZero.add(valueToAdd));
} else {
return Signed(divTowardsZero);
}
}
/**
* @notice Divides one `Signed` by an unscaled int256 and "ceil's" the quotient, reverting on overflow or division by 0.
* @param a a FixedPoint numerator.
* @param b an int256 denominator.
* @return the quotient of `a` divided by `b`.
*/
function divAwayFromZero(Signed memory a, int256 b) internal pure returns (Signed memory) {
// Because it is possible that a quotient gets truncated, we can't just call "Signed(a.rawValue.div(b))"
// similarly to mulCeil with an int256 as the second parameter. Therefore we need to convert b into an Signed.
// This creates the possibility of overflow if b is very large.
return divAwayFromZero(a, fromUnscaledInt(b));
}
/**
* @notice Raises an `Signed` to the power of an unscaled uint256, reverting on overflow. E.g., `b=2` squares `a`.
* @dev This will "floor" the result.
* @param a a FixedPoint.Signed.
* @param b a uint256 (negative exponents are not allowed).
* @return output is `a` to the power of `b`.
*/
function pow(Signed memory a, uint256 b) internal pure returns (Signed memory output) {
output = fromUnscaledInt(1);
for (uint256 i = 0; i < b; i = i.add(1)) {
output = mul(output, a);
}
}
}// SPDX-License-Identifier: MIT
// Taken from https://github.com/compound-finance/compound-protocol/blob/master/contracts/CTokenInterfaces.sol
pragma solidity 0.6.12;
pragma experimental ABIEncoderV2;
import "./IERC20withDec.sol";
interface ICUSDCContract is IERC20withDec {
/*** User Interface ***/
function mint(uint256 mintAmount) external returns (uint256);
function redeem(uint256 redeemTokens) external returns (uint256);
function redeemUnderlying(uint256 redeemAmount) external returns (uint256);
function borrow(uint256 borrowAmount) external returns (uint256);
function repayBorrow(uint256 repayAmount) external returns (uint256);
function repayBorrowBehalf(address borrower, uint256 repayAmount) external returns (uint256);
function liquidateBorrow(
address borrower,
uint256 repayAmount,
address cTokenCollateral
) external returns (uint256);
function getAccountSnapshot(address account)
external
view
returns (
uint256,
uint256,
uint256,
uint256
);
function balanceOfUnderlying(address owner) external returns (uint256);
function exchangeRateCurrent() external returns (uint256);
/*** Admin Functions ***/
function _addReserves(uint256 addAmount) external returns (uint256);
}// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
pragma experimental ABIEncoderV2;
abstract contract ICreditDesk {
uint256 public totalWritedowns;
uint256 public totalLoansOutstanding;
function setUnderwriterGovernanceLimit(address underwriterAddress, uint256 limit) external virtual;
function drawdown(address creditLineAddress, uint256 amount) external virtual;
function pay(address creditLineAddress, uint256 amount) external virtual;
function assessCreditLine(address creditLineAddress) external virtual;
function applyPayment(address creditLineAddress, uint256 amount) external virtual;
function getNextPaymentAmount(address creditLineAddress, uint256 asOfBLock) external view virtual returns (uint256);
}// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
pragma experimental ABIEncoderV2;
interface ICreditLine {
function borrower() external view returns (address);
function limit() external view returns (uint256);
function interestApr() external view returns (uint256);
function paymentPeriodInDays() external view returns (uint256);
function termInDays() external view returns (uint256);
function lateFeeApr() external view returns (uint256);
// Accounting variables
function balance() external view returns (uint256);
function interestOwed() external view returns (uint256);
function principalOwed() external view returns (uint256);
function termEndTime() external view returns (uint256);
function nextDueTime() external view returns (uint256);
function interestAccruedAsOf() external view returns (uint256);
function lastFullPaymentTime() external view returns (uint256);
}// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
pragma experimental ABIEncoderV2;
import "@openzeppelin/contracts-ethereum-package/contracts/token/ERC20/IERC20.sol";
/*
Only addition is the `decimals` function, which we need, and which both our Fidu and USDC use, along with most ERC20's.
*/
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20withDec is IERC20 {
/**
* @dev Returns the number of decimals used for the token
*/
function decimals() external view returns (uint8);
}// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
pragma experimental ABIEncoderV2;
import "./IERC20withDec.sol";
interface IFidu is IERC20withDec {
function mintTo(address to, uint256 amount) external;
function burnFrom(address to, uint256 amount) external;
function renounceRole(bytes32 role, address account) external;
}// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
pragma experimental ABIEncoderV2;
interface IGoldfinchConfig {
function getNumber(uint256 index) external returns (uint256);
function getAddress(uint256 index) external returns (address);
function setAddress(uint256 index, address newAddress) external returns (address);
function setNumber(uint256 index, uint256 newNumber) external returns (uint256);
}// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
pragma experimental ABIEncoderV2;
interface IGoldfinchFactory {
function createCreditLine() external returns (address);
function createBorrower(address owner) external returns (address);
function createPool(
address _borrower,
uint256 _juniorFeePercent,
uint256 _limit,
uint256 _interestApr,
uint256 _paymentPeriodInDays,
uint256 _termInDays,
uint256 _lateFeeApr
) external returns (address);
function createMigratedPool(
address _borrower,
uint256 _juniorFeePercent,
uint256 _limit,
uint256 _interestApr,
uint256 _paymentPeriodInDays,
uint256 _termInDays,
uint256 _lateFeeApr
) external returns (address);
function updateGoldfinchConfig() external;
}// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
pragma experimental ABIEncoderV2;
abstract contract IPool {
uint256 public sharePrice;
function deposit(uint256 amount) external virtual;
function withdraw(uint256 usdcAmount) external virtual;
function withdrawInFidu(uint256 fiduAmount) external virtual;
function collectInterestAndPrincipal(
address from,
uint256 interest,
uint256 principal
) public virtual;
function transferFrom(
address from,
address to,
uint256 amount
) public virtual returns (bool);
function drawdown(address to, uint256 amount) public virtual returns (bool);
function sweepToCompound() public virtual;
function sweepFromCompound() public virtual;
function distributeLosses(address creditlineAddress, int256 writedownDelta) external virtual;
function assets() public view virtual returns (uint256);
}// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
pragma experimental ABIEncoderV2;
import "@openzeppelin/contracts-ethereum-package/contracts/token/ERC721/IERC721.sol";
interface IPoolTokens is IERC721 {
event TokenMinted(
address indexed owner,
address indexed pool,
uint256 indexed tokenId,
uint256 amount,
uint256 tranche
);
event TokenRedeemed(
address indexed owner,
address indexed pool,
uint256 indexed tokenId,
uint256 principalRedeemed,
uint256 interestRedeemed,
uint256 tranche
);
event TokenBurned(address indexed owner, address indexed pool, uint256 indexed tokenId);
struct TokenInfo {
address pool;
uint256 tranche;
uint256 principalAmount;
uint256 principalRedeemed;
uint256 interestRedeemed;
}
struct MintParams {
uint256 principalAmount;
uint256 tranche;
}
function mint(MintParams calldata params, address to) external returns (uint256);
function redeem(
uint256 tokenId,
uint256 principalRedeemed,
uint256 interestRedeemed
) external;
function burn(uint256 tokenId) external;
function onPoolCreated(address newPool) external;
function getTokenInfo(uint256 tokenId) external view returns (TokenInfo memory);
function validPool(address sender) external view returns (bool);
function isApprovedOrOwner(address spender, uint256 tokenId) external view returns (bool);
}// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
pragma experimental ABIEncoderV2;
import "./ITranchedPool.sol";
abstract contract ISeniorPool {
uint256 public sharePrice;
uint256 public totalLoansOutstanding;
uint256 public totalWritedowns;
function deposit(uint256 amount) external virtual returns (uint256 depositShares);
function depositWithPermit(
uint256 amount,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external virtual returns (uint256 depositShares);
function withdraw(uint256 usdcAmount) external virtual returns (uint256 amount);
function withdrawInFidu(uint256 fiduAmount) external virtual returns (uint256 amount);
function sweepToCompound() public virtual;
function sweepFromCompound() public virtual;
function invest(ITranchedPool pool) public virtual;
function estimateInvestment(ITranchedPool pool) public view virtual returns (uint256);
function investJunior(ITranchedPool pool, uint256 amount) public virtual;
function redeem(uint256 tokenId) public virtual;
function writedown(uint256 tokenId) public virtual;
function calculateWritedown(uint256 tokenId) public view virtual returns (uint256 writedownAmount);
function assets() public view virtual returns (uint256);
function getNumShares(uint256 amount) public view virtual returns (uint256);
}// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
pragma experimental ABIEncoderV2;
import "./ISeniorPool.sol";
import "./ITranchedPool.sol";
abstract contract ISeniorPoolStrategy {
function invest(ISeniorPool seniorPool, ITranchedPool pool) public view virtual returns (uint256 amount);
function estimateInvestment(ISeniorPool seniorPool, ITranchedPool pool) public view virtual returns (uint256);
}// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
pragma experimental ABIEncoderV2;
import "./IV2CreditLine.sol";
abstract contract ITranchedPool {
IV2CreditLine public creditLine;
uint256 public createdAt;
enum Tranches {Reserved, Senior, Junior}
struct TrancheInfo {
uint256 id;
uint256 principalDeposited;
uint256 principalSharePrice;
uint256 interestSharePrice;
uint256 lockedUntil;
}
function initialize(
address _config,
address _borrower,
uint256 _juniorFeePercent,
uint256 _limit,
uint256 _interestApr,
uint256 _paymentPeriodInDays,
uint256 _termInDays,
uint256 _lateFeeApr
) public virtual;
function getTranche(uint256 tranche) external view virtual returns (TrancheInfo memory);
function pay(uint256 amount) external virtual;
function lockJuniorCapital() external virtual;
function lockPool() external virtual;
function drawdown(uint256 amount) external virtual;
function deposit(uint256 tranche, uint256 amount) external virtual returns (uint256 tokenId);
function assess() external virtual;
function depositWithPermit(
uint256 tranche,
uint256 amount,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external virtual returns (uint256 tokenId);
function availableToWithdraw(uint256 tokenId)
external
view
virtual
returns (uint256 interestRedeemable, uint256 principalRedeemable);
function withdraw(uint256 tokenId, uint256 amount)
external
virtual
returns (uint256 interestWithdrawn, uint256 principalWithdrawn);
function withdrawMax(uint256 tokenId)
external
virtual
returns (uint256 interestWithdrawn, uint256 principalWithdrawn);
function withdrawMultiple(uint256[] calldata tokenIds, uint256[] calldata amounts) external virtual;
}// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
pragma experimental ABIEncoderV2;
import "./ICreditLine.sol";
abstract contract IV2CreditLine is ICreditLine {
function principal() external view virtual returns (uint256);
function totalInterestAccrued() external view virtual returns (uint256);
function termStartTime() external view virtual returns (uint256);
function setLimit(uint256 newAmount) external virtual;
function setBalance(uint256 newBalance) external virtual;
function setPrincipal(uint256 _principal) external virtual;
function setTotalInterestAccrued(uint256 _interestAccrued) external virtual;
function drawdown(uint256 amount) external virtual;
function assess()
external
virtual
returns (
uint256,
uint256,
uint256
);
function initialize(
address _config,
address owner,
address _borrower,
uint256 _limit,
uint256 _interestApr,
uint256 _paymentPeriodInDays,
uint256 _termInDays,
uint256 _lateFeeApr
) public virtual;
function setTermEndTime(uint256 newTermEndTime) external virtual;
function setNextDueTime(uint256 newNextDueTime) external virtual;
function setInterestOwed(uint256 newInterestOwed) external virtual;
function setPrincipalOwed(uint256 newPrincipalOwed) external virtual;
function setInterestAccruedAsOf(uint256 newInterestAccruedAsOf) external virtual;
function setWritedownAmount(uint256 newWritedownAmount) external virtual;
function setLastFullPaymentTime(uint256 newLastFullPaymentTime) external virtual;
function setLateFeeApr(uint256 newLateFeeApr) external virtual;
function updateGoldfinchConfig() external virtual;
}// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
pragma experimental ABIEncoderV2;
import "@openzeppelin/contracts-ethereum-package/contracts/token/ERC20/IERC20.sol";
/**
* @title Safe ERC20 Transfer
* @notice Reverts when transfer is not successful
* @author Goldfinch
*/
abstract contract SafeERC20Transfer {
function safeERC20Transfer(
IERC20 erc20,
address to,
uint256 amount,
string memory message
) internal {
require(to != address(0), "Can't send to zero address");
bool success = erc20.transfer(to, amount);
require(success, message);
}
function safeERC20Transfer(
IERC20 erc20,
address to,
uint256 amount
) internal {
safeERC20Transfer(erc20, to, amount, "Failed to transfer ERC20");
}
function safeERC20TransferFrom(
IERC20 erc20,
address from,
address to,
uint256 amount,
string memory message
) internal {
require(to != address(0), "Can't send to zero address");
bool success = erc20.transferFrom(from, to, amount);
require(success, message);
}
function safeERC20TransferFrom(
IERC20 erc20,
address from,
address to,
uint256 amount
) internal {
string memory message = "Failed to transfer ERC20";
safeERC20TransferFrom(erc20, from, to, amount, message);
}
function safeERC20Approve(
IERC20 erc20,
address spender,
uint256 allowance,
string memory message
) internal {
bool success = erc20.approve(spender, allowance);
require(success, message);
}
function safeERC20Approve(
IERC20 erc20,
address spender,
uint256 allowance
) internal {
string memory message = "Failed to approve ERC20";
safeERC20Approve(erc20, spender, allowance, message);
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
pragma experimental ABIEncoderV2;
import "@openzeppelin/contracts-ethereum-package/contracts/access/AccessControl.sol";
import "@openzeppelin/contracts-ethereum-package/contracts/utils/ReentrancyGuard.sol";
import "@openzeppelin/contracts-ethereum-package/contracts/Initializable.sol";
import "@openzeppelin/contracts-ethereum-package/contracts/math/SafeMath.sol";
import "./PauserPausable.sol";
/**
* @title BaseUpgradeablePausable contract
* @notice This is our Base contract that most other contracts inherit from. It includes many standard
* useful abilities like ugpradeability, pausability, access control, and re-entrancy guards.
* @author Goldfinch
*/
contract BaseUpgradeablePausable is
Initializable,
AccessControlUpgradeSafe,
PauserPausable,
ReentrancyGuardUpgradeSafe
{
bytes32 public constant OWNER_ROLE = keccak256("OWNER_ROLE");
using SafeMath for uint256;
// Pre-reserving a few slots in the base contract in case we need to add things in the future.
// This does not actually take up gas cost or storage cost, but it does reserve the storage slots.
// See OpenZeppelin's use of this pattern here:
// https://github.com/OpenZeppelin/openzeppelin-contracts-ethereum-package/blob/master/contracts/GSN/Context.sol#L37
uint256[50] private __gap1;
uint256[50] private __gap2;
uint256[50] private __gap3;
uint256[50] private __gap4;
// solhint-disable-next-line func-name-mixedcase
function __BaseUpgradeablePausable__init(address owner) public initializer {
require(owner != address(0), "Owner cannot be the zero address");
__AccessControl_init_unchained();
__Pausable_init_unchained();
__ReentrancyGuard_init_unchained();
_setupRole(OWNER_ROLE, owner);
_setupRole(PAUSER_ROLE, owner);
_setRoleAdmin(PAUSER_ROLE, OWNER_ROLE);
_setRoleAdmin(OWNER_ROLE, OWNER_ROLE);
}
function isAdmin() public view returns (bool) {
return hasRole(OWNER_ROLE, _msgSender());
}
modifier onlyAdmin() {
require(isAdmin(), "Must have admin role to perform this action");
_;
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
pragma experimental ABIEncoderV2;
import "./GoldfinchConfig.sol";
import "../../interfaces/IPool.sol";
import "../../interfaces/IFidu.sol";
import "../../interfaces/ISeniorPool.sol";
import "../../interfaces/ISeniorPoolStrategy.sol";
import "../../interfaces/ICreditDesk.sol";
import "../../interfaces/IERC20withDec.sol";
import "../../interfaces/ICUSDCContract.sol";
import "../../interfaces/IPoolTokens.sol";
import "../../interfaces/IGoldfinchFactory.sol";
/**
* @title ConfigHelper
* @notice A convenience library for getting easy access to other contracts and constants within the
* protocol, through the use of the GoldfinchConfig contract
* @author Goldfinch
*/
library ConfigHelper {
function getPool(GoldfinchConfig config) internal view returns (IPool) {
return IPool(poolAddress(config));
}
function getSeniorPool(GoldfinchConfig config) internal view returns (ISeniorPool) {
return ISeniorPool(seniorPoolAddress(config));
}
function getSeniorPoolStrategy(GoldfinchConfig config) internal view returns (ISeniorPoolStrategy) {
return ISeniorPoolStrategy(seniorPoolStrategyAddress(config));
}
function getUSDC(GoldfinchConfig config) internal view returns (IERC20withDec) {
return IERC20withDec(usdcAddress(config));
}
function getCreditDesk(GoldfinchConfig config) internal view returns (ICreditDesk) {
return ICreditDesk(creditDeskAddress(config));
}
function getFidu(GoldfinchConfig config) internal view returns (IFidu) {
return IFidu(fiduAddress(config));
}
function getCUSDCContract(GoldfinchConfig config) internal view returns (ICUSDCContract) {
return ICUSDCContract(cusdcContractAddress(config));
}
function getPoolTokens(GoldfinchConfig config) internal view returns (IPoolTokens) {
return IPoolTokens(poolTokensAddress(config));
}
function getGoldfinchFactory(GoldfinchConfig config) internal view returns (IGoldfinchFactory) {
return IGoldfinchFactory(goldfinchFactoryAddress(config));
}
function oneInchAddress(GoldfinchConfig config) internal view returns (address) {
return config.getAddress(uint256(ConfigOptions.Addresses.OneInch));
}
function creditLineImplementationAddress(GoldfinchConfig config) internal view returns (address) {
return config.getAddress(uint256(ConfigOptions.Addresses.CreditLineImplementation));
}
function trustedForwarderAddress(GoldfinchConfig config) internal view returns (address) {
return config.getAddress(uint256(ConfigOptions.Addresses.TrustedForwarder));
}
function configAddress(GoldfinchConfig config) internal view returns (address) {
return config.getAddress(uint256(ConfigOptions.Addresses.GoldfinchConfig));
}
function poolAddress(GoldfinchConfig config) internal view returns (address) {
return config.getAddress(uint256(ConfigOptions.Addresses.Pool));
}
function poolTokensAddress(GoldfinchConfig config) internal view returns (address) {
return config.getAddress(uint256(ConfigOptions.Addresses.PoolTokens));
}
function seniorPoolAddress(GoldfinchConfig config) internal view returns (address) {
return config.getAddress(uint256(ConfigOptions.Addresses.SeniorPool));
}
function seniorPoolStrategyAddress(GoldfinchConfig config) internal view returns (address) {
return config.getAddress(uint256(ConfigOptions.Addresses.SeniorPoolStrategy));
}
function creditDeskAddress(GoldfinchConfig config) internal view returns (address) {
return config.getAddress(uint256(ConfigOptions.Addresses.CreditDesk));
}
function goldfinchFactoryAddress(GoldfinchConfig config) internal view returns (address) {
return config.getAddress(uint256(ConfigOptions.Addresses.GoldfinchFactory));
}
function fiduAddress(GoldfinchConfig config) internal view returns (address) {
return config.getAddress(uint256(ConfigOptions.Addresses.Fidu));
}
function cusdcContractAddress(GoldfinchConfig config) internal view returns (address) {
return config.getAddress(uint256(ConfigOptions.Addresses.CUSDCContract));
}
function usdcAddress(GoldfinchConfig config) internal view returns (address) {
return config.getAddress(uint256(ConfigOptions.Addresses.USDC));
}
function tranchedPoolAddress(GoldfinchConfig config) internal view returns (address) {
return config.getAddress(uint256(ConfigOptions.Addresses.TranchedPoolImplementation));
}
function migratedTranchedPoolAddress(GoldfinchConfig config) internal view returns (address) {
return config.getAddress(uint256(ConfigOptions.Addresses.MigratedTranchedPoolImplementation));
}
function reserveAddress(GoldfinchConfig config) internal view returns (address) {
return config.getAddress(uint256(ConfigOptions.Addresses.TreasuryReserve));
}
function protocolAdminAddress(GoldfinchConfig config) internal view returns (address) {
return config.getAddress(uint256(ConfigOptions.Addresses.ProtocolAdmin));
}
function borrowerImplementationAddress(GoldfinchConfig config) internal view returns (address) {
return config.getAddress(uint256(ConfigOptions.Addresses.BorrowerImplementation));
}
function getReserveDenominator(GoldfinchConfig config) internal view returns (uint256) {
return config.getNumber(uint256(ConfigOptions.Numbers.ReserveDenominator));
}
function getWithdrawFeeDenominator(GoldfinchConfig config) internal view returns (uint256) {
return config.getNumber(uint256(ConfigOptions.Numbers.WithdrawFeeDenominator));
}
function getLatenessGracePeriodInDays(GoldfinchConfig config) internal view returns (uint256) {
return config.getNumber(uint256(ConfigOptions.Numbers.LatenessGracePeriodInDays));
}
function getLatenessMaxDays(GoldfinchConfig config) internal view returns (uint256) {
return config.getNumber(uint256(ConfigOptions.Numbers.LatenessMaxDays));
}
function getDrawdownPeriodInSeconds(GoldfinchConfig config) internal view returns (uint256) {
return config.getNumber(uint256(ConfigOptions.Numbers.DrawdownPeriodInSeconds));
}
function getTransferRestrictionPeriodInDays(GoldfinchConfig config) internal view returns (uint256) {
return config.getNumber(uint256(ConfigOptions.Numbers.TransferRestrictionPeriodInDays));
}
function getLeverageRatio(GoldfinchConfig config) internal view returns (uint256) {
return config.getNumber(uint256(ConfigOptions.Numbers.LeverageRatio));
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
pragma experimental ABIEncoderV2;
/**
* @title ConfigOptions
* @notice A central place for enumerating the configurable options of our GoldfinchConfig contract
* @author Goldfinch
*/
library ConfigOptions {
// NEVER EVER CHANGE THE ORDER OF THESE!
// You can rename or append. But NEVER change the order.
enum Numbers {
TransactionLimit,
TotalFundsLimit,
MaxUnderwriterLimit,
ReserveDenominator,
WithdrawFeeDenominator,
LatenessGracePeriodInDays,
LatenessMaxDays,
DrawdownPeriodInSeconds,
TransferRestrictionPeriodInDays,
LeverageRatio
}
enum Addresses {
Pool,
CreditLineImplementation,
GoldfinchFactory,
CreditDesk,
Fidu,
USDC,
TreasuryReserve,
ProtocolAdmin,
OneInch,
TrustedForwarder,
CUSDCContract,
GoldfinchConfig,
PoolTokens,
TranchedPoolImplementation,
SeniorPool,
SeniorPoolStrategy,
MigratedTranchedPoolImplementation,
BorrowerImplementation
}
function getNumberName(uint256 number) public pure returns (string memory) {
Numbers numberName = Numbers(number);
if (Numbers.TransactionLimit == numberName) {
return "TransactionLimit";
}
if (Numbers.TotalFundsLimit == numberName) {
return "TotalFundsLimit";
}
if (Numbers.MaxUnderwriterLimit == numberName) {
return "MaxUnderwriterLimit";
}
if (Numbers.ReserveDenominator == numberName) {
return "ReserveDenominator";
}
if (Numbers.WithdrawFeeDenominator == numberName) {
return "WithdrawFeeDenominator";
}
if (Numbers.LatenessGracePeriodInDays == numberName) {
return "LatenessGracePeriodInDays";
}
if (Numbers.LatenessMaxDays == numberName) {
return "LatenessMaxDays";
}
if (Numbers.DrawdownPeriodInSeconds == numberName) {
return "DrawdownPeriodInSeconds";
}
if (Numbers.TransferRestrictionPeriodInDays == numberName) {
return "TransferRestrictionPeriodInDays";
}
if (Numbers.LeverageRatio == numberName) {
return "LeverageRatio";
}
revert("Unknown value passed to getNumberName");
}
function getAddressName(uint256 addressKey) public pure returns (string memory) {
Addresses addressName = Addresses(addressKey);
if (Addresses.Pool == addressName) {
return "Pool";
}
if (Addresses.CreditLineImplementation == addressName) {
return "CreditLineImplementation";
}
if (Addresses.GoldfinchFactory == addressName) {
return "GoldfinchFactory";
}
if (Addresses.CreditDesk == addressName) {
return "CreditDesk";
}
if (Addresses.Fidu == addressName) {
return "Fidu";
}
if (Addresses.USDC == addressName) {
return "USDC";
}
if (Addresses.TreasuryReserve == addressName) {
return "TreasuryReserve";
}
if (Addresses.ProtocolAdmin == addressName) {
return "ProtocolAdmin";
}
if (Addresses.OneInch == addressName) {
return "OneInch";
}
if (Addresses.TrustedForwarder == addressName) {
return "TrustedForwarder";
}
if (Addresses.CUSDCContract == addressName) {
return "CUSDCContract";
}
if (Addresses.PoolTokens == addressName) {
return "PoolTokens";
}
if (Addresses.TranchedPoolImplementation == addressName) {
return "TranchedPoolImplementation";
}
if (Addresses.SeniorPool == addressName) {
return "SeniorPool";
}
if (Addresses.SeniorPoolStrategy == addressName) {
return "SeniorPoolStrategy";
}
if (Addresses.MigratedTranchedPoolImplementation == addressName) {
return "MigratedTranchedPoolImplementation";
}
if (Addresses.BorrowerImplementation == addressName) {
return "BorrowerImplementation";
}
revert("Unknown value passed to getAddressName");
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
pragma experimental ABIEncoderV2;
import "./BaseUpgradeablePausable.sol";
import "../../interfaces/IGoldfinchConfig.sol";
import "./ConfigOptions.sol";
/**
* @title GoldfinchConfig
* @notice This contract stores mappings of useful "protocol config state", giving a central place
* for all other contracts to access it. For example, the TransactionLimit, or the PoolAddress. These config vars
* are enumerated in the `ConfigOptions` library, and can only be changed by admins of the protocol.
* Note: While this inherits from BaseUpgradeablePausable, it is not deployed as an upgradeable contract (this
* is mostly to save gas costs of having each call go through a proxy)
* @author Goldfinch
*/
contract GoldfinchConfig is BaseUpgradeablePausable {
bytes32 public constant GO_LISTER_ROLE = keccak256("GO_LISTER_ROLE");
mapping(uint256 => address) public addresses;
mapping(uint256 => uint256) public numbers;
mapping(address => bool) public goList;
event AddressUpdated(address owner, uint256 index, address oldValue, address newValue);
event NumberUpdated(address owner, uint256 index, uint256 oldValue, uint256 newValue);
event GoListed(address indexed member);
event NoListed(address indexed member);
bool public valuesInitialized;
function initialize(address owner) public initializer {
require(owner != address(0), "Owner address cannot be empty");
__BaseUpgradeablePausable__init(owner);
_setupRole(GO_LISTER_ROLE, owner);
_setRoleAdmin(GO_LISTER_ROLE, OWNER_ROLE);
}
function setAddress(uint256 addressIndex, address newAddress) public onlyAdmin {
require(addresses[addressIndex] == address(0), "Address has already been initialized");
emit AddressUpdated(msg.sender, addressIndex, addresses[addressIndex], newAddress);
addresses[addressIndex] = newAddress;
}
function setNumber(uint256 index, uint256 newNumber) public onlyAdmin {
emit NumberUpdated(msg.sender, index, numbers[index], newNumber);
numbers[index] = newNumber;
}
function setTreasuryReserve(address newTreasuryReserve) public onlyAdmin {
uint256 key = uint256(ConfigOptions.Addresses.TreasuryReserve);
emit AddressUpdated(msg.sender, key, addresses[key], newTreasuryReserve);
addresses[key] = newTreasuryReserve;
}
function setSeniorPoolStrategy(address newStrategy) public onlyAdmin {
uint256 key = uint256(ConfigOptions.Addresses.SeniorPoolStrategy);
emit AddressUpdated(msg.sender, key, addresses[key], newStrategy);
addresses[key] = newStrategy;
}
function setCreditLineImplementation(address newAddress) public onlyAdmin {
uint256 key = uint256(ConfigOptions.Addresses.CreditLineImplementation);
emit AddressUpdated(msg.sender, key, addresses[key], newAddress);
addresses[key] = newAddress;
}
function setBorrowerImplementation(address newAddress) public onlyAdmin {
uint256 key = uint256(ConfigOptions.Addresses.BorrowerImplementation);
emit AddressUpdated(msg.sender, key, addresses[key], newAddress);
addresses[key] = newAddress;
}
function setGoldfinchConfig(address newAddress) public onlyAdmin {
uint256 key = uint256(ConfigOptions.Addresses.GoldfinchConfig);
emit AddressUpdated(msg.sender, key, addresses[key], newAddress);
addresses[key] = newAddress;
}
function initializeFromOtherConfig(address _initialConfig) public onlyAdmin {
require(!valuesInitialized, "Already initialized values");
IGoldfinchConfig initialConfig = IGoldfinchConfig(_initialConfig);
for (uint256 i = 0; i < 10; i++) {
setNumber(i, initialConfig.getNumber(i));
}
for (uint256 i = 0; i < 11; i++) {
if (getAddress(i) == address(0)) {
setAddress(i, initialConfig.getAddress(i));
}
}
valuesInitialized = true;
}
/**
* @dev Adds a user to go-list
* @param _member address to add to go-list
*/
function addToGoList(address _member) public onlyGoListerRole {
goList[_member] = true;
emit GoListed(_member);
}
/**
* @dev removes a user from go-list
* @param _member address to remove from go-list
*/
function removeFromGoList(address _member) public onlyGoListerRole {
goList[_member] = false;
emit NoListed(_member);
}
/**
* @dev adds many users to go-list at once
* @param _members addresses to ad to go-list
*/
function bulkAddToGoList(address[] calldata _members) external onlyGoListerRole {
for (uint256 i = 0; i < _members.length; i++) {
addToGoList(_members[i]);
}
}
/**
* @dev removes many users from go-list at once
* @param _members addresses to remove from go-list
*/
function bulkRemoveFromGoList(address[] calldata _members) external onlyGoListerRole {
for (uint256 i = 0; i < _members.length; i++) {
removeFromGoList(_members[i]);
}
}
/*
Using custom getters in case we want to change underlying implementation later,
or add checks or validations later on.
*/
function getAddress(uint256 index) public view returns (address) {
return addresses[index];
}
function getNumber(uint256 index) public view returns (uint256) {
return numbers[index];
}
modifier onlyGoListerRole() {
require(hasRole(GO_LISTER_ROLE, _msgSender()), "Must have go-lister role to perform this action");
_;
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
pragma experimental ABIEncoderV2;
import "@openzeppelin/contracts-ethereum-package/contracts/utils/Pausable.sol";
import "@openzeppelin/contracts-ethereum-package/contracts/access/AccessControl.sol";
/**
* @title PauserPausable
* @notice Inheriting from OpenZeppelin's Pausable contract, this does small
* augmentations to make it work with a PAUSER_ROLE, leveraging the AccessControl contract.
* It is meant to be inherited.
* @author Goldfinch
*/
contract PauserPausable is AccessControlUpgradeSafe, PausableUpgradeSafe {
bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
// solhint-disable-next-line func-name-mixedcase
function __PauserPausable__init() public initializer {
__Pausable_init_unchained();
}
/**
* @dev Pauses all functions guarded by Pause
*
* See {Pausable-_pause}.
*
* Requirements:
*
* - the caller must have the PAUSER_ROLE.
*/
function pause() public onlyPauserRole {
_pause();
}
/**
* @dev Unpauses the contract
*
* See {Pausable-_unpause}.
*
* Requirements:
*
* - the caller must have the Pauser role
*/
function unpause() public onlyPauserRole {
_unpause();
}
modifier onlyPauserRole() {
require(hasRole(PAUSER_ROLE, _msgSender()), "Must have pauser role to perform this action");
_;
}
}{
"evmVersion": "istanbul",
"libraries": {},
"metadata": {
"bytecodeHash": "ipfs",
"useLiteralContent": true
},
"optimizer": {
"enabled": true,
"runs": 200
},
"remappings": [],
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"abi"
]
}
}
}Contract ABI
API[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldCreditLine","type":"address"},{"indexed":true,"internalType":"address","name":"newCreditLine","type":"address"}],"name":"CreditLineMigrated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"uint256","name":"tranche","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"DepositMade","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"borrower","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"DrawdownMade","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pool","type":"address"}],"name":"DrawdownsPaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pool","type":"address"}],"name":"DrawdownsUnpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pool","type":"address"}],"name":"EmergencyShutdown","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"payer","type":"address"},{"indexed":true,"internalType":"address","name":"pool","type":"address"},{"indexed":false,"internalType":"uint256","name":"interestAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"principalAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"remainingAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"reserveAmount","type":"uint256"}],"name":"PaymentApplied","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"ReserveFundsCollected","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"pool","type":"address"},{"indexed":true,"internalType":"uint256","name":"tranche","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"principalSharePrice","type":"uint256"},{"indexed":false,"internalType":"int256","name":"principalDelta","type":"int256"},{"indexed":false,"internalType":"uint256","name":"interestSharePrice","type":"uint256"},{"indexed":false,"internalType":"int256","name":"interestDelta","type":"int256"}],"name":"SharePriceUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"uint256","name":"tranche","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"tokenId","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"interestWithdrawn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"principalWithdrawn","type":"uint256"}],"name":"WithdrawalMade","type":"event"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FP_SCALING_FACTOR","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"LOCKER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ONE_HUNDRED","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"OWNER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PAUSER_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SECONDS_PER_DAY","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"__BaseUpgradeablePausable__init","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"__PauserPausable__init","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"assess","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"availableToWithdraw","outputs":[{"internalType":"uint256","name":"interestRedeemable","type":"uint256"},{"internalType":"uint256","name":"principalRedeemable","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"borrower","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"config","outputs":[{"internalType":"contract GoldfinchConfig","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"createdAt","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"creditLine","outputs":[{"internalType":"contract IV2CreditLine","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tranche","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"deposit","outputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tranche","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"depositWithPermit","outputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"drawdown","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"drawdownsPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"emergencyShutdown","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"uint256","name":"index","type":"uint256"}],"name":"getRoleMember","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleMemberCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tranche","type":"uint256"}],"name":"getTranche","outputs":[{"components":[{"internalType":"uint256","name":"id","type":"uint256"},{"internalType":"uint256","name":"principalDeposited","type":"uint256"},{"internalType":"uint256","name":"principalSharePrice","type":"uint256"},{"internalType":"uint256","name":"interestSharePrice","type":"uint256"},{"internalType":"uint256","name":"lockedUntil","type":"uint256"}],"internalType":"struct ITranchedPool.TrancheInfo","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_config","type":"address"},{"internalType":"address","name":"_borrower","type":"address"},{"internalType":"uint256","name":"_juniorFeePercent","type":"uint256"},{"internalType":"uint256","name":"_limit","type":"uint256"},{"internalType":"uint256","name":"_interestApr","type":"uint256"},{"internalType":"uint256","name":"_paymentPeriodInDays","type":"uint256"},{"internalType":"uint256","name":"_termInDays","type":"uint256"},{"internalType":"uint256","name":"_lateFeeApr","type":"uint256"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"interestApr","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isAdmin","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"juniorFeePercent","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lateFeeApr","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"limit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"lockJuniorCapital","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"lockPool","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newCl","type":"address"}],"name":"migrateAndSetNewCreditLine","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_borrower","type":"address"},{"internalType":"uint256","name":"_limit","type":"uint256"},{"internalType":"uint256","name":"_interestApr","type":"uint256"},{"internalType":"uint256","name":"_paymentPeriodInDays","type":"uint256"},{"internalType":"uint256","name":"_termInDays","type":"uint256"},{"internalType":"uint256","name":"_lateFeeApr","type":"uint256"}],"name":"migrateCreditLine","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"pauseDrawdowns","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"pay","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paymentPeriodInDays","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"sharePrice","type":"uint256"},{"internalType":"uint256","name":"totalShares","type":"uint256"}],"name":"sharePriceToUsdc","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"termInDays","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpauseDrawdowns","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"updateGoldfinchConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"totalShares","type":"uint256"}],"name":"usdcToSharePrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"withdraw","outputs":[{"internalType":"uint256","name":"interestWithdrawn","type":"uint256"},{"internalType":"uint256","name":"principalWithdrawn","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenId","type":"uint256"}],"name":"withdrawMax","outputs":[{"internalType":"uint256","name":"interestWithdrawn","type":"uint256"},{"internalType":"uint256","name":"principalWithdrawn","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenIds","type":"uint256[]"},{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"name":"withdrawMultiple","outputs":[],"stateMutability":"nonpayable","type":"function"}]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.