@4mica/sdk 0.5.0
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/.eslintrc.cjs +29 -0
- package/.github/workflows/ci.yml +31 -0
- package/.prettierignore +3 -0
- package/.prettierrc +6 -0
- package/LICENSE +21 -0
- package/README.md +444 -0
- package/dist/abi/core4mica.json +1605 -0
- package/dist/abi/erc20.json +187 -0
- package/dist/auth.d.ts +62 -0
- package/dist/auth.js +255 -0
- package/dist/bls.d.ts +5 -0
- package/dist/bls.js +58 -0
- package/dist/client.d.ts +58 -0
- package/dist/client.js +245 -0
- package/dist/config.d.ts +36 -0
- package/dist/config.js +123 -0
- package/dist/contract.d.ts +33 -0
- package/dist/contract.js +134 -0
- package/dist/errors.d.ts +43 -0
- package/dist/errors.js +62 -0
- package/dist/guarantee.d.ts +3 -0
- package/dist/guarantee.js +66 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +28 -0
- package/dist/models.d.ts +150 -0
- package/dist/models.js +176 -0
- package/dist/rpc.d.ts +37 -0
- package/dist/rpc.js +163 -0
- package/dist/signing.d.ts +106 -0
- package/dist/signing.js +223 -0
- package/dist/src/abi/core4mica.json +1605 -0
- package/dist/src/abi/erc20.json +187 -0
- package/dist/src/bls.d.ts +5 -0
- package/dist/src/bls.js +45 -0
- package/dist/src/client.d.ts +55 -0
- package/dist/src/client.js +214 -0
- package/dist/src/config.d.ts +21 -0
- package/dist/src/config.js +76 -0
- package/dist/src/contract.d.ts +33 -0
- package/dist/src/contract.js +121 -0
- package/dist/src/errors.d.ts +17 -0
- package/dist/src/errors.js +31 -0
- package/dist/src/guarantee.d.ts +3 -0
- package/dist/src/guarantee.js +66 -0
- package/dist/src/index.d.ts +11 -0
- package/dist/src/index.js +27 -0
- package/dist/src/models.d.ts +119 -0
- package/dist/src/models.js +132 -0
- package/dist/src/rpc.d.ts +30 -0
- package/dist/src/rpc.js +122 -0
- package/dist/src/signing.d.ts +16 -0
- package/dist/src/signing.js +102 -0
- package/dist/src/utils.d.ts +8 -0
- package/dist/src/utils.js +78 -0
- package/dist/src/x402.d.ts +77 -0
- package/dist/src/x402.js +214 -0
- package/dist/tests/config.test.d.ts +1 -0
- package/dist/tests/config.test.js +26 -0
- package/dist/tests/guarantee.test.d.ts +1 -0
- package/dist/tests/guarantee.test.js +26 -0
- package/dist/tests/rpc.test.d.ts +1 -0
- package/dist/tests/rpc.test.js +44 -0
- package/dist/tests/signing.test.d.ts +1 -0
- package/dist/tests/signing.test.js +25 -0
- package/dist/tests/utils.test.d.ts +1 -0
- package/dist/tests/utils.test.js +25 -0
- package/dist/tests/x402.test.d.ts +1 -0
- package/dist/tests/x402.test.js +65 -0
- package/dist/utils.d.ts +8 -0
- package/dist/utils.js +78 -0
- package/dist/x402/index.d.ts +22 -0
- package/dist/x402/index.js +136 -0
- package/dist/x402/models.d.ts +80 -0
- package/dist/x402/models.js +2 -0
- package/dist/x402.d.ts +78 -0
- package/dist/x402.js +231 -0
- package/eslint.config.mjs +22 -0
- package/package.json +31 -0
- package/src/abi/core4mica.json +1605 -0
- package/src/abi/erc20.json +187 -0
- package/src/auth.ts +325 -0
- package/src/bls.ts +76 -0
- package/src/client.ts +347 -0
- package/src/config.ts +149 -0
- package/src/contract.ts +194 -0
- package/src/errors.ts +40 -0
- package/src/guarantee.ts +96 -0
- package/src/index.ts +12 -0
- package/src/models.ts +309 -0
- package/src/rpc.ts +225 -0
- package/src/signing.ts +330 -0
- package/src/utils.ts +75 -0
- package/src/x402/index.ts +192 -0
- package/src/x402/models.ts +94 -0
- package/tests/auth.integration.test.ts +97 -0
- package/tests/auth.test.ts +292 -0
- package/tests/config.test.ts +56 -0
- package/tests/guarantee.test.ts +26 -0
- package/tests/rpc.test.ts +76 -0
- package/tests/signing.test.ts +52 -0
- package/tests/utils.test.ts +35 -0
- package/tests/x402.test.ts +152 -0
- package/tsconfig.build.json +5 -0
- package/tsconfig.json +15 -0
- package/vitest.config.ts +12 -0
|
@@ -0,0 +1,187 @@
|
|
|
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/dist/auth.d.ts
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { FetchFn } from './rpc';
|
|
2
|
+
import { EvmSigner } from './signing';
|
|
3
|
+
export type AuthTokens = {
|
|
4
|
+
accessToken: string;
|
|
5
|
+
refreshToken: string;
|
|
6
|
+
expiresIn: number;
|
|
7
|
+
};
|
|
8
|
+
export type SiweTemplate = {
|
|
9
|
+
domain: string;
|
|
10
|
+
uri: string;
|
|
11
|
+
chainId: number;
|
|
12
|
+
statement: string;
|
|
13
|
+
expiration: string;
|
|
14
|
+
issuedAt: string;
|
|
15
|
+
};
|
|
16
|
+
export type AuthNonceResponse = {
|
|
17
|
+
nonce: string;
|
|
18
|
+
siwe: SiweTemplate;
|
|
19
|
+
};
|
|
20
|
+
export declare function buildSiweMessage(input: {
|
|
21
|
+
domain: string;
|
|
22
|
+
address: string;
|
|
23
|
+
statement: string;
|
|
24
|
+
uri: string;
|
|
25
|
+
chainId: number | string;
|
|
26
|
+
nonce: string;
|
|
27
|
+
issuedAt: string;
|
|
28
|
+
expiration: string;
|
|
29
|
+
}): string;
|
|
30
|
+
export declare class AuthClient {
|
|
31
|
+
private baseUrl;
|
|
32
|
+
private fetchFn;
|
|
33
|
+
constructor(endpoint: string, fetchFn?: FetchFn);
|
|
34
|
+
getNonce(address: string): Promise<AuthNonceResponse>;
|
|
35
|
+
verify(address: string, message: string, signature: string): Promise<AuthTokens>;
|
|
36
|
+
refresh(refreshToken: string): Promise<AuthTokens>;
|
|
37
|
+
logout(refreshToken: string): Promise<void>;
|
|
38
|
+
private post;
|
|
39
|
+
private request;
|
|
40
|
+
}
|
|
41
|
+
export declare class AuthSession {
|
|
42
|
+
private authClient;
|
|
43
|
+
private signer;
|
|
44
|
+
private refreshMarginSecs;
|
|
45
|
+
private tokens?;
|
|
46
|
+
private inFlight?;
|
|
47
|
+
private loginPromise?;
|
|
48
|
+
constructor(options: {
|
|
49
|
+
authUrl: string;
|
|
50
|
+
signer: EvmSigner;
|
|
51
|
+
refreshMarginSecs?: number;
|
|
52
|
+
fetchFn?: FetchFn;
|
|
53
|
+
});
|
|
54
|
+
accessToken(): Promise<string>;
|
|
55
|
+
login(): Promise<AuthTokens>;
|
|
56
|
+
logout(): Promise<void>;
|
|
57
|
+
private refreshOrLogin;
|
|
58
|
+
private performLogin;
|
|
59
|
+
private refreshTokens;
|
|
60
|
+
private cacheTokens;
|
|
61
|
+
private isExpiringSoon;
|
|
62
|
+
}
|
package/dist/auth.js
ADDED
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AuthSession = exports.AuthClient = void 0;
|
|
4
|
+
exports.buildSiweMessage = buildSiweMessage;
|
|
5
|
+
const errors_1 = require("./errors");
|
|
6
|
+
const utils_1 = require("./utils");
|
|
7
|
+
const isRecord = (value) => Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
|
8
|
+
const readString = (value, label) => {
|
|
9
|
+
if (typeof value === 'string' && value.trim()) {
|
|
10
|
+
return value;
|
|
11
|
+
}
|
|
12
|
+
throw new errors_1.AuthDecodeError(`invalid auth response: missing ${label}`);
|
|
13
|
+
};
|
|
14
|
+
const readNumber = (value, label) => {
|
|
15
|
+
if (typeof value === 'number' && Number.isFinite(value)) {
|
|
16
|
+
return value;
|
|
17
|
+
}
|
|
18
|
+
if (typeof value === 'string' && value.trim()) {
|
|
19
|
+
const parsed = Number(value);
|
|
20
|
+
if (Number.isFinite(parsed)) {
|
|
21
|
+
return parsed;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
throw new errors_1.AuthDecodeError(`invalid auth response: missing ${label}`);
|
|
25
|
+
};
|
|
26
|
+
const parseTokens = (payload) => {
|
|
27
|
+
const accessToken = readString(payload.access_token ?? payload.accessToken, 'access_token');
|
|
28
|
+
const refreshToken = readString(payload.refresh_token ?? payload.refreshToken, 'refresh_token');
|
|
29
|
+
const expiresIn = readNumber(payload.expires_in ?? payload.expiresIn, 'expires_in');
|
|
30
|
+
return { accessToken, refreshToken, expiresIn };
|
|
31
|
+
};
|
|
32
|
+
const parseSiweTemplate = (payload) => {
|
|
33
|
+
const domain = readString(payload.domain, 'siwe.domain');
|
|
34
|
+
const uri = readString(payload.uri, 'siwe.uri');
|
|
35
|
+
const chainId = readNumber(payload.chain_id ?? payload.chainId, 'siwe.chain_id');
|
|
36
|
+
const statement = readString(payload.statement, 'siwe.statement');
|
|
37
|
+
const expiration = readString(payload.expiration, 'siwe.expiration');
|
|
38
|
+
const issuedAt = readString(payload.issued_at ?? payload.issuedAt, 'siwe.issued_at');
|
|
39
|
+
return { domain, uri, chainId, statement, expiration, issuedAt };
|
|
40
|
+
};
|
|
41
|
+
const parseNonceResponse = (payload) => {
|
|
42
|
+
if (!isRecord(payload)) {
|
|
43
|
+
throw new errors_1.AuthDecodeError('invalid auth response: nonce payload');
|
|
44
|
+
}
|
|
45
|
+
const nonce = readString(payload.nonce, 'nonce');
|
|
46
|
+
if (!isRecord(payload.siwe)) {
|
|
47
|
+
throw new errors_1.AuthDecodeError('invalid auth response: missing siwe template');
|
|
48
|
+
}
|
|
49
|
+
return { nonce, siwe: parseSiweTemplate(payload.siwe) };
|
|
50
|
+
};
|
|
51
|
+
function buildSiweMessage(input) {
|
|
52
|
+
const chainId = String(input.chainId);
|
|
53
|
+
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}`;
|
|
54
|
+
}
|
|
55
|
+
class AuthClient {
|
|
56
|
+
constructor(endpoint, fetchFn = fetch) {
|
|
57
|
+
try {
|
|
58
|
+
const validated = (0, utils_1.validateUrl)(endpoint);
|
|
59
|
+
this.baseUrl = validated.endsWith('/') ? validated.slice(0, -1) : validated;
|
|
60
|
+
}
|
|
61
|
+
catch (err) {
|
|
62
|
+
if (err instanceof utils_1.ValidationError) {
|
|
63
|
+
throw new errors_1.AuthUrlError(err.message);
|
|
64
|
+
}
|
|
65
|
+
throw err;
|
|
66
|
+
}
|
|
67
|
+
this.fetchFn = fetchFn;
|
|
68
|
+
}
|
|
69
|
+
async getNonce(address) {
|
|
70
|
+
const payload = await this.post('/auth/nonce', { address });
|
|
71
|
+
return parseNonceResponse(payload);
|
|
72
|
+
}
|
|
73
|
+
async verify(address, message, signature) {
|
|
74
|
+
const payload = await this.post('/auth/verify', { address, message, signature });
|
|
75
|
+
if (!isRecord(payload)) {
|
|
76
|
+
throw new errors_1.AuthDecodeError('invalid auth response: verify payload');
|
|
77
|
+
}
|
|
78
|
+
return parseTokens(payload);
|
|
79
|
+
}
|
|
80
|
+
async refresh(refreshToken) {
|
|
81
|
+
const payload = await this.post('/auth/refresh', { refresh_token: refreshToken });
|
|
82
|
+
if (!isRecord(payload)) {
|
|
83
|
+
throw new errors_1.AuthDecodeError('invalid auth response: refresh payload');
|
|
84
|
+
}
|
|
85
|
+
return parseTokens(payload);
|
|
86
|
+
}
|
|
87
|
+
async logout(refreshToken) {
|
|
88
|
+
await this.post('/auth/logout', { refresh_token: refreshToken });
|
|
89
|
+
}
|
|
90
|
+
async post(path, body) {
|
|
91
|
+
return this.request(path, {
|
|
92
|
+
method: 'POST',
|
|
93
|
+
headers: { 'content-type': 'application/json' },
|
|
94
|
+
body: JSON.stringify(body),
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
async request(path, init) {
|
|
98
|
+
let response;
|
|
99
|
+
try {
|
|
100
|
+
response = await this.fetchFn(`${this.baseUrl}${path}`, init);
|
|
101
|
+
}
|
|
102
|
+
catch (err) {
|
|
103
|
+
throw new errors_1.AuthTransportError(`auth request failed: ${String(err)}`);
|
|
104
|
+
}
|
|
105
|
+
let text;
|
|
106
|
+
try {
|
|
107
|
+
text = await response.text();
|
|
108
|
+
}
|
|
109
|
+
catch (err) {
|
|
110
|
+
throw new errors_1.AuthDecodeError(`invalid response from ${response.url}: ${String(err)}`);
|
|
111
|
+
}
|
|
112
|
+
let payload = text;
|
|
113
|
+
if (text) {
|
|
114
|
+
try {
|
|
115
|
+
payload = JSON.parse(text);
|
|
116
|
+
}
|
|
117
|
+
catch (err) {
|
|
118
|
+
if (response.ok) {
|
|
119
|
+
throw new errors_1.AuthDecodeError(`invalid JSON response from ${response.url}: ${String(err)}`);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
payload = null;
|
|
125
|
+
}
|
|
126
|
+
if (response.ok) {
|
|
127
|
+
return payload;
|
|
128
|
+
}
|
|
129
|
+
let message = 'unknown error';
|
|
130
|
+
if (payload && typeof payload === 'object') {
|
|
131
|
+
const record = payload;
|
|
132
|
+
const error = record.error;
|
|
133
|
+
const msg = record.message;
|
|
134
|
+
message =
|
|
135
|
+
(typeof error === 'string' && error) ||
|
|
136
|
+
(typeof msg === 'string' && msg) ||
|
|
137
|
+
JSON.stringify(record, (_k, v) => v);
|
|
138
|
+
}
|
|
139
|
+
else if (typeof payload === 'string' && payload.trim()) {
|
|
140
|
+
message = payload.trim();
|
|
141
|
+
}
|
|
142
|
+
throw new errors_1.AuthApiError(`${response.status}: ${message}`, {
|
|
143
|
+
status: response.status,
|
|
144
|
+
body: payload,
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
exports.AuthClient = AuthClient;
|
|
149
|
+
class AuthSession {
|
|
150
|
+
constructor(options) {
|
|
151
|
+
if (!options.authUrl) {
|
|
152
|
+
throw new errors_1.AuthMissingConfigError('missing auth_url');
|
|
153
|
+
}
|
|
154
|
+
this.authClient = new AuthClient(options.authUrl, options.fetchFn);
|
|
155
|
+
this.signer = options.signer;
|
|
156
|
+
const margin = options.refreshMarginSecs ?? 60;
|
|
157
|
+
if (!Number.isFinite(margin) || margin < 0) {
|
|
158
|
+
throw new errors_1.AuthConfigError('refresh margin must be non-negative');
|
|
159
|
+
}
|
|
160
|
+
this.refreshMarginSecs = margin;
|
|
161
|
+
}
|
|
162
|
+
async accessToken() {
|
|
163
|
+
if (this.tokens && !this.isExpiringSoon()) {
|
|
164
|
+
return this.tokens.accessToken;
|
|
165
|
+
}
|
|
166
|
+
if (this.inFlight) {
|
|
167
|
+
return this.inFlight;
|
|
168
|
+
}
|
|
169
|
+
this.inFlight = this.refreshOrLogin();
|
|
170
|
+
try {
|
|
171
|
+
return await this.inFlight;
|
|
172
|
+
}
|
|
173
|
+
finally {
|
|
174
|
+
this.inFlight = undefined;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
async login() {
|
|
178
|
+
if (this.loginPromise) {
|
|
179
|
+
return this.loginPromise;
|
|
180
|
+
}
|
|
181
|
+
this.loginPromise = this.performLogin();
|
|
182
|
+
try {
|
|
183
|
+
return await this.loginPromise;
|
|
184
|
+
}
|
|
185
|
+
finally {
|
|
186
|
+
this.loginPromise = undefined;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
async logout() {
|
|
190
|
+
if (!this.tokens?.refreshToken) {
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
await this.authClient.logout(this.tokens.refreshToken);
|
|
194
|
+
this.tokens = undefined;
|
|
195
|
+
}
|
|
196
|
+
async refreshOrLogin() {
|
|
197
|
+
if (this.tokens?.refreshToken) {
|
|
198
|
+
try {
|
|
199
|
+
const tokens = await this.refreshTokens();
|
|
200
|
+
return tokens.accessToken;
|
|
201
|
+
}
|
|
202
|
+
catch (err) {
|
|
203
|
+
if (err instanceof errors_1.AuthApiError && err.status === 401) {
|
|
204
|
+
const tokens = await this.login();
|
|
205
|
+
return tokens.accessToken;
|
|
206
|
+
}
|
|
207
|
+
throw err;
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
const tokens = await this.login();
|
|
211
|
+
return tokens.accessToken;
|
|
212
|
+
}
|
|
213
|
+
async performLogin() {
|
|
214
|
+
const address = this.signer.address;
|
|
215
|
+
const nonce = await this.authClient.getNonce(address);
|
|
216
|
+
const message = buildSiweMessage({
|
|
217
|
+
domain: nonce.siwe.domain,
|
|
218
|
+
address,
|
|
219
|
+
statement: nonce.siwe.statement,
|
|
220
|
+
uri: nonce.siwe.uri,
|
|
221
|
+
chainId: nonce.siwe.chainId,
|
|
222
|
+
nonce: nonce.nonce,
|
|
223
|
+
issuedAt: nonce.siwe.issuedAt,
|
|
224
|
+
expiration: nonce.siwe.expiration,
|
|
225
|
+
});
|
|
226
|
+
const signature = await this.signer.signMessage({ message });
|
|
227
|
+
const tokens = await this.authClient.verify(address, message, signature);
|
|
228
|
+
this.cacheTokens(tokens);
|
|
229
|
+
return tokens;
|
|
230
|
+
}
|
|
231
|
+
async refreshTokens() {
|
|
232
|
+
if (!this.tokens?.refreshToken) {
|
|
233
|
+
throw new errors_1.AuthMissingConfigError('missing refresh token');
|
|
234
|
+
}
|
|
235
|
+
const tokens = await this.authClient.refresh(this.tokens.refreshToken);
|
|
236
|
+
this.cacheTokens(tokens);
|
|
237
|
+
return tokens;
|
|
238
|
+
}
|
|
239
|
+
cacheTokens(tokens) {
|
|
240
|
+
const now = Date.now() / 1000;
|
|
241
|
+
this.tokens = {
|
|
242
|
+
accessToken: tokens.accessToken,
|
|
243
|
+
refreshToken: tokens.refreshToken,
|
|
244
|
+
expiresAt: now + tokens.expiresIn,
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
isExpiringSoon() {
|
|
248
|
+
if (!this.tokens) {
|
|
249
|
+
return true;
|
|
250
|
+
}
|
|
251
|
+
const now = Date.now() / 1000;
|
|
252
|
+
return now + this.refreshMarginSecs >= this.tokens.expiresAt;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
exports.AuthSession = AuthSession;
|
package/dist/bls.d.ts
ADDED
package/dist/bls.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.signatureToWords = signatureToWords;
|
|
4
|
+
const ethers_1 = require("ethers");
|
|
5
|
+
const errors_1 = require("./errors");
|
|
6
|
+
function splitFp(value) {
|
|
7
|
+
const be48 = value.toString(16).padStart(96, '0');
|
|
8
|
+
const bytes = (0, ethers_1.getBytes)(`0x${be48}`);
|
|
9
|
+
const hi = new Uint8Array(32);
|
|
10
|
+
hi.set(bytes.slice(0, 16), 16);
|
|
11
|
+
const lo = bytes.slice(16);
|
|
12
|
+
return [hi, lo];
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Expand a compressed G2 signature into the tuple expected by the contract.
|
|
16
|
+
* This mirrors the Python helper and requires the optional `@noble/curves` dependency.
|
|
17
|
+
*/
|
|
18
|
+
function signatureToWords(signatureHex) {
|
|
19
|
+
let curves;
|
|
20
|
+
try {
|
|
21
|
+
// Lazy require to keep the dependency optional.
|
|
22
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
23
|
+
curves = require('@noble/curves/bls12-381');
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
throw new errors_1.VerificationError('BLS decoding requires @noble/curves; install it to enable remuneration');
|
|
27
|
+
}
|
|
28
|
+
const toBigint = (field) => {
|
|
29
|
+
if (typeof field === 'bigint' || typeof field === 'number' || typeof field === 'string') {
|
|
30
|
+
return BigInt(field);
|
|
31
|
+
}
|
|
32
|
+
if (field && typeof field === 'object' && 'value' in field && field.value !== undefined) {
|
|
33
|
+
const value = field.value;
|
|
34
|
+
if (typeof value === 'bigint' || typeof value === 'number' || typeof value === 'string') {
|
|
35
|
+
return BigInt(value);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
throw new errors_1.VerificationError('invalid BLS field element');
|
|
39
|
+
};
|
|
40
|
+
try {
|
|
41
|
+
const sigBytes = (0, ethers_1.getBytes)(signatureHex);
|
|
42
|
+
const point = curves.bls12_381.G2.ProjectivePoint.fromHex(sigBytes);
|
|
43
|
+
const affine = point.toAffine();
|
|
44
|
+
const [x0, x1] = affine.x.c;
|
|
45
|
+
const [y0, y1] = affine.y.c;
|
|
46
|
+
const coords = [x0, x1, y0, y1].map((fp) => toBigint(fp));
|
|
47
|
+
const words = [];
|
|
48
|
+
coords.forEach((coord) => {
|
|
49
|
+
const [hi, lo] = splitFp(coord);
|
|
50
|
+
words.push(hi, lo);
|
|
51
|
+
});
|
|
52
|
+
return words;
|
|
53
|
+
}
|
|
54
|
+
catch (err) {
|
|
55
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
56
|
+
throw new errors_1.VerificationError(`invalid BLS signature: ${message}`);
|
|
57
|
+
}
|
|
58
|
+
}
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { AuthTokens } from './auth';
|
|
2
|
+
import { Config } from './config';
|
|
3
|
+
import { ContractGateway } from './contract';
|
|
4
|
+
import { AssetBalanceInfo, BLSCert, CollateralEventInfo, GuaranteeInfo, PaymentGuaranteeClaims, PaymentGuaranteeRequestClaims, PaymentSignature, PendingRemunerationInfo, RecipientPaymentInfo, SigningScheme, TabInfo, TabPaymentStatus, UserInfo } from './models';
|
|
5
|
+
import { RpcProxy } from './rpc';
|
|
6
|
+
import { CorePublicParameters, PaymentSigner } from './signing';
|
|
7
|
+
export declare class Client {
|
|
8
|
+
readonly rpc: RpcProxy;
|
|
9
|
+
readonly params: CorePublicParameters;
|
|
10
|
+
readonly gateway: ContractGateway;
|
|
11
|
+
readonly guaranteeDomain: string;
|
|
12
|
+
readonly user: UserClient;
|
|
13
|
+
readonly recipient: RecipientClient;
|
|
14
|
+
readonly signer: PaymentSigner;
|
|
15
|
+
private authSession?;
|
|
16
|
+
private constructor();
|
|
17
|
+
static new(cfg: Config): Promise<Client>;
|
|
18
|
+
private static buildGateway;
|
|
19
|
+
private static validateChainId;
|
|
20
|
+
aclose(): Promise<void>;
|
|
21
|
+
login(): Promise<AuthTokens>;
|
|
22
|
+
}
|
|
23
|
+
export declare class UserClient {
|
|
24
|
+
private client;
|
|
25
|
+
constructor(client: Client);
|
|
26
|
+
get guaranteeDomain(): string;
|
|
27
|
+
approveErc20(token: string, amount: number | bigint | string): Promise<unknown>;
|
|
28
|
+
deposit(amount: number | bigint | string, erc20Token?: string): Promise<unknown>;
|
|
29
|
+
getUser(): Promise<UserInfo[]>;
|
|
30
|
+
getTabPaymentStatus(tabId: number | bigint): Promise<TabPaymentStatus>;
|
|
31
|
+
signPayment(claims: PaymentGuaranteeRequestClaims, scheme?: SigningScheme): Promise<PaymentSignature>;
|
|
32
|
+
payTab(tabId: number | bigint, reqId: number | bigint, amount: number | bigint | string, recipientAddress: string, erc20Token?: string): Promise<unknown>;
|
|
33
|
+
requestWithdrawal(amount: number | bigint | string, erc20Token?: string): Promise<unknown>;
|
|
34
|
+
cancelWithdrawal(erc20Token?: string): Promise<unknown>;
|
|
35
|
+
finalizeWithdrawal(erc20Token?: string): Promise<unknown>;
|
|
36
|
+
}
|
|
37
|
+
export declare class RecipientClient {
|
|
38
|
+
private client;
|
|
39
|
+
constructor(client: Client);
|
|
40
|
+
private get recipientAddress();
|
|
41
|
+
get guaranteeDomain(): string;
|
|
42
|
+
private checkSigner;
|
|
43
|
+
createTab(userAddress: string, recipientAddress: string, erc20Token: string | undefined | null, ttl?: number | null): Promise<bigint>;
|
|
44
|
+
getTabPaymentStatus(tabId: number | bigint): Promise<TabPaymentStatus>;
|
|
45
|
+
issuePaymentGuarantee(claims: PaymentGuaranteeRequestClaims, signature: string, scheme: SigningScheme): Promise<BLSCert>;
|
|
46
|
+
verifyPaymentGuarantee(cert: BLSCert): PaymentGuaranteeClaims;
|
|
47
|
+
remunerate(cert: BLSCert): Promise<unknown>;
|
|
48
|
+
listSettledTabs(): Promise<TabInfo[]>;
|
|
49
|
+
listPendingRemunerations(): Promise<PendingRemunerationInfo[]>;
|
|
50
|
+
getTab(tabId: number | bigint): Promise<TabInfo | null>;
|
|
51
|
+
listRecipientTabs(settlementStatuses?: string[]): Promise<TabInfo[]>;
|
|
52
|
+
getTabGuarantees(tabId: number | bigint): Promise<GuaranteeInfo[]>;
|
|
53
|
+
getLatestGuarantee(tabId: number | bigint): Promise<GuaranteeInfo | null>;
|
|
54
|
+
getGuarantee(tabId: number | bigint, reqId: number | bigint): Promise<GuaranteeInfo | null>;
|
|
55
|
+
listRecipientPayments(): Promise<RecipientPaymentInfo[]>;
|
|
56
|
+
getCollateralEventsForTab(tabId: number | bigint): Promise<CollateralEventInfo[]>;
|
|
57
|
+
getUserAssetBalance(userAddress: string, assetAddress: string): Promise<AssetBalanceInfo | null>;
|
|
58
|
+
}
|