Implement cToken sweepToken and return accidentally sent funds

Two months ago, a significant amount of cUNI was accidentally sent to the cUNI contract in this transaction. While it is quite common for accidents to occur within DeFi, I believe that as a community, we should try to return accidentally sent funds when possible and establish a policy regarding it. This proposal acts to do so.

I propose a process as follows:

  • Create a proposal at most once every 6 months to return accidentally sent funds.
  • Impose a 10% penalty on the sent funds.
  • Only return funds to the address they were sent from.
  • Require the sender to reply below this post requesting their funds back along with a signed signature or on-chain transaction with call data verifying their identity.
  • A minimum value of $1,000 at the time of the post.
  • Post by the sender must be made within 6 months of the accidental send. After this timeframe, the funds will be considered community assets.

Technical

I have added a function sweepToken(address token) to the CErc20 smart contract. This will send the entire balance of any token other than the underlying to the admin (Timelock). It is callable by anyone allowing for future proposals returning user funds to be a simple as possible (the funds will already be in the Timelock).

I have also commented out unused verify hooks for gas savings.

The changes implemented here are very straightforward and limited in scope so I don’t believe a formal audit is required. The new feature is extensively tested through scenario testing and the proposal has been simulated through fork simulations. The code has already been reviewed by many community members, and hopefully more will review it in the coming days. View the changes in the PR.

Proposal

The proposal will contain the following calls:

  1. Update the cUNI implementation to the implementation here
  2. sweep cUNI out of the cUNI contract into the Timelock
  3. Transfer 90% of the cUNI to the sender: 0xf22c2e3475e4a066f4e9f44567c950dd36112d05
  4. Grant 30 comp to Arr00

I have been in touch with the sender of cUNI to the cUNI contract and they have sent me a signature confirming their identity and requested for their cUNI to be returned. The signature is posted below. The message is signed by the wallet that executed the transaction and is one of the owners of the multisig which sent the cUNI. The cUNI will be sent back to the multisig.

{
  "address": "0x3f9a39602853f6fa8a6f134dd1d63e8a48a7a0ba",
  "msg": "“We sent cUNI to cUNI contract. We would like it returned to the original wallet.”",
  "sig": "0x2c0ab1890fe2df7d1793d36e213b1d70620c232f1e566de4f5ebcdb92f136d90320b9a1a825d92662ddbea9f95de2fdd010fd9aa39707649335ac541572557711b",
  "version": "3",
  "signer": "MEW"
}

Discussion throughout development: CToken sweepToken function

8 Likes

I love the idea that you have set up a clear process, and I mostly agree with the terms. I think the minimum value thing is something to talk more in depth on.

The work you have done has been noted and I think you deserve the 30 COMP for this, but the reward, IMO should be separate from the proposal.
A grants/contributor committee would be a better place to talk about it, rather than including it here.

I think this can set unintended consequences…What other funds need to be swept back?
Maybe if every few months we put together a large handful of accidental transfers and reverse them, instead of one by one.

Overall, I support this and I personally appreciate the effort you have put into this and I hope it gets passed.

I hope this opens up more discussion about the future of how we as a community deal with problems like this.

1 Like

Currently, there are not any other funds eligible for return under this policy. The proposal which occurs once every six months will return all funds eligible to be returned at once.

2 Likes

@arr00 thank you for taking charge of this development; having a sweep() function on cTokens will be useful for recovering trapped assets. We just have to make sure the community (and governance) have a clear process for when/how governance retrieves trapped assets, since it can be relatively onerous.

Couple quick questions:

  1. Is the PR final? Can you confirm it matches the deployed implementation?
  2. Any reason why we can’t upgrade COMP, or USDT/DAI to the latest implementation in the proposal as well?
  3. Have you tested the implementation on testnet? Does the simulation remove cUNI from cUNI successfully?

Awesome work!

3 Likes

The PR is final and hasn’t had changes to the contracts in 9 days. You can use saddle match to verify the new implementation was deployed correctly.

We could, however, since there are no funds which are returnable under the tentative policy trapped in any other upgradable cToken, I would prefer to wait to update their implementations.

Yes, I have run forking simulations which show it will work as expected. Additionally, I updated cUSDT on Kovan to use the new code. You can see the sweepToken usage on Koran here.

1 Like

I think this is valuable work that you are doing here, I question whether the 30 comp should come from the community fund. (I’m not against it) I think we should consider a penalty on the sender (offender) in this situation as had they not erred, the work would be unnecessary.

Great point. The new policy actually places a 10% on all cases of returning accidentally sent funds. In this first instance, that penalty will be ~$70k in cUNI which is significantly more than the value of the COMP being granted. While we could give cUNI instead of COMP, I think that contributors to the protocol should be granted COMP when possible.

5 Likes

I second that opinion – granting COMP to developers is (1) the intended use of COMP, (2) aligns incentives, (3) sets a sustainable precedent since it’s repeatable, where cUNI is not. I’m a fan!

3 Likes

Great work, @arr00!

Is it worth considering not allowing cUNI transfers to the cUNI contract and adding similar functionality to the other ctoken contracts?

The deployed contract bytecode was successfully verified with the source code.

Matching contract at 0xa1849880593E96d2f7dF77D0D38a7f2372aE10E0 to CCompLikeDelegate with args []
:white_check_mark: Successfully matched CCompLikeDelegate to 0xa1849880593E96d2f7dF77D0D38a7f2372aE10E0 with args []

3 Likes

Proposal 37 is now live. I have pushed a post propose fork simulation, which confirms that the proposal was formatted correctly and will execute as expected, here.

2 Likes

Great proposal, thanks for setting this up.
Quick question on the 10% penalty, where will those funds go to?

1 Like

They go to the Timelock contract, which is controlled by COMP Governance. You can think of it as going to the protocol.

5 Likes

Assuming this passes, will this apply retroactively to accidental deposits that happened more than 6 months ago, as long as the sender can verify its identity? What will be the process for requesting a return of funds?

There are some cases where it is not possible recovering.
The old version of cToken contract like cETH cUSDC cWBTC cZRX cBAT cSAI cREP are immutable contracts and cannot implement any new function to them,
the only possible contracts to recover accidentally sent tokens are the comptroller itself or cDAI cUSDT cUNI cCOMP
another thing that makes the recovers impossible if lets say DAI was sent directly to cDAI contract, in that case the DAI become part of the supply and all the “current” supplier receive it as extra interest, in other words only those token can be recovered if it is different than the underlying token.

2 Likes

Sorry, I’m not very technically-savvy on these things. Would this transaction be recoverable?

1 Like

unfortunatelly not, this is one of the case when the accidentally sent token is the same what the ctoken contract holds:(

2 Likes

That’s what I was afraid of, but thanks for the explanation. I still think it’s a good proposal and am definitely going to vote for it.

1 Like

to those who want to vote and not spend gas can use https://comp.vote/
it’s in beta though, i think

2 Likes

The interface doesn’t allow me to vote (Proposal 037). it’s shows me “View Only”. The timing of the vote (Manual) is over?