From 2e87a43f412ab5b669e8010c05007efc43dd4871 Mon Sep 17 00:00:00 2001 From: Amie Corso Date: Fri, 29 May 2026 11:24:24 -0700 Subject: [PATCH 1/5] feat(factory): add variantParams bytes field to B20Created event for stablecoin currency (BOP-158) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a `bytes variantParams` field to the `B20Created` event carrying variant-specific immutable identity data that isn't already covered by the fixed event fields. The bytes payload leads with a version byte and is ABI-encoded per variant — same shape as the existing `createParams` pattern — so stream-based indexers (e.g. Coindexer) that can't make mid-handler RPC calls can decode by `(variant, leading version byte)` and recover immutable fields without reading storage. Payload per variant: - DEFAULT: empty (no extra immutable fields beyond what's already in the event) - STABLECOIN: abi.encode(B20StablecoinEventParams { version, currency }) - SECURITY: empty (its `isin` and `minimumRedeemable` are mutable and surfaced via their own SecurityIdentifierUpdated / MinimumRedeemableUpdated events) Forward-compatible: any variant can promote from empty to non-empty in a future version without re-issuing a new event type. Adds: - `struct B20StablecoinEventParams { uint8 version; string currency; }` in IB20Factory.sol - `uint8 B20_STABLECOIN_EVENT_PARAMS_VERSION = 1` constant in B20FactoryLib.sol - New emission logic in MockB20Factory.createB20 that builds variantParams per variant - Updates to the two existing B20Created event-match tests (DEFAULT, SECURITY) for the new 6-arg signature - Two new STABLECOIN-focused tests: expectEmit-level pin of the encoded payload, and a recorded-logs decode test that round-trips the currency through B20StablecoinEventParams and verifies the version constant Supersedes Option B (separate StablecoinCreated/SecurityCreated companion events) — see closed PR #85. The Rust precompile counterpart is tracked at BOP-216. --- src/interfaces/IB20Factory.sol | 52 +++++++++++++- src/lib/B20FactoryLib.sol | 8 +++ test/lib/mocks/MockB20Factory.sol | 19 ++++- test/unit/B20Factory/createToken.t.sol | 97 ++++++++++++++++++++++++-- 4 files changed, 170 insertions(+), 6 deletions(-) diff --git a/src/interfaces/IB20Factory.sol b/src/interfaces/IB20Factory.sol index ff031318..da0d53a0 100644 --- a/src/interfaces/IB20Factory.sol +++ b/src/interfaces/IB20Factory.sol @@ -162,6 +162,37 @@ interface IB20Factory { uint256 minimumRedeemable; } + /// @notice Event payload carried in the `variantParams` field of + /// `B20Created` for STABLECOIN-variant tokens. ABI-encoded + /// and emitted with a leading `version` byte so stream-based + /// indexers can decode by `(variant, version)` without an + /// RPC call to read storage. + /// @param version Event-encoding version. Currently `1`. + /// Independent of `B20StablecoinCreateParams.version`; + /// the event payload schema can evolve separately + /// from the create-call payload schema. + /// @param currency The immutable stablecoin currency identifier, same + /// value that was passed in + /// `B20StablecoinCreateParams.currency`. Surfaced here + /// because `currency` has no setter and no other + /// event ever emits it; indexers that cannot make + /// mid-handler view calls (e.g. Coindexer) would + /// otherwise have no way to recover it from the + /// event stream. + /// @dev DEFAULT and SECURITY variants currently emit an empty + /// `variantParams` byte string: DEFAULT has no extra + /// immutable identity fields beyond what's already in + /// `B20Created`, and SECURITY's `isin` and + /// `minimumRedeemable` are mutable and surfaced via their + /// own `SecurityIdentifierUpdated` / + /// `MinimumRedeemableUpdated` events. Either variant can + /// promote from empty to a non-empty payload in a future + /// version without re-issuing a new event type. + struct B20StablecoinEventParams { + uint8 version; + string currency; + } + /*////////////////////////////////////////////////////////////// ERRORS //////////////////////////////////////////////////////////////*/ @@ -211,7 +242,26 @@ interface IB20Factory { /// `B20Created` is the token-identity signal only. The /// "demonstrate no owner" path (`initialAdmin == address(0)`) /// skips the grant and emits no `RoleGranted` at bootstrap. - event B20Created(address indexed token, B20Variant indexed variant, string name, string symbol, uint8 decimals); + /// @param token Address of the newly-created token. + /// @param variant Which `B20Variant` was created. + /// @param name ERC-20 token name. + /// @param symbol ERC-20 token symbol. + /// @param decimals ERC-20 decimals (fixed per variant). + /// @param variantParams ABI-encoded variant-specific immutable identity + /// fields, leading with a version byte. Empty + /// (`""`) for DEFAULT and SECURITY (no extra + /// immutable fields not already covered); for + /// STABLECOIN, carries `abi.encode(B20StablecoinEventParams)` + /// with `currency`. Indexers decode by + /// `(variant, leading version byte)`. + event B20Created( + address indexed token, + B20Variant indexed variant, + string name, + string symbol, + uint8 decimals, + bytes variantParams + ); /*////////////////////////////////////////////////////////////// CREATE diff --git a/src/lib/B20FactoryLib.sol b/src/lib/B20FactoryLib.sol index c1b06437..0bb1575a 100644 --- a/src/lib/B20FactoryLib.sol +++ b/src/lib/B20FactoryLib.sol @@ -60,6 +60,14 @@ library B20FactoryLib { /// Independent of the other variants' versions. uint8 internal constant B20_SECURITY_CREATE_PARAMS_VERSION = 1; + /// @notice Current encoding version for `B20StablecoinEventParams`, + /// the payload carried in the `variantParams` field of the + /// `B20Created` event for STABLECOIN-variant tokens. + /// Independent of `B20_STABLECOIN_CREATE_PARAMS_VERSION` — + /// the event payload schema can evolve separately from the + /// create-call payload schema. + uint8 internal constant B20_STABLECOIN_EVENT_PARAMS_VERSION = 1; + /// @notice Two parallel arrays passed to a `build*` helper had /// different lengths. /// diff --git a/test/lib/mocks/MockB20Factory.sol b/test/lib/mocks/MockB20Factory.sol index d11becda..ca0a1e99 100644 --- a/test/lib/mocks/MockB20Factory.sol +++ b/test/lib/mocks/MockB20Factory.sol @@ -177,7 +177,24 @@ contract MockB20Factory is IB20Factory { // -- 6. Emit B20Created. Identity-only signal; admin role // assignment is announced via the standard RoleGranted // event from step 7. - emit B20Created(token, variant, name_, symbol_, decimals); + // + // The `variantParams` field carries variant-specific + // immutable identity that isn't already covered by the + // fixed event fields. STABLECOIN emits an ABI-encoded + // `B20StablecoinEventParams` so stream-based indexers + // (e.g. Coindexer) can recover the immutable `currency` + // without an RPC call. DEFAULT and SECURITY emit empty + // bytes (SECURITY's `isin` / `minimumRedeemable` are + // mutable and surfaced via their own update events). + bytes memory variantParams; + if (variant == B20Variant.STABLECOIN) { + variantParams = abi.encode( + IB20Factory.B20StablecoinEventParams({ + version: B20FactoryLib.B20_STABLECOIN_EVENT_PARAMS_VERSION, currency: currency_ + }) + ); + } + emit B20Created(token, variant, name_, symbol_, decimals, variantParams); // -- 7. Grant the initial admin role via the canonical path. // msg.sender at the token is address(this) == factory, diff --git a/test/unit/B20Factory/createToken.t.sol b/test/unit/B20Factory/createToken.t.sol index c8ef5fa1..0a91e415 100644 --- a/test/unit/B20Factory/createToken.t.sol +++ b/test/unit/B20Factory/createToken.t.sol @@ -7,6 +7,7 @@ import {IB20} from "src/interfaces/IB20.sol"; import {IB20Stablecoin} from "src/interfaces/IB20Stablecoin.sol"; import {IB20Security} from "src/interfaces/IB20Security.sol"; import {IB20Factory} from "src/interfaces/IB20Factory.sol"; +import {B20FactoryLib} from "src/lib/B20FactoryLib.sol"; import {MockB20, B20Constants} from "test/lib/mocks/MockB20.sol"; import {MockB20Security} from "test/lib/mocks/MockB20Security.sol"; @@ -118,6 +119,18 @@ contract B20FactoryCreateB20Test is B20FactoryTest { return true; } + /// @dev Deterministic seed → 3-letter uppercase ASCII fiat code, guaranteed + /// to pass `_isValidFiatCode`. Use in success-path fuzz tests instead of + /// `vm.assume(_isValidFiatCode(...))` over a raw `string` input, which + /// would hit foundry's rejection-rate ceiling. + function _make3LetterUppercase(uint256 seed) private pure returns (string memory) { + bytes memory b = new bytes(3); + b[0] = bytes1(uint8(0x41 + (seed % 26))); + b[1] = bytes1(uint8(0x41 + ((seed >> 8) % 26))); + b[2] = bytes1(uint8(0x41 + ((seed >> 16) % 26))); + return string(b); + } + /// @notice Verifies createToken reverts for any unsupported version byte on the SECURITY variant /// @dev Each variant arm has its own version check; this exercises the security arm's check. function test_createB20_revert_unsupportedVersion_security(address caller, uint8 badVersion, bytes32 salt) public { @@ -461,30 +474,106 @@ contract B20FactoryCreateB20Test is B20FactoryTest { /// @notice Verifies createToken emits B20Created with the correct identity fields /// @dev Event integrity: token, variant, name, symbol, decimals must match derived variant defaults. /// Admin role assignment is announced via RoleGranted, not as a field on this event; - /// see test_createB20_success_emitsRoleGrantedForInitialAdmin for that. + /// see test_createB20_success_emitsRoleGrantedForInitialAdmin for that. DEFAULT variant + /// emits empty `variantParams` (no extra immutable identity fields beyond what's already + /// in the event). function test_createB20_success_emitsB20Created(address caller, bytes32 salt) public { _assumeValidCaller(caller); IB20Factory.B20CreateParams memory p = _b20Params("MyToken", "MYT", admin); address predicted = factory.getB20Address(IB20Factory.B20Variant.DEFAULT, caller, salt); vm.expectEmit(true, true, false, true, address(factory)); - emit IB20Factory.B20Created(predicted, IB20Factory.B20Variant.DEFAULT, "MyToken", "MYT", 18); + emit IB20Factory.B20Created(predicted, IB20Factory.B20Variant.DEFAULT, "MyToken", "MYT", 18, bytes("")); _createDefault(caller, salt, p, new bytes[](0)); } /// @notice Verifies createToken emits B20Created with decimals=6 for the security variant /// @dev Variant-specific dedicated event test: the security arm pins decimals=6 the same - /// way the default emitter test pins decimals=18. + /// way the default emitter test pins decimals=18. SECURITY variant emits empty + /// `variantParams` (its `isin` and `minimumRedeemable` are mutable and surfaced via + /// their own update events). function test_createB20_success_emitsB20Created_security(address caller, bytes32 salt) public { _assumeValidCaller(caller); IB20Factory.B20SecurityCreateParams memory p = _securityParams("Security Test", "SEC", admin, DEFAULT_ISIN, 0); address predicted = factory.getB20Address(IB20Factory.B20Variant.SECURITY, caller, salt); vm.expectEmit(true, true, false, true, address(factory)); - emit IB20Factory.B20Created(predicted, IB20Factory.B20Variant.SECURITY, "Security Test", "SEC", 6); + emit IB20Factory.B20Created(predicted, IB20Factory.B20Variant.SECURITY, "Security Test", "SEC", 6, bytes("")); _createSecurity(caller, salt, p, new bytes[](0)); } + /// @notice Verifies createToken emits B20Created with decimals=6 and a non-empty + /// `variantParams` payload for the stablecoin variant + /// @dev STABLECOIN-only behavior: the `variantParams` field carries + /// `abi.encode(B20StablecoinEventParams { version, currency })` so stream-based + /// indexers (e.g. Coindexer) can recover the immutable `currency` without an RPC + /// call to `currency()`. + function test_createB20_success_emitsB20Created_stablecoin(address caller, bytes32 salt) public { + _assumeValidCaller(caller); + string memory currency = "USD"; + IB20Factory.B20StablecoinCreateParams memory p = _stablecoinParams("USD Stable", "USDS", admin, currency); + address predicted = factory.getB20Address(IB20Factory.B20Variant.STABLECOIN, caller, salt); + + bytes memory expectedVariantParams = abi.encode( + IB20Factory.B20StablecoinEventParams({ + version: B20FactoryLib.B20_STABLECOIN_EVENT_PARAMS_VERSION, currency: currency + }) + ); + + vm.expectEmit(true, true, false, true, address(factory)); + emit IB20Factory.B20Created( + predicted, IB20Factory.B20Variant.STABLECOIN, "USD Stable", "USDS", 6, expectedVariantParams + ); + _createStablecoin(caller, salt, p, new bytes[](0)); + } + + /// @notice Verifies the `variantParams` payload of `B20Created` for STABLECOIN decodes + /// back to a `B20StablecoinEventParams` whose `currency` round-trips the value + /// passed at creation and whose `version` matches the canonical event-encoding + /// version constant. + /// @dev Decode-level pin (complementary to `_emitsB20Created_stablecoin`'s + /// expectEmit-level pin). Catches a future regression that emits a payload with + /// the right SHAPE but wrong VERSION or CONTENT — for example, accidentally + /// re-using `B20_STABLECOIN_CREATE_PARAMS_VERSION` instead of + /// `B20_STABLECOIN_EVENT_PARAMS_VERSION`, or echoing the `name` field instead of + /// the `currency` field. Recorded-logs replay so the assertion runs against the + /// exact bytes the factory actually emitted, not a re-computed expectation. + function test_createB20_success_b20CreatedVariantParams_stablecoin_decodes( + address caller, + bytes32 salt, + uint256 currencySeed + ) public { + _assumeValidCaller(caller); + // Generate a guaranteed-valid 3-letter uppercase fiat code from the fuzz seed. + // Avoids vm.assume rejection-rate exhaustion that filtering random strings would hit. + string memory currency = _make3LetterUppercase(currencySeed); + IB20Factory.B20StablecoinCreateParams memory p = _stablecoinParams("Stable", "STB", admin, currency); + + vm.recordLogs(); + _createStablecoin(caller, salt, p, new bytes[](0)); + Vm.Log[] memory logs = vm.getRecordedLogs(); + + bytes32 selector = IB20Factory.B20Created.selector; + bytes memory variantParams; + for (uint256 i = 0; i < logs.length; ++i) { + if (logs[i].topics.length > 0 && logs[i].topics[0] == selector) { + // B20Created data payload: (name, symbol, decimals, variantParams). + (,,, variantParams) = abi.decode(logs[i].data, (string, string, uint8, bytes)); + break; + } + } + assertGt(variantParams.length, 0, "STABLECOIN variantParams must be non-empty"); + + IB20Factory.B20StablecoinEventParams memory decoded = + abi.decode(variantParams, (IB20Factory.B20StablecoinEventParams)); + assertEq( + decoded.version, + B20FactoryLib.B20_STABLECOIN_EVENT_PARAMS_VERSION, + "decoded version must equal B20_STABLECOIN_EVENT_PARAMS_VERSION" + ); + assertEq(decoded.currency, currency, "decoded currency must round-trip the value passed at creation"); + } + /// @notice Verifies createToken executes each entry in initCalls during the bootstrap window /// @dev The bootstrap-window auth bypass is bound to the call site (msg.sender == factory && !initialized), /// not to RBAC. The factory is never granted any role on the token. We verify the bypass by passing From 541d34af309cac55582676ced756f4dfd1fdf2d0 Mon Sep 17 00:00:00 2001 From: Amie Corso Date: Fri, 29 May 2026 12:11:00 -0700 Subject: [PATCH 2/5] refactor(factory): extract encodeStablecoinEventParams into B20FactoryLib Pull the abi.encode of B20StablecoinEventParams out of MockB20Factory and into a B20FactoryLib.encodeStablecoinEventParams helper, matching the existing encodeXCreateParams pattern (encodeDefaultCreateParams / encodeStablecoinCreateParams / encodeSecurityCreateParams). The struct itself stays in IB20Factory.sol alongside the create-params structs. Adds the matching encoder unit test file (test/unit/B20FactoryLib/encodeStablecoinEventParams.t.sol) with the two-test shape used by every existing encoder: - round-trips-through-decode (pins version constant + currency) - matches-hand-encoded-struct (pins byte-for-byte encoding shape) --- src/lib/B20FactoryLib.sol | 18 ++++++++ test/lib/mocks/MockB20Factory.sol | 6 +-- .../encodeStablecoinEventParams.t.sol | 42 +++++++++++++++++++ 3 files changed, 61 insertions(+), 5 deletions(-) create mode 100644 test/unit/B20FactoryLib/encodeStablecoinEventParams.t.sol diff --git a/src/lib/B20FactoryLib.sol b/src/lib/B20FactoryLib.sol index 0bb1575a..75b0f611 100644 --- a/src/lib/B20FactoryLib.sol +++ b/src/lib/B20FactoryLib.sol @@ -225,6 +225,24 @@ library B20FactoryLib { ); } + /// @notice Encodes a `B20StablecoinEventParams` as the `variantParams` + /// blob the factory emits in the `B20Created` event when + /// `variant == B20Variant.STABLECOIN`. The leading byte is + /// `B20_STABLECOIN_EVENT_PARAMS_VERSION`. Indexers decode + /// this blob by `(variant, leading version byte)` to recover + /// the immutable `currency` without an RPC call. + /// + /// @param currency ISO 4217 fiat code this stablecoin tracks; same + /// value passed to `encodeStablecoinCreateParams` + /// at creation time. + /// + /// @return The ABI-encoded `B20StablecoinEventParams` blob. + function encodeStablecoinEventParams(string memory currency) internal pure returns (bytes memory) { + return abi.encode( + IB20Factory.B20StablecoinEventParams({version: B20_STABLECOIN_EVENT_PARAMS_VERSION, currency: currency}) + ); + } + /*////////////////////////////////////////////////////////////// INIT-CALL SETTER ENCODERS //////////////////////////////////////////////////////////////*/ diff --git a/test/lib/mocks/MockB20Factory.sol b/test/lib/mocks/MockB20Factory.sol index ca0a1e99..04ae2a84 100644 --- a/test/lib/mocks/MockB20Factory.sol +++ b/test/lib/mocks/MockB20Factory.sol @@ -188,11 +188,7 @@ contract MockB20Factory is IB20Factory { // mutable and surfaced via their own update events). bytes memory variantParams; if (variant == B20Variant.STABLECOIN) { - variantParams = abi.encode( - IB20Factory.B20StablecoinEventParams({ - version: B20FactoryLib.B20_STABLECOIN_EVENT_PARAMS_VERSION, currency: currency_ - }) - ); + variantParams = B20FactoryLib.encodeStablecoinEventParams(currency_); } emit B20Created(token, variant, name_, symbol_, decimals, variantParams); diff --git a/test/unit/B20FactoryLib/encodeStablecoinEventParams.t.sol b/test/unit/B20FactoryLib/encodeStablecoinEventParams.t.sol new file mode 100644 index 00000000..ee226cb9 --- /dev/null +++ b/test/unit/B20FactoryLib/encodeStablecoinEventParams.t.sol @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {B20FactoryLib} from "src/lib/B20FactoryLib.sol"; +import {IB20Factory} from "src/interfaces/IB20Factory.sol"; + +import {B20FactoryLibTest} from "test/lib/B20FactoryLibTest.sol"; + +contract B20FactoryLibEncodeStablecoinEventParamsTest is B20FactoryLibTest { + /// @notice Verifies the output decodes back to a `B20StablecoinEventParams` + /// with the caller's currency and the current event-encoding version byte. + /// @dev Round-trips through `abi.decode` to pin the wire format the + /// `B20Created` `variantParams` field carries for STABLECOIN. The + /// event-params version is independent of the create-params version, + /// so this test pins against `B20_STABLECOIN_EVENT_PARAMS_VERSION` + /// specifically (not `B20_STABLECOIN_CREATE_PARAMS_VERSION`). + function test_encodeStablecoinEventParams_success_roundTripsThroughDecode(string memory currency) public pure { + bytes memory blob = B20FactoryLib.encodeStablecoinEventParams(currency); + IB20Factory.B20StablecoinEventParams memory decoded = abi.decode(blob, (IB20Factory.B20StablecoinEventParams)); + + assertEq( + decoded.version, + B20FactoryLib.B20_STABLECOIN_EVENT_PARAMS_VERSION, + "version byte must match library constant" + ); + assertEq(decoded.currency, currency, "currency must round-trip"); + } + + /// @notice Verifies the encoded blob is byte-identical to a hand-encoded + /// `B20StablecoinEventParams` struct. + /// @dev Pins the encoding shape so future field reordering on the + /// struct is caught against an explicit reference. + function test_encodeStablecoinEventParams_success_matchesHandEncodedStruct(string memory currency) public pure { + bytes memory expected = abi.encode( + IB20Factory.B20StablecoinEventParams({ + version: B20FactoryLib.B20_STABLECOIN_EVENT_PARAMS_VERSION, currency: currency + }) + ); + bytes memory actual = B20FactoryLib.encodeStablecoinEventParams(currency); + assertEq(actual, expected, "encoded blob must match hand-encoded struct byte-for-byte"); + } +} From 64ec9b9dc61ba148bc4e19f706c82e653f09fd83 Mon Sep 17 00:00:00 2001 From: Amie Corso Date: Fri, 29 May 2026 13:27:26 -0700 Subject: [PATCH 3/5] =?UTF-8?q?chore(factory):=20address=20PR=20review=20(?= =?UTF-8?q?drop=20internal=20tool=20name,=20rename=20variantParams=20?= =?UTF-8?q?=E2=86=92=20eventParams)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two cleanups from Stevie's review on PR #102: 1. Strip three Coindexer references from OSS-facing NatSpec/comments (src/interfaces/IB20Factory.sol, test/lib/mocks/MockB20Factory.sol, test/unit/B20Factory/createToken.t.sol). Per the leak-prevention rule in AGENTS.md, internal Coinbase tool names should not appear in anything under projects//. The remaining text — "stream-based indexers" / "indexers that cannot make mid-handler view calls" — conveys the same meaning without naming a specific tool. 2. Rename the B20Created event field variantParams → eventParams to align with the B20*EventParams struct family stem (B20StablecoinEventParams, encodeStablecoinEventParams, B20_STABLECOIN_EVENT_PARAMS_VERSION). The struct family already uses the *EventParams stem to signal event-time payload (vs. *CreateParams for create-time payload); the field now shares that stem. Wire-level B20Created selector changes because the field rename changes the canonical ABI signature (bytes variantParams → bytes eventParams). Will need to ship in lockstep with the Rust counterpart (BOP-216). --- src/interfaces/IB20Factory.sol | 15 +++++-------- src/lib/B20FactoryLib.sol | 4 ++-- test/lib/mocks/MockB20Factory.sol | 10 ++++----- test/unit/B20Factory/createToken.t.sol | 22 +++++++++---------- .../encodeStablecoinEventParams.t.sol | 2 +- 5 files changed, 24 insertions(+), 29 deletions(-) diff --git a/src/interfaces/IB20Factory.sol b/src/interfaces/IB20Factory.sol index da0d53a0..3d3bf240 100644 --- a/src/interfaces/IB20Factory.sol +++ b/src/interfaces/IB20Factory.sol @@ -162,7 +162,7 @@ interface IB20Factory { uint256 minimumRedeemable; } - /// @notice Event payload carried in the `variantParams` field of + /// @notice Event payload carried in the `eventParams` field of /// `B20Created` for STABLECOIN-variant tokens. ABI-encoded /// and emitted with a leading `version` byte so stream-based /// indexers can decode by `(variant, version)` without an @@ -176,11 +176,11 @@ interface IB20Factory { /// `B20StablecoinCreateParams.currency`. Surfaced here /// because `currency` has no setter and no other /// event ever emits it; indexers that cannot make - /// mid-handler view calls (e.g. Coindexer) would + /// mid-handler view calls would /// otherwise have no way to recover it from the /// event stream. /// @dev DEFAULT and SECURITY variants currently emit an empty - /// `variantParams` byte string: DEFAULT has no extra + /// `eventParams` byte string: DEFAULT has no extra /// immutable identity fields beyond what's already in /// `B20Created`, and SECURITY's `isin` and /// `minimumRedeemable` are mutable and surfaced via their @@ -247,7 +247,7 @@ interface IB20Factory { /// @param name ERC-20 token name. /// @param symbol ERC-20 token symbol. /// @param decimals ERC-20 decimals (fixed per variant). - /// @param variantParams ABI-encoded variant-specific immutable identity + /// @param eventParams ABI-encoded variant-specific immutable identity /// fields, leading with a version byte. Empty /// (`""`) for DEFAULT and SECURITY (no extra /// immutable fields not already covered); for @@ -255,12 +255,7 @@ interface IB20Factory { /// with `currency`. Indexers decode by /// `(variant, leading version byte)`. event B20Created( - address indexed token, - B20Variant indexed variant, - string name, - string symbol, - uint8 decimals, - bytes variantParams + address indexed token, B20Variant indexed variant, string name, string symbol, uint8 decimals, bytes eventParams ); /*////////////////////////////////////////////////////////////// diff --git a/src/lib/B20FactoryLib.sol b/src/lib/B20FactoryLib.sol index 75b0f611..d8ba17d6 100644 --- a/src/lib/B20FactoryLib.sol +++ b/src/lib/B20FactoryLib.sol @@ -61,7 +61,7 @@ library B20FactoryLib { uint8 internal constant B20_SECURITY_CREATE_PARAMS_VERSION = 1; /// @notice Current encoding version for `B20StablecoinEventParams`, - /// the payload carried in the `variantParams` field of the + /// the payload carried in the `eventParams` field of the /// `B20Created` event for STABLECOIN-variant tokens. /// Independent of `B20_STABLECOIN_CREATE_PARAMS_VERSION` — /// the event payload schema can evolve separately from the @@ -225,7 +225,7 @@ library B20FactoryLib { ); } - /// @notice Encodes a `B20StablecoinEventParams` as the `variantParams` + /// @notice Encodes a `B20StablecoinEventParams` as the `eventParams` /// blob the factory emits in the `B20Created` event when /// `variant == B20Variant.STABLECOIN`. The leading byte is /// `B20_STABLECOIN_EVENT_PARAMS_VERSION`. Indexers decode diff --git a/test/lib/mocks/MockB20Factory.sol b/test/lib/mocks/MockB20Factory.sol index 04ae2a84..561d0c05 100644 --- a/test/lib/mocks/MockB20Factory.sol +++ b/test/lib/mocks/MockB20Factory.sol @@ -178,19 +178,19 @@ contract MockB20Factory is IB20Factory { // assignment is announced via the standard RoleGranted // event from step 7. // - // The `variantParams` field carries variant-specific + // The `eventParams` field carries variant-specific // immutable identity that isn't already covered by the // fixed event fields. STABLECOIN emits an ABI-encoded // `B20StablecoinEventParams` so stream-based indexers - // (e.g. Coindexer) can recover the immutable `currency` + // can recover the immutable `currency` // without an RPC call. DEFAULT and SECURITY emit empty // bytes (SECURITY's `isin` / `minimumRedeemable` are // mutable and surfaced via their own update events). - bytes memory variantParams; + bytes memory eventParams; if (variant == B20Variant.STABLECOIN) { - variantParams = B20FactoryLib.encodeStablecoinEventParams(currency_); + eventParams = B20FactoryLib.encodeStablecoinEventParams(currency_); } - emit B20Created(token, variant, name_, symbol_, decimals, variantParams); + emit B20Created(token, variant, name_, symbol_, decimals, eventParams); // -- 7. Grant the initial admin role via the canonical path. // msg.sender at the token is address(this) == factory, diff --git a/test/unit/B20Factory/createToken.t.sol b/test/unit/B20Factory/createToken.t.sol index 0a91e415..f4f8f471 100644 --- a/test/unit/B20Factory/createToken.t.sol +++ b/test/unit/B20Factory/createToken.t.sol @@ -475,7 +475,7 @@ contract B20FactoryCreateB20Test is B20FactoryTest { /// @dev Event integrity: token, variant, name, symbol, decimals must match derived variant defaults. /// Admin role assignment is announced via RoleGranted, not as a field on this event; /// see test_createB20_success_emitsRoleGrantedForInitialAdmin for that. DEFAULT variant - /// emits empty `variantParams` (no extra immutable identity fields beyond what's already + /// emits empty `eventParams` (no extra immutable identity fields beyond what's already /// in the event). function test_createB20_success_emitsB20Created(address caller, bytes32 salt) public { _assumeValidCaller(caller); @@ -490,7 +490,7 @@ contract B20FactoryCreateB20Test is B20FactoryTest { /// @notice Verifies createToken emits B20Created with decimals=6 for the security variant /// @dev Variant-specific dedicated event test: the security arm pins decimals=6 the same /// way the default emitter test pins decimals=18. SECURITY variant emits empty - /// `variantParams` (its `isin` and `minimumRedeemable` are mutable and surfaced via + /// `eventParams` (its `isin` and `minimumRedeemable` are mutable and surfaced via /// their own update events). function test_createB20_success_emitsB20Created_security(address caller, bytes32 salt) public { _assumeValidCaller(caller); @@ -503,10 +503,10 @@ contract B20FactoryCreateB20Test is B20FactoryTest { } /// @notice Verifies createToken emits B20Created with decimals=6 and a non-empty - /// `variantParams` payload for the stablecoin variant - /// @dev STABLECOIN-only behavior: the `variantParams` field carries + /// `eventParams` payload for the stablecoin variant + /// @dev STABLECOIN-only behavior: the `eventParams` field carries /// `abi.encode(B20StablecoinEventParams { version, currency })` so stream-based - /// indexers (e.g. Coindexer) can recover the immutable `currency` without an RPC + /// indexers can recover the immutable `currency` without an RPC /// call to `currency()`. function test_createB20_success_emitsB20Created_stablecoin(address caller, bytes32 salt) public { _assumeValidCaller(caller); @@ -527,7 +527,7 @@ contract B20FactoryCreateB20Test is B20FactoryTest { _createStablecoin(caller, salt, p, new bytes[](0)); } - /// @notice Verifies the `variantParams` payload of `B20Created` for STABLECOIN decodes + /// @notice Verifies the `eventParams` payload of `B20Created` for STABLECOIN decodes /// back to a `B20StablecoinEventParams` whose `currency` round-trips the value /// passed at creation and whose `version` matches the canonical event-encoding /// version constant. @@ -554,18 +554,18 @@ contract B20FactoryCreateB20Test is B20FactoryTest { Vm.Log[] memory logs = vm.getRecordedLogs(); bytes32 selector = IB20Factory.B20Created.selector; - bytes memory variantParams; + bytes memory eventParams; for (uint256 i = 0; i < logs.length; ++i) { if (logs[i].topics.length > 0 && logs[i].topics[0] == selector) { - // B20Created data payload: (name, symbol, decimals, variantParams). - (,,, variantParams) = abi.decode(logs[i].data, (string, string, uint8, bytes)); + // B20Created data payload: (name, symbol, decimals, eventParams). + (,,, eventParams) = abi.decode(logs[i].data, (string, string, uint8, bytes)); break; } } - assertGt(variantParams.length, 0, "STABLECOIN variantParams must be non-empty"); + assertGt(eventParams.length, 0, "STABLECOIN eventParams must be non-empty"); IB20Factory.B20StablecoinEventParams memory decoded = - abi.decode(variantParams, (IB20Factory.B20StablecoinEventParams)); + abi.decode(eventParams, (IB20Factory.B20StablecoinEventParams)); assertEq( decoded.version, B20FactoryLib.B20_STABLECOIN_EVENT_PARAMS_VERSION, diff --git a/test/unit/B20FactoryLib/encodeStablecoinEventParams.t.sol b/test/unit/B20FactoryLib/encodeStablecoinEventParams.t.sol index ee226cb9..6120efbc 100644 --- a/test/unit/B20FactoryLib/encodeStablecoinEventParams.t.sol +++ b/test/unit/B20FactoryLib/encodeStablecoinEventParams.t.sol @@ -10,7 +10,7 @@ contract B20FactoryLibEncodeStablecoinEventParamsTest is B20FactoryLibTest { /// @notice Verifies the output decodes back to a `B20StablecoinEventParams` /// with the caller's currency and the current event-encoding version byte. /// @dev Round-trips through `abi.decode` to pin the wire format the - /// `B20Created` `variantParams` field carries for STABLECOIN. The + /// `B20Created` `eventParams` field carries for STABLECOIN. The /// event-params version is independent of the create-params version, /// so this test pins against `B20_STABLECOIN_EVENT_PARAMS_VERSION` /// specifically (not `B20_STABLECOIN_CREATE_PARAMS_VERSION`). From b0b7bde3656fbc2eca144f3764dff741afa19441 Mon Sep 17 00:00:00 2001 From: Amie Corso Date: Fri, 29 May 2026 14:29:54 -0700 Subject: [PATCH 4/5] revert: keep B20Created field name as variantParams (per ilikesymmetry review) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Roll back the variantParams → eventParams rename from 64ec9b9 per Conner's review comment on PR #102. Stevie's earlier ask was to align the event field with the underlying encoded data; that's satisfied either direction (eventParams matching the *EventParams struct family, or variantParams emphasizing the per-variant payload nature). Conner reviewed three minutes after the rename push and explicitly suggested variantParams, so the field returns to its original name. The B20StablecoinEventParams struct, the encodeStablecoinEventParams helper, and the B20_STABLECOIN_EVENT_PARAMS_VERSION constant all keep their *EventParams names (they are about event-time payload type, distinct from the variantParams field name which is about per-variant event-payload bytes carriage). Coindexer strip from 64ec9b9 stays. --- src/interfaces/IB20Factory.sol | 13 ++++++++---- src/lib/B20FactoryLib.sol | 4 ++-- test/lib/mocks/MockB20Factory.sol | 8 ++++---- test/unit/B20Factory/createToken.t.sol | 20 +++++++++---------- .../encodeStablecoinEventParams.t.sol | 2 +- 5 files changed, 26 insertions(+), 21 deletions(-) diff --git a/src/interfaces/IB20Factory.sol b/src/interfaces/IB20Factory.sol index 3d3bf240..5f724c2c 100644 --- a/src/interfaces/IB20Factory.sol +++ b/src/interfaces/IB20Factory.sol @@ -162,7 +162,7 @@ interface IB20Factory { uint256 minimumRedeemable; } - /// @notice Event payload carried in the `eventParams` field of + /// @notice Event payload carried in the `variantParams` field of /// `B20Created` for STABLECOIN-variant tokens. ABI-encoded /// and emitted with a leading `version` byte so stream-based /// indexers can decode by `(variant, version)` without an @@ -180,7 +180,7 @@ interface IB20Factory { /// otherwise have no way to recover it from the /// event stream. /// @dev DEFAULT and SECURITY variants currently emit an empty - /// `eventParams` byte string: DEFAULT has no extra + /// `variantParams` byte string: DEFAULT has no extra /// immutable identity fields beyond what's already in /// `B20Created`, and SECURITY's `isin` and /// `minimumRedeemable` are mutable and surfaced via their @@ -247,7 +247,7 @@ interface IB20Factory { /// @param name ERC-20 token name. /// @param symbol ERC-20 token symbol. /// @param decimals ERC-20 decimals (fixed per variant). - /// @param eventParams ABI-encoded variant-specific immutable identity + /// @param variantParams ABI-encoded variant-specific immutable identity /// fields, leading with a version byte. Empty /// (`""`) for DEFAULT and SECURITY (no extra /// immutable fields not already covered); for @@ -255,7 +255,12 @@ interface IB20Factory { /// with `currency`. Indexers decode by /// `(variant, leading version byte)`. event B20Created( - address indexed token, B20Variant indexed variant, string name, string symbol, uint8 decimals, bytes eventParams + address indexed token, + B20Variant indexed variant, + string name, + string symbol, + uint8 decimals, + bytes variantParams ); /*////////////////////////////////////////////////////////////// diff --git a/src/lib/B20FactoryLib.sol b/src/lib/B20FactoryLib.sol index d8ba17d6..75b0f611 100644 --- a/src/lib/B20FactoryLib.sol +++ b/src/lib/B20FactoryLib.sol @@ -61,7 +61,7 @@ library B20FactoryLib { uint8 internal constant B20_SECURITY_CREATE_PARAMS_VERSION = 1; /// @notice Current encoding version for `B20StablecoinEventParams`, - /// the payload carried in the `eventParams` field of the + /// the payload carried in the `variantParams` field of the /// `B20Created` event for STABLECOIN-variant tokens. /// Independent of `B20_STABLECOIN_CREATE_PARAMS_VERSION` — /// the event payload schema can evolve separately from the @@ -225,7 +225,7 @@ library B20FactoryLib { ); } - /// @notice Encodes a `B20StablecoinEventParams` as the `eventParams` + /// @notice Encodes a `B20StablecoinEventParams` as the `variantParams` /// blob the factory emits in the `B20Created` event when /// `variant == B20Variant.STABLECOIN`. The leading byte is /// `B20_STABLECOIN_EVENT_PARAMS_VERSION`. Indexers decode diff --git a/test/lib/mocks/MockB20Factory.sol b/test/lib/mocks/MockB20Factory.sol index 561d0c05..9b0e0d92 100644 --- a/test/lib/mocks/MockB20Factory.sol +++ b/test/lib/mocks/MockB20Factory.sol @@ -178,7 +178,7 @@ contract MockB20Factory is IB20Factory { // assignment is announced via the standard RoleGranted // event from step 7. // - // The `eventParams` field carries variant-specific + // The `variantParams` field carries variant-specific // immutable identity that isn't already covered by the // fixed event fields. STABLECOIN emits an ABI-encoded // `B20StablecoinEventParams` so stream-based indexers @@ -186,11 +186,11 @@ contract MockB20Factory is IB20Factory { // without an RPC call. DEFAULT and SECURITY emit empty // bytes (SECURITY's `isin` / `minimumRedeemable` are // mutable and surfaced via their own update events). - bytes memory eventParams; + bytes memory variantParams; if (variant == B20Variant.STABLECOIN) { - eventParams = B20FactoryLib.encodeStablecoinEventParams(currency_); + variantParams = B20FactoryLib.encodeStablecoinEventParams(currency_); } - emit B20Created(token, variant, name_, symbol_, decimals, eventParams); + emit B20Created(token, variant, name_, symbol_, decimals, variantParams); // -- 7. Grant the initial admin role via the canonical path. // msg.sender at the token is address(this) == factory, diff --git a/test/unit/B20Factory/createToken.t.sol b/test/unit/B20Factory/createToken.t.sol index f4f8f471..66ccb740 100644 --- a/test/unit/B20Factory/createToken.t.sol +++ b/test/unit/B20Factory/createToken.t.sol @@ -475,7 +475,7 @@ contract B20FactoryCreateB20Test is B20FactoryTest { /// @dev Event integrity: token, variant, name, symbol, decimals must match derived variant defaults. /// Admin role assignment is announced via RoleGranted, not as a field on this event; /// see test_createB20_success_emitsRoleGrantedForInitialAdmin for that. DEFAULT variant - /// emits empty `eventParams` (no extra immutable identity fields beyond what's already + /// emits empty `variantParams` (no extra immutable identity fields beyond what's already /// in the event). function test_createB20_success_emitsB20Created(address caller, bytes32 salt) public { _assumeValidCaller(caller); @@ -490,7 +490,7 @@ contract B20FactoryCreateB20Test is B20FactoryTest { /// @notice Verifies createToken emits B20Created with decimals=6 for the security variant /// @dev Variant-specific dedicated event test: the security arm pins decimals=6 the same /// way the default emitter test pins decimals=18. SECURITY variant emits empty - /// `eventParams` (its `isin` and `minimumRedeemable` are mutable and surfaced via + /// `variantParams` (its `isin` and `minimumRedeemable` are mutable and surfaced via /// their own update events). function test_createB20_success_emitsB20Created_security(address caller, bytes32 salt) public { _assumeValidCaller(caller); @@ -503,8 +503,8 @@ contract B20FactoryCreateB20Test is B20FactoryTest { } /// @notice Verifies createToken emits B20Created with decimals=6 and a non-empty - /// `eventParams` payload for the stablecoin variant - /// @dev STABLECOIN-only behavior: the `eventParams` field carries + /// `variantParams` payload for the stablecoin variant + /// @dev STABLECOIN-only behavior: the `variantParams` field carries /// `abi.encode(B20StablecoinEventParams { version, currency })` so stream-based /// indexers can recover the immutable `currency` without an RPC /// call to `currency()`. @@ -527,7 +527,7 @@ contract B20FactoryCreateB20Test is B20FactoryTest { _createStablecoin(caller, salt, p, new bytes[](0)); } - /// @notice Verifies the `eventParams` payload of `B20Created` for STABLECOIN decodes + /// @notice Verifies the `variantParams` payload of `B20Created` for STABLECOIN decodes /// back to a `B20StablecoinEventParams` whose `currency` round-trips the value /// passed at creation and whose `version` matches the canonical event-encoding /// version constant. @@ -554,18 +554,18 @@ contract B20FactoryCreateB20Test is B20FactoryTest { Vm.Log[] memory logs = vm.getRecordedLogs(); bytes32 selector = IB20Factory.B20Created.selector; - bytes memory eventParams; + bytes memory variantParams; for (uint256 i = 0; i < logs.length; ++i) { if (logs[i].topics.length > 0 && logs[i].topics[0] == selector) { - // B20Created data payload: (name, symbol, decimals, eventParams). - (,,, eventParams) = abi.decode(logs[i].data, (string, string, uint8, bytes)); + // B20Created data payload: (name, symbol, decimals, variantParams). + (,,, variantParams) = abi.decode(logs[i].data, (string, string, uint8, bytes)); break; } } - assertGt(eventParams.length, 0, "STABLECOIN eventParams must be non-empty"); + assertGt(variantParams.length, 0, "STABLECOIN variantParams must be non-empty"); IB20Factory.B20StablecoinEventParams memory decoded = - abi.decode(eventParams, (IB20Factory.B20StablecoinEventParams)); + abi.decode(variantParams, (IB20Factory.B20StablecoinEventParams)); assertEq( decoded.version, B20FactoryLib.B20_STABLECOIN_EVENT_PARAMS_VERSION, diff --git a/test/unit/B20FactoryLib/encodeStablecoinEventParams.t.sol b/test/unit/B20FactoryLib/encodeStablecoinEventParams.t.sol index 6120efbc..ee226cb9 100644 --- a/test/unit/B20FactoryLib/encodeStablecoinEventParams.t.sol +++ b/test/unit/B20FactoryLib/encodeStablecoinEventParams.t.sol @@ -10,7 +10,7 @@ contract B20FactoryLibEncodeStablecoinEventParamsTest is B20FactoryLibTest { /// @notice Verifies the output decodes back to a `B20StablecoinEventParams` /// with the caller's currency and the current event-encoding version byte. /// @dev Round-trips through `abi.decode` to pin the wire format the - /// `B20Created` `eventParams` field carries for STABLECOIN. The + /// `B20Created` `variantParams` field carries for STABLECOIN. The /// event-params version is independent of the create-params version, /// so this test pins against `B20_STABLECOIN_EVENT_PARAMS_VERSION` /// specifically (not `B20_STABLECOIN_CREATE_PARAMS_VERSION`). From 90dbcb0517fa96278a45574be3fce0a46ce61f7e Mon Sep 17 00:00:00 2001 From: Amie Corso Date: Fri, 29 May 2026 14:34:23 -0700 Subject: [PATCH 5/5] =?UTF-8?q?rename:=20B20Created=20field=20variantParam?= =?UTF-8?q?s=20=E2=86=92=20variantEventParams?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Compromise between the two reviewer threads on PR #102: - Conner asked for variantParams (emphasizes per-variant nature) - Stevie asked for the field to share a stem with the encoded data, which lives in the B20*EventParams struct family variantEventParams captures both: "variant" prefix preserves Conner's per-variant semantics; "EventParams" suffix matches the struct / encoder / version-constant family already in place (B20StablecoinEventParams, encodeStablecoinEventParams, B20_STABLECOIN_EVENT_PARAMS_VERSION). Side effect: the longer identifier pushes the event signature past forge fmt's 120-char limit, so it reflows to one-param-per-line. --- src/interfaces/IB20Factory.sol | 8 ++++---- src/lib/B20FactoryLib.sol | 4 ++-- test/lib/mocks/MockB20Factory.sol | 8 ++++---- test/unit/B20Factory/createToken.t.sol | 20 +++++++++---------- .../encodeStablecoinEventParams.t.sol | 2 +- 5 files changed, 21 insertions(+), 21 deletions(-) diff --git a/src/interfaces/IB20Factory.sol b/src/interfaces/IB20Factory.sol index 5f724c2c..31cce00c 100644 --- a/src/interfaces/IB20Factory.sol +++ b/src/interfaces/IB20Factory.sol @@ -162,7 +162,7 @@ interface IB20Factory { uint256 minimumRedeemable; } - /// @notice Event payload carried in the `variantParams` field of + /// @notice Event payload carried in the `variantEventParams` field of /// `B20Created` for STABLECOIN-variant tokens. ABI-encoded /// and emitted with a leading `version` byte so stream-based /// indexers can decode by `(variant, version)` without an @@ -180,7 +180,7 @@ interface IB20Factory { /// otherwise have no way to recover it from the /// event stream. /// @dev DEFAULT and SECURITY variants currently emit an empty - /// `variantParams` byte string: DEFAULT has no extra + /// `variantEventParams` byte string: DEFAULT has no extra /// immutable identity fields beyond what's already in /// `B20Created`, and SECURITY's `isin` and /// `minimumRedeemable` are mutable and surfaced via their @@ -247,7 +247,7 @@ interface IB20Factory { /// @param name ERC-20 token name. /// @param symbol ERC-20 token symbol. /// @param decimals ERC-20 decimals (fixed per variant). - /// @param variantParams ABI-encoded variant-specific immutable identity + /// @param variantEventParams ABI-encoded variant-specific immutable identity /// fields, leading with a version byte. Empty /// (`""`) for DEFAULT and SECURITY (no extra /// immutable fields not already covered); for @@ -260,7 +260,7 @@ interface IB20Factory { string name, string symbol, uint8 decimals, - bytes variantParams + bytes variantEventParams ); /*////////////////////////////////////////////////////////////// diff --git a/src/lib/B20FactoryLib.sol b/src/lib/B20FactoryLib.sol index 75b0f611..d2e6e7ab 100644 --- a/src/lib/B20FactoryLib.sol +++ b/src/lib/B20FactoryLib.sol @@ -61,7 +61,7 @@ library B20FactoryLib { uint8 internal constant B20_SECURITY_CREATE_PARAMS_VERSION = 1; /// @notice Current encoding version for `B20StablecoinEventParams`, - /// the payload carried in the `variantParams` field of the + /// the payload carried in the `variantEventParams` field of the /// `B20Created` event for STABLECOIN-variant tokens. /// Independent of `B20_STABLECOIN_CREATE_PARAMS_VERSION` — /// the event payload schema can evolve separately from the @@ -225,7 +225,7 @@ library B20FactoryLib { ); } - /// @notice Encodes a `B20StablecoinEventParams` as the `variantParams` + /// @notice Encodes a `B20StablecoinEventParams` as the `variantEventParams` /// blob the factory emits in the `B20Created` event when /// `variant == B20Variant.STABLECOIN`. The leading byte is /// `B20_STABLECOIN_EVENT_PARAMS_VERSION`. Indexers decode diff --git a/test/lib/mocks/MockB20Factory.sol b/test/lib/mocks/MockB20Factory.sol index 9b0e0d92..01817658 100644 --- a/test/lib/mocks/MockB20Factory.sol +++ b/test/lib/mocks/MockB20Factory.sol @@ -178,7 +178,7 @@ contract MockB20Factory is IB20Factory { // assignment is announced via the standard RoleGranted // event from step 7. // - // The `variantParams` field carries variant-specific + // The `variantEventParams` field carries variant-specific // immutable identity that isn't already covered by the // fixed event fields. STABLECOIN emits an ABI-encoded // `B20StablecoinEventParams` so stream-based indexers @@ -186,11 +186,11 @@ contract MockB20Factory is IB20Factory { // without an RPC call. DEFAULT and SECURITY emit empty // bytes (SECURITY's `isin` / `minimumRedeemable` are // mutable and surfaced via their own update events). - bytes memory variantParams; + bytes memory variantEventParams; if (variant == B20Variant.STABLECOIN) { - variantParams = B20FactoryLib.encodeStablecoinEventParams(currency_); + variantEventParams = B20FactoryLib.encodeStablecoinEventParams(currency_); } - emit B20Created(token, variant, name_, symbol_, decimals, variantParams); + emit B20Created(token, variant, name_, symbol_, decimals, variantEventParams); // -- 7. Grant the initial admin role via the canonical path. // msg.sender at the token is address(this) == factory, diff --git a/test/unit/B20Factory/createToken.t.sol b/test/unit/B20Factory/createToken.t.sol index 66ccb740..7cdc4a0e 100644 --- a/test/unit/B20Factory/createToken.t.sol +++ b/test/unit/B20Factory/createToken.t.sol @@ -475,7 +475,7 @@ contract B20FactoryCreateB20Test is B20FactoryTest { /// @dev Event integrity: token, variant, name, symbol, decimals must match derived variant defaults. /// Admin role assignment is announced via RoleGranted, not as a field on this event; /// see test_createB20_success_emitsRoleGrantedForInitialAdmin for that. DEFAULT variant - /// emits empty `variantParams` (no extra immutable identity fields beyond what's already + /// emits empty `variantEventParams` (no extra immutable identity fields beyond what's already /// in the event). function test_createB20_success_emitsB20Created(address caller, bytes32 salt) public { _assumeValidCaller(caller); @@ -490,7 +490,7 @@ contract B20FactoryCreateB20Test is B20FactoryTest { /// @notice Verifies createToken emits B20Created with decimals=6 for the security variant /// @dev Variant-specific dedicated event test: the security arm pins decimals=6 the same /// way the default emitter test pins decimals=18. SECURITY variant emits empty - /// `variantParams` (its `isin` and `minimumRedeemable` are mutable and surfaced via + /// `variantEventParams` (its `isin` and `minimumRedeemable` are mutable and surfaced via /// their own update events). function test_createB20_success_emitsB20Created_security(address caller, bytes32 salt) public { _assumeValidCaller(caller); @@ -503,8 +503,8 @@ contract B20FactoryCreateB20Test is B20FactoryTest { } /// @notice Verifies createToken emits B20Created with decimals=6 and a non-empty - /// `variantParams` payload for the stablecoin variant - /// @dev STABLECOIN-only behavior: the `variantParams` field carries + /// `variantEventParams` payload for the stablecoin variant + /// @dev STABLECOIN-only behavior: the `variantEventParams` field carries /// `abi.encode(B20StablecoinEventParams { version, currency })` so stream-based /// indexers can recover the immutable `currency` without an RPC /// call to `currency()`. @@ -527,7 +527,7 @@ contract B20FactoryCreateB20Test is B20FactoryTest { _createStablecoin(caller, salt, p, new bytes[](0)); } - /// @notice Verifies the `variantParams` payload of `B20Created` for STABLECOIN decodes + /// @notice Verifies the `variantEventParams` payload of `B20Created` for STABLECOIN decodes /// back to a `B20StablecoinEventParams` whose `currency` round-trips the value /// passed at creation and whose `version` matches the canonical event-encoding /// version constant. @@ -554,18 +554,18 @@ contract B20FactoryCreateB20Test is B20FactoryTest { Vm.Log[] memory logs = vm.getRecordedLogs(); bytes32 selector = IB20Factory.B20Created.selector; - bytes memory variantParams; + bytes memory variantEventParams; for (uint256 i = 0; i < logs.length; ++i) { if (logs[i].topics.length > 0 && logs[i].topics[0] == selector) { - // B20Created data payload: (name, symbol, decimals, variantParams). - (,,, variantParams) = abi.decode(logs[i].data, (string, string, uint8, bytes)); + // B20Created data payload: (name, symbol, decimals, variantEventParams). + (,,, variantEventParams) = abi.decode(logs[i].data, (string, string, uint8, bytes)); break; } } - assertGt(variantParams.length, 0, "STABLECOIN variantParams must be non-empty"); + assertGt(variantEventParams.length, 0, "STABLECOIN variantEventParams must be non-empty"); IB20Factory.B20StablecoinEventParams memory decoded = - abi.decode(variantParams, (IB20Factory.B20StablecoinEventParams)); + abi.decode(variantEventParams, (IB20Factory.B20StablecoinEventParams)); assertEq( decoded.version, B20FactoryLib.B20_STABLECOIN_EVENT_PARAMS_VERSION, diff --git a/test/unit/B20FactoryLib/encodeStablecoinEventParams.t.sol b/test/unit/B20FactoryLib/encodeStablecoinEventParams.t.sol index ee226cb9..08646c0f 100644 --- a/test/unit/B20FactoryLib/encodeStablecoinEventParams.t.sol +++ b/test/unit/B20FactoryLib/encodeStablecoinEventParams.t.sol @@ -10,7 +10,7 @@ contract B20FactoryLibEncodeStablecoinEventParamsTest is B20FactoryLibTest { /// @notice Verifies the output decodes back to a `B20StablecoinEventParams` /// with the caller's currency and the current event-encoding version byte. /// @dev Round-trips through `abi.decode` to pin the wire format the - /// `B20Created` `variantParams` field carries for STABLECOIN. The + /// `B20Created` `variantEventParams` field carries for STABLECOIN. The /// event-params version is independent of the create-params version, /// so this test pins against `B20_STABLECOIN_EVENT_PARAMS_VERSION` /// specifically (not `B20_STABLECOIN_CREATE_PARAMS_VERSION`).