From fcae5c10d0005055673b0e246ead9600e9091e7a Mon Sep 17 00:00:00 2001 From: ninokeldishvili Date: Wed, 10 Jul 2024 01:30:11 +0400 Subject: [PATCH 1/8] Test ERC20PriceOracleReceiptVault is constracted --- ...C20PriceOracleReceiptVault.construct.t.sol | 78 ++++++++ test/priceOracle/Initialize.test.ts | 176 ------------------ 2 files changed, 78 insertions(+), 176 deletions(-) create mode 100644 test/foundry/src/concrete/erc20PriceOracle/ERC20PriceOracleReceiptVault.construct.t.sol delete mode 100644 test/priceOracle/Initialize.test.ts diff --git a/test/foundry/src/concrete/erc20PriceOracle/ERC20PriceOracleReceiptVault.construct.t.sol b/test/foundry/src/concrete/erc20PriceOracle/ERC20PriceOracleReceiptVault.construct.t.sol new file mode 100644 index 00000000..72e6b18e --- /dev/null +++ b/test/foundry/src/concrete/erc20PriceOracle/ERC20PriceOracleReceiptVault.construct.t.sol @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: CAL +pragma solidity =0.8.25; + +import {ReceiptVaultConfig, VaultConfig} from "../../../../../contracts/abstract/ReceiptVault.sol"; +import { + ERC20PriceOracleReceiptVault, + ERC20PriceOracleReceiptVaultConfig +} from "../../../../../contracts/concrete/vault/ERC20PriceOracleReceiptVault.sol"; +import {ERC20PriceOracleReceiptVaultTest, Vm} from "test/foundry/abstract/ERC20PriceOracleReceiptVaultTest.sol"; +import {TwoPriceOracle} from "../../../../../contracts/oracle/price/TwoPriceOracle.sol"; + +contract ERC20PriceOracleReceiptVaultConstructionTest is ERC20PriceOracleReceiptVaultTest { + /// Test ERC20PriceOracleReceiptVault is constracted + function testCheckConstructionEvent( + uint256 fuzzedKeyAlice, + string memory assetName, + string memory assetSymbol, + uint256 timestamp, + uint8 xauDecimals, + uint8 usdDecimals, + uint80 answeredInRound + ) external { + // Ensure the fuzzed key is within the valid range for secp256 + address alice = vm.addr((fuzzedKeyAlice % (SECP256K1_ORDER - 1)) + 1); + // Use common decimal bounds for price feeds + // Use 0-20 so we at least have some coverage higher than 18 + usdDecimals = uint8(bound(usdDecimals, 0, 20)); + xauDecimals = uint8(bound(xauDecimals, 0, 20)); + timestamp = bound(timestamp, 0, type(uint32).max); + + vm.warp(timestamp); + TwoPriceOracle twoPriceOracle = createTwoPriceOracle(usdDecimals, usdDecimals, timestamp, answeredInRound); + vm.startPrank(alice); + + // Start recording logs + vm.recordLogs(); + + ERC20PriceOracleReceiptVault vault = createVault(address(twoPriceOracle), assetName, assetSymbol); + // Get the logs + Vm.Log[] memory logs = vm.getRecordedLogs(); + + // Note: To use vm.expectEmit receipt address is needed to be known. + // Find the OffchainAssetReceiptVaultInitialized event log + address msgSender = address(0); + address priceOracle = address(0); + address assetAddress = address(0); + string memory name = ""; + string memory symbol = ""; + bool eventFound = false; // Flag to indicate whether the event log was found + for (uint256 i = 0; i < logs.length; i++) { + if ( + logs[i].topics[0] + == keccak256( + "ERC20PriceOracleReceiptVaultInitialized(address,(address,(address,(address,string,string))))" + ) + ) { + // Decode the event data + (address sender, ERC20PriceOracleReceiptVaultConfig memory config) = + abi.decode(logs[i].data, (address, ERC20PriceOracleReceiptVaultConfig)); + msgSender = sender; + priceOracle = config.priceOracle; + name = config.receiptVaultConfig.vaultConfig.name; + symbol = config.receiptVaultConfig.vaultConfig.symbol; + assetAddress = address(config.receiptVaultConfig.vaultConfig.asset); + eventFound = true; // Set the flag to true since event log was found + break; + } + } + // Assert that the event log was found + assertTrue(eventFound, "ERC20PriceOracleReceiptVaultInitialized event log not found"); + + assertEq(msgSender, address(iFactory)); + assertEq(priceOracle, address(twoPriceOracle)); + assert(address(vault) != address(0)); + assertEq(keccak256(bytes(vault.name())), keccak256(bytes(name))); + assertEq(keccak256(bytes(vault.symbol())), keccak256(bytes(symbol))); + } +} diff --git a/test/priceOracle/Initialize.test.ts b/test/priceOracle/Initialize.test.ts deleted file mode 100644 index ce8b7163..00000000 --- a/test/priceOracle/Initialize.test.ts +++ /dev/null @@ -1,176 +0,0 @@ -import { artifacts, ethers } from "hardhat"; -import { - basePrice, - expectedName, - expectedSymbol, - latestBlockNow, - quotePrice, - usdDecimals, - xauDecimals, -} from "../util"; -import { - ERC20PriceOracleReceiptVault, - ERC20PriceOracleReceiptVaultInitializedEvent, -} from "../../typechain-types/contracts/vault/priceOracle/ERC20PriceOracleReceiptVault"; -import { - ERC20PriceOracleReceiptVaultFactory, - NewChildEvent, -} from "../../typechain-types/contracts/vault/priceOracle/ERC20PriceOracleReceiptVaultFactory"; - -import { getEventArgs } from "../util"; -import { - ReceiptFactory, - MockChainlinkDataFeed, - TestErc20, - TwoPriceOracle, -} from "../../typechain-types"; -import { Contract } from "ethers"; - -const assert = require("assert"); - -describe("PriceOracle construction", async function () { - it("Checks construction event", async function () { - const now = await latestBlockNow(); - - const oracleFactory = await ethers.getContractFactory( - "MockChainlinkDataFeed" - ); - const basePriceOracle = - (await oracleFactory.deploy()) as MockChainlinkDataFeed; - await basePriceOracle.deployed(); - // ETHUSD as of 2022-06-30 - - await basePriceOracle.setDecimals(usdDecimals); - await basePriceOracle.setRoundData(1, { - startedAt: now, - updatedAt: now, - answer: basePrice, - answeredInRound: 1, - }); - - const quotePriceOracle = - (await oracleFactory.deploy()) as MockChainlinkDataFeed; - await quotePriceOracle.deployed(); - // XAUUSD as of 2022-06-30 - await quotePriceOracle.setDecimals(xauDecimals); - await quotePriceOracle.setRoundData(1, { - startedAt: now, - updatedAt: now, - answer: quotePrice, - answeredInRound: 1, - }); - - // 1 hour - const baseStaleAfter = 60 * 60; - // 48 hours - const quoteStaleAfter = 48 * 60 * 60; - - const testErc20 = await ethers.getContractFactory("TestErc20"); - const asset = (await testErc20.deploy()) as TestErc20; - await asset.deployed(); - - const chainlinkFeedPriceOracleFactory = await ethers.getContractFactory( - "ChainlinkFeedPriceOracle" - ); - const chainlinkFeedPriceOracleBase = - await chainlinkFeedPriceOracleFactory.deploy({ - feed: basePriceOracle.address, - staleAfter: baseStaleAfter, - }); - const chainlinkFeedPriceOracleQuote = - await chainlinkFeedPriceOracleFactory.deploy({ - feed: quotePriceOracle.address, - staleAfter: quoteStaleAfter, - }); - await chainlinkFeedPriceOracleBase.deployed(); - await chainlinkFeedPriceOracleQuote.deployed(); - - const twoPriceOracleFactory = await ethers.getContractFactory( - "TwoPriceOracle" - ); - const twoPriceOracle = (await twoPriceOracleFactory.deploy({ - base: chainlinkFeedPriceOracleBase.address, - quote: chainlinkFeedPriceOracleQuote.address, - })) as TwoPriceOracle; - - const ERC20PriceOracleReceiptVaultImplementationFactory = - await ethers.getContractFactory("ERC20PriceOracleReceiptVault"); - const ERC20PriceOracleReceiptVaultImplementation = - (await ERC20PriceOracleReceiptVaultImplementationFactory.deploy()) as ERC20PriceOracleReceiptVault; - - const receiptFactoryFactory = await ethers.getContractFactory( - "ReceiptFactory" - ); - const receiptFactoryContract = - (await receiptFactoryFactory.deploy()) as ReceiptFactory; - await receiptFactoryContract.deployed(); - - const erc20PriceOracleVaultConfig = { - priceOracle: twoPriceOracle.address, - vaultConfig: { - asset: asset.address, - name: "PriceOracleVault", - symbol: "POV", - }, - }; - - const erc20PriceOracleVaultFactoryFactory = await ethers.getContractFactory( - "ERC20PriceOracleReceiptVaultFactory" - ); - - let erc20PriceOracleReceiptVaultFactory = - (await erc20PriceOracleVaultFactoryFactory.deploy({ - implementation: ERC20PriceOracleReceiptVaultImplementation.address, - receiptFactory: receiptFactoryContract.address, - })) as ERC20PriceOracleReceiptVaultFactory; - await erc20PriceOracleReceiptVaultFactory.deployed(); - - let tx = await erc20PriceOracleReceiptVaultFactory.createChildTyped( - erc20PriceOracleVaultConfig - ); - let { child } = (await getEventArgs( - tx, - "NewChild", - erc20PriceOracleReceiptVaultFactory - )) as NewChildEvent["args"]; - - let receiptFactoryArgs = (await getEventArgs( - tx, - "NewChild", - receiptFactoryContract - )) as NewChildEvent["args"]; - - let childContract = new Contract( - child, - (await artifacts.readArtifact("ERC20PriceOracleReceiptVault")).abi - ); - - let { sender, config } = (await getEventArgs( - tx, - "ERC20PriceOracleReceiptVaultInitialized", - childContract - )) as ERC20PriceOracleReceiptVaultInitializedEvent["args"]; - - assert( - sender === erc20PriceOracleReceiptVaultFactory.address, - "wrong deploy sender" - ); - assert( - config.receiptVaultConfig.vaultConfig.asset === asset.address, - "wrong asset address" - ); - assert( - config.receiptVaultConfig.vaultConfig.name === expectedName, - "wrong deploy name" - ); - assert( - config.receiptVaultConfig.vaultConfig.symbol === expectedSymbol, - "wrong deploy symbol" - ); - assert(config.receiptVaultConfig.receipt === receiptFactoryArgs.child); - assert( - config.priceOracle === twoPriceOracle.address, - `wrong deploy priceOracle address: expected ${config.priceOracle} got ${twoPriceOracle.address}` - ); - }); -}); From 0e7f3d5586ad14d81b13a53f62e7d42522b35f63 Mon Sep 17 00:00:00 2001 From: ninokeldishvili Date: Wed, 10 Jul 2024 02:16:09 +0400 Subject: [PATCH 2/8] Add tests Test vault asset, Test vault sets the minShareRatio, Test vault sets the withdraw Id, Test vault totalAssets --- ...PriceOracleReceiptVault.receiptVault.t.sol | 139 ++++++++++++++++++ test/receipt/ReceiptVault.test.ts | 61 -------- 2 files changed, 139 insertions(+), 61 deletions(-) create mode 100644 test/foundry/src/concrete/erc20PriceOracle/ERC20PriceOracleReceiptVault.receiptVault.t.sol diff --git a/test/foundry/src/concrete/erc20PriceOracle/ERC20PriceOracleReceiptVault.receiptVault.t.sol b/test/foundry/src/concrete/erc20PriceOracle/ERC20PriceOracleReceiptVault.receiptVault.t.sol new file mode 100644 index 00000000..543aee1e --- /dev/null +++ b/test/foundry/src/concrete/erc20PriceOracle/ERC20PriceOracleReceiptVault.receiptVault.t.sol @@ -0,0 +1,139 @@ +// SPDX-License-Identifier: CAL +pragma solidity =0.8.25; + +import {MinShareRatio, ZeroAssetsAmount, ZeroReceiver} from "../../../../../contracts/abstract/ReceiptVault.sol"; +import {ERC20PriceOracleReceiptVault} from "../../../../../contracts/concrete/vault/ERC20PriceOracleReceiptVault.sol"; +import {ERC20PriceOracleReceiptVaultTest, Vm} from "test/foundry/abstract/ERC20PriceOracleReceiptVaultTest.sol"; +import {TwoPriceOracle} from "../../../../../contracts/oracle/price/TwoPriceOracle.sol"; +import { + LibFixedPointDecimalArithmeticOpenZeppelin, + Math +} from "rain.math.fixedpoint/lib/LibFixedPointDecimalArithmeticOpenZeppelin.sol"; +import {IERC20} from "forge-std/interfaces/IERC20.sol"; + +contract ERC20PriceOracleReceiptVaultreceiptVaultTest is ERC20PriceOracleReceiptVaultTest { + using LibFixedPointDecimalArithmeticOpenZeppelin for uint256; + + /// Test vault asset + function testVaultAsset( + uint256 fuzzedKeyAlice, + string memory assetName, + uint256 timestamp, + uint8 xauDecimals, + uint8 usdDecimals, + uint80 answeredInRound + ) external { + // Ensure the fuzzed key is within the valid range for secp256 + address alice = vm.addr((fuzzedKeyAlice % (SECP256K1_ORDER - 1)) + 1); + // Use common decimal bounds for price feeds + // Use 0-20 so we at least have some coverage higher than 18 + usdDecimals = uint8(bound(usdDecimals, 0, 20)); + xauDecimals = uint8(bound(xauDecimals, 0, 20)); + timestamp = bound(timestamp, 0, type(uint32).max); + + vm.warp(timestamp); + TwoPriceOracle twoPriceOracle = createTwoPriceOracle(usdDecimals, usdDecimals, timestamp, answeredInRound); + vm.startPrank(alice); + + ERC20PriceOracleReceiptVault vault = createVault(address(twoPriceOracle), assetName, assetName); + + assertEq(vault.asset(), address(iAsset)); + } + + /// Test vault sets the minShareRatio + function testSetMinShareRatio( + uint256 fuzzedKeyAlice, + string memory assetName, + uint256 timestamp, + uint8 xauDecimals, + uint8 usdDecimals, + uint80 answeredInRound, + uint256 minShareRatio + ) external { + // Ensure the fuzzed key is within the valid range for secp256 + address alice = vm.addr((fuzzedKeyAlice % (SECP256K1_ORDER - 1)) + 1); + // Use common decimal bounds for price feeds + // Use 0-20 so we at least have some coverage higher than 18 + usdDecimals = uint8(bound(usdDecimals, 0, 20)); + xauDecimals = uint8(bound(xauDecimals, 0, 20)); + timestamp = bound(timestamp, 0, type(uint32).max); + + vm.warp(timestamp); + TwoPriceOracle twoPriceOracle = createTwoPriceOracle(usdDecimals, usdDecimals, timestamp, answeredInRound); + vm.startPrank(alice); + + ERC20PriceOracleReceiptVault vault = createVault(address(twoPriceOracle), assetName, assetName); + + vault.setMinShareRatio(minShareRatio); + + uint256 resultMinShareRatio = vault.sMinShareRatios(alice); + + assertEqUint(minShareRatio, resultMinShareRatio); + } + + /// Test vault sets the withdraw Id + function testSetWithdrawId( + uint256 fuzzedKeyAlice, + string memory assetName, + uint256 timestamp, + uint8 xauDecimals, + uint8 usdDecimals, + uint80 answeredInRound, + uint256 withdrawId + ) external { + // Ensure the fuzzed key is within the valid range for secp256 + address alice = vm.addr((fuzzedKeyAlice % (SECP256K1_ORDER - 1)) + 1); + // Use common decimal bounds for price feeds + // Use 0-20 so we at least have some coverage higher than 18 + usdDecimals = uint8(bound(usdDecimals, 0, 20)); + xauDecimals = uint8(bound(xauDecimals, 0, 20)); + timestamp = bound(timestamp, 0, type(uint32).max); + + vm.warp(timestamp); + TwoPriceOracle twoPriceOracle = createTwoPriceOracle(usdDecimals, usdDecimals, timestamp, answeredInRound); + vm.startPrank(alice); + + ERC20PriceOracleReceiptVault vault = createVault(address(twoPriceOracle), assetName, assetName); + + vault.setWithdrawId(withdrawId); + + uint256 resultwithdrawId = vault.sWithdrawIds(alice); + + assertEqUint(withdrawId, resultwithdrawId); + } + + /// Test vault totalAssets + function testTotalAssets( + uint256 fuzzedKeyAlice, + string memory assetName, + uint256 timestamp, + uint256 assets, + uint8 xauDecimals, + uint8 usdDecimals, + uint80 answeredInRound + ) external { + // Ensure the fuzzed key is within the valid range for secp256 + address alice = vm.addr((fuzzedKeyAlice % (SECP256K1_ORDER - 1)) + 1); + // Use common decimal bounds for price feeds + // Use 0-20 so we at least have some coverage higher than 18 + usdDecimals = uint8(bound(usdDecimals, 0, 20)); + xauDecimals = uint8(bound(xauDecimals, 0, 20)); + + timestamp = bound(timestamp, 0, type(uint32).max); + assets = bound(assets, 1, type(uint256).max); + + vm.warp(timestamp); + TwoPriceOracle twoPriceOracle = createTwoPriceOracle(usdDecimals, usdDecimals, timestamp, answeredInRound); + vm.startPrank(alice); + + ERC20PriceOracleReceiptVault vault = createVault(address(twoPriceOracle), assetName, assetName); + + vm.mockCall( + address(iAsset), abi.encodeWithSelector(IERC20.balanceOf.selector, address(vault)), abi.encode(assets) + ); + + uint256 resultAssets = vault.totalAssets(); + + assertEqUint(assets, resultAssets); + } +} diff --git a/test/receipt/ReceiptVault.test.ts b/test/receipt/ReceiptVault.test.ts index df9daed7..62f51829 100644 --- a/test/receipt/ReceiptVault.test.ts +++ b/test/receipt/ReceiptVault.test.ts @@ -23,67 +23,6 @@ const assert = require("assert"); let owner: SignerWithAddress; describe("Receipt vault", async function () { - it("Returns the address of the underlying asset that is deposited", async function () { - const [vault, asset] = await deployERC20PriceOracleVault(); - - const signers = await ethers.getSigners(); - const alice = signers[0]; - - const vaultAsset = await vault.connect(alice).asset(); - - assert( - vaultAsset === asset.address, - `Wrong asset address ${asset.address} ${vaultAsset}` - ); - }); - it("Sets the correct min Share Ratio", async function () { - [owner] = await ethers.getSigners(); - const expectedMinShareRatio = ethers.BigNumber.from("100"); - const signers = await ethers.getSigners(); - const alice = signers[0]; - - const [vault] = await deployERC20PriceOracleVault(); - await vault.connect(alice).setMinShareRatio(100); - let minShareRatio = await vault - .connect(alice) - .minShareRatios(owner.address); - - assert( - minShareRatio.eq(expectedMinShareRatio), - `Wrong min Share Ratio ${expectedMinShareRatio} ${minShareRatio}` - ); - }); - it("Sets the correct withdraw Id", async function () { - [owner] = await ethers.getSigners(); - const expectedWithdrawId = ethers.BigNumber.from("100"); - const signers = await ethers.getSigners(); - const alice = signers[0]; - - const [vault] = await deployERC20PriceOracleVault(); - await vault.connect(alice).setWithdrawId(100); - let withdrawId = await vault.connect(alice).withdrawIds(owner.address); - - assert( - withdrawId.eq(expectedWithdrawId), - `Wrong withdraw Id ${expectedWithdrawId} ${withdrawId}` - ); - }); - it("Checks total asset is same as balance", async function () { - const [vault, asset] = await deployERC20PriceOracleVault(); - const signers = await ethers.getSigners(); - const alice = signers[0]; - - await asset.transfer(vault.address, ethers.BigNumber.from(1000)); - - const assets = await asset.balanceOf(vault.address); - - const totalAssets = await vault.connect(alice).totalAssets(); - - assert( - totalAssets.eq(assets), - `Wrong total assets ${assets} ${totalAssets}` - ); - }); it("Calculates correct assets", async function () { const signers = await ethers.getSigners(); const alice = signers[0]; From 45cefa710732ab0d1b509236ab234db1b9bcd1fc Mon Sep 17 00:00:00 2001 From: ninokeldishvili Date: Wed, 10 Jul 2024 02:43:06 +0400 Subject: [PATCH 3/8] Convert several more tests from receiptVait hardhat tests and cleanup it --- ...PriceOracleReceiptVault.receiptVault.t.sol | 200 ++++ test/receipt/ReceiptVault.test.ts | 1011 +---------------- 2 files changed, 222 insertions(+), 989 deletions(-) diff --git a/test/foundry/src/concrete/erc20PriceOracle/ERC20PriceOracleReceiptVault.receiptVault.t.sol b/test/foundry/src/concrete/erc20PriceOracle/ERC20PriceOracleReceiptVault.receiptVault.t.sol index 543aee1e..7cc25885 100644 --- a/test/foundry/src/concrete/erc20PriceOracle/ERC20PriceOracleReceiptVault.receiptVault.t.sol +++ b/test/foundry/src/concrete/erc20PriceOracle/ERC20PriceOracleReceiptVault.receiptVault.t.sol @@ -136,4 +136,204 @@ contract ERC20PriceOracleReceiptVaultreceiptVaultTest is ERC20PriceOracleReceipt assertEqUint(assets, resultAssets); } + + /// Test convertToAssets + function testConvertToAssets( + uint256 fuzzedKeyAlice, + string memory assetName, + uint256 timestamp, + uint256 shares, + uint8 xauDecimals, + uint8 usdDecimals, + uint80 answeredInRound + ) external { + // Ensure the fuzzed key is within the valid range for secp256 + address alice = vm.addr((fuzzedKeyAlice % (SECP256K1_ORDER - 1)) + 1); + // Use common decimal bounds for price feeds + // Use 0-20 so we at least have some coverage higher than 18 + usdDecimals = uint8(bound(usdDecimals, 0, 20)); + xauDecimals = uint8(bound(xauDecimals, 0, 20)); + + timestamp = bound(timestamp, 0, type(uint32).max); + shares = bound(shares, 1, type(uint64).max); + + vm.warp(timestamp); + TwoPriceOracle twoPriceOracle = createTwoPriceOracle(usdDecimals, usdDecimals, timestamp, answeredInRound); + vm.startPrank(alice); + + ERC20PriceOracleReceiptVault vault = createVault(address(twoPriceOracle), assetName, assetName); + + uint256 oraclePrice = twoPriceOracle.price(); + uint256 expectedAssets = shares.fixedPointDiv(oraclePrice, Math.Rounding.Down); + uint256 resultAssets = vault.convertToAssets(shares); + + assertEqUint(expectedAssets, resultAssets); + } + + /// Test convertToAssets shows no variations based on caller + function testConvertToAssetsDifferentCaller( + uint256 fuzzedKeyAlice, + uint256 fuzzedKeyBob, + string memory assetName, + uint256 timestamp, + uint256 shares, + uint8 xauDecimals, + uint8 usdDecimals, + uint80 answeredInRound + ) external { + // Ensure the fuzzed key is within the valid range for secp256 + address alice = vm.addr((fuzzedKeyAlice % (SECP256K1_ORDER - 1)) + 1); + address bob = vm.addr((fuzzedKeyBob % (SECP256K1_ORDER - 1)) + 1); + // Use common decimal bounds for price feeds + // Use 0-20 so we at least have some coverage higher than 18 + usdDecimals = uint8(bound(usdDecimals, 0, 20)); + xauDecimals = uint8(bound(xauDecimals, 0, 20)); + + timestamp = bound(timestamp, 0, type(uint32).max); + shares = bound(shares, 1, type(uint64).max); + + vm.warp(timestamp); + TwoPriceOracle twoPriceOracle = createTwoPriceOracle(usdDecimals, usdDecimals, timestamp, answeredInRound); + vm.startPrank(alice); + + ERC20PriceOracleReceiptVault vault = createVault(address(twoPriceOracle), assetName, assetName); + + uint256 resultAssetsAlice = vault.convertToAssets(shares); + vm.stopPrank(); + + vm.startPrank(bob); + + uint256 resultAssetsBob = vault.convertToAssets(shares); + + assertEqUint(resultAssetsAlice, resultAssetsBob); + } + + /// Test convertToShares + function testConvertToShares( + uint256 fuzzedKeyAlice, + string memory assetName, + uint256 timestamp, + uint256 assets, + uint8 xauDecimals, + uint8 usdDecimals, + uint80 answeredInRound + ) external { + // Ensure the fuzzed key is within the valid range for secp256 + address alice = vm.addr((fuzzedKeyAlice % (SECP256K1_ORDER - 1)) + 1); + // Use common decimal bounds for price feeds + // Use 0-20 so we at least have some coverage higher than 18 + usdDecimals = uint8(bound(usdDecimals, 0, 20)); + xauDecimals = uint8(bound(xauDecimals, 0, 20)); + + timestamp = bound(timestamp, 0, type(uint32).max); + assets = bound(assets, 1, type(uint256).max); + + vm.warp(timestamp); + TwoPriceOracle twoPriceOracle = createTwoPriceOracle(usdDecimals, usdDecimals, timestamp, answeredInRound); + vm.startPrank(alice); + + ERC20PriceOracleReceiptVault vault = createVault(address(twoPriceOracle), assetName, assetName); + + uint256 oraclePrice = twoPriceOracle.price(); + + uint256 expectedShares = assets.fixedPointMul(oraclePrice, Math.Rounding.Down); + uint256 resultShares = vault.convertToShares(assets); + + assertEqUint(expectedShares, resultShares); + } + + /// Test convertToShares + function testConvertToSharesDifferentCaller( + uint256 fuzzedKeyAlice, + uint256 fuzzedKeyBob, + string memory assetName, + uint256 timestamp, + uint256 assets, + uint8 xauDecimals, + uint8 usdDecimals, + uint80 answeredInRound + ) external { + // Ensure the fuzzed key is within the valid range for secp256 + address alice = vm.addr((fuzzedKeyAlice % (SECP256K1_ORDER - 1)) + 1); + address bob = vm.addr((fuzzedKeyBob % (SECP256K1_ORDER - 1)) + 1); + // Use common decimal bounds for price feeds + // Use 0-20 so we at least have some coverage higher than 18 + usdDecimals = uint8(bound(usdDecimals, 0, 20)); + xauDecimals = uint8(bound(xauDecimals, 0, 20)); + + timestamp = bound(timestamp, 0, type(uint32).max); + assets = bound(assets, 1, type(uint256).max); + + vm.warp(timestamp); + TwoPriceOracle twoPriceOracle = createTwoPriceOracle(usdDecimals, usdDecimals, timestamp, answeredInRound); + vm.startPrank(alice); + + ERC20PriceOracleReceiptVault vault = createVault(address(twoPriceOracle), assetName, assetName); + uint256 resultSharesAlice = vault.convertToShares(assets); + vm.stopPrank(); + + vm.startPrank(bob); + + uint256 resultSharesBob = vault.convertToShares(assets); + + assertEqUint(resultSharesAlice, resultSharesBob); + } + + /// Test vault sets correct max deposit + function testMaxDeposit( + uint256 fuzzedKeyAlice, + string memory assetName, + uint256 timestamp, + uint8 xauDecimals, + uint8 usdDecimals, + uint80 answeredInRound + ) external { + // Ensure the fuzzed key is within the valid range for secp256 + address alice = vm.addr((fuzzedKeyAlice % (SECP256K1_ORDER - 1)) + 1); + // Use common decimal bounds for price feeds + // Use 0-20 so we at least have some coverage higher than 18 + usdDecimals = uint8(bound(usdDecimals, 0, 20)); + xauDecimals = uint8(bound(xauDecimals, 0, 20)); + + timestamp = bound(timestamp, 0, type(uint32).max); + + vm.warp(timestamp); + TwoPriceOracle twoPriceOracle = createTwoPriceOracle(usdDecimals, usdDecimals, timestamp, answeredInRound); + vm.startPrank(alice); + + ERC20PriceOracleReceiptVault vault = createVault(address(twoPriceOracle), assetName, assetName); + + uint256 maxDeposit = vault.maxDeposit(alice); + + assertEqUint(maxDeposit, type(uint256).max); + } + + /// Test vault sets correct max Mint + function testMaxShares( + uint256 fuzzedKeyAlice, + string memory assetName, + uint256 timestamp, + uint8 xauDecimals, + uint8 usdDecimals, + uint80 answeredInRound + ) external { + // Ensure the fuzzed key is within the valid range for secp256 + address alice = vm.addr((fuzzedKeyAlice % (SECP256K1_ORDER - 1)) + 1); + // Use common decimal bounds for price feeds + // Use 0-20 so we at least have some coverage higher than 18 + usdDecimals = uint8(bound(usdDecimals, 0, 20)); + xauDecimals = uint8(bound(xauDecimals, 0, 20)); + + timestamp = bound(timestamp, 0, type(uint32).max); + + vm.warp(timestamp); + TwoPriceOracle twoPriceOracle = createTwoPriceOracle(usdDecimals, usdDecimals, timestamp, answeredInRound); + vm.startPrank(alice); + + ERC20PriceOracleReceiptVault vault = createVault(address(twoPriceOracle), assetName, assetName); + + uint256 maxMint = vault.maxMint(alice); + + assertEqUint(maxMint, type(uint256).max); + } } diff --git a/test/receipt/ReceiptVault.test.ts b/test/receipt/ReceiptVault.test.ts index 62f51829..e2c101a7 100644 --- a/test/receipt/ReceiptVault.test.ts +++ b/test/receipt/ReceiptVault.test.ts @@ -1,683 +1,41 @@ import { ethers } from "hardhat"; import { - assertError, deployERC20PriceOracleVault, - fixedPointDiv, fixedPointMul, - ADDRESS_ZERO, - expectedReferencePrice, getEvent, } from "../util"; import { DepositEvent } from "../../typechain-types/@openzeppelin/contracts-upgradeable/interfaces/IERC4626Upgradeable"; -import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; import { - DepositWithReceiptEvent, ReceiptVaultInformationEvent, - WithdrawWithReceiptEvent, } from "../../typechain-types/contracts/vault/receipt/ReceiptVault"; import { getEventArgs } from "../util"; import { ReceiptInformationEvent } from "../../typechain-types/contracts/vault/receipt/Receipt"; const assert = require("assert"); -let owner: SignerWithAddress; - -describe("Receipt vault", async function () { - it("Calculates correct assets", async function () { - const signers = await ethers.getSigners(); - const alice = signers[0]; - - const [vault, , priceOracle] = await deployERC20PriceOracleVault(); - - const shareRatio = await priceOracle.price(); - - assert( - shareRatio.eq(expectedReferencePrice), - `Incorrect referencePrice ${shareRatio} ${expectedReferencePrice}` - ); - - const share = ethers.BigNumber.from("10").pow(20); - const expectedAsset = fixedPointDiv(share, shareRatio); - - const assets = await vault.connect(alice).convertToAssets(share); - - assert(assets.eq(expectedAsset), `Wrong asset ${expectedAsset} ${assets}`); - }); - it("Shows no variations based on caller", async function () { - const signers = await ethers.getSigners(); - const alice = signers[0]; - const bob = signers[1]; - - const [vault] = await deployERC20PriceOracleVault(); - - const aliceVault = vault.connect(alice); - const bobVault = vault.connect(bob); - - const share = ethers.BigNumber.from("10").pow(20); - - const assetsAlice = await aliceVault.convertToAssets(share); - const assetsBob = await bobVault.convertToAssets(share); - - assert( - assetsAlice.eq(assetsBob), - `Wrong asset ${assetsAlice} ${assetsBob}` - ); - }); - it("Calculates correct shares", async function () { - [owner] = await ethers.getSigners(); - - const [vault, , priceOracle] = await deployERC20PriceOracleVault(); - - const shareRatio = await priceOracle.price(); - - const minPrice = ethers.BigNumber.from(0); - - assert( - shareRatio >= minPrice, - `ShareRatio is less then minShareRatio ${shareRatio} ${minPrice}` - ); - - assert( - shareRatio.eq(expectedReferencePrice), - `Incorrect shareRatio ${shareRatio} ${expectedReferencePrice}` - ); - - const assets = ethers.BigNumber.from("10").pow(20); - const expectedShares = fixedPointMul(assets, shareRatio); - - const shares = await vault.connect(owner).convertToShares(assets); - - assert( - shares.eq(expectedShares), - `Wrong share ${expectedShares} ${shares}` - ); - }); - it("Shows no variations based on caller for convertToShares", async function () { - const signers = await ethers.getSigners(); - const alice = signers[0]; - const bob = signers[1]; - - const [vault] = await deployERC20PriceOracleVault(); - - const aliceVault = vault.connect(alice); - const bobVault = vault.connect(bob); - - const assets = ethers.BigNumber.from("10").pow(20); - - const sharesAlice = await aliceVault.convertToShares(assets); - const sharesBob = await bobVault.convertToShares(assets); - - assert( - sharesAlice.eq(sharesBob), - `Wrong shares ${sharesAlice} ${sharesBob}` - ); - }); - it("Sets correct max deposit value", async function () { - const [vault] = await deployERC20PriceOracleVault(); - const signers = await ethers.getSigners(); - const alice = signers[0]; - - const expectedMaxDeposit = ethers.BigNumber.from(2) - .pow(256) - //up to 2**256 so should substruct 1 - .sub(1); - const maxDeposit = await vault.connect(alice).maxDeposit(owner.address); - - assert( - maxDeposit.eq(expectedMaxDeposit), - `Wrong max deposit ${expectedMaxDeposit} ${maxDeposit}` - ); - }); - it("Respects min shareRatio in case of previewDeposit", async function () { - const signers = await ethers.getSigners(); - const alice = signers[0]; - - const [vault, , priceOracle] = await deployERC20PriceOracleVault(); - - const assets = ethers.BigNumber.from("10").pow(20); - const shareRatio = await priceOracle.price(); - - await vault.connect(alice).setMinShareRatio(shareRatio.add(1)); - - await assertError( - async () => await vault.connect(alice).previewDeposit(assets), - "MinShareRatio", - "failed to respect min share ratio" - ); - }); - it("Sets correct shares by previewDeposit", async function () { - const [vault, , priceOracle] = await deployERC20PriceOracleVault(); - const signers = await ethers.getSigners(); - const alice = signers[0]; - - const assets = ethers.BigNumber.from("10").pow(20); - - const shareRatio = await priceOracle.price(); - - assert( - shareRatio.eq(expectedReferencePrice), - `Incorrect referencePrice ${shareRatio} ${expectedReferencePrice}` - ); - - const expectedshares = fixedPointMul(assets, shareRatio); - const share = await vault.connect(alice).previewDeposit(assets); - - assert(share.eq(expectedshares), `Wrong shares ${expectedshares} ${share}`); - }); - it("Checks ReceiptVaultInformation event is emitted", async function () { - const [vault] = await deployERC20PriceOracleVault(); - const signers = await ethers.getSigners(); - const alice = signers[0]; - const { sender, vaultInformation } = (await getEventArgs( - await vault.connect(alice).receiptVaultInformation([1]), - "ReceiptVaultInformation", - vault - )) as ReceiptVaultInformationEvent["args"]; - - assert( - sender === alice.address, - `Incorrect sender. Expected ${alice.address} got ${sender}` - ); - - assert( - vaultInformation === "0x01", - `Incorrect sender. Expected 0x01 got ${vaultInformation}` - ); - }); -}); -describe("Deposit", async () => { - it("Must respect min Price", async function () { - const [vault, asset, priceOracle] = await deployERC20PriceOracleVault(); - const shareRatio = await priceOracle.price(); - - const signers = await ethers.getSigners(); - const alice = signers[0]; - const bob = signers[1]; - - const assets = ethers.BigNumber.from("10").pow(20); - - await vault.connect(alice).setMinShareRatio(shareRatio.add(1)); - - await asset.connect(alice).increaseAllowance(vault.address, assets); - - await assertError( - async () => - await vault - .connect(alice) - ["deposit(uint256,address)"](assets, bob.address), - "MinShareRatio", - "failed to respect min price" - ); - }); - it("Calculates shares correctly with min shareRatio set", async function () { - const signers = await ethers.getSigners(); - - const [vault, asset, priceOracle] = await deployERC20PriceOracleVault(); - - const alice = signers[1]; - - const totalTokenSupply = await asset.totalSupply(); - - const assets = totalTokenSupply.div(2); - - // give alice reserve to cover cost - await asset.transfer(alice.address, assets); - - // Min shareRatio MUST be respected - const shareRatio = await priceOracle.price(); - - await asset.connect(alice).increaseAllowance(vault.address, assets); - - const expectedShares = fixedPointMul(assets, shareRatio); - - await vault.connect(alice).setMinShareRatio(shareRatio.sub(1)); - - await vault - .connect(alice) - ["deposit(uint256,address)"](assets, alice.address); - const shares = await vault - .connect(alice) - ["balanceOf(address)"](alice.address); - - assert( - shares.eq(expectedShares), - `Wrong alice shares ${expectedShares} ${shares}` - ); - }); - it("Calculates shares correctly with NO min shareRatio set", async function () { - const signers = await ethers.getSigners(); - - const [vault, asset, priceOracle] = await deployERC20PriceOracleVault(); - - const alice = signers[1]; - - const totalTokenSupply = await asset.totalSupply(); - - const assets = totalTokenSupply.div(2); - - // give alice reserve to cover cost - await asset.transfer(alice.address, assets); - - // Min shareRatio MUST be respected - const shareRatio = await priceOracle.price(); - - await asset.connect(alice).increaseAllowance(vault.address, assets); - - const expectedShares = fixedPointMul(assets, shareRatio); - - await vault - .connect(alice) - ["deposit(uint256,address)"](assets, alice.address); - const shares = await vault - .connect(alice) - ["balanceOf(address)"](alice.address); - - assert( - shares.eq(expectedShares), - `Wrong alice ETHg ${expectedShares} ${shares}` - ); - }); - it("Reverts if not enough assets to be transferred", async function () { - const signers = await ethers.getSigners(); - - const [vault, asset] = await deployERC20PriceOracleVault(); - - const alice = signers[1]; - - const totalTokenSupply = await asset.totalSupply(); - - const assets = totalTokenSupply.div(2); - - await asset.connect(alice).increaseAllowance(vault.address, assets); - - await assertError( - async () => - await vault - .connect(alice) - ["deposit(uint256,address)"](assets, alice.address), - "ERC20: transfer amount exceeds balance", - "failed to respect min price" - ); - }); - it("Receiver MAY be different user to depositor", async function () { - const signers = await ethers.getSigners(); - const alice = signers[0]; - const bob = signers[1]; - - const [vault, asset, priceOracle] = await deployERC20PriceOracleVault(); - const shareRatio = await priceOracle.price(); - - const totalTokenSupply = await asset.totalSupply(); - const assets = totalTokenSupply.div(2); - // give alice reserve to cover cost - await asset.transfer(alice.address, assets); - - await asset.connect(alice).increaseAllowance(vault.address, assets); - - await vault.connect(alice)["deposit(uint256,address)"](assets, bob.address); - const shares = await vault - .connect(alice) - ["balanceOf(address)"](bob.address); - const expectedShares = fixedPointMul(assets, shareRatio); - - assert( - shares.eq(expectedShares), - `wrong alice ETHg ${expectedShares} ${shares}` - ); - }); - it("Receiver receives BOTH erc20 and erc1155, depositor gets nothing but MUST transfer assets", async function () { - const signers = await ethers.getSigners(); - const alice = signers[0]; - const bob = signers[1]; - - const [vault, asset, priceOracle, receipt] = - await deployERC20PriceOracleVault(); - const shareRatio = await priceOracle.price(); - - const totalTokenSupply = await asset.totalSupply(); - const assets = totalTokenSupply.div(2); - // give alice reserve to cover cost - await asset.transfer(alice.address, assets); - - await asset.connect(alice).increaseAllowance(vault.address, assets); - - //Alice assets before deposit - const aliceAssetBefore = await asset - .connect(alice) - .balanceOf(alice.address); - - await vault.connect(alice)["deposit(uint256,address)"](assets, bob.address); - - const expectedBobBalance = fixedPointMul(assets, shareRatio); - - //Receiver gets both Erc20 and Erc1155 - const erc1155Balance = await receipt - .connect(alice) - ["balanceOf(address,uint256)"](bob.address, shareRatio); - - const bobErc20Balance = await vault - .connect(alice) - ["balanceOf(address)"](bob.address); - - assert( - erc1155Balance.eq(expectedBobBalance), - `wrong bob erc1155 balance ${expectedBobBalance} ${erc1155Balance}` - ); - - assert( - bobErc20Balance.eq(expectedBobBalance), - `wrong bob erc1155 balance ${expectedBobBalance} ${bobErc20Balance}` - ); - - //Depositor Gets nothing - const aliceErc20Balance = await vault - .connect(alice) - ["balanceOf(address)"](alice.address); - - const aliceErc1155Balance = await receipt - .connect(alice) - ["balanceOf(address,uint256)"](alice.address, shareRatio); - - assert( - aliceErc1155Balance.eq(0), - `wrong alice erc20 balance ${aliceErc1155Balance} 0` - ); - - assert( - aliceErc20Balance.eq(0), - `wrong alice erc20 balance ${aliceErc20Balance} 0` - ); - - //Depositor MUST transfer assets - const aliceAssetAft = await asset.connect(alice).balanceOf(alice.address); - - assert( - aliceAssetAft.eq(aliceAssetBefore.sub(assets)), - `wrong alice assets ${aliceAssetAft} ${aliceAssetBefore.sub(assets)}` - ); - }); - it("MUST revert if the vault can't take enough assets from the depositor", async function () { - const signers = await ethers.getSigners(); - const alice = signers[1]; - - const [vault, asset] = await deployERC20PriceOracleVault(); - - const totalTokenSupply = await asset.totalSupply(); - const assets = totalTokenSupply.div(2); - // give alice reserve to cover cost - await asset.transfer(alice.address, assets); - - //alice has assets of totalsuply/2 - await asset.connect(alice).increaseAllowance(vault.address, assets.add(1)); - - //try to deposit more assets then alice owns - await assertError( - async () => - await vault - .connect(alice) - ["deposit(uint256,address)"](assets.add(1), alice.address), - "ERC20: transfer amount exceeds balance", - "failed to revert" - ); - }); - it("MUST NOT successfully deposit if the vault is not approved for the depositor's assets", async function () { - const signers = await ethers.getSigners(); - const alice = signers[0]; - - const [vault, asset] = await deployERC20PriceOracleVault(); - - const totalTokenSupply = await asset.totalSupply(); - const assets = totalTokenSupply.div(2); - // give alice reserve to cover cost - await asset.transfer(alice.address, assets); - - //try to deposit without approve - await assertError( - async () => - await vault - .connect(alice) - ["deposit(uint256,address)"](assets, alice.address), - "ERC20: insufficient allowance'", - "failed to revert" - ); - }); - it("should not deposit to zero address", async function () { - const signers = await ethers.getSigners(); - const alice = signers[1]; - - const [vault, asset] = await deployERC20PriceOracleVault(); - - const totalTokenSupply = await asset.totalSupply(); - const aliceDepositAmount = totalTokenSupply.div(2); - - // give alice reserve to cover cost - await asset.transfer(alice.address, aliceDepositAmount); - - const aliceReserveBalance = await asset.balanceOf(alice.address); - - await asset.connect(alice).approve(vault.address, aliceReserveBalance); - - await assertError( - async () => - await vault - .connect(alice) - ["deposit(uint256,address)"](aliceReserveBalance, ADDRESS_ZERO), - "ZeroReceiver", - "failed to prevent deposit to zero address" - ); - }); - it("Check deposit event is emitted", async function () { - const signers = await ethers.getSigners(); - const alice = signers[0]; - - const [vault, asset, priceOracle] = await deployERC20PriceOracleVault(); - - const shareRatio = await priceOracle.price(); - - const aliceAmount = ethers.BigNumber.from(5000); - await asset.transfer(alice.address, aliceAmount); - - await asset.connect(alice).increaseAllowance(vault.address, aliceAmount); - - const expectedShares = fixedPointMul(aliceAmount, shareRatio); - - const depositTX = await vault - .connect(alice) - ["deposit(uint256,address)"](aliceAmount, alice.address); - const depositEvent = (await getEvent( - depositTX, - "Deposit", - vault - )) as DepositEvent; - - assert( - depositEvent.args.assets.eq(aliceAmount), - `wrong assets expected ${aliceAmount} got ${depositEvent.args.assets}` - ); - assert( - depositEvent.args.shares.eq(expectedShares), - `wrong shares expected ${depositEvent.args.shares} got ${expectedShares}` - ); - assert( - depositEvent.args.sender === alice.address, - `wrong sender expected ${alice.address} got ${depositEvent.args.sender}` - ); - assert( - depositEvent.args.owner === alice.address, - `wrong owner expected ${alice.address} got ${depositEvent.args.owner}` - ); - }); -}); -describe("Overloaded `deposit`", async () => { - it("Must respect min shareRatio", async function () { - const [vault, asset, priceOracle] = await deployERC20PriceOracleVault(); - const shareRatio = await priceOracle.price(); - - const signers = await ethers.getSigners(); - const alice = signers[0]; - - const totalTokenSupply = await asset.totalSupply(); - - const assets = totalTokenSupply.div(2); - - await vault.connect(alice).setMinShareRatio(shareRatio.add(1)); - - await asset.connect(alice).increaseAllowance(vault.address, assets); - - await assertError( - async () => - await vault - .connect(alice) - ["deposit(uint256,address,uint256,bytes)"]( - assets, - alice.address, - shareRatio.add(1), - [] - ), - "MinShareRatio", - "failed to respect min shareRatio" - ); - }); - it("Calculates shares correctly with min shareRatio set", async function () { - const signers = await ethers.getSigners(); - - const [vault, asset, priceOracle] = await deployERC20PriceOracleVault(); - - const alice = signers[1]; - - const totalTokenSupply = await asset.totalSupply(); - - const assets = totalTokenSupply.div(2); - - // give alice reserve to cover cost - await asset.transfer(alice.address, assets); - - // Min shareRatio MUST be respected - const shareRatio = await priceOracle.price(); - - await asset.connect(alice).increaseAllowance(vault.address, assets); - - const expectedShares = fixedPointMul(assets, shareRatio); - - await vault.connect(alice).setMinShareRatio(shareRatio.sub(1)); - - await vault - .connect(alice) - ["deposit(uint256,address,uint256,bytes)"]( - assets, - alice.address, - shareRatio, - [] - ); - const shares = await vault - .connect(alice) - ["balanceOf(address)"](alice.address); - - assert( - shares.eq(expectedShares), - `wrong alice shares ${expectedShares} ${shares}` - ); - }); - it("Calculates shares correctly with NO min shareRatio set", async function () { +describe("Receipt vault", async function () { + it("Checks ReceiptVaultInformation event is emitted", async function () { + const [vault] = await deployERC20PriceOracleVault(); const signers = await ethers.getSigners(); - - const [vault, asset, priceOracle] = await deployERC20PriceOracleVault(); - - const alice = signers[1]; - - const totalTokenSupply = await asset.totalSupply(); - - const assets = totalTokenSupply.div(2); - - // give alice reserve to cover cost - await asset.transfer(alice.address, assets); - - // Min shareRatio price MUST be respected - const shareRatio = await priceOracle.price(); - - await asset.connect(alice).increaseAllowance(vault.address, assets); - - const expectedShares = fixedPointMul(assets, shareRatio); - - await vault - .connect(alice) - ["deposit(uint256,address,uint256,bytes)"]( - assets, - alice.address, - shareRatio, - [] - ); - const shares = await vault - .connect(alice) - ["balanceOf(address)"](alice.address); + const alice = signers[0]; + const { sender, vaultInformation } = (await getEventArgs( + await vault.connect(alice).receiptVaultInformation([1]), + "ReceiptVaultInformation", + vault + )) as ReceiptVaultInformationEvent["args"]; assert( - shares.eq(expectedShares), - `wrong alice ETHg ${expectedShares} ${shares}` - ); - }); - it("Reverts if not enough assets to be transferred", async function () { - const signers = await ethers.getSigners(); - - const [vault, asset, priceOracle] = await deployERC20PriceOracleVault(); - - const alice = signers[1]; - - const totalTokenSupply = await asset.totalSupply(); - - const assets = totalTokenSupply.div(2); - - const shareRatio = await priceOracle.price(); - - await asset.connect(alice).increaseAllowance(vault.address, assets); - - await assertError( - async () => - await vault - .connect(alice) - ["deposit(uint256,address,uint256,bytes)"]( - assets, - alice.address, - shareRatio, - [] - ), - "ERC20: transfer amount exceeds balance", - "failed to deposit" + sender === alice.address, + `Incorrect sender. Expected ${alice.address} got ${sender}` ); - }); - it("Receiver MAY be different user to depositor", async function () { - const signers = await ethers.getSigners(); - const alice = signers[0]; - const bob = signers[1]; - - const [vault, asset, priceOracle] = await deployERC20PriceOracleVault(); - const shareRatio = await priceOracle.price(); - - const totalTokenSupply = await asset.totalSupply(); - const assets = totalTokenSupply.div(2); - // give alice reserve to cover cost - await asset.transfer(alice.address, assets); - - await asset.connect(alice).increaseAllowance(vault.address, assets); - - await vault - .connect(alice) - ["deposit(uint256,address,uint256,bytes)"]( - assets, - bob.address, - shareRatio, - [] - ); - const shares = await vault - .connect(alice) - ["balanceOf(address)"](bob.address); - const expectedShares = fixedPointMul(assets, shareRatio); assert( - shares.eq(expectedShares), - `wrong alice shares ${expectedShares} ${shares}` + vaultInformation === "0x01", + `Incorrect sender. Expected 0x01 got ${vaultInformation}` ); }); +}); +describe("Deposit", async () => { it("Receiver receives BOTH erc20 and erc1155, depositor gets nothing but MUST transfer assets", async function () { const signers = await ethers.getSigners(); const alice = signers[0]; @@ -699,14 +57,7 @@ describe("Overloaded `deposit`", async () => { .connect(alice) .balanceOf(alice.address); - await vault - .connect(alice) - ["deposit(uint256,address,uint256,bytes)"]( - assets, - bob.address, - shareRatio, - [] - ); + await vault.connect(alice)["deposit(uint256,address)"](assets, bob.address); const expectedBobBalance = fixedPointMul(assets, shareRatio); @@ -756,94 +107,6 @@ describe("Overloaded `deposit`", async () => { `wrong alice assets ${aliceAssetAft} ${aliceAssetBefore.sub(assets)}` ); }); - it("MUST revert if the vault can't take enough assets from the depositor", async function () { - const signers = await ethers.getSigners(); - const alice = signers[1]; - - const [vault, asset, priceOracle] = await deployERC20PriceOracleVault(); - const shareRatio = await priceOracle.price(); - - const totalTokenSupply = await asset.totalSupply(); - const assets = totalTokenSupply.div(2); - // give alice reserve to cover cost - await asset.transfer(alice.address, assets); - - //alice has assets of totalsuply/2 - await asset.connect(alice).increaseAllowance(vault.address, assets.add(1)); - - //try to deposit more assets than alice owns - await assertError( - async () => - await vault - .connect(alice) - ["deposit(uint256,address,uint256,bytes)"]( - assets.add(1), - alice.address, - shareRatio, - [] - ), - "ERC20: transfer amount exceeds balance", - "failed to revert" - ); - }); - it("MUST NOT successfully deposit if the vault is not approved for the depositor's assets", async function () { - const signers = await ethers.getSigners(); - const alice = signers[0]; - - const [vault, asset, priceOracle] = await deployERC20PriceOracleVault(); - const shareRatio = await priceOracle.price(); - - const totalTokenSupply = await asset.totalSupply(); - const assets = totalTokenSupply.div(2); - // give alice reserve to cover cost - await asset.transfer(alice.address, assets); - - //try to deposit without approve - await assertError( - async () => - await vault - .connect(alice) - ["deposit(uint256,address,uint256,bytes)"]( - assets, - alice.address, - shareRatio, - [] - ), - "ERC20: insufficient allowance'", - "failed to revert" - ); - }); - it("should not deposit to zero address", async function () { - const signers = await ethers.getSigners(); - const alice = signers[1]; - - const [vault, asset, oraclePrice] = await deployERC20PriceOracleVault(); - - const totalTokenSupply = await asset.totalSupply(); - const aliceDepositAmount = totalTokenSupply.div(2); - - // give alice reserve to cover cost - await asset.transfer(alice.address, aliceDepositAmount); - - const aliceReserveBalance = await asset.balanceOf(alice.address); - - await asset.connect(alice).approve(vault.address, aliceReserveBalance); - - const shareRatio = await oraclePrice.price(); - await assertError( - async () => - await vault - .connect(alice) - ["deposit(uint256,address,uint256,bytes)"]( - aliceReserveBalance, - ADDRESS_ZERO, - shareRatio, - [] - ), - "ZeroReceiver", - "failed to prevent deposit to zero address" - ); - }); it("Check deposit event is emitted", async function () { const signers = await ethers.getSigners(); const alice = signers[0]; @@ -861,12 +124,7 @@ describe("Overloaded `deposit`", async () => { const depositTX = await vault .connect(alice) - ["deposit(uint256,address,uint256,bytes)"]( - aliceAmount, - alice.address, - shareRatio, - [] - ); + ["deposit(uint256,address)"](aliceAmount, alice.address); const depositEvent = (await getEvent( depositTX, "Deposit", @@ -883,73 +141,15 @@ describe("Overloaded `deposit`", async () => { ); assert( depositEvent.args.sender === alice.address, - `wrong caller expected ${alice.address} got ${depositEvent.args.sender}` + `wrong sender expected ${alice.address} got ${depositEvent.args.sender}` ); assert( depositEvent.args.owner === alice.address, `wrong owner expected ${alice.address} got ${depositEvent.args.owner}` ); }); - it("Check DepositWithReceipt event is emitted", async function () { - const signers = await ethers.getSigners(); - const alice = signers[0]; - - const [vault, asset, priceOracle] = await deployERC20PriceOracleVault(); - - const shareRatio = await priceOracle.price(); - - const aliceAmount = ethers.BigNumber.from(5000); - await asset.transfer(alice.address, aliceAmount); - - await asset.connect(alice).increaseAllowance(vault.address, aliceAmount); - - const expectedId = shareRatio; - //take random bytes for information - const information = [125, 126]; - //generate hex string - const expectedInformation = - "0x" + information.map((num) => num.toString(16)).join(""); - - const expectedShares = fixedPointMul(aliceAmount, shareRatio); - - const { sender, owner, assets, shares, id, receiptInformation } = - (await getEventArgs( - await vault - .connect(alice) - ["deposit(uint256,address,uint256,bytes)"]( - aliceAmount, - alice.address, - shareRatio, - information - ), - "DepositWithReceipt", - vault - )) as DepositWithReceiptEvent["args"]; - - assert( - sender === alice.address, - `wrong sender expected ${alice.address} got ${sender}` - ); - assert(id.eq(expectedId), `wrong id expected ${id} got ${expectedId}`); - - assert( - owner === alice.address, - `wrong owner expected ${alice.address} got ${owner}` - ); - assert( - assets.eq(aliceAmount), - `wrong assets expected ${aliceAmount} got ${assets}` - ); - assert( - shares.eq(expectedShares), - `wrong shares expected ${expectedShares} got ${shares}` - ); - - assert( - receiptInformation === expectedInformation, - `wrong receiptInformation expected ${receiptInformation} got ${expectedInformation}` - ); - }); +}); +describe("Overloaded `deposit`", async () => { it("Check ReceiptInformation event is emitted", async function () { const signers = await ethers.getSigners(); const alice = signers[0]; @@ -994,172 +194,5 @@ describe("Overloaded `deposit`", async () => { information === expectedInformation, `wrong information expected ${information} got ${expectedInformation}` ); - }); - it("Check WithdrawWithReceipt event is emitted", async function () { - const signers = await ethers.getSigners(); - const alice = signers[0]; - - const [vault, asset, priceOracle, receipt] = - await deployERC20PriceOracleVault(); - - const shareRatio = await priceOracle.price(); - - const aliceAmount = ethers.BigNumber.from(5000); - await asset.transfer(alice.address, aliceAmount); - - await asset.connect(alice).increaseAllowance(vault.address, aliceAmount); - - const expectedId = shareRatio; - //take random bytes for information - const information = [125, 126]; - - const depositTx = await vault - .connect(alice) - ["deposit(uint256,address,uint256,bytes)"]( - aliceAmount, - alice.address, - shareRatio, - information - ); - - await depositTx.wait(); - - const erc1155Balance = await receipt - .connect(alice) - ["balanceOf(address,uint256)"](alice.address, shareRatio); - - const { sender, receiver, owner, assets, shares, id, receiptInformation } = - (await getEventArgs( - await vault - .connect(alice) - ["withdraw(uint256,address,address,uint256,bytes)"]( - erc1155Balance, - alice.address, - alice.address, - shareRatio, - [1] - ), - "WithdrawWithReceipt", - vault - )) as WithdrawWithReceiptEvent["args"]; - - const expectedShares = fixedPointMul(assets, shareRatio).add(1); - - assert( - sender === alice.address, - `wrong caller expected ${alice.address} got ${sender}` - ); - - assert( - receiver === alice.address, - `wrong receiver expected ${alice.address} got ${receiver}` - ); - - assert( - owner === alice.address, - `wrong owner expected ${alice.address} got ${owner}` - ); - - assert( - assets.eq(erc1155Balance), - `wrong assets expected ${erc1155Balance} got ${assets}` - ); - assert( - shares.eq(expectedShares), - `wrong shares expected ${expectedShares} got ${shares}` - ); - - assert(id.eq(expectedId), `wrong id expected ${id} got ${expectedId}`); - - assert( - receiptInformation === "0x01", - `wrong receiptInformation expected 0x01 got ${receiptInformation}` - ); - }); -}); -describe("Mint", async function () { - it("Sets maxShares correctly", async function () { - const signers = await ethers.getSigners(); - const alice = signers[0]; - - const [vault] = await deployERC20PriceOracleVault(); - - const expectedMaxShares = ethers.BigNumber.from(2) - .pow(256) - //up to 2**256 so should substruct 1 - .sub(1); - const maxShares = await vault.connect(alice).maxMint(owner.address); - - assert( - maxShares.eq(expectedMaxShares), - `Wrong max deposit ${expectedMaxShares} ${maxShares}` - ); - }); - it("Checks min share ratio is less than share ratio", async function () { - const [vault, , priceOracle] = await deployERC20PriceOracleVault(); - const shareRatio = await priceOracle.price(); - - const signers = await ethers.getSigners(); - const alice = signers[0]; - - const shares = ethers.BigNumber.from("10").pow(20); - - await vault.connect(alice).setMinShareRatio(shareRatio.add(1)); - - await assertError( - async () => await vault.connect(alice).previewMint(shares), - "MinShareRatio", - "failed to respect min shareRatio" - ); - }); - it("PreviewMint - Calculates assets correctly with round up", async function () { - const [vault, , priceOracle] = await deployERC20PriceOracleVault(); - const signers = await ethers.getSigners(); - const alice = signers[0]; - const shareRatio = await priceOracle.price(); - - assert( - shareRatio.eq(expectedReferencePrice), - `Incorrect shareRatio ${shareRatio} ${expectedReferencePrice}` - ); - - const shares = ethers.BigNumber.from("10").pow(20); - const expectedAssets = fixedPointDiv(shares, shareRatio).add(1); - - const assets = await vault.connect(alice).previewMint(shares); - - assert( - assets.eq(expectedAssets), - `Wrong max deposit ${expectedAssets} ${assets}` - ); - }); - it("Mint - Calculates assets correctly", async function () { - const signers = await ethers.getSigners(); - - const [vault, asset, priceOracle] = await deployERC20PriceOracleVault(); - - const alice = signers[0]; - - const assets = ethers.BigNumber.from(5000); - await asset.transfer(alice.address, assets); - await asset.connect(alice).increaseAllowance(vault.address, assets); - - const aliceBalanceBefore = await asset.balanceOf(alice.address); - - const shareRatio = await priceOracle.price(); - - const shares = fixedPointMul(assets, shareRatio); - - await vault.connect(alice)["mint(uint256,address)"](shares, alice.address); - - const expectedAssets = fixedPointDiv(shares, shareRatio).add(1); - - const aliceBalanceAfter = await asset.balanceOf(alice.address); - const aliceBalanceDiff = aliceBalanceBefore.sub(aliceBalanceAfter); - - assert( - aliceBalanceDiff.eq(expectedAssets), - `wrong alice assets ${expectedAssets} ${aliceBalanceDiff}` - ); - }); -}); + }) +}) \ No newline at end of file From a023863c05ba24e6c209dc35d470eb53cd30c855 Mon Sep 17 00:00:00 2001 From: ninokeldishvili Date: Wed, 10 Jul 2024 02:45:28 +0400 Subject: [PATCH 4/8] Remove tests already converted --- test/priceOracle/Erc1155.test.ts | 112 ------------------------------- test/priceOracle/Erc20.test.ts | 110 ------------------------------ 2 files changed, 222 deletions(-) delete mode 100644 test/priceOracle/Erc1155.test.ts delete mode 100644 test/priceOracle/Erc20.test.ts diff --git a/test/priceOracle/Erc1155.test.ts b/test/priceOracle/Erc1155.test.ts deleted file mode 100644 index a119861d..00000000 --- a/test/priceOracle/Erc1155.test.ts +++ /dev/null @@ -1,112 +0,0 @@ -import { ethers } from "hardhat"; -import { - deployERC20PriceOracleVault, - expectedUri, - fixedPointMul, -} from "../util"; - -const assert = require("assert"); - -describe("erc1155 usage", async function () { - it("should initialize well", async function () { - const [vault, asset, price, receipt] = await deployERC20PriceOracleVault(); - - const signers = await ethers.getSigners(); - const alice = signers[0]; - const id = 12345; - - const erc1155Uri = await receipt.connect(alice).uri(id); - - assert( - erc1155Uri === expectedUri, - `erc1155 did not construct with correct uri expected - ${expectedUri}, got - ${erc1155Uri} ` - ); - }); - - it("should only send itself", async function () { - const signers = await ethers.getSigners(); - - const [vault, erc20Token, priceOracle, receipt] = - await deployERC20PriceOracleVault(); - const shareRatio = await priceOracle.price(); - const alice = signers[0]; - const bob = signers[1]; - const depositAmount = ethers.BigNumber.from(1000); - - await erc20Token - .connect(alice) - .increaseAllowance(vault.address, depositAmount); - await vault - .connect(alice) - ["deposit(uint256,address,uint256,bytes)"]( - depositAmount, - alice.address, - shareRatio, - [] - ); - - const expectedErc20Balance = fixedPointMul(depositAmount, shareRatio); - const expectedErc20BalanceAfter = expectedErc20Balance; - const expectedErc1155Balance = expectedErc20Balance; - const expectedErc1155BalanceAfter = expectedErc1155Balance.div(2); - const expected1155ID = await priceOracle.price(); - - const erc20Balance = await vault - .connect(alice) - ["balanceOf(address)"](signers[0].address); - assert( - erc20Balance.eq(expectedErc20Balance), - `wrong erc20 balance ${expectedErc20Balance} ${erc20Balance}` - ); - - const erc1155Balance = await receipt - .connect(alice) - ["balanceOf(address,uint256)"](alice.address, expected1155ID); - assert( - erc1155Balance.eq(expectedErc1155Balance), - `wrong erc1155 balance ${expectedErc20Balance} ${erc1155Balance}` - ); - - await receipt - .connect(alice) - .safeTransferFrom( - alice.address, - bob.address, - expected1155ID, - expectedErc1155BalanceAfter, - [] - ); - - const erc20BalanceAfter = await vault - .connect(alice) - ["balanceOf(address)"](alice.address); - assert( - erc20BalanceAfter.eq(expectedErc20BalanceAfter), - `wrong erc20 balance after ${expectedErc20BalanceAfter} ${erc20BalanceAfter}` - ); - - const erc20BalanceAfter2 = await vault - .connect(bob) - ["balanceOf(address)"](bob.address); - assert( - erc20BalanceAfter2.eq(0), - `wrong erc20 balance after 2 0 ${erc20BalanceAfter2}` - ); - - const erc1155BalanceAfter = await receipt - .connect(alice) - ["balanceOf(address,uint256)"](alice.address, expected1155ID); - assert( - erc1155BalanceAfter.eq(expectedErc1155BalanceAfter), - `wrong erc1155 balance after ${expectedErc1155BalanceAfter} ${erc1155BalanceAfter}` - ); - - const erc1155BalanceAfter2 = await receipt - .connect(bob) - ["balanceOf(address,uint256)"](bob.address, expected1155ID); - assert( - erc1155BalanceAfter2.eq(expectedErc1155BalanceAfter), - `wrong erc1155 balance 2 after ${expectedErc1155BalanceAfter} ${erc1155BalanceAfter2}` - ); - }); -}); diff --git a/test/priceOracle/Erc20.test.ts b/test/priceOracle/Erc20.test.ts deleted file mode 100644 index 917b5dd4..00000000 --- a/test/priceOracle/Erc20.test.ts +++ /dev/null @@ -1,110 +0,0 @@ -import { ethers } from "hardhat"; -import { - deployERC20PriceOracleVault, - expectedName, - expectedSymbol, - fixedPointMul, -} from "../util"; -const assert = require("assert"); - -describe("erc20 usage", async function () { - it("should construct well", async function () { - const signers = await ethers.getSigners(); - const alice = signers[0]; - - const [vault] = await deployERC20PriceOracleVault(); - - const erc20Name = await vault.connect(alice).name(); - const erc20Symbol = await vault.connect(alice).symbol(); - - assert( - erc20Name === expectedName, - "erc20 did not construct with correct name" - ); - assert( - erc20Symbol === expectedSymbol, - "erc20 did not construct with correct symbol" - ); - }); - - it("should only send itself", async function () { - const signers = await ethers.getSigners(); - - const [vault, asset, priceOracle, receipt] = - await deployERC20PriceOracleVault(); - - const alice = signers[0]; - const bob = signers[1]; - let shareRatio = await priceOracle.price(); - - const assetAmount = ethers.BigNumber.from(1000); - - await asset.connect(alice).increaseAllowance(vault.address, assetAmount); - await vault - .connect(alice) - ["deposit(uint256,address,uint256,bytes)"]( - assetAmount, - alice.address, - shareRatio, - [] - ); - - const expectedErc20Balance = fixedPointMul(assetAmount, shareRatio); - - const expectedErc20BalanceAfter = expectedErc20Balance.div(2); - const expectedErc1155Balance = expectedErc20Balance; - const expectedErc1155BalanceAfter = expectedErc1155Balance; - const expected1155ID = shareRatio; - - const erc20Balance = await vault - .connect(alice) - ["balanceOf(address)"](alice.address); - assert( - erc20Balance.eq(expectedErc20Balance), - `wrong erc20 balance ${expectedErc20Balance} ${erc20Balance}` - ); - - const erc1155Balance = await receipt - .connect(alice) - ["balanceOf(address,uint256)"](alice.address, expected1155ID); - assert( - erc1155Balance.eq(expectedErc1155Balance), - `wrong erc1155 balance ${expectedErc20Balance} ${erc1155Balance}` - ); - - await vault.connect(alice).transfer(bob.address, expectedErc20BalanceAfter); - - const erc20BalanceAfter = await vault - .connect(alice) - ["balanceOf(address)"](alice.address); - assert( - erc20BalanceAfter.eq(expectedErc20BalanceAfter), - `wrong erc20 balance after ${expectedErc20BalanceAfter} ${erc20BalanceAfter}` - ); - - const erc20BalanceAfter2 = await vault - .connect(bob) - ["balanceOf(address)"](bob.address); - assert( - erc20BalanceAfter2.eq(expectedErc20BalanceAfter), - `wrong erc20 balance after 2 ${expectedErc20BalanceAfter} ${erc20BalanceAfter2}` - ); - - const erc1155BalanceAfter = await receipt - .connect(alice) - ["balanceOf(address,uint256)"](alice.address, expected1155ID); - assert( - erc1155BalanceAfter.eq(expectedErc20Balance), - `wrong erc1155 balance after ${expectedErc1155BalanceAfter} ${erc1155BalanceAfter}` - ); - - assert( - ( - await receipt - .connect(bob) - ["balanceOf(address,uint256)"](bob.address, expected1155ID) - ).eq(0), - `wrong erc1155 balance 2 after ${expectedErc1155BalanceAfter} ${erc1155BalanceAfter}` - ); - }); -}); From 76839c0972730556f1fb8e2f1aae6a553ced3070 Mon Sep 17 00:00:00 2001 From: ninokeldishvili Date: Wed, 10 Jul 2024 13:27:03 +0400 Subject: [PATCH 5/8] Add depositor receipt balance check to deposit --- .../ERC20PriceOracleReceiptVault.deposit.t.sol | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/test/foundry/src/concrete/erc20PriceOracle/ERC20PriceOracleReceiptVault.deposit.t.sol b/test/foundry/src/concrete/erc20PriceOracle/ERC20PriceOracleReceiptVault.deposit.t.sol index 6f2dfcda..1a247145 100644 --- a/test/foundry/src/concrete/erc20PriceOracle/ERC20PriceOracleReceiptVault.deposit.t.sol +++ b/test/foundry/src/concrete/erc20PriceOracle/ERC20PriceOracleReceiptVault.deposit.t.sol @@ -10,6 +10,7 @@ import { Math } from "rain.math.fixedpoint/lib/LibFixedPointDecimalArithmeticOpenZeppelin.sol"; import {IERC20} from "forge-std/interfaces/IERC20.sol"; +import {Receipt as ReceiptContract} from "../../../../../contracts/concrete/receipt/Receipt.sol"; contract ERC20PriceOracleReceiptVaultDepositTest is ERC20PriceOracleReceiptVaultTest { using LibFixedPointDecimalArithmeticOpenZeppelin for uint256; @@ -35,7 +36,6 @@ contract ERC20PriceOracleReceiptVaultDepositTest is ERC20PriceOracleReceiptVault vm.warp(timestamp); TwoPriceOracle twoPriceOracle = createTwoPriceOracle(usdDecimals, usdDecimals, timestamp, answeredInRound); vm.startPrank(alice); - ERC20PriceOracleReceiptVault vault; { vault = createVault(address(twoPriceOracle), assetName, assetName); @@ -78,6 +78,7 @@ contract ERC20PriceOracleReceiptVaultDepositTest is ERC20PriceOracleReceiptVault // Ensure the fuzzed key is within the valid range for secp256 address alice = vm.addr((fuzzedKeyAlice % (SECP256K1_ORDER - 1)) + 1); address bob = vm.addr((fuzzedKeyBob % (SECP256K1_ORDER - 1)) + 1); + vm.assume(alice != bob); // Use common decimal bounds for price feeds // Use 0-20 so we at least have some coverage higher than 18 @@ -88,6 +89,8 @@ contract ERC20PriceOracleReceiptVaultDepositTest is ERC20PriceOracleReceiptVault vm.warp(timestamp); TwoPriceOracle twoPriceOracle = createTwoPriceOracle(usdDecimals, usdDecimals, timestamp, answeredInRound); vm.startPrank(alice); + + vm.recordLogs(); ERC20PriceOracleReceiptVault vault; { vault = createVault(address(twoPriceOracle), assetName, assetName); @@ -99,7 +102,7 @@ contract ERC20PriceOracleReceiptVaultDepositTest is ERC20PriceOracleReceiptVault uint256 totalSupply = iAsset.totalSupply(); // Getting ZeroSharesAmount if bounded from 1 - assets = bound(assets, 2, totalSupply); + assets = bound(assets, 20, totalSupply); vm.mockCall( address(iAsset), @@ -107,14 +110,24 @@ contract ERC20PriceOracleReceiptVaultDepositTest is ERC20PriceOracleReceiptVault abi.encode(true) ); } + ReceiptContract receipt = getReceipt(); + uint256 oraclePrice = twoPriceOracle.price(); uint256 expectedShares = assets.fixedPointMul(oraclePrice, Math.Rounding.Down); + uint256 aliceReceiptBalance = receipt.balanceOf(alice, oraclePrice); + vault.deposit(assets, bob, oraclePrice, bytes("")); // Assert that the total supply is equal to expectedShares assertEqUint(vault.totalSupply(), expectedShares); // Check balance assertEqUint(vault.balanceOf(bob), expectedShares); + + // Check bob's receipt balance + assertEqUint(receipt.balanceOf(bob, oraclePrice), expectedShares); + + // Check alice's receipt balance does not change + assertEqUint(receipt.balanceOf(alice, oraclePrice), aliceReceiptBalance); } /// Test deposit function with zero assets From caa9d12fcec272ec07dea593ec38b4bf059d4970 Mon Sep 17 00:00:00 2001 From: ninokeldishvili Date: Wed, 10 Jul 2024 13:27:41 +0400 Subject: [PATCH 6/8] Test vault receiptVaultInformation --- ...PriceOracleReceiptVault.receiptVault.t.sol | 33 ++++ test/receipt/ReceiptVault.test.ts | 141 +----------------- 2 files changed, 34 insertions(+), 140 deletions(-) diff --git a/test/foundry/src/concrete/erc20PriceOracle/ERC20PriceOracleReceiptVault.receiptVault.t.sol b/test/foundry/src/concrete/erc20PriceOracle/ERC20PriceOracleReceiptVault.receiptVault.t.sol index 7cc25885..ddf8fa2c 100644 --- a/test/foundry/src/concrete/erc20PriceOracle/ERC20PriceOracleReceiptVault.receiptVault.t.sol +++ b/test/foundry/src/concrete/erc20PriceOracle/ERC20PriceOracleReceiptVault.receiptVault.t.sol @@ -14,6 +14,8 @@ import {IERC20} from "forge-std/interfaces/IERC20.sol"; contract ERC20PriceOracleReceiptVaultreceiptVaultTest is ERC20PriceOracleReceiptVaultTest { using LibFixedPointDecimalArithmeticOpenZeppelin for uint256; + event ReceiptVaultInformation(address sender, bytes vaultInformation); + /// Test vault asset function testVaultAsset( uint256 fuzzedKeyAlice, @@ -336,4 +338,35 @@ contract ERC20PriceOracleReceiptVaultreceiptVaultTest is ERC20PriceOracleReceipt assertEqUint(maxMint, type(uint256).max); } + + /// Test vault receiptVaultInformation + function testReceiptVaultInformation( + uint256 fuzzedKeyAlice, + string memory assetName, + uint256 timestamp, + uint8 xauDecimals, + uint8 usdDecimals, + uint80 answeredInRound, + bytes memory information + ) external { + // Ensure the fuzzed key is within the valid range for secp256 + address alice = vm.addr((fuzzedKeyAlice % (SECP256K1_ORDER - 1)) + 1); + // Use common decimal bounds for price feeds + // Use 0-20 so we at least have some coverage higher than 18 + usdDecimals = uint8(bound(usdDecimals, 0, 20)); + xauDecimals = uint8(bound(xauDecimals, 0, 20)); + + timestamp = bound(timestamp, 0, type(uint32).max); + + vm.warp(timestamp); + TwoPriceOracle twoPriceOracle = createTwoPriceOracle(usdDecimals, usdDecimals, timestamp, answeredInRound); + vm.startPrank(alice); + + ERC20PriceOracleReceiptVault vault = createVault(address(twoPriceOracle), assetName, assetName); + + vm.expectEmit(false, false, false, true); + emit ReceiptVaultInformation(alice, information); + + vault.receiptVaultInformation(information); + } } diff --git a/test/receipt/ReceiptVault.test.ts b/test/receipt/ReceiptVault.test.ts index e2c101a7..444719e1 100644 --- a/test/receipt/ReceiptVault.test.ts +++ b/test/receipt/ReceiptVault.test.ts @@ -14,99 +14,7 @@ import { ReceiptInformationEvent } from "../../typechain-types/contracts/vault/r const assert = require("assert"); describe("Receipt vault", async function () { - it("Checks ReceiptVaultInformation event is emitted", async function () { - const [vault] = await deployERC20PriceOracleVault(); - const signers = await ethers.getSigners(); - const alice = signers[0]; - const { sender, vaultInformation } = (await getEventArgs( - await vault.connect(alice).receiptVaultInformation([1]), - "ReceiptVaultInformation", - vault - )) as ReceiptVaultInformationEvent["args"]; - - assert( - sender === alice.address, - `Incorrect sender. Expected ${alice.address} got ${sender}` - ); - - assert( - vaultInformation === "0x01", - `Incorrect sender. Expected 0x01 got ${vaultInformation}` - ); - }); -}); describe("Deposit", async () => { - it("Receiver receives BOTH erc20 and erc1155, depositor gets nothing but MUST transfer assets", async function () { - const signers = await ethers.getSigners(); - const alice = signers[0]; - const bob = signers[1]; - - const [vault, asset, priceOracle, receipt] = - await deployERC20PriceOracleVault(); - const shareRatio = await priceOracle.price(); - - const totalTokenSupply = await asset.totalSupply(); - const assets = totalTokenSupply.div(2); - // give alice reserve to cover cost - await asset.transfer(alice.address, assets); - - await asset.connect(alice).increaseAllowance(vault.address, assets); - - //Alice assets before deposit - const aliceAssetBefore = await asset - .connect(alice) - .balanceOf(alice.address); - - await vault.connect(alice)["deposit(uint256,address)"](assets, bob.address); - - const expectedBobBalance = fixedPointMul(assets, shareRatio); - - //Receiver gets both Erc20 and Erc1155 - const erc1155Balance = await receipt - .connect(alice) - ["balanceOf(address,uint256)"](bob.address, shareRatio); - - const bobErc20Balance = await vault - .connect(alice) - ["balanceOf(address)"](bob.address); - - assert( - erc1155Balance.eq(expectedBobBalance), - `wrong bob erc1155 balance ${expectedBobBalance} ${erc1155Balance}` - ); - - assert( - bobErc20Balance.eq(expectedBobBalance), - `wrong bob erc1155 balance ${expectedBobBalance} ${bobErc20Balance}` - ); - - //Depositor Gets nothing - const aliceErc20Balance = await vault - .connect(alice) - ["balanceOf(address)"](alice.address); - - const aliceErc1155Balance = await receipt - .connect(alice) - ["balanceOf(address,uint256)"](alice.address, shareRatio); - - assert( - aliceErc1155Balance.eq(0), - `wrong alice erc20 balance ${aliceErc1155Balance} 0` - ); - - assert( - aliceErc20Balance.eq(0), - `wrong alice erc20 balance ${aliceErc20Balance} 0` - ); - - //Depositor MUST transfer assets - const aliceAssetAft = await asset.connect(alice).balanceOf(alice.address); - - assert( - aliceAssetAft.eq(aliceAssetBefore.sub(assets)), - `wrong alice assets ${aliceAssetAft} ${aliceAssetBefore.sub(assets)}` - ); - }); it("Check deposit event is emitted", async function () { const signers = await ethers.getSigners(); const alice = signers[0]; @@ -148,51 +56,4 @@ describe("Deposit", async () => { `wrong owner expected ${alice.address} got ${depositEvent.args.owner}` ); }); -}); -describe("Overloaded `deposit`", async () => { - it("Check ReceiptInformation event is emitted", async function () { - const signers = await ethers.getSigners(); - const alice = signers[0]; - - const [vault, asset, priceOracle, receipt] = - await deployERC20PriceOracleVault(); - - const shareRatio = await priceOracle.price(); - - const aliceAmount = ethers.BigNumber.from(5000); - await asset.transfer(alice.address, aliceAmount); - - await asset.connect(alice).increaseAllowance(vault.address, aliceAmount); - - const expectedId = shareRatio; - - const informationBytes = [125, 126]; - //generate hex string - const expectedInformation = - "0x" + informationBytes.map((num) => num.toString(16)).join(""); - - const { sender, id, information } = (await getEventArgs( - await vault - .connect(alice) - ["deposit(uint256,address,uint256,bytes)"]( - aliceAmount, - alice.address, - shareRatio, - informationBytes - ), - "ReceiptInformation", - receipt - )) as ReceiptInformationEvent["args"]; - - assert( - sender === alice.address, - `wrong assets expected ${alice.address} got ${sender}` - ); - assert(id.eq(expectedId), `wrong shares expected ${id} got ${expectedId}`); - - assert( - information === expectedInformation, - `wrong information expected ${information} got ${expectedInformation}` - ); - }) -}) \ No newline at end of file +}); \ No newline at end of file From 28d83521bc1655a9f92a85a6279cbd0f23752219 Mon Sep 17 00:00:00 2001 From: ninokeldishvili Date: Wed, 10 Jul 2024 13:37:25 +0400 Subject: [PATCH 7/8] Add DepositWithReceipt event check on deposits --- ...ERC20PriceOracleReceiptVault.deposit.t.sol | 9 +++ test/receipt/ReceiptVault.test.ts | 59 ------------------- 2 files changed, 9 insertions(+), 59 deletions(-) delete mode 100644 test/receipt/ReceiptVault.test.ts diff --git a/test/foundry/src/concrete/erc20PriceOracle/ERC20PriceOracleReceiptVault.deposit.t.sol b/test/foundry/src/concrete/erc20PriceOracle/ERC20PriceOracleReceiptVault.deposit.t.sol index 1a247145..d04c229c 100644 --- a/test/foundry/src/concrete/erc20PriceOracle/ERC20PriceOracleReceiptVault.deposit.t.sol +++ b/test/foundry/src/concrete/erc20PriceOracle/ERC20PriceOracleReceiptVault.deposit.t.sol @@ -15,6 +15,10 @@ import {Receipt as ReceiptContract} from "../../../../../contracts/concrete/rece contract ERC20PriceOracleReceiptVaultDepositTest is ERC20PriceOracleReceiptVaultTest { using LibFixedPointDecimalArithmeticOpenZeppelin for uint256; + event DepositWithReceipt( + address sender, address owner, uint256 assets, uint256 shares, uint256 id, bytes receiptInformation + ); + /// Test deposit function function testDeposit( uint256 fuzzedKeyAlice, @@ -57,6 +61,9 @@ contract ERC20PriceOracleReceiptVaultDepositTest is ERC20PriceOracleReceiptVault } uint256 oraclePrice = twoPriceOracle.price(); uint256 expectedShares = assets.fixedPointMul(oraclePrice, Math.Rounding.Down); + vm.expectEmit(false, false, false, true); + emit DepositWithReceipt(alice, alice, assets, expectedShares, oraclePrice, bytes("")); + vault.deposit(assets, alice, oraclePrice, bytes("")); // Assert that the total supply is equal to expectedShares @@ -116,6 +123,8 @@ contract ERC20PriceOracleReceiptVaultDepositTest is ERC20PriceOracleReceiptVault uint256 expectedShares = assets.fixedPointMul(oraclePrice, Math.Rounding.Down); uint256 aliceReceiptBalance = receipt.balanceOf(alice, oraclePrice); + vm.expectEmit(false, false, false, true); + emit DepositWithReceipt(alice, bob, assets, expectedShares, oraclePrice, bytes("")); vault.deposit(assets, bob, oraclePrice, bytes("")); // Assert that the total supply is equal to expectedShares diff --git a/test/receipt/ReceiptVault.test.ts b/test/receipt/ReceiptVault.test.ts deleted file mode 100644 index 444719e1..00000000 --- a/test/receipt/ReceiptVault.test.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { ethers } from "hardhat"; -import { - deployERC20PriceOracleVault, - fixedPointMul, - getEvent, -} from "../util"; -import { DepositEvent } from "../../typechain-types/@openzeppelin/contracts-upgradeable/interfaces/IERC4626Upgradeable"; -import { - ReceiptVaultInformationEvent, -} from "../../typechain-types/contracts/vault/receipt/ReceiptVault"; - -import { getEventArgs } from "../util"; -import { ReceiptInformationEvent } from "../../typechain-types/contracts/vault/receipt/Receipt"; -const assert = require("assert"); - -describe("Receipt vault", async function () { -describe("Deposit", async () => { - it("Check deposit event is emitted", async function () { - const signers = await ethers.getSigners(); - const alice = signers[0]; - - const [vault, asset, priceOracle] = await deployERC20PriceOracleVault(); - - const shareRatio = await priceOracle.price(); - - const aliceAmount = ethers.BigNumber.from(5000); - await asset.transfer(alice.address, aliceAmount); - - await asset.connect(alice).increaseAllowance(vault.address, aliceAmount); - - const expectedShares = fixedPointMul(aliceAmount, shareRatio); - - const depositTX = await vault - .connect(alice) - ["deposit(uint256,address)"](aliceAmount, alice.address); - const depositEvent = (await getEvent( - depositTX, - "Deposit", - vault - )) as DepositEvent; - - assert( - depositEvent.args.assets.eq(aliceAmount), - `wrong assets expected ${aliceAmount} got ${depositEvent.args.assets}` - ); - assert( - depositEvent.args.shares.eq(expectedShares), - `wrong shares expected ${depositEvent.args.shares} got ${expectedShares}` - ); - assert( - depositEvent.args.sender === alice.address, - `wrong sender expected ${alice.address} got ${depositEvent.args.sender}` - ); - assert( - depositEvent.args.owner === alice.address, - `wrong owner expected ${alice.address} got ${depositEvent.args.owner}` - ); - }); -}); \ No newline at end of file From 5bc75bc03b92e50f25e0f3c5c380112b9ff9ad1f Mon Sep 17 00:00:00 2001 From: ninokeldishvili Date: Wed, 10 Jul 2024 13:38:38 +0400 Subject: [PATCH 8/8] Remove events hardhat test. --- test/priceOracle/Event.test.ts | 148 --------------------------------- 1 file changed, 148 deletions(-) delete mode 100644 test/priceOracle/Event.test.ts diff --git a/test/priceOracle/Event.test.ts b/test/priceOracle/Event.test.ts deleted file mode 100644 index 47b83656..00000000 --- a/test/priceOracle/Event.test.ts +++ /dev/null @@ -1,148 +0,0 @@ -import { ethers } from "hardhat"; -import { - deployERC20PriceOracleVault, - fixedPointDiv, - fixedPointMul, - getEventArgs, -} from "../util"; - -const assert = require("assert"); - -describe("events", async function () { - it("should emit events on deposit and withdraw", async function () { - const signers = await ethers.getSigners(); - const [vault, asset, priceOracle, receipt] = - await deployERC20PriceOracleVault(); - - const alice = signers[0]; - - const shareRatio = await priceOracle.price(); - - const ethAmount = 5000; - - const id1155 = shareRatio; - await asset.connect(alice).increaseAllowance(vault.address, ethAmount); - - const depositTx = await vault - .connect(alice) - ["deposit(uint256,address)"](ethAmount, alice.address); - - const depositEventArgs = await getEventArgs(depositTx, "Deposit", vault); - - assert( - depositEventArgs.assets.eq(ethAmount), - `incorrect assets. expected ${ethAmount} got ${depositEventArgs.assets}` - ); - - const aliceBalance = await vault - .connect(alice) - ["balanceOf(address)"](alice.address); - - const alice1155BalanceBefore = await receipt - .connect(alice) - ["balanceOf(address,uint256)"](alice.address, id1155); - assert( - aliceBalance.eq(alice1155BalanceBefore), - `incorrect balance before. expected ${aliceBalance} got ${alice1155BalanceBefore}` - ); - - const { sender, owner, assets, shares, id } = await getEventArgs( - depositTx, - "DepositWithReceipt", - vault - ); - - let expectedShares = fixedPointMul( - ethers.BigNumber.from(ethAmount), - shareRatio - ); - assert( - sender === alice.address, - `incorrect sender expected ${alice.address} got ${sender}` - ); - assert( - owner === alice.address, - `incorrect owner expected ${alice.address} got ${owner}` - ); - assert( - assets.eq(ethAmount), - `incorrect assets expected ${ethAmount} got ${assets}` - ); - assert( - shares.eq(expectedShares), - `incorrect shares expected ${expectedShares} got ${shares}` - ); - assert(id.eq(id1155), `incorrect id expected ${id1155} got ${id}`); - - const transferEventArgs = await getEventArgs(depositTx, "Transfer", vault); - assert( - transferEventArgs.value.eq(aliceBalance), - `incorrect Transfer value. expected ${aliceBalance} got ${transferEventArgs.value}` - ); - - const ERC1155Amount = aliceBalance; - const redeemTx = await vault - .connect(alice) - ["redeem(uint256,address,address,uint256,bytes)"]( - ERC1155Amount, - alice.address, - alice.address, - shareRatio, - [] - ); - - const withdrawEventArgs = await getEventArgs(redeemTx, "Withdraw", vault); - // withdrawAmount is always rounded down. - const withdrawAmount = fixedPointDiv(ERC1155Amount, shareRatio); - assert( - withdrawEventArgs.assets.eq(withdrawAmount), - `wrong assets amount. expected ${withdrawAmount} actual ${withdrawEventArgs.assets}` - ); - - const withdrawEvent = await getEventArgs( - redeemTx, - "WithdrawWithReceipt", - vault - ); - - let expectedAssets = fixedPointDiv( - ethers.BigNumber.from(ERC1155Amount), - shareRatio - ); - - assert( - withdrawEvent.sender === alice.address, - `incorrect sender expected ${alice.address} got ${withdrawEvent.sender}` - ); - assert( - withdrawEvent.receiver === alice.address, - `incorrect receiver expected ${alice.address} got ${withdrawEvent.receiver}` - ); - assert( - withdrawEvent.assets.eq(expectedAssets), - `incorrect assets expected ${expectedAssets} got ${withdrawEvent.assets}` - ); - assert( - withdrawEvent.shares.eq(ERC1155Amount), - `incorrect shares expected ${ERC1155Amount} got ${withdrawEvent.shares}` - ); - assert( - withdrawEvent.id.eq(id1155), - `incorrect id expected ${id1155} got ${withdrawEvent.id}` - ); - assert( - withdrawEvent.owner === alice.address, - `incorrect id expected ${alice.address} got ${withdrawEvent.owner}` - ); - - const alice1155BalanceAfter = await receipt - .connect(alice) - ["balanceOf(address,uint256)"](alice.address, id1155); - - const expected1155BalanceAfter = alice1155BalanceBefore.sub(ERC1155Amount); - assert( - alice1155BalanceAfter.eq(expected1155BalanceAfter), - `incorrect 1155 balance after. expected ${expected1155BalanceAfter} got ${alice1155BalanceAfter}` - ); - }); -});