@vlayer/sdk 0.1.0-nightly-20250127-5ee6ca8 → 0.1.0-nightly-20250128-0a32cfc

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.
Files changed (50) hide show
  1. package/package.json +2 -1
  2. package/src/api/email/dnsResolver.test.ts +19 -0
  3. package/src/api/email/dnsResolver.ts +87 -0
  4. package/src/api/email/parseEmail.test.ts +133 -0
  5. package/src/api/email/parseEmail.ts +55 -0
  6. package/src/api/email/preverify.test.ts +201 -0
  7. package/src/api/email/preverify.ts +70 -0
  8. package/src/api/email/testdata/test_email.txt +21 -0
  9. package/src/api/email/testdata/test_email_multiple_dkims.txt +28 -0
  10. package/src/api/email/testdata/test_email_subdomain.txt +21 -0
  11. package/src/api/email/testdata/test_email_unknown_domain.txt +21 -0
  12. package/src/api/lib/client.test.ts +261 -0
  13. package/src/api/lib/client.ts +191 -0
  14. package/src/api/lib/errors.ts +19 -0
  15. package/src/api/lib/types/ethereum.ts +45 -0
  16. package/src/api/lib/types/index.ts +3 -0
  17. package/src/api/lib/types/viem.ts +26 -0
  18. package/src/api/lib/types/vlayer.ts +156 -0
  19. package/src/api/lib/types/webProofProvider.ts +68 -0
  20. package/src/api/prover.ts +120 -0
  21. package/src/api/utils/prefixAllButNthSubstring.test.ts +24 -0
  22. package/src/api/utils/prefixAllButNthSubstring.ts +13 -0
  23. package/src/api/utils/versions.test.ts +52 -0
  24. package/src/api/utils/versions.ts +31 -0
  25. package/src/api/v_call.ts +58 -0
  26. package/src/api/v_getProofReceipt.ts +48 -0
  27. package/src/api/v_versions.ts +68 -0
  28. package/src/api/webProof/createWebProofRequest.ts +15 -0
  29. package/src/api/webProof/index.ts +3 -0
  30. package/src/api/webProof/providers/extension.test.ts +122 -0
  31. package/src/api/webProof/providers/extension.ts +197 -0
  32. package/src/api/webProof/providers/index.ts +1 -0
  33. package/src/api/webProof/steps/expectUrl.ts +12 -0
  34. package/src/api/webProof/steps/index.ts +11 -0
  35. package/src/api/webProof/steps/notarize.ts +20 -0
  36. package/src/api/webProof/steps/startPage.ts +12 -0
  37. package/src/config/createContext.ts +69 -0
  38. package/src/config/deploy.ts +108 -0
  39. package/src/config/getChainConfirmations.ts +6 -0
  40. package/src/config/getConfig.ts +71 -0
  41. package/src/config/index.ts +5 -0
  42. package/src/config/types.ts +26 -0
  43. package/src/config/writeEnvVariables.ts +28 -0
  44. package/src/index.ts +7 -0
  45. package/src/testHelpers/readFile.ts +3 -0
  46. package/src/web-proof-commons/index.ts +3 -0
  47. package/src/web-proof-commons/types/message.ts +176 -0
  48. package/src/web-proof-commons/types/redaction.test.ts +97 -0
  49. package/src/web-proof-commons/types/redaction.ts +201 -0
  50. package/src/web-proof-commons/utils.ts +11 -0
@@ -0,0 +1,197 @@
1
+ import {
2
+ type WebProofProvider,
3
+ type WebProofProviderSetup,
4
+ type WebProofRequestInput,
5
+ } from "../../lib/types/webProofProvider";
6
+
7
+ import {
8
+ EXTENSION_STEP,
9
+ ExtensionAction,
10
+ type ExtensionMessage,
11
+ ExtensionMessageType,
12
+ type WebProofStep,
13
+ type PresentationJSON,
14
+ ZkProvingStatus,
15
+ assertUrl,
16
+ assertUrlPattern,
17
+ type RedactionConfig,
18
+ RedactionItemsArray,
19
+ type MessageToExtension,
20
+ } from "../../../web-proof-commons";
21
+
22
+ import debug from "debug";
23
+
24
+ const log = debug("vlayer:WebProof:provider");
25
+
26
+ const EXTENSION_ID = "jbchhcgphfokabmfacnkafoeeeppjmpl";
27
+
28
+ declare let chrome: {
29
+ runtime: {
30
+ sendMessage: (
31
+ extensionId: string | undefined,
32
+ message: MessageToExtension,
33
+ ) => void;
34
+ connect: (extensionId: string) => {
35
+ onMessage: {
36
+ addListener: (message: unknown) => void;
37
+ };
38
+ postMessage: (message: MessageToExtension) => void;
39
+ onDisconnect: {
40
+ addListener: (callback: () => void) => void;
41
+ };
42
+ };
43
+ };
44
+ };
45
+
46
+ class ExtensionWebProofProvider implements WebProofProvider {
47
+ private port: ReturnType<typeof chrome.runtime.connect> | null = null;
48
+
49
+ private listeners: Partial<
50
+ Record<
51
+ ExtensionMessageType,
52
+ ((
53
+ args: Extract<ExtensionMessage, { type: ExtensionMessageType }>,
54
+ ) => void)[]
55
+ >
56
+ > = {};
57
+
58
+ constructor(
59
+ private notaryUrl: string,
60
+ private wsProxyUrl: string,
61
+ ) {}
62
+
63
+ public notifyZkProvingStatus(status: ZkProvingStatus) {
64
+ if (typeof chrome !== "undefined") {
65
+ // Chrome does not provide reliable api to check if given extension is installed
66
+ // what we could do is to use management api but
67
+ // 1) this will need to provided extra permission
68
+ // 2) still is not reliable because this api becomes defined when first extension that uses it is installed
69
+ // so still will need to try catch
70
+ try {
71
+ chrome.runtime.sendMessage(EXTENSION_ID, {
72
+ action: ExtensionAction.NotifyZkProvingStatus,
73
+ payload: { status },
74
+ });
75
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
76
+ } catch (e) {
77
+ log("Cant send message", "look that extension is not installed ");
78
+ }
79
+ }
80
+ }
81
+
82
+ private connectToExtension() {
83
+ if (!this.port) {
84
+ this.port = chrome.runtime.connect(EXTENSION_ID);
85
+ this.port.onDisconnect.addListener(() => {
86
+ this.port = null;
87
+ this.connectToExtension();
88
+ });
89
+ this.port.onMessage.addListener((message: ExtensionMessage) => {
90
+ if (message.type === ExtensionMessageType.ProofDone) {
91
+ this.listeners[ExtensionMessageType.ProofDone]?.forEach((cb) => {
92
+ cb(message);
93
+ });
94
+ }
95
+ if (message.type === ExtensionMessageType.ProofError) {
96
+ this.listeners[ExtensionMessageType.ProofError]?.forEach((cb) => {
97
+ cb(message);
98
+ });
99
+ }
100
+ });
101
+ }
102
+ return this.port;
103
+ }
104
+
105
+ public addEventListeners<T extends ExtensionMessageType>(
106
+ messageType: T,
107
+ listener: (args: Extract<ExtensionMessage, { type: T }>) => void,
108
+ ) {
109
+ this.connectToExtension();
110
+ if (!this.listeners[messageType]) {
111
+ this.listeners[messageType] = [];
112
+ }
113
+ this.listeners[messageType].push(
114
+ listener as (args: ExtensionMessage) => void,
115
+ );
116
+ }
117
+
118
+ public requestWebProof(webProofRequest: WebProofRequestInput) {
119
+ validateWebProofRequest(webProofRequest);
120
+ this.connectToExtension().postMessage({
121
+ action: ExtensionAction.RequestWebProof,
122
+ payload: {
123
+ notaryUrl: this.notaryUrl,
124
+ wsProxyUrl: this.wsProxyUrl,
125
+ logoUrl: webProofRequest.logoUrl,
126
+ steps: webProofRequest.steps,
127
+ },
128
+ });
129
+ }
130
+
131
+ public async getWebProof(webProofRequest: WebProofRequestInput): Promise<{
132
+ presentationJSON: PresentationJSON;
133
+ decodedTranscript: {
134
+ sent: string;
135
+ recv: string;
136
+ };
137
+ }> {
138
+ return new Promise<{
139
+ presentationJSON: PresentationJSON;
140
+ decodedTranscript: {
141
+ sent: string;
142
+ recv: string;
143
+ };
144
+ }>((resolve, reject) => {
145
+ chrome.runtime.sendMessage(EXTENSION_ID, {
146
+ action: ExtensionAction.RequestWebProof,
147
+ payload: {
148
+ notaryUrl: this.notaryUrl,
149
+ wsProxyUrl: this.wsProxyUrl,
150
+ logoUrl: webProofRequest.logoUrl,
151
+ steps: webProofRequest.steps,
152
+ },
153
+ });
154
+
155
+ this.connectToExtension().onMessage.addListener(
156
+ (message: ExtensionMessage) => {
157
+ if (message.type === ExtensionMessageType.ProofDone) {
158
+ resolve(message.payload);
159
+ }
160
+ if (message.type === ExtensionMessageType.ProofError) {
161
+ reject(new Error(message.payload.error));
162
+ }
163
+ },
164
+ );
165
+ });
166
+ }
167
+ }
168
+
169
+ const validateSteps = (steps: WebProofStep[]) => {
170
+ steps.forEach((step) => {
171
+ if (step.step === EXTENSION_STEP.startPage) {
172
+ assertUrl(step.url);
173
+ } else {
174
+ assertUrlPattern(step.url);
175
+ }
176
+ if (step.step === EXTENSION_STEP.notarize) {
177
+ validateRedaction(step.redact ?? []);
178
+ }
179
+ });
180
+ };
181
+
182
+ const validateRedaction = (redaction: RedactionConfig) => {
183
+ RedactionItemsArray.parse(redaction);
184
+ };
185
+
186
+ export const validateWebProofRequest = (
187
+ webProofRequest: WebProofRequestInput,
188
+ ) => {
189
+ validateSteps(webProofRequest.steps);
190
+ };
191
+
192
+ export const createExtensionWebProofProvider = ({
193
+ notaryUrl = "https://notary.pse.dev/v0.1.0-alpha.7",
194
+ wsProxyUrl = "wss://notary.pse.dev/proxy",
195
+ }: WebProofProviderSetup = {}): WebProofProvider => {
196
+ return new ExtensionWebProofProvider(notaryUrl, wsProxyUrl);
197
+ };
@@ -0,0 +1 @@
1
+ export * from "./extension";
@@ -0,0 +1,12 @@
1
+ import {
2
+ EXTENSION_STEP,
3
+ type WebProofStepExpectUrl,
4
+ } from "../../../web-proof-commons";
5
+
6
+ export const expectUrl = (url: string, label: string) => {
7
+ return {
8
+ url,
9
+ label,
10
+ step: EXTENSION_STEP.expectUrl,
11
+ } as WebProofStepExpectUrl;
12
+ };
@@ -0,0 +1,11 @@
1
+ import { expectUrl } from "./expectUrl";
2
+ import { startPage } from "./startPage";
3
+ import { notarize } from "./notarize";
4
+
5
+ const steps = {
6
+ expectUrl,
7
+ startPage,
8
+ notarize,
9
+ };
10
+
11
+ export { expectUrl, startPage, notarize, steps };
@@ -0,0 +1,20 @@
1
+ import {
2
+ EXTENSION_STEP,
3
+ type WebProofStepNotarize,
4
+ type RedactionConfig,
5
+ } from "../../../web-proof-commons";
6
+
7
+ export const notarize = (
8
+ url: string,
9
+ method: string = "GET",
10
+ label: string,
11
+ redact?: RedactionConfig,
12
+ ) => {
13
+ return {
14
+ url,
15
+ method,
16
+ label,
17
+ redact: redact ?? [],
18
+ step: EXTENSION_STEP.notarize,
19
+ } as WebProofStepNotarize;
20
+ };
@@ -0,0 +1,12 @@
1
+ import {
2
+ EXTENSION_STEP,
3
+ type WebProofStepStartPage,
4
+ } from "../../../web-proof-commons";
5
+
6
+ export const startPage = (url: string, label: string) => {
7
+ return {
8
+ url,
9
+ label,
10
+ step: EXTENSION_STEP.startPage,
11
+ } as WebProofStepStartPage;
12
+ };
@@ -0,0 +1,69 @@
1
+ import {
2
+ type Chain,
3
+ createWalletClient,
4
+ http,
5
+ publicActions,
6
+ type CustomTransport,
7
+ custom,
8
+ type PrivateKeyAccount,
9
+ } from "viem";
10
+ import { privateKeyToAccount } from "viem/accounts";
11
+ import { getChainConfirmations } from "./getChainConfirmations";
12
+ import * as chains from "viem/chains";
13
+ import type { EnvConfig, VlayerContextConfig } from "./types";
14
+
15
+ const getChainSpecs = (chainName: string): Chain => {
16
+ try {
17
+ return chains[chainName as keyof typeof chains] as Chain;
18
+ } catch {
19
+ throw Error(`Cannot import ${chainName} from viem/chains`);
20
+ }
21
+ };
22
+
23
+ export const customTransport = custom;
24
+
25
+ const createEthClient = (
26
+ chain: Chain,
27
+ jsonRpcUrl: string,
28
+ transport?: CustomTransport,
29
+ ) =>
30
+ createWalletClient({
31
+ chain,
32
+ transport: transport || http(jsonRpcUrl),
33
+ }).extend(publicActions);
34
+
35
+ export function createContext(config: EnvConfig): {
36
+ chain: Chain;
37
+ account: ReturnType<typeof privateKeyToAccount>;
38
+ jsonRpcUrl: string;
39
+ ethClient: ReturnType<typeof createEthClient>;
40
+ confirmations: number;
41
+ } & EnvConfig;
42
+
43
+ export function createContext(
44
+ config: VlayerContextConfig,
45
+ transport?: CustomTransport,
46
+ ): {
47
+ chain: Chain;
48
+ jsonRpcUrl: string;
49
+ account: PrivateKeyAccount;
50
+ ethClient: ReturnType<typeof createEthClient>;
51
+ confirmations: number;
52
+ } & VlayerContextConfig;
53
+
54
+ export function createContext(
55
+ config: VlayerContextConfig | EnvConfig,
56
+ transport?: CustomTransport,
57
+ ) {
58
+ const chain = getChainSpecs(config.chainName);
59
+ const jsonRpcUrl = config.jsonRpcUrl ?? chain.rpcUrls.default.http[0];
60
+
61
+ return {
62
+ ...config,
63
+ chain,
64
+ account: config.privateKey && privateKeyToAccount(config.privateKey),
65
+ jsonRpcUrl,
66
+ ethClient: createEthClient(chain, jsonRpcUrl, transport),
67
+ confirmations: getChainConfirmations(config.chainName),
68
+ };
69
+ }
@@ -0,0 +1,108 @@
1
+ import { getConfig } from "./getConfig";
2
+ import { createContext } from "./createContext";
3
+ import { type ContractArg, type ContractSpec } from "types/ethereum";
4
+ import { type Address } from "viem";
5
+ import { getChainConfirmations } from "./getChainConfirmations";
6
+ import debug from "debug";
7
+
8
+ const log = debug("vlayer:prover");
9
+
10
+ export const waitForContractDeploy = async ({
11
+ hash,
12
+ }: {
13
+ hash: `0x${string}`;
14
+ }): Promise<Address> => {
15
+ const { ethClient: client } = createContext(getConfig());
16
+ const receipt = await client.waitForTransactionReceipt({
17
+ hash,
18
+ confirmations: getChainConfirmations(client.chain?.name),
19
+ retryCount: 120,
20
+ retryDelay: 1000,
21
+ });
22
+
23
+ if (!receipt.contractAddress || receipt.status !== "success") {
24
+ throw new Error(
25
+ `Cannot get contract address from receipt: ${receipt.status}`,
26
+ );
27
+ }
28
+
29
+ return receipt.contractAddress;
30
+ };
31
+
32
+ export const waitForTransactionReceipt = async ({
33
+ hash,
34
+ }: {
35
+ hash: `0x${string}`;
36
+ }) => {
37
+ const { ethClient } = createContext(getConfig());
38
+ return ethClient.waitForTransactionReceipt({
39
+ hash,
40
+ confirmations: getChainConfirmations(ethClient.chain?.name),
41
+ retryCount: 120,
42
+ retryDelay: 1000,
43
+ });
44
+ };
45
+
46
+ export const deployProver = async ({
47
+ proverSpec,
48
+ proverArgs,
49
+ }: {
50
+ proverSpec: ContractSpec;
51
+ proverArgs?: ContractArg[];
52
+ }) => {
53
+ const config = getConfig();
54
+ const { ethClient, account, chain } = createContext(config);
55
+
56
+ const proverHash = await ethClient.deployContract({
57
+ chain,
58
+ account,
59
+ args: proverArgs,
60
+ abi: proverSpec.abi,
61
+ bytecode: proverSpec.bytecode.object,
62
+ });
63
+ log(`Prover hash: ${proverHash}`);
64
+ const prover = await waitForContractDeploy({ hash: proverHash });
65
+ return prover;
66
+ };
67
+
68
+ export const deployVlayerContracts = async ({
69
+ proverSpec,
70
+ verifierSpec,
71
+ proverArgs,
72
+ verifierArgs,
73
+ }: {
74
+ proverSpec: ContractSpec;
75
+ verifierSpec: ContractSpec;
76
+ proverArgs?: ContractArg[];
77
+ verifierArgs?: ContractArg[];
78
+ }) => {
79
+ log("Starting contract deployment process...");
80
+ const config = getConfig();
81
+ const { chain, ethClient, account } = createContext(config);
82
+
83
+ log("Deploying prover contract...");
84
+ const proverHash = await ethClient.deployContract({
85
+ chain,
86
+ account,
87
+ args: proverArgs,
88
+ abi: proverSpec.abi,
89
+ bytecode: proverSpec.bytecode.object,
90
+ });
91
+ log(`Prover hash: ${proverHash}`);
92
+ const prover = await waitForContractDeploy({ hash: proverHash });
93
+ log(`Prover contract deployed at: ${prover}`);
94
+
95
+ log("Deploying verifier contract...");
96
+ const verifierHash = await ethClient.deployContract({
97
+ chain,
98
+ account,
99
+ args: prover ? [prover, ...(verifierArgs ?? [])] : verifierArgs,
100
+ abi: verifierSpec.abi,
101
+ bytecode: verifierSpec.bytecode.object,
102
+ });
103
+ const verifier = await waitForContractDeploy({ hash: verifierHash });
104
+ log(`Verifier contract deployed at: ${verifier}`);
105
+
106
+ log("Contract deployment completed successfully");
107
+ return { prover, verifier };
108
+ };
@@ -0,0 +1,6 @@
1
+ export const getChainConfirmations = (chainName?: string): number => {
2
+ if (!chainName || chainName.toLowerCase() === "anvil") {
3
+ return 1;
4
+ }
5
+ return 6;
6
+ };
@@ -0,0 +1,71 @@
1
+ import dotenvflow from "dotenv-flow";
2
+ import { type EnvConfig } from "./types";
3
+
4
+ const ensureEnvVariable = (envVar: string) => {
5
+ if (!process.env[envVar]) {
6
+ if (envVar === "EXAMPLES_TEST_PRIVATE_KEY") {
7
+ throw new Error(
8
+ `${envVar} missing. Add a HEX private key with ETH in .env.local for deploy and verify transactions.`,
9
+ );
10
+ }
11
+ throw new Error(`${envVar} is not set`);
12
+ }
13
+ return process.env[envVar];
14
+ };
15
+
16
+ const ensureVlayerEnv = () => {
17
+ try {
18
+ if (!process.env.VLAYER_ENV) {
19
+ throw new Error("VLAYER_ENV is not set. Available options: testnet, dev");
20
+ }
21
+ if (!["testnet", "dev"].includes(process.env.VLAYER_ENV)) {
22
+ throw new Error(
23
+ `Invalid VLAYER_ENV: ${process.env.VLAYER_ENV}. Available options: testnet, anvil, mainnet`,
24
+ );
25
+ }
26
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
27
+ } catch (e) {
28
+ return "dev";
29
+ }
30
+
31
+ return process.env.VLAYER_ENV;
32
+ };
33
+
34
+ const dotEnvFlowConfig = () => {
35
+ dotenvflow.config({
36
+ node_env: ensureVlayerEnv(),
37
+ });
38
+ };
39
+
40
+ export const toCamelCase = (str: string) =>
41
+ str
42
+ .toLowerCase()
43
+ .replace(/([-_][a-z])/g, (group) =>
44
+ group.toUpperCase().replace("-", "").replace("_", ""),
45
+ );
46
+
47
+ const envVars = [
48
+ { var: "CHAIN_NAME" },
49
+ { var: "PROVER_URL" },
50
+ { var: "JSON_RPC_URL" },
51
+ { var: "L2_JSON_RPC_URL", optional: true },
52
+ { var: "EXAMPLES_TEST_PRIVATE_KEY", to: "privateKey" },
53
+ { var: "VLAYER_API_TOKEN", to: "token", optional: true },
54
+ ];
55
+
56
+ export const getConfig = () => {
57
+ dotEnvFlowConfig();
58
+ return envVars.reduce((config, envVar) => {
59
+ try {
60
+ return {
61
+ ...config,
62
+ [envVar.to ?? toCamelCase(envVar.var)]: ensureEnvVariable(envVar.var),
63
+ };
64
+ } catch (e) {
65
+ if (envVar.optional) {
66
+ return { ...config };
67
+ }
68
+ throw e;
69
+ }
70
+ }, {} as EnvConfig);
71
+ };
@@ -0,0 +1,5 @@
1
+ export * from "./getConfig";
2
+ export * from "./createContext";
3
+ export * from "./deploy";
4
+ export * from "./writeEnvVariables";
5
+ export * from "./types";
@@ -0,0 +1,26 @@
1
+ // result of env parsing
2
+ // mostly needed by the examples to be able to properly perform
3
+ // pre run deployment
4
+
5
+ export type EnvConfig = {
6
+ chainName: string;
7
+ proverUrl: string;
8
+ jsonRpcUrl: string;
9
+ l2JsonRpcUrl?: string;
10
+ privateKey: `0x${string}`;
11
+ token?: string;
12
+ };
13
+
14
+ // represents what is needed by client to properly
15
+ // work in whole vlayer flow
16
+ // privateKey is optional and used only for anvil
17
+ // to avoid involving metamask into the flow
18
+
19
+ export type VlayerContextConfig = {
20
+ chainName: string;
21
+ jsonRpcUrl: string;
22
+ proverUrl: string;
23
+ wsProxyUrl?: string;
24
+ notaryUrl?: string;
25
+ privateKey?: `0x${string}`;
26
+ };
@@ -0,0 +1,28 @@
1
+ import fs from "fs";
2
+ import dotenv from "dotenv";
3
+ import debug from "debug";
4
+
5
+ const log = debug("vlayer:config");
6
+
7
+ export const writeEnvVariables = async (
8
+ envPath: string,
9
+ overrides: { [key: string]: string | undefined },
10
+ ) => {
11
+ fs.appendFileSync(envPath, "");
12
+ const envFile = Bun.file(envPath);
13
+ let envContent = await envFile.text();
14
+
15
+ if (!envContent) {
16
+ envContent = "";
17
+ }
18
+
19
+ const newEnvs = Object.assign(dotenv.parse(envContent), overrides);
20
+
21
+ const envLines = Object.entries(newEnvs)
22
+ .map(([key, value]) => `${key}=${value}`)
23
+ .join("\n");
24
+
25
+ await Bun.write(envPath, envLines);
26
+
27
+ log(`Successfully updated the ${envPath} with: `, overrides);
28
+ };
package/src/index.ts ADDED
@@ -0,0 +1,7 @@
1
+ export { preverifyEmail } from "./api/email/preverify";
2
+ export { createVlayerClient } from "./api/lib/client";
3
+
4
+ export * from "./api/lib/types";
5
+
6
+ export * from "./web-proof-commons/utils";
7
+ export * from "./web-proof-commons/types/message";
@@ -0,0 +1,3 @@
1
+ import fs from "fs";
2
+
3
+ export const readFile = (path: string) => fs.readFileSync(path).toString();
@@ -0,0 +1,3 @@
1
+ export * from "./types/message";
2
+ export * from "./types/redaction";
3
+ export * from "./utils";