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

<WIP> feat: set reserveOf transient #33

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
@@ -1 +1 @@
142035
137875
Original file line number Diff line number Diff line change
@@ -1 +1 @@
158207
147818
Original file line number Diff line number Diff line change
@@ -1 +1 @@
303831
295519
2 changes: 1 addition & 1 deletion .forge-snapshots/BinPoolManagerTest#testGasBurnOneBin.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
139396
131085
2 changes: 1 addition & 1 deletion .forge-snapshots/BinPoolManagerTest#testGasDonate.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
125193
120152
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1013119
973898
Original file line number Diff line number Diff line change
@@ -1 +1 @@
341133
336092
Original file line number Diff line number Diff line change
@@ -1 +1 @@
380471
341251
Original file line number Diff line number Diff line change
@@ -1 +1 @@
149232
144192
Original file line number Diff line number Diff line change
@@ -1 +1 @@
187313
179604
Original file line number Diff line number Diff line change
@@ -1 +1 @@
193298
185589
Original file line number Diff line number Diff line change
@@ -1 +1 @@
145662
137953
Original file line number Diff line number Diff line change
@@ -1 +1 @@
327364
308921
2 changes: 1 addition & 1 deletion .forge-snapshots/BinPoolManagerTest#testNoOpGas_Burn.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
76641
76706
Original file line number Diff line number Diff line change
@@ -1 +1 @@
54134
54199
2 changes: 1 addition & 1 deletion .forge-snapshots/BinPoolManagerTest#testNoOpGas_Mint.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
69634
69699
2 changes: 1 addition & 1 deletion .forge-snapshots/BinPoolManagerTest#testNoOpGas_Swap.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
57563
57628
Original file line number Diff line number Diff line change
@@ -1 +1 @@
401866
362749
Original file line number Diff line number Diff line change
@@ -1 +1 @@
182854
177917
Original file line number Diff line number Diff line change
@@ -1 +1 @@
247993
249195
2 changes: 1 addition & 1 deletion .forge-snapshots/CLPoolManagerTest#donateBothTokens.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
175518
170492
2 changes: 1 addition & 1 deletion .forge-snapshots/CLPoolManagerTest#gasDonateOneToken.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
116900
114415
Original file line number Diff line number Diff line change
@@ -1 +1 @@
129601
124439
Original file line number Diff line number Diff line change
@@ -1 +1 @@
148767
141058
Original file line number Diff line number Diff line change
@@ -1 +1 @@
177177
174695
Original file line number Diff line number Diff line change
@@ -1 +1 @@
24940941
24933275
2 changes: 1 addition & 1 deletion .forge-snapshots/CLPoolManagerTest#swap_simple.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
78822
78887
Original file line number Diff line number Diff line change
@@ -1 +1 @@
158284
153122
2 changes: 1 addition & 1 deletion .forge-snapshots/CLPoolManagerTest#swap_withHooks.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
95329
95394
2 changes: 1 addition & 1 deletion .forge-snapshots/CLPoolManagerTest#swap_withNative.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
78825
78890
Original file line number Diff line number Diff line change
@@ -1 +1 @@
53926
53991
Original file line number Diff line number Diff line change
@@ -1 +1 @@
60244
60309
2 changes: 1 addition & 1 deletion .forge-snapshots/CLPoolManagerTest#testNoOp_gas_Swap.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
56919
56984
2 changes: 1 addition & 1 deletion .forge-snapshots/VaultTest#Vault.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
7437
7136
2 changes: 1 addition & 1 deletion .forge-snapshots/VaultTest#collectFee.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
53360
25187
Original file line number Diff line number Diff line change
@@ -1 +1 @@
159350
81544
2 changes: 1 addition & 1 deletion .forge-snapshots/VaultTest#lockSettledWhenFlashloan.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
103575
121535
Original file line number Diff line number Diff line change
@@ -1 +1 @@
118273
47404
2 changes: 1 addition & 1 deletion .forge-snapshots/VaultTest#lockSettledWhenSwap.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
118272
47404
2 changes: 1 addition & 1 deletion .forge-snapshots/VaultTest#testLock_NoOp.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
32989
32864

This file was deleted.

This file was deleted.

54 changes: 15 additions & 39 deletions src/Vault.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,17 @@ import {Currency, CurrencyLibrary} from "./types/Currency.sol";
import {BalanceDelta} from "./types/BalanceDelta.sol";
import {ILockCallback} from "./interfaces/ILockCallback.sol";
import {SafeCast} from "./libraries/SafeCast.sol";
import {VaultReserves} from "./libraries/VaultReserves.sol";
import {VaultToken} from "./VaultToken.sol";

contract Vault is IVault, VaultToken, Ownable {
using SafeCast for *;
using PoolIdLibrary for PoolKey;
using CurrencyLibrary for Currency;
using VaultReserves for Currency;

mapping(address => bool) public override isPoolManagerRegistered;

/// @dev keep track of the reserves of the whole vault
mapping(Currency currency => uint256) public override reservesOfVault;

/// @dev keep track of each pool manager's reserves
mapping(IPoolManager poolManager => mapping(Currency currency => uint256 reserve)) public reservesOfPoolManager;

Expand Down Expand Up @@ -104,7 +103,6 @@ contract Vault is IVault, VaultToken, Ownable {
/// @inheritdoc IVault
function take(Currency currency, address to, uint256 amount) external override isLocked {
SettlementGuard.accountDelta(msg.sender, currency, amount.toInt128());
if (!currency.isNative()) reservesOfVault[currency] -= amount;
currency.transfer(to, amount);
}

Expand All @@ -114,13 +112,18 @@ contract Vault is IVault, VaultToken, Ownable {
_mint(to, currency, amount);
}

function sync(Currency currency) public returns (uint256 balance) {
balance = currency.balanceOfSelf();
currency.setVaultReserves(balance);
}

/// @inheritdoc IVault
function settle(Currency currency) external payable override isLocked returns (uint256 paid) {
if (!currency.isNative()) {
if (msg.value > 0) revert SettleNonNativeCurrencyWithValue();
uint256 reservesBefore = reservesOfVault[currency];
reservesOfVault[currency] = currency.balanceOfSelf();
paid = reservesOfVault[currency] - reservesBefore;
uint256 reservesBefore = currency.getVaultReserves();
uint256 reservesNow = sync(currency);
paid = reservesNow - reservesBefore;
} else {
paid = msg.value;
}
Expand All @@ -129,36 +132,6 @@ contract Vault is IVault, VaultToken, Ownable {
SettlementGuard.accountDelta(msg.sender, currency, -(paid.toInt128()));
}

function settleAndMintRefund(Currency currency, address to)
external
payable
override
isLocked
returns (uint256 paid, uint256 refund)
{
if (!currency.isNative()) {
if (msg.value > 0) revert SettleNonNativeCurrencyWithValue();
uint256 reservesBefore = reservesOfVault[currency];
reservesOfVault[currency] = currency.balanceOfSelf();
paid = reservesOfVault[currency] - reservesBefore;
} else {
paid = msg.value;
}

int256 currentDelta = SettlementGuard.getCurrencyDelta(msg.sender, currency);
if (currentDelta >= 0) {
uint256 currentDeltaUint256 = currentDelta.toUint256();
if (paid > currentDeltaUint256) {
// msg.sender owes vault but paid more than than whats owed
refund = paid - currentDeltaUint256;
paid = currentDeltaUint256;
}
}

SettlementGuard.accountDelta(msg.sender, currency, -(paid.toInt128()));
if (refund > 0) _mint(to, currency, refund);
}

/// @inheritdoc IVault
function settleFor(Currency currency, address target, uint256 amount) external isLocked {
/// @notice settle all outstanding debt if amount is 0
Expand All @@ -177,11 +150,14 @@ contract Vault is IVault, VaultToken, Ownable {
/// @inheritdoc IVault
function collectFee(Currency currency, uint256 amount, address recipient) external {
reservesOfPoolManager[IPoolManager(msg.sender)][currency] -= amount;
if (!currency.isNative()) reservesOfVault[currency] -= amount;

currency.transfer(recipient, amount);
}

/// @inheritdoc IVault
function reservesOfVault(Currency currency) external view returns (uint256 amount) {
return currency.getVaultReserves();
}

function _accountDeltaOfPoolManager(IPoolManager poolManager, Currency currency, int128 delta) internal {
if (delta == 0) return;

Expand Down
16 changes: 4 additions & 12 deletions src/interfaces/IVault.sol
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ interface IVault is IVaultToken {

function isPoolManagerRegistered(address poolManager) external returns (bool);

/// @notice Returns the reserves for a given ERC20 currency
/// @notice Returns the reserves for a currency thats sync in transient storage
function reservesOfVault(Currency currency) external view returns (uint256);

/// @notice Returns the reserves for a a given pool type and currency
Expand Down Expand Up @@ -67,20 +67,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
function sync(Currency token0) external returns (uint256 balance);

/// @notice Called by the user to pay what is owed
function settle(Currency token) external payable returns (uint256 paid);

/// @notice Called by the user to pay what is owed. If the payment is more than the debt, the surplus is refunded by minting
/// @dev To claim the refund, caller must eventually call vault.burn() -> vault.take() to take the ERC20 token from vault
/// @param currency The currency to settle
/// @param to The address to mint the refund
/// @return paid The amount paid
/// @return refund The amount refunded
function settleAndMintRefund(Currency currency, address to)
external
payable
returns (uint256 paid, uint256 refund);

/// @notice move the delta from target to the msg.sender, only payment delta can be moved
/// @param currency The currency to settle
/// @param target The address whose delta will be updated
Expand Down
50 changes: 50 additions & 0 deletions src/libraries/VaultReserves.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// SPDX-License-Identifier: GPL-2.0-or-later
// Copyright (C) 2024 PancakeSwap
pragma solidity ^0.8.24;

import {Currency} from "../types/Currency.sol";

/// @notice This is a workaround when transient keyword is absent. It manages:
/// - 0: mapping(currency => uint256) reserveOfVault
library VaultReserves {
/// @notice Thrown when getVaultReserves is called before sync
error ReserveNotSync();

// uint256 constant RESERVE_OF_VAULT_SLOT = uint256(keccak256("reservesOfVault")) - 1;
uint256 constant RESERVE_OF_VAULT_SLOT = uint256(0xb54c65c0f448723e3496562a0e878a1341c4dd2511ef542b5fd5f19cebc47663);

/// @notice Set balance to the max as a sentinel to track that it has been set if amount == 0
uint256 constant ZERO_BALANCE = type(uint256).max;

/// @notice Transient store the currency reserve
/// @dev if the amount is 0, the value stored would be ZERO_BALANCE, a sentinel value
function setVaultReserves(Currency currency, uint256 amount) internal {
if (amount == 0) amount = ZERO_BALANCE;

bytes32 slotKey = _getCurrencySlotKey(currency);
assembly {
tstore(slotKey, amount)
}
}

/// @notice Transient load the currency reserve
/// @dev If this is called before vault.sync, it will be reverted
function getVaultReserves(Currency currency) internal view returns (uint256 amount) {
bytes32 slotKey = _getCurrencySlotKey(currency);
assembly {
amount := tload(slotKey)
}

if (amount == 0) revert ReserveNotSync();
if (amount == ZERO_BALANCE) return 0;
}

function _getCurrencySlotKey(Currency currency) internal pure returns (bytes32 key) {
uint256 slot = RESERVE_OF_VAULT_SLOT;
assembly {
mstore(0x0, slot)
mstore(0x20, currency)
key := keccak256(0x0, 0x40)
}
Comment on lines +44 to +48
Copy link
Contributor

@chef-omelette chef-omelette May 6, 2024

Choose a reason for hiding this comment

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

just curious, why not keccak256(abi.encodePacked(slot, currency.unwrap())) ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

mostly around gas savings, can see CLPosition and BinPosition using similar technique.

}
}
Loading
Loading