@usebutr/sui 0.2.5 → 0.2.7

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 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 SuiSignPersonalMessageInput = {
6
5
  account: WalletStandardWalletAccount;
@@ -68,7 +67,6 @@ declare const buildSuiAdapter: (wallet: WalletStandardWallet,
68
67
  /** Optional. Called with a function that pushes a synthetic
69
68
  * `disconnected` event to all current subscribers. The discovery
70
69
  * layer invokes it on Wallet Standard `unregister`. */
71
-
72
70
  registerDisconnector?: (emit: () => void) => void) => WalletAdapter | null;
73
71
  //#endregion
74
72
  //#region src/wallet-standard.d.ts
@@ -79,7 +77,7 @@ registerDisconnector?: (emit: () => void) => void) => WalletAdapter | null;
79
77
  * Runtime requires the `@wallet-standard/app` package, which is an
80
78
  * **optional peer dependency** of butr.
81
79
  *
82
- * Sui wallets share the same `getWallets()` bus as SVM wallets — Phantom
80
+ * Sui wallets share the same `getWallets()` bus as SVM wallets; Phantom
83
81
  * advertises features for SVM, Sui, and Bitcoin from a single Wallet
84
82
  * Standard wallet object. `buildSuiAdapter` returns `null` for wallets
85
83
  * that don't advertise any `sui:*` features, so we cleanly end up with
@@ -139,7 +137,7 @@ type WalletStandardCapabilityInput = {
139
137
  * broadcast with their own RPC client (`@mysten/sui`'s SuiClient).
140
138
  * - `signMessage = sui:signPersonalMessage present`.
141
139
  * - `signIn = false`. Sui has no SIWS-equivalent feature in Wallet Standard.
142
- * - `switchChain = chainCount > 1`. Same posture as SVM — local re-routing
140
+ * - `switchChain = chainCount > 1`. Same posture as SVM; local re-routing
143
141
  * for `signAndExecuteTransaction`'s per-call `chain` input.
144
142
  */
145
143
  declare const resolveSuiCapabilities: (input: WalletStandardCapabilityInput) => WalletCapabilities;
@@ -147,7 +145,7 @@ declare const resolveSuiCapabilities: (input: WalletStandardCapabilityInput) =>
147
145
  //#region src/discoverer.d.ts
148
146
  /**
149
147
  * Sui's `PlatformDiscoverer`: Sui Wallet Standard announcements. No
150
- * legacy fallback — Sui standardised on Wallet Standard from launch.
148
+ * legacy fallback; Sui standardised on Wallet Standard from launch.
151
149
  */
152
150
  declare const suiDiscoverer: PlatformDiscoverer;
153
151
  //#endregion
@@ -162,7 +160,7 @@ declare module "@usebutr/core" {
162
160
  interface SignerForPlatform {
163
161
  sui: WalletStandardWallet;
164
162
  }
165
- } //# sourceMappingURL=signer-augmentation.d.ts.map
163
+ }
166
164
  //#endregion
167
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, slugify, suiDiscoverer };
168
166
  //# sourceMappingURL=index.d.ts.map
@@ -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":";;;;KAaK,2BAAA;EACH,OAAA,EAAS,2BAAA;EACT,OAAA,EAAS,UAAU;AAAA;AAAA,KAGhB,4BAAA;EACH,KAAA;EACA,SAAS;AAAA;AAAA,KAGN,6BAAA;EACH,mBAAA,GACE,KAAA,EAAO,2BAAA,KACJ,OAAA,CAAQ,4BAAA;AAAA;AAAA,KAGV,uBAAA;EACH,OAAA,EAAS,2BAAA;EACT,KAAA;EACA,WAAA;IAAe,MAAA,QAAc,OAAO;EAAA;AAAA;AAAA,KAGjC,wBAAA;EACH,KAAA;EACA,SAAS;AAAA;AAAA,KAGN,yBAAA;EACH,eAAA,GAAkB,KAAA,EAAO,uBAAA,KAA4B,OAAA,CAAQ,wBAAA;AAAA;AAAA,KAG1D,iCAAA;EACH,OAAA,EAAS,2BAAA;EACT,KAAA;EACA,WAAA;IAAe,MAAA,QAAc,OAAO;EAAA;AAAA;AAAA,KAGjC,kCAAA;EACH,KAAA;EACA,MAAA;EACA,OAAA;EACA,SAAA;AAAA;AAAA,KAGG,mCAAA;EACH,yBAAA,GACE,KAAA,EAAO,iCAAA,KACJ,OAAA,CAAQ,kCAAA;AAAA;;;cClCT,OAAA,GAAW,IAAY;;ADfsD;;;;;;;;;AAI9D;AAAA;;;;AAKV;AAAA;;cC+DL,eAAA,GACJ,MAAA,EAAQ,oBAAA;;;;;AAIR,oBAAA,IAAwB,IAAA,0BACvB,aAAa;;;;;;AD9EmE;;;;;;;;;AAI9D;AAAA;;;cEMf,mBAAA,GAAuB,SAAA,GAAY,OAAA,EAAS,aAAa;;;cCdzD,UAAA;EAAA;;;;;;;;;;;;;;;;;;;;;;;;;cAiBA,eAAA,EAAiB,aAAa,CAAC,SAAA;;;KCtBhC,6BAAA;EACH,UAAA;EACA,QAAA;IACE,MAAA;IACA,yBAAA;IACA,WAAA;IACA,eAAA;EAAA;AAAA;;;;AJOiB;AAAA;;;;AAKV;AAAA;;;;cIKL,sBAAA,GAA0B,KAAA,EAAO,6BAAA,KAAgC,kBAWrE;;;;;;AJzBiF;cKH7E,aAAA,EAAe,kBAGpB;;;;;;ALAkF;;;;YMDvE,iBAAA;IACR,GAAA,EAAK,oBAAoB;EAAA;AAAA"}
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;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;;;;cCxCT,UAAW;;;;;;;;;;;;;;;;;;;cAsCX,kBACJ,QAAQ,sBAIR;;;;AAAA,wBAAwB,8BACvB;;;;;;;;;;;;;;;;;;;cC3CG,sBAAuB,YAAY,SAAS;;;cCd5C;;;;;;;;;;;;;;;;;;;;;;;;;;cAiBA,iBAAiB,cAAc;;;KCtBhC;EACH;EACA;IACE;IACA;IACA;IACA;;;;;;;;;;;;;;;;cAiBE,yBAA0B,OAAO,kCAAgC;;;;;;;cCjBjE,eAAe;;;;;;;;;;YCET;IACR,KAAK"}
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import { base64ToBytes, logWarn, sanitizeIcon } from "@usebutr/core";
2
- import { buildAccount, discoverWalletStandard, getFeature, pickAccountByAddress, pickFirstAddress, slugify as slugify$1 } from "@usebutr/wallet-standard-shared";
1
+ import { base64ToBytes } from "@usebutr/core";
2
+ import { createWalletStandardCore, discoverWalletStandard, getFeature, slugify as slugify$1 } from "@usebutr/wallet-standard-shared";
3
3
 
4
4
  //#region src/capabilities.ts
5
5
  /**
@@ -12,7 +12,7 @@ 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
15
+ * - `switchChain = chainCount > 1`. Same posture as SVM; local re-routing
16
16
  * for `signAndExecuteTransaction`'s per-call `chain` input.
17
17
  */
18
18
  const resolveSuiCapabilities = (input) => ({
@@ -32,19 +32,8 @@ const resolveSuiCapabilities = (input) => ({
32
32
  //#region src/wallet-standard-adapter.ts
33
33
  const SUI_PREFIX = "sui:";
34
34
  const SUI_DECIMALS = 9;
35
+ const SUI_MAINNET = "sui:mainnet";
35
36
  const slugify = (name) => slugify$1("sui", name);
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
- });
47
- const buildSuiAccount = (address, chain) => buildAccount(address, chain);
48
37
  /** Coerce butr's `unknown` tx into the shape `sui:signAndExecuteTransaction`
49
38
  * expects (an object with `toJSON()` returning a Promise<string>). When
50
39
  * consumers pass a base64 string directly, we wrap it in a stub object so
@@ -52,7 +41,7 @@ const buildSuiAccount = (address, chain) => buildAccount(address, chain);
52
41
  * `@mysten/sui`. */
53
42
  const coerceSuiTransaction = (tx) => {
54
43
  if (typeof tx === "string") return tx;
55
- if (tx && typeof tx === "object" && "toJSON" in tx && typeof tx.toJSON === "function") return tx;
44
+ if (typeof tx === "object" && tx !== null && "toJSON" in tx && typeof tx.toJSON === "function") return tx;
56
45
  throw new TypeError("Sui sendTx/signTransaction expects a @mysten/sui Transaction (with toJSON()) or a base64-encoded string");
57
46
  };
58
47
  /**
@@ -74,104 +63,62 @@ const coerceSuiTransaction = (tx) => {
74
63
  * controls its global cluster setting.
75
64
  */
76
65
  const buildSuiAdapter = (wallet, registerDisconnector) => {
77
- const suiChainId = pickSuiChain(wallet);
78
- if (!suiChainId) return null;
79
- const connect = getFeature(wallet, "standard:connect");
80
- if (!connect) return null;
81
- const disconnect = getFeature(wallet, "standard:disconnect");
82
- const events = getFeature(wallet, "standard:events");
66
+ const core = createWalletStandardCore({
67
+ chainPrefix: SUI_PREFIX,
68
+ id: slugify(wallet.name),
69
+ label: "Sui",
70
+ namespace: "sui",
71
+ platform: "Sui",
72
+ preferredChainIds: [SUI_MAINNET],
73
+ registerDisconnector,
74
+ trackChainChanges: true,
75
+ wallet
76
+ });
77
+ if (core === null) return null;
83
78
  const signMessage = getFeature(wallet, "sui:signPersonalMessage");
84
79
  const signAndExecute = getFeature(wallet, "sui:signAndExecuteTransaction");
85
80
  const signTx = getFeature(wallet, "sui:signTransaction");
86
- let currentChainId = suiChainId;
87
- const currentChain = () => buildSuiChain(currentChainId, wallet.name);
88
- const listeners = /* @__PURE__ */ new Set();
89
- const notifyAccountChanged = () => {
90
- if (wallet.accounts.length === 0) return;
91
- const chain = currentChain();
92
- const built = wallet.accounts.map((a) => buildSuiAccount(a.address, chain));
93
- const first = built[0];
94
- if (!first) return;
95
- for (const listener of listeners) listener({
96
- account: first,
97
- accounts: built,
98
- type: "accountChanged"
99
- });
81
+ const executeTx = async (tx, account) => {
82
+ if (signAndExecute === void 0) throw new Error(`Wallet ${wallet.name} does not advertise sui:signAndExecuteTransaction`);
83
+ const wsAccount = core.resolveAccount(account);
84
+ const transaction = coerceSuiTransaction(tx);
85
+ return (await signAndExecute.signAndExecuteTransaction({
86
+ account: wsAccount,
87
+ chain: core.currentChainId(),
88
+ transaction
89
+ })).digest;
100
90
  };
101
- registerDisconnector?.(() => {
102
- for (const listener of listeners) listener({ type: "disconnected" });
103
- });
104
91
  return {
92
+ ...core,
105
93
  capabilities: resolveSuiCapabilities({
106
- chainCount: wallet.chains.filter((c) => c.startsWith(SUI_PREFIX)).length,
94
+ chainCount: core.chainCount,
107
95
  features: {
108
- events: Boolean(events),
96
+ events: core.hasEvents,
109
97
  signAndExecuteTransaction: Boolean(signAndExecute),
110
98
  signMessage: Boolean(signMessage),
111
99
  signTransaction: Boolean(signTx)
112
100
  }
113
101
  }),
114
102
  chainPlatform: "sui",
115
- async connect(opts) {
116
- await connect.connect(opts?.silent ? { silent: true } : void 0);
117
- },
118
- async disconnect() {
119
- if (disconnect) try {
120
- await disconnect.disconnect();
121
- } catch (error) {
122
- logWarn("[butr] Sui Wallet Standard disconnect threw:", error);
123
- }
124
- },
125
- getAccount() {
126
- const address = pickFirstAddress(wallet.accounts);
127
- if (!address) return Promise.resolve(null);
128
- return Promise.resolve(buildSuiAccount(address, currentChain()));
129
- },
130
- getAccounts() {
131
- const chain = currentChain();
132
- return Promise.resolve(wallet.accounts.map((a) => buildSuiAccount(a.address, chain)));
133
- },
134
- getBalance() {
135
- return Promise.resolve({
136
- decimals: SUI_DECIMALS,
137
- formatted: "0",
138
- symbol: "SUI",
139
- value: 0n
140
- });
141
- },
142
- getSigner() {
143
- return Promise.resolve(wallet);
144
- },
145
- getTransactionReceipt() {
146
- return Promise.resolve({ status: "Pending" });
147
- },
148
- icon: sanitizeIcon(wallet.icon),
149
- id: slugify(wallet.name),
150
- name: wallet.name,
103
+ getBalance: () => Promise.resolve({
104
+ decimals: SUI_DECIMALS,
105
+ formatted: "0",
106
+ symbol: "SUI",
107
+ value: 0n
108
+ }),
109
+ getTransactionReceipt: () => Promise.resolve({ status: "Pending" }),
151
110
  async requestAccounts() {
152
- await connect.connect();
153
- },
154
- async sendTx(tx, account) {
155
- if (!signAndExecute) throw new Error(`Wallet ${wallet.name} does not advertise sui:signAndExecuteTransaction`);
156
- const wsAccount = account ? pickAccountByAddress(wallet.accounts, account.walletAddress) : wallet.accounts[0] ?? null;
157
- if (!wsAccount) throw new Error("No connected account");
158
- const transaction = coerceSuiTransaction(tx);
159
- return (await signAndExecute.signAndExecuteTransaction({
160
- account: wsAccount,
161
- chain: currentChainId,
162
- transaction
163
- })).digest;
111
+ await core.connect();
164
112
  },
165
- sendTxToChain(tx, _targetChainId, account, cb) {
113
+ sendTx: (tx, account) => executeTx(tx, account),
114
+ sendTxToChain: (tx, _targetChainId, account, cb) => {
166
115
  cb?.();
167
- return this.sendTx(tx, account);
116
+ return executeTx(tx, account);
168
117
  },
169
118
  async signMessage(msg, account) {
170
- if (!signMessage) throw new Error(`Wallet ${wallet.name} does not advertise sui:signPersonalMessage`);
171
- const wsAccount = account ? pickAccountByAddress(wallet.accounts, account.walletAddress) : wallet.accounts[0] ?? null;
172
- if (!wsAccount) throw new Error("No connected account");
119
+ if (signMessage === void 0) throw new Error(`Wallet ${wallet.name} does not advertise sui:signPersonalMessage`);
173
120
  const output = await signMessage.signPersonalMessage({
174
- account: wsAccount,
121
+ account: core.resolveAccount(account),
175
122
  message: msg
176
123
  });
177
124
  return {
@@ -179,57 +126,15 @@ const buildSuiAdapter = (wallet, registerDisconnector) => {
179
126
  signedMessage: base64ToBytes(output.bytes)
180
127
  };
181
128
  },
182
- ...signTx ? { async signTransaction(tx, account) {
183
- const wsAccount = account ? pickAccountByAddress(wallet.accounts, account.walletAddress) : wallet.accounts[0] ?? null;
184
- if (!wsAccount) throw new Error("No connected account");
129
+ ...signTx === void 0 ? {} : { async signTransaction(tx, account) {
130
+ const wsAccount = core.resolveAccount(account);
185
131
  const transaction = coerceSuiTransaction(tx);
186
132
  return base64ToBytes((await signTx.signTransaction({
187
133
  account: wsAccount,
188
- chain: currentChainId,
134
+ chain: core.currentChainId(),
189
135
  transaction
190
136
  })).bytes);
191
- } } : {},
192
- subscribe(listener) {
193
- listeners.add(listener);
194
- let unsubWallet = null;
195
- if (events) {
196
- const unsub = events.on("change", (changes) => {
197
- if (changes.chains) {
198
- const next = changes.chains.find((c) => c === "sui:mainnet") ?? changes.chains.find((c) => c.startsWith(SUI_PREFIX));
199
- if (next) currentChainId = next;
200
- }
201
- if (changes.accounts) {
202
- if (changes.accounts.length === 0) {
203
- listener({ type: "disconnected" });
204
- return;
205
- }
206
- const chain = currentChain();
207
- const built = changes.accounts.map((a) => buildSuiAccount(a.address, chain));
208
- const first = built[0];
209
- if (!first) return;
210
- listener({
211
- account: first,
212
- accounts: built,
213
- type: "accountChanged"
214
- });
215
- return;
216
- }
217
- if (changes.chains) notifyAccountChanged();
218
- });
219
- unsubWallet = () => unsub();
220
- }
221
- return () => {
222
- listeners.delete(listener);
223
- unsubWallet?.();
224
- };
225
- },
226
- switchChain(chain) {
227
- if (chain.namespace !== "sui") throw new Error(`Sui adapter received non-Sui chain "${chain.id}". Pass a chain with namespace "sui".`);
228
- if (!wallet.chains.includes(chain.id)) throw new Error(`Wallet ${wallet.name} does not advertise chain "${chain.id}". Available: ${wallet.chains.join(", ")}`);
229
- currentChainId = chain.id;
230
- notifyAccountChanged();
231
- return Promise.resolve();
232
- }
137
+ } }
233
138
  };
234
139
  };
235
140
 
@@ -242,7 +147,7 @@ const buildSuiAdapter = (wallet, registerDisconnector) => {
242
147
  * Runtime requires the `@wallet-standard/app` package, which is an
243
148
  * **optional peer dependency** of butr.
244
149
  *
245
- * Sui wallets share the same `getWallets()` bus as SVM wallets — Phantom
150
+ * Sui wallets share the same `getWallets()` bus as SVM wallets; Phantom
246
151
  * advertises features for SVM, Sui, and Bitcoin from a single Wallet
247
152
  * Standard wallet object. `buildSuiAdapter` returns `null` for wallets
248
153
  * that don't advertise any `sui:*` features, so we cleanly end up with
@@ -287,7 +192,7 @@ const SUI_CHAINS_LIST = Object.values(SUI_CHAINS);
287
192
  //#region src/discoverer.ts
288
193
  /**
289
194
  * Sui's `PlatformDiscoverer`: Sui Wallet Standard announcements. No
290
- * legacy fallback — Sui standardised on Wallet Standard from launch.
195
+ * legacy fallback; Sui standardised on Wallet Standard from launch.
291
196
  */
292
197
  const suiDiscoverer = {
293
198
  platform: "sui",
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 { Account, 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 // Same posture as SVM: butr ships no chain-id → name table. Consumers\n // overlay via structural typing.\n name: walletName,\n namespace: \"sui\",\n reference: chainId.slice(SUI_PREFIX.length),\n});\n\nconst buildSuiAccount = (address: string, chain: ChainBase): Account =>\n buildAccount(address, chain);\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 (tx && typeof tx === \"object\" && \"toJSON\" in tx && typeof tx.toJSON === \"function\") {\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) {\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<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) => buildSuiAccount(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: 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 ? { silent: true } : undefined);\n },\n\n async disconnect() {\n if (disconnect) {\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) {\n return Promise.resolve(null);\n }\n return Promise.resolve(buildSuiAccount(address, currentChain()));\n },\n\n getAccounts() {\n const chain = currentChain();\n return Promise.resolve(wallet.accounts.map((a) => buildSuiAccount(a.address, chain)));\n },\n\n getBalance() {\n return Promise.resolve({\n decimals: SUI_DECIMALS,\n formatted: \"0\",\n symbol: \"SUI\",\n value: 0n,\n });\n },\n\n getSigner() {\n return Promise.resolve(wallet);\n },\n\n getTransactionReceipt() {\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 await connect.connect();\n },\n\n async sendTx(tx, account) {\n if (!signAndExecute) {\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] ?? null);\n if (!wsAccount) {\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 sendTxToChain(tx, _targetChainId, account, cb) {\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 sui:signPersonalMessage`);\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.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\n ? {\n async signTransaction(tx, account) {\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 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\n subscribe(listener) {\n listeners.add(listener);\n let unsubWallet: (() => void) | null = null;\n if (events) {\n const unsub = events.on(\"change\", (changes) => {\n if (changes.chains) {\n const next =\n changes.chains.find((c) => c === \"sui:mainnet\") ??\n changes.chains.find((c) => c.startsWith(SUI_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 const chain = currentChain();\n const built = changes.accounts.map((a) => buildSuiAccount(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 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 !== \"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":["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 { WalletAdapter } from \"@usebutr/core\";\nimport { base64ToBytes } from \"@usebutr/core\";\nimport {\n createWalletStandardCore,\n getFeature,\n slugify as kitSlugify,\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\nconst slugify = (name: string): string => kitSlugify(\"sui\", name);\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(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\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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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@usebutr/sui",
3
- "version": "0.2.5",
3
+ "version": "0.2.7",
4
4
  "description": "Wallet Standard adapter for butr (Sui).",
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.1",
29
- "@usebutr/wallet-standard-shared": "0.2.5"
28
+ "@usebutr/core": "0.5.0",
29
+ "@usebutr/wallet-standard-shared": "0.3.0"
30
30
  },
31
31
  "devDependencies": {
32
32
  "@wallet-standard/app": "^1.1.1",
33
33
  "typescript": "^6.0.3",
34
- "vitest": "^4.1.9",
34
+ "vitest": "^4.1.10",
35
35
  "@repo/config-vitest": "0.0.0",
36
36
  "@repo/typescript-config": "0.0.0"
37
37
  },