{"componentChunkName":"component---src-pages-sips-sip-markdown-remark-frontmatter-sip-tsx","path":"/sips/sip-77/","result":{"data":{"markdownRemark":{"fileAbsolutePath":"/vercel/path0/content/sips/sip-77.md","frontmatter":{"sip":77,"sccp":null,"title":"StakingRewards bug fix's and Pausable stake()","network":"Ethereum","author":"Clinton Ennis (@hav-noms), Anton Jurisevic (@zyzek)","type":"Governance","proposal":null,"implementor":null,"release":null,"created":"2020-08-06T00:00:00.000Z","updated":null,"status":"Implemented"},"html":"<!--You can leave these HTML comments in your merged SIP and delete the visible duplicate text guides, they will not appear and may be helpful to refer to if you edit it again. This is the suggested template for new SIPs. Note that an SIP number will be assigned by an editor. When opening a pull request to submit your SIP, please use an abbreviated title in the filename, `sip-draft_title_abbrev.md`. The title should be 44 characters or less.-->\n<h2 id=\"simple-summary\" style=\"position:relative;\"><a href=\"#simple-summary\" aria-label=\"simple summary permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Simple Summary</h2>\n<!--\"If you can't explain it simply, you don't understand it well enough.\" Simply describe the outcome the proposed changes intends to achieve. This should be non-technical and accessible to a casual community member.-->\n<p><code>stake()</code> needs to be pausable for completed incentives and two bug fixes.</p>\n<h2 id=\"abstract\" style=\"position:relative;\"><a href=\"#abstract\" aria-label=\"abstract permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Abstract</h2>\n<!--A short (~200 word) description of the proposed change, the abstract should clearly describe the proposed change. This is what *will* be done if the SIP is implemented, not *why* it should be done or *how* it will be done. If the SIP proposes deploying a new contract, write, \"we propose to deploy a new contract that will do x\".-->\n<p>Enhancements include:</p>\n<ul>\n<li>Inheriting <code>Pausable</code> contract and add <code>notPaused</code> modifer to <code>stake()</code> to prevent staking into deprecated pools</li>\n<li>Fix a potential overflow bug in the reward notification function reported by samcsun</li>\n<li>Fix to <code>setRewardsDuration</code> to allow <code>rewardsDuration</code> to be updated after the initial setting</li>\n</ul>\n<h2 id=\"motivation\" style=\"position:relative;\"><a href=\"#motivation\" aria-label=\"motivation permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Motivation</h2>\n<!--This is the problem statement. This is the *why* of the SIP. It should clearly explain *why* the current state of the protocol is inadequate.  It is critical that you explain *why* the change is needed, if the SIP proposes changing how something is calculated, you must address *why* the current calculation is inaccurate or wrong. This is not the place to describe how the SIP will address the issue!-->\n<h3 id=\"pause-stake-when-rewards-completed\" style=\"position:relative;\"><a href=\"#pause-stake-when-rewards-completed\" aria-label=\"pause stake when rewards completed permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Pause stake when rewards completed</h3>\n<p>When a <code>StakingRewards</code> campaign has completed the contract needs to prevent anyone from staking into it. The staker will not accrue any rewards and can cause blocking issues with inverse Synths that need to be purged so that they can be relanced.\nAdding <code>Pausable.sol</code> and modifier <code>notPaused</code> to <code>stake()</code> will allow the admin to set <code>paused</code> to <code>true</code> preventing anyone from staking. <code>SelfDestructible</code> has not been implemented and given the amount of value in these contracts probably best not to implement.</p>\n<h3 id=\"potential-overflow-bug-fix\" style=\"position:relative;\"><a href=\"#potential-overflow-bug-fix\" aria-label=\"potential overflow bug fix permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Potential overflow bug fix</h3>\n<h4 id=\"summary\" style=\"position:relative;\"><a href=\"#summary\" aria-label=\"summary permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Summary</h4>\n<p>There is a multiplication overflow that can occur inside the rewardPerToken function, on <a href=\"https://github.com/Synthetixio/synthetix/blob/c4dd4413cbbd3c0b40dfee2f9119af2dcb6a82e5/contracts/StakingRewards.sol#L66\">line 66</a>:</p>\n<pre><code>lastTimeRewardApplicable().sub(lastUpdateTime).mul(rewardRate).mul(1e18).div(_totalSupply)\n</code></pre>\n<p>An overflow occurs whenever <code>rewardRate >= 2^256 / (10^18 * (lastTimeRewardApplicable() - lastUpdateTime))</code>.</p>\n<p>This can happen when the updateReward modifier is invoked, which will cause the following functions to revert:</p>\n<ul>\n<li><code>earned</code></li>\n<li><code>stake</code></li>\n<li><code>withdraw</code></li>\n<li><code>getReward</code></li>\n<li><code>exit</code></li>\n<li><code>notifyRewardAmount</code></li>\n</ul>\n<p>The reward rate is set inside <code>notifyRewardAmount</code>, if a value that is too large is provided to the function.\nOf particular note is that <code>notifyRewardAmount</code> is itself affected by this problem, which means that if the provided\nreward is incorrect, then the problem is unrecoverable.</p>\n<h4 id=\"solution\" style=\"position:relative;\"><a href=\"#solution\" aria-label=\"solution permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Solution</h4>\n<p>The <code>notifyRewardAmount</code> transaction should be reverted if a value greater than <code>2^256 / 10^18</code> is provided.\nAs an additional safety mechanism, this value will be required to be no greater than the remaining\nbalance of the rewards token in the contract. This will both prevent the overflow, and also provide an additional check\nthat the reward rate is being set to a value in the appropriate range (for example, no extra/missing zeroes).</p>\n<h4 id=\"details\" style=\"position:relative;\"><a href=\"#details\" aria-label=\"details permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Details</h4>\n<p>Specifically, this problem occurs when <code>rewardRate</code> is too high; it is set inside the <code>notifyRewardAmount</code> function on\nlines <a href=\"https://github.com/Synthetixio/synthetix/blob/c4dd4413cbbd3c0b40dfee2f9119af2dcb6a82e5/contracts/StakingRewards.sol#L114\">114</a> and <a href=\"https://github.com/Synthetixio/synthetix/blob/c4dd4413cbbd3c0b40dfee2f9119af2dcb6a82e5/contracts/StakingRewards.sol#L118\">118</a>.</p>\n<pre><code>rewardRate = floor(reward / rewardsDuration) = (reward - k) / rewardsDuration\n</code></pre>\n<p>for some <code>0 &#x3C;= k &#x3C; rewardsDuration</code>.</p>\n<p>For the bug to occur, we need:</p>\n<pre><code>(reward - k) / rewardsDuration >= 2^256 / (10^18 * (lastTimeRewardApplicable - lastUpdateTime))\nreward                         >= rewardsDuration * 2^256 / (10^18 * (lastTimeRewardApplicable - lastUpdateTime)) + k\n</code></pre>\n<p>Hence, we can ensure the bug does not occur if we force:</p>\n<pre><code>reward &#x3C; rewardsDuration * 2^256 / (10^18 * (lastTimeRewardApplicable - lastUpdateTime))\n</code></pre>\n<p>So we should constrain <code>reward</code> to be less than the minimum value of the RHS.</p>\n<p>The smallest possible value of <code>lastUpdateTime</code> is the block timestamp when <code>notifyRewardAmount</code> was last called.\nThe largest possible value of <code>lastTimeRewardApplicable</code> is <code>periodFinish</code>,\nand <code>periodFinish = notificationBlock.timestamp + rewardsDuration</code> (<a href=\"https://github.com/Synthetixio/synthetix/blob/c4dd4413cbbd3c0b40dfee2f9119af2dcb6a82e5/contracts/StakingRewards.sol#L121\">line 121</a>).\nPutting these together we have:</p>\n<pre><code>(lastTimeRewardApplicable - lastUpdateTime) &#x3C;= rewardsDuration\n</code></pre>\n<p>Ergo, we need:</p>\n<pre><code>reward &#x3C; rewardsDuration * 2^256 / (10^18 * rewardsDuration)\n\t                     = 2^256 / 10^18\n</code></pre>\n<p>So the problem will not emerge whenever we require</p>\n<pre><code>    reward &#x3C; uint(-1) / UNIT\n</code></pre>\n<h3 id=\"fix-to-setrewardsduration-to-allow-updates-after-the-initial-setting\" style=\"position:relative;\"><a href=\"#fix-to-setrewardsduration-to-allow-updates-after-the-initial-setting\" aria-label=\"fix to setrewardsduration to allow updates after the initial setting permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Fix to setRewardsDuration to allow updates after the initial setting</h3>\n<p><code>setRewardsDuration</code> was intended to allow the <code>rewardsDuration</code> to be set after the duration had completed. However a flaw in the require meant it could be changed after the initial setting.</p>\n<p>Current code</p>\n<pre><code>require(periodFinish == 0 || block.timestamp > periodFinish);\n</code></pre>\n<p>Proposed change</p>\n<pre><code>require(block.timestamp > periodFinish);\n</code></pre>\n<h3 id=\"technical-specification\" style=\"position:relative;\"><a href=\"#technical-specification\" aria-label=\"technical specification permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Technical Specification</h3>\n<!--The technical specification should outline the public API of the changes proposed. That is, changes to any of the interfaces Synthetix currently exposes or the creations of new ones.-->\n<ul>\n<li>Inherit the <code>Pausable.sol</code> contract and add modifier <code>notPaused</code> to <code>stake()</code></li>\n<li>Revert the <code>notifyRewardAmount</code> transaction if the computer reward rate would pay out more than the balance of the contract over the reward period.</li>\n<li>Change the <code>require</code> in <code>setRewardsDuration</code> to only check the period has finished</li>\n</ul>\n<h3 id=\"test-cases\" style=\"position:relative;\"><a href=\"#test-cases\" aria-label=\"test cases permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Test Cases</h3>\n<!--Test cases for an implementation are mandatory for SIPs but can be included with the implementation..-->\n<ul>\n<li>Pausable\n<ul>\n<li>should revert when stake is called when paused is true</li>\n<li>should allow a call to stake to succeed when paused is false</li>\n</ul>\n</li>\n<li>Overflow bugfix\n<ul>\n<li>should revert <code>notifyRewardAmount</code> if reward is greater than the contract balance</li>\n<li>should revert <code>notifyRewardAmount</code> if reward plus any leftover from the previous period is greater than the contract balance</li>\n<li>should not revert <code>notifyRewardAmount</code> if reward is equal to the contract balance</li>\n</ul>\n</li>\n<li>setRewardsDuration bug fix\n<ul>\n<li>should revert when setting setRewardsDuration before the period has finished</li>\n<li>should update rewardsDuration when calling setRewardsDuration after the rewards period has finished</li>\n</ul>\n</li>\n</ul>\n<h3 id=\"configurable-values-via-sccp\" style=\"position:relative;\"><a href=\"#configurable-values-via-sccp\" aria-label=\"configurable values via sccp permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Configurable Values (Via SCCP)</h3>\n<!--Please list all values configurable via SCCP under this implementation.-->\n<p>Please list all values configurable via SCCP under this implementation.</p>\n<h2 id=\"copyright\" style=\"position:relative;\"><a href=\"#copyright\" aria-label=\"copyright permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Copyright</h2>\n<p>Copyright and related rights waived via <a href=\"https://creativecommons.org/publicdomain/zero/1.0/\">CC0</a>.</p>"}},"pageContext":{"id":"5cb353ea-5892-5de1-9e13-cfee30bddd7e","frontmatter__sip":77,"__params":{"frontmatter__sip":"77"}}},"staticQueryHashes":[]}