From 178988aaa6cae36fe76ef30fa2ffba34984d41fb Mon Sep 17 00:00:00 2001 From: Domen Grabec Date: Wed, 13 Nov 2024 23:13:49 +0100 Subject: [PATCH 1/6] remove nonreentrant modifiers from the toke code as they are not needed --- contracts/contracts/token/OUSD.sol | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/contracts/contracts/token/OUSD.sol b/contracts/contracts/token/OUSD.sol index 415601dd77..6088eefef9 100644 --- a/contracts/contracts/token/OUSD.sol +++ b/contracts/contracts/token/OUSD.sol @@ -385,7 +385,7 @@ contract OUSD is Governable { * * - `to` cannot be the zero address. */ - function _mint(address _account, uint256 _amount) internal nonReentrant { + function _mint(address _account, uint256 _amount) internal { require(_account != address(0), "Mint to the zero address"); // Account @@ -419,7 +419,7 @@ contract OUSD is Governable { * - `_account` cannot be the zero address. * - `_account` must have at least `_amount` tokens. */ - function _burn(address _account, uint256 _amount) internal nonReentrant { + function _burn(address _account, uint256 _amount) internal { require(_account != address(0), "Burn from the zero address"); if (_amount == 0) { return; @@ -513,7 +513,6 @@ contract OUSD is Governable { */ function governanceRebaseOptIn(address _account) external - nonReentrant onlyGovernor { _rebaseOptIn(_account); @@ -524,7 +523,7 @@ contract OUSD is Governable { * address's balance will be part of rebases and the account will be exposed * to upside and downside. */ - function rebaseOptIn() external nonReentrant { + function rebaseOptIn() external { _rebaseOptIn(msg.sender); } @@ -556,7 +555,7 @@ contract OUSD is Governable { emit AccountRebasingEnabled(_account); } - function rebaseOptOut() external nonReentrant { + function rebaseOptOut() external { _rebaseOptOut(msg.sender); } @@ -594,7 +593,6 @@ contract OUSD is Governable { function changeSupply(uint256 _newTotalSupply) external onlyVault - nonReentrant { require(_totalSupply > 0, "Cannot increase 0 supply"); @@ -631,7 +629,6 @@ contract OUSD is Governable { function delegateYield(address from, address to) external onlyGovernor - nonReentrant { require(from != to, "Cannot delegate to self"); require( @@ -693,7 +690,7 @@ contract OUSD is Governable { _rebasingCredits += credits; } - function undelegateYield(address from) external onlyGovernor nonReentrant { + function undelegateYield(address from) external onlyGovernor { // Require a delegation, which will also ensure a valid delegation require(yieldTo[from] != address(0), ""); From 4fde658a23a49d4d4a493eaf66981c81beebf5ac Mon Sep 17 00:00:00 2001 From: Domen Grabec Date: Thu, 14 Nov 2024 15:38:29 +0100 Subject: [PATCH 2/6] add setup for all account types --- .../contracts/mocks/TestUpgradedOUSD.sol | 27 ++ contracts/contracts/token/OUSD.sol | 2 +- contracts/deploy/deployActions.js | 8 +- contracts/test/_fixture.js | 300 +++++++++++++++--- contracts/test/token/token-transfers.js | 83 +++++ 5 files changed, 377 insertions(+), 43 deletions(-) create mode 100644 contracts/contracts/mocks/TestUpgradedOUSD.sol create mode 100644 contracts/test/token/token-transfers.js diff --git a/contracts/contracts/mocks/TestUpgradedOUSD.sol b/contracts/contracts/mocks/TestUpgradedOUSD.sol new file mode 100644 index 0000000000..6d3b62748c --- /dev/null +++ b/contracts/contracts/mocks/TestUpgradedOUSD.sol @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "../token/OUSD.sol"; + +// used to alter internal state of OUSD contract +contract TestUpgradedOUSD is OUSD { + constructor() OUSD() {} + + function overwriteCreditBalances(address _account, uint256 _creditBalance) + public + { + _creditBalances[_account] = _creditBalance; + } + + function overwriteAlternativeCPT(address _account, uint256 _acpt) + public + { + alternativeCreditsPerToken[_account] = _acpt; + } + + function overwriteRebaseState(address _account, RebaseOptions _rebaseOption) + public + { + rebaseState[_account] = _rebaseOption; + } +} diff --git a/contracts/contracts/token/OUSD.sol b/contracts/contracts/token/OUSD.sol index 6088eefef9..5bc29f7695 100644 --- a/contracts/contracts/token/OUSD.sol +++ b/contracts/contracts/token/OUSD.sol @@ -48,7 +48,7 @@ contract OUSD is Governable { uint256 public _totalSupply; mapping(address => mapping(address => uint256)) private _allowances; address public vaultAddress = address(0); - mapping(address => uint256) private _creditBalances; + mapping(address => uint256) internal _creditBalances; uint256 private _rebasingCredits; // Sum of all rebasing credits (_creditBalances for rebasing accounts) uint256 private _rebasingCreditsPerToken; uint256 public nonRebasingSupply; // All nonrebasing balances diff --git a/contracts/deploy/deployActions.js b/contracts/deploy/deployActions.js index 2c6d7d5a1e..29a136b8a6 100644 --- a/contracts/deploy/deployActions.js +++ b/contracts/deploy/deployActions.js @@ -6,6 +6,7 @@ const { getOracleAddresses, isMainnet, isHolesky, + isTest, } = require("../test/helpers.js"); const { deployWithConfirmation, withConfirmation } = require("../utils/deploy"); const { @@ -1190,7 +1191,12 @@ const deployOUSDCore = async () => { await deployWithConfirmation("VaultProxy"); // Main contracts - const dOUSD = await deployWithConfirmation("OUSD"); + let dOUSD; + if (isTest) { + dOUSD = await deployWithConfirmation("TestUpgradedOUSD"); + } else { + dOUSD = await deployWithConfirmation("OUSD"); + } const dVault = await deployWithConfirmation("Vault"); const dVaultCore = await deployWithConfirmation("VaultCore"); const dVaultAdmin = await deployWithConfirmation("VaultAdmin"); diff --git a/contracts/test/_fixture.js b/contracts/test/_fixture.js index 908eea9f95..3f4a157d35 100644 --- a/contracts/test/_fixture.js +++ b/contracts/test/_fixture.js @@ -20,6 +20,7 @@ const { fundAccounts, fundAccountsForOETHUnitTests, } = require("../utils/funding"); + const { replaceContractAt } = require("../utils/hardhat"); const { getAssetAddresses, @@ -189,11 +190,220 @@ const simpleOETHFixture = deployments.createFixture(async () => { }; }); -const defaultFixture = deployments.createFixture(async () => { - if (!snapshotId && !isFork) { - snapshotId = await nodeSnapshot(); +const getVaultAndTokenConracts = async () => { + const ousdProxy = await ethers.getContract("OUSDProxy"); + const vaultProxy = await ethers.getContract("VaultProxy"); + + const ousd = await ethers.getContractAt("OUSD", ousdProxy.address); + // the same contract as the "ousd" one just with some unlocked features + const ousdUnlocked = await ethers.getContractAt("TestUpgradedOUSD", ousdProxy.address); + + const vault = await ethers.getContractAt("IVault", vaultProxy.address); + + const oethProxy = await ethers.getContract("OETHProxy"); + const OETHVaultProxy = await ethers.getContract("OETHVaultProxy"); + const oethVault = await ethers.getContractAt( + "IVault", + OETHVaultProxy.address + ); + const oeth = await ethers.getContractAt("OETH", oethProxy.address); + + let woeth, woethProxy, mockNonRebasing, mockNonRebasingTwo; + + if (isFork) { + woethProxy = await ethers.getContract("WOETHProxy"); + woeth = await ethers.getContractAt("WOETH", woethProxy.address); + } else { + // Mock contracts for testing rebase opt out + mockNonRebasing = await ethers.getContract("MockNonRebasing"); + await mockNonRebasing.setOUSD(ousd.address); + mockNonRebasingTwo = await ethers.getContract("MockNonRebasingTwo"); + await mockNonRebasingTwo.setOUSD(ousd.address); + } + + return { + ousd, + ousdUnlocked, + vault, + oethVault, + oeth, + woeth, + mockNonRebasing, + mockNonRebasingTwo + }; +}; + +const createAccountTypes = async ({ vault, ousd, ousdUnlocked, deploy }) => { + const signers = await hre.ethers.getSigners(); + const matt = signers[4]; + const governor = signers[1]; + + if (!isFork) { + + await fundAccounts(); + const dai = await ethers.getContract("MockDAI"); + await dai.connect(matt).approve(vault.address, daiUnits("1000")); + await vault.connect(matt).mint(dai.address, daiUnits("1000"), 0); + } + + // yiedlsource + // yieldtarget + + const createAccount = async () => { + let account = ethers.Wallet.createRandom(); + // Give ETH to user + await hardhatSetBalance(account.address, "1000000"); + account = account.connect(ethers.provider); + return account; + }; + + const createContract = async (name) => { + const fullName = `MockNonRebasing_${name}` + await deploy(fullName, { + from: matt.address, + contract: "MockNonRebasing", + }); + + const contract = await ethers.getContract(fullName); + await contract.setOUSD(ousd.address); + + return contract; + }; + + // generate alternativeCreditsPerToken BigNumber and creditBalance BigNumber + // for a given credits per token + const generateCreditsBalancePair = ({ creditsPerToken, tokenBalance }) => { + const creditsPerTokenBN = parseUnits(`${creditsPerToken}`, 27); + // 1e18 * 1e27 / 1e18 + const creditsBalanceBN = tokenBalance.mul(creditsPerTokenBN).div(parseUnits('1', 18)); + + return { + creditsPerTokenBN, + creditsBalanceBN + }; + } + + const createNonRebasingNotSetAlternativeCptContract = async ({ name, creditsPerToken, balance }) => { + const nonrebase_cotract_notSet_altcpt_gt = await createContract(name); + await ousd.connect(matt).transfer(nonrebase_cotract_notSet_altcpt_gt.address, balance); + const { creditsPerTokenBN, creditsBalanceBN } = generateCreditsBalancePair({ + creditsPerToken, + tokenBalance: balance + }); + await ousdUnlocked.connect(matt).overwriteCreditBalances( + nonrebase_cotract_notSet_altcpt_gt.address, + creditsBalanceBN + ); + await ousdUnlocked.connect(matt).overwriteAlternativeCPT( + nonrebase_cotract_notSet_altcpt_gt.address, + creditsPerTokenBN + ); + await ousdUnlocked.connect(matt).overwriteRebaseState( + nonrebase_cotract_notSet_altcpt_gt.address, + 0 // NotSet + ); + + return nonrebase_cotract_notSet_altcpt_gt; + } + + const rebase_eoa_notset_0 = await createAccount(); + await ousd.connect(matt).transfer(rebase_eoa_notset_0.address, ousdUnits("11")); + const rebase_eoa_notset_1 = await createAccount(); + await ousd.connect(matt).transfer(rebase_eoa_notset_1.address, ousdUnits("12")); + + const rebase_eoa_stdRebasing_0 = await createAccount(); + await ousd.connect(matt).transfer(rebase_eoa_stdRebasing_0.address, ousdUnits("21")); + await ousd.connect(rebase_eoa_stdRebasing_0).rebaseOptOut(); + await ousd.connect(rebase_eoa_stdRebasing_0).rebaseOptIn(); + const rebase_eoa_stdRebasing_1 = await createAccount(); + await ousd.connect(matt).transfer(rebase_eoa_stdRebasing_1.address, ousdUnits("22")); + await ousd.connect(rebase_eoa_stdRebasing_1).rebaseOptOut(); + await ousd.connect(rebase_eoa_stdRebasing_1).rebaseOptIn(); + + const rebase_contract_0 = await createContract("rebase_contract_0"); + await ousd.connect(matt).transfer(rebase_contract_0.address, ousdUnits("33")); + await rebase_contract_0.connect(matt).rebaseOptIn(); + const rebase_contract_1 = await createContract("rebase_contract_1"); + await ousd.connect(matt).transfer(rebase_contract_1.address, ousdUnits("34")); + await rebase_contract_1.connect(matt).rebaseOptIn(); + + const nonrebase_eoa_0 = await createAccount(); + await ousd.connect(matt).transfer(nonrebase_eoa_0.address, ousdUnits("44")); + await ousd.connect(nonrebase_eoa_0).rebaseOptOut(); + const nonrebase_eoa_1 = await createAccount(); + await ousd.connect(matt).transfer(nonrebase_eoa_1.address, ousdUnits("45")); + await ousd.connect(nonrebase_eoa_1).rebaseOptOut(); + + const nonrebase_cotract_0 = await createContract("nonrebase_cotract_0"); + await ousd.connect(matt).transfer(nonrebase_cotract_0.address, ousdUnits("55")); + await nonrebase_cotract_0.connect(matt).rebaseOptIn(); + await nonrebase_cotract_0.connect(matt).rebaseOptOut(); + const nonrebase_cotract_1 = await createContract("nonrebase_cotract_1"); + await ousd.connect(matt).transfer(nonrebase_cotract_1.address, ousdUnits("56")); + await nonrebase_cotract_1.connect(matt).rebaseOptIn(); + await nonrebase_cotract_1.connect(matt).rebaseOptOut(); + + const nonrebase_cotract_notSet_0 = await createContract("nonrebase_cotract_notSet_0"); + const nonrebase_cotract_notSet_1 = await createContract("nonrebase_cotract_notSet_1"); + + const nonrebase_cotract_notSet_altcpt_gt_0 = await createNonRebasingNotSetAlternativeCptContract({ + name: "nonrebase_cotract_notSet_altcpt_gt_0", + creditsPerToken: 0.934232, + balance: ousdUnits("65") + }); + + const nonrebase_cotract_notSet_altcpt_gt_1 = await createNonRebasingNotSetAlternativeCptContract({ + name: "nonrebase_cotract_notSet_altcpt_gt_1", + creditsPerToken: 0.890232, + balance: ousdUnits("66") + }); + + const rebase_source_0 = await createAccount(); + await ousd.connect(matt).transfer(rebase_source_0.address, ousdUnits("76")); + const rebase_target_0 = await createAccount(); + await ousd.connect(matt).transfer(rebase_target_0.address, ousdUnits("77")); + + await ousd + .connect(governor) + .delegateYield(rebase_source_0.address, rebase_target_0.address); + + const rebase_source_1 = await createAccount(); + await ousd.connect(matt).transfer(rebase_source_1.address, ousdUnits("87")); + const rebase_target_1 = await createAccount(); + await ousd.connect(matt).transfer(rebase_target_1.address, ousdUnits("88")); + + await ousd + .connect(governor) + .delegateYield(rebase_source_1.address, rebase_target_1.address); + + return { + rebase_eoa_notset_0, + rebase_eoa_notset_1, + rebase_eoa_stdRebasing_0, + rebase_eoa_stdRebasing_1, + rebase_contract_0, + rebase_contract_1, + nonrebase_eoa_0, + nonrebase_eoa_1, + nonrebase_cotract_0, + nonrebase_cotract_1, + nonrebase_cotract_notSet_0, + nonrebase_cotract_notSet_1, + nonrebase_cotract_notSet_altcpt_gt_0, + nonrebase_cotract_notSet_altcpt_gt_1, + rebase_source_0, + rebase_source_1, + rebase_target_0, + rebase_target_1, } +}; + +/** + * Vault and token fixture with extra functionality regarding different types of accounts + * (rebaseStates and alternativeCreditsPerToken ) when testing token contract behaviour + */ +const loadTokenTransferFixture = deployments.createFixture(async () => { log(`Forked from block: ${await hre.ethers.provider.getBlockNumber()}`); log(`Before deployments with param "${isFork ? undefined : ["unit_tests"]}"`); @@ -209,30 +419,46 @@ const defaultFixture = deployments.createFixture(async () => { const { governorAddr, strategistAddr, timelockAddr } = await getNamedAccounts(); - const ousdProxy = await ethers.getContract("OUSDProxy"); - const vaultProxy = await ethers.getContract("VaultProxy"); + const vaultAndTokenConracts = await getVaultAndTokenConracts(); - const compoundStrategyProxy = await ethers.getContract( - "CompoundStrategyProxy" - ); + const accountTypes = await createAccountTypes({ + ousd: vaultAndTokenConracts.ousd, + ousdUnlocked: vaultAndTokenConracts.ousdUnlocked, + vault: vaultAndTokenConracts.vault, + deploy: deployments.deploy, + governor: deployments.governor, + }); - const ousd = await ethers.getContractAt("OUSD", ousdProxy.address); - const vault = await ethers.getContractAt("IVault", vaultProxy.address); + return { + ...vaultAndTokenConracts, + ...accountTypes, + governorAddr, + strategistAddr, + timelockAddr + } +}); - const oethProxy = await ethers.getContract("OETHProxy"); - const OETHVaultProxy = await ethers.getContract("OETHVaultProxy"); - const oethVault = await ethers.getContractAt( - "IVault", - OETHVaultProxy.address - ); - const oeth = await ethers.getContractAt("OETH", oethProxy.address); +const defaultFixture = deployments.createFixture(async () => { + if (!snapshotId && !isFork) { + snapshotId = await nodeSnapshot(); + } - let woeth, woethProxy; + log(`Forked from block: ${await hre.ethers.provider.getBlockNumber()}`); - if (isFork) { - woethProxy = await ethers.getContract("WOETHProxy"); - woeth = await ethers.getContractAt("WOETH", woethProxy.address); - } + log(`Before deployments with param "${isFork ? undefined : ["unit_tests"]}"`); + + // Run the contract deployments + await deployments.fixture(isFork ? undefined : ["unit_tests"], { + keepExistingDeployments: true, + fallbackToGlobal: true, + }); + + log(`Block after deployments: ${await hre.ethers.provider.getBlockNumber()}`); + + const { governorAddr, strategistAddr, timelockAddr } = + await getNamedAccounts(); + + const vaultAndTokenConracts = await getVaultAndTokenConracts(); const harvesterProxy = await ethers.getContract("HarvesterProxy"); const harvester = await ethers.getContractAt( @@ -253,6 +479,11 @@ const defaultFixture = deployments.createFixture(async () => { const CompoundStrategyFactory = await ethers.getContractFactory( "CompoundStrategy" ); + + const compoundStrategyProxy = await ethers.getContract( + "CompoundStrategyProxy" + ); + const compoundStrategy = await ethers.getContractAt( "CompoundStrategy", compoundStrategyProxy.address @@ -341,8 +572,6 @@ const defaultFixture = deployments.createFixture(async () => { sfrxETH, sDAI, usdcMetaMorphoSteakHouseVault, - mockNonRebasing, - mockNonRebasingTwo, LUSD, fdai, fusdt, @@ -613,12 +842,6 @@ const defaultFixture = deployments.createFixture(async () => { "MockChainlinkOracleFeedETH" ); - // Mock contracts for testing rebase opt out - mockNonRebasing = await ethers.getContract("MockNonRebasing"); - await mockNonRebasing.setOUSD(ousd.address); - mockNonRebasingTwo = await ethers.getContract("MockNonRebasingTwo"); - await mockNonRebasingTwo.setOUSD(ousd.address); - flipper = await ethers.getContract("Flipper"); const LUSDMetaStrategyProxy = await ethers.getContract( @@ -649,10 +872,10 @@ const defaultFixture = deployments.createFixture(async () => { const sGovernor = await ethers.provider.getSigner(governorAddr); // Add TUSD in fixture, it is disabled by default in deployment - await vault.connect(sGovernor).supportAsset(assetAddresses.TUSD, 0); + await vaultAndTokenConracts.vault.connect(sGovernor).supportAsset(assetAddresses.TUSD, 0); // Enable capital movement - await vault.connect(sGovernor).unpauseCapital(); + await vaultAndTokenConracts.vault.connect(sGovernor).unpauseCapital(); } const signers = await hre.ethers.getSigners(); @@ -678,11 +901,12 @@ const defaultFixture = deployments.createFixture(async () => { // Matt and Josh each have $100 OUSD for (const user of [matt, josh]) { - await dai.connect(user).approve(vault.address, daiUnits("100")); - await vault.connect(user).mint(dai.address, daiUnits("100"), 0); + await dai.connect(user).approve(vaultAndTokenConracts.vault.address, daiUnits("100")); + await vaultAndTokenConracts.vault.connect(user).mint(dai.address, daiUnits("100"), 0); } } return { + ...vaultAndTokenConracts, // Accounts matt, josh, @@ -696,13 +920,9 @@ const defaultFixture = deployments.createFixture(async () => { timelock, oldTimelock, // Contracts - ousd, - vault, vaultValueChecker, harvester, dripper, - mockNonRebasing, - mockNonRebasingTwo, // Oracle chainlinkOracleFeedDAI, chainlinkOracleFeedUSDT, @@ -779,9 +999,7 @@ const defaultFixture = deployments.createFixture(async () => { fusdt, // OETH - oethVault, oethVaultValueChecker, - oeth, frxETH, sfrxETH, sDAI, @@ -792,7 +1010,6 @@ const defaultFixture = deployments.createFixture(async () => { lidoWithdrawalStrategy, balancerREthStrategy, oethMorphoAaveStrategy, - woeth, convexEthMetaStrategy, oethDripper, oethHarvester, @@ -2540,6 +2757,7 @@ module.exports = { resetAllowance, defaultFixture, oethDefaultFixture, + loadTokenTransferFixture, mockVaultFixture, compoundFixture, compoundVaultFixture, diff --git a/contracts/test/token/token-transfers.js b/contracts/test/token/token-transfers.js new file mode 100644 index 0000000000..34264e0d00 --- /dev/null +++ b/contracts/test/token/token-transfers.js @@ -0,0 +1,83 @@ +const { expect } = require("chai"); +const { loadTokenTransferFixture } = require("../_fixture"); + +const { isFork } = require("../helpers"); + +describe.only("Token Transfers", function () { + if (isFork) { + this.timeout(0); + } + let fixture; + beforeEach(async () => { + fixture = await loadTokenTransferFixture(); + }); + + it("Accounts should have correct initial states", async () => { + const { + rebase_eoa_notset_0, + rebase_eoa_notset_1, + rebase_eoa_stdRebasing_0, + rebase_eoa_stdRebasing_1, + rebase_contract_0, + rebase_contract_1, + nonrebase_eoa_0, + nonrebase_eoa_1, + nonrebase_cotract_0, + nonrebase_cotract_1, + nonrebase_cotract_notSet_0, + nonrebase_cotract_notSet_1, + nonrebase_cotract_notSet_altcpt_gt_0, + nonrebase_cotract_notSet_altcpt_gt_1, + rebase_source_0, + rebase_source_1, + rebase_target_0, + rebase_target_1, + ousd + } = fixture; + + expect(await ousd.rebaseState(rebase_eoa_notset_0.address)).to.equal(0); // rebaseState:NotSet + await expect(rebase_eoa_notset_0).has.a.balanceOf("11", ousd); + expect(await ousd.rebaseState(rebase_eoa_notset_1.address)).to.equal(0); // rebaseState:NotSet + await expect(rebase_eoa_notset_1).has.a.balanceOf("12", ousd); + + expect(await ousd.rebaseState(rebase_eoa_stdRebasing_0.address)).to.equal(2); // rebaseState:StdRebasing + await expect(rebase_eoa_stdRebasing_0).has.a.balanceOf("21", ousd); + expect(await ousd.rebaseState(rebase_eoa_stdRebasing_1.address)).to.equal(2); // rebaseState:StdRebasing + await expect(rebase_eoa_stdRebasing_1).has.a.balanceOf("22", ousd); + + expect(await ousd.rebaseState(rebase_contract_0.address)).to.equal(2); // rebaseState:StdRebasing + await expect(rebase_contract_0).has.a.balanceOf("33", ousd); + expect(await ousd.rebaseState(rebase_contract_1.address)).to.equal(2); // rebaseState:StdRebasing + await expect(rebase_contract_1).has.a.balanceOf("34", ousd); + + expect(await ousd.rebaseState(nonrebase_eoa_0.address)).to.equal(1); // rebaseState:StdNonRebasing + await expect(nonrebase_eoa_0).has.a.balanceOf("44", ousd); + expect(await ousd.rebaseState(nonrebase_eoa_1.address)).to.equal(1); // rebaseState:StdNonRebasing + await expect(nonrebase_eoa_1).has.a.balanceOf("45", ousd); + + expect(await ousd.rebaseState(nonrebase_cotract_0.address)).to.equal(1); // rebaseState:StdNonRebasing + await expect(nonrebase_cotract_0).has.a.balanceOf("55", ousd); + expect(await ousd.rebaseState(nonrebase_cotract_1.address)).to.equal(1); // rebaseState:StdNonRebasing + await expect(nonrebase_cotract_1).has.a.balanceOf("56", ousd); + + expect(await ousd.rebaseState(nonrebase_cotract_notSet_0.address)).to.equal(0); // rebaseState:NotSet + await expect(nonrebase_cotract_notSet_0).has.a.balanceOf("0", ousd); + expect(await ousd.rebaseState(nonrebase_cotract_notSet_1.address)).to.equal(0); // rebaseState:NotSet + await expect(nonrebase_cotract_notSet_1).has.a.balanceOf("0", ousd); + + expect(await ousd.rebaseState(nonrebase_cotract_notSet_altcpt_gt_0.address)).to.equal(0); // rebaseState:NotSet + await expect(nonrebase_cotract_notSet_altcpt_gt_0).has.a.balanceOf("65", ousd); + expect(await ousd.rebaseState(nonrebase_cotract_notSet_altcpt_gt_1.address)).to.equal(0); // rebaseState:NotSet + await expect(nonrebase_cotract_notSet_altcpt_gt_1).has.a.balanceOf("66", ousd); + + expect(await ousd.rebaseState(rebase_source_0.address)).to.equal(3); // rebaseState:YieldDelegationSource + await expect(rebase_source_0).has.a.balanceOf("76", ousd); + expect(await ousd.rebaseState(rebase_source_1.address)).to.equal(3); // rebaseState:YieldDelegationSource + await expect(rebase_source_1).has.a.balanceOf("87", ousd); + + expect(await ousd.rebaseState(rebase_target_0.address)).to.equal(4); // rebaseState:YieldDelegationTarget + await expect(rebase_target_0).has.a.balanceOf("77", ousd); + expect(await ousd.rebaseState(rebase_target_1.address)).to.equal(4); // rebaseState:YieldDelegationTarget + await expect(rebase_target_1).has.a.balanceOf("88", ousd); + }); +}); From 6b981b0d6a18538c201536d79e90a08d9c728770 Mon Sep 17 00:00:00 2001 From: Domen Grabec Date: Thu, 14 Nov 2024 21:52:27 +0100 Subject: [PATCH 3/6] check all relevant contract initial states --- contracts/test/_fixture.js | 3 +++ contracts/test/token/ousd.js | 4 ++-- contracts/test/token/token-transfers.js | 11 ++++++++--- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/contracts/test/_fixture.js b/contracts/test/_fixture.js index 3f4a157d35..a2405286d4 100644 --- a/contracts/test/_fixture.js +++ b/contracts/test/_fixture.js @@ -376,6 +376,9 @@ const createAccountTypes = async ({ vault, ousd, ousdUnlocked, deploy }) => { .connect(governor) .delegateYield(rebase_source_1.address, rebase_target_1.address); + // matt burn remaining OUSD + await vault.connect(matt).redeemAll(ousdUnits("0")); + return { rebase_eoa_notset_0, rebase_eoa_notset_1, diff --git a/contracts/test/token/ousd.js b/contracts/test/token/ousd.js index 945fd3d329..f24ec792eb 100644 --- a/contracts/test/token/ousd.js +++ b/contracts/test/token/ousd.js @@ -796,7 +796,7 @@ describe("Token", function () { const beforeReceiver = await ousd.balanceOf(mockNonRebasing.address); await ousd.connect(matt).transfer(mockNonRebasing.address, amount); const afterReceiver = await ousd.balanceOf(mockNonRebasing.address); - expect(beforeReceiver.add(amount)).to.equal(afterReceiver); + await expect(beforeReceiver.add(amount)).to.equal(afterReceiver); }; // Helper to verify balance-exact transfers out @@ -804,7 +804,7 @@ describe("Token", function () { const beforeReceiver = await ousd.balanceOf(mockNonRebasing.address); await mockNonRebasing.transfer(matt.address, amount); const afterReceiver = await ousd.balanceOf(mockNonRebasing.address); - expect(beforeReceiver.sub(amount)).to.equal(afterReceiver); + await expect(beforeReceiver.sub(amount)).to.equal(afterReceiver); }; // In diff --git a/contracts/test/token/token-transfers.js b/contracts/test/token/token-transfers.js index 34264e0d00..5a0aa6e10c 100644 --- a/contracts/test/token/token-transfers.js +++ b/contracts/test/token/token-transfers.js @@ -1,7 +1,7 @@ const { expect } = require("chai"); const { loadTokenTransferFixture } = require("../_fixture"); -const { isFork } = require("../helpers"); +const { isFork, ousdUnits} = require("../helpers"); describe.only("Token Transfers", function () { if (isFork) { @@ -12,7 +12,7 @@ describe.only("Token Transfers", function () { fixture = await loadTokenTransferFixture(); }); - it("Accounts should have correct initial states", async () => { + it("Accounts and ousd contract should have correct initial states", async () => { const { rebase_eoa_notset_0, rebase_eoa_notset_1, @@ -32,7 +32,7 @@ describe.only("Token Transfers", function () { rebase_source_1, rebase_target_0, rebase_target_1, - ousd + ousd, } = fixture; expect(await ousd.rebaseState(rebase_eoa_notset_0.address)).to.equal(0); // rebaseState:NotSet @@ -79,5 +79,10 @@ describe.only("Token Transfers", function () { await expect(rebase_target_0).has.a.balanceOf("77", ousd); expect(await ousd.rebaseState(rebase_target_1.address)).to.equal(4); // rebaseState:YieldDelegationTarget await expect(rebase_target_1).has.a.balanceOf("88", ousd); + + const totalSupply = 11 + 12 + 21 + 22 + 33 + 34 + 44 + 45 + 55 + 56 + 65 + 66 + 76 + 87 + 77 + 88; + const nonRebasingSupply = 44 + 45 + 55 + 56 + 65 + 66; + expect(await ousd.totalSupply()).to.equal(ousdUnits(`${totalSupply}`)); + expect(await ousd.nonRebasingSupply()).to.equal(ousdUnits(`${nonRebasingSupply}`)); }); }); From c97fd943126830321f6a9d08419828c6129c4c07 Mon Sep 17 00:00:00 2001 From: Domen Grabec Date: Thu, 14 Nov 2024 23:00:26 +0100 Subject: [PATCH 4/6] add test between any possible contract accounts --- contracts/test/_fixture.js | 2 +- contracts/test/token/token-transfers.js | 68 ++++++++++++++++++++++++- 2 files changed, 68 insertions(+), 2 deletions(-) diff --git a/contracts/test/_fixture.js b/contracts/test/_fixture.js index a2405286d4..3cddf46588 100644 --- a/contracts/test/_fixture.js +++ b/contracts/test/_fixture.js @@ -266,7 +266,7 @@ const createAccountTypes = async ({ vault, ousd, ousdUnlocked, deploy }) => { const contract = await ethers.getContract(fullName); await contract.setOUSD(ousd.address); - + return contract; }; diff --git a/contracts/test/token/token-transfers.js b/contracts/test/token/token-transfers.js index 5a0aa6e10c..5b4ef3b14b 100644 --- a/contracts/test/token/token-transfers.js +++ b/contracts/test/token/token-transfers.js @@ -3,7 +3,7 @@ const { loadTokenTransferFixture } = require("../_fixture"); const { isFork, ousdUnits} = require("../helpers"); -describe.only("Token Transfers", function () { +describe("Token Transfers", function () { if (isFork) { this.timeout(0); } @@ -85,4 +85,70 @@ describe.only("Token Transfers", function () { expect(await ousd.totalSupply()).to.equal(ousdUnits(`${totalSupply}`)); expect(await ousd.nonRebasingSupply()).to.equal(ousdUnits(`${nonRebasingSupply}`)); }); + + const fromAccounts = [ + {name: "rebase_eoa_notset_0", affectsRebasingCredits: true, isContract: false}, + {name: "rebase_eoa_stdRebasing_0", affectsRebasingCredits: true, isContract: false}, + {name: "rebase_contract_0", affectsRebasingCredits: true, isContract: true}, + {name: "nonrebase_eoa_0", affectsRebasingCredits: false, isContract: false}, + {name: "nonrebase_cotract_0", affectsRebasingCredits: false, isContract: true}, + // can not initiate a transfer from below contract since it has the balance of 0 + //{name: "nonrebase_cotract_notSet_0", affectsRebasingCredits: false, isContract: true}, + {name: "nonrebase_cotract_notSet_altcpt_gt_0", affectsRebasingCredits: false, isContract: true}, + {name: "rebase_source_0", affectsRebasingCredits: true, isContract: false}, + {name: "rebase_target_0", affectsRebasingCredits: true, isContract: false}, + ]; + + const toAccounts = [ + {name: "rebase_eoa_notset_1", affectsRebasingCredits: true}, + {name: "rebase_eoa_stdRebasing_1", affectsRebasingCredits: true}, + {name: "rebase_contract_1", affectsRebasingCredits: true}, + {name: "nonrebase_eoa_1", affectsRebasingCredits: false}, + {name: "nonrebase_cotract_1", affectsRebasingCredits: false}, + {name: "nonrebase_cotract_notSet_1", affectsRebasingCredits: false}, + {name: "nonrebase_cotract_notSet_altcpt_gt_1", affectsRebasingCredits: false}, + {name: "rebase_source_1", affectsRebasingCredits: true}, + {name: "rebase_target_1", affectsRebasingCredits: true}, + ]; + + const totalSupply = ousdUnits("792"); + const nonRebasingSupply = ousdUnits("331"); + for (let i = 0; i < fromAccounts.length; i++) { + for (let j = 0; j < toAccounts.length; j++) { + const {name: fromName, affectsRebasingCredits: fromAffectsRC, isContract } = fromAccounts[i]; + const {name: toName, affectsRebasingCredits: toAffectsRC } = toAccounts[j]; + + it(`Should transfer from ${fromName} to ${toName}`, async () => { + const fromAccount = fixture[fromName]; + const toAccount = fixture[toName]; + const { ousd } = fixture; + + const fromBalance = await ousd.balanceOf(fromAccount.address); + const toBalance = await ousd.balanceOf(toAccount.address); + // Random transfer between 2-8 + const amount = ousdUnits(`${2 + Math.random() * 6}`); + + if (isContract) { + await fromAccount.transfer(toAccount.address, amount); + } else { + await ousd.connect(fromAccount).transfer(toAccount.address, amount); + } + + // check balances + await expect(await ousd.balanceOf(fromAccount.address)).to.equal(fromBalance.sub(amount)); + await expect(await ousd.balanceOf(toAccount.address)).to.equal(toBalance.add(amount)); + + let expectedNonRebasingSupply = nonRebasingSupply; + if (!fromAffectsRC) { + expectedNonRebasingSupply = expectedNonRebasingSupply.sub(amount); + } + if (!toAffectsRC) { + expectedNonRebasingSupply = expectedNonRebasingSupply.add(amount); + } + // check global contract (in)variants + await expect(await ousd.totalSupply()).to.equal(totalSupply); + await expect(await ousd.nonRebasingSupply()).to.equal(expectedNonRebasingSupply); + }); + } + } }); From 32a627c64bc2a4b81c5a2d3e10501b92262e37a8 Mon Sep 17 00:00:00 2001 From: Domen Grabec Date: Thu, 14 Nov 2024 23:01:10 +0100 Subject: [PATCH 5/6] prettier --- .../contracts/mocks/TestUpgradedOUSD.sol | 4 +- contracts/contracts/token/OUSD.sol | 28 ++-- contracts/test/_fixture.js | 129 +++++++++------ contracts/test/token/ousd.js | 18 +-- contracts/test/token/token-transfers.js | 150 ++++++++++++++---- 5 files changed, 213 insertions(+), 116 deletions(-) diff --git a/contracts/contracts/mocks/TestUpgradedOUSD.sol b/contracts/contracts/mocks/TestUpgradedOUSD.sol index 6d3b62748c..9d3555f9a2 100644 --- a/contracts/contracts/mocks/TestUpgradedOUSD.sol +++ b/contracts/contracts/mocks/TestUpgradedOUSD.sol @@ -13,9 +13,7 @@ contract TestUpgradedOUSD is OUSD { _creditBalances[_account] = _creditBalance; } - function overwriteAlternativeCPT(address _account, uint256 _acpt) - public - { + function overwriteAlternativeCPT(address _account, uint256 _acpt) public { alternativeCreditsPerToken[_account] = _acpt; } diff --git a/contracts/contracts/token/OUSD.sol b/contracts/contracts/token/OUSD.sol index 5bc29f7695..7c4ae00542 100644 --- a/contracts/contracts/token/OUSD.sol +++ b/contracts/contracts/token/OUSD.sol @@ -456,22 +456,22 @@ contract OUSD is Governable { /** * @dev Before a `rebaseOptIn` or non yield delegating token `transfer` can be executed contract - * accounts need to have a more explicitly defined rebasing state set. - * + * accounts need to have a more explicitly defined rebasing state set. + * * Contract account can be in the following states before `autoMigrate` is called: * 1. Under any token contract codebase they haven't been part of any token transfers yet * having rebaseState `NotSet` and `alternativeCreditsPerToken == 0` * 2. Under older token contract codebase they have the default rebaseState set to `NotSet` and - * the codebase has "auto-migrated" them by setting the `alternativeCreditsPerToken` to some + * the codebase has "auto-migrated" them by setting the `alternativeCreditsPerToken` to some * value greater than 0. * 3. Contract has under any token contract codebase explicitly requested to be opted out of rebasing * * Case 1. Needs to be migrated using autoMigrate to a nonRebasing account. - * + * * Note: Even with this _autoMigrate function in place there will still be Case 2 accounts existing that * will behave exactly like RebaseState StdNonRebasing account, and still having their rebase state * set to `NotSet` - * + * * @param _account Address of the account. */ function _autoMigrate(address _account) internal returns (bool) { @@ -511,10 +511,7 @@ contract OUSD is Governable { * to upside and downside. * @param _account Address of the account. */ - function governanceRebaseOptIn(address _account) - external - onlyGovernor - { + function governanceRebaseOptIn(address _account) external onlyGovernor { _rebaseOptIn(_account); } @@ -537,7 +534,7 @@ contract OUSD is Governable { RebaseOptions state = rebaseState[_account]; require( state == RebaseOptions.StdNonRebasing || - state == RebaseOptions.NotSet, + state == RebaseOptions.NotSet, "Only standard non-rebasing accounts can opt in" ); @@ -590,10 +587,7 @@ contract OUSD is Governable { * the exchange rate between "credits" and OUSD tokens to change balances. * @param _newTotalSupply New total supply of OUSD. */ - function changeSupply(uint256 _newTotalSupply) - external - onlyVault - { + function changeSupply(uint256 _newTotalSupply) external onlyVault { require(_totalSupply > 0, "Cannot increase 0 supply"); if (_totalSupply == _newTotalSupply) { @@ -626,10 +620,7 @@ contract OUSD is Governable { ); } - function delegateYield(address from, address to) - external - onlyGovernor - { + function delegateYield(address from, address to) external onlyGovernor { require(from != to, "Cannot delegate to self"); require( yieldFrom[to] == address(0) && @@ -655,7 +646,6 @@ contract OUSD is Governable { "Invalid rebaseState to" ); - if ( alternativeCreditsPerToken[from] == 0 && (stateFrom == RebaseOptions.NotSet || diff --git a/contracts/test/_fixture.js b/contracts/test/_fixture.js index 3cddf46588..e02f8853dd 100644 --- a/contracts/test/_fixture.js +++ b/contracts/test/_fixture.js @@ -196,7 +196,10 @@ const getVaultAndTokenConracts = async () => { const ousd = await ethers.getContractAt("OUSD", ousdProxy.address); // the same contract as the "ousd" one just with some unlocked features - const ousdUnlocked = await ethers.getContractAt("TestUpgradedOUSD", ousdProxy.address); + const ousdUnlocked = await ethers.getContractAt( + "TestUpgradedOUSD", + ousdProxy.address + ); const vault = await ethers.getContractAt("IVault", vaultProxy.address); @@ -229,7 +232,7 @@ const getVaultAndTokenConracts = async () => { oeth, woeth, mockNonRebasing, - mockNonRebasingTwo + mockNonRebasingTwo, }; }; @@ -239,7 +242,6 @@ const createAccountTypes = async ({ vault, ousd, ousdUnlocked, deploy }) => { const governor = signers[1]; if (!isFork) { - await fundAccounts(); const dai = await ethers.getContract("MockDAI"); await dai.connect(matt).approve(vault.address, daiUnits("1000")); @@ -253,12 +255,12 @@ const createAccountTypes = async ({ vault, ousd, ousdUnlocked, deploy }) => { let account = ethers.Wallet.createRandom(); // Give ETH to user await hardhatSetBalance(account.address, "1000000"); - account = account.connect(ethers.provider); + account = account.connect(ethers.provider); return account; }; const createContract = async (name) => { - const fullName = `MockNonRebasing_${name}` + const fullName = `MockNonRebasing_${name}`; await deploy(fullName, { from: matt.address, contract: "MockNonRebasing", @@ -266,7 +268,7 @@ const createAccountTypes = async ({ vault, ousd, ousdUnlocked, deploy }) => { const contract = await ethers.getContract(fullName); await contract.setOUSD(ousd.address); - + return contract; }; @@ -275,48 +277,68 @@ const createAccountTypes = async ({ vault, ousd, ousdUnlocked, deploy }) => { const generateCreditsBalancePair = ({ creditsPerToken, tokenBalance }) => { const creditsPerTokenBN = parseUnits(`${creditsPerToken}`, 27); // 1e18 * 1e27 / 1e18 - const creditsBalanceBN = tokenBalance.mul(creditsPerTokenBN).div(parseUnits('1', 18)); + const creditsBalanceBN = tokenBalance + .mul(creditsPerTokenBN) + .div(parseUnits("1", 18)); return { creditsPerTokenBN, - creditsBalanceBN + creditsBalanceBN, }; - } + }; - const createNonRebasingNotSetAlternativeCptContract = async ({ name, creditsPerToken, balance }) => { + const createNonRebasingNotSetAlternativeCptContract = async ({ + name, + creditsPerToken, + balance, + }) => { const nonrebase_cotract_notSet_altcpt_gt = await createContract(name); - await ousd.connect(matt).transfer(nonrebase_cotract_notSet_altcpt_gt.address, balance); + await ousd + .connect(matt) + .transfer(nonrebase_cotract_notSet_altcpt_gt.address, balance); const { creditsPerTokenBN, creditsBalanceBN } = generateCreditsBalancePair({ creditsPerToken, - tokenBalance: balance + tokenBalance: balance, }); - await ousdUnlocked.connect(matt).overwriteCreditBalances( - nonrebase_cotract_notSet_altcpt_gt.address, - creditsBalanceBN - ); - await ousdUnlocked.connect(matt).overwriteAlternativeCPT( - nonrebase_cotract_notSet_altcpt_gt.address, - creditsPerTokenBN - ); + await ousdUnlocked + .connect(matt) + .overwriteCreditBalances( + nonrebase_cotract_notSet_altcpt_gt.address, + creditsBalanceBN + ); + await ousdUnlocked + .connect(matt) + .overwriteAlternativeCPT( + nonrebase_cotract_notSet_altcpt_gt.address, + creditsPerTokenBN + ); await ousdUnlocked.connect(matt).overwriteRebaseState( nonrebase_cotract_notSet_altcpt_gt.address, 0 // NotSet ); return nonrebase_cotract_notSet_altcpt_gt; - } + }; const rebase_eoa_notset_0 = await createAccount(); - await ousd.connect(matt).transfer(rebase_eoa_notset_0.address, ousdUnits("11")); + await ousd + .connect(matt) + .transfer(rebase_eoa_notset_0.address, ousdUnits("11")); const rebase_eoa_notset_1 = await createAccount(); - await ousd.connect(matt).transfer(rebase_eoa_notset_1.address, ousdUnits("12")); + await ousd + .connect(matt) + .transfer(rebase_eoa_notset_1.address, ousdUnits("12")); const rebase_eoa_stdRebasing_0 = await createAccount(); - await ousd.connect(matt).transfer(rebase_eoa_stdRebasing_0.address, ousdUnits("21")); + await ousd + .connect(matt) + .transfer(rebase_eoa_stdRebasing_0.address, ousdUnits("21")); await ousd.connect(rebase_eoa_stdRebasing_0).rebaseOptOut(); await ousd.connect(rebase_eoa_stdRebasing_0).rebaseOptIn(); const rebase_eoa_stdRebasing_1 = await createAccount(); - await ousd.connect(matt).transfer(rebase_eoa_stdRebasing_1.address, ousdUnits("22")); + await ousd + .connect(matt) + .transfer(rebase_eoa_stdRebasing_1.address, ousdUnits("22")); await ousd.connect(rebase_eoa_stdRebasing_1).rebaseOptOut(); await ousd.connect(rebase_eoa_stdRebasing_1).rebaseOptIn(); @@ -335,28 +357,38 @@ const createAccountTypes = async ({ vault, ousd, ousdUnlocked, deploy }) => { await ousd.connect(nonrebase_eoa_1).rebaseOptOut(); const nonrebase_cotract_0 = await createContract("nonrebase_cotract_0"); - await ousd.connect(matt).transfer(nonrebase_cotract_0.address, ousdUnits("55")); + await ousd + .connect(matt) + .transfer(nonrebase_cotract_0.address, ousdUnits("55")); await nonrebase_cotract_0.connect(matt).rebaseOptIn(); await nonrebase_cotract_0.connect(matt).rebaseOptOut(); const nonrebase_cotract_1 = await createContract("nonrebase_cotract_1"); - await ousd.connect(matt).transfer(nonrebase_cotract_1.address, ousdUnits("56")); + await ousd + .connect(matt) + .transfer(nonrebase_cotract_1.address, ousdUnits("56")); await nonrebase_cotract_1.connect(matt).rebaseOptIn(); await nonrebase_cotract_1.connect(matt).rebaseOptOut(); - const nonrebase_cotract_notSet_0 = await createContract("nonrebase_cotract_notSet_0"); - const nonrebase_cotract_notSet_1 = await createContract("nonrebase_cotract_notSet_1"); + const nonrebase_cotract_notSet_0 = await createContract( + "nonrebase_cotract_notSet_0" + ); + const nonrebase_cotract_notSet_1 = await createContract( + "nonrebase_cotract_notSet_1" + ); - const nonrebase_cotract_notSet_altcpt_gt_0 = await createNonRebasingNotSetAlternativeCptContract({ - name: "nonrebase_cotract_notSet_altcpt_gt_0", - creditsPerToken: 0.934232, - balance: ousdUnits("65") - }); + const nonrebase_cotract_notSet_altcpt_gt_0 = + await createNonRebasingNotSetAlternativeCptContract({ + name: "nonrebase_cotract_notSet_altcpt_gt_0", + creditsPerToken: 0.934232, + balance: ousdUnits("65"), + }); - const nonrebase_cotract_notSet_altcpt_gt_1 = await createNonRebasingNotSetAlternativeCptContract({ - name: "nonrebase_cotract_notSet_altcpt_gt_1", - creditsPerToken: 0.890232, - balance: ousdUnits("66") - }); + const nonrebase_cotract_notSet_altcpt_gt_1 = + await createNonRebasingNotSetAlternativeCptContract({ + name: "nonrebase_cotract_notSet_altcpt_gt_1", + creditsPerToken: 0.890232, + balance: ousdUnits("66"), + }); const rebase_source_0 = await createAccount(); await ousd.connect(matt).transfer(rebase_source_0.address, ousdUnits("76")); @@ -398,8 +430,7 @@ const createAccountTypes = async ({ vault, ousd, ousdUnlocked, deploy }) => { rebase_source_1, rebase_target_0, rebase_target_1, - } - + }; }; /** @@ -437,8 +468,8 @@ const loadTokenTransferFixture = deployments.createFixture(async () => { ...accountTypes, governorAddr, strategistAddr, - timelockAddr - } + timelockAddr, + }; }); const defaultFixture = deployments.createFixture(async () => { @@ -875,7 +906,9 @@ const defaultFixture = deployments.createFixture(async () => { const sGovernor = await ethers.provider.getSigner(governorAddr); // Add TUSD in fixture, it is disabled by default in deployment - await vaultAndTokenConracts.vault.connect(sGovernor).supportAsset(assetAddresses.TUSD, 0); + await vaultAndTokenConracts.vault + .connect(sGovernor) + .supportAsset(assetAddresses.TUSD, 0); // Enable capital movement await vaultAndTokenConracts.vault.connect(sGovernor).unpauseCapital(); @@ -904,8 +937,12 @@ const defaultFixture = deployments.createFixture(async () => { // Matt and Josh each have $100 OUSD for (const user of [matt, josh]) { - await dai.connect(user).approve(vaultAndTokenConracts.vault.address, daiUnits("100")); - await vaultAndTokenConracts.vault.connect(user).mint(dai.address, daiUnits("100"), 0); + await dai + .connect(user) + .approve(vaultAndTokenConracts.vault.address, daiUnits("100")); + await vaultAndTokenConracts.vault + .connect(user) + .mint(dai.address, daiUnits("100"), 0); } } return { diff --git a/contracts/test/token/ousd.js b/contracts/test/token/ousd.js index f24ec792eb..c8237daf98 100644 --- a/contracts/test/token/ousd.js +++ b/contracts/test/token/ousd.js @@ -248,9 +248,7 @@ describe("Token", function () { let { ousd, vault, matt, usdc, josh, mockNonRebasing } = fixture; // Give Josh an allowance to move Matt's OUSD - await ousd - .connect(matt) - .approve(await josh.getAddress(), ousdUnits("100")); + await ousd.connect(matt).approve(await josh.getAddress(), ousdUnits("100")); // Give contract 100 OUSD from Matt via Josh await ousd @@ -287,9 +285,7 @@ describe("Token", function () { let { ousd, vault, matt, usdc, josh, mockNonRebasing } = fixture; // Give Josh an allowance to move Matt's OUSD - await ousd - .connect(matt) - .approve(await josh.getAddress(), ousdUnits("150")); + await ousd.connect(matt).approve(await josh.getAddress(), ousdUnits("150")); // Give contract 100 OUSD from Matt via Josh await ousd .connect(josh) @@ -334,10 +330,7 @@ describe("Token", function () { await expect(matt).has.an.approxBalanceOf("100.00", ousd); await expect(josh).has.an.approxBalanceOf("0", ousd); await expect(mockNonRebasing).has.an.approxBalanceOf("100.00", ousd); - await mockNonRebasing.approve( - await matt.getAddress(), - ousdUnits("100") - ); + await mockNonRebasing.approve(await matt.getAddress(), ousdUnits("100")); await ousd .connect(matt) @@ -381,10 +374,7 @@ describe("Token", function () { await expect(matt).has.an.approxBalanceOf("250", ousd); await expect(mockNonRebasing).has.an.approxBalanceOf("150.00", ousd); // Transfer contract balance to Josh - await mockNonRebasing.approve( - await matt.getAddress(), - ousdUnits("150") - ); + await mockNonRebasing.approve(await matt.getAddress(), ousdUnits("150")); await ousd .connect(matt) diff --git a/contracts/test/token/token-transfers.js b/contracts/test/token/token-transfers.js index 5b4ef3b14b..dadbc77c35 100644 --- a/contracts/test/token/token-transfers.js +++ b/contracts/test/token/token-transfers.js @@ -1,7 +1,7 @@ const { expect } = require("chai"); const { loadTokenTransferFixture } = require("../_fixture"); -const { isFork, ousdUnits} = require("../helpers"); +const { isFork, ousdUnits } = require("../helpers"); describe("Token Transfers", function () { if (isFork) { @@ -34,15 +34,19 @@ describe("Token Transfers", function () { rebase_target_1, ousd, } = fixture; - + expect(await ousd.rebaseState(rebase_eoa_notset_0.address)).to.equal(0); // rebaseState:NotSet await expect(rebase_eoa_notset_0).has.a.balanceOf("11", ousd); expect(await ousd.rebaseState(rebase_eoa_notset_1.address)).to.equal(0); // rebaseState:NotSet await expect(rebase_eoa_notset_1).has.a.balanceOf("12", ousd); - expect(await ousd.rebaseState(rebase_eoa_stdRebasing_0.address)).to.equal(2); // rebaseState:StdRebasing + expect(await ousd.rebaseState(rebase_eoa_stdRebasing_0.address)).to.equal( + 2 + ); // rebaseState:StdRebasing await expect(rebase_eoa_stdRebasing_0).has.a.balanceOf("21", ousd); - expect(await ousd.rebaseState(rebase_eoa_stdRebasing_1.address)).to.equal(2); // rebaseState:StdRebasing + expect(await ousd.rebaseState(rebase_eoa_stdRebasing_1.address)).to.equal( + 2 + ); // rebaseState:StdRebasing await expect(rebase_eoa_stdRebasing_1).has.a.balanceOf("22", ousd); expect(await ousd.rebaseState(rebase_contract_0.address)).to.equal(2); // rebaseState:StdRebasing @@ -60,15 +64,29 @@ describe("Token Transfers", function () { expect(await ousd.rebaseState(nonrebase_cotract_1.address)).to.equal(1); // rebaseState:StdNonRebasing await expect(nonrebase_cotract_1).has.a.balanceOf("56", ousd); - expect(await ousd.rebaseState(nonrebase_cotract_notSet_0.address)).to.equal(0); // rebaseState:NotSet + expect(await ousd.rebaseState(nonrebase_cotract_notSet_0.address)).to.equal( + 0 + ); // rebaseState:NotSet await expect(nonrebase_cotract_notSet_0).has.a.balanceOf("0", ousd); - expect(await ousd.rebaseState(nonrebase_cotract_notSet_1.address)).to.equal(0); // rebaseState:NotSet + expect(await ousd.rebaseState(nonrebase_cotract_notSet_1.address)).to.equal( + 0 + ); // rebaseState:NotSet await expect(nonrebase_cotract_notSet_1).has.a.balanceOf("0", ousd); - expect(await ousd.rebaseState(nonrebase_cotract_notSet_altcpt_gt_0.address)).to.equal(0); // rebaseState:NotSet - await expect(nonrebase_cotract_notSet_altcpt_gt_0).has.a.balanceOf("65", ousd); - expect(await ousd.rebaseState(nonrebase_cotract_notSet_altcpt_gt_1.address)).to.equal(0); // rebaseState:NotSet - await expect(nonrebase_cotract_notSet_altcpt_gt_1).has.a.balanceOf("66", ousd); + expect( + await ousd.rebaseState(nonrebase_cotract_notSet_altcpt_gt_0.address) + ).to.equal(0); // rebaseState:NotSet + await expect(nonrebase_cotract_notSet_altcpt_gt_0).has.a.balanceOf( + "65", + ousd + ); + expect( + await ousd.rebaseState(nonrebase_cotract_notSet_altcpt_gt_1.address) + ).to.equal(0); // rebaseState:NotSet + await expect(nonrebase_cotract_notSet_altcpt_gt_1).has.a.balanceOf( + "66", + ousd + ); expect(await ousd.rebaseState(rebase_source_0.address)).to.equal(3); // rebaseState:YieldDelegationSource await expect(rebase_source_0).has.a.balanceOf("76", ousd); @@ -80,43 +98,101 @@ describe("Token Transfers", function () { expect(await ousd.rebaseState(rebase_target_1.address)).to.equal(4); // rebaseState:YieldDelegationTarget await expect(rebase_target_1).has.a.balanceOf("88", ousd); - const totalSupply = 11 + 12 + 21 + 22 + 33 + 34 + 44 + 45 + 55 + 56 + 65 + 66 + 76 + 87 + 77 + 88; + const totalSupply = + 11 + + 12 + + 21 + + 22 + + 33 + + 34 + + 44 + + 45 + + 55 + + 56 + + 65 + + 66 + + 76 + + 87 + + 77 + + 88; const nonRebasingSupply = 44 + 45 + 55 + 56 + 65 + 66; expect(await ousd.totalSupply()).to.equal(ousdUnits(`${totalSupply}`)); - expect(await ousd.nonRebasingSupply()).to.equal(ousdUnits(`${nonRebasingSupply}`)); + expect(await ousd.nonRebasingSupply()).to.equal( + ousdUnits(`${nonRebasingSupply}`) + ); }); const fromAccounts = [ - {name: "rebase_eoa_notset_0", affectsRebasingCredits: true, isContract: false}, - {name: "rebase_eoa_stdRebasing_0", affectsRebasingCredits: true, isContract: false}, - {name: "rebase_contract_0", affectsRebasingCredits: true, isContract: true}, - {name: "nonrebase_eoa_0", affectsRebasingCredits: false, isContract: false}, - {name: "nonrebase_cotract_0", affectsRebasingCredits: false, isContract: true}, + { + name: "rebase_eoa_notset_0", + affectsRebasingCredits: true, + isContract: false, + }, + { + name: "rebase_eoa_stdRebasing_0", + affectsRebasingCredits: true, + isContract: false, + }, + { + name: "rebase_contract_0", + affectsRebasingCredits: true, + isContract: true, + }, + { + name: "nonrebase_eoa_0", + affectsRebasingCredits: false, + isContract: false, + }, + { + name: "nonrebase_cotract_0", + affectsRebasingCredits: false, + isContract: true, + }, // can not initiate a transfer from below contract since it has the balance of 0 //{name: "nonrebase_cotract_notSet_0", affectsRebasingCredits: false, isContract: true}, - {name: "nonrebase_cotract_notSet_altcpt_gt_0", affectsRebasingCredits: false, isContract: true}, - {name: "rebase_source_0", affectsRebasingCredits: true, isContract: false}, - {name: "rebase_target_0", affectsRebasingCredits: true, isContract: false}, + { + name: "nonrebase_cotract_notSet_altcpt_gt_0", + affectsRebasingCredits: false, + isContract: true, + }, + { + name: "rebase_source_0", + affectsRebasingCredits: true, + isContract: false, + }, + { + name: "rebase_target_0", + affectsRebasingCredits: true, + isContract: false, + }, ]; const toAccounts = [ - {name: "rebase_eoa_notset_1", affectsRebasingCredits: true}, - {name: "rebase_eoa_stdRebasing_1", affectsRebasingCredits: true}, - {name: "rebase_contract_1", affectsRebasingCredits: true}, - {name: "nonrebase_eoa_1", affectsRebasingCredits: false}, - {name: "nonrebase_cotract_1", affectsRebasingCredits: false}, - {name: "nonrebase_cotract_notSet_1", affectsRebasingCredits: false}, - {name: "nonrebase_cotract_notSet_altcpt_gt_1", affectsRebasingCredits: false}, - {name: "rebase_source_1", affectsRebasingCredits: true}, - {name: "rebase_target_1", affectsRebasingCredits: true}, + { name: "rebase_eoa_notset_1", affectsRebasingCredits: true }, + { name: "rebase_eoa_stdRebasing_1", affectsRebasingCredits: true }, + { name: "rebase_contract_1", affectsRebasingCredits: true }, + { name: "nonrebase_eoa_1", affectsRebasingCredits: false }, + { name: "nonrebase_cotract_1", affectsRebasingCredits: false }, + { name: "nonrebase_cotract_notSet_1", affectsRebasingCredits: false }, + { + name: "nonrebase_cotract_notSet_altcpt_gt_1", + affectsRebasingCredits: false, + }, + { name: "rebase_source_1", affectsRebasingCredits: true }, + { name: "rebase_target_1", affectsRebasingCredits: true }, ]; const totalSupply = ousdUnits("792"); const nonRebasingSupply = ousdUnits("331"); for (let i = 0; i < fromAccounts.length; i++) { for (let j = 0; j < toAccounts.length; j++) { - const {name: fromName, affectsRebasingCredits: fromAffectsRC, isContract } = fromAccounts[i]; - const {name: toName, affectsRebasingCredits: toAffectsRC } = toAccounts[j]; + const { + name: fromName, + affectsRebasingCredits: fromAffectsRC, + isContract, + } = fromAccounts[i]; + const { name: toName, affectsRebasingCredits: toAffectsRC } = + toAccounts[j]; it(`Should transfer from ${fromName} to ${toName}`, async () => { const fromAccount = fixture[fromName]; @@ -135,8 +211,12 @@ describe("Token Transfers", function () { } // check balances - await expect(await ousd.balanceOf(fromAccount.address)).to.equal(fromBalance.sub(amount)); - await expect(await ousd.balanceOf(toAccount.address)).to.equal(toBalance.add(amount)); + await expect(await ousd.balanceOf(fromAccount.address)).to.equal( + fromBalance.sub(amount) + ); + await expect(await ousd.balanceOf(toAccount.address)).to.equal( + toBalance.add(amount) + ); let expectedNonRebasingSupply = nonRebasingSupply; if (!fromAffectsRC) { @@ -147,7 +227,9 @@ describe("Token Transfers", function () { } // check global contract (in)variants await expect(await ousd.totalSupply()).to.equal(totalSupply); - await expect(await ousd.nonRebasingSupply()).to.equal(expectedNonRebasingSupply); + await expect(await ousd.nonRebasingSupply()).to.equal( + expectedNonRebasingSupply + ); }); } } From c2f48b2a02190c274b83f878d0e759897c74ec6d Mon Sep 17 00:00:00 2001 From: Domen Grabec Date: Fri, 15 Nov 2024 00:00:30 +0100 Subject: [PATCH 6/6] add some documentation and prettier --- contracts/test/_fixture.js | 71 ++++++++++++++++++++----- contracts/test/token/token-transfers.js | 64 ++++++++++------------ 2 files changed, 86 insertions(+), 49 deletions(-) diff --git a/contracts/test/_fixture.js b/contracts/test/_fixture.js index e02f8853dd..3f06329f9c 100644 --- a/contracts/test/_fixture.js +++ b/contracts/test/_fixture.js @@ -236,6 +236,11 @@ const getVaultAndTokenConracts = async () => { }; }; +/** + * This fixture creates the 4 different OUSD contract account types in all of + * the possible storage configuration: StdRebasing, StdNonRebasing, YieldDelegationSource, + * YieldDelegationTarget + */ const createAccountTypes = async ({ vault, ousd, ousdUnlocked, deploy }) => { const signers = await hre.ethers.getSigners(); const matt = signers[4]; @@ -390,46 +395,84 @@ const createAccountTypes = async ({ vault, ousd, ousdUnlocked, deploy }) => { balance: ousdUnits("66"), }); - const rebase_source_0 = await createAccount(); - await ousd.connect(matt).transfer(rebase_source_0.address, ousdUnits("76")); - const rebase_target_0 = await createAccount(); - await ousd.connect(matt).transfer(rebase_target_0.address, ousdUnits("77")); + const rebase_delegate_source_0 = await createAccount(); + await ousd + .connect(matt) + .transfer(rebase_delegate_source_0.address, ousdUnits("76")); + const rebase_delegate_target_0 = await createAccount(); + await ousd + .connect(matt) + .transfer(rebase_delegate_target_0.address, ousdUnits("77")); await ousd .connect(governor) - .delegateYield(rebase_source_0.address, rebase_target_0.address); + .delegateYield( + rebase_delegate_source_0.address, + rebase_delegate_target_0.address + ); - const rebase_source_1 = await createAccount(); - await ousd.connect(matt).transfer(rebase_source_1.address, ousdUnits("87")); - const rebase_target_1 = await createAccount(); - await ousd.connect(matt).transfer(rebase_target_1.address, ousdUnits("88")); + const rebase_delegate_source_1 = await createAccount(); + await ousd + .connect(matt) + .transfer(rebase_delegate_source_1.address, ousdUnits("87")); + const rebase_delegate_target_1 = await createAccount(); + await ousd + .connect(matt) + .transfer(rebase_delegate_target_1.address, ousdUnits("88")); await ousd .connect(governor) - .delegateYield(rebase_source_1.address, rebase_target_1.address); + .delegateYield( + rebase_delegate_source_1.address, + rebase_delegate_target_1.address + ); // matt burn remaining OUSD await vault.connect(matt).redeemAll(ousdUnits("0")); return { + // StdRebasing account type: + // - all have alternativeCreditsPerToken = 0 + // - _creditBalances non zero using global contract's rebasingCredits to compute balance + + // EOA account that has rebaseState: NotSet rebase_eoa_notset_0, rebase_eoa_notset_1, + // EOA account that has rebaseState: StdRebasing rebase_eoa_stdRebasing_0, rebase_eoa_stdRebasing_1, + // contract account that has rebaseState: StdRebasing rebase_contract_0, rebase_contract_1, + + // StdNonRebasing account type: + // - alternativeCreditsPerToken > 0 & 1e18 for new accounts + // - _creditBalances non zero: + // - new accounts match _creditBalances to their token balance + // - older accounts use _creditBalances & alternativeCreditsPerToken to compute token balance + + // EOA account that has rebaseState: StdNonRebasing nonrebase_eoa_0, nonrebase_eoa_1, + // contract account that has rebaseState: StdNonRebasing nonrebase_cotract_0, nonrebase_cotract_1, + // contract account that has rebaseState: NotSet nonrebase_cotract_notSet_0, nonrebase_cotract_notSet_1, + // contract account that has rebaseState: NotSet & alternativeCreditsPerToken > 0 + // note: these are older accounts that have been migrated by the older versions of + // of the code without explicitly setting rebaseState to StdNonRebasing nonrebase_cotract_notSet_altcpt_gt_0, nonrebase_cotract_notSet_altcpt_gt_1, - rebase_source_0, - rebase_source_1, - rebase_target_0, - rebase_target_1, + + // account delegating yield + rebase_delegate_source_0, + rebase_delegate_source_1, + + // account receiving delegated yield + rebase_delegate_target_0, + rebase_delegate_target_1, }; }; diff --git a/contracts/test/token/token-transfers.js b/contracts/test/token/token-transfers.js index dadbc77c35..2c1dd9bb9f 100644 --- a/contracts/test/token/token-transfers.js +++ b/contracts/test/token/token-transfers.js @@ -28,10 +28,10 @@ describe("Token Transfers", function () { nonrebase_cotract_notSet_1, nonrebase_cotract_notSet_altcpt_gt_0, nonrebase_cotract_notSet_altcpt_gt_1, - rebase_source_0, - rebase_source_1, - rebase_target_0, - rebase_target_1, + rebase_delegate_source_0, + rebase_delegate_source_1, + rebase_delegate_target_0, + rebase_delegate_target_1, ousd, } = fixture; @@ -88,33 +88,27 @@ describe("Token Transfers", function () { ousd ); - expect(await ousd.rebaseState(rebase_source_0.address)).to.equal(3); // rebaseState:YieldDelegationSource - await expect(rebase_source_0).has.a.balanceOf("76", ousd); - expect(await ousd.rebaseState(rebase_source_1.address)).to.equal(3); // rebaseState:YieldDelegationSource - await expect(rebase_source_1).has.a.balanceOf("87", ousd); - - expect(await ousd.rebaseState(rebase_target_0.address)).to.equal(4); // rebaseState:YieldDelegationTarget - await expect(rebase_target_0).has.a.balanceOf("77", ousd); - expect(await ousd.rebaseState(rebase_target_1.address)).to.equal(4); // rebaseState:YieldDelegationTarget - await expect(rebase_target_1).has.a.balanceOf("88", ousd); - - const totalSupply = - 11 + - 12 + - 21 + - 22 + - 33 + - 34 + - 44 + - 45 + - 55 + - 56 + - 65 + - 66 + - 76 + - 87 + - 77 + - 88; + expect(await ousd.rebaseState(rebase_delegate_source_0.address)).to.equal( + 3 + ); // rebaseState:YieldDelegationSource + await expect(rebase_delegate_source_0).has.a.balanceOf("76", ousd); + expect(await ousd.rebaseState(rebase_delegate_source_1.address)).to.equal( + 3 + ); // rebaseState:YieldDelegationSource + await expect(rebase_delegate_source_1).has.a.balanceOf("87", ousd); + + expect(await ousd.rebaseState(rebase_delegate_target_0.address)).to.equal( + 4 + ); // rebaseState:YieldDelegationTarget + await expect(rebase_delegate_target_0).has.a.balanceOf("77", ousd); + expect(await ousd.rebaseState(rebase_delegate_target_1.address)).to.equal( + 4 + ); // rebaseState:YieldDelegationTarget + await expect(rebase_delegate_target_1).has.a.balanceOf("88", ousd); + + // prettier-ignore + const totalSupply = 11 + 12 + 21 + 22 + 33 + 34 + 44 + + 45 + 55 + 56 + 65 + 66 + 76 + 87 + 77 + 88; const nonRebasingSupply = 44 + 45 + 55 + 56 + 65 + 66; expect(await ousd.totalSupply()).to.equal(ousdUnits(`${totalSupply}`)); expect(await ousd.nonRebasingSupply()).to.equal( @@ -156,12 +150,12 @@ describe("Token Transfers", function () { isContract: true, }, { - name: "rebase_source_0", + name: "rebase_delegate_source_0", affectsRebasingCredits: true, isContract: false, }, { - name: "rebase_target_0", + name: "rebase_delegate_target_0", affectsRebasingCredits: true, isContract: false, }, @@ -178,8 +172,8 @@ describe("Token Transfers", function () { name: "nonrebase_cotract_notSet_altcpt_gt_1", affectsRebasingCredits: false, }, - { name: "rebase_source_1", affectsRebasingCredits: true }, - { name: "rebase_target_1", affectsRebasingCredits: true }, + { name: "rebase_delegate_source_1", affectsRebasingCredits: true }, + { name: "rebase_delegate_target_1", affectsRebasingCredits: true }, ]; const totalSupply = ousdUnits("792");