Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/audit part6 #165

Merged
merged 4 commits into from
Sep 10, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .forge-snapshots/BinPoolManagerBytecodeSize.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
24241
24243
2 changes: 1 addition & 1 deletion .forge-snapshots/BinPoolManagerTest#testGasDonate.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
119705
119403
Original file line number Diff line number Diff line change
@@ -1 +1 @@
971865
971563
Original file line number Diff line number Diff line change
@@ -1 +1 @@
331176
330874
Original file line number Diff line number Diff line change
@@ -1 +1 @@
338777
338475
Original file line number Diff line number Diff line change
@@ -1 +1 @@
141327
141025
Original file line number Diff line number Diff line change
@@ -1 +1 @@
174101
173950
Original file line number Diff line number Diff line change
@@ -1 +1 @@
180130
179979
Original file line number Diff line number Diff line change
@@ -1 +1 @@
134132
133981
Original file line number Diff line number Diff line change
@@ -1 +1 @@
305605
305454
2 changes: 1 addition & 1 deletion .forge-snapshots/CLPoolManagerBytecodeSize.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
21178
21207
Original file line number Diff line number Diff line change
@@ -1 +1 @@
348525
348246
Original file line number Diff line number Diff line change
@@ -1 +1 @@
163996
163717
Original file line number Diff line number Diff line change
@@ -1 +1 @@
239021
239044
2 changes: 1 addition & 1 deletion .forge-snapshots/CLPoolManagerTest#donateBothTokens.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
164280
163978
2 changes: 1 addition & 1 deletion .forge-snapshots/CLPoolManagerTest#gasDonateOneToken.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
109095
108944
Original file line number Diff line number Diff line change
@@ -1 +1 @@
115495
115518
Original file line number Diff line number Diff line change
@@ -1 +1 @@
131890
131739
Original file line number Diff line number Diff line change
@@ -1 +1 @@
164446
164295
Original file line number Diff line number Diff line change
@@ -1 +1 @@
149907
149756
2 changes: 1 addition & 1 deletion .forge-snapshots/VaultBytecodeSize.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
7931
7891
2 changes: 1 addition & 1 deletion script/02_DeployCLPoolManager.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ contract DeployCLPoolManagerScript is BaseScript {
address vault = getAddressFromConfig("vault");
console.log("vault address: ", address(vault));

CLPoolManager clPoolManager = new CLPoolManager(IVault(address(vault)), 500000);
CLPoolManager clPoolManager = new CLPoolManager(IVault(address(vault)));
console.log("CLPoolManager contract deployed at ", address(clPoolManager));

console.log("Registering CLPoolManager");
Expand Down
2 changes: 1 addition & 1 deletion script/03_DeployBinPoolManager.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ contract DeployBinPoolManagerScript is BaseScript {
address vault = getAddressFromConfig("vault");
console.log("vault address: ", address(vault));

BinPoolManager binPoolManager = new BinPoolManager(IVault(address(vault)), 500000);
BinPoolManager binPoolManager = new BinPoolManager(IVault(address(vault)));
console.log("BinPoolManager contract deployed at ", address(binPoolManager));

console.log("Registering BinPoolManager");
Expand Down
15 changes: 9 additions & 6 deletions src/ProtocolFees.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ import {ProtocolFeeLibrary} from "./libraries/ProtocolFeeLibrary.sol";
import {PoolKey} from "./types/PoolKey.sol";
import {PoolId} from "./types/PoolId.sol";
import {IVault} from "./interfaces/IVault.sol";
import {BipsLibrary} from "./libraries/BipsLibrary.sol";

abstract contract ProtocolFees is IProtocolFees, Owner {
using ProtocolFeeLibrary for uint24;
using BipsLibrary for uint256;

/// @inheritdoc IProtocolFees
mapping(Currency currency => uint256 amount) public protocolFeesAccrued;
Expand All @@ -23,11 +25,12 @@ abstract contract ProtocolFees is IProtocolFees, Owner {
/// @inheritdoc IProtocolFees
IVault public immutable vault;

uint256 private immutable controllerGasLimit;
// a percentage of the block.gaslimit denoted in basis points, used as the gas limit for fee controller calls
// 100 bps is 1%, at 30M gas, the limit is 300K
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(just comment) this mean likely 300k on eth, and 1mil gas on bsc chain

uint256 private constant BLOCK_LIMIT_BPS = 100;

constructor(IVault _vault, uint256 _controllerGasLimit) {
constructor(IVault _vault) {
vault = _vault;
controllerGasLimit = _controllerGasLimit;
}

function _setProtocolFee(PoolId id, uint24 newProtocolFee) internal virtual;
Expand All @@ -47,19 +50,19 @@ abstract contract ProtocolFees is IProtocolFees, Owner {
/// @dev the success of this function must be checked when called in setProtocolFee
function _fetchProtocolFee(PoolKey memory key) internal returns (bool success, uint24 protocolFee) {
if (address(protocolFeeController) != address(0)) {
uint256 controllerGasLimit = block.gaslimit.calculatePortion(BLOCK_LIMIT_BPS);

// note that EIP-150 mandates that calls requesting more than 63/64ths of remaining gas
// will be allotted no more than this amount, so controllerGasLimit must be set with this
// in mind.
if (gasleft() < controllerGasLimit) revert ProtocolFeeCannotBeFetched();

uint256 gasLimit = controllerGasLimit;
address targetProtocolFeeController = address(protocolFeeController);
bytes memory data = abi.encodeCall(IProtocolFeeController.protocolFeeForPool, (key));
uint256 returnData;
assembly ("memory-safe") {
// only load the first 32 bytes of the return data to prevent gas griefing
success := call(gasLimit, targetProtocolFeeController, 0, add(data, 0x20), mload(data), 0, 32)

success := call(controllerGasLimit, targetProtocolFeeController, 0, add(data, 0x20), mload(data), 0, 32)
// if success is false this wont actually be returned, instead 0 will be returned
returnData := mload(0)

Expand Down
12 changes: 7 additions & 5 deletions src/Vault.sol
Original file line number Diff line number Diff line change
Expand Up @@ -122,10 +122,12 @@ contract Vault is IVault, VaultToken, Ownable {
}

function sync(Currency currency) public override isLocked {
VaultReserve.alreadySettledLastSync();
if (currency.isNative()) return;
uint256 balance = currency.balanceOfSelf();
VaultReserve.setVaultReserve(currency, balance);
if (currency.isNative()) {
VaultReserve.setVaultReserve(CurrencyLibrary.NATIVE, 0);
Copy link
Collaborator

@ChefMist ChefMist Sep 9, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i wonder if we can have a helper function so its clearer eg. VaultReserve.clearVaultReserve() or something

and within the helper function, we can document why its set currencyLibrary.Native and 0 amount

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if agreeable, we can create an issue to tackle in another PR (so it doesn't pollute this)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

😎hello

} else {
uint256 balance = currency.balanceOfSelf();
VaultReserve.setVaultReserve(currency, balance);
}
}

/// @inheritdoc IVault
Expand Down Expand Up @@ -186,7 +188,7 @@ contract Vault is IVault, VaultToken, Ownable {
uint256 reservesNow = currency.balanceOfSelf();
paid = reservesNow - reservesBefore;

/// @dev reset the reserve after settled otherwise next sync() call will throw LastSyncNotSettled
/// @dev reset the reserve after settled
VaultReserve.setVaultReserve(CurrencyLibrary.NATIVE, 0);
} else {
// NATIVE token does not require sync call before settle
Expand Down
7 changes: 6 additions & 1 deletion src/interfaces/IVault.sol
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,12 @@ interface IVault is IVaultToken {
/// @dev Can also be used as a mechanism for _free_ flash loans
function take(Currency currency, address to, uint256 amount) external;

/// @notice Called before erc20 transfer to tstore the current reserve balance
/// @notice Writes the current ERC20 balance of the specified currency to transient storage
/// This is used to checkpoint balances for the manager and derive deltas for the caller.
/// @dev This MUST be called before any ERC20 tokens are sent into the contract, but can be skipped
/// for native tokens because the amount to settle is determined by the sent value.
/// However, if an ERC20 token has been synced and not settled, and the caller instead wants to settle
/// native funds, this function can be called with the native currency to then be able to settle the native currency
function sync(Currency token0) external;

/// @notice Called by the user to pay what is owed
Expand Down
17 changes: 17 additions & 0 deletions src/libraries/BipsLibrary.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

/// @title For calculating a percentage of an amount, using bips
library BipsLibrary {
uint256 internal constant BPS_DENOMINATOR = 10_000;

/// @notice emitted when an invalid percentage is provided
error InvalidBips();

/// @param amount The total amount to calculate a percentage of
/// @param bips The percentage to calculate, in bips
function calculatePortion(uint256 amount, uint256 bips) internal pure returns (uint256) {
if (bips > BPS_DENOMINATOR) revert InvalidBips();
return (amount * bips) / BPS_DENOMINATOR;
}
}
12 changes: 0 additions & 12 deletions src/libraries/VaultReserve.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,12 @@ import {Currency} from "../types/Currency.sol";
/// It records a single reserve for a currency each time, this is helpful for
/// calculating how many tokens has been transferred to the vault right after the sync
library VaultReserve {
/// @notice Thrown when trying to sync a reserve when last sync is not settled
error LastSyncNotSettled();

// uint256 constant RESERVE_TYPE_SLOT = uint256(keccak256("reserveType")) - 1;
uint256 internal constant RESERVE_TYPE_SLOT = 0x52a1be34b47478d7c75e2b6c3eea1e05dcb8dbb8c6a42c6482d0dca0df53cb27;

// uint256 constant RESERVE_AMOUNT_SLOT = uint256(keccak256("reserveAmount")) - 1;
uint256 internal constant RESERVE_AMOUNT_SLOT = 0xb0879d96d58bcff08d1fd45590200072d5a8c380da0b5aa1052b48b84e115207;

function alreadySettledLastSync() internal view {
Currency currency;
assembly ("memory-safe") {
currency := tload(RESERVE_TYPE_SLOT)
}

if (!currency.isNative()) revert LastSyncNotSettled();
}

/// @notice Transient store the currency reserve
/// @param currency The currency to be saved
/// @param amount The amount of the currency to be saved
Expand Down
2 changes: 1 addition & 1 deletion src/pool-bin/BinPoolManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ contract BinPoolManager is IBinPoolManager, ProtocolFees, Extsload {

mapping(PoolId id => PoolKey poolKey) public poolIdToPoolKey;

constructor(IVault vault, uint256 controllerGasLimit) ProtocolFees(vault, controllerGasLimit) {}
constructor(IVault vault) ProtocolFees(vault) {}

/// @notice pool manager specified in the pool key must match current contract
modifier poolManagerMatch(address poolManager) {
Expand Down
2 changes: 1 addition & 1 deletion src/pool-cl/CLPoolManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ contract CLPoolManager is ICLPoolManager, ProtocolFees, Extsload {

mapping(PoolId id => PoolKey poolKey) public poolIdToPoolKey;

constructor(IVault _vault, uint256 controllerGasLimit) ProtocolFees(_vault, controllerGasLimit) {}
constructor(IVault _vault) ProtocolFees(_vault) {}

/// @notice pool manager specified in the pool key must match current contract
modifier poolManagerMatch(address poolManager) {
Expand Down
2 changes: 1 addition & 1 deletion src/pool-cl/libraries/Tick.sol
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ library Tick {
// tick spacing will never be 0 since TickMath.MIN_TICK_SPACING is 1
assembly ("memory-safe") {
tickSpacing := signextend(2, tickSpacing)
let minTick := sdiv(MIN_TICK, tickSpacing)
let minTick := sub(sdiv(MIN_TICK, tickSpacing), slt(smod(MIN_TICK, tickSpacing), 0))
let maxTick := sdiv(MAX_TICK, tickSpacing)
let numTicks := add(sub(maxTick, minTick), 1)
result := div(sub(shl(128, 1), 1), numTicks)
Expand Down
2 changes: 1 addition & 1 deletion src/test/MockFeePoolManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ contract MockFeePoolManager is ProtocolFees {
uint24 protocolFee;
}

constructor(IVault vault, uint256 controllerGasLimit) ProtocolFees(vault, controllerGasLimit) {}
constructor(IVault vault, uint256 controllerGasLimit) ProtocolFees(vault) {}

function initialize(PoolKey memory key, bytes calldata) external {
PoolId id = key.toId();
Expand Down
2 changes: 1 addition & 1 deletion test/Extsload.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ contract ExtsloadTest is Test, GasSnapshot {

function setUp() public {
IVault vault = new Vault();
poolManager = new CLPoolManager(vault, 500000);
poolManager = new CLPoolManager(vault);

poolManager.setProtocolFeeController(IProtocolFeeController(address(0xabcd)));
}
Expand Down
51 changes: 51 additions & 0 deletions test/libraries/BipsLibrary.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import "forge-std/Test.sol";
import {BipsLibrary} from "../../src/libraries/BipsLibrary.sol";

contract BipsLibraryTest is Test {
using BipsLibrary for uint256;

// The block gas limit set in foundry config is 300_000_000 (300M) for testing purposes
uint256 BLOCK_GAS_LIMIT;

function setUp() public {
BLOCK_GAS_LIMIT = block.gaslimit;
}

function test_fuzz_calculatePortion(uint256 amount, uint256 bips) public {
amount = bound(amount, 0, uint256(type(uint128).max));
if (bips > BipsLibrary.BPS_DENOMINATOR) {
vm.expectRevert(BipsLibrary.InvalidBips.selector);
amount.calculatePortion(bips);
} else {
assertEq(amount.calculatePortion(bips), amount * bips / BipsLibrary.BPS_DENOMINATOR);
}
}

function test_fuzz_gasLimit(uint256 bips) public {
if (bips > BipsLibrary.BPS_DENOMINATOR) {
vm.expectRevert(BipsLibrary.InvalidBips.selector);
block.gaslimit.calculatePortion(bips);
} else {
assertEq(block.gaslimit.calculatePortion(bips), BLOCK_GAS_LIMIT * bips / BipsLibrary.BPS_DENOMINATOR);
}
}

function test_gasLimit_100_percent() public view {
assertEq(block.gaslimit, block.gaslimit.calculatePortion(10_000));
}

function test_gasLimit_1_percent() public view {
// 100 bps = 1%
// 1% of 30M is 300K
assertEq(BLOCK_GAS_LIMIT / 100, block.gaslimit.calculatePortion(100));
}

function test_gasLimit_1BP() public view {
// 1bp is 0.01%
// 0.01% of 30M is 300
assertEq(BLOCK_GAS_LIMIT / 10000, block.gaslimit.calculatePortion(1));
}
}
11 changes: 0 additions & 11 deletions test/libraries/VaultReserves.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,6 @@ contract VaultReserveTest is Test {
currency0 = Currency.wrap(address(0xabcd));
}

function test_alreadySettledLastSync() public {
VaultReserve.alreadySettledLastSync();

VaultReserve.setVaultReserve(currency0, 10);
vm.expectRevert(VaultReserve.LastSyncNotSettled.selector);
VaultReserve.alreadySettledLastSync();

VaultReserve.setVaultReserve(currency0, 0);
VaultReserve.alreadySettledLastSync();
}

function test_slot_correctness() public pure {
assertEq(uint256(keccak256("reserveType")) - 1, VaultReserve.RESERVE_TYPE_SLOT);
assertEq(uint256(keccak256("reserveAmount")) - 1, VaultReserve.RESERVE_AMOUNT_SLOT);
Expand Down
2 changes: 1 addition & 1 deletion test/pool-bin/BinHook.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ contract BinHookTest is BinTestHelper, GasSnapshot {

function setUp() public {
vault = new MockVault();
poolManager = new BinPoolManager(IVault(address(vault)), 500000);
poolManager = new BinPoolManager(IVault(address(vault)));
mockHooks = new MockBinHooks();
}

Expand Down
2 changes: 1 addition & 1 deletion test/pool-bin/BinHookReturnsDelta.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ contract BinHookReturnsDelta is Test, GasSnapshot, BinTestHelper {

function setUp() public {
vault = new Vault();
poolManager = new BinPoolManager(IVault(address(vault)), 500000);
poolManager = new BinPoolManager(IVault(address(vault)));

vault.registerApp(address(poolManager));

Expand Down
2 changes: 1 addition & 1 deletion test/pool-bin/BinHookReturnsFee.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ contract BinHookReturnsFeeTest is Test, BinTestHelper {

function setUp() public {
vault = new Vault();
poolManager = new BinPoolManager(IVault(address(vault)), 500000);
poolManager = new BinPoolManager(IVault(address(vault)));
vault.registerApp(address(poolManager));

// initializeTokens
Expand Down
Loading
Loading