diff --git a/packages/abis/system/contracts/interfaces/common/IOracleAdapter.sol/IOracleAdapter.json b/packages/abis/system/contracts/interfaces/common/IOracleAdapter.sol/IOracleAdapter.json index 86aa08add..2fe41b477 100644 --- a/packages/abis/system/contracts/interfaces/common/IOracleAdapter.sol/IOracleAdapter.json +++ b/packages/abis/system/contracts/interfaces/common/IOracleAdapter.sol/IOracleAdapter.json @@ -73,7 +73,7 @@ "outputs": [ { "internalType": "int256", - "name": "price", + "name": "latestAnswer", "type": "int256" }, { diff --git a/packages/dma-contracts/contracts/actions/common/ChargeFee.sol b/packages/dma-contracts/contracts/actions/common/ChargeFee.sol index 352ddc134..7fcc623a8 100644 --- a/packages/dma-contracts/contracts/actions/common/ChargeFee.sol +++ b/packages/dma-contracts/contracts/actions/common/ChargeFee.sol @@ -9,11 +9,12 @@ import "../../core/types/Common.sol"; import { PercentageUtils } from "../../libs/PercentageUtils.sol"; import { PriceUtils } from "../../libs/PriceUtils.sol"; - -import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import { IOracleAdapter } from "../../interfaces/common/IOracleAdapter.sol"; import { USD } from "../../interfaces/common/IOracleAdapter.sol"; +import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; +import { Address } from "@openzeppelin/contracts/utils/Address.sol"; + /** * @title ChargeFee Action contract * @notice Action used to charge the AUM fee for a position. It will validate the data passed from the executor that @@ -24,6 +25,7 @@ import { USD } from "../../interfaces/common/IOracleAdapter.sol"; contract ChargeFee is Executable, UseStore { using ECDSA for bytes32; using PercentageUtils for uint256; + using Address for address payable; error InvalidSigner(address expectedSigner, address recoveredSigner); @@ -53,7 +55,7 @@ contract ChargeFee is Executable, UseStore { function _verifySignature(ChargeFeeData memory chargeFeeData) internal view { address expectedBackendSigner = registry.getRegisteredService(BACKEND_MSG_SIGNER); address recoveredBackendSigner = keccak256( - abi.encode( + abi.encodePacked( chargeFeeData.feeAmount, chargeFeeData.maxFeePercentage, chargeFeeData.collateralAmount, @@ -105,8 +107,9 @@ contract ChargeFee is Executable, UseStore { function _calculateFeeAmount( ChargeFeeData memory chargeFeeData, uint256 netWorthInCollateral - ) internal pure returns (uint256) { + ) internal view returns (uint256) { uint256 maxFeeAmount = netWorthInCollateral.applyPercentage(chargeFeeData.maxFeePercentage); + if (chargeFeeData.feeAmount > maxFeeAmount) { return maxFeeAmount; } @@ -119,6 +122,7 @@ contract ChargeFee is Executable, UseStore { address payable feeRecipient = payable( registry.getRegisteredService("FeeRecipientJustForTest") ); - feeRecipient.call{ value: feeAmount }(""); + + feeRecipient.sendValue(feeAmount); } } diff --git a/packages/dma-contracts/contracts/interfaces/common/IOracleAdapter.sol b/packages/dma-contracts/contracts/interfaces/common/IOracleAdapter.sol index 9cc5e9474..aa6c4dc54 100644 --- a/packages/dma-contracts/contracts/interfaces/common/IOracleAdapter.sol +++ b/packages/dma-contracts/contracts/interfaces/common/IOracleAdapter.sol @@ -26,7 +26,7 @@ interface IOracleAdapter { function getLatestPrice( address token, address baseToken - ) external view returns (int256 price, uint8 decimals); + ) external view returns (int256 latestAnswer, uint8 decimals); /// ERRORS error OracleNotFound(address token, address baseToken); diff --git a/packages/dma-contracts/test/unit/actions/common/charge-fee-mocked.test.ts b/packages/dma-contracts/test/unit/actions/common/charge-fee-mocked.test.ts index cbafd199d..7c515f048 100644 --- a/packages/dma-contracts/test/unit/actions/common/charge-fee-mocked.test.ts +++ b/packages/dma-contracts/test/unit/actions/common/charge-fee-mocked.test.ts @@ -4,6 +4,7 @@ import { createDeploy } from '@dma-common/utils/deploy' import init from '@dma-common/utils/init' import { calldataTypes } from '@dma-library' import { toSolidityPercentage } from '@dma-library/utils/percentages/percentage-utils' +import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' import { BigNumber, utils } from 'ethers' import { ethers } from 'hardhat' @@ -14,9 +15,8 @@ import { IServiceRegistry, IServiceRegistry__factory, } from '../../../../typechain' -import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' -describe.only('ChargeFee Action | Unit', () => { +describe('ChargeFee Action | Unit', () => { const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000' const USD_ADDRESS = ZERO_ADDRESS const BACKEND_MSG_SIGNER = 'BackendMsgSigner' @@ -28,10 +28,10 @@ describe.only('ChargeFee Action | Unit', () => { const DEBT_ASSET_PRICE = BigNumber.from('200000000') const ORACLE_ADAPTER = 'OracleAdapter' const FEE_RECIPIENT_JUST_FOR_TEST = 'FeeRecipientJustForTest' - const FEE_RECIPIENT_JUST_FOR_TEST_ADDRESS = '0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599' let backendSigner: SignerWithAddress let backendSignerAddress: string + let feeRecipientAddress: string let chargeFee: ChargeFee let serviceRegistry: FakeContract let oracleAdapter: FakeContract @@ -89,6 +89,7 @@ describe.only('ChargeFee Action | Unit', () => { // BACKEND SIGNER backendSigner = (await ethers.getSigners())[0] backendSignerAddress = await backendSigner.getAddress() + feeRecipientAddress = await (await ethers.getSigners())[1].getAddress() // REGISTRY serviceRegistry = await smock.fake(IServiceRegistry__factory.abi) @@ -97,39 +98,71 @@ describe.only('ChargeFee Action | Unit', () => { .returns(backendSignerAddress) serviceRegistry.getRegisteredService .whenCalledWith(FEE_RECIPIENT_JUST_FOR_TEST) - .returns(FEE_RECIPIENT_JUST_FOR_TEST_ADDRESS) + .returns(feeRecipientAddress) // ORACLES oracleAdapter = await smock.fake(IOracleAdapter__factory.abi) oracleAdapter.getLatestPrice.whenCalledWith(COLLATERAL_ASSET_ADDRESS, USD_ADDRESS).returns({ latestAnswer: COLLATERAL_ASSET_PRICE, - decimals: COLLATERAL_ASSET_DECIMALS, + decimals: BigNumber.from(COLLATERAL_ASSET_DECIMALS), }) oracleAdapter.getLatestPrice.whenCalledWith(DEBT_ASSET_ADDRESS, USD_ADDRESS).returns({ latestAnswer: DEBT_ASSET_PRICE, - decimals: DEBT_ASSET_DECIMALS, + decimals: BigNumber.from(DEBT_ASSET_DECIMALS), }) serviceRegistry.getRegisteredService .whenCalledWith(ORACLE_ADAPTER) .returns(oracleAdapter.address) + // CHARGE FEE ACTION const [deployedChargeFee] = await deploy('ChargeFee', [serviceRegistry.address]) chargeFee = deployedChargeFee as ChargeFee }) - it('should send correct fee to recipient', async () => { + it('should charge less than suggested fee', async () => { const chargeFeeCalldata = await getChargeFeeCalldata( BigNumber.from('1000000000000000000'), toSolidityPercentage(1.0), BigNumber.from('100000000000000000000'), COLLATERAL_ASSET_ADDRESS, COLLATERAL_ASSET_DECIMALS, - BigNumber.from('20000000000000000000'), + BigNumber.from('2000000000'), DEBT_ASSET_ADDRESS, DEBT_ASSET_DECIMALS, ) - await chargeFee.execute(chargeFeeCalldata, []) + const feeRecipientBalanceBefore = await ethers.provider.getBalance(feeRecipientAddress) + + await chargeFee.execute(chargeFeeCalldata, [], { value: ethers.utils.parseEther('10') }) + + const feeRecipientBalanceAfter = await ethers.provider.getBalance(feeRecipientAddress) + + expect(feeRecipientBalanceAfter.sub(feeRecipientBalanceBefore)).to.be.equal( + BigNumber.from('999781897491821155'), + ) + }) + + it('should charge the suggested fee', async () => { + const chargeFeeCalldata = await getChargeFeeCalldata( + BigNumber.from('800000000000000000'), + toSolidityPercentage(1.0), + BigNumber.from('100000000000000000000'), + COLLATERAL_ASSET_ADDRESS, + COLLATERAL_ASSET_DECIMALS, + BigNumber.from('2000000000'), + DEBT_ASSET_ADDRESS, + DEBT_ASSET_DECIMALS, + ) + + const feeRecipientBalanceBefore = await ethers.provider.getBalance(feeRecipientAddress) + + await chargeFee.execute(chargeFeeCalldata, [], { value: ethers.utils.parseEther('10') }) + + const feeRecipientBalanceAfter = await ethers.provider.getBalance(feeRecipientAddress) + + expect(feeRecipientBalanceAfter.sub(feeRecipientBalanceBefore)).to.be.equal( + BigNumber.from('800000000000000000'), + ) }) })