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
LaminatedProxycontract for the sender using thecomputeProxyAddressfunction. This computation uses theCREATE2opcode, allowing for deterministic addresses based on the sender's address, the contract bytecode, and a salt value. -
Proxy Contract Creation: If a
LaminatedProxycontract does not already exist at the computed address,getOrCreateProxydeploys a new one using theCREATE2opcode. This ensures that each user has a dedicated proxy for managing their deferred function calls. Upon successful creation of a new proxy, theProxyCreatedevent 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
_getOrCreateProxyto retrieve the address of the user'sLaminatedProxycontract, creating a new one if necessary. -
Deferred Function Call Queuing: The function encodes the provided calldata and pushes it into the
LaminatedProxycontract using itspushfunction. 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
pushfunction 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);
}