Skip to content

Commit

Permalink
feat: upd currentBridged on liquidity transfers
Browse files Browse the repository at this point in the history
  • Loading branch information
DhairyaSethi committed Dec 11, 2024
1 parent b23faa7 commit ff1a97a
Show file tree
Hide file tree
Showing 3 changed files with 116 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ import {UpgradeableTokenPool} from "./UpgradeableTokenPool.sol";
/// - Implementation of Initializable to allow upgrades
/// - Move of allowlist and router definition to initialization stage
/// - Addition of a bridge limit to regulate the maximum amount of tokens that can be transferred out (burned/locked)
/// - Increment bridged amount on transferLiquidity, reverts if amount + current bridged > bridge limit
/// - Increment bridged amount on provideLiquidity, reverts if amount + current bridged > bridge limit
/// - Decrement bridged amount on withdrawLiquidity, reverts if amount > gho.balanceOf(this)

/// @notice Token pool used for tokens on their native chain. This uses a lock and release mechanism.
/// Because of lock/unlock requiring liquidity, this pool contract also has function to add and remove
Expand Down Expand Up @@ -202,10 +205,13 @@ contract UpgradeableLockReleaseTokenPool is Initializable, UpgradeableTokenPool,

/// @notice Adds liquidity to the pool. The tokens should be approved first.
/// @param amount The amount of liquidity to provide.
/// @dev amount being added + currentBridged needs to be within the bridge limit, otherwise increase bridge limit first
function provideLiquidity(uint256 amount) external {
if (!i_acceptLiquidity) revert LiquidityNotAccepted();
if (s_rebalancer != msg.sender) revert Unauthorized(msg.sender);

if ((s_currentBridged += amount) > s_bridgeLimit) revert BridgeLimitExceeded(s_bridgeLimit);

i_token.safeTransferFrom(msg.sender, address(this), amount);
emit LiquidityAdded(msg.sender, amount);
}
Expand All @@ -215,6 +221,8 @@ contract UpgradeableLockReleaseTokenPool is Initializable, UpgradeableTokenPool,
function withdrawLiquidity(uint256 amount) external {
if (s_rebalancer != msg.sender) revert Unauthorized(msg.sender);

s_currentBridged -= amount;

if (i_token.balanceOf(address(this)) < amount) revert InsufficientLiquidity();
i_token.safeTransfer(msg.sender, amount);
emit LiquidityRemoved(msg.sender, amount);
Expand All @@ -229,11 +237,14 @@ contract UpgradeableLockReleaseTokenPool is Initializable, UpgradeableTokenPool,
/// changing which pool CCIP uses, to ensure both pools can operate. Then the pool should be changed in the
/// TokenAdminRegistry, which will activate the new pool. All new transactions will use the new pool and its
/// liquidity. Finally, the remaining liquidity can be transferred to the new pool using this function one more time.
/// @dev amount being added + currentBridged needs to be within the bridge limit, otherwise increase bridge limit first
/// @param from The address of the old pool.
/// @param amount The amount of liquidity to transfer.
function transferLiquidity(address from, uint256 amount) external onlyOwner {
UpgradeableLockReleaseTokenPool(from).withdrawLiquidity(amount);

if ((s_currentBridged += amount) > s_bridgeLimit) revert BridgeLimitExceeded(s_bridgeLimit);

emit LiquidityTransferred(from, amount);
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
```diff
diff --git a/src/v0.8/ccip/pools/LockReleaseTokenPool.sol b/src/v0.8/ccip/pools/GHO/UpgradeableLockReleaseTokenPool.sol
index ecc28a14dd..bd46601d55 100644
index ecc28a14dd..21bd2df50d 100644
--- a/src/v0.8/ccip/pools/LockReleaseTokenPool.sol
+++ b/src/v0.8/ccip/pools/GHO/UpgradeableLockReleaseTokenPool.sol
@@ -1,25 +1,41 @@
@@ -1,25 +1,44 @@
// SPDX-License-Identifier: BUSL-1.1
-pragma solidity 0.8.24;
+pragma solidity ^0.8.0;
Expand Down Expand Up @@ -35,6 +35,9 @@ index ecc28a14dd..bd46601d55 100644
+/// - Implementation of Initializable to allow upgrades
+/// - Move of allowlist and router definition to initialization stage
+/// - Addition of a bridge limit to regulate the maximum amount of tokens that can be transferred out (burned/locked)
+/// - Increment bridged amount on transferLiquidity, reverts if amount + current bridged > bridge limit
+/// - Increment bridged amount on provideLiquidity, reverts if amount + current bridged > bridge limit
+/// - Decrement bridged amount on withdrawLiquidity, reverts if amount > gho.balanceOf(this)

/// @notice Token pool used for tokens on their native chain. This uses a lock and release mechanism.
/// Because of lock/unlock requiring liquidity, this pool contract also has function to add and remove
Expand All @@ -54,7 +57,7 @@ index ecc28a14dd..bd46601d55 100644

event LiquidityTransferred(address indexed from, uint256 amount);

@@ -33,30 +49,69 @@ contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion
@@ -33,30 +52,69 @@ contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion
/// @notice The address of the rebalancer.
address internal s_rebalancer;

Expand Down Expand Up @@ -133,7 +136,7 @@ index ecc28a14dd..bd46601d55 100644
}

/// @notice Release tokens from the pool to the recipient
@@ -64,11 +119,18 @@ contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion
@@ -64,11 +122,18 @@ contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion
function releaseOrMint(
Pool.ReleaseOrMintInV1 calldata releaseOrMintIn
) external virtual override returns (Pool.ReleaseOrMintOutV1 memory) {
Expand All @@ -154,7 +157,7 @@ index ecc28a14dd..bd46601d55 100644

// Release to the recipient
getToken().safeTransfer(releaseOrMintIn.receiver, localAmount);
@@ -79,9 +141,7 @@ contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion
@@ -79,9 +144,7 @@ contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion
}

/// @inheritdoc IERC165
Expand All @@ -165,7 +168,7 @@ index ecc28a14dd..bd46601d55 100644
return interfaceId == type(ILiquidityContainer).interfaceId || super.supportsInterface(interfaceId);
}

@@ -93,12 +153,47 @@ contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion
@@ -93,12 +156,47 @@ contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion

/// @notice Sets the LiquidityManager address.
/// @dev Only callable by the owner.
Expand Down Expand Up @@ -216,18 +219,23 @@ index ecc28a14dd..bd46601d55 100644
/// @notice Checks if the pool can accept liquidity.
/// @return true if the pool can accept liquidity, false otherwise.
function canAcceptLiquidity() external view returns (bool) {
@@ -107,9 +202,7 @@ contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion
@@ -107,23 +205,24 @@ contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion

/// @notice Adds liquidity to the pool. The tokens should be approved first.
/// @param amount The amount of liquidity to provide.
- function provideLiquidity(
- uint256 amount
- ) external {
+ /// @dev amount being added + currentBridged needs to be within the bridge limit, otherwise increase bridge limit first
+ function provideLiquidity(uint256 amount) external {
if (!i_acceptLiquidity) revert LiquidityNotAccepted();
if (s_rebalancer != msg.sender) revert Unauthorized(msg.sender);

@@ -119,9 +212,7 @@ contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion
+ if ((s_currentBridged += amount) > s_bridgeLimit) revert BridgeLimitExceeded(s_bridgeLimit);
+
i_token.safeTransferFrom(msg.sender, address(this), amount);
emit LiquidityAdded(msg.sender, amount);
}

/// @notice Removed liquidity to the pool. The tokens will be sent to msg.sender.
/// @param amount The amount of liquidity to remove.
Expand All @@ -237,13 +245,23 @@ index ecc28a14dd..bd46601d55 100644
+ function withdrawLiquidity(uint256 amount) external {
if (s_rebalancer != msg.sender) revert Unauthorized(msg.sender);

+ s_currentBridged -= amount;
+
if (i_token.balanceOf(address(this)) < amount) revert InsufficientLiquidity();
@@ -141,7 +232,7 @@ contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion
i_token.safeTransfer(msg.sender, amount);
emit LiquidityRemoved(msg.sender, amount);
@@ -138,10 +237,13 @@ contract LockReleaseTokenPool is TokenPool, ILiquidityContainer, ITypeAndVersion
/// changing which pool CCIP uses, to ensure both pools can operate. Then the pool should be changed in the
/// TokenAdminRegistry, which will activate the new pool. All new transactions will use the new pool and its
/// liquidity. Finally, the remaining liquidity can be transferred to the new pool using this function one more time.
+ /// @dev amount being added + currentBridged needs to be within the bridge limit, otherwise increase bridge limit first
/// @param from The address of the old pool.
/// @param amount The amount of liquidity to transfer.
function transferLiquidity(address from, uint256 amount) external onlyOwner {
- LockReleaseTokenPool(from).withdrawLiquidity(amount);
+ UpgradeableLockReleaseTokenPool(from).withdrawLiquidity(amount);
+
+ if ((s_currentBridged += amount) > s_bridgeLimit) revert BridgeLimitExceeded(s_bridgeLimit);

emit LiquidityTransferred(from, amount);
}
Expand Down
81 changes: 78 additions & 3 deletions contracts/src/v0.8/ccip/test/pools/GHO/GhoTokenPoolEthereum.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -422,16 +422,26 @@ contract GhoTokenPoolEthereum_canAcceptLiquidity is GhoTokenPoolEthereumSetup {
}

contract GhoTokenPoolEthereum_provideLiquidity is GhoTokenPoolEthereumSetup {
error BridgeLimitExceeded(uint256 limit);

function testFuzz_ProvideLiquiditySuccess(uint256 amount) public {
vm.assume(amount < type(uint128).max);

uint256 bridgedAmount = s_ghoTokenPool.getCurrentBridgedAmount();

uint256 balancePre = s_token.balanceOf(OWNER);
s_token.approve(address(s_ghoTokenPool), amount);

if (amount > s_ghoTokenPool.getBridgeLimit()) {
vm.expectRevert(abi.encodeWithSelector(BridgeLimitExceeded.selector, s_ghoTokenPool.getBridgeLimit()));
}
s_ghoTokenPool.provideLiquidity(amount);

assertEq(s_token.balanceOf(OWNER), balancePre - amount);
assertEq(s_token.balanceOf(address(s_ghoTokenPool)), amount);
if (amount < s_ghoTokenPool.getBridgeLimit()) {
assertEq(s_token.balanceOf(OWNER), balancePre - amount);
assertEq(s_token.balanceOf(address(s_ghoTokenPool)), amount);
assertEq(s_ghoTokenPool.getCurrentBridgedAmount(), bridgedAmount + amount);
}
}

// Reverts
Expand All @@ -445,6 +455,11 @@ contract GhoTokenPoolEthereum_provideLiquidity is GhoTokenPoolEthereumSetup {

function testFuzz_ExceedsAllowance(uint256 amount) public {
vm.assume(amount > 0);

changePrank(AAVE_DAO);
s_ghoTokenPool.setBridgeLimit(amount);
changePrank(OWNER);

vm.expectRevert(stdError.arithmeticError);
s_ghoTokenPool.provideLiquidity(amount);
}
Expand All @@ -459,7 +474,7 @@ contract GhoTokenPoolEthereum_provideLiquidity is GhoTokenPoolEthereumSetup {

contract GhoTokenPoolEthereum_withdrawalLiquidity is GhoTokenPoolEthereumSetup {
function testFuzz_WithdrawalLiquiditySuccess(uint256 amount) public {
vm.assume(amount < type(uint128).max);
amount = bound(amount, 1, s_ghoTokenPool.getBridgeLimit());

uint256 balancePre = s_token.balanceOf(OWNER);
s_token.approve(address(s_ghoTokenPool), amount);
Expand All @@ -481,6 +496,11 @@ contract GhoTokenPoolEthereum_withdrawalLiquidity is GhoTokenPoolEthereumSetup {

function testInsufficientLiquidityReverts() public {
uint256 maxUint128 = 2 ** 128 - 1;

changePrank(AAVE_DAO);
s_ghoTokenPool.setBridgeLimit(maxUint128);
changePrank(OWNER);

s_token.approve(address(s_ghoTokenPool), maxUint128);
s_ghoTokenPool.provideLiquidity(maxUint128);

Expand All @@ -493,6 +513,61 @@ contract GhoTokenPoolEthereum_withdrawalLiquidity is GhoTokenPoolEthereumSetup {
}
}

contract GhoTokenPoolEthereum_transferLiquidity is GhoTokenPoolEthereumSetup {
UpgradeableLockReleaseTokenPool internal s_oldLockReleaseTokenPool;

uint256 internal s_amount = 100_000_000e18;

error OnlyCallableByOwner();
error BridgeLimitExceeded(uint256 limit);

function setUp() public virtual override {
super.setUp();

s_oldLockReleaseTokenPool = UpgradeableLockReleaseTokenPool(
_deployUpgradeableLockReleaseTokenPool(
address(s_token),
address(s_mockRMN),
address(s_sourceRouter),
AAVE_DAO,
INITIAL_BRIDGE_LIMIT,
PROXY_ADMIN
)
);
deal(address(s_token), address(s_oldLockReleaseTokenPool), s_amount);
// write to currentBridged
vm.store(address(s_oldLockReleaseTokenPool), bytes32(uint256(12)), bytes32(s_amount));
changePrank(AAVE_DAO);
}

function testFuzz_TransferLiquidity(uint256 amount) public {
amount = bound(amount, 1, s_amount);

s_oldLockReleaseTokenPool.setRebalancer(address(s_ghoTokenPool));
uint256 bridgedAmount = s_ghoTokenPool.getCurrentBridgedAmount();

if (amount > s_ghoTokenPool.getBridgeLimit()) {
vm.expectRevert(abi.encodeWithSelector(BridgeLimitExceeded.selector, s_ghoTokenPool.getBridgeLimit()));
}
s_ghoTokenPool.transferLiquidity(address(s_oldLockReleaseTokenPool), amount);

if (amount < s_ghoTokenPool.getBridgeLimit()) {
assertEq(s_token.balanceOf(address(s_ghoTokenPool)), amount);
assertEq(s_token.balanceOf(address(s_oldLockReleaseTokenPool)), s_amount - amount);
assertEq(s_ghoTokenPool.getCurrentBridgedAmount(), bridgedAmount + amount);
}
}

// Reverts

function test_UnauthorizedReverts() public {
changePrank(STRANGER);
vm.expectRevert(OnlyCallableByOwner.selector);

s_ghoTokenPool.transferLiquidity(address(1), 1);
}
}

contract GhoTokenPoolEthereum_supportsInterface is GhoTokenPoolEthereumSetup {
function testSupportsInterfaceSuccess() public view {
assertTrue(s_ghoTokenPool.supportsInterface(type(ILiquidityContainer).interfaceId));
Expand Down

0 comments on commit ff1a97a

Please sign in to comment.