@solana-mobile/mobile-wallet-adapter-protocol 2.2.2 → 2.2.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/android/build.gradle +2 -2
- package/android/src/main/java/com/solanamobile/mobilewalletadapter/reactnative/SolanaMobileWalletAdapterModule.kt +110 -83
- package/lib/cjs/index.native.js +8 -0
- package/package.json +1 -1
- package/.gitignore +0 -2
- package/android/.gitignore +0 -14
- package/src/__forks__/react-native/base64Utils.ts +0 -1
- package/src/__forks__/react-native/transact.ts +0 -91
- package/src/arrayBufferToBase64String.ts +0 -10
- package/src/associationPort.ts +0 -19
- package/src/base64Utils.ts +0 -22
- package/src/createHelloReq.ts +0 -12
- package/src/createMobileWalletProxy.ts +0 -182
- package/src/createSIWSMessage.ts +0 -14
- package/src/createSequenceNumberVector.ts +0 -11
- package/src/encryptedMessage.ts +0 -60
- package/src/errors.ts +0 -101
- package/src/generateAssociationKeypair.ts +0 -10
- package/src/generateECDHKeypair.ts +0 -10
- package/src/getAssociateAndroidIntentURL.ts +0 -77
- package/src/getJWS.ts +0 -19
- package/src/getStringWithURLUnsafeBase64CharactersReplaced.ts +0 -11
- package/src/index.ts +0 -3
- package/src/jsonRpcMessage.ts +0 -38
- package/src/parseHelloRsp.ts +0 -46
- package/src/parseSessionProps.ts +0 -33
- package/src/reflectorId.ts +0 -31
- package/src/startSession.ts +0 -98
- package/src/transact.ts +0 -593
- package/src/types.ts +0 -201
- package/tsconfig.cjs.json +0 -7
- package/tsconfig.json +0 -8
package/android/build.gradle
CHANGED
|
@@ -8,7 +8,7 @@ buildscript {
|
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
dependencies {
|
|
11
|
-
classpath 'com.android.tools.build:gradle:8.
|
|
11
|
+
classpath 'com.android.tools.build:gradle:8.12.0'
|
|
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.
|
|
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
|
-
|
|
26
|
+
SolanaMobileWalletAdapterSpec(reactContext), CoroutineScope {
|
|
25
27
|
|
|
26
28
|
data class SessionState(
|
|
27
|
-
|
|
28
|
-
|
|
29
|
+
val client: MobileWalletAdapterClient,
|
|
30
|
+
val localAssociation: LocalAssociationScenario,
|
|
29
31
|
)
|
|
30
32
|
|
|
31
33
|
override val coroutineContext =
|
|
32
|
-
|
|
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
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
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,87 @@ class SolanaMobileWalletAdapterModule(reactContext: ReactApplicationContext) :
|
|
|
68
79
|
override fun startSession(config: ReadableMap?, promise: Promise): Unit {
|
|
69
80
|
launch {
|
|
70
81
|
mutex.lock()
|
|
71
|
-
Log.d(
|
|
82
|
+
Log.d(TAG, "startSession with config $config")
|
|
83
|
+
var sessionTaskId: Int? = null
|
|
84
|
+
val headlessJsTaskContext = HeadlessJsTaskContext.getInstance(reactApplicationContext)
|
|
85
|
+
val finishHeadlessTask = { taskId: Int? ->
|
|
86
|
+
try {
|
|
87
|
+
if (taskId != null && headlessJsTaskContext.isTaskRunning(taskId)) {
|
|
88
|
+
headlessJsTaskContext.finishTask(taskId)
|
|
89
|
+
}
|
|
90
|
+
} catch (e: Exception) {
|
|
91
|
+
Log.w(TAG, "Failed to finish headless JS task", e)
|
|
92
|
+
}
|
|
93
|
+
}
|
|
72
94
|
try {
|
|
73
95
|
val uriPrefix = config?.getString("baseUri")?.let { Uri.parse(it) }
|
|
74
96
|
val localAssociation =
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
97
|
+
LocalAssociationScenario(
|
|
98
|
+
CLIENT_TIMEOUT_MS,
|
|
99
|
+
)
|
|
78
100
|
val intent =
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
101
|
+
LocalAssociationIntentCreator.createAssociationIntent(
|
|
102
|
+
uriPrefix,
|
|
103
|
+
localAssociation.port,
|
|
104
|
+
localAssociation.session
|
|
105
|
+
)
|
|
106
|
+
withContext(Dispatchers.Main) {
|
|
107
|
+
sessionTaskId = headlessJsTaskContext.startTask(sessionBackgroundTaskConfig)
|
|
108
|
+
}
|
|
84
109
|
associationResultCallback = { resultCode ->
|
|
85
110
|
if (resultCode == Activity.RESULT_CANCELED) {
|
|
86
|
-
Log.d(
|
|
111
|
+
Log.d(TAG, "Local association cancelled by user, ending session")
|
|
87
112
|
promise.reject(
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
113
|
+
"Session not established: Local association cancelled by user",
|
|
114
|
+
LocalAssociationScenario.ConnectionFailedException(
|
|
115
|
+
"Local association cancelled by user"
|
|
116
|
+
)
|
|
92
117
|
)
|
|
93
118
|
localAssociation.close()
|
|
94
119
|
}
|
|
120
|
+
|
|
121
|
+
// stop the headless js task, regardless if the association was successful or not
|
|
122
|
+
finishHeadlessTask(sessionTaskId)
|
|
95
123
|
}
|
|
96
124
|
currentActivity?.startActivityForResult(intent, REQUEST_LOCAL_ASSOCIATION)
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
val client =
|
|
101
|
-
|
|
102
|
-
.start()
|
|
103
|
-
.get(ASSOCIATION_TIMEOUT_MS.toLong(), TimeUnit.MILLISECONDS)
|
|
125
|
+
?: throw NullPointerException(
|
|
126
|
+
"Could not find a current activity from which to launch a local association"
|
|
127
|
+
)
|
|
128
|
+
val client = localAssociation.start()
|
|
129
|
+
.get(ASSOCIATION_TIMEOUT_MS.toLong(), TimeUnit.MILLISECONDS)
|
|
104
130
|
sessionState = SessionState(client, localAssociation)
|
|
105
131
|
val sessionPropertiesMap: WritableMap = WritableNativeMap()
|
|
106
132
|
sessionPropertiesMap.putString(
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
133
|
+
"protocol_version",
|
|
134
|
+
when (localAssociation.session.sessionProperties.protocolVersion) {
|
|
135
|
+
ProtocolVersion.LEGACY -> "legacy"
|
|
136
|
+
ProtocolVersion.V1 -> "v1"
|
|
137
|
+
}
|
|
112
138
|
)
|
|
113
139
|
promise.resolve(sessionPropertiesMap)
|
|
114
140
|
} catch (e: ActivityNotFoundException) {
|
|
115
|
-
Log.e(
|
|
141
|
+
Log.e(TAG, "Found no installed wallet that supports the mobile wallet protocol", e)
|
|
142
|
+
finishHeadlessTask(sessionTaskId)
|
|
116
143
|
cleanup()
|
|
117
144
|
promise.reject("ERROR_WALLET_NOT_FOUND", e)
|
|
118
145
|
} catch (e: TimeoutException) {
|
|
119
|
-
Log.e(
|
|
146
|
+
Log.e(TAG, "Timed out waiting for local association to be ready", e)
|
|
147
|
+
finishHeadlessTask(sessionTaskId)
|
|
120
148
|
cleanup()
|
|
121
149
|
promise.reject("Timed out waiting for local association to be ready", e)
|
|
122
150
|
} catch (e: InterruptedException) {
|
|
123
|
-
Log.w(
|
|
151
|
+
Log.w(TAG, "Interrupted while waiting for local association to be ready", e)
|
|
152
|
+
finishHeadlessTask(sessionTaskId)
|
|
124
153
|
cleanup()
|
|
125
154
|
promise.reject(e)
|
|
126
155
|
} catch (e: ExecutionException) {
|
|
127
|
-
Log.e(
|
|
156
|
+
Log.e(TAG, "Failed establishing local association with wallet", e.cause)
|
|
157
|
+
finishHeadlessTask(sessionTaskId)
|
|
128
158
|
cleanup()
|
|
129
159
|
promise.reject(e)
|
|
130
160
|
} catch (e: Throwable) {
|
|
131
|
-
Log.e(
|
|
161
|
+
Log.e(TAG, "Failed to start session", e)
|
|
162
|
+
finishHeadlessTask(sessionTaskId)
|
|
132
163
|
cleanup()
|
|
133
164
|
promise.reject(e)
|
|
134
165
|
}
|
|
@@ -137,58 +168,54 @@ class SolanaMobileWalletAdapterModule(reactContext: ReactApplicationContext) :
|
|
|
137
168
|
|
|
138
169
|
@ReactMethod
|
|
139
170
|
override fun invoke(method: String, params: ReadableMap?, promise: Promise): Unit =
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
val
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
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)
|
|
171
|
+
sessionState?.let {
|
|
172
|
+
Log.d(TAG, "invoke `$method` with params $params")
|
|
173
|
+
try {
|
|
174
|
+
val result = it.client
|
|
175
|
+
.methodCall(method, convertMapToJson(params), CLIENT_TIMEOUT_MS)
|
|
176
|
+
.get() as JSONObject
|
|
177
|
+
promise.resolve(convertJsonToMap(result))
|
|
178
|
+
} catch (e: ExecutionException) {
|
|
179
|
+
val cause = e.cause
|
|
180
|
+
if (cause is JsonRpc20Client.JsonRpc20RemoteException) {
|
|
181
|
+
val userInfo = Arguments.createMap()
|
|
182
|
+
userInfo.putInt("jsonRpcErrorCode", cause.code)
|
|
183
|
+
promise.reject("JSON_RPC_ERROR", cause, userInfo)
|
|
184
|
+
} else if (cause is TimeoutException) {
|
|
185
|
+
promise.reject("Timed out waiting for response", e)
|
|
186
|
+
} else {
|
|
187
|
+
throw e
|
|
163
188
|
}
|
|
189
|
+
} catch (e: Throwable) {
|
|
190
|
+
Log.e(TAG, "Failed to invoke `$method` with params $params", e)
|
|
191
|
+
promise.reject(e)
|
|
164
192
|
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
193
|
+
} ?: throw NullPointerException(
|
|
194
|
+
"Tried to invoke `$method` without an active session"
|
|
195
|
+
)
|
|
168
196
|
|
|
169
197
|
@ReactMethod
|
|
170
198
|
override fun endSession(promise: Promise): Unit {
|
|
171
199
|
sessionState?.let {
|
|
172
200
|
launch {
|
|
173
|
-
Log.d(
|
|
201
|
+
Log.d(TAG, "endSession")
|
|
174
202
|
try {
|
|
175
203
|
it.localAssociation
|
|
176
|
-
|
|
177
|
-
|
|
204
|
+
.close()
|
|
205
|
+
.get(ASSOCIATION_TIMEOUT_MS.toLong(), TimeUnit.MILLISECONDS)
|
|
178
206
|
cleanup()
|
|
179
207
|
promise.resolve(true)
|
|
180
208
|
} catch (e: TimeoutException) {
|
|
181
|
-
Log.e(
|
|
209
|
+
Log.e(TAG, "Timed out waiting for local association to close", e)
|
|
182
210
|
cleanup()
|
|
183
211
|
promise.reject("Failed to end session", e)
|
|
184
212
|
} catch (e: Throwable) {
|
|
185
|
-
Log.e(
|
|
213
|
+
Log.e(TAG, "Failed to end session", e)
|
|
186
214
|
cleanup()
|
|
187
215
|
promise.reject("Failed to end session", e)
|
|
188
216
|
}
|
|
189
217
|
}
|
|
190
|
-
}
|
|
191
|
-
?: throw NullPointerException("Tried to end a session without an active session")
|
|
218
|
+
} ?: throw NullPointerException("Tried to end a session without an active session")
|
|
192
219
|
}
|
|
193
220
|
|
|
194
221
|
private fun cleanup() {
|
package/lib/cjs/index.native.js
CHANGED
|
@@ -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,7 +1,7 @@
|
|
|
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.
|
|
4
|
+
"version": "2.2.3",
|
|
5
5
|
"author": "Steven Luscher <steven.luscher@solanamobile.com>",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
package/.gitignore
DELETED
package/android/.gitignore
DELETED
|
@@ -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
|
-
}
|
package/src/associationPort.ts
DELETED
|
@@ -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
|
-
}
|
package/src/base64Utils.ts
DELETED
|
@@ -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
|
-
}
|
package/src/createHelloReq.ts
DELETED
|
@@ -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
|
-
}
|