-
Notifications
You must be signed in to change notification settings - Fork 105
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
212 additions
and
4 deletions.
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
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,16 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity 0.8.24; | ||
|
||
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; | ||
|
||
interface IBridgePermissions is IERC165 { | ||
/** | ||
* @dev Emitted when the permissions for the contract are updated. | ||
*/ | ||
event PermissionsUpdated(bool newPermissions); | ||
|
||
/** | ||
* @dev Returns true if the contract allows bridging of its assets. | ||
*/ | ||
function allowsBridging() external view returns (bool); | ||
} |
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,6 @@ | ||
pragma solidity 0.8.24; | ||
|
||
interface ICrossVMBridgeFulfillment { | ||
function fulfillToEVM(address to, uint256 id, bytes memory data) external; | ||
function vmBridgeAddress() external view returns (address); | ||
} |
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,53 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity 0.8.24; | ||
|
||
import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; | ||
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; | ||
import {ERC165Upgradeable} from "@openzeppelin/contracts-upgradeable/utils/introspection/ERC165Upgradeable.sol"; | ||
import {IBridgePermissions} from "../interfaces/IBridgePermissions.sol"; | ||
|
||
/** | ||
* @dev Contract for which implementation is checked by the Flow VM bridge as an opt-out mechanism | ||
* for non-standard asset contracts that wish to opt-out of bridging between Cadence & EVM. By | ||
* default, the VM bridge operates on a permissionless basis, meaning anyone can request an asset | ||
* be onboarded. However, some small subset of non-standard projects may wish to opt-out of this | ||
* and this contract provides a way to do so while also enabling future opt-in. | ||
* | ||
* Note: The Flow VM bridge checks for permissions at asset onboarding. If your asset has already | ||
* been onboarded, setting `permissions` to `false` will not affect movement between VMs. | ||
*/ | ||
abstract contract BridgePermissionsUpgradeable is Initializable, ERC165Upgradeable, IBridgePermissions { | ||
// The permissions for the contract to allow or disallow bridging of its assets. | ||
bool private _permissions; | ||
|
||
function __BridgePermissions_init() internal onlyInitializing { | ||
_permissions = false; | ||
} | ||
|
||
/** | ||
* @dev See {IERC165-supportsInterface}. | ||
*/ | ||
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165Upgradeable, IERC165) returns (bool) { | ||
return interfaceId == type(IBridgePermissions).interfaceId || super.supportsInterface(interfaceId); | ||
} | ||
|
||
/** | ||
* @dev Returns true if the contract allows bridging of its assets. Checked by the Flow VM | ||
* bridge at asset onboarding to enable non-standard asset contracts to opt-out of bridging | ||
* between Cadence & EVM. Implementing this contract opts out by default but can be | ||
* overridden to opt-in or used in conjunction with a switch to enable opting in. | ||
*/ | ||
function allowsBridging() external view virtual returns (bool) { | ||
return _permissions; | ||
} | ||
|
||
/** | ||
* @dev Set the permissions for the contract to allow or disallow bridging of its assets. | ||
* | ||
* Emits a {PermissionsUpdated} event. | ||
*/ | ||
function _setPermissions(bool permissions) internal { | ||
_permissions = permissions; | ||
emit PermissionsUpdated(permissions); | ||
} | ||
} |
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,52 @@ | ||
// SPDX-License-Identifier: Unlicense | ||
pragma solidity 0.8.24; | ||
|
||
import {ContextUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol"; | ||
import {ICrossVMBridgeFulfillment} from "../interfaces/ICrossVMBridgeFulfillment.sol"; | ||
|
||
/** | ||
* @title CrossVMBridgeCallable | ||
* @dev A base contract intended for use in implementations on Flow, allowing a contract to define | ||
* access to the Cadence X EVM bridge on certain methods. | ||
*/ | ||
abstract contract CrossVMBridgeCallableUpgradeable is ContextUpgradeable { | ||
|
||
address private _vmBridgeAddress; | ||
|
||
error CrossVMBridgeCallableZeroInitialization(); | ||
error CrossVMBridgeCallableUnauthorizedAccount(address account); | ||
|
||
/** | ||
* @dev Sets the bridge EVM address such that only the bridge COA can call the privileged methods | ||
*/ | ||
function _init_vm_bridge_address(address vmBridgeAddress_) internal { | ||
if (vmBridgeAddress_ == address(0)) { | ||
revert CrossVMBridgeCallableZeroInitialization(); | ||
} | ||
_vmBridgeAddress = vmBridgeAddress_; | ||
} | ||
|
||
/** | ||
* @dev Modifier restricting access to the designated VM bridge EVM address | ||
*/ | ||
modifier onlyVMBridge() { | ||
_checkVMBridgeAddress(); | ||
_; | ||
} | ||
|
||
/** | ||
* @dev Returns the designated VM bridge’s EVM address | ||
*/ | ||
function vmBridgeAddress() public view virtual returns (address) { | ||
return _vmBridgeAddress; | ||
} | ||
|
||
/** | ||
* @dev Checks that msg.sender is the designated vm bridge address | ||
*/ | ||
function _checkVMBridgeAddress() internal view virtual { | ||
if (vmBridgeAddress() != _msgSender()) { | ||
revert CrossVMBridgeCallableUnauthorizedAccount(_msgSender()); | ||
} | ||
} | ||
} |
66 changes: 66 additions & 0 deletions
66
evm-bridging/src/lib/CrossVMBridgeFulfillmentUpgradeable.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,66 @@ | ||
// SPDX-License-Identifier: Unlicense | ||
pragma solidity 0.8.24; | ||
|
||
import {CrossVMBridgeCallableUpgradeable} from "./CrossVMBridgeCallableUpgradeable.sol"; | ||
import {ERC721Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol"; | ||
import {ERC165Upgradeable} from "@openzeppelin/contracts-upgradeable/utils/introspection/ERC165Upgradeable.sol"; | ||
import {ICrossVMBridgeFulfillment} from "../interfaces/ICrossVMBridgeFulfillment.sol"; | ||
|
||
abstract contract CrossVMBridgeFulfillmentUpgradeable is CrossVMBridgeCallableUpgradeable, ERC165Upgradeable, ERC721Upgradeable { | ||
|
||
error FulfillmentFailedTokenNotEscrowed(uint256 id, address escrowAddress); | ||
|
||
function __CrossVMBridgeFulfillment_init(address vmBridgeAddress_) internal onlyInitializing { | ||
__CrossVMBridgeFulfillment_init_unchained(vmBridgeAddress_); | ||
} | ||
|
||
function __CrossVMBridgeFulfillment_init_unchained(address vmBridgeAddress_) internal onlyInitializing { | ||
_init_vm_bridge_address(vmBridgeAddress_); | ||
} | ||
|
||
/** | ||
* @dev Fulfills the bridge request, minting (if non-existent) or transferring (if escrowed) the | ||
* token with the given ID to the provided address. For dynamic metadata handling between | ||
* Cadence & EVM, implementations should override and assign metadata as encoded from Cadence | ||
* side. If overriding, be sure to preserve the mint/escrow pattern as shown in the default | ||
* implementation. | ||
* | ||
* @param _to address of the token recipient | ||
* @param _id the id of the token being moved into EVM from Cadence | ||
*/ | ||
function fulfillToEVM(address _to, uint256 _id, bytes memory /*_data*/) external onlyVMBridge { | ||
if (_ownerOf(_id) == address(0)) { | ||
_validateMint(_to, _id); | ||
_mint(_to, _id); // Doesn't exist, mint the token | ||
} else { | ||
// Should be escrowed under vm bridge - transfer from escrow to recipient | ||
_requireEscrowed(_id); | ||
safeTransferFrom(vmBridgeAddress(), _to, _id); | ||
} | ||
} | ||
|
||
function _validateMint(address _to, uint256 _id) internal view { | ||
// no-op, override in implementation if needed | ||
} | ||
|
||
/** | ||
* @dev Allows a caller to determine the contract conforms to the `ICrossVMFulfillment` interface | ||
*/ | ||
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165Upgradeable, ERC721Upgradeable) returns (bool) { | ||
return interfaceId == type(ICrossVMBridgeFulfillment).interfaceId || super.supportsInterface(interfaceId); | ||
} | ||
|
||
/** | ||
* @dev Internal method that reverts with FulfillmentFailedTokenNotEscrowed if the provided | ||
* token is not escrowed with the assigned vm bridge address as owner. | ||
* | ||
* @param _id the token id that must be escrowed | ||
*/ | ||
function _requireEscrowed(uint256 _id) internal view { | ||
address owner = _ownerOf(_id); | ||
address vmBridgeAddress_ = vmBridgeAddress(); | ||
if (owner != vmBridgeAddress_) { | ||
revert FulfillmentFailedTokenNotEscrowed(_id, vmBridgeAddress_); | ||
} | ||
} | ||
} |
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