Skip to content
This repository was archived by the owner on Dec 11, 2025. It is now read-only.

Commit ff1a97a

Browse files
committed
feat: upd currentBridged on liquidity transfers
1 parent b23faa7 commit ff1a97a

File tree

3 files changed

+116
-12
lines changed

3 files changed

+116
-12
lines changed

contracts/src/v0.8/ccip/pools/GHO/UpgradeableLockReleaseTokenPool.sol

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ import {UpgradeableTokenPool} from "./UpgradeableTokenPool.sol";
2121
/// - Implementation of Initializable to allow upgrades
2222
/// - Move of allowlist and router definition to initialization stage
2323
/// - Addition of a bridge limit to regulate the maximum amount of tokens that can be transferred out (burned/locked)
24+
/// - Increment bridged amount on transferLiquidity, reverts if amount + current bridged > bridge limit
25+
/// - Increment bridged amount on provideLiquidity, reverts if amount + current bridged > bridge limit
26+
/// - Decrement bridged amount on withdrawLiquidity, reverts if amount > gho.balanceOf(this)
2427

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

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

213+
if ((s_currentBridged += amount) > s_bridgeLimit) revert BridgeLimitExceeded(s_bridgeLimit);
214+
209215
i_token.safeTransferFrom(msg.sender, address(this), amount);
210216
emit LiquidityAdded(msg.sender, amount);
211217
}
@@ -215,6 +221,8 @@ contract UpgradeableLockReleaseTokenPool is Initializable, UpgradeableTokenPool,
215221
function withdrawLiquidity(uint256 amount) external {
216222
if (s_rebalancer != msg.sender) revert Unauthorized(msg.sender);
217223

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

246+
if ((s_currentBridged += amount) > s_bridgeLimit) revert BridgeLimitExceeded(s_bridgeLimit);
247+
237248
emit LiquidityTransferred(from, amount);
238249
}
239250
}

contracts/src/v0.8/ccip/pools/GHO/diffs/UpgradeableLockReleaseTokenPool_diff.md

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
```diff
22
diff --git a/src/v0.8/ccip/pools/LockReleaseTokenPool.sol b/src/v0.8/ccip/pools/GHO/UpgradeableLockReleaseTokenPool.sol
3-
index ecc28a14dd..bd46601d55 100644
3+
index ecc28a14dd..21bd2df50d 100644
44
--- a/src/v0.8/ccip/pools/LockReleaseTokenPool.sol
55
+++ b/src/v0.8/ccip/pools/GHO/UpgradeableLockReleaseTokenPool.sol
6-
@@ -1,25 +1,41 @@
6+
@@ -1,25 +1,44 @@
77
// SPDX-License-Identifier: BUSL-1.1
88
-pragma solidity 0.8.24;
99
+pragma solidity ^0.8.0;
@@ -35,6 +35,9 @@ index ecc28a14dd..bd46601d55 100644
3535
+/// - Implementation of Initializable to allow upgrades
3636
+/// - Move of allowlist and router definition to initialization stage
3737
+/// - Addition of a bridge limit to regulate the maximum amount of tokens that can be transferred out (burned/locked)
38+
+/// - Increment bridged amount on transferLiquidity, reverts if amount + current bridged > bridge limit
39+
+/// - Increment bridged amount on provideLiquidity, reverts if amount + current bridged > bridge limit
40+
+/// - Decrement bridged amount on withdrawLiquidity, reverts if amount > gho.balanceOf(this)
3841

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

5558
event LiquidityTransferred(address indexed from, uint256 amount);
5659

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

@@ -133,7 +136,7 @@ index ecc28a14dd..bd46601d55 100644
133136
}
134137

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

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

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

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

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

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

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

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

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

248266
emit LiquidityTransferred(from, amount);
249267
}

contracts/src/v0.8/ccip/test/pools/GHO/GhoTokenPoolEthereum.t.sol

Lines changed: 78 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -422,16 +422,26 @@ contract GhoTokenPoolEthereum_canAcceptLiquidity is GhoTokenPoolEthereumSetup {
422422
}
423423

424424
contract GhoTokenPoolEthereum_provideLiquidity is GhoTokenPoolEthereumSetup {
425+
error BridgeLimitExceeded(uint256 limit);
426+
425427
function testFuzz_ProvideLiquiditySuccess(uint256 amount) public {
426428
vm.assume(amount < type(uint128).max);
427429

430+
uint256 bridgedAmount = s_ghoTokenPool.getCurrentBridgedAmount();
431+
428432
uint256 balancePre = s_token.balanceOf(OWNER);
429433
s_token.approve(address(s_ghoTokenPool), amount);
430434

435+
if (amount > s_ghoTokenPool.getBridgeLimit()) {
436+
vm.expectRevert(abi.encodeWithSelector(BridgeLimitExceeded.selector, s_ghoTokenPool.getBridgeLimit()));
437+
}
431438
s_ghoTokenPool.provideLiquidity(amount);
432439

433-
assertEq(s_token.balanceOf(OWNER), balancePre - amount);
434-
assertEq(s_token.balanceOf(address(s_ghoTokenPool)), amount);
440+
if (amount < s_ghoTokenPool.getBridgeLimit()) {
441+
assertEq(s_token.balanceOf(OWNER), balancePre - amount);
442+
assertEq(s_token.balanceOf(address(s_ghoTokenPool)), amount);
443+
assertEq(s_ghoTokenPool.getCurrentBridgedAmount(), bridgedAmount + amount);
444+
}
435445
}
436446

437447
// Reverts
@@ -445,6 +455,11 @@ contract GhoTokenPoolEthereum_provideLiquidity is GhoTokenPoolEthereumSetup {
445455

446456
function testFuzz_ExceedsAllowance(uint256 amount) public {
447457
vm.assume(amount > 0);
458+
459+
changePrank(AAVE_DAO);
460+
s_ghoTokenPool.setBridgeLimit(amount);
461+
changePrank(OWNER);
462+
448463
vm.expectRevert(stdError.arithmeticError);
449464
s_ghoTokenPool.provideLiquidity(amount);
450465
}
@@ -459,7 +474,7 @@ contract GhoTokenPoolEthereum_provideLiquidity is GhoTokenPoolEthereumSetup {
459474

460475
contract GhoTokenPoolEthereum_withdrawalLiquidity is GhoTokenPoolEthereumSetup {
461476
function testFuzz_WithdrawalLiquiditySuccess(uint256 amount) public {
462-
vm.assume(amount < type(uint128).max);
477+
amount = bound(amount, 1, s_ghoTokenPool.getBridgeLimit());
463478

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

482497
function testInsufficientLiquidityReverts() public {
483498
uint256 maxUint128 = 2 ** 128 - 1;
499+
500+
changePrank(AAVE_DAO);
501+
s_ghoTokenPool.setBridgeLimit(maxUint128);
502+
changePrank(OWNER);
503+
484504
s_token.approve(address(s_ghoTokenPool), maxUint128);
485505
s_ghoTokenPool.provideLiquidity(maxUint128);
486506

@@ -493,6 +513,61 @@ contract GhoTokenPoolEthereum_withdrawalLiquidity is GhoTokenPoolEthereumSetup {
493513
}
494514
}
495515

516+
contract GhoTokenPoolEthereum_transferLiquidity is GhoTokenPoolEthereumSetup {
517+
UpgradeableLockReleaseTokenPool internal s_oldLockReleaseTokenPool;
518+
519+
uint256 internal s_amount = 100_000_000e18;
520+
521+
error OnlyCallableByOwner();
522+
error BridgeLimitExceeded(uint256 limit);
523+
524+
function setUp() public virtual override {
525+
super.setUp();
526+
527+
s_oldLockReleaseTokenPool = UpgradeableLockReleaseTokenPool(
528+
_deployUpgradeableLockReleaseTokenPool(
529+
address(s_token),
530+
address(s_mockRMN),
531+
address(s_sourceRouter),
532+
AAVE_DAO,
533+
INITIAL_BRIDGE_LIMIT,
534+
PROXY_ADMIN
535+
)
536+
);
537+
deal(address(s_token), address(s_oldLockReleaseTokenPool), s_amount);
538+
// write to currentBridged
539+
vm.store(address(s_oldLockReleaseTokenPool), bytes32(uint256(12)), bytes32(s_amount));
540+
changePrank(AAVE_DAO);
541+
}
542+
543+
function testFuzz_TransferLiquidity(uint256 amount) public {
544+
amount = bound(amount, 1, s_amount);
545+
546+
s_oldLockReleaseTokenPool.setRebalancer(address(s_ghoTokenPool));
547+
uint256 bridgedAmount = s_ghoTokenPool.getCurrentBridgedAmount();
548+
549+
if (amount > s_ghoTokenPool.getBridgeLimit()) {
550+
vm.expectRevert(abi.encodeWithSelector(BridgeLimitExceeded.selector, s_ghoTokenPool.getBridgeLimit()));
551+
}
552+
s_ghoTokenPool.transferLiquidity(address(s_oldLockReleaseTokenPool), amount);
553+
554+
if (amount < s_ghoTokenPool.getBridgeLimit()) {
555+
assertEq(s_token.balanceOf(address(s_ghoTokenPool)), amount);
556+
assertEq(s_token.balanceOf(address(s_oldLockReleaseTokenPool)), s_amount - amount);
557+
assertEq(s_ghoTokenPool.getCurrentBridgedAmount(), bridgedAmount + amount);
558+
}
559+
}
560+
561+
// Reverts
562+
563+
function test_UnauthorizedReverts() public {
564+
changePrank(STRANGER);
565+
vm.expectRevert(OnlyCallableByOwner.selector);
566+
567+
s_ghoTokenPool.transferLiquidity(address(1), 1);
568+
}
569+
}
570+
496571
contract GhoTokenPoolEthereum_supportsInterface is GhoTokenPoolEthereumSetup {
497572
function testSupportsInterfaceSuccess() public view {
498573
assertTrue(s_ghoTokenPool.supportsInterface(type(ILiquidityContainer).interfaceId));

0 commit comments

Comments
 (0)