From fc6df34c12b739d578982d8c0a219386c9f69cbb Mon Sep 17 00:00:00 2001 From: Gakarot Date: Fri, 29 May 2026 02:41:24 +0530 Subject: [PATCH 1/2] Fix: Update mintSBT to enforce on-chain identity derivation --- src/SoulBoundToken.sol | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) 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, From cf87245ab192b035981de884f98c5b8e5bce4c4c Mon Sep 17 00:00:00 2001 From: Gakarot Date: Fri, 29 May 2026 02:47:50 +0530 Subject: [PATCH 2/2] Docs: Update mint flow --- docs/PROTOCOL_SPEC.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) 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.