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

Feat/productionize examples #1035

Closed
wants to merge 107 commits into from
Closed
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
107 commits
Select commit Hold shift + click to select a range
31f917d
initial scaffolding for new implementations
jhweintraub May 29, 2024
c8755c2
Finished initial versions, no tests or examples yet.
jhweintraub Jun 6, 2024
c72d6c8
modify based on devrel feedback
jhweintraub Jun 7, 2024
99b0a89
initial tests for production-examples. Sufficient coverage but needs …
jhweintraub Jun 13, 2024
ae52306
ccip-precommit and fix token approval bug where tokenAmounts includes…
jhweintraub Jun 14, 2024
e4d6209
fill in coverage gaps and fix devrel feedback
jhweintraub Jun 19, 2024
4546faf
fix headings, add typeAndVersion, move interface file, and other form…
jhweintraub Jun 20, 2024
8d2f283
being deprecating existing arc's and fix tests to reflect new product…
jhweintraub Jun 24, 2024
b9ca301
re-organize folders for new ARCs, and fix Self-Funded ping-pong tests
jhweintraub Jun 25, 2024
7f41744
Merge branch 'ccip-develop' into feat/productionize-examples
jhweintraub Jun 25, 2024
13b9c91
fix issues created during merge, and
jhweintraub Jun 26, 2024
6d0b0c1
updated geth wrappers for new audited reference contracts
jhweintraub Jun 28, 2024
8376f9c
Merge branch 'ccip-develop' into feat/productionize-examples
jhweintraub Jun 28, 2024
330b6a4
re-add messageHasher gethwrapper that accidentally got removed while …
jhweintraub Jun 28, 2024
690971f
typo fix
jhweintraub Jun 28, 2024
534ec1a
updated gas snapshot
jhweintraub Jun 28, 2024
631406c
standardized the file naming, ACK's should be capitalized. Hopefully …
jhweintraub Jun 28, 2024
e8f876b
another wrapper generation attempt
jhweintraub Jun 28, 2024
2708df7
Merge e8f876b1b35702cf9baac3d2f2282a3c22585990 into 8cf4caa16b37c97bc…
jhweintraub Jun 28, 2024
2a04bcf
Update gethwrappers
app-token-issuer-infra-releng[bot] Jun 28, 2024
2adba13
Merge branch 'ccip-develop' into feat/productionize-examples
jhweintraub Jun 28, 2024
dab2069
add MIT licenses to example code
jhweintraub Jun 28, 2024
d6f5cc3
forgot a file
jhweintraub Jun 28, 2024
e391d17
fixes based on feedback from review.
jhweintraub Jul 1, 2024
5bfe40e
Merge branch 'ccip-develop' into feat/productionize-examples
jhweintraub Jul 1, 2024
d35c967
Merge 5bfe40ed24d608e28400d4695c0f9dcc10ea3dd0 into c0b080a914fb226bb…
jhweintraub Jul 1, 2024
938b6d8
Update gethwrappers
app-token-issuer-infra-releng[bot] Jul 1, 2024
c3bb047
comments from pair
matYang Jul 2, 2024
3c9c623
more cosmetic modifications based on sr. feedback
jhweintraub Jul 2, 2024
f838f6c
Merge branch 'ccip-develop' into feat/productionize-examples
jhweintraub Jul 2, 2024
59e32c2
Merge f838f6c6de289e27ee964b6252b3778109e72536 into 1b1964b1cc18a11cd…
jhweintraub Jul 2, 2024
6c86884
Update gethwrappers
app-token-issuer-infra-releng[bot] Jul 2, 2024
d835176
Merge branch 'ccip-develop' into feat/productionize-examples
jhweintraub Jul 3, 2024
4b98ecb
re add onlyOwner modifier to retryMessage and other coverage gaps fil…
jhweintraub Jul 3, 2024
6e1a227
ccip precommit
jhweintraub Jul 3, 2024
3abe1e7
Merge 6e1a2272e10a1bc0351ba0742b33c1c74be1df74 into 3bbc37fc5ead07dfe…
jhweintraub Jul 3, 2024
e24e512
Update gethwrappers
app-token-issuer-infra-releng[bot] Jul 3, 2024
ea30ec9
Merge branch 'ccip-develop' into feat/productionize-examples
jhweintraub Jul 3, 2024
1e3c64e
Merge branch 'ccip-develop' into feat/productionize-examples
jhweintraub Jul 3, 2024
64d1ec8
additional comments
jhweintraub Jul 4, 2024
caa27c1
Merge 64d1ec8d554bc70188562d819189c6be5132390a into a8e009fe8a74c505a…
jhweintraub Jul 4, 2024
1e907e9
Update gethwrappers
app-token-issuer-infra-releng[bot] Jul 4, 2024
19830dd
more comments to address
matYang Jul 4, 2024
3f07b0e
fixes for more requested feedback. Restructure ACK receivers and Clie…
jhweintraub Jul 8, 2024
d1e9ea6
Merge branch 'ccip-develop' into feat/productionize-examples
jhweintraub Jul 8, 2024
0da5306
Merge d1e9ea6d3289434da3de95843363801d24b92790 into 5db8286a50df2a262…
jhweintraub Jul 8, 2024
63b76e4
Update gethwrappers
app-token-issuer-infra-releng[bot] Jul 8, 2024
a791156
Merge branch 'ccip-develop' into feat/productionize-examples
jhweintraub Jul 8, 2024
96b9b32
pair review comments
matYang Jul 9, 2024
cd06c1b
Merge 96b9b3200e409c71c2e03eadb601a1b4a016c636 into 7d7b54aad371d2da5…
jhweintraub Jul 9, 2024
c94eac4
Update gethwrappers
app-token-issuer-infra-releng[bot] Jul 9, 2024
d6aff68
Merge branch 'ccip-develop' into feat/productionize-examples
jhweintraub Jul 9, 2024
f46fe99
Merge branch 'ccip-develop' into feat/productionize-examples
jhweintraub Jul 9, 2024
b26c912
better comments and natspec
jhweintraub Jul 9, 2024
d8763f6
Merge b26c912026d03e446438ea84f3cba9957dd133d4 into 5f54e6de68ce1ff7a…
jhweintraub Jul 9, 2024
5640b46
Update gethwrappers
app-token-issuer-infra-releng[bot] Jul 9, 2024
4137d04
fill in coverage gaps
jhweintraub Jul 9, 2024
724d9bc
Merge 4137d04db6c1cce78b30f97acd46aa9562cc2d23 into 5f54e6de68ce1ff7a…
jhweintraub Jul 9, 2024
ae26d9b
Update gethwrappers
app-token-issuer-infra-releng[bot] Jul 9, 2024
972b1fe
Merge branch 'ccip-develop' into feat/productionize-examples
jhweintraub Jul 10, 2024
9defd5c
fixed but with duplicate naming error
jhweintraub Jul 10, 2024
3e9bb2c
small nits
matYang Jul 11, 2024
867173c
Merge branch 'ccip-develop' into feat/productionize-examples
jhweintraub Jul 12, 2024
aaa82cd
Merge branch 'ccip-develop' into feat/productionize-examples
jhweintraub Jul 12, 2024
b4b7cd5
Merge branch 'ccip-develop' into feat/productionize-examples
jhweintraub Jul 15, 2024
9d6199b
update gethwrappers for missing example contract
jhweintraub Jul 15, 2024
a54d2de
attempted bug fix of CCIPClient fee-token allowance when pre-funding
jhweintraub Jul 15, 2024
e8aa64f
Merge branch 'ccip-develop' into feat/productionize-examples
jhweintraub Jul 15, 2024
d323953
Merge e8aa64f8469958f81203f48f8179eba3984b8561 into 1b5ec8e5bd44cefc2…
jhweintraub Jul 15, 2024
4312d74
Update gethwrappers
app-token-issuer-infra-releng[bot] Jul 15, 2024
e21f356
fix bug arising from tokenAmounts containing the fee token
jhweintraub Jul 15, 2024
b07d479
Merge branch 'ccip-develop' into feat/productionize-examples
jhweintraub Jul 15, 2024
911f067
Merge b07d479312bc731e6476ee3bb8b90b36859ce182 into 6876a78e5fcb5c455…
jhweintraub Jul 15, 2024
ba0a487
Update gethwrappers
app-token-issuer-infra-releng[bot] Jul 15, 2024
3f76b9f
typos
RensR Jul 16, 2024
62a0b8d
gas optimizations and nitpick fixes
jhweintraub Jul 16, 2024
993b6a9
Merge branch 'ccip-develop' into feat/productionize-examples
jhweintraub Jul 16, 2024
170c599
Merge 993b6a9f13a083e3c9e418cf6e108483b138420d into 2f2dc93b6e88acf8a…
jhweintraub Jul 16, 2024
730c44f
Update gethwrappers
app-token-issuer-infra-releng[bot] Jul 16, 2024
4c04986
reintroduce EtherSenderReceiver Tests that were previously deprecated
jhweintraub Jul 16, 2024
f9f20d5
forge fmt
jhweintraub Jul 16, 2024
7462cc7
formatting fixes and rework CCIPClient Pre-funding mechanism
jhweintraub Jul 17, 2024
2d42614
Merge branch 'ccip-develop' into feat/productionize-examples
jhweintraub Jul 17, 2024
1ac84f0
Merge 2d42614540ae7af3020f047f6980dee95ddcfebc into f420af8542bc9e9d8…
jhweintraub Jul 17, 2024
ed535fd
Update gethwrappers
app-token-issuer-infra-releng[bot] Jul 17, 2024
7ebb3cf
modify pre-funding state variable to be immutable
jhweintraub Jul 17, 2024
992b783
Merge 7ebb3cfb0c078daeee88b0190eb09084196e76f8 into f420af8542bc9e9d8…
jhweintraub Jul 17, 2024
8d1019b
Update gethwrappers
app-token-issuer-infra-releng[bot] Jul 17, 2024
be8eb68
Merge branch 'ccip-develop' into feat/productionize-examples
jhweintraub Jul 18, 2024
57e7d54
more feedback fixes
jhweintraub Jul 19, 2024
7991028
Merge branch 'ccip-develop' into feat/productionize-examples
jhweintraub Jul 19, 2024
edab7eb
Merge 79910284a9809eac337ab01fe81f4432aa429031 into ef923c333aecfd154…
jhweintraub Jul 19, 2024
66569ca
Update gethwrappers
app-token-issuer-infra-releng[bot] Jul 19, 2024
686ac30
gas snapshot fix
jhweintraub Jul 19, 2024
5f3b1e4
Merge branch 'ccip-develop' into feat/productionize-examples
jhweintraub Jul 29, 2024
bcc9404
Merge branch 'ccip-develop' into feat/productionize-examples
jhweintraub Jul 30, 2024
ab28217
comment formatting cleanup
jhweintraub Jul 30, 2024
34869f4
Merge branch 'ccip-develop' into feat/productionize-examples
jhweintraub Jul 30, 2024
27d7a1a
Merge branch 'ccip-develop' into feat/productionize-examples
jhweintraub Jul 31, 2024
7b20a73
gas snapshot fixes
jhweintraub Jul 31, 2024
4c83496
Merge branch 'ccip-develop' into feat/productionize-examples
jhweintraub Jul 31, 2024
352b7a6
Merge branch 'ccip-develop' into feat/productionize-examples
jhweintraub Aug 1, 2024
0d92255
Merge branch 'ccip-develop' into feat/productionize-examples
jhweintraub Aug 1, 2024
8a400fb
Merge branch 'ccip-develop' into feat/productionize-examples
jhweintraub Aug 5, 2024
5722746
Merge branch 'ccip-develop' into feat/productionize-examples
jhweintraub Sep 5, 2024
e01e113
fix compilation and test issues that arose during merge
jhweintraub Sep 5, 2024
cb2849c
formalize comment and formatting cleanup
jhweintraub Sep 9, 2024
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
1,537 changes: 780 additions & 757 deletions contracts/gas-snapshots/ccip.gas-snapshot

Large diffs are not rendered by default.

99 changes: 99 additions & 0 deletions contracts/src/v0.8/ccip/production-examples/CCIPClient.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
pragma solidity ^0.8.0;

import {IERC20} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/utils/SafeERC20.sol";

import {CCIPReceiverWithACK} from "./CCIPReceiverWithACK.sol";

import {IRouterClient} from "../interfaces/IRouterClient.sol";
import {Client} from "../libraries/Client.sol";

contract CCIPClient is CCIPReceiverWithACK {
using SafeERC20 for IERC20;

error InvalidConfig();
error CannotAcknowledgeUnsentMessage(bytes32);

/// @notice You can't import CCIPReceiver and Sender due to similar parents so functionality of CCIPSender is duplicated here
constructor(address router, IERC20 feeToken) CCIPReceiverWithACK(router, feeToken) {}

function ccipSend(
uint64 destChainSelector,
Client.EVMTokenAmount[] memory tokenAmounts,
bytes memory data,
address feeToken
) public payable validChain(destChainSelector) returns (bytes32 messageId) {
Client.EVM2AnyMessage memory message = Client.EVM2AnyMessage({
receiver: s_chains[destChainSelector],
data: data,
tokenAmounts: tokenAmounts,
extraArgs: s_extraArgsBytes[destChainSelector],
feeToken: feeToken
});

uint256 fee = IRouterClient(i_ccipRouter).getFee(destChainSelector, message);

bool sendingFeeTokenNormally;

for (uint256 i = 0; i < tokenAmounts.length; ++i) {
// Transfer the tokens to pay for tokens in tokenAmounts
IERC20(tokenAmounts[i].token).safeTransferFrom(msg.sender, address(this), tokenAmounts[i].amount);

// If they are sending the feeToken through, and its the same as the ack fee token, and also paying in it, then you don't need to approve
// it at all cause its already set as type(uint).max. You can't use safeIncreaseAllowance() either cause it will overflow the token allowance
if (tokenAmounts[i].token == feeToken && feeToken != address(0) && feeToken == address(s_feeToken)) {
sendingFeeTokenNormally = true;
IERC20(tokenAmounts[i].token).safeTransferFrom(msg.sender, address(this), fee);
}
// If they're not sending the fee token, then go ahead and approve
else {
IERC20(tokenAmounts[i].token).safeApprove(i_ccipRouter, tokenAmounts[i].amount);
}
}

// Since the fee token was already set in the ReceiverWithACK parent, we don't need to approve it to spend, only to ensure we have enough
// funds for the transfer
if (!sendingFeeTokenNormally && feeToken == address(s_feeToken) && feeToken != address(0)) {
IERC20(feeToken).safeTransferFrom(msg.sender, address(this), fee);
} else if (feeToken == address(0) && msg.value < fee) {
revert IRouterClient.InsufficientFeeTokenAmount();
}

messageId =
IRouterClient(i_ccipRouter).ccipSend{value: feeToken == address(0) ? fee : 0}(destChainSelector, message);

s_messageStatus[messageId] = CCIPReceiverWithACK.MessageStatus.SENT;

// Since the message was outgoing, and not ACK, use bytes32(0) to reflect this
emit MessageSent(messageId, bytes32(0));

return messageId;
}

/// CCIPReceiver processMessage to make easier to modify
/// @notice function requres that
function processMessage(Client.Any2EVMMessage calldata message) external virtual override onlySelf {
(MessagePayload memory payload) = abi.decode(message.data, (MessagePayload));

if (payload.messageType == MessageType.OUTGOING) {
// Insert Processing workflow here.

// If the message was outgoing, then send an ack response.
_sendAck(message);
} else if (payload.messageType == MessageType.ACK) {
// Decode message into the magic-bytes and the messageId to ensure the message is encoded correctly
(bytes memory magicBytes, bytes32 messageId) = abi.decode(payload.data, (bytes, bytes32));

// Ensure Ack Message contains proper magic-bytes
if (keccak256(magicBytes) != keccak256(ACKMESSAGEMAGICBYTES)) revert InvalidMagicBytes();

// Make sure the ACK message was originally sent by this contract
if (s_messageStatus[messageId] != MessageStatus.SENT) revert CannotAcknowledgeUnsentMessage(messageId);

// Mark the message has finalized from a proper ack-message.
s_messageStatus[messageId] = MessageStatus.ACKNOWLEDGED;

emit MessageAckReceived(messageId);
}
}
}
100 changes: 100 additions & 0 deletions contracts/src/v0.8/ccip/production-examples/CCIPClientBase.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
pragma solidity ^0.8.0;

import {OwnerIsCreator} from "../../shared/access/OwnerIsCreator.sol";

import {IERC20} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/utils/SafeERC20.sol";
import {Address} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/Address.sol";

import {ICCIPClientBase} from "./interfaces/ICCIPClientBase.sol";

abstract contract CCIPClientBase is ICCIPClientBase, OwnerIsCreator {
using SafeERC20 for IERC20;
using Address for address;

address internal immutable i_ccipRouter;

mapping(uint64 destChainSelector => mapping(bytes sender => bool approved)) public s_approvedSenders;
mapping(uint64 destChainSelector => bytes recipient) public s_chains;
mapping(uint64 destChainselector => bytes extraArgsBytes) public s_extraArgsBytes;

constructor(address router) {
if (router == address(0)) revert InvalidRouter(address(0));
i_ccipRouter = router;
}

/////////////////////////////////////////////////////////////////////
jhweintraub marked this conversation as resolved.
Show resolved Hide resolved
// Router Management
/////////////////////////////////////////////////////////////////////

function getRouter() public view returns (address) {
return i_ccipRouter;
}

/// @dev only calls from the set router are accepted.
modifier onlyRouter() {
if (msg.sender != getRouter()) revert InvalidRouter(msg.sender);
_;
}

/////////////////////////////////////////////////////////////////////
// Sender/Receiver Management
/////////////////////////////////////////////////////////////////////

function updateApprovedSenders(
approvedSenderUpdate[] calldata adds,
approvedSenderUpdate[] calldata removes
) external onlyOwner {
for (uint256 i = 0; i < removes.length; ++i) {
delete s_approvedSenders[removes[i].destChainSelector][removes[i].sender];
}

for (uint256 i = 0; i < adds.length; ++i) {
s_approvedSenders[adds[i].destChainSelector][adds[i].sender] = true;
}
}

/////////////////////////////////////////////////////////////////////
// Fee Token Management
/////////////////////////////////////////////////////////////////////

fallback() external payable {}
receive() external payable {}

function withdrawNativeToken(address payable to, uint256 amount) external onlyOwner {
Address.sendValue(to, amount);
}

function withdrawTokens(address token, address to, uint256 amount) external onlyOwner {
IERC20(token).safeTransfer(to, amount);
}

/////////////////////////////////////////////////////////////////////
// Chain Management
/////////////////////////////////////////////////////////////////////

function enableChain(
uint64 chainSelector,
bytes calldata recipient,
bytes calldata extraArgsBytes
) external onlyOwner {
s_chains[chainSelector] = recipient;

if (extraArgsBytes.length != 0) s_extraArgsBytes[chainSelector] = extraArgsBytes;
}

function disableChain(uint64 chainSelector) external onlyOwner {
delete s_chains[chainSelector];
delete s_extraArgsBytes[chainSelector];
}

modifier validChain(uint64 chainSelector) {
if (s_chains[chainSelector].length == 0) revert InvalidChain(chainSelector);
_;
}

modifier validSender(uint64 chainSelector, bytes memory sender) {
if (!s_approvedSenders[chainSelector][sender]) revert InvalidSender(sender);
_;
}
}
131 changes: 131 additions & 0 deletions contracts/src/v0.8/ccip/production-examples/CCIPReceiver.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import {Client} from "../libraries/Client.sol";
import {CCIPClientBase} from "./CCIPClientBase.sol";

import {IERC20} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/IERC20.sol";
import {SafeERC20} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/token/ERC20/utils/SafeERC20.sol";
import {EnumerableMap} from "../../vendor/openzeppelin-solidity/v4.8.3/contracts/utils/structs/EnumerableMap.sol";

contract CCIPReceiver is CCIPClientBase {
using SafeERC20 for IERC20;
using EnumerableMap for EnumerableMap.Bytes32ToUintMap;

error OnlySelf();
error ErrorCase();
error MessageNotFailed(bytes32 messageId);

event MessageFailed(bytes32 indexed messageId, bytes reason);
event MessageSucceeded(bytes32 indexed messageId);
event MessageRecovered(bytes32 indexed messageId);

// Example error code, could have many different error codes.
enum ErrorCode {
// RESOLVED is first so that the default value is resolved.
RESOLVED,
// Could have any number of error codes here.
FAILED
}

// The message contents of failed messages are stored here.
mapping(bytes32 messageId => Client.Any2EVMMessage contents) internal s_messageContents;

// Contains failed messages and their state.
EnumerableMap.Bytes32ToUintMap internal s_failedMessages;

bool internal s_simRevert;

constructor(address router) CCIPClientBase(router) {}

/// @notice The entrypoint for the CCIP router to call. This function should
/// never revert, all errors should be handled internally in this contract.
/// @param message The message to process.
/// @dev Extremely important to ensure only router calls this.
function ccipReceive(Client.Any2EVMMessage calldata message)
external
virtual
onlyRouter
validChain(message.sourceChainSelector)
{
try this.processMessage(message) {}
catch (bytes memory err) {
// Could set different error codes based on the caught error. Each could be
// handled differently.
s_failedMessages.set(message.messageId, uint256(ErrorCode.FAILED));

s_messageContents[message.messageId] = message;

// Don't revert so CCIP doesn't revert. Emit event instead.
// The message can be retried later without having to do manual execution of CCIP.
emit MessageFailed(message.messageId, err);
return;
}

emit MessageSucceeded(message.messageId);
}

/// @notice This function the entrypoint for this contract to process messages.
/// @param message The message to process.
/// @dev This example just sends the tokens to the owner of this contracts. More
/// interesting functions could be implemented.
/// @dev It has to be external because of the try/catch.
function processMessage(Client.Any2EVMMessage calldata message)
external
virtual
onlySelf
validSender(message.sourceChainSelector, message.sender)
{
// Insert Custom logic here
if (s_simRevert) revert ErrorCase();
}

function _retryFailedMessage(Client.Any2EVMMessage memory message) internal virtual {
// Owner rescues tokens sent with a failed message
for (uint256 i = 0; i < message.destTokenAmounts.length; ++i) {
uint256 amount = message.destTokenAmounts[i].amount;
address token = message.destTokenAmounts[i].token;

IERC20(token).safeTransfer(owner(), amount);
}
}

/// @notice This function is callable by the owner when a message has failed
/// to unblock the tokens that are associated with that message.
/// @dev This function is only callable by the owner.
function retryFailedMessage(bytes32 messageId) external onlyOwner {
if (s_failedMessages.get(messageId) != uint256(ErrorCode.FAILED)) revert MessageNotFailed(messageId);

// Set the error code to 0 to disallow reentry and retry the same failed message
// multiple times.
s_failedMessages.set(messageId, uint256(ErrorCode.RESOLVED));

// Do stuff to retry message, potentially just releasing the associated tokens
Client.Any2EVMMessage memory message = s_messageContents[messageId];

// Let the user override the implementation, since different workflow may be desired for retrying a merssage
_retryFailedMessage(message);

s_failedMessages.remove(messageId); // If retry succeeds, remove from set of failed messages.

emit MessageRecovered(messageId);
}

function getMessageContents(bytes32 messageId) public view returns (Client.Any2EVMMessage memory) {
return s_messageContents[messageId];
}

function getMessageStatus(bytes32 messageId) public view returns (uint256) {
return s_failedMessages.get(messageId);
}

// An example function to demonstrate recovery
function setSimRevert(bool simRevert) external onlyOwner {
s_simRevert = simRevert;
}

modifier onlySelf() {
if (msg.sender != address(this)) revert OnlySelf();
_;
}
}
Loading
Loading