@fernolab/wallet-adapter-core 0.1.0 → 0.1.1
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 +14 -2
- package/dist/controller.d.ts.map +1 -1
- package/dist/controller.js +31 -4
- package/dist/crypto-vault.d.ts +90 -0
- package/dist/crypto-vault.d.ts.map +1 -0
- package/dist/crypto-vault.js +210 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/solana.d.ts +26 -0
- package/dist/solana.d.ts.map +1 -0
- package/dist/solana.js +172 -0
- package/dist/wallet-standard.d.ts +14 -2
- package/dist/wallet-standard.d.ts.map +1 -1
- package/dist/wallet-standard.js +41 -4
- package/package.json +5 -2
package/dist/controller.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type IdentifierString, type StandardWallet, type WalletAccount } from './wallet-standard';
|
|
1
|
+
import { type IdentifierString, type StandardWallet, type WalletAccount, type WalletStorage } from './wallet-standard';
|
|
2
2
|
export type WalletConnectionStatus = 'disconnected' | 'connecting' | 'connected';
|
|
3
3
|
export type WalletAddress = string;
|
|
4
4
|
export interface WalletControllerState<TWallet extends StandardWallet = StandardWallet> {
|
|
@@ -6,6 +6,7 @@ export interface WalletControllerState<TWallet extends StandardWallet = Standard
|
|
|
6
6
|
connected: boolean;
|
|
7
7
|
connecting: boolean;
|
|
8
8
|
error: Error | null;
|
|
9
|
+
lastUsedWalletName: string | null;
|
|
9
10
|
publicKey: WalletAddress | null;
|
|
10
11
|
shortAddress: string;
|
|
11
12
|
signing: boolean;
|
|
@@ -19,21 +20,31 @@ export interface WalletSignMessageResult {
|
|
|
19
20
|
signedMessage: string | Uint8Array;
|
|
20
21
|
}
|
|
21
22
|
export type WalletControllerListener<TWallet extends StandardWallet = StandardWallet> = (state: WalletControllerState<TWallet>) => void;
|
|
23
|
+
export interface WalletControllerOptions {
|
|
24
|
+
lastUsedWalletName?: string | null;
|
|
25
|
+
storage?: WalletStorage | null;
|
|
26
|
+
storageKey?: string;
|
|
27
|
+
}
|
|
22
28
|
export declare function resolveSolanaChainFromEndpoint(endpoint: string): IdentifierString;
|
|
23
29
|
export declare class WalletController<TWallet extends StandardWallet = StandardWallet> {
|
|
24
30
|
private availableWalletsValue;
|
|
25
31
|
private connectedValue;
|
|
26
32
|
private connectingValue;
|
|
27
33
|
private errorValue;
|
|
34
|
+
private lastUsedWalletNameValue;
|
|
28
35
|
private listeners;
|
|
29
36
|
private publicKeyValue;
|
|
30
37
|
private signingValue;
|
|
31
38
|
private standardAccountValue;
|
|
32
39
|
private standardEventsOff;
|
|
33
40
|
private standardWalletValue;
|
|
41
|
+
private storageValue;
|
|
42
|
+
private storageKeyValue;
|
|
43
|
+
constructor(options?: WalletControllerOptions);
|
|
34
44
|
get state(): WalletControllerState<TWallet>;
|
|
35
45
|
subscribe(listener: WalletControllerListener<TWallet>): () => void;
|
|
36
46
|
setAvailableWallets(wallets: readonly TWallet[]): void;
|
|
47
|
+
setLastUsedWalletName(walletName: string | null): void;
|
|
37
48
|
clearError(): void;
|
|
38
49
|
connectStandard(wallet: TWallet): Promise<void>;
|
|
39
50
|
disconnect(): Promise<void>;
|
|
@@ -41,8 +52,9 @@ export declare class WalletController<TWallet extends StandardWallet = StandardW
|
|
|
41
52
|
private applyStandardAccount;
|
|
42
53
|
private watchStandardWallet;
|
|
43
54
|
private clearStandardEventsListener;
|
|
55
|
+
private setLastUsedWalletNameValue;
|
|
44
56
|
private setError;
|
|
45
57
|
private emit;
|
|
46
58
|
}
|
|
47
|
-
export declare function createWalletController<TWallet extends StandardWallet = StandardWallet>(): WalletController<TWallet>;
|
|
59
|
+
export declare function createWalletController<TWallet extends StandardWallet = StandardWallet>(options?: WalletControllerOptions): WalletController<TWallet>;
|
|
48
60
|
//# sourceMappingURL=controller.d.ts.map
|
package/dist/controller.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["../src/controller.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["../src/controller.ts"],"names":[],"mappings":"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"}
|
package/dist/controller.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { SolanaSignMessage, StandardConnect, StandardDisconnect, StandardEvents } from './wallet-standard';
|
|
1
|
+
import { SolanaSignMessage, StandardConnect, StandardDisconnect, StandardEvents, LAST_USED_WALLET_STORAGE_KEY, clearLastUsedWalletName, readLastUsedWalletName, saveLastUsedWalletName, sortWalletsByLastUsed } from './wallet-standard';
|
|
2
2
|
function getWalletFeature(wallet, featureName) {
|
|
3
3
|
const features = wallet.features;
|
|
4
4
|
return features[featureName] ?? null;
|
|
@@ -38,18 +38,30 @@ export class WalletController {
|
|
|
38
38
|
connectedValue = false;
|
|
39
39
|
connectingValue = false;
|
|
40
40
|
errorValue = null;
|
|
41
|
+
lastUsedWalletNameValue;
|
|
41
42
|
listeners = new Set();
|
|
42
43
|
publicKeyValue = null;
|
|
43
44
|
signingValue = false;
|
|
44
45
|
standardAccountValue = null;
|
|
45
46
|
standardEventsOff = null;
|
|
46
47
|
standardWalletValue = null;
|
|
48
|
+
storageValue;
|
|
49
|
+
storageKeyValue;
|
|
50
|
+
constructor(options = {}) {
|
|
51
|
+
this.storageValue = options.storage ?? null;
|
|
52
|
+
this.storageKeyValue = options.storageKey ?? LAST_USED_WALLET_STORAGE_KEY;
|
|
53
|
+
this.lastUsedWalletNameValue =
|
|
54
|
+
'lastUsedWalletName' in options
|
|
55
|
+
? options.lastUsedWalletName ?? null
|
|
56
|
+
: readLastUsedWalletName(this.storageValue, this.storageKeyValue);
|
|
57
|
+
}
|
|
47
58
|
get state() {
|
|
48
59
|
return {
|
|
49
60
|
availableWallets: this.availableWalletsValue,
|
|
50
61
|
connected: this.connectedValue,
|
|
51
62
|
connecting: this.connectingValue,
|
|
52
63
|
error: this.errorValue,
|
|
64
|
+
lastUsedWalletName: this.lastUsedWalletNameValue,
|
|
53
65
|
publicKey: this.publicKeyValue,
|
|
54
66
|
shortAddress: formatShortAddress(this.publicKeyValue),
|
|
55
67
|
signing: this.signingValue,
|
|
@@ -64,7 +76,11 @@ export class WalletController {
|
|
|
64
76
|
return () => this.listeners.delete(listener);
|
|
65
77
|
}
|
|
66
78
|
setAvailableWallets(wallets) {
|
|
67
|
-
this.availableWalletsValue = wallets;
|
|
79
|
+
this.availableWalletsValue = sortWalletsByLastUsed(wallets, this.lastUsedWalletNameValue);
|
|
80
|
+
this.emit();
|
|
81
|
+
}
|
|
82
|
+
setLastUsedWalletName(walletName) {
|
|
83
|
+
this.setLastUsedWalletNameValue(walletName);
|
|
68
84
|
this.emit();
|
|
69
85
|
}
|
|
70
86
|
clearError() {
|
|
@@ -84,6 +100,7 @@ export class WalletController {
|
|
|
84
100
|
if (!isWalletAccount(account)) {
|
|
85
101
|
throw new Error('Wallet did not return an account');
|
|
86
102
|
}
|
|
103
|
+
this.setLastUsedWalletNameValue(wallet.name);
|
|
87
104
|
this.applyStandardAccount(wallet, account);
|
|
88
105
|
this.watchStandardWallet(wallet);
|
|
89
106
|
}
|
|
@@ -183,6 +200,16 @@ export class WalletController {
|
|
|
183
200
|
this.standardEventsOff?.();
|
|
184
201
|
this.standardEventsOff = null;
|
|
185
202
|
}
|
|
203
|
+
setLastUsedWalletNameValue(walletName) {
|
|
204
|
+
this.lastUsedWalletNameValue = walletName;
|
|
205
|
+
this.availableWalletsValue = sortWalletsByLastUsed(this.availableWalletsValue, this.lastUsedWalletNameValue);
|
|
206
|
+
if (walletName) {
|
|
207
|
+
saveLastUsedWalletName(this.storageValue, walletName, this.storageKeyValue);
|
|
208
|
+
}
|
|
209
|
+
else {
|
|
210
|
+
clearLastUsedWalletName(this.storageValue, this.storageKeyValue);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
186
213
|
setError(error) {
|
|
187
214
|
this.errorValue = error;
|
|
188
215
|
this.emit();
|
|
@@ -192,6 +219,6 @@ export class WalletController {
|
|
|
192
219
|
this.listeners.forEach((listener) => listener(state));
|
|
193
220
|
}
|
|
194
221
|
}
|
|
195
|
-
export function createWalletController() {
|
|
196
|
-
return new WalletController();
|
|
222
|
+
export function createWalletController(options = {}) {
|
|
223
|
+
return new WalletController(options);
|
|
197
224
|
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
export declare const FERNO_HOT_WALLET_VAULT_VERSION = 1;
|
|
2
|
+
export interface WalletWrappingKeyInput {
|
|
3
|
+
chainId: string;
|
|
4
|
+
domain: string;
|
|
5
|
+
purpose?: string;
|
|
6
|
+
walletAddress: string;
|
|
7
|
+
walletSignature: Uint8Array;
|
|
8
|
+
}
|
|
9
|
+
export interface WrappedHotWallet {
|
|
10
|
+
algorithm: 'Ed25519';
|
|
11
|
+
createdAt: string;
|
|
12
|
+
iv: Uint8Array;
|
|
13
|
+
publicKey: Uint8Array;
|
|
14
|
+
salt: Uint8Array;
|
|
15
|
+
version: typeof FERNO_HOT_WALLET_VAULT_VERSION;
|
|
16
|
+
wrappedPrivateKey: Uint8Array;
|
|
17
|
+
}
|
|
18
|
+
export interface UnlockedHotWallet {
|
|
19
|
+
algorithm: 'Ed25519';
|
|
20
|
+
privateKey: CryptoKey;
|
|
21
|
+
publicKey: Uint8Array;
|
|
22
|
+
}
|
|
23
|
+
export declare function getHotWalletAddress(wallet: Pick<WrappedHotWallet | UnlockedHotWallet, 'publicKey'>): string;
|
|
24
|
+
export declare function getHotWalletStorageKey({ chainId, ownerAddress, storagePrefix }: {
|
|
25
|
+
chainId: string;
|
|
26
|
+
ownerAddress: string;
|
|
27
|
+
storagePrefix?: string;
|
|
28
|
+
}): string;
|
|
29
|
+
export declare function serializeWrappedHotWallet(wallet: WrappedHotWallet): string;
|
|
30
|
+
export declare function deserializeWrappedHotWallet(value: string): WrappedHotWallet;
|
|
31
|
+
export declare function readWrappedHotWallet({ chainId, ownerAddress, storage, storagePrefix }: {
|
|
32
|
+
chainId: string;
|
|
33
|
+
ownerAddress: string;
|
|
34
|
+
storage: Pick<Storage, 'getItem'> | null | undefined;
|
|
35
|
+
storagePrefix?: string;
|
|
36
|
+
}): WrappedHotWallet | null;
|
|
37
|
+
export declare function saveWrappedHotWallet({ chainId, ownerAddress, storage, storagePrefix, wallet }: {
|
|
38
|
+
chainId: string;
|
|
39
|
+
ownerAddress: string;
|
|
40
|
+
storage: Pick<Storage, 'setItem'> | null | undefined;
|
|
41
|
+
storagePrefix?: string;
|
|
42
|
+
wallet: WrappedHotWallet;
|
|
43
|
+
}): void;
|
|
44
|
+
export declare function createHotWalletUnlockMessage({ chainId, domain, uri, walletAddress }: {
|
|
45
|
+
chainId: string;
|
|
46
|
+
domain: string;
|
|
47
|
+
uri: string;
|
|
48
|
+
walletAddress: string;
|
|
49
|
+
}): string;
|
|
50
|
+
export declare function deriveHotWalletWrappingKey({ chainId, crypto, domain, purpose, walletAddress, walletSignature }: WalletWrappingKeyInput & {
|
|
51
|
+
crypto?: Crypto;
|
|
52
|
+
}): Promise<{
|
|
53
|
+
key: CryptoKey;
|
|
54
|
+
salt: Uint8Array;
|
|
55
|
+
}>;
|
|
56
|
+
export declare function deriveHotWalletSeed({ chainId, crypto, domain, purpose, walletAddress, walletSignature }: WalletWrappingKeyInput & {
|
|
57
|
+
crypto?: Crypto;
|
|
58
|
+
}): Promise<{
|
|
59
|
+
salt: Uint8Array;
|
|
60
|
+
seed: Uint8Array;
|
|
61
|
+
}>;
|
|
62
|
+
export declare function createWrappedHotWallet({ crypto, seed, wrappingKey }: {
|
|
63
|
+
crypto?: Crypto;
|
|
64
|
+
seed?: Uint8Array;
|
|
65
|
+
wrappingKey: CryptoKey;
|
|
66
|
+
}): Promise<WrappedHotWallet>;
|
|
67
|
+
export declare function createWrappedHotWalletFromSignature(input: WalletWrappingKeyInput & {
|
|
68
|
+
crypto?: Crypto;
|
|
69
|
+
}): Promise<WrappedHotWallet>;
|
|
70
|
+
export declare function unlockHotWallet({ crypto, wrappedWallet, wrappingKey }: {
|
|
71
|
+
crypto?: Crypto;
|
|
72
|
+
wrappedWallet: WrappedHotWallet;
|
|
73
|
+
wrappingKey: CryptoKey;
|
|
74
|
+
}): Promise<UnlockedHotWallet>;
|
|
75
|
+
export declare function unlockHotWalletFromSignature(input: WalletWrappingKeyInput & {
|
|
76
|
+
crypto?: Crypto;
|
|
77
|
+
wrappedWallet: WrappedHotWallet;
|
|
78
|
+
}): Promise<UnlockedHotWallet>;
|
|
79
|
+
export declare function signWithHotWallet({ crypto, message, privateKey }: {
|
|
80
|
+
crypto?: Crypto;
|
|
81
|
+
message: Uint8Array;
|
|
82
|
+
privateKey: CryptoKey;
|
|
83
|
+
}): Promise<Uint8Array>;
|
|
84
|
+
export declare function verifyHotWalletSignature({ crypto, message, publicKey, signature }: {
|
|
85
|
+
crypto?: Crypto;
|
|
86
|
+
message: Uint8Array;
|
|
87
|
+
publicKey: Uint8Array;
|
|
88
|
+
signature: Uint8Array;
|
|
89
|
+
}): Promise<boolean>;
|
|
90
|
+
//# sourceMappingURL=crypto-vault.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crypto-vault.d.ts","sourceRoot":"","sources":["../src/crypto-vault.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import { ed25519 } from '@noble/curves/ed25519.js';
|
|
2
|
+
import { base58Encode } from './solana';
|
|
3
|
+
export const FERNO_HOT_WALLET_VAULT_VERSION = 1;
|
|
4
|
+
const DEFAULT_PURPOSE = 'ferno hot wallet seed encryption';
|
|
5
|
+
const HOT_WALLET_SEED_INFO = 'ferno deterministic hot wallet seed v1';
|
|
6
|
+
const WRAPPING_KEY_INFO = 'ferno hot wallet wrapping key v1';
|
|
7
|
+
const SALT_PREFIX = 'ferno:v1:hot-wallet-vault';
|
|
8
|
+
const ED25519_PKCS8_SEED_PREFIX = new Uint8Array([
|
|
9
|
+
0x30, 0x2e, 0x02, 0x01, 0x00, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x70, 0x04, 0x22,
|
|
10
|
+
0x04, 0x20
|
|
11
|
+
]);
|
|
12
|
+
export function getHotWalletAddress(wallet) {
|
|
13
|
+
return base58Encode(wallet.publicKey);
|
|
14
|
+
}
|
|
15
|
+
export function getHotWalletStorageKey({ chainId, ownerAddress, storagePrefix = 'ferno:hot-wallet:' }) {
|
|
16
|
+
return `${storagePrefix}${chainId}:${ownerAddress}`;
|
|
17
|
+
}
|
|
18
|
+
export function serializeWrappedHotWallet(wallet) {
|
|
19
|
+
return JSON.stringify({
|
|
20
|
+
algorithm: wallet.algorithm,
|
|
21
|
+
createdAt: wallet.createdAt,
|
|
22
|
+
iv: bytesToBase64(wallet.iv),
|
|
23
|
+
publicKey: bytesToBase64(wallet.publicKey),
|
|
24
|
+
salt: bytesToBase64(wallet.salt),
|
|
25
|
+
version: wallet.version,
|
|
26
|
+
wrappedPrivateKey: bytesToBase64(wallet.wrappedPrivateKey)
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
export function deserializeWrappedHotWallet(value) {
|
|
30
|
+
const parsed = JSON.parse(value);
|
|
31
|
+
return {
|
|
32
|
+
algorithm: parsed.algorithm,
|
|
33
|
+
createdAt: parsed.createdAt,
|
|
34
|
+
iv: base64ToBytes(parsed.iv),
|
|
35
|
+
publicKey: base64ToBytes(parsed.publicKey),
|
|
36
|
+
salt: base64ToBytes(parsed.salt),
|
|
37
|
+
version: parsed.version,
|
|
38
|
+
wrappedPrivateKey: base64ToBytes(parsed.wrappedPrivateKey)
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
export function readWrappedHotWallet({ chainId, ownerAddress, storage, storagePrefix }) {
|
|
42
|
+
const stored = storage?.getItem(getHotWalletStorageKey({ chainId, ownerAddress, storagePrefix }));
|
|
43
|
+
return stored ? deserializeWrappedHotWallet(stored) : null;
|
|
44
|
+
}
|
|
45
|
+
export function saveWrappedHotWallet({ chainId, ownerAddress, storage, storagePrefix, wallet }) {
|
|
46
|
+
storage?.setItem(getHotWalletStorageKey({ chainId, ownerAddress, storagePrefix }), serializeWrappedHotWallet(wallet));
|
|
47
|
+
}
|
|
48
|
+
export function createHotWalletUnlockMessage({ chainId, domain, uri, walletAddress }) {
|
|
49
|
+
return `${domain} wants you to unlock your Ferno hot wallet encryption key.
|
|
50
|
+
|
|
51
|
+
This signature does not authorize a transaction.
|
|
52
|
+
Wallet: ${walletAddress}
|
|
53
|
+
Purpose: ${DEFAULT_PURPOSE}
|
|
54
|
+
Version: ${FERNO_HOT_WALLET_VAULT_VERSION}
|
|
55
|
+
URI: ${uri}
|
|
56
|
+
Chain ID: ${chainId}
|
|
57
|
+
Resources:
|
|
58
|
+
- urn:ferno:hot-wallet-seed:v${FERNO_HOT_WALLET_VAULT_VERSION}`;
|
|
59
|
+
}
|
|
60
|
+
export async function deriveHotWalletWrappingKey({ chainId, crypto = globalThis.crypto, domain, purpose = DEFAULT_PURPOSE, walletAddress, walletSignature }) {
|
|
61
|
+
assertWebCrypto(crypto);
|
|
62
|
+
const signatureCopy = new Uint8Array(walletSignature);
|
|
63
|
+
const salt = await createWrappingSalt({ chainId, crypto, domain, purpose, walletAddress });
|
|
64
|
+
const keyMaterial = await crypto.subtle.importKey('raw', toArrayBuffer(signatureCopy), 'HKDF', false, ['deriveKey']);
|
|
65
|
+
signatureCopy.fill(0);
|
|
66
|
+
const key = await crypto.subtle.deriveKey({
|
|
67
|
+
name: 'HKDF',
|
|
68
|
+
hash: 'SHA-256',
|
|
69
|
+
salt: toArrayBuffer(salt),
|
|
70
|
+
info: toArrayBuffer(encodeUtf8(WRAPPING_KEY_INFO))
|
|
71
|
+
}, keyMaterial, { name: 'AES-GCM', length: 256 }, false, ['wrapKey', 'unwrapKey']);
|
|
72
|
+
return { key, salt };
|
|
73
|
+
}
|
|
74
|
+
export async function deriveHotWalletSeed({ chainId, crypto = globalThis.crypto, domain, purpose = DEFAULT_PURPOSE, walletAddress, walletSignature }) {
|
|
75
|
+
assertWebCrypto(crypto);
|
|
76
|
+
const signatureCopy = new Uint8Array(walletSignature);
|
|
77
|
+
const salt = await createWrappingSalt({ chainId, crypto, domain, purpose, walletAddress });
|
|
78
|
+
const keyMaterial = await crypto.subtle.importKey('raw', toArrayBuffer(signatureCopy), 'HKDF', false, ['deriveBits']);
|
|
79
|
+
signatureCopy.fill(0);
|
|
80
|
+
const seed = new Uint8Array(await crypto.subtle.deriveBits({
|
|
81
|
+
name: 'HKDF',
|
|
82
|
+
hash: 'SHA-256',
|
|
83
|
+
salt: toArrayBuffer(salt),
|
|
84
|
+
info: toArrayBuffer(encodeUtf8(HOT_WALLET_SEED_INFO))
|
|
85
|
+
}, keyMaterial, 256));
|
|
86
|
+
return { salt, seed };
|
|
87
|
+
}
|
|
88
|
+
export async function createWrappedHotWallet({ crypto = globalThis.crypto, seed, wrappingKey }) {
|
|
89
|
+
assertWebCrypto(crypto);
|
|
90
|
+
const keyPair = seed
|
|
91
|
+
? await importEd25519SeedKeyPair({ crypto, seed })
|
|
92
|
+
: await crypto.subtle.generateKey({ name: 'Ed25519' }, true, ['sign', 'verify']);
|
|
93
|
+
const iv = crypto.getRandomValues(new Uint8Array(12));
|
|
94
|
+
const publicKey = new Uint8Array(await crypto.subtle.exportKey('raw', keyPair.publicKey));
|
|
95
|
+
const wrappedPrivateKey = new Uint8Array(await crypto.subtle.wrapKey('pkcs8', keyPair.privateKey, wrappingKey, {
|
|
96
|
+
name: 'AES-GCM',
|
|
97
|
+
iv: toArrayBuffer(iv)
|
|
98
|
+
}));
|
|
99
|
+
return {
|
|
100
|
+
algorithm: 'Ed25519',
|
|
101
|
+
createdAt: new Date().toISOString(),
|
|
102
|
+
iv,
|
|
103
|
+
publicKey,
|
|
104
|
+
salt: new Uint8Array(),
|
|
105
|
+
version: FERNO_HOT_WALLET_VAULT_VERSION,
|
|
106
|
+
wrappedPrivateKey
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
export async function createWrappedHotWalletFromSignature(input) {
|
|
110
|
+
const crypto = input.crypto ?? globalThis.crypto;
|
|
111
|
+
const { key, salt } = await deriveHotWalletWrappingKey({ ...input, crypto });
|
|
112
|
+
const { seed } = await deriveHotWalletSeed({ ...input, crypto });
|
|
113
|
+
try {
|
|
114
|
+
const wrappedWallet = await createWrappedHotWallet({ crypto, seed, wrappingKey: key });
|
|
115
|
+
return {
|
|
116
|
+
...wrappedWallet,
|
|
117
|
+
salt
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
finally {
|
|
121
|
+
seed.fill(0);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
export async function unlockHotWallet({ crypto = globalThis.crypto, wrappedWallet, wrappingKey }) {
|
|
125
|
+
assertWebCrypto(crypto);
|
|
126
|
+
assertSupportedWrappedWallet(wrappedWallet);
|
|
127
|
+
const privateKey = await crypto.subtle.unwrapKey('pkcs8', toArrayBuffer(wrappedWallet.wrappedPrivateKey), wrappingKey, { name: 'AES-GCM', iv: toArrayBuffer(wrappedWallet.iv) }, { name: 'Ed25519' }, false, ['sign']);
|
|
128
|
+
return {
|
|
129
|
+
algorithm: 'Ed25519',
|
|
130
|
+
privateKey,
|
|
131
|
+
publicKey: wrappedWallet.publicKey
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
export async function unlockHotWalletFromSignature(input) {
|
|
135
|
+
const crypto = input.crypto ?? globalThis.crypto;
|
|
136
|
+
const { key } = await deriveHotWalletWrappingKey({ ...input, crypto });
|
|
137
|
+
return await unlockHotWallet({ crypto, wrappedWallet: input.wrappedWallet, wrappingKey: key });
|
|
138
|
+
}
|
|
139
|
+
export async function signWithHotWallet({ crypto = globalThis.crypto, message, privateKey }) {
|
|
140
|
+
assertWebCrypto(crypto);
|
|
141
|
+
return new Uint8Array(await crypto.subtle.sign({ name: 'Ed25519' }, privateKey, toArrayBuffer(message)));
|
|
142
|
+
}
|
|
143
|
+
export async function verifyHotWalletSignature({ crypto = globalThis.crypto, message, publicKey, signature }) {
|
|
144
|
+
assertWebCrypto(crypto);
|
|
145
|
+
const key = await crypto.subtle.importKey('raw', toArrayBuffer(publicKey), { name: 'Ed25519' }, false, ['verify']);
|
|
146
|
+
return await crypto.subtle.verify({ name: 'Ed25519' }, key, toArrayBuffer(signature), toArrayBuffer(message));
|
|
147
|
+
}
|
|
148
|
+
async function createWrappingSalt({ chainId, crypto, domain, purpose, walletAddress }) {
|
|
149
|
+
const saltInput = [SALT_PREFIX, domain, chainId, walletAddress, purpose].join('\n');
|
|
150
|
+
return new Uint8Array(await crypto.subtle.digest('SHA-256', toArrayBuffer(encodeUtf8(saltInput))));
|
|
151
|
+
}
|
|
152
|
+
function assertSupportedWrappedWallet(wrappedWallet) {
|
|
153
|
+
if (wrappedWallet.version !== FERNO_HOT_WALLET_VAULT_VERSION ||
|
|
154
|
+
wrappedWallet.algorithm !== 'Ed25519') {
|
|
155
|
+
throw new Error('Unsupported Ferno hot wallet vault');
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
function assertWebCrypto(crypto) {
|
|
159
|
+
if (!crypto?.subtle || !crypto.getRandomValues) {
|
|
160
|
+
throw new Error('WebCrypto is not available');
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
function encodeUtf8(value) {
|
|
164
|
+
return new TextEncoder().encode(value);
|
|
165
|
+
}
|
|
166
|
+
function bytesToBase64(bytes) {
|
|
167
|
+
let binary = '';
|
|
168
|
+
for (const byte of bytes) {
|
|
169
|
+
binary += String.fromCharCode(byte);
|
|
170
|
+
}
|
|
171
|
+
return btoa(binary);
|
|
172
|
+
}
|
|
173
|
+
function base64ToBytes(value) {
|
|
174
|
+
return Uint8Array.from(atob(value), (char) => char.charCodeAt(0));
|
|
175
|
+
}
|
|
176
|
+
async function importEd25519SeedKeyPair({ crypto, seed }) {
|
|
177
|
+
if (seed.byteLength !== 32) {
|
|
178
|
+
throw new Error('Ed25519 hot wallet seeds must be 32 bytes');
|
|
179
|
+
}
|
|
180
|
+
const seedCopy = new Uint8Array(seed);
|
|
181
|
+
const privateKeyBytes = concatBytes(ED25519_PKCS8_SEED_PREFIX, seedCopy);
|
|
182
|
+
try {
|
|
183
|
+
const [privateKey, publicKey] = await Promise.all([
|
|
184
|
+
crypto.subtle.importKey('pkcs8', toArrayBuffer(privateKeyBytes), { name: 'Ed25519' }, true, [
|
|
185
|
+
'sign'
|
|
186
|
+
]),
|
|
187
|
+
crypto.subtle.importKey('raw', toArrayBuffer(ed25519.getPublicKey(seedCopy)), { name: 'Ed25519' }, true, ['verify'])
|
|
188
|
+
]);
|
|
189
|
+
return { privateKey, publicKey };
|
|
190
|
+
}
|
|
191
|
+
finally {
|
|
192
|
+
seedCopy.fill(0);
|
|
193
|
+
privateKeyBytes.fill(0);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
function concatBytes(...chunks) {
|
|
197
|
+
const length = chunks.reduce((total, chunk) => total + chunk.byteLength, 0);
|
|
198
|
+
const bytes = new Uint8Array(length);
|
|
199
|
+
let offset = 0;
|
|
200
|
+
for (const chunk of chunks) {
|
|
201
|
+
bytes.set(chunk, offset);
|
|
202
|
+
offset += chunk.byteLength;
|
|
203
|
+
}
|
|
204
|
+
return bytes;
|
|
205
|
+
}
|
|
206
|
+
function toArrayBuffer(bytes) {
|
|
207
|
+
const copy = new Uint8Array(bytes.byteLength);
|
|
208
|
+
copy.set(bytes);
|
|
209
|
+
return copy.buffer;
|
|
210
|
+
}
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +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"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,cAAc,CAAC;AAC7B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,oBAAoB,CAAC;AACnC,cAAc,UAAU,CAAC;AACzB,cAAc,mBAAmB,CAAC"}
|
package/dist/index.js
CHANGED
package/dist/solana.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export interface SystemTransferTransactionInput {
|
|
2
|
+
blockhash: string;
|
|
3
|
+
fromAddress: string;
|
|
4
|
+
lamports: bigint;
|
|
5
|
+
toAddress: string;
|
|
6
|
+
}
|
|
7
|
+
export interface SystemTransferTransaction {
|
|
8
|
+
messageBytes: Uint8Array;
|
|
9
|
+
transaction: Uint8Array;
|
|
10
|
+
}
|
|
11
|
+
export declare function base58Encode(bytes: Uint8Array): string;
|
|
12
|
+
export declare function base58Decode(value: string): Uint8Array;
|
|
13
|
+
export declare function solToLamports(value: string): bigint;
|
|
14
|
+
export declare function lamportsToSol(lamportsValue: bigint | null): string;
|
|
15
|
+
export declare function solanaRpc<T>({ endpoint, method, params }: {
|
|
16
|
+
endpoint: string;
|
|
17
|
+
method: string;
|
|
18
|
+
params: unknown[];
|
|
19
|
+
}): Promise<T>;
|
|
20
|
+
export declare function getLatestSolanaBlockhash(endpoint: string): Promise<string>;
|
|
21
|
+
export declare function getSolanaBalance(endpoint: string, address: string): Promise<bigint>;
|
|
22
|
+
export declare function sendSerializedSolanaTransaction(endpoint: string, transaction: Uint8Array): Promise<string>;
|
|
23
|
+
export declare function createSystemTransferTransaction({ blockhash, fromAddress, lamports, toAddress }: SystemTransferTransactionInput): SystemTransferTransaction;
|
|
24
|
+
export declare function applySolanaTransactionSignature(transaction: Uint8Array, signature: Uint8Array): Uint8Array;
|
|
25
|
+
export declare function normalizeSignatureBytes(signature: string | Uint8Array): Uint8Array;
|
|
26
|
+
//# sourceMappingURL=solana.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"solana.d.ts","sourceRoot":"","sources":["../src/solana.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,8BAA8B;IAC7C,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,yBAAyB;IACxC,YAAY,EAAE,UAAU,CAAC;IACzB,WAAW,EAAE,UAAU,CAAC;CACzB;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,UAAU,GAAG,MAAM,CAuBtD;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,CA4BtD;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CASnD;AAED,wBAAgB,aAAa,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CASlE;AAED,wBAAsB,SAAS,CAAC,CAAC,EAAE,EACjC,QAAQ,EACR,MAAM,EACN,MAAM,EACP,EAAE;IACD,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,OAAO,EAAE,CAAC;CACnB,GAAG,OAAO,CAAC,CAAC,CAAC,CA2Bb;AAED,wBAAsB,wBAAwB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAQhF;AAED,wBAAsB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAQzF;AAED,wBAAsB,+BAA+B,CACnD,QAAQ,EAAE,MAAM,EAChB,WAAW,EAAE,UAAU,GACtB,OAAO,CAAC,MAAM,CAAC,CAajB;AAED,wBAAgB,+BAA+B,CAAC,EAC9C,SAAS,EACT,WAAW,EACX,QAAQ,EACR,SAAS,EACV,EAAE,8BAA8B,GAAG,yBAAyB,CAmC5D;AAED,wBAAgB,+BAA+B,CAC7C,WAAW,EAAE,UAAU,EACvB,SAAS,EAAE,UAAU,GACpB,UAAU,CASZ;AAED,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,MAAM,GAAG,UAAU,GAAG,UAAU,CAElF"}
|
package/dist/solana.js
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
const BASE58_ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
|
|
2
|
+
const BASE58_ZERO = '1';
|
|
3
|
+
const LAMPORTS_PER_SOL = 1000000000n;
|
|
4
|
+
export function base58Encode(bytes) {
|
|
5
|
+
let value = 0n;
|
|
6
|
+
for (const byte of bytes) {
|
|
7
|
+
value = value * 256n + BigInt(byte);
|
|
8
|
+
}
|
|
9
|
+
let encoded = '';
|
|
10
|
+
while (value > 0n) {
|
|
11
|
+
const remainder = Number(value % 58n);
|
|
12
|
+
value /= 58n;
|
|
13
|
+
encoded = (BASE58_ALPHABET[remainder] ?? BASE58_ZERO) + encoded;
|
|
14
|
+
}
|
|
15
|
+
for (const byte of bytes) {
|
|
16
|
+
if (byte !== 0) {
|
|
17
|
+
break;
|
|
18
|
+
}
|
|
19
|
+
encoded = BASE58_ZERO + encoded;
|
|
20
|
+
}
|
|
21
|
+
return encoded || BASE58_ZERO;
|
|
22
|
+
}
|
|
23
|
+
export function base58Decode(value) {
|
|
24
|
+
let decoded = 0n;
|
|
25
|
+
for (const char of value) {
|
|
26
|
+
const index = BASE58_ALPHABET.indexOf(char);
|
|
27
|
+
if (index < 0) {
|
|
28
|
+
throw new Error(`Invalid base58 character ${char}`);
|
|
29
|
+
}
|
|
30
|
+
decoded = decoded * 58n + BigInt(index);
|
|
31
|
+
}
|
|
32
|
+
const bytes = [];
|
|
33
|
+
while (decoded > 0n) {
|
|
34
|
+
bytes.unshift(Number(decoded % 256n));
|
|
35
|
+
decoded /= 256n;
|
|
36
|
+
}
|
|
37
|
+
for (const char of value) {
|
|
38
|
+
if (char !== BASE58_ZERO) {
|
|
39
|
+
break;
|
|
40
|
+
}
|
|
41
|
+
bytes.unshift(0);
|
|
42
|
+
}
|
|
43
|
+
return new Uint8Array(bytes);
|
|
44
|
+
}
|
|
45
|
+
export function solToLamports(value) {
|
|
46
|
+
const normalized = value.trim();
|
|
47
|
+
if (!/^\d+(\.\d{0,9})?$/.test(normalized)) {
|
|
48
|
+
throw new Error('Enter a SOL amount with up to 9 decimals');
|
|
49
|
+
}
|
|
50
|
+
const [whole, fractional = ''] = normalized.split('.');
|
|
51
|
+
return BigInt(whole || '0') * LAMPORTS_PER_SOL + BigInt(fractional.padEnd(9, '0'));
|
|
52
|
+
}
|
|
53
|
+
export function lamportsToSol(lamportsValue) {
|
|
54
|
+
if (lamportsValue === null) {
|
|
55
|
+
return 'Unknown';
|
|
56
|
+
}
|
|
57
|
+
const whole = lamportsValue / LAMPORTS_PER_SOL;
|
|
58
|
+
const fractional = (lamportsValue % LAMPORTS_PER_SOL).toString().padStart(9, '0').replace(/0+$/, '');
|
|
59
|
+
return fractional ? `${whole}.${fractional} SOL` : `${whole} SOL`;
|
|
60
|
+
}
|
|
61
|
+
export async function solanaRpc({ endpoint, method, params }) {
|
|
62
|
+
const response = await fetch(endpoint, {
|
|
63
|
+
method: 'POST',
|
|
64
|
+
headers: { 'content-type': 'application/json' },
|
|
65
|
+
body: JSON.stringify({
|
|
66
|
+
jsonrpc: '2.0',
|
|
67
|
+
id: `ferno-${Date.now()}`,
|
|
68
|
+
method,
|
|
69
|
+
params
|
|
70
|
+
})
|
|
71
|
+
});
|
|
72
|
+
if (!response.ok) {
|
|
73
|
+
throw new Error(`Solana RPC ${response.status}`);
|
|
74
|
+
}
|
|
75
|
+
const payload = (await response.json());
|
|
76
|
+
if (payload.error) {
|
|
77
|
+
throw new Error(payload.error.message ?? 'Solana RPC request failed');
|
|
78
|
+
}
|
|
79
|
+
if (payload.result === undefined) {
|
|
80
|
+
throw new Error('Solana RPC returned no result');
|
|
81
|
+
}
|
|
82
|
+
return payload.result;
|
|
83
|
+
}
|
|
84
|
+
export async function getLatestSolanaBlockhash(endpoint) {
|
|
85
|
+
const result = await solanaRpc({
|
|
86
|
+
endpoint,
|
|
87
|
+
method: 'getLatestBlockhash',
|
|
88
|
+
params: [{ commitment: 'confirmed' }]
|
|
89
|
+
});
|
|
90
|
+
return result.value.blockhash;
|
|
91
|
+
}
|
|
92
|
+
export async function getSolanaBalance(endpoint, address) {
|
|
93
|
+
const result = await solanaRpc({
|
|
94
|
+
endpoint,
|
|
95
|
+
method: 'getBalance',
|
|
96
|
+
params: [address, { commitment: 'confirmed' }]
|
|
97
|
+
});
|
|
98
|
+
return BigInt(result.value);
|
|
99
|
+
}
|
|
100
|
+
export async function sendSerializedSolanaTransaction(endpoint, transaction) {
|
|
101
|
+
return await solanaRpc({
|
|
102
|
+
endpoint,
|
|
103
|
+
method: 'sendTransaction',
|
|
104
|
+
params: [
|
|
105
|
+
bytesToBase64(transaction),
|
|
106
|
+
{
|
|
107
|
+
encoding: 'base64',
|
|
108
|
+
preflightCommitment: 'confirmed',
|
|
109
|
+
skipPreflight: false
|
|
110
|
+
}
|
|
111
|
+
]
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
export function createSystemTransferTransaction({ blockhash, fromAddress, lamports, toAddress }) {
|
|
115
|
+
const from = base58Decode(fromAddress);
|
|
116
|
+
const to = base58Decode(toAddress);
|
|
117
|
+
const recentBlockhash = base58Decode(blockhash);
|
|
118
|
+
const systemProgram = base58Decode('11111111111111111111111111111111');
|
|
119
|
+
if (from.byteLength !== 32 || to.byteLength !== 32 || recentBlockhash.byteLength !== 32) {
|
|
120
|
+
throw new Error('Invalid Solana transfer address or blockhash');
|
|
121
|
+
}
|
|
122
|
+
const transferData = new Uint8Array(12);
|
|
123
|
+
const transferDataView = new DataView(transferData.buffer);
|
|
124
|
+
transferDataView.setUint32(0, 2, true);
|
|
125
|
+
transferDataView.setBigUint64(4, lamports, true);
|
|
126
|
+
const instruction = concatBytes(new Uint8Array([2]), encodeShortVecLength(2), new Uint8Array([0, 1]), encodeShortVecLength(transferData.byteLength), transferData);
|
|
127
|
+
const messageBytes = concatBytes(new Uint8Array([1, 0, 1]), encodeShortVecLength(3), from, to, systemProgram, recentBlockhash, encodeShortVecLength(1), instruction);
|
|
128
|
+
const transaction = concatBytes(encodeShortVecLength(1), new Uint8Array(64), messageBytes);
|
|
129
|
+
return { messageBytes, transaction };
|
|
130
|
+
}
|
|
131
|
+
export function applySolanaTransactionSignature(transaction, signature) {
|
|
132
|
+
if (signature.byteLength !== 64) {
|
|
133
|
+
throw new Error('Solana transaction signatures must be 64 bytes');
|
|
134
|
+
}
|
|
135
|
+
const signed = new Uint8Array(transaction);
|
|
136
|
+
const signatureOffset = encodeShortVecLength(1).byteLength;
|
|
137
|
+
signed.set(signature, signatureOffset);
|
|
138
|
+
return signed;
|
|
139
|
+
}
|
|
140
|
+
export function normalizeSignatureBytes(signature) {
|
|
141
|
+
return typeof signature === 'string' ? base58Decode(signature) : new Uint8Array(signature);
|
|
142
|
+
}
|
|
143
|
+
function bytesToBase64(bytes) {
|
|
144
|
+
let binary = '';
|
|
145
|
+
for (const byte of bytes) {
|
|
146
|
+
binary += String.fromCharCode(byte);
|
|
147
|
+
}
|
|
148
|
+
return btoa(binary);
|
|
149
|
+
}
|
|
150
|
+
function encodeShortVecLength(length) {
|
|
151
|
+
const bytes = [];
|
|
152
|
+
let remaining = length;
|
|
153
|
+
do {
|
|
154
|
+
let byte = remaining & 0x7f;
|
|
155
|
+
remaining >>= 7;
|
|
156
|
+
if (remaining > 0) {
|
|
157
|
+
byte |= 0x80;
|
|
158
|
+
}
|
|
159
|
+
bytes.push(byte);
|
|
160
|
+
} while (remaining > 0);
|
|
161
|
+
return new Uint8Array(bytes);
|
|
162
|
+
}
|
|
163
|
+
function concatBytes(...chunks) {
|
|
164
|
+
const length = chunks.reduce((total, chunk) => total + chunk.byteLength, 0);
|
|
165
|
+
const bytes = new Uint8Array(length);
|
|
166
|
+
let offset = 0;
|
|
167
|
+
for (const chunk of chunks) {
|
|
168
|
+
bytes.set(chunk, offset);
|
|
169
|
+
offset += chunk.byteLength;
|
|
170
|
+
}
|
|
171
|
+
return bytes;
|
|
172
|
+
}
|
|
@@ -118,8 +118,20 @@ export interface StandardWallet<TWallet extends StandardWalletProvider = Standar
|
|
|
118
118
|
icon: string;
|
|
119
119
|
wallet: TWallet;
|
|
120
120
|
}
|
|
121
|
+
export declare const LAST_USED_WALLET_STORAGE_KEY = "lastConnectedWallet";
|
|
122
|
+
export interface WalletStorage {
|
|
123
|
+
getItem(key: string): string | null;
|
|
124
|
+
removeItem(key: string): void;
|
|
125
|
+
setItem(key: string, value: string): void;
|
|
126
|
+
}
|
|
121
127
|
export declare function isSolanaWallet<TWallet extends StandardWalletProvider>(wallet: TWallet): boolean;
|
|
122
128
|
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>[];
|
|
129
|
+
export declare function dedupeStandardWallets<TWallet extends StandardWalletProvider>(wallets: readonly StandardWallet<TWallet>[], preferredNames?: readonly string[], lastUsedWalletName?: string | null): StandardWallet<TWallet>[];
|
|
130
|
+
export declare function adaptStandardWallets<TWallet extends StandardWalletProvider>(wallets: readonly TWallet[], preferredNames?: readonly string[], lastUsedWalletName?: string | null): StandardWallet<TWallet>[];
|
|
131
|
+
export declare function sortWalletsByLastUsed<TWallet extends {
|
|
132
|
+
name: string;
|
|
133
|
+
}>(wallets: readonly TWallet[], lastUsedWalletName: string | null | undefined): TWallet[];
|
|
134
|
+
export declare function readLastUsedWalletName(storage: WalletStorage | null | undefined, storageKey?: string): string | null;
|
|
135
|
+
export declare function saveLastUsedWalletName(storage: WalletStorage | null | undefined, walletName: string, storageKey?: string): void;
|
|
136
|
+
export declare function clearLastUsedWalletName(storage: WalletStorage | null | undefined, storageKey?: string): void;
|
|
125
137
|
//# sourceMappingURL=wallet-standard.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"wallet-standard.d.ts","sourceRoot":"","sources":["../src/wallet-standard.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"wallet-standard.d.ts","sourceRoot":"","sources":["../src/wallet-standard.ts"],"names":[],"mappings":"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"}
|
package/dist/wallet-standard.js
CHANGED
|
@@ -5,6 +5,7 @@ export const SolanaSignAndSendTransaction = 'solana:signAndSendTransaction';
|
|
|
5
5
|
export const SolanaSignMessage = 'solana:signMessage';
|
|
6
6
|
export const SolanaSignTransaction = 'solana:signTransaction';
|
|
7
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 const LAST_USED_WALLET_STORAGE_KEY = 'lastConnectedWallet';
|
|
8
9
|
export function isSolanaWallet(wallet) {
|
|
9
10
|
const { features, chains } = wallet;
|
|
10
11
|
if (!features) {
|
|
@@ -27,7 +28,7 @@ export function adaptWallet(wallet) {
|
|
|
27
28
|
wallet
|
|
28
29
|
};
|
|
29
30
|
}
|
|
30
|
-
export function dedupeStandardWallets(wallets, preferredNames = []) {
|
|
31
|
+
export function dedupeStandardWallets(wallets, preferredNames = [], lastUsedWalletName = null) {
|
|
31
32
|
const walletsByName = new Map();
|
|
32
33
|
for (const wallet of wallets) {
|
|
33
34
|
const existing = walletsByName.get(wallet.name);
|
|
@@ -44,11 +45,47 @@ export function dedupeStandardWallets(wallets, preferredNames = []) {
|
|
|
44
45
|
const deduped = Array.from(walletsByName.values());
|
|
45
46
|
const preferred = deduped.filter((wallet) => preferredNames.includes(wallet.name));
|
|
46
47
|
const otherWallets = deduped.filter((wallet) => !preferredNames.includes(wallet.name));
|
|
47
|
-
return [...preferred, ...otherWallets];
|
|
48
|
+
return sortWalletsByLastUsed([...preferred, ...otherWallets], lastUsedWalletName);
|
|
48
49
|
}
|
|
49
|
-
export function adaptStandardWallets(wallets, preferredNames = []) {
|
|
50
|
+
export function adaptStandardWallets(wallets, preferredNames = [], lastUsedWalletName = null) {
|
|
50
51
|
const adapted = wallets
|
|
51
52
|
.map(adaptWallet)
|
|
52
53
|
.filter((wallet) => wallet !== null);
|
|
53
|
-
return dedupeStandardWallets(adapted, preferredNames);
|
|
54
|
+
return dedupeStandardWallets(adapted, preferredNames, lastUsedWalletName);
|
|
55
|
+
}
|
|
56
|
+
export function sortWalletsByLastUsed(wallets, lastUsedWalletName) {
|
|
57
|
+
const ordered = [...wallets];
|
|
58
|
+
const walletName = lastUsedWalletName?.trim();
|
|
59
|
+
if (!walletName) {
|
|
60
|
+
return ordered;
|
|
61
|
+
}
|
|
62
|
+
const lastUsedIndex = ordered.findIndex((wallet) => wallet.name === walletName);
|
|
63
|
+
if (lastUsedIndex <= 0) {
|
|
64
|
+
return ordered;
|
|
65
|
+
}
|
|
66
|
+
const [lastUsedWallet] = ordered.splice(lastUsedIndex, 1);
|
|
67
|
+
return lastUsedWallet ? [lastUsedWallet, ...ordered] : ordered;
|
|
68
|
+
}
|
|
69
|
+
export function readLastUsedWalletName(storage, storageKey = LAST_USED_WALLET_STORAGE_KEY) {
|
|
70
|
+
try {
|
|
71
|
+
const walletName = storage?.getItem(storageKey)?.trim();
|
|
72
|
+
return walletName || null;
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
export function saveLastUsedWalletName(storage, walletName, storageKey = LAST_USED_WALLET_STORAGE_KEY) {
|
|
79
|
+
try {
|
|
80
|
+
storage?.setItem(storageKey, walletName);
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
export function clearLastUsedWalletName(storage, storageKey = LAST_USED_WALLET_STORAGE_KEY) {
|
|
86
|
+
try {
|
|
87
|
+
storage?.removeItem(storageKey);
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
}
|
|
54
91
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fernolab/wallet-adapter-core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Framework-agnostic Solana Wallet Standard adapter core.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
@@ -41,6 +41,9 @@
|
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"typescript": "^6.0.3",
|
|
44
|
-
"vitest": "^4.1.
|
|
44
|
+
"vitest": "^4.1.9"
|
|
45
|
+
},
|
|
46
|
+
"dependencies": {
|
|
47
|
+
"@noble/curves": "^2.2.0"
|
|
45
48
|
}
|
|
46
49
|
}
|