@pay-skill/sdk 0.1.1 → 0.1.3
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/README.md +154 -154
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +2 -2
- package/dist/auth.js.map +1 -1
- package/dist/client.d.ts +8 -7
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +106 -54
- package/dist/client.js.map +1 -1
- package/dist/eip3009.d.ts +24 -0
- package/dist/eip3009.d.ts.map +1 -0
- package/dist/eip3009.js +56 -0
- package/dist/eip3009.js.map +1 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/models.d.ts +35 -26
- package/dist/models.d.ts.map +1 -1
- package/dist/signer.d.ts +1 -1
- package/dist/signer.d.ts.map +1 -1
- package/dist/signer.js +1 -2
- package/dist/signer.js.map +1 -1
- package/dist/wallet.d.ts.map +1 -1
- package/dist/wallet.js +6 -3
- package/dist/wallet.js.map +1 -1
- package/jsr.json +12 -0
- package/package.json +48 -44
- package/src/auth.ts +200 -200
- package/src/client.ts +644 -644
- package/src/eip3009.ts +79 -79
- package/src/index.ts +51 -51
- package/src/models.ts +77 -77
- package/src/ows-signer.ts +223 -223
- package/src/signer.ts +147 -147
- package/src/wallet.ts +445 -445
- package/tests/test_auth_rejection.ts +154 -154
- package/tests/test_crypto.ts +251 -251
- package/tests/test_e2e.ts +158 -158
- package/tests/test_ows_integration.ts +92 -92
- package/tests/test_ows_signer.ts +365 -365
- package/tests/test_validation.ts +66 -66
|
@@ -1,154 +1,154 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Auth rejection tests — proves that:
|
|
3
|
-
* 1. Requests without auth headers are rejected with 401
|
|
4
|
-
* 2. Requests with invalid/wrong signatures are rejected with 401
|
|
5
|
-
* 3. The SDK surfaces auth errors as PayServerError with correct statusCode
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { describe, it, beforeEach, afterEach } from "node:test";
|
|
9
|
-
import assert from "node:assert/strict";
|
|
10
|
-
import { createServer, type Server, type IncomingMessage, type ServerResponse } from "node:http";
|
|
11
|
-
import { once } from "node:events";
|
|
12
|
-
|
|
13
|
-
import { PayClient, PayServerError, CallbackSigner, RawKeySigner } from "../src/index.js";
|
|
14
|
-
import type { Hex, Address } from "viem";
|
|
15
|
-
|
|
16
|
-
const ANVIL_PK =
|
|
17
|
-
"0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" as Hex;
|
|
18
|
-
const WRONG_PK =
|
|
19
|
-
"0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d" as Hex;
|
|
20
|
-
|
|
21
|
-
const TEST_ROUTER = "0x5FbDB2315678afecb367f032d93F642f64180aa3" as Address;
|
|
22
|
-
const TEST_CHAIN_ID = 8453;
|
|
23
|
-
const VALID_ADDR = "0x" + "a1".repeat(20);
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Minimal HTTP server that enforces X-Pay-* auth headers.
|
|
27
|
-
* Returns 401 if any required header is missing, 200 otherwise.
|
|
28
|
-
*/
|
|
29
|
-
function createAuthServer(): Server {
|
|
30
|
-
return createServer((req: IncomingMessage, res: ServerResponse) => {
|
|
31
|
-
const agent = req.headers["x-pay-agent"];
|
|
32
|
-
const sig = req.headers["x-pay-signature"];
|
|
33
|
-
const ts = req.headers["x-pay-timestamp"];
|
|
34
|
-
const nonce = req.headers["x-pay-nonce"];
|
|
35
|
-
|
|
36
|
-
if (!agent || !sig || !ts || !nonce) {
|
|
37
|
-
res.writeHead(401, { "Content-Type": "application/json" });
|
|
38
|
-
res.end(JSON.stringify({ error: "Missing auth headers" }));
|
|
39
|
-
return;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// Check that sig looks like a real 65-byte hex signature
|
|
43
|
-
const sigStr = Array.isArray(sig) ? sig[0] : sig;
|
|
44
|
-
if (!sigStr.startsWith("0x") || sigStr.length !== 132) {
|
|
45
|
-
res.writeHead(401, { "Content-Type": "application/json" });
|
|
46
|
-
res.end(JSON.stringify({ error: "Invalid signature format" }));
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// Check that sig is not all zeros (stub detection)
|
|
51
|
-
if (sigStr === "0x" + "0".repeat(130)) {
|
|
52
|
-
res.writeHead(401, { "Content-Type": "application/json" });
|
|
53
|
-
res.end(JSON.stringify({ error: "Stub signature rejected" }));
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// Auth passed — return mock data
|
|
58
|
-
res.writeHead(200, { "Content-Type": "application/json" });
|
|
59
|
-
res.end(
|
|
60
|
-
JSON.stringify({
|
|
61
|
-
address: agent,
|
|
62
|
-
balance: 100_000_000,
|
|
63
|
-
open_tabs: [],
|
|
64
|
-
})
|
|
65
|
-
);
|
|
66
|
-
});
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
let server: Server;
|
|
70
|
-
let baseUrl: string;
|
|
71
|
-
|
|
72
|
-
describe("Auth rejection", () => {
|
|
73
|
-
beforeEach(async () => {
|
|
74
|
-
server = createAuthServer();
|
|
75
|
-
server.listen(0); // random port
|
|
76
|
-
await once(server, "listening");
|
|
77
|
-
const addr = server.address();
|
|
78
|
-
if (typeof addr === "object" && addr) {
|
|
79
|
-
baseUrl = `http://127.0.0.1:${addr.port}`;
|
|
80
|
-
}
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
afterEach(async () => {
|
|
84
|
-
server.close();
|
|
85
|
-
await once(server, "close");
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
it("rejects request without auth headers (no private key configured)", async () => {
|
|
89
|
-
// Client with no auth config — sends no X-Pay-* headers
|
|
90
|
-
const client = new PayClient({
|
|
91
|
-
apiUrl: baseUrl,
|
|
92
|
-
signer: new CallbackSigner((_h: Uint8Array) => new Uint8Array(65)),
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
await assert.rejects(
|
|
96
|
-
() => client.getStatus(),
|
|
97
|
-
(err: unknown) => {
|
|
98
|
-
assert.ok(err instanceof PayServerError);
|
|
99
|
-
assert.equal(err.statusCode, 401);
|
|
100
|
-
assert.ok(err.message.includes("Missing auth headers"));
|
|
101
|
-
return true;
|
|
102
|
-
}
|
|
103
|
-
);
|
|
104
|
-
});
|
|
105
|
-
|
|
106
|
-
it("rejects request with stub signer (all-zero signature)", async () => {
|
|
107
|
-
// Client with a stub signer that returns zeros — server should reject
|
|
108
|
-
const client = new PayClient({
|
|
109
|
-
apiUrl: baseUrl,
|
|
110
|
-
signer: new CallbackSigner((_h: Uint8Array) => new Uint8Array(65)),
|
|
111
|
-
chainId: TEST_CHAIN_ID,
|
|
112
|
-
routerAddress: TEST_ROUTER,
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
await assert.rejects(
|
|
116
|
-
() => client.getStatus(),
|
|
117
|
-
(err: unknown) => {
|
|
118
|
-
assert.ok(err instanceof PayServerError);
|
|
119
|
-
assert.equal(err.statusCode, 401);
|
|
120
|
-
return true;
|
|
121
|
-
}
|
|
122
|
-
);
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
it("accepts request with valid auth headers (real signing)", async () => {
|
|
126
|
-
const client = new PayClient({
|
|
127
|
-
apiUrl: baseUrl,
|
|
128
|
-
privateKey: ANVIL_PK,
|
|
129
|
-
chainId: TEST_CHAIN_ID,
|
|
130
|
-
routerAddress: TEST_ROUTER,
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
// Should NOT throw — server accepts valid auth
|
|
134
|
-
const status = await client.getStatus();
|
|
135
|
-
assert.ok(status.balance >= 0);
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
it("PayServerError has statusCode 401 for auth failures", async () => {
|
|
139
|
-
// Directly verify error structure
|
|
140
|
-
const client = new PayClient({
|
|
141
|
-
apiUrl: baseUrl,
|
|
142
|
-
signer: new CallbackSigner((_h: Uint8Array) => new Uint8Array(65)),
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
try {
|
|
146
|
-
await client.getStatus();
|
|
147
|
-
assert.fail("Should have thrown PayServerError");
|
|
148
|
-
} catch (err) {
|
|
149
|
-
assert.ok(err instanceof PayServerError, "must be PayServerError");
|
|
150
|
-
assert.equal(err.statusCode, 401, "statusCode must be 401");
|
|
151
|
-
assert.equal(err.code, "server_error");
|
|
152
|
-
}
|
|
153
|
-
});
|
|
154
|
-
});
|
|
1
|
+
/**
|
|
2
|
+
* Auth rejection tests — proves that:
|
|
3
|
+
* 1. Requests without auth headers are rejected with 401
|
|
4
|
+
* 2. Requests with invalid/wrong signatures are rejected with 401
|
|
5
|
+
* 3. The SDK surfaces auth errors as PayServerError with correct statusCode
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { describe, it, beforeEach, afterEach } from "node:test";
|
|
9
|
+
import assert from "node:assert/strict";
|
|
10
|
+
import { createServer, type Server, type IncomingMessage, type ServerResponse } from "node:http";
|
|
11
|
+
import { once } from "node:events";
|
|
12
|
+
|
|
13
|
+
import { PayClient, PayServerError, CallbackSigner, RawKeySigner } from "../src/index.js";
|
|
14
|
+
import type { Hex, Address } from "viem";
|
|
15
|
+
|
|
16
|
+
const ANVIL_PK =
|
|
17
|
+
"0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80" as Hex;
|
|
18
|
+
const WRONG_PK =
|
|
19
|
+
"0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d" as Hex;
|
|
20
|
+
|
|
21
|
+
const TEST_ROUTER = "0x5FbDB2315678afecb367f032d93F642f64180aa3" as Address;
|
|
22
|
+
const TEST_CHAIN_ID = 8453;
|
|
23
|
+
const VALID_ADDR = "0x" + "a1".repeat(20);
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Minimal HTTP server that enforces X-Pay-* auth headers.
|
|
27
|
+
* Returns 401 if any required header is missing, 200 otherwise.
|
|
28
|
+
*/
|
|
29
|
+
function createAuthServer(): Server {
|
|
30
|
+
return createServer((req: IncomingMessage, res: ServerResponse) => {
|
|
31
|
+
const agent = req.headers["x-pay-agent"];
|
|
32
|
+
const sig = req.headers["x-pay-signature"];
|
|
33
|
+
const ts = req.headers["x-pay-timestamp"];
|
|
34
|
+
const nonce = req.headers["x-pay-nonce"];
|
|
35
|
+
|
|
36
|
+
if (!agent || !sig || !ts || !nonce) {
|
|
37
|
+
res.writeHead(401, { "Content-Type": "application/json" });
|
|
38
|
+
res.end(JSON.stringify({ error: "Missing auth headers" }));
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Check that sig looks like a real 65-byte hex signature
|
|
43
|
+
const sigStr = Array.isArray(sig) ? sig[0] : sig;
|
|
44
|
+
if (!sigStr.startsWith("0x") || sigStr.length !== 132) {
|
|
45
|
+
res.writeHead(401, { "Content-Type": "application/json" });
|
|
46
|
+
res.end(JSON.stringify({ error: "Invalid signature format" }));
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Check that sig is not all zeros (stub detection)
|
|
51
|
+
if (sigStr === "0x" + "0".repeat(130)) {
|
|
52
|
+
res.writeHead(401, { "Content-Type": "application/json" });
|
|
53
|
+
res.end(JSON.stringify({ error: "Stub signature rejected" }));
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Auth passed — return mock data
|
|
58
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
59
|
+
res.end(
|
|
60
|
+
JSON.stringify({
|
|
61
|
+
address: agent,
|
|
62
|
+
balance: 100_000_000,
|
|
63
|
+
open_tabs: [],
|
|
64
|
+
})
|
|
65
|
+
);
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
let server: Server;
|
|
70
|
+
let baseUrl: string;
|
|
71
|
+
|
|
72
|
+
describe("Auth rejection", () => {
|
|
73
|
+
beforeEach(async () => {
|
|
74
|
+
server = createAuthServer();
|
|
75
|
+
server.listen(0); // random port
|
|
76
|
+
await once(server, "listening");
|
|
77
|
+
const addr = server.address();
|
|
78
|
+
if (typeof addr === "object" && addr) {
|
|
79
|
+
baseUrl = `http://127.0.0.1:${addr.port}`;
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
afterEach(async () => {
|
|
84
|
+
server.close();
|
|
85
|
+
await once(server, "close");
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it("rejects request without auth headers (no private key configured)", async () => {
|
|
89
|
+
// Client with no auth config — sends no X-Pay-* headers
|
|
90
|
+
const client = new PayClient({
|
|
91
|
+
apiUrl: baseUrl,
|
|
92
|
+
signer: new CallbackSigner((_h: Uint8Array) => new Uint8Array(65)),
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
await assert.rejects(
|
|
96
|
+
() => client.getStatus(),
|
|
97
|
+
(err: unknown) => {
|
|
98
|
+
assert.ok(err instanceof PayServerError);
|
|
99
|
+
assert.equal(err.statusCode, 401);
|
|
100
|
+
assert.ok(err.message.includes("Missing auth headers"));
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it("rejects request with stub signer (all-zero signature)", async () => {
|
|
107
|
+
// Client with a stub signer that returns zeros — server should reject
|
|
108
|
+
const client = new PayClient({
|
|
109
|
+
apiUrl: baseUrl,
|
|
110
|
+
signer: new CallbackSigner((_h: Uint8Array) => new Uint8Array(65)),
|
|
111
|
+
chainId: TEST_CHAIN_ID,
|
|
112
|
+
routerAddress: TEST_ROUTER,
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
await assert.rejects(
|
|
116
|
+
() => client.getStatus(),
|
|
117
|
+
(err: unknown) => {
|
|
118
|
+
assert.ok(err instanceof PayServerError);
|
|
119
|
+
assert.equal(err.statusCode, 401);
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
122
|
+
);
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it("accepts request with valid auth headers (real signing)", async () => {
|
|
126
|
+
const client = new PayClient({
|
|
127
|
+
apiUrl: baseUrl,
|
|
128
|
+
privateKey: ANVIL_PK,
|
|
129
|
+
chainId: TEST_CHAIN_ID,
|
|
130
|
+
routerAddress: TEST_ROUTER,
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
// Should NOT throw — server accepts valid auth
|
|
134
|
+
const status = await client.getStatus();
|
|
135
|
+
assert.ok(status.balance >= 0);
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it("PayServerError has statusCode 401 for auth failures", async () => {
|
|
139
|
+
// Directly verify error structure
|
|
140
|
+
const client = new PayClient({
|
|
141
|
+
apiUrl: baseUrl,
|
|
142
|
+
signer: new CallbackSigner((_h: Uint8Array) => new Uint8Array(65)),
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
try {
|
|
146
|
+
await client.getStatus();
|
|
147
|
+
assert.fail("Should have thrown PayServerError");
|
|
148
|
+
} catch (err) {
|
|
149
|
+
assert.ok(err instanceof PayServerError, "must be PayServerError");
|
|
150
|
+
assert.equal(err.statusCode, 401, "statusCode must be 401");
|
|
151
|
+
assert.equal(err.code, "server_error");
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
});
|