@vlayer/sdk 0.1.0-nightly-20241028-591419e → 0.1.0-nightly-202410292-42360cf
Sign up to get free protection for your applications and to get access to all the features.
- package/dist/api/email/dnsResolver.d.ts +1 -0
- package/dist/api/email/dnsResolver.js +6 -0
- package/dist/api/email/dnsResolver.test.d.ts +1 -0
- package/dist/api/email/dnsResolver.test.js +12 -0
- package/dist/api/email/parseEmail.d.ts +10 -0
- package/dist/api/email/parseEmail.js +38 -0
- package/dist/api/email/parseEmail.test.d.ts +1 -0
- package/dist/api/email/parseEmail.test.js +91 -0
- package/dist/api/email/preverify.d.ts +4 -0
- package/dist/api/email/preverify.js +18 -0
- package/dist/api/email/preverify.test.d.ts +1 -0
- package/dist/api/email/preverify.test.js +25 -0
- package/dist/api/helpers.d.ts +38 -0
- package/dist/api/helpers.js +72 -0
- package/dist/api/lib/client.d.ts +6 -0
- package/dist/api/lib/client.js +45 -0
- package/dist/api/lib/types/ethereum.d.ts +14 -0
- package/dist/api/lib/types/ethereum.js +12 -0
- package/dist/api/lib/types/index.js +3 -0
- package/dist/api/lib/types/viem.d.ts +8 -0
- package/dist/api/lib/types/viem.js +1 -0
- package/dist/api/lib/types/vlayer.d.ts +47 -0
- package/dist/api/lib/types/vlayer.js +1 -0
- package/dist/api/lib/types/webProofProvider.d.ts +29 -0
- package/dist/api/lib/types/webProofProvider.js +1 -0
- package/dist/api/prover.d.ts +2 -0
- package/dist/api/prover.js +15 -0
- package/dist/api/v_call.d.ts +2 -0
- package/dist/api/v_call.js +30 -0
- package/dist/api/webProof/createWebProof.d.ts +2 -0
- package/dist/api/webProof/createWebProof.js +7 -0
- package/dist/api/webProof/index.js +3 -0
- package/dist/api/webProof/providers/extension.d.ts +2 -0
- package/dist/api/webProof/providers/extension.js +32 -0
- package/dist/api/webProof/providers/index.js +1 -0
- package/dist/api/webProof/steps/expectUrl.d.ts +2 -0
- package/dist/api/webProof/steps/expectUrl.js +8 -0
- package/dist/api/webProof/steps/index.d.ts +9 -0
- package/dist/api/webProof/steps/index.js +9 -0
- package/dist/api/webProof/steps/notarize.d.ts +2 -0
- package/dist/api/webProof/steps/notarize.js +9 -0
- package/dist/api/webProof/steps/startPage.d.ts +2 -0
- package/dist/api/webProof/steps/startPage.js +8 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +7 -0
- package/dist/testHelpers/readFile.d.ts +1 -0
- package/dist/testHelpers/readFile.js +2 -0
- package/dist/web-proof-commons/index.d.ts +3 -0
- package/dist/web-proof-commons/index.js +3 -0
- package/dist/web-proof-commons/types/message.d.ts +56 -0
- package/dist/web-proof-commons/types/message.js +5 -0
- package/dist/web-proof-commons/types/webProof.d.ts +86 -0
- package/dist/web-proof-commons/types/webProof.js +10 -0
- package/dist/web-proof-commons/utils.d.ts +7 -0
- package/dist/web-proof-commons/utils.js +5 -0
- package/package.json +16 -7
- package/.changeset/config.json +0 -11
- package/CHANGELOG.md +0 -7
- package/eslint.config.ts +0 -22
- package/src/api/email/dnsResolver.test.ts +0 -18
- package/src/api/email/dnsResolver.ts +0 -7
- package/src/api/email/parseEmail.test.ts +0 -133
- package/src/api/email/parseEmail.ts +0 -49
- package/src/api/email/preverify.test.ts +0 -37
- package/src/api/email/preverify.ts +0 -19
- package/src/api/email/testdata/test_email.txt +0 -21
- package/src/api/email/testdata/test_email_multiple_dkims.txt +0 -28
- package/src/api/email/testdata/test_email_unknown_domain.txt +0 -21
- package/src/api/helpers.ts +0 -173
- package/src/api/lib/client.ts +0 -76
- package/src/api/lib/types/ethereum.ts +0 -43
- package/src/api/lib/types/viem.ts +0 -28
- package/src/api/lib/types/vlayer.ts +0 -60
- package/src/api/lib/types/webProofProvider.ts +0 -44
- package/src/api/prover.ts +0 -34
- package/src/api/v_call.ts +0 -45
- package/src/api/webProof/createWebProof.ts +0 -9
- package/src/api/webProof/providers/extension.ts +0 -72
- package/src/api/webProof/steps/expectUrl.ts +0 -12
- package/src/api/webProof/steps/index.ts +0 -11
- package/src/api/webProof/steps/notarize.ts +0 -17
- package/src/api/webProof/steps/startPage.ts +0 -12
- package/src/index.ts +0 -9
- package/src/testHelpers/readFile.ts +0 -3
- package/src/web-proof-commons/index.ts +0 -3
- package/src/web-proof-commons/types/message.ts +0 -73
- package/src/web-proof-commons/types/webProof.ts +0 -111
- package/src/web-proof-commons/utils.ts +0 -12
- package/tsconfig.json +0 -24
- package/vite.config.ts +0 -7
- /package/{src/api/lib/types/index.ts → dist/api/lib/types/index.d.ts} +0 -0
- /package/{src/api/webProof/index.ts → dist/api/webProof/index.d.ts} +0 -0
- /package/{src/api/webProof/providers/index.ts → dist/api/webProof/providers/index.d.ts} +0 -0
@@ -0,0 +1 @@
|
|
1
|
+
export declare function resolveDkimDns(domain: string, selector: string): Promise<string>;
|
@@ -0,0 +1 @@
|
|
1
|
+
export {};
|
@@ -0,0 +1,12 @@
|
|
1
|
+
import { describe, expect, test } from "vitest";
|
2
|
+
import { resolveDkimDns } from "./dnsResolver";
|
3
|
+
describe("resolveDkimDns Integration", () => {
|
4
|
+
test("resolves VLayer DNS", async () => {
|
5
|
+
const resolved = await resolveDkimDns("vlayer-xyz.20230601.gappssmtp.com", "20230601");
|
6
|
+
const expected = "v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3gWcOhCm99qzN+h7/2+LeP3CLsJkQQ4EP/2mrceXle5pKq8uZmBl1U4d2Vxn4w+pWFANDLmcHolLboESLFqEL5N6ae7u9b236dW4zn9AFkXAGenTzQEeif9VUFtLAZ0Qh2eV7OQgz/vPj5IaNqJ7h9hpM9gO031fe4v+J0DLCE8Rgo7hXbNgJavctc0983DaCDQaznHZ44LZ6TtZv9TBs+QFvsy4+UCTfsuOtHzoEqOOuXsVXZKLP6B882XbEnBpXEF8QzV4J26HiAJFUbO3mAqZL2UeKC0hhzoIZqZXNG0BfuzOF0VLpDa18GYMUiu+LhEJPJO9D8zhzvQIHNrpGwIDAQAB";
|
7
|
+
expect(resolved).toBe(expected);
|
8
|
+
});
|
9
|
+
test("throws error if dns not found", async () => {
|
10
|
+
await expect(resolveDkimDns("not-a-domain.com", "abcd")).rejects.toThrow();
|
11
|
+
});
|
12
|
+
});
|
@@ -0,0 +1,10 @@
|
|
1
|
+
import { Email } from "postal-mime";
|
2
|
+
export declare class DkimParsingError extends Error {
|
3
|
+
constructor(message: string);
|
4
|
+
}
|
5
|
+
export declare function parseEmail(mime: string): Promise<Email>;
|
6
|
+
export declare function getDkimSigners(mail: Email): {
|
7
|
+
domain: string;
|
8
|
+
selector: string;
|
9
|
+
}[];
|
10
|
+
export declare function parseParams(str: string): Record<string, string>;
|
@@ -0,0 +1,38 @@
|
|
1
|
+
import PostalMime from "postal-mime";
|
2
|
+
export class DkimParsingError extends Error {
|
3
|
+
constructor(message) {
|
4
|
+
super(message);
|
5
|
+
this.name = "DkimParsingError";
|
6
|
+
}
|
7
|
+
}
|
8
|
+
export async function parseEmail(mime) {
|
9
|
+
return await PostalMime.parse(mime.trim());
|
10
|
+
}
|
11
|
+
export function getDkimSigners(mail) {
|
12
|
+
const dkimHeader = mail.headers.filter((h) => h.key === "dkim-signature");
|
13
|
+
if (dkimHeader.length === 0)
|
14
|
+
throw new DkimParsingError("No DKIM header found");
|
15
|
+
return dkimHeader.map(parseHeader);
|
16
|
+
}
|
17
|
+
export function parseParams(str) {
|
18
|
+
return Object.fromEntries(str.split(";").map((s) => s
|
19
|
+
.trim()
|
20
|
+
.split("=")
|
21
|
+
.map((v) => v && v.trim())));
|
22
|
+
}
|
23
|
+
function parseHeader(header) {
|
24
|
+
const params = parseParams(header.value);
|
25
|
+
if (!params) {
|
26
|
+
throw new DkimParsingError(`Invalid DKIM header ${header.value}`);
|
27
|
+
}
|
28
|
+
if (!params.d) {
|
29
|
+
throw new DkimParsingError("DKIM header missing domain");
|
30
|
+
}
|
31
|
+
if (!params.s) {
|
32
|
+
throw new DkimParsingError("DKIM header missing selector");
|
33
|
+
}
|
34
|
+
return {
|
35
|
+
domain: params.d,
|
36
|
+
selector: params.s,
|
37
|
+
};
|
38
|
+
}
|
@@ -0,0 +1 @@
|
|
1
|
+
export {};
|
@@ -0,0 +1,91 @@
|
|
1
|
+
import { describe, expect, test } from "vitest";
|
2
|
+
import { getDkimSigners, parseEmail, parseParams } from "./parseEmail";
|
3
|
+
const emailHeaders = `From: "John Doe" <john@d.oe>
|
4
|
+
To: "Jane Doe" <jane@d.oe>
|
5
|
+
Subject: Hello World
|
6
|
+
Date: Thu, 1 Jan 1970 00:00:00 +0000
|
7
|
+
`;
|
8
|
+
const dkimHeader = "DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=example.com; h=from:to:subject; s=selector1; b=abcdef;";
|
9
|
+
const body = "Hello, World!";
|
10
|
+
const emailFixture = `${emailHeaders}${dkimHeader}\n\n${body}`;
|
11
|
+
describe("parseEmail", () => {
|
12
|
+
test("should get dkim header from email", async () => { });
|
13
|
+
test("correctly parses untrimmed email", async () => {
|
14
|
+
const untrimmed = `\n ${emailFixture} \n`;
|
15
|
+
const email = await parseEmail(untrimmed);
|
16
|
+
expect(email.headers.find((h) => h.key === "dkim-signature")).toBeDefined();
|
17
|
+
});
|
18
|
+
test("works well with multiple dkim headers", async () => {
|
19
|
+
const dkimHeader2 = "DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=second.signer; h=from:to:subject; s=selector2; b=abcdef;";
|
20
|
+
const email = await parseEmail(`${emailHeaders}${dkimHeader}\n${dkimHeader2}\n\n${body}`);
|
21
|
+
const dkim = email.headers.filter((h) => h.key === "dkim-signature");
|
22
|
+
expect(dkim).toHaveLength(2);
|
23
|
+
expect(parseParams(dkim[0].value).s).toBe("selector1");
|
24
|
+
expect(parseParams(dkim[1].value).s).toBe("selector2");
|
25
|
+
});
|
26
|
+
});
|
27
|
+
describe("getDkimSigners", () => {
|
28
|
+
test("should get dkim signers from email", async () => {
|
29
|
+
const email = await parseEmail(emailFixture);
|
30
|
+
const dkim = getDkimSigners(email);
|
31
|
+
expect(dkim).toHaveLength(1);
|
32
|
+
expect(dkim[0].domain).toBe("example.com");
|
33
|
+
expect(dkim[0].selector).toBe("selector1");
|
34
|
+
});
|
35
|
+
test("should get multiple dkim signers from email", async () => {
|
36
|
+
const dkimHeader2 = "DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=second.signer; h=from:to:subject; s=selector2; b=abcdef;";
|
37
|
+
const email = await parseEmail(`${emailHeaders}${dkimHeader}\n${dkimHeader2}\n\n${body}`);
|
38
|
+
const dkim = getDkimSigners(email);
|
39
|
+
expect(dkim).toHaveLength(2);
|
40
|
+
expect(dkim[0].domain).toBe("example.com");
|
41
|
+
expect(dkim[0].selector).toBe("selector1");
|
42
|
+
expect(dkim[1].domain).toBe("second.signer");
|
43
|
+
expect(dkim[1].selector).toBe("selector2");
|
44
|
+
});
|
45
|
+
test("should throw if no dkim header found", async () => {
|
46
|
+
const email = await parseEmail(emailHeaders);
|
47
|
+
expect(() => getDkimSigners(email)).toThrowError("No DKIM header found");
|
48
|
+
});
|
49
|
+
test("should throw if dkim header is invalid", async () => {
|
50
|
+
const email = await parseEmail(`${emailHeaders}DKIM-Signature: invalid\n\n${body}`);
|
51
|
+
expect(() => getDkimSigners(email)).toThrowError("DKIM header missing domain");
|
52
|
+
});
|
53
|
+
test("should throw if dkim header is missing domain", async () => {
|
54
|
+
const email = await parseEmail(`${emailHeaders}DKIM-Signature: v=1; s=selector\n\n${body}`);
|
55
|
+
expect(() => getDkimSigners(email)).toThrowError("DKIM header missing domain");
|
56
|
+
});
|
57
|
+
test("should throw if dkim header is missing selector", async () => {
|
58
|
+
const email = await parseEmail(`${emailHeaders}DKIM-Signature: v=1; d=example.com\n\n${body}`);
|
59
|
+
expect(() => getDkimSigners(email)).toThrowError("DKIM header missing selector");
|
60
|
+
});
|
61
|
+
});
|
62
|
+
describe("parseParams", () => {
|
63
|
+
test("should parse single parameter", () => {
|
64
|
+
const params = parseParams("a=b");
|
65
|
+
expect(params).toEqual({ a: "b" });
|
66
|
+
});
|
67
|
+
test("should parse multiple parameters", () => {
|
68
|
+
const params = parseParams("a=b; c=d; e=f");
|
69
|
+
expect(params).toEqual({ a: "b", c: "d", e: "f" });
|
70
|
+
});
|
71
|
+
test("should trim spaces around parameters", () => {
|
72
|
+
const params = parseParams(" a = b ; c = d ; e = f ");
|
73
|
+
expect(params).toEqual({ a: "b", c: "d", e: "f" });
|
74
|
+
});
|
75
|
+
test("should handle empty values", () => {
|
76
|
+
const params = parseParams("a=; b=c");
|
77
|
+
expect(params).toEqual({ a: "", b: "c" });
|
78
|
+
});
|
79
|
+
test("should handle missing values", () => {
|
80
|
+
const params = parseParams("a; b=c");
|
81
|
+
expect(params).toEqual({ a: undefined, b: "c" });
|
82
|
+
});
|
83
|
+
test("should handle empty string", () => {
|
84
|
+
const params = parseParams("");
|
85
|
+
expect(params).toEqual({});
|
86
|
+
});
|
87
|
+
test("should handle parameters with extra semicolons", () => {
|
88
|
+
const params = parseParams("a=b;; c=d;");
|
89
|
+
expect(params).toEqual({ a: "b", c: "d" });
|
90
|
+
});
|
91
|
+
});
|
@@ -0,0 +1,18 @@
|
|
1
|
+
import { parseEmail, getDkimSigners } from "./parseEmail";
|
2
|
+
import { resolveDkimDns } from "./dnsResolver";
|
3
|
+
export async function preverifyEmail(mimeEmail) {
|
4
|
+
const parsedEmail = await parseEmail(mimeEmail);
|
5
|
+
const signers = getDkimSigners(parsedEmail);
|
6
|
+
if (signers.length === 0) {
|
7
|
+
throw new Error("No DKIM header found");
|
8
|
+
}
|
9
|
+
if (signers.length > 1) {
|
10
|
+
throw new Error("Multiple DKIM headers found");
|
11
|
+
}
|
12
|
+
const [{ domain, selector }] = signers;
|
13
|
+
const dnsRecord = await resolveDkimDns(domain, selector);
|
14
|
+
return {
|
15
|
+
email: mimeEmail,
|
16
|
+
dnsRecords: [dnsRecord],
|
17
|
+
};
|
18
|
+
}
|
@@ -0,0 +1 @@
|
|
1
|
+
export {};
|
@@ -0,0 +1,25 @@
|
|
1
|
+
import { describe, expect, test } from "vitest";
|
2
|
+
import { preverifyEmail } from "./preverify";
|
3
|
+
import { readFile } from "../../testHelpers/readFile";
|
4
|
+
describe("Preverify email: integration", () => {
|
5
|
+
test("adds dns record to email mime", async () => {
|
6
|
+
const rawEmail = readFile("./src/api/email/testdata/test_email.txt");
|
7
|
+
const preverifiedEmail = await preverifyEmail(rawEmail);
|
8
|
+
expect(preverifiedEmail).toMatchObject({
|
9
|
+
email: rawEmail,
|
10
|
+
dnsRecords: [expect.stringContaining("v=DKIM1; k=rsa; p=")],
|
11
|
+
});
|
12
|
+
});
|
13
|
+
test("throws error if DKIM not found", async () => {
|
14
|
+
const emailWithNoDkimHeader = 'From: "Alice"\n\nBody';
|
15
|
+
await expect(preverifyEmail(emailWithNoDkimHeader)).rejects.toThrow("No DKIM header found");
|
16
|
+
});
|
17
|
+
test("throws error if DNS could not be resolved", async () => {
|
18
|
+
const emailWithNoDkimHeader = readFile("./src/api/email/testdata/test_email_unknown_domain.txt");
|
19
|
+
await expect(preverifyEmail(emailWithNoDkimHeader)).rejects.toThrow();
|
20
|
+
});
|
21
|
+
test("throws error if multiple DNS records found", async () => {
|
22
|
+
const emailWithNoDkimHeader = readFile("./src/api/email/testdata/test_email_multiple_dkims.txt");
|
23
|
+
await expect(preverifyEmail(emailWithNoDkimHeader)).rejects.toThrow("Multiple DKIM headers found");
|
24
|
+
});
|
25
|
+
});
|
@@ -0,0 +1,38 @@
|
|
1
|
+
import { type Abi, type Address, type Chain, type ContractFunctionArgs, type ContractFunctionName, type Hex, PublicClient, walletActions } from "viem";
|
2
|
+
import type { ContractSpec, ContractArg } from "types/ethereum";
|
3
|
+
export declare const chainIds: 31337[];
|
4
|
+
export declare function createAnvilClient(chainId?: number): ReturnType<typeof walletActions> & PublicClient;
|
5
|
+
export declare function deployContract(contractSpec: ContractSpec, args?: ContractArg[], chainId?: number): Promise<Address>;
|
6
|
+
type DeploySpec<T extends Abi> = {
|
7
|
+
abi: T;
|
8
|
+
bytecode: {
|
9
|
+
object: Hex;
|
10
|
+
};
|
11
|
+
};
|
12
|
+
type Tail<T> = T extends readonly [unknown, ...infer U] ? U : [];
|
13
|
+
export declare function deployProverVerifier<P extends Abi, V extends Abi>(proverSpec: DeploySpec<P>, verifierSpec: DeploySpec<V>, args?: {
|
14
|
+
prover?: ContractArg[];
|
15
|
+
verifier?: Tail<ContractArg>[];
|
16
|
+
}, chainId?: number): Promise<`0x${string}`[]>;
|
17
|
+
export declare function call<T extends Abi, F extends ContractFunctionName<T, "pure" | "view">>(abi: T, address: Address, functionName: F, args?: ContractFunctionArgs<T, "pure" | "view", F>, chainId?: number): Promise<import("viem").ContractFunctionReturnType<T, "pure" | "view", F, ContractFunctionArgs<T, "pure" | "view", F>>>;
|
18
|
+
export declare function writeContract<T extends Abi, F extends ContractFunctionName<T, "payable" | "nonpayable">>(address: Address, abi: T, functionName: F, args: ContractFunctionArgs<T, "payable" | "nonpayable", F>, sender?: Address, chain?: Chain): Promise<import("viem").TransactionReceipt>;
|
19
|
+
export declare const getTestAccount: () => {
|
20
|
+
address: Address;
|
21
|
+
nonceManager?: import("viem").NonceManager | undefined;
|
22
|
+
sign: (parameters: {
|
23
|
+
hash: import("viem").Hash;
|
24
|
+
}) => Promise<Hex>;
|
25
|
+
experimental_signAuthorization: (parameters: import("viem/experimental").Authorization) => Promise<import("viem/accounts").SignAuthorizationReturnType>;
|
26
|
+
signMessage: ({ message }: {
|
27
|
+
message: import("viem").SignableMessage;
|
28
|
+
}) => Promise<Hex>;
|
29
|
+
signTransaction: <serializer extends import("viem").SerializeTransactionFn<import("viem").TransactionSerializable> = import("viem").SerializeTransactionFn<import("viem").TransactionSerializable>, transaction extends Parameters<serializer>[0] = Parameters<serializer>[0]>(transaction: transaction, options?: {
|
30
|
+
serializer?: serializer | undefined;
|
31
|
+
} | undefined) => Promise<import("viem").IsNarrowable<import("viem").TransactionSerialized<import("viem").GetTransactionType<transaction>>, Hex> extends true ? import("viem").TransactionSerialized<import("viem").GetTransactionType<transaction>> : Hex>;
|
32
|
+
signTypedData: <const typedData extends import("viem").TypedData | Record<string, unknown>, primaryType extends keyof typedData | "EIP712Domain" = keyof typedData>(parameters: import("viem").TypedDataDefinition<typedData, primaryType>) => Promise<Hex>;
|
33
|
+
publicKey: Hex;
|
34
|
+
source: "privateKey";
|
35
|
+
type: "local";
|
36
|
+
};
|
37
|
+
export declare const getTestAddresses: (chainId?: number) => Promise<Address[]>;
|
38
|
+
export {};
|
@@ -0,0 +1,72 @@
|
|
1
|
+
import { createTestClient, http, publicActions, walletActions, } from "viem";
|
2
|
+
import { privateKeyToAccount, generatePrivateKey } from "viem/accounts";
|
3
|
+
import { foundry } from "viem/chains";
|
4
|
+
const rpcUrls = new Map([[foundry.id, http()]]);
|
5
|
+
export const chainIds = [foundry.id];
|
6
|
+
export function createAnvilClient(chainId = foundry.id) {
|
7
|
+
const transport = rpcUrls.get(chainId);
|
8
|
+
if (transport == undefined) {
|
9
|
+
throw Error(`No url for chainId ${chainId}`);
|
10
|
+
}
|
11
|
+
return createTestClient({
|
12
|
+
chain: foundry,
|
13
|
+
mode: "anvil",
|
14
|
+
transport: transport,
|
15
|
+
})
|
16
|
+
.extend(publicActions)
|
17
|
+
.extend(walletActions);
|
18
|
+
}
|
19
|
+
export async function deployContract(contractSpec, args = [], chainId = foundry.id) {
|
20
|
+
const ethClient = createAnvilClient(chainId);
|
21
|
+
const [deployer] = await ethClient.getAddresses();
|
22
|
+
const txHash = await ethClient.deployContract({
|
23
|
+
abi: contractSpec.abi,
|
24
|
+
bytecode: contractSpec.bytecode.object,
|
25
|
+
account: deployer,
|
26
|
+
args,
|
27
|
+
chain: foundry,
|
28
|
+
});
|
29
|
+
const receipt = await ethClient.waitForTransactionReceipt({ hash: txHash });
|
30
|
+
if (receipt.status != "success") {
|
31
|
+
throw new Error(`Contract deployment failed with status: ${receipt.status}`);
|
32
|
+
}
|
33
|
+
return receipt.contractAddress;
|
34
|
+
}
|
35
|
+
export async function deployProverVerifier(proverSpec, verifierSpec, args = {}, chainId = foundry.id) {
|
36
|
+
console.log("Deploying prover");
|
37
|
+
const proverAddress = await deployContract(proverSpec, args.prover ?? [], chainId);
|
38
|
+
console.log(`Prover has been deployed on ${proverAddress} address`);
|
39
|
+
console.log("Deploying verifier");
|
40
|
+
const verifierAddress = await deployContract(verifierSpec, [proverAddress, ...(args.verifier ?? [])], chainId);
|
41
|
+
console.log(`Verifier has been deployed on ${verifierAddress} address`);
|
42
|
+
return [proverAddress, verifierAddress];
|
43
|
+
}
|
44
|
+
export async function call(abi, address, functionName, args, chainId = foundry.id) {
|
45
|
+
const ethClient = createAnvilClient(chainId);
|
46
|
+
return ethClient.readContract({
|
47
|
+
abi,
|
48
|
+
address,
|
49
|
+
functionName,
|
50
|
+
args,
|
51
|
+
});
|
52
|
+
}
|
53
|
+
export async function writeContract(address, abi, functionName, args, sender, chain = foundry) {
|
54
|
+
const ethClient = createAnvilClient(chain.id);
|
55
|
+
const selectedSender = sender || (await ethClient.getAddresses())[0];
|
56
|
+
const txHash = await ethClient.writeContract({
|
57
|
+
address,
|
58
|
+
abi: abi,
|
59
|
+
functionName,
|
60
|
+
args: args,
|
61
|
+
chain,
|
62
|
+
account: selectedSender,
|
63
|
+
chainOverride: undefined,
|
64
|
+
});
|
65
|
+
const txReceipt = await ethClient.waitForTransactionReceipt({ hash: txHash });
|
66
|
+
if (txReceipt.status != "success") {
|
67
|
+
throw new Error(`Transaction failed with status: ${txReceipt.status}`);
|
68
|
+
}
|
69
|
+
return txReceipt;
|
70
|
+
}
|
71
|
+
export const getTestAccount = () => privateKeyToAccount(generatePrivateKey());
|
72
|
+
export const getTestAddresses = (chainId = foundry.id) => createAnvilClient(chainId).getAddresses();
|
@@ -0,0 +1,45 @@
|
|
1
|
+
import { prove } from "../prover";
|
2
|
+
import { createExtensionWebProofProvider } from "../webProof";
|
3
|
+
import { decodeFunctionResult } from "viem";
|
4
|
+
function dropEmptyProofFromArgs(args) {
|
5
|
+
if (Array.isArray(args)) {
|
6
|
+
return args.slice(1);
|
7
|
+
}
|
8
|
+
return [];
|
9
|
+
}
|
10
|
+
function generateRandomHash() {
|
11
|
+
let hash = "0x";
|
12
|
+
for (let i = 0; i < 40; ++i) {
|
13
|
+
hash += Math.floor(Math.random() * 16).toString(16);
|
14
|
+
}
|
15
|
+
return hash;
|
16
|
+
}
|
17
|
+
export const createVlayerClient = ({ url = "http://127.0.0.1:3000", webProofProvider = createExtensionWebProofProvider(), } = {
|
18
|
+
url: "http://127.0.0.1:3000",
|
19
|
+
webProofProvider: createExtensionWebProofProvider(),
|
20
|
+
}) => {
|
21
|
+
console.log("createVlayerClient with", url, webProofProvider);
|
22
|
+
const resultHashMap = new Map();
|
23
|
+
return {
|
24
|
+
// eslint-disable-next-line @typescript-eslint/require-await
|
25
|
+
prove: async ({ address, functionName, chainId, proverAbi, args }) => {
|
26
|
+
const result_promise = prove(address, proverAbi, functionName, args, chainId, url);
|
27
|
+
const hash = generateRandomHash();
|
28
|
+
resultHashMap.set(hash, [result_promise, proverAbi, functionName]);
|
29
|
+
return { hash };
|
30
|
+
},
|
31
|
+
waitForProvingResult: async ({ hash }) => {
|
32
|
+
const savedProvingData = resultHashMap.get(hash);
|
33
|
+
if (!savedProvingData) {
|
34
|
+
throw new Error("No result found for hash " + hash);
|
35
|
+
}
|
36
|
+
const { result: { proof, evm_call_result }, } = await savedProvingData[0];
|
37
|
+
const result = dropEmptyProofFromArgs(decodeFunctionResult({
|
38
|
+
abi: savedProvingData[1],
|
39
|
+
data: evm_call_result,
|
40
|
+
functionName: savedProvingData[2],
|
41
|
+
}));
|
42
|
+
return [proof, ...result];
|
43
|
+
},
|
44
|
+
};
|
45
|
+
};
|
@@ -0,0 +1,14 @@
|
|
1
|
+
import { Abi, Address, Hex } from "viem";
|
2
|
+
import { Branded } from "../../../web-proof-commons";
|
3
|
+
export type Bytecode = {
|
4
|
+
object: Hex;
|
5
|
+
};
|
6
|
+
export type ContractSpec = {
|
7
|
+
abi: Abi;
|
8
|
+
bytecode: Bytecode;
|
9
|
+
};
|
10
|
+
export type ContractArg = number | string | boolean | bigint | number[] | string[] | boolean[] | bigint[] | Address[];
|
11
|
+
export type EthereumAddress = Branded<Hex, "EthereumAddress">;
|
12
|
+
export type EthereumTxHash = Branded<Hex, "EthereumTxHash">;
|
13
|
+
export declare function assertEthereumAddress(hash: string): asserts hash is EthereumAddress;
|
14
|
+
export declare function assertEthereumTxHash(hash: string): asserts hash is EthereumTxHash;
|
@@ -0,0 +1,12 @@
|
|
1
|
+
export function assertEthereumAddress(hash) {
|
2
|
+
const regex = /^(0x)?[0-9a-fA-F]{40}$/;
|
3
|
+
if (!regex.test(hash)) {
|
4
|
+
throw new Error(`Invalid ethereum account ${hash}`);
|
5
|
+
}
|
6
|
+
}
|
7
|
+
export function assertEthereumTxHash(hash) {
|
8
|
+
const regex = /^(0x)?[0-9a-fA-F]{64}$/;
|
9
|
+
if (!regex.test(hash)) {
|
10
|
+
throw new Error(`Invalid ethereum tx hash ${hash}`);
|
11
|
+
}
|
12
|
+
}
|
@@ -0,0 +1,8 @@
|
|
1
|
+
import { Abi, ContractFunctionName } from "viem";
|
2
|
+
import { AbiParametersToPrimitiveTypes, ExtractAbiFunction } from "abitype";
|
3
|
+
type Without<T extends readonly unknown[], P> = T extends readonly [
|
4
|
+
infer F,
|
5
|
+
...infer R
|
6
|
+
] ? F extends P ? Without<R, P> : readonly [F, ...Without<R, P>] : [];
|
7
|
+
export type ContractFunctionArgsWithout<abi extends Abi, functionName extends ContractFunctionName<abi>, without> = AbiParametersToPrimitiveTypes<Without<ExtractAbiFunction<abi extends Abi ? abi : Abi, functionName>["inputs"], without>, "inputs"> extends infer args ? [args] extends [never] ? readonly unknown[] : args : readonly unknown[];
|
8
|
+
export {};
|
@@ -0,0 +1 @@
|
|
1
|
+
export {};
|
@@ -0,0 +1,47 @@
|
|
1
|
+
import { Abi, AbiStateMutability, Address, ContractFunctionArgs, ContractFunctionName, Hex } from "viem";
|
2
|
+
type Calldata = string;
|
3
|
+
export type CallParams = {
|
4
|
+
to: Address;
|
5
|
+
data: Calldata;
|
6
|
+
};
|
7
|
+
export type CallContext = {
|
8
|
+
chain_id: number;
|
9
|
+
};
|
10
|
+
export type Proof = {
|
11
|
+
length: bigint;
|
12
|
+
seal: {
|
13
|
+
verifierSelector: Hex;
|
14
|
+
seal: readonly [Hex, Hex, Hex, Hex, Hex, Hex, Hex, Hex];
|
15
|
+
mode: number;
|
16
|
+
};
|
17
|
+
callAssumptions: {
|
18
|
+
proverContractAddress: Address;
|
19
|
+
functionSelector: Hex;
|
20
|
+
settleBlockHash: Hex;
|
21
|
+
settleBlockNumber: bigint;
|
22
|
+
};
|
23
|
+
};
|
24
|
+
export interface VCallResult {
|
25
|
+
evm_call_result: Hex;
|
26
|
+
proof: Proof;
|
27
|
+
}
|
28
|
+
export interface VCallResponse {
|
29
|
+
jsonrpc: string;
|
30
|
+
result: VCallResult;
|
31
|
+
id: number;
|
32
|
+
}
|
33
|
+
export type VlayerClient = {
|
34
|
+
prove: <T extends Abi, F extends ContractFunctionName<T>>(args: {
|
35
|
+
address: Hex;
|
36
|
+
proverAbi: T;
|
37
|
+
functionName: F;
|
38
|
+
chainId: number;
|
39
|
+
args: ContractFunctionArgs<T, AbiStateMutability, F>;
|
40
|
+
}) => Promise<{
|
41
|
+
hash: string;
|
42
|
+
}>;
|
43
|
+
waitForProvingResult: ({ hash, }: {
|
44
|
+
hash: string;
|
45
|
+
}) => Promise<[Proof, ...unknown[]]>;
|
46
|
+
};
|
47
|
+
export {};
|
@@ -0,0 +1 @@
|
|
1
|
+
export {};
|
@@ -0,0 +1,29 @@
|
|
1
|
+
import { Hex, Abi, ContractFunctionName } from "viem";
|
2
|
+
import type { ContractFunctionArgsWithout } from "./viem";
|
3
|
+
import { Branded, WebProof, WebProofStep } from "../../../web-proof-commons";
|
4
|
+
export type WebProofSetupInput = {
|
5
|
+
logoUrl: string;
|
6
|
+
steps: WebProofStep[];
|
7
|
+
};
|
8
|
+
export type WebProofSetup = Branded<WebProofSetupInput & {
|
9
|
+
isWebProof: true;
|
10
|
+
}, "webProof">;
|
11
|
+
export type ProverCallCommitment<T extends Abi, F extends ContractFunctionName<T>> = {
|
12
|
+
address: Hex;
|
13
|
+
proverAbi: T;
|
14
|
+
functionName: F;
|
15
|
+
commitmentArgs: ContractFunctionArgsWithout<T, F, {
|
16
|
+
name: "webProof";
|
17
|
+
}>;
|
18
|
+
chainId: number;
|
19
|
+
};
|
20
|
+
export type GetWebProofArgs<T extends Abi, F extends ContractFunctionName<T>> = {
|
21
|
+
proverCallCommitment: ProverCallCommitment<T, F>;
|
22
|
+
} & WebProofSetupInput;
|
23
|
+
export type WebProofProvider = {
|
24
|
+
getWebProof: <T extends Abi, F extends ContractFunctionName<T>>(args: GetWebProofArgs<T, F>) => Promise<WebProof>;
|
25
|
+
};
|
26
|
+
export type WebProofProviderSetup = {
|
27
|
+
notaryUrl?: string;
|
28
|
+
wsProxyUrl?: string;
|
29
|
+
};
|
@@ -0,0 +1 @@
|
|
1
|
+
export {};
|
@@ -0,0 +1,2 @@
|
|
1
|
+
import { type Abi, AbiStateMutability, type Address, ContractFunctionArgs, ContractFunctionName } from "viem";
|
2
|
+
export declare function prove<T extends Abi, F extends ContractFunctionName<T>>(prover: Address, abi: T, functionName: F, args: ContractFunctionArgs<T, AbiStateMutability, F>, chainId?: number, url?: string): Promise<import("types/vlayer").VCallResponse>;
|
@@ -0,0 +1,15 @@
|
|
1
|
+
import { encodeFunctionData, } from "viem";
|
2
|
+
import { v_call } from "./v_call";
|
3
|
+
import { foundry } from "viem/chains";
|
4
|
+
export async function prove(prover, abi, functionName, args, chainId = foundry.id, url = "http://127.0.0.1:3000") {
|
5
|
+
const calldata = encodeFunctionData({
|
6
|
+
abi: abi,
|
7
|
+
functionName: functionName,
|
8
|
+
args: args,
|
9
|
+
});
|
10
|
+
const call = { to: prover, data: calldata };
|
11
|
+
const context = {
|
12
|
+
chain_id: chainId,
|
13
|
+
};
|
14
|
+
return v_call(call, context, url);
|
15
|
+
}
|
@@ -0,0 +1,30 @@
|
|
1
|
+
function v_callBody(call, context) {
|
2
|
+
return {
|
3
|
+
method: "v_call",
|
4
|
+
params: [call, context],
|
5
|
+
id: 1,
|
6
|
+
jsonrpc: "2.0",
|
7
|
+
};
|
8
|
+
}
|
9
|
+
export async function v_call(call, context, url = "http://127.0.0.1:3000") {
|
10
|
+
const response = await fetch(url, {
|
11
|
+
method: "POST",
|
12
|
+
body: JSON.stringify(v_callBody(call, context)),
|
13
|
+
headers: { "Content-Type": "application/json" },
|
14
|
+
});
|
15
|
+
if (!response.ok) {
|
16
|
+
throw new Error(`HTTP error! status: ${response.status}`);
|
17
|
+
}
|
18
|
+
const response_json = await response.json();
|
19
|
+
//TODO we should launch some schema validation here
|
20
|
+
assertObject(response_json);
|
21
|
+
if ("error" in response_json) {
|
22
|
+
throw new Error(`Error response: ${response_json.error.message || "unknown error"}`);
|
23
|
+
}
|
24
|
+
return response_json;
|
25
|
+
}
|
26
|
+
function assertObject(x) {
|
27
|
+
if (typeof x !== "object") {
|
28
|
+
throw new Error("Expected object");
|
29
|
+
}
|
30
|
+
}
|
@@ -0,0 +1,32 @@
|
|
1
|
+
// this id is fixed in the extension by the key in manifest.json
|
2
|
+
const EXTENSION_ID = "jbchhcgphfokabmfacnkafoeeeppjmpl";
|
3
|
+
export const createExtensionWebProofProvider = ({ notaryUrl = "https://notary.pse.dev/v0.1.0-alpha.5/", wsProxyUrl = "wss://notary.pse.dev/proxy", } = {
|
4
|
+
notaryUrl: "https://notary.pse.dev/v0.1.0-alpha.5/",
|
5
|
+
wsProxyUrl: "wss://notary.pse.dev/proxy",
|
6
|
+
}) => {
|
7
|
+
return {
|
8
|
+
getWebProof: async function (webProofSetup) {
|
9
|
+
return new Promise((resolve, reject) => {
|
10
|
+
chrome.runtime.sendMessage(EXTENSION_ID, {
|
11
|
+
action: 0 /* ExtensionAction.RequestWebProof */,
|
12
|
+
payload: {
|
13
|
+
notaryUrl,
|
14
|
+
wsProxyUrl,
|
15
|
+
logoUrl: webProofSetup.logoUrl,
|
16
|
+
steps: webProofSetup.steps,
|
17
|
+
},
|
18
|
+
});
|
19
|
+
const port = chrome.runtime.connect(EXTENSION_ID);
|
20
|
+
// TODO: validate message in runtime
|
21
|
+
port.onMessage.addListener((message) => {
|
22
|
+
if (message.type === "ProofDone" /* ExtensionMessageType.ProofDone */) {
|
23
|
+
resolve(message.proof);
|
24
|
+
}
|
25
|
+
if (message.type === "ProofError" /* ExtensionMessageType.ProofError */) {
|
26
|
+
reject(new Error(message.error));
|
27
|
+
}
|
28
|
+
});
|
29
|
+
});
|
30
|
+
},
|
31
|
+
};
|
32
|
+
};
|
@@ -0,0 +1 @@
|
|
1
|
+
export * from "./extension";
|