@phantom/api-key-stamper 0.0.2

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 ADDED
@@ -0,0 +1,120 @@
1
+ # @phantom/api-key-stamper
2
+
3
+ API key stamper for authenticating requests to Phantom Wallet API.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @phantom/api-key-stamper
9
+ # or
10
+ yarn add @phantom/api-key-stamper
11
+ ```
12
+
13
+ ## Usage
14
+
15
+ The `ApiKeyStamper` is used to sign HTTP requests with Ed25519 signatures for authentication with Phantom's API.
16
+
17
+ ### Basic Usage
18
+
19
+ ```typescript
20
+ import { ApiKeyStamper } from "@phantom/api-key-stamper";
21
+ import { PhantomClient } from "@phantom/client";
22
+
23
+ // Create a stamper with your secret key
24
+ const stamper = new ApiKeyStamper({
25
+ apiSecretKey: "your-base58-encoded-secret-key",
26
+ });
27
+
28
+ // Use it with PhantomClient
29
+ const client = new PhantomClient(
30
+ {
31
+ apiBaseUrl: "https://api.phantom.com",
32
+ organizationId: "your-org-id",
33
+ },
34
+ stamper,
35
+ );
36
+
37
+ // Now all requests will be automatically signed
38
+ const wallet = await client.createWallet("My Wallet");
39
+ ```
40
+
41
+ ### With Server SDK
42
+
43
+ The `@phantom/server-sdk` package uses this stamper internally:
44
+
45
+ ```typescript
46
+ import { ServerSDK } from "@phantom/server-sdk";
47
+
48
+ const sdk = new ServerSDK({
49
+ organizationId: "your-org-id",
50
+ apiBaseUrl: "https://api.phantom.com",
51
+ apiPrivateKey: "your-base58-encoded-secret-key",
52
+ });
53
+
54
+ // The SDK automatically creates and uses an ApiKeyStamper
55
+ const wallet = await sdk.createWallet("My Wallet");
56
+ ```
57
+
58
+ ### How it Works
59
+
60
+ 1. The stamper takes your base58-encoded Ed25519 secret key
61
+ 2. For each request, it signs the request body with the secret key
62
+ 3. The stamp is added to the request headers as `X-Phantom-Stamp` containing:
63
+ - `publicKey`: Base64url-encoded public key
64
+ - `signature`: Base64url-encoded signature
65
+ - `kind`: Always "PKI" for this authentication method
66
+ 4. The entire stamp object is JSON-encoded and then base64url-encoded
67
+ 5. The server verifies the signature to authenticate the request
68
+
69
+ #### Example Stamp Structure
70
+
71
+ Before encoding, the stamp object looks like:
72
+
73
+ ```json
74
+ {
75
+ "publicKey": "base64url-encoded-public-key",
76
+ "signature": "base64url-encoded-signature",
77
+ "kind": "PKI"
78
+ }
79
+ ```
80
+
81
+ This JSON is then base64url-encoded and sent as the `X-Phantom-Stamp` header.
82
+
83
+ ### Security Notes
84
+
85
+ - Keep your secret key secure and never expose it in client-side code
86
+ - Only use this stamper in server-side applications
87
+ - The secret key should be stored securely (e.g., in environment variables)
88
+
89
+ ## Development
90
+
91
+ ### Running Tests
92
+
93
+ ```bash
94
+ npm test
95
+ ```
96
+
97
+ The test suite includes:
98
+
99
+ - Constructor validation with valid/invalid keys
100
+ - Header addition and preservation
101
+ - Different data types (string, object, undefined)
102
+ - Stamp structure validation
103
+ - Cryptographic signature verification
104
+ - Base64url encoding/decoding utilities
105
+ - Edge cases and error handling
106
+
107
+ ## API Reference
108
+
109
+ ### ApiKeyStamper
110
+
111
+ ```typescript
112
+ class ApiKeyStamper {
113
+ constructor(config: ApiKeyStamperConfig);
114
+ stamp(config: AxiosRequestConfig): Promise<AxiosRequestConfig>;
115
+ }
116
+
117
+ interface ApiKeyStamperConfig {
118
+ apiSecretKey: string; // Base58-encoded Ed25519 secret key
119
+ }
120
+ ```
@@ -0,0 +1,18 @@
1
+ import { AxiosRequestConfig } from 'axios';
2
+
3
+ interface ApiKeyStamperConfig {
4
+ apiSecretKey: string;
5
+ }
6
+ /**
7
+ * ApiKeyStamper that signs requests with Ed25519
8
+ */
9
+ declare class ApiKeyStamper {
10
+ private keypair;
11
+ constructor(config: ApiKeyStamperConfig);
12
+ /**
13
+ * Stamp (sign) an axios request configuration
14
+ */
15
+ stamp(config: AxiosRequestConfig): Promise<AxiosRequestConfig>;
16
+ }
17
+
18
+ export { ApiKeyStamper, ApiKeyStamperConfig };
package/dist/index.js ADDED
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var src_exports = {};
32
+ __export(src_exports, {
33
+ ApiKeyStamper: () => ApiKeyStamper
34
+ });
35
+ module.exports = __toCommonJS(src_exports);
36
+ var import_buffer = require("buffer");
37
+ var import_base64url = require("@phantom/base64url");
38
+ var import_crypto = require("@phantom/crypto");
39
+ var import_bs58 = __toESM(require("bs58"));
40
+ var ApiKeyStamper = class {
41
+ constructor(config) {
42
+ this.keypair = (0, import_crypto.createKeyPairFromSecret)(config.apiSecretKey);
43
+ }
44
+ /**
45
+ * Stamp (sign) an axios request configuration
46
+ */
47
+ async stamp(config) {
48
+ const requestBody = typeof config.data === "string" ? config.data : config.data === void 0 ? "" : JSON.stringify(config.data);
49
+ const dataUtf8 = import_buffer.Buffer.from(requestBody, "utf8");
50
+ const signature = (0, import_crypto.signWithSecret)(this.keypair.secretKey, dataUtf8);
51
+ const stampData = {
52
+ // The keypair is bs58 encoded, need to decode and then base64url encode the public key
53
+ publicKey: (0, import_base64url.base64urlEncode)(import_bs58.default.decode(this.keypair.publicKey)),
54
+ signature: (0, import_base64url.base64urlEncode)(signature),
55
+ kind: "PKI"
56
+ };
57
+ const stampJson = JSON.stringify(stampData);
58
+ const stamp = (0, import_base64url.stringToBase64url)(stampJson);
59
+ config.headers = config.headers || {};
60
+ config.headers["X-Phantom-Stamp"] = stamp;
61
+ return Promise.resolve(config);
62
+ }
63
+ };
64
+ // Annotate the CommonJS export names for ESM import in node:
65
+ 0 && (module.exports = {
66
+ ApiKeyStamper
67
+ });
package/dist/index.mjs ADDED
@@ -0,0 +1,32 @@
1
+ // src/index.ts
2
+ import { Buffer } from "buffer";
3
+ import { base64urlEncode, stringToBase64url } from "@phantom/base64url";
4
+ import { createKeyPairFromSecret, signWithSecret } from "@phantom/crypto";
5
+ import bs58 from "bs58";
6
+ var ApiKeyStamper = class {
7
+ constructor(config) {
8
+ this.keypair = createKeyPairFromSecret(config.apiSecretKey);
9
+ }
10
+ /**
11
+ * Stamp (sign) an axios request configuration
12
+ */
13
+ async stamp(config) {
14
+ const requestBody = typeof config.data === "string" ? config.data : config.data === void 0 ? "" : JSON.stringify(config.data);
15
+ const dataUtf8 = Buffer.from(requestBody, "utf8");
16
+ const signature = signWithSecret(this.keypair.secretKey, dataUtf8);
17
+ const stampData = {
18
+ // The keypair is bs58 encoded, need to decode and then base64url encode the public key
19
+ publicKey: base64urlEncode(bs58.decode(this.keypair.publicKey)),
20
+ signature: base64urlEncode(signature),
21
+ kind: "PKI"
22
+ };
23
+ const stampJson = JSON.stringify(stampData);
24
+ const stamp = stringToBase64url(stampJson);
25
+ config.headers = config.headers || {};
26
+ config.headers["X-Phantom-Stamp"] = stamp;
27
+ return Promise.resolve(config);
28
+ }
29
+ };
30
+ export {
31
+ ApiKeyStamper
32
+ };
package/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "@phantom/api-key-stamper",
3
+ "version": "0.0.2",
4
+ "description": "API key stamper for Phantom Wallet SDK",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.mjs",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.mjs",
11
+ "require": "./dist/index.js",
12
+ "types": "./dist/index.d.ts"
13
+ }
14
+ },
15
+ "scripts": {
16
+ "build": "rimraf ./dist && tsup src/index.ts --format cjs,esm --dts --external buffer,@phantom/base64url,@phantom/crypto",
17
+ "dev": "tsc --watch",
18
+ "clean": "rm -rf dist",
19
+ "test": "jest",
20
+ "test:watch": "jest --watch",
21
+ "lint": "tsc --noEmit && eslint --cache . --ext .ts,.tsx",
22
+ "prettier": "prettier --write \"src/**/*.{ts,tsx}\""
23
+ },
24
+ "devDependencies": {
25
+ "@types/bs58": "^4.0.1",
26
+ "@types/jest": "^29.5.12",
27
+ "@types/node": "^20.11.0",
28
+ "eslint": "8.53.0",
29
+ "jest": "^29.7.0",
30
+ "prettier": "^3.5.2",
31
+ "rimraf": "^6.0.1",
32
+ "ts-jest": "^29.1.2",
33
+ "tsup": "^6.7.0",
34
+ "typescript": "^5.0.4"
35
+ },
36
+ "dependencies": {
37
+ "@phantom/base64url": "workspace:^",
38
+ "@phantom/crypto": "workspace:^",
39
+ "axios": "^1.10.0",
40
+ "bs58": "^6.0.0",
41
+ "buffer": "^6.0.3"
42
+ },
43
+ "files": [
44
+ "dist"
45
+ ]
46
+ }