@nevermined-io/core-kit 0.1.20 → 0.1.22

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.
@@ -1,14 +1,44 @@
1
1
  import { PublicClient } from 'viem';
2
2
  import { SmartAccount } from 'viem/account-abstraction';
3
- export type AgentX402AccessToken = {
4
- x402Version: number;
5
- scheme: string;
3
+ /**
4
+ * x402 Resource information
5
+ */
6
+ export type X402Resource = {
7
+ url: string;
8
+ description?: string;
9
+ mimeType?: string;
10
+ };
11
+ /**
12
+ * x402 Accepted scheme - nvm:erc4337 specific fields
13
+ */
14
+ export type X402Accepted = {
15
+ scheme: 'nvm:erc4337';
6
16
  network: string;
7
- subscriberAddress: `0x${string}`;
8
- agentId?: string;
17
+ planId: string;
18
+ extra: {
19
+ version: string;
20
+ agentId?: string;
21
+ httpVerb?: string;
22
+ };
23
+ };
24
+ /**
25
+ * x402 Access Token aligned with x402 v2 spec
26
+ *
27
+ * Key changes from previous version:
28
+ * - `subscriberAddress` moved to `payload.authorization.from`
29
+ * - `planId` is now in `accepted` (required)
30
+ * - `agentId` moved to `accepted.extra.agentId` (optional)
31
+ * - `scheme` and `network` moved to `accepted`
32
+ * - Added `resource` and `extensions` for x402 v2 alignment
33
+ */
34
+ export type AgentX402AccessToken = {
35
+ x402Version: 2;
36
+ resource?: X402Resource;
37
+ accepted: X402Accepted;
9
38
  payload: {
10
39
  signature: `0x${string}`;
11
40
  authorization: {
41
+ from: `0x${string}`;
12
42
  sessionKeysProvider: string;
13
43
  sessionKeys: {
14
44
  id: string;
@@ -16,6 +46,7 @@ export type AgentX402AccessToken = {
16
46
  }[];
17
47
  };
18
48
  };
49
+ extensions: Record<string, unknown>;
19
50
  };
20
51
  export type NeverminedTypedDataDomain = {
21
52
  name: 'Nevermined';
@@ -36,6 +67,10 @@ export type NeverminedTypedData = {
36
67
  type: 'SessionKey[]';
37
68
  }];
38
69
  Authorization: [
70
+ {
71
+ name: 'from';
72
+ type: 'address';
73
+ },
39
74
  {
40
75
  name: 'sessionKeysProvider';
41
76
  type: 'string';
@@ -48,14 +83,57 @@ export type NeverminedTypedData = {
48
83
  };
49
84
  export declare const NeverminedTypedDataTypes: NeverminedTypedData;
50
85
  export type NeverminedTypedDataMessage = {
86
+ from: `0x${string}`;
51
87
  sessionKeysProvider: string;
52
88
  sessionKeys: {
53
89
  id: string;
54
90
  data: string;
55
91
  }[];
56
92
  };
57
- export declare const generateAgentX402AccessToken: (subscriber: SmartAccount, orderSessionKey: string, burnSessionKey: string, publicClient: PublicClient, agentId?: string) => Promise<AgentX402AccessToken>;
58
- export declare const verifyAgentX402AccessToken: (accessToken: AgentX402AccessToken, publicClient: PublicClient, subscriberAddress: `0x${string}`) => Promise<boolean>;
93
+ /**
94
+ * Options for generating an x402 access token
95
+ */
96
+ export type GenerateX402TokenOptions = {
97
+ subscriber: SmartAccount;
98
+ orderSessionKey: string;
99
+ redeemSessionKey: string;
100
+ publicClient: PublicClient;
101
+ planId: string;
102
+ /** Scheme version (e.g., '1') - stored in accepted.extra.version */
103
+ schemeVersion?: string;
104
+ agentId?: string;
105
+ resource?: X402Resource;
106
+ httpVerb?: string;
107
+ };
108
+ /**
109
+ * Generate an x402 access token aligned with x402 v2 spec
110
+ *
111
+ * @param options - Token generation options
112
+ * @returns x402 access token with EIP-712 signature
113
+ */
114
+ export declare const generateAgentX402AccessToken: (options: GenerateX402TokenOptions) => Promise<AgentX402AccessToken>;
115
+ /**
116
+ * Verify an x402 access token signature
117
+ *
118
+ * @param accessToken - The x402 access token to verify
119
+ * @param publicClient - Viem public client
120
+ * @returns true if signature is valid
121
+ */
122
+ export declare const verifyAgentX402AccessToken: (accessToken: AgentX402AccessToken, publicClient: PublicClient) => Promise<boolean>;
123
+ /**
124
+ * Validation result for x402 token structure
125
+ */
126
+ export type X402TokenValidationResult = {
127
+ valid: boolean;
128
+ errors: string[];
129
+ };
130
+ /**
131
+ * Validate the structure of an x402 access token
132
+ *
133
+ * @param token - The token to validate (unknown type for safety)
134
+ * @returns Validation result with errors if any
135
+ */
136
+ export declare const validateX402TokenStructure: (token: unknown) => X402TokenValidationResult;
59
137
  export declare const b64EncodeX402AccessToken: (accessToken: AgentX402AccessToken) => string;
60
138
  export declare const b64DecodeX402AccessToken: (encoded: string) => AgentX402AccessToken;
61
139
  //# sourceMappingURL=AgentX402AccessToken.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"AgentX402AccessToken.d.ts","sourceRoot":"","sources":["../../src/models/AgentX402AccessToken.ts"],"names":[],"mappings":"AAAA,OAAO,EAAa,YAAY,EAAgC,MAAM,MAAM,CAAA;AAC5E,OAAO,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAEvD,MAAM,MAAM,oBAAoB,GAAG;IACjC,WAAW,EAAE,MAAM,CAAA;IACnB,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;IACf,iBAAiB,EAAE,KAAK,MAAM,EAAE,CAAA;IAChC,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE;QACP,SAAS,EAAE,KAAK,MAAM,EAAE,CAAA;QACxB,aAAa,EAAE;YACb,mBAAmB,EAAE,MAAM,CAAA;YAC3B,WAAW,EAAE;gBAAE,EAAE,EAAE,MAAM,CAAC;gBAAC,IAAI,EAAE,MAAM,CAAA;aAAE,EAAE,CAAA;SAC5C,CAAA;KACF,CAAA;CACF,CAAA;AAED,MAAM,MAAM,yBAAyB,GAAG;IACtC,IAAI,EAAE,YAAY,CAAA;IAClB,OAAO,EAAE,GAAG,CAAA;IACZ,OAAO,EAAE,MAAM,CAAA;IACf,iBAAiB,EAAE,KAAK,MAAM,EAAE,CAAA;CACjC,CAAA;AAED,MAAM,MAAM,mBAAmB,GAAG;IAChC,UAAU,EAAE,CAAC;QAAE,IAAI,EAAE,IAAI,CAAC;QAAC,IAAI,EAAE,QAAQ,CAAA;KAAE,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,QAAQ,CAAA;KAAE,CAAC,CAAA;IAC9E,WAAW,EAAE,CAAC;QAAE,IAAI,EAAE,aAAa,CAAC;QAAC,IAAI,EAAE,cAAc,CAAA;KAAE,CAAC,CAAA;IAC5D,aAAa,EAAE;QACb;YAAE,IAAI,EAAE,qBAAqB,CAAC;YAAC,IAAI,EAAE,QAAQ,CAAA;SAAE;QAC/C;YAAE,IAAI,EAAE,aAAa,CAAC;YAAC,IAAI,EAAE,cAAc,CAAA;SAAE;KAC9C,CAAA;CACF,CAAA;AAED,eAAO,MAAM,wBAAwB,EAAE,mBAUtC,CAAA;AAED,MAAM,MAAM,0BAA0B,GAAG;IACvC,mBAAmB,EAAE,MAAM,CAAA;IAC3B,WAAW,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;CAC5C,CAAA;AAED,eAAO,MAAM,4BAA4B,GACvC,YAAY,YAAY,EACxB,iBAAiB,MAAM,EACvB,gBAAgB,MAAM,EACtB,cAAc,YAAY,EAC1B,UAAU,MAAM,KACf,OAAO,CAAC,oBAAoB,CAiD9B,CAAA;AAED,eAAO,MAAM,0BAA0B,GACrC,aAAa,oBAAoB,EACjC,cAAc,YAAY,EAC1B,mBAAmB,KAAK,MAAM,EAAE,KAC/B,OAAO,CAAC,OAAO,CAiBjB,CAAA;AAED,eAAO,MAAM,wBAAwB,GAAI,aAAa,oBAAoB,KAAG,MAE5E,CAAA;AAED,eAAO,MAAM,wBAAwB,GAAI,SAAS,MAAM,KAAG,oBAE1D,CAAA"}
1
+ {"version":3,"file":"AgentX402AccessToken.d.ts","sourceRoot":"","sources":["../../src/models/AgentX402AccessToken.ts"],"names":[],"mappings":"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"}
@@ -6,16 +6,25 @@ export const NeverminedTypedDataTypes = {
6
6
  ],
7
7
  SessionKeys: [{ name: 'sessionKeys', type: 'SessionKey[]' }],
8
8
  Authorization: [
9
+ { name: 'from', type: 'address' },
9
10
  { name: 'sessionKeysProvider', type: 'string' },
10
11
  { name: 'sessionKeys', type: 'SessionKey[]' },
11
12
  ],
12
13
  };
13
- export const generateAgentX402AccessToken = async (subscriber, orderSessionKey, burnSessionKey, publicClient, agentId) => {
14
+ /**
15
+ * Generate an x402 access token aligned with x402 v2 spec
16
+ *
17
+ * @param options - Token generation options
18
+ * @returns x402 access token with EIP-712 signature
19
+ */
20
+ export const generateAgentX402AccessToken = async (options) => {
21
+ const { subscriber, orderSessionKey, redeemSessionKey, publicClient, planId, schemeVersion = '1', agentId, resource, httpVerb, } = options;
14
22
  // hash the session keys
15
- const burnSessionKeyHash = keccak256(toBytes(burnSessionKey));
23
+ const redeemSessionKeyHash = keccak256(toBytes(redeemSessionKey));
16
24
  const orderSessionKeyHash = keccak256(toBytes(orderSessionKey));
17
- // generate the message
25
+ // generate the message with `from` address
18
26
  const message = {
27
+ from: subscriber.address,
19
28
  sessionKeysProvider: 'zerodev',
20
29
  sessionKeys: [
21
30
  {
@@ -24,7 +33,7 @@ export const generateAgentX402AccessToken = async (subscriber, orderSessionKey,
24
33
  },
25
34
  {
26
35
  id: 'redeem',
27
- data: burnSessionKeyHash,
36
+ data: redeemSessionKeyHash,
28
37
  },
29
38
  ],
30
39
  };
@@ -42,19 +51,39 @@ export const generateAgentX402AccessToken = async (subscriber, orderSessionKey,
42
51
  message,
43
52
  };
44
53
  const signature = await subscriber.signTypedData(eip712Data);
54
+ // Get network in CAIP-2 format (eip155:chainId)
55
+ const chainId = publicClient.chain?.id;
56
+ const network = chainId ? `eip155:${chainId}` : publicClient.chain?.name;
45
57
  return {
46
58
  x402Version: 2,
47
- scheme: 'contract',
48
- network: publicClient.chain?.name,
49
- subscriberAddress: subscriber.address,
50
- ...(agentId && { agentId }),
59
+ ...(resource && { resource }),
60
+ accepted: {
61
+ scheme: 'nvm:erc4337',
62
+ network,
63
+ planId,
64
+ extra: {
65
+ version: schemeVersion,
66
+ ...(agentId && { agentId }),
67
+ ...(httpVerb && { httpVerb }),
68
+ },
69
+ },
51
70
  payload: {
52
71
  signature,
53
72
  authorization: message,
54
73
  },
74
+ extensions: {},
55
75
  };
56
76
  };
57
- export const verifyAgentX402AccessToken = async (accessToken, publicClient, subscriberAddress) => {
77
+ /**
78
+ * Verify an x402 access token signature
79
+ *
80
+ * @param accessToken - The x402 access token to verify
81
+ * @param publicClient - Viem public client
82
+ * @returns true if signature is valid
83
+ */
84
+ export const verifyAgentX402AccessToken = async (accessToken, publicClient) => {
85
+ // Extract subscriber address from payload.authorization.from
86
+ const subscriberAddress = accessToken.payload.authorization.from;
58
87
  const domain = {
59
88
  name: 'Nevermined',
60
89
  version: '1',
@@ -72,6 +101,82 @@ export const verifyAgentX402AccessToken = async (accessToken, publicClient, subs
72
101
  });
73
102
  return isValid;
74
103
  };
104
+ /**
105
+ * Validate the structure of an x402 access token
106
+ *
107
+ * @param token - The token to validate (unknown type for safety)
108
+ * @returns Validation result with errors if any
109
+ */
110
+ export const validateX402TokenStructure = (token) => {
111
+ const errors = [];
112
+ if (!token || typeof token !== 'object') {
113
+ return { valid: false, errors: ['Token must be an object'] };
114
+ }
115
+ const t = token;
116
+ // Validate x402Version
117
+ if (t.x402Version !== 2) {
118
+ errors.push('x402Version must be 2');
119
+ }
120
+ // Validate accepted object
121
+ if (!t.accepted || typeof t.accepted !== 'object') {
122
+ errors.push('accepted is required');
123
+ }
124
+ else {
125
+ const accepted = t.accepted;
126
+ if (accepted.scheme !== 'nvm:erc4337') {
127
+ errors.push('scheme must be nvm:erc4337');
128
+ }
129
+ if (!accepted.network || typeof accepted.network !== 'string') {
130
+ errors.push('network is required in accepted');
131
+ }
132
+ if (!accepted.planId || typeof accepted.planId !== 'string') {
133
+ errors.push('planId is required in accepted');
134
+ }
135
+ // Validate extra
136
+ if (!accepted.extra || typeof accepted.extra !== 'object') {
137
+ errors.push('extra is required in accepted');
138
+ }
139
+ else {
140
+ const extra = accepted.extra;
141
+ if (!extra.version || typeof extra.version !== 'string') {
142
+ errors.push('version is required in accepted.extra');
143
+ }
144
+ }
145
+ }
146
+ // Validate payload
147
+ if (!t.payload || typeof t.payload !== 'object') {
148
+ errors.push('payload is required');
149
+ }
150
+ else {
151
+ const payload = t.payload;
152
+ if (!payload.signature || typeof payload.signature !== 'string') {
153
+ errors.push('signature is required in payload');
154
+ }
155
+ if (!payload.authorization || typeof payload.authorization !== 'object') {
156
+ errors.push('authorization is required in payload');
157
+ }
158
+ else {
159
+ const auth = payload.authorization;
160
+ if (!auth.from || typeof auth.from !== 'string') {
161
+ errors.push('from address is required in payload.authorization');
162
+ }
163
+ else if (!auth.from.startsWith('0x')) {
164
+ errors.push('from must be a valid hex address');
165
+ }
166
+ if (!auth.sessionKeysProvider || typeof auth.sessionKeysProvider !== 'string') {
167
+ errors.push('sessionKeysProvider is required in payload.authorization');
168
+ }
169
+ if (!Array.isArray(auth.sessionKeys)) {
170
+ errors.push('sessionKeys array is required in payload.authorization');
171
+ }
172
+ }
173
+ }
174
+ // Validate extensions (must be present, even if empty)
175
+ if (t.extensions === undefined) {
176
+ errors.push('extensions is required (can be empty object)');
177
+ }
178
+ return { valid: errors.length === 0, errors };
179
+ };
75
180
  export const b64EncodeX402AccessToken = (accessToken) => {
76
181
  return Buffer.from(JSON.stringify(accessToken)).toString('base64');
77
182
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nevermined-io/core-kit",
3
- "version": "0.1.20",
3
+ "version": "0.1.22",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",