@usebutr/sui 0.1.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.
@@ -0,0 +1,168 @@
1
+ import { ChainBase, PlatformDiscoverer, WalletAdapter, WalletCapabilities } from "@usebutr/core";
2
+ import { StandardConnectFeature, StandardDisconnectFeature, StandardEventsFeature, StandardEventsListener, WalletStandardAppModule, WalletStandardWallet, WalletStandardWallet as WalletStandardWallet$1, WalletStandardWalletAccount, WalletStandardWalletAccount as WalletStandardWalletAccount$1, WalletsApp } from "@usebutr/wallet-standard-shared";
3
+
4
+ //#region src/wallet-standard-types.d.ts
5
+ type SuiSignPersonalMessageInput = {
6
+ account: WalletStandardWalletAccount$1;
7
+ message: Uint8Array;
8
+ };
9
+ type SuiSignPersonalMessageOutput = {
10
+ bytes: string;
11
+ signature: string;
12
+ };
13
+ type SuiSignPersonalMessageFeature = {
14
+ signPersonalMessage(input: SuiSignPersonalMessageInput): Promise<SuiSignPersonalMessageOutput>;
15
+ };
16
+ type SuiSignTransactionInput = {
17
+ account: WalletStandardWalletAccount$1;
18
+ chain: string;
19
+ transaction: {
20
+ toJSON(): Promise<string>;
21
+ } | string;
22
+ };
23
+ type SuiSignTransactionOutput = {
24
+ bytes: string;
25
+ signature: string;
26
+ };
27
+ type SuiSignTransactionFeature = {
28
+ signTransaction(input: SuiSignTransactionInput): Promise<SuiSignTransactionOutput>;
29
+ };
30
+ type SuiSignAndExecuteTransactionInput = {
31
+ account: WalletStandardWalletAccount$1;
32
+ chain: string;
33
+ transaction: {
34
+ toJSON(): Promise<string>;
35
+ } | string;
36
+ };
37
+ type SuiSignAndExecuteTransactionOutput = {
38
+ bytes: string;
39
+ digest: string;
40
+ effects: string;
41
+ signature: string;
42
+ };
43
+ type SuiSignAndExecuteTransactionFeature = {
44
+ signAndExecuteTransaction(input: SuiSignAndExecuteTransactionInput): Promise<SuiSignAndExecuteTransactionOutput>;
45
+ };
46
+ //#endregion
47
+ //#region src/wallet-standard-adapter.d.ts
48
+ declare const slugify: (name: string) => string;
49
+ /**
50
+ * Adapt a Sui Wallet Standard `Wallet` into a butr `WalletAdapter`.
51
+ *
52
+ * Returns `null` if the wallet doesn't advertise any Sui chain or doesn't
53
+ * expose `standard:connect`. Same posture as `buildSvmAdapter`: gate
54
+ * features on what each wallet advertises, return placeholders for
55
+ * RPC-backed reads (`getBalance`, `getTransactionReceipt`), forward
56
+ * `subscribe` through `standard:events`.
57
+ *
58
+ * **Known limitations**
59
+ *
60
+ * - `getBalance` returns a `{ value: 0n, symbol: "SUI" }` placeholder.
61
+ * Consumers wrap `@mysten/sui`'s `SuiClient` for real reads.
62
+ * - `getTransactionReceipt` returns `"Pending"` for the same reason.
63
+ * - `switchChain` is local-state-only (mirrors SVM). Sui Wallet Standard
64
+ * has no "tell the wallet to switch network" RPC; the wallet's UI
65
+ * controls its global cluster setting.
66
+ */
67
+ declare const buildSuiAdapter: (wallet: WalletStandardWallet$1,
68
+ /** Optional. Called with a function that pushes a synthetic
69
+ * `disconnected` event to all current subscribers. The discovery
70
+ * layer invokes it on Wallet Standard `unregister`. */
71
+
72
+ registerDisconnector?: (emit: () => void) => void) => WalletAdapter | null;
73
+ //#endregion
74
+ //#region src/wallet-standard.d.ts
75
+ /**
76
+ * Subscribe to Sui Wallet Standard announcements.
77
+ * Spec: https://docs.sui.io/standards/wallet-standard
78
+ *
79
+ * Runtime requires the `@wallet-standard/app` package, which is an
80
+ * **optional peer dependency** of butr.
81
+ *
82
+ * Sui wallets share the same `getWallets()` bus as SVM wallets — Phantom
83
+ * advertises features for SVM, Sui, and Bitcoin from a single Wallet
84
+ * Standard wallet object. `buildSuiAdapter` returns `null` for wallets
85
+ * that don't advertise any `sui:*` features, so we cleanly end up with
86
+ * one adapter per platform on multi-chain wallets.
87
+ *
88
+ * The discovery loop (dynamic import, dedupe, register / unregister
89
+ * bridge) lives in `@usebutr/wallet-standard-shared`.
90
+ */
91
+ declare const discoverSuiAdapters: (onAdapter: (adapter: WalletAdapter) => void) => (() => void);
92
+ //#endregion
93
+ //#region src/chains.d.ts
94
+ declare const SUI_CHAINS: {
95
+ readonly devnet: {
96
+ readonly id: "sui:devnet";
97
+ readonly name: "Sui Devnet";
98
+ readonly namespace: "sui";
99
+ readonly reference: "devnet";
100
+ };
101
+ readonly localnet: {
102
+ readonly id: "sui:localnet";
103
+ readonly name: "Sui Localnet";
104
+ readonly namespace: "sui";
105
+ readonly reference: "localnet";
106
+ };
107
+ readonly mainnet: {
108
+ readonly id: "sui:mainnet";
109
+ readonly name: "Sui Mainnet";
110
+ readonly namespace: "sui";
111
+ readonly reference: "mainnet";
112
+ };
113
+ readonly testnet: {
114
+ readonly id: "sui:testnet";
115
+ readonly name: "Sui Testnet";
116
+ readonly namespace: "sui";
117
+ readonly reference: "testnet";
118
+ };
119
+ };
120
+ declare const SUI_CHAINS_LIST: ReadonlyArray<ChainBase>;
121
+ //#endregion
122
+ //#region src/capabilities.d.ts
123
+ type WalletStandardCapabilityInput = {
124
+ chainCount: number;
125
+ features: {
126
+ events: boolean;
127
+ signAndExecuteTransaction: boolean;
128
+ signMessage: boolean;
129
+ signTransaction: boolean;
130
+ };
131
+ };
132
+ /**
133
+ * Sui Wallet Standard capability mapping. Mirrors the SVM resolver:
134
+ * butr ships no RPC, so balance/receipt always false.
135
+ *
136
+ * - `sendTransaction = sui:signAndExecuteTransaction present`. The wallet
137
+ * broadcasts; consumers receive the digest.
138
+ * - `signTransaction = sui:signTransaction present`. Sign-only; consumers
139
+ * broadcast with their own RPC client (`@mysten/sui`'s SuiClient).
140
+ * - `signMessage = sui:signPersonalMessage present`.
141
+ * - `signIn = false`. Sui has no SIWS-equivalent feature in Wallet Standard.
142
+ * - `switchChain = chainCount > 1`. Same posture as SVM — local re-routing
143
+ * for `signAndExecuteTransaction`'s per-call `chain` input.
144
+ */
145
+ declare const resolveSuiCapabilities: (input: WalletStandardCapabilityInput) => WalletCapabilities;
146
+ //#endregion
147
+ //#region src/discoverer.d.ts
148
+ /**
149
+ * Sui's `PlatformDiscoverer`: Sui Wallet Standard announcements. No
150
+ * legacy fallback — Sui standardised on Wallet Standard from launch.
151
+ */
152
+ declare const suiDiscoverer: PlatformDiscoverer;
153
+ //#endregion
154
+ //#region src/signer-augmentation.d.ts
155
+ /**
156
+ * Augment `@usebutr/core`'s `SignerForPlatform` registry with the Sui
157
+ * entry. Sui's `getSigner()` returns the Wallet Standard wallet
158
+ * object, which consumers narrow further via its `features` map (e.g.
159
+ * `wallet.features["sui:signAndExecuteTransaction"]`).
160
+ */
161
+ declare module "@usebutr/core" {
162
+ interface SignerForPlatform {
163
+ sui: WalletStandardWallet$1;
164
+ }
165
+ } //# sourceMappingURL=signer-augmentation.d.ts.map
166
+ //#endregion
167
+ export { SUI_CHAINS, SUI_CHAINS_LIST, type StandardConnectFeature, type StandardDisconnectFeature, type StandardEventsFeature, type StandardEventsListener, type SuiSignAndExecuteTransactionFeature, type SuiSignAndExecuteTransactionInput, type SuiSignAndExecuteTransactionOutput, type SuiSignPersonalMessageFeature, type SuiSignPersonalMessageInput, type SuiSignPersonalMessageOutput, type SuiSignTransactionFeature, type SuiSignTransactionInput, type SuiSignTransactionOutput, type WalletStandardAppModule, type WalletStandardCapabilityInput, type WalletStandardWallet, type WalletStandardWalletAccount, type WalletsApp, buildSuiAdapter, discoverSuiAdapters, resolveSuiCapabilities, slugify, suiDiscoverer };
168
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +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,6BAAA;EACT,OAAA,EAAS,UAAU;AAAA;AAAA,KAGhB,4BAAA;EACH,KAAA;EACA,SAAS;AAAA;AAAA,KAGN,6BAAA;EACH,mBAAA,CAAoB,KAAA,EAAO,2BAAA,GAA8B,OAAA,CAAQ,4BAAA;AAAA;AAAA,KAG9D,uBAAA;EACH,OAAA,EAAS,6BAAA;EACT,KAAA;EACA,WAAA;IAAe,MAAA,IAAU,OAAO;EAAA;AAAA;AAAA,KAG7B,wBAAA;EACH,KAAA;EACA,SAAS;AAAA;AAAA,KAGN,yBAAA;EACH,eAAA,CAAgB,KAAA,EAAO,uBAAA,GAA0B,OAAA,CAAQ,wBAAA;AAAA;AAAA,KAGtD,iCAAA;EACH,OAAA,EAAS,6BAAA;EACT,KAAA;EACA,WAAA;IAAe,MAAA,IAAU,OAAO;EAAA;AAAA;AAAA,KAG7B,kCAAA;EACH,KAAA;EACA,MAAA;EACA,OAAA;EACA,SAAA;AAAA;AAAA,KAGG,mCAAA;EACH,yBAAA,CACE,KAAA,EAAO,iCAAA,GACN,OAAA,CAAQ,kCAAA;AAAA;;;cCpBP,OAAA,GAAW,IAAY;;AD3BsD;;;;;;;;;AAI9D;AAAA;;;;AAKV;AAAA;;cC2EL,eAAA,GACJ,MAAA,EAAQ,sBAAA;;;;;AAIR,oBAAA,IAAwB,IAAA,0BACvB,aAAa;;;;;;AD1FmE;;;;;;;;;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,sBAAoB;EAAA;AAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ import{logWarn as e}from"@usebutr/core";import{buildAccount as t,discoverWalletStandard as n,getFeature as r,pickAccountByAddress as i,pickFirstAddress as a,slugify as o}from"@usebutr/wallet-standard-shared";const s=e=>({getBalance:!1,getTransactionReceipt:!1,requestAccounts:!1,sendTransaction:e.features.signAndExecuteTransaction,signIn:!1,signMessage:e.features.signMessage,signTransaction:e.features.signTransaction,subscribe:e.features.events,switchAccount:!1,switchChain:e.chainCount>1}),c=`sui:`,l=e=>{let t=atob(e),n=new Uint8Array(t.length);for(let e=0;e<t.length;e+=1)n[e]=t.codePointAt(e)??0;return n},u=e=>o(`sui`,e),d=e=>e.chains.find(e=>e===`sui:mainnet`)||(e.chains.find(e=>e.startsWith(c))??null),f=(e,t)=>({id:e,name:t,namespace:`sui`,reference:e.slice(4)}),p=(e,n)=>t(e,n),m=e=>{if(typeof e==`string`||e&&typeof e==`object`&&`toJSON`in e&&typeof e.toJSON==`function`)return e;throw TypeError(`Sui sendTx/signTransaction expects a @mysten/sui Transaction (with toJSON()) or a base64-encoded string`)},h=(t,n)=>{let o=d(t);if(!o)return null;let h=r(t,`standard:connect`);if(!h)return null;let g=r(t,`standard:disconnect`),_=r(t,`standard:events`),v=r(t,`sui:signPersonalMessage`),y=r(t,`sui:signAndExecuteTransaction`),b=r(t,`sui:signTransaction`),x=o,S=()=>f(x,t.name),C=new Set,w=()=>{if(t.accounts.length===0)return;let e=S(),n=t.accounts.map(t=>p(t.address,e)),r=n[0];if(r)for(let e of C)e({account:r,accounts:n,type:`accountChanged`})};return n?.(()=>{for(let e of C)e({type:`disconnected`})}),{capabilities:s({chainCount:t.chains.filter(e=>e.startsWith(c)).length,features:{events:!!_,signAndExecuteTransaction:!!y,signMessage:!!v,signTransaction:!!b}}),chainPlatform:`sui`,async connect(e){await h.connect(e?.silent?{silent:!0}:void 0)},async disconnect(){if(g)try{await g.disconnect()}catch(t){e(`[butr] Sui Wallet Standard disconnect threw:`,t)}},getAccount(){let e=a(t.accounts);return e?Promise.resolve(p(e,S())):Promise.resolve(null)},getAccounts(){let e=S();return Promise.resolve(t.accounts.map(t=>p(t.address,e)))},getBalance(){return Promise.resolve({decimals:9,formatted:`0`,symbol:`SUI`,value:0n})},getSigner(){return Promise.resolve(t)},getTransactionReceipt(){return Promise.resolve({status:`Pending`})},icon:t.icon,id:u(t.name),name:t.name,async requestAccounts(){await h.connect()},async sendTx(e,n){if(!y)throw Error(`Wallet ${t.name} does not advertise sui:signAndExecuteTransaction`);let r=n?i(t.accounts,n.walletAddress):t.accounts[0]??null;if(!r)throw Error(`No connected account`);let a=m(e);return(await y.signAndExecuteTransaction({account:r,chain:x,transaction:a})).digest},sendTxToChain(e,t,n,r){return r?.(),this.sendTx(e,n)},async signMessage(e,n){if(!v)throw Error(`Wallet ${t.name} does not advertise sui:signPersonalMessage`);let r=n?i(t.accounts,n.walletAddress):t.accounts[0]??null;if(!r)throw Error(`No connected account`);let a=await v.signPersonalMessage({account:r,message:e});return{signature:l(a.signature),signedMessage:l(a.bytes)}},...b?{async signTransaction(e,n){let r=n?i(t.accounts,n.walletAddress):t.accounts[0]??null;if(!r)throw Error(`No connected account`);let a=m(e);return l((await b.signTransaction({account:r,chain:x,transaction:a})).bytes)}}:{},subscribe(e){C.add(e);let t=null;if(_){let n=_.on(`change`,t=>{if(t.chains){let e=t.chains.find(e=>e===`sui:mainnet`)??t.chains.find(e=>e.startsWith(c));e&&(x=e)}if(t.accounts){if(t.accounts.length===0){e({type:`disconnected`});return}let n=S(),r=t.accounts.map(e=>p(e.address,n)),i=r[0];if(!i)return;e({account:i,accounts:r,type:`accountChanged`});return}t.chains&&w()});t=()=>n()}return()=>{C.delete(e),t?.()}},switchChain(e){if(e.namespace!==`sui`)throw Error(`Sui adapter received non-Sui chain "${e.id}". Pass a chain with namespace "sui".`);if(!t.chains.includes(e.id))throw Error(`Wallet ${t.name} does not advertise chain "${e.id}". Available: ${t.chains.join(`, `)}`);return x=e.id,w(),Promise.resolve()}}},g=e=>n(e,(e,t)=>h(e,t)),_={devnet:{id:`sui:devnet`,name:`Sui Devnet`,namespace:`sui`,reference:`devnet`},localnet:{id:`sui:localnet`,name:`Sui Localnet`,namespace:`sui`,reference:`localnet`},mainnet:{id:`sui:mainnet`,name:`Sui Mainnet`,namespace:`sui`,reference:`mainnet`},testnet:{id:`sui:testnet`,name:`Sui Testnet`,namespace:`sui`,reference:`testnet`}},v=Object.values(_),y={platform:`sui`,subscribe:g};export{_ as SUI_CHAINS,v as SUI_CHAINS_LIST,h as buildSuiAdapter,g as discoverSuiAdapters,s as resolveSuiCapabilities,u as slugify,y as suiDiscoverer};
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +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 { logWarn } 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\n/** Cross-platform base64 → Uint8Array. Sui's Wallet Standard features\n * return base64 strings (`bytes`, `signature`), while butr's\n * `Wallet.signMessage` contract is Uint8Array. */\nconst base64ToBytes = (b64: string): Uint8Array => {\n const binary = atob(b64);\n const out = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i += 1) {\n out[i] = binary.codePointAt(i) ?? 0;\n }\n return out;\n};\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: 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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"}
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "@usebutr/sui",
3
+ "version": "0.1.0",
4
+ "description": "Wallet Standard adapter for butr (Sui).",
5
+ "license": "MIT",
6
+ "author": "Pedro Filho <pedro@filho.me>",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/pedroapfilho/usebutr.git",
10
+ "directory": "packages/sui"
11
+ },
12
+ "files": [
13
+ "dist"
14
+ ],
15
+ "type": "module",
16
+ "sideEffects": false,
17
+ "types": "dist/index.d.ts",
18
+ "exports": {
19
+ ".": {
20
+ "types": "./dist/index.d.ts",
21
+ "default": "./dist/index.js"
22
+ }
23
+ },
24
+ "publishConfig": {
25
+ "access": "public"
26
+ },
27
+ "dependencies": {
28
+ "@usebutr/core": "0.1.0",
29
+ "@usebutr/wallet-standard-shared": "0.1.0"
30
+ },
31
+ "devDependencies": {
32
+ "@wallet-standard/app": "^1.1.0",
33
+ "typescript": "^6.0.3",
34
+ "vitest": "^4.1.6",
35
+ "@repo/config-vitest": "0.0.0",
36
+ "@repo/typescript-config": "0.0.0"
37
+ },
38
+ "peerDependencies": {
39
+ "@wallet-standard/app": "^1.1.0"
40
+ },
41
+ "peerDependenciesMeta": {
42
+ "@wallet-standard/app": {
43
+ "optional": true
44
+ }
45
+ },
46
+ "scripts": {
47
+ "build": "tsdown",
48
+ "clean": "rm -rf dist",
49
+ "dev": "tsdown --watch",
50
+ "lint": "oxlint src",
51
+ "test": "vitest run --passWithNoTests",
52
+ "test:coverage": "vitest run --coverage --passWithNoTests",
53
+ "typecheck": "tsc -p tsconfig.json --noEmit"
54
+ }
55
+ }