| name | ordinals-marketplace |
| description | This skill should be used when working with 1Sat Ordinals marketplace operations — listing ordinals for sale, purchasing listings, canceling listings, browsing available ordinals, or managing OrdLock marketplace scripts. Triggers on 'list ordinal', 'sell NFT', 'buy ordinal', 'purchase listing', 'cancel listing', 'marketplace', 'OrdLock', 'ordinal price', or 'browse ordinals'. Uses @1sat/actions ordinals module. |
| disable-model-invocation | true |
Ordinals Marketplace
List, purchase, and cancel ordinal listings using @1sat/actions and the OrdLock script.
Actions
| Action | Description |
|---|
getOrdinals | List ordinals in the wallet (with BEEF for spending) |
transferOrdinals | Transfer ordinals to new owners |
listOrdinal | List an ordinal for sale at a price |
cancelListing | Cancel an active listing |
purchaseOrdinal | Purchase a listed ordinal |
deriveCancelAddress | Get the cancel address for a listing |
inputBEEF: When Required vs Optional
inputBEEF provides the SPV proof chain for the inputs being spent. Whether it's required depends on who owns the input:
- Optional for inputs from your own wallet — the action auto-resolves BEEF via the output's ID tag (
listOutputs with tag filter). Transfer, list, and cancel all work without passing inputBEEF when the ordinal is in your wallet.
- Required for external inputs not in your wallet — e.g. purchasing a listing from another user or a marketplace. The buyer's wallet has no BEEF for the seller's ordlock UTXO, so the marketplace/overlay must provide it.
Get Ordinals from Wallet
import { getOrdinals, createContext } from '@1sat/actions'
const ctx = createContext(wallet, { services })
const { outputs, BEEF } = await getOrdinals.execute(ctx, {
limit: 100,
})
for (const o of outputs) {
console.log(o.outpoint, o.tags)
}
Transfer Ordinals
import { transferOrdinals, getOrdinals, createContext } from '@1sat/actions'
const ctx = createContext(wallet, { services })
const { outputs, BEEF } = await getOrdinals.execute(ctx, {})
const ordinal = outputs[0]
const result = await transferOrdinals.execute(ctx, {
transfers: [
{ ordinal, counterparty: '02abc...' },
],
})
const result2 = await transferOrdinals.execute(ctx, {
transfers: [
{ ordinal, address: '1Recipient...' },
],
})
const result3 = await transferOrdinals.execute(ctx, {
transfers: [
{ ordinal: outputs[0], counterparty: '02abc...' },
{ ordinal: outputs[1], address: '1Bob...' },
],
})
List Ordinal for Sale
import { listOrdinal, getOrdinals, createContext } from '@1sat/actions'
const ctx = createContext(wallet, { services })
const { outputs, BEEF } = await getOrdinals.execute(ctx, {})
const ordinal = outputs[0]
const result = await listOrdinal.execute(ctx, {
ordinal,
price: 100000,
payAddress: '1YourPaymentAddress...',
})
if (result.txid) {
console.log('Listed! txid:', result.txid)
}
How Listing Works
- Creates an OrdLock script that encodes the price and payment address
- The ordinal is locked in this script — only a valid purchase or cancel can spend it
- The listing is submitted to the marketplace overlay for indexing
- Tags are updated:
ordlock tag is added, basket remains ordinals
Purchase a Listed Ordinal
import { purchaseOrdinal, createContext } from '@1sat/actions'
const ctx = createContext(wallet, { services })
const result = await purchaseOrdinal.execute(ctx, {
outpoint: 'txid_0',
marketplaceAddress: '1MarketplaceAddress...',
marketplaceRate: 0.02,
})
if (result.txid) {
console.log('Purchased! txid:', result.txid)
}
How Purchase Works
- Fetches the listing BEEF from the overlay
- Reads the OrdLock script to extract price and payment address
- Builds a transaction that satisfies the OrdLock:
- Pays the seller the listed price
- Pays marketplace fee (if applicable)
- Transfers the ordinal to the buyer
- Signs and broadcasts
Cancel a Listing
import { cancelListing, createContext } from '@1sat/actions'
const ctx = createContext(wallet, { services })
const { outputs } = await ctx.wallet.listOutputs({
basket: '1sat',
includeTags: true,
includeCustomInstructions: true,
tagsJSON: JSON.stringify(['ordlock']),
})
const listing = outputs[0]
const result = await cancelListing.execute(ctx, { listing })
if (result.txid) {
console.log('Cancelled! txid:', result.txid)
}
How Cancel Works
- Derives the cancel key using the listing's custom instructions
- Signs the OrdLock input using
OrdLock.cancelWithWallet from @1sat/templates
- Transfers the ordinal back to the wallet (removes
ordlock tag)
- Submits to overlay to clear the listing
Derive Cancel Address
import { deriveCancelAddress, createContext } from '@1sat/actions'
const ctx = createContext(wallet)
const result = await deriveCancelAddress.execute(ctx, {
ordinal: listedOrdinal,
})
console.log('Cancel address:', result.address)
OrdLock Script
The OrdLock script encodes a marketplace listing:
<lockPrefix> <payAddress> <price> <lockSuffix>
- The script is satisfied by either:
- Purchase: Transaction includes an output paying the seller at
payAddress for price satoshis
- Cancel: Signed by the cancel key (derived from the ordinal's custom instructions)
Browsing Marketplace via API
Use the 1sat-stack API to browse listings:
const res = await fetch('https://api.1sat.app/1sat/owner/1Address.../txos')
const txos = await res.json()
const listings = txos.filter(t => t.data?.ordlock)
const content = await fetch('https://api.1sat.app/1sat/content/txid_0')
Tags on Marketplace Outputs
| Tag | Meaning |
|---|
ordlock | Currently listed for sale |
type:{contentType} | MIME type of the inscription |
origin:{outpoint} | Origin outpoint of the ordinal |
name:{value} | Name from MAP metadata |
Requirements
bun add @1sat/actions @1sat/wallet @bsv/sdk
Marketplace operations require services for overlay submission and BEEF fetching.