@pvium/p2id-core 0.1.2 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,22 +6,16 @@ import {P2IDHash} from "./lib/P2IDHash.sol";
6
6
  import {PviumIdentity} from "./PviumIdentity.sol";
7
7
 
8
8
  /// @title PviumVerifier
9
- /// @notice IP2IDVerifier backed by the Pvium ZK circuit. Verifies an identity proof against the
10
- /// registered Privy signing key (via PviumIdentity) and, when a constraint commitment is
11
- /// given, that the registered attestation signer has signed that commitment.
9
+ /// @notice Resolves an EVM wallet from a PviumIdentity attestation and checks an optional
10
+ /// constraint signature against the registered constraint signers.
12
11
  /// @dev proof = abi.encode(bytes zkProof, bytes32[] publicInputs). The wallet returned is the
13
12
  /// EVM address the circuit read out of the Privy-signed token (public input 1), cross-checked
14
13
  /// here against the proof's walletHash with the same formula. A constraint is satisfied by
15
14
  /// a registered signer's EIP-712 signature over `Constraint(bytes32 commitment)` in this
16
- /// contract's domain (name "PviumVerifier", version "1", chain id, this address), so a
17
- /// signature is valid on one chain and one verifier only. The identity side is immutable
18
- /// (a new key set or circuit is a new deployment the vault factory registers alongside).
19
- /// The signer set is the owner's: signers only gate constrained deposits, and never choose
20
- /// the wallet, so a compromised signer or owner can release screened funds to their rightful
21
- /// payee early but cannot redirect them. An owner of address(0) freezes the set.
22
- /// What a commitment means is up to the funder; for a screening attestation it should
23
- /// commit to the payee as well as the policy, plus a salt (see screeningCommitment),
24
- /// otherwise one signature releases every deposit under that policy for every payee.
15
+ /// contract's domain (name "PviumVerifier", version "1", chain id, this address).
16
+ /// The PviumIdentity address is immutable; its accepted key set can change separately.
17
+ /// Constraint signatures authorize a commitment, while the attestation supplies the wallet.
18
+ /// This contract does not interpret the commitment's contents or enforce signature single-use.
25
19
  contract PviumVerifier is IP2IDVerifier {
26
20
  bytes32 private constant EIP712_DOMAIN_TYPEHASH =
27
21
  keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");
@@ -51,7 +45,7 @@ contract PviumVerifier is IP2IDVerifier {
51
45
  error NotPendingOwner();
52
46
  error InvalidSigner();
53
47
 
54
- /// @param _owner Manages the signer set; address(0) makes the set given here permanent.
48
+ /// @param _owner Constraint-signer administrator; address(0) disables owner-only calls.
55
49
  /// @param _signers Initial constraint signers (may be empty: constraints unsupported until one is added).
56
50
  constructor(PviumIdentity _pviumIdentity, address _owner, address[] memory _signers) {
57
51
  pviumIdentity = _pviumIdentity;
@@ -62,8 +56,7 @@ contract PviumVerifier is IP2IDVerifier {
62
56
  _cachedDomainSeparator = _domainSeparator(block.chainid);
63
57
  }
64
58
 
65
- /// @notice Register or revoke a constraint signer. Revoking one makes its signatures fail from
66
- /// now on, including for deposits already funded; those wait for a registered signer.
59
+ /// @notice Set a constraint signer's membership. Signatures are checked against current membership.
67
60
  function setConstraintSigner(address signer, bool allowed) external onlyOwner {
68
61
  _setSigner(signer, allowed);
69
62
  }
@@ -80,7 +73,7 @@ contract PviumVerifier is IP2IDVerifier {
80
73
  pendingOwner = address(0);
81
74
  }
82
75
 
83
- /// @notice EIP-712 domain separator; recomputed if the chain id changes (chain fork).
76
+ /// @notice EIP-712 domain separator; recomputed when block.chainid differs from its deployment value.
84
77
  function DOMAIN_SEPARATOR() public view returns (bytes32) {
85
78
  return block.chainid == _cachedChainId ? _cachedDomainSeparator : _domainSeparator(block.chainid);
86
79
  }
@@ -97,7 +90,7 @@ contract PviumVerifier is IP2IDVerifier {
97
90
  ) external view returns (address wallet, uint64 iat) {
98
91
  (bytes memory zkProof, bytes32[] memory publicInputs) = abi.decode(proof, (bytes, bytes32[]));
99
92
 
100
- // Reverts (InvalidProof / UnknownSigner / …) unless the proof is valid under the registered key.
93
+ // Verify against PviumIdentity's current accepted signer set.
101
94
  PviumIdentity.Attestation memory a = pviumIdentity.verifyAttestation(zkProof, publicInputs);
102
95
  if (a.identityHash != identityHash) revert IdentityMismatch();
103
96
  if (a.wallet == address(0)) revert NoEvmWallet();
@@ -111,8 +104,7 @@ contract PviumVerifier is IP2IDVerifier {
111
104
  }
112
105
 
113
106
  /// @inheritdoc IP2IDVerifier
114
- /// @dev Attester changes never affect which wallet a proof resolves to, so only the identity
115
- /// contract's key revocations count.
107
+ /// @dev Forward the identity key-set revision; constraint-signer changes do not increment it.
116
108
  function revision() external view returns (uint64) {
117
109
  return pviumIdentity.keySetRevision();
118
110
  }
@@ -127,8 +119,8 @@ contract PviumVerifier is IP2IDVerifier {
127
119
  return keccak256(abi.encodePacked("\x19\x01", DOMAIN_SEPARATOR(), keccak256(abi.encode(CONSTRAINT_TYPEHASH, commitment))));
128
120
  }
129
121
 
130
- /// @notice Suggested commitment for a screening attestation: binds the policy to the payee, with
131
- /// a random salt so each deposit has its own commitment (a funder can use one only once).
122
+ /// @notice Hash policyHash, identityHash and caller-supplied salt using abi.encode.
123
+ /// Does not generate a salt or enforce uniqueness.
132
124
  function screeningCommitment(bytes32 policyHash, bytes32 identityHash, bytes32 salt) public pure returns (bytes32) {
133
125
  return keccak256(abi.encode(policyHash, identityHash, salt));
134
126
  }
@@ -2,26 +2,11 @@
2
2
  pragma solidity ^0.8.27;
3
3
 
4
4
  /// @title IP2IDPolicy
5
- /// @notice Everything about P2ID vaults that is expected to evolve lives behind this interface,
6
- /// so it can change without changing vault bytecode (and therefore without moving any
7
- /// P2ID address). The vault factory points at one policy and can replace it only through
8
- /// a timelock; every vault consults the factory's current policy on each call.
9
- ///
10
- /// What a policy decides, and the limits the vault enforces on it:
11
- /// - which verifiers deposits may be funded under and claimed through. Gating is the one
12
- /// thing a policy can use to stop claims (by disallowing a verifier), which freezes
13
- /// claims under it but never moves funds; refunds never consult the policy.
14
- /// - the protocol fee on payouts, in basis points. The vault caps it at its own
15
- /// MAX_FEE_BPS, fixes a deposit's rate when the deposit is made, never charges it on
16
- /// refunds, and treats a failing quote as no fee.
17
- /// - how accrued fees are distributed. Fees accrue in each vault per (verifier, token);
18
- /// anyone can have a vault hand them to the policy (withdrawFees), which pulls them and
19
- /// splits them however it chooses, e.g. between a verifier's operator and the protocol.
20
- ///
21
- /// Launch policy: an owner-managed verifier allowlist and no fee. Permissionless,
22
- /// stake-based verifier registration and fees are later policies.
5
+ /// @notice Verifier approval, fee quotation and fee distribution callbacks used by P2IDVault.
6
+ /// @dev P2IDVault caps fee quotes, stores rates on funded deposits and quotes untracked funds at
7
+ /// sweep time. It skips policy approval for claims through the current default verifier.
23
8
  interface IP2IDPolicy {
24
- /// @notice Whether deposits may be funded under, and claimed through, `verifier`.
9
+ /// @notice Approval used for funding and for claims through a non-default verifier.
25
10
  function isVerifierAllowed(address verifier) external view returns (bool);
26
11
 
27
12
  /// @notice Fee on paying out `token` claimed through `verifier`, in basis points. Quoted when a
@@ -31,8 +16,7 @@ interface IP2IDPolicy {
31
16
  /// @notice Called by a vault holding fees earned through `verifier`. For an ERC-20 the vault
32
17
  /// first approves this policy for exactly `amount`: pull the tokens (transferFrom the
33
18
  /// caller) and distribute them; whatever is not pulled stays accrued in the vault, and
34
- /// the allowance is reset afterwards. Pulling from msg.sender makes the amount
35
- /// self-verifying: a caller that is not a vault can only give away its own tokens.
19
+ /// the allowance is reset afterwards.
36
20
  /// For the native coin (token == address(0)) the vault sends `amount` as msg.value
37
21
  /// instead, and all of it counts as distributed.
38
22
  function distributeFee(address verifier, address token, uint256 amount) external payable;
@@ -4,10 +4,8 @@ pragma solidity ^0.8.27;
4
4
  import {IP2IDVerifier} from "./IP2IDVerifier.sol";
5
5
 
6
6
  /// @title IP2IDVault
7
- /// @notice A vault holding ERC-20 tokens and the native coin for one identity until its owner
8
- /// proves it and sweeps. The native coin is the token address(0) in every function.
9
- /// Every deposit names the verifier whose proofs can release it; the factory's policy
10
- /// decides which verifiers are allowed and what (capped) fee applies.
7
+ /// @notice Deposit funding, claims, refunds and accounting for one identity commitment.
8
+ /// Token address(0) denotes native coin. Each deposit records its verifier and fee rate.
11
9
  interface IP2IDVault {
12
10
  /// @dev Field order packs into 5 storage slots:
13
11
  /// [funder, fundedAt, consumed] [token, refundWindow] [verifier, feeBps] [amount] [constraint].
@@ -25,8 +23,7 @@ interface IP2IDVault {
25
23
  }
26
24
 
27
25
  // ------------------------------------------------------------------ events
28
- // Every deposit is Funded once and then either Refunded (by its funder) or Claimed by a sweep.
29
- // Sweeps also emit one aggregate event with the net amount paid and the fee.
26
+ // Funding emits Funded; consumption emits Refunded or Claimed. Sweeps emit aggregate amounts.
30
27
 
31
28
  /// @notice A deposit was recorded via fund()/fundWith()/fundFor().
32
29
  /// @param ref Opaque application reference; zero means absent. Emitted only, not stored or enforced as unique.
@@ -35,15 +32,15 @@ interface IP2IDVault {
35
32
  event Refunded(uint256 indexed depositId, address indexed funder, address indexed token, uint256 amount);
36
33
  /// @notice A deposit was paid out by a sweep: `amount` gross, of which `fee` accrued as a fee.
37
34
  event Claimed(uint256 indexed depositId, address indexed to, uint256 amount, uint256 fee);
38
- /// @notice A proof newer than any seen under `verifier` set that verifier's owner wallet; older proofs are now refused.
35
+ /// @notice The owner cache was set or revalidated under the verifier's current revision.
39
36
  event OwnerRefreshed(address indexed verifier, address indexed owner, uint64 iat);
40
- /// @notice Default-bucket funds under `verifier` were paid to its owner wallet (`amount` net of `fee`).
37
+ /// @notice Default sweep result, including eligible untracked funds; amount is net of fee and may be zero.
41
38
  event Swept(address indexed verifier, address indexed token, uint256 amount, uint256 fee, address indexed to, uint256 depositsConsumed);
42
39
  /// @notice Funds from a constrained bucket were paid to the wallet the proof resolved to (`amount` net of `fee`).
43
40
  event SweptBucket(address indexed verifier, bytes32 indexed constraint, address indexed token, uint256 amount, uint256 fee, address to, uint256 depositsConsumed);
44
41
  /// @notice A fee accrued for `verifier`; it stays in the vault until withdrawFees() hands it to the policy.
45
42
  event FeeAccrued(address indexed verifier, address indexed token, uint256 amount);
46
- /// @notice The policy pulled `amount` of accrued fees to distribute.
43
+ /// @notice Amount deducted from accrued fees after policy distribution, via token pull or native transfer.
47
44
  event FeesDistributed(address indexed verifier, address indexed token, uint256 amount, address policy);
48
45
 
49
46
  // setup (factory only, once)
@@ -56,7 +53,7 @@ interface IP2IDVault {
56
53
  /// @notice Fund under any verifier the factory's policy allows.
57
54
  /// @param ref Opaque application reference emitted in Funded; bytes32(0) for none.
58
55
  function fundWith(address verifier, address token, uint256 amount, bytes32 constraint, uint64 refundWindow, bytes32 ref) external payable returns (uint256 depositId);
59
- /// @notice Factory-only: record a deposit owned by `funder`; tokens are pulled from the factory.
56
+ /// @notice Factory-only: record a deposit for `funder`; pull ERC-20 from the factory or receive native msg.value.
60
57
  /// @param ref Opaque application reference emitted in Funded; bytes32(0) for none.
61
58
  function fundFor(address funder, address verifier, address token, uint256 amount, bytes32 constraint, uint64 refundWindow, bytes32 ref) external payable returns (uint256 depositId);
62
59
  function refund(uint256 depositId) external;
@@ -66,9 +63,16 @@ interface IP2IDVault {
66
63
  /// if the proof is newer than anything seen under it, and retires older proofs.
67
64
  function refreshProof(address verifier, bytes calldata proof) external;
68
65
  function refreshProofAndSweep(address verifier, bytes calldata proof, address token, uint256 depositCountLimit) external returns (uint256 amount, uint256 consumed);
66
+ /// @notice Apply a proof the vault's own proxy has already verified (msg.sender must be this
67
+ /// address): the freshness, revision and owner-cache rules of refreshProof without
68
+ /// re-verifying. Reverts unless `wallet` is the recorded owner under `verifier` afterwards.
69
+ /// Returns its own selector as acknowledgement so the proxy rejects empty fallbacks.
70
+ /// The acknowledgement checks compatibility; implementations remain trusted to apply the result.
71
+ function acceptOwnerProof(address verifier, address wallet, uint64 iat) external returns (bytes4);
69
72
 
70
73
  // claiming (amounts returned are net of fees)
71
- /// @notice Sweep `verifier`'s default bucket to its owner; the default verifier's sweep also takes untracked funds.
74
+ /// @notice Sweep a page of the verifier's default bucket to its cached owner. Eligible untracked
75
+ /// funds are included for the current default verifier when its cache meets the cross-default floor.
72
76
  function sweep(address verifier, address token, uint256 depositCountLimit) external returns (uint256 amount, uint256 consumed);
73
77
  function sweepUntracked(address token) external returns (uint256 amount);
74
78
  function sweepDeposits(address verifier, address token, uint256[] calldata depositIds) external returns (uint256 amount);
@@ -83,9 +87,11 @@ interface IP2IDVault {
83
87
  /// @notice P2ID vault interface version implemented by this contract.
84
88
  function p2idVersion() external pure returns (string memory);
85
89
  function MAX_FEE_BPS() external view returns (uint16);
90
+ /// @notice Record cap for cursor-based sweeps; a zero or above-cap depositCountLimit uses this value.
91
+ function MAX_SWEEP_PAGE() external view returns (uint256);
86
92
  /// @notice address(0): the token address standing for the native coin.
87
93
  function NATIVE() external view returns (address);
88
- /// @notice keccak256 of the address scheme this vault was issued under, e.g. keccak256("pvium.vault.v1").
94
+ /// @notice Namespace value supplied at initialization; not used to recompute this vault's address.
89
95
  function nsHash() external view returns (bytes32);
90
96
  function factory() external view returns (address);
91
97
  function policy() external view returns (address);
@@ -96,17 +102,18 @@ interface IP2IDVault {
96
102
  function untrackedProofIat() external view returns (uint64);
97
103
  function saltCommitment() external view returns (bytes32);
98
104
  function depositCount() external view returns (uint256);
99
- function bucketDepositIds(address verifier, bytes32 constraint, address token) external view returns (uint256[] memory);
105
+ /// @notice A page of the bucket's deposit ids: up to `limit` from `offset` (limit 0 = all from `offset`).
106
+ function bucketDepositIds(address verifier, bytes32 constraint, address token, uint256 offset, uint256 limit) external view returns (uint256[] memory);
100
107
  function bucketDepositCount(address verifier, bytes32 constraint, address token) external view returns (uint256);
101
- /// @notice Whether `funder` has funded under `constraint`, and which deposit. A funder can use a
102
- /// constraint once (refunded or claimed, it stays used), so a signature over it releases
103
- /// at most one of their deposits. Funders must make constraints unique, e.g. by salting.
108
+ /// @notice Deposit recorded for a nonzero constraint and funder in this vault. The record persists
109
+ /// after a refund or claim and prevents the same funder from reusing that constraint here.
104
110
  function constraintDeposit(bytes32 constraint, address funder) external view returns (bool used, uint256 depositId);
105
111
  function bucketTotal(address verifier, bytes32 constraint, address token) external view returns (uint256);
106
112
  function trackedTotal(address token) external view returns (uint256);
107
113
  function feesOwed(address verifier, address token) external view returns (uint256);
108
114
  function feesOwedTotal(address token) external view returns (uint256);
109
- /// @notice Gross amount `sweep(verifier, token, 0)` would release now, before fees.
115
+ /// @notice Total default-bucket amount plus eligible untracked funds, before fees.
116
+ /// Does not apply the sweep page limit or validate default-bucket claimability.
110
117
  function sweepable(address verifier, address token) external view returns (uint256);
111
118
  function untrackedBalance(address token) external view returns (uint256);
112
119
  }
@@ -0,0 +1,38 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.27;
3
+
4
+ /// @title IP2IDVaultProxy
5
+ /// @notice A P2ID proxy with creation code fixed for its deployed factory. Its upgradeTo entry
6
+ /// point requires an identity proof and a registered target. Delegated implementations
7
+ /// are trusted with all proxy storage, including the implementation slot.
8
+ interface IP2IDVaultProxy {
9
+ /// @notice ERC-1967 upgrade event.
10
+ event Upgraded(address indexed implementation);
11
+
12
+ /// @notice The factory that deployed this proxy and whose registry bounds its implementations.
13
+ function factory() external view returns (address);
14
+ /// @notice The implementation used for delegated calls; proxy-defined selectors take precedence.
15
+ function implementation() external view returns (address);
16
+ /// @notice The wallet and issue time of the proof behind the last upgrade (zero before the first).
17
+ /// Later upgrades need a proof at least this recent; an equal time only for the same wallet.
18
+ /// Delegated code can overwrite this record; upgradeTo ignores a record beyond its future-time bound.
19
+ function lastUpgrade() external view returns (address wallet, uint64 iat);
20
+ /// @notice Move this vault to `newImplementation`, which the factory must have registered.
21
+ /// `proof` is verified through the factory's default verifier for `identityHash` (the
22
+ /// identity this address was derived from); the wallet it resolves to must be the caller,
23
+ /// and its issue time must not exceed block.timestamp + 15 minutes or fall below lastUpgrade.
24
+ /// It must also meet the current implementation's latestProofIat for that verifier and
25
+ /// untrackedProofIat; failed, malformed or excessively future-dated floor reads count as zero.
26
+ /// This call writes the implementation slot and lastUpgrade record, then calls
27
+ /// acceptOwnerProof through the new implementation without verifying the proof again.
28
+ /// Hook failure reverts the entire upgrade. P2IDVault's hook requires the proven wallet
29
+ /// to be the resulting cached owner, rejecting conflicting equal-time wallet proofs.
30
+ /// The proxy requires the hook selector as a canonical 32-byte ABI acknowledgement;
31
+ /// absent, malformed or incorrect acknowledgements revert the entire upgrade.
32
+ /// After the hook, the implementation must still match the target, owner(verifier)
33
+ /// must report the proven wallet, and latestProofIat(verifier) must be at least iat.
34
+ /// Nested upgradeTo calls revert while an upgrade is running.
35
+ /// @dev Getters run through delegated code and cannot establish that an implementation is safe.
36
+ /// This entry point performs no separate initialization or storage-migration call.
37
+ function upgradeTo(bytes32 identityHash, address newImplementation, bytes calldata proof) external;
38
+ }
@@ -2,8 +2,7 @@
2
2
  pragma solidity ^0.8.27;
3
3
 
4
4
  /// @title IP2IDVerifier
5
- /// @notice Use-case-agnostic identity verification: given a proof, return the wallet the proven
6
- /// identity resolves to. Optionally also verify a funding constraint.
5
+ /// @notice Resolve an identity proof to a wallet and issue time, with optional constraint evidence.
7
6
  interface IP2IDVerifier {
8
7
  /// @notice A funding constraint and the evidence that it is satisfied.
9
8
  /// @param commitment bytes32(0) = no constraint; otherwise the commitment a deposit was funded with.
@@ -15,9 +14,7 @@ interface IP2IDVerifier {
15
14
  bytes signature;
16
15
  }
17
16
 
18
- /// @param identityHash The identity the caller expects the proof to be for (e.g. the vault's
19
- /// commitment). Proofs are public once used on-chain, so the caller MUST
20
- /// pin the identity rather than trust whatever the submitted proof proves.
17
+ /// @param identityHash Expected identity commitment that the implementation must match against the proof.
21
18
  /// @param proof Opaque identity proof; the implementation defines the encoding.
22
19
  /// @param constraint Skipped when `constraint.commitment == bytes32(0)`.
23
20
  /// @return wallet Wallet associated with the proven identity (never address(0)).
@@ -30,15 +27,14 @@ interface IP2IDVerifier {
30
27
  Constraint calldata constraint
31
28
  ) external view returns (address wallet, uint64 iat);
32
29
 
33
- /// @notice Whether this verifier can ever satisfy a non-zero constraint. Vaults refuse
34
- /// constrained deposits under a verifier that returns false (or does not implement
35
- /// this), so a payer cannot lock funds behind a condition nobody can meet.
30
+ /// @notice Whether this verifier currently supports nonzero constraints. P2IDVault requires
31
+ /// a successful true response when funding a constrained deposit; this does not establish
32
+ /// that any particular commitment can be satisfied.
36
33
  function supportsConstraints() external view returns (bool);
37
34
 
38
- /// @notice Changes (increments) whenever proofs this verifier accepted before may no longer be
39
- /// trusted, e.g. a signing key was revoked. A vault caches the wallet a proof resolved
40
- /// to and pays it without re-verifying; it discards that cache when the revision moved,
41
- /// so revoking a key also revokes what was proven under it. Return a constant if
42
- /// nothing this verifier trusts can ever be revoked.
35
+ /// @notice Cache revision. Implementations must increment it when previously accepted identity
36
+ /// proofs may no longer be trusted, or return a constant if trust cannot be revoked.
37
+ /// @dev P2IDVault rejects cached-owner claims when this differs from the stored revision, and
38
+ /// rejects cached-owner claims and proof updates when the revision cannot be read.
43
39
  function revision() external view returns (uint64);
44
40
  }
@@ -2,30 +2,57 @@
2
2
  pragma solidity ^0.8.27;
3
3
 
4
4
  /// @title IP2IdVaultFactory
5
- /// @notice Deploys one P2IDVault per identity at an address anyone can derive offline from the
6
- /// identity hash, and points every vault at the current policy and default verifier.
5
+ /// @notice Vault proxy deployment, address derivation, funding and shared configuration views.
7
6
  interface IP2IdVaultFactory {
8
7
  event VaultDeployed(bytes32 indexed identityHash, address indexed vault);
8
+ event ImplementationProposed(address indexed implementation, uint64 eta);
9
+ event ImplementationProposalCancelled(address indexed implementation);
10
+ event ImplementationRegistered(address indexed implementation);
11
+ event ImplementationDeployed(address indexed implementation, bytes32 creationCodeHash);
12
+ event ImplementationDefaultProposed(address indexed implementation, bool makeDefault, uint64 eta);
13
+ event DefaultImplementationActivated(address indexed implementation);
14
+ event ImplementationRevoked(address indexed implementation);
9
15
 
10
16
  /// @notice P2ID factory interface version implemented by this contract.
11
17
  function p2idVersion() external pure returns (string memory);
12
18
 
13
- // ---- what vaults consult on every call; both change only through a timelock ----
14
- /// @notice The IP2IDPolicy deciding which verifiers are allowed and what (capped) fee applies.
19
+ // ---- shared configuration ----
20
+ /// @notice Policy address used for verifier approval, fee quotes and fee distribution.
15
21
  function policy() external view returns (address);
16
22
  /// @notice Verifier used when a payer does not choose one, and the one bare transfers are claimed through.
17
23
  function defaultVerifier() external view returns (address);
18
24
 
25
+ // ---- vault implementations ----
26
+ /// @notice The implementation every newly deployed vault proxy starts on.
27
+ function baseImplementation() external view returns (address);
28
+ /// @notice Whether the implementation is registered as an upgrade target; other upgrade checks also apply.
29
+ function isRegisteredImplementation(address implementation) external view returns (bool);
30
+
31
+ function initialImplementation() external view returns (address);
32
+ function implementationFor(bytes32 creationCodeHash) external view returns (address);
33
+ /// @notice Deploy full creation code and propose registration; makeDefault applies after the delay.
34
+ function deployVaultImplementation(bytes memory creationCode, bool makeDefault) external returns (address);
35
+ function proposeDefaultImplementation(address implementation) external;
36
+
37
+ // ---- address derivation constants ----
38
+ /// @notice Namespace value supplied to the factory constructor and passed to vault initialization.
39
+ function nsHash() external view returns (bytes32);
40
+ /// @notice Proxy creation-code hash used with this factory's address and an identityHash for CREATE2 derivation.
41
+ function initCodeHash() external pure returns (bytes32);
42
+ /// @notice Refund-window bounds passed to new vaults during initialization.
43
+ function minRefundWindow() external view returns (uint64);
44
+ function maxRefundWindow() external view returns (uint64);
45
+
19
46
  // ---- vaults ----
20
47
  /// @notice The vault address for `identityHash`, deployed or not:
21
- /// `keccak256(0xff ‖ factory ‖ identityHash ‖ keccak256(P2IDVault creationCode))`.
48
+ /// Low 160 bits of `keccak256(0xff || factory || identityHash || initCodeHash())`.
22
49
  function vaultFor(bytes32 identityHash) external view returns (address);
23
50
  function isDeployed(bytes32 identityHash) external view returns (bool);
24
51
  /// @notice Deploy the vault for `identityHash`; returns the existing one if already deployed.
25
52
  function deploy(bytes32 identityHash) external returns (address vault);
26
53
  /// @notice Deploy if needed, then fund under the default verifier on the caller's behalf
27
- /// (caller keeps the refund right). ERC-20: approve this factory once to pay any
28
- /// identity. Native coin: token = address(0), send `amount` as msg.value.
54
+ /// (caller is recorded as funder). ERC-20: approve this factory for the requested amount.
55
+ /// Native coin: token = address(0), send `amount` as msg.value.
29
56
  /// @param ref Opaque application reference forwarded to the vault's Funded event; bytes32(0) for none.
30
57
  function fund(
31
58
  bytes32 identityHash,
@@ -2,19 +2,17 @@
2
2
  pragma solidity ^0.8.27;
3
3
 
4
4
  /// @title IPviumIdentity
5
- /// @notice Verify a Pvium attestation on chain: "identity X and wallet W belong to the same
6
- /// Privy user". Every function reverts unless the proof is valid, was made from a token
7
- /// signed by one of the accepted keys, and binds exactly the values given. The return value is
8
- /// when Privy issued that token (unix seconds); freshness policy is the caller's.
5
+ /// @notice Attestation verification and signer/version views exposed by PviumIdentity.
6
+ /// verifyIdentity checks the supplied identity type, identity hash and wallet hash.
9
7
  interface IPviumIdentity {
10
8
  /// @notice Circuit version this deployment verifies. Attestations state theirs; use the matching deployment.
11
9
  function circuitVersion() external view returns (uint16);
12
- /// @notice Whether tokens signed by this P-256 key are accepted. Keys are added with 7 days' public notice.
10
+ /// @notice Whether this P-256 key belongs to the deployment's current accepted signer set.
13
11
  function isSignerKey(uint256 x, uint256 y) external view returns (bool);
14
12
 
15
13
  /// @notice Verify that the proof binds exactly this identity and this wallet.
16
- /// @dev Both are passed as hashes (P2IDHash on chain, or the SDK's identityHash), so the raw
17
- /// identity never appears in calldata.
14
+ /// @dev The expected identity and wallet are supplied as hashes. The function does not impose
15
+ /// an age limit on the attestation.
18
16
  /// @param identityType Identity type id: 0 email, 3 twitter, 5 github, … (see P2IDHash)
19
17
  /// @param identityHash P2IDHash.identityHash(identityType, value)
20
18
  /// @param walletHash P2IDHash.walletHash(wallet): an address, or a string for a non-EVM wallet
@@ -2,10 +2,10 @@
2
2
  pragma solidity ^0.8.27;
3
3
 
4
4
  /// @title P2IDHash
5
- /// @notice The identity commitment exactly as circuit/src/main.nr computes it:
5
+ /// @notice Identity commitment:
6
6
  /// sha256("p2id.identity.v1" || identityType || normalize(value)).
7
- /// `normalize` ASCII-lowercases every type except phone and wallet, and lowercases
8
- /// `0x…` (EVM, hex) wallet addresses; base58 (Solana) addresses are untouched.
7
+ /// Normalization ASCII-lowercases non-phone, non-wallet values. Wallet values are
8
+ /// lowercased only when they start with lowercase `0x`; other wallet strings are unchanged.
9
9
  library P2IDHash {
10
10
  bytes internal constant PREFIX = "p2id.identity.v1";
11
11
  uint8 internal constant PHONE = 1;
@@ -27,7 +27,7 @@ library P2IDHash {
27
27
  return sha256(abi.encodePacked(PREFIX, WALLET, toLowerHex(wallet)));
28
28
  }
29
29
 
30
- /// @notice Commitment for a wallet on any chain, given as Privy stores it (e.g. base58).
30
+ /// @notice Wallet-string commitment using type 12, with identityHash's normalization and length checks.
31
31
  function walletHash(string memory wallet) internal pure returns (bytes32) {
32
32
  return identityHash(WALLET, bytes(wallet));
33
33
  }
@@ -48,7 +48,7 @@ library P2IDHash {
48
48
  return out;
49
49
  }
50
50
 
51
- /// @dev "0x" + 40 lowercase hex chars, the form the circuit hashes an EVM address in.
51
+ /// @dev Encode an address as "0x" followed by 40 lowercase hexadecimal characters.
52
52
  function toLowerHex(address a) internal pure returns (bytes memory out) {
53
53
  bytes16 digits = "0123456789abcdef";
54
54
  out = new bytes(42);
@@ -5,10 +5,10 @@ exports.P2ID_SCHEME = exports.P2ID_SCHEMES = void 0;
5
5
  exports.P2ID_SCHEMES = {
6
6
  "pvium.vault.v1": {
7
7
  "identityDomain": "p2id.identity.v1",
8
- "vaultInitCodeHash": "0x59c2f7f1e9725340e25a45b122fae7d1e9cbd9c7b00dba6215ebaa67cfb691ef",
8
+ "vaultInitCodeHash": "0x4c76d638c6e156d8a95617576f1dcac6189257696607110346ba4f5fe2932ca6",
9
9
  "factories": {
10
10
  "production": null,
11
- "sandbox": "0xd1DEa4e8911955DAD961B643AFdE6Ab34d9Bd371"
11
+ "sandbox": "0x5d173035d5Ae0b67a521FbFE72bDe43C96a0080A"
12
12
  }
13
13
  }
14
14
  };
@@ -18,10 +18,10 @@ export interface P2IDScheme {
18
18
  export declare const P2ID_SCHEMES: {
19
19
  readonly "pvium.vault.v1": {
20
20
  readonly identityDomain: "p2id.identity.v1";
21
- readonly vaultInitCodeHash: "0x59c2f7f1e9725340e25a45b122fae7d1e9cbd9c7b00dba6215ebaa67cfb691ef";
21
+ readonly vaultInitCodeHash: "0x4c76d638c6e156d8a95617576f1dcac6189257696607110346ba4f5fe2932ca6";
22
22
  readonly factories: {
23
23
  readonly production: null;
24
- readonly sandbox: "0xd1DEa4e8911955DAD961B643AFdE6Ab34d9Bd371";
24
+ readonly sandbox: "0x5d173035d5Ae0b67a521FbFE72bDe43C96a0080A";
25
25
  };
26
26
  };
27
27
  };
@@ -2,10 +2,10 @@
2
2
  export const P2ID_SCHEMES = {
3
3
  "pvium.vault.v1": {
4
4
  "identityDomain": "p2id.identity.v1",
5
- "vaultInitCodeHash": "0x59c2f7f1e9725340e25a45b122fae7d1e9cbd9c7b00dba6215ebaa67cfb691ef",
5
+ "vaultInitCodeHash": "0x4c76d638c6e156d8a95617576f1dcac6189257696607110346ba4f5fe2932ca6",
6
6
  "factories": {
7
7
  "production": null,
8
- "sandbox": "0xd1DEa4e8911955DAD961B643AFdE6Ab34d9Bd371"
8
+ "sandbox": "0x5d173035d5Ae0b67a521FbFE72bDe43C96a0080A"
9
9
  }
10
10
  }
11
11
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pvium/p2id-core",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "P2ID protocol core: identity types and hashing, chain-agnostic P2ID address derivation, and the Solidity sources",
5
5
  "license": "MIT",
6
6
  "type": "module",