Skip to main content

Postmortem for medium severity bug identified on May 24, 2022.

author - jango

PR: https://github.com/jbx-protocol/juice-contracts-v2/pull/268

Updated PR #1: https://github.com/jbx-protocol/juice-contracts-v2/pull/271

Updated PR #2: https://github.com/jbx-protocol/juice-contracts-v2/pull/274

Summary

A bug triggered by two successive reconfigurations in a rolled-over funding cycle would result in an unexpected state for the project's funding cycle. No fund were at risk or lost. The issue is now mitigated.

Overview

This bug was stumbled upon by 0xSTVG and confirmed by Jango in a Discord message here: https://discord.com/channels/775859454780244028/876252266594721873/978704562540122172 (Screenshots attached at the bottom).

The Pull Request that fixes the bug can be found here: https://github.com/jbx-protocol/juice-contracts-v2/pull/265

Severity

No funds were immediately at risk. The bug made it possible for projects to end up in an unexpected funding cycle state, which would interrupt regular treasury operating behavior and allow projects to bypass funding cycle duration constraints.

Behavior

To recreate the bug, follow these steps:

  • Configure a funding cycle (FC#N) with a duration.
  • Let the FC#N rollover to another funding cycle (FC#N+1). This step is necessary.
  • Send a transaction to reconfigure the next funding cycle (FC#N+2) before FC#N+1 expires.
  • Send another transaction to reconfigure the next funding cycle (FC#N+2) before FC#N+1 expires.

The expected behavior is that FC#N+1 is still the current funding cycle (JBFundingCycleStore.getCurrentOf(...)), and that the queued funding cycle is the latest configuration of FC#N+2 (JBFundingCycleStore.getQueuedOf(...)).

The unexpected behavior that takes affect is that the first reconfiguration of FC#N+2 immediately becomes the current funding cycle (JBFundingCycleStore.getCurrentOf(...)) once the second reconfiguration transaction succeeds.

Specifics

The bug was caused by the lack of an OR condition in an if statement in the _configureIntrinsicPropertiesFor of the JBFundingCycleStore. See this pull request for the code specifics and accompanying added tests https://github.com/jbx-protocol/juice-contracts-v2/pull/265.

The fix was redeployed on May 25, 2022 alongside changes merged from the following PRs:

https://github.com/jbx-protocol/juice-contracts-v2/pull/257 https://github.com/jbx-protocol/juice-contracts-v2/pull/262 https://github.com/jbx-protocol/juice-contracts-v2/pull/264 https://github.com/jbx-protocol/juice-contracts-v2/pull/266

The updated artificats can be found in this PR:

https://github.com/jbx-protocol/juice-contracts-v2/pull/267

A new JBFundingCycleStore was deployed on May 25, 2022. All contracts that dependend on the old JBFundingCycleStore or upstream dependencies were also redeployed, including JBDirectory, JBController, JBETHPaymentTerminal, JBSingleTokenPaymentTerminalStore, JBSplitsStore, and JBTokenStore.

The addresses of old contracts are as follows:

  • JBDirectory: 0xd73D3Df051f6C7fa9e5bDC2fd71ecD3bc835C808
  • JBController: 0x43780E780DF2bD1e4d955d3d4b577a490841241A
  • JBETHPaymentTerminal: 0x2109e1CaF001980C318Ad7efdE8Fc204a844273b
  • JBSingleTokenPaymentTerminalStore: 0x0fF58316f44D53ec1bA2b9D07f163Bd0d9270794
  • JBSplitsStore: 0x32bb71C6DbD6A1b3A37394565872D0eB7fF3846D
  • JBTokenStore: 0x9c54a10a35bF8cC8bF4AE52198c782c5681c9470

The addresses of the new contracts are as follow:

  • JBDirectory: 0xf2e11aAeE51D34921f9397731f8fa16E35ed4088
  • JBController: 0x833d3996D3EF1b34320F56ac97c899065A3Cb2EF
  • JBETHPaymentTerminal: 0x5a92f8aa0d3e656aE209E99C5B77B1ca464Ae1a9
  • JBSingleTokenPaymentTerminalStore: 0x31e8B24E93e807E4a82960f4181ee37c7b04CcCd
  • JBSplitsStore: 0x2880B17aA444666a296Ab2866968e1bbE94Dd7FC
  • JBTokenStore: 0xC42f85261aEbd06edd0615005954063B4Fd032aB

The old contracts and interfaces were kept, and new ones were added alongside. For contracts that were updated, there are now a 1.sol and 2.sol files under a folder identified by the contract's name or interface's name. Similarly, the new deployment artifacts were added alongside the old versions suffixed by _2.json.

Given the nascency of the project and that external system depending on this repository's artifacts are known, once there is no longer a need to reference the old contracts in the repository to resolve potential migrations, the 1.sol files can safely be removed and _2.json artifacts can overwrite the plain versions to minimize complexity for future contributors, auditors, and audiences in general.

Update May 28, 2022

The folder structure has been flattened, and the contracts redeployed within their new repository context. The updated contract addresses are as follows:

  • JBDirectory: 0x041D9425F7755AbB9965C4B2144da87b14d21497
  • JBController: 0x869034F16049580335981095690c33239d4cCa80
  • JBETHPaymentTerminal: 0x7E14A45bbe8BbcbadC0f8A3EFAbE6563a00aB720
  • JBSingleTokenPaymentTerminalStore: 0x59E4Be742bB745A24DdccAe6A520528Fb56d8290
  • JBSplitsStore: 0xC9aB3626ACB308C48feb4833B9d9F6ED9111AEce
  • JBTokenStore: 0x0E7714Ec817F07B792df62b4BE186c0F49bE67e9

The repo should no longer change.

Update May 29, 2022

In order to prevent projects to accidentally freeze their funding cycles by adding a ballot contract that does not conform to IJBFundingCycleBallot, an update was proposed before initiating the recourse for this postmortem. The updated contract addresses are as follows:

  • JBDirectory: 0xCc8f7a89d89c2AB3559f484E0C656423E979ac9C
  • JBFundingCycleStore: 0x6B8e01DAA8A61b544F96d2738893E05D04BF1D12
  • JBController: 0x4e3ef8AFCC2B52E4e704f4c8d9B7E7948F651351
  • JBETHPaymentTerminal: 0x7Ae63FBa045Fec7CaE1a75cF7Aa14183483b8397
  • JBSingleTokenPaymentTerminalStore: 0x96a594ABE6B910E05E486b63B32fFe29DA5d33f7
  • JBSplitsStore: 0xFBE1075826B7FFd898cf8D944885ba6a8D714A7F
  • JBTokenStore: 0xCBB8e16d998161AdB20465830107ca298995f371
  • JB3DayReconfigurationBufferBallot: 0x4b9f876c7Fc5f6DEF8991fDe639b2C812a85Fb12
  • JB7DayReconfigurationBufferBallot: 0x642EFF5259624FD09D021AB764a4b47d1DbD5770

The repo should no longer change.

Update May 31, 2022

Deployed Multipay contract. Ran script with tag 3.

Code: https://github.com/jbx-protocol/juice-contracts-v2/pull/276 Commit: https://github.com/jbx-protocol/juice-contracts-v2/pull/276/commits/76346c80e5a9c38998f461f2ebe8765d57e252ea Multipay contract address: 0x65E114702215323671Ce23cb232aE8C74D623861 Transaction: https://etherscan.io/tx/0xba5d115d434208eb64cc582624f532388d8629ff37998ba104f2ec0271808220

Recourse

Projects do not have to re-mint their ERC721 instance in JBProjects, nor do they have to re-issue their ERC-20 tokens. Projects can move their already-issued ERC20's from the old JBTokenStore to the new JBTokenStore using JBTokenStore.changeFor(...) transactions as follows:

  • On the old JBTokenStore, call changeFor(...) with no new token (empty address), and the address of the new JBTokenStore as the _newOwner.
  • On the new JBTokenStore, call changeFor(...) passing the token's address.

In order to use the updated contracts, projects must re-instantiate their funding cycles, re-distribute tokens to their holders, and move funds out of the current terminal contracts and optionally into their updated payment terminal once all other steps are complete.

They can re-instantiate their funding cycles as follows:

  • On the new JBController, call launchFundingCyclesFor(...) with the desired funding cycle properties along with the address of the new JBETHPaymentTerminal where funds will now be accepted.

Projects can re-distribute tokens to their holders as follows:

  • While following the steps above to re-instantiate funding cycles, begin with a funding cycle with no duration that allows token minting.
  • Mint a batch of tokens commensurate to sum of tokens that should be distributed to previous holders.
    • If a project's ERC-20 are being ported over using the changeFor(...) strategy described above, do not include ERC-20 balances in this calculation. Only include unclaimed balances as the ERC-20 balance will carry over once ported.
  • Distribute the tokens to previous holders manually, via an airdrop, or via a 1:1 exchange.
  • Reconfigure the project's funding cycle as desired.

If a project's treasury funds are greater than its current distribution limit, they can remove funds out of the current payment terminal by reconfiguring their next funding cycle to raise the distribution limit so that it encompasses all of the treasury's funds, routing them to an address that will facilitate their transfer to the updated payment terminal via a call to addToBalance(...).

Update May 28, 2022

Given the relatively few projects and project token holders that are in need of taking action to mitigate risks due to the bug, JuiceboxDAO contributors are encouraging projects to re-issue their ERC-20s, and will recreate each project's activity and token distribution with its own funds. Projects will no longer need to concern themselves with re-distributing tokens.

References