@pvium/p2id-core 0.1.0
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.
- package/README.md +239 -0
- package/contracts/P2IDVault.sol +736 -0
- package/contracts/PviumIdentity.sol +268 -0
- package/contracts/PviumP2IDPolicy.sol +78 -0
- package/contracts/PviumP2IdVaultFactory.sol +327 -0
- package/contracts/PviumVerifier.sol +165 -0
- package/contracts/PviumZKVerifier.sol +2465 -0
- package/contracts/interfaces/IP2IDPolicy.sol +39 -0
- package/contracts/interfaces/IP2IDVault.sol +112 -0
- package/contracts/interfaces/IP2IDVerifier.sol +44 -0
- package/contracts/interfaces/IP2IdVaultFactory.sol +49 -0
- package/contracts/interfaces/IPviumIdentity.sol +28 -0
- package/contracts/lib/P2IDHash.sol +63 -0
- package/dist/cjs/identity.js +68 -0
- package/dist/cjs/identityNames.js +49 -0
- package/dist/cjs/index.js +24 -0
- package/dist/cjs/p2id.js +91 -0
- package/dist/cjs/p2idConstants.js +16 -0
- package/dist/cjs/package.json +3 -0
- package/dist/esm/identity.d.ts +38 -0
- package/dist/esm/identity.js +61 -0
- package/dist/esm/identityNames.d.ts +43 -0
- package/dist/esm/identityNames.js +44 -0
- package/dist/esm/index.d.ts +5 -0
- package/dist/esm/index.js +5 -0
- package/dist/esm/p2id.d.ts +54 -0
- package/dist/esm/p2id.js +82 -0
- package/dist/esm/p2idConstants.d.ts +30 -0
- package/dist/esm/p2idConstants.js +13 -0
- package/dist/esm/package.json +3 -0
- package/dist/identity.d.ts +38 -0
- package/dist/identity.js +61 -0
- package/dist/identityNames.d.ts +43 -0
- package/dist/identityNames.js +44 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +5 -0
- package/dist/p2id.d.ts +54 -0
- package/dist/p2id.js +82 -0
- package/dist/p2idConstants.d.ts +38 -0
- package/dist/p2idConstants.js +21 -0
- package/package.json +44 -0
package/README.md
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
# @pvium/p2id-core
|
|
2
|
+
|
|
3
|
+
Derive deterministic EVM vault addresses from email addresses, social handles and other supported
|
|
4
|
+
identities. Addresses can receive funds before the recipient registers or the vault is deployed.
|
|
5
|
+
Claims pay the wallet bound by an accepted identity proof.
|
|
6
|
+
|
|
7
|
+
The package includes identity hashing, address derivation and Solidity sources. For off-chain
|
|
8
|
+
proof verification, use
|
|
9
|
+
[`@pvium/p2id-verifier`](https://www.npmjs.com/package/@pvium/p2id-verifier).
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```sh
|
|
14
|
+
yarn add @pvium/p2id-core
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Requires Node.js 20+ or a browser with Web Crypto support.
|
|
18
|
+
|
|
19
|
+
## Quick start
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
import { p2idAddress, IdentityType } from '@pvium/p2id-core';
|
|
23
|
+
|
|
24
|
+
const to = p2idAddress({ identityType: IdentityType.Email, identityValue: 'you@example.com' });
|
|
25
|
+
// Checksummed vault address for native coin or supported ERC-20 transfers.
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Transfer funds to the derived address on a chain with the matching factory deployment.
|
|
29
|
+
Email addresses are case-insensitive: `You@Example.com` derives the same address.
|
|
30
|
+
|
|
31
|
+
Production is the default environment. Use `sandbox` on testnets or pass a custom factory address:
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
p2idAddress({ identityType: IdentityType.X, identityValue: 'jack', environment: 'sandbox' });
|
|
35
|
+
p2idAddress({ identityType: IdentityType.Github, identityValue: 'octocat', factory: '0xYourFactory…' });
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Derivation requires a recorded factory for the selected environment or an explicit `factory`.
|
|
39
|
+
|
|
40
|
+
## Pay an identity
|
|
41
|
+
|
|
42
|
+
Using [ethers](https://docs.ethers.org) v6:
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
import { ethers } from 'ethers';
|
|
46
|
+
import { p2idAddress, IdentityType } from '@pvium/p2id-core';
|
|
47
|
+
|
|
48
|
+
const signer = new ethers.Wallet(process.env.PRIVATE_KEY!, new ethers.JsonRpcProvider(process.env.RPC_URL));
|
|
49
|
+
const to = p2idAddress({ identityType: IdentityType.Telegram, identityValue: 'durov' });
|
|
50
|
+
|
|
51
|
+
// the native coin (BNB on BNB Chain, ETH on Base)
|
|
52
|
+
await signer.sendTransaction({ to, value: ethers.parseEther('0.1') });
|
|
53
|
+
|
|
54
|
+
// an ERC-20
|
|
55
|
+
const USDC = '0x…'; // the token's contract address on the chain you are paying on
|
|
56
|
+
const usdc = new ethers.Contract(USDC, ['function transfer(address to, uint256 amount) returns (bool)'], signer);
|
|
57
|
+
await usdc.transfer(to, 25_000_000n); // 25 USDC (6 decimals)
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Direct transfers have no refund path. Funds can be claimed after vault deployment and identity
|
|
61
|
+
verification.
|
|
62
|
+
|
|
63
|
+
### Refundable deposits
|
|
64
|
+
|
|
65
|
+
Use `factory.fund` to record a deposit with a refund window. The funder can refund an unclaimed
|
|
66
|
+
deposit after that window elapses.
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
import { ethers } from 'ethers';
|
|
70
|
+
import { identityHash, p2idScheme, IdentityType } from '@pvium/p2id-core';
|
|
71
|
+
|
|
72
|
+
const signer = new ethers.Wallet(process.env.PRIVATE_KEY!, new ethers.JsonRpcProvider(process.env.RPC_URL));
|
|
73
|
+
const factory = new ethers.Contract(
|
|
74
|
+
p2idScheme().factories.production!, // or .sandbox on testnets
|
|
75
|
+
['function fund(bytes32 identityHash, address token, uint256 amount, bytes32 constraint, uint64 refundWindow, bytes32 ref) payable returns (address vault, uint256 depositId)'],
|
|
76
|
+
signer,
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
const id = identityHash(IdentityType.Email, 'you@example.com');
|
|
80
|
+
const NO_CONSTRAINT = ethers.ZeroHash; // no additional claim requirement
|
|
81
|
+
const WEEK = 7 * 24 * 3600; // refund window in seconds
|
|
82
|
+
const ref = ethers.id('invoice-42'); // application-defined bytes32; ethers.ZeroHash for none
|
|
83
|
+
|
|
84
|
+
// Native coin: use the zero address for `token` and send `amount` as transaction value.
|
|
85
|
+
const amount = ethers.parseEther('0.1');
|
|
86
|
+
await factory.fund(id, ethers.ZeroAddress, amount, NO_CONSTRAINT, WEEK, ref, { value: amount });
|
|
87
|
+
|
|
88
|
+
// an ERC-20: approve the factory, then fund
|
|
89
|
+
const USDC = '0x…'; // the token's contract address
|
|
90
|
+
const usdc = new ethers.Contract(USDC, ['function approve(address spender, uint256 amount) returns (bool)'], signer);
|
|
91
|
+
await usdc.approve(await factory.getAddress(), 25_000_000n);
|
|
92
|
+
await factory.fund(id, USDC, 25_000_000n, NO_CONSTRAINT, WEEK, ref);
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
`fund` accepts the same arguments for native coin and ERC-20 deposits:
|
|
96
|
+
|
|
97
|
+
| Argument | Meaning |
|
|
98
|
+
| --- | --- |
|
|
99
|
+
| `identityHash` | Recipient commitment from `identityHash(type, value)` |
|
|
100
|
+
| `token` | ERC-20 contract address, or the zero address for native coin |
|
|
101
|
+
| `amount` | Amount in the asset's smallest unit |
|
|
102
|
+
| `constraint` | A 32-byte commitment to an additional claim requirement; zero for none |
|
|
103
|
+
| `refundWindow` | Seconds before an unclaimed deposit becomes refundable |
|
|
104
|
+
| `ref` | Application-defined 32-byte reference, emitted in `Funded`; zero for none |
|
|
105
|
+
|
|
106
|
+
The window must fall within the factory's configured limits. After it elapses, the funder calls
|
|
107
|
+
`refund(depositId)` on the vault. Contract interfaces are included; see [Solidity](#solidity).
|
|
108
|
+
|
|
109
|
+
`ref` is event-only metadata, not a claim constraint or an idempotency key. Repeated references
|
|
110
|
+
are allowed. Match a `Funded` event to an application record using its reference and identify
|
|
111
|
+
the deposit by chain, vault address and `depositId`. The contract does not store the reference
|
|
112
|
+
or interpret its contents. The `memo` URI parameter remains application text, not an onchain
|
|
113
|
+
funding argument.
|
|
114
|
+
|
|
115
|
+
These funding signatures apply to `pvium.vault.v1`. Its factory addresses must be configured
|
|
116
|
+
before use.
|
|
117
|
+
|
|
118
|
+
## Read balances
|
|
119
|
+
|
|
120
|
+
Read native coin and ERC-20 balances at the derived address:
|
|
121
|
+
|
|
122
|
+
```ts
|
|
123
|
+
const provider = new ethers.JsonRpcProvider(process.env.RPC_URL);
|
|
124
|
+
await provider.getBalance(to); // native coin
|
|
125
|
+
|
|
126
|
+
const token = new ethers.Contract(USDC, ['function balanceOf(address) view returns (uint256)'], provider);
|
|
127
|
+
await token.balanceOf(to); // ERC-20
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Identity types
|
|
131
|
+
|
|
132
|
+
Identity parameters accept an `IdentityType` enum value or a P2ID type name such as `'email'`,
|
|
133
|
+
`'x'` or `'github'` (`'twitter'` is accepted as an alias of `'x'`). P2ID core is agnostic of any
|
|
134
|
+
identity provider: it does not accept provider-specific account types (e.g. Privy's
|
|
135
|
+
`'twitter_oauth'`) — callers map those to P2ID names themselves. `resolveIdentityType` converts
|
|
136
|
+
these to the numeric IDs used in identity hashes, and `identityTypeName` gives the P2ID name of an
|
|
137
|
+
ID. The type table is append-only.
|
|
138
|
+
|
|
139
|
+
| `IdentityType.` | Id | Value | Example | Lowercased |
|
|
140
|
+
| --- | ---: | --- | --- | :---: |
|
|
141
|
+
| `Email` | 0 | address | `you@example.com` | yes |
|
|
142
|
+
| `Phone` | 1 | E.164 number | `+15551234567` | no |
|
|
143
|
+
| `Google` | 2 | email | `you@gmail.com` | yes |
|
|
144
|
+
| `X` (alias `Twitter`) | 3 | username, no `@` | `jack` | yes |
|
|
145
|
+
| `Discord` | 4 | username | `wumpus` | yes |
|
|
146
|
+
| `Github` | 5 | username | `octocat` | yes |
|
|
147
|
+
| `Linkedin` | 6 | email | `you@example.com` | yes |
|
|
148
|
+
| `Apple` | 7 | email | `you@icloud.com` | yes |
|
|
149
|
+
| `Telegram` | 8 | username | `durov` | yes |
|
|
150
|
+
| `Tiktok` | 9 | username | `charlidamelio` | yes |
|
|
151
|
+
| `Instagram` | 10 | username | `instagram` | yes |
|
|
152
|
+
| `Farcaster` | 11 | username | `dwr` | yes |
|
|
153
|
+
| `Wallet` | 12 | address | `0xA01b…0f98` or base58 | only `0x…` |
|
|
154
|
+
|
|
155
|
+
The same email produces different addresses under `Email`, `Google`, `Linkedin` and `Apple`.
|
|
156
|
+
Select the type the recipient will use to authenticate.
|
|
157
|
+
|
|
158
|
+
Handle reassignment can allow a new holder to claim remaining funds.
|
|
159
|
+
|
|
160
|
+
## Address derivation
|
|
161
|
+
|
|
162
|
+
```
|
|
163
|
+
identityHash = sha256( "p2id.identity.v1" ‖ byte(typeId) ‖ normalize(value) )
|
|
164
|
+
p2id = keccak256( 0xff ‖ factory ‖ identityHash ‖ vaultInitCodeHash )[12..]
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
```ts
|
|
168
|
+
import { identityHash, p2idAddressForHash, p2idScheme, P2ID_SCHEME, IdentityType } from '@pvium/p2id-core';
|
|
169
|
+
|
|
170
|
+
const hash = identityHash(IdentityType.Email, 'you@example.com'); // identity commitment and vault salt
|
|
171
|
+
p2idAddressForHash(hash, { environment: 'sandbox' }); // same result as p2idAddress(...)
|
|
172
|
+
|
|
173
|
+
P2ID_SCHEME; // 'pvium.vault.v1', the current address scheme
|
|
174
|
+
p2idScheme(); // { identityDomain, vaultInitCodeHash, factories: { production, sandbox } }
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
The formula excludes the chain ID. Addresses match across chains with standard CREATE2 semantics
|
|
178
|
+
when the factory address and vault creation bytecode match. `production` and `sandbox` use
|
|
179
|
+
separate factories.
|
|
180
|
+
|
|
181
|
+
An address scheme fixes the identity domain, factory addresses and vault creation-code hash.
|
|
182
|
+
Factory or vault bytecode changes require a new scheme. To derive an address under a previous
|
|
183
|
+
scheme, pass its name, for example `scheme: 'pvium.vault.v1'`.
|
|
184
|
+
See [P2ID.md](https://github.com/pvium/zkid/blob/main/P2ID.md) for the protocol specification.
|
|
185
|
+
|
|
186
|
+
## Solidity
|
|
187
|
+
|
|
188
|
+
The package includes Solidity interfaces and contract sources for on-chain identity verification.
|
|
189
|
+
Compute `identityHash` off chain and pass it to `verifyIdentity` to keep the raw identity
|
|
190
|
+
value out of the call's arguments:
|
|
191
|
+
|
|
192
|
+
```solidity
|
|
193
|
+
import {IPviumIdentity} from "@pvium/p2id-core/contracts/interfaces/IPviumIdentity.sol";
|
|
194
|
+
import {P2IDHash} from "@pvium/p2id-core/contracts/lib/P2IDHash.sol";
|
|
195
|
+
|
|
196
|
+
contract PayByEmail {
|
|
197
|
+
IPviumIdentity public immutable pvium;
|
|
198
|
+
|
|
199
|
+
constructor(IPviumIdentity _pvium) {
|
|
200
|
+
pvium = _pvium;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/// @param identityHash identityHash(IdentityType.Email, "you@example.com") from the SDK
|
|
204
|
+
function pay(bytes calldata proof, bytes32[] calldata inputs, bytes32 identityHash, address payable wallet) external payable {
|
|
205
|
+
uint64 issuedAt = pvium.verifyIdentity(proof, inputs, 0 /* IdentityType.Email */, identityHash, P2IDHash.walletHash(wallet));
|
|
206
|
+
require(block.timestamp - issuedAt < 30 days, "attestation too old");
|
|
207
|
+
(bool ok, ) = wallet.call{value: msg.value}("");
|
|
208
|
+
require(ok, "payment failed");
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
For a base64-encoded attestation proof:
|
|
214
|
+
|
|
215
|
+
```ts
|
|
216
|
+
const hash = identityHash(IdentityType.Email, 'you@example.com');
|
|
217
|
+
const proof = ethers.decodeBase64(attestation.proof);
|
|
218
|
+
await payByEmail.pay(proof, publicInputFields, hash, attestation.wallet, { value });
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
`verifyIdentity` checks the identity type, identity hash and wallet hash against the proof.
|
|
222
|
+
It reverts on failure and returns the attestation's issue time on success. Computing the wallet
|
|
223
|
+
hash on chain binds the payout address to the proof. Verification costs approximately 4.4M gas
|
|
224
|
+
with the current circuit.
|
|
225
|
+
|
|
226
|
+
For a wallet on another chain, such as a base58 Solana address, pass
|
|
227
|
+
`P2IDHash.walletHash("…")` with the wallet as a string.
|
|
228
|
+
|
|
229
|
+
## Exports
|
|
230
|
+
|
|
231
|
+
| Export | Purpose |
|
|
232
|
+
| --- | --- |
|
|
233
|
+
| `p2idAddress(input)` | Derive a vault address from `{ identityType, identityValue, environment?, scheme?, factory? }` |
|
|
234
|
+
| `p2idAddressForHash(hash, opts?)` | Derive a vault address from an identity hash |
|
|
235
|
+
| `identityHash(type, value, scheme?)` | Compute the identity commitment used as the vault salt |
|
|
236
|
+
| `P2ID_SCHEME`, `P2ID_SCHEMES`, `p2idScheme(name?)` | Read address-scheme constants |
|
|
237
|
+
| `IdentityType`, `IdentityTypeName`, `IDENTITY_TYPE_BY_NAME`, `resolveIdentityType` | Identity type IDs and name mappings |
|
|
238
|
+
| `normalizeIdentityValue`, `isCaseInsensitive`, `HASH_PREFIX` | Identity normalisation rules and hash prefix |
|
|
239
|
+
| `checksumAddress`, `toHex` | Address checksum and hexadecimal encoding |
|