Skip to main content

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:

  1. Computes the Proxy Address: It calculates the address of a LaminatedProxy contract for the sender using the computeProxyAddress function. This computation uses the CREATE2 opcode, allowing for deterministic addresses based on the sender's address, the contract bytecode, and a salt value.

  2. Proxy Contract Creation: If a LaminatedProxy contract does not already exist at the computed address, getOrCreateProxy deploys a new one using the CREATE2 opcode. This ensures that each user has a dedicated proxy for managing their deferred function calls. Upon successful creation of a new proxy, the ProxyCreated 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:

  1. Proxy Retrieval or Creation: It calls _getOrCreateProxy to retrieve the address of the user's LaminatedProxy contract, creating a new one if necessary.

  2. Deferred Function Call Queuing: The function encodes the provided calldata and pushes it into the LaminatedProxy contract using its push function. This calldata represents the deferred function call, including its parameters and the delay before execution.

  3. Sequence Number Assignment: Each deferred function call is assigned a sequence number, which is returned by the push function of the LaminatedProxy. 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);
}