@usebutr/sui 0.2.6 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +3 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +33 -137
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -44,7 +44,6 @@ type SuiSignAndExecuteTransactionFeature = {
|
|
|
44
44
|
};
|
|
45
45
|
//#endregion
|
|
46
46
|
//#region src/wallet-standard-adapter.d.ts
|
|
47
|
-
declare const slugify: (name: string) => string;
|
|
48
47
|
/**
|
|
49
48
|
* Adapt a Sui Wallet Standard `Wallet` into a butr `WalletAdapter`.
|
|
50
49
|
*
|
|
@@ -68,8 +67,6 @@ declare const buildSuiAdapter: (wallet: WalletStandardWallet,
|
|
|
68
67
|
* `disconnected` event to all current subscribers. The discovery
|
|
69
68
|
* layer invokes it on Wallet Standard `unregister`. */
|
|
70
69
|
registerDisconnector?: (emit: () => void) => void) => WalletAdapter | null;
|
|
71
|
-
//#endregion
|
|
72
|
-
//#region src/wallet-standard.d.ts
|
|
73
70
|
/**
|
|
74
71
|
* Subscribe to Sui Wallet Standard announcements.
|
|
75
72
|
* Spec: https://docs.sui.io/standards/wallet-standard
|
|
@@ -128,8 +125,8 @@ type WalletStandardCapabilityInput = {
|
|
|
128
125
|
};
|
|
129
126
|
};
|
|
130
127
|
/**
|
|
131
|
-
* Sui Wallet Standard capability mapping.
|
|
132
|
-
*
|
|
128
|
+
* Sui Wallet Standard feature → butr capability mapping. The flags that hold
|
|
129
|
+
* for every namespace are documented on `buildWalletCapabilities`.
|
|
133
130
|
*
|
|
134
131
|
* - `sendTransaction = sui:signAndExecuteTransaction present`. The wallet
|
|
135
132
|
* broadcasts; consumers receive the digest.
|
|
@@ -137,8 +134,6 @@ type WalletStandardCapabilityInput = {
|
|
|
137
134
|
* broadcast with their own RPC client (`@mysten/sui`'s SuiClient).
|
|
138
135
|
* - `signMessage = sui:signPersonalMessage present`.
|
|
139
136
|
* - `signIn = false`. Sui has no SIWS-equivalent feature in Wallet Standard.
|
|
140
|
-
* - `switchChain = chainCount > 1`. Same posture as SVM; local re-routing
|
|
141
|
-
* for `signAndExecuteTransaction`'s per-call `chain` input.
|
|
142
137
|
*/
|
|
143
138
|
declare const resolveSuiCapabilities: (input: WalletStandardCapabilityInput) => WalletCapabilities;
|
|
144
139
|
//#endregion
|
|
@@ -162,5 +157,5 @@ declare module "@usebutr/core" {
|
|
|
162
157
|
}
|
|
163
158
|
}
|
|
164
159
|
//#endregion
|
|
165
|
-
export { SUI_CHAINS, SUI_CHAINS_LIST, type SuiSignAndExecuteTransactionFeature, type SuiSignAndExecuteTransactionInput, type SuiSignAndExecuteTransactionOutput, type SuiSignPersonalMessageFeature, type SuiSignPersonalMessageInput, type SuiSignPersonalMessageOutput, type SuiSignTransactionFeature, type SuiSignTransactionInput, type SuiSignTransactionOutput, type WalletStandardCapabilityInput, buildSuiAdapter, discoverSuiAdapters, resolveSuiCapabilities,
|
|
160
|
+
export { SUI_CHAINS, SUI_CHAINS_LIST, type SuiSignAndExecuteTransactionFeature, type SuiSignAndExecuteTransactionInput, type SuiSignAndExecuteTransactionOutput, type SuiSignPersonalMessageFeature, type SuiSignPersonalMessageInput, type SuiSignPersonalMessageOutput, type SuiSignTransactionFeature, type SuiSignTransactionInput, type SuiSignTransactionOutput, type WalletStandardCapabilityInput, buildSuiAdapter, discoverSuiAdapters, resolveSuiCapabilities, suiDiscoverer };
|
|
166
161
|
//# 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/
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/wallet-standard-types.ts","../src/wallet-standard-adapter.ts","../src/chains.ts","../src/capabilities.ts","../src/discoverer.ts","../src/signer-augmentation.ts"],"mappings":";;;KAaK;EACH,SAAS;EACT,SAAS;;KAGN;EACH;EACA;;KAGG;EACH,sBACE,OAAO,gCACJ,QAAQ;;KAGV;EACH,SAAS;EACT;EACA;IAAe,cAAc;;;KAG1B;EACH;EACA;;KAGG;EACH,kBAAkB,OAAO,4BAA4B,QAAQ;;KAG1D;EACH,SAAS;EACT;EACA;IAAe,cAAc;;;KAG1B;EACH;EACA;EACA;EACA;;KAGG;EACH,4BACE,OAAO,sCACJ,QAAQ;;;;;;;;;;;;;;;;;;;;;;cCHT,kBACJ,QAAQ,sBAIR;;;;AAAA,wBAAwB,8BACvB;;;;;;;;;;;;;;;;;cAsHG,sBAAuB,YAAY,SAAS;;;cC9K5C;;;;;;;;;;;;;;;;;;;;;;;;;;cAiBA,iBAAiB,cAAc;;;KCrBhC;EACH;EACA;IACE;IACA;IACA;IACA;;;;;;;;;;;;;;cAeE,yBAA0B,OAAO,kCAAgC;;;;;;;cChBjE,eAAe;;;;;;;;;;YCET;IACR,KAAK"}
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { base64ToBytes
|
|
2
|
-
import {
|
|
1
|
+
import { base64ToBytes } from "@usebutr/core";
|
|
2
|
+
import { buildWalletCapabilities, createWalletStandardCore, discoverWalletStandard, getFeature, slugify } from "@usebutr/wallet-standard-shared";
|
|
3
3
|
|
|
4
4
|
//#region src/capabilities.ts
|
|
5
5
|
/**
|
|
6
|
-
* Sui Wallet Standard capability mapping.
|
|
7
|
-
*
|
|
6
|
+
* Sui Wallet Standard feature → butr capability mapping. The flags that hold
|
|
7
|
+
* for every namespace are documented on `buildWalletCapabilities`.
|
|
8
8
|
*
|
|
9
9
|
* - `sendTransaction = sui:signAndExecuteTransaction present`. The wallet
|
|
10
10
|
* broadcasts; consumers receive the digest.
|
|
@@ -12,38 +12,21 @@ import { buildAccount, discoverWalletStandard, getFeature, pickAccountByAddress,
|
|
|
12
12
|
* broadcast with their own RPC client (`@mysten/sui`'s SuiClient).
|
|
13
13
|
* - `signMessage = sui:signPersonalMessage present`.
|
|
14
14
|
* - `signIn = false`. Sui has no SIWS-equivalent feature in Wallet Standard.
|
|
15
|
-
* - `switchChain = chainCount > 1`. Same posture as SVM; local re-routing
|
|
16
|
-
* for `signAndExecuteTransaction`'s per-call `chain` input.
|
|
17
15
|
*/
|
|
18
|
-
const resolveSuiCapabilities = (input) => ({
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
requestAccounts: false,
|
|
16
|
+
const resolveSuiCapabilities = (input) => buildWalletCapabilities({
|
|
17
|
+
chainCount: input.chainCount,
|
|
18
|
+
events: input.features.events,
|
|
22
19
|
sendTransaction: input.features.signAndExecuteTransaction,
|
|
23
20
|
signIn: false,
|
|
24
21
|
signMessage: input.features.signMessage,
|
|
25
|
-
signTransaction: input.features.signTransaction
|
|
26
|
-
subscribe: input.features.events,
|
|
27
|
-
switchAccount: false,
|
|
28
|
-
switchChain: input.chainCount > 1
|
|
22
|
+
signTransaction: input.features.signTransaction
|
|
29
23
|
});
|
|
30
24
|
|
|
31
25
|
//#endregion
|
|
32
26
|
//#region src/wallet-standard-adapter.ts
|
|
33
27
|
const SUI_PREFIX = "sui:";
|
|
34
28
|
const SUI_DECIMALS = 9;
|
|
35
|
-
const
|
|
36
|
-
const pickSuiChain = (wallet) => {
|
|
37
|
-
const mainnet = wallet.chains.find((c) => c === "sui:mainnet");
|
|
38
|
-
if (mainnet) return mainnet;
|
|
39
|
-
return wallet.chains.find((c) => c.startsWith(SUI_PREFIX)) ?? null;
|
|
40
|
-
};
|
|
41
|
-
const buildSuiChain = (chainId, walletName) => ({
|
|
42
|
-
id: chainId,
|
|
43
|
-
name: walletName,
|
|
44
|
-
namespace: "sui",
|
|
45
|
-
reference: chainId.slice(4)
|
|
46
|
-
});
|
|
29
|
+
const SUI_MAINNET = "sui:mainnet";
|
|
47
30
|
/** Coerce butr's `unknown` tx into the shape `sui:signAndExecuteTransaction`
|
|
48
31
|
* expects (an object with `toJSON()` returning a Promise<string>). When
|
|
49
32
|
* consumers pass a base64 string directly, we wrap it in a stub object so
|
|
@@ -73,87 +56,52 @@ const coerceSuiTransaction = (tx) => {
|
|
|
73
56
|
* controls its global cluster setting.
|
|
74
57
|
*/
|
|
75
58
|
const buildSuiAdapter = (wallet, registerDisconnector) => {
|
|
76
|
-
const
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
59
|
+
const core = createWalletStandardCore({
|
|
60
|
+
chainPrefix: SUI_PREFIX,
|
|
61
|
+
id: slugify("sui", wallet.name),
|
|
62
|
+
label: "Sui",
|
|
63
|
+
namespace: "sui",
|
|
64
|
+
platform: "Sui",
|
|
65
|
+
preferredChainIds: [SUI_MAINNET],
|
|
66
|
+
registerDisconnector,
|
|
67
|
+
trackChainChanges: true,
|
|
68
|
+
wallet
|
|
69
|
+
});
|
|
70
|
+
if (core === null) return null;
|
|
82
71
|
const signMessage = getFeature(wallet, "sui:signPersonalMessage");
|
|
83
72
|
const signAndExecute = getFeature(wallet, "sui:signAndExecuteTransaction");
|
|
84
73
|
const signTx = getFeature(wallet, "sui:signTransaction");
|
|
85
|
-
let currentChainId = suiChainId;
|
|
86
|
-
const currentChain = () => buildSuiChain(currentChainId, wallet.name);
|
|
87
|
-
const listeners = /* @__PURE__ */ new Set();
|
|
88
|
-
const notifyAccountChanged = () => {
|
|
89
|
-
if (wallet.accounts.length === 0) return;
|
|
90
|
-
const chain = currentChain();
|
|
91
|
-
const built = wallet.accounts.map((a) => buildAccount(a.address, chain));
|
|
92
|
-
const first = built[0];
|
|
93
|
-
if (first === void 0) return;
|
|
94
|
-
for (const listener of listeners) listener({
|
|
95
|
-
account: first,
|
|
96
|
-
accounts: built,
|
|
97
|
-
type: "accountChanged"
|
|
98
|
-
});
|
|
99
|
-
};
|
|
100
|
-
registerDisconnector?.(() => {
|
|
101
|
-
for (const listener of listeners) listener({ type: "disconnected" });
|
|
102
|
-
});
|
|
103
74
|
const executeTx = async (tx, account) => {
|
|
104
75
|
if (signAndExecute === void 0) throw new Error(`Wallet ${wallet.name} does not advertise sui:signAndExecuteTransaction`);
|
|
105
|
-
const wsAccount =
|
|
106
|
-
if (wsAccount === void 0) throw new Error("No connected account");
|
|
76
|
+
const wsAccount = core.resolveAccount(account);
|
|
107
77
|
const transaction = coerceSuiTransaction(tx);
|
|
108
78
|
return (await signAndExecute.signAndExecuteTransaction({
|
|
109
79
|
account: wsAccount,
|
|
110
|
-
chain: currentChainId,
|
|
80
|
+
chain: core.currentChainId(),
|
|
111
81
|
transaction
|
|
112
82
|
})).digest;
|
|
113
83
|
};
|
|
114
84
|
return {
|
|
85
|
+
...core,
|
|
115
86
|
capabilities: resolveSuiCapabilities({
|
|
116
|
-
chainCount:
|
|
87
|
+
chainCount: core.chainCount,
|
|
117
88
|
features: {
|
|
118
|
-
events:
|
|
89
|
+
events: core.hasEvents,
|
|
119
90
|
signAndExecuteTransaction: Boolean(signAndExecute),
|
|
120
91
|
signMessage: Boolean(signMessage),
|
|
121
92
|
signTransaction: Boolean(signTx)
|
|
122
93
|
}
|
|
123
94
|
}),
|
|
124
95
|
chainPlatform: "sui",
|
|
125
|
-
async connect(opts) {
|
|
126
|
-
await connect.connect(opts?.silent === true ? { silent: true } : void 0);
|
|
127
|
-
},
|
|
128
|
-
async disconnect() {
|
|
129
|
-
if (disconnect !== void 0) try {
|
|
130
|
-
await disconnect.disconnect();
|
|
131
|
-
} catch (error) {
|
|
132
|
-
logWarn("[butr] Sui Wallet Standard disconnect threw:", error);
|
|
133
|
-
}
|
|
134
|
-
},
|
|
135
|
-
getAccount: () => {
|
|
136
|
-
const address = pickFirstAddress(wallet.accounts);
|
|
137
|
-
if (address === null) return Promise.resolve(null);
|
|
138
|
-
return Promise.resolve(buildAccount(address, currentChain()));
|
|
139
|
-
},
|
|
140
|
-
getAccounts: () => {
|
|
141
|
-
const chain = currentChain();
|
|
142
|
-
return Promise.resolve(wallet.accounts.map((a) => buildAccount(a.address, chain)));
|
|
143
|
-
},
|
|
144
96
|
getBalance: () => Promise.resolve({
|
|
145
97
|
decimals: SUI_DECIMALS,
|
|
146
98
|
formatted: "0",
|
|
147
99
|
symbol: "SUI",
|
|
148
100
|
value: 0n
|
|
149
101
|
}),
|
|
150
|
-
getSigner: () => Promise.resolve(wallet),
|
|
151
102
|
getTransactionReceipt: () => Promise.resolve({ status: "Pending" }),
|
|
152
|
-
icon: sanitizeIcon(wallet.icon),
|
|
153
|
-
id: slugify(wallet.name),
|
|
154
|
-
name: wallet.name,
|
|
155
103
|
async requestAccounts() {
|
|
156
|
-
await
|
|
104
|
+
await core.connect();
|
|
157
105
|
},
|
|
158
106
|
sendTx: (tx, account) => executeTx(tx, account),
|
|
159
107
|
sendTxToChain: (tx, _targetChainId, account, cb) => {
|
|
@@ -162,10 +110,8 @@ const buildSuiAdapter = (wallet, registerDisconnector) => {
|
|
|
162
110
|
},
|
|
163
111
|
async signMessage(msg, account) {
|
|
164
112
|
if (signMessage === void 0) throw new Error(`Wallet ${wallet.name} does not advertise sui:signPersonalMessage`);
|
|
165
|
-
const wsAccount = account ? pickAccountByAddress(wallet.accounts, account.walletAddress) : wallet.accounts[0];
|
|
166
|
-
if (wsAccount === void 0) throw new Error("No connected account");
|
|
167
113
|
const output = await signMessage.signPersonalMessage({
|
|
168
|
-
account:
|
|
114
|
+
account: core.resolveAccount(account),
|
|
169
115
|
message: msg
|
|
170
116
|
});
|
|
171
117
|
return {
|
|
@@ -174,63 +120,16 @@ const buildSuiAdapter = (wallet, registerDisconnector) => {
|
|
|
174
120
|
};
|
|
175
121
|
},
|
|
176
122
|
...signTx === void 0 ? {} : { async signTransaction(tx, account) {
|
|
177
|
-
const wsAccount =
|
|
178
|
-
if (wsAccount === void 0) throw new Error("No connected account");
|
|
123
|
+
const wsAccount = core.resolveAccount(account);
|
|
179
124
|
const transaction = coerceSuiTransaction(tx);
|
|
180
125
|
return base64ToBytes((await signTx.signTransaction({
|
|
181
126
|
account: wsAccount,
|
|
182
|
-
chain: currentChainId,
|
|
127
|
+
chain: core.currentChainId(),
|
|
183
128
|
transaction
|
|
184
129
|
})).bytes);
|
|
185
|
-
} }
|
|
186
|
-
subscribe(listener) {
|
|
187
|
-
listeners.add(listener);
|
|
188
|
-
let unsubWallet = null;
|
|
189
|
-
if (events !== void 0) {
|
|
190
|
-
const unsub = events.on("change", (changes) => {
|
|
191
|
-
if (changes.chains !== void 0) {
|
|
192
|
-
const next = changes.chains.find((c) => c === "sui:mainnet") ?? changes.chains.find((c) => c.startsWith(SUI_PREFIX));
|
|
193
|
-
if (next !== void 0) currentChainId = next;
|
|
194
|
-
}
|
|
195
|
-
if (changes.accounts !== void 0) {
|
|
196
|
-
if (changes.accounts.length === 0) {
|
|
197
|
-
listener({ type: "disconnected" });
|
|
198
|
-
return;
|
|
199
|
-
}
|
|
200
|
-
const chain = currentChain();
|
|
201
|
-
const built = changes.accounts.map((a) => buildAccount(a.address, chain));
|
|
202
|
-
const first = built[0];
|
|
203
|
-
if (first === void 0) return;
|
|
204
|
-
listener({
|
|
205
|
-
account: first,
|
|
206
|
-
accounts: built,
|
|
207
|
-
type: "accountChanged"
|
|
208
|
-
});
|
|
209
|
-
return;
|
|
210
|
-
}
|
|
211
|
-
if (changes.chains !== void 0) notifyAccountChanged();
|
|
212
|
-
});
|
|
213
|
-
unsubWallet = () => {
|
|
214
|
-
unsub();
|
|
215
|
-
};
|
|
216
|
-
}
|
|
217
|
-
return () => {
|
|
218
|
-
listeners.delete(listener);
|
|
219
|
-
unsubWallet?.();
|
|
220
|
-
};
|
|
221
|
-
},
|
|
222
|
-
switchChain: (chain) => {
|
|
223
|
-
if (chain.namespace !== "sui") throw new Error(`Sui adapter received non-Sui chain "${chain.id}". Pass a chain with namespace "sui".`);
|
|
224
|
-
if (!wallet.chains.includes(chain.id)) throw new Error(`Wallet ${wallet.name} does not advertise chain "${chain.id}". Available: ${wallet.chains.join(", ")}`);
|
|
225
|
-
currentChainId = chain.id;
|
|
226
|
-
notifyAccountChanged();
|
|
227
|
-
return Promise.resolve();
|
|
228
|
-
}
|
|
130
|
+
} }
|
|
229
131
|
};
|
|
230
132
|
};
|
|
231
|
-
|
|
232
|
-
//#endregion
|
|
233
|
-
//#region src/wallet-standard.ts
|
|
234
133
|
/**
|
|
235
134
|
* Subscribe to Sui Wallet Standard announcements.
|
|
236
135
|
* Spec: https://docs.sui.io/standards/wallet-standard
|
|
@@ -285,11 +184,8 @@ const SUI_CHAINS_LIST = Object.values(SUI_CHAINS);
|
|
|
285
184
|
* Sui's `PlatformDiscoverer`: Sui Wallet Standard announcements. No
|
|
286
185
|
* legacy fallback; Sui standardised on Wallet Standard from launch.
|
|
287
186
|
*/
|
|
288
|
-
const suiDiscoverer = {
|
|
289
|
-
platform: "sui",
|
|
290
|
-
subscribe: discoverSuiAdapters
|
|
291
|
-
};
|
|
187
|
+
const suiDiscoverer = { subscribe: discoverSuiAdapters };
|
|
292
188
|
|
|
293
189
|
//#endregion
|
|
294
|
-
export { SUI_CHAINS, SUI_CHAINS_LIST, buildSuiAdapter, discoverSuiAdapters, resolveSuiCapabilities,
|
|
190
|
+
export { SUI_CHAINS, SUI_CHAINS_LIST, buildSuiAdapter, discoverSuiAdapters, resolveSuiCapabilities, suiDiscoverer };
|
|
295
191
|
//# sourceMappingURL=index.js.map
|
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 signAndExecuteTransaction: boolean;\n signMessage: boolean;\n signTransaction: boolean;\n };\n};\n\n/**\n * Sui Wallet Standard capability mapping. Mirrors the SVM resolver:\n * butr ships no RPC, so balance/receipt always false.\n *\n * - `sendTransaction = sui:signAndExecuteTransaction present`. The wallet\n * broadcasts; consumers receive the digest.\n * - `signTransaction = sui:signTransaction present`. Sign-only; consumers\n * broadcast with their own RPC client (`@mysten/sui`'s SuiClient).\n * - `signMessage = sui:signPersonalMessage present`.\n * - `signIn = false`. Sui has no SIWS-equivalent feature in Wallet Standard.\n * - `switchChain = chainCount > 1`. Same posture as SVM; local re-routing\n * for `signAndExecuteTransaction`'s per-call `chain` input.\n */\nconst resolveSuiCapabilities = (input: WalletStandardCapabilityInput): WalletCapabilities => ({\n getBalance: false,\n getTransactionReceipt: false,\n requestAccounts: false,\n sendTransaction: input.features.signAndExecuteTransaction,\n signIn: false,\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 { resolveSuiCapabilities };\n","import type { ChainBase, ConnectorEvent, WalletAdapter } from \"@usebutr/core\";\nimport { base64ToBytes, 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 { resolveSuiCapabilities } from \"./capabilities\";\nimport type {\n SuiSignAndExecuteTransactionFeature,\n SuiSignPersonalMessageFeature,\n SuiSignTransactionFeature,\n} from \"./wallet-standard-types\";\n\nconst SUI_PREFIX = \"sui:\";\nconst SUI_DECIMALS = 9;\n\nconst slugify = (name: string): string => kitSlugify(\"sui\", name);\n\nconst pickSuiChain = (wallet: WalletStandardWallet): string | null => {\n const mainnet = wallet.chains.find((c) => c === \"sui:mainnet\");\n if (mainnet) {\n return mainnet;\n }\n return wallet.chains.find((c) => c.startsWith(SUI_PREFIX)) ?? null;\n};\n\nconst buildSuiChain = (chainId: string, walletName: string): ChainBase => ({\n id: chainId,\n name: walletName,\n namespace: \"sui\",\n reference: chainId.slice(SUI_PREFIX.length),\n});\n\n/** Coerce butr's `unknown` tx into the shape `sui:signAndExecuteTransaction`\n * expects (an object with `toJSON()` returning a Promise<string>). When\n * consumers pass a base64 string directly, we wrap it in a stub object so\n * the feature contract stays satisfied without us depending on\n * `@mysten/sui`. */\nconst coerceSuiTransaction = (tx: unknown): { toJSON: () => Promise<string> } | string => {\n if (typeof tx === \"string\") {\n return tx;\n }\n if (typeof tx === \"object\" && tx !== null && \"toJSON\" in tx && typeof tx.toJSON === \"function\") {\n // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- validated toJSON() above; @mysten/sui Transaction is otherwise untyped here\n return tx as { toJSON: () => Promise<string> };\n }\n throw new TypeError(\n \"Sui sendTx/signTransaction expects a @mysten/sui Transaction (with toJSON()) or a base64-encoded string\",\n );\n};\n\n/**\n * Adapt a Sui Wallet Standard `Wallet` into a butr `WalletAdapter`.\n *\n * Returns `null` if the wallet doesn't advertise any Sui chain or doesn't\n * expose `standard:connect`. Same posture as `buildSvmAdapter`: gate\n * features on what each wallet advertises, return placeholders for\n * RPC-backed reads (`getBalance`, `getTransactionReceipt`), forward\n * `subscribe` through `standard:events`.\n *\n * **Known limitations**\n *\n * - `getBalance` returns a `{ value: 0n, symbol: \"SUI\" }` placeholder.\n * Consumers wrap `@mysten/sui`'s `SuiClient` for real reads.\n * - `getTransactionReceipt` returns `\"Pending\"` for the same reason.\n * - `switchChain` is local-state-only (mirrors SVM). Sui Wallet Standard\n * has no \"tell the wallet to switch network\" RPC; the wallet's UI\n * controls its global cluster setting.\n */\nconst buildSuiAdapter = (\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`. */\n registerDisconnector?: (emit: () => void) => void,\n): WalletAdapter | null => {\n const suiChainId = pickSuiChain(wallet);\n if (suiChainId === 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<SuiSignPersonalMessageFeature>(wallet, \"sui:signPersonalMessage\");\n const signAndExecute = getFeature<SuiSignAndExecuteTransactionFeature>(\n wallet,\n \"sui:signAndExecuteTransaction\",\n );\n const signTx = getFeature<SuiSignTransactionFeature>(wallet, \"sui:signTransaction\");\n\n let currentChainId = suiChainId;\n const currentChain = (): ChainBase => buildSuiChain(currentChainId, wallet.name);\n\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 executeTx = async (tx: unknown, account?: { walletAddress: string }): Promise<string> => {\n if (signAndExecute === undefined) {\n throw new Error(`Wallet ${wallet.name} does not advertise sui:signAndExecuteTransaction`);\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 transaction = coerceSuiTransaction(tx);\n const output = await signAndExecute.signAndExecuteTransaction({\n account: wsAccount,\n chain: currentChainId,\n transaction,\n });\n return output.digest;\n };\n\n return {\n capabilities: resolveSuiCapabilities({\n chainCount: wallet.chains.filter((c) => c.startsWith(SUI_PREFIX)).length,\n features: {\n events: Boolean(events),\n signAndExecuteTransaction: Boolean(signAndExecute),\n signMessage: Boolean(signMessage),\n signTransaction: Boolean(signTx),\n },\n }),\n chainPlatform: \"sui\",\n\n async connect(opts) {\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] Sui 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 getBalance: () =>\n Promise.resolve({\n decimals: SUI_DECIMALS,\n formatted: \"0\",\n symbol: \"SUI\",\n value: 0n,\n }),\n\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 await connect.connect();\n },\n\n sendTx: (tx, account) => executeTx(tx, account),\n\n sendTxToChain: (tx, _targetChainId, account, cb) => {\n cb?.();\n return executeTx(tx, account);\n },\n\n async signMessage(msg, account) {\n if (signMessage === undefined) {\n throw new Error(`Wallet ${wallet.name} does not advertise sui:signPersonalMessage`);\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.signPersonalMessage({\n account: wsAccount,\n message: msg,\n });\n return {\n signature: base64ToBytes(output.signature),\n signedMessage: base64ToBytes(output.bytes),\n };\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 const transaction = coerceSuiTransaction(tx);\n const output = await signTx.signTransaction({\n account: wsAccount,\n chain: currentChainId,\n transaction,\n });\n return base64ToBytes(output.bytes);\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 if (changes.chains !== undefined) {\n const next =\n changes.chains.find((c) => c === \"sui:mainnet\") ??\n changes.chains.find((c) => c.startsWith(SUI_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 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 !== \"sui\") {\n throw new Error(\n `Sui adapter received non-Sui chain \"${chain.id}\". Pass a chain with namespace \"sui\".`,\n );\n }\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 notifyAccountChanged();\n return Promise.resolve();\n },\n };\n};\n\nexport { buildSuiAdapter, slugify };\n","import type { WalletAdapter } from \"@usebutr/core\";\nimport { discoverWalletStandard } from \"@usebutr/wallet-standard-shared\";\n\nimport { buildSuiAdapter } from \"./wallet-standard-adapter\";\n\n/**\n * Subscribe to Sui Wallet Standard announcements.\n * Spec: https://docs.sui.io/standards/wallet-standard\n *\n * Runtime requires the `@wallet-standard/app` package, which is an\n * **optional peer dependency** of butr.\n *\n * Sui wallets share the same `getWallets()` bus as SVM wallets; Phantom\n * advertises features for SVM, Sui, and Bitcoin from a single Wallet\n * Standard wallet object. `buildSuiAdapter` returns `null` for wallets\n * that don't advertise any `sui:*` features, so we cleanly end up with\n * one adapter per platform on multi-chain wallets.\n *\n * The discovery loop (dynamic import, dedupe, register / unregister\n * bridge) lives in `@usebutr/wallet-standard-shared`.\n */\nconst discoverSuiAdapters = (onAdapter: (adapter: WalletAdapter) => void): (() => void) =>\n discoverWalletStandard(onAdapter, (wallet, registerDisconnector) =>\n buildSuiAdapter(wallet, registerDisconnector),\n );\n\nexport { discoverSuiAdapters };\n","import type { ChainBase } from \"@usebutr/core\";\n\n// Sui wallets follow the Wallet Standard convention of advertising\n// chains as `sui:mainnet` / `sui:testnet` / `sui:devnet` / `sui:localnet`\n// rather than the strict CAIP-2 form (the genesis-checkpoint hash).\n// Sui Wallet, Suiet, Phantom (Sui), and Surf all exchange these short\n// names; the adapter's `switchChain` rejects anything else.\nconst SUI_CHAINS = {\n devnet: { id: \"sui:devnet\", name: \"Sui Devnet\", namespace: \"sui\", reference: \"devnet\" },\n localnet: {\n id: \"sui:localnet\",\n name: \"Sui Localnet\",\n namespace: \"sui\",\n reference: \"localnet\",\n },\n mainnet: {\n id: \"sui:mainnet\",\n name: \"Sui Mainnet\",\n namespace: \"sui\",\n reference: \"mainnet\",\n },\n testnet: { id: \"sui:testnet\", name: \"Sui Testnet\", namespace: \"sui\", reference: \"testnet\" },\n} as const satisfies Record<string, ChainBase>;\n\nconst SUI_CHAINS_LIST: ReadonlyArray<ChainBase> = Object.values(SUI_CHAINS);\n\nexport { SUI_CHAINS, SUI_CHAINS_LIST };\n","import type { PlatformDiscoverer } from \"@usebutr/core\";\n\nimport { discoverSuiAdapters } from \"./wallet-standard\";\n\n/**\n * Sui's `PlatformDiscoverer`: Sui Wallet Standard announcements. No\n * legacy fallback; Sui standardised on Wallet Standard from launch.\n */\nconst suiDiscoverer: PlatformDiscoverer = {\n platform: \"sui\",\n subscribe: discoverSuiAdapters,\n};\n\nexport { suiDiscoverer };\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/capabilities.ts","../src/wallet-standard-adapter.ts","../src/chains.ts","../src/discoverer.ts"],"sourcesContent":["import type { WalletCapabilities } from \"@usebutr/core\";\nimport { buildWalletCapabilities } from \"@usebutr/wallet-standard-shared\";\n\ntype WalletStandardCapabilityInput = {\n chainCount: number;\n features: {\n events: boolean;\n signAndExecuteTransaction: boolean;\n signMessage: boolean;\n signTransaction: boolean;\n };\n};\n\n/**\n * Sui Wallet Standard feature → butr capability mapping. The flags that hold\n * for every namespace are documented on `buildWalletCapabilities`.\n *\n * - `sendTransaction = sui:signAndExecuteTransaction present`. The wallet\n * broadcasts; consumers receive the digest.\n * - `signTransaction = sui:signTransaction present`. Sign-only; consumers\n * broadcast with their own RPC client (`@mysten/sui`'s SuiClient).\n * - `signMessage = sui:signPersonalMessage present`.\n * - `signIn = false`. Sui has no SIWS-equivalent feature in Wallet Standard.\n */\nconst resolveSuiCapabilities = (input: WalletStandardCapabilityInput): WalletCapabilities =>\n buildWalletCapabilities({\n chainCount: input.chainCount,\n events: input.features.events,\n sendTransaction: input.features.signAndExecuteTransaction,\n signIn: false,\n signMessage: input.features.signMessage,\n signTransaction: input.features.signTransaction,\n });\n\nexport type { WalletStandardCapabilityInput };\nexport { resolveSuiCapabilities };\n","import type { WalletAdapter } from \"@usebutr/core\";\nimport { base64ToBytes } from \"@usebutr/core\";\nimport {\n createWalletStandardCore,\n discoverWalletStandard,\n getFeature,\n slugify,\n} from \"@usebutr/wallet-standard-shared\";\nimport type { WalletStandardWallet } from \"@usebutr/wallet-standard-shared\";\n\nimport { resolveSuiCapabilities } from \"./capabilities\";\nimport type {\n SuiSignAndExecuteTransactionFeature,\n SuiSignPersonalMessageFeature,\n SuiSignTransactionFeature,\n} from \"./wallet-standard-types\";\n\nconst SUI_PREFIX = \"sui:\";\nconst SUI_DECIMALS = 9;\nconst SUI_MAINNET = \"sui:mainnet\";\n\n/** Coerce butr's `unknown` tx into the shape `sui:signAndExecuteTransaction`\n * expects (an object with `toJSON()` returning a Promise<string>). When\n * consumers pass a base64 string directly, we wrap it in a stub object so\n * the feature contract stays satisfied without us depending on\n * `@mysten/sui`. */\nconst coerceSuiTransaction = (tx: unknown): { toJSON: () => Promise<string> } | string => {\n if (typeof tx === \"string\") {\n return tx;\n }\n if (typeof tx === \"object\" && tx !== null && \"toJSON\" in tx && typeof tx.toJSON === \"function\") {\n // oxlint-disable-next-line typescript/no-unsafe-type-assertion -- validated toJSON() above; @mysten/sui Transaction is otherwise untyped here\n return tx as { toJSON: () => Promise<string> };\n }\n throw new TypeError(\n \"Sui sendTx/signTransaction expects a @mysten/sui Transaction (with toJSON()) or a base64-encoded string\",\n );\n};\n\n/**\n * Adapt a Sui Wallet Standard `Wallet` into a butr `WalletAdapter`.\n *\n * Returns `null` if the wallet doesn't advertise any Sui chain or doesn't\n * expose `standard:connect`. Same posture as `buildSvmAdapter`: gate\n * features on what each wallet advertises, return placeholders for\n * RPC-backed reads (`getBalance`, `getTransactionReceipt`), forward\n * `subscribe` through `standard:events`.\n *\n * **Known limitations**\n *\n * - `getBalance` returns a `{ value: 0n, symbol: \"SUI\" }` placeholder.\n * Consumers wrap `@mysten/sui`'s `SuiClient` for real reads.\n * - `getTransactionReceipt` returns `\"Pending\"` for the same reason.\n * - `switchChain` is local-state-only (mirrors SVM). Sui Wallet Standard\n * has no \"tell the wallet to switch network\" RPC; the wallet's UI\n * controls its global cluster setting.\n */\nconst buildSuiAdapter = (\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`. */\n registerDisconnector?: (emit: () => void) => void,\n): WalletAdapter | null => {\n const core = createWalletStandardCore({\n chainPrefix: SUI_PREFIX,\n id: slugify(\"sui\", wallet.name),\n label: \"Sui\",\n namespace: \"sui\",\n platform: \"Sui\",\n preferredChainIds: [SUI_MAINNET],\n registerDisconnector,\n trackChainChanges: true,\n wallet,\n });\n if (core === null) {\n return null;\n }\n\n const signMessage = getFeature<SuiSignPersonalMessageFeature>(wallet, \"sui:signPersonalMessage\");\n const signAndExecute = getFeature<SuiSignAndExecuteTransactionFeature>(\n wallet,\n \"sui:signAndExecuteTransaction\",\n );\n const signTx = getFeature<SuiSignTransactionFeature>(wallet, \"sui:signTransaction\");\n\n const executeTx = async (tx: unknown, account?: { walletAddress: string }): Promise<string> => {\n if (signAndExecute === undefined) {\n throw new Error(`Wallet ${wallet.name} does not advertise sui:signAndExecuteTransaction`);\n }\n const wsAccount = core.resolveAccount(account);\n const transaction = coerceSuiTransaction(tx);\n const output = await signAndExecute.signAndExecuteTransaction({\n account: wsAccount,\n chain: core.currentChainId(),\n transaction,\n });\n return output.digest;\n };\n\n return {\n ...core,\n capabilities: resolveSuiCapabilities({\n chainCount: core.chainCount,\n features: {\n events: core.hasEvents,\n signAndExecuteTransaction: Boolean(signAndExecute),\n signMessage: Boolean(signMessage),\n signTransaction: Boolean(signTx),\n },\n }),\n chainPlatform: \"sui\",\n\n getBalance: () =>\n Promise.resolve({\n decimals: SUI_DECIMALS,\n formatted: \"0\",\n symbol: \"SUI\",\n value: 0n,\n }),\n\n getTransactionReceipt: () => Promise.resolve({ status: \"Pending\" as const }),\n\n async requestAccounts() {\n await core.connect();\n },\n\n sendTx: (tx, account) => executeTx(tx, account),\n\n sendTxToChain: (tx, _targetChainId, account, cb) => {\n cb?.();\n return executeTx(tx, account);\n },\n\n async signMessage(msg, account) {\n if (signMessage === undefined) {\n throw new Error(`Wallet ${wallet.name} does not advertise sui:signPersonalMessage`);\n }\n const output = await signMessage.signPersonalMessage({\n account: core.resolveAccount(account),\n message: msg,\n });\n return {\n signature: base64ToBytes(output.signature),\n signedMessage: base64ToBytes(output.bytes),\n };\n },\n\n ...(signTx === undefined\n ? {}\n : {\n async signTransaction(tx, account) {\n const wsAccount = core.resolveAccount(account);\n const transaction = coerceSuiTransaction(tx);\n const output = await signTx.signTransaction({\n account: wsAccount,\n chain: core.currentChainId(),\n transaction,\n });\n return base64ToBytes(output.bytes);\n },\n }),\n };\n};\n\n/**\n * Subscribe to Sui Wallet Standard announcements.\n * Spec: https://docs.sui.io/standards/wallet-standard\n *\n * Runtime requires the `@wallet-standard/app` package, which is an\n * **optional peer dependency** of butr.\n *\n * Sui wallets share the same `getWallets()` bus as SVM wallets; Phantom\n * advertises features for SVM, Sui, and Bitcoin from a single Wallet\n * Standard wallet object. `buildSuiAdapter` returns `null` for wallets\n * that don't advertise any `sui:*` features, so we cleanly end up with\n * one adapter per platform on multi-chain wallets.\n *\n * The discovery loop (dynamic import, dedupe, register / unregister\n * bridge) lives in `@usebutr/wallet-standard-shared`.\n */\nconst discoverSuiAdapters = (onAdapter: (adapter: WalletAdapter) => void): (() => void) =>\n discoverWalletStandard(onAdapter, (wallet, registerDisconnector) =>\n buildSuiAdapter(wallet, registerDisconnector),\n );\n\nexport { buildSuiAdapter, discoverSuiAdapters };\n","import type { ChainBase } from \"@usebutr/core\";\n\n// Sui wallets follow the Wallet Standard convention of advertising\n// chains as `sui:mainnet` / `sui:testnet` / `sui:devnet` / `sui:localnet`\n// rather than the strict CAIP-2 form (the genesis-checkpoint hash).\n// Sui Wallet, Suiet, Phantom (Sui), and Surf all exchange these short\n// names; the adapter's `switchChain` rejects anything else.\nconst SUI_CHAINS = {\n devnet: { id: \"sui:devnet\", name: \"Sui Devnet\", namespace: \"sui\", reference: \"devnet\" },\n localnet: {\n id: \"sui:localnet\",\n name: \"Sui Localnet\",\n namespace: \"sui\",\n reference: \"localnet\",\n },\n mainnet: {\n id: \"sui:mainnet\",\n name: \"Sui Mainnet\",\n namespace: \"sui\",\n reference: \"mainnet\",\n },\n testnet: { id: \"sui:testnet\", name: \"Sui Testnet\", namespace: \"sui\", reference: \"testnet\" },\n} as const satisfies Record<string, ChainBase>;\n\nconst SUI_CHAINS_LIST: ReadonlyArray<ChainBase> = Object.values(SUI_CHAINS);\n\nexport { SUI_CHAINS, SUI_CHAINS_LIST };\n","import type { PlatformDiscoverer } from \"@usebutr/core\";\n\nimport { discoverSuiAdapters } from \"./wallet-standard-adapter\";\n\n/**\n * Sui's `PlatformDiscoverer`: Sui Wallet Standard announcements. No\n * legacy fallback; Sui standardised on Wallet Standard from launch.\n */\nconst suiDiscoverer: PlatformDiscoverer = {\n subscribe: discoverSuiAdapters,\n};\n\nexport { suiDiscoverer };\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@usebutr/sui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "Wallet Standard adapter for butr (Sui).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Pedro Filho <pedro@filho.me>",
|
|
@@ -25,8 +25,8 @@
|
|
|
25
25
|
"access": "public"
|
|
26
26
|
},
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@usebutr/core": "0.
|
|
29
|
-
"@usebutr/wallet-standard-shared": "0.
|
|
28
|
+
"@usebutr/core": "1.0.0",
|
|
29
|
+
"@usebutr/wallet-standard-shared": "0.4.0"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
32
|
"@wallet-standard/app": "^1.1.1",
|