import {z} from 'zod' export type CommonDID = string; export type Sort = 'transactionAmount:ASC' | 'transactionAmount:DESC' | 'updatedAt:ASC' | 'updatedAt:DESC' | 'createdAt:ASC' | 'createdAt:DESC'; export type Group = string; export type TransactionAsset = string; export type CommonTransactionType = 'TRAVELRULE' | 'BELOW_THRESHOLD' | 'NON_CUSTODIAL' | 'UNKNOWN'; export type Direction = 'incoming' | 'outgoing' | 'internal'; export type CommonDate = string; export type GetApiTxListArgs = {vaspDID: CommonDID, sort?: Sort | undefined, page?: number | undefined, resultsPerPage?: number | undefined, txGroup?: Group | undefined, transactionAsset?: TransactionAsset | undefined, transactionType?: CommonTransactionType | undefined, transactionRef?: string | undefined, status?: Array | undefined, q?: string | undefined, txDirection?: Direction | undefined, decrypt?: boolean | undefined, csv?: boolean | undefined, includeActions?: boolean | undefined, jurisdictions?: Array | undefined, vasps?: Array | undefined, start?: CommonDate | undefined, end?: CommonDate | undefined, wallet?: string | undefined}; export type GetApiTxListOptions = {baseUrl?: string | undefined, bearerAuth?: string | undefined, fetch?: typeof fetch | undefined}; export const transactionID = z.string(); export const transactionStatus = z.enum(["SAVED", "MISSING_BENEFICIARY_DATA", "NEW", "WAITING_FOR_INFORMATION", "SENT", "REJECTED", "DECLINED", "ACK", "CANCELLED", "INCOMPLETE", "ACCEPTED", "NOT_READY"]); export const transactionType = z.enum(["BELOW_THRESHOLD", "NON_CUSTODIAL", "TRAVELRULE", "UNKNOWN"]); export const commonDID = z.string(); export const transactionBlockchainInfo = z.object({txHash: z.string().optional(), origin: z.string().optional(), destination: z.string()}); export const text = z.string(); export const naturalPersonNameTypeCode = z.enum(["ALIA", "BIRT", "MAID", "LEGL", "MISC"]); export const naturalPersonNameID = z.object({primaryIdentifier: text.optional(), secondaryIdentifier: text.optional(), nameIdentifierType: naturalPersonNameTypeCode.optional()}); export const localNaturalPersonNameID = z.object({primaryIdentifier: text.optional(), secondaryIdentifier: text.optional(), nameIdentifierType: naturalPersonNameTypeCode.optional()}); export const naturalPersonName = z.object({nameIdentifier: z.array(naturalPersonNameID), localNameIdentifier: z.array(localNaturalPersonNameID).optional(), phoneticNameIdentifier: z.array(localNaturalPersonNameID).optional()}); export const addressTypeCode = z.enum(["HOME", "BIZZ", "GEOG"]); export const ivms101CountryCode = z.string(); export const ivms101Address = z.object({addressType: addressTypeCode.optional(), department: text.optional(), subDepartment: text.optional(), streetName: text.optional(), buildingNumber: text.optional(), buildingName: text.optional(), floor: text.optional(), postBox: text.optional(), room: text.optional(), postCode: text.optional(), townName: text.optional(), townLocationName: text.optional(), districtName: text.optional(), countrySubDivision: text.optional(), addressLine: z.array(text).optional(), country: ivms101CountryCode.optional()}); export const nationalIdentifierTypeCode = z.enum(["ARNU", "CCPT", "RAID", "DRLC", "FIIN", "TXID", "SOCS", "IDCD", "LEIX", "MISC"]); export const nationalIdentification = z.object({nationalIdentifier: text, nationalIdentifierType: nationalIdentifierTypeCode, countryOfIssue: ivms101CountryCode.optional(), registrationAuthority: text.optional()}); export const date = z.string(); export const dateAndPlaceOfBirth = z.object({dateOfBirth: date, placeOfBirth: text}); export const naturalPerson = z.object({name: z.array(naturalPersonName), geographicAddress: z.array(ivms101Address).optional(), nationalIdentification: nationalIdentification.optional(), customerIdentification: text.optional(), dateAndPlaceOfBirth: dateAndPlaceOfBirth.optional(), countryOfResidence: ivms101CountryCode.optional()}); export const legalPersonNameTypeCode = z.enum(["LEGL", "SHRT", "TRAD"]); export const legalPersonNameID = z.object({legalPersonName: text.optional(), legalPersonNameIdentifierType: legalPersonNameTypeCode.optional()}); export const localLegalPersonNameID = z.object({legalPersonName: text.optional(), legalPersonNameIdentifierType: legalPersonNameTypeCode.optional()}); export const legalPersonName = z.object({nameIdentifier: z.array(legalPersonNameID), localNameIdentifier: z.array(localLegalPersonNameID).optional(), phoneticNameIdentifier: z.array(localLegalPersonNameID).optional()}); export const legalPerson = z.object({name: legalPersonName, geographicAddress: z.array(ivms101Address).optional(), customerNumber: text.optional(), nationalIdentification: nationalIdentification.optional(), dateOfRegistration: date.optional(), countryOfRegistration: ivms101CountryCode.optional()}); export const ivms101Person = z.object({naturalPerson: naturalPerson.optional(), legalPerson: legalPerson.optional()}); export const ivms101Originator = z.object({originatorPersons: z.array(ivms101Person), accountNumber: z.array(text).optional()}); export const ivms101Beneficiary = z.object({beneficiaryPersons: z.array(ivms101Person), accountNumber: z.array(text).optional()}); export const originatingVASP = z.object({originatingVASP: ivms101Person.optional()}); export const beneficiaryVASP = z.object({beneficiaryVASP: ivms101Person.optional()}); export const ivms101Number = z.number().int(); export const intermediaryVASP = z.object({intermediaryVASP: ivms101Person, sequence: ivms101Number}); export const transferPath = z.object({transferPath: z.array(intermediaryVASP).optional()}); export const transliterationMethodCode = z.enum(["arab", "aran", "armn", "cyrl", "deva", "geor", "grek", "hani", "hebr", "kana", "kore", "thai", "othr"]); export const payloadMetadata = z.object({transliterationMethod: z.array(transliterationMethodCode).optional()}); export const ivms101IVMS101 = z.object({originator: ivms101Originator.optional(), beneficiary: ivms101Beneficiary.optional(), originatingVASP: originatingVASP.optional(), beneficiaryVASP: beneficiaryVASP.optional(), transferPath: transferPath.optional(), payloadMetadata: payloadMetadata.optional()}); export const trTravelRuleProtocol = z.enum(["TRLight", "TRP", "OpenVASP"]); export const protocolData = z.object({trLightData: z.object({notificationEmails: z.array(z.string()).optional()}).optional()}); export const ownershipProof = z.union([z.object({type: z.enum(["eip-191", "eip-712", "bip-137"]), proof: z.string(), attestation: z.string(), address: z.string().optional(), status: z.string().optional()}), z.object({type: z.literal("screenshot"), url: z.string()}), z.object({type: z.literal("self-declaration"), did: z.string().optional(), address: z.string().optional(), attestation: z.string(), confirmed: z.boolean().optional(), status: z.string().optional()}), z.object({type: z.literal("microtransfer"), did: z.string().optional(), address: z.string().optional(), proof: z.string(), chain: z.string().optional(), destination: z.string().optional(), amountSubunits: z.string().optional(), status: z.string().optional()}), z.object({type: z.string()}).and(z.record(z.string(), z.unknown()))]); export const transactionAction = z.object({hash: z.string().optional(), issuerDID: commonDID.optional(), action: z.string().optional(), actionData: z.string().optional()}); export const transactionResolved = z.object({id: transactionID, status: transactionStatus.optional(), transactionType: transactionType, transactionAsset: z.string(), transactionAssetDecimals: z.number(), transactionAmount: z.string(), chargedQuantity: z.number(), amountInLocalCurrency: z.object({amountInLocalCurrency: z.number().optional(), currency: z.string().optional()}).optional(), originatorDid: commonDID.optional(), originatorRef: z.string().optional(), beneficiaryDid: commonDID, beneficiaryRef: z.string().optional(), originatorVASPdid: commonDID.optional(), beneficiaryVASPdid: commonDID.optional(), transactionBlockchainInfo: transactionBlockchainInfo.optional(), ivms101: ivms101IVMS101.optional(), ivms101Encrypted: z.string().optional(), protocol: trTravelRuleProtocol.optional(), protocolData: protocolData.optional(), originatorProof: ownershipProof.optional(), beneficiaryProof: ownershipProof.optional(), actions: z.array(transactionAction).optional()}); export const paginateInfo = z.object({totalItems: z.number().int().optional(), itemCount: z.number().int().optional(), itemsPerPage: z.number().int().optional(), totalPages: z.number().int().optional(), currentPage: z.number().int().optional()}); export const transactionsList = z.object({transactions: z.array(transactionResolved).optional(), paginateInfo: paginateInfo.optional()}); export const validateResponse = async ( schema: { readonly '~standard': { readonly validate: (value: unknown) => ({ readonly value: Output; readonly issues?: undefined } | { readonly issues: ReadonlyArray<{ readonly message: string; readonly path?: ReadonlyArray | undefined }> }) | Promise<{ readonly value: Output; readonly issues?: undefined } | { readonly issues: ReadonlyArray<{ readonly message: string; readonly path?: ReadonlyArray | undefined }> }> } }, value: unknown ): Promise => { const result = await schema['~standard'].validate(value) if (result.issues !== undefined) { const detail = result.issues .map(({ message, path }) => path?.length ? `${path.map((segment) => String(typeof segment === 'object' ? segment.key : segment)).join('.')}: ${message}` : message ) .join('; ') throw new Error(`Response does not match the schema: ${detail}`, { cause: { issues: result.issues } }) } return result.value }; /** * List transactions * * GET /tx/list */ export const getApiTxList = async (args: GetApiTxListArgs, options?: GetApiTxListOptions) => { const {baseUrl = "https://api.eu1.notabene.id", fetch: fetchFn = fetch, bearerAuth} = options ?? {} const query = new URLSearchParams() const headers = new Headers() headers.set('accept', "application/json") if (args.vaspDID != null) query.append("vaspDID", String(args.vaspDID)) if (args.sort != null) query.append("sort", String(args.sort)) if (args.page != null) query.append("page", String(args.page)) if (args.resultsPerPage != null) query.append("resultsPerPage", String(args.resultsPerPage)) if (args.txGroup != null) query.append("txGroup", String(args.txGroup)) if (args.transactionAsset != null) query.append("transactionAsset", String(args.transactionAsset)) if (args.transactionType != null) query.append("transactionType", String(args.transactionType)) if (args.transactionRef != null) query.append("transactionRef", String(args.transactionRef)) for (const item of args.status ?? []) query.append("status", String(item)) if (args.q != null) query.append("q", String(args.q)) if (args.txDirection != null) query.append("txDirection", String(args.txDirection)) if (args.decrypt != null) query.append("decrypt", String(args.decrypt)) if (args.csv != null) query.append("csv", String(args.csv)) if (args.includeActions != null) query.append("includeActions", String(args.includeActions)) for (const item of args.jurisdictions ?? []) query.append("jurisdictions", String(item)) for (const item of args.vasps ?? []) query.append("vasps", String(item)) if (args.start != null) query.append("start", String(args.start)) if (args.end != null) query.append("end", String(args.end)) if (args.wallet != null) query.append("wallet", String(args.wallet)) if (bearerAuth !== undefined) headers.set('authorization', `Bearer ${bearerAuth}`) const url = `${baseUrl.replace(/\/+$/, '')}/tx/list${query.size > 0 ? `?${query}` : ''}` const res = await fetchFn(url, {method: "GET", headers}) if (!res.ok) throw new Error(`Request failed with status ${res.status}`, {cause: {status: res.status, body: await res.text()}}) return validateResponse(transactionsList, await res.json()) };