2025-05-06 18:03:51 +03:00

183 lines
4.7 KiB
TypeScript

import { constructTake, extractRead, GRead, GTake, ResolvedOperation, ResolvedRead } from '@coinweb/contract-kit';
import { CwebTake } from '@coinweb/contract-kit/dist/types/operations/take';
import { ResolvedOp, TypedClaim } from '../../types';
import { resultKey } from '../claims';
import { mutexBlockLockKey, mutexBlockUnlockKey, mutexExecOpsKey, MutexExecOpsResult } from '../mutex';
import { isResolvedBlockOp, isResolvedReadOp, isResolvedTakeOp } from '../utils';
const extractFunds = (fundsOp: GRead<ResolvedRead>, takenFundsIds: string[]) => {
if (!fundsOp) {
return {
takeOps: [],
amount: 0n,
};
}
const claims = extractRead(fundsOp);
const newClaims = claims?.filter(({ content }) => !takenFundsIds.includes((content.key.second_part as [string])[0]));
if (!newClaims?.length) {
return {
takeOps: [],
amount: 0n,
};
}
return {
takeOps: newClaims.map(({ content }) => constructTake(content.key)),
amount: newClaims.reduce((acc, { content }) => acc + BigInt(content.fees_stored), 0n),
};
};
export const extractOps = ({
contractArgs,
takenFundsIds,
execOpsIndexes,
}: {
contractArgs: ResolvedOperation[];
takenFundsIds: string[];
execOpsIndexes: number[];
}) => {
if (!contractArgs.length) {
return {
extractedOps: [],
fundsTakeOps: [],
storedCweb: 0n,
executionOpsTakeOp: null,
};
}
const [fundsOp, ...resolvedOps] = contractArgs;
if (!isResolvedReadOp(fundsOp)) {
throw new Error('Wrong funds claims');
}
const extractedOps: ResolvedOp[] = [];
let executedOps: MutexExecOpsResult = [];
let executionOpsProfit: bigint = 0n;
let executionOpsTakeOp: GTake<CwebTake> | null = null;
let i = 0;
while (i < resolvedOps.length) {
const op = resolvedOps[i];
if (isResolvedBlockOp(op)) {
const { first } = op.BlockOp.blocks_on[0][0];
console.log('first >>>', JSON.stringify(first));
//Maybe it is needed to check more conditions here
if (Array.isArray(first)) {
switch (first[0]) {
case resultKey: {
const nextAfterBlock = resolvedOps[i + 1];
if (!isResolvedTakeOp(nextAfterBlock)) {
throw new Error('Wrong subcall result');
}
extractedOps.push({ ChildOp: 0 }, ...(nextAfterBlock.TakeOp.result as TypedClaim<ResolvedOp[]>).body);
i += 2;
continue;
}
case mutexBlockLockKey: {
const nextAfterBlock = resolvedOps[i + 1];
if (!isResolvedTakeOp(nextAfterBlock)) {
throw new Error('Wrong mutex lock result');
}
extractedOps.push({ SlotOp: { ok: true } });
i += 2;
continue;
}
case mutexBlockUnlockKey: {
const nextAfterBlock = resolvedOps[i + 1];
if (!isResolvedTakeOp(nextAfterBlock)) {
throw new Error('Wrong mutex unlock result');
}
extractedOps.push({ SlotOp: { ok: true } });
i += 2;
continue;
}
case mutexExecOpsKey: {
const nextAfterBlock = resolvedOps[i + 1];
const execOpResultClaim = extractRead(nextAfterBlock)?.[0]?.content;
if (!execOpResultClaim) {
throw new Error('Wrong mutex exec result claim');
}
executedOps = execOpResultClaim.body as MutexExecOpsResult;
executionOpsProfit = BigInt(execOpResultClaim.fees_stored);
executionOpsTakeOp = constructTake(execOpResultClaim.key);
i += 2;
continue;
}
default:
break;
}
}
}
if (isResolvedTakeOp(op)) {
const keyFirstPart = op.TakeOp.result.key.first_part;
if (Array.isArray(keyFirstPart) && keyFirstPart[0] === mutexExecOpsKey) {
i++;
continue;
}
}
extractedOps.push(op);
i++;
}
const allOps: ResolvedOp[] = [];
const totalOpsCount = extractedOps.length + executedOps.length;
for (let i = 0; i < totalOpsCount; i++) {
if (execOpsIndexes.includes(i)) {
const op = executedOps.shift();
if (!op) {
throw new Error('Wrong mutex exec result place');
}
if (op.ok) {
allOps.push(op.resolved);
} else {
allOps.push({ SlotOp: { ok: false, error: op.error } });
}
} else {
const op = extractedOps.shift();
if (!op) {
throw new Error('Contract call args parsing error');
}
allOps.push(op);
}
}
const { takeOps, amount } = extractFunds(fundsOp, takenFundsIds);
return {
fundsTakeOps: takeOps,
storedCweb: amount + executionOpsProfit,
extractedOps: allOps,
executionOpsTakeOp,
};
};