@1sat/sweep-ui 0.0.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/components/SweepApp.d.ts +6 -0
- package/dist/components/SweepApp.d.ts.map +1 -0
- package/dist/components/asset-preview.d.ts +32 -0
- package/dist/components/asset-preview.d.ts.map +1 -0
- package/dist/components/connect-wallet.d.ts +8 -0
- package/dist/components/connect-wallet.d.ts.map +1 -0
- package/dist/components/opns-section.d.ts +13 -0
- package/dist/components/opns-section.d.ts.map +1 -0
- package/dist/components/sweep-progress.d.ts +9 -0
- package/dist/components/sweep-progress.d.ts.map +1 -0
- package/dist/components/tx-history.d.ts +14 -0
- package/dist/components/tx-history.d.ts.map +1 -0
- package/dist/components/ui/badge.d.ts +8 -0
- package/dist/components/ui/badge.d.ts.map +1 -0
- package/dist/components/ui/button.d.ts +9 -0
- package/dist/components/ui/button.d.ts.map +1 -0
- package/dist/components/ui/card.d.ts +10 -0
- package/dist/components/ui/card.d.ts.map +1 -0
- package/dist/components/ui/input.d.ts +4 -0
- package/dist/components/ui/input.d.ts.map +1 -0
- package/dist/components/ui/tabs.d.ts +14 -0
- package/dist/components/ui/tabs.d.ts.map +1 -0
- package/dist/components/wif-input.d.ts +9 -0
- package/dist/components/wif-input.d.ts.map +1 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2556 -0
- package/dist/lib/legacy-send.d.ts +24 -0
- package/dist/lib/legacy-send.d.ts.map +1 -0
- package/dist/lib/scanner.d.ts +33 -0
- package/dist/lib/scanner.d.ts.map +1 -0
- package/dist/lib/services.d.ts +4 -0
- package/dist/lib/services.d.ts.map +1 -0
- package/dist/lib/sweeper.d.ts +18 -0
- package/dist/lib/sweeper.d.ts.map +1 -0
- package/dist/lib/utils.d.ts +6 -0
- package/dist/lib/utils.d.ts.map +1 -0
- package/dist/lib/wallet.d.ts +9 -0
- package/dist/lib/wallet.d.ts.map +1 -0
- package/dist/types.d.ts +6 -0
- package/dist/types.d.ts.map +1 -0
- package/package.json +44 -0
- package/src/components/SweepApp.tsx +269 -0
- package/src/components/asset-preview.tsx +224 -0
- package/src/components/connect-wallet.tsx +59 -0
- package/src/components/opns-section.tsx +108 -0
- package/src/components/sweep-progress.tsx +69 -0
- package/src/components/tx-history.tsx +63 -0
- package/src/components/ui/badge.tsx +39 -0
- package/src/components/ui/button.tsx +52 -0
- package/src/components/ui/card.tsx +32 -0
- package/src/components/ui/input.tsx +20 -0
- package/src/components/ui/tabs.tsx +51 -0
- package/src/components/wif-input.tsx +332 -0
- package/src/index.ts +28 -0
- package/src/lib/legacy-send.ts +234 -0
- package/src/lib/scanner.ts +226 -0
- package/src/lib/services.ts +18 -0
- package/src/lib/sweeper.ts +93 -0
- package/src/lib/utils.ts +23 -0
- package/src/lib/wallet.ts +32 -0
- package/src/types.ts +5 -0
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import { PrivateKey } from "@bsv/sdk";
|
|
2
|
+
import type { IndexedOutput } from "@1sat/types";
|
|
3
|
+
import { getServices } from "./services";
|
|
4
|
+
|
|
5
|
+
export interface EnrichedOrdinal extends IndexedOutput {
|
|
6
|
+
origin?: string;
|
|
7
|
+
contentType?: string;
|
|
8
|
+
name?: string;
|
|
9
|
+
contentUrl: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface TokenBalance {
|
|
13
|
+
tokenId: string;
|
|
14
|
+
symbol?: string;
|
|
15
|
+
icon: string;
|
|
16
|
+
decimals: number;
|
|
17
|
+
totalAmount: bigint;
|
|
18
|
+
outputs: IndexedOutput[];
|
|
19
|
+
isActive: boolean;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface ScannedAssets {
|
|
23
|
+
funding: IndexedOutput[];
|
|
24
|
+
ordinals: EnrichedOrdinal[];
|
|
25
|
+
opnsNames: EnrichedOrdinal[];
|
|
26
|
+
bsv21Tokens: TokenBalance[];
|
|
27
|
+
bsv20Tokens: IndexedOutput[];
|
|
28
|
+
locked: IndexedOutput[];
|
|
29
|
+
totalBsv: number;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface ScanProgress {
|
|
33
|
+
phase: string;
|
|
34
|
+
detail?: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function deriveAddress(wif: string): string {
|
|
38
|
+
return PrivateKey.fromWif(wif.trim()).toPublicKey().toAddress();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function getEvent(events: string[], prefix: string): string | undefined {
|
|
42
|
+
const e = events.find((e) => e.startsWith(prefix));
|
|
43
|
+
return e ? e.slice(prefix.length) : undefined;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function getEvents(events: string[], prefix: string): string[] {
|
|
47
|
+
return events.filter((e) => e.startsWith(prefix)).map((e) => e.slice(prefix.length));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function enrichOrdinal(out: IndexedOutput): EnrichedOrdinal {
|
|
51
|
+
const events = out.events ?? [];
|
|
52
|
+
const origin = getEvent(events, "origin:");
|
|
53
|
+
const types = getEvents(events, "type:");
|
|
54
|
+
const contentType = types.find((t) => t.includes("/")) ?? types[0];
|
|
55
|
+
const name = getEvent(events, "name:");
|
|
56
|
+
const contentUrl = getServices().ordfs.getContentUrl(origin ?? out.outpoint, { raw: true });
|
|
57
|
+
|
|
58
|
+
return { ...out, origin, contentType, name, contentUrl };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function resolveIconOutpoint(tokenId: string, icon?: string): string | undefined {
|
|
62
|
+
if (!icon) return undefined;
|
|
63
|
+
if (icon.startsWith("_")) {
|
|
64
|
+
const txid = tokenId.split("_")[0];
|
|
65
|
+
return `${txid}${icon}`;
|
|
66
|
+
}
|
|
67
|
+
return icon;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async function groupBsv21Tokens(outputs: IndexedOutput[]): Promise<TokenBalance[]> {
|
|
71
|
+
const groups = new Map<string, { outputs: IndexedOutput[]; totalAmount: bigint }>();
|
|
72
|
+
|
|
73
|
+
for (const out of outputs) {
|
|
74
|
+
const events = out.events ?? [];
|
|
75
|
+
const tokenId = getEvent(events, "bsv21:");
|
|
76
|
+
if (!tokenId) continue;
|
|
77
|
+
|
|
78
|
+
const amtStr = getEvent(events, "amt:");
|
|
79
|
+
const amount = amtStr ? BigInt(amtStr) : 0n;
|
|
80
|
+
|
|
81
|
+
let group = groups.get(tokenId);
|
|
82
|
+
if (!group) {
|
|
83
|
+
group = { outputs: [], totalAmount: 0n };
|
|
84
|
+
groups.set(tokenId, group);
|
|
85
|
+
}
|
|
86
|
+
group.outputs.push(out);
|
|
87
|
+
group.totalAmount += amount;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (groups.size === 0) return [];
|
|
91
|
+
|
|
92
|
+
const services = getServices();
|
|
93
|
+
const tokenIds = [...groups.keys()];
|
|
94
|
+
|
|
95
|
+
let details: Array<{ tokenId: string; token?: { sym?: string; dec?: string; icon?: string }; status?: { is_active?: boolean } }> = [];
|
|
96
|
+
try {
|
|
97
|
+
details = await services.bsv21.lookupTokens(tokenIds);
|
|
98
|
+
} catch {
|
|
99
|
+
// BSV21 service may not be available
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const detailMap = new Map(details.map((d) => [d.tokenId, d]));
|
|
103
|
+
|
|
104
|
+
const balances: TokenBalance[] = [];
|
|
105
|
+
for (const [tokenId, group] of groups) {
|
|
106
|
+
const detail = detailMap.get(tokenId);
|
|
107
|
+
const iconOutpoint = resolveIconOutpoint(tokenId, detail?.token?.icon);
|
|
108
|
+
|
|
109
|
+
balances.push({
|
|
110
|
+
tokenId,
|
|
111
|
+
symbol: detail?.token?.sym,
|
|
112
|
+
icon: iconOutpoint ? services.ordfs.getContentUrl(iconOutpoint) : "",
|
|
113
|
+
decimals: Number(detail?.token?.dec ?? 0),
|
|
114
|
+
totalAmount: group.totalAmount,
|
|
115
|
+
outputs: group.outputs,
|
|
116
|
+
isActive: detail?.status?.is_active ?? false,
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
return balances;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async function categorizeOutputs(outputs: IndexedOutput[]): Promise<ScannedAssets> {
|
|
123
|
+
const funding: IndexedOutput[] = [];
|
|
124
|
+
const rawOrdinals: IndexedOutput[] = [];
|
|
125
|
+
const opnsRaw: IndexedOutput[] = [];
|
|
126
|
+
const bsv21Raw: IndexedOutput[] = [];
|
|
127
|
+
const bsv20Tokens: IndexedOutput[] = [];
|
|
128
|
+
const locked: IndexedOutput[] = [];
|
|
129
|
+
|
|
130
|
+
for (const out of outputs) {
|
|
131
|
+
const events = out.events ?? [];
|
|
132
|
+
const sats = out.satoshis ?? 0;
|
|
133
|
+
|
|
134
|
+
if (events.some((e) => e.startsWith("bsv21:"))) {
|
|
135
|
+
bsv21Raw.push(out);
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (events.some((e) => e.startsWith("lock:"))) {
|
|
140
|
+
locked.push(out);
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (events.some((e) => e === "type:application/bsv-20" || e === "type:Token")) {
|
|
145
|
+
bsv20Tokens.push(out);
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (sats === 1) {
|
|
150
|
+
if (events.some((e) => e === "type:application/op-ns")) {
|
|
151
|
+
opnsRaw.push(out);
|
|
152
|
+
} else {
|
|
153
|
+
rawOrdinals.push(out);
|
|
154
|
+
}
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (sats > 1) {
|
|
159
|
+
funding.push(out);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return {
|
|
164
|
+
funding,
|
|
165
|
+
ordinals: rawOrdinals.map(enrichOrdinal),
|
|
166
|
+
opnsNames: opnsRaw.map(enrichOrdinal),
|
|
167
|
+
bsv21Tokens: await groupBsv21Tokens(bsv21Raw),
|
|
168
|
+
bsv20Tokens,
|
|
169
|
+
locked,
|
|
170
|
+
totalBsv: funding.reduce((sum, o) => sum + (o.satoshis ?? 0), 0),
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export async function scanAddress(
|
|
175
|
+
address: string,
|
|
176
|
+
onProgress?: (p: ScanProgress) => void,
|
|
177
|
+
): Promise<ScannedAssets> {
|
|
178
|
+
const services = getServices();
|
|
179
|
+
|
|
180
|
+
onProgress?.({ phase: "sync", detail: "Syncing address..." });
|
|
181
|
+
for await (const event of services.owner.getTxos(address, { refresh: true, limit: 1 })) {
|
|
182
|
+
if (event.type === "sync") {
|
|
183
|
+
const p = event.data;
|
|
184
|
+
onProgress?.({
|
|
185
|
+
phase: "sync",
|
|
186
|
+
detail: `${p.phase}: ${p.processed ?? 0}/${p.total ?? "?"}`,
|
|
187
|
+
});
|
|
188
|
+
} else if (event.type === "done" || event.type === "error") {
|
|
189
|
+
break;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
onProgress?.({ phase: "search", detail: "Searching for assets..." });
|
|
194
|
+
const allOutputs = await services.txo.search(`own:${address}`, {
|
|
195
|
+
unspent: true,
|
|
196
|
+
events: true,
|
|
197
|
+
sats: true,
|
|
198
|
+
limit: 0,
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
onProgress?.({ phase: "categorize", detail: "Loading token details..." });
|
|
202
|
+
return await categorizeOutputs(allOutputs);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
export async function scanAddresses(
|
|
206
|
+
addresses: string[],
|
|
207
|
+
onProgress?: (p: ScanProgress) => void,
|
|
208
|
+
): Promise<ScannedAssets> {
|
|
209
|
+
const unique = [...new Set(addresses)];
|
|
210
|
+
const allResults: ScannedAssets[] = [];
|
|
211
|
+
|
|
212
|
+
for (const addr of unique) {
|
|
213
|
+
onProgress?.({ phase: "sync", detail: `Scanning ${addr.slice(0, 8)}...` });
|
|
214
|
+
allResults.push(await scanAddress(addr, onProgress));
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
return {
|
|
218
|
+
funding: allResults.flatMap((r) => r.funding),
|
|
219
|
+
ordinals: allResults.flatMap((r) => r.ordinals),
|
|
220
|
+
opnsNames: allResults.flatMap((r) => r.opnsNames),
|
|
221
|
+
bsv21Tokens: allResults.flatMap((r) => r.bsv21Tokens),
|
|
222
|
+
bsv20Tokens: allResults.flatMap((r) => r.bsv20Tokens),
|
|
223
|
+
locked: allResults.flatMap((r) => r.locked),
|
|
224
|
+
totalBsv: allResults.reduce((sum, r) => sum + r.totalBsv, 0),
|
|
225
|
+
};
|
|
226
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { OneSatServices } from "@1sat/client";
|
|
2
|
+
|
|
3
|
+
let _services: OneSatServices | null = null;
|
|
4
|
+
let _baseUrl: string | undefined;
|
|
5
|
+
|
|
6
|
+
export function configureServices(baseUrl: string): void {
|
|
7
|
+
_baseUrl = baseUrl;
|
|
8
|
+
_services = null;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function getServices(): OneSatServices {
|
|
12
|
+
if (!_services) {
|
|
13
|
+
const url = _baseUrl ?? (typeof window !== "undefined" ? window.location.origin : "");
|
|
14
|
+
if (!url) throw new Error("No base URL configured. Call configureServices() first.");
|
|
15
|
+
_services = new OneSatServices("main", url);
|
|
16
|
+
}
|
|
17
|
+
return _services;
|
|
18
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createContext,
|
|
3
|
+
prepareSweepInputs,
|
|
4
|
+
sweepBsv,
|
|
5
|
+
sweepOrdinals,
|
|
6
|
+
sweepBsv21,
|
|
7
|
+
} from "@1sat/actions";
|
|
8
|
+
import type { IndexedOutput } from "@1sat/types";
|
|
9
|
+
import type { WalletInterface } from "@bsv/sdk";
|
|
10
|
+
import { getServices } from "./services";
|
|
11
|
+
|
|
12
|
+
export interface SweepResult {
|
|
13
|
+
bsvTxid?: string;
|
|
14
|
+
ordinalTxids: string[];
|
|
15
|
+
bsv21Txids: string[];
|
|
16
|
+
errors: string[];
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export async function executeSweep(params: {
|
|
20
|
+
wallet: WalletInterface;
|
|
21
|
+
wif: string;
|
|
22
|
+
funding: IndexedOutput[];
|
|
23
|
+
ordinals: IndexedOutput[];
|
|
24
|
+
bsv21Tokens: IndexedOutput[];
|
|
25
|
+
amount?: number;
|
|
26
|
+
onProgress: (stage: string) => void;
|
|
27
|
+
}): Promise<SweepResult> {
|
|
28
|
+
const { wallet, wif, funding, ordinals, bsv21Tokens, amount, onProgress } = params;
|
|
29
|
+
const ctx = createContext(wallet, { services: getServices(), chain: "main" });
|
|
30
|
+
|
|
31
|
+
const result: SweepResult = {
|
|
32
|
+
ordinalTxids: [],
|
|
33
|
+
bsv21Txids: [],
|
|
34
|
+
errors: [],
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
if (funding.length > 0) {
|
|
38
|
+
onProgress(`Sweeping ${funding.length} BSV UTXOs...`);
|
|
39
|
+
try {
|
|
40
|
+
const inputs = await prepareSweepInputs(ctx, funding);
|
|
41
|
+
const bsvResult = await sweepBsv.execute(ctx, { inputs, wif, amount });
|
|
42
|
+
if (bsvResult.error) result.errors.push(`BSV: ${bsvResult.error}`);
|
|
43
|
+
else if (bsvResult.txid) result.bsvTxid = bsvResult.txid;
|
|
44
|
+
} catch (e) {
|
|
45
|
+
result.errors.push(`BSV: ${e instanceof Error ? e.message : String(e)}`);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (ordinals.length > 0) {
|
|
50
|
+
onProgress(`Sweeping ${ordinals.length} ordinals...`);
|
|
51
|
+
try {
|
|
52
|
+
const inputs = await prepareSweepInputs(ctx, ordinals);
|
|
53
|
+
const ordResult = await sweepOrdinals.execute(ctx, { inputs, wif });
|
|
54
|
+
if (ordResult.error) result.errors.push(`Ordinals: ${ordResult.error}`);
|
|
55
|
+
else if (ordResult.txid) result.ordinalTxids.push(ordResult.txid);
|
|
56
|
+
} catch (e) {
|
|
57
|
+
result.errors.push(`Ordinals: ${e instanceof Error ? e.message : String(e)}`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (bsv21Tokens.length > 0) {
|
|
62
|
+
const groups = new Map<string, IndexedOutput[]>();
|
|
63
|
+
for (const token of bsv21Tokens) {
|
|
64
|
+
const tokenEvent = token.events?.find((e) => e.startsWith("tokenId:"));
|
|
65
|
+
const tokenId = tokenEvent?.slice(8) ?? "unknown";
|
|
66
|
+
const group = groups.get(tokenId) ?? [];
|
|
67
|
+
group.push(token);
|
|
68
|
+
groups.set(tokenId, group);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
for (const [tokenId, tokens] of groups) {
|
|
72
|
+
onProgress(`Sweeping ${tokens.length} tokens (${tokenId.slice(0, 8)}...)...`);
|
|
73
|
+
try {
|
|
74
|
+
const inputs = await prepareSweepInputs(ctx, tokens);
|
|
75
|
+
const tokenResult = await sweepBsv21.execute(ctx, {
|
|
76
|
+
inputs: inputs.map((inp) => ({
|
|
77
|
+
...inp,
|
|
78
|
+
tokenId,
|
|
79
|
+
amount: "0",
|
|
80
|
+
})),
|
|
81
|
+
wif,
|
|
82
|
+
});
|
|
83
|
+
if (tokenResult.error) result.errors.push(`BSV-21 (${tokenId.slice(0, 8)}): ${tokenResult.error}`);
|
|
84
|
+
else if (tokenResult.txid) result.bsv21Txids.push(tokenResult.txid);
|
|
85
|
+
} catch (e) {
|
|
86
|
+
result.errors.push(`BSV-21 (${tokenId.slice(0, 8)}): ${e instanceof Error ? e.message : String(e)}`);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
onProgress("Sweep complete");
|
|
92
|
+
return result;
|
|
93
|
+
}
|
package/src/lib/utils.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type ClassValue, clsx } from "clsx";
|
|
2
|
+
import { twMerge } from "tailwind-merge";
|
|
3
|
+
|
|
4
|
+
export function cn(...inputs: ClassValue[]) {
|
|
5
|
+
return twMerge(clsx(inputs));
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function formatSats(sats: number): string {
|
|
9
|
+
return sats.toLocaleString();
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function formatTokenAmount(rawAmount: string, decimals: number): string {
|
|
13
|
+
if (decimals === 0) return rawAmount;
|
|
14
|
+
const padded = rawAmount.padStart(decimals + 1, "0");
|
|
15
|
+
const intPart = padded.slice(0, -decimals) || "0";
|
|
16
|
+
const decPart = padded.slice(-decimals).replace(/0+$/, "");
|
|
17
|
+
return decPart ? `${intPart}.${decPart}` : intPart;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function truncate(s: string, len = 8): string {
|
|
21
|
+
if (s.length <= len * 2 + 3) return s;
|
|
22
|
+
return `${s.slice(0, len)}...${s.slice(-len)}`;
|
|
23
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { connectWallet as connect, type ConnectWalletResult } from "@1sat/connect";
|
|
2
|
+
import type { WalletInterface } from "@bsv/sdk";
|
|
3
|
+
|
|
4
|
+
let connection: ConnectWalletResult | null = null;
|
|
5
|
+
|
|
6
|
+
export async function connectWallet(): Promise<ConnectWalletResult> {
|
|
7
|
+
const result = await connect();
|
|
8
|
+
if (!result) throw new Error("No wallet available");
|
|
9
|
+
connection = result;
|
|
10
|
+
return result;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function getWallet(): WalletInterface | null {
|
|
14
|
+
return connection?.wallet ?? null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function getIdentityKey(): string | null {
|
|
18
|
+
return connection?.identityKey ?? null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function getProvider(): string | null {
|
|
22
|
+
return connection?.provider ?? null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function disconnectWallet(): void {
|
|
26
|
+
connection?.disconnect();
|
|
27
|
+
connection = null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function isConnected(): boolean {
|
|
31
|
+
return connection !== null;
|
|
32
|
+
}
|