Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
297f863
docs(consensus): restore snapshot reorg buffer comments
jdogresorg Sep 17, 2026
c3b68a7
ci: sibling checkouts follow the pull request's base branch, keeping …
jdogresorg Sep 17, 2026
4f601f0
fix(consensus): re-cut the LTC and DOGE mirror-admission heights onto…
jdogresorg Sep 17, 2026
5d766e6
test: resolve indexer parity handlers from the drift map
jdogresorg Sep 18, 2026
1cdf119
docs(ci): repair the sibling guard references in .ci-siblings
jdogresorg Sep 18, 2026
5a14cc2
Merge master into develop after the v0.20.0 train
jdogresorg Sep 19, 2026
fcf6801
fix(protocol): ship LTC:testnet mirror admission as null under dq4 (a)
jdogresorg Sep 19, 2026
1e2dd5e
ci(sdk): publish tagged releases to npm via Trusted Publishing
jdogresorg Sep 19, 2026
9608a90
fix(consensus): re-slide v0.20.1 BTC and DOGE activation heights
jdogresorg Sep 19, 2026
244a70d
fix(sdk): forward caller encoder options through the bridge workflows
jdogresorg Sep 19, 2026
43368d9
fix(drift-gate): hash companion handler parts
jdogresorg Sep 20, 2026
70bcb35
Land row L019-36 from cx-l019-36
jdogresorg Sep 20, 2026
43c77f6
fix(hub): use public registry for keyless discovery
jdogresorg Sep 20, 2026
4535c40
Land row L024-74 from cx-l024-74
jdogresorg Sep 20, 2026
1a7fc10
Refactor public discovery test suite
jdogresorg Sep 21, 2026
8efd668
Land row RATCHET-SDK from cx-ratchet-sdk
jdogresorg Sep 21, 2026
3b20618
docs(comments): clarify nine rewritten explanations
jdogresorg Sep 21, 2026
93482c6
test(sdk): stub the discovery call the SDK actually makes
jdogresorg Sep 21, 2026
7701f45
Merge: docs(comments): clarify nine rewritten explanations
jdogresorg Sep 21, 2026
ce8a6e7
fix(sdk): mirror leg-amount consolidation as a per-leg error
jdogresorg Sep 22, 2026
000ef08
Merge: fix(sdk): mirror leg-amount consolidation as a per-leg error
jdogresorg Sep 22, 2026
e7b9793
fix(sdk): expectedMismatch refuses a null identity field instead of s…
jdogresorg Sep 22, 2026
d68b169
build(ci): grade the fast tier on a push, defer the heavy tiers to th…
jdogresorg Sep 22, 2026
75561ae
test(sdk): a null identity field is refused, and the opt-in case no l…
jdogresorg Sep 22, 2026
21d2a69
Merge: fix(sdk): expectedMismatch refuses a null identity field inste…
jdogresorg Sep 22, 2026
4254dd3
Merge: test(sdk): a null identity field is refused, and the opt-in ca…
jdogresorg Sep 22, 2026
97085a6
fix: canonicalize npm repository URLs
jdogresorg Sep 22, 2026
081a569
Merge: fix: canonicalize npm repository URLs
jdogresorg Sep 22, 2026
c09ccf3
fix(sdk): emit TIS v1.1.1 documents from nft.tisDocument and make the…
jdogresorg Sep 23, 2026
9be6187
docs(sdk): cite the real contract ABI spec path
jdogresorg Sep 23, 2026
1e88447
chore(preflight): re-pin the issue handler after a comment-only citat…
jdogresorg Sep 23, 2026
c1d4c15
fix(sdk): exact arithmetic for BET feed days and coinpay plans, curre…
jdogresorg Sep 23, 2026
54450ce
chore(pins): re-pin the vendored action manifest after the flag-doc p…
jdogresorg Sep 23, 2026
1a00adf
docs(sdk): correct the reference-impl consensus headers and register …
jdogresorg Sep 23, 2026
1340fdb
test(sdk): cover x402's fail-closed guard path and the published MCP …
jdogresorg Sep 23, 2026
8efe7d3
Merge: test(sdk): cover x402's fail-closed guard path and the publish…
jdogresorg Sep 23, 2026
209aa81
fix(consensus): re-slide v0.20.1 BTC and DOGE activation heights, 40h…
jdogresorg Sep 23, 2026
0effe92
chore(release): v0.20.1
jdogresorg Sep 23, 2026
453e6bb
chore(consensus): remove internal rollout label from public comments
jdogresorg Sep 23, 2026
6610bf3
fix(consensus): re-slide DOGE:testnet mirror admission to the 84h tra…
jdogresorg Sep 23, 2026
ba0f4d1
Merge branch 'cut/v0201-dq4' into cut/v0201-land
jdogresorg Sep 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 9 additions & 6 deletions .ci-siblings
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,16 @@
# resolves with existsSync, calls this.skip(), and the gate still prints PASS,
# so the run reports green having exercised none of them. Measured 2026-08-12,
# that gap was ~36 guards (a partial CI checkout ran 3444 passing / 40 pending
# where a full local checkout runs 3515 / 4). See test/unit/sibling_coverage.test.js,
# which reports the surface this file leaves uncovered; the same gap applies
# to a sibling that is declared but missing.
# where a full local checkout runs 3515 / 4). See
# test/unit/tooling/sibling_coverage.test.js, which reports the surface this file
# leaves uncovered; the same gap applies to a sibling that is declared but missing.
#
# ci-dispatch.sh ships each of these at its origin/master (falling back to local
# HEAD) and REFUSES the push if one is not a checkout beside this repo, so a
# declared sibling can never be silently absent.
# ci-dispatch.sh, the shared pre-push dispatcher (it lives in the CI tooling on
# the pushing host, outside this repo: there is no bin/ci-dispatch.sh here and
# never was), ships each of these at the pushed branch, falling back through
# origin/master, origin/main, origin/HEAD, then local HEAD. It REFUSES the push
# if one is not a checkout beside this repo, so a declared sibling can never be
# silently absent.

# Drift gate (§8.5) over the mapped indexer handlers.
xchain-indexer
Expand Down
10 changes: 5 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ jobs:
uses: XChain-Platform/.github/.github/workflows/ci-reusable.yml@6f4d39ae85787fc31e90a31588d87610a2c33103 # pin: XChain-Platform/.github @ master 2026-08-14; bump deliberately
with:
# A hotfix or release PR compares against siblings at the same branch, not develop.
siblings-ref: ${{ (startsWith(github.head_ref, 'release/') || startsWith(github.head_ref, 'hotfix/')) && github.head_ref || '' }}
siblings-ref: ${{ (startsWith(github.head_ref, 'release/') || startsWith(github.head_ref, 'hotfix/')) && github.head_ref || github.base_ref || '' }}
# Opt into the perf gate: the SDK's perf suite (Validator throughput) needs
# no infra, so it runs cleanly as a post-gate step. First adopter of run-perf.
run-perf: true
Expand Down Expand Up @@ -48,7 +48,7 @@ jobs:
# canonical sibling is read at the sibling release branch, because develop
# keeps moving under the frozen train (the indexer handler map this gate
# grades is re-pinned at every indexer develop landing).
ref: ${{ (startsWith(github.head_ref, 'release/') || startsWith(github.head_ref, 'hotfix/')) && github.head_ref || (github.ref == 'refs/heads/master' && 'master' || 'develop') }}
ref: ${{ (startsWith(github.head_ref, 'release/') || startsWith(github.head_ref, 'hotfix/')) && github.head_ref || github.base_ref || (github.ref == 'refs/heads/master' && 'master' || 'develop') }}
ssh-key: ${{ secrets.XCHAIN_HUB_DEPLOY_KEY }}
path: xchain-hub

Expand Down Expand Up @@ -83,7 +83,7 @@ jobs:
# canonical sibling is read at the sibling release branch, because develop
# keeps moving under the frozen train (the indexer handler map this gate
# grades is re-pinned at every indexer develop landing).
ref: ${{ (startsWith(github.head_ref, 'release/') || startsWith(github.head_ref, 'hotfix/')) && github.head_ref || (github.ref == 'refs/heads/master' && 'master' || 'develop') }}
ref: ${{ (startsWith(github.head_ref, 'release/') || startsWith(github.head_ref, 'hotfix/')) && github.head_ref || github.base_ref || (github.ref == 'refs/heads/master' && 'master' || 'develop') }}
ssh-key: ${{ secrets.XCHAIN_EXPLORER_DEPLOY_KEY }}
path: xchain-explorer

Expand Down Expand Up @@ -117,7 +117,7 @@ jobs:
# canonical sibling is read at the sibling release branch, because develop
# keeps moving under the frozen train (the indexer handler map this gate
# grades is re-pinned at every indexer develop landing).
ref: ${{ (startsWith(github.head_ref, 'release/') || startsWith(github.head_ref, 'hotfix/')) && github.head_ref || (github.ref == 'refs/heads/master' && 'master' || 'develop') }}
ref: ${{ (startsWith(github.head_ref, 'release/') || startsWith(github.head_ref, 'hotfix/')) && github.head_ref || github.base_ref || (github.ref == 'refs/heads/master' && 'master' || 'develop') }}
ssh-key: ${{ secrets.XCHAIN_INDEXER_DEPLOY_KEY }}
path: xchain-indexer

Expand Down Expand Up @@ -150,7 +150,7 @@ jobs:
- name: Check out declared sibling repositories
uses: XChain-Platform/.github/actions/checkout-siblings@master # one definition for every call site; see the action for why this tracks master
with:
ref: ${{ (startsWith(github.head_ref, 'release/') || startsWith(github.head_ref, 'hotfix/')) && github.head_ref || '' }}
ref: ${{ (startsWith(github.head_ref, 'release/') || startsWith(github.head_ref, 'hotfix/')) && github.head_ref || github.base_ref || '' }}

- name: Use Node.js 22
uses: actions/setup-node@v4
Expand Down
67 changes: 67 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
name: Publish to npm

on:
push:
tags:
- "v*"

permissions:
contents: read
id-token: write

jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6

- name: Check out declared sibling repositories
uses: XChain-Platform/.github/actions/checkout-siblings@master
with:
ref: master

- name: Use Node.js 22
uses: actions/setup-node@v6
with:
node-version: "22.22.3"
registry-url: "https://registry.npmjs.org"
package-manager-cache: false

- name: Install an OIDC-capable npm CLI
run: npm install --global "npm@^11.5.1"

- name: Verify release versions
env:
RELEASE_TAG: ${{ github.ref_name }}
run: |
node <<'NODE'
const packages = [
require('./package.json'),
require('./mcp/package.json'),
];
const expectedVersion = process.env.RELEASE_TAG.replace(/^v/, '');

for (const pkg of packages) {
if (pkg.version !== expectedVersion) {
throw new Error(`${pkg.name} is ${pkg.version}, expected ${expectedVersion}`);
}
}
NODE

- name: Install dependencies
run: npm ci

- name: Test SDK
env:
XCHAIN_REQUIRE_SIBLINGS: "1"
run: npm run ci

- name: Test MCP package
run: npm --prefix mcp run ci

- name: Publish SDK
run: npm publish --access public

- name: Publish MCP server
working-directory: mcp
run: npm publish --access public
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [0.20.1] - 2026-09-23

### Fixed
- Re-slid BTC and DOGE testnet activations with LTC inert, added exact BET and Coinpay arithmetic, and emitted TIS v1.1.1 NFT documents.


## [0.20.0] - 2026-09-17

### Added
Expand Down
3 changes: 2 additions & 1 deletion bin/check-preflight-drift.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,8 @@ const { indexerRootCandidates, describeIndexerCandidates, resolveIndexerRoot } =

/* The table rows, each tagged 'file', 'directory' or 'malformed' by the shape of the
* handler path it names. A directory row carries a trailing slash and is hashed over every
* file in the directory, so a handler split into parts cannot leave them unhashed. */
* file in the directory. A file row also covers a same-named directory beside its entry,
* so either supported handler split shape includes every part in its pin. */
function parseMap(mapPath) {
return handlerDirs.parseMapRows(fs.readFileSync(mapPath, 'utf8'));
}
Expand Down
43 changes: 42 additions & 1 deletion bin/ci-full.sh
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,36 @@ SELF="$(pwd)"
SIB="$(cd .. && pwd)"

FAILED=""
# >>> ci-tier (generated block; re-run the tier wirer to update) >>>
# Tier classes. A push grades the FAST tier only: the unit job, the pin and
# drift guards, and the structure and hygiene checks the hook runs before it
# dispatches. The tiers named below (coverage re-runs, perf scenarios) are
# skipped when the gate sets CI_TIER=fast, and each skip is recorded so the
# closing verdict can never claim a green it did not earn. Nothing stops
# being graded: a scheduled sweep re-runs this same script with CI_TIER=full
# on every repo every three hours and before any release or deploy, and a
# red there is tracked down and fixed first. CI_TIER is unset for a hand
# run, so a bare `npm run ci:full` still runs every tier as it always did.
CI_TIER_FULL_ONLY=(
"perf: hot-path throughput (test:performance)"
"coverage ratchet (coverage:check)"
)
DEFERRED=""
ci_tier_deferred() {
[ "${CI_TIER:-full}" = "fast" ] || return 1
local t
for t in ${CI_TIER_FULL_ONLY[@]+"${CI_TIER_FULL_ONLY[@]}"}; do
if [ "$t" = "$1" ]; then
DEFERRED="$DEFERRED [$1]"
echo; echo "ci:full ===== $1 DEFERRED (CI_TIER=fast, runs in the full sweep) ====="
return 0
fi
done
return 1
}
# <<< ci-tier <<<
run_tier() {
ci_tier_deferred "$1" && return 0 # ci-tier guard (generated)
local name="$1"; shift
echo; echo "ci:full ===== $name ====="
if "$@"; then
Expand Down Expand Up @@ -119,8 +148,20 @@ run_tier "drift: pre-flight <-> handler gate" \
run_tier "coverage ratchet (coverage:check)" npm run coverage:check

echo
# >>> ci-tier summary (generated) >>>
echo "ci:full: tier class ${CI_TIER:-full}"
if [ -n "${DEFERRED:-}" ]; then
echo "ci:full: DEFERRED to the full sweep:$DEFERRED"
fi
# <<< ci-tier summary <<<
if [ -n "$FAILED" ]; then
echo "ci:full: RED tiers:$FAILED"
exit 1
fi
echo "ci:full: all tiers green (same set GitHub CI runs)"
# >>> ci-tier verdict (generated) >>>
if [ "${CI_TIER:-full}" = "fast" ]; then
echo "ci:full: all FAST tiers green; the DEFERRED tiers above were NOT graded here"
else
echo "ci:full: all tiers green (same set GitHub CI runs)"
fi
# <<< ci-tier verdict <<<
2 changes: 1 addition & 1 deletion bin/pins/identity.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"src/checkpoint.js": "0b0e80f1372ef59fb6c8e7ec00a1f66d5e88ddca0a12584cadd087c1db595117",
"src/contract/abi-core.js": "8222b193b5e65da7130af0bb589ff38d069f3a088f0d9f901826ffb18a3e9294",
"test/fixtures/roundtrip-conformance.json": "d9963ac7c35bdcfab99595fec040d7791d73119cb62fef20db136f8ea56236ed",
"test/fixtures/action-manifest.json": "93ac85b4d76f078951a2e95eb3ca303f39fb9f18d0fb91b1ae16693716bef72f",
"test/fixtures/action-manifest.json": "05813d667976ec49bdeb001fc7f5aea41fce4f343a37576a2716d176b334a823",
"test/unit/xcall-constants-cross-repo.test.js": "baa3bd32e8626a6b177c92f001455bf6922e4ec85ddad46456f5e8a6a903df96",
"test/fixtures/gate-min-amount-vectors.json": "414a68df798c8eb24532abc4e0275326cc138019541a7cd4fb3b0221eef2b3c2"
},
Expand Down
82 changes: 72 additions & 10 deletions bin/preflight_handler_dirs.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@
*
* - a map row may name a DIRECTORY (`src/actions/<name>/`, trailing slash), hashed over
* every file in it, recursively, so editing, adding or removing any part moves the pin;
* - the fee walk reads every source file of a directory handler, not only index.js;
* - a FILE row also includes a same-named companion directory, since another valid split
* leaves `<name>.js` as the entry and moves its imported logic into `<name>/`;
* - the fee walk reads every source file owned by either split shape, not only its entry;
* - a fee-quote literal is found exactly once across the indexer's src/, so moving it
* out of the loader into a part neither loses it nor lets a second copy go unseen.
*
Expand Down Expand Up @@ -111,14 +113,44 @@ function hashDirectory(dirAbs) {
return { digest: sha256(manifest), parts };
}

// A flat entry may keep its original path while its implementation moves into a directory
// beside it. In that shape the row still names the entry, but its pin must commit to both
// the entry and every companion part. Names are relative to src/actions/ so the manifest
// distinguishes the entry from its tree and detects part renames as well as byte changes.
function hashFileWithCompanionParts(fileAbs) {
const partsDir = fileAbs.replace(/\.js$/, '');
let partsStat;
try {
partsStat = fs.lstatSync(partsDir);
} catch (e) {
if (e && e.code === 'ENOENT') return { digest: sha256(fs.readFileSync(fileAbs)) };
throw e;
}
if (partsStat.isSymbolicLink()) {
throw new Error(`drift-gate: companion ${partsDir} is a symbolic link; handler parts must be in a plain directory.`);
}
if (!partsStat.isDirectory()) return { digest: sha256(fs.readFileSync(fileAbs)) };

const entryName = path.basename(fileAbs);
const partsName = path.basename(partsDir);
const parts = [{ name: entryName, sha256: sha256(fs.readFileSync(fileAbs)) }];
for (const name of listParts(partsDir)) {
parts.push({ name: `${partsName}/${name}`, sha256: sha256(fs.readFileSync(path.join(partsDir, name))) });
}
parts.sort((a, b) => Buffer.compare(Buffer.from(a.name, 'utf8'), Buffer.from(b.name, 'utf8')));
const manifest = parts.map((p) => `${p.sha256} ${p.name}\n`).join('');
return { digest: sha256(manifest), parts };
}

function isDirectory(abs) {
try { return fs.lstatSync(abs).isDirectory(); } catch (e) { return false; }
}

// Hash one mapped row against the checkout. Returns { actual, parts? } or { problem }.

// A flat row is hashed as the file's bytes, which is what every pin in the table already
// records, so directory support leaves the shipped map valid with no re-pin.
// A flat row with no companion directory is hashed as the file's bytes, preserving legacy
// pins. When a companion directory exists, the entry and its parts form one manifest and
// the row needs the deliberate re-pin that closes the old blind spot.

// The problems a directory split introduces are named one by one below rather than folded
// into a hash mismatch, because each has its own fix.
Expand All @@ -129,7 +161,14 @@ function hashMappedRow(indexerRoot, row) {
return { problem: `${row.handler}: not a mappable handler path; a row names src/actions/<name>.js or the directory src/actions/<name>/` };
}
if (row.kind === 'file') {
if (fs.existsSync(abs)) return { actual: sha256(fs.readFileSync(abs)) };
if (fs.existsSync(abs)) {
try {
const { digest, parts } = hashFileWithCompanionParts(abs);
return { actual: digest, parts };
} catch (e) {
return { problem: `${row.handler}: ${e && e.message ? e.message : String(e)}` };
}
}
// A flat row whose handler became a directory: the fix is a re-pin as a directory row.
const dir = abs.replace(/\.js$/, '');
if (isDirectory(dir)) {
Expand All @@ -144,7 +183,7 @@ function hashMappedRow(indexerRoot, row) {
// require('./<name>') resolves a flat <name>.js ahead of <name>/index.js, so with both
// present the directory this would hash is not the code that runs.
if (fs.existsSync(flat)) {
return { problem: `${row.handler}: a flat ${row.handler.replace(/\/$/, '.js')} beside it is what require() resolves first, so the directory is not the code that runs` };
return { problem: `${row.handler}: a flat ${row.handler.replace(/\/$/, '.js')} beside it is what require() resolves first; map that entry file so its pin includes this companion parts directory` };
}
try {
const { digest, parts } = hashDirectory(abs);
Expand Down Expand Up @@ -181,8 +220,8 @@ function stripCommentsAndStrings(src) {
}

// The handlers the fee walk reads, each with every source file that belongs to it. A
// handler is either src/actions/<name>.js or, since the M3 feature-directory pass,
// src/actions/<name>/index.js.
// handler is src/actions/<name>.js, optionally with parts in src/actions/<name>/, or a
// directory handler whose entry is src/actions/<name>/index.js.

// A flat readdir of *.js alone silently drops every directory handler, and a dropped
// handler reads as "charges no fee" rather than as a broken walk, so both shapes are
Expand All @@ -196,11 +235,18 @@ function stripCommentsAndStrings(src) {
// as a flat readdir drops a directory.
function feeWalkHandlers(actionsDir, entries) {
const handlers = [];
const flatNames = new Set(entries
.filter((e) => e.isFile() && e.name.endsWith('.js') && e.name !== 'index.js')
.map((e) => path.basename(e.name, '.js')));
for (const e of entries) {
if (e.isDirectory()) {
const dirAbs = path.join(actionsDir, e.name);
const files = listParts(dirAbs, { allowEmpty: true }).filter((n) => JS_SOURCE.test(n)).map((n) => path.join(dirAbs, n));
if (fs.existsSync(path.join(dirAbs, 'index.js'))) {
// A same-named flat entry owns this parts tree. It is added with the entry in
// the file branch below, including any index.js the tree happens to contain.
if (flatNames.has(e.name)) {
continue;
} else if (fs.existsSync(path.join(dirAbs, 'index.js'))) {
handlers.push({ action: e.name, files });
// A directory with no index.js is not a handler, but if one of its files charges
// a fee the walk cannot say for which action, so that fails CLOSED here rather
Expand All @@ -210,7 +256,23 @@ function feeWalkHandlers(actionsDir, entries) {
+ 'calls createFeesObject. The fee walk cannot name the action that charges; fix the layout or the walk.');
}
} else if (e.isFile() && e.name.endsWith('.js') && e.name !== 'index.js') {
handlers.push({ action: path.basename(e.name, '.js'), files: [path.join(actionsDir, e.name)] });
const action = path.basename(e.name, '.js');
const entry = path.join(actionsDir, e.name);
const partsDir = path.join(actionsDir, action);
const files = [entry];
let partsStat;
try { partsStat = fs.lstatSync(partsDir); } catch (err) {
if (!err || err.code !== 'ENOENT') throw err;
}
if (partsStat && partsStat.isSymbolicLink()) {
throw new Error(`drift-gate: companion src/actions/${action}/ is a symbolic link; handler parts must be in a plain directory.`);
}
if (partsStat && partsStat.isDirectory()) {
files.push(...listParts(partsDir, { allowEmpty: true })
.filter((n) => JS_SOURCE.test(n))
.map((n) => path.join(partsDir, n)));
}
handlers.push({ action, files });
}
}
return handlers;
Expand Down Expand Up @@ -290,6 +352,6 @@ function main(argv) {

if (require.main === module) process.exit(main(process.argv.slice(2)));
module.exports = {
parseMapRows, listParts, hashDirectory, hashMappedRow, compareRows, formatDrift,
parseMapRows, listParts, hashDirectory, hashFileWithCompanionParts, hashMappedRow, compareRows, formatDrift,
stripCommentsAndStrings, feeWalkHandlers, declarationPattern, indexerLiteralReader, main,
};
4 changes: 2 additions & 2 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -954,7 +954,7 @@ export declare class ContractUtils {

/*
* NFT helpers: pure builders for the NFT pattern (ISSUE with DECIMALS=0 +
* LOCK_MAX_SUPPLY=1). No network. Spec: protocol/NFT_Standard.md
* LOCK_MAX_SUPPLY=1). No network. Spec: protocol/nft-standard.md
*/

export interface NftUniqueParams { tick: string; description?: string; transfer?: string; memo?: string; }
Expand Down Expand Up @@ -2110,7 +2110,7 @@ export declare class XChainSDK {
/** Distribute a dividend to all holders of a token */
distributeDividend(wif: string, dividendParams: DividendParams | ActionParams, opts?: Partial<SubmitActionOpts>): Promise<SubmitActionResult>;

/** Pure NFT param builders + classifier (no network). Spec: protocol/NFT_Standard.md */
/** Pure NFT param builders + classifier (no network). Spec: protocol/nft-standard.md */
readonly nft: NftHelpers;

/** Issue a unique 1-of-1 NFT, fully minted to the issuer */
Expand Down
Loading
Loading