Transaction Hash:
Block:
12367755 at May-04-2021 12:30:29 PM +UTC
Transaction Fee:
0.002254604 ETH
$4.46
Gas Used:
51,241 Gas / 44 Gwei
Emitted Events:
| 386 |
OwnedUpgradeabilityProxy.0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925( 0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925, 0x000000000000000000000000c3a2735456c975f4a1694175d1f204a8de0fbf09, 0x0000000000000000000000008fd832757f58f71bac53196270a4a55c8e1a29d9, 0000000000000000000000000000000000000000000000efbe631dcea09a4bce )
|
Account State Difference:
| Address | Before | After | State Difference | ||
|---|---|---|---|---|---|
|
0x4F9bEBE3...9dfFA52d5
Miner
| (Crazy Pool) | 49.152180203487037537 Eth | 49.154434807487037537 Eth | 0.002254604 | |
| 0xa1e72267...E470e4149 | |||||
| 0xC3A27354...8DE0Fbf09 |
0.387305386440826942 Eth
Nonce: 10
|
0.385050782440826942 Eth
Nonce: 11
| 0.002254604 |
Execution Trace
OwnedUpgradeabilityProxy.095ea7b3( )
-
TrueFiPool.approve( spender=0x8FD832757F58F71BAC53196270A4a55c8E1a29D9, amount=4422490675279778433998 ) => ( True )
approve[ERC20 (ln:711)]
_approve[ERC20 (ln:712)]Approval[ERC20 (ln:869)]
_msgSender[ERC20 (ln:712)]
File 1 of 2: OwnedUpgradeabilityProxy
File 2 of 2: TrueFiPool
/*
.'''''''''''.. ..''''''''''''''''.. ..'''''''''''''''..
.;;;;;;;;;;;'. .';;;;;;;;;;;;;;;;;;,. .,;;;;;;;;;;;;;;;;;,.
.;;;;;;;;;;,. .,;;;;;;;;;;;;;;;;;;;,. .,;;;;;;;;;;;;;;;;;;,.
.;;;;;;;;;,. .,;;;;;;;;;;;;;;;;;;;;,. .;;;;;;;;;;;;;;;;;;;;,.
';;;;;;;;'. .';;;;;;;;;;;;;;;;;;;;;;,. .';;;;;;;;;;;;;;;;;;;;;,.
';;;;;,.. .';;;;;;;;;;;;;;;;;;;;;;;,..';;;;;;;;;;;;;;;;;;;;;;,.
...... .';;;;;;;;;;;;;,'''''''''''.,;;;;;;;;;;;;;,'''''''''..
.,;;;;;;;;;;;;;. .,;;;;;;;;;;;;;.
.,;;;;;;;;;;;;,. .,;;;;;;;;;;;;,.
.,;;;;;;;;;;;;,. .,;;;;;;;;;;;;,.
.,;;;;;;;;;;;;,. .;;;;;;;;;;;;;,. .....
.;;;;;;;;;;;;;'. ..';;;;;;;;;;;;;'. .',;;;;,'.
.';;;;;;;;;;;;;'. .';;;;;;;;;;;;;;'. .';;;;;;;;;;.
.';;;;;;;;;;;;;'. .';;;;;;;;;;;;;;'. .;;;;;;;;;;;,.
.,;;;;;;;;;;;;;'...........,;;;;;;;;;;;;;;. .;;;;;;;;;;;,.
.,;;;;;;;;;;;;,..,;;;;;;;;;;;;;;;;;;;;;;;,. ..;;;;;;;;;,.
.,;;;;;;;;;;;;,. .,;;;;;;;;;;;;;;;;;;;;;;,. .',;;;,,..
.,;;;;;;;;;;;;,. .,;;;;;;;;;;;;;;;;;;;;;,. ....
..',;;;;;;;;,. .,;;;;;;;;;;;;;;;;;;;;,.
..',;;;;'. .,;;;;;;;;;;;;;;;;;;;'.
...'.. .';;;;;;;;;;;;;;,,,'.
...............
*/
// https://github.com/trusttoken/smart-contracts
// SPDX-License-Identifier: MIT
// File: contracts/proxy/OwnedUpgradeabilityProxy.sol
pragma solidity 0.6.10;
/**
* @title OwnedUpgradeabilityProxy
* @dev This contract combines an upgradeability proxy with basic authorization control functionalities
*/
contract OwnedUpgradeabilityProxy {
/**
* @dev Event to show ownership has been transferred
* @param previousOwner representing the address of the previous owner
* @param newOwner representing the address of the new owner
*/
event ProxyOwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Event to show ownership transfer is pending
* @param currentOwner representing the address of the current owner
* @param pendingOwner representing the address of the pending owner
*/
event NewPendingOwner(address currentOwner, address pendingOwner);
// Storage position of the owner and pendingOwner of the contract
bytes32 private constant proxyOwnerPosition = 0x6279e8199720cf3557ecd8b58d667c8edc486bd1cf3ad59ea9ebdfcae0d0dfac; //keccak256("trueUSD.proxy.owner");
bytes32 private constant pendingProxyOwnerPosition = 0x8ddbac328deee8d986ec3a7b933a196f96986cb4ee030d86cc56431c728b83f4; //keccak256("trueUSD.pending.proxy.owner");
/**
* @dev the constructor sets the original owner of the contract to the sender account.
*/
constructor() public {
_setUpgradeabilityOwner(msg.sender);
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyProxyOwner() {
require(msg.sender == proxyOwner(), "only Proxy Owner");
_;
}
/**
* @dev Throws if called by any account other than the pending owner.
*/
modifier onlyPendingProxyOwner() {
require(msg.sender == pendingProxyOwner(), "only pending Proxy Owner");
_;
}
/**
* @dev Tells the address of the owner
* @return owner the address of the owner
*/
function proxyOwner() public view returns (address owner) {
bytes32 position = proxyOwnerPosition;
assembly {
owner := sload(position)
}
}
/**
* @dev Tells the address of the owner
* @return pendingOwner the address of the pending owner
*/
function pendingProxyOwner() public view returns (address pendingOwner) {
bytes32 position = pendingProxyOwnerPosition;
assembly {
pendingOwner := sload(position)
}
}
/**
* @dev Sets the address of the owner
*/
function _setUpgradeabilityOwner(address newProxyOwner) internal {
bytes32 position = proxyOwnerPosition;
assembly {
sstore(position, newProxyOwner)
}
}
/**
* @dev Sets the address of the owner
*/
function _setPendingUpgradeabilityOwner(address newPendingProxyOwner) internal {
bytes32 position = pendingProxyOwnerPosition;
assembly {
sstore(position, newPendingProxyOwner)
}
}
/**
* @dev Allows the current owner to transfer control of the contract to a newOwner.
*changes the pending owner to newOwner. But doesn't actually transfer
* @param newOwner The address to transfer ownership to.
*/
function transferProxyOwnership(address newOwner) external onlyProxyOwner {
require(newOwner != address(0));
_setPendingUpgradeabilityOwner(newOwner);
emit NewPendingOwner(proxyOwner(), newOwner);
}
/**
* @dev Allows the pendingOwner to claim ownership of the proxy
*/
function claimProxyOwnership() external onlyPendingProxyOwner {
emit ProxyOwnershipTransferred(proxyOwner(), pendingProxyOwner());
_setUpgradeabilityOwner(pendingProxyOwner());
_setPendingUpgradeabilityOwner(address(0));
}
/**
* @dev Allows the proxy owner to upgrade the current version of the proxy.
* @param implementation representing the address of the new implementation to be set.
*/
function upgradeTo(address implementation) public virtual onlyProxyOwner {
address currentImplementation;
bytes32 position = implementationPosition;
assembly {
currentImplementation := sload(position)
}
require(currentImplementation != implementation);
assembly {
sstore(position, implementation)
}
emit Upgraded(implementation);
}
/**
* @dev This event will be emitted every time the implementation gets upgraded
* @param implementation representing the address of the upgraded implementation
*/
event Upgraded(address indexed implementation);
// Storage position of the address of the current implementation
bytes32 private constant implementationPosition = 0x6e41e0fbe643dfdb6043698bf865aada82dc46b953f754a3468eaa272a362dc7; //keccak256("trueUSD.proxy.implementation");
function implementation() public view returns (address impl) {
bytes32 position = implementationPosition;
assembly {
impl := sload(position)
}
}
/**
* @dev Fallback functions allowing to perform a delegatecall to the given implementation.
* This function will return whatever the implementation call returns
*/
fallback() external payable {
proxyCall();
}
receive() external payable {
proxyCall();
}
function proxyCall() internal {
bytes32 position = implementationPosition;
assembly {
let ptr := mload(0x40)
calldatacopy(ptr, returndatasize(), calldatasize())
let result := delegatecall(gas(), sload(position), ptr, calldatasize(), returndatasize(), returndatasize())
returndatacopy(ptr, 0, returndatasize())
switch result
case 0 {
revert(ptr, returndatasize())
}
default {
return(ptr, returndatasize())
}
}
}
}File 2 of 2: TrueFiPool
/*
.'''''''''''.. ..''''''''''''''''.. ..'''''''''''''''..
.;;;;;;;;;;;'. .';;;;;;;;;;;;;;;;;;,. .,;;;;;;;;;;;;;;;;;,.
.;;;;;;;;;;,. .,;;;;;;;;;;;;;;;;;;;,. .,;;;;;;;;;;;;;;;;;;,.
.;;;;;;;;;,. .,;;;;;;;;;;;;;;;;;;;;,. .;;;;;;;;;;;;;;;;;;;;,.
';;;;;;;;'. .';;;;;;;;;;;;;;;;;;;;;;,. .';;;;;;;;;;;;;;;;;;;;;,.
';;;;;,.. .';;;;;;;;;;;;;;;;;;;;;;;,..';;;;;;;;;;;;;;;;;;;;;;,.
...... .';;;;;;;;;;;;;,'''''''''''.,;;;;;;;;;;;;;,'''''''''..
.,;;;;;;;;;;;;;. .,;;;;;;;;;;;;;.
.,;;;;;;;;;;;;,. .,;;;;;;;;;;;;,.
.,;;;;;;;;;;;;,. .,;;;;;;;;;;;;,.
.,;;;;;;;;;;;;,. .;;;;;;;;;;;;;,. .....
.;;;;;;;;;;;;;'. ..';;;;;;;;;;;;;'. .',;;;;,'.
.';;;;;;;;;;;;;'. .';;;;;;;;;;;;;;'. .';;;;;;;;;;.
.';;;;;;;;;;;;;'. .';;;;;;;;;;;;;;'. .;;;;;;;;;;;,.
.,;;;;;;;;;;;;;'...........,;;;;;;;;;;;;;;. .;;;;;;;;;;;,.
.,;;;;;;;;;;;;,..,;;;;;;;;;;;;;;;;;;;;;;;,. ..;;;;;;;;;,.
.,;;;;;;;;;;;;,. .,;;;;;;;;;;;;;;;;;;;;;;,. .',;;;,,..
.,;;;;;;;;;;;;,. .,;;;;;;;;;;;;;;;;;;;;;,. ....
..',;;;;;;;;,. .,;;;;;;;;;;;;;;;;;;;;,.
..',;;;;'. .,;;;;;;;;;;;;;;;;;;;'.
...'.. .';;;;;;;;;;;;;;,,,'.
...............
*/
// https://github.com/trusttoken/smart-contracts
// Dependency file: @openzeppelin/contracts/token/ERC20/IERC20.sol
// SPDX-License-Identifier: MIT
// 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);
}
// Dependency file: @openzeppelin/contracts/utils/ReentrancyGuard.sol
// pragma solidity ^0.6.0;
/**
* @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 ReentrancyGuard {
// Booleans are more expensive than uint256 or any type that takes up a full
// word because each write operation emits an extra SLOAD to first read the
// slot's contents, replace the bits taken up by the boolean, and then write
// back. This is the compiler's defense against contract upgrades and
// pointer aliasing, and it cannot be disabled.
// The values being 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 percentage 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.
uint256 private constant _NOT_ENTERED = 1;
uint256 private constant _ENTERED = 2;
uint256 private _status;
constructor () internal {
_status = _NOT_ENTERED;
}
/**
* @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(_status != _ENTERED, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
_status = _ENTERED;
_;
// By storing the original value once again, a refund is triggered (see
// https://eips.ethereum.org/EIPS/eip-2200)
_status = _NOT_ENTERED;
}
}
// Dependency file: @openzeppelin/contracts/math/SafeMath.sol
// 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) {
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;
}
}
// Dependency file: @openzeppelin/contracts/utils/Address.sol
// 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) {
// This method relies in extcodesize, which returns 0 for contracts in
// construction, since the code is only stored at the end of the
// constructor execution.
uint256 size;
// solhint-disable-next-line no-inline-assembly
assembly { size := extcodesize(account) }
return size > 0;
}
/**
* @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");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain`call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCall(target, data, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
return _functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
return _functionCallWithValue(target, data, value, errorMessage);
}
function _functionCallWithValue(address target, bytes memory data, uint256 weiValue, string memory errorMessage) private returns (bytes memory) {
require(isContract(target), "Address: call to non-contract");
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytes memory returndata) = target.call{ value: weiValue }(data);
if (success) {
return returndata;
} else {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
// solhint-disable-next-line no-inline-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}
// Dependency file: @openzeppelin/contracts/GSN/Context.sol
// pragma solidity ^0.6.0;
/*
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with 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.
*/
abstract contract Context {
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;
}
}
// Dependency file: contracts/truefi/common/Initializable.sol
// Copied from https://github.com/OpenZeppelin/openzeppelin-contracts-ethereum-package/blob/v3.0.0/contracts/Initializable.sol
// pragma solidity 0.6.10;
/**
* @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;
}
// Dependency file: contracts/truefi/common/UpgradeableERC20.sol
// pragma solidity 0.6.10;
// import {Address} from "@openzeppelin/contracts/utils/Address.sol";
// import {Context} from "@openzeppelin/contracts/GSN/Context.sol";
// import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
// import {SafeMath} from "@openzeppelin/contracts/math/SafeMath.sol";
// import {Initializable} from "contracts/truefi/common/Initializable.sol";
/**
* @dev Implementation of the {IERC20} interface.
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using {_mint}.
* For a generic mechanism see {ERC20PresetMinterPauser}.
*
* TIP: For a detailed writeup see our guide
* https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms].
*
* We have followed general OpenZeppelin guidelines: functions revert instead
* of returning `false` on failure. This behavior is nonetheless conventional
* and does not conflict with the expectations of ERC20 applications.
*
* Additionally, an {Approval} event is emitted on calls to {transferFrom}.
* This allows applications to reconstruct the allowance for all accounts just
* by listening to said events. Other implementations of the EIP may not emit
* these events, as it isn't required by the specification.
*
* Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
* functions have been added to mitigate the well-known issues around setting
* allowances. See {IERC20-approve}.
*/
contract ERC20 is Initializable, Context, IERC20 {
using SafeMath for uint256;
using Address for address;
mapping(address => uint256) private _balances;
mapping(address => mapping(address => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
uint8 private _decimals;
/**
* @dev Sets the values for {name} and {symbol}, initializes {decimals} with
* a default value of 18.
*
* To select a different value for {decimals}, use {_setupDecimals}.
*
* All three of these values are immutable: they can only be set once during
* construction.
*/
function __ERC20_initialize(string memory name, string memory symbol) internal initializer {
_name = name;
_symbol = symbol;
_decimals = 18;
}
/**
* @dev Returns the name of the token.
*/
function name() public view returns (string memory) {
return _name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public view returns (string memory) {
return _symbol;
}
/**
* @dev Returns the number of decimals used to get its user representation.
* For example, if `decimals` equals `2`, a balance of `505` tokens should
* be displayed to a user as `5,05` (`505 / 10 ** 2`).
*
* Tokens usually opt for a value of 18, imitating the relationship between
* Ether and Wei. This is the value {ERC20} uses, unless {_setupDecimals} is
* called.
*
* NOTE: This information is only used for _display_ purposes: it in
* no way affects any of the arithmetic of the contract, including
* {IERC20-balanceOf} and {IERC20-transfer}.
*/
function decimals() public view returns (uint8) {
return _decimals;
}
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() public override view returns (uint256) {
return _totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(address account) public override view returns (uint256) {
return _balances[account];
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `recipient` cannot be the zero address.
* - the caller must have a balance of at least `amount`.
*/
function transfer(address recipient, uint256 amount) public virtual override returns (bool) {
_transfer(_msgSender(), recipient, amount);
return true;
}
/**
* @dev See {IERC20-allowance}.
*/
function allowance(address owner, address spender) public virtual override view returns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function approve(address spender, uint256 amount) public virtual override returns (bool) {
_approve(_msgSender(), spender, amount);
return true;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Emits an {Approval} event indicating the updated allowance. This is not
* required by the EIP. See the note at the beginning of {ERC20};
*
* Requirements:
* - `sender` and `recipient` cannot be the zero address.
* - `sender` must have a balance of at least `amount`.
* - the caller must have allowance for ``sender``'s tokens of at least
* `amount`.
*/
function transferFrom(
address sender,
address recipient,
uint256 amount
) public virtual override returns (bool) {
_transfer(sender, recipient, amount);
_approve(sender, _msgSender(), _allowances[sender][_msgSender()].sub(amount, "ERC20: transfer amount exceeds allowance"));
return true;
}
/**
* @dev Atomically increases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
_approve(_msgSender(), spender, _allowances[_msgSender()][spender].add(addedValue));
return true;
}
/**
* @dev Atomically decreases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `spender` must have allowance for the caller of at least
* `subtractedValue`.
*/
function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
_approve(
_msgSender(),
spender,
_allowances[_msgSender()][spender].sub(subtractedValue, "ERC20: decreased allowance below zero")
);
return true;
}
/**
* @dev Moves tokens `amount` from `sender` to `recipient`.
*
* This is internal function is equivalent to {transfer}, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a {Transfer} event.
*
* Requirements:
*
* - `sender` cannot be the zero address.
* - `recipient` cannot be the zero address.
* - `sender` must have a balance of at least `amount`.
*/
function _transfer(
address sender,
address recipient,
uint256 amount
) internal virtual {
require(sender != address(0), "ERC20: transfer from the zero address");
require(recipient != address(0), "ERC20: transfer to the zero address");
_beforeTokenTransfer(sender, recipient, amount);
_balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
_balances[recipient] = _balances[recipient].add(amount);
emit Transfer(sender, recipient, amount);
}
/** @dev Creates `amount` tokens and assigns them to `account`, increasing
* the total supply.
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* Requirements
*
* - `to` cannot be the zero address.
*/
function _mint(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: mint to the zero address");
_beforeTokenTransfer(address(0), account, amount);
_totalSupply = _totalSupply.add(amount);
_balances[account] = _balances[account].add(amount);
emit Transfer(address(0), account, amount);
}
/**
* @dev Destroys `amount` tokens from `account`, reducing the
* total supply.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* Requirements
*
* - `account` cannot be the zero address.
* - `account` must have at least `amount` tokens.
*/
function _burn(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: burn from the zero address");
_beforeTokenTransfer(account, address(0), amount);
_balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance");
_totalSupply = _totalSupply.sub(amount);
emit Transfer(account, address(0), amount);
}
/**
* @dev Sets `amount` as the allowance of `spender` over the `owner`s tokens.
*
* This is internal function is equivalent to `approve`, and can be used to
* e.g. set automatic allowances for certain subsystems, etc.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*/
function _approve(
address owner,
address spender,
uint256 amount
) internal virtual {
require(owner != address(0), "ERC20: approve from the zero address");
require(spender != address(0), "ERC20: approve to the zero address");
_allowances[owner][spender] = amount;
emit Approval(owner, spender, amount);
}
/**
* @dev Sets {decimals} to a value other than the default one of 18.
*
* WARNING: This function should only be called from the constructor. Most
* applications that interact with token contracts will not expect
* {decimals} to ever change, and may work incorrectly if it does.
*/
function _setupDecimals(uint8 decimals_) internal {
_decimals = decimals_;
}
/**
* @dev Hook that is called before any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* will be to transferred to `to`.
* - when `from` is zero, `amount` tokens will be minted for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens will be burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _beforeTokenTransfer(
address from,
address to,
uint256 amount
) internal virtual {}
function updateNameAndSymbol(string memory __name, string memory __symbol) internal {
_name = __name;
_symbol = __symbol;
}
}
// Dependency file: contracts/truefi/common/UpgradeableOwnable.sol
// pragma solidity 0.6.10;
// import {Context} from "@openzeppelin/contracts/GSN/Context.sol";
// import {Initializable} from "contracts/truefi/common/Initializable.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
contract Ownable is Initializable, Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
function initialize() internal initializer {
address msgSender = _msgSender();
_owner = msgSender;
emit OwnershipTransferred(address(0), msgSender);
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view returns (address) {
return _owner;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(_owner == _msgSender(), "Ownable: caller is not the owner");
_;
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions anymore. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby removing any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
emit OwnershipTransferred(_owner, address(0));
_owner = address(0);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
emit OwnershipTransferred(_owner, newOwner);
_owner = newOwner;
}
}
// Dependency file: contracts/truefi/interface/IYToken.sol
// pragma solidity 0.6.10;
// import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
interface IYToken is IERC20 {
function getPricePerFullShare() external view returns (uint256);
}
// Dependency file: contracts/truefi/interface/ICurve.sol
// pragma solidity 0.6.10;
// import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
// import {IYToken} from "contracts/truefi/interface/IYToken.sol";
interface ICurve {
function calc_token_amount(uint256[4] memory amounts, bool deposit) external view returns (uint256);
function get_virtual_price() external view returns (uint256);
}
interface ICurveGauge {
function balanceOf(address depositor) external view returns (uint256);
function minter() external returns (ICurveMinter);
function deposit(uint256 amount) external;
function withdraw(uint256 amount) external;
}
interface ICurveMinter {
function mint(address gauge) external;
function token() external view returns (IERC20);
}
interface ICurvePool {
function add_liquidity(uint256[4] memory amounts, uint256 min_mint_amount) external;
function remove_liquidity_one_coin(
uint256 _token_amount,
int128 i,
uint256 min_amount,
bool donate_dust
) external;
function calc_withdraw_one_coin(uint256 _token_amount, int128 i) external view returns (uint256);
function token() external view returns (IERC20);
function curve() external view returns (ICurve);
function coins(int128 id) external view returns (IYToken);
}
// Dependency file: contracts/truefi/interface/ITrueFiPool.sol
// pragma solidity 0.6.10;
// import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
/**
* TruePool is an ERC20 which represents a share of a pool
*
* This contract can be used to wrap opportunities to be compatible
* with TrueFi and allow users to directly opt-in through the TUSD contract
*
* Each TruePool is also a staking opportunity for TRU
*/
interface ITrueFiPool is IERC20 {
/// @dev pool token (TUSD)
function currencyToken() external view returns (IERC20);
/// @dev stake token (TRU)
function stakeToken() external view returns (IERC20);
/**
* @dev join pool
* 1. Transfer TUSD from sender
* 2. Mint pool tokens based on value to sender
*/
function join(uint256 amount) external;
/**
* @dev exit pool
* 1. Transfer pool tokens from sender
* 2. Burn pool tokens
* 3. Transfer value of pool tokens in TUSD to sender
*/
function exit(uint256 amount) external;
/**
* @dev borrow from pool
* 1. Transfer TUSD to sender
* 2. Only lending pool should be allowed to call this
*/
function borrow(uint256 amount, uint256 fee) external;
/**
* @dev join pool
* 1. Transfer TUSD from sender
* 2. Only lending pool should be allowed to call this
*/
function repay(uint256 amount) external;
}
// Dependency file: contracts/truefi/interface/ITrueLender.sol
// pragma solidity 0.6.10;
interface ITrueLender {
function value() external view returns (uint256);
function distribute(
address recipient,
uint256 numerator,
uint256 denominator
) external;
}
// Dependency file: contracts/truefi/interface/IUniswapRouter.sol
// pragma solidity 0.6.10;
interface IUniswapRouter {
function swapExactTokensForTokens(
uint256 amountIn,
uint256 amountOutMin,
address[] calldata path,
address to,
uint256 deadline
) external returns (uint256[] memory amounts);
}
// Dependency file: contracts/truefi/Log.sol
/*
* ABDK Math 64.64 Smart Contract Library. Copyright © 2019 by ABDK Consulting.
* Author: Mikhail Vladimirov <mikhail.vladimirov@gmail.com>
*/
// pragma solidity 0.6.10;
/**
* Smart contract library of mathematical functions operating with signed
* 64.64-bit fixed point numbers. Signed 64.64-bit fixed point number is
* basically a simple fraction whose numerator is signed 128-bit integer and
* denominator is 2^64. As long as denominator is always the same, there is no
* need to store it, thus in Solidity signed 64.64-bit fixed point numbers are
* represented by int128 type holding only the numerator.
*/
library ABDKMath64x64 {
/**
* Convert unsigned 256-bit integer number into signed 64.64-bit fixed point
* number. Revert on overflow.
*
* @param x unsigned 256-bit integer number
* @return signed 64.64-bit fixed point number
*/
function fromUInt(uint256 x) internal pure returns (int128) {
require(x <= 0x7FFFFFFFFFFFFFFF);
return int128(x << 64);
}
/**
* Calculate binary logarithm of x. Revert if x <= 0.
*
* @param x signed 64.64-bit fixed point number
* @return signed 64.64-bit fixed point number
*/
function log_2(int128 x) internal pure returns (int128) {
require(x > 0);
int256 msb = 0;
int256 xc = x;
if (xc >= 0x10000000000000000) {
xc >>= 64;
msb += 64;
}
if (xc >= 0x100000000) {
xc >>= 32;
msb += 32;
}
if (xc >= 0x10000) {
xc >>= 16;
msb += 16;
}
if (xc >= 0x100) {
xc >>= 8;
msb += 8;
}
if (xc >= 0x10) {
xc >>= 4;
msb += 4;
}
if (xc >= 0x4) {
xc >>= 2;
msb += 2;
}
if (xc >= 0x2) msb += 1; // No need to shift xc anymore
int256 result = (msb - 64) << 64;
uint256 ux = uint256(x) << uint256(127 - msb);
for (int256 bit = 0x8000000000000000; bit > 0; bit >>= 1) {
ux *= ux;
uint256 b = ux >> 255;
ux >>= 127 + b;
result += bit * int256(b);
}
return int128(result);
}
/**
* Calculate natural logarithm of x. Revert if x <= 0.
*
* @param x signed 64.64-bit fixed point number
* @return signed 64.64-bit fixed point number
*/
function ln(int128 x) internal pure returns (int128) {
require(x > 0);
return int128((uint256(log_2(x)) * 0xB17217F7D1CF79ABC9E3B39803F2F6AF) >> 128);
}
}
// Dependency file: contracts/truefi/interface/ITruPriceOracle.sol
// pragma solidity 0.6.10;
interface ITruPriceOracle {
function usdToTru(uint256 amount) external view returns (uint256);
function truToUsd(uint256 amount) external view returns (uint256);
}
// Root file: contracts/truefi/TrueFiPool.sol
pragma solidity 0.6.10;
// import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
// import {ReentrancyGuard} from "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
// import {SafeMath} from "@openzeppelin/contracts/math/SafeMath.sol";
// import {ERC20} from "contracts/truefi/common/UpgradeableERC20.sol";
// import {Ownable} from "contracts/truefi/common/UpgradeableOwnable.sol";
// import {ICurveGauge, ICurveMinter, ICurvePool} from "contracts/truefi/interface/ICurve.sol";
// import {ITrueFiPool} from "contracts/truefi/interface/ITrueFiPool.sol";
// import {ITrueLender} from "contracts/truefi/interface/ITrueLender.sol";
// import {IUniswapRouter} from "contracts/truefi/interface/IUniswapRouter.sol";
// import {ABDKMath64x64} from "contracts/truefi/Log.sol";
// import {ITruPriceOracle} from "contracts/truefi/interface/ITruPriceOracle.sol";
/**
* @title TrueFi Pool
* @dev Lending pool which uses curve.fi to store idle funds
* Earn high interest rates on currency deposits through uncollateralized loans
*
* Funds deposited in this pool are not fully liquid. Luqidity
* Exiting the pool has 2 options:
* - withdraw a basket of LoanTokens backing the pool
* - take an exit penallty depending on pool liquidity
* After exiting, an account will need to wait for LoanTokens to expire and burn them
* It is recommended to perform a zap or swap tokens on Uniswap for increased liquidity
*
* Funds are managed through an external function to save gas on deposits
*/
contract TrueFiPool is ITrueFiPool, ERC20, ReentrancyGuard, Ownable {
using SafeMath for uint256;
// ================ WARNING ==================
// ===== THIS CONTRACT IS INITIALIZABLE ======
// === STORAGE VARIABLES ARE DECLARED BELOW ==
// REMOVAL OR REORDER OF VARIABLES WILL RESULT
// ========= IN STORAGE CORRUPTION ===========
ICurvePool public _curvePool;
ICurveGauge public _curveGauge;
IERC20 public _currencyToken;
ITrueLender public _lender;
ICurveMinter public _minter;
IUniswapRouter public _uniRouter;
// fee for deposits
uint256 public joiningFee;
// track claimable fees
uint256 public claimableFees;
mapping(address => uint256) latestJoinBlock;
IERC20 public _stakeToken;
// cache values during sync for gas optimization
bool private inSync;
uint256 private yTokenValueCache;
uint256 private loansValueCache;
// TRU price oracle
ITruPriceOracle public _oracle;
// fund manager can call functions to help manage pool funds
// fund manager can be set to 0 or governance
address public fundsManager;
// allow pausing of deposits
bool public isJoiningPaused;
// ======= STORAGE DECLARATION END ============
// curve.fi data
uint8 constant N_TOKENS = 4;
uint8 constant TUSD_INDEX = 3;
/**
* @dev Emitted when stake token address
* @param token New stake token address
*/
event StakeTokenChanged(IERC20 token);
/**
* @dev Emitted oracle was changed
* @param newOracle New oracle address
*/
event OracleChanged(ITruPriceOracle newOracle);
/**
* @dev Emitted when funds manager is changed
* @param newManager New manager address
*/
event FundsManagerChanged(address newManager);
/**
* @dev Emitted when fee is changed
* @param newFee New fee
*/
event JoiningFeeChanged(uint256 newFee);
/**
* @dev Emitted when someone joins the pool
* @param staker Account staking
* @param deposited Amount deposited
* @param minted Amount of pool tokens minted
*/
event Joined(address indexed staker, uint256 deposited, uint256 minted);
/**
* @dev Emitted when someone exits the pool
* @param staker Account exiting
* @param amount Amount unstaking
*/
event Exited(address indexed staker, uint256 amount);
/**
* @dev Emitted when funds are flushed into curve.fi
* @param currencyAmount Amount of tokens deposited
*/
event Flushed(uint256 currencyAmount);
/**
* @dev Emitted when funds are pulled from curve.fi
* @param yAmount Amount of pool tokens
*/
event Pulled(uint256 yAmount);
/**
* @dev Emitted when funds are borrowed from pool
* @param borrower Borrower address
* @param amount Amount of funds borrowed from pool
* @param fee Fees collected from this transaction
*/
event Borrow(address borrower, uint256 amount, uint256 fee);
/**
* @dev Emitted when borrower repays the pool
* @param payer Address of borrower
* @param amount Amount repaid
*/
event Repaid(address indexed payer, uint256 amount);
/**
* @dev Emitted when fees are collected
* @param beneficiary Account to receive fees
* @param amount Amount of fees collected
*/
event Collected(address indexed beneficiary, uint256 amount);
/**
* @dev Emitted when joining is paused or unpaused
* @param isJoiningPaused New pausing status
*/
event JoiningPauseStatusChanged(bool isJoiningPaused);
/**
* @dev Initialize pool
* @param __curvePool curve pool address
* @param __curveGauge curve gauge address
* @param __currencyToken curve pool underlying token
* @param __lender TrueLender address
* @param __uniRouter Uniswap router
*/
function initialize(
ICurvePool __curvePool,
ICurveGauge __curveGauge,
IERC20 __currencyToken,
ITrueLender __lender,
IUniswapRouter __uniRouter,
IERC20 __stakeToken,
ITruPriceOracle __oracle
) public initializer {
ERC20.__ERC20_initialize("TrueFi LP", "TFI-LP");
Ownable.initialize();
_curvePool = __curvePool;
_curveGauge = __curveGauge;
_currencyToken = __currencyToken;
_lender = __lender;
_minter = _curveGauge.minter();
_uniRouter = __uniRouter;
_stakeToken = __stakeToken;
_oracle = __oracle;
joiningFee = 25;
}
/**
* @dev only lender can perform borrowing or repaying
*/
modifier onlyLender() {
require(msg.sender == address(_lender), "TrueFiPool: Caller is not the lender");
_;
}
/**
* @dev pool can only be joined when it's unpaused
*/
modifier joiningNotPaused() {
require(!isJoiningPaused, "TrueFiPool: Joining the pool is paused");
_;
}
/**
* @dev only lender can perform borrowing or repaying
*/
modifier onlyOwnerOrManager() {
require(msg.sender == owner() || msg.sender == fundsManager, "TrueFiPool: Caller is neither owner nor funds manager");
_;
}
/**
* Sync values to avoid making expensive calls multiple times
* Will set inSync to true, allowing getter functions to return cached values
* Wipes cached values to save gas
*/
modifier sync() {
// sync
yTokenValueCache = yTokenValue();
loansValueCache = loansValue();
inSync = true;
_;
// wipe
inSync = false;
yTokenValueCache = 0;
loansValueCache = 0;
}
/**
* @dev get currency token address
* @return currency token address
*/
function currencyToken() public override view returns (IERC20) {
return _currencyToken;
}
/**
* @dev get stake token address
* @return stake token address
*/
function stakeToken() public override view returns (IERC20) {
return _stakeToken;
}
/**
* @dev set stake token address
* @param token stake token address
*/
function setStakeToken(IERC20 token) public onlyOwner {
_stakeToken = token;
emit StakeTokenChanged(token);
}
/**
* @dev set funds manager address
*/
function setFundsManager(address newFundsManager) public onlyOwner {
fundsManager = newFundsManager;
emit FundsManagerChanged(newFundsManager);
}
/**
* @dev set oracle token address
* @param newOracle new oracle address
*/
function setOracle(ITruPriceOracle newOracle) public onlyOwner {
_oracle = newOracle;
emit OracleChanged(newOracle);
}
/**
* @dev Allow pausing of deposits in case of emergency
* @param status New deposit status
*/
function changeJoiningPauseStatus(bool status) external onlyOwnerOrManager {
isJoiningPaused = status;
emit JoiningPauseStatusChanged(status);
}
/**
* @dev Get total balance of stake tokens
* @return Balance of stake tokens in this contract
*/
function stakeTokenBalance() public view returns (uint256) {
return _stakeToken.balanceOf(address(this));
}
/**
* @dev Get total balance of curve.fi pool tokens
* @return Balance of y pool tokens in this contract
*/
function yTokenBalance() public view returns (uint256) {
return _curvePool.token().balanceOf(address(this)).add(_curveGauge.balanceOf(address(this)));
}
/**
* @dev Virtual value of yCRV tokens in the pool
* Will return sync value if inSync
* @return yTokenValue in USD.
*/
function yTokenValue() public view returns (uint256) {
if (inSync) {
return yTokenValueCache;
}
return yTokenBalance().mul(_curvePool.curve().get_virtual_price()).div(1 ether);
}
/**
* @dev Price of TRU in USD
* @return Oracle price of TRU in USD
*/
function truValue() public view returns (uint256) {
uint256 balance = stakeTokenBalance();
if (balance == 0) {
return 0;
}
return _oracle.truToUsd(balance);
}
/**
* @dev Virtual value of liquid assets in the pool
* @return Virtual liquid value of pool assets
*/
function liquidValue() public view returns (uint256) {
return currencyBalance().add(yTokenValue());
}
/**
* @dev Calculate pool value in TUSD
* "virtual price" of entire pool - LoanTokens, TUSD, curve y pool tokens
* @return pool value in USD
*/
function poolValue() public view returns (uint256) {
// this assumes defaulted loans are worth their full value
return liquidValue().add(loansValue());
}
/**
* @dev Virtual value of loan assets in the pool
* Will return cached value if inSync
* @return Value of loans in pool
*/
function loansValue() public view returns (uint256) {
if (inSync) {
return loansValueCache;
}
return _lender.value();
}
/**
* @dev ensure enough curve.fi pool tokens are available
* Check if current available amount of TUSD is enough and
* withdraw remainder from gauge
* @param neededAmount amount required
*/
function ensureEnoughTokensAreAvailable(uint256 neededAmount) internal {
uint256 currentlyAvailableAmount = _curvePool.token().balanceOf(address(this));
if (currentlyAvailableAmount < neededAmount) {
_curveGauge.withdraw(neededAmount.sub(currentlyAvailableAmount));
}
}
/**
* @dev set pool join fee
* @param fee new fee
*/
function setJoiningFee(uint256 fee) external onlyOwner {
require(fee <= 10000, "TrueFiPool: Fee cannot exceed transaction value");
joiningFee = fee;
emit JoiningFeeChanged(fee);
}
/**
* @dev sets all token allowances used to 0
*/
function resetApprovals() external onlyOwner {
_currencyToken.approve(address(_curvePool), 0);
_curvePool.token().approve(address(_curvePool), 0);
_curvePool.token().approve(address(_curveGauge), 0);
}
/**
* @dev Join the pool by depositing currency tokens
* @param amount amount of currency token to deposit
*/
function join(uint256 amount) external override joiningNotPaused {
uint256 fee = amount.mul(joiningFee).div(10000);
uint256 mintedAmount = mint(amount.sub(fee));
claimableFees = claimableFees.add(fee);
latestJoinBlock[tx.origin] = block.number;
require(_currencyToken.transferFrom(msg.sender, address(this), amount));
emit Joined(msg.sender, amount, mintedAmount);
}
// prettier-ignore
/**
* @dev Exit pool
* This function will withdraw a basket of currencies backing the pool value
* @param amount amount of pool tokens to redeem for underlying tokens
*/
function exit(uint256 amount) external override nonReentrant {
require(block.number != latestJoinBlock[tx.origin], "TrueFiPool: Cannot join and exit in same block");
require(amount <= balanceOf(msg.sender), "TrueFiPool: insufficient funds");
uint256 _totalSupply = totalSupply();
// get share of currency tokens kept in the pool
uint256 currencyAmountToTransfer = amount.mul(
currencyBalance()).div(_totalSupply);
// calculate amount of curve.fi pool tokens
uint256 curveLiquidityAmountToTransfer = amount.mul(
yTokenBalance()).div(_totalSupply);
// calculate amount of stake tokens
uint256 stakeTokenAmountToTransfer = amount.mul(
stakeTokenBalance()).div(_totalSupply);
// burn tokens sent
_burn(msg.sender, amount);
// withdraw basket of loan tokens
_lender.distribute(msg.sender, amount, _totalSupply);
// if currency remaining, transfer
if (currencyAmountToTransfer > 0) {
require(_currencyToken.transfer(msg.sender, currencyAmountToTransfer));
}
// if curve tokens remaining, transfer
if (curveLiquidityAmountToTransfer > 0) {
ensureEnoughTokensAreAvailable(curveLiquidityAmountToTransfer);
require(_curvePool.token().transfer(msg.sender, curveLiquidityAmountToTransfer));
}
// if stake token remaining, transfer
if (stakeTokenAmountToTransfer > 0) {
require(_stakeToken.transfer(msg.sender, stakeTokenAmountToTransfer));
}
emit Exited(msg.sender, amount);
}
/**
* @dev Exit pool only with liquid tokens
* This function will withdraw TUSD but with a small penalty
* Uses the sync() modifier to reduce gas costs of using curve
* @param amount amount of pool tokens to redeem for underlying tokens
*/
function liquidExit(uint256 amount) external nonReentrant sync {
require(block.number != latestJoinBlock[tx.origin], "TrueFiPool: Cannot join and exit in same block");
require(amount <= balanceOf(msg.sender), "TrueFiPool: Insufficient funds");
uint256 amountToWithdraw = poolValue().mul(amount).div(totalSupply());
amountToWithdraw = amountToWithdraw.mul(liquidExitPenalty(amountToWithdraw)).div(10000);
require(amountToWithdraw <= liquidValue(), "TrueFiPool: Not enough liquidity in pool");
// burn tokens sent
_burn(msg.sender, amount);
if (amountToWithdraw > currencyBalance()) {
removeLiquidityFromCurve(amountToWithdraw.sub(currencyBalance()));
require(amountToWithdraw <= currencyBalance(), "TrueFiPool: Not enough funds were withdrawn from Curve");
}
require(_currencyToken.transfer(msg.sender, amountToWithdraw));
emit Exited(msg.sender, amountToWithdraw);
}
/**
* @dev Penalty (in % * 100) applied if liquid exit is performed with this amount
* returns 10000 if no penalty
*/
function liquidExitPenalty(uint256 amount) public view returns (uint256) {
uint256 lv = liquidValue();
uint256 pv = poolValue();
if (amount == pv) {
return 10000;
}
uint256 liquidRatioBefore = lv.mul(10000).div(pv);
uint256 liquidRatioAfter = lv.sub(amount).mul(10000).div(pv.sub(amount));
return uint256(10000).sub(averageExitPenalty(liquidRatioAfter, liquidRatioBefore));
}
/**
* @dev Calculates integral of 5/(x+50)dx times 10000
*/
function integrateAtPoint(uint256 x) public pure returns (uint256) {
return uint256(ABDKMath64x64.ln(ABDKMath64x64.fromUInt(x.add(50)))).mul(50000).div(2**64);
}
/**
* @dev Calculates average penalty on interval [from; to]
* @return average exit penalty
*/
function averageExitPenalty(uint256 from, uint256 to) public pure returns (uint256) {
require(from <= to, "TrueFiPool: To precedes from");
if (from == 10000) {
// When all liquid, dont penalize
return 0;
}
if (from == to) {
return uint256(50000).div(from.add(50));
}
return integrateAtPoint(to).sub(integrateAtPoint(from)).div(to.sub(from));
}
/**
* @dev Deposit idle funds into curve.fi pool and stake in gauge
* Called by owner to help manage funds in pool and save on gas for deposits
* @param currencyAmount Amount of funds to deposit into curve
* @param minMintAmount Minimum amount to mint
*/
function flush(uint256 currencyAmount, uint256 minMintAmount) external onlyOwnerOrManager {
require(currencyAmount <= currencyBalance(), "TrueFiPool: Insufficient currency balance");
uint256[N_TOKENS] memory amounts = [0, 0, 0, currencyAmount];
// add TUSD to curve
_currencyToken.approve(address(_curvePool), currencyAmount);
_curvePool.add_liquidity(amounts, minMintAmount);
// stake yCurve tokens in gauge
uint256 yBalance = _curvePool.token().balanceOf(address(this));
_curvePool.token().approve(address(_curveGauge), yBalance);
_curveGauge.deposit(yBalance);
emit Flushed(currencyAmount);
}
/**
* @dev Remove liquidity from curve
* @param yAmount amount of curve pool tokens
* @param minCurrencyAmount minimum amount of tokens to withdraw
*/
function pull(uint256 yAmount, uint256 minCurrencyAmount) external onlyOwnerOrManager {
require(yAmount <= yTokenBalance(), "TrueFiPool: Insufficient Curve liquidity balance");
// unstake in gauge
ensureEnoughTokensAreAvailable(yAmount);
// remove TUSD from curve
_curvePool.token().approve(address(_curvePool), yAmount);
_curvePool.remove_liquidity_one_coin(yAmount, TUSD_INDEX, minCurrencyAmount, false);
emit Pulled(yAmount);
}
// prettier-ignore
/**
* @dev Remove liquidity from curve if necessary and transfer to lender
* @param amount amount for lender to withdraw
*/
function borrow(uint256 amount, uint256 fee) external override nonReentrant onlyLender {
// if there is not enough TUSD, withdraw from curve
if (amount > currencyBalance()) {
removeLiquidityFromCurve(amount.sub(currencyBalance()));
require(amount <= currencyBalance(), "TrueFiPool: Not enough funds in pool to cover borrow");
}
mint(fee);
require(_currencyToken.transfer(msg.sender, amount.sub(fee)));
emit Borrow(msg.sender, amount, fee);
}
function removeLiquidityFromCurve(uint256 amountToWithdraw) internal {
// get rough estimate of how much yCRV we should sell
uint256 roughCurveTokenAmount = calcTokenAmount(amountToWithdraw).mul(1005).div(1000);
require(roughCurveTokenAmount <= yTokenBalance(), "TrueFiPool: Not enough Curve liquidity tokens in pool to cover borrow");
// pull tokens from gauge
ensureEnoughTokensAreAvailable(roughCurveTokenAmount);
// remove TUSD from curve
_curvePool.token().approve(address(_curvePool), roughCurveTokenAmount);
uint256 minAmount = roughCurveTokenAmount.mul(_curvePool.curve().get_virtual_price()).mul(999).div(1000).div(1 ether);
_curvePool.remove_liquidity_one_coin(roughCurveTokenAmount, TUSD_INDEX, minAmount, false);
}
/**
* @dev repay debt by transferring tokens to the contract
* @param currencyAmount amount to repay
*/
function repay(uint256 currencyAmount) external override onlyLender {
require(_currencyToken.transferFrom(msg.sender, address(this), currencyAmount));
emit Repaid(msg.sender, currencyAmount);
}
/**
* @dev Collect CRV tokens minted by staking at gauge
*/
function collectCrv() external onlyOwnerOrManager {
_minter.mint(address(_curveGauge));
}
/**
* @dev Sell collected CRV on Uniswap
* - Selling CRV is managed by the contract owner
* - Calculations can be made off-chain and called based on market conditions
* - Need to pass path of exact pairs to go through while executing exchange
* For example, CRV -> WETH -> TUSD
*
* @param amountIn see https://uniswap.org/docs/v2/smart-contracts/router02/#swapexacttokensfortokens
* @param amountOutMin see https://uniswap.org/docs/v2/smart-contracts/router02/#swapexacttokensfortokens
* @param path see https://uniswap.org/docs/v2/smart-contracts/router02/#swapexacttokensfortokens
*/
function sellCrv(
uint256 amountIn,
uint256 amountOutMin,
address[] calldata path
) public onlyOwnerOrManager {
_minter.token().approve(address(_uniRouter), amountIn);
_uniRouter.swapExactTokensForTokens(amountIn, amountOutMin, path, address(this), block.timestamp + 1 hours);
}
/**
* @dev Sell collected TRU on Uniswap
* - Selling TRU is managed by the contract owner
* - Calculations can be made off-chain and called based on market conditions
* - Need to pass path of exact pairs to go through while executing exchange
* For example, CRV -> WETH -> TUSD
*
* @param amountIn see https://uniswap.org/docs/v2/smart-contracts/router02/#swapexacttokensfortokens
* @param amountOutMin see https://uniswap.org/docs/v2/smart-contracts/router02/#swapexacttokensfortokens
* @param path see https://uniswap.org/docs/v2/smart-contracts/router02/#swapexacttokensfortokens
*/
function sellStakeToken(
uint256 amountIn,
uint256 amountOutMin,
address[] calldata path
) public onlyOwnerOrManager {
_stakeToken.approve(address(_uniRouter), amountIn);
_uniRouter.swapExactTokensForTokens(amountIn, amountOutMin, path, address(this), block.timestamp + 1 hours);
}
/**
* @dev Claim fees from the pool
* @param beneficiary account to send funds to
*/
function collectFees(address beneficiary) external onlyOwnerOrManager {
uint256 amount = claimableFees;
claimableFees = 0;
if (amount > 0) {
require(_currencyToken.transfer(beneficiary, amount));
}
emit Collected(beneficiary, amount);
}
/**
* @notice Expected amount of minted Curve.fi yDAI/yUSDC/yUSDT/yTUSD tokens.
* Can be used to control slippage
* Called in flush() function
* @param currencyAmount amount to calculate for
* @return expected amount minted given currency amount
*/
function calcTokenAmount(uint256 currencyAmount) public view returns (uint256) {
// prettier-ignore
uint256 yTokenAmount = currencyAmount.mul(1e18).div(
_curvePool.coins(TUSD_INDEX).getPricePerFullShare());
uint256[N_TOKENS] memory yAmounts = [0, 0, 0, yTokenAmount];
return _curvePool.curve().calc_token_amount(yAmounts, true);
}
/**
* @dev Converts the value of a single yCRV into an underlying asset
* @param yAmount amount of curve pool tokens to calculate for
* @return Value of one y pool token
*/
function calcWithdrawOneCoin(uint256 yAmount) public view returns (uint256) {
return _curvePool.calc_withdraw_one_coin(yAmount, TUSD_INDEX);
}
/**
* @dev Currency token balance
* @return Currency token balance
*/
function currencyBalance() internal view returns (uint256) {
return _currencyToken.balanceOf(address(this)).sub(claimableFees);
}
/**
* @param depositedAmount Amount of currency deposited
* @return amount minted from this transaction
*/
function mint(uint256 depositedAmount) internal returns (uint256) {
uint256 mintedAmount = depositedAmount;
if (mintedAmount == 0) {
return mintedAmount;
}
// first staker mints same amount deposited
if (totalSupply() > 0) {
mintedAmount = totalSupply().mul(depositedAmount).div(poolValue());
}
// mint pool tokens
_mint(msg.sender, mintedAmount);
return mintedAmount;
}
/**
* @dev Update name and symbol of this contract
*/
function updateNameAndSymbol() public {
super.updateNameAndSymbol("TrueFi TrueUSD", "tfTUSD");
}
}