Untitled
unknown
plain_text
a year ago
12 kB
11
Indexable
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;
import { OApp, Origin, MessagingFee } from "@layerzerolabs/lz-evm-oapp-v2/contracts/oapp/OApp.sol";
import { OAppOptionsType3 } from "@layerzerolabs/lz-evm-oapp-v2/contracts/oapp/libs/OAppOptionsType3.sol";
import { IOAppMsgInspector } from "@layerzerolabs/lz-evm-oapp-v2/contracts/oapp/interfaces/IOAppMsgInspector.sol";
import { OAppPreCrimeSimulator } from "@layerzerolabs/lz-evm-oapp-v2/contracts/precrime/OAppPreCrimeSimulator.sol";
import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
import { ERC721 } from "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import { ERC721URIStorage } from "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import { OFTComposeMsgCodec } from "@layerzerolabs/lz-evm-oapp-v2/contracts/oft/libs/OFTComposeMsgCodec.sol";
import { MessagingReceipt, MessagingFee } from "@layerzerolabs/lz-evm-oapp-v2/contracts/oft/interfaces/IOFT.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
/**
* @dev Struct representing OFT limit information.
* @dev These amounts can change dynamically and are up the the specific oft implementation.
*/
struct OFTLimit {
uint256 tokenID;
}
/**
* @dev Struct representing OFT receipt information.
*/
struct OFTReceipt {
uint256 tokenID;
}
/**
* @dev Struct representing OFT fee details.
* @dev Future proof mechanism to provide a standardized way to communicate fees to things like a UI.
*/
struct OFTFeeDetail {
int256 feeAmountLD; // Amount of the fee in local decimals.
string description; // Description of the fee.
}
struct MessagingParams {
uint32 dstEid;
bytes32 receiver;
bytes message;
bytes options;
bool payInLzToken;
}
/**
* @dev Struct representing token parameters for the OFT send() operation.
*/
struct SendParam {
uint32 dstEid; // Destination endpoint ID.
bytes32 to; // Recipient address.
uint32 tokenID; // Token ID to transfer..
string tokenURI; // Token URI.
bytes extraOptions;
}
contract OFT721 is OApp, ERC721, ERC721URIStorage {
using Counters for Counters.Counter;
Counters.Counter private _tokenIdTracker;
string private _baseTokenURI;
uint256 private mintRate;
event TokenMinted(address recipient, uint256 tokenId);
event testAction(string action);
// Events
event OFTSent(
bytes32 indexed guid, // GUID of the OFT message.
uint32 dstEid, // Destination Endpoint ID.
address indexed fromAddress, // Address of the sender on the src chain.
uint256 tokenID
);
event OFTReceived(
bytes32 indexed guid, // GUID of the OFT message.
uint32 srcEid, // Source Endpoint ID.
address indexed toAddress, // Address of the recipient on the dst chain.
uint256 tokenID
);
constructor(
address _endpoint,
address _owner,
string memory _name,
string memory _symbol,
uint256 _initialMintRate,
string memory baseTokenURI,
uint256 _tokenIDStart
) OApp(_endpoint, _owner) ERC721(_name, _symbol) Ownable(_owner) {
_baseTokenURI = baseTokenURI;
mintRate = _initialMintRate;
_tokenIdTracker._value = _tokenIDStart;
}
/**
* @dev Called when data is received from the protocol. It overrides the equivalent function in the parent contract.
* Protocol messages are defined as packets, comprised of the following parameters.
* @param _origin A struct containing information about where the packet came from.
* @param _guid A global unique identifier for tracking the packet.
* @param payload Encoded message.
*/
// Your function implementation
function _lzReceive(
Origin calldata _origin,
bytes32 _guid,
bytes calldata payload,
address, // Executor address as specified by the OApp.
bytes calldata // Any extra data or options to trigger on receipt.
) internal override {
// Declare inFlightToken and decode the payload
SendParam memory inFlightToken = abi.decode(payload, (SendParam));
// Use inFlightToken for the _credit function
if (!_credit(bytes32ToAddress(inFlightToken.to), inFlightToken.tokenID, inFlightToken.tokenURI)) {
revert("Failed to mint token on destination chain");
}
}
/**
* @dev Check if the peer is considered 'trusted' by the OApp.
* @param _eid The endpoint ID to check.
* @param _peer The peer to check.
* @return Whether the peer passed is considered 'trusted' by the OApp.
*
* @dev Enables OAppPreCrimeSimulator to check whether a potential Inbound Packet is from a trusted source.
*/
function isPeer(uint32 _eid, bytes32 _peer) public view virtual returns (bool) {
return peers[_eid] == _peer;
}
function _debit(
address _from,
uint256 _tokenID,
uint32 _dstEid
) internal virtual returns (bool success) {
// @dev Burn the token on the source chain.
if (ownerOf(_tokenID) != _from) {
revert("Transfer of token that is not own");
}
_burn(_tokenID);
return true;
}
function _credit(
address _to,
uint256 _tokenID,
string memory _tokenURI
) internal virtual returns (bool success) {
// @dev Mint the token on the destination chain.
_mint(_to, _tokenID);
_setTokenURI(_tokenID, _tokenURI);
return true;
}
/**
* @dev Executes the send operation.
* @param _sendParam The parameters for the send operation.
* @param _fee The calculated fee for the send() operation.
* - nativeFee: The native fee.
* - lzTokenFee: The lzToken fee.
* @param _refundAddress The address to receive any excess funds.
* @return msgReceipt The receipt for the send operation.
* @return oftReceipt The OFT receipt information.
*
* @dev MessagingReceipt: LayerZero msg receipt
* - guid: The unique identifier for the sent message.
* - nonce: The nonce of the sent message.
* - fee: The LayerZero fee incurred for the message.
*/
function send(
SendParam calldata _sendParam,
MessagingFee calldata _fee,
address _refundAddress
) external payable virtual returns (MessagingReceipt memory msgReceipt, OFTReceipt memory oftReceipt) {
if (!_debit(msg.sender, _sendParam.tokenID, _sendParam.dstEid)) {
revert("Transfer of token that is not own or failed to burn token.");
}
(bytes memory message, bytes memory options) = _buildMsgAndOptions(_sendParam);
require(message.length > 0, "Failed to build message.");
msgReceipt = _lzSend(_sendParam.dstEid, message, options, _fee, _refundAddress);
require(msgReceipt.guid != bytes32(0), "Failed to send message to LayerZero endpoint.");
oftReceipt = OFTReceipt(_sendParam.tokenID);
emit OFTSent(msgReceipt.guid, _sendParam.dstEid, msg.sender, _sendParam.tokenID);
}
/* @dev Quotes the gas needed to pay for the full omnichain transaction.
* @return nativeFee Estimated gas fee in native gas.
* @return lzTokenFee Estimated gas fee in ZRO token.
*/
function quote(
uint32 _dstEid, // Destination chain's endpoint ID.
SendParam memory _message, // The message to send.
bytes calldata _options, // Message execution options
bool _payInLzToken // boolean for which token to return fee in
) public view returns (uint256 nativeFee, uint256 lzTokenFee) {
bytes memory _payload = abi.encode(_message);
MessagingFee memory fee = _quote(_dstEid, _payload, _options, _payInLzToken);
return (fee.nativeFee, fee.lzTokenFee);
}
/**
* @dev Encodes an OFT LayerZero message.
* @param _sendTo The recipient address.
* @param _tokenID The token ID.
* @param _tokenURI The token URI..
* @return _msg The encoded message.
*/
function encode(
bytes32 _sendTo,
uint64 _tokenID,
string memory _tokenURI
) internal view returns (bytes memory _msg) {
_msg = abi.encodePacked(_sendTo, _tokenID, _tokenURI);
}
/**
* @dev Converts an address to bytes32.
* @param _addr The address to convert.
* @return The bytes32 representation of the address.
*/
function addressToBytes32(address _addr) internal pure returns (bytes32) {
return bytes32(uint256(uint160(_addr)));
}
/**
* @dev Converts bytes32 to an address.
* @param _b The bytes32 value to convert.
* @return The address representation of bytes32.
*/
function bytes32ToAddress(bytes32 _b) internal pure returns (address) {
return address(uint160(uint256(_b)));
}
/**
* @dev Internal function to build the message and options.
* @param _sendParam The parameters for the send() operation.
* @return message The encoded message.
* @return options The encoded options.
*/
function _buildMsgAndOptions(
SendParam calldata _sendParam
) internal view virtual returns (bytes memory message, bytes memory options) {
bool hasCompose;
// @dev This generated message has the msg.sender encoded into the payload so the remote knows who the caller is.
message = encode(
_sendParam.to,
_sendParam.tokenID,
_sendParam.tokenURI
);
// @dev Change the msg type depending if its composed or not.
// uint16 msgType = hasCompose ? SEND_AND_CALL : SEND;
// @dev Combine the callers _extraOptions with the enforced options via the OAppOptionsType3.
options = _sendParam.extraOptions; // combineOptions(_sendParam.dstEid, msgType, _sendParam.extraOptions);
// @dev Optionally inspect the message and options depending if the OApp owner has set a msg inspector.
// @dev If it fails inspection, needs to revert in the implementation. ie. does not rely on return boolean
// if (msgInspector != address(0)) IOAppMsgInspector(msgInspector).inspect(message, options);
}
function tokenURI(uint256 tokenId) public view virtual override(ERC721, ERC721URIStorage) returns (string memory) {
return super.tokenURI(tokenId);
}
/**
* @dev Returns the base URI set for the tokens.
*/
function _baseURI() internal view virtual override returns (string memory) {
return _baseTokenURI;
}
/**
* @dev Public mint function to mint new tokens with metadata.
* @param recipient The address to mint the token to.
* @param metadataURI The metadata URI to associate with the token.
*/
function mint(address recipient, string memory metadataURI) public virtual payable {
require(msg.value >= mintRate, "Insufficient mint fee");
uint256 tokenId = _tokenIdTracker.current();
_mint(recipient, tokenId);
_setTokenURI(tokenId, metadataURI);
_tokenIdTracker.increment();
emit TokenMinted(recipient, tokenId);
}
/**
* @dev Sets the mint rate for new tokens.
* @param _mintRate The new mint rate.
*/
function setRate(uint256 _mintRate) external onlyOwner {
mintRate = _mintRate;
}
/**
* @dev Withdraws the balance of the contract to the owner's address.
*/
function withdraw() external onlyOwner {
uint256 amount = address(this).balance;
require(amount > 0, "No funds to withdraw");
payable(owner()).transfer(amount);
}
/**
* @dev See {ERC721-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override(ERC721, ERC721URIStorage) returns (bool) {
return super.supportsInterface(interfaceId);
}
}Editor is loading...
Leave a Comment