@radiant-core/sdk 0.1.0 → 0.2.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 +12 -0
- package/dist/index.cjs +179 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +176 -4
- package/dist/index.d.ts +176 -4
- package/dist/index.js +172 -13
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -223,6 +223,40 @@ declare function unpackRef(ref: string): {
|
|
|
223
223
|
txid: string;
|
|
224
224
|
vout: number;
|
|
225
225
|
};
|
|
226
|
+
/** Owner address hash from a bare P2PKH script, if that's all it is. */
|
|
227
|
+
declare function parseP2pkhScript(scriptHex: string): {
|
|
228
|
+
addressHash?: string;
|
|
229
|
+
};
|
|
230
|
+
/**
|
|
231
|
+
* Parse an NFT output script into its singleton ref + owner hash.
|
|
232
|
+
*
|
|
233
|
+
* Matches the plain singleton:
|
|
234
|
+
* OP_PUSHINPUTREFSINGLETON <ref> OP_DROP <P2PKH>
|
|
235
|
+
* d8 <ref:72> 75 76a914 <h160:40> 88ac
|
|
236
|
+
*
|
|
237
|
+
* ...and the AUTH form that a mutable NFT (a WAVE name) is forced into after a
|
|
238
|
+
* target update — the mutable covenant makes the token output re-commit the
|
|
239
|
+
* mutable ref plus its scriptSig hash:
|
|
240
|
+
* ( OP_REQUIREINPUTREF <ref> <scriptSigHash> OP_2DROP )+ OP_STATESEPARATOR
|
|
241
|
+
* OP_PUSHINPUTREFSINGLETON <ref> OP_DROP <P2PKH>
|
|
242
|
+
*
|
|
243
|
+
* The singleton ref and owner are always the TRAILING `d8…/p2pkh`, so the
|
|
244
|
+
* optional preamble is skipped without changing what's captured. Returns {} for
|
|
245
|
+
* anything that isn't an NFT script.
|
|
246
|
+
*/
|
|
247
|
+
declare function parseNftScript(scriptHex: string): {
|
|
248
|
+
ref?: string;
|
|
249
|
+
addressHash?: string;
|
|
250
|
+
};
|
|
251
|
+
/** Parse an FT output script into its ref + owner hash. Returns {} if not an FT. */
|
|
252
|
+
declare function parseFtScript(scriptHex: string): {
|
|
253
|
+
ref?: string;
|
|
254
|
+
addressHash?: string;
|
|
255
|
+
};
|
|
256
|
+
/** What kind of token a script is, if any. */
|
|
257
|
+
type TokenScriptKind = "nft" | "ft" | null;
|
|
258
|
+
/** Classify a token output script by SHAPE (not by a loose ref scan). */
|
|
259
|
+
declare function tokenScriptKind(scriptHex: string): TokenScriptKind;
|
|
226
260
|
|
|
227
261
|
/**
|
|
228
262
|
* ElectrumX WebSocket client.
|
|
@@ -631,15 +665,67 @@ interface TransferTokenParams {
|
|
|
631
665
|
network?: NetworkName;
|
|
632
666
|
}
|
|
633
667
|
/**
|
|
634
|
-
* Transfer a Glyph token (FT or NFT) to a new owner.
|
|
635
|
-
*
|
|
636
|
-
*
|
|
668
|
+
* Transfer a Glyph token (FT or NFT, including a WAVE name) to a new owner.
|
|
669
|
+
*
|
|
670
|
+
* Moves the UTXO whole: the token output's value is preserved, which for an FT
|
|
671
|
+
* preserves the amount. To send PART of an FT balance (which needs multi-UTXO
|
|
672
|
+
* accumulation and a change output), that is a different operation and this is
|
|
673
|
+
* not it.
|
|
637
674
|
*/
|
|
638
675
|
declare function transferToken(params: TransferTokenParams): Promise<{
|
|
639
676
|
txid: string;
|
|
640
677
|
hex: string;
|
|
641
678
|
ref: string | null;
|
|
642
679
|
}>;
|
|
680
|
+
interface TransferFungibleParams {
|
|
681
|
+
client: ElectrumClient;
|
|
682
|
+
/** Current owner address (signs the token inputs, pays the fee, gets change). */
|
|
683
|
+
address: string;
|
|
684
|
+
/** Current owner WIF. */
|
|
685
|
+
wif: string;
|
|
686
|
+
/**
|
|
687
|
+
* FT UTXOs to draw from. They must ALL be the same token — see the ref check
|
|
688
|
+
* in {@link transferFungible}. Pass the owner's whole balance for that ref;
|
|
689
|
+
* only as many as needed are spent.
|
|
690
|
+
*/
|
|
691
|
+
tokenUtxos: Utxo[];
|
|
692
|
+
/** Recipient address. */
|
|
693
|
+
toAddress: string;
|
|
694
|
+
/**
|
|
695
|
+
* Amount to send, in the token's base units. For a Glyph FT the amount IS the
|
|
696
|
+
* output's photon value — the covenant sums photons per code-script.
|
|
697
|
+
*/
|
|
698
|
+
amount: bigint;
|
|
699
|
+
/** Token-free RXD funding UTXOs for `address` to cover the fee. */
|
|
700
|
+
fundingUtxos: Utxo[];
|
|
701
|
+
feeRate?: bigint;
|
|
702
|
+
network?: NetworkName;
|
|
703
|
+
}
|
|
704
|
+
/**
|
|
705
|
+
* Send PART of a fungible balance, accumulating across UTXOs and returning the
|
|
706
|
+
* remainder as FT change.
|
|
707
|
+
*
|
|
708
|
+
* This is what {@link transferToken} cannot do: that moves one UTXO whole, so
|
|
709
|
+
* "send 50 of my 500" is inexpressible on it.
|
|
710
|
+
*
|
|
711
|
+
* The token outputs conserve the accumulated sum EXACTLY. That is not tidiness —
|
|
712
|
+
* the FT covenant enforces `sum(inputs) >= sum(outputs)` for its code-script
|
|
713
|
+
* (`OP_CODESCRIPTHASHVALUESUM_UTXOS ... OP_GREATERTHANOREQUAL OP_VERIFY`), so it
|
|
714
|
+
* permits burning and only forbids minting. Emit less than you spent and the
|
|
715
|
+
* difference is destroyed, silently and permanently. Hence the change output
|
|
716
|
+
* whenever the accumulation overshoots.
|
|
717
|
+
*
|
|
718
|
+
* The RXD arithmetic falls out of that conservation: token value in equals token
|
|
719
|
+
* value out, so the only surplus is the RXD funding, and `buildTx`'s change is
|
|
720
|
+
* exactly `funding - fee`.
|
|
721
|
+
*/
|
|
722
|
+
declare function transferFungible(params: TransferFungibleParams): Promise<{
|
|
723
|
+
txid: string;
|
|
724
|
+
hex: string;
|
|
725
|
+
ref: string;
|
|
726
|
+
sent: bigint;
|
|
727
|
+
change: bigint;
|
|
728
|
+
}>;
|
|
643
729
|
|
|
644
730
|
/**
|
|
645
731
|
* WAVE name resolution.
|
|
@@ -678,6 +764,92 @@ declare function waveResolve(name: string, options?: WaveResolveOptions): Promis
|
|
|
678
764
|
*/
|
|
679
765
|
declare function waveResolveAddress(name: string, options?: WaveResolveOptions): Promise<string | null>;
|
|
680
766
|
|
|
767
|
+
/**
|
|
768
|
+
* Token discovery — global, newest-first asset lists.
|
|
769
|
+
*
|
|
770
|
+
* Backed by the RXinDexer v4 discovery indexes (Glyph DB schema 4,
|
|
771
|
+
* deployed 2026-07-18):
|
|
772
|
+
* GET {base}/glyphs/recent — newest across ALL types
|
|
773
|
+
* GET {base}/glyphs/by-type/{typeId}?order=… — one type, ref or recent order
|
|
774
|
+
*
|
|
775
|
+
* Pages are cursor-paginated: pass the previous response's `nextCursor` back
|
|
776
|
+
* to fetch the next page. Cursors are opaque, URL-safe strings, and are
|
|
777
|
+
* order-specific — never reuse a cursor across a change of `order` or type.
|
|
778
|
+
*
|
|
779
|
+
* The killer pattern this enables is an incremental watermark sync: do one
|
|
780
|
+
* full walk, remember the newest `deploy_height` you saw, then on later runs
|
|
781
|
+
* page newest-first and STOP as soon as `deploy_height` drops below your
|
|
782
|
+
* watermark — you only ever pay for tokens minted since the last run.
|
|
783
|
+
*/
|
|
784
|
+
/** GlyphTokenType ids as served by the indexer (`type` field). */
|
|
785
|
+
declare const GLYPH_TOKEN_TYPE: {
|
|
786
|
+
readonly UNKNOWN: 0;
|
|
787
|
+
readonly FT: 1;
|
|
788
|
+
readonly NFT: 2;
|
|
789
|
+
readonly DAT: 3;
|
|
790
|
+
readonly DMINT: 4;
|
|
791
|
+
readonly WAVE: 5;
|
|
792
|
+
readonly CONTAINER: 6;
|
|
793
|
+
readonly AUTHORITY: 7;
|
|
794
|
+
};
|
|
795
|
+
type GlyphTokenTypeId = (typeof GLYPH_TOKEN_TYPE)[keyof typeof GLYPH_TOKEN_TYPE];
|
|
796
|
+
/**
|
|
797
|
+
* One token row from a discovery list. Only the stable, always-present core is
|
|
798
|
+
* typed; the indexer returns many more fields (supply, dMint, icon, attrs, …)
|
|
799
|
+
* which remain reachable through the index signature.
|
|
800
|
+
*/
|
|
801
|
+
interface GlyphTokenSummary {
|
|
802
|
+
/** Display ref: big-endian txid + "_" + vout (canonical output form). */
|
|
803
|
+
ref: string;
|
|
804
|
+
/** 72-hex internal ref (raw index key bytes) — feed this to get_by_ref-style APIs. */
|
|
805
|
+
ref_hex: string;
|
|
806
|
+
/** Primary token type id (see GLYPH_TOKEN_TYPE). */
|
|
807
|
+
type: number;
|
|
808
|
+
type_name: string;
|
|
809
|
+
/** Glyph protocol ids the token carries (1=FT, 2=NFT, 8=ENCRYPTED, 11=WAVE, …). */
|
|
810
|
+
protocols: number[];
|
|
811
|
+
name: string | null;
|
|
812
|
+
ticker: string | null;
|
|
813
|
+
deploy_height: number;
|
|
814
|
+
deploy_txid: string;
|
|
815
|
+
is_spent: boolean;
|
|
816
|
+
[extra: string]: unknown;
|
|
817
|
+
}
|
|
818
|
+
/** One page of a discovery list. */
|
|
819
|
+
interface TokenPage {
|
|
820
|
+
tokens: GlyphTokenSummary[];
|
|
821
|
+
/** Pass back as `cursor` to fetch the next page; null = no more pages. */
|
|
822
|
+
nextCursor: string | null;
|
|
823
|
+
}
|
|
824
|
+
/** Options shared by the discovery queries. */
|
|
825
|
+
interface DiscoveryOptions {
|
|
826
|
+
/** Max rows per page (indexer caps at 500). Default 100. */
|
|
827
|
+
limit?: number;
|
|
828
|
+
/** Opaque cursor from the previous page's `nextCursor`. */
|
|
829
|
+
cursor?: string;
|
|
830
|
+
/** REST base URL. Default https://radiantcore.org/api */
|
|
831
|
+
restBase?: string;
|
|
832
|
+
/** Inject a fetch implementation (tests / non-global-fetch runtimes). */
|
|
833
|
+
fetchImpl?: typeof fetch;
|
|
834
|
+
/** AbortSignal to cancel the request. */
|
|
835
|
+
signal?: AbortSignal;
|
|
836
|
+
}
|
|
837
|
+
/**
|
|
838
|
+
* Newest-deployed tokens across every type (or one type via `typeId`),
|
|
839
|
+
* newest-first.
|
|
840
|
+
*/
|
|
841
|
+
declare function getRecentTokens(options?: DiscoveryOptions & {
|
|
842
|
+
typeId?: GlyphTokenTypeId | number;
|
|
843
|
+
}): Promise<TokenPage>;
|
|
844
|
+
/**
|
|
845
|
+
* Tokens of one type. `order: "recent"` = newest-deployed first;
|
|
846
|
+
* `order: "ref"` (default, matches the pre-v4 API) = stable ref-hash order.
|
|
847
|
+
* Cursors are order-specific.
|
|
848
|
+
*/
|
|
849
|
+
declare function getTokensByType(typeId: GlyphTokenTypeId | number, options?: DiscoveryOptions & {
|
|
850
|
+
order?: "ref" | "recent";
|
|
851
|
+
}): Promise<TokenPage>;
|
|
852
|
+
|
|
681
853
|
/**
|
|
682
854
|
* Single, centralised access point to `@radiant-core/radiantjs`.
|
|
683
855
|
*
|
|
@@ -690,4 +862,4 @@ declare function waveResolveAddress(name: string, options?: WaveResolveOptions):
|
|
|
690
862
|
/** The raw radiantjs namespace, for advanced callers that need an escape hatch. */
|
|
691
863
|
declare const radiantjs: any;
|
|
692
864
|
|
|
693
|
-
export { type BuildTxInput, type BuildTxParams, type BuiltTx, DEFAULT_ELECTRUM_ENDPOINT, DEFAULT_REST_BASE, DUST_LIMIT, type DerivedKey, ElectrumClient, type ElectrumClientOptions, ElectrumError, type FundingSelection, GLYPH_PROTOCOL, type GlyphPayload, HDWallet, type HDWalletOptions, InsufficientFundsError, Keys, MIN_RELAY_FEE_RATE, type MintFtParams, type MintNftParams, type MintResult, type NetworkName, PHOTONS_PER_RXD, RADIANT_COIN_TYPE, RXD_DECIMALS, RadiantSdkError, type ScriptHashBalance, type SelectRxdFundingOptions, TokenBurnGuardError, type TransferTokenParams, type TxOutput, type Utxo, type UtxoRef, ValidationError, type WaveResolution, type WaveResolveOptions, addressToScriptHash, assertFundingSafe, buildRxdTransfer, buildTx, encodeGlyph, estimateFee, filterFundingCandidates, ftScript, isFundingSafe, isTokenBearing, mintFT, mintNFT, nftScript, p2pkhScript, packRef, parseTokenRef, photonsToRxd, radiantjs, rxdToPhotons, scriptHash, selectRxdFunding, sumValue, transferToken, unpackRef, waveLabel, waveResolve, waveResolveAddress, zeroRefs };
|
|
865
|
+
export { type BuildTxInput, type BuildTxParams, type BuiltTx, DEFAULT_ELECTRUM_ENDPOINT, DEFAULT_REST_BASE, DUST_LIMIT, type DerivedKey, type DiscoveryOptions, ElectrumClient, type ElectrumClientOptions, ElectrumError, type FundingSelection, GLYPH_PROTOCOL, GLYPH_TOKEN_TYPE, type GlyphPayload, type GlyphTokenSummary, type GlyphTokenTypeId, HDWallet, type HDWalletOptions, InsufficientFundsError, Keys, MIN_RELAY_FEE_RATE, type MintFtParams, type MintNftParams, type MintResult, type NetworkName, PHOTONS_PER_RXD, RADIANT_COIN_TYPE, RXD_DECIMALS, RadiantSdkError, type ScriptHashBalance, type SelectRxdFundingOptions, TokenBurnGuardError, type TokenPage, type TokenScriptKind, type TransferFungibleParams, type TransferTokenParams, type TxOutput, type Utxo, type UtxoRef, ValidationError, type WaveResolution, type WaveResolveOptions, addressToScriptHash, assertFundingSafe, buildRxdTransfer, buildTx, encodeGlyph, estimateFee, filterFundingCandidates, ftScript, getRecentTokens, getTokensByType, isFundingSafe, isTokenBearing, mintFT, mintNFT, nftScript, p2pkhScript, packRef, parseFtScript, parseNftScript, parseP2pkhScript, parseTokenRef, photonsToRxd, radiantjs, rxdToPhotons, scriptHash, selectRxdFunding, sumValue, tokenScriptKind, transferFungible, transferToken, unpackRef, waveLabel, waveResolve, waveResolveAddress, zeroRefs };
|
package/dist/index.d.ts
CHANGED
|
@@ -223,6 +223,40 @@ declare function unpackRef(ref: string): {
|
|
|
223
223
|
txid: string;
|
|
224
224
|
vout: number;
|
|
225
225
|
};
|
|
226
|
+
/** Owner address hash from a bare P2PKH script, if that's all it is. */
|
|
227
|
+
declare function parseP2pkhScript(scriptHex: string): {
|
|
228
|
+
addressHash?: string;
|
|
229
|
+
};
|
|
230
|
+
/**
|
|
231
|
+
* Parse an NFT output script into its singleton ref + owner hash.
|
|
232
|
+
*
|
|
233
|
+
* Matches the plain singleton:
|
|
234
|
+
* OP_PUSHINPUTREFSINGLETON <ref> OP_DROP <P2PKH>
|
|
235
|
+
* d8 <ref:72> 75 76a914 <h160:40> 88ac
|
|
236
|
+
*
|
|
237
|
+
* ...and the AUTH form that a mutable NFT (a WAVE name) is forced into after a
|
|
238
|
+
* target update — the mutable covenant makes the token output re-commit the
|
|
239
|
+
* mutable ref plus its scriptSig hash:
|
|
240
|
+
* ( OP_REQUIREINPUTREF <ref> <scriptSigHash> OP_2DROP )+ OP_STATESEPARATOR
|
|
241
|
+
* OP_PUSHINPUTREFSINGLETON <ref> OP_DROP <P2PKH>
|
|
242
|
+
*
|
|
243
|
+
* The singleton ref and owner are always the TRAILING `d8…/p2pkh`, so the
|
|
244
|
+
* optional preamble is skipped without changing what's captured. Returns {} for
|
|
245
|
+
* anything that isn't an NFT script.
|
|
246
|
+
*/
|
|
247
|
+
declare function parseNftScript(scriptHex: string): {
|
|
248
|
+
ref?: string;
|
|
249
|
+
addressHash?: string;
|
|
250
|
+
};
|
|
251
|
+
/** Parse an FT output script into its ref + owner hash. Returns {} if not an FT. */
|
|
252
|
+
declare function parseFtScript(scriptHex: string): {
|
|
253
|
+
ref?: string;
|
|
254
|
+
addressHash?: string;
|
|
255
|
+
};
|
|
256
|
+
/** What kind of token a script is, if any. */
|
|
257
|
+
type TokenScriptKind = "nft" | "ft" | null;
|
|
258
|
+
/** Classify a token output script by SHAPE (not by a loose ref scan). */
|
|
259
|
+
declare function tokenScriptKind(scriptHex: string): TokenScriptKind;
|
|
226
260
|
|
|
227
261
|
/**
|
|
228
262
|
* ElectrumX WebSocket client.
|
|
@@ -631,15 +665,67 @@ interface TransferTokenParams {
|
|
|
631
665
|
network?: NetworkName;
|
|
632
666
|
}
|
|
633
667
|
/**
|
|
634
|
-
* Transfer a Glyph token (FT or NFT) to a new owner.
|
|
635
|
-
*
|
|
636
|
-
*
|
|
668
|
+
* Transfer a Glyph token (FT or NFT, including a WAVE name) to a new owner.
|
|
669
|
+
*
|
|
670
|
+
* Moves the UTXO whole: the token output's value is preserved, which for an FT
|
|
671
|
+
* preserves the amount. To send PART of an FT balance (which needs multi-UTXO
|
|
672
|
+
* accumulation and a change output), that is a different operation and this is
|
|
673
|
+
* not it.
|
|
637
674
|
*/
|
|
638
675
|
declare function transferToken(params: TransferTokenParams): Promise<{
|
|
639
676
|
txid: string;
|
|
640
677
|
hex: string;
|
|
641
678
|
ref: string | null;
|
|
642
679
|
}>;
|
|
680
|
+
interface TransferFungibleParams {
|
|
681
|
+
client: ElectrumClient;
|
|
682
|
+
/** Current owner address (signs the token inputs, pays the fee, gets change). */
|
|
683
|
+
address: string;
|
|
684
|
+
/** Current owner WIF. */
|
|
685
|
+
wif: string;
|
|
686
|
+
/**
|
|
687
|
+
* FT UTXOs to draw from. They must ALL be the same token — see the ref check
|
|
688
|
+
* in {@link transferFungible}. Pass the owner's whole balance for that ref;
|
|
689
|
+
* only as many as needed are spent.
|
|
690
|
+
*/
|
|
691
|
+
tokenUtxos: Utxo[];
|
|
692
|
+
/** Recipient address. */
|
|
693
|
+
toAddress: string;
|
|
694
|
+
/**
|
|
695
|
+
* Amount to send, in the token's base units. For a Glyph FT the amount IS the
|
|
696
|
+
* output's photon value — the covenant sums photons per code-script.
|
|
697
|
+
*/
|
|
698
|
+
amount: bigint;
|
|
699
|
+
/** Token-free RXD funding UTXOs for `address` to cover the fee. */
|
|
700
|
+
fundingUtxos: Utxo[];
|
|
701
|
+
feeRate?: bigint;
|
|
702
|
+
network?: NetworkName;
|
|
703
|
+
}
|
|
704
|
+
/**
|
|
705
|
+
* Send PART of a fungible balance, accumulating across UTXOs and returning the
|
|
706
|
+
* remainder as FT change.
|
|
707
|
+
*
|
|
708
|
+
* This is what {@link transferToken} cannot do: that moves one UTXO whole, so
|
|
709
|
+
* "send 50 of my 500" is inexpressible on it.
|
|
710
|
+
*
|
|
711
|
+
* The token outputs conserve the accumulated sum EXACTLY. That is not tidiness —
|
|
712
|
+
* the FT covenant enforces `sum(inputs) >= sum(outputs)` for its code-script
|
|
713
|
+
* (`OP_CODESCRIPTHASHVALUESUM_UTXOS ... OP_GREATERTHANOREQUAL OP_VERIFY`), so it
|
|
714
|
+
* permits burning and only forbids minting. Emit less than you spent and the
|
|
715
|
+
* difference is destroyed, silently and permanently. Hence the change output
|
|
716
|
+
* whenever the accumulation overshoots.
|
|
717
|
+
*
|
|
718
|
+
* The RXD arithmetic falls out of that conservation: token value in equals token
|
|
719
|
+
* value out, so the only surplus is the RXD funding, and `buildTx`'s change is
|
|
720
|
+
* exactly `funding - fee`.
|
|
721
|
+
*/
|
|
722
|
+
declare function transferFungible(params: TransferFungibleParams): Promise<{
|
|
723
|
+
txid: string;
|
|
724
|
+
hex: string;
|
|
725
|
+
ref: string;
|
|
726
|
+
sent: bigint;
|
|
727
|
+
change: bigint;
|
|
728
|
+
}>;
|
|
643
729
|
|
|
644
730
|
/**
|
|
645
731
|
* WAVE name resolution.
|
|
@@ -678,6 +764,92 @@ declare function waveResolve(name: string, options?: WaveResolveOptions): Promis
|
|
|
678
764
|
*/
|
|
679
765
|
declare function waveResolveAddress(name: string, options?: WaveResolveOptions): Promise<string | null>;
|
|
680
766
|
|
|
767
|
+
/**
|
|
768
|
+
* Token discovery — global, newest-first asset lists.
|
|
769
|
+
*
|
|
770
|
+
* Backed by the RXinDexer v4 discovery indexes (Glyph DB schema 4,
|
|
771
|
+
* deployed 2026-07-18):
|
|
772
|
+
* GET {base}/glyphs/recent — newest across ALL types
|
|
773
|
+
* GET {base}/glyphs/by-type/{typeId}?order=… — one type, ref or recent order
|
|
774
|
+
*
|
|
775
|
+
* Pages are cursor-paginated: pass the previous response's `nextCursor` back
|
|
776
|
+
* to fetch the next page. Cursors are opaque, URL-safe strings, and are
|
|
777
|
+
* order-specific — never reuse a cursor across a change of `order` or type.
|
|
778
|
+
*
|
|
779
|
+
* The killer pattern this enables is an incremental watermark sync: do one
|
|
780
|
+
* full walk, remember the newest `deploy_height` you saw, then on later runs
|
|
781
|
+
* page newest-first and STOP as soon as `deploy_height` drops below your
|
|
782
|
+
* watermark — you only ever pay for tokens minted since the last run.
|
|
783
|
+
*/
|
|
784
|
+
/** GlyphTokenType ids as served by the indexer (`type` field). */
|
|
785
|
+
declare const GLYPH_TOKEN_TYPE: {
|
|
786
|
+
readonly UNKNOWN: 0;
|
|
787
|
+
readonly FT: 1;
|
|
788
|
+
readonly NFT: 2;
|
|
789
|
+
readonly DAT: 3;
|
|
790
|
+
readonly DMINT: 4;
|
|
791
|
+
readonly WAVE: 5;
|
|
792
|
+
readonly CONTAINER: 6;
|
|
793
|
+
readonly AUTHORITY: 7;
|
|
794
|
+
};
|
|
795
|
+
type GlyphTokenTypeId = (typeof GLYPH_TOKEN_TYPE)[keyof typeof GLYPH_TOKEN_TYPE];
|
|
796
|
+
/**
|
|
797
|
+
* One token row from a discovery list. Only the stable, always-present core is
|
|
798
|
+
* typed; the indexer returns many more fields (supply, dMint, icon, attrs, …)
|
|
799
|
+
* which remain reachable through the index signature.
|
|
800
|
+
*/
|
|
801
|
+
interface GlyphTokenSummary {
|
|
802
|
+
/** Display ref: big-endian txid + "_" + vout (canonical output form). */
|
|
803
|
+
ref: string;
|
|
804
|
+
/** 72-hex internal ref (raw index key bytes) — feed this to get_by_ref-style APIs. */
|
|
805
|
+
ref_hex: string;
|
|
806
|
+
/** Primary token type id (see GLYPH_TOKEN_TYPE). */
|
|
807
|
+
type: number;
|
|
808
|
+
type_name: string;
|
|
809
|
+
/** Glyph protocol ids the token carries (1=FT, 2=NFT, 8=ENCRYPTED, 11=WAVE, …). */
|
|
810
|
+
protocols: number[];
|
|
811
|
+
name: string | null;
|
|
812
|
+
ticker: string | null;
|
|
813
|
+
deploy_height: number;
|
|
814
|
+
deploy_txid: string;
|
|
815
|
+
is_spent: boolean;
|
|
816
|
+
[extra: string]: unknown;
|
|
817
|
+
}
|
|
818
|
+
/** One page of a discovery list. */
|
|
819
|
+
interface TokenPage {
|
|
820
|
+
tokens: GlyphTokenSummary[];
|
|
821
|
+
/** Pass back as `cursor` to fetch the next page; null = no more pages. */
|
|
822
|
+
nextCursor: string | null;
|
|
823
|
+
}
|
|
824
|
+
/** Options shared by the discovery queries. */
|
|
825
|
+
interface DiscoveryOptions {
|
|
826
|
+
/** Max rows per page (indexer caps at 500). Default 100. */
|
|
827
|
+
limit?: number;
|
|
828
|
+
/** Opaque cursor from the previous page's `nextCursor`. */
|
|
829
|
+
cursor?: string;
|
|
830
|
+
/** REST base URL. Default https://radiantcore.org/api */
|
|
831
|
+
restBase?: string;
|
|
832
|
+
/** Inject a fetch implementation (tests / non-global-fetch runtimes). */
|
|
833
|
+
fetchImpl?: typeof fetch;
|
|
834
|
+
/** AbortSignal to cancel the request. */
|
|
835
|
+
signal?: AbortSignal;
|
|
836
|
+
}
|
|
837
|
+
/**
|
|
838
|
+
* Newest-deployed tokens across every type (or one type via `typeId`),
|
|
839
|
+
* newest-first.
|
|
840
|
+
*/
|
|
841
|
+
declare function getRecentTokens(options?: DiscoveryOptions & {
|
|
842
|
+
typeId?: GlyphTokenTypeId | number;
|
|
843
|
+
}): Promise<TokenPage>;
|
|
844
|
+
/**
|
|
845
|
+
* Tokens of one type. `order: "recent"` = newest-deployed first;
|
|
846
|
+
* `order: "ref"` (default, matches the pre-v4 API) = stable ref-hash order.
|
|
847
|
+
* Cursors are order-specific.
|
|
848
|
+
*/
|
|
849
|
+
declare function getTokensByType(typeId: GlyphTokenTypeId | number, options?: DiscoveryOptions & {
|
|
850
|
+
order?: "ref" | "recent";
|
|
851
|
+
}): Promise<TokenPage>;
|
|
852
|
+
|
|
681
853
|
/**
|
|
682
854
|
* Single, centralised access point to `@radiant-core/radiantjs`.
|
|
683
855
|
*
|
|
@@ -690,4 +862,4 @@ declare function waveResolveAddress(name: string, options?: WaveResolveOptions):
|
|
|
690
862
|
/** The raw radiantjs namespace, for advanced callers that need an escape hatch. */
|
|
691
863
|
declare const radiantjs: any;
|
|
692
864
|
|
|
693
|
-
export { type BuildTxInput, type BuildTxParams, type BuiltTx, DEFAULT_ELECTRUM_ENDPOINT, DEFAULT_REST_BASE, DUST_LIMIT, type DerivedKey, ElectrumClient, type ElectrumClientOptions, ElectrumError, type FundingSelection, GLYPH_PROTOCOL, type GlyphPayload, HDWallet, type HDWalletOptions, InsufficientFundsError, Keys, MIN_RELAY_FEE_RATE, type MintFtParams, type MintNftParams, type MintResult, type NetworkName, PHOTONS_PER_RXD, RADIANT_COIN_TYPE, RXD_DECIMALS, RadiantSdkError, type ScriptHashBalance, type SelectRxdFundingOptions, TokenBurnGuardError, type TransferTokenParams, type TxOutput, type Utxo, type UtxoRef, ValidationError, type WaveResolution, type WaveResolveOptions, addressToScriptHash, assertFundingSafe, buildRxdTransfer, buildTx, encodeGlyph, estimateFee, filterFundingCandidates, ftScript, isFundingSafe, isTokenBearing, mintFT, mintNFT, nftScript, p2pkhScript, packRef, parseTokenRef, photonsToRxd, radiantjs, rxdToPhotons, scriptHash, selectRxdFunding, sumValue, transferToken, unpackRef, waveLabel, waveResolve, waveResolveAddress, zeroRefs };
|
|
865
|
+
export { type BuildTxInput, type BuildTxParams, type BuiltTx, DEFAULT_ELECTRUM_ENDPOINT, DEFAULT_REST_BASE, DUST_LIMIT, type DerivedKey, type DiscoveryOptions, ElectrumClient, type ElectrumClientOptions, ElectrumError, type FundingSelection, GLYPH_PROTOCOL, GLYPH_TOKEN_TYPE, type GlyphPayload, type GlyphTokenSummary, type GlyphTokenTypeId, HDWallet, type HDWalletOptions, InsufficientFundsError, Keys, MIN_RELAY_FEE_RATE, type MintFtParams, type MintNftParams, type MintResult, type NetworkName, PHOTONS_PER_RXD, RADIANT_COIN_TYPE, RXD_DECIMALS, RadiantSdkError, type ScriptHashBalance, type SelectRxdFundingOptions, TokenBurnGuardError, type TokenPage, type TokenScriptKind, type TransferFungibleParams, type TransferTokenParams, type TxOutput, type Utxo, type UtxoRef, ValidationError, type WaveResolution, type WaveResolveOptions, addressToScriptHash, assertFundingSafe, buildRxdTransfer, buildTx, encodeGlyph, estimateFee, filterFundingCandidates, ftScript, getRecentTokens, getTokensByType, isFundingSafe, isTokenBearing, mintFT, mintNFT, nftScript, p2pkhScript, packRef, parseFtScript, parseNftScript, parseP2pkhScript, parseTokenRef, photonsToRxd, radiantjs, rxdToPhotons, scriptHash, selectRxdFunding, sumValue, tokenScriptKind, transferFungible, transferToken, unpackRef, waveLabel, waveResolve, waveResolveAddress, zeroRefs };
|
package/dist/index.js
CHANGED
|
@@ -43,6 +43,11 @@ var MIN_RELAY_FEE_RATE = {
|
|
|
43
43
|
testnet: 1000n,
|
|
44
44
|
regtest: 1000n
|
|
45
45
|
};
|
|
46
|
+
var MAX_REASONABLE_FEE_RATE = {
|
|
47
|
+
mainnet: MIN_RELAY_FEE_RATE.mainnet * 2n,
|
|
48
|
+
testnet: MIN_RELAY_FEE_RATE.testnet * 2n,
|
|
49
|
+
regtest: MIN_RELAY_FEE_RATE.regtest * 2n
|
|
50
|
+
};
|
|
46
51
|
var DUST_LIMIT = 1000n;
|
|
47
52
|
var TOKEN_OUTPUT_VALUE = 1n;
|
|
48
53
|
var DEFAULT_ELECTRUM_ENDPOINT = {
|
|
@@ -241,6 +246,25 @@ function unpackRef(ref) {
|
|
|
241
246
|
const vout = buf.readUInt32LE(32);
|
|
242
247
|
return { txid, vout };
|
|
243
248
|
}
|
|
249
|
+
function parseP2pkhScript(scriptHex) {
|
|
250
|
+
const [, addressHash] = scriptHex.toLowerCase().match(/^76a914([0-9a-f]{40})88ac$/) || [];
|
|
251
|
+
return { addressHash };
|
|
252
|
+
}
|
|
253
|
+
function parseNftScript(scriptHex) {
|
|
254
|
+
const pattern = /^(?:d1[0-9a-f]{72}20[0-9a-f]{64}6d)*(?:bd)?d8([0-9a-f]{72})7576a914([0-9a-f]{40})88ac$/;
|
|
255
|
+
const [, ref, addressHash] = scriptHex.toLowerCase().match(pattern) || [];
|
|
256
|
+
return { ref, addressHash };
|
|
257
|
+
}
|
|
258
|
+
function parseFtScript(scriptHex) {
|
|
259
|
+
const pattern = /^76a914([0-9a-f]{40})88acbdd0([0-9a-f]{72})dec0e9aa76e378e4a269e69d$/;
|
|
260
|
+
const [, addressHash, ref] = scriptHex.toLowerCase().match(pattern) || [];
|
|
261
|
+
return { ref, addressHash };
|
|
262
|
+
}
|
|
263
|
+
function tokenScriptKind(scriptHex) {
|
|
264
|
+
if (parseNftScript(scriptHex).ref) return "nft";
|
|
265
|
+
if (parseFtScript(scriptHex).ref) return "ft";
|
|
266
|
+
return null;
|
|
267
|
+
}
|
|
244
268
|
|
|
245
269
|
// src/client.ts
|
|
246
270
|
var WS_OPEN = 1;
|
|
@@ -646,6 +670,17 @@ function sumValue(utxos) {
|
|
|
646
670
|
}
|
|
647
671
|
|
|
648
672
|
// src/tx.ts
|
|
673
|
+
function assertSaneFee(tx, network) {
|
|
674
|
+
const sizeBytes = BigInt(tx.toString().length / 2);
|
|
675
|
+
const reference = MAX_REASONABLE_FEE_RATE[network];
|
|
676
|
+
const ceiling = sizeBytes * reference * 12n / 10n;
|
|
677
|
+
const actual = BigInt(tx.getFee?.() ?? 0);
|
|
678
|
+
if (actual > ceiling) {
|
|
679
|
+
throw new ValidationError(
|
|
680
|
+
`buildTx: fee ${actual} photons is above the sanity ceiling ${ceiling} for a ${sizeBytes}-byte tx (${network} reference ${reference} photons/byte). Refusing to build \u2014 this is almost always a units mistake or an over-funded change-less tx, and the excess would be paid to miners irrecoverably.`
|
|
681
|
+
);
|
|
682
|
+
}
|
|
683
|
+
}
|
|
649
684
|
function bn(value) {
|
|
650
685
|
return new crypto.BN(value.toString());
|
|
651
686
|
}
|
|
@@ -720,6 +755,7 @@ function buildTx(params) {
|
|
|
720
755
|
}
|
|
721
756
|
tx.sign(privKeys[0]);
|
|
722
757
|
if (typeof tx.seal === "function") tx.seal();
|
|
758
|
+
assertSaneFee(tx, network);
|
|
723
759
|
const hex = tx.toString();
|
|
724
760
|
const resolvedOutputs = tx.outputs.map((o) => ({
|
|
725
761
|
script: o.script.toHex(),
|
|
@@ -803,16 +839,14 @@ function parseTokenRef(scriptHex) {
|
|
|
803
839
|
}
|
|
804
840
|
return null;
|
|
805
841
|
}
|
|
806
|
-
function
|
|
807
|
-
const
|
|
808
|
-
|
|
809
|
-
const
|
|
810
|
-
if (
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
}
|
|
815
|
-
return scriptHex.replace(re, `76a914${newPkh}88ac`);
|
|
842
|
+
function tokenTransferScript(scriptHex, toAddress, network) {
|
|
843
|
+
const nft = parseNftScript(scriptHex);
|
|
844
|
+
if (nft.ref) return nftScript(toAddress, nft.ref, network);
|
|
845
|
+
const ft = parseFtScript(scriptHex);
|
|
846
|
+
if (ft.ref) return ftScript(toAddress, ft.ref, network);
|
|
847
|
+
throw new ValidationError(
|
|
848
|
+
"transferToken: tokenUtxo.script is not a recognised NFT or FT output script"
|
|
849
|
+
);
|
|
816
850
|
}
|
|
817
851
|
async function commitReveal(base, payload, refType, buildRevealOutputScript, tokenOutputValue) {
|
|
818
852
|
const network = base.network ?? "mainnet";
|
|
@@ -926,8 +960,8 @@ async function transferToken(params) {
|
|
|
926
960
|
"transferToken: tokenUtxo.script is required to rebuild the token output"
|
|
927
961
|
);
|
|
928
962
|
}
|
|
929
|
-
const newScript =
|
|
930
|
-
const ref =
|
|
963
|
+
const newScript = tokenTransferScript(tokenUtxo.script, params.toAddress, network);
|
|
964
|
+
const ref = parseNftScript(tokenUtxo.script).ref ?? parseFtScript(tokenUtxo.script).ref ?? null;
|
|
931
965
|
const selection = selectRxdFunding(params.fundingUtxos, 0n, feeRate, {
|
|
932
966
|
baseOutputCount: 1,
|
|
933
967
|
// the token output
|
|
@@ -961,6 +995,76 @@ async function transferToken(params) {
|
|
|
961
995
|
await params.client.broadcastTx(built.hex);
|
|
962
996
|
return { txid: built.txid, hex: built.hex, ref };
|
|
963
997
|
}
|
|
998
|
+
async function transferFungible(params) {
|
|
999
|
+
const network = params.network ?? "mainnet";
|
|
1000
|
+
const feeRate = params.feeRate ?? MIN_RELAY_FEE_RATE[network];
|
|
1001
|
+
const { amount, tokenUtxos } = params;
|
|
1002
|
+
if (amount <= 0n) {
|
|
1003
|
+
throw new ValidationError("transferFungible: amount must be positive");
|
|
1004
|
+
}
|
|
1005
|
+
if (!tokenUtxos.length) {
|
|
1006
|
+
throw new ValidationError("transferFungible: no token UTXOs given");
|
|
1007
|
+
}
|
|
1008
|
+
let ref;
|
|
1009
|
+
for (const u of tokenUtxos) {
|
|
1010
|
+
const parsed = u.script ? parseFtScript(u.script) : {};
|
|
1011
|
+
if (!parsed.ref) {
|
|
1012
|
+
throw new ValidationError(
|
|
1013
|
+
`transferFungible: ${u.txid}:${u.vout} is not an FT output (its script must be the on-chain ftScript)`
|
|
1014
|
+
);
|
|
1015
|
+
}
|
|
1016
|
+
if (ref && parsed.ref !== ref) {
|
|
1017
|
+
throw new ValidationError(
|
|
1018
|
+
"transferFungible: token UTXOs are for different tokens \u2014 refusing to mix refs, it would burn one of them"
|
|
1019
|
+
);
|
|
1020
|
+
}
|
|
1021
|
+
ref = parsed.ref;
|
|
1022
|
+
}
|
|
1023
|
+
const sorted = [...tokenUtxos].sort((a, b) => a.value < b.value ? 1 : a.value > b.value ? -1 : 0);
|
|
1024
|
+
const spend = [];
|
|
1025
|
+
let sum = 0n;
|
|
1026
|
+
for (const u of sorted) {
|
|
1027
|
+
spend.push(u);
|
|
1028
|
+
sum += u.value;
|
|
1029
|
+
if (sum >= amount) break;
|
|
1030
|
+
}
|
|
1031
|
+
if (sum < amount) {
|
|
1032
|
+
throw new ValidationError(
|
|
1033
|
+
`transferFungible: insufficient token balance \u2014 have ${sum}, need ${amount}`
|
|
1034
|
+
);
|
|
1035
|
+
}
|
|
1036
|
+
const change = sum - amount;
|
|
1037
|
+
const outputs = [{ script: ftScript(params.toAddress, ref, network), value: amount }];
|
|
1038
|
+
if (change > 0n) {
|
|
1039
|
+
outputs.push({ script: ftScript(params.address, ref, network), value: change });
|
|
1040
|
+
}
|
|
1041
|
+
const selection = selectRxdFunding(params.fundingUtxos, 0n, feeRate, {
|
|
1042
|
+
baseOutputCount: outputs.length,
|
|
1043
|
+
extraInputBytes: BigInt(148 * spend.length),
|
|
1044
|
+
// the token inputs we add below
|
|
1045
|
+
withChange: true
|
|
1046
|
+
});
|
|
1047
|
+
const inputs = [
|
|
1048
|
+
...spend.map((u) => ({ txid: u.txid, vout: u.vout, value: u.value, script: u.script })),
|
|
1049
|
+
...selection.inputs.map((u) => ({
|
|
1050
|
+
txid: u.txid,
|
|
1051
|
+
vout: u.vout,
|
|
1052
|
+
value: u.value,
|
|
1053
|
+
script: u.script || void 0
|
|
1054
|
+
}))
|
|
1055
|
+
];
|
|
1056
|
+
const built = buildTx({
|
|
1057
|
+
address: params.address,
|
|
1058
|
+
wif: params.wif,
|
|
1059
|
+
inputs,
|
|
1060
|
+
outputs,
|
|
1061
|
+
addChange: true,
|
|
1062
|
+
feeRate,
|
|
1063
|
+
network
|
|
1064
|
+
});
|
|
1065
|
+
await params.client.broadcastTx(built.hex);
|
|
1066
|
+
return { txid: built.txid, hex: built.hex, ref, sent: amount, change };
|
|
1067
|
+
}
|
|
964
1068
|
|
|
965
1069
|
// src/wave.ts
|
|
966
1070
|
function waveLabel(name) {
|
|
@@ -1010,6 +1114,61 @@ async function waveResolveAddress(name, options = {}) {
|
|
|
1010
1114
|
return r.registered ? r.address ?? null : null;
|
|
1011
1115
|
}
|
|
1012
1116
|
|
|
1013
|
-
|
|
1117
|
+
// src/discovery.ts
|
|
1118
|
+
var GLYPH_TOKEN_TYPE = {
|
|
1119
|
+
UNKNOWN: 0,
|
|
1120
|
+
FT: 1,
|
|
1121
|
+
NFT: 2,
|
|
1122
|
+
DAT: 3,
|
|
1123
|
+
DMINT: 4,
|
|
1124
|
+
WAVE: 5,
|
|
1125
|
+
CONTAINER: 6,
|
|
1126
|
+
AUTHORITY: 7
|
|
1127
|
+
};
|
|
1128
|
+
async function fetchTokenPage(path, params, options, fnName) {
|
|
1129
|
+
const base = (options.restBase ?? DEFAULT_REST_BASE).replace(/\/+$/, "");
|
|
1130
|
+
const doFetch = options.fetchImpl ?? globalThis.fetch;
|
|
1131
|
+
if (typeof doFetch !== "function") {
|
|
1132
|
+
throw new ValidationError(
|
|
1133
|
+
`${fnName}: no fetch implementation available; pass options.fetchImpl`
|
|
1134
|
+
);
|
|
1135
|
+
}
|
|
1136
|
+
const query = new URLSearchParams();
|
|
1137
|
+
for (const [k, v] of Object.entries(params)) {
|
|
1138
|
+
if (v !== void 0) query.set(k, String(v));
|
|
1139
|
+
}
|
|
1140
|
+
const qs = query.toString();
|
|
1141
|
+
const url = `${base}${path}${qs ? `?${qs}` : ""}`;
|
|
1142
|
+
const res = await doFetch(url, { signal: options.signal });
|
|
1143
|
+
if (!res.ok) {
|
|
1144
|
+
throw new ValidationError(`${fnName}: HTTP ${res.status} ${res.statusText}`);
|
|
1145
|
+
}
|
|
1146
|
+
const data = await res.json();
|
|
1147
|
+
return {
|
|
1148
|
+
tokens: Array.isArray(data?.tokens) ? data.tokens : [],
|
|
1149
|
+
nextCursor: data?.next_cursor ?? null
|
|
1150
|
+
};
|
|
1151
|
+
}
|
|
1152
|
+
async function getRecentTokens(options = {}) {
|
|
1153
|
+
return fetchTokenPage(
|
|
1154
|
+
"/glyphs/recent",
|
|
1155
|
+
{ limit: options.limit, cursor: options.cursor, type_id: options.typeId },
|
|
1156
|
+
options,
|
|
1157
|
+
"getRecentTokens"
|
|
1158
|
+
);
|
|
1159
|
+
}
|
|
1160
|
+
async function getTokensByType(typeId, options = {}) {
|
|
1161
|
+
if (!Number.isInteger(typeId) || typeId < 0 || typeId > 7) {
|
|
1162
|
+
throw new ValidationError(`getTokensByType: invalid typeId ${typeId}`);
|
|
1163
|
+
}
|
|
1164
|
+
return fetchTokenPage(
|
|
1165
|
+
`/glyphs/by-type/${typeId}`,
|
|
1166
|
+
{ limit: options.limit, cursor: options.cursor, order: options.order },
|
|
1167
|
+
options,
|
|
1168
|
+
"getTokensByType"
|
|
1169
|
+
);
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
export { DEFAULT_ELECTRUM_ENDPOINT, DEFAULT_REST_BASE, DUST_LIMIT, ElectrumClient, ElectrumError, GLYPH_PROTOCOL, GLYPH_TOKEN_TYPE, HDWallet, InsufficientFundsError, Keys, MIN_RELAY_FEE_RATE, PHOTONS_PER_RXD, RADIANT_COIN_TYPE, RXD_DECIMALS, RadiantSdkError, TokenBurnGuardError, ValidationError, addressToScriptHash, assertFundingSafe, buildRxdTransfer, buildTx, encodeGlyph, estimateFee, filterFundingCandidates, ftScript, getRecentTokens, getTokensByType, isFundingSafe, isTokenBearing, mintFT, mintNFT, nftScript, p2pkhScript, packRef, parseFtScript, parseNftScript, parseP2pkhScript, parseTokenRef, photonsToRxd, radiantjs, rxdToPhotons, scriptHash, selectRxdFunding, sumValue, tokenScriptKind, transferFungible, transferToken, unpackRef, waveLabel, waveResolve, waveResolveAddress, zeroRefs };
|
|
1014
1173
|
//# sourceMappingURL=index.js.map
|
|
1015
1174
|
//# sourceMappingURL=index.js.map
|