Skip to main content

CallBreaker

The CallBreaker is responsible for managing and executing the sequence of transactions, making sure they meet the conditions set by the user. Transactions are then verified to ensure everything went according to plan.

Call Verification

The verify function is a critical component of the contract, designed to execute a set of pre-queued calls and compare their actual return values against expected return values provided as part of the function's arguments. This function plays a pivotal role in ensuring that a sequence of operations meets expected outcomes before any permanent changes are committed.

Verify first decodes the list of call objects and their expected return values from the provided arguments. It then resets and populates internal storage structures used for tracking the calls, return values, and associated data; it also iterates over each call, executing it and verifying its actual return against the expected return.

function verify(
bytes calldata callsBytes,
bytes calldata returnsBytes,
bytes calldata associatedData,
bytes calldata hintdices
) external payable onlyPortalClosed {
if (msg.sender.code.length != 0) {
revert MustBeEOA();
}
_setPortalOpen();

CallObject[] memory calls = abi.decode(callsBytes, (CallObject[]));
ReturnObject[] memory returnValues = abi.decode(returnsBytes, (ReturnObject[]));

if (calls.length != returnValues.length) {
revert LengthMismatch();
}

_resetTraceStoresWith(calls, returnValues);
_populateAssociatedDataStore(associatedData);
_populateHintdices(hintdices);
_populateCallIndices();

uint256 l = calls.length;
for (uint256 i = 0; i < l; i++) {
_setCurrentlyExecutingCallIndex(i);
_executeAndVerifyCall(i);
}

_cleanUpStorage();
_setPortalClosed();
emit VerifyStxn();
}

Call Introspection

There are several functions for inspecting call objects and enforcing transaction ordering within a smart transaction bundle. The following functions allows users to prevent frontruns and backruns within a transaction bundle through ordering invariants (in addition to existing call invariants).

getPair - This function fetches a pair consisting of a CallObject and a ReturnObject from the contract's storage, based on a given index. getCompleteCallIndexList - This function is designed to search through a list of calls (the callList) for all occurrences of a specific call, identified by the CallObject. It returns an array of indices representing the positions where the given call appears in the list. getCallIndex and getReverseIndex - fetches indices from the hintIndicesStore based on a CallObject, validating the correctness of these indices against the call's actual position in the callList. getCurrentlyExecuting - This function returns the index of the currently executing call.

Tipping

The receive() function, used for handling Ether transfers to the contract, acts as a mechanism for receiving tips. When Ether is sent to the contract without calling any specific function, the receive() function is invoked automatically. It retrieves an address (presumably the solver) from the transaction's associated data store (via. fetchFromAssociatedDataStore) using a predetermined key ("tipYourBartender"), and it forwards the received Ether to the address fetched from the associated data store.