Transaction Hash:
Block:
12581714 at Jun-06-2021 03:18:13 PM +UTC
Transaction Fee:
0.00387305508 ETH
$8.05
Gas Used:
319,559 Gas / 12.12 Gwei
Emitted Events:
| 368 |
TimeOwnedUpgradeabilityProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x000000000000000000000000ecfd4f2c07eabdb7b592308732b59713728a957f, 0x00000000000000000000000023696914ca9737466d8553a2d619948f548ee424, 0000000000000000000000000000000000000000000000000000022eb300870f )
|
| 369 |
OwnedUpgradeabilityProxy.0xddc9c30275a04c48091f24199f9c405765de34d979d6847f5b9798a57232d2e5( 0xddc9c30275a04c48091f24199f9c405765de34d979d6847f5b9798a57232d2e5, 0000000000000000000000000000000000000000000000000000022eb300870f )
|
| 370 |
OwnedUpgradeabilityProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x0000000000000000000000000000000000000000000000000000000000000000, 0x000000000000000000000000a5a340396887eabfc2d93a689fe89d6dcef1b50f, 000000000000000000000000000000000000000000000000000000416627a93c )
|
| 371 |
OwnedUpgradeabilityProxy.0xdec2bacdd2f05b59de34da9b523dff8be42e5e38e818c82fdb0bae774387a724( 0xdec2bacdd2f05b59de34da9b523dff8be42e5e38e818c82fdb0bae774387a724, 0x000000000000000000000000a5a340396887eabfc2d93a689fe89d6dcef1b50f, 0000000000000000000000000000000000000000000000000000000000000000, 000000000000000000000000000000000000000000000000000000416627a93c )
|
| 372 |
OwnedUpgradeabilityProxy.0xebedb8b3c678666e7f36970bc8f57abf6d8fa2e828c0da91ea5b75bf68ed101a( 0xebedb8b3c678666e7f36970bc8f57abf6d8fa2e828c0da91ea5b75bf68ed101a, 0x000000000000000000000000a5a340396887eabfc2d93a689fe89d6dcef1b50f, 000000000000000000000000000000000000000000000000000000416627a93c )
|
| 373 |
TimeOwnedUpgradeabilityProxy.0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef( 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef, 0x000000000000000000000000a5a340396887eabfc2d93a689fe89d6dcef1b50f, 0x00000000000000000000000023696914ca9737466d8553a2d619948f548ee424, 000000000000000000000000000000000000000000000000000000416627a93c )
|
| 374 |
TimeOwnedUpgradeabilityProxy.0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925( 0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925, 0x000000000000000000000000a5a340396887eabfc2d93a689fe89d6dcef1b50f, 0x00000000000000000000000023696914ca9737466d8553a2d619948f548ee424, 0000000000000000000000000000000000000000000000000000000000000000 )
|
Account State Difference:
| Address | Before | After | State Difference | ||
|---|---|---|---|---|---|
| 0x23696914...f548Ee424 | |||||
| 0x4C19596f...AE6543784 | |||||
| 0xa5a34039...dCeF1B50F |
0.06252864032 Eth
Nonce: 7
|
0.05865558524 Eth
Nonce: 8
| 0.00387305508 | ||
|
0xc365c331...C4058C8E1
Miner
| (Binance Pool) | 3,380.773966664306357915 Eth | 3,380.777839719386357915 Eth | 0.00387305508 | |
| 0xecfD4F2C...3728A957F |
Execution Trace
OwnedUpgradeabilityProxy.a694fc3a( )
StkTruToken.stake( amount=280886749500 )
OwnedUpgradeabilityProxy.STATICCALL( )-
LinearTrueDistributor.DELEGATECALL( )
-
OwnedUpgradeabilityProxy.STATICCALL( )-
LinearTrueDistributor.DELEGATECALL( )
-
OwnedUpgradeabilityProxy.CALL( )LinearTrueDistributor.DELEGATECALL( )
TimeOwnedUpgradeabilityProxy.a9059cbb( )
-
TrueFi.transfer( recipient=0x23696914Ca9737466D8553a2d619948f548Ee424, amount=2399594907407 ) => ( True )
-
TimeOwnedUpgradeabilityProxy.70a08231( )
-
TrueFi.balanceOf( 0x23696914Ca9737466D8553a2d619948f548Ee424 ) => ( 10758228051382172 )
-
OwnedUpgradeabilityProxy.70a08231( )
-
TrueFiPool.balanceOf( account=0x23696914Ca9737466D8553a2d619948f548Ee424 ) => ( 55424096695143452279260 )
-
OwnedProxyWithReference.70a08231( )
-
ImplementationReference.STATICCALL( ) -
0x01bd87bc97e27cb11e3c45dab9b59bc44edc4fc6.70a08231( )
-
TimeOwnedUpgradeabilityProxy.23b872dd( )
-
TrueFi.transferFrom( sender=0xa5a340396887Eabfc2D93A689Fe89D6dCeF1B50F, recipient=0x23696914Ca9737466D8553a2d619948f548Ee424, amount=280886749500 ) => ( True )
-
stake[StkTruToken (ln:1550)]
_stakeWithoutTransfer[StkTruToken (ln:1551)]safeTransferFrom[StkTruToken (ln:1552)]
File 1 of 10: OwnedUpgradeabilityProxy
File 2 of 10: TimeOwnedUpgradeabilityProxy
File 3 of 10: OwnedUpgradeabilityProxy
File 4 of 10: StkTruToken
File 5 of 10: LinearTrueDistributor
File 6 of 10: TrueFi
File 7 of 10: OwnedUpgradeabilityProxy
File 8 of 10: TrueFiPool
File 9 of 10: OwnedProxyWithReference
File 10 of 10: ImplementationReference
/*
.'''''''''''.. ..''''''''''''''''.. ..'''''''''''''''..
.;;;;;;;;;;;'. .';;;;;;;;;;;;;;;;;;,. .,;;;;;;;;;;;;;;;;;,.
.;;;;;;;;;;,. .,;;;;;;;;;;;;;;;;;;;,. .,;;;;;;;;;;;;;;;;;;,.
.;;;;;;;;;,. .,;;;;;;;;;;;;;;;;;;;;,. .;;;;;;;;;;;;;;;;;;;;,.
';;;;;;;;'. .';;;;;;;;;;;;;;;;;;;;;;,. .';;;;;;;;;;;;;;;;;;;;;,.
';;;;;,.. .';;;;;;;;;;;;;;;;;;;;;;;,..';;;;;;;;;;;;;;;;;;;;;;,.
...... .';;;;;;;;;;;;;,'''''''''''.,;;;;;;;;;;;;;,'''''''''..
.,;;;;;;;;;;;;;. .,;;;;;;;;;;;;;.
.,;;;;;;;;;;;;,. .,;;;;;;;;;;;;,.
.,;;;;;;;;;;;;,. .,;;;;;;;;;;;;,.
.,;;;;;;;;;;;;,. .;;;;;;;;;;;;;,. .....
.;;;;;;;;;;;;;'. ..';;;;;;;;;;;;;'. .',;;;;,'.
.';;;;;;;;;;;;;'. .';;;;;;;;;;;;;;'. .';;;;;;;;;;.
.';;;;;;;;;;;;;'. .';;;;;;;;;;;;;;'. .;;;;;;;;;;;,.
.,;;;;;;;;;;;;;'...........,;;;;;;;;;;;;;;. .;;;;;;;;;;;,.
.,;;;;;;;;;;;;,..,;;;;;;;;;;;;;;;;;;;;;;;,. ..;;;;;;;;;,.
.,;;;;;;;;;;;;,. .,;;;;;;;;;;;;;;;;;;;;;;,. .',;;;,,..
.,;;;;;;;;;;;;,. .,;;;;;;;;;;;;;;;;;;;;;,. ....
..',;;;;;;;;,. .,;;;;;;;;;;;;;;;;;;;;,.
..',;;;;'. .,;;;;;;;;;;;;;;;;;;;'.
...'.. .';;;;;;;;;;;;;;,,,'.
...............
*/
// 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 10: TimeOwnedUpgradeabilityProxy
// File: @trusttoken/trusttokens/contracts/Proxy/OwnedUpgradeabilityProxy.sol
pragma solidity 0.5.13;
/**
* @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 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 the address of the 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 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 function allowing to perform a delegatecall to the given implementation.
* This function will return whatever the implementation call returns
*/
function() external payable {
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: @trusttoken/trusttokens/contracts/Proxy/TimeOwnedUpgradeabilityProxy.sol
pragma solidity 0.5.13;
/**
* @title TimeOwnedUpgradeabilityProxy
* @dev This contract combines an upgradeability proxy with
* basic authorization control functionalities
*
* This contract allows us to specify a time at which the proxy can no longer
* be upgraded
*/
contract TimeOwnedUpgradeabilityProxy is OwnedUpgradeabilityProxy {
bytes32 private constant expirationPosition = bytes32(uint256(keccak256('trusttoken.expiration')) - 1);
/**
* @dev the constructor sets the original owner of the contract to the sender account.
*/
constructor() public {
_setUpgradeabilityOwner(msg.sender);
// set expiration to ~4 months from now
_setExpiration(block.timestamp + 124 days);
}
/**
* @dev sets new expiration time
*/
function setExpiration(uint256 newExpirationTime) external onlyProxyOwner {
require(block.timestamp < expiration(), "after expiration time");
require(block.timestamp < newExpirationTime, "new expiration time must be in the future");
_setExpiration(newExpirationTime);
}
function _setExpiration(uint256 newExpirationTime) internal onlyProxyOwner {
bytes32 position = expirationPosition;
assembly {
sstore(position, newExpirationTime)
}
}
function expiration() public view returns (uint256 _expiration) {
bytes32 position = expirationPosition;
assembly {
_expiration := sload(position)
}
}
/**
* @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 onlyProxyOwner {
require(block.timestamp < expiration(), "after expiration date");
super.upgradeTo(implementation);
}
}File 3 of 10: OwnedUpgradeabilityProxy
/*
.'''''''''''.. ..''''''''''''''''.. ..'''''''''''''''..
.;;;;;;;;;;;'. .';;;;;;;;;;;;;;;;;;,. .,;;;;;;;;;;;;;;;;;,.
.;;;;;;;;;;,. .,;;;;;;;;;;;;;;;;;;;,. .,;;;;;;;;;;;;;;;;;;,.
.;;;;;;;;;,. .,;;;;;;;;;;;;;;;;;;;;,. .;;;;;;;;;;;;;;;;;;;;,.
';;;;;;;;'. .';;;;;;;;;;;;;;;;;;;;;;,. .';;;;;;;;;;;;;;;;;;;;;,.
';;;;;,.. .';;;;;;;;;;;;;;;;;;;;;;;,..';;;;;;;;;;;;;;;;;;;;;;,.
...... .';;;;;;;;;;;;;,'''''''''''.,;;;;;;;;;;;;;,'''''''''..
.,;;;;;;;;;;;;;. .,;;;;;;;;;;;;;.
.,;;;;;;;;;;;;,. .,;;;;;;;;;;;;,.
.,;;;;;;;;;;;;,. .,;;;;;;;;;;;;,.
.,;;;;;;;;;;;;,. .;;;;;;;;;;;;;,. .....
.;;;;;;;;;;;;;'. ..';;;;;;;;;;;;;'. .',;;;;,'.
.';;;;;;;;;;;;;'. .';;;;;;;;;;;;;;'. .';;;;;;;;;;.
.';;;;;;;;;;;;;'. .';;;;;;;;;;;;;;'. .;;;;;;;;;;;,.
.,;;;;;;;;;;;;;'...........,;;;;;;;;;;;;;;. .;;;;;;;;;;;,.
.,;;;;;;;;;;;;,..,;;;;;;;;;;;;;;;;;;;;;;;,. ..;;;;;;;;;,.
.,;;;;;;;;;;;;,. .,;;;;;;;;;;;;;;;;;;;;;;,. .',;;;,,..
.,;;;;;;;;;;;;,. .,;;;;;;;;;;;;;;;;;;;;;,. ....
..',;;;;;;;;,. .,;;;;;;;;;;;;;;;;;;;;,.
..',;;;;'. .,;;;;;;;;;;;;;;;;;;;'.
...'.. .';;;;;;;;;;;;;;,,,'.
...............
*/
// 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 4 of 10: StkTruToken
/*
.'''''''''''.. ..''''''''''''''''.. ..'''''''''''''''..
.;;;;;;;;;;;'. .';;;;;;;;;;;;;;;;;;,. .,;;;;;;;;;;;;;;;;;,.
.;;;;;;;;;;,. .,;;;;;;;;;;;;;;;;;;;,. .,;;;;;;;;;;;;;;;;;;,.
.;;;;;;;;;,. .,;;;;;;;;;;;;;;;;;;;;,. .;;;;;;;;;;;;;;;;;;;;,.
';;;;;;;;'. .';;;;;;;;;;;;;;;;;;;;;;,. .';;;;;;;;;;;;;;;;;;;;;,.
';;;;;,.. .';;;;;;;;;;;;;;;;;;;;;;;,..';;;;;;;;;;;;;;;;;;;;;;,.
...... .';;;;;;;;;;;;;,'''''''''''.,;;;;;;;;;;;;;,'''''''''..
.,;;;;;;;;;;;;;. .,;;;;;;;;;;;;;.
.,;;;;;;;;;;;;,. .,;;;;;;;;;;;;,.
.,;;;;;;;;;;;;,. .,;;;;;;;;;;;;,.
.,;;;;;;;;;;;;,. .;;;;;;;;;;;;;,. .....
.;;;;;;;;;;;;;'. ..';;;;;;;;;;;;;'. .',;;;;,'.
.';;;;;;;;;;;;;'. .';;;;;;;;;;;;;;'. .';;;;;;;;;;.
.';;;;;;;;;;;;;'. .';;;;;;;;;;;;;;'. .;;;;;;;;;;;,.
.,;;;;;;;;;;;;;'...........,;;;;;;;;;;;;;;. .;;;;;;;;;;;,.
.,;;;;;;;;;;;;,..,;;;;;;;;;;;;;;;;;;;;;;;,. ..;;;;;;;;;,.
.,;;;;;;;;;;;;,. .,;;;;;;;;;;;;;;;;;;;;;;,. .',;;;,,..
.,;;;;;;;;;;;;,. .,;;;;;;;;;;;;;;;;;;;;;,. ....
..',;;;;;;;;,. .,;;;;;;;;;;;;;;;;;;;;,.
..',;;;;'. .,;;;;;;;;;;;;;;;;;;;'.
...'.. .';;;;;;;;;;;;;;,,,'.
...............
*/
// 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/token/ERC20/SafeERC20.sol
// pragma solidity ^0.6.0;
// import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
// import "@openzeppelin/contracts/math/SafeMath.sol";
// import "@openzeppelin/contracts/utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using SafeMath for uint256;
using Address for address;
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
/**
* @dev Deprecated. This function has issues similar to the ones found in
* {IERC20-approve}, and its usage is discouraged.
*
* Whenever possible, use {safeIncreaseAllowance} and
* {safeDecreaseAllowance} instead.
*/
function safeApprove(IERC20 token, address spender, uint256 value) internal {
// safeApprove should only be called when setting an initial allowance,
// or when resetting it to zero. To increase and decrease it, use
// 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
// solhint-disable-next-line max-line-length
require((value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 newAllowance = token.allowance(address(this), spender).add(value);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero");
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
if (returndata.length > 0) { // Return data is optional
// solhint-disable-next-line max-line-length
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}
// 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/governance/common/ProxyStorage.sol
// pragma solidity 0.6.10;
/**
* All storage must be declared here
* New storage must be appended to the end
* Never remove items from this list
*/
contract ProxyStorage {
bool public initalized;
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;
mapping(uint144 => uint256) attributes_Depricated;
address owner_;
address pendingOwner_;
mapping(address => address) public delegates; // A record of votes checkpoints for each account, by index
struct Checkpoint {
// A checkpoint for marking number of votes from a given block
uint32 fromBlock;
uint96 votes;
}
mapping(address => mapping(uint32 => Checkpoint)) public checkpoints; // A record of votes checkpoints for each account, by index
mapping(address => uint32) public numCheckpoints; // The number of checkpoints for each account
mapping(address => uint256) public nonces;
/* Additionally, we have several keccak-based storage locations.
* If you add more keccak-based storage mappings, such as mappings, you must document them here.
* If the length of the keccak input is the same as an existing mapping, it is possible there could be a preimage collision.
* A preimage collision can be used to attack the contract by treating one storage location as another,
* which would always be a critical issue.
* Carefully examine future keccak-based storage to ensure there can be no preimage collisions.
*******************************************************************************************************
** length input usage
*******************************************************************************************************
** 19 "trueXXX.proxy.owner" Proxy Owner
** 27 "trueXXX.pending.proxy.owner" Pending Proxy Owner
** 28 "trueXXX.proxy.implementation" Proxy Implementation
** 64 uint256(address),uint256(1) balanceOf
** 64 uint256(address),keccak256(uint256(address),uint256(2)) allowance
** 64 uint256(address),keccak256(bytes32,uint256(3)) attributes
**/
}
// Dependency file: contracts/governance/common/ERC20.sol
/**
* @notice This is a copy of openzeppelin ERC20 contract with removed state variables.
* Removing state variables has been necessary due to proxy pattern usage.
* Changes to Openzeppelin ERC20 https://github.com/OpenZeppelin/openzeppelin-contracts/blob/de99bccbfd4ecd19d7369d01b070aa72c64423c9/contracts/token/ERC20/ERC20.sol:
* - Remove state variables _name, _symbol, _decimals
* - Use state variables balances, allowances, totalSupply from ProxyStorage
* - Remove constructor
* - Solidity version changed from ^0.6.0 to 0.6.10
* - Contract made abstract
* - Remove inheritance from IERC20 because of ProxyStorage name conflicts
*
* See also: ClaimableOwnable.sol and ProxyStorage.sol
*/
// pragma solidity 0.6.10;
// import {Context} from "@openzeppelin/contracts/GSN/Context.sol";
// import {SafeMath} from "@openzeppelin/contracts/math/SafeMath.sol";
// import {Address} from "@openzeppelin/contracts/utils/Address.sol";
// import {ProxyStorage} from "contracts/governance/common/ProxyStorage.sol";
// prettier-ignore
/**
* @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}.
*/
abstract contract ERC20 is ProxyStorage, Context {
using SafeMath for uint256;
using Address for address;
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the name of the token.
*/
function name() public virtual pure returns (string memory);
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public virtual pure returns (string memory);
/**
* @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 virtual pure returns (uint8) {
return 18;
}
/**
* @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 returns (bool) {
_transfer(_msgSender(), recipient, amount);
return true;
}
/**
* @dev See {IERC20-approve}.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function approve(address spender, uint256 amount) public virtual 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 returns (bool) {
_transfer(sender, recipient, amount);
_approve(sender, _msgSender(), allowance[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, allowance[_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, allowance[_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);
balanceOf[sender] = balanceOf[sender].sub(amount, "ERC20: transfer amount exceeds balance");
balanceOf[recipient] = balanceOf[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);
balanceOf[account] = balanceOf[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);
balanceOf[account] = balanceOf[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");
allowance[owner][spender] = amount;
emit Approval(owner, spender, amount);
}
/**
* @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].
*/
// solhint-disable-next-line no-empty-blocks
function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { }
}
// Dependency file: contracts/governance/interface/IVoteToken.sol
// pragma solidity ^0.6.10;
// import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
interface IVoteToken {
function delegate(address delegatee) external;
function delegateBySig(
address delegatee,
uint256 nonce,
uint256 expiry,
uint8 v,
bytes32 r,
bytes32 s
) external;
function getCurrentVotes(address account) external view returns (uint96);
function getPriorVotes(address account, uint256 blockNumber) external view returns (uint96);
}
interface IVoteTokenWithERC20 is IVoteToken, IERC20 {}
// Dependency file: contracts/governance/VoteToken.sol
// AND COPIED FROM https://github.com/compound-finance/compound-protocol/blob/c5fcc34222693ad5f547b14ed01ce719b5f4b000/contracts/Governance/Comp.sol
// Copyright 2020 Compound Labs, Inc.
// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Ctrl+f for OLD to see all the modifications.
// pragma solidity 0.6.10;
// import {ERC20} from "contracts/governance/common/ERC20.sol";
// import {IVoteToken} from "contracts/governance/interface/IVoteToken.sol";
/**
* @title VoteToken
* @notice Custom token which tracks voting power for governance
* @dev This is an abstraction of a fork of the Compound governance contract
* VoteToken is used by TRU and stkTRU to allow tracking voting power
* Checkpoints are created every time state is changed which record voting power
* Inherits standard ERC20 behavior
*/
abstract contract VoteToken is ERC20, IVoteToken {
bytes32 public constant DOMAIN_TYPEHASH = keccak256("EIP712Domain(string name,uint256 chainId,address verifyingContract)");
bytes32 public constant DELEGATION_TYPEHASH = keccak256("Delegation(address delegatee,uint256 nonce,uint256 expiry)");
event DelegateChanged(address indexed delegator, address indexed fromDelegate, address indexed toDelegate);
event DelegateVotesChanged(address indexed delegate, uint256 previousBalance, uint256 newBalance);
function delegate(address delegatee) public override {
return _delegate(msg.sender, delegatee);
}
/**
* @dev Delegate votes using signature
*/
function delegateBySig(
address delegatee,
uint256 nonce,
uint256 expiry,
uint8 v,
bytes32 r,
bytes32 s
) public override {
bytes32 domainSeparator = keccak256(abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(name())), getChainId(), address(this)));
bytes32 structHash = keccak256(abi.encode(DELEGATION_TYPEHASH, delegatee, nonce, expiry));
bytes32 digest = keccak256(abi.encodePacked("", domainSeparator, structHash));
address signatory = ecrecover(digest, v, r, s);
require(signatory != address(0), "TrustToken::delegateBySig: invalid signature");
require(nonce == nonces[signatory]++, "TrustToken::delegateBySig: invalid nonce");
require(now <= expiry, "TrustToken::delegateBySig: signature expired");
return _delegate(signatory, delegatee);
}
/**
* @dev Get current voting power for an account
* @param account Account to get voting power for
* @return Voting power for an account
*/
function getCurrentVotes(address account) public virtual override view returns (uint96) {
uint32 nCheckpoints = numCheckpoints[account];
return nCheckpoints > 0 ? checkpoints[account][nCheckpoints - 1].votes : 0;
}
/**
* @dev Get voting power at a specific block for an account
* @param account Account to get voting power for
* @param blockNumber Block to get voting power at
* @return Voting power for an account at specific block
*/
function getPriorVotes(address account, uint256 blockNumber) public virtual override view returns (uint96) {
require(blockNumber < block.number, "TrustToken::getPriorVotes: not yet determined");
uint32 nCheckpoints = numCheckpoints[account];
if (nCheckpoints == 0) {
return 0;
}
// First check most recent balance
if (checkpoints[account][nCheckpoints - 1].fromBlock <= blockNumber) {
return checkpoints[account][nCheckpoints - 1].votes;
}
// Next check implicit zero balance
if (checkpoints[account][0].fromBlock > blockNumber) {
return 0;
}
uint32 lower = 0;
uint32 upper = nCheckpoints - 1;
while (upper > lower) {
uint32 center = upper - (upper - lower) / 2; // ceil, avoiding overflow
Checkpoint memory cp = checkpoints[account][center];
if (cp.fromBlock == blockNumber) {
return cp.votes;
} else if (cp.fromBlock < blockNumber) {
lower = center;
} else {
upper = center - 1;
}
}
return checkpoints[account][lower].votes;
}
/**
* @dev Internal function to delegate voting power to an account
* @param delegator Account to delegate votes from
* @param delegatee Account to delegate votes to
*/
function _delegate(address delegator, address delegatee) internal {
address currentDelegate = delegates[delegator];
// OLD: uint96 delegatorBalance = balanceOf(delegator);
uint96 delegatorBalance = safe96(_balanceOf(delegator), "StkTruToken: uint96 overflow");
delegates[delegator] = delegatee;
emit DelegateChanged(delegator, currentDelegate, delegatee);
_moveDelegates(currentDelegate, delegatee, delegatorBalance);
}
function _balanceOf(address account) internal view virtual returns (uint256) {
return balanceOf[account];
}
function _transfer(
address _from,
address _to,
uint256 _value
) internal virtual override {
super._transfer(_from, _to, _value);
_moveDelegates(delegates[_from], delegates[_to], safe96(_value, "StkTruToken: uint96 overflow"));
}
function _mint(address account, uint256 amount) internal virtual override {
super._mint(account, amount);
_moveDelegates(address(0), delegates[account], safe96(amount, "StkTruToken: uint96 overflow"));
}
function _burn(address account, uint256 amount) internal virtual override {
super._burn(account, amount);
_moveDelegates(delegates[account], address(0), safe96(amount, "StkTruToken: uint96 overflow"));
}
/**
* @dev internal function to move delegates between accounts
*/
function _moveDelegates(
address srcRep,
address dstRep,
uint96 amount
) internal {
if (srcRep != dstRep && amount > 0) {
if (srcRep != address(0)) {
uint32 srcRepNum = numCheckpoints[srcRep];
uint96 srcRepOld = srcRepNum > 0 ? checkpoints[srcRep][srcRepNum - 1].votes : 0;
uint96 srcRepNew = sub96(srcRepOld, amount, "TrustToken::_moveVotes: vote amount underflows");
_writeCheckpoint(srcRep, srcRepNum, srcRepOld, srcRepNew);
}
if (dstRep != address(0)) {
uint32 dstRepNum = numCheckpoints[dstRep];
uint96 dstRepOld = dstRepNum > 0 ? checkpoints[dstRep][dstRepNum - 1].votes : 0;
uint96 dstRepNew = add96(dstRepOld, amount, "TrustToken::_moveVotes: vote amount overflows");
_writeCheckpoint(dstRep, dstRepNum, dstRepOld, dstRepNew);
}
}
}
/**
* @dev internal function to write a checkpoint for voting power
*/
function _writeCheckpoint(
address delegatee,
uint32 nCheckpoints,
uint96 oldVotes,
uint96 newVotes
) internal {
uint32 blockNumber = safe32(block.number, "TrustToken::_writeCheckpoint: block number exceeds 32 bits");
if (nCheckpoints > 0 && checkpoints[delegatee][nCheckpoints - 1].fromBlock == blockNumber) {
checkpoints[delegatee][nCheckpoints - 1].votes = newVotes;
} else {
checkpoints[delegatee][nCheckpoints] = Checkpoint(blockNumber, newVotes);
numCheckpoints[delegatee] = nCheckpoints + 1;
}
emit DelegateVotesChanged(delegatee, oldVotes, newVotes);
}
/**
* @dev internal function to convert from uint256 to uint32
*/
function safe32(uint256 n, string memory errorMessage) internal pure returns (uint32) {
require(n < 2**32, errorMessage);
return uint32(n);
}
/**
* @dev internal function to convert from uint256 to uint96
*/
function safe96(uint256 n, string memory errorMessage) internal pure returns (uint96) {
require(n < 2**96, errorMessage);
return uint96(n);
}
/**
* @dev internal safe math function to add two uint96 numbers
*/
function add96(
uint96 a,
uint96 b,
string memory errorMessage
) internal pure returns (uint96) {
uint96 c = a + b;
require(c >= a, errorMessage);
return c;
}
/**
* @dev internal safe math function to subtract two uint96 numbers
*/
function sub96(
uint96 a,
uint96 b,
string memory errorMessage
) internal pure returns (uint96) {
require(b <= a, errorMessage);
return a - b;
}
/**
* @dev internal function to get chain ID
*/
function getChainId() internal pure returns (uint256) {
uint256 chainId;
assembly {
chainId := chainid()
}
return chainId;
}
}
// Dependency file: contracts/truefi/interface/ITrueDistributor.sol
// pragma solidity 0.6.10;
// import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
interface ITrueDistributor {
function trustToken() external view returns (IERC20);
function farm() external view returns (address);
function distribute() external;
function nextDistribution() external view returns (uint256);
function empty() external;
}
// Dependency file: contracts/governance/common/StkClaimableContract.sol
// pragma solidity 0.6.10;
// import {ProxyStorage} from "contracts/governance/common/ProxyStorage.sol";
/**
* @title ClaimableContract
* @dev The ClaimableContract contract is a copy of Claimable Contract by Zeppelin.
and provides basic authorization control functions. Inherits storage layout of
ProxyStorage.
*/
contract StkClaimableContract is ProxyStorage {
function owner() public view returns (address) {
return owner_;
}
function pendingOwner() public view returns (address) {
return pendingOwner_;
}
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev sets the original `owner` of the contract to the sender
* at construction. Must then be reinitialized
*/
constructor() public {
owner_ = msg.sender;
emit OwnershipTransferred(address(0), msg.sender);
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(msg.sender == owner_, "only owner");
_;
}
/**
* @dev Modifier throws if called by any account other than the pendingOwner.
*/
modifier onlyPendingOwner() {
require(msg.sender == pendingOwner_);
_;
}
/**
* @dev Allows the current owner to set the pendingOwner address.
* @param newOwner The address to transfer ownership to.
*/
function transferOwnership(address newOwner) public onlyOwner {
pendingOwner_ = newOwner;
}
/**
* @dev Allows the pendingOwner address to finalize the transfer.
*/
function claimOwnership() public onlyPendingOwner {
address _pendingOwner = pendingOwner_;
emit OwnershipTransferred(owner_, _pendingOwner);
owner_ = _pendingOwner;
pendingOwner_ = address(0);
}
}
// Dependency file: contracts/common/interface/IPauseableContract.sol
// pragma solidity 0.6.10;
/**
* @dev interface to allow standard pause function
*/
interface IPauseableContract {
function setPauseStatus(bool pauseStatus) external;
}
// Root file: contracts/governance/StkTruToken.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 {SafeERC20} from "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
// import {VoteToken} from "contracts/governance/VoteToken.sol";
// import {ITrueDistributor} from "contracts/truefi/interface/ITrueDistributor.sol";
// import {StkClaimableContract} from "contracts/governance/common/StkClaimableContract.sol";
// import {IPauseableContract} from "contracts/common/interface/IPauseableContract.sol";
/**
* @title stkTRU
* @dev Staking contract for TrueFi
* TRU is staked and stored in the contract
* stkTRU is minted when staking
* Holders of stkTRU accrue rewards over time
* Rewards are paid in TRU and tfUSD
* stkTRU can be used to vote in governance
* stkTRU can be used to rate and approve loans
*/
contract StkTruToken is VoteToken, StkClaimableContract, IPauseableContract, ReentrancyGuard {
using SafeMath for uint256;
using SafeERC20 for IERC20;
uint256 private constant PRECISION = 1e30;
uint256 private constant MIN_DISTRIBUTED_AMOUNT = 100e8;
struct FarmRewards {
// track overall cumulative rewards
uint256 cumulativeRewardPerToken;
// track previous cumulate rewards for accounts
mapping(address => uint256) previousCumulatedRewardPerToken;
// track claimable rewards for accounts
mapping(address => uint256) claimableReward;
// track total rewards
uint256 totalClaimedRewards;
uint256 totalFarmRewards;
}
struct ScheduledTfUsdRewards {
uint64 timestamp;
uint96 amount;
}
// ================ WARNING ==================
// ===== THIS CONTRACT IS INITIALIZABLE ======
// === STORAGE VARIABLES ARE DECLARED BELOW ==
// REMOVAL OR REORDER OF VARIABLES WILL RESULT
// ========= IN STORAGE CORRUPTION ===========
IERC20 public tru;
IERC20 public tfusd;
ITrueDistributor public distributor;
address public liquidator;
uint256 public stakeSupply;
mapping(address => uint256) private cooldowns;
uint256 public cooldownTime;
uint256 public unstakePeriodDuration;
mapping(IERC20 => FarmRewards) public farmRewards;
uint32[] public sortedScheduledRewardIndices;
ScheduledTfUsdRewards[] public scheduledRewards;
uint256 public undistributedTfusdRewards;
uint32 public nextDistributionIndex;
mapping(address => bool) public whitelistedFeePayers;
mapping(address => uint256) public receivedDuringCooldown;
// allow pausing of deposits
bool public pauseStatus;
IERC20 public feeToken;
// ======= STORAGE DECLARATION END ============
event Stake(address indexed staker, uint256 amount);
event Unstake(address indexed staker, uint256 burntAmount);
event Claim(address indexed who, IERC20 indexed token, uint256 amountClaimed);
event Withdraw(uint256 amount);
event Cooldown(address indexed who, uint256 endTime);
event CooldownTimeChanged(uint256 newUnstakePeriodDuration);
event UnstakePeriodDurationChanged(uint256 newUnstakePeriodDuration);
event FeePayerWhitelistingStatusChanged(address payer, bool status);
event PauseStatusChanged(bool pauseStatus);
event FeeTokenChanged(IERC20 token);
event LiquidatorChanged(address liquidator);
/**
* @dev pool can only be joined when it's unpaused
*/
modifier joiningNotPaused() {
require(!pauseStatus, "StkTruToken: Joining the pool is paused");
_;
}
/**
* @dev Only Liquidator contract can perform TRU liquidations
*/
modifier onlyLiquidator() {
require(msg.sender == liquidator, "StkTruToken: Can be called only by the liquidator");
_;
}
/**
* @dev Only whitelisted payers can pay fees
*/
modifier onlyWhitelistedPayers() {
require(whitelistedFeePayers[msg.sender], "StkTruToken: Can be called only by whitelisted payers");
_;
}
/**
* Get TRU from distributor
*/
modifier distribute() {
// pull TRU from distributor
// do not pull small amounts to save some gas
// only pull if there is distribution and distributor farm is set to this farm
if (distributor.nextDistribution() > MIN_DISTRIBUTED_AMOUNT && distributor.farm() == address(this)) {
distributor.distribute();
}
_;
}
/**
* Update all rewards when an account changes state
* @param account Account to update rewards for
*/
modifier update(address account) {
updateTotalRewards(tru);
updateClaimableRewards(tru, account);
updateTotalRewards(tfusd);
updateClaimableRewards(tfusd, account);
updateTotalRewards(feeToken);
updateClaimableRewards(feeToken, account);
_;
}
/**
* Update rewards for a specific token when an account changes state
* @param account Account to update rewards for
* @param token Token to update rewards for
*/
modifier updateRewards(address account, IERC20 token) {
if (token == tru) {
updateTotalRewards(tru);
updateClaimableRewards(tru, account);
} else if (token == tfusd) {
updateTotalRewards(tfusd);
updateClaimableRewards(tfusd, account);
} else if (token == feeToken) {
updateTotalRewards(feeToken);
updateClaimableRewards(feeToken, account);
}
_;
}
/**
* @dev Initialize contract and set default values
* @param _tru TRU token
* @param _tfusd tfUSD token
* @param _feeToken Token for fees, currently tfUSDC
* @param _distributor Distributor for this contract
* @param _liquidator Liquidator for staked TRU
*/
function initialize(
IERC20 _tru,
IERC20 _tfusd,
IERC20 _feeToken,
ITrueDistributor _distributor,
address _liquidator
) public {
require(!initalized, "StkTruToken: Already initialized");
tru = _tru;
tfusd = _tfusd;
feeToken = _feeToken;
distributor = _distributor;
liquidator = _liquidator;
cooldownTime = 14 days;
unstakePeriodDuration = 2 days;
owner_ = msg.sender;
initalized = true;
}
/**
* @dev Set tfUSDC address
* @param _feeToken Address of tfUSDC to be set
*/
function setFeeToken(IERC20 _feeToken) external onlyOwner {
require(rewardBalance(feeToken) == 0, "StkTruToken: Cannot replace fee token with underlying rewards");
feeToken = _feeToken;
emit FeeTokenChanged(_feeToken);
}
/**
* @dev Set liquidator address
* @param _liquidator Address of liquidator to be set
*/
function setLiquidator(address _liquidator) external onlyOwner {
liquidator = _liquidator;
emit LiquidatorChanged(_liquidator);
}
/**
* @dev Owner can use this function to add new addresses to payers whitelist
* Only whitelisted payers can call payFee method
* @param payer Address that is being added to or removed from whitelist
* @param status New whitelisting status
*/
function setPayerWhitelistingStatus(address payer, bool status) external onlyOwner {
whitelistedFeePayers[payer] = status;
emit FeePayerWhitelistingStatusChanged(payer, status);
}
/**
* @dev Owner can use this function to set cooldown time
* Cooldown time defines how long a staker waits to unstake TRU
* @param newCooldownTime New cooldown time for stakers
*/
function setCooldownTime(uint256 newCooldownTime) external onlyOwner {
// Avoid overflow
require(newCooldownTime <= 100 * 365 days, "StkTruToken: Cooldown too large");
cooldownTime = newCooldownTime;
emit CooldownTimeChanged(newCooldownTime);
}
/**
* @dev Allow pausing of deposits in case of emergency
* @param status New deposit status
*/
function setPauseStatus(bool status) external override onlyOwner {
pauseStatus = status;
emit PauseStatusChanged(status);
}
/**
* @dev Owner can set unstake period duration
* Unstake period defines how long after cooldown a user has to withdraw stake
* @param newUnstakePeriodDuration New unstake period
*/
function setUnstakePeriodDuration(uint256 newUnstakePeriodDuration) external onlyOwner {
require(newUnstakePeriodDuration > 0, "StkTruToken: Unstake period cannot be 0");
// Avoid overflow
require(newUnstakePeriodDuration <= 100 * 365 days, "StkTruToken: Unstake period too large");
unstakePeriodDuration = newUnstakePeriodDuration;
emit UnstakePeriodDurationChanged(newUnstakePeriodDuration);
}
/**
* @dev Stake TRU for stkTRU
* Updates rewards when staking
* @param amount Amount of TRU to stake for stkTRU
*/
function stake(uint256 amount) external distribute update(msg.sender) joiningNotPaused {
_stakeWithoutTransfer(amount);
tru.safeTransferFrom(msg.sender, address(this), amount);
}
/**
* @dev Unstake stkTRU for TRU
* Can only unstake when cooldown complete and within unstake period
* Claims rewards when unstaking
* @param amount Amount of stkTRU to unstake for TRU
*/
function unstake(uint256 amount) external distribute update(msg.sender) nonReentrant {
require(amount > 0, "StkTruToken: Cannot unstake 0");
require(unstakable(msg.sender) >= amount, "StkTruToken: Insufficient balance");
require(unlockTime(msg.sender) <= block.timestamp, "StkTruToken: Stake on cooldown");
_claim(tru);
_claim(tfusd);
_claim(feeToken);
uint256 amountToTransfer = amount.mul(stakeSupply).div(totalSupply);
_burn(msg.sender, amount);
stakeSupply = stakeSupply.sub(amountToTransfer);
tru.safeTransfer(msg.sender, amountToTransfer);
emit Unstake(msg.sender, amount);
}
/**
* @dev Initiate cooldown period
*/
function cooldown() public {
cooldowns[msg.sender] = block.timestamp;
receivedDuringCooldown[msg.sender] = 0;
emit Cooldown(msg.sender, block.timestamp.add(cooldownTime));
}
/**
* @dev Withdraw TRU from the contract for liquidation
* @param amount Amount to withdraw for liquidation
*/
function withdraw(uint256 amount) external onlyLiquidator {
stakeSupply = stakeSupply.sub(amount);
tru.safeTransfer(liquidator, amount);
emit Withdraw(amount);
}
/**
* @dev View function to get unlock time for an account
* @param account Account to get unlock time for
* @return Unlock time for account
*/
function unlockTime(address account) public view returns (uint256) {
if (cooldowns[account] == 0 || cooldowns[account].add(cooldownTime).add(unstakePeriodDuration) < block.timestamp) {
return type(uint256).max;
}
return cooldowns[account].add(cooldownTime);
}
/**
* @dev Give tfUSD as origination fee to stake.this
* 50% are given immediately and 50% after `endTime` passes
*/
function payFee(uint256 amount, uint256 endTime) external onlyWhitelistedPayers {
require(endTime < type(uint64).max, "StkTruToken: time overflow");
require(amount < type(uint96).max, "StkTruToken: amount overflow");
tfusd.safeTransferFrom(msg.sender, address(this), amount);
undistributedTfusdRewards = undistributedTfusdRewards.add(amount.div(2));
scheduledRewards.push(ScheduledTfUsdRewards({amount: uint96(amount.div(2)), timestamp: uint64(endTime)}));
uint32 newIndex = findPositionForTimestamp(endTime);
insertAt(newIndex, uint32(scheduledRewards.length) - 1);
}
/**
* @dev Claim all rewards
*/
function claim() external distribute update(msg.sender) {
_claim(tru);
_claim(tfusd);
_claim(feeToken);
}
/**
* @dev Claim rewards for specific token
* Allows account to claim specific token to save gas
* @param token Token to claim rewards for
*/
function claimRewards(IERC20 token) external distribute updateRewards(msg.sender, token) {
require(token == tfusd || token == tru || token == feeToken, "Token not supported for rewards");
_claim(token);
}
/**
* @dev Claim TRU rewards, transfer in extraStakeAmount, and
* stake both the rewards and the new amount.
* Allows account to save more gas by avoiding out-and-back transfers of rewards
*/
function claimRestake(uint256 extraStakeAmount) external distribute update(msg.sender) {
uint256 amount = _claimWithoutTransfer(tru).add(extraStakeAmount);
_stakeWithoutTransfer(amount);
if (extraStakeAmount > 0) {
tru.safeTransferFrom(msg.sender, address(this), extraStakeAmount);
}
}
/**
* @dev View to estimate the claimable reward for an account
* @param account Account to get claimable reward for
* @param token Token to get rewards for
* @return claimable rewards for account
*/
function claimable(address account, IERC20 token) external view returns (uint256) {
// estimate pending reward from distributor
uint256 pending = token == tru ? distributor.nextDistribution() : 0;
// calculate total rewards (including pending)
uint256 newTotalFarmRewards = rewardBalance(token)
.add(pending > MIN_DISTRIBUTED_AMOUNT ? pending : 0)
.add(farmRewards[token].totalClaimedRewards)
.mul(PRECISION);
// calculate block reward
uint256 totalBlockReward = newTotalFarmRewards.sub(farmRewards[token].totalFarmRewards);
// calculate next cumulative reward per token
uint256 nextCumulativeRewardPerToken = farmRewards[token].cumulativeRewardPerToken.add(totalBlockReward.div(totalSupply));
// return claimable reward for this account
return
farmRewards[token].claimableReward[account].add(
balanceOf[account]
.mul(nextCumulativeRewardPerToken.sub(farmRewards[token].previousCumulatedRewardPerToken[account]))
.div(PRECISION)
);
}
/**
* @dev max amount of stkTRU than can be unstaked after current cooldown period is over
*/
function unstakable(address staker) public view returns (uint256) {
if (unlockTime(staker) == type(uint256).max) {
return balanceOf[staker];
}
if (receivedDuringCooldown[staker] > balanceOf[staker]) {
return 0;
}
return balanceOf[staker].sub(receivedDuringCooldown[staker]);
}
/**
* @dev Prior votes votes are calculated as priorVotes * stakedSupply / totalSupply
* This dilutes voting power when TRU is liquidated
* @param account Account to get current voting power for
* @param blockNumber Block to get prior votes at
* @return prior voting power for account and block
*/
function getPriorVotes(address account, uint256 blockNumber) public override view returns (uint96) {
uint96 votes = super.getPriorVotes(account, blockNumber);
return safe96(stakeSupply.mul(votes).div(totalSupply), "StkTruToken: uint96 overflow");
}
/**
* @dev Current votes are calculated as votes * stakedSupply / totalSupply
* This dilutes voting power when TRU is liquidated
* @param account Account to get current voting power for
* @return voting power for account
*/
function getCurrentVotes(address account) public override view returns (uint96) {
uint96 votes = super.getCurrentVotes(account);
return safe96(stakeSupply.mul(votes).div(totalSupply), "StkTruToken: uint96 overflow");
}
function decimals() public override pure returns (uint8) {
return 8;
}
function rounding() public pure returns (uint8) {
return 8;
}
function name() public override pure returns (string memory) {
return "Staked TrueFi";
}
function symbol() public override pure returns (string memory) {
return "stkTRU";
}
function _transfer(
address sender,
address recipient,
uint256 amount
) internal override distribute update(sender) {
updateClaimableRewards(tru, recipient);
updateClaimableRewards(tfusd, recipient);
updateClaimableRewards(feeToken, recipient);
// unlockTime returns MAX_UINT256 when there's no ongoing cooldown for the address
if (unlockTime(recipient) != type(uint256).max) {
receivedDuringCooldown[recipient] = receivedDuringCooldown[recipient].add(amount);
}
if (unlockTime(sender) != type(uint256).max) {
receivedDuringCooldown[sender] = receivedDuringCooldown[sender].sub(min(receivedDuringCooldown[sender], amount));
}
super._transfer(sender, recipient, amount);
}
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Internal claim function
* Claim rewards for a specific ERC20 token
* @param token Token to claim rewards for
*/
function _claim(IERC20 token) internal {
uint256 rewardToClaim = _claimWithoutTransfer(token);
if (rewardToClaim > 0) {
token.safeTransfer(msg.sender, rewardToClaim);
}
}
/**
* @dev Internal claim function that returns the transfer value
* Claim rewards for a specific ERC20 token to return in a uint256
* @param token Token to claim rewards for
*/
function _claimWithoutTransfer(IERC20 token) internal returns (uint256) {
uint256 rewardToClaim = farmRewards[token].claimableReward[msg.sender];
farmRewards[token].totalClaimedRewards = farmRewards[token].totalClaimedRewards.add(rewardToClaim);
farmRewards[token].claimableReward[msg.sender] = 0;
emit Claim(msg.sender, token, rewardToClaim);
return rewardToClaim;
}
/**
* @dev Internal stake of TRU for stkTRU from a uint256
* Caller is responsible for ensuring amount is transferred from a valid source
* @param amount Amount of TRU to stake for stkTRU
*/
function _stakeWithoutTransfer(uint256 amount) internal {
require(amount > 0, "StkTruToken: Cannot stake 0");
if (cooldowns[msg.sender] != 0 && cooldowns[msg.sender].add(cooldownTime).add(unstakePeriodDuration) > block.timestamp) {
cooldown();
}
if (delegates[msg.sender] == address(0)) {
delegates[msg.sender] = msg.sender;
}
uint256 amountToMint = stakeSupply == 0 ? amount : amount.mul(totalSupply).div(stakeSupply);
_mint(msg.sender, amountToMint);
stakeSupply = stakeSupply.add(amount);
emit Stake(msg.sender, amount);
}
/**
* @dev Get reward balance of this contract for a token
* @param token Token to get reward balance for
* @return Reward balance for token
*/
function rewardBalance(IERC20 token) internal view returns (uint256) {
if (address(token) == address(0)) {
return 0;
}
if (token == tru) {
return token.balanceOf(address(this)).sub(stakeSupply);
}
if (token == tfusd) {
return token.balanceOf(address(this)).sub(undistributedTfusdRewards);
}
if (token == feeToken) {
return token.balanceOf(address(this));
}
return 0;
}
/**
* @dev Check if any scheduled rewards should be distributed
*/
function distributeScheduledRewards() internal {
uint32 index = nextDistributionIndex;
while (index < scheduledRewards.length && scheduledRewards[sortedScheduledRewardIndices[index]].timestamp < block.timestamp) {
undistributedTfusdRewards = undistributedTfusdRewards.sub(scheduledRewards[sortedScheduledRewardIndices[index]].amount);
index++;
}
if (nextDistributionIndex != index) {
nextDistributionIndex = index;
}
}
/**
* @dev Update rewards state for `token`
*/
function updateTotalRewards(IERC20 token) internal {
if (token == tfusd) {
distributeScheduledRewards();
}
// calculate total rewards
uint256 newTotalFarmRewards = rewardBalance(token).add(farmRewards[token].totalClaimedRewards).mul(PRECISION);
if (newTotalFarmRewards == farmRewards[token].totalFarmRewards) {
return;
}
// calculate block reward
uint256 totalBlockReward = newTotalFarmRewards.sub(farmRewards[token].totalFarmRewards);
// update farm rewards
farmRewards[token].totalFarmRewards = newTotalFarmRewards;
// if there are stakers
if (totalSupply > 0) {
farmRewards[token].cumulativeRewardPerToken = farmRewards[token].cumulativeRewardPerToken.add(
totalBlockReward.div(totalSupply)
);
}
}
/**
* @dev Update claimable rewards for a token and account
* @param token Token to update claimable rewards for
* @param user Account to update claimable rewards for
*/
function updateClaimableRewards(IERC20 token, address user) internal {
// update claimable reward for sender
if (balanceOf[user] > 0) {
farmRewards[token].claimableReward[user] = farmRewards[token].claimableReward[user].add(
balanceOf[user]
.mul(farmRewards[token].cumulativeRewardPerToken.sub(farmRewards[token].previousCumulatedRewardPerToken[user]))
.div(PRECISION)
);
}
// update previous cumulative for sender
farmRewards[token].previousCumulatedRewardPerToken[user] = farmRewards[token].cumulativeRewardPerToken;
}
/**
* @dev Find next distribution index given a timestamp
* @param timestamp Timestamp to find next distribution index for
*/
function findPositionForTimestamp(uint256 timestamp) internal view returns (uint32 i) {
for (i = nextDistributionIndex; i < sortedScheduledRewardIndices.length; i++) {
if (scheduledRewards[sortedScheduledRewardIndices[i]].timestamp > timestamp) {
break;
}
}
}
/**
* @dev internal function to insert distribution index in a sorted list
* @param index Index to insert at
* @param value Value at index
*/
function insertAt(uint32 index, uint32 value) internal {
sortedScheduledRewardIndices.push(0);
for (uint32 j = uint32(sortedScheduledRewardIndices.length) - 1; j > index; j--) {
sortedScheduledRewardIndices[j] = sortedScheduledRewardIndices[j - 1];
}
sortedScheduledRewardIndices[index] = value;
}
}File 5 of 10: LinearTrueDistributor
/*
.'''''''''''.. ..''''''''''''''''.. ..'''''''''''''''..
.;;;;;;;;;;;'. .';;;;;;;;;;;;;;;;;;,. .,;;;;;;;;;;;;;;;;;,.
.;;;;;;;;;;,. .,;;;;;;;;;;;;;;;;;;;,. .,;;;;;;;;;;;;;;;;;;,.
.;;;;;;;;;,. .,;;;;;;;;;;;;;;;;;;;;,. .;;;;;;;;;;;;;;;;;;;;,.
';;;;;;;;'. .';;;;;;;;;;;;;;;;;;;;;;,. .';;;;;;;;;;;;;;;;;;;;;,.
';;;;;,.. .';;;;;;;;;;;;;;;;;;;;;;;,..';;;;;;;;;;;;;;;;;;;;;;,.
...... .';;;;;;;;;;;;;,'''''''''''.,;;;;;;;;;;;;;,'''''''''..
.,;;;;;;;;;;;;;. .,;;;;;;;;;;;;;.
.,;;;;;;;;;;;;,. .,;;;;;;;;;;;;,.
.,;;;;;;;;;;;;,. .,;;;;;;;;;;;;,.
.,;;;;;;;;;;;;,. .;;;;;;;;;;;;;,. .....
.;;;;;;;;;;;;;'. ..';;;;;;;;;;;;;'. .',;;;;,'.
.';;;;;;;;;;;;;'. .';;;;;;;;;;;;;;'. .';;;;;;;;;;.
.';;;;;;;;;;;;;'. .';;;;;;;;;;;;;;'. .;;;;;;;;;;;,.
.,;;;;;;;;;;;;;'...........,;;;;;;;;;;;;;;. .;;;;;;;;;;;,.
.,;;;;;;;;;;;;,..,;;;;;;;;;;;;;;;;;;;;;;;,. ..;;;;;;;;;,.
.,;;;;;;;;;;;;,. .,;;;;;;;;;;;;;;;;;;;;;;,. .',;;;,,..
.,;;;;;;;;;;;;,. .,;;;;;;;;;;;;;;;;;;;;;,. ....
..',;;;;;;;;,. .,;;;;;;;;;;;;;;;;;;;;,.
..',;;;;'. .,;;;;;;;;;;;;;;;;;;;'.
...'.. .';;;;;;;;;;;;;;,,,'.
...............
*/
// https://github.com/trusttoken/smart-contracts
// Dependency file: @openzeppelin/contracts/math/SafeMath.sol
// SPDX-License-Identifier: MIT
// 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/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/common/Initializable.sol
// Copied from https://github.com/OpenZeppelin/openzeppelin-contracts-ethereum-package/blob/v3.0.0/contracts/Initializable.sol
// Added public isInitialized() view of private initialized bool.
// 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;
}
/**
* @dev Return true if and only if the contract has been initialized
* @return whether the contract has been initialized
*/
function isInitialized() public view returns (bool) {
return initialized;
}
// Reserved storage space to allow for layout changes in the future.
uint256[50] private ______gap;
}
// Dependency file: contracts/common/UpgradeableOwnable.sol
// pragma solidity 0.6.10;
// import {Context} from "@openzeppelin/contracts/GSN/Context.sol";
// import {Initializable} from "contracts/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: @openzeppelin/contracts/token/ERC20/IERC20.sol
// 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: contracts/truefi/interface/ITrueDistributor.sol
// pragma solidity 0.6.10;
// import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
interface ITrueDistributor {
function trustToken() external view returns (IERC20);
function farm() external view returns (address);
function distribute() external;
function nextDistribution() external view returns (uint256);
function empty() external;
}
// Root file: contracts/truefi/distributors/LinearTrueDistributor.sol
pragma solidity 0.6.10;
// import {SafeMath} from "@openzeppelin/contracts/math/SafeMath.sol";
// import {Ownable} from "contracts/common/UpgradeableOwnable.sol";
// import {ITrueDistributor, IERC20} from "contracts/truefi/interface/ITrueDistributor.sol";
/**
* @title LinearTrueDistributor
* @notice Distribute TRU in a linear fashion
* @dev Distributor contract which uses a linear distribution
*
* Contracts are registered to receive distributions. Once registered,
* a farm contract can claim TRU from the distributor.
* - Distributions are based on time.
* - Owner can withdraw funds in case distribution need to be re-allocated
*/
contract LinearTrueDistributor is ITrueDistributor, 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 ===========
IERC20 public override trustToken;
uint256 public distributionStart;
uint256 public duration;
uint256 public totalAmount;
uint256 public lastDistribution;
uint256 public distributed;
// contract which claim tokens from distributor
address public override farm;
// ======= STORAGE DECLARATION END ============
/**
* @dev Emitted when the farm address is changed
* @param newFarm new farm contract
*/
event FarmChanged(address newFarm);
/**
* @dev Emitted when the total distributed amount is changed
* @param newTotalAmount new totalAmount value
*/
event TotalAmountChanged(uint256 newTotalAmount);
/**
* @dev Emitted when a distribution occurs
* @param amount Amount of TRU distributed to farm
*/
event Distributed(uint256 amount);
/**
* @dev Emitted when a distribution is restarted after it was over
*/
event DistributionRestarted(uint256 _distributionStart, uint256 _duration, uint256 _dailyDistribution);
/**
* @dev Initialize distributor
* @param _distributionStart Start time for distribution
* @param _duration Length of distribution
* @param _amount Amount to distribute
* @param _trustToken TRU address
*/
function initialize(
uint256 _distributionStart,
uint256 _duration,
uint256 _amount,
IERC20 _trustToken
) public initializer {
Ownable.initialize();
distributionStart = _distributionStart;
lastDistribution = _distributionStart;
duration = _duration;
totalAmount = _amount;
trustToken = _trustToken;
}
/**
* @dev Set contract to receive distributions
* Will distribute to previous contract if farm already exists
* @param newFarm New farm for distribution
*/
function setFarm(address newFarm) external onlyOwner {
distribute();
farm = newFarm;
emit FarmChanged(newFarm);
}
/**
* @dev Distribute tokens to farm in linear fashion based on time
*/
function distribute() public override {
// cannot distribute until distribution start
uint256 amount = nextDistribution();
if (amount == 0) {
return;
}
// transfer tokens & update state
lastDistribution = block.timestamp;
distributed = distributed.add(amount);
require(trustToken.transfer(farm, amount));
emit Distributed(amount);
}
/**
* @dev Calculate next distribution amount
* @return amount of tokens for next distribution
*/
function nextDistribution() public override view returns (uint256) {
// return 0 if before distribution or farm is not set
if (block.timestamp < distributionStart || farm == address(0)) {
return 0;
}
// calculate distribution amount
uint256 amount = totalAmount.sub(distributed);
if (block.timestamp < distributionStart.add(duration)) {
amount = block.timestamp.sub(lastDistribution).mul(totalAmount).div(duration);
}
return amount;
}
/**
* @dev Withdraw funds (for instance if owner decides to create a new distribution)
* Distributes remaining funds before withdrawing
* Ends current distribution
*/
function empty() public override onlyOwner {
distribute();
distributed = 0;
totalAmount = 0;
require(trustToken.transfer(msg.sender, trustToken.balanceOf(address(this))));
}
/**
* @dev Change amount of tokens distributed daily by changing total distributed amount
* @param dailyDistribution New daily distribution
*/
function setDailyDistribution(uint256 dailyDistribution) public onlyOwner {
distribute();
uint256 timeLeft = distributionStart.add(duration).sub(block.timestamp);
if (timeLeft > duration) {
timeLeft = duration;
} else {
distributionStart = block.timestamp;
duration = timeLeft;
}
totalAmount = dailyDistribution.mul(timeLeft).div(1 days);
distributed = 0;
emit TotalAmountChanged(totalAmount);
}
/**
* @dev Restart the distribution that has ended
*/
function restart(
uint256 _distributionStart,
uint256 _duration,
uint256 _dailyDistribution
) public onlyOwner {
require(
block.timestamp > distributionStart.add(duration),
"LinearTrueDistributor: Cannot restart distribution before it's over"
);
require(_distributionStart > block.timestamp, "LinearTrueDistributor: Cannot restart distribution from the past");
distribute();
distributionStart = _distributionStart;
lastDistribution = _distributionStart;
duration = _duration;
totalAmount = _dailyDistribution.mul(_duration).div(1 days);
distributed = 0;
}
}File 6 of 10: TrueFi
/**
*Submitted for verification at Etherscan.io on 2020-11-12
*/
// &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
// &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
// &&&&&&&&&&&&&&&&&&&&&&&&&&&&&%&&&&%%%%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
// &&&&&&&&&&&&&&&&&&&&&&&&%(, .*#&&&&&&&&&&&&&&&&&&&&&&&
// &&&&&&&&&&&&&&&&&&&&#. ,/#%%%%%%%#(/, *%&&&&&&&&&&&&&&&&&&
// &&&&&&&&&&&&&&&&&( /&&&&&&&&&&&&&&&&&&&&&%&%* ,%&&&&&&&&&&&&&&&
// &&&&&&&&&&&&&&%, *%&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&%, (&&&&&&&&&&&&&
// &&&&&&&&&&&&&, #&&# ./#&&&&&&&&%, %&&&&&&&&&&&
// &&&&&&&&&&&# (&&&&# ,%&&&&&&*/&&&&&&&&&&
// &&&&&&&&&&( %&&&&&# /&&&&&&&&&&&&&&&&&&%#, .%&&&&&(%&&&&&&&&
// &&&&&&&&&% #&&&&&&# /&&&&&&&&&&&&&&&&&&&&&&&( .%&&&&&&&&&&&&&
// &&&&&&&&&/ ,&&&&&&&# /&&&&&&&&&&&&&&&&%%%%&&&&%% /&&&&&&&&&&&&
// &&&&&&&&&* /&&&&&&&# %&&&&&&, (&&&&&&&* /&&&&&&&&&&&
// &&&&&&&&&/ *&&&&&&&%////////* %&&&&&&, (&&&&&&&&, #&&&&&&&&&&
// &&&&&&&&&# &&&&&&&&&&&&&&&&%. %&&&&&&. #&&&&&&&&# *&&&&&&&&&&
// &&&&&&&&&&* ,&&%&&&&&&&&&&&&%. %&&&&&( .%&&&&&&&&# *&&&&&&&&&&
// &&&&&&&&&&&, .&&&&&&&&&&&&&&%. /&&&&&# #&&&&&&&&&* (&&&&&&&&&&
// &&&&&&&&&&&&# ,&&&&&&&&&&&&%.,%&&&&%, %&&&&&&&&&# .%&&&&&&&&&&
// &&&&&&&&&&&&&&( ,#&&&&&&&&&&&&&&( (&&&&&&&&&&/ .%&&&&&&&&&&&
// &&&&&&&&&&&&&&&&&* .. ,%&&&&&&&&&&# *&&&&&&&&&&&&&
// &&&&&&&&&&&&&&&&&&&&%(. ./%&&&&&&&&&&&%* *&&&&&&&&&&&&&&&
// &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&%(. .#&&&&&&&&&&&&&&&&&
// &&&&&&&&&&&&&&&&&&&&&%/(%&&&&&&&&&&&&%#/. *%&&&&&&&&&&&&&&&&&&&&
// &&&&&&&&&&&&&&&&&&&&&&&&&&%(, ,(%&&&&&&&&&&&&&&&&&&&&&&&&&
// &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
// &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
// &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
// SPDX-License-Identifier: MIT
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;
}
}
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.10;
interface RegistryClone {
function syncAttributeValue(
address _who,
bytes32 _attribute,
uint256 _value
) external;
}
contract Registry {
struct AttributeData {
uint256 value;
bytes32 notes;
address adminAddr;
uint256 timestamp;
}
// never remove any storage variables
address public owner;
address public pendingOwner;
bool initialized;
// Stores arbitrary attributes for users. An example use case is an IERC20
// token that requires its users to go through a KYC/AML check - in this case
// a validator can set an account's "hasPassedKYC/AML" attribute to 1 to indicate
// that account can use the token. This mapping stores that value (1, in the
// example) as well as which validator last set the value and at what time,
// so that e.g. the check can be renewed at appropriate intervals.
mapping(address => mapping(bytes32 => AttributeData)) attributes;
// The logic governing who is allowed to set what attributes is abstracted as
// this accessManager, so that it may be replaced by the owner as needed
bytes32 constant WRITE_PERMISSION = keccak256("canWriteTo-");
mapping(bytes32 => RegistryClone[]) subscribers;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
event SetAttribute(address indexed who, bytes32 attribute, uint256 value, bytes32 notes, address indexed adminAddr);
event SetManager(address indexed oldManager, address indexed newManager);
event StartSubscription(bytes32 indexed attribute, RegistryClone indexed subscriber);
event StopSubscription(bytes32 indexed attribute, RegistryClone indexed subscriber);
// Allows a write if either a) the writer is that Registry's owner, or
// b) the writer is writing to attribute foo and that writer already has
// the canWriteTo-foo attribute set (in that same Registry)
function confirmWrite(bytes32 _attribute, address _admin) internal view returns (bool) {
return (_admin == owner || hasAttribute(_admin, keccak256(abi.encodePacked(WRITE_PERMISSION ^ _attribute))));
}
// Writes are allowed only if the accessManager approves
function setAttribute(
address _who,
bytes32 _attribute,
uint256 _value,
bytes32 _notes
) public {
require(confirmWrite(_attribute, msg.sender));
attributes[_who][_attribute] = AttributeData(_value, _notes, msg.sender, block.timestamp);
emit SetAttribute(_who, _attribute, _value, _notes, msg.sender);
RegistryClone[] storage targets = subscribers[_attribute];
uint256 index = targets.length;
while (index-- > 0) {
targets[index].syncAttributeValue(_who, _attribute, _value);
}
}
function subscribe(bytes32 _attribute, RegistryClone _syncer) external onlyOwner {
subscribers[_attribute].push(_syncer);
emit StartSubscription(_attribute, _syncer);
}
function unsubscribe(bytes32 _attribute, uint256 _index) external onlyOwner {
uint256 length = subscribers[_attribute].length;
require(_index < length);
emit StopSubscription(_attribute, subscribers[_attribute][_index]);
subscribers[_attribute][_index] = subscribers[_attribute][length - 1];
subscribers[_attribute].pop();
}
function subscriberCount(bytes32 _attribute) public view returns (uint256) {
return subscribers[_attribute].length;
}
function setAttributeValue(
address _who,
bytes32 _attribute,
uint256 _value
) public {
require(confirmWrite(_attribute, msg.sender));
attributes[_who][_attribute] = AttributeData(_value, "", msg.sender, block.timestamp);
emit SetAttribute(_who, _attribute, _value, "", msg.sender);
RegistryClone[] storage targets = subscribers[_attribute];
uint256 index = targets.length;
while (index-- > 0) {
targets[index].syncAttributeValue(_who, _attribute, _value);
}
}
// Returns true if the uint256 value stored for this attribute is non-zero
function hasAttribute(address _who, bytes32 _attribute) public view returns (bool) {
return attributes[_who][_attribute].value != 0;
}
// Returns the exact value of the attribute, as well as its metadata
function getAttribute(address _who, bytes32 _attribute)
public
view
returns (
uint256,
bytes32,
address,
uint256
)
{
AttributeData memory data = attributes[_who][_attribute];
return (data.value, data.notes, data.adminAddr, data.timestamp);
}
function getAttributeValue(address _who, bytes32 _attribute) public view returns (uint256) {
return attributes[_who][_attribute].value;
}
function getAttributeAdminAddr(address _who, bytes32 _attribute) public view returns (address) {
return attributes[_who][_attribute].adminAddr;
}
function getAttributeTimestamp(address _who, bytes32 _attribute) public view returns (uint256) {
return attributes[_who][_attribute].timestamp;
}
function syncAttribute(
bytes32 _attribute,
uint256 _startIndex,
address[] calldata _addresses
) external {
RegistryClone[] storage targets = subscribers[_attribute];
uint256 index = targets.length;
while (index-- > _startIndex) {
RegistryClone target = targets[index];
for (uint256 i = _addresses.length; i-- > 0; ) {
address who = _addresses[i];
target.syncAttributeValue(who, _attribute, attributes[who][_attribute].value);
}
}
}
function reclaimEther(address payable _to) external onlyOwner {
_to.transfer(address(this).balance);
}
function reclaimToken(IERC20 token, address _to) external onlyOwner {
uint256 balance = token.balanceOf(address(this));
token.transfer(_to, balance);
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(msg.sender == owner, "only Owner");
_;
}
/**
* @dev Modifier throws if called by any account other than the pendingOwner.
*/
modifier onlyPendingOwner() {
require(msg.sender == pendingOwner);
_;
}
/**
* @dev Allows the current owner to set the pendingOwner address.
* @param newOwner The address to transfer ownership to.
*/
function transferOwnership(address newOwner) public onlyOwner {
pendingOwner = newOwner;
}
/**
* @dev Allows the pendingOwner address to finalize the transfer.
*/
function claimOwnership() public onlyPendingOwner {
emit OwnershipTransferred(owner, pendingOwner);
owner = pendingOwner;
pendingOwner = address(0);
}
}
pragma solidity 0.6.10;
/**
* All storage must be declared here
* New storage must be appended to the end
* Never remove items from this list
*/
contract ProxyStorage {
bool initalized;
uint256 public totalSupply;
mapping(address => uint256) public balanceOf;
mapping(address => mapping(address => uint256)) public allowance;
mapping(uint144 => uint256) attributes; // see RegistrySubscriber
address owner_;
address pendingOwner_;
/* Additionally, we have several keccak-based storage locations.
* If you add more keccak-based storage mappings, such as mappings, you must document them here.
* If the length of the keccak input is the same as an existing mapping, it is possible there could be a preimage collision.
* A preimage collision can be used to attack the contract by treating one storage location as another,
* which would always be a critical issue.
* Carefully examine future keccak-based storage to ensure there can be no preimage collisions.
*******************************************************************************************************
** length input usage
*******************************************************************************************************
** 19 "trueXXX.proxy.owner" Proxy Owner
** 27 "trueXXX.pending.proxy.owner" Pending Proxy Owner
** 28 "trueXXX.proxy.implementation" Proxy Implementation
** 64 uint256(address),uint256(1) balanceOf
** 64 uint256(address),keccak256(uint256(address),uint256(2)) allowance
** 64 uint256(address),keccak256(bytes32,uint256(3)) attributes
**/
}
pragma solidity 0.6.10;
/**
* @title ClaimableContract
* @dev The ClaimableContract contract is a copy of Claimable Contract by Zeppelin.
and provides basic authorization control functions. Inherits storage layout of
ProxyStorage.
*/
contract ClaimableContract is ProxyStorage {
function owner() public view returns (address) {
return owner_;
}
function pendingOwner() public view returns (address) {
return pendingOwner_;
}
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev sets the original `owner` of the contract to the sender
* at construction. Must then be reinitialized
*/
constructor() public {
owner_ = msg.sender;
emit OwnershipTransferred(address(0), msg.sender);
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(msg.sender == owner_, "only owner");
_;
}
/**
* @dev Modifier throws if called by any account other than the pendingOwner.
*/
modifier onlyPendingOwner() {
require(msg.sender == pendingOwner_);
_;
}
/**
* @dev Allows the current owner to set the pendingOwner address.
* @param newOwner The address to transfer ownership to.
*/
function transferOwnership(address newOwner) public onlyOwner {
pendingOwner_ = newOwner;
}
/**
* @dev Allows the pendingOwner address to finalize the transfer.
*/
function claimOwnership() public onlyPendingOwner {
address _pendingOwner = pendingOwner_;
emit OwnershipTransferred(owner_, _pendingOwner);
owner_ = _pendingOwner;
pendingOwner_ = address(0);
}
}
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;
}
}
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");
}
/**
* @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);
}
}
}
}
// File: contracts/trusttoken/common/ERC20.sol
/**
* @notice This is a copy of openzeppelin ERC20 contract with removed state variables.
* Removing state variables has been necessary due to proxy pattern usage.
* Changes to Openzeppelin ERC20 https://github.com/OpenZeppelin/openzeppelin-contracts/blob/de99bccbfd4ecd19d7369d01b070aa72c64423c9/contracts/token/ERC20/ERC20.sol:
* - Remove state variables _name, _symbol, _decimals
* - Use state variables balances, allowances, totalSupply from ProxyStorage
* - Remove constructor
* - Solidity version changed from ^0.6.0 to 0.6.10
* - Contract made abstract
* - Remove inheritance from IERC20 because of ProxyStorage name conflicts
*
* See also: ClaimableOwnable.sol and ProxyStorage.sol
*/
pragma solidity 0.6.10;
/**
* @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}.
*/
abstract contract ERC20 is ProxyStorage, Context {
using SafeMath for uint256;
using Address for address;
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the name of the token.
*/
function name() public virtual pure returns (string memory);
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public virtual pure returns (string memory);
/**
* @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 virtual pure returns (uint8) {
return 18;
}
/**
* @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 returns (bool) {
_transfer(_msgSender(), recipient, amount);
return true;
}
/**
* @dev See {IERC20-approve}.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function approve(address spender, uint256 amount) public virtual 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 returns (bool) {
_transfer(sender, recipient, amount);
_approve(sender, _msgSender(), allowance[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, allowance[_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, allowance[_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);
balanceOf[sender] = balanceOf[sender].sub(amount, "ERC20: transfer amount exceeds balance");
balanceOf[recipient] = balanceOf[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);
balanceOf[account] = balanceOf[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);
balanceOf[account] = balanceOf[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");
allowance[owner][spender] = amount;
emit Approval(owner, spender, amount);
}
/**
* @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].
*/
// solhint-disable-next-line no-empty-blocks
function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual { }
}
pragma solidity 0.6.10;
/**
* @title TimeLockedToken
* @notice Time Locked ERC20 Token
* @author Harold Hyatt
* @dev Contract which gives the ability to time-lock tokens
*
* The registerLockup() function allows an account to transfer
* its tokens to another account, locking them according to the
* distribution epoch periods
*
* By overriding the balanceOf(), transfer(), and transferFrom()
* functions in ERC20, an account can show its full, post-distribution
* balance but only transfer or spend up to an allowed amount
*
* Every time an epoch passes, a portion of previously non-spendable tokens
* are allowed to be transferred, and after all epochs have passed, the full
* account balance is unlocked
*/
abstract contract TimeLockedToken is ERC20, ClaimableContract {
using SafeMath for uint256;
// represents total distribution for locked balances
mapping(address => uint256) distribution;
// start of the lockup period
// Friday, July 24, 2020 4:58:31 PM GMT
uint256 constant LOCK_START = 1595609911;
// length of time to delay first epoch
uint256 constant FIRST_EPOCH_DELAY = 30 days;
// how long does an epoch last
uint256 constant EPOCH_DURATION = 90 days;
// number of epochs
uint256 constant TOTAL_EPOCHS = 8;
// registry of locked addresses
address public timeLockRegistry;
// allow unlocked transfers to special account
bool public returnsLocked;
modifier onlyTimeLockRegistry() {
require(msg.sender == timeLockRegistry, "only TimeLockRegistry");
_;
}
/**
* @dev Set TimeLockRegistry address
* @param newTimeLockRegistry Address of TimeLockRegistry contract
*/
function setTimeLockRegistry(address newTimeLockRegistry) external onlyOwner {
require(newTimeLockRegistry != address(0), "cannot be zero address");
require(newTimeLockRegistry != timeLockRegistry, "must be new TimeLockRegistry");
timeLockRegistry = newTimeLockRegistry;
}
/**
* @dev Permanently lock transfers to return address
* Lock returns so there isn't always a way to send locked tokens
*/
function lockReturns() external onlyOwner {
returnsLocked = true;
}
/**
* @dev Transfer function which includes unlocked tokens
* Locked tokens can always be transfered back to the returns address
* Transferring to owner allows re-issuance of funds through registry
*
* @param _from The address to send tokens from
* @param _to The address that will receive the tokens
* @param _value The amount of tokens to be transferred
*/
function _transfer(
address _from,
address _to,
uint256 _value
) internal override {
require(balanceOf[_from] >= _value, "insufficient balance");
// transfers to owner proceed as normal when returns allowed
if (!returnsLocked && _to == owner_) {
transferToOwner(_from, _value);
return;
}
// check if enough unlocked balance to transfer
require(unlockedBalance(_from) >= _value, "attempting to transfer locked funds");
super._transfer(_from, _to, _value);
}
/**
* @dev Transfer tokens to owner. Used only when returns allowed.
* @param _from The address to send tokens from
* @param _value The amount of tokens to be transferred
*/
function transferToOwner(address _from, uint256 _value) internal {
uint256 unlocked = unlockedBalance(_from);
if (unlocked < _value) {
// We want to have unlocked = value, i.e.
// value = balance - distribution * epochsLeft / totalEpochs
// distribution = (balance - value) * totalEpochs / epochsLeft
distribution[_from] = balanceOf[_from].sub(_value).mul(TOTAL_EPOCHS).div(epochsLeft());
}
super._transfer(_from, owner_, _value);
}
/**
* @dev Check if amount we want to burn is unlocked before burning
* @param _from The address whose tokens will burn
* @param _value The amount of tokens to be burnt
*/
function _burn(address _from, uint256 _value) internal override {
require(balanceOf[_from] >= _value, "insufficient balance");
require(unlockedBalance(_from) >= _value, "attempting to burn locked funds");
super._burn(_from, _value);
}
/**
* @dev Transfer tokens to another account under the lockup schedule
* Emits a transfer event showing a transfer to the recipient
* Only the registry can call this function
* @param receiver Address to receive the tokens
* @param amount Tokens to be transferred
*/
function registerLockup(address receiver, uint256 amount) external onlyTimeLockRegistry {
require(balanceOf[msg.sender] >= amount, "insufficient balance");
// add amount to locked distribution
distribution[receiver] = distribution[receiver].add(amount);
// transfer to recipient
_transfer(msg.sender, receiver, amount);
}
/**
* @dev Get locked balance for an account
* @param account Account to check
* @return Amount locked
*/
function lockedBalance(address account) public view returns (uint256) {
// distribution * (epochsLeft / totalEpochs)
return distribution[account].mul(epochsLeft()).div(TOTAL_EPOCHS);
}
/**
* @dev Get unlocked balance for an account
* @param account Account to check
* @return Amount that is unlocked and available eg. to transfer
*/
function unlockedBalance(address account) public view returns (uint256) {
// totalBalance - lockedBalance
return balanceOf[account].sub(lockedBalance(account));
}
/*
* @dev Get number of epochs passed
* @return Value between 0 and 8 of lockup epochs already passed
*/
function epochsPassed() public view returns (uint256) {
// return 0 if timestamp is lower than start time
if (block.timestamp < LOCK_START) {
return 0;
}
// how long it has been since the beginning of lockup period
uint256 timePassed = block.timestamp.sub(LOCK_START);
// 1st epoch is FIRST_EPOCH_DELAY longer; we check to prevent subtraction underflow
if (timePassed < FIRST_EPOCH_DELAY) {
return 0;
}
// subtract the FIRST_EPOCH_DELAY, so that we can count all epochs as lasting EPOCH_DURATION
uint256 totalEpochsPassed = timePassed.sub(FIRST_EPOCH_DELAY).div(EPOCH_DURATION);
// epochs don't count over TOTAL_EPOCHS
if (totalEpochsPassed > TOTAL_EPOCHS) {
return TOTAL_EPOCHS;
}
return totalEpochsPassed;
}
function epochsLeft() public view returns (uint256) {
return TOTAL_EPOCHS.sub(epochsPassed());
}
/**
* @dev Get timestamp of next epoch
* Will revert if all epochs have passed
* @return Timestamp of when the next epoch starts
*/
function nextEpoch() public view returns (uint256) {
// get number of epochs passed
uint256 passed = epochsPassed();
// if all epochs passed, return
if (passed == TOTAL_EPOCHS) {
// return INT_MAX
return uint256(-1);
}
// if no epochs passed, return latest epoch + delay + standard duration
if (passed == 0) {
return latestEpoch().add(FIRST_EPOCH_DELAY).add(EPOCH_DURATION);
}
// otherwise return latest epoch + epoch duration
return latestEpoch().add(EPOCH_DURATION);
}
/**
* @dev Get timestamp of latest epoch
* @return Timestamp of when the current epoch has started
*/
function latestEpoch() public view returns (uint256) {
// get number of epochs passed
uint256 passed = epochsPassed();
// if no epochs passed, return lock start time
if (passed == 0) {
return LOCK_START;
}
// accounts for first epoch being longer
// lockStart + firstEpochDelay + (epochsPassed * epochDuration)
return LOCK_START.add(FIRST_EPOCH_DELAY).add(passed.mul(EPOCH_DURATION));
}
/**
* @dev Get timestamp of final epoch
* @return Timestamp of when the last epoch ends and all funds are released
*/
function finalEpoch() public pure returns (uint256) {
// lockStart + firstEpochDelay + (epochDuration * totalEpochs)
return LOCK_START.add(FIRST_EPOCH_DELAY).add(EPOCH_DURATION.mul(TOTAL_EPOCHS));
}
/**
* @dev Get timestamp of locking period start
* @return Timestamp of locking period start
*/
function lockStart() public pure returns (uint256) {
return LOCK_START;
}
}
pragma solidity 0.6.10;
/**
* @title TrustToken
* @dev The TrustToken contract is a claimable contract where the
* owner can only mint or transfer ownership. TrustTokens use 8 decimals
* in order to prevent rewards from getting stuck in the remainder on division.
* Tolerates dilution to slash stake and accept rewards.
*/
contract TrueFi is TimeLockedToken {
using SafeMath for uint256;
uint256 constant MAX_SUPPLY = 145000000000000000;
/**
* @dev initialize trusttoken and give ownership to sender
* This is necessary to set ownership for proxy
*/
function initialize() public {
require(!initalized, "already initialized");
owner_ = msg.sender;
initalized = true;
}
/**
* @dev mint TRU
* Can never mint more than MAX_SUPPLY = 1.45 billion
*/
function mint(address _to, uint256 _amount) external onlyOwner {
if (totalSupply.add(_amount) <= MAX_SUPPLY) {
_mint(_to, _amount);
} else {
revert("Max supply exceeded");
}
}
function burn(uint256 amount) external {
_burn(msg.sender, amount);
}
function decimals() public override pure returns (uint8) {
return 8;
}
function rounding() public pure returns (uint8) {
return 8;
}
function name() public override pure returns (string memory) {
return "TrueFi";
}
function symbol() public override pure returns (string memory) {
return "TRU";
}
}File 7 of 10: OwnedUpgradeabilityProxy
/*
.'''''''''''.. ..''''''''''''''''.. ..'''''''''''''''..
.;;;;;;;;;;;'. .';;;;;;;;;;;;;;;;;;,. .,;;;;;;;;;;;;;;;;;,.
.;;;;;;;;;;,. .,;;;;;;;;;;;;;;;;;;;,. .,;;;;;;;;;;;;;;;;;;,.
.;;;;;;;;;,. .,;;;;;;;;;;;;;;;;;;;;,. .;;;;;;;;;;;;;;;;;;;;,.
';;;;;;;;'. .';;;;;;;;;;;;;;;;;;;;;;,. .';;;;;;;;;;;;;;;;;;;;;,.
';;;;;,.. .';;;;;;;;;;;;;;;;;;;;;;;,..';;;;;;;;;;;;;;;;;;;;;;,.
...... .';;;;;;;;;;;;;,'''''''''''.,;;;;;;;;;;;;;,'''''''''..
.,;;;;;;;;;;;;;. .,;;;;;;;;;;;;;.
.,;;;;;;;;;;;;,. .,;;;;;;;;;;;;,.
.,;;;;;;;;;;;;,. .,;;;;;;;;;;;;,.
.,;;;;;;;;;;;;,. .;;;;;;;;;;;;;,. .....
.;;;;;;;;;;;;;'. ..';;;;;;;;;;;;;'. .',;;;;,'.
.';;;;;;;;;;;;;'. .';;;;;;;;;;;;;;'. .';;;;;;;;;;.
.';;;;;;;;;;;;;'. .';;;;;;;;;;;;;;'. .;;;;;;;;;;;,.
.,;;;;;;;;;;;;;'...........,;;;;;;;;;;;;;;. .;;;;;;;;;;;,.
.,;;;;;;;;;;;;,..,;;;;;;;;;;;;;;;;;;;;;;;,. ..;;;;;;;;;,.
.,;;;;;;;;;;;;,. .,;;;;;;;;;;;;;;;;;;;;;;,. .',;;;,,..
.,;;;;;;;;;;;;,. .,;;;;;;;;;;;;;;;;;;;;;,. ....
..',;;;;;;;;,. .,;;;;;;;;;;;;;;;;;;;;,.
..',;;;;'. .,;;;;;;;;;;;;;;;;;;;'.
...'.. .';;;;;;;;;;;;;;,,,'.
...............
*/
// 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 8 of 10: 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/common/Initializable.sol
// Copied from https://github.com/OpenZeppelin/openzeppelin-contracts-ethereum-package/blob/v3.0.0/contracts/Initializable.sol
// Added public isInitialized() view of private initialized bool.
// 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;
}
/**
* @dev Return true if and only if the contract has been initialized
* @return whether the contract has been initialized
*/
function isInitialized() public view returns (bool) {
return initialized;
}
// Reserved storage space to allow for layout changes in the future.
uint256[50] private ______gap;
}
// Dependency file: contracts/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/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 virtual 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/common/UpgradeableOwnable.sol
// pragma solidity 0.6.10;
// import {Context} from "@openzeppelin/contracts/GSN/Context.sol";
// import {Initializable} from "contracts/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/ICrvPriceOracle.sol
// pragma solidity 0.6.10;
interface ICrvPriceOracle {
function usdToCrv(uint256 amount) external view returns (uint256);
function crvToUsd(uint256 amount) external view returns (uint256);
}
// Dependency file: contracts/common/interface/IPauseableContract.sol
// pragma solidity 0.6.10;
/**
* @dev interface to allow standard pause function
*/
interface IPauseableContract {
function setPauseStatus(bool pauseStatus) external;
}
// Dependency file: contracts/truefi2/interface/ITrueLender2.sol
// pragma solidity 0.6.10;
// import {ITrueFiPool2} from "contracts/truefi2/interface/ITrueFiPool2.sol";
interface ITrueLender2 {
// @dev calculate overall value of the pools
function value(ITrueFiPool2 pool) external view returns (uint256);
// @dev distribute a basket of tokens for exiting user
function distribute(
address recipient,
uint256 numerator,
uint256 denominator
) external;
}
// Dependency file: contracts/truefi2/interface/IERC20WithDecimals.sol
// pragma solidity 0.6.10;
// import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
interface IERC20WithDecimals is IERC20 {
function decimals() external view returns (uint256);
}
// Dependency file: contracts/truefi2/interface/ITrueFiPoolOracle.sol
// pragma solidity 0.6.10;
// import {IERC20WithDecimals} from "contracts/truefi2/interface/IERC20WithDecimals.sol";
/**
* @dev Oracle that converts any token to and from TRU
* Used for liquidations and valuing of liquidated TRU in the pool
*/
interface ITrueFiPoolOracle {
// token address
function token() external view returns (IERC20WithDecimals);
// amount of tokens 1 TRU is worth
function truToToken(uint256 truAmount) external view returns (uint256);
// amount of TRU 1 token is worth
function tokenToTru(uint256 tokenAmount) external view returns (uint256);
// USD price of token with 18 decimals
function tokenToUsd(uint256 tokenAmount) external view returns (uint256);
}
// Dependency file: contracts/truefi2/interface/I1Inch3.sol
// pragma solidity 0.6.10;
pragma experimental ABIEncoderV2;
interface I1Inch3 {
struct SwapDescription {
address srcToken;
address dstToken;
address srcReceiver;
address dstReceiver;
uint256 amount;
uint256 minReturnAmount;
uint256 flags;
bytes permit;
}
function swap(
address caller,
SwapDescription calldata desc,
bytes calldata data
)
external
returns (
uint256 returnAmount,
uint256 gasLeft,
uint256 chiSpent
);
function unoswap(
address srcToken,
uint256 amount,
uint256 minReturn,
bytes32[] calldata /* pools */
) external payable returns (uint256 returnAmount);
}
// Dependency file: contracts/truefi2/interface/ITrueFiPool2.sol
// pragma solidity 0.6.10;
// import {ERC20, IERC20} from "contracts/common/UpgradeableERC20.sol";
// import {ITrueLender2} from "contracts/truefi2/interface/ITrueLender2.sol";
// import {ITrueFiPoolOracle} from "contracts/truefi2/interface/ITrueFiPoolOracle.sol";
// import {I1Inch3} from "contracts/truefi2/interface/I1Inch3.sol";
interface ITrueFiPool2 is IERC20 {
function initialize(
ERC20 _token,
ERC20 _stakingToken,
ITrueLender2 _lender,
I1Inch3 __1Inch,
address __owner
) external;
function token() external view returns (ERC20);
function oracle() external view returns (ITrueFiPoolOracle);
/**
* @dev Join the pool by depositing tokens
* @param amount amount of tokens to deposit
*/
function join(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) external;
/**
* @dev pay borrowed money back to pool
* 1. Transfer TUSD from sender
* 2. Only lending pool should be allowed to call this
*/
function repay(uint256 currencyAmount) external;
}
// 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/common/UpgradeableERC20.sol";
// import {Ownable} from "contracts/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 {ICrvPriceOracle} from "contracts/truefi/interface/ICrvPriceOracle.sol";
// import {IPauseableContract} from "contracts/common/interface/IPauseableContract.sol";
// import {ITrueFiPool2, ITrueFiPoolOracle, ITrueLender2} from "contracts/truefi2/interface/ITrueFiPool2.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. Liquidity
* Exiting the pool has 2 options:
* - withdraw a basket of LoanTokens backing the pool
* - take an exit penalty 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, IPauseableContract, 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 token;
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
ITrueFiPoolOracle 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 pauseStatus;
// CRV price oracle
ICrvPriceOracle public _crvOracle;
ITrueLender2 public _lender2;
// ======= STORAGE DECLARATION END ============
// curve.fi data
uint8 constant N_TOKENS = 4;
uint8 constant TUSD_INDEX = 3;
uint256 constant MAX_PRICE_SLIPPAGE = 200; // 2%
/**
* @dev Emitted when TrueFi oracle was changed
* @param newOracle New oracle address
*/
event TruOracleChanged(ITrueFiPoolOracle newOracle);
/**
* @dev Emitted when CRV oracle was changed
* @param newOracle New oracle address
*/
event CrvOracleChanged(ICrvPriceOracle 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 pauseStatus New pausing status
*/
event PauseStatusChanged(bool pauseStatus);
/**
* @dev only lender can perform borrowing or repaying
*/
modifier onlyLender() {
require(msg.sender == address(_lender) || msg.sender == address(_lender2), "TrueFiPool: Caller is not the lender");
_;
}
/**
* @dev pool can only be joined when it's unpaused
*/
modifier joiningNotPaused() {
require(!pauseStatus, "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");
_;
}
/**
* @dev ensure than as a result of running a function,
* balance of `token` increases by at least `expectedGain`
*/
modifier exchangeProtector(uint256 expectedGain, IERC20 _token) {
uint256 balanceBefore = _token.balanceOf(address(this));
_;
uint256 balanceDiff = _token.balanceOf(address(this)).sub(balanceBefore);
require(balanceDiff >= conservativePriceEstimation(expectedGain), "TrueFiPool: Not optimal exchange");
}
/**
* 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 support borrow function from pool V2
function borrow(uint256 amount) external {
borrow(amount, 0);
}
/**
* @dev get currency token address
* @return currency token address
*/
function currencyToken() public override view returns (IERC20) {
return token;
}
/**
* @dev get stake token address
* @return stake token address
*/
function stakeToken() public override view returns (IERC20) {
return _stakeToken;
}
/**
* @dev set TrueLenderV2
*/
function setLender2(ITrueLender2 lender2) public onlyOwner {
require(address(_lender2) == address(0), "TrueFiPool: Lender 2 is already set");
_lender2 = lender2;
}
/**
* @dev set funds manager address
*/
function setFundsManager(address newFundsManager) public onlyOwner {
fundsManager = newFundsManager;
emit FundsManagerChanged(newFundsManager);
}
/**
* @dev set TrueFi price oracle token address
* @param newOracle new oracle address
*/
function setTruOracle(ITrueFiPoolOracle newOracle) public onlyOwner {
oracle = newOracle;
emit TruOracleChanged(newOracle);
}
/**
* @dev set CRV price oracle token address
* @param newOracle new oracle address
*/
function setCrvOracle(ICrvPriceOracle newOracle) public onlyOwner {
_crvOracle = newOracle;
emit CrvOracleChanged(newOracle);
}
/**
* @dev Allow pausing of deposits in case of emergency
* @param status New deposit status
*/
function setPauseStatus(bool status) external override onlyOwnerOrManager {
pauseStatus = status;
emit PauseStatusChanged(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 CRV tokens
* @return Balance of stake tokens in this contract
*/
function crvBalance() public view returns (uint256) {
if (address(_minter) == address(0)) {
return 0;
}
return _minter.token().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 || address(oracle) == address(0)) {
return 0;
}
return conservativePriceEstimation(oracle.truToToken(balance));
}
/**
* @dev Price of CRV in USD
* @return Oracle price of TRU in USD
*/
function crvValue() public view returns (uint256) {
uint256 balance = crvBalance();
if (balance == 0 || address(_crvOracle) == address(0)) {
return 0;
}
return conservativePriceEstimation(_crvOracle.crvToUsd(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()).add(crvValue());
}
/**
* @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;
}
if (address(_lender2) != address(0)) {
return _lender.value().add(_lender2.value(ITrueFiPool2(address(this))));
}
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 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(token.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);
// calculate amount of CRV
uint256 crvTokenAmountToTransfer = amount.mul(
crvBalance()).div(_totalSupply);
// burn tokens sent
_burn(msg.sender, amount);
// withdraw basket of loan tokens
_lender.distribute(msg.sender, amount, _totalSupply);
if (address(_lender2) != address(0)) {
_lender2.distribute(msg.sender, amount, _totalSupply);
}
// if currency remaining, transfer
if (currencyAmountToTransfer > 0) {
require(token.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));
}
// if crv remaining, transfer
if (crvTokenAmountToTransfer > 0) {
require(_minter.token().transfer(msg.sender, crvTokenAmountToTransfer));
}
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(token.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, don't 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 onlyOwner {
require(currencyAmount <= currencyBalance(), "TrueFiPool: Insufficient currency balance");
// add TUSD to curve
_flush(currencyAmount, 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);
}
function _flush(uint256 currencyAmount, uint256 minMintAmount)
internal
exchangeProtector(calcTokenAmount(currencyAmount), _curvePool.token())
{
uint256[N_TOKENS] memory amounts = [0, 0, 0, currencyAmount];
token.approve(address(_curvePool), currencyAmount);
_curvePool.add_liquidity(amounts, minMintAmount);
}
/**
* @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) public 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(token.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(token.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 exchangeProtector(_crvOracle.crvToUsd(amountIn), token) {
_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 exchangeProtector(oracle.truToToken(amountIn), token) {
_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(token.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 Currency token balance
* @return Currency token balance
*/
function currencyBalance() public view returns (uint256) {
return token.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 Calculate price minus max percentage of slippage during exchange
* This will lead to the pool value become a bit undervalued
* compared to the oracle price but will ensure that the value doesn't drop
* when token exchanges are performed.
*/
function conservativePriceEstimation(uint256 price) internal pure returns (uint256) {
return price.mul(uint256(10000).sub(MAX_PRICE_SLIPPAGE)).div(10000);
}
}File 9 of 10: OwnedProxyWithReference
/**
*Submitted for verification at Etherscan.io on 2021-05-05
*/
/*
.'''''''''''.. ..''''''''''''''''.. ..'''''''''''''''..
.;;;;;;;;;;;'. .';;;;;;;;;;;;;;;;;;,. .,;;;;;;;;;;;;;;;;;,.
.;;;;;;;;;;,. .,;;;;;;;;;;;;;;;;;;;,. .,;;;;;;;;;;;;;;;;;;,.
.;;;;;;;;;,. .,;;;;;;;;;;;;;;;;;;;;,. .;;;;;;;;;;;;;;;;;;;;,.
';;;;;;;;'. .';;;;;;;;;;;;;;;;;;;;;;,. .';;;;;;;;;;;;;;;;;;;;;,.
';;;;;,.. .';;;;;;;;;;;;;;;;;;;;;;;,..';;;;;;;;;;;;;;;;;;;;;;,.
...... .';;;;;;;;;;;;;,'''''''''''.,;;;;;;;;;;;;;,'''''''''..
.,;;;;;;;;;;;;;. .,;;;;;;;;;;;;;.
.,;;;;;;;;;;;;,. .,;;;;;;;;;;;;,.
.,;;;;;;;;;;;;,. .,;;;;;;;;;;;;,.
.,;;;;;;;;;;;;,. .;;;;;;;;;;;;;,. .....
.;;;;;;;;;;;;;'. ..';;;;;;;;;;;;;'. .',;;;;,'.
.';;;;;;;;;;;;;'. .';;;;;;;;;;;;;;'. .';;;;;;;;;;.
.';;;;;;;;;;;;;'. .';;;;;;;;;;;;;;'. .;;;;;;;;;;;,.
.,;;;;;;;;;;;;;'...........,;;;;;;;;;;;;;;. .;;;;;;;;;;;,.
.,;;;;;;;;;;;;,..,;;;;;;;;;;;;;;;;;;;;;;;,. ..;;;;;;;;;,.
.,;;;;;;;;;;;;,. .,;;;;;;;;;;;;;;;;;;;;;;,. .',;;;,,..
.,;;;;;;;;;;;;,. .,;;;;;;;;;;;;;;;;;;;;;,. ....
..',;;;;;;;;,. .,;;;;;;;;;;;;;;;;;;;;,.
..',;;;;'. .,;;;;;;;;;;;;;;;;;;;'.
...'.. .';;;;;;;;;;;;;;,,,'.
...............
*/
// https://github.com/trusttoken/smart-contracts
// SPDX-License-Identifier: MIT
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;
}
}
/**
* @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;
}
/**
* @dev Return true if and only if the contract has been initialized
* @return whether the contract has been initialized
*/
function isInitialized() public view returns (bool) {
return initialized;
}
// Reserved storage space to allow for layout changes in the future.
uint256[50] private ______gap;
}
/**
* @title UpgradeableClaimable
* @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. Since
* this contract combines Claimable and UpgradableOwnable contracts, ownership
* can be later change via 2 step method {transferOwnership} and {claimOwnership}
*
* 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 UpgradeableClaimable is Initializable, Context {
address private _owner;
address private _pendingOwner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting a custom initial owner of choice.
* @param __owner Initial owner of contract to be set.
*/
function initialize(address __owner) internal initializer {
_owner = __owner;
emit OwnershipTransferred(address(0), __owner);
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view returns (address) {
return _owner;
}
/**
* @dev Returns the address of the pending owner.
*/
function pendingOwner() public view returns (address) {
return _pendingOwner;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(_owner == _msgSender(), "Ownable: caller is not the owner");
_;
}
/**
* @dev Modifier throws if called by any account other than the pendingOwner.
*/
modifier onlyPendingOwner() {
require(msg.sender == _pendingOwner, "Ownable: caller is not the pending owner");
_;
}
/**
* @dev Allows the current owner to set the pendingOwner address.
* @param newOwner The address to transfer ownership to.
*/
function transferOwnership(address newOwner) public onlyOwner {
_pendingOwner = newOwner;
}
/**
* @dev Allows the pendingOwner address to finalize the transfer.
*/
function claimOwnership() public onlyPendingOwner {
emit OwnershipTransferred(_owner, _pendingOwner);
_owner = _pendingOwner;
_pendingOwner = address(0);
}
}
/**
* @title ImplementationReference
* @dev This contract is made to serve a simple purpose only.
* To hold the address of the implementation contract to be used by proxy.
* The implementation address, is changeable anytime by the owner of this contract.
*/
contract ImplementationReference is UpgradeableClaimable {
address public implementation;
/**
* @dev Event to show that implementation address has been changed
* @param newImplementation New address of the implementation
*/
event ImplementationChanged(address newImplementation);
/**
* @dev Set initial ownership and implementation address
* @param _implementation Initial address of the implementation
*/
constructor(address _implementation) public {
UpgradeableClaimable.initialize(msg.sender);
implementation = _implementation;
}
/**
* @dev Function to change the implementation address, which can be called only by the owner
* @param newImplementation New address of the implementation
*/
function setImplementation(address newImplementation) external onlyOwner {
implementation = newImplementation;
emit ImplementationChanged(newImplementation);
}
}
/**
* @title OwnedProxyWithReference
* @dev This contract combines an upgradeability proxy with basic authorization control functionalities
* Its structure makes it easy for a group of contracts alike, to share an implementation and to change it easily for all of them at once
*/
contract OwnedProxyWithReference {
/**
* @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);
/**
* @dev Event to show implementation reference has been changed
* @param implementationReference address of the new implementation reference contract
*/
event ImplementationReferenceChanged(address implementationReference);
// Storage position of the owner and pendingOwner and implementationReference of the contract
// This is made to ensure, that memory spaces do not interfere with each other
bytes32 private constant proxyOwnerPosition = 0x6279e8199720cf3557ecd8b58d667c8edc486bd1cf3ad59ea9ebdfcae0d0dfac; //keccak256("trueUSD.proxy.owner");
bytes32 private constant pendingProxyOwnerPosition = 0x8ddbac328deee8d986ec3a7b933a196f96986cb4ee030d86cc56431c728b83f4; //keccak256("trueUSD.pending.proxy.owner");
bytes32 private constant implementationReferencePosition = keccak256("trueFiPool.implementation.reference"); //keccak256("trueFiPool.implementation.reference");
/**
* @dev the constructor sets the original owner of the contract to the sender account.
* @param _owner Initial owner of the proxy
* @param _implementationReference initial ImplementationReference address
*/
constructor(address _owner, address _implementationReference) public {
_setUpgradeabilityOwner(_owner);
_changeImplementationReference(_implementationReference);
}
/**
* @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
* @param newProxyOwner New owner to be set
*/
function _setUpgradeabilityOwner(address newProxyOwner) internal {
bytes32 position = proxyOwnerPosition;
assembly {
sstore(position, newProxyOwner)
}
}
/**
* @dev Sets the address of the owner
* @param newPendingProxyOwner New pending owner address
*/
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 change the contract holding address of implementation.
* @param _implementationReference representing the address contract, which holds implementation.
*/
function changeImplementationReference(address _implementationReference) public virtual onlyProxyOwner {
_changeImplementationReference(_implementationReference);
}
/**
* @dev Get the address of current implementation.
* @return Returns address of implementation contract
*/
function implementation() public view returns (address) {
bytes32 position = implementationReferencePosition;
address implementationReference;
assembly {
implementationReference := sload(position)
}
return ImplementationReference(implementationReference).implementation();
}
/**
* @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();
}
/**
* @dev This fallback function gets called only when this contract is called without any calldata e.g. send(), transfer()
* This would also trigger receive() function on called implementation
*/
receive() external payable {
proxyCall();
}
/**
* @dev Performs a low level call, to the contract holding all the logic, changing state on this contract at the same time
*/
function proxyCall() internal {
address impl = implementation();
assembly {
let ptr := mload(0x40)
calldatacopy(ptr, 0, calldatasize())
let result := delegatecall(gas(), impl, ptr, calldatasize(), 0, 0)
returndatacopy(ptr, 0, returndatasize())
switch result
case 0 {
revert(ptr, returndatasize())
}
default {
return(ptr, returndatasize())
}
}
}
/**
* @dev Function to internally change the contract holding address of implementation.
* @param _implementationReference representing the address contract, which holds implementation.
*/
function _changeImplementationReference(address _implementationReference) internal virtual {
bytes32 position = implementationReferencePosition;
assembly {
sstore(position, _implementationReference)
}
emit ImplementationReferenceChanged(address(_implementationReference));
}
}File 10 of 10: ImplementationReference
// SPDX-License-Identifier: MIT
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;
}
}
// Copied from https://github.com/OpenZeppelin/openzeppelin-contracts-ethereum-package/blob/v3.0.0/contracts/Initializable.sol
// Added public isInitialized() view of private initialized bool.
// SPDX-License-Identifier: MIT
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;
}
/**
* @dev Return true if and only if the contract has been initialized
* @return whether the contract has been initialized
*/
function isInitialized() public view returns (bool) {
return initialized;
}
// Reserved storage space to allow for layout changes in the future.
uint256[50] private ______gap;
}
// SPDX-License-Identifier: MIT
pragma solidity 0.6.10;
import {Context} from "Context.sol";
import {Initializable} from "Initializable.sol";
/**
* @title UpgradeableClaimable
* @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. Since
* this contract combines Claimable and UpgradableOwnable contracts, ownership
* can be later change via 2 step method {transferOwnership} and {claimOwnership}
*
* 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 UpgradeableClaimable is Initializable, Context {
address private _owner;
address private _pendingOwner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting a custom initial owner of choice.
* @param __owner Initial owner of contract to be set.
*/
function initialize(address __owner) internal initializer {
_owner = __owner;
emit OwnershipTransferred(address(0), __owner);
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view returns (address) {
return _owner;
}
/**
* @dev Returns the address of the pending owner.
*/
function pendingOwner() public view returns (address) {
return _pendingOwner;
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
require(_owner == _msgSender(), "Ownable: caller is not the owner");
_;
}
/**
* @dev Modifier throws if called by any account other than the pendingOwner.
*/
modifier onlyPendingOwner() {
require(msg.sender == _pendingOwner, "Ownable: caller is not the pending owner");
_;
}
/**
* @dev Allows the current owner to set the pendingOwner address.
* @param newOwner The address to transfer ownership to.
*/
function transferOwnership(address newOwner) public onlyOwner {
_pendingOwner = newOwner;
}
/**
* @dev Allows the pendingOwner address to finalize the transfer.
*/
function claimOwnership() public onlyPendingOwner {
emit OwnershipTransferred(_owner, _pendingOwner);
_owner = _pendingOwner;
_pendingOwner = address(0);
}
}
// SPDX-License-Identifier: MIT
pragma solidity 0.6.10;
import {UpgradeableClaimable as Claimable} from "UpgradeableClaimable.sol";
/**
* @title ImplementationReference
* @dev This contract is made to serve a simple purpose only.
* To hold the address of the implementation contract to be used by proxy.
* The implementation address, is changeable anytime by the owner of this contract.
*/
contract ImplementationReference is Claimable {
address public implementation;
/**
* @dev Event to show that implementation address has been changed
* @param newImplementation New address of the implementation
*/
event ImplementationChanged(address newImplementation);
/**
* @dev Set initial ownership and implementation address
* @param _implementation Initial address of the implementation
*/
constructor(address _implementation) public {
Claimable.initialize(msg.sender);
implementation = _implementation;
}
/**
* @dev Function to change the implementation address, which can be called only by the owner
* @param newImplementation New address of the implementation
*/
function setImplementation(address newImplementation) external onlyOwner {
implementation = newImplementation;
emit ImplementationChanged(newImplementation);
}
}