@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 +25 -0
- package/LICENSE +21 -0
- package/README.md +299 -0
- package/dist/index.cjs +1789 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +311 -0
- package/dist/index.d.ts +311 -0
- package/dist/index.js +1770 -0
- package/dist/index.js.map +1 -0
- package/examples/node.ts +28 -0
- package/examples/talisman.ts +192 -0
- package/package.json +74 -0
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).
|