Laminator
The Laminator contract acts as a factory for laminated proxies.
Factory for LaminatedProxies: The Laminator acts as a factory, creating instances of LaminatorProxy
for users. Each proxy serves as a mempool where user transactions are queued and managed.
Initialization Hub: It serves as the starting point for users to engage with smart transactions. By creating a LaminatorProxy
, users are able to queue and manage their smart transactions.
The Laminator contract provides two essential functions for managing deferred function calls through LaminatedProxy
contracts: getOrCreateProxy
and pushToProxy
.
getOrCreateProxy
Function
The getOrCreateProxy
function is responsible for ensuring that each user has a unique LaminatedProxy
contract. It performs two key operations:
-
Computes the Proxy Address: It calculates the address of a
LaminatedProxy
contract for the sender using thecomputeProxyAddress
function. This computation uses theCREATE2
opcode, allowing for deterministic addresses based on the sender's address, the contract bytecode, and a salt value. -
Proxy Contract Creation: If a
LaminatedProxy
contract does not already exist at the computed address,getOrCreateProxy
deploys a new one using theCREATE2
opcode. This ensures that each user has a dedicated proxy for managing their deferred function calls. Upon successful creation of a new proxy, theProxyCreated
event is emitted, signaling the creation of a new proxy contract for the user.
function _getOrCreateProxy(address sender) internal returns (address) {
address proxyAddress = computeProxyAddress(sender);
// Check if the proxy contract already exists
uint256 codeSize;
assembly {
codeSize := extcodesize(proxyAddress)
}
if (codeSize == 0) {
// Create a new proxy contract using create2
// Encode the constructor arguments and append the constructor arguments to the bytecode
bytes32 salt = keccak256(abi.encodePacked(sender));
bytes memory constructorArgs = abi.encode(address(this), address(callBreaker), sender);
bytes memory bytecode = abi.encodePacked(type(LaminatedProxy).creationCode, constructorArgs);
assembly {
proxyAddress := create2(0, add(bytecode, 32), mload(bytecode), salt)
}
emit ProxyCreated(sender, proxyAddress);
}
return proxyAddress;
}
pushToProxy
Function
The pushToProxy
function allows users to queue deferred function calls in their LaminatedProxy
contract. It performs the following steps:
-
Proxy Retrieval or Creation: It calls
_getOrCreateProxy
to retrieve the address of the user'sLaminatedProxy
contract, creating a new one if necessary. -
Deferred Function Call Queuing: The function encodes the provided calldata and pushes it into the
LaminatedProxy
contract using itspush
function. This calldata represents the deferred function call, including its parameters and the delay before execution. -
Sequence Number Assignment: Each deferred function call is assigned a sequence number, which is returned by the
push
function of theLaminatedProxy
. This sequence number is used to track and manage the queued function call.
These functions together facilitate the creation and management of deferred function calls, allowing users to queue and execute transactions through their dedicated LaminatedProxy
contracts. Users (transaction pushers) will interact with the laminator contract, while solvers will interact with each user's LaminatedProxy contract.
function pushToProxy(bytes calldata cData, uint32 delay, bytes32 selector, SolverData[] memory dataValues)
external
returns (uint256 sequenceNumber)
{
LaminatedProxy proxy = LaminatedProxy(payable(_getOrCreateProxy(msg.sender)));
sequenceNumber = proxy.push(cData, delay, dataValues);
CallObject[] memory callObjs = abi.decode(cData, (CallObject[]));
emit ProxyPushed(address(proxy), callObjs, sequenceNumber, selector, dataValues);
}