Last active
May 2, 2021 03:49
-
-
Save jfluity/32216082afa16a5420471088df228a37 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/packages/contracts/contracts/Interfaces/ICommunityIssuance.sol b/packages/contracts/contracts/Interfaces/ICommunityIssuance.sol | |
index 64aef59f..91a7ccd3 100644 | |
--- a/packages/contracts/contracts/Interfaces/ICommunityIssuance.sol | |
+++ b/packages/contracts/contracts/Interfaces/ICommunityIssuance.sol | |
@@ -12,7 +12,7 @@ interface ICommunityIssuance { | |
// --- Functions --- | |
- function setAddresses(address _lqtyTokenAddress, address _stabilityPoolAddress) external; | |
+ function setAddresses(address _lqtyTokenAddress, address _stabilityPoolAddress, address _lqtyStakingAddress) external; | |
function issueLQTY() external returns (uint); | |
diff --git a/packages/contracts/contracts/Interfaces/ILQTYStaking.sol b/packages/contracts/contracts/Interfaces/ILQTYStaking.sol | |
index 3dfba6ba..d9d2f63b 100644 | |
--- a/packages/contracts/contracts/Interfaces/ILQTYStaking.sol | |
+++ b/packages/contracts/contracts/Interfaces/ILQTYStaking.sol | |
@@ -42,4 +42,12 @@ interface ILQTYStaking { | |
function getPendingETHGain(address _user) external view returns (uint); | |
function getPendingLUSDGain(address _user) external view returns (uint); | |
+ | |
+ function addEarning(address _user, uint _amount) external; | |
+ | |
+ function withdrawEarning(uint _amount) external; | |
+ | |
+ function withdrawableEarning(address _user) external view returns (uint, uint, uint); | |
+ | |
+ function earnedBalances(address _user) external view returns (uint, uint[2][] memory); | |
} | |
diff --git a/packages/contracts/contracts/LPRewards/Interfaces/IUnipool.sol b/packages/contracts/contracts/LPRewards/Interfaces/IUnipool.sol | |
index 1d0e26cc..a4b458bc 100644 | |
--- a/packages/contracts/contracts/LPRewards/Interfaces/IUnipool.sol | |
+++ b/packages/contracts/contracts/LPRewards/Interfaces/IUnipool.sol | |
@@ -4,7 +4,7 @@ pragma solidity 0.6.11; | |
interface IUnipool { | |
- function setParams(address _lqtyTokenAddress, address _uniTokenAddress, uint256 _duration) external; | |
+ function setParams(address _lqtyTokenAddress, address _uniTokenAddress, address _lqtyStakingAddress, uint256 _duration) external; | |
function lastTimeRewardApplicable() external view returns (uint256); | |
function rewardPerToken() external view returns (uint256); | |
function earned(address account) external view returns (uint256); | |
diff --git a/packages/contracts/contracts/LPRewards/Unipool.sol b/packages/contracts/contracts/LPRewards/Unipool.sol | |
index 74b1d9dc..942d4e06 100644 | |
--- a/packages/contracts/contracts/LPRewards/Unipool.sol | |
+++ b/packages/contracts/contracts/LPRewards/Unipool.sol | |
@@ -7,6 +7,7 @@ import "../Dependencies/SafeMath.sol"; | |
import "../Dependencies/Ownable.sol"; | |
import "../Dependencies/CheckContract.sol"; | |
import "../Interfaces/ILQTYToken.sol"; | |
+import "../Interfaces/ILQTYStaking.sol"; | |
import "./Dependencies/SafeERC20.sol"; | |
import "./Interfaces/ILPTokenWrapper.sol"; | |
import "./Interfaces/IUnipool.sol"; | |
@@ -76,6 +77,7 @@ contract Unipool is LPTokenWrapper, Ownable, CheckContract, IUnipool { | |
uint256 public duration; | |
ILQTYToken public lqtyToken; | |
+ ILQTYStaking public lqtyStakingAddress; | |
uint256 public periodFinish = 0; | |
uint256 public rewardRate = 0; | |
@@ -85,6 +87,7 @@ contract Unipool is LPTokenWrapper, Ownable, CheckContract, IUnipool { | |
mapping(address => uint256) public rewards; | |
event LQTYTokenAddressChanged(address _lqtyTokenAddress); | |
+ event LQTYStakingAddressChanged(address _lqtyStakingAddress); | |
event UniTokenAddressChanged(address _uniTokenAddress); | |
event RewardAdded(uint256 reward); | |
event Staked(address indexed user, uint256 amount); | |
@@ -95,6 +98,7 @@ contract Unipool is LPTokenWrapper, Ownable, CheckContract, IUnipool { | |
function setParams( | |
address _lqtyTokenAddress, | |
address _uniTokenAddress, | |
+ address _lqtyStakingAddress, | |
uint _duration | |
) | |
external | |
@@ -103,15 +107,21 @@ contract Unipool is LPTokenWrapper, Ownable, CheckContract, IUnipool { | |
{ | |
checkContract(_lqtyTokenAddress); | |
checkContract(_uniTokenAddress); | |
+ checkContract(_lqtyStakingAddress); | |
uniToken = IERC20(_uniTokenAddress); | |
lqtyToken = ILQTYToken(_lqtyTokenAddress); | |
+ lqtyStakingAddress = ILQTYStaking(_lqtyStakingAddress); | |
duration = _duration; | |
+ // LQTY staking as earning vesting should be allowed to transfer | |
+ lqtyToken.approve(_lqtyStakingAddress, uint(-1)); | |
+ | |
_notifyRewardAmount(lqtyToken.getLpRewardsEntitlement(), _duration); | |
emit LQTYTokenAddressChanged(_lqtyTokenAddress); | |
emit UniTokenAddressChanged(_uniTokenAddress); | |
+ emit LQTYStakingAddressChanged(_lqtyStakingAddress); | |
_renounceOwnership(); | |
} | |
@@ -186,7 +196,7 @@ contract Unipool is LPTokenWrapper, Ownable, CheckContract, IUnipool { | |
require(reward > 0, "Nothing to claim"); | |
rewards[msg.sender] = 0; | |
- lqtyToken.transfer(msg.sender, reward); | |
+ lqtyStakingAddress.addEarning(msg.sender, reward); | |
emit RewardPaid(msg.sender, reward); | |
} | |
diff --git a/packages/contracts/contracts/LQTY/CommunityIssuance.sol b/packages/contracts/contracts/LQTY/CommunityIssuance.sol | |
index 0919a4fd..8d3663a3 100644 | |
--- a/packages/contracts/contracts/LQTY/CommunityIssuance.sol | |
+++ b/packages/contracts/contracts/LQTY/CommunityIssuance.sol | |
@@ -3,6 +3,7 @@ | |
pragma solidity 0.6.11; | |
import "../Interfaces/ILQTYToken.sol"; | |
+import "../Interfaces/ILQTYStaking.sol"; | |
import "../Interfaces/ICommunityIssuance.sol"; | |
import "../Dependencies/BaseMath.sol"; | |
import "../Dependencies/LiquityMath.sol"; | |
@@ -40,11 +41,12 @@ contract CommunityIssuance is ICommunityIssuance, Ownable, CheckContract, BaseMa | |
* The community LQTY supply cap is the starting balance of the Community Issuance contract. | |
* It should be minted to this contract by LQTYToken, when the token is deployed. | |
* | |
- * Set to 32M (slightly less than 1/3) of total LQTY supply. | |
+ * Set to 35M of total LQTY supply. | |
*/ | |
- uint constant public LQTYSupplyCap = 32e24; // 32 million | |
+ uint constant public LQTYSupplyCap = 35e24; // 35 million | |
ILQTYToken public lqtyToken; | |
+ ILQTYStaking public lqtyStakingAddress; | |
address public stabilityPoolAddress; | |
@@ -55,6 +57,7 @@ contract CommunityIssuance is ICommunityIssuance, Ownable, CheckContract, BaseMa | |
event LQTYTokenAddressSet(address _lqtyTokenAddress); | |
event StabilityPoolAddressSet(address _stabilityPoolAddress); | |
+ event LQTYStakingAddressSet(address _lqtyStakingAddress); | |
event TotalLQTYIssuedUpdated(uint _totalLQTYIssued); | |
// --- Functions --- | |
@@ -66,7 +69,8 @@ contract CommunityIssuance is ICommunityIssuance, Ownable, CheckContract, BaseMa | |
function setAddresses | |
( | |
address _lqtyTokenAddress, | |
- address _stabilityPoolAddress | |
+ address _stabilityPoolAddress, | |
+ address _lqtyStakingAddress | |
) | |
external | |
onlyOwner | |
@@ -74,9 +78,14 @@ contract CommunityIssuance is ICommunityIssuance, Ownable, CheckContract, BaseMa | |
{ | |
checkContract(_lqtyTokenAddress); | |
checkContract(_stabilityPoolAddress); | |
+ checkContract(_lqtyStakingAddress); | |
lqtyToken = ILQTYToken(_lqtyTokenAddress); | |
stabilityPoolAddress = _stabilityPoolAddress; | |
+ lqtyStakingAddress = ILQTYStaking(_lqtyStakingAddress); | |
+ | |
+ // LQTY staking as earning vesting should be allowed to transfer | |
+ lqtyToken.approve(_lqtyStakingAddress, uint(-1)); | |
// When LQTYToken deployed, it should have transferred CommunityIssuance's LQTY entitlement | |
uint LQTYBalance = lqtyToken.balanceOf(address(this)); | |
@@ -84,6 +93,7 @@ contract CommunityIssuance is ICommunityIssuance, Ownable, CheckContract, BaseMa | |
emit LQTYTokenAddressSet(_lqtyTokenAddress); | |
emit StabilityPoolAddressSet(_stabilityPoolAddress); | |
+ emit LQTYStakingAddressSet(_lqtyStakingAddress); | |
_renounceOwnership(); | |
} | |
@@ -121,7 +131,7 @@ contract CommunityIssuance is ICommunityIssuance, Ownable, CheckContract, BaseMa | |
function sendLQTY(address _account, uint _LQTYamount) external override { | |
_requireCallerIsStabilityPool(); | |
- lqtyToken.transfer(_account, _LQTYamount); | |
+ lqtyStakingAddress.addEarning(_account, _LQTYamount); | |
} | |
// --- 'require' functions --- | |
diff --git a/packages/contracts/contracts/LQTY/LQTYStaking.sol b/packages/contracts/contracts/LQTY/LQTYStaking.sol | |
index 85e49058..41b0eb38 100644 | |
--- a/packages/contracts/contracts/LQTY/LQTYStaking.sol | |
+++ b/packages/contracts/contracts/LQTY/LQTYStaking.sol | |
@@ -32,6 +32,27 @@ contract LQTYStaking is ILQTYStaking, Ownable, CheckContract, BaseMath { | |
uint F_LUSD_Snapshot; | |
} | |
+ mapping (address => LockedBalance[]) private userEarnings; | |
+ | |
+ struct LockedBalance { | |
+ uint amount; | |
+ uint unlockTime; | |
+ } | |
+ | |
+ // Duration that rewards are streamed over | |
+ uint public constant REWARDS_DURATION = 86400 * 7; | |
+ // Duration of lock/earned penalty period | |
+ uint public constant LOCK_DURATION = REWARDS_DURATION * 13; | |
+ | |
+ mapping (address => Balances) private userBalances; | |
+ | |
+ struct Balances { | |
+ uint earned; | |
+ } | |
+ | |
+ // Total penalty | |
+ uint public totalBurned = 0; | |
+ | |
ILQTYToken public lqtyToken; | |
ILUSDToken public lusdToken; | |
@@ -55,6 +76,9 @@ contract LQTYStaking is ILQTYStaking, Ownable, CheckContract, BaseMath { | |
event EtherSent(address _account, uint _amount); | |
event StakerSnapshotsUpdated(address _staker, uint _F_ETH, uint _F_LUSD); | |
+ event EarningAdd(address indexed user, uint amount); | |
+ event EarningWithdraw(address indexed user, uint amount, uint penaltyAmount, uint totalBurned); | |
+ | |
// --- Functions --- | |
function setAddresses | |
@@ -244,4 +268,125 @@ contract LQTYStaking is ILQTYStaking, Ownable, CheckContract, BaseMath { | |
receive() external payable { | |
_requireCallerIsActivePool(); | |
} | |
+ | |
+ // --- Earning-related functions --- | |
+ | |
+ /* | |
+ * Add earning from other accounts, which will be locked for 3 months. | |
+ * Early exit is allowed, by 50% will be penalized. | |
+ */ | |
+ function addEarning(address _user, uint _amount) external override { | |
+ Balances storage bal = userBalances[_user]; | |
+ bal.earned = bal.earned.add(_amount); | |
+ | |
+ uint unlockTime = block.timestamp.div(REWARDS_DURATION).mul(REWARDS_DURATION).add(LOCK_DURATION); | |
+ LockedBalance[] storage earnings = userEarnings[_user]; | |
+ uint idx = earnings.length; | |
+ | |
+ if (idx == 0 || earnings[idx-1].unlockTime < unlockTime) { | |
+ earnings.push(LockedBalance({amount: _amount, unlockTime: unlockTime})); | |
+ } else { | |
+ earnings[idx-1].amount = earnings[idx-1].amount.add(_amount); | |
+ } | |
+ | |
+ // Actually transfer LQTY earnings to this contract | |
+ lqtyToken.transferFrom(msg.sender, address(this), _amount); | |
+ } | |
+ | |
+ /* | |
+ * Withdraw staked tokens. First withdraws unlocked tokens, then earned tokens. Withdrawing | |
+ * earned tokens before lock expiry will incur a 50% penalty to be burned. | |
+ */ | |
+ function withdrawEarning(uint _amount) external override { | |
+ require(_amount > 0, "Cannot withdraw 0"); | |
+ Balances storage bal = userBalances[msg.sender]; | |
+ uint penaltyAmount = 0; | |
+ | |
+ uint remaining = _amount; | |
+ bal.earned = bal.earned.sub(remaining); | |
+ for (uint i = 0; ; i++) { | |
+ uint earnedAmount = userEarnings[msg.sender][i].amount; | |
+ if (earnedAmount == 0) { | |
+ continue; | |
+ } | |
+ if (penaltyAmount == 0 && userEarnings[msg.sender][i].unlockTime > block.timestamp) { | |
+ penaltyAmount = remaining; | |
+ totalBurned = totalBurned.add(penaltyAmount); | |
+ require(bal.earned >= remaining, "Insufficient balance after penalty"); | |
+ bal.earned = bal.earned.sub(remaining); | |
+ if (bal.earned == 0) { | |
+ delete userEarnings[msg.sender]; | |
+ break; | |
+ } | |
+ remaining = remaining.mul(2); | |
+ } | |
+ if (remaining <= earnedAmount) { | |
+ userEarnings[msg.sender][i].amount = earnedAmount.sub(remaining); | |
+ break; | |
+ } else { | |
+ delete userEarnings[msg.sender][i]; | |
+ remaining = remaining.sub(earnedAmount); | |
+ } | |
+ } | |
+ | |
+ lqtyToken.transfer(msg.sender, _amount); | |
+ emit EarningWithdraw(msg.sender, _amount, penaltyAmount, totalBurned); | |
+ } | |
+ | |
+ // Final balance received and penalty balance paid by user upon exit | |
+ function withdrawableEarning | |
+ ( | |
+ address _user | |
+ ) | |
+ external | |
+ view | |
+ override | |
+ returns (uint withdrawableTotal, uint penalty, uint withdrawableNoPenalty) | |
+ { | |
+ Balances storage bal = userBalances[_user]; | |
+ if (bal.earned > 0) { | |
+ uint length = userEarnings[_user].length; | |
+ for (uint i = 0; i < length; i++) { | |
+ uint earnedAmount = userEarnings[_user][i].amount; | |
+ if (earnedAmount == 0) { | |
+ continue; | |
+ } | |
+ if (userEarnings[_user][i].unlockTime > block.timestamp) { | |
+ break; | |
+ } | |
+ withdrawableNoPenalty = withdrawableNoPenalty.add(earnedAmount); | |
+ } | |
+ | |
+ penalty = bal.earned.sub(withdrawableNoPenalty).div(2); | |
+ } | |
+ withdrawableTotal = bal.earned.sub(penalty); | |
+ return (withdrawableTotal, penalty, withdrawableNoPenalty); | |
+ } | |
+ | |
+ // Information on the "earned" balances of a user | |
+ // Earned balances may be withdrawn immediately for a 50% penalty | |
+ function earnedBalances | |
+ ( | |
+ address _user | |
+ ) | |
+ view | |
+ external | |
+ override | |
+ returns (uint total, uint[2][] memory earningsData) | |
+ { | |
+ LockedBalance[] storage earnings = userEarnings[_user]; | |
+ uint idx; | |
+ for (uint i = 0; i < earnings.length; i++) { | |
+ if (earnings[i].unlockTime > block.timestamp) { | |
+ if (idx == 0) { | |
+ earningsData = new uint[2][](earnings.length - i); | |
+ } | |
+ earningsData[idx][0] = earnings[i].amount; | |
+ earningsData[idx][1] = earnings[i].unlockTime; | |
+ idx++; | |
+ total = total.add(earnings[i].amount); | |
+ } | |
+ } | |
+ return (total, earningsData); | |
+ } | |
} | |
diff --git a/packages/contracts/contracts/LQTY/LQTYToken.sol b/packages/contracts/contracts/LQTY/LQTYToken.sol | |
index ed928295..7745e700 100644 | |
--- a/packages/contracts/contracts/LQTY/LQTYToken.sol | |
+++ b/packages/contracts/contracts/LQTY/LQTYToken.sol | |
@@ -52,8 +52,8 @@ contract LQTYToken is CheckContract, ILQTYToken { | |
// --- ERC20 Data --- | |
- string constant internal _NAME = "LQTY"; | |
- string constant internal _SYMBOL = "LQTY"; | |
+ string constant internal _NAME = "FLTY"; | |
+ string constant internal _SYMBOL = "FLTY"; | |
string constant internal _VERSION = "1"; | |
uint8 constant internal _DECIMALS = 18; | |
@@ -95,6 +95,10 @@ contract LQTYToken is CheckContract, ILQTYToken { | |
ILockupContractFactory public immutable lockupContractFactory; | |
+ // Controlling transfer switch | |
+ bool public transferPaused; | |
+ address public transferAdmin; | |
+ | |
// --- Events --- | |
event CommunityIssuanceAddressSet(address _communityIssuanceAddress); | |
@@ -135,23 +139,30 @@ contract LQTYToken is CheckContract, ILQTYToken { | |
// --- Initial LQTY allocations --- | |
- uint bountyEntitlement = _1_MILLION.mul(2); // Allocate 2 million for bounties/hackathons | |
+ uint bountyEntitlement = _1_MILLION.mul(5); // Allocate 5 million for bounties/marketing | |
_mint(_bountyAddress, bountyEntitlement); | |
- uint depositorsAndFrontEndsEntitlement = _1_MILLION.mul(32); // Allocate 32 million to the algorithmic issuance schedule | |
+ uint depositorsAndFrontEndsEntitlement = _1_MILLION.mul(35); // Allocate 35 million to the algorithmic issuance schedule | |
_mint(_communityIssuanceAddress, depositorsAndFrontEndsEntitlement); | |
- uint _lpRewardsEntitlement = _1_MILLION.mul(4).div(3); // Allocate 1.33 million for LP rewards | |
+ uint _lpRewardsEntitlement = _1_MILLION.mul(10); // Allocate half of 20 million for LP rewards for BNB/FLTY | |
lpRewardsEntitlement = _lpRewardsEntitlement; | |
_mint(_lpRewardsAddress, _lpRewardsEntitlement); | |
- // Allocate the remainder to the LQTY Multisig: (100 - 2 - 32 - 1.33) million = 64.66 million | |
+ // Allocate the remainder to the LQTY Multisig: (100 - 5 - 35 -10) million = 50 million: | |
+ // - 25% airdrop lockup | |
+ // - 15% team & development | |
+ // - 10% future LP rewards for stable coin pool | |
uint multisigEntitlement = _1_MILLION.mul(100) | |
.sub(bountyEntitlement) | |
.sub(depositorsAndFrontEndsEntitlement) | |
.sub(_lpRewardsEntitlement); | |
_mint(_multisigAddress, multisigEntitlement); | |
+ | |
+ // Set transfer admin for unpausing transfer | |
+ transferAdmin = msg.sender; | |
+ transferPaused = true; | |
} | |
// --- External functions --- | |
@@ -173,11 +184,6 @@ contract LQTYToken is CheckContract, ILQTYToken { | |
} | |
function transfer(address recipient, uint256 amount) external override returns (bool) { | |
- // Restrict the multisig's transfers in first year | |
- if (_callerIsMultisig() && _isFirstYear()) { | |
- _requireRecipientIsRegisteredLC(recipient); | |
- } | |
- | |
_requireValidRecipient(recipient); | |
// Otherwise, standard transfer functionality | |
@@ -226,6 +232,13 @@ contract LQTYToken is CheckContract, ILQTYToken { | |
_transfer(_sender, lqtyStakingAddress, _amount); | |
} | |
+ function unpause() external { | |
+ require(msg.sender == transferAdmin, "ERC20: invalid unpausing"); | |
+ transferPaused = false; | |
+ // Renounce transfer admin | |
+ transferAdmin = address(0); | |
+ } | |
+ | |
// --- EIP 2612 functionality --- | |
function domainSeparator() public view override returns (bytes32) { | |
@@ -249,13 +262,13 @@ contract LQTYToken is CheckContract, ILQTYToken { | |
external | |
override | |
{ | |
- require(deadline >= now, 'LQTY: expired deadline'); | |
+ require(deadline >= now, 'FLTY: expired deadline'); | |
bytes32 digest = keccak256(abi.encodePacked('\x19\x01', | |
domainSeparator(), keccak256(abi.encode( | |
_PERMIT_TYPEHASH, owner, spender, amount, | |
_nonces[owner]++, deadline)))); | |
address recoveredAddress = ecrecover(digest, v, r, s); | |
- require(recoveredAddress == owner, 'LQTY: invalid signature'); | |
+ require(recoveredAddress == owner, 'FLTY: invalid signature'); | |
_approve(owner, spender, amount); | |
} | |
@@ -278,6 +291,7 @@ contract LQTYToken is CheckContract, ILQTYToken { | |
function _transfer(address sender, address recipient, uint256 amount) internal { | |
require(sender != address(0), "ERC20: transfer from the zero address"); | |
require(recipient != address(0), "ERC20: transfer to the zero address"); | |
+ require(!transferPaused, "ERC20: transfer paused"); | |
_balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance"); | |
_balances[recipient] = _balances[recipient].add(amount); | |
@@ -316,30 +330,24 @@ contract LQTYToken is CheckContract, ILQTYToken { | |
require( | |
_recipient != address(0) && | |
_recipient != address(this), | |
- "LQTY: Cannot transfer tokens directly to the LQTY token contract or the zero address" | |
+ "FLTY: Cannot transfer tokens directly to the LQTY token contract or the zero address" | |
); | |
require( | |
- _recipient != communityIssuanceAddress && | |
- _recipient != lqtyStakingAddress, | |
- "LQTY: Cannot transfer tokens directly to the community issuance or staking contract" | |
+ _recipient != communityIssuanceAddress, | |
+ "FLTY: Cannot transfer tokens directly to the community issuance" | |
); | |
} | |
- function _requireRecipientIsRegisteredLC(address _recipient) internal view { | |
- require(lockupContractFactory.isRegisteredLockup(_recipient), | |
- "LQTYToken: recipient must be a LockupContract registered in the Factory"); | |
- } | |
- | |
function _requireSenderIsNotMultisig(address _sender) internal view { | |
- require(_sender != multisigAddress, "LQTYToken: sender must not be the multisig"); | |
+ require(_sender != multisigAddress, "FLTYToken: sender must not be the multisig"); | |
} | |
function _requireCallerIsNotMultisig() internal view { | |
- require(!_callerIsMultisig(), "LQTYToken: caller must not be the multisig"); | |
+ require(!_callerIsMultisig(), "FLTYToken: caller must not be the multisig"); | |
} | |
function _requireCallerIsLQTYStaking() internal view { | |
- require(msg.sender == lqtyStakingAddress, "LQTYToken: caller must be the LQTYStaking contract"); | |
+ require(msg.sender == lqtyStakingAddress, "FLTYToken: caller must be the FLTYStaking contract"); | |
} | |
// --- Optional functions --- | |
diff --git a/packages/contracts/contracts/LUSDToken.sol b/packages/contracts/contracts/LUSDToken.sol | |
index 2338ffae..b2c7b192 100644 | |
--- a/packages/contracts/contracts/LUSDToken.sol | |
+++ b/packages/contracts/contracts/LUSDToken.sol | |
@@ -28,8 +28,8 @@ contract LUSDToken is CheckContract, ILUSDToken { | |
using SafeMath for uint256; | |
uint256 private _totalSupply; | |
- string constant internal _NAME = "LUSD Stablecoin"; | |
- string constant internal _SYMBOL = "LUSD"; | |
+ string constant internal _NAME = "FLUSD Stablecoin"; | |
+ string constant internal _SYMBOL = "FLUSD"; | |
string constant internal _VERSION = "1"; | |
uint8 constant internal _DECIMALS = 18; | |
@@ -59,6 +59,10 @@ contract LUSDToken is CheckContract, ILUSDToken { | |
address public immutable stabilityPoolAddress; | |
address public immutable borrowerOperationsAddress; | |
+ // Controlling transfer switch | |
+ bool public transferPaused; | |
+ address public transferAdmin; | |
+ | |
// --- Events --- | |
event TroveManagerAddressChanged(address _troveManagerAddress); | |
event StabilityPoolAddressChanged(address _newStabilityPoolAddress); | |
@@ -92,6 +96,10 @@ contract LUSDToken is CheckContract, ILUSDToken { | |
_HASHED_VERSION = hashedVersion; | |
_CACHED_CHAIN_ID = _chainID(); | |
_CACHED_DOMAIN_SEPARATOR = _buildDomainSeparator(_TYPE_HASH, hashedName, hashedVersion); | |
+ | |
+ // Set transfer admin for unpausing transfer | |
+ transferAdmin = msg.sender; | |
+ transferPaused = true; | |
} | |
// --- Functions for intra-Liquity calls --- | |
@@ -116,6 +124,13 @@ contract LUSDToken is CheckContract, ILUSDToken { | |
_transfer(_poolAddress, _receiver, _amount); | |
} | |
+ function unpause() external { | |
+ require(msg.sender == transferAdmin, "ERC20: invalid unpausing"); | |
+ transferPaused = false; | |
+ // Renounce transfer admin | |
+ transferAdmin = address(0); | |
+ } | |
+ | |
// --- External functions --- | |
function totalSupply() external view override returns (uint256) { | |
@@ -181,13 +196,13 @@ contract LUSDToken is CheckContract, ILUSDToken { | |
external | |
override | |
{ | |
- require(deadline >= now, 'LUSD: expired deadline'); | |
+ require(deadline >= now, 'FLUSD: expired deadline'); | |
bytes32 digest = keccak256(abi.encodePacked('\x19\x01', | |
domainSeparator(), keccak256(abi.encode( | |
_PERMIT_TYPEHASH, owner, spender, amount, | |
_nonces[owner]++, deadline)))); | |
address recoveredAddress = ecrecover(digest, v, r, s); | |
- require(recoveredAddress == owner, 'LUSD: invalid signature'); | |
+ require(recoveredAddress == owner, 'FLUSD: invalid signature'); | |
_approve(owner, spender, amount); | |
} | |
@@ -213,6 +228,7 @@ contract LUSDToken is CheckContract, ILUSDToken { | |
function _transfer(address sender, address recipient, uint256 amount) internal { | |
assert(sender != address(0)); | |
assert(recipient != address(0)); | |
+ require(!transferPaused, "ERC20: transfer paused"); | |
_balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance"); | |
_balances[recipient] = _balances[recipient].add(amount); | |
@@ -221,6 +237,7 @@ contract LUSDToken is CheckContract, ILUSDToken { | |
function _mint(address account, uint256 amount) internal { | |
assert(account != address(0)); | |
+ require(!transferPaused, "ERC20: mint paused"); | |
_totalSupply = _totalSupply.add(amount); | |
_balances[account] = _balances[account].add(amount); | |
@@ -229,6 +246,7 @@ contract LUSDToken is CheckContract, ILUSDToken { | |
function _burn(address account, uint256 amount) internal { | |
assert(account != address(0)); | |
+ require(!transferPaused, "ERC20: burn paused"); | |
_balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance"); | |
_totalSupply = _totalSupply.sub(amount); | |
@@ -249,13 +267,13 @@ contract LUSDToken is CheckContract, ILUSDToken { | |
require( | |
_recipient != address(0) && | |
_recipient != address(this), | |
- "LUSD: Cannot transfer tokens directly to the LUSD token contract or the zero address" | |
+ "FLUSD: Cannot transfer tokens directly to the LUSD token contract or the zero address" | |
); | |
require( | |
_recipient != stabilityPoolAddress && | |
_recipient != troveManagerAddress && | |
_recipient != borrowerOperationsAddress, | |
- "LUSD: Cannot transfer tokens directly to the StabilityPool, TroveManager or BorrowerOps" | |
+ "FLUSD: Cannot transfer tokens directly to the StabilityPool, TroveManager or BorrowerOps" | |
); | |
} | |
@@ -268,18 +286,18 @@ contract LUSDToken is CheckContract, ILUSDToken { | |
msg.sender == borrowerOperationsAddress || | |
msg.sender == troveManagerAddress || | |
msg.sender == stabilityPoolAddress, | |
- "LUSD: Caller is neither BorrowerOperations nor TroveManager nor StabilityPool" | |
+ "FLUSD: Caller is neither BorrowerOperations nor TroveManager nor StabilityPool" | |
); | |
} | |
function _requireCallerIsStabilityPool() internal view { | |
- require(msg.sender == stabilityPoolAddress, "LUSD: Caller is not the StabilityPool"); | |
+ require(msg.sender == stabilityPoolAddress, "FLUSD: Caller is not the StabilityPool"); | |
} | |
function _requireCallerIsTroveMorSP() internal view { | |
require( | |
msg.sender == troveManagerAddress || msg.sender == stabilityPoolAddress, | |
- "LUSD: Caller is neither TroveManager nor StabilityPool"); | |
+ "FLUSD: Caller is neither TroveManager nor StabilityPool"); | |
} | |
// --- Optional functions --- | |
diff --git a/packages/contracts/contracts/PriceFeed.sol b/packages/contracts/contracts/PriceFeed.sol | |
index e0ee14e8..0e2cfc62 100644 | |
--- a/packages/contracts/contracts/PriceFeed.sol | |
+++ b/packages/contracts/contracts/PriceFeed.sol | |
@@ -79,6 +79,9 @@ contract PriceFeed is Ownable, CheckContract, BaseMath, IPriceFeed { | |
// The current status of the PricFeed, which determines the conditions for the next price fetch attempt | |
Status public status; | |
+ // Since tellor is not yet available on BSC, have an admin to switch tellor address once it's on | |
+ address tellorAdmin; | |
+ | |
event LastGoodPriceUpdated(uint _lastGoodPrice); | |
event PriceFeedStatusChanged(Status newStatus); | |
@@ -110,6 +113,14 @@ contract PriceFeed is Ownable, CheckContract, BaseMath, IPriceFeed { | |
_storeChainlinkPrice(chainlinkResponse); | |
_renounceOwnership(); | |
+ | |
+ // Have separate tellor admin address as owner | |
+ tellorAdmin = msg.sender; | |
+ } | |
+ | |
+ function updateTellor(address _tellorCallerAddress) public { | |
+ require(msg.sender == tellorAdmin, "PriceFeed: tellor can't only updated by tellor admin"); | |
+ tellorCaller = ITellorCaller(_tellorCallerAddress); | |
} | |
// --- Functions --- | |
diff --git a/packages/contracts/contracts/StabilityPool.sol b/packages/contracts/contracts/StabilityPool.sol | |
index b97756cc..91b566ac 100644 | |
--- a/packages/contracts/contracts/StabilityPool.sol | |
+++ b/packages/contracts/contracts/StabilityPool.sol | |
@@ -234,6 +234,9 @@ contract StabilityPool is LiquityBase, Ownable, CheckContract, IStabilityPool { | |
uint public lastETHError_Offset; | |
uint public lastLUSDLossError_Offset; | |
+ // Control flag to allow / disallow frontend kickback | |
+ bool public allowFrontEndKickback = true; | |
+ | |
// --- Events --- | |
event StabilityPoolETHBalanceUpdated(uint _newBalance); | |
@@ -269,6 +272,11 @@ contract StabilityPool is LiquityBase, Ownable, CheckContract, IStabilityPool { | |
// --- Contract setters --- | |
+ // Must be called before calling `setAddresses` | |
+ function setAllowFrontEndKickback(bool _allowFrontEndKickback) external onlyOwner { | |
+ allowFrontEndKickback = _allowFrontEndKickback; | |
+ } | |
+ | |
function setAddresses( | |
address _borrowerOperationsAddress, | |
address _troveManagerAddress, | |
@@ -852,6 +860,10 @@ contract StabilityPool is LiquityBase, Ownable, CheckContract, IStabilityPool { | |
// Front end makes a one-time selection of kickback rate upon registering | |
function registerFrontEnd(uint _kickbackRate) external override { | |
+ if (!allowFrontEndKickback) { | |
+ // No-op | |
+ return; | |
+ } | |
_requireFrontEndNotRegistered(msg.sender); | |
_requireUserHasNoDeposit(msg.sender); | |
_requireValidKickbackRate(_kickbackRate); | |
diff --git a/packages/contracts/contracts/TestContracts/NoOpTellor.sol b/packages/contracts/contracts/TestContracts/NoOpTellor.sol | |
new file mode 100644 | |
index 00000000..30e4a40b | |
--- /dev/null | |
+++ b/packages/contracts/contracts/TestContracts/NoOpTellor.sol | |
@@ -0,0 +1,20 @@ | |
+// SPDX-License-Identifier: MIT | |
+ | |
+pragma solidity 0.6.11; | |
+ | |
+ | |
+contract NoOpTellor { | |
+ // --- Mock data reporting functions --- | |
+ | |
+ function getTimestampbyRequestIDandIndex(uint, uint) external pure returns (uint) { | |
+ return 0; | |
+ } | |
+ | |
+ function getNewValueCountbyRequestId(uint) external pure returns (uint) { | |
+ return 1; | |
+ } | |
+ | |
+ function retrieveData(uint256, uint256) external pure returns (uint256) { | |
+ return 0; | |
+ } | |
+} | |
diff --git a/packages/contracts/contracts/TestContracts/NonPayable.sol b/packages/contracts/contracts/TestContracts/NonPayable.sol | |
index cc22d779..30d1e331 100644 | |
--- a/packages/contracts/contracts/TestContracts/NonPayable.sol | |
+++ b/packages/contracts/contracts/TestContracts/NonPayable.sol | |
@@ -21,4 +21,9 @@ contract NonPayable { | |
receive() external payable { | |
require(isPayable); | |
} | |
+ | |
+ function addEarning(address _user, uint _amount) external { | |
+ // No-op | |
+ } | |
+ | |
} | |
diff --git a/packages/contracts/contracts/TroveManager.sol b/packages/contracts/contracts/TroveManager.sol | |
index 147e4335..4b8aeefc 100644 | |
--- a/packages/contracts/contracts/TroveManager.sol | |
+++ b/packages/contracts/contracts/TroveManager.sol | |
@@ -48,7 +48,7 @@ contract TroveManager is LiquityBase, Ownable, CheckContract, ITroveManager { | |
uint constant public MAX_BORROWING_FEE = DECIMAL_PRECISION / 100 * 5; // 5% | |
// During bootsrap period redemptions are not allowed | |
- uint constant public BOOTSTRAP_PERIOD = 14 days; | |
+ uint constant public BOOTSTRAP_PERIOD = 10 days; | |
/* | |
* BETA: 18 digit decimal. Parameter by which to divide the redeemed fraction, in order to calc the new base rate from a redemption. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment