@provex/eas 1.9.2 → 1.10.0-rc.20260912192040.fdbb8fc
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/dist/eas-builder.d.ts +2 -1
- package/dist/eas-builder.js +4 -13
- package/dist/eas-sdk-namespace.browser.d.ts +14 -0
- package/dist/eas-sdk-namespace.browser.js +14 -0
- package/dist/eas-sdk-namespace.node.d.ts +23 -0
- package/dist/eas-sdk-namespace.node.js +24 -0
- package/dist/offchain-verify.d.ts +25 -0
- package/dist/offchain-verify.js +21 -0
- package/dist/offchain-verify.test.js +31 -1
- package/dist/viem-only.test.js +11 -4
- package/package.json +11 -2
package/dist/eas-builder.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import * as
|
|
1
|
+
import type * as EasSdkModule from '@ethereum-attestation-service/eas-sdk';
|
|
2
2
|
import type { Address, Hex, LocalAccount } from 'viem';
|
|
3
|
+
declare const easSdkNamespace: typeof EasSdkModule;
|
|
3
4
|
type OffchainAttestationVersionEnum = (typeof easSdkNamespace)['OffchainAttestationVersion'][keyof (typeof easSdkNamespace)['OffchainAttestationVersion']];
|
|
4
5
|
/**
|
|
5
6
|
* @provex/eas — off-chain Ethereum Attestation Service attestation builder.
|
package/dist/eas-builder.js
CHANGED
|
@@ -1,18 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
// unworkable across our two test runners:
|
|
3
|
-
// 1. The package ships CJS (`main`) + ESM (`module`), with no `"type"`
|
|
4
|
-
// field at root and no `.js` extensions inside its ESM build.
|
|
5
|
-
// 2. tsx (Node strict ESM) refuses the ESM build (extensionless paths)
|
|
6
|
-
// and falls back to the CJS build, where the named exports live on
|
|
7
|
-
// `default` — `* as` gives `{ default, 'module.exports' }`.
|
|
8
|
-
// vitest (Vite resolver) loads the ESM build directly, where
|
|
9
|
-
// `* as` gives a flat namespace of named exports.
|
|
10
|
-
//
|
|
11
|
-
// Resolve once, then reach into `default` if the flat namespace is empty.
|
|
12
|
-
// This is the resolver-agnostic interop shape.
|
|
13
|
-
import * as easSdkNamespace from '@ethereum-attestation-service/eas-sdk';
|
|
1
|
+
import easSdkNamespaceRaw from '#eas-sdk-namespace';
|
|
14
2
|
import { zeroAddress } from 'viem';
|
|
15
3
|
import { getAgeSchemaUid, getEasAddress, getReleaseSchemaUid, getSchemaUid, PROVEX_AGE_SCHEMA, PROVEX_IDENTITY_SCHEMA, PROVEX_RELEASE_SCHEMA, } from './eas.js';
|
|
4
|
+
// Runtime namespace, typed against the erased `import type` above so the shims
|
|
5
|
+
// below (`typeof easSdkNamespace`) keep their exact SDK types.
|
|
6
|
+
const easSdkNamespace = easSdkNamespaceRaw;
|
|
16
7
|
const easSdkExports = (() => {
|
|
17
8
|
const flat = easSdkNamespace;
|
|
18
9
|
if (typeof flat.SchemaEncoder === 'function')
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser resolution of the Ethereum Attestation Service software development
|
|
3
|
+
* kit.
|
|
4
|
+
*
|
|
5
|
+
* A bundler (Vite/Rollup, esbuild in browser mode) compiles this build, and
|
|
6
|
+
* unlike plain Node it adds the missing file extensions throughout the kit's
|
|
7
|
+
* ECMAScript build as it inlines the code — so an ordinary namespace import
|
|
8
|
+
* works and produces the flat shape the browser bundle expects. The Node twin's
|
|
9
|
+
* `createRequire` has no browser equivalent (there is no `node:module`), which
|
|
10
|
+
* is the whole reason these two files exist separately; `#eas-sdk-namespace` in
|
|
11
|
+
* the package's `imports` map selects this one under the `browser` condition.
|
|
12
|
+
*/
|
|
13
|
+
import * as easSdkNamespace from '@ethereum-attestation-service/eas-sdk';
|
|
14
|
+
export default easSdkNamespace;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser resolution of the Ethereum Attestation Service software development
|
|
3
|
+
* kit.
|
|
4
|
+
*
|
|
5
|
+
* A bundler (Vite/Rollup, esbuild in browser mode) compiles this build, and
|
|
6
|
+
* unlike plain Node it adds the missing file extensions throughout the kit's
|
|
7
|
+
* ECMAScript build as it inlines the code — so an ordinary namespace import
|
|
8
|
+
* works and produces the flat shape the browser bundle expects. The Node twin's
|
|
9
|
+
* `createRequire` has no browser equivalent (there is no `node:module`), which
|
|
10
|
+
* is the whole reason these two files exist separately; `#eas-sdk-namespace` in
|
|
11
|
+
* the package's `imports` map selects this one under the `browser` condition.
|
|
12
|
+
*/
|
|
13
|
+
import * as easSdkNamespace from '@ethereum-attestation-service/eas-sdk';
|
|
14
|
+
export default easSdkNamespace;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Node resolution of the Ethereum Attestation Service software development kit.
|
|
3
|
+
*
|
|
4
|
+
* Plain Node's ECMAScript-module resolver rejects the kit's published
|
|
5
|
+
* ECMAScript build outright: its relative imports carry no file extension
|
|
6
|
+
* (`export ... from './eas'`), and so do a transitive dependency's subpaths
|
|
7
|
+
* (`lodash/isEqual`). Node's own resolver treats those as fatal —
|
|
8
|
+
* ERR_MODULE_NOT_FOUND — which crashed the gate's identity-verify route the
|
|
9
|
+
* instant it loaded `@provex/eas/build` at runtime (the built gate runs exactly
|
|
10
|
+
* that resolver, with this package left external).
|
|
11
|
+
*
|
|
12
|
+
* CommonJS `require` resolves every one of those extensionless paths
|
|
13
|
+
* transparently, across the whole dependency subtree, so this file forces the
|
|
14
|
+
* kit's `require` condition through createRequire. Callers load it lazily, so
|
|
15
|
+
* the kit still never touches the process boot path — the boot-safety property
|
|
16
|
+
* the gate depends on is preserved.
|
|
17
|
+
*
|
|
18
|
+
* The browser twin (`eas-sdk-namespace.browser.ts`) resolves the kit the other
|
|
19
|
+
* way; `#eas-sdk-namespace` in the package's `imports` map picks between them.
|
|
20
|
+
*/
|
|
21
|
+
import type * as EasSdkModule from '@ethereum-attestation-service/eas-sdk';
|
|
22
|
+
declare const easSdkNamespace: typeof EasSdkModule;
|
|
23
|
+
export default easSdkNamespace;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Node resolution of the Ethereum Attestation Service software development kit.
|
|
3
|
+
*
|
|
4
|
+
* Plain Node's ECMAScript-module resolver rejects the kit's published
|
|
5
|
+
* ECMAScript build outright: its relative imports carry no file extension
|
|
6
|
+
* (`export ... from './eas'`), and so do a transitive dependency's subpaths
|
|
7
|
+
* (`lodash/isEqual`). Node's own resolver treats those as fatal —
|
|
8
|
+
* ERR_MODULE_NOT_FOUND — which crashed the gate's identity-verify route the
|
|
9
|
+
* instant it loaded `@provex/eas/build` at runtime (the built gate runs exactly
|
|
10
|
+
* that resolver, with this package left external).
|
|
11
|
+
*
|
|
12
|
+
* CommonJS `require` resolves every one of those extensionless paths
|
|
13
|
+
* transparently, across the whole dependency subtree, so this file forces the
|
|
14
|
+
* kit's `require` condition through createRequire. Callers load it lazily, so
|
|
15
|
+
* the kit still never touches the process boot path — the boot-safety property
|
|
16
|
+
* the gate depends on is preserved.
|
|
17
|
+
*
|
|
18
|
+
* The browser twin (`eas-sdk-namespace.browser.ts`) resolves the kit the other
|
|
19
|
+
* way; `#eas-sdk-namespace` in the package's `imports` map picks between them.
|
|
20
|
+
*/
|
|
21
|
+
import { createRequire } from 'node:module';
|
|
22
|
+
const requireFromHere = createRequire(import.meta.url);
|
|
23
|
+
const easSdkNamespace = requireFromHere('@ethereum-attestation-service/eas-sdk');
|
|
24
|
+
export default easSdkNamespace;
|
|
@@ -78,6 +78,31 @@ export declare const isOffchainAttestationShape: (value: unknown) => value is Of
|
|
|
78
78
|
* attestation."
|
|
79
79
|
*/
|
|
80
80
|
export declare const liftWireOffchainAttestation: (wire: Record<string, unknown>) => OffchainEasAttestation | null;
|
|
81
|
+
/**
|
|
82
|
+
* Wire form of an off-chain attestation: identical to
|
|
83
|
+
* {@link OffchainEasAttestation} except `message.time` and
|
|
84
|
+
* `message.expirationTime` are base-10 strings rather than `bigint`.
|
|
85
|
+
*/
|
|
86
|
+
export type WireOffchainAttestation = Omit<OffchainEasAttestation, 'message'> & {
|
|
87
|
+
message: Omit<OffchainEasAttestation['message'], 'time' | 'expirationTime'> & {
|
|
88
|
+
time: string;
|
|
89
|
+
expirationTime: string;
|
|
90
|
+
};
|
|
91
|
+
};
|
|
92
|
+
/**
|
|
93
|
+
* Lower a signed off-chain attestation to its wire form, so it survives
|
|
94
|
+
* `JSON.stringify` — which throws outright on `bigint`.
|
|
95
|
+
*
|
|
96
|
+
* This is the exact inverse of {@link liftWireOffchainAttestation}, and the two
|
|
97
|
+
* are pinned as a round-trip pair by a test. Every server returning an
|
|
98
|
+
* attestation to a client must go through here: a raw `res.json(attestation)`
|
|
99
|
+
* does not fail gracefully, it throws `TypeError: Do not know how to serialize
|
|
100
|
+
* a BigInt` and turns a successful issuance into a 500.
|
|
101
|
+
*
|
|
102
|
+
* Only the two bigint fields are touched — the signature and every hashed field
|
|
103
|
+
* are copied verbatim, so lowering can never invalidate the attestation.
|
|
104
|
+
*/
|
|
105
|
+
export declare const toWireOffchainAttestation: (attestation: OffchainEasAttestation) => WireOffchainAttestation;
|
|
81
106
|
export type OffchainSignatureCheck = {
|
|
82
107
|
/** Signature recovered cleanly and (when an expected signer was given) matched. */
|
|
83
108
|
valid: boolean;
|
package/dist/offchain-verify.js
CHANGED
|
@@ -170,6 +170,27 @@ export const liftWireOffchainAttestation = (wire) => {
|
|
|
170
170
|
},
|
|
171
171
|
};
|
|
172
172
|
};
|
|
173
|
+
/**
|
|
174
|
+
* Lower a signed off-chain attestation to its wire form, so it survives
|
|
175
|
+
* `JSON.stringify` — which throws outright on `bigint`.
|
|
176
|
+
*
|
|
177
|
+
* This is the exact inverse of {@link liftWireOffchainAttestation}, and the two
|
|
178
|
+
* are pinned as a round-trip pair by a test. Every server returning an
|
|
179
|
+
* attestation to a client must go through here: a raw `res.json(attestation)`
|
|
180
|
+
* does not fail gracefully, it throws `TypeError: Do not know how to serialize
|
|
181
|
+
* a BigInt` and turns a successful issuance into a 500.
|
|
182
|
+
*
|
|
183
|
+
* Only the two bigint fields are touched — the signature and every hashed field
|
|
184
|
+
* are copied verbatim, so lowering can never invalidate the attestation.
|
|
185
|
+
*/
|
|
186
|
+
export const toWireOffchainAttestation = (attestation) => ({
|
|
187
|
+
...attestation,
|
|
188
|
+
message: {
|
|
189
|
+
...attestation.message,
|
|
190
|
+
time: attestation.message.time.toString(),
|
|
191
|
+
expirationTime: attestation.message.expirationTime.toString(),
|
|
192
|
+
},
|
|
193
|
+
});
|
|
173
194
|
/**
|
|
174
195
|
* Recover the signing address from an off-chain EAS attestation and
|
|
175
196
|
* report whether the signature is cryptographically valid.
|
|
@@ -3,7 +3,7 @@ import assert from 'node:assert/strict';
|
|
|
3
3
|
import { privateKeyToAccount } from 'viem/accounts';
|
|
4
4
|
import { buildOffchainEasAttestation, encodeIdentityClaim, encodeReleaseClaim, } from './eas-builder.js';
|
|
5
5
|
import { EAS_MAINNET_ADDRESS, PROVEX_IDENTITY_SCHEMA_UID } from './eas.js';
|
|
6
|
-
import { decodeIdentityClaim, decodeReleaseClaim, liftWireOffchainAttestation, recoverOffchainAttestationSigner, } from './offchain-verify.js';
|
|
6
|
+
import { decodeIdentityClaim, decodeReleaseClaim, liftWireOffchainAttestation, recoverOffchainAttestationSigner, toWireOffchainAttestation, } from './offchain-verify.js';
|
|
7
7
|
const TEST_KEY = '0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d';
|
|
8
8
|
const SAMPLE_CLAIM = {
|
|
9
9
|
platformHash: ('0x' + '11'.repeat(32)),
|
|
@@ -51,6 +51,36 @@ describe('liftWireOffchainAttestation', () => {
|
|
|
51
51
|
assert.equal(liftWireOffchainAttestation({ not: 'an attestation' }), null);
|
|
52
52
|
});
|
|
53
53
|
});
|
|
54
|
+
describe('toWireOffchainAttestation', () => {
|
|
55
|
+
it('produces output JSON.stringify accepts, which the raw attestation does not', async () => {
|
|
56
|
+
const signed = await signedSample();
|
|
57
|
+
// The whole reason this function exists: the raw form is unserializable.
|
|
58
|
+
assert.throws(() => JSON.stringify(signed), TypeError);
|
|
59
|
+
assert.doesNotThrow(() => JSON.stringify(toWireOffchainAttestation(signed)));
|
|
60
|
+
});
|
|
61
|
+
it('agrees with the bigint JSON replacer the UI already consumes', async () => {
|
|
62
|
+
const signed = await signedSample();
|
|
63
|
+
// `toWire` is an independent implementation (a JSON replacer, matching the
|
|
64
|
+
// notary-gate's existing convention). Agreement proves the new producer
|
|
65
|
+
// emits the exact shape every existing consumer is already parsing —
|
|
66
|
+
// otherwise this helper would silently fork the wire format.
|
|
67
|
+
assert.deepEqual(JSON.parse(JSON.stringify(toWireOffchainAttestation(signed))), toWire(signed));
|
|
68
|
+
});
|
|
69
|
+
it('round-trips through liftWireOffchainAttestation unchanged', async () => {
|
|
70
|
+
const signed = await signedSample();
|
|
71
|
+
const lifted = liftWireOffchainAttestation(JSON.parse(JSON.stringify(toWireOffchainAttestation(signed))));
|
|
72
|
+
assert.ok(lifted);
|
|
73
|
+
assert.deepEqual(lifted, signed);
|
|
74
|
+
});
|
|
75
|
+
it('preserves the signature, so lowering cannot invalidate an attestation', async () => {
|
|
76
|
+
const signed = await signedSample();
|
|
77
|
+
const lifted = liftWireOffchainAttestation(JSON.parse(JSON.stringify(toWireOffchainAttestation(signed))));
|
|
78
|
+
assert.ok(lifted);
|
|
79
|
+
const check = await recoverOffchainAttestationSigner(lifted);
|
|
80
|
+
assert.equal(check.valid, true);
|
|
81
|
+
assert.equal(check.signer?.toLowerCase(), privateKeyToAccount(TEST_KEY).address.toLowerCase());
|
|
82
|
+
});
|
|
83
|
+
});
|
|
54
84
|
describe('recoverOffchainAttestationSigner', () => {
|
|
55
85
|
it('recovers the gate signer from a genuine attestation', async () => {
|
|
56
86
|
const attester = privateKeyToAccount(TEST_KEY);
|
package/dist/viem-only.test.js
CHANGED
|
@@ -69,11 +69,18 @@ describe('viem-only invariant for @provex/eas/verify', () => {
|
|
|
69
69
|
const reached = reachablePackageSpecifiers(resolve(HERE, 'offchain-verify.ts'));
|
|
70
70
|
assert.deepEqual([...reached].sort(), ['viem']);
|
|
71
71
|
});
|
|
72
|
-
it('confirms the
|
|
72
|
+
it('confirms the software development kit IS reached where used, proving the walker works', () => {
|
|
73
73
|
// WHY: a graph walker that silently resolves nothing would make the two
|
|
74
|
-
// assertions above vacuously pass
|
|
75
|
-
//
|
|
76
|
-
|
|
74
|
+
// assertions above vacuously pass, so it needs a known-positive control —
|
|
75
|
+
// a file that genuinely imports the software development kit at runtime.
|
|
76
|
+
//
|
|
77
|
+
// `eas-builder.ts` reaches the kit only transitively now, through the
|
|
78
|
+
// `#eas-sdk-namespace` conditional import (a bare specifier the walker
|
|
79
|
+
// treats as a leaf, exactly as it treats `viem`). The direct static import
|
|
80
|
+
// lives in `eas-sdk-namespace.browser.ts` — the browser half of that
|
|
81
|
+
// conditional — so that is the control. This still proves the walker
|
|
82
|
+
// detects the kit, and pins that the browser namespace path pulls it in.
|
|
83
|
+
const reached = reachablePackageSpecifiers(resolve(HERE, 'eas-sdk-namespace.browser.ts'));
|
|
77
84
|
assert.ok(reached.has(EAS_SOFTWARE_DEVELOPMENT_KIT));
|
|
78
85
|
});
|
|
79
86
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@provex/eas",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.10.0-rc.20260912192040.fdbb8fc",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"type": "module",
|
|
@@ -9,6 +9,15 @@
|
|
|
9
9
|
"url": "git+https://github.com/provex-tech/monorepo.git",
|
|
10
10
|
"directory": "packages/eas"
|
|
11
11
|
},
|
|
12
|
+
"publishConfig": {
|
|
13
|
+
"access": "public"
|
|
14
|
+
},
|
|
15
|
+
"imports": {
|
|
16
|
+
"#eas-sdk-namespace": {
|
|
17
|
+
"browser": "./dist/eas-sdk-namespace.browser.js",
|
|
18
|
+
"default": "./dist/eas-sdk-namespace.node.js"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
12
21
|
"exports": {
|
|
13
22
|
".": {
|
|
14
23
|
"import": "./dist/index.js",
|
|
@@ -56,4 +65,4 @@
|
|
|
56
65
|
"typescript": "^5.9.2",
|
|
57
66
|
"viem": "^2.38.0"
|
|
58
67
|
}
|
|
59
|
-
}
|
|
68
|
+
}
|