UNPKG

js-moi-providers

Version:

Module to connect and interact with MOI network

333 lines 13.8 kB
import { ErrorCode, ErrorUtils, OpType, participantCreateSchema, assetActionSchema, assetCreateSchema, assetSupplySchema, bytesToHex, hexToBytes, logicSchema, toQuantity, trimHexPrefix, LockType } from "js-moi-utils"; import { Polorizer } from "js-polo"; import { ZERO_ADDRESS } from "js-moi-constants"; /** * Validates the payload for PARTICIPANT_CREATE operation type. * * @param {OperationPayload} payload - The operation payload. * @returns {AssetActionPayload} - The validated payload. * @throws {Error} - Throws an error if the payload is invalid. */ export const validateParticipantCreatePayload = (payload) => { if ('address' in payload && 'amount' in payload) { return payload; } throw new Error("Invalid participant create payload"); }; /** * Validates the payload for ASSET_CREATE operation type. * * @param {OperationPayload} payload - The operation payload. * @returns {AssetCreatePayload} - The validated payload. * @throws {Error} - Throws an error if the payload is invalid. */ export const validateAssetCreatePayload = (payload) => { if ('symbol' in payload && 'supply' in payload && 'standard' in payload) { return payload; } throw new Error("Invalid asset create payload"); }; /** * Validates the payload for ASSET_MINT and ASSET_BURN operation types. * * @param {OperationPayload} payload - The operation payload. * @returns {AssetSupplyPayload} - The validated payload. * @throws {Error} - Throws an error if the payload is invalid. */ export const validateAssetSupplyPayload = (payload) => { if ('asset_id' in payload && 'amount' in payload) { return payload; } throw new Error("Invalid asset mint or burn payload"); }; /** * Validates the payload for ASSET_TRANSFER operation type. * * @param {OperationPayload} payload - The operation payload. * @returns {AssetActionPayload} - The validated logic action payload. * @throws {Error} - Throws an error if the payload is invalid. */ export const validateAssetTransferPayload = (payload) => { if ('beneficiary' in payload && 'asset_id' in payload && 'amount' in payload) { return payload; } throw new Error("Invalid asset transfer payload"); }; /** * Validates the payload for LOGIC_DEPLOY operation type. * * @param {OperationPayload} payload - The operation payload. * @returns {LogicDeployPayload} - The validated logic deploy payload. * @throws {Error} - Throws an error if the payload is invalid. */ export const validateLogicDeployPayload = (payload) => { if ('manifest' in payload && 'callsite' in payload) { return payload; } throw new Error("Invalid logic deploy payload"); }; /** * Validates the payload for LOGIC_INVOKE and LOGIC_ENLIST operation types. * * @param {OperationPayload} payload - The operation payload. * @returns {LogicActionPayload} - The validated logic action payload. * @throws {Error} - Throws an error if the payload is invalid. */ export const validateLogicActionPayload = (payload) => { if ('logic_id' in payload && 'callsite' in payload) { return payload; } throw new Error("Invalid logic invoke or enlist payload"); }; /** * Processes the payload based on the operation type. * * @param {OpType} opType - The operation type. * @param {OperationPayload} payload - The operation payload. * @returns {OperationPayload} - The processed operation payload. * @throws {Error} - Throws an error if the operation type is unsupported. */ const processPayload = (opType, payload) => { switch (opType) { case OpType.PARTICIPANT_CREATE: { const participantPayload = validateParticipantCreatePayload(payload); return { ...participantPayload, address: hexToBytes(participantPayload.address), }; } case OpType.ASSET_CREATE: { const createPayload = validateAssetCreatePayload(payload); return { ...createPayload }; } case OpType.ASSET_MINT: case OpType.ASSET_BURN: { const supplyPayload = validateAssetSupplyPayload(payload); return { ...supplyPayload, asset_id: trimHexPrefix(supplyPayload.asset_id), }; } case OpType.ASSET_TRANSFER: { const actionPayload = validateAssetTransferPayload(payload); return { ...actionPayload, benefactor: hexToBytes(actionPayload.benefactor ?? ZERO_ADDRESS), beneficiary: hexToBytes(actionPayload.beneficiary), asset_id: trimHexPrefix(actionPayload.asset_id), }; } case OpType.LOGIC_DEPLOY: { const logicPayload = validateLogicDeployPayload(payload); return { manifest: hexToBytes(logicPayload.manifest), callsite: logicPayload.callsite, calldata: logicPayload.calldata ? hexToBytes(logicPayload.calldata) : null, }; } case OpType.LOGIC_INVOKE: case OpType.LOGIC_ENLIST: { const logicPayload = validateLogicActionPayload(payload); return { logic_id: trimHexPrefix(logicPayload.logic_id), callsite: logicPayload.callsite, calldata: logicPayload.calldata ? hexToBytes(logicPayload.calldata) : null, }; } default: ErrorUtils.throwError(`Unsupported operation type: ${opType}`, ErrorCode.UNSUPPORTED_OPERATION); } }; /** * Serializes the payload of a operation based on its type. * This function polorizes (serializes) the payload using the appropriate schema * based on the operation type and returns it as a byte array. * * @param {OpType} opType - The type of the operation (e.g., ASSET_TRANSFER, ASSET_CREATE). * @param {OperationPayload} payload - The payload of the operation to be serialized. * @returns {Uint8Array} - A serialized byte array representing the processed payload. * @throws {Error} - Throws an error if the operation type is unsupported. */ export const serializePayload = (opType, payload) => { const polorizer = new Polorizer(); const processedPayload = processPayload(opType, payload); switch (opType) { case OpType.PARTICIPANT_CREATE: polorizer.polorize(processedPayload, participantCreateSchema); return polorizer.bytes(); case OpType.ASSET_TRANSFER: polorizer.polorize(processedPayload, assetActionSchema); return polorizer.bytes(); case OpType.ASSET_CREATE: polorizer.polorize(processedPayload, assetCreateSchema); return polorizer.bytes(); case OpType.ASSET_MINT: case OpType.ASSET_BURN: polorizer.polorize(processedPayload, assetSupplySchema); return polorizer.bytes(); case OpType.LOGIC_DEPLOY: case OpType.LOGIC_INVOKE: case OpType.LOGIC_ENLIST: polorizer.polorize(processedPayload, logicSchema); return polorizer.bytes(); default: ErrorUtils.throwError(`Unsupported operation type: ${opType}`, ErrorCode.UNSUPPORTED_OPERATION); } }; /** * Processes the interaction object to extract and consolidate asset funds from * ix_operations and asset funds. * * @param {InteractionObject} ixObject - The interaction object containing ix_operations and asset funds. * @returns {ProcessedIxAssetFund[]} - The consolidated list of processed asset funds. */ const processFunds = (ixObject) => { const assetFunds = new Map(); ixObject.ix_operations.forEach(operation => { switch (operation.type) { case OpType.ASSET_TRANSFER: case OpType.ASSET_BURN: { const payload = operation.payload; const amount = assetFunds.get(payload.asset_id) ?? 0; if (typeof payload.amount === "bigint" || typeof amount === "bigint") { assetFunds.set(payload.asset_id, toQuantity(BigInt(payload.amount) + BigInt(amount))); return; } assetFunds.set(payload.asset_id, toQuantity(Number(payload.amount) + Number(amount))); } } }); if (ixObject.funds != null) { // Add additional asset funds to the list if not present ixObject.funds.forEach(assetFund => { if (!assetFunds.has(assetFund.asset_id)) { assetFunds.set(assetFund.asset_id, toQuantity(assetFund.amount)); } }); } return Array.from(assetFunds, ([asset_id, amount]) => ({ asset_id, amount })); }; /** * Processes a series of ix_operations and returns an array of processed participants. * Each participant is derived based on the type of operation and its associated payload. * * @param {IxOperation[]} steps - The array of operation steps to process. * @returns {IxParticipant[]} - The array of processed participants. * @throws {Error} - Throws an error if an unsupported operation type is encountered. */ const processParticipants = (ixObject) => { const participants = new Map(); // Add sender to participants participants.set(trimHexPrefix(ixObject.sender), { address: ixObject.sender, lock_type: LockType.MUTATE_LOCK }); // Add payer if it exists if (ixObject.payer != null) { participants.set(trimHexPrefix(ixObject.payer), { address: ixObject.payer, lock_type: LockType.MUTATE_LOCK }); } // Process ix_operations and add participants ixObject.ix_operations.forEach((operation) => { switch (operation.type) { case OpType.PARTICIPANT_CREATE: { const participantCreatePayload = operation.payload; participants.set(participantCreatePayload.address, { address: participantCreatePayload.address, lock_type: LockType.MUTATE_LOCK }); break; } case OpType.ASSET_CREATE: break; case OpType.ASSET_MINT: case OpType.ASSET_BURN: { const assetSupplyPayload = operation.payload; const address = "0x" + trimHexPrefix(assetSupplyPayload.asset_id).slice(8); participants.set(address, { address: address, lock_type: LockType.MUTATE_LOCK }); break; } case OpType.ASSET_TRANSFER: { const assetActionPayload = operation.payload; participants.set(assetActionPayload.beneficiary, { address: assetActionPayload.beneficiary, lock_type: LockType.MUTATE_LOCK }); break; } case OpType.LOGIC_DEPLOY: break; case OpType.LOGIC_ENLIST: case OpType.LOGIC_INVOKE: { const logicPayload = operation.payload; const address = "0x" + trimHexPrefix(logicPayload.logic_id).slice(6); participants.set(address, { address: address, lock_type: LockType.MUTATE_LOCK }); break; } default: ErrorUtils.throwError("Unsupported Ix type", ErrorCode.INVALID_ARGUMENT); } }); // Add additional participants if they exist if (ixObject.participants != null) { ixObject.participants.forEach((participant) => { const address = trimHexPrefix(participant.address); if (!participants.has(address)) { participants.set(address, { address: participant.address, lock_type: participant.lock_type }); } }); } return Array.from(participants.values()); }; /** * Processes an array of ix_operations by serializing their payloads into byte form * and returns the processed ix_operations. * * @param {IxOperation[]} ix_operations - Operations to process. * @returns {ProcessedIxOperation[]} - Processed ix_operations with serialized payloads. * @throws {Error} - If the payload is missing or operation type is unsupported. */ const processOperations = (ix_operations) => { return ix_operations.map(operation => { if (!operation.payload) { ErrorUtils.throwError("Payload is missing!", ErrorCode.MISSING_ARGUMENT); } const payload = serializePayload(operation.type, operation.payload); return { ...operation, payload: "0x" + bytesToHex(payload) }; }); }; /** * Processes the interaction object based on its type and returns the processed object. * * @param {CallorEstimateIxObject} ixObject - The interaction object to be processed. * @returns {ProcessedCallorEstimateIxObject} - The processed interaction object. * @throws {Error} - Throws an error if the interaction type is unsupported or if there is a missing payload. */ export const processIxObject = (ixObject) => { try { return { nonce: toQuantity(ixObject.nonce), sender: ixObject.sender, fuel_price: toQuantity(ixObject.fuel_price), fuel_limit: toQuantity(ixObject.fuel_limit), funds: processFunds(ixObject), ix_operations: processOperations(ixObject.ix_operations), participants: processParticipants(ixObject) }; } catch (err) { ErrorUtils.throwError("Failed to process interaction object", ErrorCode.UNKNOWN_ERROR, { originalError: err }); } }; //# sourceMappingURL=interaction.js.map