Skip to content

Commit

Permalink
Implement Encoding with Assembly
Browse files Browse the repository at this point in the history
  • Loading branch information
nlordell committed Nov 29, 2023
1 parent 6fccb45 commit b3e1785
Show file tree
Hide file tree
Showing 2 changed files with 120 additions and 30 deletions.
89 changes: 89 additions & 0 deletions 4337/contracts/Safe4337Module.sol
Original file line number Diff line number Diff line change
Expand Up @@ -212,4 +212,93 @@ contract Safe4337Module is IAccount, HandlerContext, CompatibilityFallbackHandle
);
}
}

/*
function _getSafeOpLessAssembly(
UserOperation calldata userOp
) internal view returns (bytes memory operationData, uint48 validAfter, uint48 validUntil, bytes calldata signatures) {
{
bytes calldata sig = userOp.signature;
validAfter = uint48(bytes6(sig[0:6]));
validUntil = uint48(bytes6(sig[6:12]));
signatures = sig[12:];
}
// It is important that **all** user operation fields are represented in the `SafeOp` data somehow, to prevent
// user operations from being submitted that do not fully respect the user preferences. The only exception are
// the `signature` bytes. Note that even `initCode` needs to be represented in the operation data, otherwise
// it can be replaced with a more expensive initialization that would charge the user additional fees.
{
bytes32[14] memory structData;
unchecked {
structData[0] = SAFE_OP_TYPEHASH;
structData[1] = bytes32(uint256(uint160(userOp.sender)));
structData[2] = bytes32(userOp.nonce);
structData[3] = keccak256(userOp.initCode);
structData[4] = keccak256(userOp.callData);
structData[5] = bytes32(userOp.callGasLimit);
structData[6] = bytes32(userOp.verificationGasLimit);
structData[7] = bytes32(userOp.preVerificationGas);
structData[8] = bytes32(userOp.maxFeePerGas);
structData[9] = bytes32(userOp.maxPriorityFeePerGas);
structData[10] = keccak256(userOp.paymasterAndData);
structData[11] = bytes32(uint256(validAfter));
structData[12] = bytes32(uint256(validUntil));
structData[13] = bytes32(uint256(uint160(SUPPORTED_ENTRYPOINT)));
}
bytes32 structHash;
assembly ("memory-safe") {
structHash := keccak256(structData, 448)
}
operationData = abi.encodePacked(
bytes1(0x19),
bytes1(0x01),
domainSeparator(),
structHash
);
}
}
function _getSafeOpThisShouldWork(
UserOperation calldata userOp
) internal view returns (bytes memory operationData, uint48 validAfter, uint48 validUntil, bytes calldata signatures) {
{
bytes calldata sig = userOp.signature;
validAfter = uint48(bytes6(sig[0:6]));
validUntil = uint48(bytes6(sig[6:12]));
signatures = sig[12:];
}
{
UserOperation calldata _userOp = userOp;
uint48 _validAfter = validAfter;
uint48 _validUntil = validUntil;
operationData = abi.encodePacked(
bytes1(0x19),
bytes1(0x01),
domainSeparator(),
keccak256(
abi.encode(
SAFE_OP_TYPEHASH,
_userOp.sender,
_userOp.nonce,
keccak256(_userOp.initCode),
keccak256(_userOp.callData),
_userOp.callGasLimit,
_userOp.verificationGasLimit,
_userOp.preVerificationGas,
_userOp.maxFeePerGas,
_userOp.maxPriorityFeePerGas,
keccak256(_userOp.paymasterAndData),
_validAfter,
_validUntil,
SUPPORTED_ENTRYPOINT
)
)
);
}
}
*/
}
61 changes: 31 additions & 30 deletions 4337/contracts/test/SafeMock.sol
Original file line number Diff line number Diff line change
Expand Up @@ -194,36 +194,37 @@ contract Safe4337Mock is SafeMock, IAccount {
signatures = sig[12:];
}

operationData = abi.encodePacked(
bytes1(0x19),
bytes1(0x01),
domainSeparator(),
_getSafeOpStructHash(validAfter, validUntil, userOp)
);
}
{
bytes32 structHash;
bytes32 typeHash = SAFE_OP_TYPEHASH;
bytes32 initCodeHash = keccak256(userOp.initCode);
bytes32 callDataHash = keccak256(userOp.callData);
bytes32 paymasterAndDataHash = keccak256(userOp.paymasterAndData);
address entryPoint = SUPPORTED_ENTRYPOINT;

// Use assembly to work around Solidity "stack too deep" errors.
// solhint-disable-next-line no-inline-assembly
assembly ("memory-safe") {
let ptr := mload(0x40)

mstore(ptr, typeHash)
calldatacopy(add(ptr, 32), userOp, 384)
mstore(add(ptr, 96), initCodeHash)
mstore(add(ptr, 128), callDataHash)
mstore(add(ptr, 320), paymasterAndDataHash)
mstore(add(ptr, 352), validAfter)
mstore(add(ptr, 384), validUntil)
mstore(add(ptr, 416), entryPoint)

structHash:= keccak256(ptr, 448)
}

function _getSafeOpStructHash(
uint48 validAfter,
uint48 validUntil,
UserOperation calldata userOp
) private view returns (bytes32 structHash) {
structHash = keccak256(
abi.encode(
SAFE_OP_TYPEHASH,
userOp.sender,
userOp.nonce,
keccak256(userOp.initCode),
keccak256(userOp.callData),
userOp.callGasLimit,
userOp.verificationGasLimit,
userOp.preVerificationGas,
userOp.maxFeePerGas,
userOp.maxPriorityFeePerGas,
keccak256(userOp.paymasterAndData),
// validAfter,
// validUntil,
SUPPORTED_ENTRYPOINT
)
);
operationData = abi.encodePacked(
bytes1(0x19),
bytes1(0x01),
domainSeparator(),
structHash
);
}
}
}

0 comments on commit b3e1785

Please sign in to comment.