@usebutr/svm 0.2.5 → 0.2.6
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/index.d.ts +7 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +65 -69
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { ChainBase, PlatformDiscoverer, WalletAdapter, WalletCapabilities } from "@usebutr/core";
|
|
2
2
|
import { WalletStandardWallet, WalletStandardWalletAccount } from "@usebutr/wallet-standard-shared";
|
|
3
|
-
|
|
4
3
|
//#region src/wallet-standard-types.d.ts
|
|
5
4
|
type SolanaSignMessageInput = {
|
|
6
5
|
account: WalletStandardWalletAccount;
|
|
@@ -30,7 +29,7 @@ declare const slugify: (name: string) => string;
|
|
|
30
29
|
/**
|
|
31
30
|
* Adapt a Solana Wallet Standard `Wallet` object into a butr
|
|
32
31
|
* `WalletAdapter`. Returns `null` if the wallet doesn't advertise any
|
|
33
|
-
* Solana chain or doesn't expose `standard:connect
|
|
32
|
+
* Solana chain or doesn't expose `standard:connect`; both required
|
|
34
33
|
* for butr to do anything useful with it.
|
|
35
34
|
*
|
|
36
35
|
* **Capability gating**
|
|
@@ -49,7 +48,7 @@ declare const slugify: (name: string) => string;
|
|
|
49
48
|
* balance reads require an RPC connection that's outside butr's
|
|
50
49
|
* scope; consumers wrap their own RPC client.
|
|
51
50
|
* - `getTransactionReceipt` returns `{ status: "Pending" }`. Same
|
|
52
|
-
* reason
|
|
51
|
+
* reason; needs an RPC.
|
|
53
52
|
* - `switchChain` updates butr's view of the current Solana cluster
|
|
54
53
|
* locally and routes subsequent `signAndSendTransaction` calls
|
|
55
54
|
* through the new chain. Wallet Standard has no "tell the wallet
|
|
@@ -62,7 +61,7 @@ declare const slugify: (name: string) => string;
|
|
|
62
61
|
* authorisations) silently return the existing accounts. butr
|
|
63
62
|
* refreshes the pool entry's `accounts` array either way.
|
|
64
63
|
* - `switchAccount` is intentionally unimplemented. Wallet Standard
|
|
65
|
-
* has no silent "use address X" feature
|
|
64
|
+
* has no silent "use address X" feature; the user picks the active
|
|
66
65
|
* account through the wallet's own UI. `signAndSendTransaction`
|
|
67
66
|
* accepts an `account` input, so consumers needing per-tx address
|
|
68
67
|
* selection can pick one from `accounts` at call time.
|
|
@@ -72,7 +71,6 @@ declare const buildSvmAdapter: (wallet: WalletStandardWallet,
|
|
|
72
71
|
* `disconnected` event to all current subscribers. The discovery
|
|
73
72
|
* layer invokes it on Wallet Standard `unregister` so a connected
|
|
74
73
|
* pool entry tears down when its extension is removed. */
|
|
75
|
-
|
|
76
74
|
registerDisconnector?: (emit: () => void) => void) => WalletAdapter | null;
|
|
77
75
|
//#endregion
|
|
78
76
|
//#region src/wallet-standard.d.ts
|
|
@@ -82,7 +80,7 @@ registerDisconnector?: (emit: () => void) => void) => WalletAdapter | null;
|
|
|
82
80
|
*
|
|
83
81
|
* Runtime requires the `@wallet-standard/app` package, which is an
|
|
84
82
|
* **optional peer dependency** of butr. EVM-only consumers don't need
|
|
85
|
-
* to install it
|
|
83
|
+
* to install it; discovery will silently skip the SVM side if the
|
|
86
84
|
* module isn't resolvable.
|
|
87
85
|
*
|
|
88
86
|
* Install for SVM auto-discovery:
|
|
@@ -137,7 +135,7 @@ type WalletStandardCapabilityInput = {
|
|
|
137
135
|
*
|
|
138
136
|
* - `getBalance` / `getTransactionReceipt`: Wallet Standard exposes no
|
|
139
137
|
* balance/receipt RPC.
|
|
140
|
-
* - `requestAccounts`: no programmatic equivalent of EIP-2255
|
|
138
|
+
* - `requestAccounts`: no programmatic equivalent of EIP-2255; re-running
|
|
141
139
|
* `standard:connect` doesn't produce new accounts on any major Wallet
|
|
142
140
|
* Standard wallet in practice (Phantom Solana, MetaMask Snap, Solflare,
|
|
143
141
|
* Backpack).
|
|
@@ -149,7 +147,7 @@ declare const resolveWalletStandardCapabilities: (input: WalletStandardCapabilit
|
|
|
149
147
|
//#region src/discoverer.d.ts
|
|
150
148
|
/**
|
|
151
149
|
* SVM's `PlatformDiscoverer`: Solana Wallet Standard announcements. No
|
|
152
|
-
* legacy fallback
|
|
150
|
+
* legacy fallback; Solana wallets standardised on Wallet Standard
|
|
153
151
|
* before butr existed, so there's no `window.solana` injected path
|
|
154
152
|
* worth shimming.
|
|
155
153
|
*/
|
|
@@ -165,7 +163,7 @@ declare module "@usebutr/core" {
|
|
|
165
163
|
interface SignerForPlatform {
|
|
166
164
|
svm: WalletStandardWallet;
|
|
167
165
|
}
|
|
168
|
-
}
|
|
166
|
+
}
|
|
169
167
|
//#endregion
|
|
170
168
|
export { SVM_CHAINS, SVM_CHAINS_LIST, type SolanaSignAndSendTransactionFeature, type SolanaSignAndSendTransactionInput, type SolanaSignAndSendTransactionOutput, type SolanaSignMessageFeature, type SolanaSignMessageInput, type SolanaSignMessageOutput, type WalletStandardCapabilityInput, buildSvmAdapter, discoverSvmAdapters, resolveWalletStandardCapabilities, slugify, svmDiscoverer };
|
|
171
169
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/wallet-standard-types.ts","../src/wallet-standard-adapter.ts","../src/wallet-standard.ts","../src/chains.ts","../src/capabilities.ts","../src/discoverer.ts","../src/signer-augmentation.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/wallet-standard-types.ts","../src/wallet-standard-adapter.ts","../src/wallet-standard.ts","../src/chains.ts","../src/capabilities.ts","../src/discoverer.ts","../src/signer-augmentation.ts"],"mappings":";;;KAaK;EACH,SAAS;EACT,SAAS;;KAGN;EACH,WAAW;EACX,eAAe;;KAGZ;EACH,iBACK,QAAQ,cAAc,4BACtB,QAAQ,cAAc;;KAGxB;EACH,SAAS;EACT;EACA,aAAa;;KAGV;EACH,WAAW;;KAGR;EACH,4BACK,QAAQ,cAAc,uCACtB,QAAQ,cAAc;;;;cCfvB,UAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cA2DX,kBACJ,QAAQ,sBAKR;;;;;AAAA,wBAAwB,8BACvB;;;;;;;;;;;;;;;;;;;;;;;cCpEG,sBAAuB,YAAY,SAAS;;;cChB5C;;;;;;;;;;;;;;;;;;;;cAgBA,iBAAiB,cAAc;;;KCvBhC;EACH;EACA;IACE;IACA;IACA;IACA;IACA;;;;;;;;;;;;;;;cAgBE,oCACJ,OAAO,kCACN;;;;;;;;;cCjBG,eAAe;;;;;;;;;YCDT;IACR,KAAK"}
|
package/dist/index.js
CHANGED
|
@@ -7,7 +7,7 @@ import { buildAccount, discoverWalletStandard, getFeature, pickAccountByAddress,
|
|
|
7
7
|
*
|
|
8
8
|
* - `getBalance` / `getTransactionReceipt`: Wallet Standard exposes no
|
|
9
9
|
* balance/receipt RPC.
|
|
10
|
-
* - `requestAccounts`: no programmatic equivalent of EIP-2255
|
|
10
|
+
* - `requestAccounts`: no programmatic equivalent of EIP-2255; re-running
|
|
11
11
|
* `standard:connect` doesn't produce new accounts on any major Wallet
|
|
12
12
|
* Standard wallet in practice (Phantom Solana, MetaMask Snap, Solflare,
|
|
13
13
|
* Backpack).
|
|
@@ -43,11 +43,10 @@ const buildSolanaChain = (chainId, walletName) => ({
|
|
|
43
43
|
namespace: "solana",
|
|
44
44
|
reference: chainId.slice(7)
|
|
45
45
|
});
|
|
46
|
-
const buildSolanaAccount = (address, chain) => buildAccount(address, chain);
|
|
47
46
|
/**
|
|
48
47
|
* Adapt a Solana Wallet Standard `Wallet` object into a butr
|
|
49
48
|
* `WalletAdapter`. Returns `null` if the wallet doesn't advertise any
|
|
50
|
-
* Solana chain or doesn't expose `standard:connect
|
|
49
|
+
* Solana chain or doesn't expose `standard:connect`; both required
|
|
51
50
|
* for butr to do anything useful with it.
|
|
52
51
|
*
|
|
53
52
|
* **Capability gating**
|
|
@@ -66,7 +65,7 @@ const buildSolanaAccount = (address, chain) => buildAccount(address, chain);
|
|
|
66
65
|
* balance reads require an RPC connection that's outside butr's
|
|
67
66
|
* scope; consumers wrap their own RPC client.
|
|
68
67
|
* - `getTransactionReceipt` returns `{ status: "Pending" }`. Same
|
|
69
|
-
* reason
|
|
68
|
+
* reason; needs an RPC.
|
|
70
69
|
* - `switchChain` updates butr's view of the current Solana cluster
|
|
71
70
|
* locally and routes subsequent `signAndSendTransaction` calls
|
|
72
71
|
* through the new chain. Wallet Standard has no "tell the wallet
|
|
@@ -79,16 +78,16 @@ const buildSolanaAccount = (address, chain) => buildAccount(address, chain);
|
|
|
79
78
|
* authorisations) silently return the existing accounts. butr
|
|
80
79
|
* refreshes the pool entry's `accounts` array either way.
|
|
81
80
|
* - `switchAccount` is intentionally unimplemented. Wallet Standard
|
|
82
|
-
* has no silent "use address X" feature
|
|
81
|
+
* has no silent "use address X" feature; the user picks the active
|
|
83
82
|
* account through the wallet's own UI. `signAndSendTransaction`
|
|
84
83
|
* accepts an `account` input, so consumers needing per-tx address
|
|
85
84
|
* selection can pick one from `accounts` at call time.
|
|
86
85
|
*/
|
|
87
86
|
const buildSvmAdapter = (wallet, registerDisconnector) => {
|
|
88
87
|
const solanaChainId = pickSolanaChain(wallet);
|
|
89
|
-
if (
|
|
88
|
+
if (solanaChainId === null) return null;
|
|
90
89
|
const connect = getFeature(wallet, "standard:connect");
|
|
91
|
-
if (
|
|
90
|
+
if (connect === void 0) return null;
|
|
92
91
|
const disconnect = getFeature(wallet, "standard:disconnect");
|
|
93
92
|
const events = getFeature(wallet, "standard:events");
|
|
94
93
|
const signMessage = getFeature(wallet, "solana:signMessage");
|
|
@@ -101,9 +100,9 @@ const buildSvmAdapter = (wallet, registerDisconnector) => {
|
|
|
101
100
|
const notifyAccountChanged = () => {
|
|
102
101
|
if (wallet.accounts.length === 0) return;
|
|
103
102
|
const chain = currentChain();
|
|
104
|
-
const built = wallet.accounts.map((a) =>
|
|
103
|
+
const built = wallet.accounts.map((a) => buildAccount(a.address, chain));
|
|
105
104
|
const first = built[0];
|
|
106
|
-
if (
|
|
105
|
+
if (first === void 0) return;
|
|
107
106
|
for (const listener of listeners) listener({
|
|
108
107
|
account: first,
|
|
109
108
|
accounts: built,
|
|
@@ -113,6 +112,19 @@ const buildSvmAdapter = (wallet, registerDisconnector) => {
|
|
|
113
112
|
registerDisconnector?.(() => {
|
|
114
113
|
for (const listener of listeners) listener({ type: "disconnected" });
|
|
115
114
|
});
|
|
115
|
+
const signAndSend = async (tx, account) => {
|
|
116
|
+
if (signAndSendTx === void 0) throw new Error(`Wallet ${wallet.name} does not advertise solana:signAndSendTransaction`);
|
|
117
|
+
const wsAccount = account ? pickAccountByAddress(wallet.accounts, account.walletAddress) : wallet.accounts[0];
|
|
118
|
+
if (wsAccount === void 0) throw new Error("No connected account");
|
|
119
|
+
if (!(tx instanceof Uint8Array)) throw new TypeError("SVM sendTx expects a serialized transaction (Uint8Array)");
|
|
120
|
+
const [output] = await signAndSendTx.signAndSendTransaction({
|
|
121
|
+
account: wsAccount,
|
|
122
|
+
chain: currentChainId,
|
|
123
|
+
transaction: tx
|
|
124
|
+
});
|
|
125
|
+
if (output === void 0) throw new Error("signAndSendTransaction returned no outputs");
|
|
126
|
+
return bytesToBase64(output.signature);
|
|
127
|
+
};
|
|
116
128
|
return {
|
|
117
129
|
capabilities: resolveWalletStandardCapabilities({
|
|
118
130
|
chainCount: wallet.chains.length,
|
|
@@ -126,114 +138,96 @@ const buildSvmAdapter = (wallet, registerDisconnector) => {
|
|
|
126
138
|
}),
|
|
127
139
|
chainPlatform: "svm",
|
|
128
140
|
async connect(opts) {
|
|
129
|
-
await connect.connect(opts?.silent ? { silent: true } : void 0);
|
|
141
|
+
await connect.connect(opts?.silent === true ? { silent: true } : void 0);
|
|
130
142
|
},
|
|
131
143
|
async disconnect() {
|
|
132
|
-
if (disconnect) try {
|
|
144
|
+
if (disconnect !== void 0) try {
|
|
133
145
|
await disconnect.disconnect();
|
|
134
146
|
} catch (error) {
|
|
135
147
|
logWarn("[butr] Wallet Standard disconnect threw:", error);
|
|
136
148
|
}
|
|
137
149
|
},
|
|
138
|
-
getAccount() {
|
|
150
|
+
getAccount: () => {
|
|
139
151
|
const address = pickFirstAddress(wallet.accounts);
|
|
140
|
-
if (
|
|
141
|
-
return Promise.resolve(
|
|
152
|
+
if (address === null) return Promise.resolve(null);
|
|
153
|
+
return Promise.resolve(buildAccount(address, currentChain()));
|
|
142
154
|
},
|
|
143
|
-
getAccounts() {
|
|
155
|
+
getAccounts: () => {
|
|
144
156
|
const chain = currentChain();
|
|
145
|
-
return Promise.resolve(wallet.accounts.map((a) =>
|
|
146
|
-
},
|
|
147
|
-
getBalance() {
|
|
148
|
-
return Promise.resolve({
|
|
149
|
-
decimals: SOLANA_DECIMALS,
|
|
150
|
-
formatted: "0",
|
|
151
|
-
symbol: "SOL",
|
|
152
|
-
value: 0n
|
|
153
|
-
});
|
|
154
|
-
},
|
|
155
|
-
getSigner() {
|
|
156
|
-
return Promise.resolve(wallet);
|
|
157
|
-
},
|
|
158
|
-
getTransactionReceipt() {
|
|
159
|
-
return Promise.resolve({ status: "Pending" });
|
|
157
|
+
return Promise.resolve(wallet.accounts.map((a) => buildAccount(a.address, chain)));
|
|
160
158
|
},
|
|
159
|
+
getBalance: () => Promise.resolve({
|
|
160
|
+
decimals: SOLANA_DECIMALS,
|
|
161
|
+
formatted: "0",
|
|
162
|
+
symbol: "SOL",
|
|
163
|
+
value: 0n
|
|
164
|
+
}),
|
|
165
|
+
getSigner: () => Promise.resolve(wallet),
|
|
166
|
+
getTransactionReceipt: () => Promise.resolve({ status: "Pending" }),
|
|
161
167
|
icon: sanitizeIcon(wallet.icon),
|
|
162
168
|
id: slugify(wallet.name),
|
|
163
169
|
name: wallet.name,
|
|
164
170
|
async requestAccounts() {
|
|
165
171
|
await connect.connect();
|
|
166
172
|
},
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
const wsAccount = account ? pickAccountByAddress(wallet.accounts, account.walletAddress) : wallet.accounts[0] ?? null;
|
|
170
|
-
if (!wsAccount) throw new Error("No connected account");
|
|
171
|
-
if (!(tx instanceof Uint8Array)) throw new TypeError("SVM sendTx expects a serialized transaction (Uint8Array)");
|
|
172
|
-
const [output] = await signAndSendTx.signAndSendTransaction({
|
|
173
|
-
account: wsAccount,
|
|
174
|
-
chain: currentChainId,
|
|
175
|
-
transaction: tx
|
|
176
|
-
});
|
|
177
|
-
if (!output) throw new Error("signAndSendTransaction returned no outputs");
|
|
178
|
-
return bytesToBase64(output.signature);
|
|
179
|
-
},
|
|
180
|
-
sendTxToChain(tx, _targetChainId, account, cb) {
|
|
173
|
+
sendTx: (tx, account) => signAndSend(tx, account),
|
|
174
|
+
sendTxToChain: (tx, _targetChainId, account, cb) => {
|
|
181
175
|
cb?.();
|
|
182
|
-
return
|
|
176
|
+
return signAndSend(tx, account);
|
|
183
177
|
},
|
|
184
178
|
async signMessage(msg, account) {
|
|
185
|
-
if (
|
|
186
|
-
const wsAccount = account ? pickAccountByAddress(wallet.accounts, account.walletAddress) : wallet.accounts[0]
|
|
187
|
-
if (
|
|
179
|
+
if (signMessage === void 0) throw new Error(`Wallet ${wallet.name} does not advertise solana:signMessage`);
|
|
180
|
+
const wsAccount = account ? pickAccountByAddress(wallet.accounts, account.walletAddress) : wallet.accounts[0];
|
|
181
|
+
if (wsAccount === void 0) throw new Error("No connected account");
|
|
188
182
|
const [output] = await signMessage.signMessage({
|
|
189
183
|
account: wsAccount,
|
|
190
184
|
message: msg
|
|
191
185
|
});
|
|
192
|
-
if (
|
|
186
|
+
if (output === void 0) throw new Error("signMessage returned no outputs");
|
|
193
187
|
return {
|
|
194
188
|
signature: output.signature,
|
|
195
189
|
signedMessage: output.signedMessage
|
|
196
190
|
};
|
|
197
191
|
},
|
|
198
|
-
...signTx ? { async signTransaction(tx, account) {
|
|
199
|
-
const wsAccount = account ? pickAccountByAddress(wallet.accounts, account.walletAddress) : wallet.accounts[0]
|
|
200
|
-
if (
|
|
192
|
+
...signTx === void 0 ? {} : { async signTransaction(tx, account) {
|
|
193
|
+
const wsAccount = account ? pickAccountByAddress(wallet.accounts, account.walletAddress) : wallet.accounts[0];
|
|
194
|
+
if (wsAccount === void 0) throw new Error("No connected account");
|
|
201
195
|
if (!(tx instanceof Uint8Array)) throw new TypeError("SVM signTransaction expects a serialized transaction (Uint8Array)");
|
|
202
196
|
const [output] = await signTx.signTransaction({
|
|
203
197
|
account: wsAccount,
|
|
204
198
|
chain: currentChainId,
|
|
205
199
|
transaction: tx
|
|
206
200
|
});
|
|
207
|
-
if (
|
|
201
|
+
if (output === void 0) throw new Error("signTransaction returned no outputs");
|
|
208
202
|
return output.signedTransaction;
|
|
209
|
-
} }
|
|
210
|
-
...signIn ? { async signIn(input) {
|
|
203
|
+
} },
|
|
204
|
+
...signIn === void 0 ? {} : { async signIn(input) {
|
|
211
205
|
const [output] = await signIn.signIn(input);
|
|
212
|
-
if (
|
|
206
|
+
if (output === void 0) throw new Error("signIn returned no outputs");
|
|
213
207
|
return {
|
|
214
|
-
account:
|
|
208
|
+
account: buildAccount(output.account.address, currentChain()),
|
|
215
209
|
signature: output.signature,
|
|
216
210
|
signedMessage: output.signedMessage
|
|
217
211
|
};
|
|
218
|
-
} }
|
|
212
|
+
} },
|
|
219
213
|
subscribe(listener) {
|
|
220
214
|
listeners.add(listener);
|
|
221
215
|
let unsubWallet = null;
|
|
222
|
-
if (events) {
|
|
216
|
+
if (events !== void 0) {
|
|
223
217
|
const unsub = events.on("change", (changes) => {
|
|
224
|
-
if (changes.chains) {
|
|
218
|
+
if (changes.chains !== void 0) {
|
|
225
219
|
const next = changes.chains.find((c) => c === "solana:mainnet" || c === "solana:mainnet-beta") ?? changes.chains.find((c) => c.startsWith(SOLANA_PREFIX));
|
|
226
|
-
if (next) currentChainId = next;
|
|
220
|
+
if (next !== void 0) currentChainId = next;
|
|
227
221
|
}
|
|
228
|
-
if (changes.accounts) {
|
|
222
|
+
if (changes.accounts !== void 0) {
|
|
229
223
|
if (changes.accounts.length === 0) {
|
|
230
224
|
listener({ type: "disconnected" });
|
|
231
225
|
return;
|
|
232
226
|
}
|
|
233
227
|
const chain = currentChain();
|
|
234
|
-
const built = changes.accounts.map((a) =>
|
|
228
|
+
const built = changes.accounts.map((a) => buildAccount(a.address, chain));
|
|
235
229
|
const first = built[0];
|
|
236
|
-
if (
|
|
230
|
+
if (first === void 0) return;
|
|
237
231
|
listener({
|
|
238
232
|
account: first,
|
|
239
233
|
accounts: built,
|
|
@@ -241,16 +235,18 @@ const buildSvmAdapter = (wallet, registerDisconnector) => {
|
|
|
241
235
|
});
|
|
242
236
|
return;
|
|
243
237
|
}
|
|
244
|
-
if (changes.chains) notifyAccountChanged();
|
|
238
|
+
if (changes.chains !== void 0) notifyAccountChanged();
|
|
245
239
|
});
|
|
246
|
-
unsubWallet = () =>
|
|
240
|
+
unsubWallet = () => {
|
|
241
|
+
unsub();
|
|
242
|
+
};
|
|
247
243
|
}
|
|
248
244
|
return () => {
|
|
249
245
|
listeners.delete(listener);
|
|
250
246
|
unsubWallet?.();
|
|
251
247
|
};
|
|
252
248
|
},
|
|
253
|
-
switchChain(chain) {
|
|
249
|
+
switchChain: (chain) => {
|
|
254
250
|
if (chain.namespace !== "solana") throw new Error(`SVM adapter received non-Solana chain "${chain.id}". Pass a chain with namespace "solana".`);
|
|
255
251
|
if (!wallet.chains.includes(chain.id)) throw new Error(`Wallet ${wallet.name} does not advertise chain "${chain.id}". Available: ${wallet.chains.join(", ")}`);
|
|
256
252
|
currentChainId = chain.id;
|
|
@@ -268,7 +264,7 @@ const buildSvmAdapter = (wallet, registerDisconnector) => {
|
|
|
268
264
|
*
|
|
269
265
|
* Runtime requires the `@wallet-standard/app` package, which is an
|
|
270
266
|
* **optional peer dependency** of butr. EVM-only consumers don't need
|
|
271
|
-
* to install it
|
|
267
|
+
* to install it; discovery will silently skip the SVM side if the
|
|
272
268
|
* module isn't resolvable.
|
|
273
269
|
*
|
|
274
270
|
* Install for SVM auto-discovery:
|
|
@@ -312,7 +308,7 @@ const SVM_CHAINS_LIST = Object.values(SVM_CHAINS);
|
|
|
312
308
|
//#region src/discoverer.ts
|
|
313
309
|
/**
|
|
314
310
|
* SVM's `PlatformDiscoverer`: Solana Wallet Standard announcements. No
|
|
315
|
-
* legacy fallback
|
|
311
|
+
* legacy fallback; Solana wallets standardised on Wallet Standard
|
|
316
312
|
* before butr existed, so there's no `window.solana` injected path
|
|
317
313
|
* worth shimming.
|
|
318
314
|
*/
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["kitSlugify"],"sources":["../src/capabilities.ts","../src/wallet-standard-adapter.ts","../src/wallet-standard.ts","../src/chains.ts","../src/discoverer.ts"],"sourcesContent":["import type { WalletCapabilities } from \"@usebutr/core\";\n\ntype WalletStandardCapabilityInput = {\n chainCount: number;\n features: {\n events: boolean;\n signAndSendTransaction: boolean;\n signIn: boolean;\n signMessage: boolean;\n signTransaction: boolean;\n };\n};\n\n/**\n * Wallet Standard capability mapping.\n *\n * - `getBalance` / `getTransactionReceipt`: Wallet Standard exposes no\n * balance/receipt RPC.\n * - `requestAccounts`: no programmatic equivalent of EIP-2255 — re-running\n * `standard:connect` doesn't produce new accounts on any major Wallet\n * Standard wallet in practice (Phantom Solana, MetaMask Snap, Solflare,\n * Backpack).\n * - `switchChain`: true when more than one chain is advertised; with a\n * single chain there's nothing to switch to.\n */\nconst resolveWalletStandardCapabilities = (\n input: WalletStandardCapabilityInput,\n): WalletCapabilities => ({\n getBalance: false,\n getTransactionReceipt: false,\n requestAccounts: false,\n sendTransaction: input.features.signAndSendTransaction,\n signIn: input.features.signIn,\n signMessage: input.features.signMessage,\n signTransaction: input.features.signTransaction,\n subscribe: input.features.events,\n switchAccount: false,\n switchChain: input.chainCount > 1,\n});\n\nexport type { WalletStandardCapabilityInput };\nexport { resolveWalletStandardCapabilities };\n","import type { Account, ChainBase, ConnectorEvent, WalletAdapter } from \"@usebutr/core\";\nimport { bytesToBase64, logWarn, sanitizeIcon } from \"@usebutr/core\";\nimport {\n buildAccount,\n getFeature,\n pickAccountByAddress,\n pickFirstAddress,\n slugify as kitSlugify,\n} from \"@usebutr/wallet-standard-shared\";\nimport type {\n StandardConnectFeature,\n StandardDisconnectFeature,\n StandardEventsFeature,\n WalletStandardWallet,\n} from \"@usebutr/wallet-standard-shared\";\n\nimport { resolveWalletStandardCapabilities } from \"./capabilities\";\nimport type {\n SolanaSignAndSendTransactionFeature,\n SolanaSignInFeature,\n SolanaSignMessageFeature,\n SolanaSignTransactionFeature,\n} from \"./wallet-standard-types\";\n\nconst SOLANA_PREFIX = \"solana:\";\nconst SOLANA_DECIMALS = 9;\n\nconst slugify = (name: string): string => kitSlugify(\"svm\", name);\n\nconst pickSolanaChain = (wallet: WalletStandardWallet): string | null => {\n // Prefer mainnet-beta, then the first solana chain advertised.\n const mainnet = wallet.chains.find((c) => c === \"solana:mainnet\" || c === \"solana:mainnet-beta\");\n if (mainnet) {\n return mainnet;\n }\n return wallet.chains.find((c) => c.startsWith(SOLANA_PREFIX)) ?? null;\n};\n\nconst buildSolanaChain = (chainId: string, walletName: string): ChainBase => ({\n id: chainId,\n // Same stance as the EIP-6963 side: we don't ship a chain-id → name\n // table. Consumers overlay via structural typing.\n name: walletName,\n namespace: \"solana\",\n reference: chainId.slice(SOLANA_PREFIX.length),\n});\n\nconst buildSolanaAccount = (address: string, chain: ChainBase): Account =>\n buildAccount(address, chain);\n\n/**\n * Adapt a Solana Wallet Standard `Wallet` object into a butr\n * `WalletAdapter`. Returns `null` if the wallet doesn't advertise any\n * Solana chain or doesn't expose `standard:connect` — both required\n * for butr to do anything useful with it.\n *\n * **Capability gating**\n *\n * - `signMessage` is only present if the wallet advertises\n * `solana:signMessage`. Without it, calling `adapter.signMessage`\n * rejects with a clear error.\n * - `sendTx` / `sendTxToChain` use `solana:signAndSendTransaction`.\n * Without it, those methods reject.\n * - `subscribe` is wired through `standard:events`. Wallets that\n * don't expose events get a no-op unsubscribe.\n *\n * **Known limitations**\n *\n * - `getBalance` returns `{ value: 0n, formatted: \"0\", … }`. Solana\n * balance reads require an RPC connection that's outside butr's\n * scope; consumers wrap their own RPC client.\n * - `getTransactionReceipt` returns `{ status: \"Pending\" }`. Same\n * reason — needs an RPC.\n * - `switchChain` updates butr's view of the current Solana cluster\n * locally and routes subsequent `signAndSendTransaction` calls\n * through the new chain. Wallet Standard has no \"tell the wallet\n * to switch cluster\" RPC; the wallet's own UI controls its global\n * cluster setting, and `signAndSendTransaction` accepts the chain\n * per-call. The chain must be one the wallet advertises in\n * `wallet.chains`; otherwise the call rejects.\n * - `requestAccounts` re-runs `standard:connect`. Some wallets show\n * their account picker again; others (those that \"remember\"\n * authorisations) silently return the existing accounts. butr\n * refreshes the pool entry's `accounts` array either way.\n * - `switchAccount` is intentionally unimplemented. Wallet Standard\n * has no silent \"use address X\" feature — the user picks the active\n * account through the wallet's own UI. `signAndSendTransaction`\n * accepts an `account` input, so consumers needing per-tx address\n * selection can pick one from `accounts` at call time.\n */\nconst buildSvmAdapter = (\n wallet: WalletStandardWallet,\n /** Optional. Called with a function that pushes a synthetic\n * `disconnected` event to all current subscribers. The discovery\n * layer invokes it on Wallet Standard `unregister` so a connected\n * pool entry tears down when its extension is removed. */\n registerDisconnector?: (emit: () => void) => void,\n): WalletAdapter | null => {\n const solanaChainId = pickSolanaChain(wallet);\n if (!solanaChainId) {\n return null;\n }\n const connect = getFeature<StandardConnectFeature>(wallet, \"standard:connect\");\n if (!connect) {\n return null;\n }\n\n const disconnect = getFeature<StandardDisconnectFeature>(wallet, \"standard:disconnect\");\n const events = getFeature<StandardEventsFeature>(wallet, \"standard:events\");\n const signMessage = getFeature<SolanaSignMessageFeature>(wallet, \"solana:signMessage\");\n const signAndSendTx = getFeature<SolanaSignAndSendTransactionFeature>(\n wallet,\n \"solana:signAndSendTransaction\",\n );\n const signTx = getFeature<SolanaSignTransactionFeature>(wallet, \"solana:signTransaction\");\n const signIn = getFeature<SolanaSignInFeature>(wallet, \"solana:signIn\");\n\n // Current Solana cluster. Mutable so `switchChain` can route subsequent\n // `signAndSendTransaction` calls through a different cluster without\n // rebuilding the adapter. butr's reducer learns about chain changes\n // via the `accountChanged` events emitted to subscribers below.\n let currentChainId = solanaChainId;\n const currentChain = (): ChainBase => buildSolanaChain(currentChainId, wallet.name);\n\n // Local listener set so `switchChain` (and `requestAccounts`) can\n // synthesise `accountChanged` events that flow into butr's pool.\n // Wallet-native events from `standard:events` are bridged alongside.\n const listeners = new Set<(event: ConnectorEvent) => void>();\n const notifyAccountChanged = () => {\n if (wallet.accounts.length === 0) {\n return;\n }\n const chain = currentChain();\n const built = wallet.accounts.map((a) => buildSolanaAccount(a.address, chain));\n const first = built[0];\n if (!first) {\n return;\n }\n for (const listener of listeners) {\n listener({ account: first, accounts: built, type: \"accountChanged\" });\n }\n };\n\n registerDisconnector?.(() => {\n for (const listener of listeners) {\n listener({ type: \"disconnected\" });\n }\n });\n\n return {\n capabilities: resolveWalletStandardCapabilities({\n chainCount: wallet.chains.length,\n features: {\n events: Boolean(events),\n signAndSendTransaction: Boolean(signAndSendTx),\n signIn: Boolean(signIn),\n signMessage: Boolean(signMessage),\n signTransaction: Boolean(signTx),\n },\n }),\n chainPlatform: \"svm\",\n\n async connect(opts) {\n // Forward `silent` so eager hydration restores already-authorized\n // accounts without re-opening the wallet's approval UI. Wallets\n // that don't honour `silent` fall back to their normal behaviour.\n await connect.connect(opts?.silent ? { silent: true } : undefined);\n },\n\n async disconnect() {\n if (disconnect) {\n try {\n await disconnect.disconnect();\n } catch (error) {\n logWarn(\"[butr] Wallet Standard disconnect threw:\", error);\n }\n }\n },\n\n getAccount() {\n const address = pickFirstAddress(wallet.accounts);\n if (!address) {\n return Promise.resolve(null);\n }\n return Promise.resolve(buildSolanaAccount(address, currentChain()));\n },\n\n getAccounts() {\n const chain = currentChain();\n return Promise.resolve(wallet.accounts.map((a) => buildSolanaAccount(a.address, chain)));\n },\n\n getBalance() {\n // Wallet Standard exposes no balance feature. Consumers wrap\n // their own RPC client; this default keeps the type signature\n // honoured without lying about a value we don't have.\n return Promise.resolve({\n decimals: SOLANA_DECIMALS,\n formatted: \"0\",\n symbol: \"SOL\",\n value: 0n,\n });\n },\n\n getSigner() {\n // Consumers cast to their preferred wrapper (e.g. an Anchor\n // provider built around the wallet's signTransaction feature).\n return Promise.resolve(wallet);\n },\n\n getTransactionReceipt() {\n // Same reason as `getBalance` — no RPC.\n return Promise.resolve({ status: \"Pending\" as const });\n },\n\n icon: sanitizeIcon(wallet.icon),\n id: slugify(wallet.name),\n name: wallet.name,\n\n async requestAccounts() {\n // Wallet Standard has no equivalent of EIP-2255's\n // `wallet_requestPermissions`. The closest we can do is re-run\n // `standard:connect`, which prompts the user on wallets that\n // surface their picker each time and silently returns the\n // existing list on wallets that remember authorisations.\n // butr's runtime calls `getAccounts()` afterwards to refresh\n // the pool entry, so newly-exposed addresses appear either way.\n await connect.connect();\n },\n\n async sendTx(tx, account) {\n if (!signAndSendTx) {\n throw new Error(`Wallet ${wallet.name} does not advertise solana:signAndSendTransaction`);\n }\n const wsAccount = account\n ? pickAccountByAddress(wallet.accounts, account.walletAddress)\n : (wallet.accounts[0] ?? null);\n if (!wsAccount) {\n throw new Error(\"No connected account\");\n }\n if (!(tx instanceof Uint8Array)) {\n throw new TypeError(\"SVM sendTx expects a serialized transaction (Uint8Array)\");\n }\n const [output] = await signAndSendTx.signAndSendTransaction({\n account: wsAccount,\n chain: currentChainId,\n transaction: tx,\n });\n if (!output) {\n throw new Error(\"signAndSendTransaction returned no outputs\");\n }\n return bytesToBase64(output.signature);\n },\n\n sendTxToChain(tx, _targetChainId, account, cb) {\n // Solana Wallet Standard's signAndSendTransaction takes the\n // target chain directly. `targetChainId` is the decimal-string\n // form butr uses, but the wallet expects a CAIP-2 chain string.\n // For now we honour the connector's primary `solanaChainId` and\n // let consumers route per-chain at a higher level if they need\n // multi-cluster support.\n cb?.();\n return this.sendTx(tx, account);\n },\n\n async signMessage(msg, account) {\n if (!signMessage) {\n throw new Error(`Wallet ${wallet.name} does not advertise solana:signMessage`);\n }\n const wsAccount = account\n ? pickAccountByAddress(wallet.accounts, account.walletAddress)\n : (wallet.accounts[0] ?? null);\n if (!wsAccount) {\n throw new Error(\"No connected account\");\n }\n const [output] = await signMessage.signMessage({ account: wsAccount, message: msg });\n if (!output) {\n throw new Error(\"signMessage returned no outputs\");\n }\n return { signature: output.signature, signedMessage: output.signedMessage };\n },\n\n ...(signTx\n ? {\n async signTransaction(tx, account) {\n // Sign-only path for wallets without\n // solana:signAndSendTransaction. butr ships no RPC, so the\n // caller broadcasts the returned bytes with their own\n // client. Gated by capabilities.signTransaction.\n const wsAccount = account\n ? pickAccountByAddress(wallet.accounts, account.walletAddress)\n : (wallet.accounts[0] ?? null);\n if (!wsAccount) {\n throw new Error(\"No connected account\");\n }\n if (!(tx instanceof Uint8Array)) {\n throw new TypeError(\n \"SVM signTransaction expects a serialized transaction (Uint8Array)\",\n );\n }\n const [output] = await signTx.signTransaction({\n account: wsAccount,\n chain: currentChainId,\n transaction: tx,\n });\n if (!output) {\n throw new Error(\"signTransaction returned no outputs\");\n }\n return output.signedTransaction;\n },\n }\n : {}),\n\n ...(signIn\n ? {\n async signIn(input) {\n // Sign In With Solana. Returns the authenticated account\n // plus the signed statement for server-side verification.\n const [output] = await signIn.signIn(input);\n if (!output) {\n throw new Error(\"signIn returned no outputs\");\n }\n return {\n account: buildSolanaAccount(output.account.address, currentChain()),\n signature: output.signature,\n signedMessage: output.signedMessage,\n };\n },\n }\n : {}),\n\n subscribe(listener) {\n listeners.add(listener);\n let unsubWallet: (() => void) | null = null;\n if (events) {\n const unsub = events.on(\"change\", (changes) => {\n // Chain change: the wallet switched cluster. Re-point\n // currentChainId so subsequent signAndSendTransaction routes\n // through it, mirroring `switchChain`'s local-state model.\n if (changes.chains) {\n const next =\n changes.chains.find((c) => c === \"solana:mainnet\" || c === \"solana:mainnet-beta\") ??\n changes.chains.find((c) => c.startsWith(SOLANA_PREFIX));\n if (next) {\n currentChainId = next;\n }\n }\n\n if (changes.accounts) {\n if (changes.accounts.length === 0) {\n listener({ type: \"disconnected\" });\n return;\n }\n // Forward the FULL accounts list — Wallet Standard's\n // change.accounts reflects the wallet's current exposure\n // set. Mirroring it into the pool entry keeps the array in\n // sync with what the wallet actually allows us to sign with,\n // so single-account-exposure wallets (Phantom Solana,\n // MetaMask Snap) don't accumulate stale addresses.\n const chain = currentChain();\n const built = changes.accounts.map((a) => buildSolanaAccount(a.address, chain));\n const first = built[0];\n if (!first) {\n return;\n }\n listener({ account: first, accounts: built, type: \"accountChanged\" });\n return;\n }\n\n // Chains changed without an accounts change — still push the\n // new chain into the pool entry via the same path switchChain\n // uses, so consumers see the cluster swap.\n if (changes.chains) {\n notifyAccountChanged();\n }\n });\n unsubWallet = () => unsub();\n }\n return () => {\n listeners.delete(listener);\n unsubWallet?.();\n };\n },\n\n switchChain(chain) {\n if (chain.namespace !== \"solana\") {\n throw new Error(\n `SVM adapter received non-Solana chain \"${chain.id}\". Pass a chain with namespace \"solana\".`,\n );\n }\n // The wallet must advertise this cluster — otherwise\n // signAndSendTransaction will reject the chain string later.\n if (!wallet.chains.includes(chain.id)) {\n throw new Error(\n `Wallet ${wallet.name} does not advertise chain \"${chain.id}\". Available: ${wallet.chains.join(\", \")}`,\n );\n }\n currentChainId = chain.id;\n // Synthesise an `accountChanged` so butr's reducer updates the\n // pool entry's chain — the wallet itself has no event to fire here.\n notifyAccountChanged();\n return Promise.resolve();\n },\n };\n};\n\nexport { buildSvmAdapter, slugify };\n","import type { WalletAdapter } from \"@usebutr/core\";\nimport { discoverWalletStandard } from \"@usebutr/wallet-standard-shared\";\n\nimport { buildSvmAdapter } from \"./wallet-standard-adapter\";\n\n/**\n * Subscribe to Solana Wallet Standard announcements.\n * Spec: https://github.com/wallet-standard/wallet-standard\n *\n * Runtime requires the `@wallet-standard/app` package, which is an\n * **optional peer dependency** of butr. EVM-only consumers don't need\n * to install it — discovery will silently skip the SVM side if the\n * module isn't resolvable.\n *\n * Install for SVM auto-discovery:\n *\n * npm install @wallet-standard/app\n *\n * The function returns a synchronous unsubscribe that's safe to call\n * before the dynamic import has resolved.\n *\n * Implementation: the discovery loop (dynamic import, dedupe, register\n * / unregister bridge) lives in `@usebutr/wallet-standard-shared`. This\n * file's only job is to hand the kit a per-wallet builder.\n */\nconst discoverSvmAdapters = (onAdapter: (adapter: WalletAdapter) => void): (() => void) =>\n discoverWalletStandard(onAdapter, (wallet, registerDisconnector) =>\n buildSvmAdapter(wallet, registerDisconnector),\n );\n\nexport { discoverSvmAdapters };\n","import type { ChainBase } from \"@usebutr/core\";\n\n// Solana chain identifiers follow the Wallet Standard convention\n// (`solana:mainnet` / `solana:devnet` / `solana:testnet`) rather than\n// the strict CAIP-2 genesis-hash form (`solana:5eykt…`). Phantom,\n// Solflare, Backpack and the rest of the Wallet Standard ecosystem\n// advertise these shortnames in `wallet.chains`, and the SVM adapter's\n// `switchChain` rejects anything not in that set. The genesis-hash form\n// remains valid CAIP-2 but isn't what real wallets exchange.\nconst SVM_CHAINS = {\n devnet: { id: \"solana:devnet\", name: \"Solana Devnet\", namespace: \"solana\", reference: \"devnet\" },\n mainnet: {\n id: \"solana:mainnet\",\n name: \"Solana Mainnet\",\n namespace: \"solana\",\n reference: \"mainnet\",\n },\n testnet: {\n id: \"solana:testnet\",\n name: \"Solana Testnet\",\n namespace: \"solana\",\n reference: \"testnet\",\n },\n} as const satisfies Record<string, ChainBase>;\n\nconst SVM_CHAINS_LIST: ReadonlyArray<ChainBase> = Object.values(SVM_CHAINS);\n\nexport { SVM_CHAINS, SVM_CHAINS_LIST };\n","import type { PlatformDiscoverer } from \"@usebutr/core\";\n\nimport { discoverSvmAdapters } from \"./wallet-standard\";\n\n/**\n * SVM's `PlatformDiscoverer`: Solana Wallet Standard announcements. No\n * legacy fallback — Solana wallets standardised on Wallet Standard\n * before butr existed, so there's no `window.solana` injected path\n * worth shimming.\n */\nconst svmDiscoverer: PlatformDiscoverer = {\n platform: \"svm\",\n subscribe: discoverSvmAdapters,\n};\n\nexport { svmDiscoverer };\n"],"mappings":";;;;;;;;;;;;;;;;AAyBA,MAAM,qCACJ,WACwB;CACxB,YAAY;CACZ,uBAAuB;CACvB,iBAAiB;CACjB,iBAAiB,MAAM,SAAS;CAChC,QAAQ,MAAM,SAAS;CACvB,aAAa,MAAM,SAAS;CAC5B,iBAAiB,MAAM,SAAS;CAChC,WAAW,MAAM,SAAS;CAC1B,eAAe;CACf,aAAa,MAAM,aAAa;AAClC;;;;ACdA,MAAM,gBAAgB;AACtB,MAAM,kBAAkB;AAExB,MAAM,WAAW,SAAyBA,UAAW,OAAO,IAAI;AAEhE,MAAM,mBAAmB,WAAgD;CAEvE,MAAM,UAAU,OAAO,OAAO,MAAM,MAAM,MAAM,oBAAoB,MAAM,qBAAqB;CAC/F,IAAI,SACF,OAAO;CAET,OAAO,OAAO,OAAO,MAAM,MAAM,EAAE,WAAW,aAAa,CAAC,KAAK;AACnE;AAEA,MAAM,oBAAoB,SAAiB,gBAAmC;CAC5E,IAAI;CAGJ,MAAM;CACN,WAAW;CACX,WAAW,QAAQ,MAAM,CAAoB;AAC/C;AAEA,MAAM,sBAAsB,SAAiB,UAC3C,aAAa,SAAS,KAAK;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0C7B,MAAM,mBACJ,QAKA,yBACyB;CACzB,MAAM,gBAAgB,gBAAgB,MAAM;CAC5C,IAAI,CAAC,eACH,OAAO;CAET,MAAM,UAAU,WAAmC,QAAQ,kBAAkB;CAC7E,IAAI,CAAC,SACH,OAAO;CAGT,MAAM,aAAa,WAAsC,QAAQ,qBAAqB;CACtF,MAAM,SAAS,WAAkC,QAAQ,iBAAiB;CAC1E,MAAM,cAAc,WAAqC,QAAQ,oBAAoB;CACrF,MAAM,gBAAgB,WACpB,QACA,+BACF;CACA,MAAM,SAAS,WAAyC,QAAQ,wBAAwB;CACxF,MAAM,SAAS,WAAgC,QAAQ,eAAe;CAMtE,IAAI,iBAAiB;CACrB,MAAM,qBAAgC,iBAAiB,gBAAgB,OAAO,IAAI;CAKlF,MAAM,4BAAY,IAAI,IAAqC;CAC3D,MAAM,6BAA6B;EACjC,IAAI,OAAO,SAAS,WAAW,GAC7B;EAEF,MAAM,QAAQ,aAAa;EAC3B,MAAM,QAAQ,OAAO,SAAS,KAAK,MAAM,mBAAmB,EAAE,SAAS,KAAK,CAAC;EAC7E,MAAM,QAAQ,MAAM;EACpB,IAAI,CAAC,OACH;EAEF,KAAK,MAAM,YAAY,WACrB,SAAS;GAAE,SAAS;GAAO,UAAU;GAAO,MAAM;EAAiB,CAAC;CAExE;CAEA,6BAA6B;EAC3B,KAAK,MAAM,YAAY,WACrB,SAAS,EAAE,MAAM,eAAe,CAAC;CAErC,CAAC;CAED,OAAO;EACL,cAAc,kCAAkC;GAC9C,YAAY,OAAO,OAAO;GAC1B,UAAU;IACR,QAAQ,QAAQ,MAAM;IACtB,wBAAwB,QAAQ,aAAa;IAC7C,QAAQ,QAAQ,MAAM;IACtB,aAAa,QAAQ,WAAW;IAChC,iBAAiB,QAAQ,MAAM;GACjC;EACF,CAAC;EACD,eAAe;EAEf,MAAM,QAAQ,MAAM;GAIlB,MAAM,QAAQ,QAAQ,MAAM,SAAS,EAAE,QAAQ,KAAK,IAAI,MAAS;EACnE;EAEA,MAAM,aAAa;GACjB,IAAI,YACF,IAAI;IACF,MAAM,WAAW,WAAW;GAC9B,SAAS,OAAO;IACd,QAAQ,4CAA4C,KAAK;GAC3D;EAEJ;EAEA,aAAa;GACX,MAAM,UAAU,iBAAiB,OAAO,QAAQ;GAChD,IAAI,CAAC,SACH,OAAO,QAAQ,QAAQ,IAAI;GAE7B,OAAO,QAAQ,QAAQ,mBAAmB,SAAS,aAAa,CAAC,CAAC;EACpE;EAEA,cAAc;GACZ,MAAM,QAAQ,aAAa;GAC3B,OAAO,QAAQ,QAAQ,OAAO,SAAS,KAAK,MAAM,mBAAmB,EAAE,SAAS,KAAK,CAAC,CAAC;EACzF;EAEA,aAAa;GAIX,OAAO,QAAQ,QAAQ;IACrB,UAAU;IACV,WAAW;IACX,QAAQ;IACR,OAAO;GACT,CAAC;EACH;EAEA,YAAY;GAGV,OAAO,QAAQ,QAAQ,MAAM;EAC/B;EAEA,wBAAwB;GAEtB,OAAO,QAAQ,QAAQ,EAAE,QAAQ,UAAmB,CAAC;EACvD;EAEA,MAAM,aAAa,OAAO,IAAI;EAC9B,IAAI,QAAQ,OAAO,IAAI;EACvB,MAAM,OAAO;EAEb,MAAM,kBAAkB;GAQtB,MAAM,QAAQ,QAAQ;EACxB;EAEA,MAAM,OAAO,IAAI,SAAS;GACxB,IAAI,CAAC,eACH,MAAM,IAAI,MAAM,UAAU,OAAO,KAAK,kDAAkD;GAE1F,MAAM,YAAY,UACd,qBAAqB,OAAO,UAAU,QAAQ,aAAa,IAC1D,OAAO,SAAS,MAAM;GAC3B,IAAI,CAAC,WACH,MAAM,IAAI,MAAM,sBAAsB;GAExC,IAAI,EAAE,cAAc,aAClB,MAAM,IAAI,UAAU,0DAA0D;GAEhF,MAAM,CAAC,UAAU,MAAM,cAAc,uBAAuB;IAC1D,SAAS;IACT,OAAO;IACP,aAAa;GACf,CAAC;GACD,IAAI,CAAC,QACH,MAAM,IAAI,MAAM,4CAA4C;GAE9D,OAAO,cAAc,OAAO,SAAS;EACvC;EAEA,cAAc,IAAI,gBAAgB,SAAS,IAAI;GAO7C,KAAK;GACL,OAAO,KAAK,OAAO,IAAI,OAAO;EAChC;EAEA,MAAM,YAAY,KAAK,SAAS;GAC9B,IAAI,CAAC,aACH,MAAM,IAAI,MAAM,UAAU,OAAO,KAAK,uCAAuC;GAE/E,MAAM,YAAY,UACd,qBAAqB,OAAO,UAAU,QAAQ,aAAa,IAC1D,OAAO,SAAS,MAAM;GAC3B,IAAI,CAAC,WACH,MAAM,IAAI,MAAM,sBAAsB;GAExC,MAAM,CAAC,UAAU,MAAM,YAAY,YAAY;IAAE,SAAS;IAAW,SAAS;GAAI,CAAC;GACnF,IAAI,CAAC,QACH,MAAM,IAAI,MAAM,iCAAiC;GAEnD,OAAO;IAAE,WAAW,OAAO;IAAW,eAAe,OAAO;GAAc;EAC5E;EAEA,GAAI,SACA,EACE,MAAM,gBAAgB,IAAI,SAAS;GAKjC,MAAM,YAAY,UACd,qBAAqB,OAAO,UAAU,QAAQ,aAAa,IAC1D,OAAO,SAAS,MAAM;GAC3B,IAAI,CAAC,WACH,MAAM,IAAI,MAAM,sBAAsB;GAExC,IAAI,EAAE,cAAc,aAClB,MAAM,IAAI,UACR,mEACF;GAEF,MAAM,CAAC,UAAU,MAAM,OAAO,gBAAgB;IAC5C,SAAS;IACT,OAAO;IACP,aAAa;GACf,CAAC;GACD,IAAI,CAAC,QACH,MAAM,IAAI,MAAM,qCAAqC;GAEvD,OAAO,OAAO;EAChB,EACF,IACA,CAAC;EAEL,GAAI,SACA,EACE,MAAM,OAAO,OAAO;GAGlB,MAAM,CAAC,UAAU,MAAM,OAAO,OAAO,KAAK;GAC1C,IAAI,CAAC,QACH,MAAM,IAAI,MAAM,4BAA4B;GAE9C,OAAO;IACL,SAAS,mBAAmB,OAAO,QAAQ,SAAS,aAAa,CAAC;IAClE,WAAW,OAAO;IAClB,eAAe,OAAO;GACxB;EACF,EACF,IACA,CAAC;EAEL,UAAU,UAAU;GAClB,UAAU,IAAI,QAAQ;GACtB,IAAI,cAAmC;GACvC,IAAI,QAAQ;IACV,MAAM,QAAQ,OAAO,GAAG,WAAW,YAAY;KAI7C,IAAI,QAAQ,QAAQ;MAClB,MAAM,OACJ,QAAQ,OAAO,MAAM,MAAM,MAAM,oBAAoB,MAAM,qBAAqB,KAChF,QAAQ,OAAO,MAAM,MAAM,EAAE,WAAW,aAAa,CAAC;MACxD,IAAI,MACF,iBAAiB;KAErB;KAEA,IAAI,QAAQ,UAAU;MACpB,IAAI,QAAQ,SAAS,WAAW,GAAG;OACjC,SAAS,EAAE,MAAM,eAAe,CAAC;OACjC;MACF;MAOA,MAAM,QAAQ,aAAa;MAC3B,MAAM,QAAQ,QAAQ,SAAS,KAAK,MAAM,mBAAmB,EAAE,SAAS,KAAK,CAAC;MAC9E,MAAM,QAAQ,MAAM;MACpB,IAAI,CAAC,OACH;MAEF,SAAS;OAAE,SAAS;OAAO,UAAU;OAAO,MAAM;MAAiB,CAAC;MACpE;KACF;KAKA,IAAI,QAAQ,QACV,qBAAqB;IAEzB,CAAC;IACD,oBAAoB,MAAM;GAC5B;GACA,aAAa;IACX,UAAU,OAAO,QAAQ;IACzB,cAAc;GAChB;EACF;EAEA,YAAY,OAAO;GACjB,IAAI,MAAM,cAAc,UACtB,MAAM,IAAI,MACR,0CAA0C,MAAM,GAAG,yCACrD;GAIF,IAAI,CAAC,OAAO,OAAO,SAAS,MAAM,EAAE,GAClC,MAAM,IAAI,MACR,UAAU,OAAO,KAAK,6BAA6B,MAAM,GAAG,gBAAgB,OAAO,OAAO,KAAK,IAAI,GACrG;GAEF,iBAAiB,MAAM;GAGvB,qBAAqB;GACrB,OAAO,QAAQ,QAAQ;EACzB;CACF;AACF;;;;;;;;;;;;;;;;;;;;;;;;AC3XA,MAAM,uBAAuB,cAC3B,uBAAuB,YAAY,QAAQ,yBACzC,gBAAgB,QAAQ,oBAAoB,CAC9C;;;;ACnBF,MAAM,aAAa;CACjB,QAAQ;EAAE,IAAI;EAAiB,MAAM;EAAiB,WAAW;EAAU,WAAW;CAAS;CAC/F,SAAS;EACP,IAAI;EACJ,MAAM;EACN,WAAW;EACX,WAAW;CACb;CACA,SAAS;EACP,IAAI;EACJ,MAAM;EACN,WAAW;EACX,WAAW;CACb;AACF;AAEA,MAAM,kBAA4C,OAAO,OAAO,UAAU;;;;;;;;;;ACf1E,MAAM,gBAAoC;CACxC,UAAU;CACV,WAAW;AACb"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["kitSlugify"],"sources":["../src/capabilities.ts","../src/wallet-standard-adapter.ts","../src/wallet-standard.ts","../src/chains.ts","../src/discoverer.ts"],"sourcesContent":["import type { WalletCapabilities } from \"@usebutr/core\";\n\ntype WalletStandardCapabilityInput = {\n chainCount: number;\n features: {\n events: boolean;\n signAndSendTransaction: boolean;\n signIn: boolean;\n signMessage: boolean;\n signTransaction: boolean;\n };\n};\n\n/**\n * Wallet Standard capability mapping.\n *\n * - `getBalance` / `getTransactionReceipt`: Wallet Standard exposes no\n * balance/receipt RPC.\n * - `requestAccounts`: no programmatic equivalent of EIP-2255; re-running\n * `standard:connect` doesn't produce new accounts on any major Wallet\n * Standard wallet in practice (Phantom Solana, MetaMask Snap, Solflare,\n * Backpack).\n * - `switchChain`: true when more than one chain is advertised; with a\n * single chain there's nothing to switch to.\n */\nconst resolveWalletStandardCapabilities = (\n input: WalletStandardCapabilityInput,\n): WalletCapabilities => ({\n getBalance: false,\n getTransactionReceipt: false,\n requestAccounts: false,\n sendTransaction: input.features.signAndSendTransaction,\n signIn: input.features.signIn,\n signMessage: input.features.signMessage,\n signTransaction: input.features.signTransaction,\n subscribe: input.features.events,\n switchAccount: false,\n switchChain: input.chainCount > 1,\n});\n\nexport type { WalletStandardCapabilityInput };\nexport { resolveWalletStandardCapabilities };\n","import type { ChainBase, ConnectorEvent, WalletAdapter } from \"@usebutr/core\";\nimport { bytesToBase64, logWarn, sanitizeIcon } from \"@usebutr/core\";\nimport {\n buildAccount,\n getFeature,\n pickAccountByAddress,\n pickFirstAddress,\n slugify as kitSlugify,\n} from \"@usebutr/wallet-standard-shared\";\nimport type {\n StandardConnectFeature,\n StandardDisconnectFeature,\n StandardEventsFeature,\n WalletStandardWallet,\n} from \"@usebutr/wallet-standard-shared\";\n\nimport { resolveWalletStandardCapabilities } from \"./capabilities\";\nimport type {\n SolanaSignAndSendTransactionFeature,\n SolanaSignInFeature,\n SolanaSignMessageFeature,\n SolanaSignTransactionFeature,\n} from \"./wallet-standard-types\";\n\nconst SOLANA_PREFIX = \"solana:\";\nconst SOLANA_DECIMALS = 9;\n\nconst slugify = (name: string): string => kitSlugify(\"svm\", name);\n\nconst pickSolanaChain = (wallet: WalletStandardWallet): string | null => {\n const mainnet = wallet.chains.find((c) => c === \"solana:mainnet\" || c === \"solana:mainnet-beta\");\n if (mainnet) {\n return mainnet;\n }\n return wallet.chains.find((c) => c.startsWith(SOLANA_PREFIX)) ?? null;\n};\n\nconst buildSolanaChain = (chainId: string, walletName: string): ChainBase => ({\n id: chainId,\n // Same stance as the EIP-6963 side: we don't ship a chain-id → name\n // table. Consumers overlay via structural typing.\n name: walletName,\n namespace: \"solana\",\n reference: chainId.slice(SOLANA_PREFIX.length),\n});\n\n/**\n * Adapt a Solana Wallet Standard `Wallet` object into a butr\n * `WalletAdapter`. Returns `null` if the wallet doesn't advertise any\n * Solana chain or doesn't expose `standard:connect`; both required\n * for butr to do anything useful with it.\n *\n * **Capability gating**\n *\n * - `signMessage` is only present if the wallet advertises\n * `solana:signMessage`. Without it, calling `adapter.signMessage`\n * rejects with a clear error.\n * - `sendTx` / `sendTxToChain` use `solana:signAndSendTransaction`.\n * Without it, those methods reject.\n * - `subscribe` is wired through `standard:events`. Wallets that\n * don't expose events get a no-op unsubscribe.\n *\n * **Known limitations**\n *\n * - `getBalance` returns `{ value: 0n, formatted: \"0\", … }`. Solana\n * balance reads require an RPC connection that's outside butr's\n * scope; consumers wrap their own RPC client.\n * - `getTransactionReceipt` returns `{ status: \"Pending\" }`. Same\n * reason; needs an RPC.\n * - `switchChain` updates butr's view of the current Solana cluster\n * locally and routes subsequent `signAndSendTransaction` calls\n * through the new chain. Wallet Standard has no \"tell the wallet\n * to switch cluster\" RPC; the wallet's own UI controls its global\n * cluster setting, and `signAndSendTransaction` accepts the chain\n * per-call. The chain must be one the wallet advertises in\n * `wallet.chains`; otherwise the call rejects.\n * - `requestAccounts` re-runs `standard:connect`. Some wallets show\n * their account picker again; others (those that \"remember\"\n * authorisations) silently return the existing accounts. butr\n * refreshes the pool entry's `accounts` array either way.\n * - `switchAccount` is intentionally unimplemented. Wallet Standard\n * has no silent \"use address X\" feature; the user picks the active\n * account through the wallet's own UI. `signAndSendTransaction`\n * accepts an `account` input, so consumers needing per-tx address\n * selection can pick one from `accounts` at call time.\n */\nconst buildSvmAdapter = (\n wallet: WalletStandardWallet,\n /** Optional. Called with a function that pushes a synthetic\n * `disconnected` event to all current subscribers. The discovery\n * layer invokes it on Wallet Standard `unregister` so a connected\n * pool entry tears down when its extension is removed. */\n registerDisconnector?: (emit: () => void) => void,\n): WalletAdapter | null => {\n const solanaChainId = pickSolanaChain(wallet);\n if (solanaChainId === null) {\n return null;\n }\n const connect = getFeature<StandardConnectFeature>(wallet, \"standard:connect\");\n if (connect === undefined) {\n return null;\n }\n\n const disconnect = getFeature<StandardDisconnectFeature>(wallet, \"standard:disconnect\");\n const events = getFeature<StandardEventsFeature>(wallet, \"standard:events\");\n const signMessage = getFeature<SolanaSignMessageFeature>(wallet, \"solana:signMessage\");\n const signAndSendTx = getFeature<SolanaSignAndSendTransactionFeature>(\n wallet,\n \"solana:signAndSendTransaction\",\n );\n const signTx = getFeature<SolanaSignTransactionFeature>(wallet, \"solana:signTransaction\");\n const signIn = getFeature<SolanaSignInFeature>(wallet, \"solana:signIn\");\n\n let currentChainId = solanaChainId;\n const currentChain = (): ChainBase => buildSolanaChain(currentChainId, wallet.name);\n\n // Local listener set so `switchChain` (and `requestAccounts`) can\n // synthesise `accountChanged` events that flow into butr's pool.\n // Wallet-native events from `standard:events` are bridged alongside.\n const listeners = new Set<(event: ConnectorEvent) => void>();\n const notifyAccountChanged = () => {\n if (wallet.accounts.length === 0) {\n return;\n }\n const chain = currentChain();\n const built = wallet.accounts.map((a) => buildAccount(a.address, chain));\n const first = built[0];\n if (first === undefined) {\n return;\n }\n for (const listener of listeners) {\n listener({ account: first, accounts: built, type: \"accountChanged\" });\n }\n };\n\n registerDisconnector?.(() => {\n for (const listener of listeners) {\n listener({ type: \"disconnected\" });\n }\n });\n\n const signAndSend = async (tx: unknown, account?: { walletAddress: string }): Promise<string> => {\n if (signAndSendTx === undefined) {\n throw new Error(`Wallet ${wallet.name} does not advertise solana:signAndSendTransaction`);\n }\n const wsAccount = account\n ? pickAccountByAddress(wallet.accounts, account.walletAddress)\n : wallet.accounts[0];\n if (wsAccount === undefined) {\n throw new Error(\"No connected account\");\n }\n if (!(tx instanceof Uint8Array)) {\n throw new TypeError(\"SVM sendTx expects a serialized transaction (Uint8Array)\");\n }\n const [output] = await signAndSendTx.signAndSendTransaction({\n account: wsAccount,\n chain: currentChainId,\n transaction: tx,\n });\n if (output === undefined) {\n throw new Error(\"signAndSendTransaction returned no outputs\");\n }\n return bytesToBase64(output.signature);\n };\n\n return {\n capabilities: resolveWalletStandardCapabilities({\n chainCount: wallet.chains.length,\n features: {\n events: Boolean(events),\n signAndSendTransaction: Boolean(signAndSendTx),\n signIn: Boolean(signIn),\n signMessage: Boolean(signMessage),\n signTransaction: Boolean(signTx),\n },\n }),\n chainPlatform: \"svm\",\n\n async connect(opts) {\n // Forward `silent` so eager hydration restores already-authorized\n // accounts without re-opening the wallet's approval UI. Wallets\n // that don't honour `silent` fall back to their normal behaviour.\n await connect.connect(opts?.silent === true ? { silent: true } : undefined);\n },\n\n async disconnect() {\n if (disconnect !== undefined) {\n try {\n await disconnect.disconnect();\n } catch (error) {\n logWarn(\"[butr] Wallet Standard disconnect threw:\", error);\n }\n }\n },\n\n getAccount: () => {\n const address = pickFirstAddress(wallet.accounts);\n if (address === null) {\n return Promise.resolve(null);\n }\n return Promise.resolve(buildAccount(address, currentChain()));\n },\n\n getAccounts: () => {\n const chain = currentChain();\n return Promise.resolve(wallet.accounts.map((a) => buildAccount(a.address, chain)));\n },\n\n // Wallet Standard exposes no balance feature. Consumers wrap their\n // own RPC client; this default keeps the type signature honoured\n // without lying about a value we don't have.\n getBalance: () =>\n Promise.resolve({\n decimals: SOLANA_DECIMALS,\n formatted: \"0\",\n symbol: \"SOL\",\n value: 0n,\n }),\n\n // Consumers wrap this in their preferred provider (e.g. an Anchor\n // provider built around the wallet's signTransaction feature).\n getSigner: () => Promise.resolve(wallet),\n\n getTransactionReceipt: () => Promise.resolve({ status: \"Pending\" as const }),\n\n icon: sanitizeIcon(wallet.icon),\n id: slugify(wallet.name),\n name: wallet.name,\n\n async requestAccounts() {\n // Wallet Standard has no equivalent of EIP-2255's\n // `wallet_requestPermissions`. The closest we can do is re-run\n // `standard:connect`, which prompts the user on wallets that\n // surface their picker each time and silently returns the\n // existing list on wallets that remember authorisations.\n // butr's runtime calls `getAccounts()` afterwards to refresh\n // the pool entry, so newly-exposed addresses appear either way.\n await connect.connect();\n },\n\n sendTx: (tx, account) => signAndSend(tx, account),\n\n sendTxToChain: (tx, _targetChainId, account, cb) => {\n // Solana Wallet Standard's signAndSendTransaction takes the\n // target chain directly. `targetChainId` is the decimal-string\n // form butr uses, but the wallet expects a CAIP-2 chain string.\n // For now we honour the connector's primary `solanaChainId` and\n // let consumers route per-chain at a higher level if they need\n // multi-cluster support.\n cb?.();\n return signAndSend(tx, account);\n },\n\n async signMessage(msg, account) {\n if (signMessage === undefined) {\n throw new Error(`Wallet ${wallet.name} does not advertise solana:signMessage`);\n }\n const wsAccount = account\n ? pickAccountByAddress(wallet.accounts, account.walletAddress)\n : wallet.accounts[0];\n if (wsAccount === undefined) {\n throw new Error(\"No connected account\");\n }\n const [output] = await signMessage.signMessage({ account: wsAccount, message: msg });\n if (output === undefined) {\n throw new Error(\"signMessage returned no outputs\");\n }\n return { signature: output.signature, signedMessage: output.signedMessage };\n },\n\n ...(signTx === undefined\n ? {}\n : {\n async signTransaction(tx, account) {\n const wsAccount = account\n ? pickAccountByAddress(wallet.accounts, account.walletAddress)\n : wallet.accounts[0];\n if (wsAccount === undefined) {\n throw new Error(\"No connected account\");\n }\n if (!(tx instanceof Uint8Array)) {\n throw new TypeError(\n \"SVM signTransaction expects a serialized transaction (Uint8Array)\",\n );\n }\n const [output] = await signTx.signTransaction({\n account: wsAccount,\n chain: currentChainId,\n transaction: tx,\n });\n if (output === undefined) {\n throw new Error(\"signTransaction returned no outputs\");\n }\n return output.signedTransaction;\n },\n }),\n\n ...(signIn === undefined\n ? {}\n : {\n async signIn(input) {\n const [output] = await signIn.signIn(input);\n if (output === undefined) {\n throw new Error(\"signIn returned no outputs\");\n }\n return {\n account: buildAccount(output.account.address, currentChain()),\n signature: output.signature,\n signedMessage: output.signedMessage,\n };\n },\n }),\n\n subscribe(listener) {\n listeners.add(listener);\n let unsubWallet: (() => void) | null = null;\n if (events !== undefined) {\n const unsub = events.on(\"change\", (changes) => {\n // Chain change: the wallet switched cluster. Re-point\n // currentChainId so subsequent signAndSendTransaction routes\n // through it, mirroring `switchChain`'s local-state model.\n if (changes.chains !== undefined) {\n const next =\n changes.chains.find((c) => c === \"solana:mainnet\" || c === \"solana:mainnet-beta\") ??\n changes.chains.find((c) => c.startsWith(SOLANA_PREFIX));\n if (next !== undefined) {\n currentChainId = next;\n }\n }\n\n if (changes.accounts !== undefined) {\n if (changes.accounts.length === 0) {\n listener({ type: \"disconnected\" });\n return;\n }\n // Forward the FULL accounts list; Wallet Standard's\n // change.accounts reflects the wallet's current exposure\n // set. Mirroring it into the pool entry keeps the array in\n // sync with what the wallet actually allows us to sign with,\n // so single-account-exposure wallets (Phantom Solana,\n // MetaMask Snap) don't accumulate stale addresses.\n const chain = currentChain();\n const built = changes.accounts.map((a) => buildAccount(a.address, chain));\n const first = built[0];\n if (first === undefined) {\n return;\n }\n listener({ account: first, accounts: built, type: \"accountChanged\" });\n return;\n }\n\n if (changes.chains !== undefined) {\n notifyAccountChanged();\n }\n });\n unsubWallet = () => {\n unsub();\n };\n }\n return () => {\n listeners.delete(listener);\n unsubWallet?.();\n };\n },\n\n switchChain: (chain) => {\n if (chain.namespace !== \"solana\") {\n throw new Error(\n `SVM adapter received non-Solana chain \"${chain.id}\". Pass a chain with namespace \"solana\".`,\n );\n }\n // The wallet must advertise this cluster; otherwise\n // signAndSendTransaction will reject the chain string later.\n if (!wallet.chains.includes(chain.id)) {\n throw new Error(\n `Wallet ${wallet.name} does not advertise chain \"${chain.id}\". Available: ${wallet.chains.join(\", \")}`,\n );\n }\n currentChainId = chain.id;\n // Synthesise an `accountChanged` so butr's reducer updates the\n // pool entry's chain; the wallet itself has no event to fire here.\n notifyAccountChanged();\n return Promise.resolve();\n },\n };\n};\n\nexport { buildSvmAdapter, slugify };\n","import type { WalletAdapter } from \"@usebutr/core\";\nimport { discoverWalletStandard } from \"@usebutr/wallet-standard-shared\";\n\nimport { buildSvmAdapter } from \"./wallet-standard-adapter\";\n\n/**\n * Subscribe to Solana Wallet Standard announcements.\n * Spec: https://github.com/wallet-standard/wallet-standard\n *\n * Runtime requires the `@wallet-standard/app` package, which is an\n * **optional peer dependency** of butr. EVM-only consumers don't need\n * to install it; discovery will silently skip the SVM side if the\n * module isn't resolvable.\n *\n * Install for SVM auto-discovery:\n *\n * npm install @wallet-standard/app\n *\n * The function returns a synchronous unsubscribe that's safe to call\n * before the dynamic import has resolved.\n *\n * Implementation: the discovery loop (dynamic import, dedupe, register\n * / unregister bridge) lives in `@usebutr/wallet-standard-shared`. This\n * file's only job is to hand the kit a per-wallet builder.\n */\nconst discoverSvmAdapters = (onAdapter: (adapter: WalletAdapter) => void): (() => void) =>\n discoverWalletStandard(onAdapter, (wallet, registerDisconnector) =>\n buildSvmAdapter(wallet, registerDisconnector),\n );\n\nexport { discoverSvmAdapters };\n","import type { ChainBase } from \"@usebutr/core\";\n\n// Solana chain identifiers follow the Wallet Standard convention\n// (`solana:mainnet` / `solana:devnet` / `solana:testnet`) rather than\n// the strict CAIP-2 genesis-hash form (`solana:5eykt…`). Phantom,\n// Solflare, Backpack and the rest of the Wallet Standard ecosystem\n// advertise these shortnames in `wallet.chains`, and the SVM adapter's\n// `switchChain` rejects anything not in that set. The genesis-hash form\n// remains valid CAIP-2 but isn't what real wallets exchange.\nconst SVM_CHAINS = {\n devnet: { id: \"solana:devnet\", name: \"Solana Devnet\", namespace: \"solana\", reference: \"devnet\" },\n mainnet: {\n id: \"solana:mainnet\",\n name: \"Solana Mainnet\",\n namespace: \"solana\",\n reference: \"mainnet\",\n },\n testnet: {\n id: \"solana:testnet\",\n name: \"Solana Testnet\",\n namespace: \"solana\",\n reference: \"testnet\",\n },\n} as const satisfies Record<string, ChainBase>;\n\nconst SVM_CHAINS_LIST: ReadonlyArray<ChainBase> = Object.values(SVM_CHAINS);\n\nexport { SVM_CHAINS, SVM_CHAINS_LIST };\n","import type { PlatformDiscoverer } from \"@usebutr/core\";\n\nimport { discoverSvmAdapters } from \"./wallet-standard\";\n\n/**\n * SVM's `PlatformDiscoverer`: Solana Wallet Standard announcements. No\n * legacy fallback; Solana wallets standardised on Wallet Standard\n * before butr existed, so there's no `window.solana` injected path\n * worth shimming.\n */\nconst svmDiscoverer: PlatformDiscoverer = {\n platform: \"svm\",\n subscribe: discoverSvmAdapters,\n};\n\nexport { svmDiscoverer };\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@usebutr/svm",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.6",
|
|
4
4
|
"description": "Wallet Standard adapter for butr (Solana / SVM).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Pedro Filho <pedro@filho.me>",
|
|
@@ -25,13 +25,13 @@
|
|
|
25
25
|
"access": "public"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@usebutr/core": "0.4.
|
|
29
|
-
"@usebutr/wallet-standard-shared": "0.2.
|
|
28
|
+
"@usebutr/core": "0.4.2",
|
|
29
|
+
"@usebutr/wallet-standard-shared": "0.2.6"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
32
|
"@wallet-standard/app": "^1.1.1",
|
|
33
33
|
"typescript": "^6.0.3",
|
|
34
|
-
"vitest": "^4.1.
|
|
34
|
+
"vitest": "^4.1.10",
|
|
35
35
|
"@repo/config-vitest": "0.0.0",
|
|
36
36
|
"@repo/typescript-config": "0.0.0"
|
|
37
37
|
},
|