@valve-tech/wallet-adapter 0.11.2 → 0.13.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/CHANGELOG.md +30 -0
- package/README.md +3 -0
- package/dist/anvil-accounts.d.ts +25 -0
- package/dist/anvil-accounts.d.ts.map +1 -0
- package/dist/anvil-accounts.js +25 -0
- package/dist/anvil-accounts.js.map +1 -0
- package/dist/anvil-fixture.d.ts +16 -0
- package/dist/anvil-fixture.d.ts.map +1 -0
- package/dist/anvil-fixture.js +92 -0
- package/dist/anvil-fixture.js.map +1 -0
- package/package.json +4 -2
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,36 @@ this file.
|
|
|
6
6
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
7
7
|
and this project adheres to [Semantic Versioning](https://semver.org/).
|
|
8
8
|
|
|
9
|
+
## [0.13.0] — 2026-05-12
|
|
10
|
+
|
|
11
|
+
### Notes
|
|
12
|
+
|
|
13
|
+
- Synchronized release — no changes to this package. Republished at
|
|
14
|
+
0.13.0 alongside the rest of the toolkit; the substantive change
|
|
15
|
+
is in `@valve-tech/tx-tracker` (new `TrackOptions.probeMined`
|
|
16
|
+
consumer-supplied mined-detection probe). See
|
|
17
|
+
`@valve-tech/tx-tracker`'s CHANGELOG for details.
|
|
18
|
+
|
|
19
|
+
## [0.12.0] — 2026-05-11
|
|
20
|
+
|
|
21
|
+
### Added
|
|
22
|
+
|
|
23
|
+
- Three more `examples/` covering wallet-plumbing classes the
|
|
24
|
+
original five didn't reach (now 8 total):
|
|
25
|
+
|
|
26
|
+
| Example | Covers | Helper |
|
|
27
|
+
|---|---|---|
|
|
28
|
+
| `06-ethers-adapter.ts` | ethers v6 `Signer` (`BrowserProvider.getSigner()` or `Wallet`). For dapps still on ethers or mid-migration to viem. | `walletAdapterFromEthersSigner(...)` |
|
|
29
|
+
| `07-privy-embedded.ts` | Privy embedded wallets via `@privy-io/react-auth`'s `useWallets()`. Handles CAIP-2 chain encoding (`eip155:<id>`) and lazy provider fetching across wallet swaps. | `walletAdapterFromPrivyWallet(...)` |
|
|
30
|
+
| `08-safe-multisig.ts` | Safe (Gnosis Safe) multisig via `@safe-global/protocol-kit` + `@safe-global/api-kit`. **Returns a safeTxHash, not an on-chain tx hash** — UIs have to fork to await the executed hash separately. File header documents the consumer-visible implications. | `walletAdapterFromSafe(...)` |
|
|
31
|
+
|
|
32
|
+
Each follows the existing example shape (inline SDK type-stubs +
|
|
33
|
+
no-network sanity check) so the files typecheck and run without
|
|
34
|
+
any wallet libraries installed.
|
|
35
|
+
|
|
36
|
+
- README "Bridging a real wallet to `WalletAdapter`" table extended
|
|
37
|
+
from 5 to 8 rows.
|
|
38
|
+
|
|
9
39
|
## [0.11.2] — 2026-05-11
|
|
10
40
|
|
|
11
41
|
### Notes
|
package/README.md
CHANGED
|
@@ -218,6 +218,9 @@ thing:
|
|
|
218
218
|
| [`03-server-relayer.ts`](./examples/03-server-relayer.ts) | Backend code: relayer signing from a private key (env var / KMS). No provider, no chain-switching, hard-fail on cross-chain. Right for sponsored-tx services, indexer write paths, integration tests. | `walletAdapterFromRelayer(...)` |
|
|
219
219
|
| [`04-erc4337-smart-account.ts`](./examples/04-erc4337-smart-account.ts) | ERC-4337 smart accounts via permissionless.js or similar. `adapter.address` is the smart-account address (not the EOA signer). | `walletAdapterFromSmartAccount(...)` |
|
|
220
220
|
| [`05-hardware-wallet-direct.ts`](./examples/05-hardware-wallet-direct.ts) | Hardware wallets attached **directly** via USB/HID (no wallet app in between) — `@ledgerhq/hw-app-eth` for Ledger; the same shape works for Trezor via `@trezor/connect`. For backend code, kiosk apps, dev tooling. | `walletAdapterFromLedger(...)` |
|
|
221
|
+
| [`06-ethers-adapter.ts`](./examples/06-ethers-adapter.ts) | Dapps still on ethers v6 (or in mid-migration), bridging an `ethers.Signer` (`BrowserProvider.getSigner()` or `Wallet`) without re-wiring to viem. | `walletAdapterFromEthersSigner(...)` |
|
|
222
|
+
| [`07-privy-embedded.ts`](./examples/07-privy-embedded.ts) | Privy embedded wallets via `@privy-io/react-auth`'s `useWallets()`. Handles CAIP-2 chain encoding and lazy provider fetching across wallet swaps. | `walletAdapterFromPrivyWallet(...)` |
|
|
223
|
+
| [`08-safe-multisig.ts`](./examples/08-safe-multisig.ts) | Safe (Gnosis Safe) multisig via `@safe-global/protocol-kit` + `@safe-global/api-kit`. **Returns a safeTxHash, not an on-chain tx hash** — UIs need to fork to await the executed hash separately. | `walletAdapterFromSafe(...)` |
|
|
221
224
|
|
|
222
225
|
Each example includes a no-network sanity check at the bottom so you
|
|
223
226
|
can run it (`yarn tsx examples/0X-...ts`) without installing any of
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Anvil's deterministic 10-account list — re-exported here as plain
|
|
3
|
+
* data so browser-mode test files can import it without pulling in
|
|
4
|
+
* the Node-only `createAnvilFixture` (which uses `node:child_process`
|
|
5
|
+
* and crashes the browser bundler).
|
|
6
|
+
*
|
|
7
|
+
* These are well-known throwaway keys baked into anvil's default
|
|
8
|
+
* mnemonic. Every account has 10_000 ETH on a fresh anvil instance.
|
|
9
|
+
* NEVER use these on any real chain.
|
|
10
|
+
*/
|
|
11
|
+
export declare const ANVIL_ACCOUNTS: {
|
|
12
|
+
readonly relayer: {
|
|
13
|
+
readonly address: "0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266";
|
|
14
|
+
readonly privateKey: "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80";
|
|
15
|
+
};
|
|
16
|
+
readonly recipient: {
|
|
17
|
+
readonly address: "0x70997970C51812dc3A010C7d01b50e0d17dc79C8";
|
|
18
|
+
readonly privateKey: "0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d";
|
|
19
|
+
};
|
|
20
|
+
readonly cosigner: {
|
|
21
|
+
readonly address: "0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC";
|
|
22
|
+
readonly privateKey: "0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a";
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
//# sourceMappingURL=anvil-accounts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"anvil-accounts.d.ts","sourceRoot":"","sources":["../src/anvil-accounts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,eAAO,MAAM,cAAc;;;;;;;;;;;;;CAgBjB,CAAA"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Anvil's deterministic 10-account list — re-exported here as plain
|
|
3
|
+
* data so browser-mode test files can import it without pulling in
|
|
4
|
+
* the Node-only `createAnvilFixture` (which uses `node:child_process`
|
|
5
|
+
* and crashes the browser bundler).
|
|
6
|
+
*
|
|
7
|
+
* These are well-known throwaway keys baked into anvil's default
|
|
8
|
+
* mnemonic. Every account has 10_000 ETH on a fresh anvil instance.
|
|
9
|
+
* NEVER use these on any real chain.
|
|
10
|
+
*/
|
|
11
|
+
export const ANVIL_ACCOUNTS = {
|
|
12
|
+
relayer: {
|
|
13
|
+
address: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
|
|
14
|
+
privateKey: '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80',
|
|
15
|
+
},
|
|
16
|
+
recipient: {
|
|
17
|
+
address: '0x70997970C51812dc3A010C7d01b50e0d17dc79C8',
|
|
18
|
+
privateKey: '0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d',
|
|
19
|
+
},
|
|
20
|
+
cosigner: {
|
|
21
|
+
address: '0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC',
|
|
22
|
+
privateKey: '0x5de4111afa1a4b94908f83103eb1f1706367c2e68ca870fc3fb9a804cdab365a',
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
//# sourceMappingURL=anvil-accounts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"anvil-accounts.js","sourceRoot":"","sources":["../src/anvil-accounts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,OAAO,EAAE;QACP,OAAO,EAAE,4CAAqD;QAC9D,UAAU,EACR,oEAA6E;KAChF;IACD,SAAS,EAAE;QACT,OAAO,EAAE,4CAAqD;QAC9D,UAAU,EACR,oEAA6E;KAChF;IACD,QAAQ,EAAE;QACR,OAAO,EAAE,4CAAqD;QAC9D,UAAU,EACR,oEAA6E;KAChF;CACO,CAAA"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export { ANVIL_ACCOUNTS } from './anvil-accounts.js';
|
|
2
|
+
export interface AnvilFixture {
|
|
3
|
+
url: string;
|
|
4
|
+
start: () => Promise<void>;
|
|
5
|
+
stop: () => Promise<void>;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Build an anvil fixture bound to a port. Tests should call
|
|
9
|
+
* `start()` in beforeAll and `stop()` in afterAll.
|
|
10
|
+
*
|
|
11
|
+
* Polls `eth_chainId` until anvil responds (anvil prints a banner
|
|
12
|
+
* but parsing stdout is racy across versions). Default poll window
|
|
13
|
+
* is 10 seconds — anvil typically starts in under 500ms locally.
|
|
14
|
+
*/
|
|
15
|
+
export declare const createAnvilFixture: (port?: number) => AnvilFixture;
|
|
16
|
+
//# sourceMappingURL=anvil-fixture.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"anvil-fixture.d.ts","sourceRoot":"","sources":["../src/anvil-fixture.ts"],"names":[],"mappings":"AAyBA,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAEpD,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IAC1B,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAC1B;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,kBAAkB,GAAI,aAAW,KAAG,YAuDhD,CAAA"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared anvil fixture for the wallet-adapter integration tests.
|
|
3
|
+
*
|
|
4
|
+
* Spawns a local anvil instance via child_process, polls its RPC
|
|
5
|
+
* until ready, exposes its URL and deterministic test-account list,
|
|
6
|
+
* and tears down on test exit. The fixture is Node-only — anvil is
|
|
7
|
+
* a native binary launched from outside the test process.
|
|
8
|
+
*
|
|
9
|
+
* Anvil's deterministic 10-account setup is what we lean on: every
|
|
10
|
+
* account has 10_000 ETH and a known private key. We use the first
|
|
11
|
+
* three slots:
|
|
12
|
+
* - account 0: relayer / signer
|
|
13
|
+
* - account 1: recipient
|
|
14
|
+
* - account 2: spare (Safe co-signer, etc.)
|
|
15
|
+
*
|
|
16
|
+
* Each test file pins its own port to avoid races when multiple
|
|
17
|
+
* integration files run in parallel (the integration config uses
|
|
18
|
+
* `singleFork: true` to serialize anyway, but per-file ports are
|
|
19
|
+
* extra defense).
|
|
20
|
+
*/
|
|
21
|
+
import { spawn } from 'node:child_process';
|
|
22
|
+
// Re-export account fixtures so Node-side test files only need one
|
|
23
|
+
// import. Browser-side files should import from `./anvil-accounts.js`
|
|
24
|
+
// directly to avoid pulling in this file's Node-only machinery.
|
|
25
|
+
export { ANVIL_ACCOUNTS } from './anvil-accounts.js';
|
|
26
|
+
/**
|
|
27
|
+
* Build an anvil fixture bound to a port. Tests should call
|
|
28
|
+
* `start()` in beforeAll and `stop()` in afterAll.
|
|
29
|
+
*
|
|
30
|
+
* Polls `eth_chainId` until anvil responds (anvil prints a banner
|
|
31
|
+
* but parsing stdout is racy across versions). Default poll window
|
|
32
|
+
* is 10 seconds — anvil typically starts in under 500ms locally.
|
|
33
|
+
*/
|
|
34
|
+
export const createAnvilFixture = (port = 8645) => {
|
|
35
|
+
const url = `http://127.0.0.1:${port}`;
|
|
36
|
+
let proc = null;
|
|
37
|
+
const waitForReady = async (deadlineMs) => {
|
|
38
|
+
const start = Date.now();
|
|
39
|
+
while (Date.now() - start < deadlineMs) {
|
|
40
|
+
try {
|
|
41
|
+
const res = await fetch(url, {
|
|
42
|
+
method: 'POST',
|
|
43
|
+
headers: { 'content-type': 'application/json' },
|
|
44
|
+
body: JSON.stringify({
|
|
45
|
+
jsonrpc: '2.0',
|
|
46
|
+
id: 1,
|
|
47
|
+
method: 'eth_chainId',
|
|
48
|
+
params: [],
|
|
49
|
+
}),
|
|
50
|
+
});
|
|
51
|
+
if (res.ok) {
|
|
52
|
+
const json = (await res.json());
|
|
53
|
+
if (typeof json.result === 'string')
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
// anvil not listening yet — keep polling.
|
|
59
|
+
}
|
|
60
|
+
await new Promise((r) => setTimeout(r, 100));
|
|
61
|
+
}
|
|
62
|
+
throw new Error(`anvil did not respond on ${url} within ${deadlineMs}ms`);
|
|
63
|
+
};
|
|
64
|
+
return {
|
|
65
|
+
url,
|
|
66
|
+
start: async () => {
|
|
67
|
+
if (proc !== null)
|
|
68
|
+
return;
|
|
69
|
+
proc = spawn('anvil', ['--port', String(port), '--silent'], {
|
|
70
|
+
stdio: 'ignore',
|
|
71
|
+
});
|
|
72
|
+
proc.on('error', (err) => {
|
|
73
|
+
console.error(`anvil spawn failed: ${err.message}`);
|
|
74
|
+
});
|
|
75
|
+
await waitForReady(10000);
|
|
76
|
+
},
|
|
77
|
+
stop: async () => {
|
|
78
|
+
if (proc === null)
|
|
79
|
+
return;
|
|
80
|
+
const exited = new Promise((resolve) => {
|
|
81
|
+
proc?.once('exit', () => resolve());
|
|
82
|
+
});
|
|
83
|
+
proc.kill('SIGTERM');
|
|
84
|
+
await Promise.race([
|
|
85
|
+
exited,
|
|
86
|
+
new Promise((r) => setTimeout(r, 2000)),
|
|
87
|
+
]);
|
|
88
|
+
proc = null;
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
};
|
|
92
|
+
//# sourceMappingURL=anvil-fixture.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"anvil-fixture.js","sourceRoot":"","sources":["../src/anvil-fixture.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,OAAO,EAAE,KAAK,EAAqB,MAAM,oBAAoB,CAAA;AAE7D,mEAAmE;AACnE,sEAAsE;AACtE,gEAAgE;AAChE,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAQpD;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,IAAI,GAAG,IAAI,EAAgB,EAAE;IAC9D,MAAM,GAAG,GAAG,oBAAoB,IAAI,EAAE,CAAA;IACtC,IAAI,IAAI,GAAwB,IAAI,CAAA;IAEpC,MAAM,YAAY,GAAG,KAAK,EAAE,UAAkB,EAAiB,EAAE;QAC/D,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;QACxB,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,GAAG,UAAU,EAAE,CAAC;YACvC,IAAI,CAAC;gBACH,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;oBAC3B,MAAM,EAAE,MAAM;oBACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;oBAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC;wBACnB,OAAO,EAAE,KAAK;wBACd,EAAE,EAAE,CAAC;wBACL,MAAM,EAAE,aAAa;wBACrB,MAAM,EAAE,EAAE;qBACX,CAAC;iBACH,CAAC,CAAA;gBACF,IAAI,GAAG,CAAC,EAAE,EAAE,CAAC;oBACX,MAAM,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAwB,CAAA;oBACtD,IAAI,OAAO,IAAI,CAAC,MAAM,KAAK,QAAQ;wBAAE,OAAM;gBAC7C,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,0CAA0C;YAC5C,CAAC;YACD,MAAM,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAA;QAC9C,CAAC;QACD,MAAM,IAAI,KAAK,CAAC,4BAA4B,GAAG,WAAW,UAAU,IAAI,CAAC,CAAA;IAC3E,CAAC,CAAA;IAED,OAAO;QACL,GAAG;QACH,KAAK,EAAE,KAAK,IAAI,EAAE;YAChB,IAAI,IAAI,KAAK,IAAI;gBAAE,OAAM;YACzB,IAAI,GAAG,KAAK,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,EAAE,UAAU,CAAC,EAAE;gBAC1D,KAAK,EAAE,QAAQ;aAChB,CAAC,CAAA;YACF,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;gBACvB,OAAO,CAAC,KAAK,CAAC,uBAAuB,GAAG,CAAC,OAAO,EAAE,CAAC,CAAA;YACrD,CAAC,CAAC,CAAA;YACF,MAAM,YAAY,CAAC,KAAM,CAAC,CAAA;QAC5B,CAAC;QACD,IAAI,EAAE,KAAK,IAAI,EAAE;YACf,IAAI,IAAI,KAAK,IAAI;gBAAE,OAAM;YACzB,MAAM,MAAM,GAAG,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE;gBAC3C,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAA;YACrC,CAAC,CAAC,CAAA;YACF,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;YACpB,MAAM,OAAO,CAAC,IAAI,CAAC;gBACjB,MAAM;gBACN,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,EAAE,IAAK,CAAC,CAAC;aACzC,CAAC,CAAA;YACF,IAAI,GAAG,IAAI,CAAA;QACb,CAAC;KACF,CAAA;AACH,CAAC,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@valve-tech/wallet-adapter",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"description": "Framework-agnostic vocabulary + runtime helpers for EVM dapp wallet integration. WalletAdapter interface (sign + send), WriteHookParams full lifecycle with rich TxContext payloads (chainId + original request) on every event so consumers don't side-channel; six named hooks (onAwaitingSignature, onTransactionHash, onConfirmed, onFailed, onDropped, onReplaced) plus complementary onPhase(event) discriminated-union shape derived from the WritePhaseSteps phase-map; sendTransactionWithHooks + awaitReceiptWithHooks helpers that fire the hooks at real boundaries (with awaitReceiptWithHooks fetching the containing block once on behalf of all downstream consumers); typed WalletRejectedError + ContractRevertedError for instanceof-discriminated catch; plus TX_STATUS / TX_FLOW / TrackedTx for tx-state UI. Lets SDKs and dapps share one contract instead of each redefining it. Part of the valve-tech/evm-toolkit synchronized release line.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://github.com/valve-tech/evm-toolkit/tree/main/packages/wallet-adapter#readme",
|
|
@@ -45,11 +45,13 @@
|
|
|
45
45
|
"typecheck:examples": "tsc -p examples",
|
|
46
46
|
"lint": "eslint src",
|
|
47
47
|
"test": "vitest run",
|
|
48
|
+
"test:browser": "vitest run --config vitest.browser.config.ts",
|
|
49
|
+
"test:integration": "vitest run --config vitest.integration.config.ts",
|
|
48
50
|
"test:coverage": "vitest run --coverage",
|
|
49
51
|
"prepare": "yarn build"
|
|
50
52
|
},
|
|
51
53
|
"dependencies": {
|
|
52
|
-
"@valve-tech/viem-errors": "^0.
|
|
54
|
+
"@valve-tech/viem-errors": "^0.13.0"
|
|
53
55
|
},
|
|
54
56
|
"peerDependencies": {
|
|
55
57
|
"viem": "^2.0.0"
|