-
Notifications
You must be signed in to change notification settings - Fork 54
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This PR will add two new pools, one burnMint and one lockRelease. These will be used as proxies for the existing <1.5 token pools when the lanes upgrade to 1.5. Please see `contracts/src/v0.8/ccip/test/legacy/BurnMintTokenPoolAndProxy.t.sol` for a full step-by-step guide on how this upgrade works for both the 1.0/1.2 and the 1.4 pools. Pools deployed after self serve is live will NOT use these proxies, but rather the clean versions that do not implement the proxy behaviour. Because of this, we only need proxies for the pools that are currently live: burnMint and LockRelease. USDC will cleanly upgrade by deploying new pools and doesn't require the proxy. All other pool flavours are not live at the moment --------- Co-authored-by: Justin Kaseman <[email protected]>
- Loading branch information
1 parent
31dba11
commit 6b98596
Showing
16 changed files
with
7,694 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"ccip": patch | ||
--- | ||
|
||
make pools backwards compatible proxies |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"@chainlink/contracts-ccip": patch | ||
--- | ||
|
||
make pools backwards compatible proxies |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.0; | ||
|
||
import {IERC20} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; | ||
|
||
// Shared public interface for multiple pool types. | ||
// Each pool type handles a different child token model (lock/unlock, mint/burn.) | ||
interface IPoolPriorTo1_5 { | ||
/// @notice Lock tokens into the pool or burn the tokens. | ||
/// @param originalSender Original sender of the tokens. | ||
/// @param receiver Receiver of the tokens on destination chain. | ||
/// @param amount Amount to lock or burn. | ||
/// @param remoteChainSelector Destination chain Id. | ||
/// @param extraArgs Additional data passed in by sender for lockOrBurn processing | ||
/// in custom pools on source chain. | ||
/// @return retData Optional field that contains bytes. Unused for now but already | ||
/// implemented to allow future upgrades while preserving the interface. | ||
function lockOrBurn( | ||
address originalSender, | ||
bytes calldata receiver, | ||
uint256 amount, | ||
uint64 remoteChainSelector, | ||
bytes calldata extraArgs | ||
) external returns (bytes memory); | ||
|
||
/// @notice Releases or mints tokens to the receiver address. | ||
/// @param originalSender Original sender of the tokens. | ||
/// @param receiver Receiver of the tokens. | ||
/// @param amount Amount to release or mint. | ||
/// @param remoteChainSelector Source chain Id. | ||
/// @param extraData Additional data supplied offchain for releaseOrMint processing in | ||
/// custom pools on dest chain. This could be an attestation that was retrieved through a | ||
/// third party API. | ||
/// @dev offchainData can come from any untrusted source. | ||
function releaseOrMint( | ||
bytes memory originalSender, | ||
address receiver, | ||
uint256 amount, | ||
uint64 remoteChainSelector, | ||
bytes memory extraData | ||
) external; | ||
|
||
/// @notice Gets the IERC20 token that this pool can lock or burn. | ||
/// @return token The IERC20 token representation. | ||
function getToken() external view returns (IERC20 token); | ||
} |
69 changes: 69 additions & 0 deletions
69
contracts/src/v0.8/ccip/pools/BurnMintTokenPoolAndProxy.sol
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
// SPDX-License-Identifier: BUSL-1.1 | ||
pragma solidity 0.8.19; | ||
|
||
import {ITypeAndVersion} from "../../shared/interfaces/ITypeAndVersion.sol"; | ||
import {IBurnMintERC20} from "../../shared/token/ERC20/IBurnMintERC20.sol"; | ||
|
||
import {Pool} from "../libraries/Pool.sol"; | ||
import {LegacyPoolWrapper} from "./LegacyPoolWrapper.sol"; | ||
|
||
contract BurnMintTokenPoolAndProxy is ITypeAndVersion, LegacyPoolWrapper { | ||
string public constant override typeAndVersion = "BurnMintTokenPoolAndProxy 1.5.0-dev"; | ||
|
||
constructor( | ||
IBurnMintERC20 token, | ||
address[] memory allowlist, | ||
address armProxy, | ||
address router | ||
) LegacyPoolWrapper(token, allowlist, armProxy, router) {} | ||
|
||
/// @notice Burn the token in the pool | ||
/// @dev The whenHealthy check is important to ensure that even if a ramp is compromised | ||
/// we're able to stop token movement via ARM. | ||
function lockOrBurn(Pool.LockOrBurnInV1 calldata lockOrBurnIn) | ||
external | ||
virtual | ||
override | ||
whenHealthy | ||
returns (Pool.LockOrBurnOutV1 memory) | ||
{ | ||
_checkAllowList(lockOrBurnIn.originalSender); | ||
_onlyOnRamp(lockOrBurnIn.remoteChainSelector); | ||
_consumeOutboundRateLimit(lockOrBurnIn.remoteChainSelector, lockOrBurnIn.amount); | ||
|
||
if (!_hasLegacyPool()) { | ||
IBurnMintERC20(address(i_token)).burn(lockOrBurnIn.amount); | ||
} else { | ||
_lockOrBurnLegacy(lockOrBurnIn); | ||
} | ||
|
||
emit Burned(msg.sender, lockOrBurnIn.amount); | ||
|
||
return Pool.LockOrBurnOutV1({destPoolAddress: getRemotePool(lockOrBurnIn.remoteChainSelector), destPoolData: ""}); | ||
} | ||
|
||
/// @notice Mint tokens from the pool to the recipient | ||
/// @dev The whenHealthy check is important to ensure that even if a ramp is compromised | ||
/// we're able to stop token movement via ARM. | ||
function releaseOrMint(Pool.ReleaseOrMintInV1 calldata releaseOrMintIn) | ||
external | ||
virtual | ||
override | ||
whenHealthy | ||
returns (Pool.ReleaseOrMintOutV1 memory) | ||
{ | ||
_onlyOffRamp(releaseOrMintIn.remoteChainSelector); | ||
_validateSourceCaller(releaseOrMintIn.remoteChainSelector, releaseOrMintIn.sourcePoolAddress); | ||
_consumeInboundRateLimit(releaseOrMintIn.remoteChainSelector, releaseOrMintIn.amount); | ||
|
||
if (!_hasLegacyPool()) { | ||
IBurnMintERC20(address(i_token)).mint(releaseOrMintIn.receiver, releaseOrMintIn.amount); | ||
} else { | ||
_releaseOrMintLegacy(releaseOrMintIn); | ||
} | ||
|
||
emit Minted(msg.sender, releaseOrMintIn.receiver, releaseOrMintIn.amount); | ||
|
||
return Pool.ReleaseOrMintOutV1({localToken: address(i_token), destinationAmount: releaseOrMintIn.amount}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
// SPDX-License-Identifier: BUSL-1.1 | ||
pragma solidity ^0.8.0; | ||
|
||
import {IPoolPriorTo1_5} from "../interfaces/IPoolPriorTo1_5.sol"; | ||
|
||
import {Pool} from "../libraries/Pool.sol"; | ||
import {TokenPool} from "./TokenPool.sol"; | ||
|
||
import {IERC20} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol"; | ||
|
||
abstract contract LegacyPoolWrapper is TokenPool { | ||
event LegacyPoolChanged(IPoolPriorTo1_5 oldPool, IPoolPriorTo1_5 newPool); | ||
|
||
/// @dev The previous pool, if there is any. This is a property to make the older 1.0-1.4 pools | ||
/// compatible with the current 1.5 pool. To achieve this, we set the previous pool address to the | ||
/// currently deployed legacy pool. Then we configure this new pool as onRamp and offRamp on the legacy pools. | ||
/// In the case of a 1.4 pool, this new pool contract has to be set to the Router as well, as it validates | ||
/// who can call it through the router calls. This contract will always return itself as the only allowed ramp. | ||
/// @dev Can be address(0), this would indicate that this pool is operating as a normal pool as opposed to | ||
/// a proxy pool. | ||
IPoolPriorTo1_5 internal s_previousPool; | ||
|
||
constructor( | ||
IERC20 token, | ||
address[] memory allowlist, | ||
address armProxy, | ||
address router | ||
) TokenPool(token, allowlist, armProxy, router) {} | ||
|
||
// ================================================================ | ||
// │ Legacy Fallbacks │ | ||
// ================================================================ | ||
// Legacy fallbacks for older token pools that do not implement the new interface. | ||
|
||
/// @notice Legacy fallback for the 1.4 token pools. | ||
function getOnRamp(uint64) external view returns (address onRampAddress) { | ||
return address(this); | ||
} | ||
|
||
/// @notice Return true if the given offRamp is a configured offRamp for the given source chain. | ||
function isOffRamp(uint64 sourceChainSelector, address offRamp) external view returns (bool) { | ||
return offRamp == address(this) || s_router.isOffRamp(sourceChainSelector, offRamp); | ||
} | ||
|
||
/// @notice Configures the legacy fallback option. If the previous pool is set, this pool will act as a proxy for | ||
/// the legacy pool. | ||
/// @param prevPool The address of the previous pool. | ||
function setPreviousPool(IPoolPriorTo1_5 prevPool) external onlyOwner { | ||
IPoolPriorTo1_5 oldPrevPool = s_previousPool; | ||
s_previousPool = prevPool; | ||
|
||
emit LegacyPoolChanged(oldPrevPool, prevPool); | ||
} | ||
|
||
function _hasLegacyPool() internal view returns (bool) { | ||
return address(s_previousPool) != address(0); | ||
} | ||
|
||
function _lockOrBurnLegacy(Pool.LockOrBurnInV1 memory lockOrBurnIn) internal { | ||
i_token.transfer(address(s_previousPool), lockOrBurnIn.amount); | ||
s_previousPool.lockOrBurn( | ||
lockOrBurnIn.originalSender, lockOrBurnIn.receiver, lockOrBurnIn.amount, lockOrBurnIn.remoteChainSelector, "" | ||
); | ||
} | ||
|
||
/// @notice This call converts the arguments from a >=1.5 pool call to those of a <1.5 pool call, and uses these | ||
/// to call the previous pool. | ||
/// @param releaseOrMintIn The 1.5 style release or mint arguments. | ||
/// @dev Since extraData has never been used in LockRelease or MintBurn token pools, we can safely ignore it. | ||
function _releaseOrMintLegacy(Pool.ReleaseOrMintInV1 memory releaseOrMintIn) internal { | ||
s_previousPool.releaseOrMint( | ||
releaseOrMintIn.originalSender, | ||
releaseOrMintIn.receiver, | ||
releaseOrMintIn.amount, | ||
releaseOrMintIn.remoteChainSelector, | ||
"" | ||
); | ||
} | ||
} |
Oops, something went wrong.