Proposal: OpenZeppelin Security Partnership - 2022 Q2 Adjustment

Simple Summary

As outlined in the original OpenZeppelin partnership proposal, this governance proposal will update the ContributorCompSpeed at the beginning of the next quarter. This also includes a migration to using Sablier streams.

Background

Starting on Dec 21st, 2021, OpenZeppelin was selected to offer the Compound DAO security services including continuous audit, security advisory, and monitoring.

OpenZeppelin has finished auditing the Compound Deployed Contracts and finalized the CToken Refactor audit (details here). On the Security Advisory services, OpenZeppelin has been focusing on improving the security of the Proposal Process and Asset Listings. Finally, on Security Monitoring, OpenZeppelin is aiming to release an initial solution by the end of March.

You can see more details on our progress in the January and February Security Updates. We’ll also have a more detailed Update of our Partnership available by the end of next week for the month of March.

Replacing Existing Contributor Grant with Sablier Stream

As outlined in the original proposal, at the start of every quarter OpenZeppelin will create a proposal to update the service fee payment in accordance with the formula outlined in the proposal.

As it is the beginning of a new quarter, OpenZeppelin is updating its streaming grant and replacing its existing COMP stream with a Sablier stream. This governance proposal sets the OpenZeppelin ContributorCompSpeed to zero and sets up a Sablier stream to OpenZeppelin instead.

Specification

As of March 23, 2022, the average 30 day $USD/COMP VWAP is $112.00, so based on the Quarterly Retainer Fee formula the quarterly fee for the next quarter is 8,928.571429 COMP tokens.

The start date for this new fee will be 04/01/2022 0:00:00 and the end date will be 07/01/2022 0:00:00 GMT. At the end of this period we would submit a new proposal for the following quarter with an updated retainer fee calculation.

5 Likes

Please vote YES on our governance proposal here once it passes the review stage

6 Likes

PSA: We’ll be cancelling proposal 94 due to a timing issue with the Sablier stream. The startDate for the stream is set for AFTER the proposal can be executed so the function will revert. We’ll be cancelling the current proposal and resubmitting a new one on Monday.

Big thanks to @arr00 for pointing out the issue.

2 Likes

The new proposal is here: Proposal 95

1 Like

Hi everyone,

Unfortunately, our second compensation proposal has also run into a timing issue with the Sablier stream. The proposal was not queued in the Timelock with enough time to execute by the startDate. Given that this is our third attempt, I want to take the time to explain the issue we’re facing in more detail and show how we’re planning to address it for a final time. Hopefully, this will also be useful for others that are using Sablier in future Compound proposals.

Sablier’s createSteam() Timing Issue Explained

As explained in a previous post, Sablier is a protocol for creating real-time payment streams within a given time frame. Sablier has been proposed as a more robust replacement for the setContributorCompSpeed function that is currently used for streaming payments to Compound contributors and vendors due to Sablier’s ability to set specific durations and amounts, as opposed to a continuous stream that must be directly turned off by governance.

To migrate a stream to Sablier through a Compound proposal, the process appears simple. You can see an example of a successful migration with Gauntlet’s proposal 82 with each step summarized below:

  1. End the setContributorCompSpeed() stream by setting the value to 0 (if applicable)
  2. Give the Sablier contract approval to access the necessary amount of COMP
  3. Call createStream() on the Sablier contract and pass it the appropriate parameters

The issue that has come up multiple times now is with the third step when calling createStream() which accepts 5 parameters as seen below. In addition to the payment details of the recipient, deposit amount and tokenAddress, the stream must know when the startTime and stopTime are for the stream.

function createStream(address recipient, uint256 deposit, address tokenAddress, uint256 startTime, uint256 stopTime) public returns (uint256)

{ …
   require(startTime >= block.timestamp, "start time before block.timestamp");
...

Most importantly, the startTime must be set AFTER the function is called and will otherwise revert. While this seems like an obvious requirement, it becomes more complicated to fulfill when going through a 7-day governance process before execution can occur. Even if the original startTime is set to be longer than 7 days, any delay in the proposal’s timeline runs the risk of missing this deadline.

In OpenZeppelin’s case, our first proposal was submitted a day later than originally planned when we created the startTime and the parameter was not updated accordingly. This issue was raised by @Arr00 during the Review stage and so we canceled this proposal before voting began. With the second proposal, we double-checked that we had an extra day to execute the proposal before the startTime deadline. However, we failed to realize that any delay in queuing the proposal after it passed would also cause the same issue. The proposal passed its voting stage over the weekend while the team was not actively watching it. As a result, it was not queued in the Timelock with enough time to execute before the startTime.

While this is ultimately a user error on our part, we are not alone as other Compound proposals, namely Proposal 79, ran into the same timing problem as well. It’s important for other Compound community members to be aware of how Sablier works to ensure it does not result in other failed proposals. We would also encourage the Sablier team to consider design changes in future versions of their protocol that are more accommodating for time-variable governance proposals.

Our Plan for a Third Proposal

Ultimately, OpenZeppelin bears responsibility for failing to account for these timing concerns in both proposals submitted. Both myself and our operations team went through an internal process to check that the accounts and amount details were correct, but we failed to fully account for meeting the Sablier stream’s startTime requirement in both cases. Given that this has occurred twice, we owe the community a detailed plan for a successful third proposal.

First of all, we intend to refund the gas costs of all 21 voters on Proposal 95 to ensure that our mistakes here don’t discourage community participation. It’s important to note that this is not meant as an encouragement or expectation that these same voters must vote YES on future OpenZeppelin proposals. While we feel fortunate not to have had a single NO vote on this proposal, we would be refunding those accounts as well if they existed.

In our third proposal’s createStream parameters, we will be providing ample buffer time for the startTime to fall comfortably after the proposal can be executed. This will include testing the startTime parameter prior to deployment of the proposal to ensure that a repeat of the first proposal is not possible. We’ll also be adjusting the compensation amount to account for the time that the old stream will continue running in Q2.

To address the issue of timely queuing and execution, we’ll be using a tool we frankly should have been using from the start, Defender Autotasks. Autotasks are a robust feature that allows web3 developers to run code snippets in response to a schedule, webhook or on-chain transaction. In this case, we’ll be configuring an Autotask that will regularly check the status of our proposal. Once it reaches the appropriate stages, our Autotask will immediately call the Queue function and then later the Execute function for the proposal. By eliminating the need for human intervention, we’ll ensure no time is lost between stages.

Given that this sort of Autotask would be useful for automating the lifecycle of other proposals, we plan to eventually publish this Autotask for the Compound community to use themselves. With a free-tier account on Defender, a Compound user will be able to setup Autotasks to automate their own proposals and other on-chain operations to easily prevent issues with Sablier stream proposals and any other time-sensitive tasks.

Summary

In short, we are addressing the issues faced by the first two proposals by doing the following:

  1. Explaining the issue we faced using Salbier so others can avoid it
  2. Refunding gas costs to voters to encourage future engagement
  3. Ensuring the startTime parameter is sufficient with tighter internal checks and tests
  4. Automating the manual steps to prevent any loss of time

Despite the annoyance this situation may have caused, we’ve learned about how to be better and we hope the community will find value in the solutions that will be shared as a result.

We expect our third proposal will be submitted by this Friday so stay tuned.

5 Likes

This is a known limitation of the Sablier v1 protocol, a release which is really a minimal streaming contract and in which we focused on providing a safe product above anything else (including user experience).

In future releases of Sablier, we will add the functionality to start a stream at block.timestamp, such that the start time gets set to whenever the tx gets mined in the future. This will make it easier for DAOs to create streams.

4 Likes

We’ve submitted our third (and what should be our final) adjustment proposal. Voting should start at the start of next week: Compound

As we shared earlier, we have done the following:

  1. Refunded gas costs to voters on the 2nd proposal. These voters should see a transaction from this address: Address 0xec405bcd169633c0d8edc8ef869e164e42b9ec1e | Etherscan
  2. Provided ample time for execution before the startTime. Our proposal should be ready for execution as soon as April 15th but our startTime is set for April 20th to account for any potential delays.
  3. Automated the queue and execute stages using Defender Autotasks. You can see the example we’ve developed on GitHub although there are improvements we are planning to implement before officially recommending it to other community members.

We appreciate the community’s patience here as well as Sablier’s willingness to address the timing issue in their future releases.

1 Like

Wondering if anyone can explain to me the discrepancy I am noting:

The current proposal says that the original proposal (Dec 21) indicated:

“…at the start of every quarter OpenZeppelin will create a proposal to update the service fee payment in accordance with the formula outlined in the proposal.”

I am noting a discrepancy with what was originally proposed and approved in the initial proposal back on 22 Dec 2021- which actually says:

“At the start of every 6 month period for one year OpenZeppelin will create a formal Compound Governance Proposal to update the service fee payment in accordance with the simple formula below:” (see page 5 of the proposal from link in the original proposal: Compound DAO Continuous Audit Proposal - Final - Google Docs)

Just wondering why we are not following the 6 month period as approved in the Dec 21 proposal which had significant back/forth as well as significant discussion surrounding the payment and method.

1 Like

Hi @kleong

Thanks for pointing this out. The 6-month statement you’re referencing in the Proposal document is outdated and should be corrected. The text of the on-chain proposal passed on 22 Dec 2021 references quarterly payments and that’s the arrangement we’ve communicated to the community both before and after it passed. The section in the Final Proposal where the statement is located also has a formula called Quarterly Payments, so this is mainly an issue of consistency in the text.

I’ll correct that in the Final Proposal to ensure it all lines up with what was approved on-chain.

Just to be clear then - So what this proposal appears to be doing is actually amending the previous proposal provisions that were passed/approved in the previous vote in December? I note that you have gone into the Final Proposal (which was linked as the Final in the actual approved/voted on Proposal), and the doc has been modified to reflect that there are changes to the Fee Structure ((i) modification from 6-months to Quarterly, and lower, under bulletpoint 2 from the retainer fee from 2 payments, to 4 payments.)

Although I understand the need to sometimes update inaccuracies in proposals/contracts - however, not sure that this proposal as drafted here is actually disclosing and appropriately modifying the previous language of the proposal/fee structure. (As currently worded, this proposal seems a bit innocuous, in that, we are simply being asked to approve what was previously agreed to - which I would suggest is not exactly the case here). Perhaps that might have been what was agreed upon in theory, in the forum discussions, but the actual language of the Final Proposal, as approved and stipulated to be the Final, reflects a different reality. If this was a $10,000 contract/proposal, I might not be as concerned, but we are talking about $1M/quarter. = $4M this year. Not chum change to have unclear contract/proposal provisions about a fee structure.

Fee Structure

OpenZeppelin charges a service fee for its best-in-class security solutions that seeks to be commensurate with the value that our solutions add to protocols. OpenZeppelin also wants to provide a strong signal of our alignment with the protocol. At the start of every 6 3 month period for one year OpenZeppelin will create a formal Compound Governance Proposal to update the service fee payment in accordance with the simple formula below (note the edits (cannot include the strikeout letters, but you can note the changes.):

Quarterly Fee [COMP] =Retainer Fee

  • Retainer Fee [COMP]
    • $1,000,000/quarter, payable in COMP at the Volume Weighted Average Price of COMP of the previous month (source: Messari)
      ** Two Four quarterly payments* paid in advance at the beginning of each 6 3 month period or 1 year streaming
    • This inclusive of all services provided in this proposal

So what this proposal appears to be doing is actually amending the previous proposal provisions that were passed/approved in the previous vote in December?

No, that is incorrect. The text in the on-chain proposal, which was read and passed by COMP voters in December, specifically says “quarterly payments” which you can see in the governance dashboard and in the tx hash on-chain.

OpenZeppelin has revised its original proposal to focus on community feedback and excludes performance fees. OpenZeppelin’s fee will be the equivalent of $1 million USD in COMP every quarter for one year, to provide these services. This fee covers all services defined in the proposal.

The Google Doc Proposal text was not updated fully but the text contained in the final forum post and the immutable, on-chain proposal are all referencing quarterly payments as the model to be used. Even the text you posted makes multiple references to quarterly payments and having those prices adjusted on a quarterly basis.

I apologize for the confusion that the text in the Final Proposal has caused but the payment terms that were approved by the DAO has always been for quarterly payments. This current proposal is following those same terms.

3 Likes

The Google Doc was incorporated by reference into the on-chain proposal (and thereby binding as well) and spells out the specifics of the services provided, as well as the detailed Fee Structure that contains the inconsistencies. All I am saying is that I think we should be careful about allowing unilateral interpretations of conflicting provisions like these, and making assumptions on what the community may/may not have understood they were agreeing to when voting.

To close my comments out here, I do want to clarify that I don’t actually have an issue with quarterly payments in this case, nor the merits of the proposal itself. And I also completely appreciate OZs discloser and really thoughtful discussion surrounding some of the challenges you have faced in this process.

I agree with your concern about discrepancies and it will definitely be noted for the future. My personal view is that the on-chain text takes precedence over an off-chain document that’s linked since that wouldn’t be immutable. However, the discrepancy is still a potential issue in case people voted primarily based on an understanding of the Google Doc’s terms. Luckily, all the community members I’ve spoken to always assumed it was quarterly so it doesn’t appear anyone voted with the wrong assumptions. I do appreciate you bringing this to our attention so we can be more careful and consistent in future proposals.

Seems like the proposal execution failed as the deposit sum must be divisible by stream duration

1 Like

Yes, we found out yesterday and realized it after investigating the failed execution.

At this point, we’re disappointed with the number of failed proposals and will go with the simplest way to resolve this for good.

1 Like

We’ve submitted a new compensation proposal that’s available here and canceled the old one that failed to execute.

To avoid any further issues, we are no longer using Sablier and will instead continue using the built-in streaming function. We’ve also simulated the proposal execution in Tenderly to be certain of success.

As we did previously, we will be refunding gas costs to voters on the last failed proposal. We’re disappointed with the recent issues that have been faced in our past proposals and we really appreciate the community’s patience as we work through them.

We’d also like to note that while we think Sablier is not currently well-suited for use in Compound governance proposals today, we look forward to future versions of the protocol that plan to address these issues. The Sablier team has been doing great work and we wouldn’t want our struggles here to undermine that fact. We’ve learned a lot ourselves and hope to share some best practices in the future that will help others avoid similar proposal issues.