diff --git a/docs/PROTOCOL_SPEC.md b/docs/PROTOCOL_SPEC.md index 2ba04ed..436e7e2 100644 --- a/docs/PROTOCOL_SPEC.md +++ b/docs/PROTOCOL_SPEC.md @@ -44,9 +44,10 @@ Non-transferable identity token. One per address. Cannot be burned or transferre ### Mint Flow 1. Controller sets `currentEulaHash` on the contract. -2. User calls `mintSBT(encryptedAccountId, zkpCommitment, eulaHash)`. -3. Contract verifies: no existing SBT for caller, valid account ID, EULA hash matches current. -4. SBT is created. The transaction signature constitutes cryptographic EULA acceptance. +2. User calls `mintSBT(zkpCommitment, eulaHash)`. +3. Contract verifies: no existing SBT for caller, EULA hash matches current. +4. Contract internally derives `encryptedAccountId` deterministically from `msg.sender` and `chainId` to enforce canonical identity derivation. +5. SBT is created. The transaction signature constitutes cryptographic EULA acceptance. ### ZKP Commitment @@ -306,4 +307,4 @@ The contracts are not upgradeable. There are no proxies and no `delegatecall`. T 3. Users deposit to new DepositPool. 4. Old ClaimPool operator processes remaining redemptions. -This is intentional. Immutable contracts are auditable contracts. \ No newline at end of file +This is intentional. Immutable contracts are auditable contracts. diff --git a/src/SoulBoundToken.sol b/src/SoulBoundToken.sol index 9a97f76..1061bc9 100644 --- a/src/SoulBoundToken.sol +++ b/src/SoulBoundToken.sol @@ -92,22 +92,23 @@ contract SoulBoundToken is ISoulBoundToken { /** * @notice Mint SBT with EULA acceptance gate - * @param _encryptedAccountId Hashed account identifier * @param _zkpCommitment Privado ID ZKP commitment (can be bytes32(0) if not yet verified) * @param _eulaHash Must match currentEulaHash — the transaction signature IS the acceptance + * @dev _encryptedAccountId is the canonical identity derivation on-chain * @dev The act of calling this function with the correct EULA hash, signed by the user's * wallet, constitutes cryptographic proof of EULA acceptance on an immutable ledger. */ function mintSBT( - bytes32 _encryptedAccountId, bytes32 _zkpCommitment, bytes32 _eulaHash ) external { if (_sbtRegistry[msg.sender].exists) revert SBTAlreadyExists(); - if (_encryptedAccountId == bytes32(0)) revert InvalidAccountId(); if (currentEulaHash == bytes32(0)) revert EulaNotSet(); if (_eulaHash != currentEulaHash) revert EulaMismatch(); + bytes32 _encryptedAccountId = keccak256(abi.encodePacked(msg.sender, "SBF_ALPHA_V1", block.chainid)); + + _sbtRegistry[msg.sender] = SBTData({ encryptedAccountId: _encryptedAccountId, zkpCommitment: _zkpCommitment,