@forevermoney/sdk 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.
package/CHANGELOG.md ADDED
@@ -0,0 +1,25 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0
4
+
5
+ - Added canonical Base and Subtensor production deployment metadata.
6
+ - Added the canonical Robinhood deployment, bridge plans, receipt parsing, and
7
+ CCIP delivery tracking.
8
+ - Added unsigned, approval-aware bridge plans in both directions.
9
+ - Enforced the 1:1 bridge invariant by encoding the bridged principal as the
10
+ minimum destination output while charging network fees separately.
11
+ - Rejected liquid Base-to-Subtensor deliveries below the `0.01 TAO` Subtensor
12
+ unstaking minimum.
13
+ - Added vault create, deposit, withdrawal, fee-claim, stake, and unstake plans.
14
+ - Added EIP-1193 and ethers transaction adapters plus canonical receipt parsers.
15
+ - Added destination-chain checkpoints and CCIP delivery status tracking with
16
+ Base-to-Subtensor recovery detection.
17
+ - Restricted delivery events to the current lane's authorized CCIP off-ramps.
18
+ - Added source transaction confirmation and canonical message-ID lookup.
19
+ - Added strict bigint, uint256, EVM address, Bittensor SS58, bytes32, whole-RAO,
20
+ source/delivery, and boolean validation.
21
+ - Added offline unit/property/protocol tests, production-fork checks, package
22
+ verification, and a guarded real-key canary workflow.
23
+ - Added pinned CI, production-fork, and npm trusted-publishing workflows.
24
+ - Rejected plaintext remote RPC endpoints and malformed transport requests.
25
+ - Raised the supported Node.js baseline to the maintained Node.js 22 line.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 ForeverMoney
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 ADDED
@@ -0,0 +1,299 @@
1
+ # `@forevermoney/sdk`
2
+
3
+ Typed, non-custodial transaction preparation for the ForeverMoney bridge and
4
+ vault contracts.
5
+
6
+ The SDK owns the canonical production deployment: chain IDs, CCIP selectors,
7
+ contract addresses, ABIs, and protocol-specific amount conversion. An
8
+ integrator supplies only RPC transports and user input. The SDK never accepts a
9
+ private key, signs a transaction, or broadcasts a transaction. Bridge principal
10
+ is transferred 1:1 and the network fee is charged separately, so the SDK fixes
11
+ the contract's minimum destination output to the bridged principal instead of
12
+ exposing configurable slippage.
13
+
14
+ ## Install
15
+
16
+ ```bash
17
+ npm install @forevermoney/sdk
18
+ ```
19
+
20
+ Node.js 22 or newer is required. Both ESM and CommonJS builds are published.
21
+
22
+ ## Create a client
23
+
24
+ ```ts
25
+ import { createForeverMoneyClient, http } from '@forevermoney/sdk'
26
+
27
+ const foreverMoney = createForeverMoneyClient({
28
+ transports: {
29
+ base: http(process.env.BASE_RPC_URL),
30
+ robinhood: http(process.env.ROBINHOOD_RPC_URL),
31
+ subtensor: http(process.env.SUBTENSOR_RPC_URL),
32
+ },
33
+ })
34
+
35
+ await foreverMoney.verifyConnections()
36
+ ```
37
+
38
+ `verifyConnections()` checks that the transports report Base (`8453`),
39
+ Robinhood (`4663`) when configured, and Subtensor EVM (`964`). A mismatched RPC
40
+ fails with `CHAIN_MISMATCH`; the SDK does not try another endpoint or silently
41
+ change networks.
42
+
43
+ Independent chain-scoped EIP-1193 transports can also be supplied directly. A
44
+ single injected wallet provider usually follows the wallet's currently selected
45
+ chain, so use it for signing rather than pretending it is two simultaneous RPC
46
+ connections:
47
+
48
+ ```ts
49
+ const foreverMoney = createForeverMoneyClient({
50
+ transports: {
51
+ base: baseReadTransport,
52
+ subtensor: subtensorReadTransport,
53
+ },
54
+ })
55
+ ```
56
+
57
+ See [`examples/talisman.ts`](./examples/talisman.ts) for account and transaction
58
+ handling.
59
+
60
+ ## Prepare a bridge
61
+
62
+ Amounts use `bigint` base units. `parseTaoAmount()` accepts at most nine decimal
63
+ places because the Subtensor protocol operates in whole RAO.
64
+
65
+ ```ts
66
+ import { parseTaoAmount } from '@forevermoney/sdk'
67
+
68
+ const prepared = await foreverMoney.bridge.prepareEvmToSubtensor({
69
+ evmChain: 'base',
70
+ sender: '0x...',
71
+ amountWei: parseTaoAmount('1.25'),
72
+ destination: '5...',
73
+ delivery: 'liquid',
74
+ })
75
+ ```
76
+
77
+ Use `evmChain: 'robinhood'` with a configured Robinhood transport for the
78
+ canonical Robinhood lane. `prepareBaseToSubtensor` remains available as the
79
+ Base-specific convenience method.
80
+
81
+ The result contains the exact quoted CCIP fee, the buffered transaction value,
82
+ and an ordered transaction plan. The plan includes an exact-amount ERC-20 or
83
+ staking-precompile approval only when the current allowance is insufficient.
84
+ Liquid Base-to-Subtensor delivery requires at least `0.01 TAO` because the
85
+ destination vault must unstake the bridged position. The SDK rejects smaller
86
+ liquid deliveries with `AMOUNT_BELOW_MINIMUM` before quoting or planning them;
87
+ staked delivery does not use this liquid-unstaking minimum.
88
+ The network-fee buffer is 2% and the estimated-gas buffer is 50%; both policies
89
+ are exported as bigint basis-point constants and covered by property tests.
90
+
91
+ For Subtensor to Base:
92
+
93
+ ```ts
94
+ const prepared = await foreverMoney.bridge.prepareSubtensorToEvm({
95
+ evmChain: 'base',
96
+ sender: '0x...',
97
+ recipient: '0x...',
98
+ amountWei: parseTaoAmount('1.25'),
99
+ source: 'liquid',
100
+ })
101
+ ```
102
+
103
+ `prepareSubtensorToBase` remains available as the Base-specific convenience
104
+ method.
105
+
106
+ For a staked source, pass the stake `netuid`. The SDK reads the staking
107
+ precompile allowance and expresses the approval in RAO.
108
+
109
+ The bridge does not deduct its fee from the destination amount. For both
110
+ directions, the SDK encodes the bridge amount itself as the contract's minimum
111
+ output; callers cannot weaken that invariant.
112
+
113
+ ## Track bridge delivery
114
+
115
+ Capture the destination block immediately before broadcasting the source bridge
116
+ transaction. Once the wallet broadcasts, resolve the source confirmation and
117
+ canonical message ID, then poll the destination status:
118
+
119
+ ```ts
120
+ const checkpoint =
121
+ await foreverMoney.bridge.getDeliveryCheckpoint('base-to-subtensor')
122
+ const source = await foreverMoney.bridge.getSourceStatus({
123
+ direction: 'base-to-subtensor',
124
+ transactionHash,
125
+ })
126
+ if (source.status !== 'confirmed') return source.status
127
+
128
+ const status = await foreverMoney.bridge.getDeliveryStatus({
129
+ direction: checkpoint.direction,
130
+ messageId: source.messageId,
131
+ fromBlock: checkpoint.fromBlock,
132
+ })
133
+ ```
134
+
135
+ The statuses are `waiting`, `success`, `failure`, and `recovery`. Recovery is
136
+ specific to Base-to-Subtensor: CCIP executed, but the canonical AlphaGateway
137
+ emitted `Claimable`, so the application must present the appropriate claim
138
+ flow. Delivery queries are restricted to the deployment's authorized CCIP
139
+ off-ramp, so another contract cannot imitate the execution event. Both
140
+ lifecycle reads verify the destination RPC chain before querying.
141
+ `getSourceStatus()` similarly verifies the source chain and returns `pending`,
142
+ `failed`, or a confirmed canonical gateway message ID. If the caller already
143
+ has a receipt, `bridgeMessageIdFromReceipt()` performs the same canonical event
144
+ check without another RPC request.
145
+
146
+ ## Execute a plan
147
+
148
+ Every plan identifies its schema version, embedded deployment version, action,
149
+ ordered steps, and deterministic hash. Transaction values and gas limits are
150
+ decimal strings so the complete plan is JSON-safe. Show the action, destination
151
+ contract, value, and approval to the user before requesting signatures. Submit
152
+ the steps in order and wait for each successful receipt before continuing.
153
+
154
+ ```ts
155
+ import { toEip1193Transaction } from '@forevermoney/sdk'
156
+
157
+ for (const step of prepared.plan.steps) {
158
+ const hash = await walletProvider.request({
159
+ method: 'eth_sendTransaction',
160
+ params: [toEip1193Transaction(step.transaction)],
161
+ })
162
+ await waitForReceipt(hash)
163
+ }
164
+ ```
165
+
166
+ Ethers consumers can pass `toEthersTransaction(step.transaction)` directly to
167
+ `Signer.sendTransaction()`. Both adapters validate the plan's decimal
168
+ quantities before conversion.
169
+
170
+ If a plan contains an approval, its later transaction intentionally has no gas
171
+ limit: that transaction cannot be simulated against pre-approval state. The
172
+ wallet should estimate it after the approval confirms.
173
+
174
+ ## Vaults
175
+
176
+ The client reads allowances for vault creation and deposits:
177
+
178
+ ```ts
179
+ const plan = await foreverMoney.vaults.prepareCreate({
180
+ owner: '0x...',
181
+ akAddress: '0x...',
182
+ poolManager: '0x...',
183
+ poolAddress: '0x...',
184
+ positionManagerImplementation: '0x...',
185
+ stashTokens: [{ token: '0x...', amount: 1_000_000n }],
186
+ })
187
+ ```
188
+
189
+ Pure builders are exported for deterministic or already-indexed workflows:
190
+
191
+ - `buildCreateVaultPlan`
192
+ - `buildDepositVaultPlan`
193
+ - `buildWithdrawVaultPlan`
194
+ - `buildClaimVaultFeesPlan`
195
+ - `buildSetVaultStakingPlan`
196
+
197
+ WETH stash entries are handled as native ETH exactly as the deployed vault
198
+ contracts expect: creation adds their amount to `msg.value`, while top-up calls
199
+ use `address(0)` plus `msg.value`. Other tokens use exact-amount approvals.
200
+
201
+ Vault manager and pool addresses are dynamic protocol data, not deployment
202
+ constants. Source them from a canonical factory receipt or the ForeverMoney
203
+ indexer and present them to the user. The SDK validates their address shape and
204
+ encodes the call; it cannot prove that an arbitrary caller-supplied manager or
205
+ pool belongs to ForeverMoney.
206
+
207
+ After confirmation, `vaultManagerFromCreationReceipt(receipt)` resolves the new
208
+ manager only from the canonical factory event. For bridge receipts,
209
+ `bridgeMessageIdFromReceipt(direction, receipt)` resolves the CCIP message ID
210
+ only from the canonical source gateway. Both return `null` when the expected
211
+ event is absent; do not infer success or submit a duplicate transaction.
212
+
213
+ ## Public API boundaries
214
+
215
+ - `createForeverMoneyClient()` owns state-dependent reads and preparation.
216
+ - `getDeliveryCheckpoint()` and `getDeliveryStatus()` own chain-verified CCIP
217
+ lifecycle reads.
218
+ - `getSourceStatus()` owns source confirmation and canonical message-ID
219
+ extraction from a transaction hash.
220
+ - Pure `build*Plan()` functions require explicit allowance and fee state and
221
+ do not read a chain.
222
+ - `toEip1193Transaction()` and `toEthersTransaction()` only convert an already
223
+ prepared transaction; they never submit it.
224
+ - `foreverMoneyDeployment` and `foreverMoneyAbis` are immutable production
225
+ metadata for Base, Robinhood, and Subtensor. There is no public manifest,
226
+ environment, address, selector, or arbitrary-chain override.
227
+ - The root package export is the supported API. Internal source modules are
228
+ not package subpaths and should not be imported by partners.
229
+
230
+ ## Production forks
231
+
232
+ A Base or Subtensor mainnet fork reports the original production chain ID and
233
+ contains the production contracts at their real addresses. Point `http()` at
234
+ the local fork RPC. Do not create a custom manifest or replace contract
235
+ addresses.
236
+
237
+ ```ts
238
+ const forkClient = createForeverMoneyClient({
239
+ transports: {
240
+ base: http('http://127.0.0.1:8545'),
241
+ subtensor: http('http://127.0.0.1:9545'),
242
+ },
243
+ })
244
+ ```
245
+
246
+ ## Errors and security
247
+
248
+ SDK failures are `ForeverMoneyError` instances with a stable `code`. Errors are
249
+ fail-closed: invalid addresses, fractional RAO, missing allowance state,
250
+ negative values, unsupported RPC schemes, and wrong chain IDs are rejected.
251
+
252
+ - Never pass private keys to an application backend or MCP server.
253
+ - The built-in HTTP transport rejects plaintext remote RPC endpoints. Plain HTTP
254
+ is accepted only for `localhost`, `127.0.0.1`, and `::1` development forks.
255
+ - Re-quote shortly before signing; CCIP fees and on-chain state change.
256
+ - Treat a transaction-plan hash as an integrity identifier, not authorization.
257
+ - Review approvals and wait for their receipts before submitting dependent
258
+ transactions.
259
+ - Confirm the wallet account and chain immediately before every signature.
260
+ - Treat a confirmed transaction with an unresolved canonical event as a
261
+ support/recovery case; never blindly resubmit it.
262
+ - Use a dedicated, low-balance wallet for production canaries.
263
+
264
+ The embedded deployment is exported as `foreverMoneyDeployment` for display and
265
+ verification. It is intentionally not replaceable through the public client
266
+ API.
267
+
268
+ ## Development
269
+
270
+ Use Node.js 22 or newer. The repository is the standalone source for the npm
271
+ package; it does not depend on the ForeverMoney website repository.
272
+
273
+ Source code is grouped by protocol responsibility while `src/index.ts` remains
274
+ the only supported package boundary:
275
+
276
+ ```text
277
+ src/
278
+ ├── abis/ Contract interfaces owned by the SDK
279
+ ├── bridge/ Bridge plans, receipt parsing, and delivery tracking
280
+ ├── chains/ Canonical production deployment metadata
281
+ ├── core/ Shared validation, transports, plans, and transaction types
282
+ ├── integration/ Production-fork integration tests
283
+ ├── vaults/ Vault plans and receipt parsing
284
+ ├── client.ts State-aware SDK client
285
+ └── index.ts Reviewed public exports
286
+ ```
287
+
288
+ ```bash
289
+ npm install
290
+ npm run verify
291
+ npm run pack:dry-run
292
+ ```
293
+
294
+ `verify` runs the offline tests, strict type checks, example checks, ESM and
295
+ CommonJS builds, and package smoke test. Production-fork and guarded real-key
296
+ testing are documented in [`docs/testing.md`](./docs/testing.md).
297
+
298
+ Security reports should follow [`SECURITY.md`](./SECURITY.md). Maintainer release
299
+ steps are in [`docs/releasing.md`](./docs/releasing.md).