@blockula/sdk 1.0.0 → 1.0.2
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/README.md +11 -14
- package/dist/index.d.ts +2 -2
- package/dist/index.js +4 -4
- package/dist/networks.d.ts +2 -0
- package/dist/networks.js +10 -7
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -11,8 +11,8 @@ npm install @blockula/sdk
|
|
|
11
11
|
```ts
|
|
12
12
|
import { Client, Wallet, encodeKeyGrant } from "@blockula/sdk";
|
|
13
13
|
|
|
14
|
-
const wallet = Wallet.generate(Client.
|
|
15
|
-
await wallet.faucet(); //
|
|
14
|
+
const wallet = Wallet.generate(Client.local()); // `blockula dev` on this machine
|
|
15
|
+
await wallet.faucet(); // dev chains: 100 FANG
|
|
16
16
|
await wallet.registerName("vlad"); // vlad.boo
|
|
17
17
|
await wallet.send("alice.boo", "1.5", { memo: "trick or treat" });
|
|
18
18
|
|
|
@@ -22,19 +22,17 @@ const agentGrantCode = encodeKeyGrant(await agent.keyGrant(wallet.account));
|
|
|
22
22
|
await wallet.addSessionKey(agentGrantCode, "claude", { limit: "10", periodBlocks: 86_400, expiresInBlocks: 30 * 86_400 });
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
For a chain you launched (`blockula summon`), pass its node: `new Client("https://<your node>", { chainId, genesisHash })`.
|
|
26
26
|
|
|
27
27
|
## Which network?
|
|
28
28
|
|
|
29
29
|
```ts
|
|
30
|
-
Client.testnet(); // the public testnet
|
|
31
|
-
new Client({ network: "testnet" }); // the same
|
|
32
|
-
new Client("http://localhost:7171", { network: "testnet" }); // your own node, held to the testnet's pins
|
|
33
30
|
Client.local(); // blockula dev on this machine
|
|
31
|
+
new Client("http://localhost:7171", { network: "testnet" }); // a blockula-testnet-2 node you run, held to its pins
|
|
34
32
|
new Client("http://<your node>:7070", { chainId: "mychain", genesisHash: "…" }); // a chain of your own
|
|
35
33
|
```
|
|
36
34
|
|
|
37
|
-
`NETWORKS` lists what each preset pins: the chain id, the genesis hash, the address prefix and the genesis clock. A client made for a network checks them on every status it reads, including the one each signature is made against, and refuses a node on another chain (`ChainMismatchError`). The prefix is known up front, so `wallet.address` is `tboo1…` on the testnet before the first request. Pins you pass as well must agree with the network's.
|
|
35
|
+
The public testnet (`blockula-testnet-2`) was retired in November 2026 and has no public node, so the `testnet` preset has no RPC: `Client.testnet()` and `new Client({ network: "testnet" })` throw unless you pass a node's URL (`Client.testnet({ rpc })`). `NETWORKS` lists what each preset pins: the chain id, the genesis hash, the address prefix and the genesis clock. A client made for a network checks them on every status it reads, including the one each signature is made against, and refuses a node on another chain (`ChainMismatchError`). The prefix is known up front, so `wallet.address` is `tboo1…` on the testnet before the first request. Pins you pass as well must agree with the network's.
|
|
38
36
|
|
|
39
37
|
A wallet signs nothing for a node that says it's on `blockula-1` unless its client pins that chain (`chainId: "blockula-1"`): it throws `MainnetNotAllowedError` instead.
|
|
40
38
|
|
|
@@ -98,16 +96,17 @@ await watchPayments(client, shopAddress, {
|
|
|
98
96
|
|
|
99
97
|
## Your own RPC node
|
|
100
98
|
|
|
101
|
-
Any node serves the same API, and one on your machine isn't rate limited. To run one
|
|
99
|
+
Any node serves the same API, and one on your machine isn't rate limited. To run one of a chain you know a node of:
|
|
102
100
|
|
|
103
101
|
```bash
|
|
104
|
-
blockula join --genesis https
|
|
102
|
+
blockula join --genesis https://<a node of the chain> --home ~/blockula-node --port 7171
|
|
105
103
|
```
|
|
106
104
|
|
|
107
105
|
```ts
|
|
106
|
+
const pins = { chainId: "mychain", genesisHash: "…" }; // a node on another chain is refused
|
|
108
107
|
const client = new Client("http://localhost:7171", {
|
|
109
|
-
|
|
110
|
-
witnesses: [new Client("https
|
|
108
|
+
...pins,
|
|
109
|
+
witnesses: [new Client("https://<another node>", pins)], // names proven against a second node
|
|
111
110
|
});
|
|
112
111
|
```
|
|
113
112
|
|
|
@@ -160,7 +159,7 @@ wallet.validForBlocks = 600;
|
|
|
160
159
|
Addresses are bech32m with the chain's prefix: `boo1…` by default, `tboo1…` on public testnets. `isAddress(s)` accepts any known prefix and `isAddress(s, "tboo")` exactly one; `addressFromPublicKey(pk, prefix)` renders one. A `Client` learns its chain's prefix from `/api/status` (`await client.addressPrefix()`), and wallets using that client sign with it (`wallet.address`, `wallet.account`). Pin it with `new Client(rpc, { prefix: "boo" })` to refuse a node on another network (see "Which chain?" below). Nodes reject addresses of another network's prefix.
|
|
161
160
|
|
|
162
161
|
- Names in a signature (audit gap-7-1). Where the chain resolves a name itself (`send`, `awardHunt`, `vouch`/`unvouch`, `grantSoul`, `flagSybil`, `clawback`, `transferName`, proposal targets), a client with `witnesses` (below) signs the name's proven owner, and one without signs the **name itself** (`alice.boo`): the chain pays whoever owns it when the tx runs, so a lying node can't point it at another account now. It could still hold the signed tx and release it later in its validity window, which counts from the height that node reported, by when the name may have lapsed and been registered again: pay a name that's about to expire by address. `Wallet.build` refuses a height more than `maxHeadLeadBlocks` (30) ahead of the lowest head the witnesses report (every witness must answer, so one honest witness is enough), or past what a `genesisClock: { createdAtMs, blockTimeMs }` pin allows by this machine's clock (`HeightAheadError`, nothing signed); without either the height is the node's word. `client.recipient(name)` says which (`signed`, `byChain`, and `how` to show before signing); the address it reports for an unproven name is the node's word, for display. Where an address is needed (`delegate`, `undelegate`, `redelegate`, `unjail`, `actAs`, `keyGrant`), a name is resolved (`client.resolution`: `proven` and `how`), and on `blockula-1` nothing is signed with one only a single node vouched for: it throws `UnprovenNameError` (checked against the very status each tx is signed for). Pass the address once its owner has confirmed it, or give the client witnesses.
|
|
163
|
-
- `wallet.address` uses the chain's prefix once the client knows it. A client made for a network (`Client.testnet()`) or pinned with `prefix` knows it up front. Otherwise it shows the `boo` form until the first request, so `await client.addressPrefix()` first if you display it.
|
|
162
|
+
- `wallet.address` uses the chain's prefix once the client knows it. A client made for a network (`Client.testnet({ rpc })`) or pinned with `prefix` knows it up front. Otherwise it shows the `boo` form until the first request, so `await client.addressPrefix()` first if you display it.
|
|
164
163
|
- Node errors throw `ApiError` (`.status`: 429 means rate limited, and `.retryAfterMs` says how long the node asked you to wait). A transaction the node reports as failed throws `TxFailedError` from `waitForTx` (`dropped` says whether it ran: see "Timeouts and pending transactions").
|
|
165
164
|
- Pass `rpId` to `createPasskeyWallet`/`unlockPasskeyWallet` to pin wallets to your production domain. Passkeys are bound to that hostname for good.
|
|
166
165
|
|
|
@@ -250,8 +249,6 @@ Every request has a timeout (`new Client(rpc, { timeoutMs })`, default 15 s, at
|
|
|
250
249
|
|
|
251
250
|
`verifyReleaseSignature(sig, checksumsFile, trustedKeyId)` checks a release's offline ML-DSA-65 signature (docs/OPERATIONS.md "Releases").
|
|
252
251
|
|
|
253
|
-
The public testnet is `https://blockula-testnet.fly.dev`, and the wallet is https://testnet.blockula.app.
|
|
254
|
-
|
|
255
252
|
Tests: `npm run test:vectors` (offline: passkey derivation, state-proof and tx-encoding vectors from the node, every protocol v1 helper against a fake node, pinning, proof freshness, timeouts, release signatures, and `test/integrators.ts`: network presets, the `blockula-1` pin, balances, the nonce queue, `sendMany`, `quote`, history, `watchPayments` and `onSigned`; `test/programs.ts` (programs: every answer check, the fuel and deposit arithmetic, receipts and balance reconciliation, `provenProgram`'s refusals one by one, and the Wallet's helpers against a fake node serving answers recorded from a dev chain), and `test/examples.ts`, which runs `examples/pay-from-a-script.mjs` against a fake node, so build first), `npm run test:kat` (ML-DSA known answers), `npm test` (end to end against `blockula dev`: `test/e2e.ts` and `test/e2e-integrators.ts`, on a chain with programs or without), `npm run test:programs` (`test/e2e-programs.ts`, which needs `blockula dev --programs`: on a chain without programs it says so and passes, and `BLOCKULA_E2E_PROGRAMS=1` makes that a failure, as CI sets it). The `examples/` directory at the repository's root has two runnable scripts: receiving payments, and paying from a script.
|
|
256
253
|
|
|
257
254
|
## Changes in 0.6
|
package/dist/index.d.ts
CHANGED
|
@@ -1258,7 +1258,7 @@ export declare class Client {
|
|
|
1258
1258
|
readonly network?: NetworkName;
|
|
1259
1259
|
/** See `ClientOptions.wireTxs`. */
|
|
1260
1260
|
readonly wireTxs: boolean;
|
|
1261
|
-
/**
|
|
1261
|
+
/** A node of blockula-testnet-2 (`NETWORKS.testnet`), every pin set: `Client.testnet({ rpc })`. The public testnet was retired and has no public node, so `rpc` is required. */
|
|
1262
1262
|
static testnet(opts?: Omit<ClientOptions, "network"> & {
|
|
1263
1263
|
rpc?: string;
|
|
1264
1264
|
}): Client;
|
|
@@ -1267,7 +1267,7 @@ export declare class Client {
|
|
|
1267
1267
|
rpc?: string;
|
|
1268
1268
|
}): Client;
|
|
1269
1269
|
/**
|
|
1270
|
-
* `new Client(rpc, opts)`, `new Client({ network: "
|
|
1270
|
+
* `new Client(rpc, opts)`, `new Client({ network: "local" })` (the network's built-in RPC), or
|
|
1271
1271
|
* `new Client(rpc, { network })` (your own node, held to that network's pins). The pins
|
|
1272
1272
|
* (`opts`, and the network's) are checked on every status: a node reporting another prefix,
|
|
1273
1273
|
* chain id or genesis hash is refused with `ChainMismatchError`.
|
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import { DEFAULT_TIP_CAP_MULTIPLE, KEY_POP_CONTEXT, PUBLIC_KEY_LEN, SIGNATURE_LE
|
|
|
6
6
|
import { NAMESPACES, checkProofResponse, decodeAccount, decodeName, decodeProgram } from "./proof.js";
|
|
7
7
|
import { ANY_PROGRAM_VERSION, MAX_PROGRAM_BINDINGS, MAX_PROGRAM_CALLS, MAX_PROGRAM_CODE_BYTES, MAX_PROGRAM_INTERFACE_BYTES, ProgramAnswerError, ProgramsOffError, defaultFuelLimit, depositWithMargin, plainText, positiveFuel, programAddressArg, programBytesArg, programHashArg, programInputArg, programMethodArg, checkCode, checkFuel, checkInterface, checkParams, checkProgram, checkProgramsStatus, checkReceipt, checkView, outcomeWords, priceFuel, programCost, programRecordDeposit, programUploadDeposit, reserveFuel, uploadFuel } from "./programs.js";
|
|
8
8
|
import { describeKind, reviewAction } from "./describe.js";
|
|
9
|
-
import { NETWORKS, isNetworkName } from "./networks.js";
|
|
9
|
+
import { NETWORKS, TESTNET_RETIRED, isNetworkName } from "./networks.js";
|
|
10
10
|
export * from "./tx.js";
|
|
11
11
|
export * from "./proof.js";
|
|
12
12
|
export * from "./distribution.js";
|
|
@@ -1987,7 +1987,7 @@ export class Client {
|
|
|
1987
1987
|
network;
|
|
1988
1988
|
/** See `ClientOptions.wireTxs`. */
|
|
1989
1989
|
wireTxs;
|
|
1990
|
-
/**
|
|
1990
|
+
/** A node of blockula-testnet-2 (`NETWORKS.testnet`), every pin set: `Client.testnet({ rpc })`. The public testnet was retired and has no public node, so `rpc` is required. */
|
|
1991
1991
|
static testnet(opts = {}) {
|
|
1992
1992
|
const { rpc, ...rest } = opts;
|
|
1993
1993
|
return new Client(rpc, { ...rest, network: "testnet" });
|
|
@@ -1998,7 +1998,7 @@ export class Client {
|
|
|
1998
1998
|
return new Client(rpc, { ...rest, network: "local" });
|
|
1999
1999
|
}
|
|
2000
2000
|
/**
|
|
2001
|
-
* `new Client(rpc, opts)`, `new Client({ network: "
|
|
2001
|
+
* `new Client(rpc, opts)`, `new Client({ network: "local" })` (the network's built-in RPC), or
|
|
2002
2002
|
* `new Client(rpc, { network })` (your own node, held to that network's pins). The pins
|
|
2003
2003
|
* (`opts`, and the network's) are checked on every status: a node reporting another prefix,
|
|
2004
2004
|
* chain id or genesis hash is refused with `ChainMismatchError`.
|
|
@@ -2017,7 +2017,7 @@ export class Client {
|
|
|
2017
2017
|
if (preset) {
|
|
2018
2018
|
url ??= preset.rpc;
|
|
2019
2019
|
if (url === undefined)
|
|
2020
|
-
throw new Error(`no public ${preset.name} RPC is built into this SDK
|
|
2020
|
+
throw new Error(preset.name === "testnet" ? TESTNET_RETIRED : `no public ${preset.name} RPC is built into this SDK: pass the URL of a ${preset.name} node you trust, new Client(url, { network: "${preset.name}" })`);
|
|
2021
2021
|
opts = withPreset(opts, preset);
|
|
2022
2022
|
}
|
|
2023
2023
|
this.rpc = url ?? DEFAULT_RPC;
|
package/dist/networks.d.ts
CHANGED
|
@@ -19,6 +19,8 @@ export interface NetworkPreset {
|
|
|
19
19
|
/** The genesis clock of `genesisHash`'s genesis: it bounds the height a tx is signed against. */
|
|
20
20
|
genesisClock?: NetworkClock;
|
|
21
21
|
}
|
|
22
|
+
/** Why `{ network: "testnet" }` needs a node URL: the public testnet has none any more. */
|
|
23
|
+
export declare const TESTNET_RETIRED = "the public testnet (blockula-testnet-2) was retired in November 2026 and has no public node: run your own chain (`blockula dev`, `blockula summon`) and pass its URL, or pass the URL of a blockula-testnet-2 node you run, new Client(url, { network: \"testnet\" })";
|
|
22
24
|
/** The networks this SDK knows: `new Client({ network })` pins what they list. */
|
|
23
25
|
export declare const NETWORKS: Readonly<Record<NetworkName, Readonly<NetworkPreset>>>;
|
|
24
26
|
/** Whether `s` names a network this SDK knows. */
|
package/dist/networks.js
CHANGED
|
@@ -1,21 +1,24 @@
|
|
|
1
1
|
// The networks this SDK knows by name, and what a client for each pins (DEV-1).
|
|
2
2
|
//
|
|
3
|
-
// `new Client({ network: "testnet" })` (or `Client.testnet()`)
|
|
4
|
-
// pins its chain id, genesis hash, address prefix
|
|
5
|
-
// it renders is already `tboo1…` and a node on
|
|
6
|
-
//
|
|
7
|
-
//
|
|
3
|
+
// `new Client("http://localhost:7171", { network: "testnet" })` (or `Client.testnet({ rpc })`)
|
|
4
|
+
// talks to your own node of blockula-testnet-2 and pins its chain id, genesis hash, address prefix
|
|
5
|
+
// and genesis clock up front, so the first address it renders is already `tboo1…` and a node on
|
|
6
|
+
// any other chain is refused before anything is signed. The public testnet was retired in November
|
|
7
|
+
// 2026 and has no public node, so no RPC is built in for it: pass your node's URL. For a chain of
|
|
8
|
+
// your own, pin it yourself: `new Client(rpc, { chainId, genesisHash })`.
|
|
8
9
|
//
|
|
9
|
-
// The testnet values match mcp/src/network.ts (`
|
|
10
|
+
// The testnet values match mcp/src/network.ts (`KNOWN_GENESIS`, `KNOWN_CLOCKS`) and
|
|
10
11
|
// web/lib/config.ts; test/integrators.ts checks the MCP's.
|
|
12
|
+
/** blockula-testnet-2, the retired public testnet: its pins, and no public RPC. */
|
|
11
13
|
const TESTNET = {
|
|
12
14
|
name: "testnet",
|
|
13
|
-
rpc: "https://blockula-testnet.fly.dev",
|
|
14
15
|
chainId: "blockula-testnet-2",
|
|
15
16
|
genesisHash: "b004e2f91b63429fc009088b16ec02b6d5388a03642699ae3dc6e325939f19b2",
|
|
16
17
|
prefix: "tboo",
|
|
17
18
|
genesisClock: { createdAtMs: 1792328803000, blockTimeMs: 1000 },
|
|
18
19
|
};
|
|
20
|
+
/** Why `{ network: "testnet" }` needs a node URL: the public testnet has none any more. */
|
|
21
|
+
export const TESTNET_RETIRED = 'the public testnet (blockula-testnet-2) was retired in November 2026 and has no public node: run your own chain (`blockula dev`, `blockula summon`) and pass its URL, or pass the URL of a blockula-testnet-2 node you run, new Client(url, { network: "testnet" })';
|
|
19
22
|
/** A `blockula dev` chain on this machine. Nothing is pinned: `--reset` makes a new chain each time. */
|
|
20
23
|
const LOCAL = { name: "local", rpc: "http://localhost:7070" };
|
|
21
24
|
const frozen = (p) => Object.freeze({ ...p, genesisClock: p.genesisClock ? Object.freeze({ ...p.genesisClock }) : undefined });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@blockula/sdk",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "\ud83e\udddb TypeScript SDK for Blockula: wallets with post-quantum signatures (ML-DSA-65), .boo names and transfers. Experimental software, provided as is under MIT.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|