From fe0e3c83dcf753e54ab8233a8abd5595e28e16f1 Mon Sep 17 00:00:00 2001 From: zajck Date: Fri, 22 May 2026 10:11:50 +0200 Subject: [PATCH 1/2] Update BPIP-12 and BPIP-13 to match implementation BPIP-12 (issues #1142, #1143): - Align executeMetaTransactionWithTokenTransferAuthorization signature: added _functionName, changed _nonce to uint256, replaced _sigR/_sigS/_sigV with _signature bytes, changed _tokenTransferAuthorization to abi.encode(bytes[]) - Fix ERC-3009 data encoding: remove from/to/value (passed from context), keep only validAfter/validBefore/nonce/v/r/s - Fix consumeForTransfer signature to include the 'to' parameter BPIP-13 (issue #1139): - Remove non-existent _computeBurnTokenId override; describe actual EXCHANGE_ID_2_2_0 versioning logic in ExchangeRedeemBase.burnVoucher - Document _skipVoucher=true flag used in the atomic commit-and-redeem path Co-Authored-By: Claude Sonnet 4.6 --- content/BPIP-12.md | 43 ++++++++++++++++++++++--------------------- content/BPIP-13.md | 8 ++++---- 2 files changed, 26 insertions(+), 25 deletions(-) diff --git a/content/BPIP-12.md b/content/BPIP-12.md index cf206b0..f7771bd 100644 --- a/content/BPIP-12.md +++ b/content/BPIP-12.md @@ -42,13 +42,19 @@ A new entry point is added alongside the existing `executeMetaTransaction`. ```solidity /** - * @notice Executes a metatransaction with a queue of per-transfer token-pull authorizations. + * @notice Same as `executeMetaTransaction`, but additionally accepts a + * token-transfer authorization queue that funds-pulling functions + * can consume in lieu of an ERC-20 allowance. * - * Each entry in _tokenTransferAuthorization is consumed in order by transferFundsIn calls - * made during execution of _functionSignature. An empty-bytes entry ("0x") is a - * fallback marker that causes that transfer slot to fall back to safeTransferFrom - * (requires a prior on-chain approve). Entries beyond the number of transferFundsIn - * calls are ignored; the queue is cleared from transient storage at transaction end. + * The protocol parks `_tokenTransferAuthorization` in transient storage for + * the duration of the transaction. The payload is `abi.encode(bytes[] queue)` + * where each entry is one of: + * - empty bytes ("0x") — fall back to safeTransferFrom for this slot + * (shortcut for (TokenTransferAuthorizationStrategy.None, "")). + * - `abi.encode(BosonTypes.TokenTransferAuthorizationStrategy strategy, bytes data)` + * — strategy-specific payload described in the table below. + * Entries beyond the number of transferFundsIn calls are ignored; the queue is + * cleared from transient storage at transaction end. * * Emits MetaTransactionExecuted event if successful. * @@ -64,32 +70,27 @@ A new entry point is added alongside the existing `executeMetaTransaction`. * - A Permit2 permitTransferFrom call fails * * @param _userAddress - the address of the user on whose behalf the transaction is being executed + * @param _functionName - the name of the function to be executed * @param _functionSignature - the encoded function call to execute * @param _nonce - nonce of the transaction, used to prevent replay attacks - * @param _sigR - r part of the metatransaction signer's signature - * @param _sigS - s part of the metatransaction signer's signature - * @param _sigV - v part of the metatransaction signer's signature - * @param _tokenTransferAuthorization - ABI-encoded queue of (TokenTransferAuthorizationStrategy, bytes) pairs, - * one per transferFundsIn call in execution order. Each non-empty entry is - * abi.encode(TokenTransferAuthorizationStrategy strategy, bytes data) where data - * holds the strategy-specific fields described below. + * @param _signature - meta transaction signature (ECDSA concatenated r,s,v for EOAs; ERC-1271 for contracts) + * @param _tokenTransferAuthorization - `abi.encode(bytes[] queue)` (see above) */ function executeMetaTransactionWithTokenTransferAuthorization( address _userAddress, + string memory _functionName, bytes calldata _functionSignature, - bytes32 _nonce, - bytes32 _sigR, - bytes32 _sigS, - uint8 _sigV, - bytes[] calldata _tokenTransferAuthorization -) external payable; + uint256 _nonce, + bytes calldata _signature, + bytes calldata _tokenTransferAuthorization +) external payable returns (bytes memory); ``` #### Per-strategy entry payload (`data` field) | Strategy | `data` encoding | |----------|-----------------| -| `ERC3009` | `abi.encode(address from, address to, uint256 value, uint256 validAfter, uint256 validBefore, bytes32 nonce, uint8 v, bytes32 r, bytes32 s)` | +| `ERC3009` | `abi.encode(uint256 validAfter, uint256 validBefore, bytes32 nonce, uint8 v, bytes32 r, bytes32 s)` | | `EIP2612` | `abi.encode(uint256 deadline, uint8 v, bytes32 r, bytes32 s)` | | `Permit2` | `abi.encode(uint256 nonce, uint256 deadline, bytes signature)` | | `None` / `"0x"` | empty — falls back to `safeTransferFrom` | @@ -116,7 +117,7 @@ A new internal library manages the queue in transient storage (EIP-1153 / Cancun - **Transient storage** — the queue is stored with `tstore` / `tload`, so it is automatically cleared at the end of each transaction with no explicit cleanup required. - **ERC-7201-style slot masking** — the last byte of each entry's base slot is zeroed, giving every entry a 256-sub-slot range that cannot overlap with any other entry for typical ERC-3009 payloads (which use 7 sub-slots). - **`discardNext()`** — advances the queue head by one without performing any work. Called by `transferFundsIn` for zero-amount transfers and by `createOfferAndCommit` when `useDepositedFunds=true` bypasses the creator pull. -- **`consumeForTransfer(token, from, amount)`** — pops the next entry, decodes the `(strategy, data)` envelope, and dispatches to the appropriate per-strategy helper. Returns `false` for `None` / empty-bytes entries, causing the caller to fall through to `safeTransferFrom`. +- **`consumeForTransfer(token, from, to, amount)`** — pops the next entry, decodes the `(strategy, data)` envelope, and dispatches to the appropriate per-strategy helper. Returns `false` for `None` / empty-bytes entries, causing the caller to fall through to `safeTransferFrom`. ### EIP-2612 diversion guard diff --git a/content/BPIP-13.md b/content/BPIP-13.md index ad8209f..bac1aef 100644 --- a/content/BPIP-13.md +++ b/content/BPIP-13.md @@ -145,7 +145,7 @@ Combining commit and redeem into a single transaction is safe because the commit To share the internal commit and redeem logic across the new orchestration methods without duplicating code or exceeding the 24 KB facet size limit, two new abstract base contracts are introduced: -- **ExchangeRedeemBase** — contains `redeemVoucherInternal`, `burnVoucher`, `transferTwins`, and `updateNFTRanges`. `ExchangeHandlerFacet` inherits it and overrides `_computeBurnTokenId` to retain pre-2.2.0 voucher token-id compatibility. +- **ExchangeRedeemBase** — contains `redeemVoucherInternal`, `burnVoucher`, `transferTwins`, and `updateNFTRanges`. Voucher token-id backward compatibility with pre-2.2.0 exchanges is handled directly inside `burnVoucher` using an immutable versioning threshold (`EXCHANGE_ID_2_2_0`): for exchange ids at or above the threshold the token id is `exchangeId | (offerId << 128)`; older ids are passed through unchanged. - **ExchangeCommitBase** — contains `commitToOfferInternal`, `verifyOffer`, `authorizeCommit`, `holdsThreshold`/`holdsSpecificToken`, `validateConditionRange`, `handleDRFeeCollection`, and `addSellerParametersToBuyerOffer`. `ExchangeCommitFacet` now inherits it instead of implementing these directly. `OrchestrationHandlerFacet2` inherits both bases and the three new methods delegate to the shared internals. @@ -158,12 +158,12 @@ This proposal does not break backward compatibility. All existing methods contin ## Implementation * Introduce `ExchangeRedeemBase` with the internal redeem logic extracted from `ExchangeHandlerFacet`. * Introduce `ExchangeCommitBase` with the internal commit logic extracted from `ExchangeCommitFacet`. -* `ExchangeHandlerFacet` inherits `ExchangeRedeemBase` and overrides `_computeBurnTokenId` for backward-compatible voucher token-id handling. +* `ExchangeHandlerFacet` inherits `ExchangeRedeemBase`. Backward-compatible voucher token-id handling is implemented directly in `ExchangeRedeemBase.burnVoucher` via `EXCHANGE_ID_2_2_0` — no override is needed. * `ExchangeCommitFacet` inherits `ExchangeCommitBase`. * `OrchestrationHandlerFacet2` inherits both `ExchangeCommitBase` and `ExchangeRedeemBase` and adds the three new public methods. * Each new method: - 1. Validates and processes the commit step using shared internals (including offer validation, condition check, fund encumbrance). - 2. Immediately calls `redeemVoucherInternal` with the same exchange, transferring any twins to `_msgSender()`. + 1. Validates and processes the commit step using shared internals (including offer validation, condition check, fund encumbrance). The commit is called with `_skipVoucher = true`, so no NFT is minted and `voucherCount` is not incremented. + 2. Immediately calls `redeemVoucherInternal(_exchangeId, _skipVoucher: true)`, which skips the burn call and buyer-ownership check (no voucher NFT was ever issued), and transfers any twins to `_msgSender()`. * Add `OfferCreatorMustBeSeller` to `BosonErrors.sol`. From 6d309ebe22f82b4bc50d024c81e4c247302ad4a0 Mon Sep 17 00:00:00 2001 From: zajck Date: Fri, 22 May 2026 10:40:39 +0200 Subject: [PATCH 2/2] BPIP-12: update tokenTransferAuthorization to bytes[] calldata The implementation reverted from a single abi.encode(bytes[]) packed bytes to a direct bytes[] calldata parameter. Update the function signature and description accordingly. Co-Authored-By: Claude Sonnet 4.6 --- content/BPIP-12.md | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/content/BPIP-12.md b/content/BPIP-12.md index f7771bd..86c6f9a 100644 --- a/content/BPIP-12.md +++ b/content/BPIP-12.md @@ -46,15 +46,14 @@ A new entry point is added alongside the existing `executeMetaTransaction`. * token-transfer authorization queue that funds-pulling functions * can consume in lieu of an ERC-20 allowance. * - * The protocol parks `_tokenTransferAuthorization` in transient storage for - * the duration of the transaction. The payload is `abi.encode(bytes[] queue)` - * where each entry is one of: + * Each entry in `_tokenTransferAuthorization` is consumed in order by + * transferFundsIn calls made during execution of `_functionSignature`. Entries + * beyond the number of transferFundsIn calls are ignored; the queue is cleared + * from transient storage at transaction end. Each element is one of: * - empty bytes ("0x") — fall back to safeTransferFrom for this slot * (shortcut for (TokenTransferAuthorizationStrategy.None, "")). * - `abi.encode(BosonTypes.TokenTransferAuthorizationStrategy strategy, bytes data)` * — strategy-specific payload described in the table below. - * Entries beyond the number of transferFundsIn calls are ignored; the queue is - * cleared from transient storage at transaction end. * * Emits MetaTransactionExecuted event if successful. * @@ -74,7 +73,7 @@ A new entry point is added alongside the existing `executeMetaTransaction`. * @param _functionSignature - the encoded function call to execute * @param _nonce - nonce of the transaction, used to prevent replay attacks * @param _signature - meta transaction signature (ECDSA concatenated r,s,v for EOAs; ERC-1271 for contracts) - * @param _tokenTransferAuthorization - `abi.encode(bytes[] queue)` (see above) + * @param _tokenTransferAuthorization - queue of per-transfer authorization entries (see above) */ function executeMetaTransactionWithTokenTransferAuthorization( address _userAddress, @@ -82,7 +81,7 @@ function executeMetaTransactionWithTokenTransferAuthorization( bytes calldata _functionSignature, uint256 _nonce, bytes calldata _signature, - bytes calldata _tokenTransferAuthorization + bytes[] calldata _tokenTransferAuthorization ) external payable returns (bytes memory); ```