@effectstream/wallets 0.100.17 → 0.100.19

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.
@@ -132,11 +132,15 @@ export class MidnightProvider implements IProvider<MidnightApi> {
132
132
  static init = async (
133
133
  conn: ActiveConnection<MidnightApi>
134
134
  ): Promise<MidnightProvider> => {
135
- const hexAddress = await MidnightProvider.fetchAddress(conn);
136
- // const prefix = pickBech32Prefix(hexAddress);
137
- // const words = bech32.toWords(hexStringToUint8Array(hexAddress));
138
- // const userAddress = bech32.encode(prefix, words, 200);
139
-
135
+ // The IProvider's primary address must be the UNSHIELDED one — that's
136
+ // the address `addressFromKey(verifyingKey)` resolves to for the
137
+ // `signData({keyType: "unshielded"})` signatures we produce in
138
+ // `signMessage`, which is what `CryptoManager.Midnight().verifySignature`
139
+ // (and therefore the batcher) cross-checks. The shielded and dust
140
+ // addresses remain reachable via `fetchAddress(conn, { type })` or
141
+ // directly through `conn.api.getShieldedAddresses()` /
142
+ // `getDustAddress()`.
143
+ const hexAddress = await MidnightProvider.fetchAddress(conn, { type: "unshielded" });
140
144
  return new MidnightProvider(conn, hexAddress as MidnightAddress);
141
145
  };
142
146
 
@@ -152,7 +156,12 @@ export class MidnightProvider implements IProvider<MidnightApi> {
152
156
  encoding: "text",
153
157
  keyType,
154
158
  })) as Signature;
155
- return signature.signature;
159
+ // Format: "signature|verifyingKey". Midnight unshielded signatures are
160
+ // not self-recovering, so `@effectstream/crypto`'s verifier needs the
161
+ // public key alongside the signature (matches the local-seed wallet
162
+ // and mirrors Cardano's "sig+key" convention with a different
163
+ // separator).
164
+ return `${signature.signature}|${signature.verifyingKey}`;
156
165
  };
157
166
 
158
167
  getAddress(): AddressAndType {
@@ -162,12 +171,49 @@ export class MidnightProvider implements IProvider<MidnightApi> {
162
171
  }
163
172
  }
164
173
 
165
- static fetchAddress = async (conn: ActiveConnection<MidnightApi>): Promise<string> => {
166
- const addresses = await conn.api.getShieldedAddresses();
167
- if (!addresses?.shieldedAddress) {
168
- throw new Error("Midnight wallet did not return a shielded address");
174
+ /**
175
+ * Fetch a specific address type from a connected Midnight wallet. Defaults
176
+ * to the **shielded** address to keep existing callers backwards-compatible
177
+ * with the pre-multi-address behaviour. The IProvider primary address
178
+ * (`getAddress()`) is the **unshielded** one — that's the address
179
+ * `addressFromKey(verifyingKey)` resolves to and what
180
+ * `CryptoManager.Midnight().verifySignature` needs to cross-check.
181
+ *
182
+ * @example
183
+ * ```ts
184
+ * const shielded = await MidnightProvider.fetchAddress(conn);
185
+ * const unshielded = await MidnightProvider.fetchAddress(conn, { type: "unshielded" });
186
+ * const dust = await MidnightProvider.fetchAddress(conn, { type: "dust" });
187
+ * ```
188
+ */
189
+ static fetchAddress = async (
190
+ conn: ActiveConnection<MidnightApi>,
191
+ options: { type?: "shielded" | "unshielded" | "dust" } = {},
192
+ ): Promise<string> => {
193
+ const type = options.type ?? "shielded";
194
+ switch (type) {
195
+ case "shielded": {
196
+ const res = await conn.api.getShieldedAddresses();
197
+ if (!res?.shieldedAddress) {
198
+ throw new Error("Midnight wallet did not return a shielded address");
199
+ }
200
+ return res.shieldedAddress;
201
+ }
202
+ case "unshielded": {
203
+ const res = await conn.api.getUnshieldedAddress();
204
+ if (!res?.unshieldedAddress) {
205
+ throw new Error("Midnight wallet did not return an unshielded address");
206
+ }
207
+ return res.unshieldedAddress;
208
+ }
209
+ case "dust": {
210
+ const res = await conn.api.getDustAddress();
211
+ if (!res?.dustAddress) {
212
+ throw new Error("Midnight wallet did not return a dust address");
213
+ }
214
+ return res.dustAddress;
215
+ }
169
216
  }
170
- return addresses.shieldedAddress;
171
217
  };
172
218
  }
173
219
 
@@ -0,0 +1,30 @@
1
+ import type { Result } from "@effectstream/utils";
2
+ import type { IProvider } from "../IProvider.ts";
3
+ import type { WalletMode, ApiForMode } from "../utils.ts";
4
+ import type { LoginInfoMap } from "../wallet-modes.ts";
5
+ import { MidnightLocalConnector } from "./local.ts";
6
+ import { formatError } from "../helpers/format-error.ts";
7
+
8
+ export async function midnightLocalLoginWrapper(
9
+ loginInfo: LoginInfoMap[WalletMode.MidnightLocal],
10
+ ): Promise<Result<IProvider<ApiForMode<WalletMode.MidnightLocal>>>> {
11
+ try {
12
+ console.log("midnightLocalLoginWrapper: deriving unshielded keystore from seed");
13
+ const provider = await MidnightLocalConnector.instance().connectFromSeed({
14
+ seed: loginInfo.seed,
15
+ networkId: loginInfo.networkId,
16
+ });
17
+ return {
18
+ success: true,
19
+ result: provider as unknown as IProvider<
20
+ ApiForMode<WalletMode.MidnightLocal>
21
+ >,
22
+ };
23
+ } catch (err) {
24
+ console.log("midnightLocalLoginWrapper: error building local Midnight wallet");
25
+ return {
26
+ success: false,
27
+ errorMessage: formatError(err),
28
+ };
29
+ }
30
+ }
package/src/mod.ts CHANGED
@@ -7,4 +7,23 @@ export { WalletMode, WalletNameMap } from "./utils.ts";
7
7
  export { walletLogin } from "./wallets.ts";
8
8
  export { allInjectedWallets } from "./utils.ts";
9
9
  export { getAddressType } from "./wallet-modes.ts";
10
+ export type { LoginInfo, LoginInfoMap } from "./wallet-modes.ts";
11
+ // Local-wallet mode helper (env-driven injected↔local swap).
12
+ export { pickWalletMode } from "./helpers/pick-mode.ts";
13
+ export type {
14
+ ChainKey,
15
+ PickWalletModeArgs,
16
+ LocalOptionsForChain,
17
+ InjectedOptionsForChain,
18
+ } from "./helpers/pick-mode.ts";
19
+ // Inline data-URI icons for the three local-JS wallet modes (orange/blue/black
20
+ // JS circles), suitable for use as `WalletOption.icon`.
21
+ export {
22
+ evmViemIcon,
23
+ cardanoLocalIcon,
24
+ midnightLocalIcon,
25
+ } from "./helpers/local-icons.ts";
26
+ // Coerces wallet/SDK rejects (including CIP-30-style `{code, info}` objects)
27
+ // to a readable string.
28
+ export { formatError } from "./helpers/format-error.ts";
10
29
  export * from "./effectstream.ts";
package/src/utils.ts CHANGED
@@ -7,18 +7,24 @@ import type {
7
7
 
8
8
  import { AlgorandConnector } from "./algorand/algorand.ts";
9
9
  import { EthersConnector } from "./evm/ethers.ts";
10
+ import { ViemConnector } from "./evm/viem.ts";
10
11
  import { EvmInjectedConnector } from "./evm/injected.ts";
11
12
  import { CardanoConnector } from "./cardano/cardano.ts";
13
+ import { CardanoLocalConnector } from "./cardano/local.ts";
12
14
  import { PolkadotConnector } from "./polkadot/polkadot.ts";
13
15
  import { MinaConnector } from "./mina/mina.ts";
14
16
  import { AvailConnector } from "./avail/avail.ts";
15
17
  import { MidnightConnector } from "./midnight/midnight.ts";
18
+ import { MidnightLocalConnector } from "./midnight/local.ts";
16
19
 
17
20
  export const enum WalletMode {
18
21
  EvmInjected,
19
22
  EvmEthers,
23
+ EvmViem,
20
24
  Midnight,
25
+ MidnightLocal,
21
26
  Cardano,
27
+ CardanoLocal,
22
28
  Polkadot,
23
29
  Algorand,
24
30
  Mina,
@@ -28,8 +34,11 @@ export const enum WalletMode {
28
34
  export const WalletNameMap: Record<WalletMode, string> = {
29
35
  [WalletMode.EvmInjected]: "EVM",
30
36
  [WalletMode.EvmEthers]: "EVM",
37
+ [WalletMode.EvmViem]: "EVM",
31
38
  [WalletMode.Midnight]: "Midnight",
39
+ [WalletMode.MidnightLocal]: "Midnight",
32
40
  [WalletMode.Cardano]: "Cardano",
41
+ [WalletMode.CardanoLocal]: "Cardano",
33
42
  [WalletMode.Polkadot]: 'Polkadot',
34
43
  [WalletMode.Algorand]: 'Algorand',
35
44
  [WalletMode.Mina]: "Mina",
@@ -39,8 +48,11 @@ export const WalletNameMap: Record<WalletMode, string> = {
39
48
  export const WalletModeMap = {
40
49
  [WalletMode.EvmInjected]: EvmInjectedConnector.instance(),
41
50
  [WalletMode.EvmEthers]: EthersConnector.instance(),
51
+ [WalletMode.EvmViem]: ViemConnector.instance(),
42
52
  [WalletMode.Midnight]: MidnightConnector.instance(),
53
+ [WalletMode.MidnightLocal]: MidnightLocalConnector.instance(),
43
54
  [WalletMode.Cardano]: CardanoConnector.instance(),
55
+ [WalletMode.CardanoLocal]: CardanoLocalConnector.instance(),
44
56
  [WalletMode.Polkadot]: PolkadotConnector.instance(),
45
57
  [WalletMode.Algorand]: AlgorandConnector.instance(),
46
58
  [WalletMode.Mina]: MinaConnector.instance(),
@@ -87,9 +99,12 @@ export async function allInjectedWallets(config: {
87
99
  [WalletMode.Polkadot]: await PolkadotConnector.getWalletOptions(),
88
100
  [WalletMode.Mina]: MinaConnector.getWalletOptions(),
89
101
  // TODO: Algorand signature support is not implemented yet.
90
- // NOTE: Midnight Do not support signature yet.
91
102
  [WalletMode.Algorand]: signatureSupport? [] : AlgorandConnector.getWalletOptions(),
92
- [WalletMode.Midnight]: signatureSupport? [] : MidnightConnector.getWalletOptions(),
103
+ // Midnight signing via ConnectedAPI.signData(message, {keyType: "unshielded"})
104
+ // works as of @effectstream/crypto's MidnightCrypto.verifySignature
105
+ // implementation, so it now surfaces in the injected-wallet list even when
106
+ // signature support is requested.
107
+ [WalletMode.Midnight]: MidnightConnector.getWalletOptions(),
93
108
  };
94
109
 
95
110
  return wallets;
@@ -2,8 +2,11 @@ import type { ApiPromise } from "avail-js-sdk";
2
2
  import type { Result } from "@effectstream/utils";
3
3
  import type { AlgorandApi } from "./algorand/algorand.ts";
4
4
  import type { CardanoApi } from "./cardano/cardano.ts";
5
+ import type { CardanoLocalNetwork } from "./cardano/local.ts";
5
6
  import type { EthersApi } from "./evm/ethers.ts";
7
+ import type { ViemApi } from "./evm/viem.ts";
6
8
  import type { EvmApi } from "./evm/injected.ts";
9
+ import type { Hex } from "viem";
7
10
  import type {
8
11
  ActiveConnection,
9
12
  IInjectedConnector,
@@ -17,7 +20,9 @@ import {
17
20
  WalletMode,
18
21
  } from "./utils.ts";
19
22
  import { AddressType } from "@effectstream/utils";
23
+ import { formatError } from "./helpers/format-error.ts";
20
24
  import type { MidnightApi } from "./midnight/midnight.ts";
25
+ import type {} from "./midnight/local.ts";
21
26
  import type { Chain } from "viem/chains";
22
27
  import { Wallet } from "ethers";
23
28
 
@@ -42,7 +47,25 @@ export type LoginInfoMap = {
42
47
  connection: ActiveConnection<EthersApi>;
43
48
  preferBatchedMode: boolean;
44
49
  };
50
+ [WalletMode.EvmViem]: {
51
+ /** 0x-prefixed 32-byte hex private key. */
52
+ privateKey: Hex;
53
+ /** RPC URL the viem WalletClient should target. */
54
+ rpcUrl: string;
55
+ /** Optional viem Chain — pass to enable chain-aware tx fields (chainId etc). */
56
+ chain?: Chain;
57
+ /** If true, even EVM inputs are batched (sign + send to batcher). */
58
+ preferBatchedMode?: boolean;
59
+ };
45
60
  [WalletMode.Cardano]: BaseLoginInfo<CardanoApi>;
61
+ [WalletMode.CardanoLocal]: {
62
+ /** BIP-39 mnemonic. Generated when omitted. */
63
+ seedPhrase?: string;
64
+ /** Cardano network — sets the bech32 prefix. */
65
+ network: CardanoLocalNetwork;
66
+ /** Optional Lucid Provider; only needed for on-chain ops, not pure signing. */
67
+ provider?: unknown;
68
+ };
46
69
  [WalletMode.Polkadot]: BaseLoginInfo<PolkadotApi>;
47
70
  [WalletMode.Algorand]: BaseLoginInfo<AlgorandApi>;
48
71
  [WalletMode.Mina]: BaseLoginInfo<MinaApi>;
@@ -54,6 +77,12 @@ export type LoginInfoMap = {
54
77
  [WalletMode.Midnight]: BaseLoginInfo<MidnightApi> & {
55
78
  networkId?: string;
56
79
  };
80
+ [WalletMode.MidnightLocal]: {
81
+ /** 64-char hex seed. Generated when omitted. */
82
+ seed?: string;
83
+ /** Midnight network id ("undeployed" | "testnet" | …) — sets the bech32 prefix. */
84
+ networkId: string;
85
+ };
57
86
  };
58
87
 
59
88
  type ToUnion<T> = {
@@ -93,7 +122,7 @@ export async function connectInjected<Api>(
93
122
 
94
123
  return {
95
124
  success: false,
96
- errorMessage: err instanceof Error ? err.message : String(err),
125
+ errorMessage: formatError(err),
97
126
  };
98
127
  }
99
128
  }
@@ -103,8 +132,10 @@ export function getAddressType(mode: WalletMode): AddressType {
103
132
 
104
133
  case WalletMode.EvmInjected:
105
134
  case WalletMode.EvmEthers:
135
+ case WalletMode.EvmViem:
106
136
  return AddressType.EVM;
107
137
  case WalletMode.Cardano:
138
+ case WalletMode.CardanoLocal:
108
139
  return AddressType.CARDANO;
109
140
  case WalletMode.Polkadot:
110
141
  return AddressType.POLKADOT;
@@ -113,6 +144,7 @@ export function getAddressType(mode: WalletMode): AddressType {
113
144
  case WalletMode.Mina:
114
145
  return AddressType.MINA;
115
146
  case WalletMode.Midnight:
147
+ case WalletMode.MidnightLocal:
116
148
  return AddressType.MIDNIGHT;
117
149
  case WalletMode.AvailJs:
118
150
  return AddressType.AVAIL;
package/src/wallets.ts CHANGED
@@ -1,9 +1,11 @@
1
1
  import type { IProvider } from "./IProvider.ts";
2
2
  import { algorandLoginWrapper } from "./algorand/wrapper.ts";
3
3
  import { cardanoLoginWrapper } from "./cardano/wrapper.ts";
4
+ import { cardanoLocalLoginWrapper } from "./cardano/wrapper-local.ts";
4
5
  import { evmLoginWrapper } from "./evm/wrapper-injected.ts";
5
6
  import { polkadotLoginWrapper } from "./polkadot/wrapper.ts";
6
7
  import { ethersLoginWrapper } from "./evm/wrapper-ethers.ts";
8
+ import { viemLoginWrapper } from "./evm/wrapper-viem.ts";
7
9
  import { minaLoginWrapper } from "./mina/wrapper.ts";
8
10
  import { availJsLoginWrapper } from "./avail/wrapper.ts";
9
11
  import { WalletMode } from "./utils.ts";
@@ -12,6 +14,7 @@ import type { Wallet } from "./types.ts";
12
14
  import type { LoginInfo } from "./wallet-modes.ts";
13
15
  // import { assertNever } from "assert-never";
14
16
  import { midnightLoginWrapper } from "./midnight/wrapper.ts";
17
+ import { midnightLocalLoginWrapper } from "./midnight/wrapper-local.ts";
15
18
 
16
19
  export async function walletLogin(
17
20
  loginInfo: LoginInfo
@@ -38,9 +41,15 @@ async function login(loginInfo: LoginInfo): Promise<Result<IProvider<unknown>>>
38
41
  case WalletMode.EvmEthers: {
39
42
  return await ethersLoginWrapper(loginInfo);
40
43
  }
44
+ case WalletMode.EvmViem: {
45
+ return await viemLoginWrapper(loginInfo);
46
+ }
41
47
  case WalletMode.Cardano: {
42
48
  return await cardanoLoginWrapper(loginInfo);
43
49
  }
50
+ case WalletMode.CardanoLocal: {
51
+ return await cardanoLocalLoginWrapper(loginInfo);
52
+ }
44
53
  case WalletMode.Polkadot: {
45
54
  return await polkadotLoginWrapper(loginInfo);
46
55
  }
@@ -56,6 +65,9 @@ async function login(loginInfo: LoginInfo): Promise<Result<IProvider<unknown>>>
56
65
  case WalletMode.Midnight: {
57
66
  return await midnightLoginWrapper(loginInfo);
58
67
  }
68
+ case WalletMode.MidnightLocal: {
69
+ return await midnightLocalLoginWrapper(loginInfo);
70
+ }
59
71
  default:
60
72
  throw new Error(`Unsupported wallet mode: ${(loginInfo as any).mode}`);
61
73
  }