// SPDX-License-Identifier: MITpragmasolidity ^0.8.0;/**
* @dev Collection of functions related to the address type
*/libraryAddress{
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
* ====
*/functionisContract(address account) internalviewreturns (bool) {
// This method relies on extcodesize, which returns 0 for contracts in// construction, since the code is only stored at the end of the// constructor execution.uint256 size;
// solhint-disable-next-line no-inline-assemblyassembly { size :=extcodesize(account) }
return size >0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/functionsendValue(addresspayable recipient, uint256 amount) internal{
require(address(this).balance>= amount, "Address: insufficient balance");
// solhint-disable-next-line avoid-low-level-calls, avoid-call-value
(bool success, ) = recipient.call{ value: amount }("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain`call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/functionfunctionCall(address target, bytesmemory data) internalreturns (bytesmemory) {
return functionCall(target, data, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/functionfunctionCall(address target, bytesmemory data, stringmemory errorMessage) internalreturns (bytesmemory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/functionfunctionCallWithValue(address target, bytesmemory data, uint256 value) internalreturns (bytesmemory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/functionfunctionCallWithValue(address target, bytesmemory data, uint256 value, stringmemory errorMessage) internalreturns (bytesmemory) {
require(address(this).balance>= value, "Address: insufficient balance for call");
require(isContract(target), "Address: call to non-contract");
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytesmemory returndata) = target.call{ value: value }(data);
return _verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/functionfunctionStaticCall(address target, bytesmemory data) internalviewreturns (bytesmemory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/functionfunctionStaticCall(address target, bytesmemory data, stringmemory errorMessage) internalviewreturns (bytesmemory) {
require(isContract(target), "Address: static call to non-contract");
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytesmemory returndata) = target.staticcall(data);
return _verifyCallResult(success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/functionfunctionDelegateCall(address target, bytesmemory data) internalreturns (bytesmemory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/functionfunctionDelegateCall(address target, bytesmemory data, stringmemory errorMessage) internalreturns (bytesmemory) {
require(isContract(target), "Address: delegate call to non-contract");
// solhint-disable-next-line avoid-low-level-calls
(bool success, bytesmemory returndata) = target.delegatecall(data);
return _verifyCallResult(success, returndata, errorMessage);
}
function_verifyCallResult(bool success, bytesmemory returndata, stringmemory errorMessage) privatepurereturns(bytesmemory) {
if (success) {
return returndata;
} else {
// Look for revert reason and bubble it up if presentif (returndata.length>0) {
// The easiest way to bubble the revert reason is using memory via assembly// solhint-disable-next-line no-inline-assemblyassembly {
let returndata_size :=mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
}
Contract Source Code
File 2 of 23: Context.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.0;/*
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/abstractcontractContext{
function_msgSender() internalviewvirtualreturns (address) {
returnmsg.sender;
}
function_msgData() internalviewvirtualreturns (bytescalldata) {
this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691returnmsg.data;
}
}
Contract Source Code
File 3 of 23: IERC20.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.0;/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/interfaceIERC20{
/**
* @dev Returns the amount of tokens in existence.
*/functiontotalSupply() externalviewreturns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/functionbalanceOf(address account) externalviewreturns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `recipient`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/functiontransfer(address recipient, uint256 amount) externalreturns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/functionallowance(address owner, address spender) externalviewreturns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/functionapprove(address spender, uint256 amount) externalreturns (bool);
/**
* @dev Moves `amount` tokens from `sender` to `recipient` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/functiontransferFrom(address sender, address recipient, uint256 amount) externalreturns (bool);
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/eventTransfer(addressindexedfrom, addressindexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/eventApproval(addressindexed owner, addressindexed spender, uint256 value);
}
Contract Source Code
File 4 of 23: IERC20Metadata.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.0;import"../IERC20.sol";
/**
* @dev Interface for the optional metadata functions from the ERC20 standard.
*/interfaceIERC20MetadataisIERC20{
/**
* @dev Returns the name of the token.
*/functionname() externalviewreturns (stringmemory);
/**
* @dev Returns the symbol of the token.
*/functionsymbol() externalviewreturns (stringmemory);
/**
* @dev Returns the decimals places of the token.
*/functiondecimals() externalviewreturns (uint8);
}
// SPDX-License-Identifier: MITpragmasolidity ^0.8.20;import { ILayerZeroEndpointV2 } from"../../../../lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol";
/**
* @title IOAppCore
*/interfaceIOAppCore{
// Custom error messageserrorOnlyPeer(uint32 eid, bytes32 sender);
errorNoPeer(uint32 eid);
errorInvalidEndpointCall();
errorInvalidDelegate();
// Event emitted when a peer (OApp) is set for a corresponding endpointeventPeerSet(uint32 eid, bytes32 peer);
/**
* @notice Retrieves the OApp version information.
* @return senderVersion The version of the OAppSender.sol contract.
* @return receiverVersion The version of the OAppReceiver.sol contract.
*/functionoAppVersion() externalviewreturns (uint64 senderVersion, uint64 receiverVersion);
/**
* @notice Retrieves the LayerZero endpoint associated with the OApp.
* @return iEndpoint The LayerZero endpoint as an interface.
*/functionendpoint() externalviewreturns (ILayerZeroEndpointV2 iEndpoint);
/**
* @notice Retrieves the peer (OApp) associated with a corresponding endpoint.
* @param _eid The endpoint ID.
* @return peer The peer address (OApp instance) associated with the corresponding endpoint.
*/functionpeers(uint32 _eid) externalviewreturns (bytes32 peer);
/**
* @notice Sets the peer address (OApp instance) for a corresponding endpoint.
* @param _eid The endpoint ID.
* @param _peer The address of the peer to be associated with the corresponding endpoint.
*/functionsetPeer(uint32 _eid, bytes32 _peer) external;
/**
* @notice Sets the delegate address for the OApp Core.
* @param _delegate The address of the delegate to be set.
*/functionsetDelegate(address _delegate) external;
}
Contract Source Code
File 12 of 23: IOAppOptionsType3.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.20;/**
* @dev Struct representing enforced option parameters.
*/structEnforcedOptionParam {
uint32 eid; // Endpoint IDuint16 msgType; // Message Typebytes options; // Additional options
}
/**
* @title IOAppOptionsType3
* @dev Interface for the OApp with Type 3 Options, allowing the setting and combining of enforced options.
*/interfaceIOAppOptionsType3{
// Custom error message for invalid optionserrorInvalidOptions(bytes options);
// Event emitted when enforced options are seteventEnforcedOptionSet(EnforcedOptionParam[] _enforcedOptions);
/**
* @notice Sets enforced options for specific endpoint and message type combinations.
* @param _enforcedOptions An array of EnforcedOptionParam structures specifying enforced options.
*/functionsetEnforcedOptions(EnforcedOptionParam[] calldata _enforcedOptions) external;
/**
* @notice Combines options for a given endpoint and message type.
* @param _eid The endpoint ID.
* @param _msgType The OApp message type.
* @param _extraOptions Additional options passed by the caller.
* @return options The combination of caller specified options AND enforced options.
*/functioncombineOptions(uint32 _eid,
uint16 _msgType,
bytescalldata _extraOptions
) externalviewreturns (bytesmemory options);
}
Contract Source Code
File 13 of 23: IOAppReceiver.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.20;import { ILayerZeroReceiver, Origin } from"../../../../lz-evm-protocol-v2/contracts/interfaces/ILayerZeroReceiver.sol";
interfaceIOAppReceiverisILayerZeroReceiver{
/**
* @notice Indicates whether an address is an approved composeMsg sender to the Endpoint.
* @param _origin The origin information containing the source endpoint and sender address.
* - srcEid: The source chain endpoint ID.
* - sender: The sender address on the src chain.
* - nonce: The nonce of the message.
* @param _message The lzReceive payload.
* @param _sender The sender address.
* @return isSender Is a valid sender.
*
* @dev Applications can optionally choose to implement a separate composeMsg sender that is NOT the bridging layer.
* @dev The default sender IS the OAppReceiver implementer.
*/functionisComposeMsgSender(
Origin calldata _origin,
bytescalldata _message,
address _sender
) externalviewreturns (bool isSender);
}
Contract Source Code
File 14 of 23: IOptimistic.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.0;interfaceTradeInterface{
//PART 1: FUNCTIONS/**
* @dev Submits a new order.
* @param direction The direction parameters of the order (source asset, destination asset, and destination chain ID).
* @param funding The funding parameters of the order (amount, minimum output, bond fee, bond asset, and bond amount).
* @param expiration The expiration parameters of the order (timestamp, challenge offset, and challenge window).
* @param isMaker Indicates if the order maker is placing the order.
*/functionplaceOrder(
OrderDirection memory direction,
OrderFunding memory funding,
OrderExpiration memory expiration,
bool isMaker
) external;
/**
* @dev Creates a new match between orders.
* @param direction The direction parameters of the source order (source asset, destination asset, and destination chain ID).
* @param srcIndex The index of the source order.
* @param dstIndex The index of the destination order.
* @param Counterparty The wallet on the destination chain. This must be the same address as dest_order.sender
* @param srcQuantity The quantity of srcAsset in the match.
* @param dstQuantity The quantity of dstAsset in the match.
*/functioncreateMatch(
OrderDirection memory direction,
uint32 srcIndex,
uint32 dstIndex,
address Counterparty,
uint96 srcQuantity,
uint96 dstQuantity
) external;
/**
* @dev Executes an existing match.
* @param direction The direction parameters of the source order (source asset, destination asset, and destination chain ID).
* @param srcIndex The index of the source order.
* @param dstIndex The index of the destination order.
* @param srcQuantity The quantity of srcAsset in the match.
* @param dstQuantity The quantity of dstAsset in the match.
*/functionexecuteMatch(
OrderDirection memory direction,
uint32 srcIndex,
uint32 dstIndex,
address Counterparty,
uint96 srcQuantity,
uint96 dstQuantity,
bool isUnwrap
) external;
/**
* @dev Confirms a match.
* @param srcIndex The index of the source order.
*/functionconfirmMatch(
OrderDirection memory direction,
uint32 srcIndex
) external;
/**
* @dev Cancels an order.
* @param direction The direction parameters of the source order (source asset, destination asset, and destination chain ID).
* @param orderIndex The index of the order.
*/functioncancelOrder(
OrderDirection memory direction,
uint32 orderIndex
) external;
/**
* @dev Challenges an existing match.
* @param direction The direction parameters of the source order (source asset, destination asset, and destination chain ID).
* @param srcIndex The nonce of the match.
*/functionunwindMatch(
OrderDirection memory direction,
uint32 srcIndex
) external;
/**
* @dev Challenges an existing match.
* @param direction The direction parameters of the source order (source asset, destination asset, and destination chain ID).
* @param srcIndex The nonce of the match.
*/functionchallengeMatch(
OrderDirection memory direction,
uint32 srcIndex,
bytescalldata _extraSendOptions, // gas settings for A -> Bbytescalldata _extraReturnOptions // gas settings for B -> A) externalpayable;
// PART 2: STRUCTS/**
* @dev Struct representing an order.
* @param sender The address of the order creator.
* @param direction The direction parameters of the order.
* @param funding The funding parameters of the order.
* @param expiration The expiration parameters of the order.
* @param isMaker Indicates if the order maker is placing the order.
*/structOrder {
address sender;
OrderFunding funding;
OrderExpiration expiration;
uint96 settled;
bool isMaker;
}
/**
* @dev Struct for direction parameters of an order.
* @param srcAsset The source asset being offered.
* @param dstAsset The destination asset desired.
* @param dstLzc The chain ID of the destination chain.
*/structOrderDirection {
address srcAsset;
address dstAsset;
uint32 dstLzc;
}
/**
* @dev Struct for funding parameters of an order.
* @param srcQuantity The quantity of srcAsset being offered.
* @param dstQuantity The minimum quantity of dstAsset to be received.
* @param bondFee The basis points percentage which will go to the bonder.
* @param bondAsset The asset used for the bond.
* @param bondAmount The amount of the bond asset.
*/structOrderFunding {
uint96 srcQuantity;
uint96 dstQuantity;
uint16 bondFee;
address bondAsset;
uint96 bondAmount;
}
/**
* @dev Struct for expiration parameters of an order.
* @param timestamp The timestamp when the order was created.
* @param challengeOffset The offset for the challenge window start.
* @param challengeWindow The duration of the challenge window in seconds.
*/structOrderExpiration {
uint32 timestamp;
uint16 challengeOffset;
uint16 challengeWindow;
}
/**
* @dev Represents a match between orders in the trading system.
* @param dstIndex Index of the destination order.
* @param srcQuantity Quantity of srcAsset in the match.
* @param dstQuantity Quantity of dstAsset in the match.
* @param receiver Address to receive the destination asset.
* @param bonder Address of the bonder.
* @param blockNumber Block number when the match was created.
* @param finalized Whether the match has been executed.
* @param challenged Whether the match is locked.
*/structMatch {
uint32 dstIndex; // Index of the destination order// Pricinguint96 srcQuantity; // Quantity of srcAsset in the matchuint96 dstQuantity; // Quantity of dstAsset in the match// Counterpartyaddress receiver; // Address to receive the destination assetaddress bonder; // Address of the bonder// Securityuint96 blockNumber; // Block number when the match was createdbool finalized; // Whether the match has been finalizedbool challenged; // Whether the match is locked.
}
/**
* @dev Represents a recipet
* @param payoutQuantity how much got paid
* @param receiver to whom it was paid
*/structReceipt {
uint96 payoutQuantity; // Quantity of srcAsset in the matchaddress receiver; // Address of who got the funds
}
structPair {
address src;
address dst;
uint16 lzc;
Order[] orders;
mapping(uint=> Match) matches; //indexed by taker order idmapping(uint=>mapping(uint=> Receipt)) receipts; // indexed by maker order id and corresponding contra-taker order id
}
structPayload {
address challenger;
uint32 srcLzc;
address srcToken;
address dstToken;
uint32 srcIndex;
uint32 dstIndex;
address taker;
uint minAmount;
uint status; //0 means undecided, 1 means challenge is true and succeeded, 2 means challenge failed
}
}
// SPDX-License-Identifier: UNLICENSEDpragmasolidity ^0.8.20;import"./interfaces/IOptimistic.sol";
import"./interfaces/IWrapped.sol";
import { OApp, MessagingFee, Origin } from"./lzApp//lz-evm-oapp-v2/contracts/oapp/OApp.sol";
import { OAppOptionsType3 } from"./lzApp/lz-evm-oapp-v2/contracts/oapp/libs/OAppOptionsType3.sol";
import { Ownable } from"@openzeppelin/contracts/access/Ownable.sol";
import"@openzeppelin/contracts/token/ERC20/IERC20.sol";
import"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import"@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
contractOrderbookisOApp, OAppOptionsType3, TradeInterface{
mapping(uint=>mapping (address=>mapping( address=> Pair ))) public book;
uint32public srcLzc;
uintprivateconstant BASIS_POINTS=10000;
uint16privateconstant SEND =1;
uint16public maxFee =1001;
//Constructorconstructor(address _endpoint, address _owner, uint32 _lzEid) OApp(_endpoint, _owner) Ownable() {
srcLzc = _lzEid;
}
functionsetMaxFee(uint16 _newMaxFee) publiconlyOwner{
maxFee = _newMaxFee;
}
eventOrderPlaced(addressindexed sender, uint32 orderIndex, OrderDirection direction, OrderFunding funding, OrderExpiration expiration, bool isMaker);
eventMatchCreated(addressindexed bonder, OrderDirection direction, uint32 srcIndex, uint32 dstIndex, uint96 srcQuantity, uint96 dstQuantity, address Maker, uint96 blockNumber);
eventMatchExecuted(addressindexed bonder, OrderDirection direction, uint32 srcIndex, uint32 dstIndex, uint96 srcQuantity, uint96 dstQuantity, address Taker, bool isWrapped);
eventMatchConfirmed(addressindexed bonder, OrderDirection direction, uint32 srcIndex, uint32 dstIndex, uint16 bondFee);
eventChallengeRaised(addressindexed challenger, OrderDirection direction, uint32 srcIndex, address bonder, uint32 dstIndex);
eventOrderCancelled(addressindexed sender, OrderDirection direction, uint32 orderIndex);
eventMatchUnwound(addressindexed bonder, OrderDirection direction, uint32 srcIndex, uint32 dstIndex);
eventChallengeResult(bool challenge_status);
//PlaceTrade FunctionsfunctionplaceOrder(
OrderDirection memory direction,
OrderFunding memory funding,
OrderExpiration memory expiration,
bool isMaker
) public{
//checkrequire((expiration.challengeOffset + expiration.challengeWindow) <1e5 , "!maxWindow");
require(funding.bondFee < maxFee , "!maxFee");
//action
Order[] storage orders=book[direction.dstLzc][direction.dstAsset][direction.srcAsset].orders;
Order memory newOrder = Order({
sender: msg.sender,
funding: funding,
expiration: expiration,
settled: uint96(0),
isMaker: isMaker
});
uint32 orderIndex=uint32(orders.length);
orders.push(newOrder);
//event emit OrderPlaced(
msg.sender,
orderIndex,
direction,
funding,
expiration,
isMaker
);
//an intent...no funds are pulled
}
//Read FunctionsfunctiongetOrders(address srcAsset, address dstAsset, uint dstLzc) publicviewreturns (Order[] memory orders) {
orders=book[dstLzc][dstAsset][srcAsset].orders;
}
functiongetOrder(address srcAsset, address dstAsset, uint dstLzc, uint index) publicviewreturns (Order memory _order) {
_order=book[dstLzc][dstAsset][srcAsset].orders[index];
}
functiongetReceipt(address srcAsset, address dstAsset, uint dstLzc, uint srcIndex, uint dstIndex) publicviewreturns (Receipt memory _receipt) {
_receipt=book[dstLzc][dstAsset][srcAsset].receipts[srcIndex][dstIndex];
}
functiongetMatch(address srcAsset, address dstAsset, uint dstLzc, uint index) publicviewreturns (Match memory _match) {
_match=book[dstLzc][dstAsset][srcAsset].matches[index];
}
functiongetCurrentBlockNumber() publicviewreturns (uint256) {
returnblock.number;
}
//Core FunctionsfunctioncreateMatch(
OrderDirection memory direction,
uint32 srcIndex,
uint32 dstIndex,
address Counterparty,
uint96 srcQuantity,
uint96 dstQuantity
) public{
Pair storage selected_pair=book[direction.dstLzc][direction.dstAsset][direction.srcAsset];
Order storage order = selected_pair.orders[srcIndex];
// Cache frequently accessed storage variables
OrderFunding memory funding = order.funding;
OrderExpiration memory expiration = order.expiration;
uint96 blockNumber=uint96(block.number);
//checksrequire(order.settled ==0, "Order has already been settled.");
require(Counterparty !=address(0), "!zero address");
require(funding.srcQuantity == srcQuantity, "Partial fills are not allowed. Source quantity must match the order amount.");
require(expiration.timestamp >=block.timestamp, "Order has expired. Check timestamp");
require(!order.isMaker, "The createMatch method is reserved for taker orders. Makers should use the executeMatch method.");
//transferIN
transferFrom(funding.bondAsset, msg.sender, funding.bondAmount); //bonder
transferFrom(direction.srcAsset, order.sender, srcQuantity); //taker//action
Match memory TakerMatch = Match({
dstIndex: dstIndex,
srcQuantity: srcQuantity,
dstQuantity: dstQuantity,
receiver: Counterparty,
bonder: msg.sender,
blockNumber: blockNumber,
finalized: false,
challenged: false
});
selected_pair.matches[srcIndex]=TakerMatch; //onlyBonder//state change
order.settled+=srcQuantity;
require(selected_pair.orders[srcIndex].settled == selected_pair.orders[srcIndex].funding.srcQuantity, "Sanity check T3"); //test case remove in prod//eventemit MatchCreated(msg.sender, direction, srcIndex, dstIndex, srcQuantity, dstQuantity, Counterparty, blockNumber);
}
functionexecuteMatch(
OrderDirection memory direction,
uint32 srcIndex,
uint32 dstIndex,
address Counterparty,
uint96 srcQuantity,
uint96 dstQuantity,
bool isUnwrap
) public{
Pair storage selected_pair=book[direction.dstLzc][direction.dstAsset][direction.srcAsset];
Order storage order= selected_pair.orders[srcIndex];
// Cache frequently accessed storage variablesuint32 expirationTimestamp = order.expiration.timestamp;
bool isOrderMaker = order.isMaker;
uint96 orderSettled = order.settled;
// checksrequire(order.sender ==msg.sender, "Only the maker wallet can call this method");
require(order.funding.srcQuantity > orderSettled, "Maker order is closed");
require((order.funding.srcQuantity - orderSettled) >= srcQuantity, "Maker order too small to cover this match");
require(srcQuantity >0, "Zero valued match");
require(Counterparty !=address(0), "!zeroAddress");
require(expirationTimestamp >=block.timestamp, "Maker order has expired. Check timestamp");
require(isOrderMaker, "The executeMatch method is reserved for maker orders. Bonders should use the createMatch method");
//actions (pull maker and pay taker)
transferFrom(direction.srcAsset, order.sender, srcQuantity); //pull maker funds//pay taker funds -- isUnwrap true is used to deliver user native gas tokens if (isUnwrap) {
//Unwrap the token and transfer srcQuantity of the native gas token to the user
IWrapped wrappedToken = IWrapped(direction.srcAsset);
wrappedToken.withdraw(srcQuantity);
// //send the gas token
(bool sent,) = Counterparty.call{value: srcQuantity}("");
require(sent, "Failed to unwrap and send native asset");
}
else {
transferTo(direction.srcAsset, Counterparty, srcQuantity); //pay counterparty
}
// Add the receipt
selected_pair.receipts[srcIndex][dstIndex] = Receipt({
payoutQuantity: srcQuantity,
receiver: Counterparty
});
//state change
order.settled+=srcQuantity;
require(selected_pair.orders[srcIndex].settled <= selected_pair.orders[srcIndex].funding.srcQuantity, "Sanity check T4"); //test case remove in prod//eventemit MatchExecuted(msg.sender, direction, srcIndex, dstIndex, srcQuantity, dstQuantity, Counterparty, isUnwrap);
}
functionconfirmMatch(
OrderDirection memory direction,
uint32 srcIndex
) public{
Pair storage selected_pair=book[direction.dstLzc][direction.dstAsset][direction.srcAsset];
Order storage _order= selected_pair.orders[srcIndex];
Match storage _match=selected_pair.matches[srcIndex];
//cache
OrderFunding memory funding = _order.funding;
OrderExpiration memory expiration = _order.expiration;
address bonder = _match.bonder;
address receiver = _match.receiver;
uint validBlock = _match.blockNumber + expiration.challengeOffset + expiration.challengeWindow;
//checkrequire(!_match.finalized &&!_match.challenged, "!Match is closed");
require(msg.sender==bonder ||msg.sender==receiver, "!OnlyMakerOrBonder");
require(block.number> validBlock, "Must wait before confirming match");
//mathuint order_amount = funding.srcQuantity;
uint16 fee =funding.bondFee;
uint maker_payout=applyFee(order_amount, fee);
uint bonder_fee_payout=bondFee(order_amount, fee);
require(_match.srcQuantity == funding.srcQuantity, "Sanity Check T1"); //Test case remove in prodrequire((bonder_fee_payout+maker_payout)==order_amount, "Sanity Check T2"); //Test case remove in prodrequire(_order.settled==order_amount, "Sanity Check T5"); //Test case remove in prod//state
_match.finalized=true;
//transfer
transferTo(direction.srcAsset, receiver, maker_payout); //pay counterparty
transferTo(direction.srcAsset, bonder, bonder_fee_payout); //pay bonder fee
transferTo(funding.bondAsset, bonder, funding.bondAmount); //give back bonder his bond//eventemit MatchConfirmed(bonder, direction, srcIndex, _match.dstIndex, fee);
}
functioncancelOrder(
OrderDirection memory direction,
uint32 orderIndex
) public{
Order storage order= book[direction.dstLzc][direction.dstAsset][direction.srcAsset].orders[orderIndex];
address sender=order.sender;
//checkrequire(msg.sender==sender, "!onlySender");
require(order.settled < order.funding.srcQuantity, "!alreadyMatched");
//action
order.funding.srcQuantity =0;
//eventemit OrderCancelled(sender, direction, orderIndex);
}
functionunwindMatch(
OrderDirection memory direction,
uint32 srcIndex
) public{
Pair storage selected_pair=book[direction.dstLzc][direction.dstAsset][direction.srcAsset];
Order storage _order= selected_pair.orders[srcIndex];
Match storage _match=selected_pair.matches[srcIndex];
//checkrequire(msg.sender== _match.receiver, "!onlyMaker");
require(!_match.finalized &&!_match.challenged, "!Match is closed");
//updates
_match.finalized =true;
//transfer
transferTo(_order.funding.bondAsset, _match.bonder, _order.funding.bondAmount); //give back bonder his bond
transferTo(direction.srcAsset, _order.sender, _order.funding.srcQuantity); //refund user
_order.funding.srcQuantity =0;
//emitemit MatchUnwound(_match.bonder, direction, srcIndex, _match.dstIndex);
}
//LayerZero FunctionseventMessageSent(bytes message, uint32 dstEid); // @notice Emitted when a challenge is sent on source chain to dest chain (src -> dst).eventReturnMessageSent(string message, uint32 dstEid); // @notice Emitted when a challenge is judges on the dest chain (src -> dst).eventMessageReceived(string message, uint32 senderEid, bytes32 sender); // @notice Emitted when a message is received from another chain.//Challenge Pattern: A->B->AfunctiondecodeMessage(bytescalldata encodedMessage) publicpurereturns (Payload memory message, uint16 msgType, uint256 extraOptionsStart, uint256 extraOptionsLength) {
extraOptionsStart =256; // Starting offset after _message, _msgType, and extraOptionsLength
Payload memory _message;
uint16 _msgType;
// Decode the first part of the message
(_message, _msgType, extraOptionsLength) =abi.decode(encodedMessage, (Payload, uint16, uint256));
// // Slice out _extraReturnOptions// bytes memory _extraReturnOptions = abi.decode(encodedMessage[extraOptionsStart:extraOptionsStart + extraOptionsLength], (bytes));return (_message, _msgType, extraOptionsStart, extraOptionsLength);
}
/**
* @notice Sends a message to a specified destination chain.
* @param direction._dstEid Destination endpoint ID for the message.
* @param _extraSendOptions Options for sending the message, such as gas settings.
* @param _extraReturnOptions Additional options for the return message.
*/functionchallengeMatch(
OrderDirection memory direction,
uint32 srcIndex,
bytescalldata _extraSendOptions, // gas settings for A -> Bbytescalldata _extraReturnOptions // gas settings for B -> A) externalpayable{
//loads
Pair storage selected_pair=book[direction.dstLzc][direction.dstAsset][direction.srcAsset];
Order storage _order= selected_pair.orders[srcIndex];
Match storage _match=selected_pair.matches[srcIndex];
//checksrequire(!_match.finalized &&!_match.challenged, "!Match is closed");
if (msg.sender!= _match.bonder) {
require(block.number> (_match.blockNumber+_order.expiration.challengeOffset), "Must wait challengeOffset challenging match");
}
//lz variablesuint16 _msgType =2; //SEND_ABAuint32 _dstEid = direction.dstLzc;
uint256 extraOptionsLength = _extraReturnOptions.length;
bytesmemory options = combineOptions(_dstEid, _msgType, _extraSendOptions);
//encode packet
Payload memory newPayload = Payload({
challenger: msg.sender,
srcLzc: srcLzc,
srcToken: direction.srcAsset,
dstToken: direction.dstAsset,
srcIndex: srcIndex,
dstIndex: _match.dstIndex,
taker: _order.sender,
minAmount: _order.funding.dstQuantity,
status: 0//0 means undecided, 1 means challenge is true and succeeded, 2 means challenge failed
});
bytesmemory lzPacket=abi.encode(newPayload, _msgType, extraOptionsLength, _extraReturnOptions, extraOptionsLength);
//Layer-zero send
_lzSend(
_dstEid,
lzPacket,
options,
MessagingFee(msg.value, 0),
payable(msg.sender)
);
//state updates
_match.challenged=true;
//eventsemit MessageSent(lzPacket, _dstEid);
//emit ChallengeRaised(msg.sender, direction.srcAsset, direction.dstAsset, direction.dstLzc, srcIndex, _match.bonder, _match.dstIndex);
}
/**
* @notice Internal function to handle receiving messages from another chain.
* @dev Decodes and processes the received message based on its type.
* @param _origin Data about the origin of the received message.
* @param _guid Globally unique identifier of the message.
* @param _packet The received message content.
*/function_lzReceive(
Origin calldata _origin,
bytes32 _guid,
bytescalldata _packet,
address, // Executor address as specified by the OApp.bytescalldata// Any extra data or options to trigger on receipt.) internaloverride{
//if message types == 2. Means B leg of ABA contract will respons, if message type == 1 means last leg of ABA, contract will just recieve.
(Payload memory _payload, uint16 _msgType, uint256 extraOptionsStart, uint256 extraOptionsLength) = decodeMessage(_packet);
require(_msgType ==1|| _msgType ==2, "Sanity Check T9");
uint32 makerEid=_origin.srcEid;
if (_msgType ==2) {
require(_payload.status ==0, "Sanity Check T8");
Pair storage selected_pair=book[makerEid][_payload.srcToken][_payload.dstToken];
Receipt memory _receipt=selected_pair.receipts[_payload.dstIndex][_payload.srcIndex];
if ((_receipt.payoutQuantity >= _payload.minAmount) && (_receipt.receiver == _payload.taker)) {
_payload.status=2;
}
else {
_payload.status=1;
}
//send back the payloadbytesmemory _options = combineOptions(makerEid, 1, _packet[extraOptionsStart:extraOptionsStart + extraOptionsLength]);
_lzSend(
makerEid,
abi.encode(_payload, 1),
_options,
MessagingFee(msg.value, 0),
payable(address(this))
);
}
else {
require(_payload.status==1|| _payload.status==2, "Sanity Check T10");
Pair storage selected_pair=book[srcLzc][_payload.dstToken][_payload.srcToken];
Order storage _order= selected_pair.orders[_payload.srcIndex];
Match storage _match=selected_pair.matches[_payload.srcIndex];
address bonder =_match.bonder;
if (_payload.status==1) {
//taker was NOT paid out. Challenge is true. Give funds from gaurentoor to challenger + tithe, + return funds to user
transferTo(_payload.srcToken, _order.sender, _order.funding.srcQuantity); //refund user
transferTo(_order.funding.bondAsset, _payload.challenger, (_order.funding.bondAmount*9)/10); //pay collateral
transferTo(_order.funding.bondAsset, owner(), (_order.funding.bondAmount)/10); //pay collateral titheemit ChallengeResult(true);
}
else {
//transfer
transferTo(_payload.srcToken, _match.receiver, applyFee(_order.funding.srcQuantity, _order.funding.bondFee)); //pay counterparty
transferTo(_payload.srcToken, bonder, bondFee(_order.funding.srcQuantity, _order.funding.bondFee)); //pay bonder fee
transferTo(_order.funding.bondAsset, bonder, _order.funding.bondAmount); //give back bonder his bond//eventemit ChallengeResult(false);
}
_match.finalized=true;
}
}
//Transfer FunctionsfunctiontransferFrom(address tkn, addressfrom, uint amount) internal{
SafeERC20.safeTransferFrom(IERC20(tkn), from, address(this), amount);
}
functiontransferTo(address tkn, address to, uint amount) internal{
SafeERC20.safeTransfer(IERC20(tkn), to, amount);
}
//Fee FunctionsfunctionbondFee(uint number, uint _fee) publicpurereturns (uint) {
return (_fee*number)/BASIS_POINTS;
}
functionapplyFee(uint number, uint _fee) publicpurereturns (uint) {
return number-((_fee*number)/BASIS_POINTS);
}
// Receive function to accept Etherreceive() externalpayable{}
}
Contract Source Code
File 17 of 23: OApp.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.20;// @dev Import the 'MessagingFee' and 'MessagingReceipt' so it's exposed to OApp implementers// solhint-disable-next-line no-unused-importimport { OAppSender, MessagingFee, MessagingReceipt } from"./OAppSender.sol";
// @dev Import the 'Origin' so it's exposed to OApp implementers// solhint-disable-next-line no-unused-importimport { OAppReceiver, Origin } from"./OAppReceiver.sol";
import { OAppCore } from"./OAppCore.sol";
/**
* @title OApp
* @dev Abstract contract serving as the base for OApp implementation, combining OAppSender and OAppReceiver functionality.
*/abstractcontractOAppisOAppSender, OAppReceiver{
/**
* @dev Constructor to initialize the OApp with the provided endpoint and owner.
* @param _endpoint The address of the LOCAL LayerZero endpoint.
* @param _delegate The delegate capable of making OApp configurations inside of the endpoint.
*/constructor(address _endpoint, address _delegate) OAppCore(_endpoint, _delegate) {}
/**
* @notice Retrieves the OApp version information.
* @return senderVersion The version of the OAppSender.sol implementation.
* @return receiverVersion The version of the OAppReceiver.sol implementation.
*/functionoAppVersion()
publicpurevirtualoverride(OAppSender, OAppReceiver)
returns (uint64 senderVersion, uint64 receiverVersion)
{
return (SENDER_VERSION, RECEIVER_VERSION);
}
}
Contract Source Code
File 18 of 23: OAppCore.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.20;import { Ownable } from"@openzeppelin/contracts/access/Ownable.sol";
import { IOAppCore, ILayerZeroEndpointV2 } from"./interfaces/IOAppCore.sol";
/**
* @title OAppCore
* @dev Abstract contract implementing the IOAppCore interface with basic OApp configurations.
*/abstractcontractOAppCoreisIOAppCore, Ownable{
// The LayerZero endpoint associated with the given OApp
ILayerZeroEndpointV2 publicimmutable endpoint;
// Mapping to store peers associated with corresponding endpointsmapping(uint32 eid =>bytes32 peer) public peers;
/**
* @dev Constructor to initialize the OAppCore with the provided endpoint and delegate.
* @param _endpoint The address of the LOCAL Layer Zero endpoint.
* @param _delegate The delegate capable of making OApp configurations inside of the endpoint.
*
* @dev The delegate typically should be set as the owner of the contract.
*/constructor(address _endpoint, address _delegate) {
endpoint = ILayerZeroEndpointV2(_endpoint);
if (_delegate ==address(0)) revert InvalidDelegate();
endpoint.setDelegate(_delegate);
}
/**
* @notice Sets the peer address (OApp instance) for a corresponding endpoint.
* @param _eid The endpoint ID.
* @param _peer The address of the peer to be associated with the corresponding endpoint.
*
* @dev Only the owner/admin of the OApp can call this function.
* @dev Indicates that the peer is trusted to send LayerZero messages to this OApp.
* @dev Set this to bytes32(0) to remove the peer address.
* @dev Peer is a bytes32 to accommodate non-evm chains.
*/functionsetPeer(uint32 _eid, bytes32 _peer) publicvirtualonlyOwner{
_setPeer(_eid, _peer);
}
/**
* @notice Sets the peer address (OApp instance) for a corresponding endpoint.
* @param _eid The endpoint ID.
* @param _peer The address of the peer to be associated with the corresponding endpoint.
*
* @dev Indicates that the peer is trusted to send LayerZero messages to this OApp.
* @dev Set this to bytes32(0) to remove the peer address.
* @dev Peer is a bytes32 to accommodate non-evm chains.
*/function_setPeer(uint32 _eid, bytes32 _peer) internalvirtual{
peers[_eid] = _peer;
emit PeerSet(_eid, _peer);
}
/**
* @notice Internal function to get the peer address associated with a specific endpoint; reverts if NOT set.
* ie. the peer is set to bytes32(0).
* @param _eid The endpoint ID.
* @return peer The address of the peer associated with the specified endpoint.
*/function_getPeerOrRevert(uint32 _eid) internalviewvirtualreturns (bytes32) {
bytes32 peer = peers[_eid];
if (peer ==bytes32(0)) revert NoPeer(_eid);
return peer;
}
/**
* @notice Sets the delegate address for the OApp.
* @param _delegate The address of the delegate to be set.
*
* @dev Only the owner/admin of the OApp can call this function.
* @dev Provides the ability for a delegate to set configs, on behalf of the OApp, directly on the Endpoint contract.
*/functionsetDelegate(address _delegate) publiconlyOwner{
endpoint.setDelegate(_delegate);
}
}
Contract Source Code
File 19 of 23: OAppOptionsType3.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.20;import { Ownable } from"@openzeppelin/contracts/access/Ownable.sol";
import { IOAppOptionsType3, EnforcedOptionParam } from"../interfaces/IOAppOptionsType3.sol";
/**
* @title OAppOptionsType3
* @dev Abstract contract implementing the IOAppOptionsType3 interface with type 3 options.
*/abstractcontractOAppOptionsType3isIOAppOptionsType3, Ownable{
uint16internalconstant OPTION_TYPE_3 =3;
// @dev The "msgType" should be defined in the child contract.mapping(uint32 eid =>mapping(uint16 msgType =>bytes enforcedOption)) public enforcedOptions;
/**
* @dev Sets the enforced options for specific endpoint and message type combinations.
* @param _enforcedOptions An array of EnforcedOptionParam structures specifying enforced options.
*
* @dev Only the owner/admin of the OApp can call this function.
* @dev Provides a way for the OApp to enforce things like paying for PreCrime, AND/OR minimum dst lzReceive gas amounts etc.
* @dev These enforced options can vary as the potential options/execution on the remote may differ as per the msgType.
* eg. Amount of lzReceive() gas necessary to deliver a lzCompose() message adds overhead you dont want to pay
* if you are only making a standard LayerZero message ie. lzReceive() WITHOUT sendCompose().
*/functionsetEnforcedOptions(EnforcedOptionParam[] calldata _enforcedOptions) publicvirtualonlyOwner{
_setEnforcedOptions(_enforcedOptions);
}
/**
* @dev Sets the enforced options for specific endpoint and message type combinations.
* @param _enforcedOptions An array of EnforcedOptionParam structures specifying enforced options.
*
* @dev Provides a way for the OApp to enforce things like paying for PreCrime, AND/OR minimum dst lzReceive gas amounts etc.
* @dev These enforced options can vary as the potential options/execution on the remote may differ as per the msgType.
* eg. Amount of lzReceive() gas necessary to deliver a lzCompose() message adds overhead you dont want to pay
* if you are only making a standard LayerZero message ie. lzReceive() WITHOUT sendCompose().
*/function_setEnforcedOptions(EnforcedOptionParam[] memory _enforcedOptions) internalvirtual{
for (uint256 i =0; i < _enforcedOptions.length; i++) {
// @dev Enforced options are only available for optionType 3, as type 1 and 2 dont support combining.
_assertOptionsType3(_enforcedOptions[i].options);
enforcedOptions[_enforcedOptions[i].eid][_enforcedOptions[i].msgType] = _enforcedOptions[i].options;
}
emit EnforcedOptionSet(_enforcedOptions);
}
/**
* @notice Combines options for a given endpoint and message type.
* @param _eid The endpoint ID.
* @param _msgType The OAPP message type.
* @param _extraOptions Additional options passed by the caller.
* @return options The combination of caller specified options AND enforced options.
*
* @dev If there is an enforced lzReceive option:
* - {gasLimit: 200k, msg.value: 1 ether} AND a caller supplies a lzReceive option: {gasLimit: 100k, msg.value: 0.5 ether}
* - The resulting options will be {gasLimit: 300k, msg.value: 1.5 ether} when the message is executed on the remote lzReceive() function.
* @dev This presence of duplicated options is handled off-chain in the verifier/executor.
*/functioncombineOptions(uint32 _eid,
uint16 _msgType,
bytescalldata _extraOptions
) publicviewvirtualreturns (bytesmemory) {
bytesmemory enforced = enforcedOptions[_eid][_msgType];
// No enforced options, pass whatever the caller supplied, even if it's empty or legacy type 1/2 options.if (enforced.length==0) return _extraOptions;
// No caller options, return enforcedif (_extraOptions.length==0) return enforced;
// @dev If caller provided _extraOptions, must be type 3 as its the ONLY type that can be combined.if (_extraOptions.length>=2) {
_assertOptionsType3(_extraOptions);
// @dev Remove the first 2 bytes containing the type from the _extraOptions and combine with enforced.returnbytes.concat(enforced, _extraOptions[2:]);
}
// No valid set of options was found.revert InvalidOptions(_extraOptions);
}
/**
* @dev Internal function to assert that options are of type 3.
* @param _options The options to be checked.
*/function_assertOptionsType3(bytesmemory _options) internalpurevirtual{
uint16 optionsType;
assembly {
optionsType :=mload(add(_options, 2))
}
if (optionsType != OPTION_TYPE_3) revert InvalidOptions(_options);
}
}
Contract Source Code
File 20 of 23: OAppReceiver.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.20;import { IOAppReceiver, Origin } from"./interfaces/IOAppReceiver.sol";
import { OAppCore } from"./OAppCore.sol";
/**
* @title OAppReceiver
* @dev Abstract contract implementing the ILayerZeroReceiver interface and extending OAppCore for OApp receivers.
*/abstractcontractOAppReceiverisIOAppReceiver, OAppCore{
// Custom error message for when the caller is not the registered endpoint/errorOnlyEndpoint(address addr);
// @dev The version of the OAppReceiver implementation.// @dev Version is bumped when changes are made to this contract.uint64internalconstant RECEIVER_VERSION =2;
/**
* @notice Retrieves the OApp version information.
* @return senderVersion The version of the OAppSender.sol contract.
* @return receiverVersion The version of the OAppReceiver.sol contract.
*
* @dev Providing 0 as the default for OAppSender version. Indicates that the OAppSender is not implemented.
* ie. this is a RECEIVE only OApp.
* @dev If the OApp uses both OAppSender and OAppReceiver, then this needs to be override returning the correct versions.
*/functionoAppVersion() publicviewvirtualreturns (uint64 senderVersion, uint64 receiverVersion) {
return (0, RECEIVER_VERSION);
}
/**
* @notice Indicates whether an address is an approved composeMsg sender to the Endpoint.
* @dev _origin The origin information containing the source endpoint and sender address.
* - srcEid: The source chain endpoint ID.
* - sender: The sender address on the src chain.
* - nonce: The nonce of the message.
* @dev _message The lzReceive payload.
* @param _sender The sender address.
* @return isSender Is a valid sender.
*
* @dev Applications can optionally choose to implement separate composeMsg senders that are NOT the bridging layer.
* @dev The default sender IS the OAppReceiver implementer.
*/functionisComposeMsgSender(
Origin calldata/*_origin*/,
bytescalldata/*_message*/,
address _sender
) publicviewvirtualreturns (bool) {
return _sender ==address(this);
}
/**
* @notice Checks if the path initialization is allowed based on the provided origin.
* @param origin The origin information containing the source endpoint and sender address.
* @return Whether the path has been initialized.
*
* @dev This indicates to the endpoint that the OApp has enabled msgs for this particular path to be received.
* @dev This defaults to assuming if a peer has been set, its initialized.
* Can be overridden by the OApp if there is other logic to determine this.
*/functionallowInitializePath(Origin calldata origin) publicviewvirtualreturns (bool) {
return peers[origin.srcEid] == origin.sender;
}
/**
* @notice Retrieves the next nonce for a given source endpoint and sender address.
* @dev _srcEid The source endpoint ID.
* @dev _sender The sender address.
* @return nonce The next nonce.
*
* @dev The path nonce starts from 1. If 0 is returned it means that there is NO nonce ordered enforcement.
* @dev Is required by the off-chain executor to determine the OApp expects msg execution is ordered.
* @dev This is also enforced by the OApp.
* @dev By default this is NOT enabled. ie. nextNonce is hardcoded to return 0.
*/functionnextNonce(uint32/*_srcEid*/, bytes32/*_sender*/) publicviewvirtualreturns (uint64 nonce) {
return0;
}
/**
* @dev Entry point for receiving messages or packets from the endpoint.
* @param _origin The origin information containing the source endpoint and sender address.
* - srcEid: The source chain endpoint ID.
* - sender: The sender address on the src chain.
* - nonce: The nonce of the message.
* @param _guid The unique identifier for the received LayerZero message.
* @param _message The payload of the received message.
* @param _executor The address of the executor for the received message.
* @param _extraData Additional arbitrary data provided by the corresponding executor.
*
* @dev Entry point for receiving msg/packet from the LayerZero endpoint.
*/functionlzReceive(
Origin calldata _origin,
bytes32 _guid,
bytescalldata _message,
address _executor,
bytescalldata _extraData
) publicpayablevirtual{
// Ensures that only the endpoint can attempt to lzReceive() messages to this OApp.if (address(endpoint) !=msg.sender) revert OnlyEndpoint(msg.sender);
// Ensure that the sender matches the expected peer for the source endpoint.if (_getPeerOrRevert(_origin.srcEid) != _origin.sender) revert OnlyPeer(_origin.srcEid, _origin.sender);
// Call the internal OApp implementation of lzReceive.
_lzReceive(_origin, _guid, _message, _executor, _extraData);
}
/**
* @dev Internal function to implement lzReceive logic without needing to copy the basic parameter validation.
*/function_lzReceive(
Origin calldata _origin,
bytes32 _guid,
bytescalldata _message,
address _executor,
bytescalldata _extraData
) internalvirtual;
}
Contract Source Code
File 21 of 23: OAppSender.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.20;import { SafeERC20, IERC20 } from"@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import { MessagingParams, MessagingFee, MessagingReceipt } from"../../../lz-evm-protocol-v2/contracts/interfaces/ILayerZeroEndpointV2.sol";
import { OAppCore } from"./OAppCore.sol";
/**
* @title OAppSender
* @dev Abstract contract implementing the OAppSender functionality for sending messages to a LayerZero endpoint.
*/abstractcontractOAppSenderisOAppCore{
usingSafeERC20forIERC20;
// Custom error messageserrorNotEnoughNative(uint256 msgValue);
errorLzTokenUnavailable();
// @dev The version of the OAppSender implementation.// @dev Version is bumped when changes are made to this contract.uint64internalconstant SENDER_VERSION =1;
/**
* @notice Retrieves the OApp version information.
* @return senderVersion The version of the OAppSender.sol contract.
* @return receiverVersion The version of the OAppReceiver.sol contract.
*
* @dev Providing 0 as the default for OAppReceiver version. Indicates that the OAppReceiver is not implemented.
* ie. this is a SEND only OApp.
* @dev If the OApp uses both OAppSender and OAppReceiver, then this needs to be override returning the correct versions
*/functionoAppVersion() publicviewvirtualreturns (uint64 senderVersion, uint64 receiverVersion) {
return (SENDER_VERSION, 0);
}
/**
* @dev Internal function to interact with the LayerZero EndpointV2.quote() for fee calculation.
* @param _dstEid The destination endpoint ID.
* @param _message The message payload.
* @param _options Additional options for the message.
* @param _payInLzToken Flag indicating whether to pay the fee in LZ tokens.
* @return fee The calculated MessagingFee for the message.
* - nativeFee: The native fee for the message.
* - lzTokenFee: The LZ token fee for the message.
*/function_quote(uint32 _dstEid,
bytesmemory _message,
bytesmemory _options,
bool _payInLzToken
) internalviewvirtualreturns (MessagingFee memory fee) {
return
endpoint.quote(
MessagingParams(_dstEid, _getPeerOrRevert(_dstEid), _message, _options, _payInLzToken),
address(this)
);
}
/**
* @dev Internal function to interact with the LayerZero EndpointV2.send() for sending a message.
* @param _dstEid The destination endpoint ID.
* @param _message The message payload.
* @param _options Additional options for the message.
* @param _fee The calculated LayerZero fee for the message.
* - nativeFee: The native fee.
* - lzTokenFee: The lzToken fee.
* @param _refundAddress The address to receive any excess fee values sent to the endpoint.
* @return receipt The receipt for the sent message.
* - guid: The unique identifier for the sent message.
* - nonce: The nonce of the sent message.
* - fee: The LayerZero fee incurred for the message.
*/function_lzSend(uint32 _dstEid,
bytesmemory _message,
bytesmemory _options,
MessagingFee memory _fee,
address _refundAddress
) internalvirtualreturns (MessagingReceipt memory receipt) {
// @dev Push corresponding fees to the endpoint, any excess is sent back to the _refundAddress from the endpoint.uint256 messageValue = _payNative(_fee.nativeFee);
if (_fee.lzTokenFee >0) _payLzToken(_fee.lzTokenFee);
return// solhint-disable-next-line check-send-result
endpoint.send{ value: messageValue }(
MessagingParams(_dstEid, _getPeerOrRevert(_dstEid), _message, _options, _fee.lzTokenFee >0),
_refundAddress
);
}
/**
* @dev Internal function to pay the native fee associated with the message.
* @param _nativeFee The native fee to be paid.
* @return nativeFee The amount of native currency paid.
*
* @dev If the OApp needs to initiate MULTIPLE LayerZero messages in a single transaction,
* this will need to be overridden because msg.value would contain multiple lzFees.
* @dev Should be overridden in the event the LayerZero endpoint requires a different native currency.
* @dev Some EVMs use an ERC20 as a method for paying transactions/gasFees.
* @dev The endpoint is EITHER/OR, ie. it will NOT support both types of native payment at a time.
*/function_payNative(uint256 _nativeFee) internalvirtualreturns (uint256 nativeFee) {
if (msg.value!= _nativeFee) revert NotEnoughNative(msg.value);
return _nativeFee;
}
/**
* @dev Internal function to pay the LZ token fee associated with the message.
* @param _lzTokenFee The LZ token fee to be paid.
*
* @dev If the caller is trying to pay in the specified lzToken, then the lzTokenFee is passed to the endpoint.
* @dev Any excess sent, is passed back to the specified _refundAddress in the _lzSend().
*/function_payLzToken(uint256 _lzTokenFee) internalvirtual{
// @dev Cannot cache the token because it is not immutable in the endpoint.address lzToken = endpoint.lzToken();
if (lzToken ==address(0)) revert LzTokenUnavailable();
// Pay LZ token fee by sending tokens to the endpoint.
IERC20(lzToken).safeTransferFrom(msg.sender, address(endpoint), _lzTokenFee);
}
}
Contract Source Code
File 22 of 23: Ownable.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.0;import"../utils/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/abstractcontractOwnableisContext{
addressprivate _owner;
eventOwnershipTransferred(addressindexed previousOwner, addressindexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/constructor () {
address msgSender = _msgSender();
_owner = msgSender;
emit OwnershipTransferred(address(0), msgSender);
}
/**
* @dev Returns the address of the current owner.
*/functionowner() publicviewvirtualreturns (address) {
return _owner;
}
/**
* @dev Throws if called by any account other than the owner.
*/modifieronlyOwner() {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
_;
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions anymore. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby removing any functionality that is only available to the owner.
*/functionrenounceOwnership() publicvirtualonlyOwner{
emit OwnershipTransferred(_owner, address(0));
_owner =address(0);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/functiontransferOwnership(address newOwner) publicvirtualonlyOwner{
require(newOwner !=address(0), "Ownable: new owner is the zero address");
emit OwnershipTransferred(_owner, newOwner);
_owner = newOwner;
}
}
Contract Source Code
File 23 of 23: SafeERC20.sol
// SPDX-License-Identifier: MITpragmasolidity ^0.8.0;import"../IERC20.sol";
import"../../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/librarySafeERC20{
usingAddressforaddress;
functionsafeTransfer(IERC20 token, address to, uint256 value) internal{
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
functionsafeTransferFrom(IERC20 token, addressfrom, address to, uint256 value) internal{
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
/**
* @dev Deprecated. This function has issues similar to the ones found in
* {IERC20-approve}, and its usage is discouraged.
*
* Whenever possible, use {safeIncreaseAllowance} and
* {safeDecreaseAllowance} instead.
*/functionsafeApprove(IERC20 token, address spender, uint256 value) internal{
// safeApprove should only be called when setting an initial allowance,// or when resetting it to zero. To increase and decrease it, use// 'safeIncreaseAllowance' and 'safeDecreaseAllowance'// solhint-disable-next-line max-line-lengthrequire((value ==0) || (token.allowance(address(this), spender) ==0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
functionsafeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal{
uint256 newAllowance = token.allowance(address(this), spender) + value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
functionsafeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal{
unchecked {
uint256 oldAllowance = token.allowance(address(this), spender);
require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
uint256 newAllowance = oldAllowance - value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/function_callOptionalReturn(IERC20 token, bytesmemory data) private{
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since// we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that// the target address contains contract code and also asserts for success in the low-level call.bytesmemory returndata =address(token).functionCall(data, "SafeERC20: low-level call failed");
if (returndata.length>0) { // Return data is optional// solhint-disable-next-line max-line-lengthrequire(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}