diff --git a/src/interfaces/IB20Factory.sol b/src/interfaces/IB20Factory.sol index ff03131..6d5c1c4 100644 --- a/src/interfaces/IB20Factory.sol +++ b/src/interfaces/IB20Factory.sol @@ -181,9 +181,9 @@ interface IB20Factory { /// any known encoding for the requested variant. error UnsupportedVersion(uint8 version, B20Variant variant); - /// @notice A required string argument was the empty string (e.g. - /// security `isin`). - error MissingRequiredField(); + /// @notice A required string argument was the empty string. + /// @param field Name of the missing field (e.g. `"isin"`, `"currency"`). + error MissingRequiredField(string field); /// @notice The stablecoin `currency` contained a non-`A`–`Z` byte. error InvalidCurrency(string code); diff --git a/test/lib/mocks/MockB20Factory.sol b/test/lib/mocks/MockB20Factory.sol index 7cdec8f..26caaa7 100644 --- a/test/lib/mocks/MockB20Factory.sol +++ b/test/lib/mocks/MockB20Factory.sol @@ -119,6 +119,8 @@ contract MockB20Factory is IB20Factory { if (p.version != B20FactoryLib.B20_STABLECOIN_CREATE_PARAMS_VERSION) { revert UnsupportedVersion(p.version, variant); } + // Required: currency must be non-empty. + if (bytes(p.currency).length == 0) revert MissingRequiredField("currency"); // Format check: every byte must be an uppercase ASCII letter (A-Z). bytes memory cb = bytes(p.currency); for (uint256 i = 0; i < cb.length; ++i) { @@ -134,7 +136,7 @@ contract MockB20Factory is IB20Factory { if (p.version != B20FactoryLib.B20_SECURITY_CREATE_PARAMS_VERSION) { revert UnsupportedVersion(p.version, variant); } - if (bytes(p.isin).length == 0) revert MissingRequiredField(); + if (bytes(p.isin).length == 0) revert MissingRequiredField("isin"); name_ = p.name; symbol_ = p.symbol; admin = p.initialAdmin; diff --git a/test/unit/B20Factory/createToken.t.sol b/test/unit/B20Factory/createToken.t.sol index 30fed40..af2703b 100644 --- a/test/unit/B20Factory/createToken.t.sol +++ b/test/unit/B20Factory/createToken.t.sol @@ -126,16 +126,24 @@ contract B20FactoryCreateB20Test is B20FactoryTest { factory.createB20(IB20Factory.B20Variant.SECURITY, salt, abi.encode(p), new bytes[](0)); } - /// @notice Verifies security createToken reverts when isin is the empty string - /// @dev Per-variant required-field check; checks MissingRequiredField() error + /// @notice Verifies security createToken reverts with the field name when isin is the empty string. function test_createB20_revert_missingIsin(address caller, bytes32 salt) public { _assumeValidCaller(caller); IB20Factory.B20SecurityCreateParams memory p = _securityParams("Security Test", "SEC", admin, "", 0); vm.prank(caller); - vm.expectRevert(IB20Factory.MissingRequiredField.selector); + vm.expectRevert(abi.encodeWithSelector(IB20Factory.MissingRequiredField.selector, "isin")); factory.createB20(IB20Factory.B20Variant.SECURITY, salt, abi.encode(p), new bytes[](0)); } + /// @notice Verifies stablecoin createToken reverts with the field name when currency is the empty string. + function test_createB20_revert_missingCurrency(address caller, bytes32 salt) public { + _assumeValidCaller(caller); + IB20Factory.B20StablecoinCreateParams memory p = _stablecoinParams("Stablecoin Test", "SCT", admin, ""); + vm.prank(caller); + vm.expectRevert(abi.encodeWithSelector(IB20Factory.MissingRequiredField.selector, "currency")); + factory.createB20(IB20Factory.B20Variant.STABLECOIN, salt, abi.encode(p), new bytes[](0)); + } + /// @notice Verifies createToken reverts when (variant, sender, salt) collides /// @dev Deterministic-address uniqueness; checks TokenAlreadyExists(token) error function test_createB20_revert_tokenAlreadyExists(address caller, bytes32 salt) public {