Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deploy and proposal for USDT market on Goerli #801

Open
wants to merge 32 commits into
base: main
Choose a base branch
from

Conversation

cwang25
Copy link
Contributor

@cwang25 cwang25 commented Aug 3, 2023

This PR introduces the deployment for USDT Goerli as well as the proposal to initialize the market there.
For ease of managing the changes the initialize proposal and migration scripts changes are in: PR#802, which will be merged later on.

Comet Contract changes in this PR:

  • Update doTransferIn and doTransferOut to support non-standard erc20,
  • Update doTransferIn to return actual transferred amount for better accounting in non-standard erc20 and fee token scenarios
  • Update supplyBase, supplyCollateral, buyCollateral to account the actual transferred values returned by doTransferIn

TODO:

  • deploy USDT instance to Goerli
  • run market initialization proposal

@cwang25 cwang25 marked this pull request as ready for review August 3, 2023 17:54
@cwang25
Copy link
Contributor Author

cwang25 commented Aug 3, 2023

The scenario will fail as we encountered back in Arbitrum native usdc deploy.
Since this is deploying another comet on the chain with existing governor contract, without proper setFactory, setConfigurator on migration script, the scenario tests will run into errors.

But this can be confirmed in migration PR, which with migration script the scenario run will succeed.

Copy link
Contributor Author

@cwang25 cwang25 Aug 31, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

 ···································|··············|·················
 |  CometFactory                    ·      24.044  ·                │
 ···································|··············|·················
 |  CometModifiedFactory            ·      24.182  ·                │
 ···································|··············|·················

The contract size is approaching to limit, compiler will complain with Warning: 2 contracts exceed the size limit for mainnet deployment.
But the actual size limit is 24.576 kb, so we should be fine :P

contracts/Comet.sol Outdated Show resolved Hide resolved
Copy link
Contributor

@kevincheng96 kevincheng96 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great! Could you also add unit tests for supply base, supply collateral, and buy collateral? I notice there is no scenarios for buy collateral, so would be good to get some unit test coverage at least.

cwang25 and others added 3 commits September 8, 2023 12:02
Co-authored-by: Kevin Cheng <kevincheng96@hotmail.com>
Co-authored-by: Kevin Cheng <kevincheng96@hotmail.com>
Co-authored-by: Kevin Cheng <kevincheng96@hotmail.com>
Copy link
Contributor

@kevincheng96 kevincheng96 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work on the tests! They look great 👍

Copy link
Contributor

@kevincheng96 kevincheng96 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Think this should be ready for audit with OZ now!

Could you update the PR description to say this introduces a new implementation of Comet that supports non-standard ERC20s and fee tokens?

Separately, I wonder if we should add this logic in a new Comet file, like NonStandardComet or FeeTokenComet?

contracts/test/NonStandardFaucetFeeToken.sol Show resolved Hide resolved
Hans Wang added 2 commits September 12, 2023 17:35
…h new Comet logics that will compare Comet's raw balance diff to credit user in accounting. Comet's always got 0 token, so comet always credit 0, thus it won't hit SupplyCapExceeded() error no more
@kevincheng96
Copy link
Contributor

Looking at the re-entrancy test fixes, I think the better fix is actually just to have the transfer and transferFrom functions in the EvilToken do actual transfer accounting while also calling performAttack().

Hans Wang added 2 commits September 14, 2023 17:40
…dded one test case to show the known vulnerability (re-entrance when token contract itself is eploited)
@cwang25
Copy link
Contributor Author

cwang25 commented Sep 15, 2023

Synced with ⛑️ offline.
For the re-entrancy issue that Comet is not immune to the tokens that's got exploited. (i.e. EvilToken.sol)
We decided to not adding additional layer to prevent that in Comet as this vulnerability will require governance to be exploited that governance has to vote and add the suspicious token into Comet's contract. If suspicious token contract is not added to Comet's market, this should not be a concern.

For reference, if import re-entrancy guard from openzeppelin which can prevent exploited token contract to execute re-entrancy attack, but it will also increase the contract size to:

 ···································|··············|·················
 |  CometFactory                    ·      24.173  ·                │
 ···································|··············|·················
 |  CometModifiedFactory            ·      24.311  ·                │
 ···································|··············|·················

Comment on lines 442 to 561
).to.be.revertedWith("custom error 'SupplyCapExceeded()'");
).to.be.revertedWith("custom error 'TransferInFailed()'");
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now the accounting in comet won't be inflated by re-entrancy supply reaching SupplyCapExceeded().
It will be blocked and throw TransferInFailed() error.

).to.be.revertedWith("custom error 'NotCollateralized()'");
).to.be.revertedWith("custom error 'TransferOutFailed()'");
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now the accounting in comet won't be mistaken by re-entrancy withdraw reaching NotColalteralized() error.
Instead, it will be blocked and throw TransferInFailed() error right away.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't it throw the re-entrancy error instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah it was bit weird, but since the error is thrown in the nested transfer() function, so it gets sort of "re-wrap" into TransferInFailed() as the code here doesn't re-raise the error (or should we make it re-raise the error?)

uint256 preTransferBalance = ERC20(asset).balanceOf(address(this));
        (bool success, bytes memory returndata) = asset.call(abi.encodeWithSelector(ERC20.transferFrom.selector, from, address(this), amount));
        if (!success || !(returndata.length == 0 || abi.decode(returndata, (bool)))) {
            revert TransferInFailed();
        }
        return ERC20(asset).balanceOf(address(this)) - preTransferBalance;

From code we can see now the transferFrom revert won't reveirt right away with returndata, but instead when it fails, we throw TransferInFailed().

contracts/Comet.sol Outdated Show resolved Hide resolved
contracts/Comet.sol Outdated Show resolved Hide resolved
Comment on lines 62 to 65
/// @dev The reentrancy guard status
uint256 internal constant COMET_REENTRANCY_GUARD_NOT_ENTERED = 1;
uint256 internal constant COMET_REENTRANCY_GUARD_ENTERED = 2;

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can just inline rather than creating constants. Also, let's use a default value of 0. Otherwise, you need to initialize the slot to 1.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I recalled seeing in openzeppelin's reentrancy guard contract saying that using 1, 2 is cheaper on running than setting it to 0. (reference: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/ReentrancyGuard.sol)

Since we're only checking on if status == COMET_REENTRANCY_GUARD_ENTERED. It looks like the initialization value of 0 or 1 wouldn't affect the first run behavior, so I ended up changing to 1,2. And each run it only checks on if the status is ENTERED(2) :P. But yeah I agree the first initialization value having to be 0 instead of 1 is a bit confusing, and I can change it back to 0 if the gas saving gain is not enough to tradeoff with the confusion initialization values.

@@ -776,7 +802,7 @@ contract Comet is CometMainInterface {
* @dev Safe ERC20 transfer out
* @dev Note: Safely handles non-standard ERC-20 tokens that do not return a value. See here: https://medium.com/coinmonks/missing-return-value-bug-at-least-130-tokens-affected-d67bf08521ca
*/
function doTransferOut(address asset, address to, uint amount) internal {
function doTransferOut(address asset, address to, uint amount) nonReentrant internal {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Having nonReentrant on the transfer functions breaks BuyCollateral right?

// EvilToken attack is using 3000 EVIL tokens, so totalSupplyBase should have 3000e6
expect(evilTotalsBasic.totalSupplyBase).to.equal(3000e6);
// EvilToken attack should be blocked
expect(evilTotalsBasic.totalSupplyBase).to.equal(0);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's add an assertion to assert the re-entrancy error.

test/supply-test.ts Outdated Show resolved Hide resolved
@@ -558,7 +558,7 @@ describe('withdraw', function () {
// in callback, EvilToken attempts to withdraw USDC to bob's address
await expect(
comet.connect(alice).withdraw(EVIL.address, 1e6)
).to.be.revertedWith("custom error 'NotCollateralized()'");
).to.be.revertedWith("custom error 'TransferOutFailed()'");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test name should change as well

).to.be.revertedWith("custom error 'NotCollateralized()'");
).to.be.revertedWith("custom error 'TransferOutFailed()'");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't it throw the re-entrancy error instead?

Address oz's feedback on USDT comet support
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants