Skip to content

Commit

Permalink
Merge pull request #3 from gyrostable/support-cross-l2-bridge
Browse files Browse the repository at this point in the history
Add logic to allow cross-l2 bridging
  • Loading branch information
danhper committed Sep 16, 2024
2 parents 1eb2b83 + a3aa3ba commit da867e6
Show file tree
Hide file tree
Showing 7 changed files with 233 additions and 172 deletions.
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

0 comments on commit da867e6

Please sign in to comment.