@v3finance/core-sdk 0.0.0-stage → 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.
Files changed (257) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/LICENSE +21 -0
  3. package/README.md +189 -2
  4. package/dist/cjs/client.d.ts +117 -0
  5. package/dist/cjs/client.d.ts.map +1 -0
  6. package/dist/cjs/client.js +83 -0
  7. package/dist/cjs/client.js.map +1 -0
  8. package/dist/cjs/core/errors.d.ts +72 -0
  9. package/dist/cjs/core/errors.d.ts.map +1 -0
  10. package/dist/cjs/core/errors.js +180 -0
  11. package/dist/cjs/core/errors.js.map +1 -0
  12. package/dist/cjs/core/gated.d.ts +54 -0
  13. package/dist/cjs/core/gated.d.ts.map +1 -0
  14. package/dist/cjs/core/gated.js +57 -0
  15. package/dist/cjs/core/gated.js.map +1 -0
  16. package/dist/cjs/core/http.d.ts +98 -0
  17. package/dist/cjs/core/http.d.ts.map +1 -0
  18. package/dist/cjs/core/http.js +193 -0
  19. package/dist/cjs/core/http.js.map +1 -0
  20. package/dist/cjs/core/pagination.d.ts +37 -0
  21. package/dist/cjs/core/pagination.d.ts.map +1 -0
  22. package/dist/cjs/core/pagination.js +50 -0
  23. package/dist/cjs/core/pagination.js.map +1 -0
  24. package/dist/cjs/generated/operations.d.ts +1384 -0
  25. package/dist/cjs/generated/operations.d.ts.map +1 -0
  26. package/dist/cjs/generated/operations.js +1391 -0
  27. package/dist/cjs/generated/operations.js.map +1 -0
  28. package/dist/cjs/generated/types.d.ts +3810 -0
  29. package/dist/cjs/generated/types.d.ts.map +1 -0
  30. package/dist/cjs/generated/types.js +5 -0
  31. package/dist/cjs/generated/types.js.map +1 -0
  32. package/dist/cjs/index.d.ts +44 -0
  33. package/dist/cjs/index.d.ts.map +1 -0
  34. package/dist/cjs/index.js +86 -0
  35. package/dist/cjs/index.js.map +1 -0
  36. package/dist/cjs/package.json +1 -0
  37. package/dist/cjs/query-keys.d.ts +17 -0
  38. package/dist/cjs/query-keys.d.ts.map +1 -0
  39. package/dist/cjs/query-keys.js +20 -0
  40. package/dist/cjs/query-keys.js.map +1 -0
  41. package/dist/cjs/resources/addresses.d.ts +44 -0
  42. package/dist/cjs/resources/addresses.d.ts.map +1 -0
  43. package/dist/cjs/resources/addresses.js +39 -0
  44. package/dist/cjs/resources/addresses.js.map +1 -0
  45. package/dist/cjs/resources/admin.d.ts +120 -0
  46. package/dist/cjs/resources/admin.d.ts.map +1 -0
  47. package/dist/cjs/resources/admin.js +139 -0
  48. package/dist/cjs/resources/admin.js.map +1 -0
  49. package/dist/cjs/resources/approvals.d.ts +47 -0
  50. package/dist/cjs/resources/approvals.d.ts.map +1 -0
  51. package/dist/cjs/resources/approvals.js +52 -0
  52. package/dist/cjs/resources/approvals.js.map +1 -0
  53. package/dist/cjs/resources/auth.d.ts +30 -0
  54. package/dist/cjs/resources/auth.d.ts.map +1 -0
  55. package/dist/cjs/resources/auth.js +36 -0
  56. package/dist/cjs/resources/auth.js.map +1 -0
  57. package/dist/cjs/resources/balances.d.ts +47 -0
  58. package/dist/cjs/resources/balances.d.ts.map +1 -0
  59. package/dist/cjs/resources/balances.js +35 -0
  60. package/dist/cjs/resources/balances.js.map +1 -0
  61. package/dist/cjs/resources/categories.d.ts +87 -0
  62. package/dist/cjs/resources/categories.d.ts.map +1 -0
  63. package/dist/cjs/resources/categories.js +135 -0
  64. package/dist/cjs/resources/categories.js.map +1 -0
  65. package/dist/cjs/resources/counterparties.d.ts +67 -0
  66. package/dist/cjs/resources/counterparties.d.ts.map +1 -0
  67. package/dist/cjs/resources/counterparties.js +107 -0
  68. package/dist/cjs/resources/counterparties.js.map +1 -0
  69. package/dist/cjs/resources/folders.d.ts +46 -0
  70. package/dist/cjs/resources/folders.d.ts.map +1 -0
  71. package/dist/cjs/resources/folders.js +81 -0
  72. package/dist/cjs/resources/folders.js.map +1 -0
  73. package/dist/cjs/resources/history.d.ts +41 -0
  74. package/dist/cjs/resources/history.d.ts.map +1 -0
  75. package/dist/cjs/resources/history.js +17 -0
  76. package/dist/cjs/resources/history.js.map +1 -0
  77. package/dist/cjs/resources/me.d.ts +37 -0
  78. package/dist/cjs/resources/me.d.ts.map +1 -0
  79. package/dist/cjs/resources/me.js +54 -0
  80. package/dist/cjs/resources/me.js.map +1 -0
  81. package/dist/cjs/resources/members.d.ts +38 -0
  82. package/dist/cjs/resources/members.d.ts.map +1 -0
  83. package/dist/cjs/resources/members.js +58 -0
  84. package/dist/cjs/resources/members.js.map +1 -0
  85. package/dist/cjs/resources/operations.d.ts +49 -0
  86. package/dist/cjs/resources/operations.d.ts.map +1 -0
  87. package/dist/cjs/resources/operations.js +70 -0
  88. package/dist/cjs/resources/operations.js.map +1 -0
  89. package/dist/cjs/resources/registries.d.ts +68 -0
  90. package/dist/cjs/resources/registries.d.ts.map +1 -0
  91. package/dist/cjs/resources/registries.js +91 -0
  92. package/dist/cjs/resources/registries.js.map +1 -0
  93. package/dist/cjs/resources/reports.d.ts +201 -0
  94. package/dist/cjs/resources/reports.d.ts.map +1 -0
  95. package/dist/cjs/resources/reports.js +113 -0
  96. package/dist/cjs/resources/reports.js.map +1 -0
  97. package/dist/cjs/resources/sharing.d.ts +52 -0
  98. package/dist/cjs/resources/sharing.d.ts.map +1 -0
  99. package/dist/cjs/resources/sharing.js +78 -0
  100. package/dist/cjs/resources/sharing.js.map +1 -0
  101. package/dist/cjs/resources/transfers.d.ts +65 -0
  102. package/dist/cjs/resources/transfers.d.ts.map +1 -0
  103. package/dist/cjs/resources/transfers.js +83 -0
  104. package/dist/cjs/resources/transfers.js.map +1 -0
  105. package/dist/cjs/resources/types.d.ts +40 -0
  106. package/dist/cjs/resources/types.d.ts.map +1 -0
  107. package/dist/cjs/resources/types.js +14 -0
  108. package/dist/cjs/resources/types.js.map +1 -0
  109. package/dist/cjs/units.d.ts +59 -0
  110. package/dist/cjs/units.d.ts.map +1 -0
  111. package/dist/cjs/units.js +129 -0
  112. package/dist/cjs/units.js.map +1 -0
  113. package/dist/cjs/version.d.ts +5 -0
  114. package/dist/cjs/version.d.ts.map +1 -0
  115. package/dist/cjs/version.js +8 -0
  116. package/dist/cjs/version.js.map +1 -0
  117. package/dist/esm/client.d.ts +117 -0
  118. package/dist/esm/client.d.ts.map +1 -0
  119. package/dist/esm/client.js +79 -0
  120. package/dist/esm/client.js.map +1 -0
  121. package/dist/esm/core/errors.d.ts +72 -0
  122. package/dist/esm/core/errors.d.ts.map +1 -0
  123. package/dist/esm/core/errors.js +168 -0
  124. package/dist/esm/core/errors.js.map +1 -0
  125. package/dist/esm/core/gated.d.ts +54 -0
  126. package/dist/esm/core/gated.d.ts.map +1 -0
  127. package/dist/esm/core/gated.js +52 -0
  128. package/dist/esm/core/gated.js.map +1 -0
  129. package/dist/esm/core/http.d.ts +98 -0
  130. package/dist/esm/core/http.d.ts.map +1 -0
  131. package/dist/esm/core/http.js +187 -0
  132. package/dist/esm/core/http.js.map +1 -0
  133. package/dist/esm/core/pagination.d.ts +37 -0
  134. package/dist/esm/core/pagination.d.ts.map +1 -0
  135. package/dist/esm/core/pagination.js +46 -0
  136. package/dist/esm/core/pagination.js.map +1 -0
  137. package/dist/esm/generated/operations.d.ts +1384 -0
  138. package/dist/esm/generated/operations.d.ts.map +1 -0
  139. package/dist/esm/generated/operations.js +1387 -0
  140. package/dist/esm/generated/operations.js.map +1 -0
  141. package/dist/esm/generated/types.d.ts +3810 -0
  142. package/dist/esm/generated/types.d.ts.map +1 -0
  143. package/dist/esm/generated/types.js +4 -0
  144. package/dist/esm/generated/types.js.map +1 -0
  145. package/dist/esm/index.d.ts +44 -0
  146. package/dist/esm/index.d.ts.map +1 -0
  147. package/dist/esm/index.js +38 -0
  148. package/dist/esm/index.js.map +1 -0
  149. package/dist/esm/query-keys.d.ts +17 -0
  150. package/dist/esm/query-keys.d.ts.map +1 -0
  151. package/dist/esm/query-keys.js +17 -0
  152. package/dist/esm/query-keys.js.map +1 -0
  153. package/dist/esm/resources/addresses.d.ts +44 -0
  154. package/dist/esm/resources/addresses.d.ts.map +1 -0
  155. package/dist/esm/resources/addresses.js +35 -0
  156. package/dist/esm/resources/addresses.js.map +1 -0
  157. package/dist/esm/resources/admin.d.ts +120 -0
  158. package/dist/esm/resources/admin.d.ts.map +1 -0
  159. package/dist/esm/resources/admin.js +135 -0
  160. package/dist/esm/resources/admin.js.map +1 -0
  161. package/dist/esm/resources/approvals.d.ts +47 -0
  162. package/dist/esm/resources/approvals.d.ts.map +1 -0
  163. package/dist/esm/resources/approvals.js +48 -0
  164. package/dist/esm/resources/approvals.js.map +1 -0
  165. package/dist/esm/resources/auth.d.ts +30 -0
  166. package/dist/esm/resources/auth.d.ts.map +1 -0
  167. package/dist/esm/resources/auth.js +32 -0
  168. package/dist/esm/resources/auth.js.map +1 -0
  169. package/dist/esm/resources/balances.d.ts +47 -0
  170. package/dist/esm/resources/balances.d.ts.map +1 -0
  171. package/dist/esm/resources/balances.js +31 -0
  172. package/dist/esm/resources/balances.js.map +1 -0
  173. package/dist/esm/resources/categories.d.ts +87 -0
  174. package/dist/esm/resources/categories.d.ts.map +1 -0
  175. package/dist/esm/resources/categories.js +131 -0
  176. package/dist/esm/resources/categories.js.map +1 -0
  177. package/dist/esm/resources/counterparties.d.ts +67 -0
  178. package/dist/esm/resources/counterparties.d.ts.map +1 -0
  179. package/dist/esm/resources/counterparties.js +102 -0
  180. package/dist/esm/resources/counterparties.js.map +1 -0
  181. package/dist/esm/resources/folders.d.ts +46 -0
  182. package/dist/esm/resources/folders.d.ts.map +1 -0
  183. package/dist/esm/resources/folders.js +77 -0
  184. package/dist/esm/resources/folders.js.map +1 -0
  185. package/dist/esm/resources/history.d.ts +41 -0
  186. package/dist/esm/resources/history.d.ts.map +1 -0
  187. package/dist/esm/resources/history.js +13 -0
  188. package/dist/esm/resources/history.js.map +1 -0
  189. package/dist/esm/resources/me.d.ts +37 -0
  190. package/dist/esm/resources/me.d.ts.map +1 -0
  191. package/dist/esm/resources/me.js +50 -0
  192. package/dist/esm/resources/me.js.map +1 -0
  193. package/dist/esm/resources/members.d.ts +38 -0
  194. package/dist/esm/resources/members.d.ts.map +1 -0
  195. package/dist/esm/resources/members.js +54 -0
  196. package/dist/esm/resources/members.js.map +1 -0
  197. package/dist/esm/resources/operations.d.ts +49 -0
  198. package/dist/esm/resources/operations.d.ts.map +1 -0
  199. package/dist/esm/resources/operations.js +66 -0
  200. package/dist/esm/resources/operations.js.map +1 -0
  201. package/dist/esm/resources/registries.d.ts +68 -0
  202. package/dist/esm/resources/registries.d.ts.map +1 -0
  203. package/dist/esm/resources/registries.js +87 -0
  204. package/dist/esm/resources/registries.js.map +1 -0
  205. package/dist/esm/resources/reports.d.ts +201 -0
  206. package/dist/esm/resources/reports.d.ts.map +1 -0
  207. package/dist/esm/resources/reports.js +109 -0
  208. package/dist/esm/resources/reports.js.map +1 -0
  209. package/dist/esm/resources/sharing.d.ts +52 -0
  210. package/dist/esm/resources/sharing.d.ts.map +1 -0
  211. package/dist/esm/resources/sharing.js +74 -0
  212. package/dist/esm/resources/sharing.js.map +1 -0
  213. package/dist/esm/resources/transfers.d.ts +65 -0
  214. package/dist/esm/resources/transfers.d.ts.map +1 -0
  215. package/dist/esm/resources/transfers.js +79 -0
  216. package/dist/esm/resources/transfers.js.map +1 -0
  217. package/dist/esm/resources/types.d.ts +40 -0
  218. package/dist/esm/resources/types.d.ts.map +1 -0
  219. package/dist/esm/resources/types.js +13 -0
  220. package/dist/esm/resources/types.js.map +1 -0
  221. package/dist/esm/units.d.ts +59 -0
  222. package/dist/esm/units.d.ts.map +1 -0
  223. package/dist/esm/units.js +116 -0
  224. package/dist/esm/units.js.map +1 -0
  225. package/dist/esm/version.d.ts +5 -0
  226. package/dist/esm/version.d.ts.map +1 -0
  227. package/dist/esm/version.js +5 -0
  228. package/dist/esm/version.js.map +1 -0
  229. package/package.json +76 -4
  230. package/src/client.ts +167 -0
  231. package/src/core/errors.ts +219 -0
  232. package/src/core/gated.ts +71 -0
  233. package/src/core/http.ts +288 -0
  234. package/src/core/pagination.ts +69 -0
  235. package/src/generated/operations.ts +1402 -0
  236. package/src/generated/types.ts +4039 -0
  237. package/src/index.ts +112 -0
  238. package/src/query-keys.ts +18 -0
  239. package/src/resources/addresses.ts +68 -0
  240. package/src/resources/admin.ts +218 -0
  241. package/src/resources/approvals.ts +82 -0
  242. package/src/resources/auth.ts +36 -0
  243. package/src/resources/balances.ts +68 -0
  244. package/src/resources/categories.ts +228 -0
  245. package/src/resources/counterparties.ts +196 -0
  246. package/src/resources/folders.ts +107 -0
  247. package/src/resources/history.ts +51 -0
  248. package/src/resources/me.ts +66 -0
  249. package/src/resources/members.ts +77 -0
  250. package/src/resources/operations.ts +114 -0
  251. package/src/resources/registries.ts +109 -0
  252. package/src/resources/reports.ts +315 -0
  253. package/src/resources/sharing.ts +114 -0
  254. package/src/resources/transfers.ts +107 -0
  255. package/src/resources/types.ts +59 -0
  256. package/src/units.ts +140 -0
  257. package/src/version.ts +5 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,44 @@
1
+ # Changelog
2
+
3
+ All notable changes to this package are documented in this file. The format is based on
4
+ [Keep a Changelog](https://keepachangelog.com), and this project adheres to [Semantic Versioning](https://semver.org).
5
+
6
+ ## 0.1.0 — 2026-11-10
7
+
8
+ First release. The whole API is covered — 171 operations grouped into resources, with the
9
+ administrative surface marked — and a test asserts that every endpoint is reachable, so one added
10
+ to the API shows up as a failure rather than as a gap nobody noticed.
11
+
12
+ ### Added
13
+
14
+ - `CoreClient` with vault scoping: `core.vault(code).addresses.list()`. A scope is an expression,
15
+ not state, so two vaults can be used side by side and switching the active one cannot leave a
16
+ stale scope pointing at the wrong place.
17
+ - Injected authentication: `getAuthHeaders`, `onTokenRotated`, `onUnauthorized`. Works with a
18
+ browser cookie, a native cookie jar or a bearer token; the package stores no credentials.
19
+ - `CoreError` with a `code` that is always present and a `kind` separating HTTP, network, timeout,
20
+ aborted and parse failures. Three real response shapes are parsed, including the validation
21
+ failure that arrives as a plain string.
22
+ - Money as smallest-unit strings with BigInt arithmetic (`amount`, `parseDecimal`, `format`, `add`,
23
+ `sub`, `compare`). There is no `toNumber`, on purpose: it would be a convenient door into the
24
+ precision loss the module exists to prevent.
25
+ - `Gated` results for the fifteen operations that can finish in more than one way, telling applied,
26
+ pending and replayed apart by HTTP status — the response bodies are identical, so a client
27
+ reading only the body reports "done" where the truth is "awaiting signatures".
28
+ - Keyset pagination helpers `paginate` and `collectAll`.
29
+ - Resources: addresses, transfers, history, balances, approvals, counterparties and their groups,
30
+ shared access, folders, members, operation labelling, operation metadata and relations, reports
31
+ with templates, snapshots and exports, registries, policy preflight, AML, sign-in, and the
32
+ administrative surface.
33
+ - Per-report-type parameters, so the compiler requires `from`/`to` and the target each report needs
34
+ rather than letting the call fail at runtime.
35
+ - `OPERATIONS`, a generated catalogue of all 171 API operations with the access level each requires.
36
+ - Cache keys for TanStack Query at `@v3finance/core-sdk/query-keys`.
37
+
38
+ ### Known limitations
39
+
40
+ - The `assets` filter on reports is accepted by the API and silently ignored by it, so it is marked
41
+ deprecated here. Use `tokenIds`.
42
+ - `runTemplate` cannot override a template's stored filters: the endpoint accepts no body.
43
+ - `/welcome` and `/webhooks/wallet` are not exposed — a browser landing page and the wallet
44
+ service's inbound channel respectively.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 V3 Finance
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,190 @@
1
- # Temporary Holding Version
1
+ # V3 Finance Core SDK
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ [![npm version](https://img.shields.io/npm/v/@v3finance/core-sdk.svg)](https://www.npmjs.com/package/@v3finance/core-sdk)
4
+ [![npm downloads](https://img.shields.io/npm/dm/@v3finance/core-sdk.svg)](https://www.npmjs.com/package/@v3finance/core-sdk)
5
+ [![types](https://img.shields.io/npm/types/@v3finance/core-sdk.svg)](https://www.npmjs.com/package/@v3finance/core-sdk)
6
+ [![license](https://img.shields.io/npm/l/@v3finance/core-sdk.svg)](./LICENSE)
7
+
8
+ The official TypeScript SDK for the V3 Finance Core API.
9
+ Custodial addresses, transfers under policy control, approvals and shared access.
10
+
11
+ - **Vault scoping** that carries no state: `core.vault(code)` is an expression, so two vaults work side by side.
12
+ - **Policy-aware results**: a transfer either executes, waits for signatures or replays — told apart by status, not guesswork.
13
+ - **Money that cannot silently lose precision**: smallest-unit strings and BigInt arithmetic, with no `toNumber`.
14
+ - **Pluggable authentication**: browser cookie, native cookie jar or bearer token — you supply headers, the SDK hands events back.
15
+ - **Complete**: every endpoint of the API, grouped into resources, with the administrative surface marked.
16
+ - **Fully typed**, zero dependencies, ESM and CommonJS. Runs on Node.js, Bun, Deno, Cloudflare Workers and React Native.
17
+
18
+ The whole API is covered: addresses and transfers, approvals, counterparties, shared access and
19
+ folders, operation labelling, reports, and the administrative surface. A test asserts that every
20
+ endpoint is reachable through some resource, so an endpoint added to the API shows up as a failure
21
+ rather than as a gap nobody noticed.
22
+
23
+ Two endpoints are deliberately absent: `/welcome` is a browser landing page the API redirects to,
24
+ and `/webhooks/wallet` is the inbound channel for the wallet service.
25
+
26
+ ## Install
27
+
28
+ ```bash
29
+ npm install @v3finance/core-sdk
30
+ ```
31
+
32
+ ## Quick start
33
+
34
+ ```ts
35
+ import { CoreClient } from '@v3finance/core-sdk'
36
+
37
+ const core = new CoreClient({
38
+ getAuthHeaders: () => ({ Authorization: `Bearer ${sessionToken}` }),
39
+ onTokenRotated: (token) => storage.save(token),
40
+ onUnauthorized: () => router.push('/login'),
41
+ })
42
+
43
+ const page = await core.vault('my-vault').addresses.list({ limit: 20 })
44
+ ```
45
+
46
+ ## Three things to know before the first call
47
+
48
+ ### 1. `vault(code)` is an expression, not state
49
+
50
+ ```ts
51
+ core.vault('alpha').addresses.list()
52
+ core.vault('beta').addresses.list()
53
+ ```
54
+
55
+ There is deliberately no "current vault". The code is visible on every call site, an administrator can
56
+ work with several vaults at once, and switching the active vault at runtime cannot leave a stale scope
57
+ pointing at the wrong one. Build a scope where you use it; do not store it.
58
+
59
+ ### 2. An operation may not execute — it may go for approval
60
+
61
+ Transfers and address creation run through the policy engine. The result is `Gated`, and the three
62
+ branches **must** be distinguished: the response body is identical, only the status differs.
63
+
64
+ ```ts
65
+ const result = await core.vault(code).transfers.send({
66
+ fromAddressId,
67
+ toAddress,
68
+ amount: '1.5',
69
+ asset: 'usdt',
70
+ idempotencyKey: crypto.randomUUID(),
71
+ })
72
+
73
+ switch (result.outcome) {
74
+ case 'applied':
75
+ return toast('Transfer sent')
76
+ case 'pending':
77
+ return toast('Awaiting signatures — the funds are reserved')
78
+ case 'replayed':
79
+ return toast('This transfer was already sent')
80
+ }
81
+ ```
82
+
83
+ Reporting "sent" on `pending` tells a person their money has gone when it is frozen.
84
+
85
+ **Always pass `idempotencyKey`** and keep it until you have an answer: after a timeout it is the only
86
+ way to retry without sending money twice.
87
+
88
+ ### 3. Amounts are strings, never numbers
89
+
90
+ Balances arrive in smallest units. `float64` loses precision above 2⁵³, and production balances go higher.
91
+
92
+ ```ts
93
+ import { amount, parseDecimal, format, add } from '@v3finance/core-sdk'
94
+
95
+ const balance = amount(address.balance ?? '0', 6, 'usdt')
96
+ format(balance, { withAsset: true }) // "1,500.00 USDT"
97
+ add(balance, parseDecimal('2.5', 6))
98
+ ```
99
+
100
+ There is no `toNumber`, on purpose — it would be a convenient door into the bug this layer prevents.
101
+
102
+ **`balance: null` is not zero.** It means the address has no ledger account yet: nothing has ever
103
+ arrived. "No account" and "empty account" deserve different wording in an interface.
104
+
105
+ ## Errors
106
+
107
+ Every failure is a `CoreError` and always carries a `code` — sent by the server or derived from the
108
+ HTTP status. Branch on `code`, display `message`.
109
+
110
+ ```ts
111
+ import { hasCode, isCoreError } from '@v3finance/core-sdk'
112
+
113
+ try {
114
+ await core.vault(code).transfers.send(input)
115
+ } catch (error) {
116
+ if (hasCode(error, 'INSUFFICIENT_BALANCE')) return showTopUp()
117
+ if (hasCode(error, 'APPROVAL_UNSATISFIABLE')) return showNoApprovers()
118
+ if (isCoreError(error) && error.kind === 'timeout') return retryWithSameKey()
119
+ throw error
120
+ }
121
+ ```
122
+
123
+ `kind` separates `http` (the server answered), `network` (the request never left), `timeout`,
124
+ `aborted` and `parse`.
125
+
126
+ ## Authentication
127
+
128
+ The SDK does not store credentials: you provide headers, it reports two events back.
129
+
130
+ | Runtime | How |
131
+ | --- | --- |
132
+ | Browser | `credentials: 'include'`, the cookie is set by the server |
133
+ | React Native | your own cookie jar, returned from `getAuthHeaders` |
134
+ | Anywhere | `Authorization: Bearer <session-token>` |
135
+
136
+ In bearer mode **`onTokenRotated` is required**: the server extends a session through the
137
+ `set-auth-token` header, and a client that does not store the new token is signed out at the next
138
+ rotation.
139
+
140
+ Sign-in is a magic link sent by email. Signing up is possible only on an administrator's invitation.
141
+
142
+ ## Pagination
143
+
144
+ Lists return `{ items, nextCursor }`. The cursor is opaque — do not parse it.
145
+
146
+ ```ts
147
+ import { paginate } from '@v3finance/core-sdk'
148
+
149
+ for await (const address of paginate((p) => core.vault(code).addresses.list(p))) {
150
+ console.log(address.address)
151
+ }
152
+ ```
153
+
154
+ ## Admin operations
155
+
156
+ The package covers the whole API, administrative endpoints included. Access level is available
157
+ programmatically:
158
+
159
+ ```ts
160
+ import { OPERATIONS, isAdminOperation } from '@v3finance/core-sdk'
161
+
162
+ isAdminOperation('POST /api/v1/policies') // true
163
+ ```
164
+
165
+ A method existing in the package grants nothing: the server decides, based on your session.
166
+
167
+ ## Staying in sync with the API
168
+
169
+ The repository holds a snapshot of the OpenAPI document (`openapi/spec.json`). Every model is
170
+ generated from it into `src/generated/types.ts` — one exported type per schema, descriptions
171
+ included — by a small purpose-built generator rather than `openapi-typescript`, which is built on
172
+ the TypeScript 5 compiler API that TypeScript 7 no longer ships.
173
+
174
+ ```bash
175
+ npm run spec:update # refresh the snapshot from the live API
176
+ npm run generate # regenerate types and the operation catalogue
177
+ ```
178
+
179
+ CI fails when the generated files drift from the snapshot, and when the API gains an operation that
180
+ has not been classified by access level.
181
+
182
+ A test keeps the generated files in step with the snapshot, so a drift shows up in a pull request
183
+ rather than in a consumer at runtime.
184
+
185
+ Development requires Node.js 24 (the build scripts are TypeScript run directly). The published
186
+ package supports Node.js 20 and above.
187
+
188
+ ## License
189
+
190
+ [MIT](./LICENSE)
@@ -0,0 +1,117 @@
1
+ import { HttpClient, type ClientOptions } from './core/http.js';
2
+ import type { CreateVaultBody, Health, VaultDetail } from './generated/types.js';
3
+ import { AddressesResource } from './resources/addresses.js';
4
+ import { ApprovalsResource } from './resources/approvals.js';
5
+ import { BalancesResource } from './resources/balances.js';
6
+ import { CounterpartiesResource, CounterpartyGroupsResource } from './resources/counterparties.js';
7
+ import { AdminResource } from './resources/admin.js';
8
+ import { AuthResource } from './resources/auth.js';
9
+ import { CategoriesResource } from './resources/categories.js';
10
+ import { FoldersResource } from './resources/folders.js';
11
+ import { HistoryResource } from './resources/history.js';
12
+ import { MembersResource } from './resources/members.js';
13
+ import { OperationsResource } from './resources/operations.js';
14
+ import { RegistriesResource } from './resources/registries.js';
15
+ import { ReportsResource } from './resources/reports.js';
16
+ import { SharingResource } from './resources/sharing.js';
17
+ import { MeResource } from './resources/me.js';
18
+ import { TransfersResource } from './resources/transfers.js';
19
+ /**
20
+ * Client for the V3 Finance Core API.
21
+ *
22
+ * CALL SHAPE: `core.vault(code).addresses.list()`.
23
+ *
24
+ * `vault(code)` is an EXPRESSION, not state. There is deliberately no
25
+ * "current vault" that you set once and forget:
26
+ *
27
+ * • the vault code is visible on every call site, so reading the code does
28
+ * not require scrolling up to find which vault is in effect;
29
+ * • an administrator works with several vaults at once, and two scopes side
30
+ * by side cannot interfere — there is no shared mutable field;
31
+ * • in the mobile app and on the web the active vault changes at runtime,
32
+ * and a remembered scope would mean requests to the wrong vault after a
33
+ * switch.
34
+ *
35
+ * A scope is cheap: it is a pair of small objects over the shared HTTP
36
+ * client, so creating one per call is fine.
37
+ *
38
+ * Cross-vault and registry endpoints live at the root: `core.vaults`, `core.me`.
39
+ *
40
+ * @example
41
+ * ```ts
42
+ * import { CoreClient } from '@v3finance/core-sdk'
43
+ *
44
+ * const core = new CoreClient({
45
+ * getAuthHeaders: () => ({ Authorization: `Bearer ${sessionToken}` }),
46
+ * onTokenRotated: (token) => save(token),
47
+ * onUnauthorized: () => signOut(),
48
+ * })
49
+ *
50
+ * const page = await core.vault('my-vault').addresses.list({ limit: 20 })
51
+ * ```
52
+ */
53
+ export interface VaultScope {
54
+ readonly code: string;
55
+ readonly addresses: AddressesResource;
56
+ readonly transfers: TransfersResource;
57
+ readonly history: HistoryResource;
58
+ readonly balances: BalancesResource;
59
+ readonly approvals: ApprovalsResource;
60
+ readonly counterparties: CounterpartiesResource;
61
+ readonly counterpartyGroups: CounterpartyGroupsResource;
62
+ readonly sharing: SharingResource;
63
+ readonly folders: FoldersResource;
64
+ readonly members: MembersResource;
65
+ readonly categories: CategoriesResource;
66
+ readonly reports: ReportsResource;
67
+ readonly operations: OperationsResource;
68
+ }
69
+ export interface VaultSummary {
70
+ id: string;
71
+ code: string;
72
+ name: string;
73
+ [extra: string]: unknown;
74
+ }
75
+ export interface Profile {
76
+ id: string;
77
+ name: string | null;
78
+ username: string | null;
79
+ email: string | null;
80
+ /** The account came from an invitation link and has no real email yet. */
81
+ needsEmail: boolean;
82
+ [extra: string]: unknown;
83
+ }
84
+ export declare class CoreClient {
85
+ /** Low-level access for endpoints the resources do not cover yet. */
86
+ readonly http: HttpClient;
87
+ constructor(options?: ClientOptions);
88
+ /** Scope of one vault. Build it at the call site; do not store it. */
89
+ vault(code: string): VaultScope;
90
+ readonly vaults: {
91
+ /** Administrators only: a plain member sees their own vault through `me.vault()`. */
92
+ list: () => Promise<VaultSummary[]>;
93
+ get: (code: string) => Promise<VaultSummary>;
94
+ /** Administrators only. */
95
+ create: (input: CreateVaultBody) => Promise<VaultDetail>;
96
+ /**
97
+ * Assembles a derivation path from segment values, and takes one apart.
98
+ *
99
+ * Use these rather than building the string yourself: the path is written
100
+ * WITHOUT apostrophes (`m/44/195/…`, not `m/44'/…`), and the segment
101
+ * order comes from the vault's own configuration, not from a convention.
102
+ */
103
+ buildDerivationPath: (code: string, body: unknown) => Promise<unknown>;
104
+ parseDerivationPath: (code: string, body: unknown) => Promise<unknown>;
105
+ };
106
+ /** Liveness probe. Needs no authentication. */
107
+ health(): Promise<Health>;
108
+ /** The caller's own profile, devices and permissions. */
109
+ readonly me: MeResource;
110
+ /** Networks, tokens, rates, policy preflight and AML. Not vault-scoped. */
111
+ readonly registries: RegistriesResource;
112
+ /** Administrative endpoints. Having one here grants nothing — the server decides. */
113
+ readonly admin: AdminResource;
114
+ /** Signing in. One way only: a magic link, and only by invitation. */
115
+ readonly auth: AuthResource;
116
+ }
117
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,83 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CoreClient = void 0;
4
+ const http_js_1 = require("./core/http.js");
5
+ const addresses_js_1 = require("./resources/addresses.js");
6
+ const approvals_js_1 = require("./resources/approvals.js");
7
+ const balances_js_1 = require("./resources/balances.js");
8
+ const counterparties_js_1 = require("./resources/counterparties.js");
9
+ const admin_js_1 = require("./resources/admin.js");
10
+ const auth_js_1 = require("./resources/auth.js");
11
+ const categories_js_1 = require("./resources/categories.js");
12
+ const folders_js_1 = require("./resources/folders.js");
13
+ const history_js_1 = require("./resources/history.js");
14
+ const members_js_1 = require("./resources/members.js");
15
+ const operations_js_1 = require("./resources/operations.js");
16
+ const registries_js_1 = require("./resources/registries.js");
17
+ const reports_js_1 = require("./resources/reports.js");
18
+ const sharing_js_1 = require("./resources/sharing.js");
19
+ const me_js_1 = require("./resources/me.js");
20
+ const transfers_js_1 = require("./resources/transfers.js");
21
+ const version_js_1 = require("./version.js");
22
+ class CoreClient {
23
+ /** Low-level access for endpoints the resources do not cover yet. */
24
+ http;
25
+ constructor(options = {}) {
26
+ this.http = new http_js_1.HttpClient({ ...options, baseUrl: options.baseUrl ?? version_js_1.DEFAULT_BASE_URL });
27
+ this.me = new me_js_1.MeResource(this.http);
28
+ this.registries = new registries_js_1.RegistriesResource(this.http);
29
+ this.admin = new admin_js_1.AdminResource(this.http);
30
+ this.auth = new auth_js_1.AuthResource(this.http);
31
+ }
32
+ /** Scope of one vault. Build it at the call site; do not store it. */
33
+ vault(code) {
34
+ if (!code)
35
+ throw new Error('@v3finance/core-sdk: a vault code is required.');
36
+ return {
37
+ code,
38
+ addresses: new addresses_js_1.AddressesResource(this.http, code),
39
+ transfers: new transfers_js_1.TransfersResource(this.http, code),
40
+ history: new history_js_1.HistoryResource(this.http, code),
41
+ balances: new balances_js_1.BalancesResource(this.http, code),
42
+ approvals: new approvals_js_1.ApprovalsResource(this.http, code),
43
+ counterparties: new counterparties_js_1.CounterpartiesResource(this.http, code),
44
+ counterpartyGroups: new counterparties_js_1.CounterpartyGroupsResource(this.http, code),
45
+ sharing: new sharing_js_1.SharingResource(this.http, code),
46
+ folders: new folders_js_1.FoldersResource(this.http, code),
47
+ members: new members_js_1.MembersResource(this.http, code),
48
+ categories: new categories_js_1.CategoriesResource(this.http, code),
49
+ reports: new reports_js_1.ReportsResource(this.http, code),
50
+ operations: new operations_js_1.OperationsResource(this.http, code),
51
+ };
52
+ }
53
+ vaults = {
54
+ /** Administrators only: a plain member sees their own vault through `me.vault()`. */
55
+ list: () => this.http.request('GET', '/api/v1/vaults'),
56
+ get: (code) => this.http.request('GET', `/api/v1/vaults/${(0, http_js_1.seg)(code)}`),
57
+ /** Administrators only. */
58
+ create: (input) => this.http.request('POST', '/api/v1/vaults', { body: input }),
59
+ /**
60
+ * Assembles a derivation path from segment values, and takes one apart.
61
+ *
62
+ * Use these rather than building the string yourself: the path is written
63
+ * WITHOUT apostrophes (`m/44/195/…`, not `m/44'/…`), and the segment
64
+ * order comes from the vault's own configuration, not from a convention.
65
+ */
66
+ buildDerivationPath: (code, body) => this.http.request('POST', `/api/v1/vaults/${(0, http_js_1.seg)(code)}/derivation-path/build`, { body }),
67
+ parseDerivationPath: (code, body) => this.http.request('POST', `/api/v1/vaults/${(0, http_js_1.seg)(code)}/derivation-path/parse`, { body }),
68
+ };
69
+ /** Liveness probe. Needs no authentication. */
70
+ health() {
71
+ return this.http.request('GET', '/');
72
+ }
73
+ /** The caller's own profile, devices and permissions. */
74
+ me;
75
+ /** Networks, tokens, rates, policy preflight and AML. Not vault-scoped. */
76
+ registries;
77
+ /** Administrative endpoints. Having one here grants nothing — the server decides. */
78
+ admin;
79
+ /** Signing in. One way only: a magic link, and only by invitation. */
80
+ auth;
81
+ }
82
+ exports.CoreClient = CoreClient;
83
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Every failure from the SDK is a `CoreError`.
3
+ *
4
+ * The API wraps errors into `{error:{code,message,details?}}`, but that
5
+ * wrapper has three gaps the client has to close itself — otherwise a precise
6
+ * error turns into "something went wrong":
7
+ *
8
+ * 1. Bodies without an `error` key pass through untouched. Batch sharing
9
+ * endpoints answer 400 with `{results:[…]}`, which is not a failure of
10
+ * the whole call but "some subjects were rejected", and has to be read
11
+ * item by item.
12
+ * 2. Validation failures arrive as a plain string — `{error: "path: msg"}`
13
+ * — with no code and no details.
14
+ * 3. Inside a batch result `message` equals the code (`{code:"X",
15
+ * message:"X"}`), so it must never be shown to a person as-is.
16
+ *
17
+ * Because of that `code` is always present here: either the server sent one,
18
+ * or it is derived from the HTTP status. Branch on `code`, display `message`.
19
+ */
20
+ /** Where the failure came from. Each kind calls for a different reaction. */
21
+ export type CoreErrorKind =
22
+ /** The server answered with a status >= 400. `status` and `code` are meaningful. */
23
+ 'http'
24
+ /** The request never left: no connection, DNS, socket closed. Retrying makes sense. */
25
+ | 'network'
26
+ /** The request timed out. On money paths retry only with the same idempotency key. */
27
+ | 'timeout'
28
+ /** The caller aborted it. Not a failure — do not surface it. */
29
+ | 'aborted'
30
+ /** A response arrived but could not be read as the contract promises. */
31
+ | 'parse';
32
+ export interface CoreErrorInit {
33
+ kind: CoreErrorKind;
34
+ message: string;
35
+ code: string;
36
+ status?: number | null;
37
+ details?: unknown;
38
+ /** `METHOD /path`, for logs. Never part of the user-facing message. */
39
+ endpoint?: string;
40
+ cause?: unknown;
41
+ }
42
+ export declare class CoreError extends Error {
43
+ readonly kind: CoreErrorKind;
44
+ /** Machine-readable code. Never empty: sent by the server or derived from the status. */
45
+ readonly code: string;
46
+ /** HTTP status, or `null` for network, timeout and aborted failures. */
47
+ readonly status: number | null;
48
+ /** Structured details when the handler provided them. The shape depends on `code`. */
49
+ readonly details: unknown;
50
+ readonly endpoint: string | undefined;
51
+ constructor(init: CoreErrorInit);
52
+ }
53
+ export declare function isCoreError(e: unknown): e is CoreError;
54
+ /** Convenience for `catch`: `if (hasCode(e, 'INSUFFICIENT_BALANCE'))`. */
55
+ export declare function hasCode(e: unknown, ...codes: readonly string[]): boolean;
56
+ /**
57
+ * Status to code, mirroring the mapping the API uses, so that both sides call
58
+ * the same thing by the same name.
59
+ */
60
+ export declare function codeFromStatus(status: number): string;
61
+ /**
62
+ * Turns an error response body into a `CoreError`.
63
+ *
64
+ * Reads both places a code can live: the nested `error.code` and a flat `code`
65
+ * next to `error`. The API populates either one depending on the path.
66
+ */
67
+ export declare function parseErrorBody(status: number, body: unknown, endpoint?: string): CoreError;
68
+ export declare const networkError: (cause: unknown, endpoint?: string) => CoreError;
69
+ export declare const timeoutError: (ms: number, endpoint?: string) => CoreError;
70
+ export declare const abortedError: (endpoint?: string) => CoreError;
71
+ export declare const parseError: (cause: unknown, endpoint?: string) => CoreError;
72
+ //# sourceMappingURL=errors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../../src/core/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,6EAA6E;AAC7E,MAAM,MAAM,aAAa;AACvB,oFAAoF;AAClF,MAAM;AACR,uFAAuF;GACrF,SAAS;AACX,sFAAsF;GACpF,SAAS;AACX,gEAAgE;GAC9D,SAAS;AACX,yEAAyE;GACvE,OAAO,CAAA;AAEX,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,aAAa,CAAA;IACnB,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,uEAAuE;IACvE,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,KAAK,CAAC,EAAE,OAAO,CAAA;CAChB;AAED,qBAAa,SAAU,SAAQ,KAAK;IAClC,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAA;IAC5B,yFAAyF;IACzF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,wEAAwE;IACxE,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IAC9B,sFAAsF;IACtF,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAA;IAErC,YAAY,IAAI,EAAE,aAAa,EAQ9B;CACF;AAED,wBAAgB,WAAW,CAAC,CAAC,EAAE,OAAO,GAAG,CAAC,IAAI,SAAS,CAEtD;AAED,0EAA0E;AAC1E,wBAAgB,OAAO,CAAC,CAAC,EAAE,OAAO,EAAE,GAAG,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAExE;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAyBrD;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAiD1F;AAsBD,eAAO,MAAM,YAAY,UAAW,OAAO,aAAa,MAAM,KAAG,SAQ7D,CAAA;AAEJ,eAAO,MAAM,YAAY,OAAQ,MAAM,aAAa,MAAM,KAAG,SAOzD,CAAA;AAEJ,eAAO,MAAM,YAAY,cAAe,MAAM,KAAG,SAO7C,CAAA;AAEJ,eAAO,MAAM,UAAU,UAAW,OAAO,aAAa,MAAM,KAAG,SAQ3D,CAAA"}