Skip to content

Commit

Permalink
Add support for MultiQuarkOperations
Browse files Browse the repository at this point in the history
  • Loading branch information
kevincheng96 committed Mar 20, 2024
1 parent b2ab0d6 commit 75568d2
Show file tree
Hide file tree
Showing 3 changed files with 117 additions and 21 deletions.
135 changes: 115 additions & 20 deletions src/quark-core/src/QuarkWallet.sol
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,19 @@ library QuarkWalletMetadata {
"QuarkOperation(uint96 nonce,address scriptAddress,bytes[] scriptSources,bytes scriptCalldata,uint256 expiry)"
);

/// @notice The EIP-712 typehash for authorizing a MultiQuarkOperation for this version of QuarkWallet
bytes32 internal constant MULTI_QUARK_OPERATION_TYPEHASH = keccak256("MultiQuarkOperation(bytes32[] opDigests)");

/// @notice The EIP-712 typehash for authorizing an EIP-1271 signature for this version of QuarkWallet
bytes32 internal constant QUARK_MSG_TYPEHASH = keccak256("QuarkMessage(bytes message)");

/// @notice The EIP-712 domain typehash for this version of QuarkWallet
bytes32 internal constant DOMAIN_TYPEHASH =
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");

/// @notice The EIP-712 domain typehash used for MultiQuarkOperations for this version of QuarkWallet
bytes32 internal constant MULTI_QUARK_OPERATION_DOMAIN_TYPEHASH =
keccak256("EIP712Domain(string name,string version)");
}

/**
Expand All @@ -44,6 +51,7 @@ contract QuarkWallet is IERC1271 {
error BadSignatory();
error EmptyCode();
error InvalidEIP1271Signature();
error InvalidMultiQuarkOperation();
error InvalidSignature();
error NoActiveCallback();
error SignatureExpired();
Expand Down Expand Up @@ -75,9 +83,16 @@ contract QuarkWallet is IERC1271 {
/// @dev The EIP-712 domain typehash for this wallet
bytes32 internal constant DOMAIN_TYPEHASH = QuarkWalletMetadata.DOMAIN_TYPEHASH;

/// @dev The EIP-712 domain typehash used for MultiQuarkOperations for this wallet
bytes32 internal constant MULTI_QUARK_OPERATION_DOMAIN_TYPEHASH =
QuarkWalletMetadata.MULTI_QUARK_OPERATION_DOMAIN_TYPEHASH;

/// @dev The EIP-712 typehash for authorizing an operation for this wallet
bytes32 internal constant QUARK_OPERATION_TYPEHASH = QuarkWalletMetadata.QUARK_OPERATION_TYPEHASH;

/// @dev The EIP-712 typehash for authorizing an operation that is part of a MultiQuarkOperation for this wallet
bytes32 internal constant MULTI_QUARK_OPERATION_TYPEHASH = QuarkWalletMetadata.MULTI_QUARK_OPERATION_TYPEHASH;

/// @dev The EIP-712 typehash for authorizing an EIP-1271 signature for this wallet
bytes32 internal constant QUARK_MSG_TYPEHASH = QuarkWalletMetadata.QUARK_MSG_TYPEHASH;

Expand Down Expand Up @@ -124,29 +139,60 @@ contract QuarkWallet is IERC1271 {
external
returns (bytes memory)
{
if (block.timestamp >= op.expiry) {
revert SignatureExpired();
}
bytes32 opDigest = getDigestForQuarkOperation(op);

bytes memory encodedArray;
for (uint256 i = 0; i < op.scriptSources.length;) {
encodedArray = abi.encodePacked(encodedArray, keccak256(op.scriptSources[i]));
unchecked {
++i;
}
return verifySigAndExecuteQuarkOperation(op, opDigest, v, r, s);
}

/**
* @notice Execute a QuarkOperation that is part of a MultiQuarkOperation via signature
* @dev Can only be called with signatures from the wallet's signer
* @param op A QuarkOperation struct
* @param opDigests A list of EIP-712 digests for the operations in a MultiQuarkOperation
* @param opIndex The index of the QuarkOperation to execute
* @param v EIP-712 signature v value
* @param r EIP-712 signature r value
* @param s EIP-712 signature s value
* @return Return value from the executed operation
*/
function executeMultiQuarkOperation(
QuarkOperation calldata op,
bytes32[] memory opDigests,
uint256 opIndex,
uint8 v,
bytes32 r,
bytes32 s
) public returns (bytes memory) {
bytes32 opDigest = getDigestForQuarkOperation(op);

// TODO: Verify inputs and lengths?
if (opDigest != opDigests[opIndex]) {
revert InvalidMultiQuarkOperation();
}
bytes32 multiOpDigest = getDigestForMultiQuarkOperation(opDigests);

bytes32 structHash = keccak256(
abi.encode(
QUARK_OPERATION_TYPEHASH,
op.nonce,
op.scriptAddress,
keccak256(encodedArray),
keccak256(op.scriptCalldata),
op.expiry
)
);
bytes32 digest = keccak256(abi.encodePacked("\x19\x01", getDomainSeparator(), structHash));
return verifySigAndExecuteQuarkOperation(op, multiOpDigest, v, r, s);
}

/**
* @notice Verify a signature and execute a QuarkOperation
* @param op A QuarkOperation struct
* @param digest A digest to verify the signature against
* @param v EIP-712 signature v value
* @param r EIP-712 signature r value
* @param s EIP-712 signature s value
* @return Return value from the executed operation
*/
function verifySigAndExecuteQuarkOperation(
QuarkOperation calldata op,
bytes32 digest,
uint8 v,
bytes32 r,
bytes32 s
) internal returns (bytes memory) {
if (block.timestamp >= op.expiry) {
revert SignatureExpired();
}

// if the signature check does not revert, the signature is valid
checkValidSignatureInternal(IHasSignerExecutor(address(this)).signer(), digest, v, r, s);
Expand All @@ -159,6 +205,7 @@ contract QuarkWallet is IERC1271 {
}
}

// TODO: Separate event for multi operation?
emit ExecuteQuarkScript(msg.sender, op.scriptAddress, op.nonce, ExecutionType.Signature);

return stateManager.setActiveNonceAndCallback(op.nonce, op.scriptAddress, op.scriptCalldata);
Expand Down Expand Up @@ -196,6 +243,54 @@ contract QuarkWallet is IERC1271 {
);
}

/**
* @dev Returns the domain separator used for MultiQuarkOperations for this Quark wallet
* @return Domain separator
*/
function getDomainSeparatorForMultiQuarkOperation() internal pure returns (bytes32) {
return keccak256(abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(NAME))));
}

/**
* @dev Returns the EIP-712 digest for a QuarkOperation
* @param op A QuarkOperation struct
* @return EIP-712 digest
*/
function getDigestForQuarkOperation(QuarkOperation calldata op) public view returns (bytes32) {
bytes memory encodedArray;
for (uint256 i = 0; i < op.scriptSources.length; ++i) {
encodedArray = abi.encodePacked(encodedArray, keccak256(op.scriptSources[i]));
}

bytes32 structHash = keccak256(
abi.encode(
QUARK_OPERATION_TYPEHASH,
op.nonce,
op.scriptAddress,
keccak256(encodedArray),
keccak256(op.scriptCalldata),
op.expiry
)
);
return keccak256(abi.encodePacked("\x19\x01", getDomainSeparator(), structHash));
}

/**
* @dev Returns the EIP-712 digest for a QuarkOperation
* @param opDigests A list of EIP-712 digests for the operations in a MultiQuarkOperation
* @return EIP-712 digest
*/
function getDigestForMultiQuarkOperation(bytes32[] memory opDigests) public pure returns (bytes32) {
bytes memory encodedArray;
for (uint256 i = 0; i < opDigests.length; ++i) {
encodedArray = abi.encodePacked(encodedArray, opDigests[i]);
}

bytes32 structHash = keccak256(abi.encode(MULTI_QUARK_OPERATION_TYPEHASH, keccak256(encodedArray)));
return keccak256(abi.encodePacked("\x19\x01", getDomainSeparatorForMultiQuarkOperation(), structHash));
}

// TODO: rename
/**
* @dev Returns the hash of a message that can be signed by `signer`
* @param message Message that should be hashed
Expand Down
2 changes: 1 addition & 1 deletion src/quark-core/src/periphery/BatchExecutor.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {QuarkWallet} from "quark-core/src/QuarkWallet.sol";
* @author Compound Labs, Inc.
*/
contract BatchExecutor {
error BatchExecutionError(uint256 opsIndex, bytes err);
error BatchExecutionError(uint256 opIndex, bytes err);

/// @notice The parameters used for processing a QuarkOperation
struct OperationParams {
Expand Down
1 change: 1 addition & 0 deletions src/quark-proxy/src/QuarkWalletProxyFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {QuarkWallet, QuarkWalletMetadata} from "quark-core/src/QuarkWallet.sol";

import {QuarkMinimalProxy} from "quark-proxy/src/QuarkMinimalProxy.sol";

// TODO: Add new functions for MultiQuarkOperation
/**
* @title Quark Wallet Proxy Factory
* @notice A factory for deploying Quark Wallet Proxy instances at deterministic addresses
Expand Down

0 comments on commit 75568d2

Please sign in to comment.