@partylayer/adapter-bron 0.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.
- package/LICENSE +21 -0
- package/dist/index.d.mts +89 -0
- package/dist/index.d.ts +89 -0
- package/dist/index.js +479 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +477 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +47 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 CantonConnect
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import * as _partylayer_core from '@partylayer/core';
|
|
2
|
+
import { WalletAdapter, CapabilityKey, AdapterDetectResult, AdapterContext, AdapterConnectResult, SignMessageParams, SignTransactionParams } from '@partylayer/core';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Bron OAuth2 Authentication Client
|
|
6
|
+
*
|
|
7
|
+
* Handles OAuth2 flows for Bron enterprise wallet.
|
|
8
|
+
*
|
|
9
|
+
* References:
|
|
10
|
+
* - Bron developer portal: https://developer.bron.org/
|
|
11
|
+
* - Bron ecosystem: https://www.canton.network/ecosystem/bron-wallet
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* OAuth2 configuration
|
|
16
|
+
*/
|
|
17
|
+
interface BronAuthConfig {
|
|
18
|
+
/** Authorization server URL */
|
|
19
|
+
authorizationUrl: string;
|
|
20
|
+
/** Token endpoint URL */
|
|
21
|
+
tokenUrl: string;
|
|
22
|
+
/** Client ID */
|
|
23
|
+
clientId: string;
|
|
24
|
+
/** Client secret (for server-side flows) */
|
|
25
|
+
clientSecret?: string;
|
|
26
|
+
/** Redirect URI */
|
|
27
|
+
redirectUri: string;
|
|
28
|
+
/** Scopes */
|
|
29
|
+
scopes?: string[];
|
|
30
|
+
/** Use PKCE (recommended for browser) */
|
|
31
|
+
usePKCE?: boolean;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Bron API Client
|
|
36
|
+
*
|
|
37
|
+
* Typed client for Bron enterprise wallet API endpoints.
|
|
38
|
+
*
|
|
39
|
+
* References:
|
|
40
|
+
* - Bron developer portal: https://developer.bron.org/
|
|
41
|
+
*/
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Bron API configuration
|
|
45
|
+
*/
|
|
46
|
+
interface BronApiConfig {
|
|
47
|
+
/** Base API URL */
|
|
48
|
+
baseUrl: string;
|
|
49
|
+
/** Access token getter */
|
|
50
|
+
getAccessToken: () => Promise<string | null>;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Bron adapter configuration
|
|
55
|
+
*/
|
|
56
|
+
interface BronAdapterConfig {
|
|
57
|
+
/** OAuth2 configuration */
|
|
58
|
+
auth: BronAuthConfig;
|
|
59
|
+
/** API configuration */
|
|
60
|
+
api: BronApiConfig;
|
|
61
|
+
/** Use mock API in development */
|
|
62
|
+
useMockApi?: boolean;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Bron Wallet Adapter
|
|
66
|
+
*/
|
|
67
|
+
declare class BronAdapter implements WalletAdapter {
|
|
68
|
+
readonly walletId: _partylayer_core.WalletId;
|
|
69
|
+
readonly name = "Bron";
|
|
70
|
+
private authClient;
|
|
71
|
+
private apiClient;
|
|
72
|
+
constructor(config: BronAdapterConfig);
|
|
73
|
+
/**
|
|
74
|
+
* Create mock API client for development
|
|
75
|
+
*/
|
|
76
|
+
private createMockApiClient;
|
|
77
|
+
getCapabilities(): CapabilityKey[];
|
|
78
|
+
detectInstalled(): Promise<AdapterDetectResult>;
|
|
79
|
+
connect(ctx: AdapterContext, _opts?: {
|
|
80
|
+
timeoutMs?: number;
|
|
81
|
+
requiredCapabilities?: CapabilityKey[];
|
|
82
|
+
}): Promise<AdapterConnectResult>;
|
|
83
|
+
disconnect(_ctx: AdapterContext, _session: _partylayer_core.Session): Promise<void>;
|
|
84
|
+
restore(_ctx: AdapterContext, persisted: _partylayer_core.PersistedSession): Promise<_partylayer_core.Session | null>;
|
|
85
|
+
signMessage(_ctx: AdapterContext, session: _partylayer_core.Session, params: SignMessageParams): Promise<_partylayer_core.SignedMessage>;
|
|
86
|
+
signTransaction(_ctx: AdapterContext, session: _partylayer_core.Session, params: SignTransactionParams): Promise<_partylayer_core.SignedTransaction>;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export { BronAdapter, type BronAdapterConfig, type BronApiConfig, type BronAuthConfig };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import * as _partylayer_core from '@partylayer/core';
|
|
2
|
+
import { WalletAdapter, CapabilityKey, AdapterDetectResult, AdapterContext, AdapterConnectResult, SignMessageParams, SignTransactionParams } from '@partylayer/core';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Bron OAuth2 Authentication Client
|
|
6
|
+
*
|
|
7
|
+
* Handles OAuth2 flows for Bron enterprise wallet.
|
|
8
|
+
*
|
|
9
|
+
* References:
|
|
10
|
+
* - Bron developer portal: https://developer.bron.org/
|
|
11
|
+
* - Bron ecosystem: https://www.canton.network/ecosystem/bron-wallet
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* OAuth2 configuration
|
|
16
|
+
*/
|
|
17
|
+
interface BronAuthConfig {
|
|
18
|
+
/** Authorization server URL */
|
|
19
|
+
authorizationUrl: string;
|
|
20
|
+
/** Token endpoint URL */
|
|
21
|
+
tokenUrl: string;
|
|
22
|
+
/** Client ID */
|
|
23
|
+
clientId: string;
|
|
24
|
+
/** Client secret (for server-side flows) */
|
|
25
|
+
clientSecret?: string;
|
|
26
|
+
/** Redirect URI */
|
|
27
|
+
redirectUri: string;
|
|
28
|
+
/** Scopes */
|
|
29
|
+
scopes?: string[];
|
|
30
|
+
/** Use PKCE (recommended for browser) */
|
|
31
|
+
usePKCE?: boolean;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Bron API Client
|
|
36
|
+
*
|
|
37
|
+
* Typed client for Bron enterprise wallet API endpoints.
|
|
38
|
+
*
|
|
39
|
+
* References:
|
|
40
|
+
* - Bron developer portal: https://developer.bron.org/
|
|
41
|
+
*/
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Bron API configuration
|
|
45
|
+
*/
|
|
46
|
+
interface BronApiConfig {
|
|
47
|
+
/** Base API URL */
|
|
48
|
+
baseUrl: string;
|
|
49
|
+
/** Access token getter */
|
|
50
|
+
getAccessToken: () => Promise<string | null>;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Bron adapter configuration
|
|
55
|
+
*/
|
|
56
|
+
interface BronAdapterConfig {
|
|
57
|
+
/** OAuth2 configuration */
|
|
58
|
+
auth: BronAuthConfig;
|
|
59
|
+
/** API configuration */
|
|
60
|
+
api: BronApiConfig;
|
|
61
|
+
/** Use mock API in development */
|
|
62
|
+
useMockApi?: boolean;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Bron Wallet Adapter
|
|
66
|
+
*/
|
|
67
|
+
declare class BronAdapter implements WalletAdapter {
|
|
68
|
+
readonly walletId: _partylayer_core.WalletId;
|
|
69
|
+
readonly name = "Bron";
|
|
70
|
+
private authClient;
|
|
71
|
+
private apiClient;
|
|
72
|
+
constructor(config: BronAdapterConfig);
|
|
73
|
+
/**
|
|
74
|
+
* Create mock API client for development
|
|
75
|
+
*/
|
|
76
|
+
private createMockApiClient;
|
|
77
|
+
getCapabilities(): CapabilityKey[];
|
|
78
|
+
detectInstalled(): Promise<AdapterDetectResult>;
|
|
79
|
+
connect(ctx: AdapterContext, _opts?: {
|
|
80
|
+
timeoutMs?: number;
|
|
81
|
+
requiredCapabilities?: CapabilityKey[];
|
|
82
|
+
}): Promise<AdapterConnectResult>;
|
|
83
|
+
disconnect(_ctx: AdapterContext, _session: _partylayer_core.Session): Promise<void>;
|
|
84
|
+
restore(_ctx: AdapterContext, persisted: _partylayer_core.PersistedSession): Promise<_partylayer_core.Session | null>;
|
|
85
|
+
signMessage(_ctx: AdapterContext, session: _partylayer_core.Session, params: SignMessageParams): Promise<_partylayer_core.SignedMessage>;
|
|
86
|
+
signTransaction(_ctx: AdapterContext, session: _partylayer_core.Session, params: SignTransactionParams): Promise<_partylayer_core.SignedTransaction>;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export { BronAdapter, type BronAdapterConfig, type BronApiConfig, type BronAuthConfig };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,479 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var core = require('@partylayer/core');
|
|
4
|
+
|
|
5
|
+
// src/bron-adapter.ts
|
|
6
|
+
|
|
7
|
+
// src/auth.ts
|
|
8
|
+
var BronAuthClient = class {
|
|
9
|
+
config;
|
|
10
|
+
storage;
|
|
11
|
+
tokens = null;
|
|
12
|
+
// In-memory by default
|
|
13
|
+
constructor(config, storage) {
|
|
14
|
+
this.config = config;
|
|
15
|
+
this.storage = storage;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Generate PKCE code verifier and challenge
|
|
19
|
+
*/
|
|
20
|
+
async generatePKCE() {
|
|
21
|
+
const verifier = this.generateRandomString(128);
|
|
22
|
+
const encoder = new TextEncoder();
|
|
23
|
+
const data = encoder.encode(verifier);
|
|
24
|
+
const digest = await crypto.subtle.digest("SHA-256", data);
|
|
25
|
+
const challenge = btoa(String.fromCharCode(...new Uint8Array(digest))).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
|
|
26
|
+
return { verifier, challenge };
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Generate random string
|
|
30
|
+
*/
|
|
31
|
+
generateRandomString(length) {
|
|
32
|
+
const array = new Uint8Array(length);
|
|
33
|
+
crypto.getRandomValues(array);
|
|
34
|
+
return Array.from(array, (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Start authorization flow
|
|
38
|
+
*/
|
|
39
|
+
async startAuth() {
|
|
40
|
+
const state = this.generateRandomString(32);
|
|
41
|
+
const params = new URLSearchParams({
|
|
42
|
+
response_type: "code",
|
|
43
|
+
client_id: this.config.clientId,
|
|
44
|
+
redirect_uri: this.config.redirectUri,
|
|
45
|
+
state,
|
|
46
|
+
scope: (this.config.scopes || ["openid", "profile"]).join(" ")
|
|
47
|
+
});
|
|
48
|
+
if (this.config.usePKCE) {
|
|
49
|
+
const pkce = await this.generatePKCE();
|
|
50
|
+
params.set("code_challenge", pkce.challenge);
|
|
51
|
+
params.set("code_challenge_method", "S256");
|
|
52
|
+
if (typeof window !== "undefined") {
|
|
53
|
+
sessionStorage.setItem("bron_pkce_verifier", pkce.verifier);
|
|
54
|
+
sessionStorage.setItem("bron_auth_state", state);
|
|
55
|
+
}
|
|
56
|
+
} else {
|
|
57
|
+
if (typeof window !== "undefined") {
|
|
58
|
+
sessionStorage.setItem("bron_auth_state", state);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
const authUrl = `${this.config.authorizationUrl}?${params.toString()}`;
|
|
62
|
+
return authUrl;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Finish authorization flow with callback URL
|
|
66
|
+
*/
|
|
67
|
+
async finishAuth(callbackUrl) {
|
|
68
|
+
const url = new URL(callbackUrl);
|
|
69
|
+
const code = url.searchParams.get("code");
|
|
70
|
+
const state = url.searchParams.get("state");
|
|
71
|
+
const error = url.searchParams.get("error");
|
|
72
|
+
if (error) {
|
|
73
|
+
throw new Error(`OAuth error: ${error}`);
|
|
74
|
+
}
|
|
75
|
+
if (!code) {
|
|
76
|
+
throw new Error("No authorization code in callback");
|
|
77
|
+
}
|
|
78
|
+
if (typeof window !== "undefined") {
|
|
79
|
+
const storedState = sessionStorage.getItem("bron_auth_state");
|
|
80
|
+
if (state !== storedState) {
|
|
81
|
+
throw new Error("State mismatch");
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
const tokenParams = new URLSearchParams({
|
|
85
|
+
grant_type: "authorization_code",
|
|
86
|
+
code,
|
|
87
|
+
redirect_uri: this.config.redirectUri,
|
|
88
|
+
client_id: this.config.clientId
|
|
89
|
+
});
|
|
90
|
+
if (this.config.usePKCE && typeof window !== "undefined") {
|
|
91
|
+
const verifier = sessionStorage.getItem("bron_pkce_verifier");
|
|
92
|
+
if (verifier) {
|
|
93
|
+
tokenParams.set("code_verifier", verifier);
|
|
94
|
+
sessionStorage.removeItem("bron_pkce_verifier");
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
if (this.config.clientSecret) {
|
|
98
|
+
tokenParams.set("client_secret", this.config.clientSecret);
|
|
99
|
+
}
|
|
100
|
+
const response = await fetch(this.config.tokenUrl, {
|
|
101
|
+
method: "POST",
|
|
102
|
+
headers: {
|
|
103
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
104
|
+
},
|
|
105
|
+
body: tokenParams.toString()
|
|
106
|
+
});
|
|
107
|
+
if (!response.ok) {
|
|
108
|
+
const error2 = await response.text();
|
|
109
|
+
throw new Error(`Token exchange failed: ${error2}`);
|
|
110
|
+
}
|
|
111
|
+
const tokenData = await response.json();
|
|
112
|
+
const tokens = {
|
|
113
|
+
accessToken: tokenData.access_token,
|
|
114
|
+
refreshToken: tokenData.refresh_token,
|
|
115
|
+
expiresAt: Date.now() + tokenData.expires_in * 1e3,
|
|
116
|
+
tokenType: tokenData.token_type || "Bearer"
|
|
117
|
+
};
|
|
118
|
+
this.tokens = tokens;
|
|
119
|
+
if (this.storage) {
|
|
120
|
+
await this.storage.set("bron_tokens", JSON.stringify(tokens));
|
|
121
|
+
}
|
|
122
|
+
if (typeof window !== "undefined") {
|
|
123
|
+
sessionStorage.removeItem("bron_auth_state");
|
|
124
|
+
}
|
|
125
|
+
return tokens;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Get current access token
|
|
129
|
+
*/
|
|
130
|
+
async getAccessToken() {
|
|
131
|
+
if (this.tokens && Date.now() < this.tokens.expiresAt) {
|
|
132
|
+
return this.tokens.accessToken;
|
|
133
|
+
}
|
|
134
|
+
if (this.storage) {
|
|
135
|
+
const stored = await this.storage.get("bron_tokens");
|
|
136
|
+
if (stored) {
|
|
137
|
+
this.tokens = JSON.parse(stored);
|
|
138
|
+
if (Date.now() < this.tokens.expiresAt) {
|
|
139
|
+
return this.tokens.accessToken;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Logout
|
|
147
|
+
*/
|
|
148
|
+
async logout() {
|
|
149
|
+
this.tokens = null;
|
|
150
|
+
if (this.storage) {
|
|
151
|
+
await this.storage.remove("bron_tokens");
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
var BronApiClient = class {
|
|
156
|
+
config;
|
|
157
|
+
constructor(config) {
|
|
158
|
+
this.config = config;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Create authenticated request headers
|
|
162
|
+
*/
|
|
163
|
+
async getHeaders() {
|
|
164
|
+
const token = await this.config.getAccessToken();
|
|
165
|
+
if (!token) {
|
|
166
|
+
throw new Error("No access token available");
|
|
167
|
+
}
|
|
168
|
+
return {
|
|
169
|
+
"Authorization": `Bearer ${token}`,
|
|
170
|
+
"Content-Type": "application/json"
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Create session / get party mapping
|
|
175
|
+
*/
|
|
176
|
+
async createSession() {
|
|
177
|
+
if (this.config.baseUrl.includes("mock") || this.config.baseUrl.includes("dev")) {
|
|
178
|
+
return {
|
|
179
|
+
sessionId: "mock-session-" + Date.now(),
|
|
180
|
+
partyId: core.toPartyId("mock-party-" + Date.now()),
|
|
181
|
+
expiresAt: Date.now() + 36e5
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
const headers = await this.getHeaders();
|
|
185
|
+
const response = await fetch(`${this.config.baseUrl}/sessions`, {
|
|
186
|
+
method: "POST",
|
|
187
|
+
headers
|
|
188
|
+
});
|
|
189
|
+
if (!response.ok) {
|
|
190
|
+
const error = await response.text();
|
|
191
|
+
throw new Error(`Failed to create session: ${error}`);
|
|
192
|
+
}
|
|
193
|
+
const data = await response.json();
|
|
194
|
+
return {
|
|
195
|
+
sessionId: data.sessionId,
|
|
196
|
+
partyId: core.toPartyId(data.partyId),
|
|
197
|
+
expiresAt: data.expiresAt
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Request signature
|
|
202
|
+
*/
|
|
203
|
+
async requestSignature(request) {
|
|
204
|
+
if (this.config.baseUrl.includes("mock") || this.config.baseUrl.includes("dev")) {
|
|
205
|
+
return {
|
|
206
|
+
requestId: "mock-request-" + Date.now(),
|
|
207
|
+
status: "pending"
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
const headers = await this.getHeaders();
|
|
211
|
+
const response = await fetch(`${this.config.baseUrl}/signatures`, {
|
|
212
|
+
method: "POST",
|
|
213
|
+
headers,
|
|
214
|
+
body: JSON.stringify(request)
|
|
215
|
+
});
|
|
216
|
+
if (!response.ok) {
|
|
217
|
+
const error = await response.text();
|
|
218
|
+
throw new Error(`Failed to request signature: ${error}`);
|
|
219
|
+
}
|
|
220
|
+
const data = await response.json();
|
|
221
|
+
return {
|
|
222
|
+
requestId: data.requestId,
|
|
223
|
+
status: data.status,
|
|
224
|
+
signature: data.signature,
|
|
225
|
+
transactionHash: data.transactionHash
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Get request status
|
|
230
|
+
*/
|
|
231
|
+
async getRequestStatus(requestId) {
|
|
232
|
+
if (this.config.baseUrl.includes("mock") || this.config.baseUrl.includes("dev")) {
|
|
233
|
+
return {
|
|
234
|
+
requestId,
|
|
235
|
+
status: "approved",
|
|
236
|
+
signature: "mock-signature-" + requestId,
|
|
237
|
+
transactionHash: "mock-tx-hash"
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
const headers = await this.getHeaders();
|
|
241
|
+
const response = await fetch(`${this.config.baseUrl}/signatures/${requestId}`, {
|
|
242
|
+
method: "GET",
|
|
243
|
+
headers
|
|
244
|
+
});
|
|
245
|
+
if (!response.ok) {
|
|
246
|
+
const error = await response.text();
|
|
247
|
+
throw new Error(`Failed to get request status: ${error}`);
|
|
248
|
+
}
|
|
249
|
+
const data = await response.json();
|
|
250
|
+
return {
|
|
251
|
+
requestId: data.requestId,
|
|
252
|
+
status: data.status,
|
|
253
|
+
signature: data.signature,
|
|
254
|
+
transactionHash: data.transactionHash,
|
|
255
|
+
error: data.error
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Poll request status until complete
|
|
260
|
+
*/
|
|
261
|
+
async pollRequestStatus(requestId, timeoutMs = 6e4, intervalMs = 2e3) {
|
|
262
|
+
const startTime = Date.now();
|
|
263
|
+
while (Date.now() - startTime < timeoutMs) {
|
|
264
|
+
const status = await this.getRequestStatus(requestId);
|
|
265
|
+
if (status.status === "approved" || status.status === "denied" || status.status === "expired") {
|
|
266
|
+
return status;
|
|
267
|
+
}
|
|
268
|
+
await new Promise((resolve) => setTimeout(resolve, intervalMs));
|
|
269
|
+
}
|
|
270
|
+
throw new Error("Polling timeout");
|
|
271
|
+
}
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
// src/bron-adapter.ts
|
|
275
|
+
var BronAdapter = class {
|
|
276
|
+
walletId = core.toWalletId("bron");
|
|
277
|
+
name = "Bron";
|
|
278
|
+
authClient;
|
|
279
|
+
apiClient;
|
|
280
|
+
constructor(config) {
|
|
281
|
+
this.authClient = new BronAuthClient(config.auth);
|
|
282
|
+
if (config.useMockApi || process.env.NODE_ENV === "development") {
|
|
283
|
+
this.apiClient = this.createMockApiClient();
|
|
284
|
+
} else {
|
|
285
|
+
this.apiClient = new BronApiClient({
|
|
286
|
+
baseUrl: config.api.baseUrl,
|
|
287
|
+
getAccessToken: async () => {
|
|
288
|
+
return await this.authClient.getAccessToken();
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Create mock API client for development
|
|
295
|
+
*/
|
|
296
|
+
createMockApiClient() {
|
|
297
|
+
const mockBaseUrl = "https://api.bron.dev";
|
|
298
|
+
return new BronApiClient({
|
|
299
|
+
baseUrl: mockBaseUrl,
|
|
300
|
+
getAccessToken: () => {
|
|
301
|
+
return Promise.resolve("mock-bron-token");
|
|
302
|
+
}
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
getCapabilities() {
|
|
306
|
+
return [
|
|
307
|
+
"connect",
|
|
308
|
+
"disconnect",
|
|
309
|
+
"remoteSigner",
|
|
310
|
+
"signMessage",
|
|
311
|
+
"signTransaction"
|
|
312
|
+
// restore depends on session persistence
|
|
313
|
+
];
|
|
314
|
+
}
|
|
315
|
+
detectInstalled() {
|
|
316
|
+
return Promise.resolve({
|
|
317
|
+
installed: true,
|
|
318
|
+
reason: "Bron is a remote signer service"
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
async connect(ctx, _opts) {
|
|
322
|
+
try {
|
|
323
|
+
let accessToken = await this.authClient.getAccessToken();
|
|
324
|
+
if (!accessToken) {
|
|
325
|
+
if (typeof window === "undefined") {
|
|
326
|
+
throw new Error("OAuth flow requires browser environment");
|
|
327
|
+
}
|
|
328
|
+
const authUrl = await this.authClient.startAuth();
|
|
329
|
+
const popup = window.open(
|
|
330
|
+
authUrl,
|
|
331
|
+
"Bron Auth",
|
|
332
|
+
"width=500,height=600"
|
|
333
|
+
);
|
|
334
|
+
if (!popup) {
|
|
335
|
+
throw new Error("Failed to open auth popup");
|
|
336
|
+
}
|
|
337
|
+
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
338
|
+
if (process.env.NODE_ENV === "development") {
|
|
339
|
+
accessToken = "mock-token";
|
|
340
|
+
} else {
|
|
341
|
+
throw new Error("OAuth callback not implemented in adapter - handle in app");
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
const session = await this.apiClient.createSession();
|
|
345
|
+
return {
|
|
346
|
+
partyId: session.partyId,
|
|
347
|
+
session: {
|
|
348
|
+
walletId: this.walletId,
|
|
349
|
+
network: ctx.network,
|
|
350
|
+
createdAt: Date.now(),
|
|
351
|
+
expiresAt: session.expiresAt,
|
|
352
|
+
capabilitiesSnapshot: ["connect", "signMessage", "signTransaction", "remoteSigner"],
|
|
353
|
+
metadata: {
|
|
354
|
+
sessionId: session.sessionId
|
|
355
|
+
}
|
|
356
|
+
},
|
|
357
|
+
capabilities: ["connect", "signMessage", "signTransaction", "remoteSigner"]
|
|
358
|
+
};
|
|
359
|
+
} catch (err) {
|
|
360
|
+
throw core.mapUnknownErrorToPartyLayerError(err, {
|
|
361
|
+
walletId: this.walletId,
|
|
362
|
+
phase: "connect",
|
|
363
|
+
transport: "remote"
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
async disconnect(_ctx, _session) {
|
|
368
|
+
await this.authClient.logout();
|
|
369
|
+
}
|
|
370
|
+
async restore(_ctx, persisted) {
|
|
371
|
+
const sessionId = persisted.metadata?.sessionId;
|
|
372
|
+
if (typeof sessionId !== "string") {
|
|
373
|
+
return null;
|
|
374
|
+
}
|
|
375
|
+
const accessToken = await this.authClient.getAccessToken();
|
|
376
|
+
if (!accessToken) {
|
|
377
|
+
return null;
|
|
378
|
+
}
|
|
379
|
+
if (persisted.expiresAt && Date.now() >= persisted.expiresAt) {
|
|
380
|
+
return null;
|
|
381
|
+
}
|
|
382
|
+
return {
|
|
383
|
+
...persisted,
|
|
384
|
+
walletId: this.walletId
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
async signMessage(_ctx, session, params) {
|
|
388
|
+
try {
|
|
389
|
+
const sessionId = session.metadata?.sessionId;
|
|
390
|
+
if (typeof sessionId !== "string") {
|
|
391
|
+
throw new Error("No session ID");
|
|
392
|
+
}
|
|
393
|
+
const signResponse = await this.apiClient.requestSignature({
|
|
394
|
+
message: params.message,
|
|
395
|
+
sessionId
|
|
396
|
+
});
|
|
397
|
+
if (signResponse.status === "pending") {
|
|
398
|
+
const status = await this.apiClient.pollRequestStatus(signResponse.requestId);
|
|
399
|
+
if (status.status === "denied") {
|
|
400
|
+
throw new core.UserRejectedError("Signature request denied");
|
|
401
|
+
}
|
|
402
|
+
if (status.status === "approved" && status.signature) {
|
|
403
|
+
return {
|
|
404
|
+
message: params.message,
|
|
405
|
+
signature: core.toSignature(status.signature),
|
|
406
|
+
partyId: session.partyId
|
|
407
|
+
};
|
|
408
|
+
}
|
|
409
|
+
throw new Error("Signature request failed");
|
|
410
|
+
}
|
|
411
|
+
if (signResponse.status === "denied") {
|
|
412
|
+
throw new core.UserRejectedError("Signature request denied");
|
|
413
|
+
}
|
|
414
|
+
if (!signResponse.signature) {
|
|
415
|
+
throw new Error("No signature in response");
|
|
416
|
+
}
|
|
417
|
+
return {
|
|
418
|
+
message: params.message,
|
|
419
|
+
signature: core.toSignature(signResponse.signature),
|
|
420
|
+
partyId: session.partyId
|
|
421
|
+
};
|
|
422
|
+
} catch (err) {
|
|
423
|
+
throw core.mapUnknownErrorToPartyLayerError(err, {
|
|
424
|
+
walletId: this.walletId,
|
|
425
|
+
phase: "signMessage",
|
|
426
|
+
transport: "remote"
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
async signTransaction(_ctx, session, params) {
|
|
431
|
+
try {
|
|
432
|
+
const sessionId = session.metadata?.sessionId;
|
|
433
|
+
if (typeof sessionId !== "string") {
|
|
434
|
+
throw new Error("No session ID");
|
|
435
|
+
}
|
|
436
|
+
const signResponse = await this.apiClient.requestSignature({
|
|
437
|
+
transaction: params.tx,
|
|
438
|
+
sessionId
|
|
439
|
+
});
|
|
440
|
+
if (signResponse.status === "pending") {
|
|
441
|
+
const status = await this.apiClient.pollRequestStatus(signResponse.requestId);
|
|
442
|
+
if (status.status === "denied") {
|
|
443
|
+
throw new core.UserRejectedError("Transaction signing denied");
|
|
444
|
+
}
|
|
445
|
+
if (status.status === "approved" && status.signature) {
|
|
446
|
+
const signedTx2 = typeof params.tx === "object" && params.tx !== null ? { ...params.tx, signature: status.signature } : { tx: params.tx, signature: status.signature };
|
|
447
|
+
return {
|
|
448
|
+
signedTx: signedTx2,
|
|
449
|
+
transactionHash: status.transactionHash ? core.toTransactionHash(status.transactionHash) : core.toTransactionHash("pending"),
|
|
450
|
+
partyId: session.partyId
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
throw new Error("Transaction signing failed");
|
|
454
|
+
}
|
|
455
|
+
if (signResponse.status === "denied") {
|
|
456
|
+
throw new core.UserRejectedError("Transaction signing denied");
|
|
457
|
+
}
|
|
458
|
+
if (!signResponse.signature) {
|
|
459
|
+
throw new Error("No signature in response");
|
|
460
|
+
}
|
|
461
|
+
const signedTx = typeof params.tx === "object" && params.tx !== null ? { ...params.tx, signature: signResponse.signature } : { tx: params.tx, signature: signResponse.signature };
|
|
462
|
+
return {
|
|
463
|
+
signedTx,
|
|
464
|
+
transactionHash: signResponse.transactionHash ? core.toTransactionHash(signResponse.transactionHash) : core.toTransactionHash("pending"),
|
|
465
|
+
partyId: session.partyId
|
|
466
|
+
};
|
|
467
|
+
} catch (err) {
|
|
468
|
+
throw core.mapUnknownErrorToPartyLayerError(err, {
|
|
469
|
+
walletId: this.walletId,
|
|
470
|
+
phase: "signTransaction",
|
|
471
|
+
transport: "remote"
|
|
472
|
+
});
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
};
|
|
476
|
+
|
|
477
|
+
exports.BronAdapter = BronAdapter;
|
|
478
|
+
//# sourceMappingURL=index.js.map
|
|
479
|
+
//# sourceMappingURL=index.js.map
|