From 77f06365e626fa731376f4c2d67d0cbcfa47eda1 Mon Sep 17 00:00:00 2001 From: Piotr Witek <739075+piotrwitek@users.noreply.github.com> Date: Tue, 15 Oct 2024 23:17:26 +0200 Subject: [PATCH] Extended swap contract with the new behavior to be able to take fee in a fixed amount. --- .../dma-contracts/contracts/swap/Swap.sol | 37 +++++++++++++++++-- packages/dma-contracts/test/config.ts | 2 +- .../unit/actions/common/swap/asset-for-dai.ts | 7 ++++ .../swap/dai-for-asset-wrong-call-params.ts | 4 ++ .../unit/actions/common/swap/dai-for-asset.ts | 7 ++++ .../swap/dai-for-non-compliant-erc20.ts | 2 + .../actions/common/swap/erc20-for-erc20.ts | 2 + .../swap/non-compliant-erc20-for-dai.ts | 2 + .../unit/actions/common/swap/swap.test.ts | 3 ++ packages/dma-library/src/actions/common.ts | 4 +- packages/dma-library/src/types/index.ts | 1 + .../dma-library/src/types/swap-fee-type.ts | 4 ++ 12 files changed, 69 insertions(+), 6 deletions(-) create mode 100644 packages/dma-library/src/types/swap-fee-type.ts diff --git a/packages/dma-contracts/contracts/swap/Swap.sol b/packages/dma-contracts/contracts/swap/Swap.sol index da5254d8b..ca31bcc33 100644 --- a/packages/dma-contracts/contracts/swap/Swap.sol +++ b/packages/dma-contracts/contracts/swap/Swap.sol @@ -6,7 +6,7 @@ import { IERC20 } from "../interfaces/tokens/IERC20.sol"; import { SafeMath } from "../libs/SafeMath.sol"; import { SafeERC20 } from "../libs/SafeERC20.sol"; import { ONE_INCH_AGGREGATOR } from "../core/constants/Common.sol"; -import { SwapData } from "../core/types/Common.sol"; +import { SwapData, FeeType } from "../core/types/Common.sol"; contract Swap { using SafeMath for uint256; @@ -23,6 +23,7 @@ contract Swap { error FeeTierDoesNotExist(uint256 fee); error FeeTierAlreadyExists(uint256 fee); error SwapFailed(); + error FeeTypeDoesNotExist(); constructor( address authorisedCaller, @@ -107,7 +108,7 @@ contract Swap { emit AssetSwap(fromAsset, toAsset, amount, balance); } - function _collectFee( + function _collectPercentageFee( address asset, uint256 fromAmount, uint256 fee @@ -127,13 +128,41 @@ contract Swap { amount = fromAmount.sub(feeToTransfer); } + function _collectFixedFee( + address asset, + uint256 fromAmount, + uint256 fee + ) internal returns (uint256 amount) { + amount = fromAmount.sub(fee); + + if (fee > 0) { + IERC20(asset).safeTransfer(feeBeneficiaryAddress, fee); + emit FeePaid(feeBeneficiaryAddress, fee, asset); + } + } + + function _collectFee( + address asset, + uint256 fromAmount, + uint256 fee, + FeeType feeType + ) internal returns (uint256 amount) { + if (feeType == FeeType.Percentage) { + amount = _collectPercentageFee(asset, fromAmount, fee); + } else if (feeType == FeeType.Fixed) { + amount = _collectFixedFee(asset, fromAmount, fee); + } else { + revert FeeTypeDoesNotExist(); + } + } + function swapTokens(SwapData calldata swapData) public returns (uint256) { IERC20(swapData.fromAsset).safeTransferFrom(msg.sender, address(this), swapData.amount); uint256 amountFrom = swapData.amount; if (swapData.collectFeeInFromToken) { - amountFrom = _collectFee(swapData.fromAsset, swapData.amount, swapData.fee); + amountFrom = _collectFee(swapData.fromAsset, swapData.amount, swapData.fee, swapData.feeType); } address oneInch = registry.getRegisteredService(ONE_INCH_AGGREGATOR); @@ -148,7 +177,7 @@ contract Swap { ); if (!swapData.collectFeeInFromToken) { - toTokenBalance = _collectFee(swapData.toAsset, toTokenBalance, swapData.fee); + toTokenBalance = _collectFee(swapData.toAsset, toTokenBalance, swapData.fee, swapData.feeType); } uint256 fromTokenBalance = IERC20(swapData.fromAsset).balanceOf(address(this)); diff --git a/packages/dma-contracts/test/config.ts b/packages/dma-contracts/test/config.ts index 1a2c8a165..faff38c1c 100644 --- a/packages/dma-contracts/test/config.ts +++ b/packages/dma-contracts/test/config.ts @@ -1,7 +1,7 @@ // Do not change test block numbers as they're linked to uniswap liquidity levels import { Network } from '@deploy-configurations/types/network' -export const testBlockNumber = 15695000 +export const testBlockNumber = 20971320 export const testBlockNumberForAaveV3 = 16597880 export const testBlockNumberForMigrations = 19324700 diff --git a/packages/dma-contracts/test/unit/actions/common/swap/asset-for-dai.ts b/packages/dma-contracts/test/unit/actions/common/swap/asset-for-dai.ts index c8c151554..d75292490 100644 --- a/packages/dma-contracts/test/unit/actions/common/swap/asset-for-dai.ts +++ b/packages/dma-contracts/test/unit/actions/common/swap/asset-for-dai.ts @@ -9,6 +9,7 @@ import { amountFromWei, amountToWei } from '@dma-common/utils/common' import { calculateFeeOnInputAmount } from '@dma-common/utils/swap' import { testBlockNumber } from '@dma-contracts/test/config' import { restoreSnapshot, TestHelpers } from '@dma-contracts/utils' +import { SwapFeeType } from '@dma-library/types' import { Contract } from '@ethersproject/contracts' import { JsonRpcProvider } from '@ethersproject/providers' import { MockExchange } from '@typechain' @@ -119,6 +120,7 @@ describe('Swap | Unit', async () => { FEE, data, true, + SwapFeeType.Percentage, ], { value: 0, @@ -204,6 +206,7 @@ describe('Swap | Unit', async () => { FEE, data, false, + SwapFeeType.Percentage, ], { value: 0, @@ -300,6 +303,7 @@ describe('Swap | Unit', async () => { FEE, data, true, + SwapFeeType.Percentage, ], { value: 0, @@ -404,6 +408,7 @@ describe('Swap | Unit', async () => { FEE, data, true, + SwapFeeType.Percentage, ], { value: 0, @@ -494,6 +499,7 @@ describe('Swap | Unit', async () => { FEE, data, true, + SwapFeeType.Percentage, ], { value: 0, @@ -547,6 +553,7 @@ describe('Swap | Unit', async () => { FEE, data, true, + SwapFeeType.Percentage, ], { value: 0, diff --git a/packages/dma-contracts/test/unit/actions/common/swap/dai-for-asset-wrong-call-params.ts b/packages/dma-contracts/test/unit/actions/common/swap/dai-for-asset-wrong-call-params.ts index 04e9ade28..d95ea212e 100644 --- a/packages/dma-contracts/test/unit/actions/common/swap/dai-for-asset-wrong-call-params.ts +++ b/packages/dma-contracts/test/unit/actions/common/swap/dai-for-asset-wrong-call-params.ts @@ -6,6 +6,7 @@ import { balanceOf } from '@dma-common/utils/balances' import { amountToWei } from '@dma-common/utils/common' import { testBlockNumber } from '@dma-contracts/test/config' import { restoreSnapshot, TestHelpers } from '@dma-contracts/utils' +import { SwapFeeType } from '@dma-library/types' import { Contract } from '@ethersproject/contracts' import { MockExchange } from '@typechain' import BigNumber from 'bignumber.js' @@ -98,6 +99,7 @@ describe('Swap | Unit', async () => { FEE, data, true, + SwapFeeType.Percentage, ], { value: 0, @@ -123,6 +125,7 @@ describe('Swap | Unit', async () => { FEE, data, true, + SwapFeeType.Percentage, ], { value: 0, @@ -154,6 +157,7 @@ describe('Swap | Unit', async () => { FEE, response.tx.data, true, + SwapFeeType.Percentage, ]) const expectedRevert = /ReceivedLess\(100000000000000000000000, \d+\)/ diff --git a/packages/dma-contracts/test/unit/actions/common/swap/dai-for-asset.ts b/packages/dma-contracts/test/unit/actions/common/swap/dai-for-asset.ts index 6b5c6ac83..9e2504ede 100644 --- a/packages/dma-contracts/test/unit/actions/common/swap/dai-for-asset.ts +++ b/packages/dma-contracts/test/unit/actions/common/swap/dai-for-asset.ts @@ -9,6 +9,7 @@ import { amountFromWei, amountToWei } from '@dma-common/utils/common' import { calculateFeeOnInputAmount } from '@dma-common/utils/swap' import { testBlockNumber } from '@dma-contracts/test/config' import { restoreSnapshot, TestHelpers } from '@dma-contracts/utils' +import { SwapFeeType } from '@dma-library/types' import { Contract } from '@ethersproject/contracts' import { JsonRpcProvider } from '@ethersproject/providers' import { MockExchange } from '@typechain' @@ -117,6 +118,7 @@ describe('Swap | Unit', async () => { FEE, data, true, + SwapFeeType.Percentage, ]) }) @@ -208,6 +210,7 @@ describe('Swap | Unit', async () => { FEE, data, true, + SwapFeeType.Percentage, ], { value: 0, @@ -325,6 +328,7 @@ describe('Swap | Unit', async () => { FEE, data, true, + SwapFeeType.Percentage, ], { value: 0, @@ -399,6 +403,7 @@ describe('Swap | Unit', async () => { FEE, data, true, + SwapFeeType.Percentage, ], { value: 0, @@ -438,6 +443,7 @@ describe('Swap | Unit', async () => { FEE, data, true, + SwapFeeType.Percentage, ], { value: 0, @@ -503,6 +509,7 @@ describe('Swap | Unit', async () => { FEE, data, true, + SwapFeeType.Percentage, ], { value: 0, diff --git a/packages/dma-contracts/test/unit/actions/common/swap/dai-for-non-compliant-erc20.ts b/packages/dma-contracts/test/unit/actions/common/swap/dai-for-non-compliant-erc20.ts index 14198fdfd..ede5b331f 100644 --- a/packages/dma-contracts/test/unit/actions/common/swap/dai-for-non-compliant-erc20.ts +++ b/packages/dma-contracts/test/unit/actions/common/swap/dai-for-non-compliant-erc20.ts @@ -7,6 +7,7 @@ import { amountFromWei, amountToWei } from '@dma-common/utils/common' import { calculateFeeOnInputAmount } from '@dma-common/utils/swap' import { testBlockNumber } from '@dma-contracts/test/config' import { restoreSnapshot, TestHelpers } from '@dma-contracts/utils' +import { SwapFeeType } from '@dma-library/types' import { Contract } from '@ethersproject/contracts' import { MockExchange } from '@typechain' import BigNumber from 'bignumber.js' @@ -106,6 +107,7 @@ describe('Swap | Unit', async () => { FEE, data, true, + SwapFeeType.Percentage, ], { value: 0, diff --git a/packages/dma-contracts/test/unit/actions/common/swap/erc20-for-erc20.ts b/packages/dma-contracts/test/unit/actions/common/swap/erc20-for-erc20.ts index 54a0b879a..65e27377c 100644 --- a/packages/dma-contracts/test/unit/actions/common/swap/erc20-for-erc20.ts +++ b/packages/dma-contracts/test/unit/actions/common/swap/erc20-for-erc20.ts @@ -9,6 +9,7 @@ import { amountFromWei, amountToWei } from '@dma-common/utils/common' import { calculateFeeOnInputAmount } from '@dma-common/utils/swap' import { testBlockNumber } from '@dma-contracts/test/config' import { restoreSnapshot, TestHelpers } from '@dma-contracts/utils' +import { SwapFeeType } from '@dma-library/types' import { Contract } from '@ethersproject/contracts' import { MockExchange } from '@typechain' import BigNumber from 'bignumber.js' @@ -111,6 +112,7 @@ describe('Swap | Unit', async () => { FEE, data, true, + SwapFeeType.Percentage, ], { value: 0, diff --git a/packages/dma-contracts/test/unit/actions/common/swap/non-compliant-erc20-for-dai.ts b/packages/dma-contracts/test/unit/actions/common/swap/non-compliant-erc20-for-dai.ts index 7c572b50d..f1f96c600 100644 --- a/packages/dma-contracts/test/unit/actions/common/swap/non-compliant-erc20-for-dai.ts +++ b/packages/dma-contracts/test/unit/actions/common/swap/non-compliant-erc20-for-dai.ts @@ -7,6 +7,7 @@ import { amountFromWei, amountToWei } from '@dma-common/utils/common' import { calculateFee } from '@dma-common/utils/swap' import { testBlockNumber } from '@dma-contracts/test/config' import { restoreSnapshot, TestHelpers } from '@dma-contracts/utils' +import { SwapFeeType } from '@dma-library/types' import { MockExchange } from '@typechain' import BigNumber from 'bignumber.js' import { Contract, Signer } from 'ethers' @@ -107,6 +108,7 @@ describe('Swap | Unit', async () => { FEE, data, true, + SwapFeeType.Percentage, ], { value: 0, diff --git a/packages/dma-contracts/test/unit/actions/common/swap/swap.test.ts b/packages/dma-contracts/test/unit/actions/common/swap/swap.test.ts index 83889834a..4db6711e4 100644 --- a/packages/dma-contracts/test/unit/actions/common/swap/swap.test.ts +++ b/packages/dma-contracts/test/unit/actions/common/swap/swap.test.ts @@ -9,6 +9,7 @@ import { amountToWei } from '@dma-common/utils/common' import { calculateFeeOnInputAmount } from '@dma-common/utils/swap' import { testBlockNumber } from '@dma-contracts/test/config' import { restoreSnapshot, TestHelpers } from '@dma-contracts/utils' +import { SwapFeeType } from '@dma-library/types' import { Contract } from '@ethersproject/contracts' import { JsonRpcProvider } from '@ethersproject/providers' import { MockExchange } from '@typechain' @@ -168,6 +169,7 @@ describe('Swap | Unit', async () => { fee, response.tx.data, true, + SwapFeeType.Percentage, ], { value: 0, @@ -215,6 +217,7 @@ describe('Swap | Unit', async () => { fee, response.tx.data, true, + SwapFeeType.Percentage, ], { value: 0, diff --git a/packages/dma-library/src/actions/common.ts b/packages/dma-library/src/actions/common.ts index a7e4110cf..4eba5227f 100644 --- a/packages/dma-library/src/actions/common.ts +++ b/packages/dma-library/src/actions/common.ts @@ -1,7 +1,7 @@ import { loadContractNames } from '@deploy-configurations/constants' import { Network } from '@deploy-configurations/types/network' import { getActionHash } from '@deploy-configurations/utils/action-hash' -import { ActionCall, calldataTypes } from '@dma-library/types' +import { ActionCall, calldataTypes, SwapFeeType } from '@dma-library/types' import BigNumber from 'bignumber.js' import { ActionFactory } from './action-factory' @@ -80,6 +80,7 @@ export function swap( fee: number withData: string | number collectFeeInFromToken: boolean + feeType: SwapFeeType }, ) { const SERVICE_REGISTRY_NAMES = loadContractNames(network) @@ -96,6 +97,7 @@ export function swap( fee: args.fee, withData: args.withData, collectFeeInFromToken: args.collectFeeInFromToken, + feeType: args.feeType ?? SwapFeeType.Percentage, }, ], ) diff --git a/packages/dma-library/src/types/index.ts b/packages/dma-library/src/types/index.ts index 91703d533..b27d528a2 100644 --- a/packages/dma-library/src/types/index.ts +++ b/packages/dma-library/src/types/index.ts @@ -118,6 +118,7 @@ export type { WithWithdrawCollateral, } export type { SwapData } +export { SwapFeeType } from './swap-fee-type' export type { Swap } export { MorphoBluePosition } diff --git a/packages/dma-library/src/types/swap-fee-type.ts b/packages/dma-library/src/types/swap-fee-type.ts new file mode 100644 index 000000000..fa0fd17fe --- /dev/null +++ b/packages/dma-library/src/types/swap-fee-type.ts @@ -0,0 +1,4 @@ +export enum SwapFeeType { + Percentage = 0, + Fixed = 1, +}