Compare commits

...

3 Commits

Author SHA1 Message Date
d5aaecce18 wip 2025-06-05 12:35:20 +03:00
d110ffee46 wip 2025-06-04 18:26:07 +03:00
186b719a94 wip 2025-05-15 10:28:57 +03:00
22 changed files with 241 additions and 88 deletions

View File

@ -14,9 +14,11 @@ includes:
blake3: 321b1f88930aead7fe47961c8664005b3441f6502824769bf21eb06c3e5aaba4 blake3: 321b1f88930aead7fe47961c8664005b3441f6502824769bf21eb06c3e5aaba4
use: use:
- dex-app.cm v0.0.67+test - jump-listener.cm v0.1.8
- eth_offer_maker 0.0.67+devnet - jump_listener_devnet v0.1.8
- jump-listener.cm v0.0.5 - jump-forwarder.cm v0.1.5
- jump_forwarder_devnet v0.1.5
interpreters: {} interpreters: {}
@ -31,3 +33,4 @@ contract_instances:
template: contract.cm v0.0.1 template: contract.cm v0.0.1
parameters: parameters:
content: [] content: []

View File

@ -4,4 +4,4 @@ NPM_PASSWORD=
# This is needed when working with WASM modules (nodeLinker: node-modules is mandatory) # This is needed when working with WASM modules (nodeLinker: node-modules is mandatory)
NODE_OPTIONS="--experimental-wasm-modules --no-warnings=ExperimentalWarning --max-old-space-size=8192" NODE_OPTIONS="--experimental-wasm-modules --no-warnings=ExperimentalWarning --max-old-space-size=8192"
REGISTRATION_PROFILE=devnet REGISTRATION_PROFILE=test

View File

@ -47,7 +47,7 @@
"@coinweb/self-register": "0.1.3", "@coinweb/self-register": "0.1.3",
"@coinweb/claims-client": "0.1.6-debug", "@coinweb/claims-client": "0.1.6-debug",
"@coinweb/contract-kit": "0.2.6", "@coinweb/contract-kit": "0.2.6",
"@coinweb/testing-sdk": "0.0.9-remote", "@coinweb/testing-sdk": "0.0.10-mutex",
"@coinweb/minimal-sdk": "1.2.21" "@coinweb/minimal-sdk": "1.2.21"
}, },
"devDependencies": { "devDependencies": {

View File

@ -9,5 +9,5 @@ export enum PUBLIC_METHODS {
} }
export const FEE = { export const FEE = {
ADD_WORD: 100000n, ADD_WORD: 10000000n,
}; };

View File

@ -0,0 +1,93 @@
import path from 'node:path';
import { constructContractIssuer, constructContractRef, type PreparedCallInfo } from '@coinweb/contract-kit';
import { UnitTest, type ExecInfo, type RoundInfo, type UnitTestContext, type DbWriteOp } from '@coinweb/testing-sdk';
import { AddWordArgs, FEE, PUBLIC_METHODS } from '../../offchain/shared';
import { CONTRACT_INSTANCE_ID, TEST_PRIVATE_KEY, TEST_PUBLIC_KEY, waitRoundsCount } from './helpers';
const addWordRound = (creatorAccount: Buffer): RoundInfo => {
const self = constructContractIssuer(CONTRACT_INSTANCE_ID);
const kvDeleteCallInfo: PreparedCallInfo = {
ref: constructContractRef(self, []),
methodInfo: { methodName: PUBLIC_METHODS.ADD_WORD, methodArgs: ['TEST_HELLO'] satisfies AddWordArgs },
contractArgs: [],
contractInfo: { providedCweb: FEE.ADD_WORD, authenticated: null }, // null = authenticated account in tests
};
const withFunds = { type: { privateKey: creatorAccount } };
return {
txsInfo: {
// @ts-ignore
txs: [{ callInfo: kvDeleteCallInfo, withFunds }],
l1_events: [],
},
claims: [],
blocks_on: [],
};
};
const runTest = async (context: UnitTestContext, input: ExecInfo, checkFn: (results: DbWriteOp[]) => void) => {
const test = new UnitTest(context);
await test.load(path.join(import.meta.dirname, '../../../tests_data/state.json'));
await test.run(input, { checkFn });
};
describe('PUBLIC_METHODS.ADD_WORD Tests', () => {
let creatorAccount: { publicKey: Buffer; privateKey: Buffer };
beforeAll(() => {
creatorAccount = {
privateKey: Buffer.from(TEST_PRIVATE_KEY, 'hex'),
publicKey: Buffer.from(TEST_PUBLIC_KEY, 'hex'),
};
});
it('Should successfully execute', async () => {
const input: ExecInfo = {
rounds: [...waitRoundsCount(50), addWordRound(creatorAccount.privateKey), ...waitRoundsCount(2)],
};
const checkFn = (results: DbWriteOp[]) => {
expect(results).toBeDefined();
// @ts-ignore
// const errors = findAllValuesRecursivelyByKey<{ ContractError: ContractError }>(results, 'Invalid');
// expect(errors.length).toBe(0);
// @ts-ignore
// const storeOps = findAllValuesRecursivelyByKey<CwebStore>(results, 'StoreOp');
// expect(storeOps.length).toBeGreaterThan(0);
// const existingPayload = storeOps.find((s) => isEqual(s.key.first_part, createFirstPartDefault()));
// expect(existingPayload).toBeDefined();
// expect(existingPayload?.key).toStrictEqual(createDefaultKey(Number.MAX_SAFE_INTEGER));
// expect(existingPayload?.key.first_part).toStrictEqual(createFirstPartDefault());
// const { id, data, owner, tx } = existingPayload?.body as StoreKvPayload;
// expect(id).toBeDefined();
// expect(data).toBeDefined();
// expect(owner).toBeDefined();
// expect(tx).toBeDefined();
// expect(tx?.timestamp).toBeDefined();
// expect(tx?.coinwebTxId).toBeDefined();
// expect(Number(id)).toStrictEqual(Number.MAX_SAFE_INTEGER);
// expect(owner).toStrictEqual({
// auth: 'EcdsaContract',
// payload: TEST_PUBLIC_KEY,
// });
};
const context: UnitTestContext = {
name: 'add_word',
testPath: path.join(import.meta.dirname, './tests_data/add_word'),
verbose: true,
};
await runTest(context, input, checkFn);
});
});

View File

@ -0,0 +1,35 @@
import path from 'node:path';
import { getInstanceFromIndex, type RoundInfo } from '@coinweb/testing-sdk';
const onchainPackage = (await import('../../../dist/out/package.json')) as {
name: string;
};
export const CONTRACT_INSTANCE = await getInstanceFromIndex({
path: path.resolve(import.meta.dirname, '../../../tests_data/index.yaml'),
instance_alias: 'cwait-contract 0.0.1-test',
});
export const CONTRACT_INSTANCE_ID = '0x'.concat(CONTRACT_INSTANCE.instance_id.replace(/0x/, ''));
export const CONTRACT_TEMPLATE_ID = `0x${onchainPackage.name.substring(5)}`;
export const TEST_PRIVATE_KEY = '31c70848e4e3aaffcf91f134853ec966e913aa9a813115bcb81512e7625f46a9';
export const TEST_PUBLIC_KEY = '03951f89fe78e13f295d96eb7afa1e0da726df7d58f9c84f7144e5febc30efeec4';
export const waitRoundsCount = (roundsCount: number): RoundInfo[] => {
const emptyRound: RoundInfo = {
txsInfo: {
txs: [],
l1_events: [],
},
claims: [],
blocks_on: [],
};
const rounds: RoundInfo[] = [];
for (let i = 0; i < roundsCount; i++) {
rounds.push(emptyRound);
}
return rounds;
};

View File

@ -58,7 +58,7 @@ export const handleContext = (ctx: Context) => {
caller, caller,
thisId, thisId,
parentId, parentId,
saveResult, shouldSaveResult,
takenFundsIds = [], takenFundsIds = [],
execOpsIndexes = [], execOpsIndexes = [],
] = getMethodArgs(); ] = getMethodArgs();
@ -70,7 +70,7 @@ export const handleContext = (ctx: Context) => {
initialContext.parentId = parentId; initialContext.parentId = parentId;
initialContext.methodName = methodName; initialContext.methodName = methodName;
initialContext.initialArgs = initialArgs ?? []; initialContext.initialArgs = initialArgs ?? [];
initialContext.needSaveResult = saveResult ?? false; initialContext.needSaveResult = shouldSaveResult ?? false;
const { authInfo } = getCallParameters(getRawContext()); const { authInfo } = getCallParameters(getRawContext());
initialContext.user = (authInfo && extractUser(authInfo)) ?? caller ?? null; initialContext.user = (authInfo && extractUser(authInfo)) ?? caller ?? null;

View File

@ -86,7 +86,11 @@ export const extractOps = ({
throw new Error('Wrong subcall result'); throw new Error('Wrong subcall result');
} }
extractedOps.push({ ChildOp: 0 }, ...(nextAfterBlock.TakeOp.result as TypedClaim<ResolvedOp[]>).body); extractedOps.push({
ChildOp: {
ops: (nextAfterBlock.TakeOp.result as TypedClaim<ResolvedOp[]>).body,
},
});
i += 2; i += 2;
continue; continue;
@ -112,7 +116,7 @@ export const extractOps = ({
throw new Error('Wrong mutex unlock result'); throw new Error('Wrong mutex unlock result');
} }
extractedOps.push({ SlotOp: { ok: true } }); extractedOps.push({ UnlockOp: 0 });
i += 2; i += 2;
continue; continue;

View File

@ -11,7 +11,7 @@ import {
} from '@coinweb/contract-kit'; } from '@coinweb/contract-kit';
import { CwebTake } from '@coinweb/contract-kit/dist/types/operations/take'; import { CwebTake } from '@coinweb/contract-kit/dist/types/operations/take';
import { ExecutorMethodArgs, PreparedExtendedStoreOp, PreparedOp, ResolvedOp, ResolvedSlotOp } from '../../../types'; import { ExecutorMethodArgs, PreparedExtendedStoreOp, PreparedOp, ResolvedSlotOp } from '../../../types';
import { constructFundsClaimRangRead, constructFundsClaimStore } from '../../claims/funds'; import { constructFundsClaimRangRead, constructFundsClaimStore } from '../../claims/funds';
import { constructResultBlockFilter, constructResultClaimTake, resultKey } from '../../claims/result'; import { constructResultBlockFilter, constructResultClaimTake, resultKey } from '../../claims/result';
import { context, getRawContext } from '../../context'; import { context, getRawContext } from '../../context';
@ -68,16 +68,18 @@ export const prepareInThreadTxs = ({
let callsPrepared = 0; let callsPrepared = 0;
const resolvedSlotOps = new Array(outThreadTasksCount).fill({ SlotOp: { ok: true } }) satisfies ResolvedSlotOp[]; const resolvedSlotOps = new Array(outThreadTasksCount).fill({ SlotOp: { ok: true } }) satisfies ResolvedSlotOp[];
//Mutex exec ops
const preparedExecOps: (PreparedExtendedStoreOp | GTake<CwebTake>)[] = []; const preparedExecOps: (PreparedExtendedStoreOp | GTake<CwebTake>)[] = [];
const excOpsIndexes: number[] = []; const excOpsIndexes: number[] = [];
const resolvedChildOps: ResolvedOp[] = [...context.ops, ...resolvedSlotOps]; // const resolvedChildOps: ResolvedOp[] = [...context.ops, ...resolvedSlotOps];
//Children
//Arg for the main call //Arg for the main call
const callArgs: PreparedOperation[] = []; const callArgs: PreparedOperation[] = [];
//Info for separate child call //Info for separate parallel calls
const childCalls: FullCallInfo[] = []; const parallelCalls: FullCallInfo[] = [];
const outThreadOps: PreparedOperation[] = []; const outThreadOps: PreparedOperation[] = [];
@ -89,14 +91,20 @@ export const prepareInThreadTxs = ({
callArgs.push(constructBlock([constructResultBlockFilter(id)]), constructResultClaimTake(id)); callArgs.push(constructBlock([constructResultBlockFilter(id)]), constructResultClaimTake(id));
txFee += 200n; txFee += 200n;
childCalls.push({ const childOps = [
...context.ops,
...resolvedSlotOps,
...ops.map((_, j) => (i === j ? { ExecOp: { id } } : { SlotOp: { ok: true } })),
];
parallelCalls.push({
callInfo: { callInfo: {
ref: constructContractRef(context.issuer, []), ref: constructContractRef(context.issuer, []),
methodInfo: { methodInfo: {
methodName: context.methodName, methodName: context.methodName,
methodArgs: [ methodArgs: [
context.initialArgs, context.initialArgs,
[...resolvedChildOps, { ExecOp: { id } }], childOps,
context.user, context.user,
id, id,
context.thisId, context.thisId,
@ -151,7 +159,7 @@ export const prepareInThreadTxs = ({
processId: context.thisId, processId: context.thisId,
}); });
childCalls.push({ callInfo }); parallelCalls.push({ callInfo });
callArgs.push(...inThreadOps); callArgs.push(...inThreadOps);
txFee += fee; txFee += fee;
@ -160,14 +168,15 @@ export const prepareInThreadTxs = ({
break; break;
} }
case isPreparedUnlockOp(op): { case isPreparedUnlockOp(op): {
console.log('prepareInThreadTxs >>> unlockOp');
const { callInfo, fee, ops } = constructUnlockCall( const { callInfo, fee, ops } = constructUnlockCall(
context.issuer, context.issuer,
op.UnlockOp.lockId, op.UnlockOp.lockId,
op.UnlockOp.timestamp, op.UnlockOp.timestamp,
false true
); );
childCalls.push({ callInfo }); parallelCalls.push({ callInfo });
callArgs.push(...ops); callArgs.push(...ops);
txFee += fee; txFee += fee;
@ -177,8 +186,6 @@ export const prepareInThreadTxs = ({
callArgs.push(op); callArgs.push(op);
txFee += 100n; txFee += 100n;
} }
resolvedChildOps.push({ SlotOp: { ok: true } });
}); });
if (preparedExecOps.length > 0) { if (preparedExecOps.length > 0) {
@ -191,9 +198,12 @@ export const prepareInThreadTxs = ({
execId, execId,
}); });
childCalls.push({ callInfo }); parallelCalls.push({ callInfo });
txFee += fee; txFee += fee;
console.log(txFee);
console.log(context.funds.availableCweb);
callArgs.push(...ops); callArgs.push(...ops);
} }
@ -206,6 +216,7 @@ export const prepareInThreadTxs = ({
if ('StoreOp' in latestCallArg && (latestCallArg.StoreOp.key.first_part as [string])[0] === resultKey) { if ('StoreOp' in latestCallArg && (latestCallArg.StoreOp.key.first_part as [string])[0] === resultKey) {
//SAVE RESULT CLAIMS //SAVE RESULT CLAIMS
console.log('SAVE RESULT CLAIMS');
if (callArgs.length > 1) { if (callArgs.length > 1) {
throw new Error('Unexpected count of result ops'); throw new Error('Unexpected count of result ops');
} }
@ -221,10 +232,12 @@ export const prepareInThreadTxs = ({
const cwebToStore = availableCweb + storedCweb - BigInt(takeOps.length) * 100n - 500n; const cwebToStore = availableCweb + storedCweb - BigInt(takeOps.length) * 100n - 500n;
console.log('cwebToStore: ', cwebToStore);
resultOps.push( resultOps.push(
constructFundsClaimStore(context.parentId, cwebToStore), passCwebFrom(context.issuer, availableCweb),
...takeOps, ...takeOps,
passCwebFrom(context.issuer, availableCweb) constructFundsClaimStore(context.parentId, cwebToStore)
); );
} }
@ -235,6 +248,8 @@ export const prepareInThreadTxs = ({
txFee += 800n + outThreadFee + BigInt(takeOps.length) * 100n; txFee += 800n + outThreadFee + BigInt(takeOps.length) * 100n;
callsPrepared++; callsPrepared++;
console.log('provided cweb: ', cwebPerCall - txFee + storedCweb);
returnTxs.push( returnTxs.push(
constructContinueTx( constructContinueTx(
getRawContext(), getRawContext(),
@ -267,8 +282,8 @@ export const prepareInThreadTxs = ({
) )
); );
if (childCalls.length || outThreadOps.length) { if (parallelCalls.length || outThreadOps.length) {
returnTxs.push(constructContinueTx(getRawContext(), outThreadOps, childCalls)); returnTxs.push(constructContinueTx(getRawContext(), outThreadOps, parallelCalls));
} }
} }
} }

View File

@ -95,6 +95,7 @@ export const prepareOutThreadTxs = ({
break; break;
} }
case isPreparedUnlockOp(op): { case isPreparedUnlockOp(op): {
console.log('prepareOutThreadTxs >>> unlockOp');
const { callInfo, fee } = constructUnlockCall(context.issuer, op.UnlockOp.lockId, op.UnlockOp.timestamp, false); const { callInfo, fee } = constructUnlockCall(context.issuer, op.UnlockOp.lockId, op.UnlockOp.timestamp, false);
preparedCalls.push({ callInfo }); preparedCalls.push({ callInfo });

View File

@ -18,7 +18,7 @@ export const executor = (method: (...args: any[]) => Promise<void>) => {
} }
if (shouldRestart) { if (shouldRestart) {
console.log('<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< executor-finish'); console.log('<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< executor-finish-restart');
return constructTx(false); return constructTx(false);
} }
@ -29,9 +29,9 @@ export const executor = (method: (...args: any[]) => Promise<void>) => {
}, 0); }, 0);
try { try {
setNextExec(() => method(...context.initialArgs)); setNextExec(() => method(...context.initialArgs), []);
const isFullyExecuted = await execLoop(); const isFullyExecuted = await execLoop();
console.log('<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< executor-finish'); console.log('<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< executor-finish-end');
return constructTx(isFullyExecuted); return constructTx(isFullyExecuted);
} catch (error) { } catch (error) {
console.log((error as Error).message); console.log((error as Error).message);

View File

@ -3,19 +3,8 @@ import { constructStore } from '@coinweb/contract-kit/dist/esm/operations/store'
import { constructResultClaim } from '../../claims/result'; import { constructResultClaim } from '../../claims/result';
import { opMarker } from '../../globals/promise'; import { opMarker } from '../../globals/promise';
import { setNextExec, stopExecution } from '../../runtime'; import { setNextExec, stopExecution } from '../../runtime';
import { import { getAwaitedTasksCount, pushAwaitedTask } from '../../runtime/awaitedTasks';
freezeAwaitedTasks, import { getUsedOps, startSavingUsedOps, stopSavingUsedOps, shiftResolvedOp } from '../../runtime/resolvedOps';
getAwaitedTasksCount,
pushAwaitedTask,
unfreezeAwaitedTasks,
} from '../../runtime/awaitedTasks';
import {
freezeResolvedOps,
getUsedOps,
saveUsedOps,
shiftResolvedOp,
unfreezeResolvedOps,
} from '../../runtime/resolvedOps';
import { isResolvedChildOp, isResolvedExecOp, isResolvedSlotOp } from '../../utils'; import { isResolvedChildOp, isResolvedExecOp, isResolvedSlotOp } from '../../utils';
import { uuid } from '../../utils'; import { uuid } from '../../utils';
@ -55,32 +44,28 @@ export const cwait = <TAsyncCallback extends (...args: any[]) => Promise<unknown
} }
if (isResolvedExecOp(op)) { if (isResolvedExecOp(op)) {
freezeAwaitedTasks();
freezeResolvedOps();
setNextExec(async () => { setNextExec(async () => {
unfreezeAwaitedTasks(); startSavingUsedOps();
unfreezeResolvedOps();
saveUsedOps();
await asyncCallback(...args); await asyncCallback(...args);
stopSavingUsedOps();
if (!getAwaitedTasksCount()) { if (!getAwaitedTasksCount()) {
console.log('push result claim');
pushAwaitedTask(constructStore(constructResultClaim(op.ExecOp.id, getUsedOps()))); pushAwaitedTask(constructStore(constructResultClaim(op.ExecOp.id, getUsedOps())));
} }
}); }, []);
stopExecution(); stopExecution(); //Check: maybe does no affect
return new Promise(() => null); return;
} }
if (isResolvedChildOp(op)) { if (isResolvedChildOp(op)) {
return asyncCallback(...args); return setNextExec(() => asyncCallback(...args), op.ChildOp.ops);
} }
throw new Error('Read operation not found'); throw new Error('Exec or Child operation not found');
} }
}) as TAsyncCallback; }) as TAsyncCallback;
}; };

View File

@ -2,11 +2,11 @@ import { getTime } from 'lib/onchain';
import { opMarker } from '../../globals/promise'; import { opMarker } from '../../globals/promise';
import { LockedKey } from '../../mutex'; import { LockedKey } from '../../mutex';
import { isResolvedLockOp, isResolvedSlotOp, isResolvedUnlockOp, uuid } from '../../utils';
import { pushAwaitedTask, shiftResolvedOp } from '../../runtime'; import { pushAwaitedTask, shiftResolvedOp } from '../../runtime';
import { isResolvedLockOp, isResolvedSlotOp, isResolvedUnlockOp, uuid } from '../../utils';
const unlock = (lockId: string, timestamp: number) => { const unlock = (lockId: string, timestamp: number) => {
console.log('lockOp'); console.log('unlockOp');
let opMarkerValue = false; let opMarkerValue = false;
const result = new Promise<void>((resolve, reject) => { const result = new Promise<void>((resolve, reject) => {

View File

@ -3,8 +3,8 @@ import { Claim, ClaimKey, constructRead, extractRead } from '@coinweb/contract-k
import { TypedClaim } from '../../../types'; import { TypedClaim } from '../../../types';
import { context } from '../../context'; import { context } from '../../context';
import { opMarker } from '../../globals/promise'; import { opMarker } from '../../globals/promise';
import { isResolvedReadOp, isResolvedSlotOp } from '../../utils';
import { pushAwaitedTask, shiftResolvedOp } from '../../runtime'; import { pushAwaitedTask, shiftResolvedOp } from '../../runtime';
import { isResolvedReadOp, isResolvedSlotOp } from '../../utils';
export const readOp = <TClaim extends Claim = TypedClaim>(key: ClaimKey) => { export const readOp = <TClaim extends Claim = TypedClaim>(key: ClaimKey) => {
let opMarkerValue = false; let opMarkerValue = false;
@ -22,10 +22,14 @@ export const readOp = <TClaim extends Claim = TypedClaim>(key: ClaimKey) => {
} }
if (!isResolvedReadOp(op)) { if (!isResolvedReadOp(op)) {
console.log(JSON.stringify(op));
throw new Error('Read operation not found'); throw new Error('Read operation not found');
} }
const claim = (extractRead(op)?.[0]?.content ?? null) as TClaim | null; const claim = (extractRead(op)?.[0]?.content ?? null) as TClaim | null;
console.log('ResolveRead claim: ', claim);
resolve(claim); resolve(claim);
} }
} catch (error) { } catch (error) {

View File

@ -2,6 +2,7 @@ import { constructContractRef, ContractIssuer } from '@coinweb/contract-kit';
import { constructMutexBlockUnlockClaimTake, constructMutexUnlockBlock } from '../claims'; import { constructMutexBlockUnlockClaimTake, constructMutexUnlockBlock } from '../claims';
import { unlockMethodName } from '../methods'; import { unlockMethodName } from '../methods';
import { unlockFee } from '../settings';
import { MutexUnlockArgs } from '../types'; import { MutexUnlockArgs } from '../types';
export const constructUnlockCall = (issuer: ContractIssuer, ...[lockId, timestamp, notify]: MutexUnlockArgs) => { export const constructUnlockCall = (issuer: ContractIssuer, ...[lockId, timestamp, notify]: MutexUnlockArgs) => {
@ -13,7 +14,7 @@ export const constructUnlockCall = (issuer: ContractIssuer, ...[lockId, timestam
methodArgs: [lockId, timestamp] satisfies MutexUnlockArgs, methodArgs: [lockId, timestamp] satisfies MutexUnlockArgs,
}, },
contractInfo: { contractInfo: {
providedCweb: 1000n, providedCweb: unlockFee,
authenticated: null, authenticated: null,
}, },
contractArgs: [], contractArgs: [],
@ -21,6 +22,6 @@ export const constructUnlockCall = (issuer: ContractIssuer, ...[lockId, timestam
ops: notify ops: notify
? ([constructMutexUnlockBlock(lockId, issuer), constructMutexBlockUnlockClaimTake(lockId)] as const) ? ([constructMutexUnlockBlock(lockId, issuer), constructMutexBlockUnlockClaimTake(lockId)] as const)
: [], : [],
fee: 1200n, fee: unlockFee + (notify ? 1000n : 800n),
}; };
}; };

View File

@ -1,7 +1,7 @@
import { constructContinueTx, constructContractRef, constructTake, Context, passCwebFrom } from '@coinweb/contract-kit'; import { constructContinueTx, constructContractRef, constructTake, Context, passCwebFrom } from '@coinweb/contract-kit';
import { getCallParameters, getContractIssuer, getMethodArguments } from 'lib/onchain'; import { getCallParameters, getContractIssuer, getMethodArguments } from 'lib/onchain';
import { constructMutexLockClaimKey } from '../claims'; import { constructMutexBlockUnlockClaimStore, constructMutexLockClaimKey } from '../claims';
import { lockFee } from '../settings'; import { lockFee } from '../settings';
import { MutexUnlockArgs } from '../types'; import { MutexUnlockArgs } from '../types';
@ -16,7 +16,11 @@ export const mutexUnlock = (context: Context) => {
return [ return [
constructContinueTx( constructContinueTx(
context, context,
[passCwebFrom(issuer, availableCweb), constructTake(constructMutexLockClaimKey(lockId, timestamp))], [
passCwebFrom(issuer, availableCweb),
constructTake(constructMutexLockClaimKey(lockId, timestamp)),
constructMutexBlockUnlockClaimStore(lockId),
],
[ [
{ {
callInfo: { callInfo: {

View File

@ -1,13 +1,8 @@
import { PreparedOp, Task } from '../../types'; import { PreparedOp, Task } from '../../types';
const awaitedTasks: Task[] = []; const awaitedTasks: Task[] = [];
let isFreezed = false;
export const pushAwaitedTask = (op: PreparedOp) => { export const pushAwaitedTask = (op: PreparedOp) => {
if (isFreezed) {
return;
}
awaitedTasks.push({ op, batchId: -1 }); awaitedTasks.push({ op, batchId: -1 });
}; };
@ -20,11 +15,3 @@ export const markTaskBatch = (count: number, batchId: number) => {
}; };
export const getAwaitedTasksCount = () => awaitedTasks.length; export const getAwaitedTasksCount = () => awaitedTasks.length;
export const freezeAwaitedTasks = () => {
isFreezed = true;
};
export const unfreezeAwaitedTasks = () => {
isFreezed = false;
};

View File

@ -1,3 +1,7 @@
import { ResolvedOp } from '../../types';
import { pushResolvedOp } from './resolvedOps';
let abortExecution: ((isFullyExecuted: boolean) => void) | null = null; let abortExecution: ((isFullyExecuted: boolean) => void) | null = null;
export const stopExecution = (isFullyExecuted = false) => { export const stopExecution = (isFullyExecuted = false) => {
@ -5,10 +9,18 @@ export const stopExecution = (isFullyExecuted = false) => {
abortExecution?.(isFullyExecuted); abortExecution?.(isFullyExecuted);
}; };
type ExecTask = () => Promise<void>; type ExecTask = () => Promise<unknown>;
type Exec = { task: ExecTask; ops: ResolvedOp[] };
let execQueue: ExecTask[] = []; const execQueue: Exec[] = [];
export const setNextExec = (task: ExecTask) => (execQueue = [task]); export const setNextExec = (task: ExecTask, ops: ResolvedOp[]) => {
return new Promise((resolve, reject) => {
execQueue.push({
task: () => task().then(resolve, reject),
ops,
});
});
};
export const execLoop = async (): Promise<boolean> => { export const execLoop = async (): Promise<boolean> => {
const nextExec = execQueue.pop(); const nextExec = execQueue.pop();
@ -17,7 +29,8 @@ export const execLoop = async (): Promise<boolean> => {
const execution = new Promise<boolean>((resolve, reject) => { const execution = new Promise<boolean>((resolve, reject) => {
abortExecution = resolve; abortExecution = resolve;
nextExec().then( pushResolvedOp(nextExec.ops);
nextExec.task().then(
() => { () => {
resolve(true); resolve(true);
}, },

View File

@ -36,16 +36,22 @@ export const shiftResolvedOp = () => {
usedOps.push(result.op); usedOps.push(result.op);
} }
console.log('shiftResolvedOp: ', JSON.stringify(result));
return result; return result;
}; };
export const getUsedOps = () => usedOps; export const getUsedOps = () => usedOps;
export const saveUsedOps = () => { export const startSavingUsedOps = () => {
usedOps = []; usedOps = [];
isSavingUsed = true; isSavingUsed = true;
}; };
export const stopSavingUsedOps = () => {
isSavingUsed = false;
};
export const freezeResolvedOps = () => { export const freezeResolvedOps = () => {
isFreezed = true; isFreezed = true;
}; };

View File

@ -37,7 +37,9 @@ export type ResolvedExecOp = {
}; };
export type ResolvedChildOp = { export type ResolvedChildOp = {
ChildOp: 0; ChildOp: {
ops: ResolvedOp[];
};
}; };
export type ResolvedLockOp = { export type ResolvedLockOp = {
@ -100,7 +102,7 @@ export type ExecutorMethodArgs = [
caller?: User, caller?: User,
thisId?: string, thisId?: string,
parentId?: string, parentId?: string,
saveResult?: boolean, shouldSaveResult?: boolean,
takenFundsIds?: string[], takenFundsIds?: string[],
execOpsIndexes?: number[], execOpsIndexes?: number[],
]; ];

View File

@ -1,4 +1,4 @@
VITE_API_URL='https://api-cloud.coinweb.io/wallet' VITE_API_URL='https://api-cloud.coinweb.io/wallet'
VITE_EXPLORER_URL='https://explorer.coinweb.io' VITE_EXPLORER_URL='https://explorer.coinweb.io'
VITE_CONTRACT_ADDRESS="0xedca91a29553fa2466e89eef02ac06dc49ab68be5e81fa91d05765cab79ec02a" VITE_CONTRACT_ADDRESS="0xc599810e4861b7b1ae25695d58eeef10556fc84f87f72fafa31c0921e86e92be"

View File

@ -379,16 +379,16 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@coinweb/testing-sdk@npm:0.0.9-remote": "@coinweb/testing-sdk@npm:0.0.10-mutex":
version: 0.0.9-remote version: 0.0.10-mutex
resolution: "@coinweb/testing-sdk@npm:0.0.9-remote" resolution: "@coinweb/testing-sdk@npm:0.0.10-mutex"
dependencies: dependencies:
"@coinweb/contract-kit": "npm:0.2.0" "@coinweb/contract-kit": "npm:0.2.0"
"@coinweb/minimal-sdk": "npm:1.2.19" "@coinweb/minimal-sdk": "npm:1.2.18"
json-stable-stringify: "npm:^1.1.1" json-stable-stringify: "npm:^1.1.1"
lodash.isequal: "npm:^4.5.0" lodash.isequal: "npm:^4.5.0"
secp256k1: "npm:^5.0.0" secp256k1: "npm:^5.0.0"
checksum: 10c0/80212780455d4bc2c1082d62ce382c6d07c6b5a107dc372f630c2257de686ec7e072c5d7a0b966e611b73f8b49ef9d6d8b68fbd66de1d968992a6b18444f218b checksum: 10c0/84785ecc631510aa39189792826cff454700cba96d13d6c9f93f4166ee035fb1c8a8c0f2eab0f51d7d5fe127c661c7964792f8dfacffb9bec5eaf3a6da7fc744
languageName: node languageName: node
linkType: hard linkType: hard