@solana-mobile/mobile-wallet-adapter-protocol 2.2.1 → 2.2.2-hotfix.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.
@@ -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.11.1'
12
12
  // noinspection DifferentKotlinGradleVersion
13
13
  classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
14
14
  }
@@ -144,7 +144,7 @@ def kotlin_version = getExtOrDefault('kotlinVersion')
144
144
  dependencies {
145
145
  //noinspection GradleDynamicVersion
146
146
  implementation "com.facebook.react:react-native:+" // From node_modules
147
- implementation "com.solanamobile:mobile-wallet-adapter-clientlib:2.0.7"
147
+ implementation "com.solanamobile:mobile-wallet-adapter-clientlib:2.0.8"
148
148
  implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
149
149
  implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.9.0"
150
150
  }
@@ -6,6 +6,8 @@ import android.content.Intent
6
6
  import android.net.Uri
7
7
  import android.util.Log
8
8
  import com.facebook.react.bridge.*
9
+ import com.facebook.react.jstasks.HeadlessJsTaskConfig
10
+ import com.facebook.react.jstasks.HeadlessJsTaskContext
9
11
  import com.solana.mobilewalletadapter.clientlib.protocol.JsonRpc20Client
10
12
  import com.solana.mobilewalletadapter.clientlib.protocol.MobileWalletAdapterClient
11
13
  import com.solana.mobilewalletadapter.clientlib.scenario.LocalAssociationIntentCreator
@@ -21,40 +23,49 @@ import kotlinx.coroutines.sync.Mutex
21
23
  import org.json.JSONObject
22
24
 
23
25
  class SolanaMobileWalletAdapterModule(reactContext: ReactApplicationContext) :
24
- SolanaMobileWalletAdapterSpec(reactContext), CoroutineScope {
26
+ SolanaMobileWalletAdapterSpec(reactContext), CoroutineScope {
25
27
 
26
28
  data class SessionState(
27
- val client: MobileWalletAdapterClient,
28
- val localAssociation: LocalAssociationScenario,
29
+ val client: MobileWalletAdapterClient,
30
+ val localAssociation: LocalAssociationScenario,
29
31
  )
30
32
 
31
33
  override val coroutineContext =
32
- Dispatchers.IO + CoroutineName("SolanaMobileWalletAdapterModuleScope") + SupervisorJob()
34
+ Dispatchers.IO + CoroutineName("SolanaMobileWalletAdapterModuleScope") + SupervisorJob()
33
35
 
34
36
  companion object {
35
37
  const val NAME = "SolanaMobileWalletAdapter"
38
+ private const val TAG = "SolanaMobileWalletAdapterModule"
36
39
  private const val ASSOCIATION_TIMEOUT_MS = 10000
37
40
  private const val CLIENT_TIMEOUT_MS = 90000
38
41
  private const val REQUEST_LOCAL_ASSOCIATION = 0
39
-
40
- // Used to ensure that you can't start more than one session at a time.
41
- private val mutex: Mutex = Mutex()
42
- private var sessionState: SessionState? = null
43
- private var associationResultCallback: ((Int) -> Unit)? = null
44
42
  }
45
43
 
44
+ // Used to ensure that you can't start more than one session at a time.
45
+ private val mutex: Mutex = Mutex()
46
+ private var sessionState: SessionState? = null
47
+ private var associationResultCallback: ((Int) -> Unit)? = null
48
+
46
49
  private val mActivityEventListener: ActivityEventListener =
47
- object : BaseActivityEventListener() {
48
- override fun onActivityResult(
49
- activity: Activity?,
50
- requestCode: Int,
51
- resultCode: Int,
52
- data: Intent?
53
- ) {
54
- if (requestCode == REQUEST_LOCAL_ASSOCIATION)
55
- associationResultCallback?.invoke(resultCode)
56
- }
50
+ object : BaseActivityEventListener() {
51
+ override fun onActivityResult(
52
+ activity: Activity?,
53
+ requestCode: Int,
54
+ resultCode: Int,
55
+ data: Intent?
56
+ ) {
57
+ if (requestCode == REQUEST_LOCAL_ASSOCIATION)
58
+ associationResultCallback?.invoke(resultCode)
57
59
  }
60
+ }
61
+
62
+ private val sessionBackgroundTaskConfig
63
+ get() = HeadlessJsTaskConfig(
64
+ taskKey = "SolanaMobileWalletAdapterSessionBackgroundTask",
65
+ timeout = 0,
66
+ data = Arguments.createMap(),
67
+ isAllowedInForeground = true
68
+ )
58
69
 
59
70
  init {
60
71
  reactContext.addActivityEventListener(mActivityEventListener)
@@ -68,67 +79,77 @@ class SolanaMobileWalletAdapterModule(reactContext: ReactApplicationContext) :
68
79
  override fun startSession(config: ReadableMap?, promise: Promise): Unit {
69
80
  launch {
70
81
  mutex.lock()
71
- Log.d(name, "startSession with config $config")
82
+ Log.d(TAG, "startSession with config $config")
72
83
  try {
73
84
  val uriPrefix = config?.getString("baseUri")?.let { Uri.parse(it) }
74
85
  val localAssociation =
75
- LocalAssociationScenario(
76
- CLIENT_TIMEOUT_MS,
77
- )
86
+ LocalAssociationScenario(
87
+ CLIENT_TIMEOUT_MS,
88
+ )
78
89
  val intent =
79
- LocalAssociationIntentCreator.createAssociationIntent(
80
- uriPrefix,
81
- localAssociation.port,
82
- localAssociation.session
83
- )
90
+ LocalAssociationIntentCreator.createAssociationIntent(
91
+ uriPrefix,
92
+ localAssociation.port,
93
+ localAssociation.session
94
+ )
95
+ val headlessJsTaskContext =
96
+ HeadlessJsTaskContext.getInstance(reactApplicationContext)
97
+ var sessionTaskId = headlessJsTaskContext.startTask(sessionBackgroundTaskConfig)
84
98
  associationResultCallback = { resultCode ->
85
99
  if (resultCode == Activity.RESULT_CANCELED) {
86
- Log.d(name, "Local association cancelled by user, ending session")
100
+ Log.d(TAG, "Local association cancelled by user, ending session")
87
101
  promise.reject(
88
- "Session not established: Local association cancelled by user",
89
- LocalAssociationScenario.ConnectionFailedException(
90
- "Local association cancelled by user"
91
- )
102
+ "Session not established: Local association cancelled by user",
103
+ LocalAssociationScenario.ConnectionFailedException(
104
+ "Local association cancelled by user"
105
+ )
92
106
  )
93
107
  localAssociation.close()
94
108
  }
109
+
110
+ // stop the headless js task, regardless if the association was successful or not
111
+ try {
112
+ if (headlessJsTaskContext.isTaskRunning(sessionTaskId)) {
113
+ headlessJsTaskContext.finishTask(sessionTaskId)
114
+ }
115
+ } catch (e: Exception) {
116
+ Log.w(TAG, "Failed to finish headless task during cleanup", e)
117
+ }
95
118
  }
96
119
  currentActivity?.startActivityForResult(intent, REQUEST_LOCAL_ASSOCIATION)
97
- ?: throw NullPointerException(
98
- "Could not find a current activity from which to launch a local association"
99
- )
100
- val client =
101
- localAssociation
102
- .start()
103
- .get(ASSOCIATION_TIMEOUT_MS.toLong(), TimeUnit.MILLISECONDS)
120
+ ?: throw NullPointerException(
121
+ "Could not find a current activity from which to launch a local association"
122
+ )
123
+ val client = localAssociation.start()
124
+ .get(ASSOCIATION_TIMEOUT_MS.toLong(), TimeUnit.MILLISECONDS)
104
125
  sessionState = SessionState(client, localAssociation)
105
126
  val sessionPropertiesMap: WritableMap = WritableNativeMap()
106
127
  sessionPropertiesMap.putString(
107
- "protocol_version",
108
- when (localAssociation.session.sessionProperties.protocolVersion) {
109
- ProtocolVersion.LEGACY -> "legacy"
110
- ProtocolVersion.V1 -> "v1"
111
- }
128
+ "protocol_version",
129
+ when (localAssociation.session.sessionProperties.protocolVersion) {
130
+ ProtocolVersion.LEGACY -> "legacy"
131
+ ProtocolVersion.V1 -> "v1"
132
+ }
112
133
  )
113
134
  promise.resolve(sessionPropertiesMap)
114
135
  } catch (e: ActivityNotFoundException) {
115
- Log.e(name, "Found no installed wallet that supports the mobile wallet protocol", e)
136
+ Log.e(TAG, "Found no installed wallet that supports the mobile wallet protocol", e)
116
137
  cleanup()
117
138
  promise.reject("ERROR_WALLET_NOT_FOUND", e)
118
139
  } catch (e: TimeoutException) {
119
- Log.e(name, "Timed out waiting for local association to be ready", e)
140
+ Log.e(TAG, "Timed out waiting for local association to be ready", e)
120
141
  cleanup()
121
142
  promise.reject("Timed out waiting for local association to be ready", e)
122
143
  } catch (e: InterruptedException) {
123
- Log.w(name, "Interrupted while waiting for local association to be ready", e)
144
+ Log.w(TAG, "Interrupted while waiting for local association to be ready", e)
124
145
  cleanup()
125
146
  promise.reject(e)
126
147
  } catch (e: ExecutionException) {
127
- Log.e(name, "Failed establishing local association with wallet", e.cause)
148
+ Log.e(TAG, "Failed establishing local association with wallet", e.cause)
128
149
  cleanup()
129
150
  promise.reject(e)
130
151
  } catch (e: Throwable) {
131
- Log.e(name, "Failed to start session", e)
152
+ Log.e(TAG, "Failed to start session", e)
132
153
  cleanup()
133
154
  promise.reject(e)
134
155
  }
@@ -137,58 +158,54 @@ class SolanaMobileWalletAdapterModule(reactContext: ReactApplicationContext) :
137
158
 
138
159
  @ReactMethod
139
160
  override fun invoke(method: String, params: ReadableMap?, promise: Promise): Unit =
140
- sessionState?.let {
141
- Log.d(name, "invoke `$method` with params $params")
142
- try {
143
- val result =
144
- it.client
145
- .methodCall(method, convertMapToJson(params), CLIENT_TIMEOUT_MS)
146
- .get() as
147
- JSONObject
148
- promise.resolve(convertJsonToMap(result))
149
- } catch (e: ExecutionException) {
150
- val cause = e.cause
151
- if (cause is JsonRpc20Client.JsonRpc20RemoteException) {
152
- val userInfo = Arguments.createMap()
153
- userInfo.putInt("jsonRpcErrorCode", cause.code)
154
- promise.reject("JSON_RPC_ERROR", cause, userInfo)
155
- } else if (cause is TimeoutException) {
156
- promise.reject("Timed out waiting for response", e)
157
- } else {
158
- throw e
159
- }
160
- } catch (e: Throwable) {
161
- Log.e(name, "Failed to invoke `$method` with params $params", e)
162
- promise.reject(e)
161
+ sessionState?.let {
162
+ Log.d(TAG, "invoke `$method` with params $params")
163
+ try {
164
+ val result = it.client
165
+ .methodCall(method, convertMapToJson(params), CLIENT_TIMEOUT_MS)
166
+ .get() as JSONObject
167
+ promise.resolve(convertJsonToMap(result))
168
+ } catch (e: ExecutionException) {
169
+ val cause = e.cause
170
+ if (cause is JsonRpc20Client.JsonRpc20RemoteException) {
171
+ val userInfo = Arguments.createMap()
172
+ userInfo.putInt("jsonRpcErrorCode", cause.code)
173
+ promise.reject("JSON_RPC_ERROR", cause, userInfo)
174
+ } else if (cause is TimeoutException) {
175
+ promise.reject("Timed out waiting for response", e)
176
+ } else {
177
+ throw e
163
178
  }
179
+ } catch (e: Throwable) {
180
+ Log.e(TAG, "Failed to invoke `$method` with params $params", e)
181
+ promise.reject(e)
164
182
  }
165
- ?: throw NullPointerException(
166
- "Tried to invoke `$method` without an active session"
167
- )
183
+ } ?: throw NullPointerException(
184
+ "Tried to invoke `$method` without an active session"
185
+ )
168
186
 
169
187
  @ReactMethod
170
188
  override fun endSession(promise: Promise): Unit {
171
189
  sessionState?.let {
172
190
  launch {
173
- Log.d(name, "endSession")
191
+ Log.d(TAG, "endSession")
174
192
  try {
175
193
  it.localAssociation
176
- .close()
177
- .get(ASSOCIATION_TIMEOUT_MS.toLong(), TimeUnit.MILLISECONDS)
194
+ .close()
195
+ .get(ASSOCIATION_TIMEOUT_MS.toLong(), TimeUnit.MILLISECONDS)
178
196
  cleanup()
179
197
  promise.resolve(true)
180
198
  } catch (e: TimeoutException) {
181
- Log.e(name, "Timed out waiting for local association to close", e)
199
+ Log.e(TAG, "Timed out waiting for local association to close", e)
182
200
  cleanup()
183
201
  promise.reject("Failed to end session", e)
184
202
  } catch (e: Throwable) {
185
- Log.e(name, "Failed to end session", e)
203
+ Log.e(TAG, "Failed to end session", e)
186
204
  cleanup()
187
205
  promise.reject("Failed to end session", e)
188
206
  }
189
207
  }
190
- }
191
- ?: throw NullPointerException("Tried to end a session without an active session")
208
+ } ?: throw NullPointerException("Tried to end a session without an active session")
192
209
  }
193
210
 
194
211
  private fun cleanup() {
@@ -247,6 +247,14 @@ function signInFallback(signInPayload, authorizationResult, protocolRequestHandl
247
247
  });
248
248
  }
249
249
 
250
+ reactNative.AppRegistry.registerHeadlessTask('SolanaMobileWalletAdapterSessionBackgroundTask', () => {
251
+ return () => __awaiter(void 0, void 0, void 0, function* () {
252
+ // This is a no-op task that is used to keep the app alive while the session is active.
253
+ // The actual session management is handled in the native module.
254
+ // This is necessary for the React Native Android implementation to work correctly.
255
+ // The task is started before startActivityResult and stopped when the activity result callback is triggered
256
+ });
257
+ });
250
258
  const LINKING_ERROR = `The package 'solana-mobile-wallet-adapter-protocol' doesn't seem to be linked. Make sure: \n\n` +
251
259
  '- You rebuilt the app after installing the package\n' +
252
260
  '- If you are using Lerna workspaces\n' +
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.1",
4
+ "version": "2.2.2-hotfix.0",
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
  },
@@ -67,4 +79,4 @@
67
79
  "javaPackageName": "com.solanamobile.mobilewalletadapter.reactnative"
68
80
  }
69
81
  }
70
- }
82
+ }
package/.gitignore DELETED
@@ -1,2 +0,0 @@
1
- lib/
2
- android/build
@@ -1,14 +0,0 @@
1
- # OSX
2
- #
3
- .DS_Store
4
-
5
- # Android/IJ
6
- #
7
- .classpath
8
- .cxx
9
- .gradle
10
- .idea
11
- .project
12
- .settings
13
- local.properties
14
- android.iml
@@ -1 +0,0 @@
1
- export { encode, fromUint8Array, toUint8Array } from 'js-base64';
@@ -1,91 +0,0 @@
1
- import { Platform } from 'react-native';
2
-
3
- import NativeSolanaMobileWalletAdapter from '../../codegenSpec/NativeSolanaMobileWalletAdapter.js';
4
- import createMobileWalletProxy from '../../createMobileWalletProxy.js';
5
- import { SolanaMobileWalletAdapterError, SolanaMobileWalletAdapterProtocolError } from '../../errors.js';
6
- import { MobileWallet, SessionProperties, WalletAssociationConfig } from '../../types.js';
7
-
8
- type ReactNativeError = Error & { code?: string; userInfo?: Record<string, unknown> };
9
-
10
- const LINKING_ERROR =
11
- `The package 'solana-mobile-wallet-adapter-protocol' doesn't seem to be linked. Make sure: \n\n` +
12
- '- You rebuilt the app after installing the package\n' +
13
- '- If you are using Lerna workspaces\n' +
14
- ' - You have added `@solana-mobile/mobile-wallet-adapter-protocol` as an explicit dependency, and\n' +
15
- ' - You have added `@solana-mobile/mobile-wallet-adapter-protocol` to the `nohoist` section of your package.json\n' +
16
- '- You are not using Expo managed workflow\n';
17
-
18
- const SolanaMobileWalletAdapter =
19
- Platform.OS === 'android' && NativeSolanaMobileWalletAdapter
20
- ? NativeSolanaMobileWalletAdapter
21
- : (new Proxy(
22
- {},
23
- {
24
- get() {
25
- throw new Error(
26
- Platform.OS !== 'android'
27
- ? 'The package `solana-mobile-wallet-adapter-protocol` is only compatible with React Native Android'
28
- : LINKING_ERROR,
29
- );
30
- },
31
- },
32
- ) as typeof NativeSolanaMobileWalletAdapter);
33
-
34
- function getErrorMessage(e: ReactNativeError): string {
35
- switch (e.code) {
36
- case 'ERROR_WALLET_NOT_FOUND':
37
- return 'Found no installed wallet that supports the mobile wallet protocol.';
38
- default:
39
- return e.message;
40
- }
41
- }
42
-
43
- function handleError(e: any): never {
44
- if (e instanceof Error) {
45
- const reactNativeError: ReactNativeError = e;
46
- switch (reactNativeError.code) {
47
- case undefined:
48
- throw e;
49
- case 'JSON_RPC_ERROR': {
50
- const details = reactNativeError.userInfo as Readonly<{ jsonRpcErrorCode: number }>;
51
- throw new SolanaMobileWalletAdapterProtocolError(
52
- 0 /* jsonRpcMessageId */,
53
- details.jsonRpcErrorCode,
54
- e.message,
55
- );
56
- }
57
- default:
58
- throw new SolanaMobileWalletAdapterError<any>(
59
- reactNativeError.code,
60
- getErrorMessage(reactNativeError),
61
- reactNativeError.userInfo,
62
- );
63
- }
64
- }
65
- throw e;
66
- }
67
-
68
- export async function transact<TReturn>(
69
- callback: (wallet: MobileWallet) => TReturn,
70
- config?: WalletAssociationConfig,
71
- ): Promise<TReturn> {
72
- let didSuccessfullyConnect = false;
73
- try {
74
- const sessionProperties: SessionProperties = await SolanaMobileWalletAdapter.startSession(config);
75
- didSuccessfullyConnect = true;
76
- const wallet = createMobileWalletProxy(sessionProperties.protocol_version, async (method, params) => {
77
- try {
78
- return SolanaMobileWalletAdapter.invoke(method, params);
79
- } catch (e) {
80
- return handleError(e);
81
- }
82
- });
83
- return await callback(wallet);
84
- } catch (e) {
85
- return handleError(e);
86
- } finally {
87
- if (didSuccessfullyConnect) {
88
- await SolanaMobileWalletAdapter.endSession();
89
- }
90
- }
91
- }
@@ -1,10 +0,0 @@
1
- // https://stackoverflow.com/a/9458996/802047
2
- export default function arrayBufferToBase64String(buffer: ArrayBuffer) {
3
- let binary = '';
4
- const bytes = new Uint8Array(buffer);
5
- const len = bytes.byteLength;
6
- for (let ii = 0; ii < len; ii++) {
7
- binary += String.fromCharCode(bytes[ii]);
8
- }
9
- return window.btoa(binary);
10
- }
@@ -1,19 +0,0 @@
1
- import { SolanaMobileWalletAdapterError, SolanaMobileWalletAdapterErrorCode } from './errors.js';
2
-
3
- declare const tag: unique symbol;
4
- export type AssociationPort = number & { readonly [tag]: 'AssociationPort' };
5
-
6
- export function getRandomAssociationPort(): AssociationPort {
7
- return assertAssociationPort(49152 + Math.floor(Math.random() * (65535 - 49152 + 1)));
8
- }
9
-
10
- export function assertAssociationPort(port: number): AssociationPort {
11
- if (port < 49152 || port > 65535) {
12
- throw new SolanaMobileWalletAdapterError(
13
- SolanaMobileWalletAdapterErrorCode.ERROR_ASSOCIATION_PORT_OUT_OF_RANGE,
14
- `Association port number must be between 49152 and 65535. ${port} given.`,
15
- { port },
16
- );
17
- }
18
- return port as AssociationPort;
19
- }
@@ -1,22 +0,0 @@
1
- export function encode(input: string): string {
2
- return window.btoa(input);
3
- }
4
-
5
- export function fromUint8Array(byteArray: Uint8Array, urlsafe?: boolean): string {
6
- const base64 = window.btoa(String.fromCharCode.call(null, ...byteArray));
7
- if (urlsafe) {
8
- return base64
9
- .replace(/\+/g, '-')
10
- .replace(/\//g, '_')
11
- .replace(/=+$/, '');
12
- } else return base64;
13
- }
14
-
15
- export function toUint8Array(base64EncodedByteArray: string): Uint8Array {
16
- return new Uint8Array(
17
- window
18
- .atob(base64EncodedByteArray)
19
- .split('')
20
- .map((c) => c.charCodeAt(0)),
21
- );
22
- }
@@ -1,12 +0,0 @@
1
- export default async function createHelloReq(ecdhPublicKey: CryptoKey, associationKeypairPrivateKey: CryptoKey) {
2
- const publicKeyBuffer = await crypto.subtle.exportKey('raw', ecdhPublicKey);
3
- const signatureBuffer = await crypto.subtle.sign(
4
- { hash: 'SHA-256', name: 'ECDSA' },
5
- associationKeypairPrivateKey,
6
- publicKeyBuffer,
7
- );
8
- const response = new Uint8Array(publicKeyBuffer.byteLength + signatureBuffer.byteLength);
9
- response.set(new Uint8Array(publicKeyBuffer), 0);
10
- response.set(new Uint8Array(signatureBuffer), publicKeyBuffer.byteLength);
11
- return response;
12
- }