@chipi-stack/starknet-connector 0.3.0 → 0.5.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/README.md CHANGED
@@ -1,35 +1,41 @@
1
1
  # @chipi-stack/starknet-connector
2
2
 
3
- **"Connect with Chipi"**, a [starknet-react](https://starknet-react.com) /
4
- [get-starknet](https://github.com/starknet-io/get-starknet) connector that lets
5
- any Starknet dApp connect to a **Chipi hosted smart-account wallet** (passkey,
6
- gasless via paymaster). No browser extension, no WalletConnect, no whitelisting,
7
- just add the package to your own dApp and it works.
3
+ "Connect with Chipi" for Starknet dApps. A [starknet-react](https://starknet-react.com) /
4
+ [get-starknet](https://github.com/starknet-io/get-starknet) connector that opens the user's
5
+ Chipi wallet (passkey, gasless) in a popup. No browser extension, no WalletConnect, no
6
+ whitelisting. It works next to injected wallets such as Bramble, Ready and Braavos.
8
7
 
9
8
  ```bash
10
9
  npm install @chipi-stack/starknet-connector
11
10
  ```
12
11
 
13
- Ships both ESM and CommonJS. Versions before 0.3.0 were ESM-only and failed
14
- under any CommonJS resolver (plain Node, `tsx`, `ts-node`, Jest) with
15
- `ERR_PACKAGE_PATH_NOT_EXPORTED`. Bundlers were never affected.
12
+ Ships ESM and CommonJS. Versions before 0.3.0 were ESM-only and failed under CommonJS
13
+ resolvers (plain Node, `tsx`, `ts-node`, Jest) with `ERR_PACKAGE_PATH_NOT_EXPORTED`.
16
14
 
17
- ## Use it
15
+ ## Use it with starknet-react
18
16
 
19
- Add one connector to your `StarknetConfig`:
17
+ Offer Chipi next to Bramble, Ready and Braavos:
20
18
 
21
19
  ```tsx
22
- import { StarknetConfig, publicProvider, argent, braavos } from "@starknet-react/core";
20
+ import type { ReactNode } from "react";
21
+ import {
22
+ StarknetConfig,
23
+ InjectedConnector,
24
+ publicProvider,
25
+ ready,
26
+ braavos,
27
+ } from "@starknet-react/core";
23
28
  import { mainnet } from "@starknet-react/chains";
24
29
  import { chipi } from "@chipi-stack/starknet-connector";
25
30
 
26
31
  const connectors = [
27
32
  chipi(), // "Connect with Chipi"
28
- argent(),
33
+ new InjectedConnector({ options: { id: "bramble", name: "Bramble" } }),
34
+ ready(), // Ready (formerly Argent X)
29
35
  braavos(),
30
36
  ];
31
37
 
32
- export function App({ children }) {
38
+ export function Providers({ children }: { children: ReactNode }) {
33
39
  return (
34
40
  <StarknetConfig chains={[mainnet]} provider={publicProvider()} connectors={connectors}>
35
41
  {children}
@@ -38,9 +44,122 @@ export function App({ children }) {
38
44
  }
39
45
  ```
40
46
 
41
- That's it. Your existing `useConnect` / `useAccount` flow now offers "Connect
42
- with Chipi", and `account.signMessage(...)` / `account.execute(...)` route to the
43
- user's Chipi wallet, **gasless**, signed with their passkey.
47
+ Your `useConnect` / `useAccount` flow now offers all four. With Chipi, `account.execute(...)`
48
+ and `account.signMessage(...)` go to the user's Chipi wallet: gasless, signed with their
49
+ passkey.
50
+
51
+ Notes for `@starknet-react/core` 5:
52
+
53
+ - List every wallet yourself. `useInjectedConnectors` only picks up objects with the legacy keys
54
+ (`provider`, `isConnected`, `enable`, `isPreauthorized`). Neither `window.starknet_chipi` nor
55
+ `window.starknet_bramble` has all of them, so it finds neither.
56
+ - `injected({ id: "bramble" })` also works, but in 5.0.3 `injected()` takes only `id`. The label
57
+ is then the wallet's own name once Bramble has injected, and `"bramble"` before that.
58
+ - `argent()` is an alias of `ready()`. Use `ready()`.
59
+ - `InjectedConnector` looks up `window.starknet_bramble` each time it is used, so Bramble's late
60
+ injection is fine. `autoConnect` runs once on mount, though: if Bramble has not injected by
61
+ then, the last Bramble session is not restored.
62
+
63
+ ## Use it with get-starknet v4
64
+
65
+ get-starknet v4 (`@starknet-io/get-starknet-core`) lists every `window.starknet*` object that
66
+ has `id`, `name`, `version`, `icon`, `request`, `on` and `off`. Creating a Chipi connector puts
67
+ `window.starknet_chipi` on the page, so both wallets show up:
68
+
69
+ ```ts
70
+ import { getStarknet } from "@starknet-io/get-starknet-core";
71
+ import { chipi } from "@chipi-stack/starknet-connector";
72
+
73
+ declare const connectButton: HTMLButtonElement;
74
+ declare function pickedWalletId(): "chipi" | "bramble"; // from your wallet picker
75
+
76
+ // Puts window.starknet_chipi on the page.
77
+ chipi();
78
+
79
+ // Bramble injects window.starknet_bramble after page load, then fires
80
+ // "starknet#initialized". Scan after that, or after a short timeout.
81
+ function waitForInjectedWallets(timeoutMs = 3000): Promise<void> {
82
+ return new Promise((resolve) => {
83
+ if ("starknet_bramble" in window) return resolve();
84
+ const done = () => {
85
+ clearTimeout(timer);
86
+ window.removeEventListener("starknet#initialized", done);
87
+ resolve();
88
+ };
89
+ const timer = setTimeout(done, timeoutMs);
90
+ window.addEventListener("starknet#initialized", done);
91
+ });
92
+ }
93
+
94
+ await waitForInjectedWallets();
95
+ const gsc = getStarknet();
96
+ const wallets = await gsc.getAvailableWallets();
97
+ // Both are listed: wallets.map((w) => w.id) includes "chipi" and "bramble".
98
+
99
+ // Prompts only from a click: Chipi opens its popup, Bramble its side panel.
100
+ connectButton.onclick = async () => {
101
+ const wallet = wallets.find((w) => w.id === pickedWalletId());
102
+ if (wallet) await gsc.enable(wallet);
103
+ };
104
+ ```
105
+
106
+ `getAuthorizedWallets()` and `getLastConnectedWallet()` send `wallet_getPermissions` to every
107
+ wallet at page load. Chipi answers that locally with `[]` and does not open its popup (see
108
+ [Passive calls](#passive-calls)). There is no silent reconnect: the user clicks connect again
109
+ on each page load.
110
+
111
+ Bramble's guide notes that get-starknet v3 also requires a `provider` key, which Bramble lacks.
112
+ Chipi's object lacks it too.
113
+
114
+ ## Wallet Standard (get-starknet v5 and v6)
115
+
116
+ `@starknet-io/get-starknet-discovery` (5.0.0 and 6.0.6 ship the same code) builds its list in
117
+ `createStore()`:
118
+
119
+ - Wallet Standard wallets register through `wallet-standard:register-wallet`, at any time.
120
+ - `window.starknet*` objects are scanned once, when `createStore()` runs, and wrapped as
121
+ injected wallets. Entries are de-duplicated by name, and a Wallet Standard entry wins.
122
+
123
+ Bramble has no Wallet Standard registration, so get-starknet v5/v6 lists it only if
124
+ `window.starknet_bramble` exists when `createStore()` runs. Register Chipi as a Wallet Standard
125
+ wallet so the order does not matter:
126
+
127
+ ```ts
128
+ import { registerChipiStandardWallet } from "@chipi-stack/starknet-connector";
129
+
130
+ const unregister = registerChipiStandardWallet({
131
+ walletUrl: "https://wallet.chipipay.com", // default
132
+ });
133
+ // later: unregister();
134
+ ```
135
+
136
+ It dispatches `wallet-standard:register-wallet` and answers `wallet-standard:app-ready`, with no
137
+ extra dependency. It exposes `starknet:walletApi`, `standard:connect`, `standard:disconnect` and
138
+ `standard:events` on `starknet:0x534e5f4d41494e` (mainnet), backed by the same popup.
139
+ `connect({ silent: true })` never opens the popup. Its default name is `"Chipi"`; the
140
+ connector's is `"Connect with Chipi"`. If `window.starknet_chipi` also exists when the store is
141
+ created, the store lists two Chipi entries: pass `register: false` to the connector, or give both
142
+ the same `name`. `createChipiStandardWallet(options)` builds the wallet
143
+ without registering it.
144
+
145
+ ## Works next to Bramble
146
+
147
+ - **No id collision.** Chipi is `window.starknet_chipi` with id `"chipi"`. Bramble is
148
+ `window.starknet_bramble` with id `"bramble"`. get-starknet v4 and starknet-react key wallets
149
+ by id, so both are listed.
150
+ - **Neither writes `window.starknet`.**
151
+ - **When each object exists.** `window.starknet_chipi` exists only after a `ChipiConnector` is
152
+ created (unless `register: false`). `disconnect()` removes it until the next `connect()`.
153
+ `window.starknet_bramble` appears after page load, followed by a `starknet#initialized` event.
154
+ - **A wallet scan never opens Chipi.** Since 0.5.0, the calls scanners send at page load are
155
+ answered without opening the popup (see [Passive calls](#passive-calls)).
156
+ - **Bramble's own rules apply to Bramble.** Prompts only from a click, at most 5 pending prompts
157
+ per site, strict params (an extra key fails with `114`), a typed-data `chainId` equal to its
158
+ active network, no `wallet_disconnect` (`114`), one account per site. Signatures are `[r, s]`
159
+ for OpenZeppelin and Braavos accounts and `["1", "0", pub, r, s]` for Argent accounts: verify
160
+ them with the account's `is_valid_signature`, not by length. starknet-react's
161
+ `InjectedConnector.disconnect()` does not send `wallet_disconnect`. See
162
+ [Bramble rules to follow](https://docs.chipipay.com/sdk/guides/trusted-wallets#bramble-rules-to-follow).
44
163
 
45
164
  ## How it works
46
165
 
@@ -54,55 +173,88 @@ account.execute(calls)
54
173
  └─ wallet_addInvokeTransaction ─► user approves ─► sponsored tx ─► tx hash
55
174
  ```
56
175
 
57
- - The connector is **transport only**: it opens the hosted wallet in a popup and
58
- forwards get-starknet `wallet_*` RPC calls over `postMessage`. It holds no
59
- keys and signs nothing itself.
60
- - The hosted wallet renders a **decoded approval** for every sign/execute (it
61
- shows the actual intent, token, amount, spender, and hard-blocks typed data
62
- it can't read), then runs the real passkey + paymaster path.
63
- - `account()` wraps the channel in a `starknet.js` `WalletAccount`, so the dApp
64
- uses a standard `AccountInterface`.
176
+ - The connector only carries messages. It opens the hosted wallet in a popup and forwards
177
+ get-starknet `wallet_*` calls over `postMessage`. It holds no keys and signs nothing.
178
+ - Each request is posted right away and matched to its answer by id. Several can be in flight at
179
+ once; the connector neither queues nor rejects them.
180
+ - `account()` wraps the channel in a starknet.js `WalletAccount`, so the dApp gets a standard
181
+ `AccountInterface`.
182
+ - There is no silent reconnect. `ready()` is `true` only after `connect()` succeeded on this
183
+ page. Call `connect()` from a click: it opens a popup.
184
+ - Chipi is mainnet-only. `chainId()` always returns `SN_MAIN`.
185
+
186
+ ## Passive calls
187
+
188
+ While no wallet window is open (before `connect()`, or after the user closed it), the connector
189
+ answers these calls itself and does not open the popup. Once the window is open, they go to the
190
+ wallet.
191
+
192
+ | Call | Answer with no wallet window open |
193
+ | ------------------------------------------------- | --------------------------------- |
194
+ | `wallet_getPermissions` | `[]` |
195
+ | `wallet_requestAccounts` with `silent_mode: true` | `[]` |
196
+ | `wallet_requestChainId` | `"0x534e5f4d41494e"` (`SN_MAIN`) |
197
+
198
+ `wallet_requestAccounts` without `silent_mode: true` opens the popup, so send it from a click.
65
199
 
66
200
  ## Options
67
201
 
68
202
  ```ts
203
+ import { chipi } from "@chipi-stack/starknet-connector";
204
+
69
205
  chipi({
70
206
  walletUrl: "https://wallet.chipipay.com", // hosted wallet base URL (default)
71
- id: "chipi", // connector id
72
- name: "Connect with Chipi", // display name
73
- icon: "data:image/svg+xml;base64,...", // display icon
74
- register: true, // self-register on window (default)
207
+ id: "chipi", // connector id (default); the window key stays starknet_chipi
208
+ name: "Connect with Chipi", // display name (default)
209
+ icon: "data:image/png;base64,...", // default: a 48x48 PNG data URI
210
+ register: true, // put window.starknet_chipi on the page (default; no-op in SSR)
211
+ transport: {
212
+ windowName: "chipi-wallet", // default: a new random name per open
213
+ windowFeatures: "popup=yes,width=420,height=680", // default
214
+ openTimeoutMs: 180_000, // wait for the wallet page to load (default)
215
+ requestTimeoutMs: 120_000, // per request; 0 disables (default)
216
+ },
75
217
  });
76
218
  ```
77
219
 
78
- `chipi(options)` is a thin wrapper: `new ChipiConnector(options)` still works
79
- identically if you prefer instantiating the class directly.
220
+ `chipi(options)` is the same as `new ChipiConnector(options)`.
80
221
 
81
- ## Compatibility
222
+ ## Errors
223
+
224
+ The connector rejects with an `Error`. Check `err.code` first, then `err.message`.
82
225
 
83
- - `@starknet-react/core` **>= 3** (peer) · tested against `5.x`
84
- - `starknet` **>= 8.1.2** (peer); `WalletAccount.connect` is identical across 8/9
85
- - **Starknet mainnet** (v1). The wallet is mainnet-only.
226
+ | `code` or message | When |
227
+ | ----------------------------------------------- | ---------------------------------------------------------------------------------------- |
228
+ | code `"UNSUPPORTED_CHAIN"` | `connect({ chainIdHint })` with a chain other than `SN_MAIN`. No popup opens. |
229
+ | code `"popup_blocked"` | The browser blocked the popup: `connect()` did not run from a click. |
230
+ | message `"wallet_open_timeout"` | The wallet page did not load within `transport.openTimeoutMs` (180 s). The popup closes. |
231
+ | code `"REQUEST_TIMEOUT"` | No answer within `transport.requestTimeoutMs` (120 s). |
232
+ | code `"POPUP_CLOSED"` | A request after the user closed the wallet window. Call `connect()` again from a click. |
233
+ | code `4001`, message `"User closed the wallet"` | The user closed the wallet window while a request was waiting. |
234
+ | code `4001`, message `"disconnected"` | `disconnect()` ran while a request, or the wallet page load, was waiting. |
235
+ | message `"chipi_no_account"` | The wallet returned no account on connect. |
236
+ | message `"chipi_not_connected"` | `account()` before `connect()`. |
237
+ | message `"chipi_connector_requires_browser"` | The popup was requested without `window` (SSR). |
238
+ | numeric code from the wallet (`113` … `163`) | The wallet's own error, passed through with its message (see below). |
86
239
 
87
- ## Discovery
240
+ Wallets built on `createChipiWindowObject` from `@chipi-stack/chipi-react` answer `113` (the
241
+ user refused), `114` (no calls, or typed data with no `chainId` or one for another chain), `115` (account
242
+ already deployed), `116` (deployment data not available) and `163` (unsupported method,
243
+ including `wallet_disconnect` and `wallet_switchStarknetChain`, or another failure).
88
244
 
89
- Instantiating the connector also **self-registers** the wallet on `window`
90
- (`window.starknet_chipi` + a `starknet:announceWallet` event, the get-starknet /
91
- Starknet-Wallet-Standard discovery path), so dApps that _scan_ for wallets find
92
- "Connect with Chipi" even if they don't import this package, no whitelisting,
93
- no coordination with us required. Pass `register: false` to opt out (e.g. to
94
- avoid a duplicate entry in a picker that already lists the connector
95
- explicitly). It's a no-op during SSR. You can also call
96
- `registerChipiWallet(swo)` directly.
245
+ ## Events
97
246
 
98
- ## Notes / limits (v1)
247
+ - The wallet's `accountsChanged` / `networkChanged` become starknet-react `change` events
248
+ (`{ account, chainId }`). An empty account list emits `disconnect`.
249
+ - `disconnect()` closes the popup and removes `window.starknet_chipi`. The next `connect()` puts
250
+ it back.
251
+
252
+ ## Compatibility
99
253
 
100
- - **Popup-based** (external dApps). The in-app-browser ("parent") mode is in the
101
- transport but not exposed yet; the hosted wallet side handles it in a later
102
- release.
103
- - Mainnet only, pass a mainnet `provider`.
104
- - One approval at a time; a dApp that fires two prompts truly simultaneously has
105
- its second request rejected (most dApps serialize).
254
+ - `@starknet-react/core` >= 3 (peer), tested against 5.0.3.
255
+ - `starknet` >= 8.1.2 (peer). `WalletAccount.connect` is the same in 8 and 9.
256
+ - Starknet mainnet only.
257
+ - Popup only. The in-app-browser ("parent") mode is in the transport but not exposed.
106
258
 
107
- MIT · [docs.chipipay.com](https://docs.chipipay.com) ·
259
+ MIT · [docs.chipipay.com](https://docs.chipipay.com/sdk/connector/overview) ·
108
260
  [example](https://github.com/chipi-pay/build-with-chipi/tree/main/tutorials/03-connect-with-chipi)