From d8b89b21d77874530325efdc4932ead4b369e7f7 Mon Sep 17 00:00:00 2001 From: codeislight Date: Sun, 23 Jul 2023 20:15:31 +0800 Subject: [PATCH 1/5] Add: Calldata library --- .../protocol/libraries/types/Calldata.sol | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 contracts/protocol/libraries/types/Calldata.sol diff --git a/contracts/protocol/libraries/types/Calldata.sol b/contracts/protocol/libraries/types/Calldata.sol new file mode 100644 index 000000000..838d1c945 --- /dev/null +++ b/contracts/protocol/libraries/types/Calldata.sol @@ -0,0 +1,39 @@ +pragma solidity ^0.8.0; + +library Calldata { + function getAddress(bytes calldata self, bytes1 pos) internal pure returns (address output) { + assembly { + output := calldataload(add(self.offset, pos)) + } + } + + function getUint256(bytes calldata self, bytes1 pos) internal pure returns (uint output) { + assembly { + output := calldataload(add(self.offset, pos)) + } + } + + function getUint16(bytes calldata self, bytes1 pos) internal pure returns (uint16 output) { + assembly { + output := calldataload(add(self.offset, pos)) + } + } + + function getUint8(bytes calldata self, bytes1 pos) internal pure returns (uint8 output) { + assembly { + output := calldataload(add(self.offset, pos)) + } + } + + function getBool(bytes calldata self, bytes1 pos) internal pure returns (bool output) { + assembly { + output := calldataload(add(self.offset, pos)) + } + } + + function getBytes32(bytes calldata self, bytes1 pos) internal pure returns (bytes32 output) { + assembly { + output := calldataload(add(self.offset, pos)) + } + } +} From b4d0e4c9a8023123de5d85be1586737996c581c2 Mon Sep 17 00:00:00 2001 From: codeislight Date: Sun, 23 Jul 2023 20:17:05 +0800 Subject: [PATCH 2/5] feat: Add MultiCall to the interface --- contracts/interfaces/IPool.sol | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/contracts/interfaces/IPool.sol b/contracts/interfaces/IPool.sol index ef306e737..eb69be579 100644 --- a/contracts/interfaces/IPool.sol +++ b/contracts/interfaces/IPool.sol @@ -218,6 +218,21 @@ interface IPool { * @param referralCode Code used to register the integrator originating the operation, for potential rewards. * 0 if the action is executed directly by the user, without any middle-man */ + + enum MultiCallAction { + Supply, + Borrow, + Repay, + Withdraw, + SetUserUseReserveAsCollateral, + SwapBorrowRateMode, + RebalanceStableBorrowRate, + SupplyWithPermit, + RepayWithPermit, + RepayWithATokens, + LiquidationCall + } + function mintUnbacked( address asset, uint256 amount, @@ -464,6 +479,14 @@ interface IPool { uint16 referralCode ) external; + /** + * @notice Allows to batch call any user relevant functions in a single transaction, + * any revert in any of the actions being executed, will result in a revert of the batch + * @param actions includes the set of MultiCallAction that a user is able to execute + * @param params Array of encoded MultiCallAction parameters + */ + function multiCall(bytes calldata actions, bytes[] calldata params) external; + /** * @notice Returns the user account data across all the reserves * @param user The address of the user From b06df6f8bf3ff83f295f2ba180f8c8dd693ac307 Mon Sep 17 00:00:00 2001 From: codeislight Date: Sun, 23 Jul 2023 20:17:26 +0800 Subject: [PATCH 3/5] feat: Implement MutliCall --- contracts/protocol/pool/Pool.sol | 78 ++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/contracts/protocol/pool/Pool.sol b/contracts/protocol/pool/Pool.sol index e41c45110..dd36253c5 100644 --- a/contracts/protocol/pool/Pool.sol +++ b/contracts/protocol/pool/Pool.sol @@ -12,6 +12,7 @@ import {FlashLoanLogic} from '../libraries/logic/FlashLoanLogic.sol'; import {BorrowLogic} from '../libraries/logic/BorrowLogic.sol'; import {LiquidationLogic} from '../libraries/logic/LiquidationLogic.sol'; import {DataTypes} from '../libraries/types/DataTypes.sol'; +import {Calldata} from '../libraries/types/Calldata.sol'; import {BridgeLogic} from '../libraries/logic/BridgeLogic.sol'; import {IERC20WithPermit} from '../../interfaces/IERC20WithPermit.sol'; import {IPoolAddressesProvider} from '../../interfaces/IPoolAddressesProvider.sol'; @@ -38,6 +39,7 @@ import {PoolStorage} from './PoolStorage.sol'; */ contract Pool is VersionedInitializable, PoolStorage, IPool { using ReserveLogic for DataTypes.ReserveData; + using Calldata for bytes; uint256 public constant POOL_REVISION = 0x1; IPoolAddressesProvider public immutable ADDRESSES_PROVIDER; @@ -440,6 +442,82 @@ contract Pool is VersionedInitializable, PoolStorage, IPool { FlashLoanLogic.executeFlashLoanSimple(_reserves[asset], flashParams); } + /// @inheritdoc IPool + function multiCall(bytes calldata actions, bytes[] calldata params) public override { + uint len = actions.length; + require(len == params.length, Errors.INCONSISTENT_PARAMS_LENGTH); + for (uint i; i < len; ) { + MultiCallAction action = MultiCallAction(uint8(actions[i])); + bytes calldata param = params[i]; + if (action == MultiCallAction.Supply) { + supply( + param.getAddress(0x00), + param.getUint256(0x20), + param.getAddress(0x40), + param.getUint16(0x60) + ); + } else if (action == MultiCallAction.Borrow) { + borrow( + param.getAddress(0x00), + param.getUint256(0x20), + param.getUint256(0x40), + param.getUint16(0x60), + param.getAddress(0x80) + ); + } else if (action == MultiCallAction.Repay) { + repay( + param.getAddress(0x00), + param.getUint256(0x20), + param.getUint256(0x40), + param.getAddress(0x60) + ); + } else if (action == MultiCallAction.Withdraw) { + withdraw(param.getAddress(0x00), param.getUint256(0x20), param.getAddress(0x40)); + } else if (action == MultiCallAction.SetUserUseReserveAsCollateral) { + setUserUseReserveAsCollateral(param.getAddress(0x00), param.getBool(0x20)); + } else if (action == MultiCallAction.SwapBorrowRateMode) { + swapBorrowRateMode(param.getAddress(0x00), param.getUint256(0x20)); + } else if (action == MultiCallAction.RebalanceStableBorrowRate) { + rebalanceStableBorrowRate(param.getAddress(0x00), param.getAddress(0x20)); + } else if (action == MultiCallAction.SupplyWithPermit) { + supplyWithPermit( + param.getAddress(0x00), + param.getUint256(0x20), + param.getAddress(0x40), + param.getUint16(0x60), + param.getUint256(0x80), + param.getUint8(0xA0), + param.getBytes32(0xC0), + param.getBytes32(0xE0) + ); + } else if (action == MultiCallAction.RepayWithPermit) { + repayWithPermit( + param.getAddress(0x00), + param.getUint256(0x20), + param.getUint256(0x40), + param.getAddress(0x60), + param.getUint256(0x80), + param.getUint8(0xA0), + param.getBytes32(0xC0), + param.getBytes32(0xE0) + ); + } else if (action == MultiCallAction.RepayWithATokens) { + repayWithATokens(param.getAddress(0x00), param.getUint256(0x20), param.getUint256(0x40)); + } else if (action == MultiCallAction.LiquidationCall) { + liquidationCall( + param.getAddress(0x00), + param.getAddress(0x20), + param.getAddress(0x40), + param.getUint256(0x60), + param.getBool(0x80) + ); + } + unchecked { + i++; + } + } + } + /// @inheritdoc IPool function mintToTreasury(address[] calldata assets) external virtual override { PoolLogic.executeMintToTreasury(_reserves, assets); From 5144a9743c391f4f8e9d663583114dc8bcbec0eb Mon Sep 17 00:00:00 2001 From: codeislight Date: Mon, 24 Jul 2023 21:09:51 +0800 Subject: [PATCH 4/5] fix: save on ADD opcode --- .../protocol/libraries/types/Calldata.sol | 30 +++++++++++-------- contracts/protocol/pool/Pool.sol | 22 +++++++------- 2 files changed, 29 insertions(+), 23 deletions(-) diff --git a/contracts/protocol/libraries/types/Calldata.sol b/contracts/protocol/libraries/types/Calldata.sol index 838d1c945..e03f751c0 100644 --- a/contracts/protocol/libraries/types/Calldata.sol +++ b/contracts/protocol/libraries/types/Calldata.sol @@ -1,39 +1,45 @@ pragma solidity ^0.8.0; library Calldata { - function getAddress(bytes calldata self, bytes1 pos) internal pure returns (address output) { + function getAddress(bytes calldata self) internal pure returns (address output) { assembly { - output := calldataload(add(self.offset, pos)) + output := calldataload(self.offset) } } - function getUint256(bytes calldata self, bytes1 pos) internal pure returns (uint output) { + function getAddress(bytes calldata self, bytes1 offset) internal pure returns (address output) { assembly { - output := calldataload(add(self.offset, pos)) + output := calldataload(add(self.offset, offset)) } } - function getUint16(bytes calldata self, bytes1 pos) internal pure returns (uint16 output) { + function getUint256(bytes calldata self, bytes1 offset) internal pure returns (uint output) { assembly { - output := calldataload(add(self.offset, pos)) + output := calldataload(add(self.offset, offset)) } } - function getUint8(bytes calldata self, bytes1 pos) internal pure returns (uint8 output) { + function getUint16(bytes calldata self, bytes1 offset) internal pure returns (uint16 output) { assembly { - output := calldataload(add(self.offset, pos)) + output := calldataload(add(self.offset, offset)) } } - function getBool(bytes calldata self, bytes1 pos) internal pure returns (bool output) { + function getUint8(bytes calldata self, bytes1 offset) internal pure returns (uint8 output) { assembly { - output := calldataload(add(self.offset, pos)) + output := calldataload(add(self.offset, offset)) } } - function getBytes32(bytes calldata self, bytes1 pos) internal pure returns (bytes32 output) { + function getBool(bytes calldata self, bytes1 offset) internal pure returns (bool output) { assembly { - output := calldataload(add(self.offset, pos)) + output := calldataload(add(self.offset, offset)) + } + } + + function getBytes32(bytes calldata self, bytes1 offset) internal pure returns (bytes32 output) { + assembly { + output := calldataload(add(self.offset, offset)) } } } diff --git a/contracts/protocol/pool/Pool.sol b/contracts/protocol/pool/Pool.sol index dd36253c5..9e884986c 100644 --- a/contracts/protocol/pool/Pool.sol +++ b/contracts/protocol/pool/Pool.sol @@ -451,14 +451,14 @@ contract Pool is VersionedInitializable, PoolStorage, IPool { bytes calldata param = params[i]; if (action == MultiCallAction.Supply) { supply( - param.getAddress(0x00), + param.getAddress(), param.getUint256(0x20), param.getAddress(0x40), param.getUint16(0x60) ); } else if (action == MultiCallAction.Borrow) { borrow( - param.getAddress(0x00), + param.getAddress(), param.getUint256(0x20), param.getUint256(0x40), param.getUint16(0x60), @@ -466,22 +466,22 @@ contract Pool is VersionedInitializable, PoolStorage, IPool { ); } else if (action == MultiCallAction.Repay) { repay( - param.getAddress(0x00), + param.getAddress(), param.getUint256(0x20), param.getUint256(0x40), param.getAddress(0x60) ); } else if (action == MultiCallAction.Withdraw) { - withdraw(param.getAddress(0x00), param.getUint256(0x20), param.getAddress(0x40)); + withdraw(param.getAddress(), param.getUint256(0x20), param.getAddress(0x40)); } else if (action == MultiCallAction.SetUserUseReserveAsCollateral) { - setUserUseReserveAsCollateral(param.getAddress(0x00), param.getBool(0x20)); + setUserUseReserveAsCollateral(param.getAddress(), param.getBool(0x20)); } else if (action == MultiCallAction.SwapBorrowRateMode) { - swapBorrowRateMode(param.getAddress(0x00), param.getUint256(0x20)); + swapBorrowRateMode(param.getAddress(), param.getUint256(0x20)); } else if (action == MultiCallAction.RebalanceStableBorrowRate) { - rebalanceStableBorrowRate(param.getAddress(0x00), param.getAddress(0x20)); + rebalanceStableBorrowRate(param.getAddress(), param.getAddress(0x20)); } else if (action == MultiCallAction.SupplyWithPermit) { supplyWithPermit( - param.getAddress(0x00), + param.getAddress(), param.getUint256(0x20), param.getAddress(0x40), param.getUint16(0x60), @@ -492,7 +492,7 @@ contract Pool is VersionedInitializable, PoolStorage, IPool { ); } else if (action == MultiCallAction.RepayWithPermit) { repayWithPermit( - param.getAddress(0x00), + param.getAddress(), param.getUint256(0x20), param.getUint256(0x40), param.getAddress(0x60), @@ -502,10 +502,10 @@ contract Pool is VersionedInitializable, PoolStorage, IPool { param.getBytes32(0xE0) ); } else if (action == MultiCallAction.RepayWithATokens) { - repayWithATokens(param.getAddress(0x00), param.getUint256(0x20), param.getUint256(0x40)); + repayWithATokens(param.getAddress(), param.getUint256(0x20), param.getUint256(0x40)); } else if (action == MultiCallAction.LiquidationCall) { liquidationCall( - param.getAddress(0x00), + param.getAddress(), param.getAddress(0x20), param.getAddress(0x40), param.getUint256(0x60), From 4c972049b98104a17f93776c409bd258e00d5268 Mon Sep 17 00:00:00 2001 From: codeislight Date: Tue, 25 Jul 2023 10:44:05 +0800 Subject: [PATCH 5/5] fix: bytes1 are right padded + better readability --- .../protocol/libraries/types/Calldata.sol | 24 +++---- contracts/protocol/pool/Pool.sol | 68 ++++++++----------- 2 files changed, 41 insertions(+), 51 deletions(-) diff --git a/contracts/protocol/libraries/types/Calldata.sol b/contracts/protocol/libraries/types/Calldata.sol index e03f751c0..b8e5f291d 100644 --- a/contracts/protocol/libraries/types/Calldata.sol +++ b/contracts/protocol/libraries/types/Calldata.sol @@ -7,39 +7,39 @@ library Calldata { } } - function getAddress(bytes calldata self, bytes1 offset) internal pure returns (address output) { + function getAddress(bytes calldata self, uint8 index) internal pure returns (address output) { assembly { - output := calldataload(add(self.offset, offset)) + output := calldataload(add(self.offset, mul(index, 0x20))) } } - function getUint256(bytes calldata self, bytes1 offset) internal pure returns (uint output) { + function getUint256(bytes calldata self, uint8 index) internal pure returns (uint output) { assembly { - output := calldataload(add(self.offset, offset)) + output := calldataload(add(self.offset, mul(index, 0x20))) } } - function getUint16(bytes calldata self, bytes1 offset) internal pure returns (uint16 output) { + function getUint16(bytes calldata self, uint8 index) internal pure returns (uint16 output) { assembly { - output := calldataload(add(self.offset, offset)) + output := calldataload(add(self.offset, mul(index, 0x20))) } } - function getUint8(bytes calldata self, bytes1 offset) internal pure returns (uint8 output) { + function getUint8(bytes calldata self, uint8 index) internal pure returns (uint8 output) { assembly { - output := calldataload(add(self.offset, offset)) + output := calldataload(add(self.offset, mul(index, 0x20))) } } - function getBool(bytes calldata self, bytes1 offset) internal pure returns (bool output) { + function getBool(bytes calldata self, uint8 index) internal pure returns (bool output) { assembly { - output := calldataload(add(self.offset, offset)) + output := calldataload(add(self.offset, mul(index, 0x20))) } } - function getBytes32(bytes calldata self, bytes1 offset) internal pure returns (bytes32 output) { + function getBytes32(bytes calldata self, uint8 index) internal pure returns (bytes32 output) { assembly { - output := calldataload(add(self.offset, offset)) + output := calldataload(add(self.offset, mul(index, 0x20))) } } } diff --git a/contracts/protocol/pool/Pool.sol b/contracts/protocol/pool/Pool.sol index 9e884986c..09df55609 100644 --- a/contracts/protocol/pool/Pool.sol +++ b/contracts/protocol/pool/Pool.sol @@ -450,66 +450,56 @@ contract Pool is VersionedInitializable, PoolStorage, IPool { MultiCallAction action = MultiCallAction(uint8(actions[i])); bytes calldata param = params[i]; if (action == MultiCallAction.Supply) { - supply( - param.getAddress(), - param.getUint256(0x20), - param.getAddress(0x40), - param.getUint16(0x60) - ); + supply(param.getAddress(), param.getUint256(1), param.getAddress(2), param.getUint16(3)); } else if (action == MultiCallAction.Borrow) { borrow( param.getAddress(), - param.getUint256(0x20), - param.getUint256(0x40), - param.getUint16(0x60), - param.getAddress(0x80) + param.getUint256(1), + param.getUint256(2), + param.getUint16(3), + param.getAddress(4) ); } else if (action == MultiCallAction.Repay) { - repay( - param.getAddress(), - param.getUint256(0x20), - param.getUint256(0x40), - param.getAddress(0x60) - ); + repay(param.getAddress(), param.getUint256(1), param.getUint256(2), param.getAddress(3)); } else if (action == MultiCallAction.Withdraw) { - withdraw(param.getAddress(), param.getUint256(0x20), param.getAddress(0x40)); + withdraw(param.getAddress(), param.getUint256(1), param.getAddress(2)); } else if (action == MultiCallAction.SetUserUseReserveAsCollateral) { - setUserUseReserveAsCollateral(param.getAddress(), param.getBool(0x20)); + setUserUseReserveAsCollateral(param.getAddress(), param.getBool(1)); } else if (action == MultiCallAction.SwapBorrowRateMode) { - swapBorrowRateMode(param.getAddress(), param.getUint256(0x20)); + swapBorrowRateMode(param.getAddress(), param.getUint256(1)); } else if (action == MultiCallAction.RebalanceStableBorrowRate) { - rebalanceStableBorrowRate(param.getAddress(), param.getAddress(0x20)); + rebalanceStableBorrowRate(param.getAddress(), param.getAddress(1)); } else if (action == MultiCallAction.SupplyWithPermit) { supplyWithPermit( param.getAddress(), - param.getUint256(0x20), - param.getAddress(0x40), - param.getUint16(0x60), - param.getUint256(0x80), - param.getUint8(0xA0), - param.getBytes32(0xC0), - param.getBytes32(0xE0) + param.getUint256(1), + param.getAddress(2), + param.getUint16(3), + param.getUint256(4), + param.getUint8(5), + param.getBytes32(6), + param.getBytes32(7) ); } else if (action == MultiCallAction.RepayWithPermit) { repayWithPermit( param.getAddress(), - param.getUint256(0x20), - param.getUint256(0x40), - param.getAddress(0x60), - param.getUint256(0x80), - param.getUint8(0xA0), - param.getBytes32(0xC0), - param.getBytes32(0xE0) + param.getUint256(1), + param.getUint256(2), + param.getAddress(3), + param.getUint256(4), + param.getUint8(5), + param.getBytes32(6), + param.getBytes32(7) ); } else if (action == MultiCallAction.RepayWithATokens) { - repayWithATokens(param.getAddress(), param.getUint256(0x20), param.getUint256(0x40)); + repayWithATokens(param.getAddress(), param.getUint256(1), param.getUint256(2)); } else if (action == MultiCallAction.LiquidationCall) { liquidationCall( param.getAddress(), - param.getAddress(0x20), - param.getAddress(0x40), - param.getUint256(0x60), - param.getBool(0x80) + param.getAddress(1), + param.getAddress(2), + param.getUint256(3), + param.getBool(4) ); } unchecked {