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

Add logic to allow cross-l2 bridging #3

Merged
merged 2 commits into from
Sep 16, 2024
Merged
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
7 changes: 4 additions & 3 deletions script/DeployGydL1CCIPEscrow.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pragma solidity ^0.8.17;
import "forge-std/Script.sol";

import {GydL1CCIPEscrow} from "src/GydL1CCIPEscrow.sol";
import {IGydBridge} from "src/IGydBridge.sol";

import {UUPSProxy} from "./UUPSProxy.sol";
import {ICREATE3Factory} from "./ICREATE3Factory.sol";
Expand Down Expand Up @@ -55,10 +56,10 @@ contract DeployGydL1CCIPEscrow is Script {
// Only support Arbitrum chain on deployment
GydL1CCIPEscrow.ChainData[] memory chains =
new GydL1CCIPEscrow.ChainData[](1);
chains[0] = GydL1CCIPEscrow.ChainData({
chains[0] = IGydBridge.ChainData({
chainSelector: arbitrumChainSelector,
metadata: GydL1CCIPEscrow.ChainMetadata({
gydAddress: l2Address,
metadata: IGydBridge.ChainMetadata({
targetAddress: l2Address,
gasLimit: gasLimit
})
});
Expand Down
80 changes: 27 additions & 53 deletions src/GydL1CCIPEscrow.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
pragma solidity ^0.8.17;

import {Initializable} from "upgradeable/proxy/utils/Initializable.sol";
import {StorageSlot} from "oz/utils/StorageSlot.sol";
import {UUPSUpgradeable} from "upgradeable/proxy/utils/UUPSUpgradeable.sol";
import {AccessControlDefaultAdminRulesUpgradeable} from
"upgradeable/access/extensions/AccessControlDefaultAdminRulesUpgradeable.sol";
Expand All @@ -12,13 +13,15 @@ import {IRouterClient} from "ccip/interfaces/IRouterClient.sol";
import {Client} from "ccip/libraries/Client.sol";
import {CCIPReceiverUpgradeable} from "./CCIPReceiverUpgradeable.sol";

import {IGydBridge} from "./IGydBridge.sol";
import {CCIPHelpers} from "./CCIPHelpers.sol";

/**
* @title GydL1CCipEscrow
* @notice Main smart contract to bridge GYD from Ethereum using Chainlink CCIP
*/
contract GydL1CCIPEscrow is
IGydBridge,
Initializable,
UUPSUpgradeable,
AccessControlDefaultAdminRulesUpgradeable,
Expand All @@ -28,15 +31,11 @@ contract GydL1CCIPEscrow is
using Address for address;
using Address for address payable;

struct ChainMetadata {
address gydAddress;
uint256 gasLimit;
}

struct ChainData {
uint64 chainSelector;
ChainMetadata metadata;
}
// Previously stored in a mapping(uint64 => uint256) at slot 4 where the
// uint64 is the CCIP chain selector
// only Arbitrum was used, so we compute its slot
bytes32 private constant _PREVIOUS_TOTAL_BRIDGED_SLOT =
keccak256(abi.encode(4_949_039_107_694_359_620, 4));

/// @notice GYD contract
IERC20 public gyd;
Expand All @@ -50,40 +49,7 @@ contract GydL1CCIPEscrow is
mapping(uint64 => ChainMetadata) public chainsMetadata;

/// @notice The total amount of GYD bridged per chain
mapping(uint64 => uint256) public totalBridgedGYD;

/// @notice This event is emitted when a new chain is added
event ChainAdded(
uint64 indexed chainSelector, address indexed gydAddress, uint256 gasLimit
);

/// @notice This event is emitted when the gas limit is updated
event GasLimitUpdated(uint64 indexed chainSelector, uint256 gasLimit);

/// @notice This event is emitted when the GYD is bridged
event GYDBridged(
uint64 indexed chainSelector,
address indexed bridger,
uint256 amount,
uint256 total
);

/// @notice This event is emitted when the GYD is claimed
event GYDClaimed(
uint64 indexed chainSelector,
address indexed bridger,
uint256 amount,
uint256 total
);

/// @notice This error is raised if message from the bridge is invalid
error MessageInvalid();

/// @notice This error is raised if the chain is not supported
error ChainNotSupported(uint64 chainSelector);

/// @notice This error is raised if the msg value is not enough for the fees
error FeesNotCovered(uint256 fees);
uint256 public totalBridgedGYD;

/// @notice Disable initializer on deploy
constructor() {
Expand Down Expand Up @@ -112,12 +78,20 @@ contract GydL1CCIPEscrow is
chainsMetadata[chains[i].chainSelector] = chains[i].metadata;
emit ChainAdded(
chains[i].chainSelector,
chains[i].metadata.gydAddress,
chains[i].metadata.targetAddress,
chains[i].metadata.gasLimit
);
}
}

function initializeTotalBridgedGYD() external {
if (totalBridgedGYD > 0) {
revert InvalidInitialization();
}
totalBridgedGYD =
StorageSlot.getUint256Slot(_PREVIOUS_TOTAL_BRIDGED_SLOT).value;
}

/**
* @dev The GydL1Escrow can only be upgraded by the owner
* @param v new GydL1Escrow implementation
Expand Down Expand Up @@ -179,21 +153,21 @@ contract GydL1CCIPEscrow is
gyd.safeTransferFrom(msg.sender, address(this), amount);

ChainMetadata memory chainMeta = chainsMetadata[destinationChainSelector];
if (chainMeta.gydAddress == address(0)) {
if (chainMeta.targetAddress == address(0)) {
revert ChainNotSupported(destinationChainSelector);
}

Client.EVM2AnyMessage memory evm2AnyMessage = CCIPHelpers.buildCCIPMessage(
chainMeta.gydAddress, recipient, amount, data, chainMeta.gasLimit
chainMeta.targetAddress, recipient, amount, data, chainMeta.gasLimit
);
uint256 fees = router.getFee(destinationChainSelector, evm2AnyMessage);
CCIPHelpers.sendCCIPMessage(
router, destinationChainSelector, evm2AnyMessage, fees
);

uint256 bridged = totalBridgedGYD[destinationChainSelector];
uint256 bridged = totalBridgedGYD;
bridged += amount;
totalBridgedGYD[destinationChainSelector] = bridged;
totalBridgedGYD = bridged;
emit GYDBridged(destinationChainSelector, msg.sender, amount, bridged);
}

Expand All @@ -212,12 +186,12 @@ contract GydL1CCIPEscrow is
bytes memory data
) public view returns (uint256) {
ChainMetadata memory chainMeta = chainsMetadata[destinationChainSelector];
if (chainMeta.gydAddress == address(0)) {
if (chainMeta.targetAddress == address(0)) {
revert ChainNotSupported(destinationChainSelector);
}

Client.EVM2AnyMessage memory evm2AnyMessage = CCIPHelpers.buildCCIPMessage(
chainMeta.gydAddress, recipient, amount, data, chainMeta.gasLimit
chainMeta.targetAddress, recipient, amount, data, chainMeta.gasLimit
);
return router.getFee(destinationChainSelector, evm2AnyMessage);
}
Expand All @@ -240,7 +214,7 @@ contract GydL1CCIPEscrow is
override
{
address expectedSender =
chainsMetadata[any2EvmMessage.sourceChainSelector].gydAddress;
chainsMetadata[any2EvmMessage.sourceChainSelector].targetAddress;
if (expectedSender == address(0)) {
revert ChainNotSupported(any2EvmMessage.sourceChainSelector);
}
Expand All @@ -251,9 +225,9 @@ contract GydL1CCIPEscrow is

(address recipient, uint256 amount, bytes memory data) =
abi.decode(any2EvmMessage.data, (address, uint256, bytes));
uint256 bridged = totalBridgedGYD[any2EvmMessage.sourceChainSelector];
uint256 bridged = totalBridgedGYD;
bridged -= amount;
totalBridgedGYD[any2EvmMessage.sourceChainSelector] = bridged;
totalBridgedGYD = bridged;

gyd.safeTransfer(recipient, amount);
if (data.length > 0) {
Expand Down
49 changes: 49 additions & 0 deletions src/IGydBridge.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

interface IGydBridge {
struct ChainMetadata {
address targetAddress;
uint256 gasLimit;
}

struct ChainData {
uint64 chainSelector;
ChainMetadata metadata;
}

/// @notice This event is emitted when a new chain is added
event ChainAdded(
uint64 indexed chainSelector,
address indexed targetAddress,
uint256 gasLimit
);

/// @notice This event is emitted when the gas limit is updated
event GasLimitUpdated(uint64 indexed chainSelector, uint256 gasLimit);

/// @notice This event is emitted when the GYD is bridged
event GYDBridged(
uint64 indexed chainSelector,
address indexed bridger,
uint256 amount,
uint256 total
);

/// @notice This event is emitted when the GYD is claimed
event GYDClaimed(
uint64 indexed chainSelector,
address indexed bridger,
uint256 amount,
uint256 total
);

/// @notice This error is raised if message from the bridge is invalid
error MessageInvalid();

/// @notice This error is raised if the chain is not supported
error ChainNotSupported(uint64 chainSelector);

/// @notice This error is raised if the msg value is not enough for the fees
error FeesNotCovered(uint256 fees);
}
Loading