UNPKG

ordinalsbot

Version:
450 lines (416 loc) 16.3 kB
import { MarketPlaceClient } from "./marketplaceClient"; import { ClientOptions, InscriptionEnv, InscriptionEnvNetwork } from "./types"; import { MarketplaceConfirmPaddingOutputsRequest, MarketplaceConfirmPaddingOutputsResponse, MarketplaceCreateListingRequest, MarketplaceCreateListingResponse, MarketplaceCreateOfferRequest, MarketplaceCreateOfferResponse, MarketplaceSetupPaddingOutputsRequest, MarketplaceSetupPaddingOutputsResponse, MarketplaceCreateRequest, MarketplaceCreateResponse, MarketplaceGetListingResponse, MarketplaceSubmitOfferRequest, MarketplaceSubmitOfferResponse, MarketplaceGetListingRequest, MarketplaceSaveListingRequest, MarketplaceSaveListingResponse, MarketplaceTransferRequest, MarketplaceTransferAPIResponse, WALLET_PROVIDER, MarketplaceConfirmListingRequest, MarketplaceConfirmListingResponse, MarketplaceReListingRequest, MarketplaceReListingResponse, MarketplaceConfirmReListResponse, MarketplaceConfirmReListRequest, MarketplaceDeListRequest, MarketplaceDeListAPIResponse, MarketplaceConfirmDeListRequest, MarketplaceConfirmDeListResponse } from "./types/marketplace_types"; import * as bitcoin from 'bitcoinjs-lib'; import { BitcoinNetworkType, SignTransactionResponse, signTransaction } from 'sats-connect'; export class MarketPlace { private network: BitcoinNetworkType; private marketplaceInstance!: MarketPlaceClient; /** * Creates an instance of MarketPlace. * @param {string} key - The API key for authentication. * @param {InscriptionEnv} [environment='mainnet'] - The environment (e.g., "testnet" , "mainnet", "signet") (optional, defaults to mainnet). * @param {ClientOptions} [options] - Options for enabling L402 support. */ constructor(key: string = "", environment: InscriptionEnv = InscriptionEnvNetwork.mainnet, options?: ClientOptions) { if (this.marketplaceInstance !== undefined) { console.error("marketplace constructore was called multiple times"); return; } environment = InscriptionEnvNetwork[environment]??InscriptionEnvNetwork.mainnet; switch (environment) { case InscriptionEnvNetwork.mainnet: this.network = BitcoinNetworkType.Mainnet; break; case InscriptionEnvNetwork.testnet: this.network = BitcoinNetworkType.Testnet; break; case InscriptionEnvNetwork.signet: // @ts-ignore this.network = BitcoinNetworkType.Signet; break; default: this.network = BitcoinNetworkType.Mainnet break; } this.marketplaceInstance = new MarketPlaceClient(key, environment, options); } createMarketplace( createMarketplaceRequest: MarketplaceCreateRequest ): Promise<MarketplaceCreateResponse> { return this.marketplaceInstance.createMarketPlace(createMarketplaceRequest); } /** * Create a new listing on the marketplace. * @param {MarketplaceCreateListingRequest} createListingRequest The request object containing information about the listing. * @returns {Promise<MarketplaceCreateListingResponse | MarketplaceConfirmListingResponse>} A promise that resolves to either a create listing response or a confirm listing response. */ async createListing( createListingRequest: MarketplaceCreateListingRequest ): Promise<MarketplaceCreateListingResponse | MarketplaceConfirmListingResponse> { try { if (!createListingRequest.walletProvider) { return await this.marketplaceInstance.createListing(createListingRequest); } else if (createListingRequest.walletProvider === WALLET_PROVIDER.xverse) { const { psbt } = await this.marketplaceInstance.createListing(createListingRequest); // Create the payload for signing the seller transaction if (!createListingRequest.sellerOrdinalAddress) { throw new Error('No seller address provided'); } const inputIndices = createListingRequest.sellerOrdinals.map((item, index) => index); const sellerInput = { address: createListingRequest.sellerOrdinalAddress, signingIndexes: inputIndices, sigHash: bitcoin.Transaction.SIGHASH_SINGLE | bitcoin.Transaction.SIGHASH_ANYONECANPAY, }; const payload = { network: { type: this.network }, message: 'Sign Seller Transaction', psbtBase64: psbt, broadcast: false, inputsToSign: [sellerInput], }; return new Promise((resolve, reject) => { signTransaction({ payload, onFinish: async (response) => { try { // ordinal ids to confirm the listing const sellerOrdinalIds = createListingRequest.sellerOrdinals.map((item, index) => item.id) const confirmListingPayload: MarketplaceConfirmListingRequest = { sellerOrdinals: sellerOrdinalIds as string[], signedListingPSBT: response.psbtBase64, } resolve(this.confirmListing(confirmListingPayload)); } catch (error) { console.error('Error confirm listing:', error); reject(error); } }, onCancel: () => { console.log('Transaction canceled'); } }); }); } else { throw new Error("Wallet not supported"); } } catch (error) { console.error('Error in createListing:', error); throw error; } } /** * Updated price for a exisiting listing on the marketplace. * @param {MarketplaceReListingRequest} reListingRequest The request object containing information about the listing. * @returns {Promise<MarketplaceReListingResponse | MarketplaceConfirmReListResponse>} A promise that resolves to either a create listing response or a confirm listing response. */ async reListing( reListingRequest: MarketplaceReListingRequest ): Promise<MarketplaceReListingResponse | MarketplaceConfirmReListResponse> { try { if (!reListingRequest.walletProvider) { return await this.marketplaceInstance.reListing(reListingRequest); } else if (reListingRequest.walletProvider === WALLET_PROVIDER.xverse) { const { psbt } = await this.marketplaceInstance.reListing(reListingRequest); // Create the payload for signing the seller transaction if (!reListingRequest.sellerOrdinalAddress) { throw new Error('No seller address provided'); } const sellerInput = { address: reListingRequest.sellerOrdinalAddress, signingIndexes: [0], sigHash: bitcoin.Transaction.SIGHASH_SINGLE | bitcoin.Transaction.SIGHASH_ANYONECANPAY, }; const payload = { network: { type: this.network }, message: 'Sign Seller Transaction', psbtBase64: psbt, broadcast: false, inputsToSign: [sellerInput], }; return new Promise((resolve, reject) => { signTransaction({ payload, onFinish: async (response) => { try { const confirmReListingPayload = { ordinalId: reListingRequest.ordinalId, signedListingPSBT: response.psbtBase64, } resolve(this.confirmReListing(confirmReListingPayload)); } catch (error) { console.error('Error confirm relisting:', error); reject(error); } }, onCancel: () => { console.log('Transaction canceled'); } }); }); } else { throw new Error("Wallet not supported"); } } catch (error) { console.error('Error in reListing:', error); throw error; } } async createOffer( createOfferRequest: MarketplaceCreateOfferRequest ): Promise<MarketplaceCreateOfferResponse | string> { if (!createOfferRequest.walletProvider) { return this.marketplaceInstance.createOffer(createOfferRequest); } else if (createOfferRequest.walletProvider === WALLET_PROVIDER.xverse) { const offer: MarketplaceCreateOfferResponse = await this.marketplaceInstance.createOffer(createOfferRequest); const sellerInput = { address: createOfferRequest.buyerPaymentAddress, signingIndexes: offer.buyerInputIndices, }; const payload = { network: { type: this.network, }, message: 'Sign Buyer Transaction', psbtBase64: offer.psbt, broadcast: false, inputsToSign: [sellerInput], }; return new Promise((resolve, reject) => { signTransaction({ payload, onFinish: async (response) => { return resolve(response.psbtBase64) }, onCancel: () => { console.log('Transaction canceled'); } }); }); } else { throw new Error("Wallet not supported"); } } submitOffer( submitOfferRequest: MarketplaceSubmitOfferRequest ): Promise<MarketplaceSubmitOfferResponse> { return this.marketplaceInstance.submitOffer(submitOfferRequest); } confirmPaddingOutputs( confirmPaddingOutputsRequest: MarketplaceConfirmPaddingOutputsRequest ): Promise<MarketplaceConfirmPaddingOutputsResponse> { return this.marketplaceInstance.confirmPaddingOutputs( confirmPaddingOutputsRequest ); } async setupPaddingOutputs( setupPaddingOutputsRequest: MarketplaceSetupPaddingOutputsRequest ): Promise<MarketplaceSetupPaddingOutputsResponse> { if (!setupPaddingOutputsRequest.walletProvider) { return this.marketplaceInstance.setupPaddingOutputs(setupPaddingOutputsRequest); } else if (setupPaddingOutputsRequest.walletProvider === WALLET_PROVIDER.xverse) { const paddingOutputResponse: MarketplaceSetupPaddingOutputsResponse = await this.marketplaceInstance.setupPaddingOutputs(setupPaddingOutputsRequest); const buyerInputs = { address: setupPaddingOutputsRequest.address, signingIndexes: paddingOutputResponse.buyerInputIndices }; const payload = { network: { type: this.network, }, message: 'Sign Padding Outputs Transaction', psbtBase64: paddingOutputResponse.psbt, broadcast: true, inputsToSign: [buyerInputs], }; return new Promise((resolve, reject) => { signTransaction({ payload, onFinish: async (response) => response, onCancel: () => { console.log('Transaction canceled'); } }); }); } else { throw new Error("Wallet not supported"); } } getListing( getListingRequest: MarketplaceGetListingRequest ): Promise<MarketplaceGetListingResponse> { return this.marketplaceInstance.getListing(getListingRequest); } saveListing( saveListingRequest: MarketplaceSaveListingRequest ): Promise<MarketplaceSaveListingResponse> { return this.marketplaceInstance.saveListing(saveListingRequest); } /** * Confirms a listing in the marketplace. * @param {MarketplaceConfirmListingRequest} confirmListingRequest - The request object for confirming the listing. * @returns {Promise<MarketplaceConfirmListingResponse>} A promise that resolves with the response from confirming the listing. */ confirmListing( confirmListingRequest: MarketplaceConfirmListingRequest ): Promise<MarketplaceConfirmListingResponse> { return this.marketplaceInstance.confirmListing(confirmListingRequest) } /** * Confirms relisting in the marketplace. * @param {MarketplaceConfirmReListRequest} confirmReListingRequest - The request object for confirming the listing. * @returns {Promise<MarketplaceConfirmReListResponse>} A promise that resolves with the response from confirming the listing. */ confirmReListing( confirmReListingRequest: MarketplaceConfirmReListRequest ): Promise<MarketplaceConfirmReListResponse> { return this.marketplaceInstance.confirmReListing(confirmReListingRequest) } async transfer( transferRequest: MarketplaceTransferRequest ): Promise<SignTransactionResponse | MarketplaceTransferAPIResponse> { if (!transferRequest.walletProvider) { return this.marketplaceInstance.transfer( transferRequest ); } const transferResponse: MarketplaceTransferAPIResponse = await this.marketplaceInstance.transfer(transferRequest); const inputsToSign = [ { address: transferRequest.senderOrdinalAddress, signingIndexes: transferResponse.senderOrdinalInputs, sigHash: bitcoin.Transaction.SIGHASH_ALL, }, { address: transferRequest.senderPaymentAddress, signingIndexes: transferResponse.senderPaymentInputs, sigHash: bitcoin.Transaction.SIGHASH_ALL, }, ]; // Create the payload for signing the seller transaction const payload = { network: { type: this.network }, message: 'Sign Transfer Transaction', psbtBase64: transferResponse.psbtBase64, broadcast: true, inputsToSign, }; return new Promise((resolve, reject) => { signTransaction({ payload, onFinish: async (response: any) => { return resolve(response) }, onCancel: () => { console.log('Transaction canceled'); } }); }); } /** * DeLists the listed ordinal from marketplace * @param {MarketplaceDeListRequest} deListingRequest - The request object for confirming the listing. * @returns {Promise<MarketplaceDeListAPIResponse>} A promise that resolves with the response from confirming the listing. */ async deList( deListingRequest: MarketplaceDeListRequest ): Promise<MarketplaceDeListAPIResponse | MarketplaceConfirmDeListResponse> { if (!deListingRequest.walletProvider) { return this.marketplaceInstance.deList( deListingRequest ); } const transferResponse: MarketplaceDeListAPIResponse = await this.marketplaceInstance.deList(deListingRequest); const inputsToSign = [ { address: deListingRequest.senderOrdinalAddress, signingIndexes: transferResponse.senderOrdinalInputs, sigHash: bitcoin.Transaction.SIGHASH_ALL, }, { address: deListingRequest.senderPaymentAddress, signingIndexes: transferResponse.senderPaymentInputs, sigHash: bitcoin.Transaction.SIGHASH_ALL, }, ]; // Create the payload for signing the seller transaction const payload = { network: { type: this.network }, message: 'Sign Transfer Transaction', psbtBase64: transferResponse.psbtBase64, broadcast: true, inputsToSign, }; return new Promise((resolve, reject) => { signTransaction({ payload, onFinish: async (response) => { try { const confirmDeListingPayload = { ordinalId: deListingRequest.ordinalId, sellerPaymentAddress: deListingRequest.senderPaymentAddress, }; const confirmDeListResponse = await this.confirmDeListing( confirmDeListingPayload ); resolve({ ...confirmDeListResponse, txId: response?.txId, }); } catch (error) { console.error("Error delisting ordinal:", error); reject(error); } }, onCancel: () => { console.log("Transaction canceled"); }, }); }); } /** * Confirms delisting in the marketplace. * @param {MarketplaceConfirmDeListRequest} confirmDeListingRequest - The request object for confirming the listing. * @returns {Promise<MarketplaceConfirmDeListResponse>} A promise that resolves with the response from confirming the listing. */ confirmDeListing( confirmDeListingRequest: MarketplaceConfirmDeListRequest ): Promise<MarketplaceConfirmDeListResponse> { return this.marketplaceInstance.confirmDeListing(confirmDeListingRequest) } }