@arxiumlabs/sdk 0.0.0-stage → 0.4.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/README.md +124 -2
- package/dist/actions.d.ts +161 -0
- package/dist/actions.js +207 -0
- package/dist/amounts.d.ts +3 -0
- package/dist/amounts.js +5 -0
- package/dist/bech32.d.ts +9 -0
- package/dist/bech32.js +39 -0
- package/dist/bincode.d.ts +31 -0
- package/dist/bincode.js +77 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +264 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +7 -0
- package/dist/keys.d.ts +37 -0
- package/dist/keys.js +36 -0
- package/dist/policy.d.ts +53 -0
- package/dist/policy.js +116 -0
- package/dist/rpc.d.ts +65 -0
- package/dist/rpc.js +74 -0
- package/package.json +39 -4
package/dist/rpc.d.ts
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { type SignedAction } from "./actions.js";
|
|
2
|
+
import { type AccountPolicy } from "./policy.js";
|
|
3
|
+
export type RpcOptions = {
|
|
4
|
+
rpc: string;
|
|
5
|
+
token?: string;
|
|
6
|
+
fetch?: typeof globalThis.fetch;
|
|
7
|
+
};
|
|
8
|
+
export type ActionStatus = {
|
|
9
|
+
status: "pending";
|
|
10
|
+
} | {
|
|
11
|
+
status: "confirmed";
|
|
12
|
+
height: number;
|
|
13
|
+
block_hash: string;
|
|
14
|
+
sender: string;
|
|
15
|
+
nonce: number;
|
|
16
|
+
} | {
|
|
17
|
+
status: "dropped";
|
|
18
|
+
reason: string;
|
|
19
|
+
} | {
|
|
20
|
+
status: "unknown";
|
|
21
|
+
};
|
|
22
|
+
export type SendActionOptions = {
|
|
23
|
+
privateKey: CryptoKey;
|
|
24
|
+
sender: string;
|
|
25
|
+
payload: Uint8Array;
|
|
26
|
+
pollIntervalMs?: number;
|
|
27
|
+
timeoutMs?: number;
|
|
28
|
+
finalized?: boolean;
|
|
29
|
+
};
|
|
30
|
+
export declare class RpcError extends Error {
|
|
31
|
+
readonly status: number;
|
|
32
|
+
constructor(status: number, message: string);
|
|
33
|
+
}
|
|
34
|
+
export declare class ArxiumRpc {
|
|
35
|
+
readonly rpc: string;
|
|
36
|
+
private readonly token?;
|
|
37
|
+
private readonly requestFetch;
|
|
38
|
+
constructor(options: RpcOptions);
|
|
39
|
+
private request;
|
|
40
|
+
private genesis?;
|
|
41
|
+
/** This chain's genesis hash, fetched once per client; every signature binds it. */
|
|
42
|
+
genesisHash(): Promise<Uint8Array>;
|
|
43
|
+
status<T = Record<string, unknown>>(): Promise<T>;
|
|
44
|
+
account<T = Record<string, unknown>>(address: string): Promise<T>;
|
|
45
|
+
stake<T = Record<string, unknown>>(address: string): Promise<T>;
|
|
46
|
+
stakes<T = Record<string, unknown>[]>(address: string): Promise<T>;
|
|
47
|
+
blsKey<T = Record<string, unknown>>(address: string): Promise<T>;
|
|
48
|
+
submit(action: SignedAction): Promise<unknown>;
|
|
49
|
+
action(signature: string): Promise<ActionStatus>;
|
|
50
|
+
blocks<T = unknown>(from: number, to: number): Promise<T>;
|
|
51
|
+
block<T = unknown>(height: number): Promise<T>;
|
|
52
|
+
blockByHash<T = unknown>(hash: string): Promise<T>;
|
|
53
|
+
validators<T = unknown>(): Promise<T>;
|
|
54
|
+
finality<T = unknown>(): Promise<T>;
|
|
55
|
+
search<T = unknown>(query: string): Promise<T>;
|
|
56
|
+
minStake<T = unknown>(): Promise<T>;
|
|
57
|
+
actionFee<T = unknown>(): Promise<T>;
|
|
58
|
+
sendAction(options: SendActionOptions): Promise<{
|
|
59
|
+
action: SignedAction;
|
|
60
|
+
status: ActionStatus;
|
|
61
|
+
}>;
|
|
62
|
+
}
|
|
63
|
+
/** Stateful witnesses require the policy at the action's pre-state, not today's
|
|
64
|
+
* policy. This checks cryptography; circuit execution proves limits and scope. */
|
|
65
|
+
export declare function verifySignedAction(action: SignedAction, genesis: Uint8Array, preStatePolicy?: AccountPolicy): Promise<boolean>;
|
package/dist/rpc.js
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { signAction, signingBytes, submitBody, verifyMultisig } from "./actions.js";
|
|
2
|
+
import { decodeAddress, isMultisigAddress } from "./bech32.js";
|
|
3
|
+
import { asBuffer, fromHex } from "./bincode.js";
|
|
4
|
+
import { verifyPolicySignature } from "./policy.js";
|
|
5
|
+
export class RpcError extends Error {
|
|
6
|
+
status;
|
|
7
|
+
constructor(status, message) {
|
|
8
|
+
super(message);
|
|
9
|
+
this.status = status;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
export class ArxiumRpc {
|
|
13
|
+
rpc;
|
|
14
|
+
token;
|
|
15
|
+
requestFetch;
|
|
16
|
+
constructor(options) { this.rpc = options.rpc.replace(/\/+$/, ""); this.token = options.token; this.requestFetch = options.fetch ?? ((input, init) => globalThis.fetch(input, init)); } // browsers and Workers throw "Illegal invocation" if fetch is called with `this` = ArxiumRpc
|
|
17
|
+
async request(path, init) { const response = await this.requestFetch(`${this.rpc}${path}`, { ...init, headers: { ...(this.token ? { Authorization: `Bearer ${this.token}` } : {}), ...init?.headers } }); const text = await response.text(); if (!response.ok)
|
|
18
|
+
throw new RpcError(response.status, text || `${response.status} ${response.statusText}`); return text ? JSON.parse(text) : undefined; }
|
|
19
|
+
genesis;
|
|
20
|
+
/** This chain's genesis hash, fetched once per client; every signature binds it. */
|
|
21
|
+
genesisHash() { return this.genesis ??= this.request("/genesis-hash").then((r) => fromHex(r.genesis_hash.replace(/^0x/, "")), (e) => { this.genesis = undefined; throw e; }); }
|
|
22
|
+
status() { return this.request("/status"); }
|
|
23
|
+
account(address) { return this.request(`/accounts/${encodeURIComponent(address)}`); }
|
|
24
|
+
stake(address) { return this.request(`/accounts/${encodeURIComponent(address)}/stake`); }
|
|
25
|
+
stakes(address) { return this.request(`/accounts/${encodeURIComponent(address)}/stakes`); }
|
|
26
|
+
blsKey(address) { return this.request(`/accounts/${encodeURIComponent(address)}/bls-key`); }
|
|
27
|
+
submit(action) { return this.request("/actions", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(action) }); }
|
|
28
|
+
async action(signature) { try {
|
|
29
|
+
return await this.request(`/actions/${encodeURIComponent(signature)}`);
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
if (error instanceof RpcError && error.status === 404)
|
|
33
|
+
return { status: "unknown" };
|
|
34
|
+
throw error;
|
|
35
|
+
} }
|
|
36
|
+
blocks(from, to) { return this.request(`/blocks?from=${from}&to=${to}`); }
|
|
37
|
+
block(height) { return this.request(`/blocks/${height}`); }
|
|
38
|
+
blockByHash(hash) { return this.request(`/blocks/by-hash/${encodeURIComponent(hash)}`); }
|
|
39
|
+
validators() { return this.request("/validators"); }
|
|
40
|
+
finality() { return this.request("/finality"); }
|
|
41
|
+
search(query) { return this.request(`/search?q=${encodeURIComponent(query)}`); }
|
|
42
|
+
minStake() { return this.request("/min-stake"); }
|
|
43
|
+
actionFee() { return this.request("/action-fee"); }
|
|
44
|
+
async sendAction(options) { const account = await this.account(options.sender).catch((error) => { if (error instanceof RpcError && error.status === 404)
|
|
45
|
+
return { nonce: 0 }; throw error; }); const signature = await signAction(options.privateKey, await this.genesisHash(), options.sender, account.nonce, options.payload); const action = submitBody(options.sender, account.nonce, signature, options.payload); await this.submit(action); const deadline = Date.now() + (options.timeoutMs ?? 120_000); while (Date.now() < deadline) {
|
|
46
|
+
const status = await this.action(signature);
|
|
47
|
+
if (status.status === "dropped")
|
|
48
|
+
throw new Error(`action dropped: ${status.reason}`);
|
|
49
|
+
if (status.status === "confirmed") {
|
|
50
|
+
if (!options.finalized)
|
|
51
|
+
return { action, status };
|
|
52
|
+
const block = await this.block(status.height);
|
|
53
|
+
if (block.finalized)
|
|
54
|
+
return { action, status };
|
|
55
|
+
}
|
|
56
|
+
await new Promise((resolve) => setTimeout(resolve, options.pollIntervalMs ?? 1_000));
|
|
57
|
+
} throw new Error("timed out waiting for action"); }
|
|
58
|
+
}
|
|
59
|
+
/** Stateful witnesses require the policy at the action's pre-state, not today's
|
|
60
|
+
* policy. This checks cryptography; circuit execution proves limits and scope. */
|
|
61
|
+
export async function verifySignedAction(action, genesis, preStatePolicy) {
|
|
62
|
+
try {
|
|
63
|
+
const message = signingBytes(genesis, action.sender, action.nonce, Uint8Array.from(action.payload));
|
|
64
|
+
if (action.signature.length > 128 && /^a7(00|01|02)/.test(action.signature))
|
|
65
|
+
return !!preStatePolicy && await verifyPolicySignature(preStatePolicy, action.signature, message) !== null;
|
|
66
|
+
if (isMultisigAddress(action.sender))
|
|
67
|
+
return await verifyMultisig(action.sender, action.signature, message);
|
|
68
|
+
const key = await crypto.subtle.importKey("raw", asBuffer(decodeAddress(action.sender)), "Ed25519", false, ["verify"]);
|
|
69
|
+
return crypto.subtle.verify("Ed25519", key, asBuffer(fromHex(action.signature)), asBuffer(message));
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,41 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arxiumlabs/sdk",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"description": "Arxium TypeScript SDK and arx CLI",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"private": false,
|
|
7
|
+
"license": "Apache-2.0",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/Arxium-Protocol/arxium.git",
|
|
11
|
+
"directory": "sdk/ts"
|
|
12
|
+
},
|
|
13
|
+
"publishConfig": {
|
|
14
|
+
"access": "public"
|
|
15
|
+
},
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
19
|
+
"import": "./dist/index.js"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"bin": {
|
|
23
|
+
"arx": "dist/cli.js"
|
|
24
|
+
},
|
|
25
|
+
"files": [
|
|
26
|
+
"dist",
|
|
27
|
+
"!dist/*.test.*",
|
|
28
|
+
"README.md"
|
|
29
|
+
],
|
|
30
|
+
"engines": {
|
|
31
|
+
"node": ">=20"
|
|
32
|
+
},
|
|
33
|
+
"scripts": {
|
|
34
|
+
"build": "tsc -p tsconfig.json",
|
|
35
|
+
"test": "npm run build && node dist/codec.test.js && node dist/policy.test.js && node dist/cli.test.js"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@types/node": "^20.0.0",
|
|
39
|
+
"typescript": "^5.0.0"
|
|
40
|
+
}
|
|
41
|
+
}
|