Skip to content

Commit

Permalink
refactor: make ordinals com as main source of data for double checkin…
Browse files Browse the repository at this point in the history
…g utxos
  • Loading branch information
alter-eggo committed Feb 22, 2024
1 parent d566426 commit d4ee55c
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 20 deletions.
36 changes: 36 additions & 0 deletions src/app/query/bitcoin/bitcoin-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,32 @@ export interface UtxoWithDerivationPath extends UtxoResponseItem {
derivationPath: string;
}

interface BestinslotInscription {
inscription_name: string | null;
inscription_id: string;
inscription_number: number;
metadata: any | null;
wallet: string;
mime_type: string;
media_length: number;
genesis_ts: number;
genesis_height: number;
genesis_fee: number;
output_value: number;
satpoint: string;
collection_name: string | null;
collection_slug: string | null;
last_transfer_block_height: number;
content_url: string;
bis_url: string;
byte_size: number;
}

interface BestinslotInscriptionResponse {
data: BestinslotInscription;
block_height: number;
}

class BestinslotInscriptionsApi {
private defaultOptions = {
headers: {
Expand All @@ -38,6 +64,16 @@ class BestinslotInscriptionsApi {

return resp.data;
}

async getInscriptionById(id: string) {
const resp = await axios.get<BestinslotInscriptionResponse>(
`https://api.bestinslot.xyz/v3/inscription/single_info_id?inscription_id=${id}`,
{
...this.defaultOptions,
}
);
return resp.data;
}
}

class AddressApi {
Expand Down
78 changes: 58 additions & 20 deletions src/app/query/bitcoin/transaction/use-check-utxos.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ interface FilterOutIntentionalInscriptionsSpendArgs {
inputs: btc.TransactionInput[];
intentionalSpendUtxoIds: string[];
}

export function filterOutIntentionalUtxoSpend({
inputs,
intentionalSpendUtxoIds,
Expand All @@ -36,6 +37,44 @@ export function filterOutIntentionalUtxoSpend({
});
}

interface CheckInscribedUtxosByBestinslotArgs {
inputs: btc.TransactionInput[];
txids: string[];
client: ReturnType<typeof useBitcoinClient>;
}

async function checkInscribedUtxosByBestinslot({
inputs,
txids,
client,
}: CheckInscribedUtxosByBestinslotArgs): Promise<boolean> {
/**
* @description Get the list of inscriptions moving on a transaction
* @see https://docs.bestinslot.xyz/reference/api-reference/ordinals-and-brc-20-and-bitmap-v3-api-mainnet+testnet/inscriptions
*/
const inscriptionIdsList = await Promise.all(
txids.map(id => client.bestinslotInscriptionsApi.getInscriptionsByTransactionId(id))
);

const inscriptionIds = inscriptionIdsList.flatMap(inscription =>
inscription.data.map(data => data.inscription_id)
);

// fetch inscriptions by id to get relevant data
const responses = await Promise.all(
inscriptionIds.map(id => client.bestinslotInscriptionsApi.getInscriptionById(id))
);

// check if any of the utxos are inscribed
return responses.some(resp => {
return inputs.some(input => {
if (!input.txid) throw new Error('Transaction ID is missing in the input');
const idWithIndex = `${bytesToHex(input.txid)}:${input.index}`;
return resp.data.satpoint.includes(idWithIndex);
});
});
}

export function useCheckInscribedUtxos(blockTxAction?: () => void) {
const client = useBitcoinClient();
const analytics = useAnalytics();
Expand Down Expand Up @@ -67,14 +106,20 @@ export function useCheckInscribedUtxos(blockTxAction?: () => void) {
throw new Error('Utxos list cannot be empty');
}

const responses = await Promise.all(
txids.map(id => client.bestinslotInscriptionsApi.getInscriptionsByTransactionId(id))
const ordinalsComResponses = await Promise.all(
txids.map(async (id, index) => {
const inscriptionIndex = inputs[index].index;
if (isUndefined(inscriptionIndex)) {
throw new Error('Inscription index is missing in the input');
}
const num = await getNumberOfInscriptionOnUtxoUsingOrdinalsCom(id, inscriptionIndex);
return num > 0;
})
);

const hasInscribedUtxo = responses.some(resp => {
return resp.data.length > 0;
});
const hasInscribedUtxo = ordinalsComResponses.some(resp => resp);

// if there are inscribed utxos in the transaction, and no error => prevent the transaction
if (hasInscribedUtxo) {
void analytics.track('utxos_includes_inscribed_one', {
txids,
Expand All @@ -83,36 +128,29 @@ export function useCheckInscribedUtxos(blockTxAction?: () => void) {
return true;
}

// if there are no inscribed utxos in the transaction => allow the transaction
return false;
} catch (e) {
if (e instanceof PreventTransactionError) {
throw e;
}

void analytics.track('error_checking_utxos_from_bestinslot', {
void analytics.track('error_checking_utxos_from_ordinalscom', {
txids,
});

const ordinalsComResponses = await Promise.all(
txids.map(async (id, index) => {
const inscriptionIndex = inputs[index].index;
if (isUndefined(inscriptionIndex)) {
throw new Error('Inscription index is missing in the input');
}
const num = await getNumberOfInscriptionOnUtxoUsingOrdinalsCom(id, inscriptionIndex);
return num > 0;
})
);

const hasInscribedUtxo = ordinalsComResponses.some(resp => resp);
const hasInscribedUtxo = await checkInscribedUtxosByBestinslot({ inputs, txids, client });

// if there are inscribed utxos in the transaction, and no error => prevent the transaction
if (hasInscribedUtxo) {
void analytics.track('utxos_includes_inscribed_one', {
txids,
});
preventTransaction();
return true;
}

return true;
// if there are no inscribed utxos in the transaction => allow the transaction
return false;
} finally {
setIsLoading(false);
}
Expand Down

0 comments on commit d4ee55c

Please sign in to comment.