Docs / Wallet provider / PSBTs & the request() interface

window.dogesoft

PSBTs & the request() interface

Sign and push custom PSBTs for marketplaces and multi-party transactions, or use the generic request() interface.

Live

Signing a PSBT

For anything the friendly methods don't cover β€” marketplace listings, multi-party trades, multi-input transactions β€” build a PSBT (Partially Signed Bitcoin Transaction) yourself and hand it to the wallet for signing. The user sees exactly what they're signing; you never see their key.

sign-psbt.js
// unsignedPsbt: hex or base64, built by your backend.
// Every input the wallet signs must carry its previous transaction (nonWitnessUtxo).
const { psbtHex, txHex } = await dogesoft.signPsbt(unsignedPsbt, {
  finalize: true
});
// psbtHex: the signed PSBT, base64-encoded (despite the name). hex is an alias.
// txHex:   broadcast-ready raw transaction, only when finalize is true
//          and every input in the PSBT is now signed.

// Or review + sign + broadcast in one call (PSBT as hex):
const { txid } = await dogesoft.pushPsbt(unsignedPsbtHex);

signPsbt() signs the inputs the connected account controls (or only the ones you name in inputToSign) and leaves every other input untouched, so several parties can sign the same PSBT in turn. Broadcast the final txHex from your backend with POST https://explorer.dogesoft.io/api/tx/broadcast (body {"hex": "…"}). pushPsbt() takes a hex PSBT, shows the same review, signs every remaining input and broadcasts. It fails if the transaction still isn't fully signed.

Marketplace flow β€” list, buy, cancel

A Doginals marketplace signs the listing, the purchase, and a self-send cancel as PSBTs through the same signPsbt() / pushPsbt() pair above. The listing stays a signed record. The purchase transaction carries one Doge Soft marketplace tag (BUY, AUCTION_SOLD, OFFER_ACCEPT, GIFT, BUNDLE, PARTIAL_FILL, COLLECTION_TRANSFER).

  1. List β€” your backend builds an unsigned PSBT (inscription input + price output). The seller signs it without finalize. Store the signed PSBT; do not broadcast it.
  2. Buy β€” your backend takes the stored listing PSBT, adds the buyer's payment inputs/outputs and change, and returns an unsigned-for-buyer PSBT. The buyer signs with finalize: true, and your backend broadcasts the resulting txHex (or the buyer calls pushPsbt()).
  3. Cancel β€” your backend builds a PSBT that spends the listed inscription back to the seller. The seller signs it and you push it.

Signing a listing β€” ANYONECANPAY

A listing PSBT at step 1 has only the seller's inscription input β€” the buyer's payment input doesn't exist yet. For the seller's signature to still be valid once your backend adds that payment input later, the seller must sign with SIGHASH_SINGLE|ANYONECANPAY (0x83), not the plain SIGHASH_ALL that signPsbt() uses by default. Pass it per input via options.inputToSign:

list-sign.js
// Seller signs only their own input, ANYONECANPAY so a buyer can add
// payment inputs later without invalidating this signature.
const { psbtHex } = await window.dogesoft.signPsbt(listingPsbtHex, {
  inputToSign: [{ inputIndex: 0, sighashType: 0x83 }]
});
// Store psbtHex. Do not broadcast β€” it isn't a complete transaction yet.
ValueMeaning
0x01SIGHASH_ALL β€” the default when no sighashType is given.
0x02SIGHASH_NONE
0x03SIGHASH_SINGLE
0x81SIGHASH_ALL | ANYONECANPAY
0x82SIGHASH_NONE | ANYONECANPAY
0x83SIGHASH_SINGLE | ANYONECANPAY β€” the listing value above.

Any other value is rejected outright rather than silently signed as ALL. A single top-level options.sighashType applies to every signed input that doesn't set its own β€” useful for pushPsbt(), where the buyer's own new inputs still sign as plain ALL by default.

batch-list.js
// Batch a listing flow by looping signPsbt() once per item,
// sequentially (one approval each). Works in both hosts.
async function signPsbtsDogesoft(items) {
  const out = [];
  for (const item of items) {
    const { psbtHex } = await window.dogesoft.signPsbt(item.psbtHex);
    out.push({ ...item, signedPsbtHex: psbtHex });
  }
  return out;
}
ℹ️

Before treating a sale or cancellation as final, confirm the Doginal's output actually moved β€” GET /api/explorer/tx/:txid returns the parsed drc20_operations / inscriptions for a transaction. The plain /api/tx/:txid only returns raw transaction fields and won't tell you whether a carrier changed hands.

The request() interface

The provider also has a generic dispatcher, shaped like the EIP-1193 pattern many wallet adapters expect. These method strings behave the same in the extension and the mobile app:

Method stringEquivalent to
doge_requestAccountsconnect()
doge_accountsgetAccounts()
doge_getBalancegetBalance()
doge_sendDogesendDoge(params), with params an object
doge_sendDrc20sendDrc20(params), with params an object
request.js
const result = await window.dogesoft.request({
  method: "doge_requestAccounts"
});

const sent = await window.dogesoft.request({
  method: "doge_sendDoge",
  params: { to: "D…recipient", amount: 1 }
});

For signing (signMessage, signPsbt, pushPsbt), call the methods directly as shown above. Their request() parameter shapes aren't the same in both hosts yet. An unknown method string rejects with Error('DogeSoft: unknown method "<method>"'). See Errors.