Skip to content

Conversation

@mgretzke
Copy link
Collaborator

Pull Request

Description

This PR adds Permit2-based allocation support to both OnChainAllocator and HybridAllocator, enabling users to deposit, register, and allocate tokens in a single transaction using Permit2 signatures. It also introduces a new nonce command structure to differentiate between on-chain, off-chain, and Permit2 allocations.

New Features

  • permit2Allocation function added to both allocators
    • Deposits tokens via Permit2 signature transfer
    • Registers the claim with The Compact
    • Allocates the claim in a single transaction
    • Verifies the claim hash matches the allocation parameters

Nonce Structure Refactoring

  • Introduced nonce commands to differentiate allocation types:
    • 0x01 - On-chain nonce (existing allocate/allocateAndRegister flows)
    • 0x02 - Off-chain nonce (signature-based authorization in HybridAllocator)
    • 0x03 - Permit2 nonce (new permit2Allocation flow)
  • Changed nonce storage from uint96 to uint88 to accommodate the command byte
  • Nonce format: command (1 byte) | sponsor address (20 bytes) | nonce (11 bytes)

@mgretzke mgretzke requested a review from a team as a code owner December 11, 2025 12:30
Copy link
Contributor

@0age 0age left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

all looking good, left a few small comments

looks like we do still need to work out the precise methodology for composing onchain + offchain allocations in one shot, but this should be a pretty good starting point for that!

AL.verifyNonce(nonce, AL.OFF_CHAIN_NONCE, sponsor);

// Check the allocator data for a valid signature by an authorized signer
bytes32 digest = _deriveDigest(claimHash, _COMPACT_DOMAIN_SEPARATOR);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note that if the domain separator on The Compact changes (e.g. from a chain fork) this will not work anymore

/// @dev The actual nonce will be a combination of the next free nonce and the user address.
mapping(address user => uint96 nonce) public nonces;
/// @dev The actual nonce will be a combination of the on chain nonce command, the users address and the free nonce.
mapping(address user => uint88 nonce) public nonces;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'd avoid "user" as terminology; better to refer to the actor in question (here it's the sponsor i believe)

revert InvalidAmount(commitments[i].amount);
}

_storeAllocation(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

since allocations are scoped to specific claim hashes anyway, I wonder if it'd be possible to store a single hash of all the respective commitments rather than one at a time

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is more of a musing than a suggestion at this stage, i think we could explore that optimization down the line but just food for thought for now

nonce := or(shl(96, sponsor), add(nonce96, 1))
sstore(nonceSlot, add(nonce96, 1))
let nonce88 := sload(nonceSlot)
nonce := or(onChainNonceCommand, or(shl(88, sponsor), add(nonce88, 1)))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minor thing but we could do the add right when we do the sload and save an operation over needing to do it twice

if (newSigner_ == address(0) || msg.sender != newSigner_) {
revert InvalidSigner();
/// @inheritdoc IHybridAllocator
function proposeOwnerReplacement(address newOwner_) external onlyOwner {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i actually think we should consider allowing address(0) here so you can "cancel" an ownership transfer; erroneously putting in address(0) is not that big of a deal since nothing will happen

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants