From 53c099040761c5286933ce2224c5582fe8ca769b Mon Sep 17 00:00:00 2001 From: Pablo Veyrat <50438397+sogipec@users.noreply.github.com> Date: Fri, 19 Jan 2024 17:16:28 +0100 Subject: [PATCH] feat: VaultManager implementation with interface IERC1155Receiver (#210) * first batch of test done * update final hash of vaultManager implementation * changing whitelist * vaultManager for IB01 * fix: comments for liquidations * add USDC collateral * feat: add ERC1155 support * fix: failed tests corrected * fix-rebase * avoid skipped line --------- Co-authored-by: gs8nrv <55771972+GuillaumeNervoXS@users.noreply.github.com> --- contracts/vaultManager/VaultManager.sol | 2 +- .../VaultManagerERC1155Receiver.sol | 44 +++++++ contracts/vaultManager/VaultManagerERC721.sol | 2 +- hardhat.config.ts | 9 ++ .../vaultManagerERC1155Receiver.test.ts | 124 ++++++++++++++++++ .../vaultManager/vaultManagerERC721.test.ts | 1 - .../vaultManager/vaultManagerSetters.test.ts | 12 +- 7 files changed, 188 insertions(+), 6 deletions(-) create mode 100644 contracts/vaultManager/VaultManagerERC1155Receiver.sol create mode 100644 test/hardhat/vaultManager/vaultManagerERC1155Receiver.test.ts diff --git a/contracts/vaultManager/VaultManager.sol b/contracts/vaultManager/VaultManager.sol index eb6b6ff0..32e2a7c7 100644 --- a/contracts/vaultManager/VaultManager.sol +++ b/contracts/vaultManager/VaultManager.sol @@ -925,7 +925,7 @@ contract VaultManager is VaultManagerPermit, IVaultManagerFunctions { /// @param target Address to toggle /// @dev If the `target` address is the zero address then this function toggles whitelisting /// for all addresses - function toggleWhitelist(address target) external virtual onlyGovernor { + function toggleWhitelist(address target) external virtual onlyGovernorOrGuardian { if (target != address(0)) { isWhitelisted[target] = 1 - isWhitelisted[target]; } else { diff --git a/contracts/vaultManager/VaultManagerERC1155Receiver.sol b/contracts/vaultManager/VaultManagerERC1155Receiver.sol new file mode 100644 index 00000000..da831886 --- /dev/null +++ b/contracts/vaultManager/VaultManagerERC1155Receiver.sol @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: GPL-3.0 + +pragma solidity ^0.8.12; + +import "@openzeppelin/contracts-upgradeable/token/ERC1155/IERC1155ReceiverUpgradeable.sol"; + +import "./VaultManager.sol"; + +/// @title VaultManagerERC1155Receiver +/// @author Angle Labs, Inc. +/// @notice VaultManager contract that can receive ERC1155 tokens +contract VaultManagerERC1155Receiver is IERC1155ReceiverUpgradeable, VaultManager { + /// @inheritdoc IERC1155ReceiverUpgradeable + /// @dev The returned value should be: + /// `bytes4(keccak256("onERC1155Received(address,address,uint256,uint256,bytes)")) = 0xf23a6e61` + function onERC1155Received(address, address, uint256, uint256, bytes calldata) external pure returns (bytes4) { + return IERC1155ReceiverUpgradeable.onERC1155Received.selector; + } + + /// @inheritdoc IERC1155ReceiverUpgradeable + /// @dev The returned value should be: + /// `bytes4(keccak256("onERC1155BatchReceived(address,address,uint256[],uint256[],bytes)")) = 0xbc197c81` + function onERC1155BatchReceived( + address, + address, + uint256[] calldata, + uint256[] calldata, + bytes calldata + ) external pure returns (bytes4) { + return IERC1155ReceiverUpgradeable.onERC1155BatchReceived.selector; + } + + /// @inheritdoc IERC165Upgradeable + function supportsInterface( + bytes4 interfaceId + ) external pure override(VaultManagerERC721, IERC165Upgradeable) returns (bool) { + return + interfaceId == type(IERC721MetadataUpgradeable).interfaceId || + interfaceId == type(IERC721Upgradeable).interfaceId || + interfaceId == type(IVaultManager).interfaceId || + interfaceId == type(IERC165Upgradeable).interfaceId || + interfaceId == type(IERC1155ReceiverUpgradeable).interfaceId; + } +} diff --git a/contracts/vaultManager/VaultManagerERC721.sol b/contracts/vaultManager/VaultManagerERC721.sol index c219791e..8799c691 100644 --- a/contracts/vaultManager/VaultManagerERC721.sol +++ b/contracts/vaultManager/VaultManagerERC721.sol @@ -115,7 +115,7 @@ abstract contract VaultManagerERC721 is IERC721MetadataUpgradeable, VaultManager // ================================ ERC165 LOGIC =============================== /// @inheritdoc IERC165Upgradeable - function supportsInterface(bytes4 interfaceId) external pure returns (bool) { + function supportsInterface(bytes4 interfaceId) external pure virtual returns (bool) { return interfaceId == type(IERC721MetadataUpgradeable).interfaceId || interfaceId == type(IERC721Upgradeable).interfaceId || diff --git a/hardhat.config.ts b/hardhat.config.ts index 6a7dc58e..2c72e165 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -91,6 +91,15 @@ const config: HardhatUserConfig = { }, }, }, + 'contracts/vaultManager/VaultManagerERC1155Receiver.sol': { + version: '0.8.12', + settings: { + optimizer: { + enabled: true, + runs: 1, + }, + }, + }, 'contracts/deprecated/OldVaultManager.sol': { version: '0.8.12', settings: { diff --git a/test/hardhat/vaultManager/vaultManagerERC1155Receiver.test.ts b/test/hardhat/vaultManager/vaultManagerERC1155Receiver.test.ts new file mode 100644 index 00000000..1a7da544 --- /dev/null +++ b/test/hardhat/vaultManager/vaultManagerERC1155Receiver.test.ts @@ -0,0 +1,124 @@ +import { parseAmount } from '@angleprotocol/sdk'; +import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'; +import { Signer } from 'ethers'; +import { parseEther } from 'ethers/lib/utils'; +import hre, { contract, ethers } from 'hardhat'; + +import { + AgToken, + AgToken__factory, + AngleHelpers, + AngleHelpers__factory, + MockOracle, + MockOracle__factory, + MockStableMaster, + MockStableMaster__factory, + MockToken, + MockToken__factory, + MockTreasury, + MockTreasury__factory, + VaultManagerERC1155Receiver, + VaultManagerERC1155Receiver__factory, +} from '../../../typechain'; +import { expect } from '../utils/chai-setup'; +import { deployUpgradeable, ZERO_ADDRESS } from '../utils/helpers'; + +contract('VaultManagerERC1155Receiver', () => { + let deployer: SignerWithAddress; + let governor: SignerWithAddress; + let guardian: SignerWithAddress; + + let treasury: MockTreasury; + let collateral: MockToken; + let oracle: MockOracle; + let stableMaster: MockStableMaster; + let agToken: AgToken; + let vaultManager: VaultManagerERC1155Receiver; + let helpers: AngleHelpers; + + const impersonatedSigners: { [key: string]: Signer } = {}; + + const collatBase = 10; + const params = { + debtCeiling: parseEther('100'), + collateralFactor: 0.5e9, + targetHealthFactor: 1.1e9, + borrowFee: 0.1e9, + interestRate: 100, + liquidationSurcharge: 0.9e9, + maxLiquidationDiscount: 0.1e9, + liquidationBooster: 0.1e9, + whitelistingActivated: false, + baseBoost: 1e9, + }; + + before(async () => { + ({ deployer, governor, guardian } = await ethers.getNamedSigners()); + const impersonatedAddresses = [{ address: '0xdC4e6DFe07EFCa50a197DF15D9200883eF4Eb1c8', name: 'governor' }]; + + for (const ob of impersonatedAddresses) { + await hre.network.provider.request({ + method: 'hardhat_impersonateAccount', + params: [ob.address], + }); + + await hre.network.provider.send('hardhat_setBalance', [ob.address, '0x10000000000000000000000000000']); + + impersonatedSigners[ob.name] = await ethers.getSigner(ob.address); + } + }); + + beforeEach(async () => { + stableMaster = await new MockStableMaster__factory(deployer).deploy(); + agToken = (await deployUpgradeable(new AgToken__factory(deployer))) as AgToken; + await agToken.connect(deployer).initialize('agEUR', 'agEUR', stableMaster.address); + collateral = await new MockToken__factory(deployer).deploy('USDC', 'USDC', collatBase); + vaultManager = (await deployUpgradeable( + new VaultManagerERC1155Receiver__factory(deployer), + )) as VaultManagerERC1155Receiver; + helpers = (await deployUpgradeable(new AngleHelpers__factory(deployer))) as AngleHelpers; + + treasury = await new MockTreasury__factory(deployer).deploy( + agToken.address, + governor.address, + guardian.address, + vaultManager.address, + ZERO_ADDRESS, + ZERO_ADDRESS, + ); + await agToken.connect(impersonatedSigners.governor).setUpTreasury(treasury.address); + await treasury.addMinter(agToken.address, vaultManager.address); + + oracle = await new MockOracle__factory(deployer).deploy(2 * 10 ** collatBase, treasury.address); + await vaultManager.initialize(treasury.address, collateral.address, oracle.address, params, 'USDC/agEUR'); + }); + + describe('ERC1155Receiver Logic', () => { + it('success - onERC1155Received', async () => { + expect( + await vaultManager.onERC1155Received( + guardian.address, + guardian.address, + parseEther('1'), + parseEther('2'), + '0x', + ), + ).to.be.equal('0xf23a6e61'); + }); + it('success - onERC1155BatchReceived', async () => { + expect( + await vaultManager.onERC1155BatchReceived( + guardian.address, + guardian.address, + [parseAmount.gwei('1')], + [parseEther('10')], + '0x', + ), + ).to.be.equal('0xbc197c81'); + }); + it('success - supportsInterface', async () => { + // Equal to: `type(IERC1155ReceiverUpgradeable).interfaceId` + expect(await vaultManager.supportsInterface('0x4e2312e0')).to.be.equal(true); + }); + }); +}); diff --git a/test/hardhat/vaultManager/vaultManagerERC721.test.ts b/test/hardhat/vaultManager/vaultManagerERC721.test.ts index 365c9891..f2023034 100644 --- a/test/hardhat/vaultManager/vaultManagerERC721.test.ts +++ b/test/hardhat/vaultManager/vaultManagerERC721.test.ts @@ -134,7 +134,6 @@ contract('VaultManagerLiquidationBoost - ERC721', () => { expect(await vaultManager.paused()).to.be.false; await expect(vaultManager.connect(deployer).toggleWhitelist(ZERO_ADDRESS)).to.be.reverted; - await expect(vaultManager.connect(guardian).toggleWhitelist(ZERO_ADDRESS)).to.be.reverted; await vaultManager.connect(governor).toggleWhitelist(ZERO_ADDRESS); expect(await vaultManager.whitelistingActivated()).to.be.true; }); diff --git a/test/hardhat/vaultManager/vaultManagerSetters.test.ts b/test/hardhat/vaultManager/vaultManagerSetters.test.ts index 23eb8c2e..5841844d 100644 --- a/test/hardhat/vaultManager/vaultManagerSetters.test.ts +++ b/test/hardhat/vaultManager/vaultManagerSetters.test.ts @@ -253,10 +253,16 @@ contract('VaultManagerLiquidationBoost - Setters', () => { describe('toggleWhitelist', () => { it('reverts - access control', async () => { - await expect(vaultManager.connect(alice).toggleWhitelist(alice.address)).to.be.revertedWith('NotGovernor'); + await expect(vaultManager.connect(alice).toggleWhitelist(alice.address)).to.be.revertedWith( + 'NotGovernorOrGuardian', + ); }); - it('reverts - guardian', async () => { - await expect(vaultManager.connect(guardian).toggleWhitelist(alice.address)).to.be.revertedWith('NotGovernor'); + it('success - guardian', async () => { + await vaultManager.connect(guardian).toggleWhitelist(alice.address); + expect(await vaultManager.isWhitelisted(alice.address)).to.be.equal(1); + + await vaultManager.connect(guardian).toggleWhitelist(alice.address); + expect(await vaultManager.isWhitelisted(alice.address)).to.be.equal(0); }); it('success - governor', async () => { await vaultManager.connect(governor).toggleWhitelist(alice.address);