Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions docs/PROTOCOL_SPEC.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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.
This is intentional. Immutable contracts are auditable contracts.
7 changes: 4 additions & 3 deletions src/SoulBoundToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down