phantasma-sdk-ts 0.9.2 → 0.10.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/README.md +6 -0
- package/dist/cjs/index.js +3 -1
- package/dist/cjs/link/index.js +5 -0
- package/dist/cjs/link/v5/capabilities.js +6 -0
- package/dist/cjs/link/v5/client.js +335 -0
- package/dist/cjs/link/v5/deeplink.js +133 -0
- package/dist/cjs/link/v5/encoding.js +95 -0
- package/dist/cjs/link/v5/envelope.js +73 -0
- package/dist/cjs/link/v5/errors.js +60 -0
- package/dist/cjs/link/v5/index.js +33 -0
- package/dist/cjs/link/v5/loopback-transport.js +70 -0
- package/dist/cjs/link/v5/methods.js +4 -0
- package/dist/cjs/link/v5/pairing.js +95 -0
- package/dist/cjs/link/v5/protocol.js +61 -0
- package/dist/cjs/link/v5/relay-transport.js +303 -0
- package/dist/cjs/link/v5/session-crypto.js +120 -0
- package/dist/cjs/link/v5/transport.js +213 -0
- package/dist/cjs/link/v5/web-deeplink.js +141 -0
- package/dist/cjs/public.js +37 -1
- package/dist/esm/index.js +2 -0
- package/dist/esm/link/index.js +5 -0
- package/dist/esm/link/v5/capabilities.js +5 -0
- package/dist/esm/link/v5/client.js +331 -0
- package/dist/esm/link/v5/deeplink.js +125 -0
- package/dist/esm/link/v5/encoding.js +87 -0
- package/dist/esm/link/v5/envelope.js +65 -0
- package/dist/esm/link/v5/errors.js +56 -0
- package/dist/esm/link/v5/index.js +17 -0
- package/dist/esm/link/v5/loopback-transport.js +66 -0
- package/dist/esm/link/v5/methods.js +3 -0
- package/dist/esm/link/v5/pairing.js +91 -0
- package/dist/esm/link/v5/protocol.js +58 -0
- package/dist/esm/link/v5/relay-transport.js +299 -0
- package/dist/esm/link/v5/session-crypto.js +104 -0
- package/dist/esm/link/v5/transport.js +209 -0
- package/dist/esm/link/v5/web-deeplink.js +133 -0
- package/dist/esm/public.js +3 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/link/v5/capabilities.d.ts +80 -0
- package/dist/types/link/v5/capabilities.d.ts.map +1 -0
- package/dist/types/link/v5/client.d.ts +119 -0
- package/dist/types/link/v5/client.d.ts.map +1 -0
- package/dist/types/link/v5/deeplink.d.ts +57 -0
- package/dist/types/link/v5/deeplink.d.ts.map +1 -0
- package/dist/types/link/v5/encoding.d.ts +15 -0
- package/dist/types/link/v5/encoding.d.ts.map +1 -0
- package/dist/types/link/v5/envelope.d.ts +48 -0
- package/dist/types/link/v5/envelope.d.ts.map +1 -0
- package/dist/types/link/v5/errors.d.ts +39 -0
- package/dist/types/link/v5/errors.d.ts.map +1 -0
- package/dist/types/link/v5/index.d.ts +15 -0
- package/dist/types/link/v5/index.d.ts.map +1 -0
- package/dist/types/link/v5/loopback-transport.d.ts +43 -0
- package/dist/types/link/v5/loopback-transport.d.ts.map +1 -0
- package/dist/types/link/v5/methods.d.ts +83 -0
- package/dist/types/link/v5/methods.d.ts.map +1 -0
- package/dist/types/link/v5/pairing.d.ts +37 -0
- package/dist/types/link/v5/pairing.d.ts.map +1 -0
- package/dist/types/link/v5/protocol.d.ts +60 -0
- package/dist/types/link/v5/protocol.d.ts.map +1 -0
- package/dist/types/link/v5/relay-transport.d.ts +73 -0
- package/dist/types/link/v5/relay-transport.d.ts.map +1 -0
- package/dist/types/link/v5/session-crypto.d.ts +51 -0
- package/dist/types/link/v5/session-crypto.d.ts.map +1 -0
- package/dist/types/link/v5/transport.d.ts +83 -0
- package/dist/types/link/v5/transport.d.ts.map +1 -0
- package/dist/types/link/v5/web-deeplink.d.ts +81 -0
- package/dist/types/link/v5/web-deeplink.d.ts.map +1 -0
- package/dist/types/public.d.ts +1 -0
- package/dist/types/public.d.ts.map +1 -1
- package/examples/web-deeplink-dapp.ts +53 -0
- package/package.json +8 -2
- package/spec/CHANGELOG.md +9 -0
- package/spec/phantasma-link-v5.md +701 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Phantasma Link v5 - message envelope (spec §4). A JSON-RPC-2.0 profile that replaces the
|
|
3
|
+
// v1-v4 delimiter-joined string: typed named params, numeric error codes, and request
|
|
4
|
+
// correlation by `id`. One logical message = one envelope.
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.isLinkRequest = isLinkRequest;
|
|
7
|
+
exports.isLinkEvent = isLinkEvent;
|
|
8
|
+
exports.isLinkErrorResponse = isLinkErrorResponse;
|
|
9
|
+
exports.isLinkSuccessResponse = isLinkSuccessResponse;
|
|
10
|
+
exports.encodeEnvelope = encodeEnvelope;
|
|
11
|
+
exports.decodeEnvelope = decodeEnvelope;
|
|
12
|
+
const protocol_js_1 = require("./protocol.js");
|
|
13
|
+
const errors_js_1 = require("./errors.js");
|
|
14
|
+
function isLinkRequest(msg) {
|
|
15
|
+
return 'method' in msg && typeof msg.method === 'string';
|
|
16
|
+
}
|
|
17
|
+
function isLinkEvent(msg) {
|
|
18
|
+
return msg.type === 'event';
|
|
19
|
+
}
|
|
20
|
+
function isLinkErrorResponse(msg) {
|
|
21
|
+
return 'error' in msg && !!msg.error;
|
|
22
|
+
}
|
|
23
|
+
function isLinkSuccessResponse(msg) {
|
|
24
|
+
return 'result' in msg;
|
|
25
|
+
}
|
|
26
|
+
/** Serialize an envelope to its on-the-wire JSON text. */
|
|
27
|
+
function encodeEnvelope(message) {
|
|
28
|
+
return JSON.stringify(message);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Parse and validate an on-the-wire JSON text into a {@link LinkMessage}.
|
|
32
|
+
*
|
|
33
|
+
* Throws {@link LinkError} with `ParseError` for non-JSON and `InvalidRequest` for a JSON
|
|
34
|
+
* value that is not a well-formed v5 envelope (wrong `plv`, missing `id`, or a shape that
|
|
35
|
+
* is neither request, response, nor event). Validation happens here, once, so every
|
|
36
|
+
* downstream consumer can trust the structure.
|
|
37
|
+
*/
|
|
38
|
+
function decodeEnvelope(text) {
|
|
39
|
+
let parsed;
|
|
40
|
+
try {
|
|
41
|
+
parsed = JSON.parse(text);
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
throw new errors_js_1.LinkError(errors_js_1.LinkErrorCode.ParseError, 'Phantasma Link message is not valid JSON');
|
|
45
|
+
}
|
|
46
|
+
if (!parsed || typeof parsed !== 'object') {
|
|
47
|
+
throw new errors_js_1.LinkError(errors_js_1.LinkErrorCode.InvalidRequest, 'Phantasma Link message must be an object');
|
|
48
|
+
}
|
|
49
|
+
const record = parsed;
|
|
50
|
+
if (record.plv !== protocol_js_1.PLV) {
|
|
51
|
+
throw new errors_js_1.LinkError(errors_js_1.LinkErrorCode.InvalidRequest, `Unsupported Phantasma Link protocol version: ${String(record.plv)}`);
|
|
52
|
+
}
|
|
53
|
+
// Event messages have no `id` and are matched by the `type` discriminator first.
|
|
54
|
+
if (record.type === 'event') {
|
|
55
|
+
if (typeof record.event !== 'string') {
|
|
56
|
+
throw new errors_js_1.LinkError(errors_js_1.LinkErrorCode.InvalidRequest, 'Event envelope is missing `event`');
|
|
57
|
+
}
|
|
58
|
+
return parsed;
|
|
59
|
+
}
|
|
60
|
+
if (typeof record.id !== 'string' || record.id.length === 0) {
|
|
61
|
+
throw new errors_js_1.LinkError(errors_js_1.LinkErrorCode.InvalidRequest, 'Envelope is missing a string `id`');
|
|
62
|
+
}
|
|
63
|
+
if (typeof record.method === 'string') {
|
|
64
|
+
return parsed;
|
|
65
|
+
}
|
|
66
|
+
if ('result' in record) {
|
|
67
|
+
return parsed;
|
|
68
|
+
}
|
|
69
|
+
if (record.error && typeof record.error === 'object') {
|
|
70
|
+
return parsed;
|
|
71
|
+
}
|
|
72
|
+
throw new errors_js_1.LinkError(errors_js_1.LinkErrorCode.InvalidRequest, 'Envelope is neither a request, response, nor event');
|
|
73
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Phantasma Link v5 - structured error taxonomy (spec §10). Replaces the v1-v4 free-text
|
|
3
|
+
// error strings that callers had to substring-match.
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.LinkError = exports.LinkErrorCode = void 0;
|
|
6
|
+
/** Numeric error codes. JSON-RPC reserved range + EIP-1193-aligned app codes + Phantasma
|
|
7
|
+
* specific codes. Carried in {@link LinkErrorObject.code}. */
|
|
8
|
+
exports.LinkErrorCode = {
|
|
9
|
+
// JSON-RPC reserved.
|
|
10
|
+
ParseError: -32700,
|
|
11
|
+
InvalidRequest: -32600,
|
|
12
|
+
MethodNotFound: -32601,
|
|
13
|
+
InvalidParams: -32602,
|
|
14
|
+
InternalError: -32603,
|
|
15
|
+
// App-level (EIP-1193-aligned where sensible).
|
|
16
|
+
UserRejected: 4001,
|
|
17
|
+
Unauthorized: 4100,
|
|
18
|
+
Disconnected: 4900,
|
|
19
|
+
UnsupportedChain: 4902,
|
|
20
|
+
// Phantasma-specific.
|
|
21
|
+
PayloadTooLarge: 5001,
|
|
22
|
+
NexusMismatch: 5002,
|
|
23
|
+
UnsupportedSignatureKind: 5003,
|
|
24
|
+
CapabilityNotSupported: 5004,
|
|
25
|
+
SessionExpired: 5100,
|
|
26
|
+
SessionRevoked: 5101,
|
|
27
|
+
};
|
|
28
|
+
/** Error thrown by the v5 client and carried over the wire as {@link LinkErrorObject}.
|
|
29
|
+
* Keeping a dedicated class lets callers branch on the numeric `code` instead of parsing
|
|
30
|
+
* message text. */
|
|
31
|
+
class LinkError extends Error {
|
|
32
|
+
constructor(code, message, data) {
|
|
33
|
+
super(message);
|
|
34
|
+
this.name = 'LinkError';
|
|
35
|
+
this.code = code;
|
|
36
|
+
this.data = data;
|
|
37
|
+
// Preserve `instanceof LinkError` across the transpiled-to-ES2020 target.
|
|
38
|
+
Object.setPrototypeOf(this, LinkError.prototype);
|
|
39
|
+
}
|
|
40
|
+
toObject() {
|
|
41
|
+
// `data` is omitted entirely when undefined so the serialized shape stays minimal.
|
|
42
|
+
return this.data === undefined
|
|
43
|
+
? { code: this.code, message: this.message }
|
|
44
|
+
: { code: this.code, message: this.message, data: this.data };
|
|
45
|
+
}
|
|
46
|
+
/** Reconstruct a {@link LinkError} from a received error object, tolerating malformed
|
|
47
|
+
* inputs (a peer may send a non-conforming shape). */
|
|
48
|
+
static fromObject(obj) {
|
|
49
|
+
if (obj && typeof obj === 'object') {
|
|
50
|
+
const record = obj;
|
|
51
|
+
const code = typeof record.code === 'number' ? record.code : exports.LinkErrorCode.InternalError;
|
|
52
|
+
const message = typeof record.message === 'string' && record.message.length > 0
|
|
53
|
+
? record.message
|
|
54
|
+
: 'Phantasma Link error';
|
|
55
|
+
return new LinkError(code, message, record.data);
|
|
56
|
+
}
|
|
57
|
+
return new LinkError(exports.LinkErrorCode.InternalError, 'Phantasma Link error');
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
exports.LinkError = LinkError;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Phantasma Link v5 - public barrel for the new protocol generation. Import via
|
|
3
|
+
// `phantasma-sdk-ts/link/v5` or the `PhantasmaLinkV5` namespace from the package root.
|
|
4
|
+
// The legacy v1-v4 client (`PhantasmaLink`) remains available and unchanged.
|
|
5
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
6
|
+
if (k2 === undefined) k2 = k;
|
|
7
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
8
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
9
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
10
|
+
}
|
|
11
|
+
Object.defineProperty(o, k2, desc);
|
|
12
|
+
}) : (function(o, m, k, k2) {
|
|
13
|
+
if (k2 === undefined) k2 = k;
|
|
14
|
+
o[k2] = m[k];
|
|
15
|
+
}));
|
|
16
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
17
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
18
|
+
};
|
|
19
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
+
__exportStar(require("./protocol.js"), exports);
|
|
21
|
+
__exportStar(require("./errors.js"), exports);
|
|
22
|
+
__exportStar(require("./encoding.js"), exports);
|
|
23
|
+
__exportStar(require("./envelope.js"), exports);
|
|
24
|
+
__exportStar(require("./capabilities.js"), exports);
|
|
25
|
+
__exportStar(require("./methods.js"), exports);
|
|
26
|
+
__exportStar(require("./session-crypto.js"), exports);
|
|
27
|
+
__exportStar(require("./pairing.js"), exports);
|
|
28
|
+
__exportStar(require("./transport.js"), exports);
|
|
29
|
+
__exportStar(require("./loopback-transport.js"), exports);
|
|
30
|
+
__exportStar(require("./deeplink.js"), exports);
|
|
31
|
+
__exportStar(require("./relay-transport.js"), exports);
|
|
32
|
+
__exportStar(require("./web-deeplink.js"), exports);
|
|
33
|
+
__exportStar(require("./client.js"), exports);
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Phantasma Link v5 - loopback transport (spec §6.2). Desktop browser/app -> the wallet's
|
|
3
|
+
// local WebSocket server. This is the TRUSTED-LOCAL path: frames are plaintext v5 envelopes
|
|
4
|
+
// (no channel encryption needed; see LinkSessionClient with no session key). Default host is
|
|
5
|
+
// `localhost` (never the literal `127.0.0.1`).
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.LoopbackTransport = void 0;
|
|
8
|
+
const errors_js_1 = require("./errors.js");
|
|
9
|
+
function defaultWebSocketFactory(url) {
|
|
10
|
+
const globalWithWs = globalThis;
|
|
11
|
+
if (!globalWithWs.WebSocket) {
|
|
12
|
+
throw new errors_js_1.LinkError(errors_js_1.LinkErrorCode.Disconnected, 'No WebSocket implementation available; pass options.webSocketFactory');
|
|
13
|
+
}
|
|
14
|
+
return new globalWithWs.WebSocket(url);
|
|
15
|
+
}
|
|
16
|
+
/** A {@link LinkTransport} over a local WebSocket to the wallet. Sends are buffered until the
|
|
17
|
+
* socket opens, then flushed, so callers can issue requests immediately after construction. */
|
|
18
|
+
class LoopbackTransport {
|
|
19
|
+
constructor(options = {}) {
|
|
20
|
+
this.outbox = [];
|
|
21
|
+
this.isOpen = false;
|
|
22
|
+
const host = options.host ?? 'localhost';
|
|
23
|
+
const port = options.port ?? 7090;
|
|
24
|
+
const path = options.path ?? '/phantasma/v5';
|
|
25
|
+
const url = `ws://${host}:${port}${path}`;
|
|
26
|
+
const factory = options.webSocketFactory ?? defaultWebSocketFactory;
|
|
27
|
+
this.socket = factory(url);
|
|
28
|
+
this.socket.onopen = () => {
|
|
29
|
+
this.isOpen = true;
|
|
30
|
+
for (const frame of this.outbox) {
|
|
31
|
+
this.socket.send(frame);
|
|
32
|
+
}
|
|
33
|
+
this.outbox.length = 0;
|
|
34
|
+
};
|
|
35
|
+
this.socket.onmessage = (event) => {
|
|
36
|
+
if (typeof event.data === 'string') {
|
|
37
|
+
this.messageHandler?.(event.data);
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
this.socket.onclose = (event) => {
|
|
41
|
+
this.isOpen = false;
|
|
42
|
+
this.closeHandler?.(event?.reason);
|
|
43
|
+
};
|
|
44
|
+
// Errors surface as a subsequent close; nothing actionable to do here.
|
|
45
|
+
this.socket.onerror = () => { };
|
|
46
|
+
}
|
|
47
|
+
send(frame) {
|
|
48
|
+
if (this.isOpen) {
|
|
49
|
+
this.socket.send(frame);
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
this.outbox.push(frame);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
onMessage(handler) {
|
|
56
|
+
this.messageHandler = handler;
|
|
57
|
+
}
|
|
58
|
+
onClose(handler) {
|
|
59
|
+
this.closeHandler = handler;
|
|
60
|
+
}
|
|
61
|
+
close() {
|
|
62
|
+
try {
|
|
63
|
+
this.socket.close();
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
// Closing an already-closed socket is harmless.
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
exports.LoopbackTransport = LoopbackTransport;
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Phantasma Link v5 - pairing URI build/parse (spec §15). The pairing material rides in the
|
|
3
|
+
// URL FRAGMENT (never sent to the server). Two channels decide the key-establishment mode:
|
|
4
|
+
// - `sym` : a 32-byte session key in the fragment - ONLY for safe channels (a
|
|
5
|
+
// domain-verified universal link, or a QR). Simplest + MITM-proof.
|
|
6
|
+
// - `ecdh` : only the dApp's ephemeral X25519 PUBLIC key - for the hijackable
|
|
7
|
+
// custom-scheme fallback, where no secret may appear.
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.buildPairingUri = buildPairingUri;
|
|
10
|
+
exports.parsePairingUri = parsePairingUri;
|
|
11
|
+
const protocol_js_1 = require("./protocol.js");
|
|
12
|
+
const errors_js_1 = require("./errors.js");
|
|
13
|
+
const encoding_js_1 = require("./encoding.js");
|
|
14
|
+
/** Build a pairing URI. Enforces the security rule that a symmetric key (a secret) must
|
|
15
|
+
* NOT be placed in a hijackable custom-scheme URL (spec §15/§18). */
|
|
16
|
+
function buildPairingUri(input) {
|
|
17
|
+
const scheme = input.scheme ?? 'universal';
|
|
18
|
+
const params = new URLSearchParams();
|
|
19
|
+
params.set('v', String(protocol_js_1.PLV));
|
|
20
|
+
params.set('t', input.topic);
|
|
21
|
+
if (input.relay) {
|
|
22
|
+
params.set('relay', input.relay);
|
|
23
|
+
}
|
|
24
|
+
if (input.callback) {
|
|
25
|
+
params.set('cb', input.callback);
|
|
26
|
+
}
|
|
27
|
+
if (input.mode === 'sym') {
|
|
28
|
+
if (!input.symKey) {
|
|
29
|
+
throw new errors_js_1.LinkError(errors_js_1.LinkErrorCode.InvalidParams, 'sym pairing requires symKey');
|
|
30
|
+
}
|
|
31
|
+
if (scheme === 'scheme') {
|
|
32
|
+
// A scheme-squatting app could read this URL; never expose a secret there.
|
|
33
|
+
throw new errors_js_1.LinkError(errors_js_1.LinkErrorCode.InvalidParams, 'A symmetric key must not be placed in a custom-scheme URL; use a universal link or QR');
|
|
34
|
+
}
|
|
35
|
+
params.set('sk', (0, encoding_js_1.bytesToBase64Url)(input.symKey));
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
if (!input.dappPublicKey) {
|
|
39
|
+
throw new errors_js_1.LinkError(errors_js_1.LinkErrorCode.InvalidParams, 'ecdh pairing requires dappPublicKey');
|
|
40
|
+
}
|
|
41
|
+
params.set('pk', (0, encoding_js_1.bytesToBase64Url)(input.dappPublicKey));
|
|
42
|
+
}
|
|
43
|
+
if (input.meta) {
|
|
44
|
+
params.set('meta', (0, encoding_js_1.bytesToBase64Url)((0, encoding_js_1.utf8ToBytes)(JSON.stringify(input.meta))));
|
|
45
|
+
}
|
|
46
|
+
const base = scheme === 'scheme'
|
|
47
|
+
? 'phantasma://v5/pair'
|
|
48
|
+
: `https://${input.host ?? protocol_js_1.DEFAULT_LINK_HOST}/v5/pair`;
|
|
49
|
+
return `${base}#${params.toString()}`;
|
|
50
|
+
}
|
|
51
|
+
/** Parse a pairing URI (universal-link or custom-scheme) into {@link PairingParams}. */
|
|
52
|
+
function parsePairingUri(uri) {
|
|
53
|
+
const hashIndex = uri.indexOf('#');
|
|
54
|
+
if (hashIndex < 0) {
|
|
55
|
+
throw new errors_js_1.LinkError(errors_js_1.LinkErrorCode.InvalidRequest, 'Pairing URI has no fragment');
|
|
56
|
+
}
|
|
57
|
+
const params = new URLSearchParams(uri.slice(hashIndex + 1));
|
|
58
|
+
const version = Number(params.get('v'));
|
|
59
|
+
if (version !== protocol_js_1.PLV) {
|
|
60
|
+
throw new errors_js_1.LinkError(errors_js_1.LinkErrorCode.InvalidRequest, `Unsupported pairing version: ${params.get('v')}`);
|
|
61
|
+
}
|
|
62
|
+
const topic = params.get('t');
|
|
63
|
+
if (!topic) {
|
|
64
|
+
throw new errors_js_1.LinkError(errors_js_1.LinkErrorCode.InvalidRequest, 'Pairing URI is missing topic');
|
|
65
|
+
}
|
|
66
|
+
const relay = params.get('relay') ?? undefined;
|
|
67
|
+
const callback = params.get('cb') ?? undefined;
|
|
68
|
+
let meta;
|
|
69
|
+
const metaRaw = params.get('meta');
|
|
70
|
+
if (metaRaw) {
|
|
71
|
+
try {
|
|
72
|
+
meta = JSON.parse((0, encoding_js_1.bytesToUtf8)((0, encoding_js_1.base64UrlToBytes)(metaRaw)));
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
throw new errors_js_1.LinkError(errors_js_1.LinkErrorCode.InvalidRequest, 'Pairing URI has malformed meta');
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
const sk = params.get('sk');
|
|
79
|
+
const pk = params.get('pk');
|
|
80
|
+
if (sk) {
|
|
81
|
+
return { version, topic, relay, callback, mode: 'sym', symKey: (0, encoding_js_1.base64UrlToBytes)(sk), meta };
|
|
82
|
+
}
|
|
83
|
+
if (pk) {
|
|
84
|
+
return {
|
|
85
|
+
version,
|
|
86
|
+
topic,
|
|
87
|
+
relay,
|
|
88
|
+
callback,
|
|
89
|
+
mode: 'ecdh',
|
|
90
|
+
dappPublicKey: (0, encoding_js_1.base64UrlToBytes)(pk),
|
|
91
|
+
meta,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
throw new errors_js_1.LinkError(errors_js_1.LinkErrorCode.InvalidRequest, 'Pairing URI carries neither sk nor pk');
|
|
95
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Phantasma Link v5 - protocol constants (the new generation; runs in parallel with the
|
|
3
|
+
// legacy v1-v4 string protocol in `../phantasma-link.ts`). See the design spec:
|
|
4
|
+
// codex-pha `.codex/context/link/phantasma-link-v5-spec.md`.
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.SignatureKind = exports.TxFormat = exports.LinkEvent = exports.LinkMethod = exports.DEFAULT_LINK_HOST = exports.PLV = void 0;
|
|
7
|
+
/** Protocol version carried in every v5 envelope (`plv`). A peer that does not recognize
|
|
8
|
+
* this value rejects the message with {@link LinkErrorCode.InvalidRequest}. */
|
|
9
|
+
exports.PLV = 5;
|
|
10
|
+
/** Default subdomain that hosts the universal links + AASA/assetlinks + relay (spec §17).
|
|
11
|
+
* NEVER `phantasma.io` (hostile). */
|
|
12
|
+
exports.DEFAULT_LINK_HOST = 'link.phantasma.info';
|
|
13
|
+
/** Request methods (dApp -> wallet). Namespaced `pha_*`, EIP-1193-aligned (spec §9). */
|
|
14
|
+
exports.LinkMethod = {
|
|
15
|
+
/** Pair or resume a session; returns the capability handshake + account + session. */
|
|
16
|
+
Connect: 'pha_connect',
|
|
17
|
+
/** End the session. */
|
|
18
|
+
Disconnect: 'pha_disconnect',
|
|
19
|
+
/** Account(s) authorized for this session. */
|
|
20
|
+
GetAccounts: 'pha_getAccounts',
|
|
21
|
+
/** Supported chains (CAIP-2) + current; `nexus` as a field. */
|
|
22
|
+
GetChains: 'pha_getChains',
|
|
23
|
+
/** Wallet name/version/capabilities/rpc. */
|
|
24
|
+
GetWalletInfo: 'pha_getWalletInfo',
|
|
25
|
+
/** Sign an arbitrary, non-transaction-forgeable message. */
|
|
26
|
+
SignMessage: 'pha_signMessage',
|
|
27
|
+
/** Sign a transaction only (do NOT broadcast); the dApp submits it. */
|
|
28
|
+
SignTransaction: 'pha_signTransaction',
|
|
29
|
+
/** Sign AND broadcast a transaction via the format's RPC endpoint. */
|
|
30
|
+
SendTransaction: 'pha_sendTransaction',
|
|
31
|
+
/** Read-only VM invoke (no keys, no approval). */
|
|
32
|
+
InvokeScript: 'pha_invokeScript',
|
|
33
|
+
};
|
|
34
|
+
/** Event names pushed wallet -> dApp on a persistent transport (spec §9.5 events caveat). */
|
|
35
|
+
exports.LinkEvent = {
|
|
36
|
+
AccountsChanged: 'pha_accountsChanged',
|
|
37
|
+
ChainChanged: 'pha_chainChanged',
|
|
38
|
+
SessionDeleted: 'pha_sessionDeleted',
|
|
39
|
+
SessionExpired: 'pha_sessionExpired',
|
|
40
|
+
/** Unsolicited connect result pushed right after a pairing approval (spec §15 step 3),
|
|
41
|
+
* letting the first connection complete in one user gesture. Unlike the other events it
|
|
42
|
+
* also rides the deeplink transport: the wallet is foreground at the approval moment, so
|
|
43
|
+
* it CAN open the callback (this is a reply to the pairing, not a spontaneous push).
|
|
44
|
+
* `data` carries the same shape as a `pha_connect` result. */
|
|
45
|
+
SessionEstablished: 'pha_sessionEstablished',
|
|
46
|
+
};
|
|
47
|
+
/** Transaction serialization format. Selects which RPC submission endpoint the wallet uses
|
|
48
|
+
* (spec §9.4): `script` -> SendRawTransaction (classic `Transaction`), `carbon` ->
|
|
49
|
+
* SendCarbonTransaction (Carbon `SignedTxMsg`). Routing is by serialization envelope, NOT
|
|
50
|
+
* by "contains a script" (a Carbon tx may also wrap a script). */
|
|
51
|
+
exports.TxFormat = {
|
|
52
|
+
Script: 'script',
|
|
53
|
+
Carbon: 'carbon',
|
|
54
|
+
};
|
|
55
|
+
/** Signature scheme used to sign a Phantasma payload (spec §9.7). Selects the key:
|
|
56
|
+
* `Ed25519` = the Phantasma key, `ECDSA` = the secp256k1 (ETH/BSC-interop) key. This is
|
|
57
|
+
* NOT native foreign-chain signing - the wallet always targets Phantasma. */
|
|
58
|
+
exports.SignatureKind = {
|
|
59
|
+
Ed25519: 'Ed25519',
|
|
60
|
+
ECDSA: 'ECDSA',
|
|
61
|
+
};
|