@4mica/sdk 0.5.0 → 0.5.1
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 +36 -3
- package/.eslintrc.cjs +0 -29
- package/.github/workflows/ci.yml +0 -31
- package/.prettierignore +0 -3
- package/.prettierrc +0 -6
- package/eslint.config.mjs +0 -22
- package/src/abi/core4mica.json +0 -1605
- package/src/abi/erc20.json +0 -187
- package/src/auth.ts +0 -325
- package/src/bls.ts +0 -76
- package/src/client.ts +0 -347
- package/src/config.ts +0 -149
- package/src/contract.ts +0 -194
- package/src/errors.ts +0 -40
- package/src/guarantee.ts +0 -96
- package/src/index.ts +0 -12
- package/src/models.ts +0 -309
- package/src/rpc.ts +0 -225
- package/src/signing.ts +0 -330
- package/src/utils.ts +0 -75
- package/src/x402/index.ts +0 -192
- package/src/x402/models.ts +0 -94
- package/tests/auth.integration.test.ts +0 -97
- package/tests/auth.test.ts +0 -292
- package/tests/config.test.ts +0 -56
- package/tests/guarantee.test.ts +0 -26
- package/tests/rpc.test.ts +0 -76
- package/tests/signing.test.ts +0 -52
- package/tests/utils.test.ts +0 -35
- package/tests/x402.test.ts +0 -152
- package/tsconfig.build.json +0 -5
- package/tsconfig.json +0 -15
- package/vitest.config.ts +0 -12
package/src/abi/erc20.json
DELETED
|
@@ -1,187 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"abi": [
|
|
3
|
-
{
|
|
4
|
-
"type": "function",
|
|
5
|
-
"name": "allowance",
|
|
6
|
-
"inputs": [
|
|
7
|
-
{
|
|
8
|
-
"name": "owner",
|
|
9
|
-
"type": "address",
|
|
10
|
-
"internalType": "address"
|
|
11
|
-
},
|
|
12
|
-
{
|
|
13
|
-
"name": "spender",
|
|
14
|
-
"type": "address",
|
|
15
|
-
"internalType": "address"
|
|
16
|
-
}
|
|
17
|
-
],
|
|
18
|
-
"outputs": [
|
|
19
|
-
{
|
|
20
|
-
"name": "",
|
|
21
|
-
"type": "uint256",
|
|
22
|
-
"internalType": "uint256"
|
|
23
|
-
}
|
|
24
|
-
],
|
|
25
|
-
"stateMutability": "view"
|
|
26
|
-
},
|
|
27
|
-
{
|
|
28
|
-
"type": "function",
|
|
29
|
-
"name": "approve",
|
|
30
|
-
"inputs": [
|
|
31
|
-
{
|
|
32
|
-
"name": "spender",
|
|
33
|
-
"type": "address",
|
|
34
|
-
"internalType": "address"
|
|
35
|
-
},
|
|
36
|
-
{
|
|
37
|
-
"name": "value",
|
|
38
|
-
"type": "uint256",
|
|
39
|
-
"internalType": "uint256"
|
|
40
|
-
}
|
|
41
|
-
],
|
|
42
|
-
"outputs": [
|
|
43
|
-
{
|
|
44
|
-
"name": "",
|
|
45
|
-
"type": "bool",
|
|
46
|
-
"internalType": "bool"
|
|
47
|
-
}
|
|
48
|
-
],
|
|
49
|
-
"stateMutability": "nonpayable"
|
|
50
|
-
},
|
|
51
|
-
{
|
|
52
|
-
"type": "function",
|
|
53
|
-
"name": "balanceOf",
|
|
54
|
-
"inputs": [
|
|
55
|
-
{
|
|
56
|
-
"name": "account",
|
|
57
|
-
"type": "address",
|
|
58
|
-
"internalType": "address"
|
|
59
|
-
}
|
|
60
|
-
],
|
|
61
|
-
"outputs": [
|
|
62
|
-
{
|
|
63
|
-
"name": "",
|
|
64
|
-
"type": "uint256",
|
|
65
|
-
"internalType": "uint256"
|
|
66
|
-
}
|
|
67
|
-
],
|
|
68
|
-
"stateMutability": "view"
|
|
69
|
-
},
|
|
70
|
-
{
|
|
71
|
-
"type": "function",
|
|
72
|
-
"name": "totalSupply",
|
|
73
|
-
"inputs": [],
|
|
74
|
-
"outputs": [
|
|
75
|
-
{
|
|
76
|
-
"name": "",
|
|
77
|
-
"type": "uint256",
|
|
78
|
-
"internalType": "uint256"
|
|
79
|
-
}
|
|
80
|
-
],
|
|
81
|
-
"stateMutability": "view"
|
|
82
|
-
},
|
|
83
|
-
{
|
|
84
|
-
"type": "function",
|
|
85
|
-
"name": "transfer",
|
|
86
|
-
"inputs": [
|
|
87
|
-
{
|
|
88
|
-
"name": "to",
|
|
89
|
-
"type": "address",
|
|
90
|
-
"internalType": "address"
|
|
91
|
-
},
|
|
92
|
-
{
|
|
93
|
-
"name": "value",
|
|
94
|
-
"type": "uint256",
|
|
95
|
-
"internalType": "uint256"
|
|
96
|
-
}
|
|
97
|
-
],
|
|
98
|
-
"outputs": [
|
|
99
|
-
{
|
|
100
|
-
"name": "",
|
|
101
|
-
"type": "bool",
|
|
102
|
-
"internalType": "bool"
|
|
103
|
-
}
|
|
104
|
-
],
|
|
105
|
-
"stateMutability": "nonpayable"
|
|
106
|
-
},
|
|
107
|
-
{
|
|
108
|
-
"type": "function",
|
|
109
|
-
"name": "transferFrom",
|
|
110
|
-
"inputs": [
|
|
111
|
-
{
|
|
112
|
-
"name": "from",
|
|
113
|
-
"type": "address",
|
|
114
|
-
"internalType": "address"
|
|
115
|
-
},
|
|
116
|
-
{
|
|
117
|
-
"name": "to",
|
|
118
|
-
"type": "address",
|
|
119
|
-
"internalType": "address"
|
|
120
|
-
},
|
|
121
|
-
{
|
|
122
|
-
"name": "value",
|
|
123
|
-
"type": "uint256",
|
|
124
|
-
"internalType": "uint256"
|
|
125
|
-
}
|
|
126
|
-
],
|
|
127
|
-
"outputs": [
|
|
128
|
-
{
|
|
129
|
-
"name": "",
|
|
130
|
-
"type": "bool",
|
|
131
|
-
"internalType": "bool"
|
|
132
|
-
}
|
|
133
|
-
],
|
|
134
|
-
"stateMutability": "nonpayable"
|
|
135
|
-
},
|
|
136
|
-
{
|
|
137
|
-
"type": "event",
|
|
138
|
-
"name": "Approval",
|
|
139
|
-
"inputs": [
|
|
140
|
-
{
|
|
141
|
-
"name": "owner",
|
|
142
|
-
"type": "address",
|
|
143
|
-
"indexed": true,
|
|
144
|
-
"internalType": "address"
|
|
145
|
-
},
|
|
146
|
-
{
|
|
147
|
-
"name": "spender",
|
|
148
|
-
"type": "address",
|
|
149
|
-
"indexed": true,
|
|
150
|
-
"internalType": "address"
|
|
151
|
-
},
|
|
152
|
-
{
|
|
153
|
-
"name": "value",
|
|
154
|
-
"type": "uint256",
|
|
155
|
-
"indexed": false,
|
|
156
|
-
"internalType": "uint256"
|
|
157
|
-
}
|
|
158
|
-
],
|
|
159
|
-
"anonymous": false
|
|
160
|
-
},
|
|
161
|
-
{
|
|
162
|
-
"type": "event",
|
|
163
|
-
"name": "Transfer",
|
|
164
|
-
"inputs": [
|
|
165
|
-
{
|
|
166
|
-
"name": "from",
|
|
167
|
-
"type": "address",
|
|
168
|
-
"indexed": true,
|
|
169
|
-
"internalType": "address"
|
|
170
|
-
},
|
|
171
|
-
{
|
|
172
|
-
"name": "to",
|
|
173
|
-
"type": "address",
|
|
174
|
-
"indexed": true,
|
|
175
|
-
"internalType": "address"
|
|
176
|
-
},
|
|
177
|
-
{
|
|
178
|
-
"name": "value",
|
|
179
|
-
"type": "uint256",
|
|
180
|
-
"indexed": false,
|
|
181
|
-
"internalType": "uint256"
|
|
182
|
-
}
|
|
183
|
-
],
|
|
184
|
-
"anonymous": false
|
|
185
|
-
}
|
|
186
|
-
]
|
|
187
|
-
}
|
package/src/auth.ts
DELETED
|
@@ -1,325 +0,0 @@
|
|
|
1
|
-
import type { FetchFn } from './rpc';
|
|
2
|
-
import {
|
|
3
|
-
AuthApiError,
|
|
4
|
-
AuthConfigError,
|
|
5
|
-
AuthDecodeError,
|
|
6
|
-
AuthMissingConfigError,
|
|
7
|
-
AuthTransportError,
|
|
8
|
-
AuthUrlError,
|
|
9
|
-
} from './errors';
|
|
10
|
-
import { ValidationError, validateUrl } from './utils';
|
|
11
|
-
import { EvmSigner } from './signing';
|
|
12
|
-
|
|
13
|
-
export type AuthTokens = {
|
|
14
|
-
accessToken: string;
|
|
15
|
-
refreshToken: string;
|
|
16
|
-
expiresIn: number;
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
export type SiweTemplate = {
|
|
20
|
-
domain: string;
|
|
21
|
-
uri: string;
|
|
22
|
-
chainId: number;
|
|
23
|
-
statement: string;
|
|
24
|
-
expiration: string;
|
|
25
|
-
issuedAt: string;
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
export type AuthNonceResponse = {
|
|
29
|
-
nonce: string;
|
|
30
|
-
siwe: SiweTemplate;
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
type RecordValue = Record<string, unknown>;
|
|
34
|
-
|
|
35
|
-
const isRecord = (value: unknown): value is RecordValue =>
|
|
36
|
-
Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
37
|
-
|
|
38
|
-
const readString = (value: unknown, label: string): string => {
|
|
39
|
-
if (typeof value === 'string' && value.trim()) {
|
|
40
|
-
return value;
|
|
41
|
-
}
|
|
42
|
-
throw new AuthDecodeError(`invalid auth response: missing ${label}`);
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
const readNumber = (value: unknown, label: string): number => {
|
|
46
|
-
if (typeof value === 'number' && Number.isFinite(value)) {
|
|
47
|
-
return value;
|
|
48
|
-
}
|
|
49
|
-
if (typeof value === 'string' && value.trim()) {
|
|
50
|
-
const parsed = Number(value);
|
|
51
|
-
if (Number.isFinite(parsed)) {
|
|
52
|
-
return parsed;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
throw new AuthDecodeError(`invalid auth response: missing ${label}`);
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
const parseTokens = (payload: RecordValue): AuthTokens => {
|
|
59
|
-
const accessToken = readString(payload.access_token ?? payload.accessToken, 'access_token');
|
|
60
|
-
const refreshToken = readString(payload.refresh_token ?? payload.refreshToken, 'refresh_token');
|
|
61
|
-
const expiresIn = readNumber(payload.expires_in ?? payload.expiresIn, 'expires_in');
|
|
62
|
-
return { accessToken, refreshToken, expiresIn };
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
const parseSiweTemplate = (payload: RecordValue): SiweTemplate => {
|
|
66
|
-
const domain = readString(payload.domain, 'siwe.domain');
|
|
67
|
-
const uri = readString(payload.uri, 'siwe.uri');
|
|
68
|
-
const chainId = readNumber(payload.chain_id ?? payload.chainId, 'siwe.chain_id');
|
|
69
|
-
const statement = readString(payload.statement, 'siwe.statement');
|
|
70
|
-
const expiration = readString(payload.expiration, 'siwe.expiration');
|
|
71
|
-
const issuedAt = readString(payload.issued_at ?? payload.issuedAt, 'siwe.issued_at');
|
|
72
|
-
return { domain, uri, chainId, statement, expiration, issuedAt };
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
const parseNonceResponse = (payload: unknown): AuthNonceResponse => {
|
|
76
|
-
if (!isRecord(payload)) {
|
|
77
|
-
throw new AuthDecodeError('invalid auth response: nonce payload');
|
|
78
|
-
}
|
|
79
|
-
const nonce = readString(payload.nonce, 'nonce');
|
|
80
|
-
if (!isRecord(payload.siwe)) {
|
|
81
|
-
throw new AuthDecodeError('invalid auth response: missing siwe template');
|
|
82
|
-
}
|
|
83
|
-
return { nonce, siwe: parseSiweTemplate(payload.siwe) };
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
export function buildSiweMessage(input: {
|
|
87
|
-
domain: string;
|
|
88
|
-
address: string;
|
|
89
|
-
statement: string;
|
|
90
|
-
uri: string;
|
|
91
|
-
chainId: number | string;
|
|
92
|
-
nonce: string;
|
|
93
|
-
issuedAt: string;
|
|
94
|
-
expiration: string;
|
|
95
|
-
}): string {
|
|
96
|
-
const chainId = String(input.chainId);
|
|
97
|
-
return `${input.domain} wants you to sign in with your Ethereum account:\n${input.address}\n\n${input.statement}\n\nURI: ${input.uri}\nVersion: 1\nChain ID: ${chainId}\nNonce: ${input.nonce}\nIssued At: ${input.issuedAt}\nExpiration Time: ${input.expiration}`;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
export class AuthClient {
|
|
101
|
-
private baseUrl: string;
|
|
102
|
-
private fetchFn: FetchFn;
|
|
103
|
-
|
|
104
|
-
constructor(endpoint: string, fetchFn: FetchFn = fetch) {
|
|
105
|
-
try {
|
|
106
|
-
const validated = validateUrl(endpoint);
|
|
107
|
-
this.baseUrl = validated.endsWith('/') ? validated.slice(0, -1) : validated;
|
|
108
|
-
} catch (err) {
|
|
109
|
-
if (err instanceof ValidationError) {
|
|
110
|
-
throw new AuthUrlError(err.message);
|
|
111
|
-
}
|
|
112
|
-
throw err;
|
|
113
|
-
}
|
|
114
|
-
this.fetchFn = fetchFn;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
async getNonce(address: string): Promise<AuthNonceResponse> {
|
|
118
|
-
const payload = await this.post('/auth/nonce', { address });
|
|
119
|
-
return parseNonceResponse(payload);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
async verify(address: string, message: string, signature: string): Promise<AuthTokens> {
|
|
123
|
-
const payload = await this.post('/auth/verify', { address, message, signature });
|
|
124
|
-
if (!isRecord(payload)) {
|
|
125
|
-
throw new AuthDecodeError('invalid auth response: verify payload');
|
|
126
|
-
}
|
|
127
|
-
return parseTokens(payload);
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
async refresh(refreshToken: string): Promise<AuthTokens> {
|
|
131
|
-
const payload = await this.post('/auth/refresh', { refresh_token: refreshToken });
|
|
132
|
-
if (!isRecord(payload)) {
|
|
133
|
-
throw new AuthDecodeError('invalid auth response: refresh payload');
|
|
134
|
-
}
|
|
135
|
-
return parseTokens(payload);
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
async logout(refreshToken: string): Promise<void> {
|
|
139
|
-
await this.post('/auth/logout', { refresh_token: refreshToken });
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
private async post(path: string, body: Record<string, unknown>): Promise<unknown> {
|
|
143
|
-
return this.request(path, {
|
|
144
|
-
method: 'POST',
|
|
145
|
-
headers: { 'content-type': 'application/json' },
|
|
146
|
-
body: JSON.stringify(body),
|
|
147
|
-
});
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
private async request(path: string, init: RequestInit): Promise<unknown> {
|
|
151
|
-
let response: Response;
|
|
152
|
-
try {
|
|
153
|
-
response = await this.fetchFn(`${this.baseUrl}${path}`, init);
|
|
154
|
-
} catch (err) {
|
|
155
|
-
throw new AuthTransportError(`auth request failed: ${String(err)}`);
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
let text: string;
|
|
159
|
-
try {
|
|
160
|
-
text = await response.text();
|
|
161
|
-
} catch (err) {
|
|
162
|
-
throw new AuthDecodeError(`invalid response from ${response.url}: ${String(err)}`);
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
let payload: unknown = text;
|
|
166
|
-
if (text) {
|
|
167
|
-
try {
|
|
168
|
-
payload = JSON.parse(text) as unknown;
|
|
169
|
-
} catch (err) {
|
|
170
|
-
if (response.ok) {
|
|
171
|
-
throw new AuthDecodeError(`invalid JSON response from ${response.url}: ${String(err)}`);
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
} else {
|
|
175
|
-
payload = null;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
if (response.ok) {
|
|
179
|
-
return payload;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
let message = 'unknown error';
|
|
183
|
-
if (payload && typeof payload === 'object') {
|
|
184
|
-
const record = payload as RecordValue;
|
|
185
|
-
const error = record.error;
|
|
186
|
-
const msg = record.message;
|
|
187
|
-
message =
|
|
188
|
-
(typeof error === 'string' && error) ||
|
|
189
|
-
(typeof msg === 'string' && msg) ||
|
|
190
|
-
JSON.stringify(record, (_k, v) => v);
|
|
191
|
-
} else if (typeof payload === 'string' && payload.trim()) {
|
|
192
|
-
message = payload.trim();
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
throw new AuthApiError(`${response.status}: ${message}`, {
|
|
196
|
-
status: response.status,
|
|
197
|
-
body: payload,
|
|
198
|
-
});
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
export class AuthSession {
|
|
203
|
-
private authClient: AuthClient;
|
|
204
|
-
private signer: EvmSigner;
|
|
205
|
-
private refreshMarginSecs: number;
|
|
206
|
-
private tokens?: { accessToken: string; refreshToken: string; expiresAt: number };
|
|
207
|
-
private inFlight?: Promise<string>;
|
|
208
|
-
private loginPromise?: Promise<AuthTokens>;
|
|
209
|
-
|
|
210
|
-
constructor(options: {
|
|
211
|
-
authUrl: string;
|
|
212
|
-
signer: EvmSigner;
|
|
213
|
-
refreshMarginSecs?: number;
|
|
214
|
-
fetchFn?: FetchFn;
|
|
215
|
-
}) {
|
|
216
|
-
if (!options.authUrl) {
|
|
217
|
-
throw new AuthMissingConfigError('missing auth_url');
|
|
218
|
-
}
|
|
219
|
-
this.authClient = new AuthClient(options.authUrl, options.fetchFn);
|
|
220
|
-
this.signer = options.signer;
|
|
221
|
-
|
|
222
|
-
const margin = options.refreshMarginSecs ?? 60;
|
|
223
|
-
if (!Number.isFinite(margin) || margin < 0) {
|
|
224
|
-
throw new AuthConfigError('refresh margin must be non-negative');
|
|
225
|
-
}
|
|
226
|
-
this.refreshMarginSecs = margin;
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
async accessToken(): Promise<string> {
|
|
230
|
-
if (this.tokens && !this.isExpiringSoon()) {
|
|
231
|
-
return this.tokens.accessToken;
|
|
232
|
-
}
|
|
233
|
-
if (this.inFlight) {
|
|
234
|
-
return this.inFlight;
|
|
235
|
-
}
|
|
236
|
-
this.inFlight = this.refreshOrLogin();
|
|
237
|
-
try {
|
|
238
|
-
return await this.inFlight;
|
|
239
|
-
} finally {
|
|
240
|
-
this.inFlight = undefined;
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
async login(): Promise<AuthTokens> {
|
|
245
|
-
if (this.loginPromise) {
|
|
246
|
-
return this.loginPromise;
|
|
247
|
-
}
|
|
248
|
-
this.loginPromise = this.performLogin();
|
|
249
|
-
try {
|
|
250
|
-
return await this.loginPromise;
|
|
251
|
-
} finally {
|
|
252
|
-
this.loginPromise = undefined;
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
|
|
256
|
-
async logout(): Promise<void> {
|
|
257
|
-
if (!this.tokens?.refreshToken) {
|
|
258
|
-
return;
|
|
259
|
-
}
|
|
260
|
-
await this.authClient.logout(this.tokens.refreshToken);
|
|
261
|
-
this.tokens = undefined;
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
private async refreshOrLogin(): Promise<string> {
|
|
265
|
-
if (this.tokens?.refreshToken) {
|
|
266
|
-
try {
|
|
267
|
-
const tokens = await this.refreshTokens();
|
|
268
|
-
return tokens.accessToken;
|
|
269
|
-
} catch (err) {
|
|
270
|
-
if (err instanceof AuthApiError && err.status === 401) {
|
|
271
|
-
const tokens = await this.login();
|
|
272
|
-
return tokens.accessToken;
|
|
273
|
-
}
|
|
274
|
-
throw err;
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
const tokens = await this.login();
|
|
278
|
-
return tokens.accessToken;
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
private async performLogin(): Promise<AuthTokens> {
|
|
282
|
-
const address = this.signer.address;
|
|
283
|
-
const nonce = await this.authClient.getNonce(address);
|
|
284
|
-
const message = buildSiweMessage({
|
|
285
|
-
domain: nonce.siwe.domain,
|
|
286
|
-
address,
|
|
287
|
-
statement: nonce.siwe.statement,
|
|
288
|
-
uri: nonce.siwe.uri,
|
|
289
|
-
chainId: nonce.siwe.chainId,
|
|
290
|
-
nonce: nonce.nonce,
|
|
291
|
-
issuedAt: nonce.siwe.issuedAt,
|
|
292
|
-
expiration: nonce.siwe.expiration,
|
|
293
|
-
});
|
|
294
|
-
const signature = await this.signer.signMessage({ message });
|
|
295
|
-
const tokens = await this.authClient.verify(address, message, signature);
|
|
296
|
-
this.cacheTokens(tokens);
|
|
297
|
-
return tokens;
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
private async refreshTokens(): Promise<AuthTokens> {
|
|
301
|
-
if (!this.tokens?.refreshToken) {
|
|
302
|
-
throw new AuthMissingConfigError('missing refresh token');
|
|
303
|
-
}
|
|
304
|
-
const tokens = await this.authClient.refresh(this.tokens.refreshToken);
|
|
305
|
-
this.cacheTokens(tokens);
|
|
306
|
-
return tokens;
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
private cacheTokens(tokens: AuthTokens): void {
|
|
310
|
-
const now = Date.now() / 1000;
|
|
311
|
-
this.tokens = {
|
|
312
|
-
accessToken: tokens.accessToken,
|
|
313
|
-
refreshToken: tokens.refreshToken,
|
|
314
|
-
expiresAt: now + tokens.expiresIn,
|
|
315
|
-
};
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
private isExpiringSoon(): boolean {
|
|
319
|
-
if (!this.tokens) {
|
|
320
|
-
return true;
|
|
321
|
-
}
|
|
322
|
-
const now = Date.now() / 1000;
|
|
323
|
-
return now + this.refreshMarginSecs >= this.tokens.expiresAt;
|
|
324
|
-
}
|
|
325
|
-
}
|
package/src/bls.ts
DELETED
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
import { getBytes } from 'ethers';
|
|
2
|
-
import { VerificationError } from './errors';
|
|
3
|
-
|
|
4
|
-
type BlsField = { value?: string | number | bigint } | string | number | bigint;
|
|
5
|
-
|
|
6
|
-
type BlsModule = {
|
|
7
|
-
bls12_381: {
|
|
8
|
-
G2: {
|
|
9
|
-
ProjectivePoint: {
|
|
10
|
-
fromHex(bytes: Uint8Array): {
|
|
11
|
-
toAffine(): {
|
|
12
|
-
x: { c: readonly [BlsField, BlsField] };
|
|
13
|
-
y: { c: readonly [BlsField, BlsField] };
|
|
14
|
-
};
|
|
15
|
-
};
|
|
16
|
-
};
|
|
17
|
-
};
|
|
18
|
-
};
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
function splitFp(value: bigint): [Uint8Array, Uint8Array] {
|
|
22
|
-
const be48 = value.toString(16).padStart(96, '0');
|
|
23
|
-
const bytes = getBytes(`0x${be48}`);
|
|
24
|
-
const hi = new Uint8Array(32);
|
|
25
|
-
hi.set(bytes.slice(0, 16), 16);
|
|
26
|
-
const lo = bytes.slice(16);
|
|
27
|
-
return [hi, lo];
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Expand a compressed G2 signature into the tuple expected by the contract.
|
|
32
|
-
* This mirrors the Python helper and requires the optional `@noble/curves` dependency.
|
|
33
|
-
*/
|
|
34
|
-
export function signatureToWords(signatureHex: string): Uint8Array[] {
|
|
35
|
-
let curves: BlsModule;
|
|
36
|
-
try {
|
|
37
|
-
// Lazy require to keep the dependency optional.
|
|
38
|
-
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
39
|
-
curves = require('@noble/curves/bls12-381') as BlsModule;
|
|
40
|
-
} catch {
|
|
41
|
-
throw new VerificationError(
|
|
42
|
-
'BLS decoding requires @noble/curves; install it to enable remuneration'
|
|
43
|
-
);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const toBigint = (field: BlsField): bigint => {
|
|
47
|
-
if (typeof field === 'bigint' || typeof field === 'number' || typeof field === 'string') {
|
|
48
|
-
return BigInt(field);
|
|
49
|
-
}
|
|
50
|
-
if (field && typeof field === 'object' && 'value' in field && field.value !== undefined) {
|
|
51
|
-
const value = field.value;
|
|
52
|
-
if (typeof value === 'bigint' || typeof value === 'number' || typeof value === 'string') {
|
|
53
|
-
return BigInt(value);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
throw new VerificationError('invalid BLS field element');
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
try {
|
|
60
|
-
const sigBytes = getBytes(signatureHex);
|
|
61
|
-
const point = curves.bls12_381.G2.ProjectivePoint.fromHex(sigBytes);
|
|
62
|
-
const affine = point.toAffine();
|
|
63
|
-
const [x0, x1] = affine.x.c;
|
|
64
|
-
const [y0, y1] = affine.y.c;
|
|
65
|
-
const coords = [x0, x1, y0, y1].map((fp) => toBigint(fp));
|
|
66
|
-
const words: Uint8Array[] = [];
|
|
67
|
-
coords.forEach((coord) => {
|
|
68
|
-
const [hi, lo] = splitFp(coord);
|
|
69
|
-
words.push(hi, lo);
|
|
70
|
-
});
|
|
71
|
-
return words;
|
|
72
|
-
} catch (err: unknown) {
|
|
73
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
74
|
-
throw new VerificationError(`invalid BLS signature: ${message}`);
|
|
75
|
-
}
|
|
76
|
-
}
|