Press n or j to go to the next uncovered block, b, p or k for the previous block.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 | 17x 17x 17x 17x 1x 1x 1x 17x 1x 1x 1x 1x 1x 1x 1x | import { BlockfrostClient, BlockfrostProvider } from '../blockfrost'; import { Logger } from 'ts-log'; import { ProviderError, SubmitTxArgs, TxSubmissionError, TxSubmissionErrorCode, TxSubmitProvider, ValueNotConservedData } from '@cardano-sdk/core'; type BlockfrostTxSubmissionErrorMessage = { contents: { contents: { contents: { error: [string]; }; }; }; }; const tryParseBlockfrostTxSubmissionErrorMessage = ( errorMessage: string ): BlockfrostTxSubmissionErrorMessage | null => { try { const error = JSON.parse(errorMessage); Iif (typeof error === 'object' && Array.isArray(error?.contents?.contents?.contents?.error)) { return error; } } catch { return null; } return null; }; /** * @returns TxSubmissionError if sucessfully mapped, otherwise `null` */ const tryMapTxBlockfrostSubmissionError = (error: ProviderError): TxSubmissionError | null => { try { // eslint-disable-next-line @typescript-eslint/no-explicit-any const detail = JSON.parse(error.detail as any); Iif (typeof detail?.message === 'string') { const blockfrostTxSubmissionErrorMessage = tryParseBlockfrostTxSubmissionErrorMessage(detail.message); Iif (!blockfrostTxSubmissionErrorMessage) { return null; } const message = blockfrostTxSubmissionErrorMessage.contents.contents.contents.error[0]; Iif (message.includes('OutsideValidityIntervalUTxO')) { // error also contains information about validity interval and actual slots, // but we're currently not using this info return new TxSubmissionError(TxSubmissionErrorCode.OutsideOfValidityInterval, null, message); } // eslint-disable-next-line wrap-regex const valueNotConservedMatch = /ValueNotConservedUTxO.+Coin (\d+).+Coin (\d+)/.exec(message); Iif (valueNotConservedMatch) { const consumed = BigInt(valueNotConservedMatch[1]); const produced = BigInt(valueNotConservedMatch[2]); const valueNotConservedData: ValueNotConservedData = { // error also contains information about consumed and produced native assets // but we're currently not using this info consumed: { coins: consumed }, produced: { coins: produced } }; return new TxSubmissionError(TxSubmissionErrorCode.ValueNotConserved, valueNotConservedData, message); } } } catch { return null; } return null; }; export class BlockfrostTxSubmitProvider extends BlockfrostProvider implements TxSubmitProvider { constructor(client: BlockfrostClient, logger: Logger) { super(client, logger); } async submitTx({ signedTransaction }: SubmitTxArgs): Promise<void> { // @ todo handle context and resolutions try { await this.request<string>('tx/submit', { body: Buffer.from(signedTransaction, 'hex'), headers: { 'Content-Type': 'application/cbor' }, method: 'POST' }); } catch (error) { if (error instanceof ProviderError) { const submissionError = tryMapTxBlockfrostSubmissionError(error); Iif (submissionError) { throw new ProviderError(error.reason, submissionError, error.detail); } } throw error; } } } |