@solana-mobile/mobile-wallet-adapter-protocol 2.2.0 → 2.2.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.
@@ -8,7 +8,7 @@ buildscript {
8
8
  }
9
9
 
10
10
  dependencies {
11
- classpath 'com.android.tools.build:gradle:8.9.2'
11
+ classpath 'com.android.tools.build:gradle:8.10.1'
12
12
  // noinspection DifferentKotlinGradleVersion
13
13
  classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
14
14
  }
package/package.json CHANGED
@@ -1,31 +1,42 @@
1
1
  {
2
2
  "name": "@solana-mobile/mobile-wallet-adapter-protocol",
3
3
  "description": "An implementation of the Solana Mobile Mobile Wallet Adapter protocol. Use this to open a session with a mobile wallet app, and to issue API calls to it.",
4
- "version": "2.2.0",
4
+ "version": "2.2.2",
5
5
  "author": "Steven Luscher <steven.luscher@solanamobile.com>",
6
6
  "repository": {
7
7
  "type": "git",
8
8
  "url": "git+https://github.com/solana-mobile/mobile-wallet-adapter.git"
9
9
  },
10
10
  "license": "Apache-2.0",
11
- "type": "module",
12
- "sideEffects": false,
13
- "main": "lib/cjs/index.js",
14
- "react-native": "lib/cjs/index.native.js",
15
- "module": "lib/esm/index.js",
16
- "types": "lib/types/index.d.ts",
11
+ "exports": {
12
+ "edge-light": {
13
+ "import": "./lib/esm/index.js",
14
+ "require": "./lib/cjs/index.js"
15
+ },
16
+ "workerd": {
17
+ "import": "./lib/esm/index.js",
18
+ "require": "./lib/cjs/index.js"
19
+ },
20
+ "browser": {
21
+ "import": "./lib/cjs/index.browser.js",
22
+ "require": "./lib/esm/index.browser.js"
23
+ },
24
+ "node": {
25
+ "import": "./lib/esm/index.js",
26
+ "require": "./lib/cjs/index.js"
27
+ },
28
+ "react-native": "./lib/cjs/index.native.js",
29
+ "types": "./lib/types/index.d.ts"
30
+ },
17
31
  "browser": {
18
32
  "./lib/cjs/index.js": "./lib/cjs/index.browser.js",
19
33
  "./lib/esm/index.js": "./lib/esm/index.browser.js"
20
34
  },
21
- "exports": {
22
- "./package.json": "./package.json",
23
- ".": {
24
- "import": "./lib/esm/index.js",
25
- "require": "./lib/cjs/index.js",
26
- "types": "./lib/types/index.d.ts"
27
- }
28
- },
35
+ "main": "lib/cjs/index.js",
36
+ "module": "lib/esm/index.js",
37
+ "react-native": "lib/cjs/index.native.js",
38
+ "types": "lib/types/index.d.ts",
39
+ "type": "module",
29
40
  "files": [
30
41
  "android",
31
42
  "src/codegenSpec",
@@ -33,6 +44,7 @@
33
44
  "lib",
34
45
  "LICENSE"
35
46
  ],
47
+ "sideEffects": false,
36
48
  "publishConfig": {
37
49
  "access": "public"
38
50
  },
@@ -57,7 +69,6 @@
57
69
  "shx": "^0.3.4"
58
70
  },
59
71
  "peerDependencies": {
60
- "@solana/web3.js": "^1.58.0",
61
72
  "react-native": ">0.69"
62
73
  },
63
74
  "codegenConfig": {
@@ -68,4 +79,4 @@
68
79
  "javaPackageName": "com.solanamobile.mobilewalletadapter.reactnative"
69
80
  }
70
81
  }
71
- }
82
+ }
@@ -1,14 +1,14 @@
1
- import {
2
- SolanaSignInInputWithRequiredFields,
3
- createSignInMessageText,
4
- } from '@solana/wallet-standard-util';
5
- import { SignInPayload } from './types';
6
- import { encode } from './base64Utils';
7
-
8
- export function createSIWSMessage(payload: SolanaSignInInputWithRequiredFields & SignInPayload): string {
9
- return createSignInMessageText(payload);
10
- }
11
-
12
- export function createSIWSMessageBase64(payload: SolanaSignInInputWithRequiredFields & SignInPayload): string {
13
- return encode(createSIWSMessage(payload));
1
+ import {
2
+ SolanaSignInInputWithRequiredFields,
3
+ createSignInMessageText,
4
+ } from '@solana/wallet-standard-util';
5
+ import { SignInPayload } from './types';
6
+ import { encode } from './base64Utils';
7
+
8
+ export function createSIWSMessage(payload: SolanaSignInInputWithRequiredFields & SignInPayload): string {
9
+ return createSignInMessageText(payload);
10
+ }
11
+
12
+ export function createSIWSMessageBase64(payload: SolanaSignInInputWithRequiredFields & SignInPayload): string {
13
+ return encode(createSIWSMessage(payload));
14
14
  }
@@ -1,60 +1,60 @@
1
- import createSequenceNumberVector, { SEQUENCE_NUMBER_BYTES } from './createSequenceNumberVector.js';
2
- import { SharedSecret } from './parseHelloRsp.js';
3
-
4
- const INITIALIZATION_VECTOR_BYTES = 12;
5
- export const ENCODED_PUBLIC_KEY_LENGTH_BYTES = 65;
6
-
7
- export async function encryptMessage(
8
- plaintext: string,
9
- sequenceNumber: number,
10
- sharedSecret: SharedSecret,
11
- ) {
12
- const sequenceNumberVector = createSequenceNumberVector(sequenceNumber);
13
- const initializationVector = new Uint8Array(INITIALIZATION_VECTOR_BYTES);
14
- crypto.getRandomValues(initializationVector);
15
- const ciphertext = await crypto.subtle.encrypt(
16
- getAlgorithmParams(sequenceNumberVector, initializationVector),
17
- sharedSecret,
18
- new TextEncoder().encode(plaintext),
19
- );
20
- const response = new Uint8Array(
21
- sequenceNumberVector.byteLength + initializationVector.byteLength + ciphertext.byteLength,
22
- );
23
- response.set(new Uint8Array(sequenceNumberVector), 0);
24
- response.set(new Uint8Array(initializationVector), sequenceNumberVector.byteLength);
25
- response.set(new Uint8Array(ciphertext), sequenceNumberVector.byteLength + initializationVector.byteLength);
26
- return response;
27
- }
28
-
29
- export async function decryptMessage(message: ArrayBuffer, sharedSecret: SharedSecret) {
30
- const sequenceNumberVector = message.slice(0, SEQUENCE_NUMBER_BYTES);
31
- const initializationVector = message.slice(
32
- SEQUENCE_NUMBER_BYTES,
33
- SEQUENCE_NUMBER_BYTES + INITIALIZATION_VECTOR_BYTES,
34
- );
35
- const ciphertext = message.slice(SEQUENCE_NUMBER_BYTES + INITIALIZATION_VECTOR_BYTES);
36
- const plaintextBuffer = await crypto.subtle.decrypt(
37
- getAlgorithmParams(sequenceNumberVector, initializationVector),
38
- sharedSecret,
39
- ciphertext,
40
- );
41
- const plaintext = getUtf8Decoder().decode(plaintextBuffer);
42
- return plaintext
43
- }
44
-
45
- function getAlgorithmParams(sequenceNumber: ArrayBuffer, initializationVector: ArrayBuffer) {
46
- return {
47
- additionalData: sequenceNumber,
48
- iv: initializationVector,
49
- name: 'AES-GCM',
50
- tagLength: 128, // 16 byte tag => 128 bits
51
- };
52
- }
53
-
54
- let _utf8Decoder: TextDecoder | undefined;
55
- function getUtf8Decoder() {
56
- if (_utf8Decoder === undefined) {
57
- _utf8Decoder = new TextDecoder('utf-8');
58
- }
59
- return _utf8Decoder;
1
+ import createSequenceNumberVector, { SEQUENCE_NUMBER_BYTES } from './createSequenceNumberVector.js';
2
+ import { SharedSecret } from './parseHelloRsp.js';
3
+
4
+ const INITIALIZATION_VECTOR_BYTES = 12;
5
+ export const ENCODED_PUBLIC_KEY_LENGTH_BYTES = 65;
6
+
7
+ export async function encryptMessage(
8
+ plaintext: string,
9
+ sequenceNumber: number,
10
+ sharedSecret: SharedSecret,
11
+ ) {
12
+ const sequenceNumberVector = createSequenceNumberVector(sequenceNumber);
13
+ const initializationVector = new Uint8Array(INITIALIZATION_VECTOR_BYTES);
14
+ crypto.getRandomValues(initializationVector);
15
+ const ciphertext = await crypto.subtle.encrypt(
16
+ getAlgorithmParams(sequenceNumberVector, initializationVector),
17
+ sharedSecret,
18
+ new TextEncoder().encode(plaintext),
19
+ );
20
+ const response = new Uint8Array(
21
+ sequenceNumberVector.byteLength + initializationVector.byteLength + ciphertext.byteLength,
22
+ );
23
+ response.set(new Uint8Array(sequenceNumberVector), 0);
24
+ response.set(new Uint8Array(initializationVector), sequenceNumberVector.byteLength);
25
+ response.set(new Uint8Array(ciphertext), sequenceNumberVector.byteLength + initializationVector.byteLength);
26
+ return response;
27
+ }
28
+
29
+ export async function decryptMessage(message: ArrayBuffer, sharedSecret: SharedSecret) {
30
+ const sequenceNumberVector = message.slice(0, SEQUENCE_NUMBER_BYTES);
31
+ const initializationVector = message.slice(
32
+ SEQUENCE_NUMBER_BYTES,
33
+ SEQUENCE_NUMBER_BYTES + INITIALIZATION_VECTOR_BYTES,
34
+ );
35
+ const ciphertext = message.slice(SEQUENCE_NUMBER_BYTES + INITIALIZATION_VECTOR_BYTES);
36
+ const plaintextBuffer = await crypto.subtle.decrypt(
37
+ getAlgorithmParams(sequenceNumberVector, initializationVector),
38
+ sharedSecret,
39
+ ciphertext,
40
+ );
41
+ const plaintext = getUtf8Decoder().decode(plaintextBuffer);
42
+ return plaintext
43
+ }
44
+
45
+ function getAlgorithmParams(sequenceNumber: ArrayBuffer, initializationVector: ArrayBuffer) {
46
+ return {
47
+ additionalData: sequenceNumber,
48
+ iv: initializationVector,
49
+ name: 'AES-GCM',
50
+ tagLength: 128, // 16 byte tag => 128 bits
51
+ };
52
+ }
53
+
54
+ let _utf8Decoder: TextDecoder | undefined;
55
+ function getUtf8Decoder() {
56
+ if (_utf8Decoder === undefined) {
57
+ _utf8Decoder = new TextDecoder('utf-8');
58
+ }
59
+ return _utf8Decoder;
60
60
  }
@@ -1,38 +1,38 @@
1
- import { decryptMessage, encryptMessage } from './encryptedMessage.js';
2
- import { SolanaMobileWalletAdapterProtocolError } from './errors.js';
3
- import { SharedSecret } from './parseHelloRsp.js';
4
-
5
- interface JSONRPCRequest<TParams> {
6
- id: number;
7
- jsonrpc: '2.0';
8
- method: string;
9
- params: TParams;
10
- }
11
-
12
- type JSONRPCResponse<TMessage> = {
13
- id: number;
14
- jsonrpc: '2.0';
15
- result: TMessage;
16
- };
17
-
18
- export async function encryptJsonRpcMessage<TParams>(
19
- jsonRpcMessage: JSONRPCRequest<TParams>,
20
- sharedSecret: SharedSecret,
21
- ) {
22
- const plaintext = JSON.stringify(jsonRpcMessage);
23
- const sequenceNumber = jsonRpcMessage.id;
24
- return encryptMessage(plaintext, sequenceNumber, sharedSecret);
25
- }
26
-
27
- export async function decryptJsonRpcMessage<TMessage>(message: ArrayBuffer, sharedSecret: SharedSecret) {
28
- const plaintext = await decryptMessage(message, sharedSecret);
29
- const jsonRpcMessage = JSON.parse(plaintext);
30
- if (Object.hasOwnProperty.call(jsonRpcMessage, 'error')) {
31
- throw new SolanaMobileWalletAdapterProtocolError<typeof jsonRpcMessage.error.code>(
32
- jsonRpcMessage.id,
33
- jsonRpcMessage.error.code,
34
- jsonRpcMessage.error.message,
35
- );
36
- }
37
- return jsonRpcMessage as JSONRPCResponse<TMessage>;
38
- }
1
+ import { decryptMessage, encryptMessage } from './encryptedMessage.js';
2
+ import { SolanaMobileWalletAdapterProtocolError } from './errors.js';
3
+ import { SharedSecret } from './parseHelloRsp.js';
4
+
5
+ interface JSONRPCRequest<TParams> {
6
+ id: number;
7
+ jsonrpc: '2.0';
8
+ method: string;
9
+ params: TParams;
10
+ }
11
+
12
+ type JSONRPCResponse<TMessage> = {
13
+ id: number;
14
+ jsonrpc: '2.0';
15
+ result: TMessage;
16
+ };
17
+
18
+ export async function encryptJsonRpcMessage<TParams>(
19
+ jsonRpcMessage: JSONRPCRequest<TParams>,
20
+ sharedSecret: SharedSecret,
21
+ ) {
22
+ const plaintext = JSON.stringify(jsonRpcMessage);
23
+ const sequenceNumber = jsonRpcMessage.id;
24
+ return encryptMessage(plaintext, sequenceNumber, sharedSecret);
25
+ }
26
+
27
+ export async function decryptJsonRpcMessage<TMessage>(message: ArrayBuffer, sharedSecret: SharedSecret) {
28
+ const plaintext = await decryptMessage(message, sharedSecret);
29
+ const jsonRpcMessage = JSON.parse(plaintext);
30
+ if (Object.hasOwnProperty.call(jsonRpcMessage, 'error')) {
31
+ throw new SolanaMobileWalletAdapterProtocolError<typeof jsonRpcMessage.error.code>(
32
+ jsonRpcMessage.id,
33
+ jsonRpcMessage.error.code,
34
+ jsonRpcMessage.error.message,
35
+ );
36
+ }
37
+ return jsonRpcMessage as JSONRPCResponse<TMessage>;
38
+ }
@@ -1,46 +1,46 @@
1
- import { ENCODED_PUBLIC_KEY_LENGTH_BYTES } from "./encryptedMessage";
2
-
3
- /**
4
- * A secret agreed upon by the app and the wallet. Used as
5
- * a symmetric key to encrypt and decrypt messages over an
6
- * unsecured channel.
7
- */
8
- export type SharedSecret = CryptoKey;
9
-
10
- export default async function parseHelloRsp(
11
- payloadBuffer: ArrayBuffer, // The X9.62-encoded wallet endpoint ephemeral ECDH public keypoint.
12
- associationPublicKey: CryptoKey,
13
- ecdhPrivateKey: CryptoKey,
14
- ): Promise<SharedSecret> {
15
- const [associationPublicKeyBuffer, walletPublicKey] = await Promise.all([
16
- crypto.subtle.exportKey('raw', associationPublicKey),
17
- crypto.subtle.importKey(
18
- 'raw',
19
- payloadBuffer.slice(0, ENCODED_PUBLIC_KEY_LENGTH_BYTES),
20
- { name: 'ECDH', namedCurve: 'P-256' },
21
- false /* extractable */,
22
- [] /* keyUsages */,
23
- ),
24
- ]);
25
- const sharedSecret = await crypto.subtle.deriveBits({ name: 'ECDH', public: walletPublicKey }, ecdhPrivateKey, 256);
26
- const ecdhSecretKey = await crypto.subtle.importKey(
27
- 'raw',
28
- sharedSecret,
29
- 'HKDF',
30
- false /* extractable */,
31
- ['deriveKey'] /* keyUsages */,
32
- );
33
- const aesKeyMaterialVal = await crypto.subtle.deriveKey(
34
- {
35
- name: 'HKDF',
36
- hash: 'SHA-256',
37
- salt: new Uint8Array(associationPublicKeyBuffer),
38
- info: new Uint8Array(),
39
- },
40
- ecdhSecretKey,
41
- { name: 'AES-GCM', length: 128 },
42
- false /* extractable */,
43
- ['encrypt', 'decrypt'],
44
- );
45
- return aesKeyMaterialVal as SharedSecret;
46
- }
1
+ import { ENCODED_PUBLIC_KEY_LENGTH_BYTES } from "./encryptedMessage";
2
+
3
+ /**
4
+ * A secret agreed upon by the app and the wallet. Used as
5
+ * a symmetric key to encrypt and decrypt messages over an
6
+ * unsecured channel.
7
+ */
8
+ export type SharedSecret = CryptoKey;
9
+
10
+ export default async function parseHelloRsp(
11
+ payloadBuffer: ArrayBuffer, // The X9.62-encoded wallet endpoint ephemeral ECDH public keypoint.
12
+ associationPublicKey: CryptoKey,
13
+ ecdhPrivateKey: CryptoKey,
14
+ ): Promise<SharedSecret> {
15
+ const [associationPublicKeyBuffer, walletPublicKey] = await Promise.all([
16
+ crypto.subtle.exportKey('raw', associationPublicKey),
17
+ crypto.subtle.importKey(
18
+ 'raw',
19
+ payloadBuffer.slice(0, ENCODED_PUBLIC_KEY_LENGTH_BYTES),
20
+ { name: 'ECDH', namedCurve: 'P-256' },
21
+ false /* extractable */,
22
+ [] /* keyUsages */,
23
+ ),
24
+ ]);
25
+ const sharedSecret = await crypto.subtle.deriveBits({ name: 'ECDH', public: walletPublicKey }, ecdhPrivateKey, 256);
26
+ const ecdhSecretKey = await crypto.subtle.importKey(
27
+ 'raw',
28
+ sharedSecret,
29
+ 'HKDF',
30
+ false /* extractable */,
31
+ ['deriveKey'] /* keyUsages */,
32
+ );
33
+ const aesKeyMaterialVal = await crypto.subtle.deriveKey(
34
+ {
35
+ name: 'HKDF',
36
+ hash: 'SHA-256',
37
+ salt: new Uint8Array(associationPublicKeyBuffer),
38
+ info: new Uint8Array(),
39
+ },
40
+ ecdhSecretKey,
41
+ { name: 'AES-GCM', length: 128 },
42
+ false /* extractable */,
43
+ ['encrypt', 'decrypt'],
44
+ );
45
+ return aesKeyMaterialVal as SharedSecret;
46
+ }
@@ -1,33 +1,33 @@
1
- import { decryptMessage } from "./encryptedMessage";
2
- import { SolanaMobileWalletAdapterError, SolanaMobileWalletAdapterErrorCode } from "./errors";
3
- import { SharedSecret } from "./parseHelloRsp";
4
- import { ProtocolVersion, SessionProperties } from "./types";
5
-
6
- export default async function parseSessionProps(
7
- message: ArrayBuffer,
8
- sharedSecret: SharedSecret
9
- ): Promise<SessionProperties> {
10
- const plaintext = await decryptMessage(message, sharedSecret);
11
- const jsonProperties = JSON.parse(plaintext);
12
- let protocolVersion: ProtocolVersion = 'legacy';
13
- if (Object.hasOwnProperty.call(jsonProperties, 'v')) {
14
- switch (jsonProperties.v) {
15
- case 1:
16
- case '1':
17
- case 'v1':
18
- protocolVersion = 'v1'
19
- break;
20
- case 'legacy':
21
- protocolVersion = 'legacy'
22
- break;
23
- default:
24
- throw new SolanaMobileWalletAdapterError(
25
- SolanaMobileWalletAdapterErrorCode.ERROR_INVALID_PROTOCOL_VERSION,
26
- `Unknown/unsupported protocol version: ${jsonProperties.v}`
27
- );
28
- }
29
- }
30
- return <SessionProperties>({
31
- protocol_version: protocolVersion
32
- })
1
+ import { decryptMessage } from "./encryptedMessage";
2
+ import { SolanaMobileWalletAdapterError, SolanaMobileWalletAdapterErrorCode } from "./errors";
3
+ import { SharedSecret } from "./parseHelloRsp";
4
+ import { ProtocolVersion, SessionProperties } from "./types";
5
+
6
+ export default async function parseSessionProps(
7
+ message: ArrayBuffer,
8
+ sharedSecret: SharedSecret
9
+ ): Promise<SessionProperties> {
10
+ const plaintext = await decryptMessage(message, sharedSecret);
11
+ const jsonProperties = JSON.parse(plaintext);
12
+ let protocolVersion: ProtocolVersion = 'legacy';
13
+ if (Object.hasOwnProperty.call(jsonProperties, 'v')) {
14
+ switch (jsonProperties.v) {
15
+ case 1:
16
+ case '1':
17
+ case 'v1':
18
+ protocolVersion = 'v1'
19
+ break;
20
+ case 'legacy':
21
+ protocolVersion = 'legacy'
22
+ break;
23
+ default:
24
+ throw new SolanaMobileWalletAdapterError(
25
+ SolanaMobileWalletAdapterErrorCode.ERROR_INVALID_PROTOCOL_VERSION,
26
+ `Unknown/unsupported protocol version: ${jsonProperties.v}`
27
+ );
28
+ }
29
+ }
30
+ return <SessionProperties>({
31
+ protocol_version: protocolVersion
32
+ })
33
33
  }
@@ -1,31 +1,31 @@
1
- import { SolanaMobileWalletAdapterError, SolanaMobileWalletAdapterErrorCode } from './errors.js';
2
-
3
- declare const tag: unique symbol;
4
- export type ReflectorId = number & { readonly [tag]: 'ReflectorId' };
5
-
6
- export function getRandomReflectorId(): ReflectorId {
7
- return assertReflectorId(getRandomInt(0, 9007199254740991)); // 0 < id < 2^53 - 1
8
- }
9
-
10
- function getRandomInt(min: number, max: number) {
11
- const randomBuffer = new Uint32Array(1);
12
-
13
- window.crypto.getRandomValues(randomBuffer);
14
-
15
- let randomNumber = randomBuffer[0] / (0xffffffff + 1);
16
-
17
- min = Math.ceil(min);
18
- max = Math.floor(max);
19
- return Math.floor(randomNumber * (max - min + 1)) + min;
20
- }
21
-
22
- export function assertReflectorId(id: number): ReflectorId {
23
- if (id < 0 || id > 9007199254740991) { // 0 < id < 2^53 - 1
24
- throw new SolanaMobileWalletAdapterError(
25
- SolanaMobileWalletAdapterErrorCode.ERROR_REFLECTOR_ID_OUT_OF_RANGE,
26
- `Association port number must be between 49152 and 65535. ${id} given.`,
27
- { id },
28
- );
29
- }
30
- return id as ReflectorId;
1
+ import { SolanaMobileWalletAdapterError, SolanaMobileWalletAdapterErrorCode } from './errors.js';
2
+
3
+ declare const tag: unique symbol;
4
+ export type ReflectorId = number & { readonly [tag]: 'ReflectorId' };
5
+
6
+ export function getRandomReflectorId(): ReflectorId {
7
+ return assertReflectorId(getRandomInt(0, 9007199254740991)); // 0 < id < 2^53 - 1
8
+ }
9
+
10
+ function getRandomInt(min: number, max: number) {
11
+ const randomBuffer = new Uint32Array(1);
12
+
13
+ window.crypto.getRandomValues(randomBuffer);
14
+
15
+ let randomNumber = randomBuffer[0] / (0xffffffff + 1);
16
+
17
+ min = Math.ceil(min);
18
+ max = Math.floor(max);
19
+ return Math.floor(randomNumber * (max - min + 1)) + min;
20
+ }
21
+
22
+ export function assertReflectorId(id: number): ReflectorId {
23
+ if (id < 0 || id > 9007199254740991) { // 0 < id < 2^53 - 1
24
+ throw new SolanaMobileWalletAdapterError(
25
+ SolanaMobileWalletAdapterErrorCode.ERROR_REFLECTOR_ID_OUT_OF_RANGE,
26
+ `Association port number must be between 49152 and 65535. ${id} given.`,
27
+ { id },
28
+ );
29
+ }
30
+ return id as ReflectorId;
31
31
  }