@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.
- package/package.json +2 -1
- package/src/api/email/dnsResolver.test.ts +19 -0
- package/src/api/email/dnsResolver.ts +87 -0
- package/src/api/email/parseEmail.test.ts +133 -0
- package/src/api/email/parseEmail.ts +55 -0
- package/src/api/email/preverify.test.ts +201 -0
- package/src/api/email/preverify.ts +70 -0
- package/src/api/email/testdata/test_email.txt +21 -0
- package/src/api/email/testdata/test_email_multiple_dkims.txt +28 -0
- package/src/api/email/testdata/test_email_subdomain.txt +21 -0
- package/src/api/email/testdata/test_email_unknown_domain.txt +21 -0
- package/src/api/lib/client.test.ts +261 -0
- package/src/api/lib/client.ts +191 -0
- package/src/api/lib/errors.ts +19 -0
- package/src/api/lib/types/ethereum.ts +45 -0
- package/src/api/lib/types/index.ts +3 -0
- package/src/api/lib/types/viem.ts +26 -0
- package/src/api/lib/types/vlayer.ts +156 -0
- package/src/api/lib/types/webProofProvider.ts +68 -0
- package/src/api/prover.ts +120 -0
- package/src/api/utils/prefixAllButNthSubstring.test.ts +24 -0
- package/src/api/utils/prefixAllButNthSubstring.ts +13 -0
- package/src/api/utils/versions.test.ts +52 -0
- package/src/api/utils/versions.ts +31 -0
- package/src/api/v_call.ts +58 -0
- package/src/api/v_getProofReceipt.ts +48 -0
- package/src/api/v_versions.ts +68 -0
- package/src/api/webProof/createWebProofRequest.ts +15 -0
- package/src/api/webProof/index.ts +3 -0
- package/src/api/webProof/providers/extension.test.ts +122 -0
- package/src/api/webProof/providers/extension.ts +197 -0
- package/src/api/webProof/providers/index.ts +1 -0
- package/src/api/webProof/steps/expectUrl.ts +12 -0
- package/src/api/webProof/steps/index.ts +11 -0
- package/src/api/webProof/steps/notarize.ts +20 -0
- package/src/api/webProof/steps/startPage.ts +12 -0
- package/src/config/createContext.ts +69 -0
- package/src/config/deploy.ts +108 -0
- package/src/config/getChainConfirmations.ts +6 -0
- package/src/config/getConfig.ts +71 -0
- package/src/config/index.ts +5 -0
- package/src/config/types.ts +26 -0
- package/src/config/writeEnvVariables.ts +28 -0
- package/src/index.ts +7 -0
- package/src/testHelpers/readFile.ts +3 -0
- package/src/web-proof-commons/index.ts +3 -0
- package/src/web-proof-commons/types/message.ts +176 -0
- package/src/web-proof-commons/types/redaction.test.ts +97 -0
- package/src/web-proof-commons/types/redaction.ts +201 -0
- package/src/web-proof-commons/utils.ts +11 -0
@@ -0,0 +1,68 @@
|
|
1
|
+
import { type Hex, type Abi, type ContractFunctionName } from "viem";
|
2
|
+
import type { ContractFunctionArgsWithout } from "./viem";
|
3
|
+
import {
|
4
|
+
type Branded,
|
5
|
+
type ExtensionMessageType,
|
6
|
+
type ExtensionMessage,
|
7
|
+
type PresentationJSON,
|
8
|
+
type WebProofStep,
|
9
|
+
type ZkProvingStatus,
|
10
|
+
} from "../../../web-proof-commons";
|
11
|
+
|
12
|
+
export type WebProofRequestInput = {
|
13
|
+
logoUrl: string;
|
14
|
+
steps: WebProofStep[];
|
15
|
+
};
|
16
|
+
|
17
|
+
export type WebProofRequest = Branded<
|
18
|
+
WebProofRequestInput & {
|
19
|
+
isWebProof: true;
|
20
|
+
},
|
21
|
+
"webProof"
|
22
|
+
>;
|
23
|
+
|
24
|
+
export type ProverCallCommitment<
|
25
|
+
T extends Abi,
|
26
|
+
F extends ContractFunctionName<T>,
|
27
|
+
> = {
|
28
|
+
address: Hex;
|
29
|
+
proverAbi: T;
|
30
|
+
functionName: F;
|
31
|
+
commitmentArgs: ContractFunctionArgsWithout<T, F, { name: "webProof" }>;
|
32
|
+
chainId: number;
|
33
|
+
};
|
34
|
+
|
35
|
+
export type GetWebProofArgs<
|
36
|
+
T extends Abi,
|
37
|
+
F extends ContractFunctionName<T>,
|
38
|
+
> = {
|
39
|
+
proverCallCommitment: ProverCallCommitment<T, F>;
|
40
|
+
} & WebProofRequestInput;
|
41
|
+
|
42
|
+
export type WebProofProvider = {
|
43
|
+
getWebProof: <T extends Abi, F extends ContractFunctionName<T>>(
|
44
|
+
args: GetWebProofArgs<T, F>,
|
45
|
+
) => Promise<{
|
46
|
+
presentationJSON: PresentationJSON;
|
47
|
+
decodedTranscript: {
|
48
|
+
sent: string;
|
49
|
+
recv: string;
|
50
|
+
};
|
51
|
+
}>;
|
52
|
+
|
53
|
+
requestWebProof: <T extends Abi, F extends ContractFunctionName<T>>(
|
54
|
+
args: GetWebProofArgs<T, F>,
|
55
|
+
) => void;
|
56
|
+
|
57
|
+
notifyZkProvingStatus: (status: ZkProvingStatus) => void;
|
58
|
+
|
59
|
+
addEventListeners: <T extends ExtensionMessageType>(
|
60
|
+
messageType: T,
|
61
|
+
listener: (args: Extract<ExtensionMessage, { type: T }>) => void,
|
62
|
+
) => void;
|
63
|
+
};
|
64
|
+
|
65
|
+
export type WebProofProviderSetup = {
|
66
|
+
notaryUrl?: string;
|
67
|
+
wsProxyUrl?: string;
|
68
|
+
};
|
@@ -0,0 +1,120 @@
|
|
1
|
+
import {
|
2
|
+
type Abi,
|
3
|
+
type AbiStateMutability,
|
4
|
+
type Address,
|
5
|
+
type ContractFunctionArgs,
|
6
|
+
type ContractFunctionName,
|
7
|
+
encodeFunctionData,
|
8
|
+
type Hex,
|
9
|
+
} from "viem";
|
10
|
+
import {
|
11
|
+
type CallContext,
|
12
|
+
type CallParams,
|
13
|
+
type BrandedHash,
|
14
|
+
type ProofDataWithMetrics,
|
15
|
+
type ProofReceipt,
|
16
|
+
ProofState,
|
17
|
+
type VGetProofReceiptParams,
|
18
|
+
type VGetProofReceiptResponse,
|
19
|
+
} from "./lib/types/vlayer";
|
20
|
+
import { match } from "ts-pattern";
|
21
|
+
import { v_call } from "./v_call";
|
22
|
+
import { v_getProofReceipt } from "./v_getProofReceipt";
|
23
|
+
import { foundry } from "viem/chains";
|
24
|
+
import { v_versions } from "./v_versions";
|
25
|
+
import { checkVersionCompatibility } from "./utils/versions";
|
26
|
+
import meta from "../../package.json" with { type: "json" };
|
27
|
+
const sdkVersion = meta.version;
|
28
|
+
|
29
|
+
export interface ProveOptions {
|
30
|
+
preverifyVersions?: boolean;
|
31
|
+
}
|
32
|
+
|
33
|
+
async function preverifyVersions(url: string, shouldPreverify: boolean) {
|
34
|
+
if (shouldPreverify) {
|
35
|
+
const proverVersions = await v_versions(url);
|
36
|
+
checkVersionCompatibility(proverVersions.result.api_version, sdkVersion);
|
37
|
+
}
|
38
|
+
}
|
39
|
+
|
40
|
+
export async function prove<T extends Abi, F extends ContractFunctionName<T>>(
|
41
|
+
prover: Address,
|
42
|
+
abi: T,
|
43
|
+
functionName: F,
|
44
|
+
args: ContractFunctionArgs<T, AbiStateMutability, F>,
|
45
|
+
chainId: number = foundry.id,
|
46
|
+
url: string = "http://127.0.0.1:3000",
|
47
|
+
gasLimit: number = 10_000_000,
|
48
|
+
token?: string,
|
49
|
+
options: ProveOptions = { preverifyVersions: false },
|
50
|
+
): Promise<BrandedHash<T, F>> {
|
51
|
+
await preverifyVersions(url, !!options.preverifyVersions);
|
52
|
+
const calldata = encodeFunctionData({
|
53
|
+
abi: abi as Abi,
|
54
|
+
functionName: functionName as string,
|
55
|
+
args: args as readonly unknown[],
|
56
|
+
});
|
57
|
+
const call: CallParams = { to: prover, data: calldata, gas_limit: gasLimit };
|
58
|
+
const context: CallContext = {
|
59
|
+
chain_id: chainId,
|
60
|
+
};
|
61
|
+
const resp = await v_call(call, context, url, token);
|
62
|
+
return { hash: resp.result } as BrandedHash<T, F>;
|
63
|
+
}
|
64
|
+
|
65
|
+
export async function getProofReceipt<
|
66
|
+
T extends Abi,
|
67
|
+
F extends ContractFunctionName<T>,
|
68
|
+
>(
|
69
|
+
hash: BrandedHash<T, F>,
|
70
|
+
url: string = "http://127.0.0.1:3000",
|
71
|
+
): Promise<ProofReceipt> {
|
72
|
+
const params: VGetProofReceiptParams = {
|
73
|
+
hash: hash.hash as Hex,
|
74
|
+
};
|
75
|
+
const resp = await v_getProofReceipt(params, url);
|
76
|
+
handleErrors(resp);
|
77
|
+
return resp.result;
|
78
|
+
}
|
79
|
+
|
80
|
+
const handleErrors = (resp: VGetProofReceiptResponse) => {
|
81
|
+
const { status, state, error } = resp.result;
|
82
|
+
if (status === 0) {
|
83
|
+
match(state)
|
84
|
+
.with(ProofState.ChainProof, () => {
|
85
|
+
throw new Error("Waiting for chain proof failed with error: " + error);
|
86
|
+
})
|
87
|
+
.with(ProofState.Preflight, () => {
|
88
|
+
throw new Error("Preflight failed with error: " + error);
|
89
|
+
})
|
90
|
+
.with(ProofState.Proving, () => {
|
91
|
+
throw new Error("Proving failed with error: " + error);
|
92
|
+
})
|
93
|
+
.exhaustive();
|
94
|
+
}
|
95
|
+
};
|
96
|
+
|
97
|
+
export async function waitForProof<
|
98
|
+
T extends Abi,
|
99
|
+
F extends ContractFunctionName<T>,
|
100
|
+
>(
|
101
|
+
hash: BrandedHash<T, F>,
|
102
|
+
url: string,
|
103
|
+
numberOfRetries: number = 240,
|
104
|
+
sleepDuration: number = 1000,
|
105
|
+
): Promise<ProofDataWithMetrics> {
|
106
|
+
for (let retry = 0; retry < numberOfRetries; retry++) {
|
107
|
+
const { state, data, metrics } = await getProofReceipt(hash, url);
|
108
|
+
if (state === ProofState.Done) {
|
109
|
+
return { data, metrics };
|
110
|
+
}
|
111
|
+
await sleep(sleepDuration);
|
112
|
+
}
|
113
|
+
throw new Error(
|
114
|
+
`Timed out waiting for ZK proof generation after ${numberOfRetries * sleepDuration}ms. Consider increasing numberOfRetries.`,
|
115
|
+
);
|
116
|
+
}
|
117
|
+
|
118
|
+
async function sleep(ms: number): Promise<void> {
|
119
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
120
|
+
}
|
@@ -0,0 +1,24 @@
|
|
1
|
+
import { describe, expect, test } from "vitest";
|
2
|
+
import { prefixAllButNthSubstring } from "./prefixAllButNthSubstring";
|
3
|
+
|
4
|
+
describe("prefixAllButNthSubstring", () => {
|
5
|
+
test("adds 'X-' prefix to all matches except n-th (indexed from 0)", () => {
|
6
|
+
const str = "abc 123 abc 456 abc 789";
|
7
|
+
expect(prefixAllButNthSubstring(str, /abc/gi, 3, 0)).toBe(
|
8
|
+
"abc 123 X-abc 456 X-abc 789",
|
9
|
+
);
|
10
|
+
expect(prefixAllButNthSubstring(str, /abc/gi, 3, 1)).toBe(
|
11
|
+
"X-abc 123 abc 456 X-abc 789",
|
12
|
+
);
|
13
|
+
expect(prefixAllButNthSubstring(str, /abc/gi, 3, 2)).toBe(
|
14
|
+
"X-abc 123 X-abc 456 abc 789",
|
15
|
+
);
|
16
|
+
});
|
17
|
+
|
18
|
+
test("does not add prefix to substrings past total substring count", () => {
|
19
|
+
const str = "abc 123 abc 456 abc 789 abc abc";
|
20
|
+
expect(prefixAllButNthSubstring(str, /abc/gi, 3, 1)).toBe(
|
21
|
+
"X-abc 123 abc 456 X-abc 789 abc abc",
|
22
|
+
);
|
23
|
+
});
|
24
|
+
});
|
@@ -0,0 +1,13 @@
|
|
1
|
+
export function prefixAllButNthSubstring(
|
2
|
+
str: string,
|
3
|
+
pattern: RegExp,
|
4
|
+
substringsCount: number,
|
5
|
+
skippedIndex: number,
|
6
|
+
) {
|
7
|
+
let occurrence = 0;
|
8
|
+
return str.replace(pattern, (match) => {
|
9
|
+
return occurrence++ === skippedIndex || occurrence > substringsCount
|
10
|
+
? match
|
11
|
+
: `X-${match}`;
|
12
|
+
});
|
13
|
+
}
|
@@ -0,0 +1,52 @@
|
|
1
|
+
import { describe, test, expect } from "vitest";
|
2
|
+
import { checkVersionCompatibility } from "./versions";
|
3
|
+
|
4
|
+
describe("versions compatibility", () => {
|
5
|
+
test("throws if major version mismatches", () => {
|
6
|
+
expect(() => {
|
7
|
+
checkVersionCompatibility("1.2.3", "2.1.3");
|
8
|
+
}).toThrowError(
|
9
|
+
"SDK version 2.1.3 is incompatible with prover version 1.2.3",
|
10
|
+
);
|
11
|
+
});
|
12
|
+
|
13
|
+
test("throws if major version mismatches with metadata after dash", () => {
|
14
|
+
expect(() => {
|
15
|
+
checkVersionCompatibility("1.2.3-dev-123456-deadbeef", "2.1.3");
|
16
|
+
}).toThrowError(
|
17
|
+
"SDK version 2.1.3 is incompatible with prover version 1.2.3-dev-123456-deadbeef",
|
18
|
+
);
|
19
|
+
});
|
20
|
+
|
21
|
+
test("throws if major version is 0 and minor version mismatches", () => {
|
22
|
+
expect(() => {
|
23
|
+
checkVersionCompatibility("0.2.3", "0.1.3");
|
24
|
+
}).toThrowError(
|
25
|
+
"SDK version 0.1.3 is incompatible with prover version 0.2.3",
|
26
|
+
);
|
27
|
+
});
|
28
|
+
|
29
|
+
test("does not throw if major and minor versions match", () => {
|
30
|
+
expect(() => {
|
31
|
+
checkVersionCompatibility("1.2.3", "1.2.13");
|
32
|
+
}).not.toThrow();
|
33
|
+
});
|
34
|
+
|
35
|
+
test("does not throw if major version is >0 and minor mismatches", () => {
|
36
|
+
expect(() => {
|
37
|
+
checkVersionCompatibility("1.2.3", "1.5.8");
|
38
|
+
}).not.toThrow();
|
39
|
+
});
|
40
|
+
|
41
|
+
test("does not throw if major version is 0 and minor matches", () => {
|
42
|
+
expect(() => {
|
43
|
+
checkVersionCompatibility("0.2.3", "0.2.7");
|
44
|
+
}).not.toThrow();
|
45
|
+
});
|
46
|
+
|
47
|
+
test("works for semvers with metadata after dash", () => {
|
48
|
+
expect(() => {
|
49
|
+
checkVersionCompatibility("0.2.3-dev-123456-deadbeef", "0.2.7");
|
50
|
+
}).not.toThrow();
|
51
|
+
});
|
52
|
+
});
|
@@ -0,0 +1,31 @@
|
|
1
|
+
import { parse, type SemVer } from "semver";
|
2
|
+
import { VersionError } from "../lib/errors";
|
3
|
+
|
4
|
+
function safeParseSemver(semverString: string): SemVer {
|
5
|
+
const parsed = parse(semverString);
|
6
|
+
if (parsed === null) {
|
7
|
+
throw new VersionError(`Invalid semver string: ${semverString}`);
|
8
|
+
}
|
9
|
+
return parsed;
|
10
|
+
}
|
11
|
+
|
12
|
+
export function checkVersionCompatibility(
|
13
|
+
proverSemver: string,
|
14
|
+
sdkSemver: string,
|
15
|
+
) {
|
16
|
+
const { major: proverMajor, minor: proverMinor } =
|
17
|
+
safeParseSemver(proverSemver);
|
18
|
+
const { major: sdkMajor, minor: sdkMinor } = safeParseSemver(sdkSemver);
|
19
|
+
|
20
|
+
if (proverMajor !== sdkMajor) {
|
21
|
+
throw new VersionError(
|
22
|
+
`SDK version ${sdkSemver} is incompatible with prover version ${proverSemver}`,
|
23
|
+
);
|
24
|
+
}
|
25
|
+
|
26
|
+
if (proverMajor === 0 && proverMinor !== sdkMinor) {
|
27
|
+
throw new VersionError(
|
28
|
+
`SDK version ${sdkSemver} is incompatible with prover version ${proverSemver}`,
|
29
|
+
);
|
30
|
+
}
|
31
|
+
}
|
@@ -0,0 +1,58 @@
|
|
1
|
+
import {
|
2
|
+
type CallContext,
|
3
|
+
type CallParams,
|
4
|
+
type VCallResponse,
|
5
|
+
} from "./lib/types/vlayer";
|
6
|
+
import { parseVCallResponseError } from "./lib/errors";
|
7
|
+
import debug from "debug";
|
8
|
+
|
9
|
+
const log = debug("vlayer:v_call");
|
10
|
+
|
11
|
+
function v_callBody(call: CallParams, context: CallContext) {
|
12
|
+
console.log("call", call);
|
13
|
+
console.log("context", context);
|
14
|
+
return {
|
15
|
+
method: "v_call",
|
16
|
+
params: [call, context],
|
17
|
+
id: 1,
|
18
|
+
jsonrpc: "2.0",
|
19
|
+
};
|
20
|
+
}
|
21
|
+
|
22
|
+
export async function v_call(
|
23
|
+
call: CallParams,
|
24
|
+
context: CallContext,
|
25
|
+
url: string = "http://127.0.0.1:3000",
|
26
|
+
token?: string,
|
27
|
+
): Promise<VCallResponse> {
|
28
|
+
const headers: Record<string, string> = {
|
29
|
+
"Content-Type": "application/json",
|
30
|
+
};
|
31
|
+
if (token !== undefined) {
|
32
|
+
headers["Authorization"] = "Bearer " + token;
|
33
|
+
}
|
34
|
+
const response = await fetch(url, {
|
35
|
+
method: "POST",
|
36
|
+
body: JSON.stringify(v_callBody(call, context)),
|
37
|
+
headers,
|
38
|
+
});
|
39
|
+
log("response", response);
|
40
|
+
if (!response.ok) {
|
41
|
+
throw new Error(`HTTP error! status: ${response.status}`);
|
42
|
+
}
|
43
|
+
const response_json = await response.json();
|
44
|
+
log("response_json", response_json);
|
45
|
+
assertObject(response_json);
|
46
|
+
if ("error" in response_json) {
|
47
|
+
throw parseVCallResponseError(
|
48
|
+
response_json.error as { message: string | undefined },
|
49
|
+
);
|
50
|
+
}
|
51
|
+
return response_json as Promise<VCallResponse>;
|
52
|
+
}
|
53
|
+
|
54
|
+
function assertObject(x: unknown): asserts x is object {
|
55
|
+
if (typeof x !== "object") {
|
56
|
+
throw new Error("Expected object");
|
57
|
+
}
|
58
|
+
}
|
@@ -0,0 +1,48 @@
|
|
1
|
+
import {
|
2
|
+
type VGetProofReceiptParams,
|
3
|
+
type VGetProofReceiptResponse,
|
4
|
+
} from "./lib/types/vlayer";
|
5
|
+
import { parseVCallResponseError } from "./lib/errors";
|
6
|
+
import { vGetProofReceiptSchema } from "./lib/types/vlayer";
|
7
|
+
import debug from "debug";
|
8
|
+
|
9
|
+
const log = debug("vlayer:v_getProofReceipt");
|
10
|
+
|
11
|
+
function v_getProofReceiptBody(params: VGetProofReceiptParams) {
|
12
|
+
return {
|
13
|
+
method: "v_getProofReceipt",
|
14
|
+
params: params,
|
15
|
+
id: 1,
|
16
|
+
jsonrpc: "2.0",
|
17
|
+
};
|
18
|
+
}
|
19
|
+
|
20
|
+
export async function v_getProofReceipt(
|
21
|
+
params: VGetProofReceiptParams,
|
22
|
+
url: string = "http://127.0.0.1:3000",
|
23
|
+
): Promise<VGetProofReceiptResponse> {
|
24
|
+
const response = await fetch(url, {
|
25
|
+
method: "POST",
|
26
|
+
body: JSON.stringify(v_getProofReceiptBody(params)),
|
27
|
+
headers: { "Content-Type": "application/json" },
|
28
|
+
});
|
29
|
+
log("response", response);
|
30
|
+
if (!response.ok) {
|
31
|
+
throw new Error(`HTTP error! status: ${response.status}`);
|
32
|
+
}
|
33
|
+
const response_json = await response.json();
|
34
|
+
log("response_json", response_json);
|
35
|
+
assertObject(response_json);
|
36
|
+
if ("error" in response_json) {
|
37
|
+
throw parseVCallResponseError(
|
38
|
+
response_json.error as { message: string | undefined },
|
39
|
+
);
|
40
|
+
}
|
41
|
+
return vGetProofReceiptSchema.parse(response_json);
|
42
|
+
}
|
43
|
+
|
44
|
+
function assertObject(x: unknown): asserts x is object {
|
45
|
+
if (typeof x !== "object") {
|
46
|
+
throw new Error("Expected object");
|
47
|
+
}
|
48
|
+
}
|
@@ -0,0 +1,68 @@
|
|
1
|
+
import debug from "debug";
|
2
|
+
|
3
|
+
const log = debug("vlayer:v_versions");
|
4
|
+
|
5
|
+
const v_versionsBody = {
|
6
|
+
method: "v_versions",
|
7
|
+
params: [],
|
8
|
+
id: 1,
|
9
|
+
jsonrpc: "2.0",
|
10
|
+
};
|
11
|
+
|
12
|
+
interface VVersionsResponseResult {
|
13
|
+
call_guest_id: string;
|
14
|
+
chain_guest_id: string;
|
15
|
+
api_version: string;
|
16
|
+
}
|
17
|
+
|
18
|
+
export interface VVersionsResponse {
|
19
|
+
jsonrpc: string;
|
20
|
+
result: VVersionsResponseResult;
|
21
|
+
id: number;
|
22
|
+
}
|
23
|
+
|
24
|
+
export async function v_versions(
|
25
|
+
url: string = "http://127.0.0.1:3000",
|
26
|
+
): Promise<VVersionsResponse> {
|
27
|
+
const response = await fetch(url, {
|
28
|
+
method: "POST",
|
29
|
+
body: JSON.stringify(v_versionsBody),
|
30
|
+
headers: { "Content-Type": "application/json" },
|
31
|
+
});
|
32
|
+
log("response", response);
|
33
|
+
if (!response.ok) {
|
34
|
+
throw new Error(`HTTP error! status: ${response.status}`);
|
35
|
+
}
|
36
|
+
const response_json = await response.json();
|
37
|
+
assertResponseObject(response_json);
|
38
|
+
return response_json;
|
39
|
+
}
|
40
|
+
|
41
|
+
function isFieldAString(
|
42
|
+
x: object,
|
43
|
+
field: keyof VVersionsResponseResult,
|
44
|
+
): boolean {
|
45
|
+
return (
|
46
|
+
field in x && typeof (x as VVersionsResponseResult)[field] === "string"
|
47
|
+
);
|
48
|
+
}
|
49
|
+
|
50
|
+
function assertResponseObject(x: unknown): asserts x is VVersionsResponse {
|
51
|
+
if (!x || typeof x !== "object") {
|
52
|
+
throw new Error("Expected object");
|
53
|
+
}
|
54
|
+
if (!("result" in x) || !x.result || typeof x.result !== "object") {
|
55
|
+
throw new Error(
|
56
|
+
`Unexpected \`v_versions\` response: ${JSON.stringify(x, null, 2)}`,
|
57
|
+
);
|
58
|
+
}
|
59
|
+
if (
|
60
|
+
!isFieldAString(x.result, "call_guest_id") ||
|
61
|
+
!isFieldAString(x.result, "chain_guest_id") ||
|
62
|
+
!isFieldAString(x.result, "api_version")
|
63
|
+
) {
|
64
|
+
throw new Error(
|
65
|
+
`Unexpected \`v_versions\` response: ${JSON.stringify(x, null, 2)}`,
|
66
|
+
);
|
67
|
+
}
|
68
|
+
}
|
@@ -0,0 +1,15 @@
|
|
1
|
+
import {
|
2
|
+
type WebProofRequest,
|
3
|
+
type WebProofRequestInput,
|
4
|
+
} from "../lib/types/webProofProvider";
|
5
|
+
|
6
|
+
export const createWebProofRequest = ({
|
7
|
+
logoUrl,
|
8
|
+
steps,
|
9
|
+
}: WebProofRequestInput) => {
|
10
|
+
return {
|
11
|
+
logoUrl,
|
12
|
+
steps,
|
13
|
+
isWebProof: true,
|
14
|
+
} as WebProofRequest;
|
15
|
+
};
|
@@ -0,0 +1,122 @@
|
|
1
|
+
import { createExtensionWebProofProvider } from "./extension";
|
2
|
+
import { describe, it, expect, vi } from "vitest";
|
3
|
+
import { expectUrl, startPage, notarize } from "../steps";
|
4
|
+
import { StepValidationError } from "../../../web-proof-commons";
|
5
|
+
|
6
|
+
const chrome = {
|
7
|
+
runtime: {
|
8
|
+
disconnectCallbacks: [] as (() => void)[],
|
9
|
+
connect: vi.fn().mockImplementation(() => ({
|
10
|
+
postMessage: vi.fn().mockImplementation(() => {}),
|
11
|
+
onMessage: {
|
12
|
+
addListener: vi.fn().mockImplementation(() => {}),
|
13
|
+
},
|
14
|
+
onDisconnect: {
|
15
|
+
addListener: vi.fn().mockImplementation((callback: () => void) => {
|
16
|
+
chrome.runtime.disconnectCallbacks.push(callback);
|
17
|
+
}),
|
18
|
+
},
|
19
|
+
})),
|
20
|
+
disconnect: vi.fn().mockImplementation(() => {
|
21
|
+
chrome.runtime.disconnectCallbacks.forEach((callback) => {
|
22
|
+
callback();
|
23
|
+
});
|
24
|
+
}),
|
25
|
+
},
|
26
|
+
};
|
27
|
+
|
28
|
+
vi.stubGlobal("chrome", chrome);
|
29
|
+
|
30
|
+
const defaults = {
|
31
|
+
logoUrl: "https://example.com/logo.png",
|
32
|
+
proverCallCommitment: {
|
33
|
+
address: "0x" as `0x${string}`,
|
34
|
+
proverAbi: [],
|
35
|
+
chainId: 1,
|
36
|
+
functionName: "test",
|
37
|
+
commitmentArgs: null as never,
|
38
|
+
},
|
39
|
+
};
|
40
|
+
|
41
|
+
const invalidUrl = "http:/example.com";
|
42
|
+
const invalidUrlPattern = "http://+.test";
|
43
|
+
const validUrl = "https://example.com";
|
44
|
+
const validUrlPattern = "https://example.com/test";
|
45
|
+
const label = "test";
|
46
|
+
|
47
|
+
describe("ExtensionWebProofProvider", () => {
|
48
|
+
it("should properly validate startPage step", () => {
|
49
|
+
const provider = createExtensionWebProofProvider();
|
50
|
+
expect(() =>
|
51
|
+
provider.requestWebProof({
|
52
|
+
...defaults,
|
53
|
+
steps: [startPage(invalidUrl, label)],
|
54
|
+
}),
|
55
|
+
).toThrow(StepValidationError);
|
56
|
+
});
|
57
|
+
|
58
|
+
it("should properly validate expectUrl step", () => {
|
59
|
+
const provider = createExtensionWebProofProvider();
|
60
|
+
expect(() =>
|
61
|
+
provider.requestWebProof({
|
62
|
+
...defaults,
|
63
|
+
steps: [expectUrl(invalidUrlPattern, label)],
|
64
|
+
}),
|
65
|
+
).toThrow(StepValidationError);
|
66
|
+
});
|
67
|
+
|
68
|
+
it("should properly validate notarize step", () => {
|
69
|
+
const provider = createExtensionWebProofProvider();
|
70
|
+
expect(() =>
|
71
|
+
provider.requestWebProof({
|
72
|
+
...defaults,
|
73
|
+
steps: [notarize(invalidUrlPattern, "GET", label)],
|
74
|
+
}),
|
75
|
+
).toThrow(StepValidationError);
|
76
|
+
});
|
77
|
+
|
78
|
+
it("successfully validates all steps", () => {
|
79
|
+
const provider = createExtensionWebProofProvider();
|
80
|
+
expect(() =>
|
81
|
+
provider.requestWebProof({
|
82
|
+
...defaults,
|
83
|
+
steps: [
|
84
|
+
startPage(validUrl, label),
|
85
|
+
expectUrl(validUrlPattern, label),
|
86
|
+
notarize(validUrlPattern, "GET", label),
|
87
|
+
],
|
88
|
+
}),
|
89
|
+
).not.toThrow(StepValidationError);
|
90
|
+
});
|
91
|
+
|
92
|
+
it("should properly work backward compatible way with only urls used", () => {
|
93
|
+
const provider = createExtensionWebProofProvider();
|
94
|
+
|
95
|
+
expect(() =>
|
96
|
+
provider.requestWebProof({
|
97
|
+
...defaults,
|
98
|
+
steps: [
|
99
|
+
startPage(validUrl, label),
|
100
|
+
expectUrl(validUrl, label),
|
101
|
+
expectUrl(validUrl, label),
|
102
|
+
notarize(validUrl, "GET", label),
|
103
|
+
],
|
104
|
+
}),
|
105
|
+
).not.toThrow();
|
106
|
+
});
|
107
|
+
|
108
|
+
it("should reconnect extension on disconnect", () => {
|
109
|
+
const provider = createExtensionWebProofProvider();
|
110
|
+
provider.requestWebProof({
|
111
|
+
...defaults,
|
112
|
+
steps: [
|
113
|
+
startPage(validUrl, label),
|
114
|
+
expectUrl(validUrlPattern, label),
|
115
|
+
notarize(validUrlPattern, "GET", label),
|
116
|
+
],
|
117
|
+
});
|
118
|
+
chrome.runtime.connect.mockClear();
|
119
|
+
chrome.runtime.disconnect();
|
120
|
+
expect(chrome.runtime.connect).toHaveBeenCalled();
|
121
|
+
});
|
122
|
+
});
|