UNPKG

ecash-agora

Version:

Library for interacting with the eCash Agora protocol

373 lines 13.8 kB
import { ChronikClient, Token, WsEndpoint } from 'chronik-client'; import { Op, OutPoint, Script, Tx, TxBuilderInput, TxInput } from 'ecash-lib'; import { Wallet } from 'ecash-wallet'; import { AgoraBroadcastParams } from './broadcast'; import { AgoraOneshot } from './oneshot.js'; import { AgoraPartial, AgoraPartialParams } from './partial.js'; /** Offer variant, determines the Script used to enforce the offer */ export type AgoraOfferVariant = { type: 'ONESHOT'; params: AgoraOneshot; } | { type: 'PARTIAL'; params: AgoraPartial; }; /** Status of the offer, i.e. if it open/taken/canceled */ export type AgoraOfferStatus = 'OPEN' | 'TAKEN' | 'CANCELED'; /** If an offer is TAKEN */ export interface TakenInfo { /** satoshis paid in taking an offer */ sats: bigint; /** * amount of token purchased in atoms aka base tokens */ atoms: bigint; /** taker outputScript as a hex string*/ takerScriptHex: string; } /** * Individual token offer on the Agora, i.e. one UTXO offering tokens. * * It can be used to accept or cancel the offer. */ export declare class AgoraOffer { variant: AgoraOfferVariant; outpoint: OutPoint; txBuilderInput: TxInput; token: Token; status: AgoraOfferStatus; takenInfo?: TakenInfo; constructor(params: { variant: AgoraOfferVariant; outpoint: OutPoint; txBuilderInput: TxInput; token: Token; status: AgoraOfferStatus; takenInfo?: TakenInfo; }); /** * Build and broadcast an acceptTx, effectively taking this offer. * * Agora offers are UTXOs on the blockchain that can be accepted by anyone * sending sufficient satoshis to a required output. * * `fuelInputs` has to provide enough sats for this offer to cover ask + tx fee. * */ acceptTx(params: { /** * Arbitrary secret key to sign the accept tx with. Recommended to set * this to a random key. Must be paired with covenantSk. **/ covenantSk: Uint8Array; /** * Arbitrary public key to sign the accept tx with, must be paired with * covenantSk. **/ covenantPk: Uint8Array; /** * Inputs fueling this tx to cover tx fee and asked sats for the * enforced outputs. Must have signatory and input.signData set * correctly. If it is set incorrectly, may fail silently and build an * invalid tx, failing at broadcast. * * The free sats of these inputs must be at least askedSats + acceptFeeSats. **/ fuelInputs: TxBuilderInput[]; /** Script to send the tokens and the leftover sats (if any) to. */ recipientScript: Script; /** For partial offers: Number of accepted atoms (base tokens) */ acceptedAtoms?: bigint; /** Dust amount to use for the token output. */ dustSats?: bigint; /** Fee per kB to use when building the tx. */ feePerKb?: bigint; /** Allow accepting an offer such that the remaining quantity is unacceptable */ allowUnspendable?: boolean; }): Tx; /** * Build and broadcast a tx that accepts an agora offer * according to user-provided params, to a user-provided * ecash-wallet Wallet * * NB there is no option to "only build" by passing a Wallet, but this can be * accomplished using the acceptTx method above */ take( /** * NB these params are identical to the params required for acceptTx method above, * except * * 1) We require a Wallet * 2) We get fuelInputs from the provided wallet instead of accepting them * as a param */ params: { /** * An initialized Wallet from ecash-wallet * This is the wallet that will take the offer */ wallet: Wallet; /** * Arbitrary secret key to sign the accept tx with. Recommended to set * this to a random key. Must be paired with covenantSk. */ covenantSk: Uint8Array; /** * Arbitrary public key to sign the accept tx with, must be paired with * covenantSk. */ covenantPk: Uint8Array; /** For partial offers: Number of accepted atoms (base tokens) */ acceptedAtoms?: bigint; /** Dust amount to use for the token output. */ dustSats?: bigint; /** Fee per kB to use when building the tx. */ feePerKb?: bigint; /** Allow accepting an offer such that the remaining quantity is unacceptable */ allowUnspendable?: boolean; } & AgoraBroadcastParams): Promise<{ success: boolean; broadcasted: string[]; unbroadcasted: string[]; errors: string[]; } | { success: boolean; broadcasted: string[]; unbroadcasted?: undefined; errors?: undefined; }>; /** * How many extra satoshis are required to fuel this offer so it can be * broadcast on the network, excluding the asked sats. * This should be displayed to the user as network fee. * The total required input amount is askedSats + acceptFeeSats. **/ acceptFeeSats(params: { /** Script to send the tokens and the leftover sats (if any) to. */ recipientScript: Script; /** Extra inputs */ extraInputs?: TxBuilderInput[]; /** Fee per kB to use when building the tx. */ feePerKb?: bigint; acceptedAtoms?: bigint; }): bigint; private _acceptTxBuilder; /** * Build a tx canceling the offer. * * An offer can only be cancelled using the secret key that created it. * * `fuelInputs` must cover the tx fee, you can calculate it with cancelFeeSats. **/ cancelTx(params: { /** * Cancel secret key of the offer, must be paired with the cancelPk of * the offer. **/ cancelSk: Uint8Array; /** * Inputs fueling this tx with sats. Must have signatory and * input.signData set correctly. If it is set incorrectly, may fail * silently and build an invalid tx, failing at broadcast. * * The free sats of these inputs must be at least cancelFeeSats. **/ fuelInputs: TxBuilderInput[]; /** Script to send canceled tokens and the leftover sats (if any) to. */ recipientScript: Script; /** Dust amount to use for the token output. */ dustSats?: bigint; /** Fee per kB to use when building the tx. */ feePerKb?: bigint; }): Tx; /** * Convenience method to cancel an offer using a Wallet * The cancel tx is broadcast by the Wallet and canceled * tokens / leftover sats are returned to this wallet * * NB this wallet must have the correct cancelSk to * actually cancel the offer, otherwise expect an error * * NB you can see in the oneshot.test.ts that cancelTx can be used * to "relist" an agora oneshot offer; this method will not do this * * Though we could add a relist() method to also simplify that procedure */ cancel(params: { /** * An initialized Wallet from ecash-wallet * This is the wallet that will take the offer * * Note this wallet must have the correct cancelSk * for the method to work * * This is also the wallet that will receive canceled * tokens and leftover sats */ wallet: Wallet; /** Dust amount to use for the token output. */ dustSats?: bigint; /** Fee per kB to use when building the tx. */ feePerKb?: bigint; } & AgoraBroadcastParams): Promise<{ success: boolean; broadcasted: string[]; unbroadcasted?: string[]; errors?: string[]; }>; /** * Update an AgoraOffer by changing its price or quantity * * We use the method name "relist" as this method will cancel the existing AgoraOffer * and create a new one. What it is doing is closer to "updating" ... but we do * in fact relist and lose this AgoraOffer * * Under the hood, this is canceling an offer and relisting it in a single tx * * While this is technically possible to do on SLP, e.g. you can see * an example in oneshot.test.ts, for now we only support ALP * as it is easier to implement and anticipated to have more use cases */ relist(params: { /** * An initialized Wallet from ecash-wallet * This is the wallet that will take the offer * * Note this wallet must have the correct cancelSk * for the method to work * * This is also the wallet that will receive canceled * tokens and leftover sats */ wallet: Wallet; /** * The updated offer to relist * * After 64-bit ints, we may consider simply accepting updated terms like * priceNanoSatsPerAtom and offeredAtoms * * But for now, we can't assure that the created offer will reasonably reflect * user wants, so we accept their already-prepared offer */ updatedPartial: AgoraPartial; /** Dust amount to use for the token output. */ dustSats?: bigint; /** Fee per kB to use when building the tx. */ feePerKb?: bigint; } & AgoraBroadcastParams): Promise<{ success: boolean; broadcasted: string[]; unbroadcasted?: string[]; errors?: string[]; }>; /** * How many extra satoshis are required to fuel cancelling this offer, * so the cancel tx can be broadcast on the network, excluding the asked * sats and a dust amount to receive the tokens. * * extraInputs can be used to add an ad input so we have the correct * estimate in case of a cancel + reoffer. * * This should be displayed to the user as cancellation network fee. * The total required sats input amount is returned by this function. **/ cancelFeeSats(params: { /** Script to send the tokens and the leftover sats (if any) to. */ recipientScript: Script; /** Extra inputs */ extraInputs?: TxBuilderInput[]; /** Fee per kB to use when building the tx. */ feePerKb?: bigint; }): bigint; private _cancelTxBuilder; /** * How many satoshis are asked to accept this offer, excluding tx fees. * This is what should be displayed to the user as the price. **/ askedSats(acceptedAtoms?: bigint): bigint; } /** Which txs to query (confirmed, unconfirmed, reverse history) */ export type TxHistoryTable = 'CONFIRMED' | 'UNCONFIRMED' | 'HISTORY'; export type AgoraQueryParamVariants = { type: 'TOKEN_ID'; tokenId: string; } | { type: 'GROUP_TOKEN_ID'; groupTokenId: string; } | { type: 'PUBKEY'; pubkeyHex: string; }; /** Params which Agora txs to query */ export type AgoraHistoryParams = AgoraQueryParamVariants & { table: TxHistoryTable; page?: number; pageSize?: number; }; /** Queried offers from the history */ export interface AgoraHistoryResult { offers: AgoraOffer[]; numTxs: number; numPages: number; } /** * Enables access to Agora, via Chronik instances that have the "agora" plugin * loaded. * * See agora.py. **/ export declare class Agora { private chronik; private plugin; private dustSats; /** * Create an Agora instance. The provided Chronik instance must have the * "agora" plugin loaded. **/ constructor(chronik: ChronikClient, dustSats?: bigint); /** * Query all the token IDs, fungible and non-fungible ones, that have active * Agora offers. **/ allOfferedTokenIds(): Promise<string[]>; /** Query all fungible token IDs that have active Agora offers. */ offeredFungibleTokenIds(): Promise<string[]>; /** * Query all token IDs of groups of non-fungible tokens that have active * Agora offers. **/ offeredGroupTokenIds(): Promise<string[]>; /** Query all active offers by token ID. */ activeOffersByTokenId(tokenId: string): Promise<AgoraOffer[]>; /** Query all active offers by group token ID. */ activeOffersByGroupTokenId(groupTokenId: string): Promise<AgoraOffer[]>; /** Query all active offers with the given cancel pubkey. */ activeOffersByPubKey(pubkeyHex: string): Promise<AgoraOffer[]>; /** * Query historic offers (paginated) * * These are basically the "candlesticks" of a specific token (and also the * cancelled offers, but those would have to be ignored). * Offers can also be queried by pubkey, giving a history of user's offers. **/ historicOffers(params: AgoraHistoryParams): Promise<AgoraHistoryResult>; /** Subscribe to updates from the websocket for some params */ subscribeWs(ws: WsEndpoint, params: AgoraQueryParamVariants): void; /** Unsubscribe from updates from the websocket for some params */ unsubscribeWs(ws: WsEndpoint, params: AgoraQueryParamVariants): void; /** * Build a safe AgoraPartial for the given parameters. * * This looks at the blockchain to avoid creating an identical offer, by * tweaking the enforcedLockTime. */ selectParams(params: Omit<AgoraPartialParams, 'enforcedLockTime'> | AgoraPartial, scriptIntegerBits?: bigint): Promise<AgoraPartial>; private _groupHex; private _allTokenIdsByPrefix; private _activeOffersByGroup; private _parseOfferUtxo; private _parseOneshotOfferUtxo; private _parsePartialOfferUtxo; } export declare function scriptOps(script: Script): Op[]; //# sourceMappingURL=agora.d.ts.map