mirror of
https://github.com/luxfi/safe-magnetar.git
synced 2026-10-30 15:18:11 +00:00
87 lines
3.5 KiB
Solidity
87 lines
3.5 KiB
Solidity
// SPDX-License-Identifier: GPL-3.0-only
|
|
pragma solidity =0.8.29;
|
|
|
|
import {Magnetar} from "./Magnetar.sol";
|
|
import {IERC4337, PackedUserOperation} from "./interfaces/IERC4337.sol";
|
|
|
|
/// @title Magnetar Account
|
|
/// @notice An ERC-4337 account whose user operations are authorised by a
|
|
/// committed Magnetar MPC-threshold SLH-DSA (FIPS 205) key, verified through the
|
|
/// `magnetarVerify` precompile.
|
|
///
|
|
/// @dev BINDING. A post-quantum key has no address-recovery trick, so the
|
|
/// account commits `(mode, keccak256(pubkey))` at construction. The user-op
|
|
/// signature carries the full public key and Magnetar signature; the key is
|
|
/// re-hashed against the commitment, then the signature is verified over
|
|
/// `userOpHash`.
|
|
///
|
|
/// SIGNATURE PAYLOAD: `abi.encode(bytes pubkey, bytes magnetarSignature)`.
|
|
contract MagnetarAccount is IERC4337 {
|
|
/// @notice The supported ERC-4337 entry point contract.
|
|
address private immutable _ENTRY_POINT;
|
|
/// @notice The Magnetar parameter-set byte this account verifies under.
|
|
uint8 private immutable _MODE;
|
|
/// @notice keccak256 of the committed Magnetar group public key.
|
|
bytes32 private immutable _PUBKEY_HASH;
|
|
|
|
/// @notice Attempt to call a function reserved for the entry point.
|
|
error UnsupportedEntryPoint();
|
|
/// @notice The mode is not a recognised Magnetar parameter set.
|
|
error InvalidMode();
|
|
/// @notice The supplied public key length is wrong for the mode.
|
|
error InvalidPublicKey();
|
|
|
|
/// @param entryPoint The ERC-4337 entry point contract.
|
|
/// @param mode The Magnetar parameter-set byte (FIPS 205 parameter set).
|
|
/// @param pubkey The full FIPS 205 group public key to bind this account to.
|
|
constructor(address entryPoint, uint8 mode, bytes memory pubkey) {
|
|
require(Magnetar.isValidMode(mode), InvalidMode());
|
|
(uint256 pkLen,) = Magnetar.sizes(mode);
|
|
require(pubkey.length == pkLen, InvalidPublicKey());
|
|
_ENTRY_POINT = entryPoint;
|
|
_MODE = mode;
|
|
_PUBKEY_HASH = keccak256(pubkey);
|
|
}
|
|
|
|
receive() external payable {}
|
|
|
|
/// @notice Function must be called by the entry point.
|
|
modifier onlyEntryPoint() {
|
|
require(msg.sender == _ENTRY_POINT, UnsupportedEntryPoint());
|
|
_;
|
|
}
|
|
|
|
/// @inheritdoc IERC4337
|
|
function validateUserOp(PackedUserOperation calldata userOp, bytes32 userOpHash, uint256 missingAccountFunds)
|
|
external
|
|
onlyEntryPoint
|
|
returns (uint256 validationData)
|
|
{
|
|
if (missingAccountFunds != 0) {
|
|
assembly ("memory-safe") {
|
|
pop(call(gas(), caller(), missingAccountFunds, 0, 0, 0, 0))
|
|
}
|
|
}
|
|
|
|
(bytes memory pubkey, bytes memory magnetarSignature) = abi.decode(userOp.signature, (bytes, bytes));
|
|
if (keccak256(pubkey) != _PUBKEY_HASH) return 1;
|
|
return Magnetar.verify(userOpHash, _MODE, pubkey, magnetarSignature) ? 0 : 1;
|
|
}
|
|
|
|
/// @notice Execute a transaction.
|
|
/// @param target The call target.
|
|
/// @param value The native token value to send.
|
|
/// @param data The call data.
|
|
function execute(address target, uint256 value, bytes calldata data) external onlyEntryPoint {
|
|
assembly ("memory-safe") {
|
|
let ptr := mload(0x40)
|
|
calldatacopy(ptr, data.offset, data.length)
|
|
|
|
if iszero(call(gas(), target, value, ptr, data.length, 0, 0)) {
|
|
returndatacopy(ptr, 0, returndatasize())
|
|
revert(ptr, returndatasize())
|
|
}
|
|
}
|
|
}
|
|
}
|