@arxiumlabs/sdk 0.4.0 → 0.8.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 +69 -13
- package/dist/actions.d.ts +33 -0
- package/dist/actions.js +43 -3
- package/dist/cli.js +80 -6
- package/dist/fees.d.ts +26 -0
- package/dist/fees.js +62 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/rpc.d.ts +191 -8
- package/dist/rpc.js +105 -15
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -29,7 +29,28 @@ const rpc = new ArxiumRpc({ rpc: "https://node.example", token: process.env.ARX_
|
|
|
29
29
|
const payload = encodeTransfer("arx1...", arxToIum("1.5"));
|
|
30
30
|
```
|
|
31
31
|
|
|
32
|
-
All token amounts are `bigint`: `1 ARX = 1_000_000_000 IUM`.
|
|
32
|
+
All token amounts are `bigint`: `1 ARX = 1_000_000_000 IUM`. That includes amounts in RPC responses (`balance`, `total_supply`, `frozen_amount`, …): the node writes u128 amounts as plain JSON numbers, so the client parses them with `parseJson`, which never rounds them. `JSON.stringify` throws on a `bigint`, so pass `bigintReplacer` when you serialize a response.
|
|
33
|
+
|
|
34
|
+
### Fees
|
|
35
|
+
|
|
36
|
+
An action costs `action_fee + weight × weight_fee`. The weight is the action type's base weight plus the signed action's size in bytes, plus 5,000 when a stateful account policy signs it. `actionFee` matches the node's metering exactly, and the tests check it against weights the node itself produced:
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
const fee = actionFee(await rpc.feeParams(), sender, nonce, payload); // single-key signature
|
|
40
|
+
const fee2 = actionFee(params, sender, nonce, payload, { signatureLength: multisigSignatureLength(2, 3) }); // before a 2-of-3 multisig signs
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
`CreateToken` also pays `token_create_fee` (`rpc.actionFee()`). `ACTION_FEE` is deprecated, because it is only the flat floor.
|
|
44
|
+
|
|
45
|
+
### Reading the chain
|
|
46
|
+
|
|
47
|
+
`ArxiumRpc` returns typed responses for status, accounts, stake, assets (`listAssets`, `asset`, `assetByAlias`, `assetHolders`, `assetBalance`), attestors, validators (`validator`, `validatorPower`, `operatorValidators`), block effects, evidence and Merkle proofs. Each method takes a type parameter if you need a different shape.
|
|
48
|
+
|
|
49
|
+
### Sending
|
|
50
|
+
|
|
51
|
+
`sendAction` signs at the account's current nonce. The node's nonce doesn't count actions still in the mempool, so one client runs sends for the same sender one after another. Concurrent calls each wait their turn and never collide on a nonce. Two separate processes sending for one sender still need their own coordination. For a multisig or programmable-account sender, pass `sign(message, nonce)` instead of `privateKey` and return the combined witness.
|
|
52
|
+
|
|
53
|
+
`rpc.sendAction({ privateKey, sender, payload })` signs, submits and waits for the block. If the chain refuses the action it throws `ActionDroppedError`, whose `reason` is the node's exact text. `rpc.assets(address)` returns the account's asset balances with `transfer_eligible` and `eligibility_reason`, so an app can check the sending side before the user signs. Walkthrough: https://arxium.network/docs/sdk-quickstart
|
|
33
54
|
|
|
34
55
|
### Chain binding
|
|
35
56
|
|
|
@@ -52,6 +73,10 @@ A co-signer should see what they're signing before they sign it. `decodePayload(
|
|
|
52
73
|
|
|
53
74
|
Set the resulting address as an admin role (genesis or `SetAdmin`) or as an asset issuer (`TransferIssuer`). To rotate members, move the role to a new multisig address.
|
|
54
75
|
|
|
76
|
+
### Programmable accounts
|
|
77
|
+
|
|
78
|
+
The SDK exports bounded account-policy codecs and stateful owner/session/guardian witness helpers. `arx multisig build|sign|combine|link` lets each member sign separately, for both immutable and stateful account addresses. To verify a historical action, `verifySignedAction` needs the proven pre-action policy for stateful witnesses, and `arx verify SIGNATURE --policy FILE` accepts that policy. See [the programmable-account guide](../../docs/Programmable_Accounts.md).
|
|
79
|
+
|
|
55
80
|
## Key files
|
|
56
81
|
|
|
57
82
|
`arx` reads and writes Console's encrypted export JSON unchanged:
|
|
@@ -100,26 +125,57 @@ arx query search arx1address
|
|
|
100
125
|
arx verify signature
|
|
101
126
|
```
|
|
102
127
|
|
|
103
|
-
Supported signed actions are `transfer`, `stake`, `unstake`, `join-validator`, `leave-validator`, `register-bls-key`, `authorize-operator`, `revoke-operator`, `grant-attestation`, `revoke-attestation`, `register-asset`, `issue-asset`, `transfer-asset`, `apply-attestor`, `block-attestor`, `propose-add-attestor`, `propose-remove-attestor`, `propose-unblock-attestor`, `vote` and `execute-proposal
|
|
128
|
+
Supported signed actions are `transfer`, `stake`, `unstake`, `join-validator`, `leave-validator`, `register-bls-key`, `authorize-operator`, `revoke-operator`, `grant-attestation`, `revoke-attestation`, `register-asset`, `issue-asset`, `transfer-asset`, `apply-attestor`, `block-attestor`, `propose-add-attestor`, `propose-remove-attestor`, `propose-unblock-attestor`, `vote` and `execute-proposal`, plus the issuer, corporate-action and token commands below. Amount arguments are raw base-unit integers (IUM for ARX, the asset's smallest unit for an asset).
|
|
104
129
|
|
|
105
130
|
```sh
|
|
106
131
|
arx send apply-attestor "Arxium Bank" <owner>,<owner>,<owner> 2 <evidence-hash> <evidence-uri> # sender is the 2-of-N multisig; validators vote
|
|
107
132
|
arx send propose-remove-attestor <attestor> "reason" # validators only; blocks that attestor's grants while the vote runs
|
|
108
133
|
arx send vote <proposal-id> yes # then, after the window: arx send execute-proposal <proposal-id>
|
|
109
134
|
arx send block-attestor # attestor emergency stop; only a governance vote lifts it
|
|
110
|
-
arx send register-asset fund-a FUNDA "Fund A" 0 kyc CH,LI # <asset-id> <symbol> <name> <decimals> [topics] [jurisdictions]
|
|
135
|
+
arx send register-asset fund-a FUNDA "Fund A" 0 kyc CH,LI # <asset-id> <symbol> <name> <decimals> [topics] [jurisdictions] [controls]
|
|
136
|
+
arx send register-asset fund-b FUNDB "Fund B" 0 - - freeze_asset,recover_holder # only these issuer controls, forever
|
|
111
137
|
arx send issue-asset arxasset1... 1000
|
|
112
138
|
arx send transfer-asset arxasset1... arx1recipient 100
|
|
139
|
+
arx send set-asset-limits arxasset1... - 150 - # <max-holders|-> [max-balance-per-holder|-] [max-attestation-age-blocks|-]; - = no limit
|
|
140
|
+
arx send freeze-holder arxasset1... arx1holder # unfreeze-holder to lift
|
|
141
|
+
arx send freeze-asset arxasset1... "reason" # unfreeze-asset arxasset1... "reason" to lift
|
|
142
|
+
arx send issue-asset-to arxasset1... arx1holder 100
|
|
143
|
+
arx send lock-holder-amount arxasset1... arx1holder 50 [until-height] # unlock-holder-amount to release
|
|
144
|
+
arx send forced-transfer arxasset1... arx1from arx1to 10 "court order"
|
|
145
|
+
arx send recover-holder arxasset1... arx1lost arx1replacement
|
|
146
|
+
arx send burn-asset arxasset1... 10
|
|
147
|
+
arx send lock-issuance arxasset1... # one-way
|
|
148
|
+
arx send transfer-issuer arxasset1... arx1newissuer
|
|
149
|
+
arx send set-metadata-uri arxasset1... ipfs://... # - clears it
|
|
150
|
+
arx send set-private-claims arxasset1... on|off
|
|
151
|
+
arx send snapshot-holders arxasset1... # then, by the snapshot height:
|
|
152
|
+
arx send distribute arxasset1... <height> arxasset1payout... 1000
|
|
153
|
+
arx send redeem-holders arxasset1... <height>
|
|
154
|
+
arx send split-asset arxasset1... <height> 2 1
|
|
155
|
+
arx send create-token ABC "Abc Token" 6 1000000 - mintable # <max-supply|-> mintable|fixed
|
|
156
|
+
arx send mint-token arxasset1... arx1to 100 # transfer-token, burn-token, renounce-mint
|
|
157
|
+
|
|
158
|
+
arx query assets [arx1address] # all assets, or one account's balances
|
|
159
|
+
arx query asset arxasset1...
|
|
160
|
+
arx query holders arxasset1...
|
|
161
|
+
arx query balance arx1address arxasset1...
|
|
162
|
+
arx query attestors # attestor <address>
|
|
163
|
+
arx query validator arx1address
|
|
164
|
+
arx query stakes arx1address
|
|
165
|
+
arx query fee
|
|
113
166
|
```
|
|
114
167
|
|
|
115
168
|
Builder docs (devnet quickstart, RWA walkthrough, arx-verify): https://arxium.network/docs
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
169
|
+
## Release validation
|
|
170
|
+
|
|
171
|
+
CI builds and installs the packed SDK on pull requests. Run `npm ci`,
|
|
172
|
+
`npm run test:release`, `npm test`, then `npm run check:package` locally.
|
|
173
|
+
The package check installs the tarball in a temporary directory, imports its
|
|
174
|
+
public entry point and checks the CLI version; it makes no chain calls.
|
|
175
|
+
|
|
176
|
+
Publication runs only for an approved `sdk-vX.Y.Z` tag matching package.json.
|
|
177
|
+
The publish job builds its own checkout, checks the packed files and smoke-tests
|
|
178
|
+
the tarball, then publishes that exact tarball with provenance. Existing npm
|
|
179
|
+
versions cannot be replaced. npm trusted-publisher configuration and release
|
|
180
|
+
authorization are still required; a passing package check is not publication
|
|
181
|
+
or on-chain compatibility proof.
|
package/dist/actions.d.ts
CHANGED
|
@@ -23,7 +23,15 @@ export declare const ACTION_VARIANT: {
|
|
|
23
23
|
readonly issuerForcedTransfer: 22;
|
|
24
24
|
readonly recoverHolder: 23;
|
|
25
25
|
readonly issueAssetTo: 24;
|
|
26
|
+
readonly lockIssuance: 25;
|
|
27
|
+
readonly transferIssuer: 26;
|
|
28
|
+
readonly setAssetMetadataUri: 27;
|
|
26
29
|
readonly setAssetLimits: 28;
|
|
30
|
+
readonly lockHolderAmountUntil: 29;
|
|
31
|
+
readonly snapshotHolders: 33;
|
|
32
|
+
readonly distributeToHolders: 34;
|
|
33
|
+
readonly redeemHolders: 35;
|
|
34
|
+
readonly splitAsset: 36;
|
|
27
35
|
readonly verifyClaimProof: 37;
|
|
28
36
|
readonly setPrivateClaims: 38;
|
|
29
37
|
readonly submitProposal: 30;
|
|
@@ -58,6 +66,13 @@ declare const TOPIC: {
|
|
|
58
66
|
};
|
|
59
67
|
export type AssetClass = keyof typeof CLASS;
|
|
60
68
|
export type ClaimTopic = keyof typeof TOPIC;
|
|
69
|
+
/** `IssuerControls` field order — positional bincode, append only. Fixed for the asset's life once registered. */
|
|
70
|
+
export declare const ISSUER_CONTROLS: readonly ["freeze_asset", "freeze_holder", "lock_holder_amount", "forced_transfer", "recover_holder", "redeem", "split", "set_limits", "set_metadata_uri"];
|
|
71
|
+
export type IssuerControl = (typeof ISSUER_CONTROLS)[number];
|
|
72
|
+
export type IssuerControls = Record<IssuerControl, boolean>;
|
|
73
|
+
/** Every power on, the chain's default (what assets had before controls were selectable). */
|
|
74
|
+
export declare const ALL_ISSUER_CONTROLS: IssuerControls;
|
|
75
|
+
/** `controls` omitted means every power on, matching the node's default. */
|
|
61
76
|
export type AssetMetadata = {
|
|
62
77
|
asset_class: AssetClass;
|
|
63
78
|
decimals: number;
|
|
@@ -67,7 +82,9 @@ export type AssetMetadata = {
|
|
|
67
82
|
metadata_uri: string | null;
|
|
68
83
|
symbol: string;
|
|
69
84
|
name: string;
|
|
85
|
+
controls?: IssuerControls;
|
|
70
86
|
};
|
|
87
|
+
/** @deprecated The flat floor only. The node charges `action_fee + weight × weight_fee`: use `actionFee(await rpc.feeParams(), …)`. */
|
|
71
88
|
export declare const ACTION_FEE = 1000000n;
|
|
72
89
|
export declare function encodeTransfer(to: string, amount: bigint): Uint8Array;
|
|
73
90
|
export declare function encodeJoinValidator(validator: string, stake: bigint, blsPubkey: Uint8Array, blsPop: Uint8Array): Uint8Array;
|
|
@@ -89,6 +106,22 @@ export declare function encodeLockHolderAmount(asset: string, holder: string, am
|
|
|
89
106
|
export declare function encodeIssuerForcedTransfer(asset: string, from: string, to: string, amount: bigint, reason: string): Uint8Array;
|
|
90
107
|
export declare function encodeRecoverHolder(asset: string, lost: string, replacement: string): Uint8Array;
|
|
91
108
|
export declare function encodeIssueAssetTo(asset: string, to: string, amount: bigint): Uint8Array;
|
|
109
|
+
/** One-way: the issuer gives up minting and `max_supply` is pinned to the current supply. */
|
|
110
|
+
export declare function encodeLockIssuance(asset: string): Uint8Array;
|
|
111
|
+
/** Hands every issuer right over `asset` to `newIssuer` (key rotation, compromise remedy). */
|
|
112
|
+
export declare function encodeTransferIssuer(asset: string, newIssuer: string): Uint8Array;
|
|
113
|
+
/** `null` clears the link. */
|
|
114
|
+
export declare function encodeSetAssetMetadataUri(asset: string, metadataUri: string | null): Uint8Array;
|
|
115
|
+
/** A holding period: the lock releases itself at `untilHeight`. */
|
|
116
|
+
export declare function encodeLockHolderAmountUntil(asset: string, holder: string, amount: bigint, untilHeight: bigint): Uint8Array;
|
|
117
|
+
/** Records the record-date cap table; the corporate actions below name it by its block height. */
|
|
118
|
+
export declare function encodeSnapshotHolders(asset: string): Uint8Array;
|
|
119
|
+
/** Dividend/coupon: pays `total` of `payoutAsset` pro rata to the snapshot at `snapshotHeight`. */
|
|
120
|
+
export declare function encodeDistributeToHolders(asset: string, snapshotHeight: bigint, payoutAsset: string, total: bigint): Uint8Array;
|
|
121
|
+
/** Forces every snapshot balance back to the issuer. Send the proceeds with `encodeDistributeToHolders` first. */
|
|
122
|
+
export declare function encodeRedeemHolders(asset: string, snapshotHeight: bigint): Uint8Array;
|
|
123
|
+
/** Each snapshot holding `b` becomes `b * numerator / denominator` (split if >1, consolidation if <1). */
|
|
124
|
+
export declare function encodeSplitAsset(asset: string, snapshotHeight: bigint, numerator: bigint, denominator: bigint): Uint8Array;
|
|
92
125
|
/** Sets issuer-controlled investor, concentration, and attestation-age limits. `null` clears each limit. */
|
|
93
126
|
/** zk-KYC: a holder's private eligibility proof. `sub` is a fixed 32-byte array on the node, so it rides raw (no length prefix); `proof` is length-prefixed. */
|
|
94
127
|
export declare function encodeVerifyClaimProof(asset: string, sub: Uint8Array, todayDays: number, proof: Uint8Array): Uint8Array;
|
package/dist/actions.js
CHANGED
|
@@ -8,7 +8,9 @@ export const ACTION_VARIANT = {
|
|
|
8
8
|
registerAsset: 12, issueAsset: 13, transferAsset: 14, freezeAsset: 16, unfreezeAsset: 17,
|
|
9
9
|
burnAsset: 18, setHolderFrozen: 19, lockHolderAmount: 20, unlockHolderAmount: 21,
|
|
10
10
|
issuerForcedTransfer: 22, recoverHolder: 23, issueAssetTo: 24,
|
|
11
|
-
|
|
11
|
+
lockIssuance: 25, transferIssuer: 26, setAssetMetadataUri: 27, setAssetLimits: 28, lockHolderAmountUntil: 29,
|
|
12
|
+
snapshotHolders: 33, distributeToHolders: 34, redeemHolders: 35, splitAsset: 36,
|
|
13
|
+
verifyClaimProof: 37, setPrivateClaims: 38,
|
|
12
14
|
submitProposal: 30, voteProposal: 31, executeProposal: 32,
|
|
13
15
|
applyAttestor: 41, blockAttestor: 42,
|
|
14
16
|
};
|
|
@@ -17,6 +19,11 @@ export const TOKEN_ACTION = 39;
|
|
|
17
19
|
export const TOKEN_VARIANT = { createToken: 0, mintToken: 1, transferToken: 2, burnToken: 3, renounceMint: 4 };
|
|
18
20
|
const CLASS = { other: 0, real_estate: 1, equity: 2, bond: 3, stablecoin: 4, commodity: 5, token: 6 };
|
|
19
21
|
const TOPIC = { kyc: 0, aml: 1, accredited: 2, jurisdiction: 3 };
|
|
22
|
+
/** `IssuerControls` field order — positional bincode, append only. Fixed for the asset's life once registered. */
|
|
23
|
+
export const ISSUER_CONTROLS = ["freeze_asset", "freeze_holder", "lock_holder_amount", "forced_transfer", "recover_holder", "redeem", "split", "set_limits", "set_metadata_uri"];
|
|
24
|
+
/** Every power on, the chain's default (what assets had before controls were selectable). */
|
|
25
|
+
export const ALL_ISSUER_CONTROLS = Object.fromEntries(ISSUER_CONTROLS.map((c) => [c, true]));
|
|
26
|
+
/** @deprecated The flat floor only. The node charges `action_fee + weight × weight_fee`: use `actionFee(await rpc.feeParams(), …)`. */
|
|
20
27
|
export const ACTION_FEE = 1000000n;
|
|
21
28
|
export function encodeTransfer(to, amount) { return new Writer().varint(ACTION_VARIANT.transfer).string(to).varint(amount).bytes(); }
|
|
22
29
|
export function encodeJoinValidator(validator, stake, blsPubkey, blsPop) { return new Writer().varint(ACTION_VARIANT.joinValidator).string(validator).varint(stake).vec(Array.from(blsPubkey), (w, b) => w.u8(b)).vec(Array.from(blsPop), (w, b) => w.u8(b)).bytes(); }
|
|
@@ -28,7 +35,8 @@ export function encodeAuthorizeOperator(operator) { return new Writer().varint(A
|
|
|
28
35
|
export function encodeRevokeOperator() { return new Writer().varint(ACTION_VARIANT.revokeOperator).bytes(); }
|
|
29
36
|
export function encodeGrantAttestation(subject, hash, topics, jurisdiction) { return new Writer().varint(ACTION_VARIANT.grantAttestation).string(subject).string(hash).vec(topics, (w, topic) => w.varint(TOPIC[topic])).option(jurisdiction, (w, value) => w.string(value)).bytes(); }
|
|
30
37
|
export function encodeRevokeAttestation(subject) { return new Writer().varint(ACTION_VARIANT.revokeAttestation).string(subject).bytes(); }
|
|
31
|
-
export function encodeRegisterAsset(assetId, complianceRequired, metadata) {
|
|
38
|
+
export function encodeRegisterAsset(assetId, complianceRequired, metadata) { const w = new Writer().varint(ACTION_VARIANT.registerAsset).string(assetId).bool(complianceRequired).varint(CLASS[metadata.asset_class]).u8(metadata.decimals).vec(metadata.required_claims, (w, topic) => w.varint(TOPIC[topic])).option(metadata.allowed_jurisdictions, (w, codes) => w.vec(codes, (ww, code) => ww.string(code))).option(metadata.max_supply, (w, cap) => w.varint(BigInt(cap))).option(metadata.metadata_uri, (w, uri) => w.string(uri)).string(metadata.symbol).string(metadata.name); const controls = metadata.controls ?? ALL_ISSUER_CONTROLS; for (const c of ISSUER_CONTROLS)
|
|
39
|
+
w.bool(controls[c]); return w.bytes(); }
|
|
32
40
|
export function encodeIssueAsset(asset, amount) { return new Writer().varint(ACTION_VARIANT.issueAsset).string(asset).varint(amount).bytes(); }
|
|
33
41
|
export function encodeTransferAsset(asset, to, amount) { return new Writer().varint(ACTION_VARIANT.transferAsset).string(asset).string(to).varint(amount).bytes(); }
|
|
34
42
|
export function encodeFreezeAsset(asset, frozen, reason) { return new Writer().varint(frozen ? ACTION_VARIANT.freezeAsset : ACTION_VARIANT.unfreezeAsset).string(asset).string(reason).bytes(); }
|
|
@@ -38,6 +46,22 @@ export function encodeLockHolderAmount(asset, holder, amount, lock) { return new
|
|
|
38
46
|
export function encodeIssuerForcedTransfer(asset, from, to, amount, reason) { return new Writer().varint(ACTION_VARIANT.issuerForcedTransfer).string(asset).string(from).string(to).varint(amount).string(reason).bytes(); }
|
|
39
47
|
export function encodeRecoverHolder(asset, lost, replacement) { return new Writer().varint(ACTION_VARIANT.recoverHolder).string(asset).string(lost).string(replacement).bytes(); }
|
|
40
48
|
export function encodeIssueAssetTo(asset, to, amount) { return new Writer().varint(ACTION_VARIANT.issueAssetTo).string(asset).string(to).varint(amount).bytes(); }
|
|
49
|
+
/** One-way: the issuer gives up minting and `max_supply` is pinned to the current supply. */
|
|
50
|
+
export function encodeLockIssuance(asset) { return new Writer().varint(ACTION_VARIANT.lockIssuance).string(asset).bytes(); }
|
|
51
|
+
/** Hands every issuer right over `asset` to `newIssuer` (key rotation, compromise remedy). */
|
|
52
|
+
export function encodeTransferIssuer(asset, newIssuer) { return new Writer().varint(ACTION_VARIANT.transferIssuer).string(asset).string(newIssuer).bytes(); }
|
|
53
|
+
/** `null` clears the link. */
|
|
54
|
+
export function encodeSetAssetMetadataUri(asset, metadataUri) { return new Writer().varint(ACTION_VARIANT.setAssetMetadataUri).string(asset).option(metadataUri, (w, uri) => w.string(uri)).bytes(); }
|
|
55
|
+
/** A holding period: the lock releases itself at `untilHeight`. */
|
|
56
|
+
export function encodeLockHolderAmountUntil(asset, holder, amount, untilHeight) { return new Writer().varint(ACTION_VARIANT.lockHolderAmountUntil).string(asset).string(holder).varint(amount).varint(untilHeight).bytes(); }
|
|
57
|
+
/** Records the record-date cap table; the corporate actions below name it by its block height. */
|
|
58
|
+
export function encodeSnapshotHolders(asset) { return new Writer().varint(ACTION_VARIANT.snapshotHolders).string(asset).bytes(); }
|
|
59
|
+
/** Dividend/coupon: pays `total` of `payoutAsset` pro rata to the snapshot at `snapshotHeight`. */
|
|
60
|
+
export function encodeDistributeToHolders(asset, snapshotHeight, payoutAsset, total) { return new Writer().varint(ACTION_VARIANT.distributeToHolders).string(asset).varint(snapshotHeight).string(payoutAsset).varint(total).bytes(); }
|
|
61
|
+
/** Forces every snapshot balance back to the issuer. Send the proceeds with `encodeDistributeToHolders` first. */
|
|
62
|
+
export function encodeRedeemHolders(asset, snapshotHeight) { return new Writer().varint(ACTION_VARIANT.redeemHolders).string(asset).varint(snapshotHeight).bytes(); }
|
|
63
|
+
/** Each snapshot holding `b` becomes `b * numerator / denominator` (split if >1, consolidation if <1). */
|
|
64
|
+
export function encodeSplitAsset(asset, snapshotHeight, numerator, denominator) { return new Writer().varint(ACTION_VARIANT.splitAsset).string(asset).varint(snapshotHeight).varint(numerator).varint(denominator).bytes(); }
|
|
41
65
|
/** Sets issuer-controlled investor, concentration, and attestation-age limits. `null` clears each limit. */
|
|
42
66
|
/** zk-KYC: a holder's private eligibility proof. `sub` is a fixed 32-byte array on the node, so it rides raw (no length prefix); `proof` is length-prefixed. */
|
|
43
67
|
export function encodeVerifyClaimProof(asset, sub, todayDays, proof) { if (sub.length !== 32)
|
|
@@ -96,6 +120,14 @@ export function encodePayload({ name, input }) {
|
|
|
96
120
|
case "issuerForcedTransfer": return encodeIssuerForcedTransfer(input.asset, input.from, input.to, BigInt(input.amount), input.reason);
|
|
97
121
|
case "recoverHolder": return encodeRecoverHolder(input.asset, input.lost, input.replacement);
|
|
98
122
|
case "issueAssetTo": return encodeIssueAssetTo(input.asset, input.to, BigInt(input.amount));
|
|
123
|
+
case "lockIssuance": return encodeLockIssuance(input.asset);
|
|
124
|
+
case "transferIssuer": return encodeTransferIssuer(input.asset, input.newIssuer);
|
|
125
|
+
case "setAssetMetadataUri": return encodeSetAssetMetadataUri(input.asset, input.metadataUri);
|
|
126
|
+
case "lockHolderAmountUntil": return encodeLockHolderAmountUntil(input.asset, input.holder, BigInt(input.amount), BigInt(input.untilHeight));
|
|
127
|
+
case "snapshotHolders": return encodeSnapshotHolders(input.asset);
|
|
128
|
+
case "distributeToHolders": return encodeDistributeToHolders(input.asset, BigInt(input.snapshotHeight), input.payoutAsset, BigInt(input.total));
|
|
129
|
+
case "redeemHolders": return encodeRedeemHolders(input.asset, BigInt(input.snapshotHeight));
|
|
130
|
+
case "splitAsset": return encodeSplitAsset(input.asset, BigInt(input.snapshotHeight), BigInt(input.numerator), BigInt(input.denominator));
|
|
99
131
|
case "setAssetLimits": return encodeSetAssetLimits(input.asset, input.maxHolders, nullableBig(input.maxBalancePerHolder), nullableBig(input.maxAttestationAge));
|
|
100
132
|
case "verifyClaimProof": return encodeVerifyClaimProof(input.asset, Uint8Array.from(input.sub), input.todayDays, Uint8Array.from(input.proof));
|
|
101
133
|
case "setPrivateClaims": return encodeSetPrivateClaims(input.asset, input.enabled);
|
|
@@ -142,7 +174,7 @@ export function decodePayload(payload) {
|
|
|
142
174
|
revokeOperator: () => ({}),
|
|
143
175
|
grantAttestation: () => ({ subject: str(), hash: str(), topics: topics(), jurisdiction: r.option(str) }),
|
|
144
176
|
revokeAttestation: () => ({ subject: str() }),
|
|
145
|
-
registerAsset: () => ({ assetId: str(), complianceRequired: r.bool(), metadata: { asset_class: known(CLASS_NAME[Number(r.varint())], "asset class"), decimals: r.u8(), required_claims: topics(), allowed_jurisdictions: r.option((rr) => rr.vec(str)), max_supply: r.option(amount), metadata_uri: r.option(str), symbol: str(), name: str() } }),
|
|
177
|
+
registerAsset: () => ({ assetId: str(), complianceRequired: r.bool(), metadata: { asset_class: known(CLASS_NAME[Number(r.varint())], "asset class"), decimals: r.u8(), required_claims: topics(), allowed_jurisdictions: r.option((rr) => rr.vec(str)), max_supply: r.option(amount), metadata_uri: r.option(str), symbol: str(), name: str(), controls: Object.fromEntries(ISSUER_CONTROLS.map((c) => [c, r.bool()])) } }),
|
|
146
178
|
issueAsset: () => ({ asset: str(), amount: amount() }),
|
|
147
179
|
transferAsset: () => ({ asset: str(), to: str(), amount: amount() }),
|
|
148
180
|
freezeAsset: () => ({ asset: str(), frozen: true, reason: str() }),
|
|
@@ -154,6 +186,14 @@ export function decodePayload(payload) {
|
|
|
154
186
|
issuerForcedTransfer: () => ({ asset: str(), from: str(), to: str(), amount: amount(), reason: str() }),
|
|
155
187
|
recoverHolder: () => ({ asset: str(), lost: str(), replacement: str() }),
|
|
156
188
|
issueAssetTo: () => ({ asset: str(), to: str(), amount: amount() }),
|
|
189
|
+
lockIssuance: () => ({ asset: str() }),
|
|
190
|
+
transferIssuer: () => ({ asset: str(), newIssuer: str() }),
|
|
191
|
+
setAssetMetadataUri: () => ({ asset: str(), metadataUri: r.option(str) }),
|
|
192
|
+
lockHolderAmountUntil: () => ({ asset: str(), holder: str(), amount: amount(), untilHeight: amount() }),
|
|
193
|
+
snapshotHolders: () => ({ asset: str() }),
|
|
194
|
+
distributeToHolders: () => ({ asset: str(), snapshotHeight: amount(), payoutAsset: str(), total: amount() }),
|
|
195
|
+
redeemHolders: () => ({ asset: str(), snapshotHeight: amount() }),
|
|
196
|
+
splitAsset: () => ({ asset: str(), snapshotHeight: amount(), numerator: amount(), denominator: amount() }),
|
|
157
197
|
setAssetLimits: () => ({ asset: str(), maxHolders: r.option((rr) => Number(rr.varint())), maxBalancePerHolder: r.option(amount), maxAttestationAge: r.option(amount) }),
|
|
158
198
|
verifyClaimProof: () => ({ asset: str(), sub: Array.from({ length: 32 }, () => r.u8()), todayDays: Number(r.varint()), proof: bytes() }),
|
|
159
199
|
setPrivateClaims: () => ({ asset: str(), enabled: r.bool() }),
|
package/dist/cli.js
CHANGED
|
@@ -3,10 +3,10 @@ import { readFile, writeFile } from "node:fs/promises";
|
|
|
3
3
|
import { basename } from "node:path";
|
|
4
4
|
import { stdin as input, stderr, stdout } from "node:process";
|
|
5
5
|
import { createPrivateKey, createPublicKey } from "node:crypto";
|
|
6
|
-
import { ArxiumRpc, RpcError, encodeApplyAttestor, encodeBlockAttestor, encodeExecuteProposal, encodeSubmitProposal, encodeVoteProposal, encodeAuthorizeOperator, encodeGrantAttestation, encodeIssueAsset, encodeRegisterAsset, encodeTransferAsset, encodeJoinValidator, encodeLeaveValidator, encodeRegisterBlsKey, encodeRevokeAttestation, encodeRevokeOperator, encodeStake, encodeTransfer, encodeUnstake, generateKey, isKeyFile, signAction, submitBody, unlockKey, verifyKeyFile, verifySignedAction, wrapPkcs8, encodeAddress, toHex, toKeyFile, PKCS8_ED25519_PREFIX, assemblePolicySignature, decodePayload, encodePayload, fromHex, multisigAddress, multisigSignature, signingBytes, asBuffer, } from "./index.js";
|
|
6
|
+
import { ArxiumRpc, RpcError, encodeApplyAttestor, encodeBlockAttestor, encodeExecuteProposal, encodeSubmitProposal, encodeVoteProposal, encodeAuthorizeOperator, encodeGrantAttestation, encodeIssueAsset, encodeRegisterAsset, encodeFreezeAsset, encodeSetHolderFrozen, encodeSetAssetLimits, encodeTransferAsset, encodeJoinValidator, encodeLeaveValidator, encodeRegisterBlsKey, encodeRevokeAttestation, encodeRevokeOperator, encodeStake, encodeTransfer, encodeUnstake, generateKey, isKeyFile, signAction, submitBody, unlockKey, verifyKeyFile, verifySignedAction, wrapPkcs8, encodeAddress, toHex, toKeyFile, PKCS8_ED25519_PREFIX, assemblePolicySignature, decodePayload, encodePayload, fromHex, multisigAddress, multisigSignature, signingBytes, asBuffer, ALL_ISSUER_CONTROLS, ISSUER_CONTROLS, bigintReplacer, encodeBurnAsset, encodeIssuerForcedTransfer, encodeRecoverHolder, encodeIssueAssetTo, encodeLockHolderAmount, encodeLockHolderAmountUntil, encodeLockIssuance, encodeTransferIssuer, encodeSetAssetMetadataUri, encodeSetPrivateClaims, encodeSnapshotHolders, encodeDistributeToHolders, encodeRedeemHolders, encodeSplitAsset, encodeCreateToken, encodeMintToken, encodeTransferToken, encodeBurnToken, encodeRenounceMint, } from "./index.js";
|
|
7
7
|
class UsageError extends Error {
|
|
8
8
|
}
|
|
9
|
-
function output(value, json) { stdout.write(json
|
|
9
|
+
function output(value, json) { stdout.write(!json && typeof value === "string" ? `${value}\n` : `${JSON.stringify(value, bigintReplacer, 2)}\n`); }
|
|
10
10
|
function parseGlobals(args) { let rpc = process.env.ARX_RPC ?? "http://127.0.0.1:8080", token = process.env.ARX_TOKEN, key = process.env.ARX_KEY, json = false, nonce; const rest = []; for (let i = 0; i < args.length; i++) {
|
|
11
11
|
const arg = args[i];
|
|
12
12
|
if (arg === "--rpc")
|
|
@@ -95,13 +95,77 @@ function payload(action, args) { const topics = (list) => list ? list.split(",")
|
|
|
95
95
|
case "register-asset": {
|
|
96
96
|
const [assetId, symbol, name, decimals] = args;
|
|
97
97
|
if (!assetId || !symbol || !name || !/^\d+$/.test(decimals ?? ""))
|
|
98
|
-
throwUsage("register-asset needs <asset-id> <symbol> <name> <decimals> [topics] [jurisdictions]");
|
|
99
|
-
return encodeRegisterAsset(assetId, false, { asset_class: "other", decimals: Number(decimals), required_claims: topics(args[4]), allowed_jurisdictions: args[5] ? args[5].split(",") : null, max_supply: null, metadata_uri: null, symbol, name });
|
|
98
|
+
throwUsage("register-asset needs <asset-id> <symbol> <name> <decimals> [topics|-] [jurisdictions|-] [controls: all|none|freeze_asset,forced_transfer,...]");
|
|
99
|
+
return encodeRegisterAsset(assetId, false, { asset_class: "other", decimals: Number(decimals), required_claims: args[4] === "-" ? [] : topics(args[4]), allowed_jurisdictions: args[5] && args[5] !== "-" ? args[5].split(",") : null, max_supply: null, metadata_uri: null, symbol, name, controls: controlsArg(args[6]) });
|
|
100
100
|
}
|
|
101
101
|
case "issue-asset": return encodeIssueAsset(args[0] ?? throwUsage("issue-asset needs <asset-ref> <amount>"), amount(1));
|
|
102
102
|
case "transfer-asset": return encodeTransferAsset(args[0] ?? throwUsage("transfer-asset needs <asset-ref> <to> <amount>"), args[1] ?? throwUsage("transfer-asset needs <asset-ref> <to> <amount>"), amount(2));
|
|
103
|
-
|
|
103
|
+
case "set-asset-limits": {
|
|
104
|
+
const asset = args[0] ?? throwUsage("set-asset-limits needs <asset-ref> <max-holders|-> [max-balance-per-holder|-] [max-attestation-age-blocks|-]");
|
|
105
|
+
const opt = (index) => { const value = args[index]; if (value === undefined || value === "-")
|
|
106
|
+
return null; if (!/^\d+$/.test(value))
|
|
107
|
+
throwUsage("limits must be integers, or - for no limit"); return BigInt(value); };
|
|
108
|
+
const holders = opt(1);
|
|
109
|
+
return encodeSetAssetLimits(asset, holders === null ? null : Number(holders), opt(2), opt(3));
|
|
110
|
+
}
|
|
111
|
+
case "freeze-asset":
|
|
112
|
+
case "unfreeze-asset": return encodeFreezeAsset(args[0] ?? throwUsage(`${action} needs <asset-ref> <reason>`), action === "freeze-asset", args[1] ?? throwUsage(`${action} needs <asset-ref> <reason>`));
|
|
113
|
+
case "freeze-holder":
|
|
114
|
+
case "unfreeze-holder": return encodeSetHolderFrozen(args[0] ?? throwUsage(`${action} needs <asset-ref> <holder>`), args[1] ?? throwUsage(`${action} needs <asset-ref> <holder>`), action === "freeze-holder");
|
|
115
|
+
default: return morePayload(action, args);
|
|
104
116
|
} }
|
|
117
|
+
/** Issuer, corporate and token actions. `<…>` arguments are required; amounts and heights are raw integers. */
|
|
118
|
+
const USAGE = {
|
|
119
|
+
"burn-asset": "<asset-ref> <amount>", "forced-transfer": "<asset-ref> <from> <to> <amount> <reason>", "recover-holder": "<asset-ref> <lost> <replacement>",
|
|
120
|
+
"issue-asset-to": "<asset-ref> <to> <amount>", "lock-holder-amount": "<asset-ref> <holder> <amount> [until-height]", "unlock-holder-amount": "<asset-ref> <holder> <amount>",
|
|
121
|
+
"lock-issuance": "<asset-ref>", "transfer-issuer": "<asset-ref> <new-issuer>", "set-metadata-uri": "<asset-ref> <uri|->", "set-private-claims": "<asset-ref> on|off",
|
|
122
|
+
"snapshot-holders": "<asset-ref>", "distribute": "<asset-ref> <snapshot-height> <payout-asset-ref> <total>", "redeem-holders": "<asset-ref> <snapshot-height>",
|
|
123
|
+
"split-asset": "<asset-ref> <snapshot-height> <numerator> <denominator>",
|
|
124
|
+
"create-token": "<symbol> <name> <decimals> <initial-supply> <max-supply|-> mintable|fixed", "mint-token": "<token-ref> <to> <amount>",
|
|
125
|
+
"transfer-token": "<token-ref> <to> <amount>", "burn-token": "<token-ref> <amount>", "renounce-mint": "<token-ref>",
|
|
126
|
+
};
|
|
127
|
+
function morePayload(action, args) {
|
|
128
|
+
const usage = USAGE[action] ?? throwUsage(`unsupported action: ${action}`);
|
|
129
|
+
const arg = (i) => args[i] ?? throwUsage(`${action} needs ${usage}`);
|
|
130
|
+
const int = (i) => { const value = arg(i); if (!/^\d+$/.test(value))
|
|
131
|
+
throwUsage(`${action} needs ${usage} (integers in base units)`); return BigInt(value); };
|
|
132
|
+
switch (action) {
|
|
133
|
+
case "burn-asset": return encodeBurnAsset(arg(0), int(1));
|
|
134
|
+
case "forced-transfer": return encodeIssuerForcedTransfer(arg(0), arg(1), arg(2), int(3), arg(4));
|
|
135
|
+
case "recover-holder": return encodeRecoverHolder(arg(0), arg(1), arg(2));
|
|
136
|
+
case "issue-asset-to": return encodeIssueAssetTo(arg(0), arg(1), int(2));
|
|
137
|
+
case "lock-holder-amount": return args[3] === undefined ? encodeLockHolderAmount(arg(0), arg(1), int(2), true) : encodeLockHolderAmountUntil(arg(0), arg(1), int(2), int(3));
|
|
138
|
+
case "unlock-holder-amount": return encodeLockHolderAmount(arg(0), arg(1), int(2), false);
|
|
139
|
+
case "lock-issuance": return encodeLockIssuance(arg(0));
|
|
140
|
+
case "transfer-issuer": return encodeTransferIssuer(arg(0), arg(1));
|
|
141
|
+
case "set-metadata-uri": return encodeSetAssetMetadataUri(arg(0), arg(1) === "-" ? null : arg(1));
|
|
142
|
+
case "set-private-claims": {
|
|
143
|
+
const on = arg(1);
|
|
144
|
+
if (on !== "on" && on !== "off")
|
|
145
|
+
throwUsage(`${action} needs ${usage}`);
|
|
146
|
+
return encodeSetPrivateClaims(arg(0), on === "on");
|
|
147
|
+
}
|
|
148
|
+
case "snapshot-holders": return encodeSnapshotHolders(arg(0));
|
|
149
|
+
case "distribute": return encodeDistributeToHolders(arg(0), int(1), arg(2), int(3));
|
|
150
|
+
case "redeem-holders": return encodeRedeemHolders(arg(0), int(1));
|
|
151
|
+
case "split-asset": return encodeSplitAsset(arg(0), int(1), int(2), int(3));
|
|
152
|
+
case "create-token": {
|
|
153
|
+
const kind = arg(5);
|
|
154
|
+
if (kind !== "mintable" && kind !== "fixed")
|
|
155
|
+
throwUsage(`${action} needs ${usage}`);
|
|
156
|
+
return encodeCreateToken(arg(0), arg(1), Number(int(2)), int(3), arg(4) === "-" ? null : int(4), kind === "mintable");
|
|
157
|
+
}
|
|
158
|
+
case "mint-token": return encodeMintToken(arg(0), arg(1), int(2));
|
|
159
|
+
case "transfer-token": return encodeTransferToken(arg(0), arg(1), int(2));
|
|
160
|
+
case "burn-token": return encodeBurnToken(arg(0), int(1));
|
|
161
|
+
default: return encodeRenounceMint(arg(0));
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
/** Enabled issuer controls, fixed for the asset's life. Omitted or "all" keeps every power (the node's default). */
|
|
165
|
+
function controlsArg(value) { if (!value || value === "all")
|
|
166
|
+
return ALL_ISSUER_CONTROLS; const on = value === "none" ? [] : value.split(","); for (const c of on)
|
|
167
|
+
if (!ISSUER_CONTROLS.includes(c))
|
|
168
|
+
throwUsage(`unknown control: ${c} (${ISSUER_CONTROLS.join(", ")})`); return Object.fromEntries(ISSUER_CONTROLS.map((c) => [c, on.includes(c)])); }
|
|
105
169
|
function hexArg(value) { if (!value || !/^[0-9a-f]*$/i.test(value) || value.length % 2)
|
|
106
170
|
throwUsage("expected even-length hex"); return Uint8Array.from(value.match(/../g)?.map((byte) => Number.parseInt(byte, 16)) ?? []); }
|
|
107
171
|
async function main() {
|
|
@@ -255,7 +319,17 @@ async function main() {
|
|
|
255
319
|
}
|
|
256
320
|
if (command === "query") {
|
|
257
321
|
const [operation, value] = rest;
|
|
258
|
-
const result = operation === "status" ? await rpc.status() : operation === "account" ? await rpc.account(value ?? throwUsage("account needs an address")) : operation === "block" ? (/^\d+$/.test(value ?? "") ? await rpc.block(Number(value)) : await rpc.blockByHash(value ?? throwUsage("block needs a height or hash"))) : operation === "action" ? await rpc.action(value ?? throwUsage("action needs a signature")) : operation === "validators" ? await rpc.validators() : operation === "finality" ? await rpc.finality() : operation === "search" ? await rpc.search(value ?? throwUsage("search needs a query"))
|
|
322
|
+
const result = operation === "status" ? await rpc.status() : operation === "account" ? await rpc.account(value ?? throwUsage("account needs an address")) : operation === "block" ? (/^\d+$/.test(value ?? "") ? await rpc.block(Number(value)) : await rpc.blockByHash(value ?? throwUsage("block needs a height or hash"))) : operation === "action" ? await rpc.action(value ?? throwUsage("action needs a signature")) : operation === "validators" ? await rpc.validators() : operation === "finality" ? await rpc.finality() : operation === "search" ? await rpc.search(value ?? throwUsage("search needs a query"))
|
|
323
|
+
: operation === "assets" ? (value ? await rpc.assets(value) : await rpc.listAssets())
|
|
324
|
+
: operation === "asset" ? await rpc.asset(value ?? throwUsage("asset needs an asset ref"))
|
|
325
|
+
: operation === "holders" ? await rpc.assetHolders(value ?? throwUsage("holders needs an asset ref"))
|
|
326
|
+
: operation === "balance" ? await rpc.assetBalance(value ?? throwUsage("balance needs <address> <asset-ref>"), rest[2] ?? throwUsage("balance needs <address> <asset-ref>"))
|
|
327
|
+
: operation === "attestors" ? await rpc.attestors()
|
|
328
|
+
: operation === "attestor" ? await rpc.attestor(value ?? throwUsage("attestor needs an address"))
|
|
329
|
+
: operation === "validator" ? await rpc.validator(value ?? throwUsage("validator needs an address"))
|
|
330
|
+
: operation === "stakes" ? await rpc.stakes(value ?? throwUsage("stakes needs an address"))
|
|
331
|
+
: operation === "fee" ? await rpc.actionFee()
|
|
332
|
+
: throwUsage("use: arx query status|account|block|action|validators|validator|finality|search|assets [address]|asset|holders|balance|attestors|attestor|stakes|fee");
|
|
259
333
|
output(result, globals.json);
|
|
260
334
|
return;
|
|
261
335
|
}
|
package/dist/fees.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/** `action_fee` and `weight_fee` from `GET /action-fee` (`rpc.feeParams()`). */
|
|
2
|
+
export type FeeParams = {
|
|
3
|
+
actionFee: bigint;
|
|
4
|
+
weightFee: bigint;
|
|
5
|
+
};
|
|
6
|
+
/** `POLICY_AUTHORIZATION_WEIGHT`: added when a stateful account policy authorizes the action. */
|
|
7
|
+
export declare const POLICY_AUTHORIZATION_WEIGHT = 5000;
|
|
8
|
+
/** A single-key ed25519 signature: 64 bytes as hex. */
|
|
9
|
+
export declare const SINGLE_SIGNATURE_LENGTH = 128;
|
|
10
|
+
/** Hex length of a `threshold`-of-`members` multisig witness (`multisigSignature`), for estimating a fee before anyone signs. */
|
|
11
|
+
export declare const multisigSignatureLength: (threshold: number, members: number) => number;
|
|
12
|
+
/**
|
|
13
|
+
* Weight that the node charges for an action. Pass the real `signature` once it is signed. Before signing, pass
|
|
14
|
+
* `signatureLength` instead (128 for one key, longer for a multisig witness), and set `policy` when a stateful account policy will sign.
|
|
15
|
+
*/
|
|
16
|
+
export declare function actionWeight(sender: string, nonce: number | bigint, payload: Uint8Array, options?: {
|
|
17
|
+
signature?: string;
|
|
18
|
+
signatureLength?: number;
|
|
19
|
+
policy?: boolean;
|
|
20
|
+
}): number;
|
|
21
|
+
/** The fee the node charges, in IUM. `CreateToken` also pays `token_create_fee` on top (see `rpc.feeParams()`). */
|
|
22
|
+
export declare function actionFee(params: FeeParams, sender: string, nonce: number | bigint, payload: Uint8Array, options?: {
|
|
23
|
+
signature?: string;
|
|
24
|
+
signatureLength?: number;
|
|
25
|
+
policy?: boolean;
|
|
26
|
+
}): bigint;
|
package/dist/fees.js
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { ACTION_VARIANT, TOKEN_ACTION, TOKEN_VARIANT } from "./actions.js";
|
|
2
|
+
import { Reader } from "./bincode.js";
|
|
3
|
+
import { ACCOUNT_POLICY_ACTION } from "./policy.js";
|
|
4
|
+
/** `base_weight` in metering.rs, by `ActionPayload` discriminant. Token and account-policy actions are handled below. */
|
|
5
|
+
const BASE_WEIGHT = {
|
|
6
|
+
[ACTION_VARIANT.transfer]: 50,
|
|
7
|
+
[ACTION_VARIANT.stake]: 100, [ACTION_VARIANT.unstake]: 100,
|
|
8
|
+
[ACTION_VARIANT.joinValidator]: 1_500, [ACTION_VARIANT.registerBlsKey]: 1_500,
|
|
9
|
+
[ACTION_VARIANT.leaveValidator]: 150,
|
|
10
|
+
[ACTION_VARIANT.authorizeOperator]: 100, [ACTION_VARIANT.revokeOperator]: 100,
|
|
11
|
+
7 /* VerifyIdentityCredential */: 5_000, [ACTION_VARIANT.verifyClaimProof]: 5_000,
|
|
12
|
+
[ACTION_VARIANT.grantAttestation]: 100, [ACTION_VARIANT.revokeAttestation]: 100,
|
|
13
|
+
[ACTION_VARIANT.applyAttestor]: 150, [ACTION_VARIANT.blockAttestor]: 150,
|
|
14
|
+
[ACTION_VARIANT.registerAsset]: 150,
|
|
15
|
+
[ACTION_VARIANT.issueAsset]: 150, [ACTION_VARIANT.issueAssetTo]: 150, [ACTION_VARIANT.transferAsset]: 150,
|
|
16
|
+
[ACTION_VARIANT.issuerForcedTransfer]: 150, [ACTION_VARIANT.burnAsset]: 150, [ACTION_VARIANT.recoverHolder]: 150,
|
|
17
|
+
[ACTION_VARIANT.freezeAsset]: 100, [ACTION_VARIANT.unfreezeAsset]: 100, [ACTION_VARIANT.setHolderFrozen]: 100,
|
|
18
|
+
[ACTION_VARIANT.lockHolderAmount]: 100, [ACTION_VARIANT.lockHolderAmountUntil]: 100, [ACTION_VARIANT.unlockHolderAmount]: 100,
|
|
19
|
+
[ACTION_VARIANT.setAssetLimits]: 100, [ACTION_VARIANT.setPrivateClaims]: 100, [ACTION_VARIANT.lockIssuance]: 100,
|
|
20
|
+
[ACTION_VARIANT.transferIssuer]: 100, [ACTION_VARIANT.setAssetMetadataUri]: 100,
|
|
21
|
+
[ACTION_VARIANT.snapshotHolders]: 2_000, [ACTION_VARIANT.distributeToHolders]: 15_000,
|
|
22
|
+
[ACTION_VARIANT.redeemHolders]: 10_000, [ACTION_VARIANT.splitAsset]: 10_000,
|
|
23
|
+
[ACTION_VARIANT.submitProposal]: 150, [ACTION_VARIANT.voteProposal]: 150, [ACTION_VARIANT.executeProposal]: 150,
|
|
24
|
+
5 /* SubmitEquivocationEvidence */: 2_000,
|
|
25
|
+
15 /* SubmitExecutionFault */: 500_000,
|
|
26
|
+
};
|
|
27
|
+
/** `POLICY_AUTHORIZATION_WEIGHT`: added when a stateful account policy authorizes the action. */
|
|
28
|
+
export const POLICY_AUTHORIZATION_WEIGHT = 5_000;
|
|
29
|
+
/** A single-key ed25519 signature: 64 bytes as hex. */
|
|
30
|
+
export const SINGLE_SIGNATURE_LENGTH = 128;
|
|
31
|
+
/** Hex length of a `threshold`-of-`members` multisig witness (`multisigSignature`), for estimating a fee before anyone signs. */
|
|
32
|
+
export const multisigSignatureLength = (threshold, members) => 2 * (2 + 32 * members + 65 * threshold);
|
|
33
|
+
function baseWeight(payload) {
|
|
34
|
+
const r = new Reader(payload), variant = Number(r.varint());
|
|
35
|
+
if (variant === TOKEN_ACTION)
|
|
36
|
+
return Number(r.varint()) === TOKEN_VARIANT.createToken ? 150 : 100;
|
|
37
|
+
if (variant === ACCOUNT_POLICY_ACTION)
|
|
38
|
+
return 1_000;
|
|
39
|
+
const weight = BASE_WEIGHT[variant];
|
|
40
|
+
if (weight === undefined)
|
|
41
|
+
throw new Error(`unknown action variant: ${variant}`);
|
|
42
|
+
return weight;
|
|
43
|
+
}
|
|
44
|
+
/** bincode `standard()` varint length. */
|
|
45
|
+
const varintLength = (n) => (n <= 250 ? 1 : n <= 0xffff ? 3 : n <= 0xffff_ffff ? 5 : 9);
|
|
46
|
+
const isPolicyWitness = (signature) => signature.length > 128 && /^a7(00|01|02)/i.test(signature);
|
|
47
|
+
/**
|
|
48
|
+
* Weight that the node charges for an action. Pass the real `signature` once it is signed. Before signing, pass
|
|
49
|
+
* `signatureLength` instead (128 for one key, longer for a multisig witness), and set `policy` when a stateful account policy will sign.
|
|
50
|
+
*/
|
|
51
|
+
export function actionWeight(sender, nonce, payload, options = {}) {
|
|
52
|
+
const sigLength = options.signature?.length ?? options.signatureLength ?? SINGLE_SIGNATURE_LENGTH;
|
|
53
|
+
const policy = options.signature !== undefined ? isPolicyWitness(options.signature) : !!options.policy;
|
|
54
|
+
const senderLength = new TextEncoder().encode(sender).length;
|
|
55
|
+
// `ActionWire`: sender string, nonce, `Some(signature)`, payload as a length-prefixed byte string.
|
|
56
|
+
const size = varintLength(senderLength) + senderLength + varintLength(Number(nonce)) + 1 + varintLength(sigLength) + sigLength + varintLength(payload.length) + payload.length;
|
|
57
|
+
return baseWeight(payload) + size + (policy ? POLICY_AUTHORIZATION_WEIGHT : 0);
|
|
58
|
+
}
|
|
59
|
+
/** The fee the node charges, in IUM. `CreateToken` also pays `token_create_fee` on top (see `rpc.feeParams()`). */
|
|
60
|
+
export function actionFee(params, sender, nonce, payload, options = {}) {
|
|
61
|
+
return params.actionFee + BigInt(actionWeight(sender, nonce, payload, options)) * params.weightFee;
|
|
62
|
+
}
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
package/dist/rpc.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { type SignedAction } from "./actions.js";
|
|
1
|
+
import { type AssetClass, type IssuerControls, type SignedAction } from "./actions.js";
|
|
2
|
+
import type { FeeParams } from "./fees.js";
|
|
2
3
|
import { type AccountPolicy } from "./policy.js";
|
|
3
4
|
export type RpcOptions = {
|
|
4
5
|
rpc: string;
|
|
@@ -19,10 +20,15 @@ export type ActionStatus = {
|
|
|
19
20
|
} | {
|
|
20
21
|
status: "unknown";
|
|
21
22
|
};
|
|
23
|
+
/**
|
|
24
|
+
* `privateKey` signs as a single-key sender. For a multisig or programmable-account sender, pass `sign` instead: it gets the
|
|
25
|
+
* exact signing bytes and the nonce, and returns the action's `signature` field (e.g. from `multisigSignature`).
|
|
26
|
+
*/
|
|
22
27
|
export type SendActionOptions = {
|
|
23
|
-
privateKey: CryptoKey;
|
|
24
28
|
sender: string;
|
|
25
29
|
payload: Uint8Array;
|
|
30
|
+
privateKey?: CryptoKey;
|
|
31
|
+
sign?: (message: Uint8Array, nonce: number) => Promise<string>;
|
|
26
32
|
pollIntervalMs?: number;
|
|
27
33
|
timeoutMs?: number;
|
|
28
34
|
finalized?: boolean;
|
|
@@ -31,34 +37,211 @@ export declare class RpcError extends Error {
|
|
|
31
37
|
readonly status: number;
|
|
32
38
|
constructor(status: number, message: string);
|
|
33
39
|
}
|
|
40
|
+
/** The chain refused the action. `reason` is the node's exact text, e.g. `compliance check failed: arx1… is not KYC'd/allowlisted`. */
|
|
41
|
+
export declare class ActionDroppedError extends Error {
|
|
42
|
+
readonly reason: string;
|
|
43
|
+
readonly action: SignedAction;
|
|
44
|
+
constructor(reason: string, action: SignedAction);
|
|
45
|
+
}
|
|
46
|
+
export type ClaimTopicName = "Kyc" | "Aml" | "Accredited" | "Jurisdiction";
|
|
47
|
+
export type NodeStatus = {
|
|
48
|
+
chain_name: string;
|
|
49
|
+
version: string;
|
|
50
|
+
genesis_hash: string;
|
|
51
|
+
tip_height: number;
|
|
52
|
+
tip_hash: string;
|
|
53
|
+
finalized_height: number | null;
|
|
54
|
+
final_watermark: number;
|
|
55
|
+
settled_height: number;
|
|
56
|
+
block_interval_secs: number | null;
|
|
57
|
+
storage_schema: number;
|
|
58
|
+
min_migratable_schema: number;
|
|
59
|
+
open_dispute_height: number | null;
|
|
60
|
+
open_dispute_header: string | null;
|
|
61
|
+
open_dispute_cause: string | null;
|
|
62
|
+
};
|
|
63
|
+
export type Account = {
|
|
64
|
+
balance: bigint;
|
|
65
|
+
nonce: number;
|
|
66
|
+
identity_hash: string | null;
|
|
67
|
+
zk_identity_verified: boolean;
|
|
68
|
+
attested_by: string | null;
|
|
69
|
+
claims: ClaimTopicName[];
|
|
70
|
+
jurisdiction: string | null;
|
|
71
|
+
attested_at: number | null;
|
|
72
|
+
programmable?: unknown;
|
|
73
|
+
};
|
|
74
|
+
export type StakeAllocation = {
|
|
75
|
+
master: string;
|
|
76
|
+
validator: string;
|
|
77
|
+
active_amount: bigint;
|
|
78
|
+
unbonding: {
|
|
79
|
+
amount: bigint;
|
|
80
|
+
unlock_at_height: number;
|
|
81
|
+
} | null;
|
|
82
|
+
created_at: number;
|
|
83
|
+
updated_at: number;
|
|
84
|
+
};
|
|
85
|
+
/** One asset in `rpc.assets(address)`. `transfer_eligible`/`eligibility_reason` describe the sending side. */
|
|
86
|
+
export type AccountAssetBalance = {
|
|
87
|
+
ref: string;
|
|
88
|
+
asset_id: string;
|
|
89
|
+
symbol: string;
|
|
90
|
+
name: string;
|
|
91
|
+
issuer: string;
|
|
92
|
+
issuer_attested: boolean;
|
|
93
|
+
compliance_required: boolean;
|
|
94
|
+
frozen: boolean;
|
|
95
|
+
holder_frozen: boolean;
|
|
96
|
+
frozen_amount: bigint;
|
|
97
|
+
transfer_eligible: boolean;
|
|
98
|
+
eligibility_reason: string;
|
|
99
|
+
decimals: number;
|
|
100
|
+
balance: bigint;
|
|
101
|
+
};
|
|
102
|
+
export type Asset = {
|
|
103
|
+
ref: string;
|
|
104
|
+
asset_ref: string;
|
|
105
|
+
asset_id: string;
|
|
106
|
+
issuer: string;
|
|
107
|
+
issuer_attested: boolean;
|
|
108
|
+
holders: number;
|
|
109
|
+
compliance_required: boolean;
|
|
110
|
+
asset_class: string;
|
|
111
|
+
decimals: number;
|
|
112
|
+
required_claims: ClaimTopicName[];
|
|
113
|
+
allowed_jurisdictions: string[] | null;
|
|
114
|
+
max_supply: bigint | null;
|
|
115
|
+
total_supply: bigint;
|
|
116
|
+
frozen: boolean;
|
|
117
|
+
metadata_uri: string | null;
|
|
118
|
+
registered_at: number;
|
|
119
|
+
symbol: string;
|
|
120
|
+
name: string;
|
|
121
|
+
issuance_locked: boolean;
|
|
122
|
+
max_holders: number | null;
|
|
123
|
+
max_balance_per_holder: bigint | null;
|
|
124
|
+
max_attestation_age: number | null;
|
|
125
|
+
holder_count: number;
|
|
126
|
+
snapshot: {
|
|
127
|
+
height: number;
|
|
128
|
+
total: bigint;
|
|
129
|
+
holders: [string, number | bigint][];
|
|
130
|
+
} | null;
|
|
131
|
+
private_claims: boolean;
|
|
132
|
+
controls: IssuerControls;
|
|
133
|
+
};
|
|
134
|
+
export type AssetHolder = {
|
|
135
|
+
address: string;
|
|
136
|
+
balance: bigint;
|
|
137
|
+
frozen: boolean;
|
|
138
|
+
frozen_amount: bigint;
|
|
139
|
+
attested_at: number | null;
|
|
140
|
+
};
|
|
141
|
+
export type Attestor = {
|
|
142
|
+
attestor: string;
|
|
143
|
+
name: string;
|
|
144
|
+
registered_at: number;
|
|
145
|
+
owners: string[];
|
|
146
|
+
threshold: number;
|
|
147
|
+
blocked: boolean;
|
|
148
|
+
self_blocked: boolean;
|
|
149
|
+
removal_pending: boolean;
|
|
150
|
+
};
|
|
151
|
+
export type ValidatorStatus = "Active" | "Pending" | "Tombstoned" | {
|
|
152
|
+
Jailed: {
|
|
153
|
+
until_epoch: number;
|
|
154
|
+
};
|
|
155
|
+
} | {
|
|
156
|
+
Leaving: {
|
|
157
|
+
from_epoch: number;
|
|
158
|
+
};
|
|
159
|
+
};
|
|
160
|
+
export type Validator = {
|
|
161
|
+
address: string;
|
|
162
|
+
status: ValidatorStatus;
|
|
163
|
+
voting_power: number;
|
|
164
|
+
bls_registered: boolean;
|
|
165
|
+
operator: string | null;
|
|
166
|
+
};
|
|
167
|
+
export type ActionFeeInfo = {
|
|
168
|
+
action_fee: bigint;
|
|
169
|
+
weight_fee: bigint;
|
|
170
|
+
max_block_weight: number;
|
|
171
|
+
token_create_fee: bigint;
|
|
172
|
+
};
|
|
173
|
+
export type { AssetClass };
|
|
174
|
+
/**
|
|
175
|
+
* `JSON.parse` that never rounds an integer: the node writes u128 amounts as bare JSON numbers, and anything above 2^53
|
|
176
|
+
* (about 9M ARX in IUM) loses digits in a plain parse. Amount keys become `bigint`; other integers stay `number` unless unsafe.
|
|
177
|
+
*/
|
|
178
|
+
export declare function parseJson(text: string): unknown;
|
|
179
|
+
/** `JSON.stringify` replacer for `bigint` values (as decimal strings). */
|
|
180
|
+
export declare const bigintReplacer: (_key: string, value: unknown) => unknown;
|
|
34
181
|
export declare class ArxiumRpc {
|
|
35
182
|
readonly rpc: string;
|
|
36
183
|
private readonly token?;
|
|
37
184
|
private readonly requestFetch;
|
|
185
|
+
/** One in-flight `sendAction` per sender: the node's account nonce doesn't count the mempool, so two concurrent sends would sign the same nonce. */
|
|
186
|
+
private readonly senders;
|
|
38
187
|
constructor(options: RpcOptions);
|
|
39
188
|
private request;
|
|
40
189
|
private genesis?;
|
|
41
190
|
/** This chain's genesis hash, fetched once per client; every signature binds it. */
|
|
42
191
|
genesisHash(): Promise<Uint8Array>;
|
|
43
|
-
status<T =
|
|
44
|
-
account<T =
|
|
45
|
-
|
|
46
|
-
|
|
192
|
+
status<T = NodeStatus>(): Promise<T>;
|
|
193
|
+
account<T = Account>(address: string): Promise<T>;
|
|
194
|
+
/** The account's asset balances, each with `transfer_eligible` and `eligibility_reason` for the sending side. Includes rows that went to zero. */
|
|
195
|
+
assets<T = AccountAssetBalance[]>(address: string): Promise<T>;
|
|
196
|
+
/** One asset balance. 404 (`RpcError`) when the asset was never registered; a registered asset held at zero is `balance: 0n`. */
|
|
197
|
+
assetBalance<T = AccountAssetBalance>(address: string, assetRef: string): Promise<T>;
|
|
198
|
+
stake<T = StakeAllocation>(address: string): Promise<T>;
|
|
199
|
+
stakes<T = StakeAllocation[]>(address: string): Promise<T>;
|
|
200
|
+
/** A stake `master` placed on `validator`'s behalf (`stake(address)` is the self-stake case). */
|
|
201
|
+
delegatedStake<T = StakeAllocation>(master: string, validator: string): Promise<T>;
|
|
47
202
|
blsKey<T = Record<string, unknown>>(address: string): Promise<T>;
|
|
203
|
+
/** Every registered asset, or one issuer's. */
|
|
204
|
+
listAssets<T = Asset[]>(issuer?: string): Promise<T>;
|
|
205
|
+
asset<T = Asset>(assetRef: string): Promise<T>;
|
|
206
|
+
/** The asset `issuer` registered as `assetId`, or `null` when that id is still free for them. */
|
|
207
|
+
assetByAlias<T = Asset>(issuer: string, assetId: string): Promise<T | null>;
|
|
208
|
+
/** The cap table: every address with a non-zero balance, with its freeze state. */
|
|
209
|
+
assetHolders<T = AssetHolder[]>(assetRef: string): Promise<T>;
|
|
210
|
+
attestors<T = Attestor[]>(): Promise<T>;
|
|
211
|
+
attestor<T = Attestor>(address: string): Promise<T>;
|
|
48
212
|
submit(action: SignedAction): Promise<unknown>;
|
|
49
213
|
action(signature: string): Promise<ActionStatus>;
|
|
50
214
|
blocks<T = unknown>(from: number, to: number): Promise<T>;
|
|
51
215
|
block<T = unknown>(height: number): Promise<T>;
|
|
52
216
|
blockByHash<T = unknown>(hash: string): Promise<T>;
|
|
53
|
-
|
|
217
|
+
/** State changes a block made (balances, assets, stake), for indexers. */
|
|
218
|
+
blockEffects<T = unknown>(height: number): Promise<T>;
|
|
219
|
+
effects<T = unknown>(from: number, to: number): Promise<T>;
|
|
220
|
+
/** Sorted validator addresses, at the tip or at `height`. */
|
|
221
|
+
validators<T = string[]>(height?: number): Promise<T>;
|
|
222
|
+
validator<T = Validator>(address: string): Promise<T>;
|
|
223
|
+
/** Voting power by validator address, at the tip or at `height`. */
|
|
224
|
+
validatorPower<T = Record<string, number>>(height?: number): Promise<T>;
|
|
225
|
+
operatorValidators<T = string[]>(operator: string): Promise<T>;
|
|
54
226
|
finality<T = unknown>(): Promise<T>;
|
|
227
|
+
/** Ids of stored equivocation/fault evidence; `evidence(id)` returns one. */
|
|
228
|
+
evidenceList<T = string[]>(): Promise<T>;
|
|
229
|
+
evidence<T = unknown>(id: string): Promise<T>;
|
|
230
|
+
/** Merkle proofs against a finalized state root, for light clients. */
|
|
231
|
+
accountProof<T = unknown>(address: string): Promise<T>;
|
|
232
|
+
assetBalanceProof<T = unknown>(address: string, assetRef: string): Promise<T>;
|
|
233
|
+
validatorProof<T = unknown>(address: string): Promise<T>;
|
|
55
234
|
search<T = unknown>(query: string): Promise<T>;
|
|
56
235
|
minStake<T = unknown>(): Promise<T>;
|
|
57
|
-
actionFee<T =
|
|
236
|
+
actionFee<T = ActionFeeInfo>(): Promise<T>;
|
|
237
|
+
/** The chain's current fee parameters, for `actionFee(params, …)`. */
|
|
238
|
+
feeParams(): Promise<FeeParams>;
|
|
239
|
+
/** Signs at the sender's current nonce, submits and polls until confirmed (or finalized). Calls for one sender run one after another. */
|
|
58
240
|
sendAction(options: SendActionOptions): Promise<{
|
|
59
241
|
action: SignedAction;
|
|
60
242
|
status: ActionStatus;
|
|
61
243
|
}>;
|
|
244
|
+
private sendNow;
|
|
62
245
|
}
|
|
63
246
|
/** Stateful witnesses require the policy at the action's pre-state, not today's
|
|
64
247
|
* policy. This checks cryptography; circuit execution proves limits and scope. */
|
package/dist/rpc.js
CHANGED
|
@@ -9,21 +9,73 @@ export class RpcError extends Error {
|
|
|
9
9
|
this.status = status;
|
|
10
10
|
}
|
|
11
11
|
}
|
|
12
|
+
/** The chain refused the action. `reason` is the node's exact text, e.g. `compliance check failed: arx1… is not KYC'd/allowlisted`. */
|
|
13
|
+
export class ActionDroppedError extends Error {
|
|
14
|
+
reason;
|
|
15
|
+
action;
|
|
16
|
+
constructor(reason, action) {
|
|
17
|
+
super(`action dropped: ${reason}`);
|
|
18
|
+
this.reason = reason;
|
|
19
|
+
this.action = action;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
/** Keys whose integer values are token amounts (u128 on the node): always `bigint`, so callers never see a rounded balance. */
|
|
23
|
+
const AMOUNT_KEYS = new Set(["balance", "amount", "stake", "active_amount", "frozen_amount", "total_supply", "max_supply", "max_balance_per_holder", "total", "action_fee", "weight_fee", "token_create_fee", "min_stake"]);
|
|
24
|
+
const MARK = "\u0000int:";
|
|
25
|
+
/**
|
|
26
|
+
* `JSON.parse` that never rounds an integer: the node writes u128 amounts as bare JSON numbers, and anything above 2^53
|
|
27
|
+
* (about 9M ARX in IUM) loses digits in a plain parse. Amount keys become `bigint`; other integers stay `number` unless unsafe.
|
|
28
|
+
*/
|
|
29
|
+
export function parseJson(text) {
|
|
30
|
+
const marked = text.replace(/"(?:[^"\\]|\\.)*"|-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?/g, (token) => token[0] === '"' || /[.eE]/.test(token) ? token : `"\\u0000int:${token}"`);
|
|
31
|
+
return JSON.parse(marked, (key, value) => {
|
|
32
|
+
if (typeof value !== "string" || !value.startsWith(MARK))
|
|
33
|
+
return value;
|
|
34
|
+
const digits = value.slice(MARK.length), number = Number(digits);
|
|
35
|
+
return AMOUNT_KEYS.has(key) || !Number.isSafeInteger(number) ? BigInt(digits) : number;
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
/** `JSON.stringify` replacer for `bigint` values (as decimal strings). */
|
|
39
|
+
export const bigintReplacer = (_key, value) => typeof value === "bigint" ? value.toString() : value;
|
|
12
40
|
export class ArxiumRpc {
|
|
13
41
|
rpc;
|
|
14
42
|
token;
|
|
15
43
|
requestFetch;
|
|
44
|
+
/** One in-flight `sendAction` per sender: the node's account nonce doesn't count the mempool, so two concurrent sends would sign the same nonce. */
|
|
45
|
+
senders = new Map();
|
|
16
46
|
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
47
|
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 ?
|
|
48
|
+
throw new RpcError(response.status, text || `${response.status} ${response.statusText}`); return text ? parseJson(text) : undefined; }
|
|
19
49
|
genesis;
|
|
20
50
|
/** This chain's genesis hash, fetched once per client; every signature binds it. */
|
|
21
51
|
genesisHash() { return this.genesis ??= this.request("/genesis-hash").then((r) => fromHex(r.genesis_hash.replace(/^0x/, "")), (e) => { this.genesis = undefined; throw e; }); }
|
|
22
52
|
status() { return this.request("/status"); }
|
|
23
53
|
account(address) { return this.request(`/accounts/${encodeURIComponent(address)}`); }
|
|
54
|
+
/** The account's asset balances, each with `transfer_eligible` and `eligibility_reason` for the sending side. Includes rows that went to zero. */
|
|
55
|
+
assets(address) { return this.request(`/accounts/${encodeURIComponent(address)}/assets`); }
|
|
56
|
+
/** One asset balance. 404 (`RpcError`) when the asset was never registered; a registered asset held at zero is `balance: 0n`. */
|
|
57
|
+
assetBalance(address, assetRef) { return this.request(`/accounts/${encodeURIComponent(address)}/assets/${encodeURIComponent(assetRef)}`); }
|
|
24
58
|
stake(address) { return this.request(`/accounts/${encodeURIComponent(address)}/stake`); }
|
|
25
59
|
stakes(address) { return this.request(`/accounts/${encodeURIComponent(address)}/stakes`); }
|
|
60
|
+
/** A stake `master` placed on `validator`'s behalf (`stake(address)` is the self-stake case). */
|
|
61
|
+
delegatedStake(master, validator) { return this.request(`/stake/${encodeURIComponent(master)}/${encodeURIComponent(validator)}`); }
|
|
26
62
|
blsKey(address) { return this.request(`/accounts/${encodeURIComponent(address)}/bls-key`); }
|
|
63
|
+
/** Every registered asset, or one issuer's. */
|
|
64
|
+
listAssets(issuer) { return this.request(issuer ? `/assets?issuer=${encodeURIComponent(issuer)}` : "/assets"); }
|
|
65
|
+
asset(assetRef) { return this.request(`/assets/${encodeURIComponent(assetRef)}`); }
|
|
66
|
+
/** The asset `issuer` registered as `assetId`, or `null` when that id is still free for them. */
|
|
67
|
+
async assetByAlias(issuer, assetId) { try {
|
|
68
|
+
return await this.request(`/assets/alias/${encodeURIComponent(issuer)}/${encodeURIComponent(assetId)}`);
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
if (error instanceof RpcError && error.status === 404)
|
|
72
|
+
return null;
|
|
73
|
+
throw error;
|
|
74
|
+
} }
|
|
75
|
+
/** The cap table: every address with a non-zero balance, with its freeze state. */
|
|
76
|
+
assetHolders(assetRef) { return this.request(`/assets/${encodeURIComponent(assetRef)}/holders`); }
|
|
77
|
+
attestors() { return this.request("/attestors"); }
|
|
78
|
+
attestor(address) { return this.request(`/attestors/${encodeURIComponent(address)}`); }
|
|
27
79
|
submit(action) { return this.request("/actions", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(action) }); }
|
|
28
80
|
async action(signature) { try {
|
|
29
81
|
return await this.request(`/actions/${encodeURIComponent(signature)}`);
|
|
@@ -36,25 +88,63 @@ export class ArxiumRpc {
|
|
|
36
88
|
blocks(from, to) { return this.request(`/blocks?from=${from}&to=${to}`); }
|
|
37
89
|
block(height) { return this.request(`/blocks/${height}`); }
|
|
38
90
|
blockByHash(hash) { return this.request(`/blocks/by-hash/${encodeURIComponent(hash)}`); }
|
|
39
|
-
|
|
91
|
+
/** State changes a block made (balances, assets, stake), for indexers. */
|
|
92
|
+
blockEffects(height) { return this.request(`/blocks/${height}/effects`); }
|
|
93
|
+
effects(from, to) { return this.request(`/effects?from=${from}&to=${to}`); }
|
|
94
|
+
/** Sorted validator addresses, at the tip or at `height`. */
|
|
95
|
+
validators(height) { return this.request(height === undefined ? "/validators" : `/validators?height=${height}`); }
|
|
96
|
+
validator(address) { return this.request(`/validators/${encodeURIComponent(address)}`); }
|
|
97
|
+
/** Voting power by validator address, at the tip or at `height`. */
|
|
98
|
+
validatorPower(height) { return this.request(height === undefined ? "/validators/power" : `/validators/power?height=${height}`); }
|
|
99
|
+
operatorValidators(operator) { return this.request(`/operators/${encodeURIComponent(operator)}/validators`); }
|
|
40
100
|
finality() { return this.request("/finality"); }
|
|
101
|
+
/** Ids of stored equivocation/fault evidence; `evidence(id)` returns one. */
|
|
102
|
+
evidenceList() { return this.request("/evidence"); }
|
|
103
|
+
evidence(id) { return this.request(`/evidence/${encodeURIComponent(id)}`); }
|
|
104
|
+
/** Merkle proofs against a finalized state root, for light clients. */
|
|
105
|
+
accountProof(address) { return this.request(`/accounts/${encodeURIComponent(address)}/proof`); }
|
|
106
|
+
assetBalanceProof(address, assetRef) { return this.request(`/accounts/${encodeURIComponent(address)}/assets/${encodeURIComponent(assetRef)}/proof`); }
|
|
107
|
+
validatorProof(address) { return this.request(`/validators/${encodeURIComponent(address)}/proof`); }
|
|
41
108
|
search(query) { return this.request(`/search?q=${encodeURIComponent(query)}`); }
|
|
42
109
|
minStake() { return this.request("/min-stake"); }
|
|
43
110
|
actionFee() { return this.request("/action-fee"); }
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
111
|
+
/** The chain's current fee parameters, for `actionFee(params, …)`. */
|
|
112
|
+
async feeParams() { const fee = await this.actionFee(); return { actionFee: BigInt(fee.action_fee), weightFee: BigInt(fee.weight_fee) }; }
|
|
113
|
+
/** Signs at the sender's current nonce, submits and polls until confirmed (or finalized). Calls for one sender run one after another. */
|
|
114
|
+
sendAction(options) {
|
|
115
|
+
const previous = this.senders.get(options.sender) ?? Promise.resolve();
|
|
116
|
+
const run = previous.catch(() => undefined).then(() => this.sendNow(options));
|
|
117
|
+
const tail = run.catch(() => undefined);
|
|
118
|
+
this.senders.set(options.sender, tail);
|
|
119
|
+
void tail.then(() => { if (this.senders.get(options.sender) === tail)
|
|
120
|
+
this.senders.delete(options.sender); });
|
|
121
|
+
return run;
|
|
122
|
+
}
|
|
123
|
+
async sendNow(options) {
|
|
124
|
+
if (!options.privateKey === !options.sign)
|
|
125
|
+
throw new Error("sendAction needs exactly one of privateKey or sign");
|
|
126
|
+
const account = await this.account(options.sender).catch((error) => { if (error instanceof RpcError && error.status === 404)
|
|
127
|
+
return { nonce: 0 }; throw error; });
|
|
128
|
+
const genesis = await this.genesisHash(), nonce = account.nonce;
|
|
129
|
+
const signature = options.sign ? await options.sign(signingBytes(genesis, options.sender, nonce, options.payload), nonce) : await signAction(options.privateKey, genesis, options.sender, nonce, options.payload);
|
|
130
|
+
const action = submitBody(options.sender, nonce, signature, options.payload);
|
|
131
|
+
await this.submit(action);
|
|
132
|
+
const deadline = Date.now() + (options.timeoutMs ?? 120_000);
|
|
133
|
+
while (Date.now() < deadline) {
|
|
134
|
+
const status = await this.action(signature);
|
|
135
|
+
if (status.status === "dropped")
|
|
136
|
+
throw new ActionDroppedError(status.reason, action);
|
|
137
|
+
if (status.status === "confirmed") {
|
|
138
|
+
if (!options.finalized)
|
|
139
|
+
return { action, status };
|
|
140
|
+
const block = await this.block(status.height);
|
|
141
|
+
if (block.finalized)
|
|
142
|
+
return { action, status };
|
|
143
|
+
}
|
|
144
|
+
await new Promise((resolve) => setTimeout(resolve, options.pollIntervalMs ?? 1_000));
|
|
55
145
|
}
|
|
56
|
-
|
|
57
|
-
}
|
|
146
|
+
throw new Error("timed out waiting for action");
|
|
147
|
+
}
|
|
58
148
|
}
|
|
59
149
|
/** Stateful witnesses require the policy at the action's pre-state, not today's
|
|
60
150
|
* policy. This checks cryptography; circuit execution proves limits and scope. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arxiumlabs/sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "Arxium TypeScript SDK and arx CLI",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"private": false,
|
|
@@ -32,6 +32,8 @@
|
|
|
32
32
|
},
|
|
33
33
|
"scripts": {
|
|
34
34
|
"build": "tsc -p tsconfig.json",
|
|
35
|
+
"check:package": "node scripts/release-check.mjs",
|
|
36
|
+
"test:release": "node --test scripts/release-check.test.mjs",
|
|
35
37
|
"test": "npm run build && node dist/codec.test.js && node dist/policy.test.js && node dist/cli.test.js"
|
|
36
38
|
},
|
|
37
39
|
"devDependencies": {
|