js-moi-providers
Version:
Module to connect and interact with MOI network
480 lines • 19.7 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.toInteractionArgs = exports.toRawSignatures = exports.toRawInteractionObject = exports.processInteractionObject = exports.validateAssetCreate = exports.validateLogicAction = exports.validateLogicDeploy = exports.validateLogicPayload = exports.validateAccountInherit = exports.validateAccountConfigure = exports.validateParticipantCreate = exports.validateAssetAction = exports.validateKeyRevoke = exports.validateKeyAdd = void 0;
const js_moi_utils_1 = require("js-moi-utils");
const js_moi_identifiers_1 = require("js-moi-identifiers");
const js_moi_constants_1 = require("js-moi-constants");
const js_polo_1 = require("js-polo");
const secp256k1_1 = require("@noble/secp256k1");
const validateKeyAdd = (key, index) => {
if (typeof key.public_key !== "string" || key.public_key.length === 0) {
throw new Error("public key must be a non-empty hex string");
}
if (typeof key.weight !== "number" || key.weight <= 0) {
throw new Error("weight must be a positive number");
}
if (key.signature_algorithm !== 0) {
throw new Error("signature algorithm must be 0");
}
};
exports.validateKeyAdd = validateKeyAdd;
const validateKeyRevoke = (key, index) => {
if (typeof key.key_id !== "number" || key.key_id < 0) {
throw new Error("key id must be a non-negative number");
}
return key;
};
exports.validateKeyRevoke = validateKeyRevoke;
const validateAssetAction = (value) => {
if (value == null)
throw new Error("payload is required");
const { asset_id, callsite, calldata, funds } = value;
if (typeof asset_id !== "string" || asset_id.length === 0) {
throw new Error("asset_id must be a non-empty hex string");
}
if (typeof callsite !== "string" || callsite.length === 0) {
throw new Error("callsite must be a non-empty string");
}
if (calldata !== undefined) {
if (typeof calldata !== "string" || calldata.length === 0) {
throw new Error("calldata must be a non-empty hex string if provided");
}
}
if (funds != null) {
if (!(funds instanceof Object)) {
throw new Error("funds must be a Object<Hex, number|bigint>");
}
for (const [k, v] of Object.entries(funds)) {
if (typeof k !== "string" || k.length === 0) {
throw new Error("funds keys must be non-empty hex strings");
}
if (typeof v !== "number" && typeof v !== "bigint") {
throw new Error("funds values must be number or bigint");
}
if (typeof v === "number" && v < 0) {
throw new Error("funds number values must be non-negative");
}
}
}
};
exports.validateAssetAction = validateAssetAction;
const validateParticipantCreate = (payload) => {
if (!payload) {
throw new Error("payload is required");
}
if (typeof payload.id !== "string" || payload.id.length === 0) {
throw new Error("id must be a non-empty string (Hex address)");
}
if (!Array.isArray(payload.keys_payload)) {
throw new Error(`keys payload must be an array`);
}
if (payload.keys_payload == null || payload.keys_payload.length === 0) {
throw new Error(`keys payload must not be empty`);
}
(0, exports.validateAssetAction)(payload.value);
payload.keys_payload.forEach((k, idx) => (0, exports.validateKeyAdd)(k, idx));
};
exports.validateParticipantCreate = validateParticipantCreate;
const validateAccountConfigure = (payload) => {
if (!payload) {
throw new Error("payload is required");
}
const hasAdd = Array.isArray(payload.add) && payload.add.length > 0;
const hasRevoke = Array.isArray(payload.revoke) && payload.revoke.length > 0;
if (!hasAdd && !hasRevoke) {
throw new Error("payload must have either non-empty add or revoke");
}
if (hasAdd) {
payload.add.forEach((k, idx) => (0, exports.validateKeyAdd)(k, idx));
}
if (hasRevoke) {
payload.revoke.forEach((k, idx) => (0, exports.validateKeyRevoke)(k, idx));
}
};
exports.validateAccountConfigure = validateAccountConfigure;
const validateAccountInherit = (payload) => {
if (!payload) {
throw new Error("payload is required");
}
if (typeof payload.target_account !== "string" || payload.target_account.length === 0) {
throw new Error("target account must be a non-empty hex string");
}
(0, exports.validateAssetAction)(payload.value);
// sub_account_index must be a non-negative number
if (typeof payload.sub_account_index !== "number" || payload.sub_account_index < 0) {
throw new Error("sub account index must be a non-negative number");
}
};
exports.validateAccountInherit = validateAccountInherit;
const validateLogicPayload = (payload) => {
if (typeof payload.callsite !== "string" || payload.callsite.length === 0) {
throw new Error("callsite must be a non-empty string");
}
if (payload.calldata !== undefined) {
if (typeof payload.calldata !== "string" || payload.calldata.length === 0) {
throw new Error("calldata must be a non-empty hex string if provided");
}
}
if (payload.interfaces !== undefined) {
if (typeof payload.interfaces !== "object" || Array.isArray(payload.interfaces)) {
throw new Error("interfaces must be an object");
}
for (const [k, v] of Object.entries(payload.interfaces)) {
if (typeof k !== "string" || k.length === 0) {
throw new Error("interface key must be a non-empty string");
}
if (typeof v !== "string" || v.length === 0) {
throw new Error(`interface['${k}'] must be a non-empty hex string`);
}
}
}
};
exports.validateLogicPayload = validateLogicPayload;
const validateLogicDeploy = (payload) => {
if (!payload) {
throw new Error("payload is required");
}
if (typeof payload.manifest == null) {
throw new Error("payload must include manifest");
}
(0, exports.validateLogicPayload)(payload);
};
exports.validateLogicDeploy = validateLogicDeploy;
const validateLogicAction = (payload) => {
if (!payload) {
throw new Error("payload is required");
}
// manifest is omitted, so we don’t validate it
if (typeof payload.logic_id !== "string" || payload.logic_id.length === 0) {
throw new Error("logic_id must be a non-empty hex string");
}
(0, exports.validateLogicPayload)(payload);
};
exports.validateLogicAction = validateLogicAction;
const validateAssetCreate = (payload) => {
if (!payload) {
throw new Error("payload is required");
}
// symbol: required, non-empty string
if (typeof payload.symbol !== "string" || payload.symbol.length === 0) {
throw new Error("symbol must be a non-empty string");
}
// dimension: optional, must be non-negative number if provided
if (payload.dimension !== undefined) {
if (typeof payload.dimension !== "number" || payload.dimension < 0) {
throw new Error("dimension must be a non-negative number if provided");
}
}
// decimals: optional, must be non-negative number if provided
if (payload.decimals !== undefined) {
if (typeof payload.decimals !== "number" || payload.decimals < 0) {
throw new Error("decimals must be a non-negative number if provided");
}
}
// standard: required
if (payload.standard == null) {
throw new Error("standard is required");
}
// enable_events: required boolean
if (typeof payload.enable_events !== "boolean") {
throw new Error("enable events must be a boolean value");
}
// manager: required non-empty hex string
if (typeof payload.manager !== "string" || payload.manager.length === 0) {
throw new Error("manager must be a non-empty hex string");
}
// max_supply: required non-negative number
if (typeof payload.max_supply !== "number" || payload.max_supply < 0) {
throw new Error("max_supply must be a non-negative number");
}
// metadata: required object with arrays of non-empty hex strings
if (payload.metadata) {
if (typeof payload.metadata !== "object" || Array.isArray(payload.metadata)) {
throw new Error("metadata must be a non-empty object");
}
for (const [k, v] of Object.entries(payload.metadata)) {
if (typeof v !== "string" || v.length === 0) {
throw new Error(`metadata['${k}'] must be a non-empty hex string`);
}
}
}
// logic_payload: optional, validated if provided
if (payload.logic_payload !== undefined) {
(0, exports.validateLogicAction)(payload.logic_payload);
}
return payload;
};
exports.validateAssetCreate = validateAssetCreate;
const polorize = (payload, schema) => {
const polorizer = new js_polo_1.Polorizer();
polorizer.polorize(payload, schema);
return polorizer.bytes();
};
const withCalldata = (payload) => ({
...payload,
calldata: payload.calldata ? (0, js_moi_utils_1.hexToBytes)(payload.calldata) : new Uint8Array(),
});
const withAssetId = (payload) => ({
...payload,
asset_id: new js_moi_identifiers_1.Identifier(payload.asset_id).toBytes(),
});
const mapPublicKeys = (keys) => keys?.map(k => ({ ...k, public_key: (0, js_moi_utils_1.hexToBytes)(k.public_key) }));
const mapHexValues = (obj = {}) => {
const out = new Map();
Object.keys(obj).forEach(k => { out[k] = (0, js_moi_utils_1.hexToBytes)(out[k]); });
return out;
};
function processParticipantCreate(payload) {
const processed = {
id: new js_moi_identifiers_1.ParticipantId(payload.id).toBytes(),
keys_payload: mapPublicKeys(payload.keys_payload),
value: withCalldata(withAssetId(payload.value)),
};
return polorize(processed, js_moi_utils_1.participantCreateSchema);
}
function processAccountConfigure(payload) {
return polorize({ ...payload, add: mapPublicKeys(payload.add) }, js_moi_utils_1.accountConfigureSchema);
}
function processAccountInherit(payload) {
const processed = {
...payload,
target_account: new js_moi_identifiers_1.Identifier(payload.target_account).toBytes(),
value: withCalldata(withAssetId(payload.value)),
};
return polorize(processed, js_moi_utils_1.accountInheritSchema);
}
function processAssetCreate(payload) {
const createPayload = {
...payload,
manager: new js_moi_identifiers_1.ParticipantId(payload.manager).toBytes(),
metadata: mapHexValues(payload.metadata),
};
if (payload.logic_payload) {
createPayload.logic_payload = {
...withCalldata(payload.logic_payload),
logic_id: new js_moi_identifiers_1.AssetId(payload.logic_payload.logic_id).toBytes(),
interfaces: mapHexValues(payload.logic_payload.interfaces),
};
}
return polorize(createPayload, js_moi_utils_1.assetCreateSchema);
}
function processAssetInvoke(op) {
(0, exports.validateAssetAction)(op);
const payload = withCalldata(withAssetId(op));
return polorize(payload, js_moi_utils_1.assetActionSchema);
}
function processLogicDeploy(payload) {
const processed = {
...withCalldata(payload),
manifest: (0, js_moi_utils_1.hexToBytes)(payload.manifest),
interfaces: mapHexValues(payload.interfaces),
};
return polorize(processed, js_moi_utils_1.logicSchema);
}
function processLogicAction(payload) {
const processed = {
...withCalldata(payload),
logic_id: new js_moi_identifiers_1.LogicId(payload.logic_id).toBytes(),
interfaces: mapHexValues(payload.interfaces),
};
return polorize(processed, js_moi_utils_1.logicSchema);
}
/**
* Processes ix_operations and returns an array of processed participants.
*
* @param {InteractionObject} ixObject - The interaction object containing sender, payer, operations, etc.
* @returns {IxParticipant[]} - The processed participants.
* @throws {Error} - If an unsupported operation type is encountered.
*/
const processParticipants = (ixObject) => {
const participants = new Map();
const addParticipant = (id, lock_type) => {
participants.set((0, js_moi_utils_1.trimHexPrefix)(id), { id, lock_type });
};
// Add sender
addParticipant(ixObject.sender.id, js_moi_utils_1.LockType.MUTATE_LOCK);
// Add payer if present
if (ixObject.payer && ixObject.payer != js_moi_constants_1.ZERO_ADDRESS) {
addParticipant(ixObject.payer, js_moi_utils_1.LockType.MUTATE_LOCK);
}
// Process operations
for (const operation of ixObject.ix_operations) {
switch (operation.type) {
case js_moi_utils_1.OpType.PARTICIPANT_CREATE: {
const { value } = operation.payload;
addParticipant(value.asset_id, js_moi_utils_1.LockType.NO_LOCK);
break;
}
case js_moi_utils_1.OpType.ACCOUNT_CONFIGURE:
break;
case js_moi_utils_1.OpType.ACCOUNT_INHERIT:
addParticipant(js_moi_constants_1.KMOI_ASSET_ID, js_moi_utils_1.LockType.NO_LOCK);
break;
case js_moi_utils_1.OpType.ASSET_CREATE:
break;
case js_moi_utils_1.OpType.ASSET_INVOKE: {
const { asset_id } = operation.payload;
addParticipant(asset_id, js_moi_utils_1.LockType.NO_LOCK);
break;
}
case js_moi_utils_1.OpType.LOGIC_DEPLOY:
break;
case js_moi_utils_1.OpType.LOGIC_ENLIST:
case js_moi_utils_1.OpType.LOGIC_INVOKE:
const { logic_id } = operation.payload;
addParticipant(`0x${logic_id}`, js_moi_utils_1.LockType.MUTATE_LOCK);
break;
default:
js_moi_utils_1.ErrorUtils.throwError("Unsupported Ix type", js_moi_utils_1.ErrorCode.INVALID_ARGUMENT);
}
}
// Merge additional participants (if not already present)
if (ixObject.participants) {
for (const { id, lock_type } of ixObject.participants) {
if (!participants.has((0, js_moi_utils_1.trimHexPrefix)(id))) {
addParticipant(id, lock_type);
}
}
}
return [...participants.values()];
};
const processInteractionObject = (ix) => {
return {
...ix,
participants: processParticipants(ix),
};
};
exports.processInteractionObject = processInteractionObject;
const toRawFund = (fund) => {
return {
...fund,
asset_id: new js_moi_identifiers_1.AssetId(fund.asset_id).toBytes(),
};
};
const toRawParticipant = (participant) => {
return {
...participant,
id: new js_moi_identifiers_1.Identifier(participant.id).toBytes(),
};
};
const toRawOperation = (operation) => {
switch (operation.type) {
case js_moi_utils_1.OpType.PARTICIPANT_CREATE: {
(0, exports.validateParticipantCreate)(operation.payload);
return {
...operation,
payload: processParticipantCreate(operation.payload)
};
}
case js_moi_utils_1.OpType.ACCOUNT_CONFIGURE: {
(0, exports.validateAccountConfigure)(operation.payload);
return {
...operation,
payload: processAccountConfigure(operation.payload)
};
}
case js_moi_utils_1.OpType.ACCOUNT_INHERIT: {
(0, exports.validateAccountInherit)(operation.payload);
return {
...operation,
payload: processAccountInherit(operation.payload)
};
}
case js_moi_utils_1.OpType.ASSET_CREATE: {
(0, exports.validateAssetCreate)(operation.payload);
return {
...operation,
payload: processAssetCreate(operation.payload)
};
}
case js_moi_utils_1.OpType.ASSET_INVOKE: {
(0, exports.validateAssetAction)(operation.payload);
return {
...operation,
payload: processAssetInvoke(operation.payload)
};
}
case js_moi_utils_1.OpType.LOGIC_DEPLOY: {
(0, exports.validateLogicDeploy)(operation.payload);
return {
...operation,
payload: processLogicDeploy(operation.payload)
};
}
case js_moi_utils_1.OpType.LOGIC_INVOKE:
case js_moi_utils_1.OpType.LOGIC_ENLIST: {
(0, exports.validateLogicAction)(operation.payload);
return {
...operation,
payload: processLogicAction(operation.payload)
};
}
default:
throw new Error(`Unsupported interaction type: ${operation.type}`);
}
};
/**
* Transforms an interaction object to a format that can be serialized to POLO.
*
* @param ix Interaction object
* @returns a raw interaction object
*/
const toRawInteractionObject = (ix) => {
ix.participants = processParticipants(ix);
return {
...ix,
sender: { ...ix.sender, id: new js_moi_identifiers_1.ParticipantId(ix.sender.id).toBytes() },
payer: ix.payer ? new js_moi_identifiers_1.ParticipantId(ix.payer).toBytes() : (0, js_moi_utils_1.hexToBytes)(js_moi_constants_1.ZERO_ADDRESS),
funds: ix.funds?.map((fund) => toRawFund(fund)),
participants: ix.participants?.map((participant) => toRawParticipant(participant)),
ix_operations: ix.ix_operations?.map((operation) => toRawOperation(operation)),
preferences: ix.preferences ? {
...ix.preferences,
compute: ix.preferences.compute ? (0, js_moi_utils_1.hexToBytes)(ix.preferences.compute) : undefined,
} : undefined,
perception: ix.perception ? (0, js_moi_utils_1.hexToBytes)(ix.perception) : undefined,
};
};
exports.toRawInteractionObject = toRawInteractionObject;
const toRawSignatures = (signs) => {
return signs.map(sign => ({
...sign,
id: (0, js_moi_utils_1.hexToBytes)(sign.id),
signature: (0, js_moi_utils_1.hexToBytes)(sign.signature)
}));
};
exports.toRawSignatures = toRawSignatures;
const toFundArgs = (fund) => {
return {
...fund,
amount: (0, js_moi_utils_1.toQuantity)(fund.amount)
};
};
const toOperationArgs = (operation) => {
const rawOpPayload = toRawOperation(operation);
return {
...operation,
payload: "0x" + (0, secp256k1_1.bytesToHex)(rawOpPayload.payload)
};
};
const toInteractionArgs = (ix) => {
ix.participants = processParticipants(ix);
return {
sender: ix.sender,
payer: ix.payer ?? js_moi_constants_1.ZERO_ADDRESS,
fuel_price: (0, js_moi_utils_1.toQuantity)(ix.fuel_price),
fuel_limit: (0, js_moi_utils_1.toQuantity)(ix.fuel_limit),
funds: ix.funds?.map((fund) => toFundArgs(fund)),
ix_operations: ix.ix_operations?.map((operation) => toOperationArgs(operation)),
preferences: ix.preferences ? {
...ix.preferences,
consensus: ix.preferences.consensus ? {
...ix.preferences.consensus,
mtq: (0, js_moi_utils_1.toQuantity)(ix.preferences.consensus.mtq ?? 0)
} : undefined,
} : undefined,
participants: ix.participants
};
};
exports.toInteractionArgs = toInteractionArgs;
//# sourceMappingURL=interaction.js.map