Skip to content

Instantly share code, notes, and snippets.

@jfluity
Last active May 2, 2021 03:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jfluity/32216082afa16a5420471088df228a37 to your computer and use it in GitHub Desktop.
Save jfluity/32216082afa16a5420471088df228a37 to your computer and use it in GitHub Desktop.
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