diff --git a/src/capabilities/index.ts b/src/capabilities/index.ts index 958875e..1bdc0bf 100644 --- a/src/capabilities/index.ts +++ b/src/capabilities/index.ts @@ -34,6 +34,7 @@ const extractOrValidateCapabilities = ( signTransaction: validateCapability('signTransaction'), sendBtcTransaction: validateCapability('sendBtcTransaction'), createInscription: validateCapability('createInscription'), + createRepeatInscriptions: validateCapability('createRepeatInscriptions'), }; return Object.entries(capabilityMap).reduce((acc, [capability, value]) => { diff --git a/src/inscriptions/createInscription.ts b/src/inscriptions/createInscription.ts index 184aa9e..e37a358 100644 --- a/src/inscriptions/createInscription.ts +++ b/src/inscriptions/createInscription.ts @@ -3,35 +3,7 @@ import { createUnsecuredToken } from 'jsontokens'; import { getProviderOrThrow } from '../provider'; import { CreateInscriptionOptions, CreateInscriptionPayload } from './types'; - -const MAX_CONTENT_LENGTH_MAINNET = 400e3; // 400kb is the max miners will mine -const MAX_CONTENT_LENGTH_TESTNET = 60e3; // 60kb limit on Testnet to prevent spam - -export const validateInscriptionPayload = (payload: CreateInscriptionPayload) => { - const { contentType, content, payloadType, network, appFeeAddress, appFee } = payload; - if (!/^[a-z]+\/[a-z0-9\-\.\+]+(?=;.*|$)/.test(contentType)) { - throw new Error('Invalid content type detected'); - } - - if (!content || content.length === 0) { - throw new Error('Empty content not allowed'); - } - - if (!payloadType || (payloadType !== 'BASE_64' && payloadType !== 'PLAIN_TEXT')) { - throw new Error('Empty invalid payloadType specified'); - } - - if ( - content.length > - (network.type === 'Mainnet' ? MAX_CONTENT_LENGTH_MAINNET : MAX_CONTENT_LENGTH_TESTNET) - ) { - throw new Error('Content too large'); - } - - if ((appFeeAddress?.length ?? 0) > 0 && (appFee ?? 0) <= 0) { - throw new Error('Invalid combination of app fee address and fee provided'); - } -}; +import { validateInscriptionPayload } from './utils'; export const createInscription = async (options: CreateInscriptionOptions) => { const { getProvider } = options; diff --git a/src/inscriptions/createRepeatInscriptions.ts b/src/inscriptions/createRepeatInscriptions.ts new file mode 100644 index 0000000..5ba9207 --- /dev/null +++ b/src/inscriptions/createRepeatInscriptions.ts @@ -0,0 +1,20 @@ +import { getProviderOrThrow } from 'src/provider'; +import { CreateInscriptionOptions, CreateRepeatInscriptionsOptions } from './types'; +import { Json, createUnsecuredToken } from 'jsontokens'; +import { validateInscriptionPayload } from './utils'; + +export const createRepeatInscriptions = async (options: CreateRepeatInscriptionsOptions) => { + const { getProvider } = options; + const provider = await getProviderOrThrow(getProvider); + + validateInscriptionPayload(options.payload); + + try { + const request = createUnsecuredToken(options.payload as unknown as Json); + const response = await provider.createInscription(request); + options.onFinish?.(response); + } catch (error) { + console.error('[Connect] Error during create repeat inscriptions', error); + options.onCancel?.(); + } +}; diff --git a/src/inscriptions/types.ts b/src/inscriptions/types.ts index 1307c43..9af3299 100644 --- a/src/inscriptions/types.ts +++ b/src/inscriptions/types.ts @@ -10,6 +10,10 @@ export interface CreateInscriptionPayload extends RequestPayload { token?: string; } +export interface CreateRepeatInscriptionsPayload extends CreateInscriptionPayload { + repeat: number; +} + export type CreateInscriptionResponse = { txId: string; }; @@ -18,3 +22,8 @@ export type CreateInscriptionOptions = RequestOptions< CreateInscriptionPayload, CreateInscriptionResponse >; + +export type CreateRepeatInscriptionsOptions = RequestOptions< + CreateRepeatInscriptionsPayload, + CreateInscriptionResponse +>; diff --git a/src/inscriptions/utils.ts b/src/inscriptions/utils.ts new file mode 100644 index 0000000..33cc67f --- /dev/null +++ b/src/inscriptions/utils.ts @@ -0,0 +1,30 @@ +import { CreateInscriptionPayload } from './types'; + +const MAX_CONTENT_LENGTH_MAINNET = 400e3; // 400kb is the max miners will mine +const MAX_CONTENT_LENGTH_TESTNET = 60e3; // 60kb limit on Testnet to prevent spam + +export const validateInscriptionPayload = (payload: CreateInscriptionPayload) => { + const { contentType, content, payloadType, network, appFeeAddress, appFee } = payload; + if (!/^[a-z]+\/[a-z0-9\-\.\+]+(?=;.*|$)/.test(contentType)) { + throw new Error('Invalid content type detected'); + } + + if (!content || content.length === 0) { + throw new Error('Empty content not allowed'); + } + + if (!payloadType || (payloadType !== 'BASE_64' && payloadType !== 'PLAIN_TEXT')) { + throw new Error('Empty invalid payloadType specified'); + } + + if ( + content.length > + (network.type === 'Mainnet' ? MAX_CONTENT_LENGTH_MAINNET : MAX_CONTENT_LENGTH_TESTNET) + ) { + throw new Error('Content too large'); + } + + if ((appFeeAddress?.length ?? 0) > 0 && (appFee ?? 0) <= 0) { + throw new Error('Invalid combination of app fee address and fee provided'); + } +}; diff --git a/src/provider/types.ts b/src/provider/types.ts index 19c5385..0b248aa 100644 --- a/src/provider/types.ts +++ b/src/provider/types.ts @@ -12,6 +12,7 @@ interface BaseBitcoinProvider { signTransaction: (request: string) => Promise; sendBtcTransaction: (request: string) => Promise; createInscription: (request: string) => Promise; + createRepeatInscriptions: (request: string) => Promise; } export type Capability = keyof BaseBitcoinProvider;