-
Notifications
You must be signed in to change notification settings - Fork 260
[ETHEREUM-CONTRACTS] ClearMacro forwarder (+ Permit2 variant) #2148
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
d10r
wants to merge
39
commits into
dev
Choose a base branch
from
2026-03-permit2_and_macro
base: dev
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 33 commits
Commits
Show all changes
39 commits
Select commit
Hold shift + click to select a range
277c4c0
first shot
d10r 6d571b2
minimal implementation
d10r 4d99c7b
smoke test passing
d10r 6ee7437
solhint: don't check for updates when not asked to
d10r 3b54b90
updated foundry to v1.3.6 (1.3 improves eip712 support)
d10r 3f3e74d
disable new linter rules (for now)
d10r 53f953f
more testing
d10r 5ee90cb
added EIP-4337-like nonce
d10r 06d230e
added provider auth
d10r 458cbec
Merge branch 'dev' into 2026-01-only712
d10r 35c55b1
fix mishap
d10r 4359446
added validity time window validation
d10r d2ff27f
updated to revised simplified schema
d10r 17a38f1
fix msgSender
d10r bd5a524
remove unneeded constructor arg
d10r 8093074
added encodeParams to forwarder
d10r 2f847fc
change naming convention for encode view functions
d10r 7a5ee29
rearrange code
d10r 61c13d3
added scripts for managing the 712 macro forwarder and related ACL
d10r 05efaf0
flattened Security fields
d10r 5c42300
added Permit2MacroForwarder
d10r 1c29d42
working PoC
d10r a5f82de
allow self-relaying
d10r 8b0711f
major refactoring of ClearSigning related code
d10r 0619c6b
Merge branch 'dev' into 2026-03-permit2_and_macro
d10r 0b0689b
renamed ClearSigningMacro -> ClearMacro
d10r 68a1028
renamed IUserDefinedMacro -> IMacro
d10r 94e2336
Merge branch 'dev' into 2026-03-permit2_and_macro
d10r c354fe9
updated CHANGELOG
d10r 077c054
add attribution
d10r 97f2c44
fix solc version
d10r 0aa11c3
added Base contract for ClearMacro
d10r 3bd4c27
added missing lib
d10r 3b8532b
rename
d10r 3800d24
renamings
d10r 9b1cc5c
Merge branch 'dev' into 2026-03-permit2_and_macro
d10r 51f03d2
more test coverage
d10r d4f3ab0
simplified ClearMacroBase
d10r cd60029
Merge branch 'dev' into 2026-03-permit2_and_macro
hellwolf File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
32 changes: 32 additions & 0 deletions
32
packages/ethereum-contracts/contracts/interfaces/external/IPermit2.sol
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| // SPDX-License-Identifier: MIT | ||
| pragma solidity ^0.8.0; | ||
|
|
||
| /// @notice Minimal interface for Uniswap Permit2 (SignatureTransfer). | ||
| /// @dev Canonical address: 0x000000000022D473030F116dDEE9F6B43aC78BA3 | ||
| /// For the full Permit2 interface, see https://github.com/Uniswap/permit2/blob/main/src/interfaces/IPermit2.sol | ||
| interface IPermit2 { | ||
| struct TokenPermissions { | ||
| address token; | ||
| uint256 amount; | ||
| } | ||
| struct PermitTransferFrom { | ||
| TokenPermissions permitted; | ||
| uint256 nonce; | ||
| uint256 deadline; | ||
| } | ||
| struct SignatureTransferDetails { | ||
| address to; | ||
| uint256 requestedAmount; | ||
| } | ||
|
|
||
| function DOMAIN_SEPARATOR() external view returns (bytes32); | ||
|
|
||
| function permitWitnessTransferFrom( | ||
| PermitTransferFrom calldata permit, | ||
| SignatureTransferDetails calldata transferDetails, | ||
| address owner, | ||
| bytes32 witness, | ||
| string calldata witnessTypeString, | ||
| bytes calldata signature | ||
| ) external; | ||
| } | ||
37 changes: 37 additions & 0 deletions
37
packages/ethereum-contracts/contracts/interfaces/utils/IClearMacro.sol
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| // SPDX-License-Identifier: MIT | ||
| pragma solidity >=0.8.11; | ||
|
|
||
| import { IMacro } from "./IMacro.sol"; | ||
|
|
||
| /** | ||
| * @dev Interface for a macro used with the ClearMacroForwarder. | ||
| * Implementations provide the EIP-712 metadata and hashing logic for the | ||
| * macro-specific action encoded in `params`. | ||
| */ | ||
| interface IClearMacro is IMacro { | ||
| /** | ||
| * @dev Returns the primary EIP-712 type name. | ||
| * This is usually rendered prominently by wallets and should concisely | ||
| * describe the action or intent to be signed. | ||
| * @param params Encoded macro-specific parameters. | ||
| * @return name The primary type name. | ||
| */ | ||
| function getPrimaryTypeName(bytes memory params) external view returns (string memory); | ||
|
|
||
| /** | ||
| * @dev Returns the EIP-712 type definition of the action. | ||
| * The type name must be `Action`; only the fields are implementation-specific. | ||
| * @param params Encoded macro-specific parameters. | ||
| * @return typeDef The `Action(...)` type definition. | ||
| */ | ||
| function getActionTypeDefinition(bytes memory params) external view returns (string memory); | ||
|
|
||
| /** | ||
| * @dev Returns the EIP-712 struct hash of the action encoded in `params`. | ||
| * The hash must be constructed from the `Action` type definition and the | ||
| * underlying action data according to the EIP-712 standard. | ||
| * @param params Encoded macro-specific parameters. | ||
| * @return structHash The EIP-712 struct hash of the action. | ||
| */ | ||
| function getActionStructHash(bytes memory params) external view returns (bytes32); | ||
| } |
124 changes: 124 additions & 0 deletions
124
packages/ethereum-contracts/contracts/interfaces/utils/IClearMacroForwarder.sol
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,124 @@ | ||
| // SPDX-License-Identifier: MIT | ||
| pragma solidity >=0.8.11; | ||
|
|
||
| import { IClearMacro } from "./IClearMacro.sol"; | ||
|
|
||
| /** | ||
| * @dev Interface for ClearMacro forwarders. | ||
| * A ClearMacro forwarder executes EIP-712 signed meta-transactions whose | ||
| * payload consists of macro-specific action data and additional security | ||
| * parameters. | ||
| */ | ||
| interface IClearMacroForwarder { | ||
| /** | ||
| * @dev Opaque macro-specific action payload, ABI-encoded for transport. | ||
| * The forwarder does not decode these fields itself; the macro defines the | ||
| * actual EIP-712 `Action` type and computes its struct hash from these bytes. | ||
| */ | ||
| struct EncodedAction { | ||
| bytes params; | ||
| } | ||
|
|
||
| /** | ||
| * @dev Top-level wire format passed to `runMacro`. | ||
| * Callers typically build this using `encodeParams`, but it can also be | ||
| * constructed manually and ABI-encoded as `bytes`. | ||
| */ | ||
| struct Payload { | ||
| EncodedAction action; | ||
| Security security; | ||
| } | ||
|
|
||
| /** | ||
| * @dev Security parameters for a ClearMacro payload. | ||
| * Includes the provider identifier, validity window, and ERC-4337-style nonce. | ||
| */ | ||
| struct Security { | ||
| string domain; | ||
| string provider; | ||
| uint256 validAfter; | ||
| uint256 validBefore; | ||
| uint256 nonce; | ||
| } | ||
|
|
||
| /** | ||
| * @dev Runs the macro with an EIP-712 signed payload. | ||
| * Reverts if the signature is invalid or if the payload fails security checks. | ||
| * @param m Target macro contract. | ||
| * @param params ABI-encoded `Payload`. | ||
| * @param signer Address which signed the payload and on whose behalf the macro runs. | ||
| * @param signature EIP-712 signature over the payload digest. | ||
| * @return success True if the macro execution succeeded. | ||
| */ | ||
| function runMacro( | ||
| IClearMacro m, | ||
| bytes calldata params, | ||
| address signer, | ||
| bytes calldata signature | ||
| ) external payable returns (bool); | ||
|
|
||
| /** | ||
| * @dev Encodes the action and security data into the payload bytes expected by `runMacro`. | ||
| * @param params ABI-encoded macro-specific parameters, opaque to the forwarder. | ||
| * @param security Security parameters (domain, provider, validAfter, validBefore, nonce). | ||
| * @return payload ABI-encoded `Payload` to pass to `runMacro`. | ||
| */ | ||
| function encodeParams( | ||
| bytes calldata params, | ||
| Security calldata security | ||
| ) external pure returns (bytes memory); | ||
|
|
||
| /** | ||
| * @dev Returns the full EIP-712 type definition string for the given macro and payload. | ||
| * @param m Target macro contract. | ||
| * @param params ABI-encoded `Payload`. | ||
| * @return typeDef Full EIP-712 type definition string. | ||
| */ | ||
| function getTypeDefinition(IClearMacro m, bytes calldata params) | ||
| external | ||
| view | ||
| returns (string memory); | ||
|
|
||
| /** | ||
| * @dev Returns the keccak256 hash of the EIP-712 type definition. | ||
| * @param m Target macro contract. | ||
| * @param params ABI-encoded `Payload`. | ||
| * @return typeHash keccak256 hash of the type definition. | ||
| */ | ||
| function getTypeHash(IClearMacro m, bytes calldata params) | ||
| external | ||
| view | ||
| returns (bytes32); | ||
|
|
||
| /** | ||
| * @dev Returns the EIP-712 struct hash of the payload. | ||
| * @param m Target macro contract. | ||
| * @param params ABI-encoded `Payload`. | ||
| * @return structHash EIP-712 struct hash of the payload. | ||
| */ | ||
| function getStructHash(IClearMacro m, bytes calldata params) | ||
| external | ||
| view | ||
| returns (bytes32); | ||
|
|
||
| /** | ||
| * @dev Returns the EIP-712 digest of the payload. | ||
| * This is the value which should be signed off-chain. | ||
| * @param m Target macro contract. | ||
| * @param params ABI-encoded `Payload`. | ||
| * @return digest EIP-712 digest of the payload. | ||
| */ | ||
| function getDigest(IClearMacro m, bytes calldata params) | ||
| external | ||
| view | ||
| returns (bytes32); | ||
|
|
||
| /** | ||
| * @dev Returns the next nonce for the given sender and key. | ||
| * Nonces follow ERC-4337-style semantics: `(uint256(key) << 64) | sequence`. | ||
| * @param sender Address for which the nonce is queried. | ||
| * @param key Nonce key. | ||
| * @return nonce The next nonce for (`sender`, `key`). | ||
| */ | ||
| function getNonce(address sender, uint192 key) external view returns (uint256); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
111 changes: 111 additions & 0 deletions
111
packages/ethereum-contracts/contracts/utils/ClearMacroBase.sol
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,111 @@ | ||
|
|
||
| // SPDX-License-Identifier: MIT | ||
| pragma solidity ^0.8.23; | ||
|
|
||
| import { IClearMacro } from "../interfaces/utils/IClearMacro.sol"; | ||
| import { IClearMacroForwarder } from "../interfaces/utils/IClearMacroForwarder.sol"; | ||
| import { ISuperfluid } from "../interfaces/superfluid/ISuperfluid.sol"; | ||
|
|
||
| /** | ||
| * @dev Abstract base for ClearMacro implementations that support multiple actions. | ||
| * The forwarder handles EIP-712 and signature verification; this base only provides | ||
| * dispatch by action code. | ||
| * Wire format: `abi.encode(uint8 actionCode, bytes32 lang, bytes actionParams)`. | ||
| * Caller must specify lang; it is passed to getActionStructHash for i18n descriptions. | ||
| */ | ||
| abstract contract ClearMacroBase is IClearMacro { | ||
|
|
||
| error UnknownActionCode(uint8 actionCode); | ||
| error UnsupportedLanguage(); | ||
|
|
||
| /** | ||
| * @dev Per-action handler. All function pointers use the same signatures so they can be stored and dispatched. | ||
| */ | ||
| struct Action { | ||
| uint8 actionCode; | ||
| bool exists; | ||
| function(bytes memory) internal view returns (string memory) getPrimaryTypeName; | ||
| function(bytes memory) internal view returns (string memory) getActionTypeDefinition; | ||
| function(bytes memory, bytes32) internal view returns (bytes32) | ||
| getActionStructHash; | ||
| function(ISuperfluid, bytes memory, address) internal view | ||
| returns (ISuperfluid.Operation[] memory) | ||
| buildOperations; | ||
| bool skipPostCheck; | ||
| function(ISuperfluid, bytes memory, address) internal view postCheckHandler; | ||
| } | ||
|
|
||
| mapping(uint8 => Action) internal _actionHandlers; | ||
|
|
||
| constructor() { | ||
| Action[] memory actions = _getActions(); | ||
| for (uint256 i = 0; i < actions.length; i++) { | ||
| _actionHandlers[actions[i].actionCode] = actions[i]; | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * @dev Override to return the list of actions supported by this macro. | ||
| */ | ||
| function _getActions() internal view virtual returns (Action[] memory); | ||
|
|
||
| /** | ||
| * @dev Decode full Payload and then action code + lang + inner params. Used when the forwarder passes full params. | ||
| */ | ||
| function _decodePayloadAndAction(bytes memory params) | ||
| internal | ||
| pure | ||
| returns (uint8 actionCode, bytes32 lang, bytes memory actionParams) | ||
| { | ||
| IClearMacroForwarder.Payload memory payload = abi.decode(params, (IClearMacroForwarder.Payload)); | ||
| (actionCode, lang, actionParams) = abi.decode(payload.action.params, (uint8, bytes32, bytes)); | ||
| } | ||
|
|
||
| /** | ||
| * @dev Decode action params (actionCode, lang, actionParams). Used when the | ||
| * forwarder passes only payload.action.params. | ||
| */ | ||
| function _decodeActionParams(bytes memory params) | ||
| internal | ||
| pure | ||
| returns (uint8 actionCode, bytes32 lang, bytes memory actionParams) | ||
| { | ||
| (actionCode, lang, actionParams) = abi.decode(params, (uint8, bytes32, bytes)); | ||
| } | ||
|
|
||
| function getPrimaryTypeName(bytes memory params) external view override returns (string memory) { | ||
| (uint8 actionCode, , bytes memory actionParams) = _decodePayloadAndAction(params); | ||
| if (!_actionHandlers[actionCode].exists) revert UnknownActionCode(actionCode); | ||
| return _actionHandlers[actionCode].getPrimaryTypeName(actionParams); | ||
| } | ||
|
|
||
| function getActionTypeDefinition(bytes memory params) external view override returns (string memory) { | ||
| (uint8 actionCode, , bytes memory actionParams) = _decodePayloadAndAction(params); | ||
| if (!_actionHandlers[actionCode].exists) revert UnknownActionCode(actionCode); | ||
| return _actionHandlers[actionCode].getActionTypeDefinition(actionParams); | ||
| } | ||
|
|
||
| function getActionStructHash(bytes memory params) external view override returns (bytes32) { | ||
| (uint8 actionCode, bytes32 lang, bytes memory actionParams) = _decodeActionParams(params); | ||
| if (!_actionHandlers[actionCode].exists) revert UnknownActionCode(actionCode); | ||
| return _actionHandlers[actionCode].getActionStructHash(actionParams, lang); | ||
| } | ||
|
|
||
| function buildBatchOperations(ISuperfluid host, bytes memory params, address msgSender) | ||
| external | ||
| view | ||
| override | ||
| returns (ISuperfluid.Operation[] memory) | ||
| { | ||
| (uint8 actionCode, , bytes memory actionParams) = _decodeActionParams(params); | ||
| if (!_actionHandlers[actionCode].exists) revert UnknownActionCode(actionCode); | ||
| return _actionHandlers[actionCode].buildOperations(host, actionParams, msgSender); | ||
| } | ||
|
|
||
| function postCheck(ISuperfluid host, bytes memory params, address msgSender) external view override { | ||
| (uint8 actionCode, , bytes memory actionParams) = _decodeActionParams(params); | ||
| if (_actionHandlers[actionCode].skipPostCheck) return; | ||
| if (!_actionHandlers[actionCode].exists) revert UnknownActionCode(actionCode); | ||
| _actionHandlers[actionCode].postCheckHandler(host, actionParams, msgSender); | ||
| } | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.