Skip to content

Commit 454820b

Browse files
authored
feat: add Lisk adapter/spoke pool (#537)
* add Lisk adapter/spoke pool --------- Signed-off-by: bennett <bennett@umaproject.org>
1 parent c2e324d commit 454820b

File tree

6 files changed

+213
-1
lines changed

6 files changed

+213
-1
lines changed

contracts/Lisk_SpokePool.sol

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// SPDX-License-Identifier: BUSL-1.1
2+
pragma solidity ^0.8.0;
3+
import "@eth-optimism/contracts/libraries/constants/Lib_PredeployAddresses.sol";
4+
5+
import "./Ovm_SpokePool.sol";
6+
import "./external/interfaces/CCTPInterfaces.sol";
7+
8+
/**
9+
* @notice Lisk Spoke pool.
10+
* @custom:security-contact bugs@umaproject.org
11+
*/
12+
contract Lisk_SpokePool is Ovm_SpokePool {
13+
/// @custom:oz-upgrades-unsafe-allow constructor
14+
constructor(
15+
address _wrappedNativeTokenAddress,
16+
uint32 _depositQuoteTimeBuffer,
17+
uint32 _fillDeadlineBuffer,
18+
IERC20 _l2Usdc,
19+
ITokenMessenger _cctpTokenMessenger
20+
)
21+
Ovm_SpokePool(
22+
_wrappedNativeTokenAddress,
23+
_depositQuoteTimeBuffer,
24+
_fillDeadlineBuffer,
25+
_l2Usdc,
26+
_cctpTokenMessenger
27+
)
28+
{} // solhint-disable-line no-empty-blocks
29+
30+
/**
31+
* @notice Construct the OVM Lisk SpokePool.
32+
* @param _initialDepositId Starting deposit ID. Set to 0 unless this is a re-deployment in order to mitigate
33+
* relay hash collisions.
34+
* @param _crossDomainAdmin Cross domain admin to set. Can be changed by admin.
35+
* @param _hubPool Hub pool address to set. Can be changed by admin.
36+
*/
37+
function initialize(
38+
uint32 _initialDepositId,
39+
address _crossDomainAdmin,
40+
address _hubPool
41+
) public initializer {
42+
__OvmSpokePool_init(_initialDepositId, _crossDomainAdmin, _hubPool, Lib_PredeployAddresses.OVM_ETH);
43+
}
44+
}
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
// SPDX-License-Identifier: BUSL-1.1
2+
pragma solidity ^0.8.0;
3+
4+
import "./interfaces/AdapterInterface.sol";
5+
import "../external/interfaces/WETH9Interface.sol";
6+
7+
// @dev Use local modified CrossDomainEnabled contract instead of one exported by eth-optimism because we need
8+
// this contract's state variables to be `immutable` because of the delegateCall call.
9+
import "./CrossDomainEnabled.sol";
10+
import "@eth-optimism/contracts/L1/messaging/IL1StandardBridge.sol";
11+
12+
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
13+
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
14+
15+
import "../libraries/CircleCCTPAdapter.sol";
16+
import "../external/interfaces/CCTPInterfaces.sol";
17+
18+
/**
19+
* @notice Contract containing logic to send messages from L1 to Lisk. This is a clone of the Base/Mode adapter
20+
* @dev Public functions calling external contracts do not guard against reentrancy because they are expected to be
21+
* called via delegatecall, which will execute this contract's logic within the context of the originating contract.
22+
* For example, the HubPool will delegatecall these functions, therefore its only necessary that the HubPool's methods
23+
* that call this contract's logic guard against reentrancy.
24+
* @custom:security-contact bugs@umaproject.org
25+
*/
26+
27+
// solhint-disable-next-line contract-name-camelcase
28+
contract Lisk_Adapter is CrossDomainEnabled, AdapterInterface, CircleCCTPAdapter {
29+
using SafeERC20 for IERC20;
30+
uint32 public constant L2_GAS_LIMIT = 200_000;
31+
32+
WETH9Interface public immutable L1_WETH;
33+
34+
IL1StandardBridge public immutable L1_STANDARD_BRIDGE;
35+
36+
/**
37+
* @notice Constructs new Adapter.
38+
* @param _l1Weth WETH address on L1.
39+
* @param _crossDomainMessenger XDomainMessenger Lisk system contract.
40+
* @param _l1StandardBridge Standard bridge contract.
41+
* @param _l1Usdc USDC address on L1.
42+
*/
43+
constructor(
44+
WETH9Interface _l1Weth,
45+
address _crossDomainMessenger,
46+
IL1StandardBridge _l1StandardBridge,
47+
IERC20 _l1Usdc
48+
)
49+
CrossDomainEnabled(_crossDomainMessenger)
50+
CircleCCTPAdapter(
51+
_l1Usdc,
52+
// Hardcode cctp messenger to 0x0 to disable CCTP bridging.
53+
ITokenMessenger(address(0)),
54+
CircleDomainIds.UNINTIALIZED
55+
)
56+
{
57+
L1_WETH = _l1Weth;
58+
L1_STANDARD_BRIDGE = _l1StandardBridge;
59+
}
60+
61+
/**
62+
* @notice Send cross-chain message to target on Lisk.
63+
* @param target Contract on Lisk that will receive message.
64+
* @param message Data to send to target.
65+
*/
66+
function relayMessage(address target, bytes calldata message) external payable override {
67+
sendCrossDomainMessage(target, L2_GAS_LIMIT, message);
68+
emit MessageRelayed(target, message);
69+
}
70+
71+
/**
72+
* @notice Bridge tokens to Lisk.
73+
* @param l1Token L1 token to deposit.
74+
* @param l2Token L2 token to receive.
75+
* @param amount Amount of L1 tokens to deposit and L2 tokens to receive.
76+
* @param to Bridge recipient.
77+
*/
78+
function relayTokens(
79+
address l1Token,
80+
address l2Token,
81+
uint256 amount,
82+
address to
83+
) external payable override {
84+
// If the l1Token is weth then unwrap it to ETH then send the ETH to the standard bridge.
85+
if (l1Token == address(L1_WETH)) {
86+
L1_WETH.withdraw(amount);
87+
L1_STANDARD_BRIDGE.depositETHTo{ value: amount }(to, L2_GAS_LIMIT, "");
88+
}
89+
// Check if this token is USDC, which requires a custom bridge via CCTP.
90+
else if (_isCCTPEnabled() && l1Token == address(usdcToken)) {
91+
_transferUsdc(to, amount);
92+
} else {
93+
IL1StandardBridge _l1StandardBridge = L1_STANDARD_BRIDGE;
94+
95+
IERC20(l1Token).safeIncreaseAllowance(address(_l1StandardBridge), amount);
96+
_l1StandardBridge.depositERC20To(l1Token, l2Token, to, amount, L2_GAS_LIMIT, "");
97+
}
98+
emit TokensRelayed(l1Token, l2Token, amount, to);
99+
}
100+
}

contracts/chain-adapters/Mode_Adapter.sol

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import "../libraries/CircleCCTPAdapter.sol";
1616
import "../external/interfaces/CCTPInterfaces.sol";
1717

1818
/**
19-
* @notice Contract containing logic to send messages from L1 to Mode. This is a clone of the Mode adapter
19+
* @notice Contract containing logic to send messages from L1 to Mode. This is a clone of the Base adapter
2020
* @dev Public functions calling external contracts do not guard against reentrancy because they are expected to be
2121
* called via delegatecall, which will execute this contract's logic within the context of the originating contract.
2222
* For example, the HubPool will delegatecall these functions, therefore its only necessary that the HubPool's methods

deploy/042_deploy_lisk_adapter.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { ZERO_ADDRESS } from "@uma/common";
2+
import { L1_ADDRESS_MAP } from "./consts";
3+
import { DeployFunction } from "hardhat-deploy/types";
4+
import { HardhatRuntimeEnvironment } from "hardhat/types";
5+
6+
const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
7+
const { deployments, getNamedAccounts, getChainId, network } = hre;
8+
const { deploy } = deployments;
9+
10+
const { deployer } = await getNamedAccounts();
11+
12+
const chainId = parseInt(await getChainId());
13+
14+
await deploy("Lisk_Adapter", {
15+
from: deployer,
16+
log: true,
17+
skipIfAlreadyDeployed: true,
18+
args: [
19+
L1_ADDRESS_MAP[chainId].weth,
20+
L1_ADDRESS_MAP[chainId].liskCrossDomainMessenger,
21+
L1_ADDRESS_MAP[chainId].liskStandardBridge,
22+
L1_ADDRESS_MAP[chainId].usdc,
23+
// L1_ADDRESS_MAP[chainId].cctpTokenMessenger,
24+
// For now, we are not using the CCTP bridge and can disable by setting
25+
// the cctpTokenMessenger to the zero address.
26+
ZERO_ADDRESS,
27+
],
28+
});
29+
};
30+
31+
module.exports = func;
32+
func.tags = ["LiskAdapter", "mainnet"];
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { deployNewProxy, getSpokePoolDeploymentInfo } from "../utils/utils.hre";
2+
import { DeployFunction } from "hardhat-deploy/types";
3+
import { HardhatRuntimeEnvironment } from "hardhat/types";
4+
import { ZERO_ADDRESS } from "@uma/common";
5+
6+
const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
7+
const { hubPool, spokeChainId } = await getSpokePoolDeploymentInfo(hre);
8+
9+
const initArgs = [
10+
1,
11+
// Set hub pool as cross domain admin since it delegatecalls the Adapter logic.
12+
hubPool.address,
13+
hubPool.address,
14+
];
15+
// Construct this spokepool with a:
16+
// * A WETH address of the WETH address
17+
// * A depositQuoteTimeBuffer of 1 hour
18+
// * A fillDeadlineBuffer of 6 hours
19+
// * Native USDC address on L2
20+
// * CCTP token messenger address on L2
21+
const constructorArgs = [
22+
"0x4200000000000000000000000000000000000006",
23+
3600,
24+
21600,
25+
ZERO_ADDRESS,
26+
// L2_ADDRESS_MAP[spokeChainId].cctpTokenMessenger,
27+
// For now, we are not using the CCTP bridge and can disable by setting
28+
// the cctpTokenMessenger to the zero address.
29+
ZERO_ADDRESS,
30+
];
31+
await deployNewProxy("Lisk_SpokePool", constructorArgs, initArgs, spokeChainId === 1135);
32+
};
33+
module.exports = func;
34+
func.tags = ["LiskSpokePool", "mode"];

deploy/consts.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ export const L1_ADDRESS_MAP: { [key: number]: { [contractName: string]: string }
3333
scrollGasPriceOracle: "0x987e300fDfb06093859358522a79098848C33852",
3434
modeCrossDomainMessenger: "0x95bDCA6c8EdEB69C98Bd5bd17660BaCef1298A6f",
3535
modeStandardBridge: "0x735aDBbE72226BD52e818E7181953f42E3b0FF21",
36+
liskCrossDomainMessenger: "0x31B72D76FB666844C41EdF08dF0254875Dbb7edB",
37+
liskStandardBridge: "0x2658723Bf70c7667De6B25F99fcce13A16D25d08",
3638
},
3739
4: {
3840
weth: "0xc778417E063141139Fce010982780140Aa0cD5Ab",

0 commit comments

Comments
 (0)