@fernolab/wallet-adapter-core 0.1.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.
- package/dist/controller.d.ts +48 -0
- package/dist/controller.d.ts.map +1 -0
- package/dist/controller.js +197 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/mobile-deeplinks.d.ts +72 -0
- package/dist/mobile-deeplinks.d.ts.map +1 -0
- package/dist/mobile-deeplinks.js +133 -0
- package/dist/wallet-standard.d.ts +125 -0
- package/dist/wallet-standard.d.ts.map +1 -0
- package/dist/wallet-standard.js +54 -0
- package/package.json +46 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { type IdentifierString, type StandardWallet, type WalletAccount } from './wallet-standard';
|
|
2
|
+
export type WalletConnectionStatus = 'disconnected' | 'connecting' | 'connected';
|
|
3
|
+
export type WalletAddress = string;
|
|
4
|
+
export interface WalletControllerState<TWallet extends StandardWallet = StandardWallet> {
|
|
5
|
+
availableWallets: readonly TWallet[];
|
|
6
|
+
connected: boolean;
|
|
7
|
+
connecting: boolean;
|
|
8
|
+
error: Error | null;
|
|
9
|
+
publicKey: WalletAddress | null;
|
|
10
|
+
shortAddress: string;
|
|
11
|
+
signing: boolean;
|
|
12
|
+
standardAccount: WalletAccount | null;
|
|
13
|
+
standardWallet: TWallet | null;
|
|
14
|
+
status: WalletConnectionStatus;
|
|
15
|
+
}
|
|
16
|
+
export interface WalletSignMessageResult {
|
|
17
|
+
signature: string | Uint8Array;
|
|
18
|
+
signatureType?: string;
|
|
19
|
+
signedMessage: string | Uint8Array;
|
|
20
|
+
}
|
|
21
|
+
export type WalletControllerListener<TWallet extends StandardWallet = StandardWallet> = (state: WalletControllerState<TWallet>) => void;
|
|
22
|
+
export declare function resolveSolanaChainFromEndpoint(endpoint: string): IdentifierString;
|
|
23
|
+
export declare class WalletController<TWallet extends StandardWallet = StandardWallet> {
|
|
24
|
+
private availableWalletsValue;
|
|
25
|
+
private connectedValue;
|
|
26
|
+
private connectingValue;
|
|
27
|
+
private errorValue;
|
|
28
|
+
private listeners;
|
|
29
|
+
private publicKeyValue;
|
|
30
|
+
private signingValue;
|
|
31
|
+
private standardAccountValue;
|
|
32
|
+
private standardEventsOff;
|
|
33
|
+
private standardWalletValue;
|
|
34
|
+
get state(): WalletControllerState<TWallet>;
|
|
35
|
+
subscribe(listener: WalletControllerListener<TWallet>): () => void;
|
|
36
|
+
setAvailableWallets(wallets: readonly TWallet[]): void;
|
|
37
|
+
clearError(): void;
|
|
38
|
+
connectStandard(wallet: TWallet): Promise<void>;
|
|
39
|
+
disconnect(): Promise<void>;
|
|
40
|
+
signMessage(message: string | Uint8Array): Promise<WalletSignMessageResult>;
|
|
41
|
+
private applyStandardAccount;
|
|
42
|
+
private watchStandardWallet;
|
|
43
|
+
private clearStandardEventsListener;
|
|
44
|
+
private setError;
|
|
45
|
+
private emit;
|
|
46
|
+
}
|
|
47
|
+
export declare function createWalletController<TWallet extends StandardWallet = StandardWallet>(): WalletController<TWallet>;
|
|
48
|
+
//# sourceMappingURL=controller.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["../src/controller.ts"],"names":[],"mappings":"AAAA,OAAO,EAQL,KAAK,gBAAgB,EAGrB,KAAK,cAAc,EACnB,KAAK,aAAa,EACnB,MAAM,mBAAmB,CAAC;AAE3B,MAAM,MAAM,sBAAsB,GAAG,cAAc,GAAG,YAAY,GAAG,WAAW,CAAC;AACjF,MAAM,MAAM,aAAa,GAAG,MAAM,CAAC;AAEnC,MAAM,WAAW,qBAAqB,CAAC,OAAO,SAAS,cAAc,GAAG,cAAc;IACpF,gBAAgB,EAAE,SAAS,OAAO,EAAE,CAAC;IACrC,SAAS,EAAE,OAAO,CAAC;IACnB,UAAU,EAAE,OAAO,CAAC;IACpB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IACpB,SAAS,EAAE,aAAa,GAAG,IAAI,CAAC;IAChC,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,OAAO,CAAC;IACjB,eAAe,EAAE,aAAa,GAAG,IAAI,CAAC;IACtC,cAAc,EAAE,OAAO,GAAG,IAAI,CAAC;IAC/B,MAAM,EAAE,sBAAsB,CAAC;CAChC;AAED,MAAM,WAAW,uBAAuB;IACtC,SAAS,EAAE,MAAM,GAAG,UAAU,CAAC;IAC/B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,GAAG,UAAU,CAAC;CACpC;AAED,MAAM,MAAM,wBAAwB,CAAC,OAAO,SAAS,cAAc,GAAG,cAAc,IAAI,CACtF,KAAK,EAAE,qBAAqB,CAAC,OAAO,CAAC,KAClC,IAAI,CAAC;AA8BV,wBAAgB,8BAA8B,CAAC,QAAQ,EAAE,MAAM,GAAG,gBAAgB,CAgBjF;AAED,qBAAa,gBAAgB,CAAC,OAAO,SAAS,cAAc,GAAG,cAAc;IAC3E,OAAO,CAAC,qBAAqB,CAA0B;IACvD,OAAO,CAAC,cAAc,CAAS;IAC/B,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,UAAU,CAAsB;IACxC,OAAO,CAAC,SAAS,CAAgD;IACjE,OAAO,CAAC,cAAc,CAA8B;IACpD,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,oBAAoB,CAA8B;IAC1D,OAAO,CAAC,iBAAiB,CAA6B;IACtD,OAAO,CAAC,mBAAmB,CAAwB;IAEnD,IAAI,KAAK,IAAI,qBAAqB,CAAC,OAAO,CAAC,CAa1C;IAED,SAAS,CAAC,QAAQ,EAAE,wBAAwB,CAAC,OAAO,CAAC,GAAG,MAAM,IAAI;IAMlE,mBAAmB,CAAC,OAAO,EAAE,SAAS,OAAO,EAAE,GAAG,IAAI;IAKtD,UAAU,IAAI,IAAI;IAIZ,eAAe,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAiC/C,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAqB3B,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,UAAU,GAAG,OAAO,CAAC,uBAAuB,CAAC;IA8CjF,OAAO,CAAC,oBAAoB;IAmB5B,OAAO,CAAC,mBAAmB;IAsB3B,OAAO,CAAC,2BAA2B;IAKnC,OAAO,CAAC,QAAQ;IAKhB,OAAO,CAAC,IAAI;CAIb;AAED,wBAAgB,sBAAsB,CAAC,OAAO,SAAS,cAAc,GAAG,cAAc,KAAK,gBAAgB,CAAC,OAAO,CAAC,CAEnH"}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { SolanaSignMessage, StandardConnect, StandardDisconnect, StandardEvents } from './wallet-standard';
|
|
2
|
+
function getWalletFeature(wallet, featureName) {
|
|
3
|
+
const features = wallet.features;
|
|
4
|
+
return features[featureName] ?? null;
|
|
5
|
+
}
|
|
6
|
+
function formatShortAddress(publicKey) {
|
|
7
|
+
return publicKey ? `${publicKey.slice(0, 4)}...${publicKey.slice(-4)}` : '';
|
|
8
|
+
}
|
|
9
|
+
function isWalletAccount(value) {
|
|
10
|
+
return (Boolean(value) &&
|
|
11
|
+
typeof value === 'object' &&
|
|
12
|
+
typeof value.address === 'string' &&
|
|
13
|
+
value.publicKey instanceof Uint8Array &&
|
|
14
|
+
Array.isArray(value.chains) &&
|
|
15
|
+
Array.isArray(value.features));
|
|
16
|
+
}
|
|
17
|
+
function getStatus(connected, connecting) {
|
|
18
|
+
if (connecting) {
|
|
19
|
+
return 'connecting';
|
|
20
|
+
}
|
|
21
|
+
return connected ? 'connected' : 'disconnected';
|
|
22
|
+
}
|
|
23
|
+
export function resolveSolanaChainFromEndpoint(endpoint) {
|
|
24
|
+
const normalized = endpoint.toLowerCase();
|
|
25
|
+
if (normalized.includes('devnet')) {
|
|
26
|
+
return 'solana:devnet';
|
|
27
|
+
}
|
|
28
|
+
if (normalized.includes('testnet')) {
|
|
29
|
+
return 'solana:testnet';
|
|
30
|
+
}
|
|
31
|
+
if (normalized.includes('localhost') || normalized.includes('127.0.0.1')) {
|
|
32
|
+
return 'solana:localnet';
|
|
33
|
+
}
|
|
34
|
+
return 'solana:mainnet';
|
|
35
|
+
}
|
|
36
|
+
export class WalletController {
|
|
37
|
+
availableWalletsValue = [];
|
|
38
|
+
connectedValue = false;
|
|
39
|
+
connectingValue = false;
|
|
40
|
+
errorValue = null;
|
|
41
|
+
listeners = new Set();
|
|
42
|
+
publicKeyValue = null;
|
|
43
|
+
signingValue = false;
|
|
44
|
+
standardAccountValue = null;
|
|
45
|
+
standardEventsOff = null;
|
|
46
|
+
standardWalletValue = null;
|
|
47
|
+
get state() {
|
|
48
|
+
return {
|
|
49
|
+
availableWallets: this.availableWalletsValue,
|
|
50
|
+
connected: this.connectedValue,
|
|
51
|
+
connecting: this.connectingValue,
|
|
52
|
+
error: this.errorValue,
|
|
53
|
+
publicKey: this.publicKeyValue,
|
|
54
|
+
shortAddress: formatShortAddress(this.publicKeyValue),
|
|
55
|
+
signing: this.signingValue,
|
|
56
|
+
standardAccount: this.standardAccountValue,
|
|
57
|
+
standardWallet: this.standardWalletValue,
|
|
58
|
+
status: getStatus(this.connectedValue, this.connectingValue)
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
subscribe(listener) {
|
|
62
|
+
this.listeners.add(listener);
|
|
63
|
+
listener(this.state);
|
|
64
|
+
return () => this.listeners.delete(listener);
|
|
65
|
+
}
|
|
66
|
+
setAvailableWallets(wallets) {
|
|
67
|
+
this.availableWalletsValue = wallets;
|
|
68
|
+
this.emit();
|
|
69
|
+
}
|
|
70
|
+
clearError() {
|
|
71
|
+
this.setError(null);
|
|
72
|
+
}
|
|
73
|
+
async connectStandard(wallet) {
|
|
74
|
+
this.connectingValue = true;
|
|
75
|
+
this.setError(null);
|
|
76
|
+
this.emit();
|
|
77
|
+
try {
|
|
78
|
+
const connectFeature = getWalletFeature(wallet.wallet, StandardConnect);
|
|
79
|
+
if (!connectFeature) {
|
|
80
|
+
throw new Error('Wallet does not support standard:connect');
|
|
81
|
+
}
|
|
82
|
+
const { accounts } = await connectFeature.connect({ silent: false });
|
|
83
|
+
const account = accounts?.[0] ?? wallet.wallet.accounts[0] ?? null;
|
|
84
|
+
if (!isWalletAccount(account)) {
|
|
85
|
+
throw new Error('Wallet did not return an account');
|
|
86
|
+
}
|
|
87
|
+
this.applyStandardAccount(wallet, account);
|
|
88
|
+
this.watchStandardWallet(wallet);
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
this.clearStandardEventsListener();
|
|
92
|
+
this.connectingValue = false;
|
|
93
|
+
const nextError = error instanceof Error ? error : new Error('Failed to connect wallet');
|
|
94
|
+
this.setError(nextError);
|
|
95
|
+
throw nextError;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
async disconnect() {
|
|
99
|
+
try {
|
|
100
|
+
if (this.standardWalletValue) {
|
|
101
|
+
const disconnectFeature = getWalletFeature(this.standardWalletValue.wallet, StandardDisconnect);
|
|
102
|
+
await disconnectFeature?.disconnect();
|
|
103
|
+
}
|
|
104
|
+
this.clearStandardEventsListener();
|
|
105
|
+
this.applyStandardAccount(null, null);
|
|
106
|
+
this.setError(null);
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
const nextError = error instanceof Error ? error : new Error('Failed to disconnect');
|
|
110
|
+
this.setError(nextError);
|
|
111
|
+
throw nextError;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
async signMessage(message) {
|
|
115
|
+
if (!this.standardWalletValue || !this.standardAccountValue) {
|
|
116
|
+
throw new Error('No wallet connected');
|
|
117
|
+
}
|
|
118
|
+
this.signingValue = true;
|
|
119
|
+
this.setError(null);
|
|
120
|
+
this.emit();
|
|
121
|
+
try {
|
|
122
|
+
const messageFeature = getWalletFeature(this.standardWalletValue.wallet, SolanaSignMessage);
|
|
123
|
+
if (!messageFeature || !this.standardAccountValue.features.includes(SolanaSignMessage)) {
|
|
124
|
+
throw new Error('Connected wallet does not support signing messages');
|
|
125
|
+
}
|
|
126
|
+
const payload = typeof message === 'string' ? new TextEncoder().encode(message) : new Uint8Array(message);
|
|
127
|
+
const results = await messageFeature.signMessage({
|
|
128
|
+
account: this.standardAccountValue,
|
|
129
|
+
message: payload
|
|
130
|
+
});
|
|
131
|
+
const result = Array.isArray(results) ? results[0] : results;
|
|
132
|
+
if (!result?.signature) {
|
|
133
|
+
throw new Error('Wallet did not return a message signature');
|
|
134
|
+
}
|
|
135
|
+
return {
|
|
136
|
+
signature: result.signature,
|
|
137
|
+
signatureType: result.signatureType,
|
|
138
|
+
signedMessage: result.signedMessage ?? payload
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
catch (error) {
|
|
142
|
+
const nextError = error instanceof Error ? error : new Error('Failed to sign message');
|
|
143
|
+
this.setError(nextError);
|
|
144
|
+
throw nextError;
|
|
145
|
+
}
|
|
146
|
+
finally {
|
|
147
|
+
this.signingValue = false;
|
|
148
|
+
this.emit();
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
applyStandardAccount(wallet, account) {
|
|
152
|
+
if (!wallet || !account) {
|
|
153
|
+
this.standardWalletValue = null;
|
|
154
|
+
this.standardAccountValue = null;
|
|
155
|
+
this.publicKeyValue = null;
|
|
156
|
+
this.connectedValue = false;
|
|
157
|
+
this.connectingValue = false;
|
|
158
|
+
this.emit();
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
this.standardWalletValue = wallet;
|
|
162
|
+
this.standardAccountValue = account;
|
|
163
|
+
this.publicKeyValue = account.address;
|
|
164
|
+
this.connectedValue = true;
|
|
165
|
+
this.connectingValue = false;
|
|
166
|
+
this.emit();
|
|
167
|
+
}
|
|
168
|
+
watchStandardWallet(wallet) {
|
|
169
|
+
this.clearStandardEventsListener();
|
|
170
|
+
const eventsFeature = getWalletFeature(wallet.wallet, StandardEvents);
|
|
171
|
+
if (!eventsFeature) {
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
const handler = (properties) => {
|
|
175
|
+
if ('accounts' in properties) {
|
|
176
|
+
const account = properties.accounts?.[0] ?? wallet.wallet.accounts[0] ?? null;
|
|
177
|
+
this.applyStandardAccount(wallet, isWalletAccount(account) ? account : null);
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
this.standardEventsOff = eventsFeature.on('change', handler);
|
|
181
|
+
}
|
|
182
|
+
clearStandardEventsListener() {
|
|
183
|
+
this.standardEventsOff?.();
|
|
184
|
+
this.standardEventsOff = null;
|
|
185
|
+
}
|
|
186
|
+
setError(error) {
|
|
187
|
+
this.errorValue = error;
|
|
188
|
+
this.emit();
|
|
189
|
+
}
|
|
190
|
+
emit() {
|
|
191
|
+
const state = this.state;
|
|
192
|
+
this.listeners.forEach((listener) => listener(state));
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
export function createWalletController() {
|
|
196
|
+
return new WalletController();
|
|
197
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,oBAAoB,CAAC;AACnC,cAAc,mBAAmB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
export type MobileWalletId = 'backpack' | 'jupiter' | 'phantom' | 'solflare';
|
|
2
|
+
export type MobilePlatform = 'android' | 'desktop' | 'ios' | 'other';
|
|
3
|
+
export interface MobileRuntime {
|
|
4
|
+
isMobile: boolean;
|
|
5
|
+
isWalletBrowser: boolean;
|
|
6
|
+
platform: MobilePlatform;
|
|
7
|
+
supportsMobileWalletAdapter: boolean;
|
|
8
|
+
walletBrowser: MobileWalletId | null;
|
|
9
|
+
}
|
|
10
|
+
export interface MobileRuntimeInput {
|
|
11
|
+
maxTouchPoints?: number;
|
|
12
|
+
userAgent?: string;
|
|
13
|
+
}
|
|
14
|
+
export interface MobileWallet {
|
|
15
|
+
browseUrlTemplate?: string;
|
|
16
|
+
docsUrl: string;
|
|
17
|
+
encodeBrowseUrl?: boolean;
|
|
18
|
+
id: MobileWalletId;
|
|
19
|
+
name: string;
|
|
20
|
+
supportsMobileWalletAdapter: boolean;
|
|
21
|
+
websiteUrl: string;
|
|
22
|
+
}
|
|
23
|
+
export type MobileDeepLinkWallet = MobileWallet & {
|
|
24
|
+
browseUrlTemplate: string;
|
|
25
|
+
};
|
|
26
|
+
export interface InjectedSolanaProvider {
|
|
27
|
+
connect?(input?: {
|
|
28
|
+
onlyIfTrusted?: boolean;
|
|
29
|
+
}): Promise<{
|
|
30
|
+
publicKey?: {
|
|
31
|
+
toBuffer?(): Uint8Array;
|
|
32
|
+
toBytes?(): Uint8Array;
|
|
33
|
+
toString(): string;
|
|
34
|
+
};
|
|
35
|
+
}>;
|
|
36
|
+
disconnect?(): Promise<void>;
|
|
37
|
+
isBackpack?: boolean;
|
|
38
|
+
isJupiter?: boolean;
|
|
39
|
+
isPhantom?: boolean;
|
|
40
|
+
isSolflare?: boolean;
|
|
41
|
+
signMessage?(message: Uint8Array, display?: 'hex' | 'utf8'): Promise<Uint8Array | {
|
|
42
|
+
signature?: Uint8Array | readonly number[];
|
|
43
|
+
}>;
|
|
44
|
+
}
|
|
45
|
+
export interface InjectedWallet {
|
|
46
|
+
id: MobileWalletId;
|
|
47
|
+
name: string;
|
|
48
|
+
provider: InjectedSolanaProvider;
|
|
49
|
+
}
|
|
50
|
+
export interface InjectedWalletTarget {
|
|
51
|
+
backpack?: {
|
|
52
|
+
solana?: InjectedSolanaProvider;
|
|
53
|
+
};
|
|
54
|
+
jupiter?: InjectedSolanaProvider;
|
|
55
|
+
phantom?: {
|
|
56
|
+
solana?: InjectedSolanaProvider;
|
|
57
|
+
};
|
|
58
|
+
solana?: InjectedSolanaProvider;
|
|
59
|
+
solflare?: InjectedSolanaProvider;
|
|
60
|
+
}
|
|
61
|
+
export declare const MOBILE_WALLETS: readonly MobileWallet[];
|
|
62
|
+
export declare const MOBILE_DEEPLINK_WALLETS: MobileDeepLinkWallet[];
|
|
63
|
+
export declare function detectInjectedWallets(target?: InjectedWalletTarget): InjectedWallet[];
|
|
64
|
+
export declare function detectMobileRuntime(input?: MobileRuntimeInput): MobileRuntime;
|
|
65
|
+
export declare function createMobileWalletBrowseUrl(wallet: MobileDeepLinkWallet, targetUrl: string, referrerUrl: string): string;
|
|
66
|
+
export type AdapterMobileWalletId = MobileWalletId;
|
|
67
|
+
export type AdapterMobilePlatform = MobilePlatform;
|
|
68
|
+
export type AdapterMobileRuntime = MobileRuntime;
|
|
69
|
+
export type AdapterMobileRuntimeInput = MobileRuntimeInput;
|
|
70
|
+
export type AdapterMobileWallet = MobileWallet;
|
|
71
|
+
export type AdapterMobileDeepLinkWallet = MobileDeepLinkWallet;
|
|
72
|
+
//# sourceMappingURL=mobile-deeplinks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mobile-deeplinks.d.ts","sourceRoot":"","sources":["../src/mobile-deeplinks.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
export const MOBILE_WALLETS = [
|
|
2
|
+
{
|
|
3
|
+
browseUrlTemplate: 'jupjupjup://browse/{url}',
|
|
4
|
+
docsUrl: 'https://docs.jup.ag/user-docs/manage/mobile/app-features',
|
|
5
|
+
encodeBrowseUrl: false,
|
|
6
|
+
id: 'jupiter',
|
|
7
|
+
name: 'Jupiter Mobile',
|
|
8
|
+
supportsMobileWalletAdapter: true,
|
|
9
|
+
websiteUrl: 'https://jup.ag/mobile'
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
browseUrlTemplate: 'https://phantom.app/ul/browse/{url}?ref={ref}',
|
|
13
|
+
docsUrl: 'https://docs.phantom.com/phantom-deeplinks/other-methods/browse',
|
|
14
|
+
id: 'phantom',
|
|
15
|
+
name: 'Phantom',
|
|
16
|
+
supportsMobileWalletAdapter: true,
|
|
17
|
+
websiteUrl: 'https://phantom.app'
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
browseUrlTemplate: 'https://solflare.com/ul/v1/browse/{url}?ref={ref}',
|
|
21
|
+
docsUrl: 'https://docs.solflare.com/solflare/technical/deeplinks/other-methods/browse',
|
|
22
|
+
id: 'solflare',
|
|
23
|
+
name: 'Solflare',
|
|
24
|
+
supportsMobileWalletAdapter: true,
|
|
25
|
+
websiteUrl: 'https://solflare.com'
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
browseUrlTemplate: 'https://backpack.app/ul/v1/browse/{url}?ref={ref}',
|
|
29
|
+
docsUrl: 'https://docs.backpack.app/deeplinks/other-methods/browse',
|
|
30
|
+
id: 'backpack',
|
|
31
|
+
name: 'Backpack',
|
|
32
|
+
supportsMobileWalletAdapter: true,
|
|
33
|
+
websiteUrl: 'https://backpack.app'
|
|
34
|
+
}
|
|
35
|
+
];
|
|
36
|
+
export const MOBILE_DEEPLINK_WALLETS = MOBILE_WALLETS.filter((wallet) => Boolean(wallet.browseUrlTemplate));
|
|
37
|
+
function getWalletById(id) {
|
|
38
|
+
const wallet = MOBILE_WALLETS.find((candidate) => candidate.id === id);
|
|
39
|
+
if (!wallet) {
|
|
40
|
+
throw new Error(`Unknown mobile wallet ${id}`);
|
|
41
|
+
}
|
|
42
|
+
return wallet;
|
|
43
|
+
}
|
|
44
|
+
function getRuntimeUserAgent(userAgent) {
|
|
45
|
+
if (userAgent !== undefined) {
|
|
46
|
+
return userAgent;
|
|
47
|
+
}
|
|
48
|
+
return globalThis.navigator?.userAgent ?? '';
|
|
49
|
+
}
|
|
50
|
+
function getRuntimeMaxTouchPoints(maxTouchPoints) {
|
|
51
|
+
if (maxTouchPoints !== undefined) {
|
|
52
|
+
return maxTouchPoints;
|
|
53
|
+
}
|
|
54
|
+
return globalThis.navigator?.maxTouchPoints ?? 0;
|
|
55
|
+
}
|
|
56
|
+
function detectPlatform(userAgent, maxTouchPoints) {
|
|
57
|
+
if (/android/i.test(userAgent)) {
|
|
58
|
+
return 'android';
|
|
59
|
+
}
|
|
60
|
+
if (/iPhone|iPad|iPod/i.test(userAgent) || (/Macintosh/i.test(userAgent) && maxTouchPoints > 1)) {
|
|
61
|
+
return 'ios';
|
|
62
|
+
}
|
|
63
|
+
if (/Mobile|webOS|BlackBerry|IEMobile|Opera Mini/i.test(userAgent)) {
|
|
64
|
+
return 'other';
|
|
65
|
+
}
|
|
66
|
+
return 'desktop';
|
|
67
|
+
}
|
|
68
|
+
function detectWalletBrowser(userAgent) {
|
|
69
|
+
const normalized = userAgent.toLowerCase();
|
|
70
|
+
if (normalized.includes('phantom')) {
|
|
71
|
+
return 'phantom';
|
|
72
|
+
}
|
|
73
|
+
if (normalized.includes('solflare')) {
|
|
74
|
+
return 'solflare';
|
|
75
|
+
}
|
|
76
|
+
if (normalized.includes('jupiter')) {
|
|
77
|
+
return 'jupiter';
|
|
78
|
+
}
|
|
79
|
+
if (normalized.includes('backpack')) {
|
|
80
|
+
return 'backpack';
|
|
81
|
+
}
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
function getRuntimeTarget(target) {
|
|
85
|
+
if (target) {
|
|
86
|
+
return target;
|
|
87
|
+
}
|
|
88
|
+
return globalThis;
|
|
89
|
+
}
|
|
90
|
+
function addInjectedWallet(wallets, seen, id, provider, isMatch) {
|
|
91
|
+
if (!provider || !isMatch(provider) || seen.has(provider)) {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
const wallet = getWalletById(id);
|
|
95
|
+
wallets.push({ id, name: wallet.name, provider });
|
|
96
|
+
seen.add(provider);
|
|
97
|
+
}
|
|
98
|
+
export function detectInjectedWallets(target) {
|
|
99
|
+
const runtimeTarget = getRuntimeTarget(target);
|
|
100
|
+
const wallets = [];
|
|
101
|
+
const seen = new Set();
|
|
102
|
+
const solana = runtimeTarget.solana;
|
|
103
|
+
addInjectedWallet(wallets, seen, 'phantom', runtimeTarget.phantom?.solana ?? (solana?.isPhantom ? solana : undefined), (provider) => Boolean(provider.isPhantom));
|
|
104
|
+
addInjectedWallet(wallets, seen, 'solflare', runtimeTarget.solflare ?? (solana?.isSolflare ? solana : undefined), (provider) => Boolean(provider.isSolflare));
|
|
105
|
+
addInjectedWallet(wallets, seen, 'jupiter', runtimeTarget.jupiter ?? (solana?.isJupiter ? solana : undefined), (provider) => Boolean(provider.isJupiter));
|
|
106
|
+
addInjectedWallet(wallets, seen, 'backpack', runtimeTarget.backpack?.solana ?? (solana?.isBackpack ? solana : undefined), (provider) => Boolean(provider.isBackpack));
|
|
107
|
+
return wallets;
|
|
108
|
+
}
|
|
109
|
+
function isAndroidChrome(userAgent) {
|
|
110
|
+
return (/Android/i.test(userAgent) &&
|
|
111
|
+
/Chrome\//i.test(userAgent) &&
|
|
112
|
+
!/; wv\)|WebView|EdgA\/|OPR\/|Firefox\/|SamsungBrowser\//i.test(userAgent));
|
|
113
|
+
}
|
|
114
|
+
export function detectMobileRuntime(input = {}) {
|
|
115
|
+
const userAgent = getRuntimeUserAgent(input.userAgent);
|
|
116
|
+
const maxTouchPoints = getRuntimeMaxTouchPoints(input.maxTouchPoints);
|
|
117
|
+
const platform = detectPlatform(userAgent, maxTouchPoints);
|
|
118
|
+
const walletBrowser = detectWalletBrowser(userAgent);
|
|
119
|
+
const isMobile = platform !== 'desktop';
|
|
120
|
+
return {
|
|
121
|
+
isMobile,
|
|
122
|
+
isWalletBrowser: walletBrowser !== null,
|
|
123
|
+
platform,
|
|
124
|
+
supportsMobileWalletAdapter: walletBrowser === null && isAndroidChrome(userAgent),
|
|
125
|
+
walletBrowser
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
export function createMobileWalletBrowseUrl(wallet, targetUrl, referrerUrl) {
|
|
129
|
+
const encodedTargetUrl = wallet.encodeBrowseUrl === false ? targetUrl : encodeURIComponent(targetUrl);
|
|
130
|
+
return wallet.browseUrlTemplate
|
|
131
|
+
.replace('{url}', encodedTargetUrl)
|
|
132
|
+
.replace('{ref}', encodeURIComponent(referrerUrl));
|
|
133
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
export declare const StandardConnect = "standard:connect";
|
|
2
|
+
export declare const StandardDisconnect = "standard:disconnect";
|
|
3
|
+
export declare const StandardEvents = "standard:events";
|
|
4
|
+
export declare const SolanaSignAndSendTransaction = "solana:signAndSendTransaction";
|
|
5
|
+
export declare const SolanaSignMessage = "solana:signMessage";
|
|
6
|
+
export declare const SolanaSignTransaction = "solana:signTransaction";
|
|
7
|
+
export type IdentifierString = string;
|
|
8
|
+
export type WalletIcon = string;
|
|
9
|
+
export interface WalletAccount {
|
|
10
|
+
address: string;
|
|
11
|
+
chains: readonly IdentifierString[];
|
|
12
|
+
features: readonly IdentifierString[];
|
|
13
|
+
label?: string;
|
|
14
|
+
publicKey: Uint8Array;
|
|
15
|
+
}
|
|
16
|
+
export interface Wallet {
|
|
17
|
+
accounts: readonly WalletAccount[];
|
|
18
|
+
chains: readonly IdentifierString[];
|
|
19
|
+
features: Readonly<Record<string, unknown>>;
|
|
20
|
+
icon?: WalletIcon;
|
|
21
|
+
name: string;
|
|
22
|
+
version?: string;
|
|
23
|
+
}
|
|
24
|
+
export interface StandardWalletProvider {
|
|
25
|
+
accounts: readonly unknown[];
|
|
26
|
+
chains?: readonly IdentifierString[];
|
|
27
|
+
features: Readonly<Record<string, unknown>>;
|
|
28
|
+
icon?: string;
|
|
29
|
+
name: string;
|
|
30
|
+
version?: string;
|
|
31
|
+
}
|
|
32
|
+
export interface StandardConnectMethod {
|
|
33
|
+
connect(input?: {
|
|
34
|
+
silent?: boolean;
|
|
35
|
+
}): Promise<{
|
|
36
|
+
accounts: readonly WalletAccount[];
|
|
37
|
+
}>;
|
|
38
|
+
version?: string;
|
|
39
|
+
}
|
|
40
|
+
export interface StandardDisconnectMethod {
|
|
41
|
+
disconnect(): Promise<void>;
|
|
42
|
+
version?: string;
|
|
43
|
+
}
|
|
44
|
+
export interface StandardEventsListeners {
|
|
45
|
+
change(input: {
|
|
46
|
+
accounts?: readonly WalletAccount[];
|
|
47
|
+
}): void;
|
|
48
|
+
}
|
|
49
|
+
export interface StandardEventsMethod {
|
|
50
|
+
on<E extends keyof StandardEventsListeners>(event: E, listener: StandardEventsListeners[E]): () => void;
|
|
51
|
+
version?: string;
|
|
52
|
+
}
|
|
53
|
+
export interface SolanaSignMessageMethod {
|
|
54
|
+
signMessage(...inputs: readonly {
|
|
55
|
+
account: WalletAccount;
|
|
56
|
+
message: Uint8Array;
|
|
57
|
+
}[]): Promise<readonly {
|
|
58
|
+
signature: string | Uint8Array;
|
|
59
|
+
signatureType?: 'ed25519' | string;
|
|
60
|
+
signedMessage?: string | Uint8Array;
|
|
61
|
+
}[]>;
|
|
62
|
+
version?: string;
|
|
63
|
+
}
|
|
64
|
+
export interface SolanaSignTransactionMethod {
|
|
65
|
+
signTransaction(...inputs: readonly {
|
|
66
|
+
account: WalletAccount;
|
|
67
|
+
chain: IdentifierString;
|
|
68
|
+
options?: {
|
|
69
|
+
minContextSlot?: number;
|
|
70
|
+
preflightCommitment?: string;
|
|
71
|
+
};
|
|
72
|
+
transaction: Uint8Array;
|
|
73
|
+
}[]): Promise<readonly {
|
|
74
|
+
signedTransaction: Uint8Array;
|
|
75
|
+
}[]>;
|
|
76
|
+
supportedTransactionVersions?: readonly ('legacy' | number)[];
|
|
77
|
+
version?: string;
|
|
78
|
+
}
|
|
79
|
+
export interface SolanaSignAndSendTransactionMethod {
|
|
80
|
+
signAndSendTransaction(input: {
|
|
81
|
+
account: WalletAccount;
|
|
82
|
+
chain: IdentifierString;
|
|
83
|
+
options?: {
|
|
84
|
+
maxRetries?: number;
|
|
85
|
+
minContextSlot?: number;
|
|
86
|
+
preflightCommitment?: string;
|
|
87
|
+
skipPreflight?: boolean;
|
|
88
|
+
};
|
|
89
|
+
transaction: Uint8Array;
|
|
90
|
+
}): Promise<{
|
|
91
|
+
signature: string | Uint8Array;
|
|
92
|
+
} | readonly {
|
|
93
|
+
signature: string | Uint8Array;
|
|
94
|
+
}[]>;
|
|
95
|
+
supportedTransactionVersions?: readonly ('legacy' | number)[];
|
|
96
|
+
version?: string;
|
|
97
|
+
}
|
|
98
|
+
export interface StandardConnectFeature {
|
|
99
|
+
[StandardConnect]: StandardConnectMethod;
|
|
100
|
+
}
|
|
101
|
+
export interface StandardDisconnectFeature {
|
|
102
|
+
[StandardDisconnect]: StandardDisconnectMethod;
|
|
103
|
+
}
|
|
104
|
+
export interface StandardEventsFeature {
|
|
105
|
+
[StandardEvents]: StandardEventsMethod;
|
|
106
|
+
}
|
|
107
|
+
export interface SolanaSignMessageFeature {
|
|
108
|
+
[SolanaSignMessage]: SolanaSignMessageMethod;
|
|
109
|
+
}
|
|
110
|
+
export interface SolanaSignTransactionFeature {
|
|
111
|
+
[SolanaSignTransaction]: SolanaSignTransactionMethod;
|
|
112
|
+
}
|
|
113
|
+
export interface SolanaSignAndSendTransactionFeature {
|
|
114
|
+
[SolanaSignAndSendTransaction]: SolanaSignAndSendTransactionMethod;
|
|
115
|
+
}
|
|
116
|
+
export interface StandardWallet<TWallet extends StandardWalletProvider = StandardWalletProvider> {
|
|
117
|
+
name: string;
|
|
118
|
+
icon: string;
|
|
119
|
+
wallet: TWallet;
|
|
120
|
+
}
|
|
121
|
+
export declare function isSolanaWallet<TWallet extends StandardWalletProvider>(wallet: TWallet): boolean;
|
|
122
|
+
export declare function adaptWallet<TWallet extends StandardWalletProvider>(wallet: TWallet): StandardWallet<TWallet> | null;
|
|
123
|
+
export declare function dedupeStandardWallets<TWallet extends StandardWalletProvider>(wallets: readonly StandardWallet<TWallet>[], preferredNames?: readonly string[]): StandardWallet<TWallet>[];
|
|
124
|
+
export declare function adaptStandardWallets<TWallet extends StandardWalletProvider>(wallets: readonly TWallet[], preferredNames?: readonly string[]): StandardWallet<TWallet>[];
|
|
125
|
+
//# sourceMappingURL=wallet-standard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wallet-standard.d.ts","sourceRoot":"","sources":["../src/wallet-standard.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,eAAe,qBAAqB,CAAC;AAClD,eAAO,MAAM,kBAAkB,wBAAwB,CAAC;AACxD,eAAO,MAAM,cAAc,oBAAoB,CAAC;AAChD,eAAO,MAAM,4BAA4B,kCAAkC,CAAC;AAC5E,eAAO,MAAM,iBAAiB,uBAAuB,CAAC;AACtD,eAAO,MAAM,qBAAqB,2BAA2B,CAAC;AAE9D,MAAM,MAAM,gBAAgB,GAAG,MAAM,CAAC;AACtC,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC;AAEhC,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,SAAS,gBAAgB,EAAE,CAAC;IACpC,QAAQ,EAAE,SAAS,gBAAgB,EAAE,CAAC;IACtC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,UAAU,CAAC;CACvB;AAED,MAAM,WAAW,MAAM;IACrB,QAAQ,EAAE,SAAS,aAAa,EAAE,CAAC;IACnC,MAAM,EAAE,SAAS,gBAAgB,EAAE,CAAC;IACpC,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAC5C,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,EAAE,SAAS,OAAO,EAAE,CAAC;IAC7B,MAAM,CAAC,EAAE,SAAS,gBAAgB,EAAE,CAAC;IACrC,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAC5C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,qBAAqB;IACpC,OAAO,CAAC,KAAK,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC;QAAE,QAAQ,EAAE,SAAS,aAAa,EAAE,CAAA;KAAE,CAAC,CAAC;IACvF,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,wBAAwB;IACvC,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,uBAAuB;IACtC,MAAM,CAAC,KAAK,EAAE;QAAE,QAAQ,CAAC,EAAE,SAAS,aAAa,EAAE,CAAA;KAAE,GAAG,IAAI,CAAC;CAC9D;AAED,MAAM,WAAW,oBAAoB;IACnC,EAAE,CAAC,CAAC,SAAS,MAAM,uBAAuB,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,uBAAuB,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI,CAAC;IACxG,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,uBAAuB;IACtC,WAAW,CACT,GAAG,MAAM,EAAE,SAAS;QAAE,OAAO,EAAE,aAAa,CAAC;QAAC,OAAO,EAAE,UAAU,CAAA;KAAE,EAAE,GACpE,OAAO,CACR,SAAS;QACP,SAAS,EAAE,MAAM,GAAG,UAAU,CAAC;QAC/B,aAAa,CAAC,EAAE,SAAS,GAAG,MAAM,CAAC;QACnC,aAAa,CAAC,EAAE,MAAM,GAAG,UAAU,CAAC;KACrC,EAAE,CACJ,CAAC;IACF,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,2BAA2B;IAC1C,eAAe,CACb,GAAG,MAAM,EAAE,SAAS;QAClB,OAAO,EAAE,aAAa,CAAC;QACvB,KAAK,EAAE,gBAAgB,CAAC;QACxB,OAAO,CAAC,EAAE;YACR,cAAc,CAAC,EAAE,MAAM,CAAC;YACxB,mBAAmB,CAAC,EAAE,MAAM,CAAC;SAC9B,CAAC;QACF,WAAW,EAAE,UAAU,CAAC;KACzB,EAAE,GACF,OAAO,CAAC,SAAS;QAAE,iBAAiB,EAAE,UAAU,CAAA;KAAE,EAAE,CAAC,CAAC;IACzD,4BAA4B,CAAC,EAAE,SAAS,CAAC,QAAQ,GAAG,MAAM,CAAC,EAAE,CAAC;IAC9D,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,kCAAkC;IACjD,sBAAsB,CAAC,KAAK,EAAE;QAC5B,OAAO,EAAE,aAAa,CAAC;QACvB,KAAK,EAAE,gBAAgB,CAAC;QACxB,OAAO,CAAC,EAAE;YACR,UAAU,CAAC,EAAE,MAAM,CAAC;YACpB,cAAc,CAAC,EAAE,MAAM,CAAC;YACxB,mBAAmB,CAAC,EAAE,MAAM,CAAC;YAC7B,aAAa,CAAC,EAAE,OAAO,CAAC;SACzB,CAAC;QACF,WAAW,EAAE,UAAU,CAAC;KACzB,GAAG,OAAO,CAAC;QAAE,SAAS,EAAE,MAAM,GAAG,UAAU,CAAA;KAAE,GAAG,SAAS;QAAE,SAAS,EAAE,MAAM,GAAG,UAAU,CAAA;KAAE,EAAE,CAAC,CAAC;IAChG,4BAA4B,CAAC,EAAE,SAAS,CAAC,QAAQ,GAAG,MAAM,CAAC,EAAE,CAAC;IAC9D,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,sBAAsB;IACrC,CAAC,eAAe,CAAC,EAAE,qBAAqB,CAAC;CAC1C;AAED,MAAM,WAAW,yBAAyB;IACxC,CAAC,kBAAkB,CAAC,EAAE,wBAAwB,CAAC;CAChD;AAED,MAAM,WAAW,qBAAqB;IACpC,CAAC,cAAc,CAAC,EAAE,oBAAoB,CAAC;CACxC;AAED,MAAM,WAAW,wBAAwB;IACvC,CAAC,iBAAiB,CAAC,EAAE,uBAAuB,CAAC;CAC9C;AAED,MAAM,WAAW,4BAA4B;IAC3C,CAAC,qBAAqB,CAAC,EAAE,2BAA2B,CAAC;CACtD;AAED,MAAM,WAAW,mCAAmC;IAClD,CAAC,4BAA4B,CAAC,EAAE,kCAAkC,CAAC;CACpE;AAKD,MAAM,WAAW,cAAc,CAAC,OAAO,SAAS,sBAAsB,GAAG,sBAAsB;IAC7F,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,OAAO,CAAC;CACjB;AAGD,wBAAgB,cAAc,CAAC,OAAO,SAAS,sBAAsB,EAAE,MAAM,EAAE,OAAO,GAAG,OAAO,CAgB/F;AAED,wBAAgB,WAAW,CAAC,OAAO,SAAS,sBAAsB,EAChE,MAAM,EAAE,OAAO,GACd,cAAc,CAAC,OAAO,CAAC,GAAG,IAAI,CAUhC;AAED,wBAAgB,qBAAqB,CAAC,OAAO,SAAS,sBAAsB,EAC1E,OAAO,EAAE,SAAS,cAAc,CAAC,OAAO,CAAC,EAAE,EAC3C,cAAc,GAAE,SAAS,MAAM,EAAO,GACrC,cAAc,CAAC,OAAO,CAAC,EAAE,CAwB3B;AAED,wBAAgB,oBAAoB,CAAC,OAAO,SAAS,sBAAsB,EACzE,OAAO,EAAE,SAAS,OAAO,EAAE,EAC3B,cAAc,GAAE,SAAS,MAAM,EAAO,GACrC,cAAc,CAAC,OAAO,CAAC,EAAE,CAM3B"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
export const StandardConnect = 'standard:connect';
|
|
2
|
+
export const StandardDisconnect = 'standard:disconnect';
|
|
3
|
+
export const StandardEvents = 'standard:events';
|
|
4
|
+
export const SolanaSignAndSendTransaction = 'solana:signAndSendTransaction';
|
|
5
|
+
export const SolanaSignMessage = 'solana:signMessage';
|
|
6
|
+
export const SolanaSignTransaction = 'solana:signTransaction';
|
|
7
|
+
const FALLBACK_ICON = 'data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2248%22%20height%3D%2248%22%20viewBox%3D%220%200%2048%2048%22%20fill%3D%22none%22%3E%3Crect%20width%3D%2248%22%20height%3D%2248%22%20rx%3D%2212%22%20fill%3D%22%23111827%22%2F%3E%3Cpath%20d%3D%22M16%2018h16v-2H16v2Zm0%207h16v-2H16v2Zm0%207h16v-2H16v2Z%22%20fill%3D%22white%22%2F%3E%3C%2Fsvg%3E';
|
|
8
|
+
export function isSolanaWallet(wallet) {
|
|
9
|
+
const { features, chains } = wallet;
|
|
10
|
+
if (!features) {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
const hasStandardFeatures = StandardConnect in features && StandardDisconnect in features && StandardEvents in features;
|
|
14
|
+
const hasSolanaSigning = SolanaSignMessage in features ||
|
|
15
|
+
SolanaSignTransaction in features ||
|
|
16
|
+
SolanaSignAndSendTransaction in features;
|
|
17
|
+
const supportsSolanaChain = chains?.some((chain) => chain.startsWith('solana:'));
|
|
18
|
+
return Boolean(hasStandardFeatures && hasSolanaSigning && supportsSolanaChain);
|
|
19
|
+
}
|
|
20
|
+
export function adaptWallet(wallet) {
|
|
21
|
+
if (!isSolanaWallet(wallet)) {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
return {
|
|
25
|
+
name: wallet.name,
|
|
26
|
+
icon: wallet.icon ?? FALLBACK_ICON,
|
|
27
|
+
wallet
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
export function dedupeStandardWallets(wallets, preferredNames = []) {
|
|
31
|
+
const walletsByName = new Map();
|
|
32
|
+
for (const wallet of wallets) {
|
|
33
|
+
const existing = walletsByName.get(wallet.name);
|
|
34
|
+
if (!existing) {
|
|
35
|
+
walletsByName.set(wallet.name, wallet);
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
const existingHasSignAndSend = SolanaSignAndSendTransaction in existing.wallet.features;
|
|
39
|
+
const newHasSignAndSend = SolanaSignAndSendTransaction in wallet.wallet.features;
|
|
40
|
+
if (newHasSignAndSend && !existingHasSignAndSend) {
|
|
41
|
+
walletsByName.set(wallet.name, wallet);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
const deduped = Array.from(walletsByName.values());
|
|
45
|
+
const preferred = deduped.filter((wallet) => preferredNames.includes(wallet.name));
|
|
46
|
+
const otherWallets = deduped.filter((wallet) => !preferredNames.includes(wallet.name));
|
|
47
|
+
return [...preferred, ...otherWallets];
|
|
48
|
+
}
|
|
49
|
+
export function adaptStandardWallets(wallets, preferredNames = []) {
|
|
50
|
+
const adapted = wallets
|
|
51
|
+
.map(adaptWallet)
|
|
52
|
+
.filter((wallet) => wallet !== null);
|
|
53
|
+
return dedupeStandardWallets(adapted, preferredNames);
|
|
54
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@fernolab/wallet-adapter-core",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Framework-agnostic Solana Wallet Standard adapter core.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"sideEffects": false,
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"files": [
|
|
10
|
+
"dist"
|
|
11
|
+
],
|
|
12
|
+
"publishConfig": {
|
|
13
|
+
"access": "public"
|
|
14
|
+
},
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "git+https://github.com/FernoLabs/ferno-wallet.git",
|
|
18
|
+
"directory": "ferno-wallet-adapter-core"
|
|
19
|
+
},
|
|
20
|
+
"bugs": {
|
|
21
|
+
"url": "https://github.com/FernoLabs/ferno-wallet/issues"
|
|
22
|
+
},
|
|
23
|
+
"homepage": "https://github.com/FernoLabs/ferno-wallet#readme",
|
|
24
|
+
"keywords": [
|
|
25
|
+
"solana",
|
|
26
|
+
"wallet-standard",
|
|
27
|
+
"wallet-adapter",
|
|
28
|
+
"ferno"
|
|
29
|
+
],
|
|
30
|
+
"scripts": {
|
|
31
|
+
"build": "tsc -p tsconfig.json",
|
|
32
|
+
"check": "tsc --noEmit",
|
|
33
|
+
"prepack": "npm run build",
|
|
34
|
+
"test": "vitest run"
|
|
35
|
+
},
|
|
36
|
+
"exports": {
|
|
37
|
+
".": {
|
|
38
|
+
"types": "./dist/index.d.ts",
|
|
39
|
+
"import": "./dist/index.js"
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"typescript": "^6.0.3",
|
|
44
|
+
"vitest": "^4.1.7"
|
|
45
|
+
}
|
|
46
|
+
}
|