Compare commits

...

2 Commits

Author SHA1 Message Date
5dd0aef472 feat: extend mutex methods 2025-04-25 19:16:05 +03:00
0d5850ec25 feat: add base mutex methods 2025-04-25 15:41:29 +03:00
39 changed files with 768 additions and 34 deletions

View File

@ -1,25 +1,9 @@
import { SELF_REGISTER_HANDLER_NAME } from '@coinweb/contract-kit'; import { constructCwebMain } from 'cwait';
import { selfRegisterHandler } from '@coinweb/self-register';
import { addMethodHandler, ContractHandlers, executeHandler, executor } from 'cwait';
import { PUBLIC_METHODS } from '../offchain/shared'; import { PUBLIC_METHODS } from '../offchain/shared';
import { addWord } from './addWord'; import { addWord } from './addWord';
const createModule = (): ContractHandlers => { export const cwebMain = constructCwebMain({
const module: ContractHandlers = { handlers: {} }; [PUBLIC_METHODS.ADD_WORD]: addWord,
});
addMethodHandler(module, PUBLIC_METHODS.ADD_WORD, executor(addWord));
addMethodHandler(module, SELF_REGISTER_HANDLER_NAME, selfRegisterHandler);
return module;
};
export const cwebMain = () => {
return (async () => {
const module = createModule();
await executeHandler(module);
})();
};

View File

@ -0,0 +1,27 @@
import { ContractHandlers as ContractHandlersOrig, SELF_REGISTER_HANDLER_NAME } from '@coinweb/contract-kit';
import { selfRegisterHandler } from '@coinweb/self-register';
import { queue } from 'lib/onchain';
import { addMethodHandler, ContractHandlers, executeHandler } from '../contract-kit';
import { executor } from './executor';
import { mutexMethods } from './mutex';
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const constructCwebMain = (methods: Record<string, (...args: any[]) => Promise<void>>) => async () => {
const module: ContractHandlers = { handlers: {} };
Object.entries(methods).forEach(([name, handler]) => {
addMethodHandler(module, name, executor(handler));
});
Object.entries(mutexMethods).forEach(([name, handler]) => {
addMethodHandler(module, name, handler);
});
addMethodHandler(module, SELF_REGISTER_HANDLER_NAME, selfRegisterHandler);
queue.applyQueue(module as ContractHandlersOrig, []);
await executeHandler(module);
};

View File

@ -43,7 +43,7 @@ export const executor = (method: (...args: any[]) => Promise<void>) => {
const isFullyExecuted = await execution; const isFullyExecuted = await execution;
return constructTx(isFullyExecuted); return constructTx(isFullyExecuted);
} catch (error) { } catch (error) {
console.log('<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< executor-error'); console.log('<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< executor-error');
console.log((error as Error).message); console.log((error as Error).message);
throw error; throw error;
} }

View File

@ -0,0 +1,26 @@
import { ContractIssuer, constructContractRef } from '@coinweb/contract-kit';
import { constructMutexLockClaimRangeRead } from '../claims';
import { getAccessMethodName } from '../methods';
import { MutexGetAccessArgs } from '../types';
export const constructGetAccessCall = (
issuer: ContractIssuer,
...[claimKey, processId, uniqueId]: MutexGetAccessArgs
) => {
return {
callInfo: {
ref: constructContractRef(issuer, []),
methodInfo: {
methodName: getAccessMethodName,
methodArgs: [claimKey, processId, uniqueId] satisfies MutexGetAccessArgs,
},
contractInfo: {
providedCweb: 800n,
authenticated: null,
},
contractArgs: [constructMutexLockClaimRangeRead(issuer)],
},
fee: 900n,
};
};

View File

@ -0,0 +1,24 @@
import { ContractIssuer, constructContractRef } from '@coinweb/contract-kit';
import { constructMutexBlockAccessClaimTake, constructMutexLockBlock } from '../claims';
import { lockMethodName } from '../methods';
import { lockFee } from '../settings';
export const constructLockCall = (issuer: ContractIssuer, lockId: string) => {
return {
callInfo: {
ref: constructContractRef(issuer, []),
methodInfo: {
methodName: lockMethodName,
methodArgs: [],
},
contractInfo: {
providedCweb: lockFee,
authenticated: null,
},
contractArgs: [],
},
ops: [constructMutexLockBlock(lockId, issuer), constructMutexBlockAccessClaimTake(lockId)],
fee: lockFee + 200n,
};
};

View File

@ -0,0 +1,23 @@
import { constructContractRef, ContractIssuer } from '@coinweb/contract-kit';
import { constructMutexLockClaimRangeRead } from '../claims';
import { tryOpMethodName } from '../methods';
import { MutexTryOpArgs } from '../types';
export const constructTryOpCall = (issuer: ContractIssuer, ...[op, processId]: MutexTryOpArgs) => {
return {
callInfo: {
ref: constructContractRef(issuer, []),
methodInfo: {
methodName: tryOpMethodName,
methodArgs: [op, processId] satisfies MutexTryOpArgs,
},
contractInfo: {
providedCweb: 800n,
authenticated: null,
},
contractArgs: [constructMutexLockClaimRangeRead(issuer)],
},
fee: 900n,
};
};

View File

@ -0,0 +1,24 @@
import { constructContractRef, ContractIssuer } from '@coinweb/contract-kit';
import { constructMutexBlockUnlockClaimTake, constructMutexUnlockBlock } from '../claims';
import { unlockMethodName } from '../methods';
import { MutexUnlockArgs } from '../types';
export const constructUnlockCall = (issuer: ContractIssuer, ...[lockId, timestamp, notify]: MutexUnlockArgs) => {
return {
callInfo: {
ref: constructContractRef(issuer, []),
methodInfo: {
methodName: unlockMethodName,
methodArgs: [lockId, timestamp] satisfies MutexUnlockArgs,
},
contractInfo: {
providedCweb: 1000n,
authenticated: null,
},
contractArgs: [],
},
ops: notify ? [constructMutexUnlockBlock(lockId, issuer), constructMutexBlockUnlockClaimTake(lockId)] : [],
fee: 1200n,
};
};

View File

@ -0,0 +1,4 @@
export * from './constructGetAccessCall';
export * from './constructLockCall';
export * from './constructTryOpCall';
export * from './constructUnlockCall';

View File

@ -0,0 +1,4 @@
export * from './mutexBlockAccess';
export * from './mutexBlockLock';
export * from './mutexBlockUnlock';
export * from './mutexLock';

View File

@ -0,0 +1,41 @@
import {
BlockFilter,
constructBlock,
constructClaim,
constructClaimKey,
constructStore,
constructTake,
ContractIssuer,
CwebStore,
GStore,
} from '@coinweb/contract-kit';
import { MutexAccessResult, MutexAccessStatus } from '../types';
export const mutexBlockAccessKey = 'mutex_block_access';
export const constructMutexBlockAccessClaimKey = (uniqueId: string) =>
constructClaimKey([mutexBlockAccessKey], [uniqueId]);
export const constructMutexBlockAccessClaim = (uniqueId: string, status: MutexAccessStatus) =>
constructClaim(constructMutexBlockAccessClaimKey(uniqueId), { status } satisfies MutexAccessResult, '0x0');
export const constructMutexBlockAccessClaimStore = (uniqueId: string, status: MutexAccessStatus): GStore<CwebStore> =>
constructStore(constructMutexBlockAccessClaim(uniqueId, status));
export const constructMutexBlockAccessClaimTake = (uniqueId: string) =>
constructTake(constructMutexBlockAccessClaimKey(uniqueId));
export const constructMutexBlockAccessBlockFilter = (lockId: string, issuer: ContractIssuer): BlockFilter => {
const { first_part: first, second_part: second } = constructMutexBlockAccessClaimKey(lockId);
return {
issuer,
first,
second,
};
};
export const constructMutexAccessBlock = (lockId: string, issuer: ContractIssuer) => {
return constructBlock([constructMutexBlockAccessBlockFilter(lockId, issuer)]);
};

View File

@ -0,0 +1,36 @@
import {
BlockFilter,
constructBlock,
constructClaim,
constructClaimKey,
constructStore,
constructTake,
ContractIssuer,
} from '@coinweb/contract-kit';
export const mutexBlockLockKey = 'mutex_block_lock';
export const constructMutexBlockLockClaimKey = (lockId: string) => constructClaimKey([mutexBlockLockKey], [lockId]);
export const constructMutexBlockLockClaim = ({ lockId }: { lockId: string }) =>
constructClaim(constructMutexBlockLockClaimKey(lockId), {}, '0x0');
export const constructMutexBlockLockClaimStore = ({ lockId }: { lockId: string }) =>
constructStore(constructMutexBlockLockClaim({ lockId }));
export const constructMutexBlockLockClaimTake = (lockId: string) =>
constructTake(constructMutexBlockLockClaimKey(lockId));
export const constructMutexLockBlockFilter = (lockId: string, issuer: ContractIssuer): BlockFilter => {
const { first_part: first, second_part: second } = constructMutexBlockLockClaimKey(lockId);
return {
issuer,
first,
second,
};
};
export const constructMutexLockBlock = (lockId: string, issuer: ContractIssuer) => {
return constructBlock([constructMutexLockBlockFilter(lockId, issuer)]);
};

View File

@ -0,0 +1,39 @@
import {
BlockFilter,
constructBlock,
constructClaim,
constructClaimKey,
constructStore,
constructTake,
ContractIssuer,
CwebStore,
GStore,
} from '@coinweb/contract-kit';
export const mutexBlockUnlockKey = 'mutex_block_unlock';
export const constructMutexBlockUnlockClaimKey = (uniqueId: string) =>
constructClaimKey([mutexBlockUnlockKey], [uniqueId]);
export const constructMutexBlockUnlockClaim = (uniqueId: string) =>
constructClaim(constructMutexBlockUnlockClaimKey(uniqueId), {}, '0x0');
export const constructMutexBlockUnlockClaimStore = (uniqueId: string): GStore<CwebStore> =>
constructStore(constructMutexBlockUnlockClaim(uniqueId));
export const constructMutexBlockUnlockClaimTake = (uniqueId: string) =>
constructTake(constructMutexBlockUnlockClaimKey(uniqueId));
export const constructMutexUnlockBlockFilter = (lockId: string, issuer: ContractIssuer): BlockFilter => {
const { first_part: first, second_part: second } = constructMutexBlockUnlockClaimKey(lockId);
return {
issuer,
first,
second,
};
};
export const constructMutexUnlockBlock = (lockId: string, issuer: ContractIssuer) => {
return constructBlock([constructMutexUnlockBlockFilter(lockId, issuer)]);
};

View File

@ -0,0 +1,63 @@
import {
constructClaim,
constructClaimKey,
constructRangeRead,
constructStore,
ContractIssuer,
GRead,
} from '@coinweb/contract-kit';
import { CwebRead } from '@coinweb/contract-kit/dist/esm/operations/read';
import { toHex } from 'lib/shared';
import { rangeReadLimit } from '../settings';
import { LockedKey, MutexLockState } from '../types';
export const mutexLockKey = 'mutex_lock';
export const constructMutexLockFirstPart = () => [mutexLockKey];
export const constructMutexLockClaimKey = (lockId: string, timestamp: number) =>
constructClaimKey(constructMutexLockFirstPart(), [timestamp, lockId]);
export const constructMutexLockClaim = ({
fee,
keys,
locked,
lockId,
timestamp,
processId,
}: {
lockId: string;
timestamp: number;
locked: boolean;
keys: LockedKey[];
fee: bigint;
processId: string;
}) =>
constructClaim(
constructMutexLockClaimKey(lockId, timestamp),
{ locked, keys, processId } satisfies MutexLockState,
toHex(fee)
);
export const constructMutexLockClaimStore = ({
fee,
keys,
locked,
lockId,
timestamp,
processId,
}: {
fee: bigint;
keys: LockedKey[];
locked: boolean;
lockId: string;
timestamp: number;
processId: string;
}) => constructStore(constructMutexLockClaim({ fee, keys, locked, lockId, timestamp, processId }));
export const constructMutexLockClaimRangeRead = (issuer: ContractIssuer): GRead<CwebRead> =>
constructRangeRead(issuer, constructMutexLockFirstPart(), {}, rangeReadLimit);
// export const constructMutexLockClaimTake = (lockId: string, timestamp: number) =>
// constructTake(constructMutexLockClaimKey(lockId, timestamp));

View File

@ -0,0 +1,20 @@
import {
execLockMethodName,
getAccessMethodName,
lockMethodName,
mutexExecLock,
mutexGetAccess,
mutexLock,
mutexUnlock,
unlockMethodName,
} from './methods';
export * from './claims';
export * from './types';
export const mutexMethods = {
[execLockMethodName]: mutexExecLock,
[getAccessMethodName]: mutexGetAccess,
[lockMethodName]: mutexLock,
[unlockMethodName]: mutexUnlock,
};

View File

@ -0,0 +1,89 @@
import {
constructClaim,
constructContinueTx,
constructContractRef,
constructStore,
constructTake,
Context,
extractContractArgs,
extractRead,
passCwebFrom,
} from '@coinweb/contract-kit';
import { getCallParameters, getContractIssuer } from 'lib/onchain';
import { toHex, TypedClaim } from 'lib/shared';
import { lockFee } from '../settings';
import { MutexLockState } from '../types';
import { isMatchLockKeys } from '../utils';
import { lockMethodName, notifyLockMethodName } from './names';
export const mutexExecLock = (context: Context) => {
const { availableCweb } = getCallParameters(context);
const issuer = getContractIssuer(context);
const lockQueue = extractRead(extractContractArgs(context.tx)[0])?.map(
({ content }) => content as TypedClaim<MutexLockState>
);
if (!lockQueue) {
throw new Error('No lock queue found');
}
const alreadyLockedKeys = lockQueue.filter(({ body }) => body.locked).map(({ key }) => key);
const lockCandidate = lockQueue.find(
({ body, key }) => !body.locked && !alreadyLockedKeys.some((lockedKey) => isMatchLockKeys(lockedKey, key))
);
if (!lockCandidate) {
return [];
}
return [
constructContinueTx(
context,
[
passCwebFrom(issuer, availableCweb),
constructTake(lockCandidate.key),
constructStore(
constructClaim(
lockCandidate.key,
{ ...lockCandidate.body, locked: true },
toHex(BigInt(lockCandidate.fees_stored) - lockFee)
)
),
],
[
{
callInfo: {
ref: constructContractRef(issuer, []),
methodInfo: {
methodName: lockMethodName,
methodArgs: [],
},
contractInfo: {
providedCweb: lockFee,
authenticated: null,
},
contractArgs: [],
},
},
{
callInfo: {
ref: constructContractRef(issuer, []),
methodInfo: {
methodName: notifyLockMethodName,
methodArgs: [(lockCandidate.key.second_part as [number, string])[1]],
},
contractInfo: {
providedCweb: 200n,
authenticated: null,
},
contractArgs: [],
},
},
]
),
];
};

View File

@ -0,0 +1,32 @@
import { constructContinueTx, Context, extractContractArgs, extractRead } from '@coinweb/contract-kit';
import { getContractArguments } from 'lib/onchain';
import { TypedClaim } from 'lib/shared';
import { constructMutexBlockAccessClaimStore } from '../claims';
import { MutexAccessStatus, MutexGetAccessArgs, MutexLockState } from '../types';
import { isMatchLockKeys } from '../utils';
export const mutexGetAccess = (context: Context) => {
const [claimKey, processId, uniqueId] = getContractArguments<MutexGetAccessArgs>(context);
const lockQueue = extractRead(extractContractArgs(context.tx)[0])?.map(
({ content }) => content as TypedClaim<MutexLockState>
);
if (!lockQueue) {
throw new Error('No lock queue found');
}
const isLockedByOtherProcess = lockQueue.some(
({ body }) => body.locked && body.processId !== processId && body.keys.some((key) => isMatchLockKeys(key, claimKey))
);
return [
constructContinueTx(context, [
constructMutexBlockAccessClaimStore(
uniqueId,
isLockedByOtherProcess ? MutexAccessStatus.DENIED : MutexAccessStatus.GRANTED
),
]),
];
};

View File

@ -0,0 +1,7 @@
export * from './execLock';
export * from './getAccess';
export * from './lock';
export * from './names';
export * from './notifyLock';
export * from './tryOp';
export * from './unlock';

View File

@ -0,0 +1,34 @@
import { constructContinueTx, constructContractRef, Context } from '@coinweb/contract-kit';
import { getCallParameters, getContractIssuer } from 'lib/onchain';
import { constructMutexLockClaimRangeRead } from '../claims';
import { execLockMethodName } from './names';
export const mutexLock = (context: Context) => {
const { availableCweb } = getCallParameters(context);
const issuer = getContractIssuer(context);
return [
constructContinueTx(
context,
[],
[
{
callInfo: {
ref: constructContractRef(issuer, []),
methodInfo: {
methodName: execLockMethodName,
methodArgs: [],
},
contractInfo: {
providedCweb: availableCweb - 700n,
authenticated: null,
},
contractArgs: [constructMutexLockClaimRangeRead(issuer)],
},
},
]
),
];
};

View File

@ -0,0 +1,6 @@
export const lockMethodName = '_mutex_lock';
export const execLockMethodName = '_mutex_execLock';
export const getAccessMethodName = '_mutex_get_access';
export const unlockMethodName = '_mutex_unlock';
export const tryOpMethodName = '_mutex_try_op';
export const notifyLockMethodName = '_mutex_notify_lock';

View File

@ -0,0 +1,11 @@
import { constructContinueTx, Context } from '@coinweb/contract-kit';
import { getContractArguments } from 'lib/onchain';
import { constructMutexBlockLockClaimStore } from '../claims/mutexBlockLock';
import { MutexNotifyLockArgs } from '../types';
export const notifyLock = (context: Context) => {
const [lockId] = getContractArguments<MutexNotifyLockArgs>(context);
return [constructContinueTx(context, [constructMutexBlockLockClaimStore({ lockId })])];
};

View File

@ -0,0 +1,30 @@
import { constructContinueTx, Context, extractContractArgs, extractRead } from '@coinweb/contract-kit';
import { getContractArguments } from 'lib/onchain';
import { TypedClaim } from 'lib/shared';
import { MutexLockState, MutexTryOpArgs } from '../types';
import { isMatchLockKeys } from '../utils';
export const tryOp = (context: Context) => {
const [op, processId] = getContractArguments<MutexTryOpArgs>(context);
const claimKey = 'StoreOp' in op ? op.StoreOp.key : op.TakeOp.key;
const lockQueue = extractRead(extractContractArgs(context.tx)[0])?.map(
({ content }) => content as TypedClaim<MutexLockState>
);
if (!lockQueue) {
throw new Error('No lock queue found');
}
const isLockedByOtherProcess = lockQueue.some(
({ body }) => body.locked && body.processId !== processId && body.keys.some((key) => isMatchLockKeys(key, claimKey))
);
if (isLockedByOtherProcess) {
return [];
}
return [constructContinueTx(context, [op])];
};

View File

@ -0,0 +1,38 @@
import { constructContinueTx, constructContractRef, constructTake, Context, passCwebFrom } from '@coinweb/contract-kit';
import { getCallParameters, getContractArguments, getContractIssuer } from 'lib/onchain';
import { constructMutexLockClaimKey } from '../claims';
import { lockFee } from '../settings';
import { MutexUnlockArgs } from '../types';
import { lockMethodName } from './names';
export const mutexUnlock = (context: Context) => {
const { availableCweb } = getCallParameters(context);
const issuer = getContractIssuer(context);
const [lockId, timestamp] = getContractArguments<MutexUnlockArgs>(context);
return [
constructContinueTx(
context,
[passCwebFrom(issuer, availableCweb), constructTake(constructMutexLockClaimKey(lockId, timestamp))],
[
{
callInfo: {
ref: constructContractRef(issuer, []),
methodInfo: {
methodName: lockMethodName,
methodArgs: [],
},
contractInfo: {
providedCweb: lockFee,
authenticated: null,
},
contractArgs: [],
},
},
]
),
];
};

View File

@ -0,0 +1,4 @@
export const lockFee = 5000n;
export const unlockFee = 1000n + lockFee;
export const rangeReadLimit = 10000;

View File

@ -0,0 +1,26 @@
import { Claim, ClaimKey, GStore, GTake } from '@coinweb/contract-kit';
export type LockedKey = Omit<ClaimKey, 'second_part'> & Partial<Pick<ClaimKey, 'second_part'>>;
export type MutexLockState = {
locked: boolean;
keys: LockedKey[];
processId: string;
};
export enum MutexAccessStatus {
GRANTED = 'granted',
DENIED = 'denied',
}
export type MutexAccessResult = {
status: MutexAccessStatus;
};
export type MutexGetAccessArgs = [claimKey: ClaimKey, processId: string, uniqueId: string];
export type MutexUnlockArgs = [lockId: string, timestamp: number, notify?: boolean];
export type MutexTryOpArgs = [op: GTake<Claim> | GStore<Claim>, processId: string];
export type MutexNotifyLockArgs = [lockId: string];

View File

@ -0,0 +1 @@
export * from './isMatchLockKeys';

View File

@ -0,0 +1,9 @@
import { LockedKey } from '../types';
export const isMatchLockKeys = (lockKey1: LockedKey, lockKey2: LockedKey) => {
if (!lockKey1.second_part || !lockKey2.second_part) {
return lockKey1.first_part === lockKey2.first_part;
}
return lockKey1.first_part === lockKey2.first_part && lockKey1.second_part === lockKey2.second_part;
};

View File

@ -27,7 +27,7 @@ export const blockOp = (filters: BlockFilter[]) => {
throw new Error('Block operation not found'); throw new Error('Block operation not found');
} }
const result = op && extractBlock(op); const result = extractBlock(op);
resolve(result); resolve(result);
} }

View File

@ -0,0 +1,42 @@
import { ResolvedLockOp } from '../../../types';
import { opMarker } from '../../global';
import { LockedKey } from '../../mutex';
import { isResolvedLockOp, isResolvedSlotOp, uuid } from '../../utils';
import { pushAwaitedTask } from '../awaited';
import { shiftResolvedOp } from '../resolved';
export const lockOp = (keys: LockedKey[] | LockedKey) => {
console.log('lockOp');
let opMarkerValue = false;
const result = new Promise<ResolvedLockOp['LockOp']>((resolve, reject) => {
try {
const { op, isOp } = shiftResolvedOp();
if (!isOp) {
pushAwaitedTask({ LockOp: { lockId: uuid(), keys: Array.isArray(keys) ? keys : [keys] } });
opMarkerValue = true;
} else {
if (isResolvedSlotOp(op)) {
console.log('storeOp-slotOp');
return;
}
if (!isResolvedLockOp(op)) {
console.log('lockOp-error');
throw new Error('Lock operation not found');
}
const result = op.LockOp;
resolve(result);
}
} catch (error) {
reject(error);
}
});
(result as Promise<ResolvedLockOp['LockOp']> & { [opMarker]: boolean })[opMarker] = opMarkerValue;
return result;
};

View File

@ -34,7 +34,7 @@ export const rangeReadOp = <TClaims extends Claim[] = TypedClaim[]>(
throw new Error('Read operation not found'); throw new Error('Read operation not found');
} }
const claim = op && ((extractRead(op)?.map((result) => result.content) ?? null) as TClaims | null); const claim = (extractRead(op)?.map((result) => result.content) ?? null) as TClaims | null;
resolve(claim); resolve(claim);
} }
} catch (error) { } catch (error) {

View File

@ -29,7 +29,7 @@ export const readOp = <TClaim extends Claim = TypedClaim>(key: ClaimKey) => {
throw new Error('Read operation not found'); throw new Error('Read operation not found');
} }
const claim = op && ((extractRead(op)?.[0]?.content ?? null) as TClaim | null); const claim = (extractRead(op)?.[0]?.content ?? null) as TClaim | null;
resolve(claim); resolve(claim);
} }
} catch (error) { } catch (error) {

View File

@ -28,7 +28,7 @@ export const storeOp = (claim: Claim) => {
throw new Error('Store operation not found'); throw new Error('Store operation not found');
} }
const result = op && extractStore(op); const result = extractStore(op);
resolve(result); resolve(result);
} }

View File

@ -28,7 +28,7 @@ export const takeOp = <TClaim extends Claim = TypedClaim>(key: ClaimKey) => {
throw new Error('Take operation not found'); throw new Error('Take operation not found');
} }
const claim = op && extractTake(op); const claim = extractTake(op);
resolve(claim as TClaim | null); resolve(claim as TClaim | null);
} }
} catch (error) { } catch (error) {

View File

@ -0,0 +1,38 @@
import { opMarker } from '../../global';
import { isResolvedSlotOp, isResolvedUnlockOp } from '../../utils';
import { pushAwaitedTask } from '../awaited';
import { shiftResolvedOp } from '../resolved';
export const unlock = (lockId: string) => {
console.log('lockOp');
let opMarkerValue = false;
const result = new Promise<void>((resolve, reject) => {
try {
const { op, isOp } = shiftResolvedOp();
if (!isOp) {
pushAwaitedTask({ UnlockOp: { lockId } });
opMarkerValue = true;
} else {
if (isResolvedSlotOp(op)) {
console.log('unlockOp-slotOp');
return;
}
if (!isResolvedUnlockOp(op)) {
console.log('unlockOp-error');
throw new Error('Unlock operation not found');
}
resolve();
}
} catch (error) {
reject(error);
}
});
(result as Promise<void> & { [opMarker]: boolean })[opMarker] = opMarkerValue;
return result;
};

View File

@ -1,2 +0,0 @@
export * from './jumpTx';
export * from './tx';

View File

@ -1 +0,0 @@
export const jumpTx = () => null;

View File

@ -1 +0,0 @@
export const tx = () => null;

View File

@ -13,7 +13,16 @@ import { CwebCallRefResolved, isResolvedCall } from '@coinweb/contract-kit/dist/
import { ResolvedBlock } from '@coinweb/contract-kit/dist/types/operations/block'; import { ResolvedBlock } from '@coinweb/contract-kit/dist/types/operations/block';
import { GBlock, GCall, GRead, GStore, GTake } from '@coinweb/contract-kit/dist/types/operations/generics'; import { GBlock, GCall, GRead, GStore, GTake } from '@coinweb/contract-kit/dist/types/operations/generics';
import { PreparedExecOp, PreparedOp, ResolvedChildOp, ResolvedExecOp, ResolvedOp, ResolvedSlotOp } from '../../types'; import {
PreparedExecOp,
PreparedOp,
ResolvedChildOp,
ResolvedExecOp,
ResolvedLockOp,
ResolvedOp,
ResolvedSlotOp,
ResolvedUnlockOp,
} from '../../types';
export const isResolvedSlotOp = (op?: ResolvedOp | null): op is ResolvedSlotOp => { export const isResolvedSlotOp = (op?: ResolvedOp | null): op is ResolvedSlotOp => {
if (op && 'SlotOp' in op) { if (op && 'SlotOp' in op) {
@ -39,6 +48,22 @@ export const isResolvedChildOp = (op?: ResolvedOp | null): op is ResolvedChildOp
return !!(op && 'ChildOp' in op); return !!(op && 'ChildOp' in op);
}; };
export const isResolvedLockOp = (op?: ResolvedOp | null): op is ResolvedLockOp => {
if (op && 'LockOp' in op) {
console.log('isResolvedLockOp >>> ', JSON.stringify(op));
}
return !!(op && 'LockOp' in op);
};
export const isResolvedUnlockOp = (op?: ResolvedOp | null): op is ResolvedUnlockOp => {
if (op && 'UnlockOp' in op) {
console.log('isResolvedUnlockOp >>> ', JSON.stringify(op));
}
return !!(op && 'UnlockOp' in op);
};
export const isResolvedBlockOp = (op?: ResolvedOp | null): op is GBlock<ResolvedBlock> => { export const isResolvedBlockOp = (op?: ResolvedOp | null): op is GBlock<ResolvedBlock> => {
if (op && 'BlockOp' in op) { if (op && 'BlockOp' in op) {
console.log('isResolvedBlockOp >>> ', JSON.stringify(op)); console.log('isResolvedBlockOp >>> ', JSON.stringify(op));

View File

@ -1,5 +1,7 @@
import { Claim, ClaimKey, OrdJson, PreparedOperation, ResolvedOperation, User } from '@coinweb/contract-kit'; import { Claim, ClaimKey, OrdJson, PreparedOperation, ResolvedOperation, User } from '@coinweb/contract-kit';
import { LockedKey } from './onchain/mutex';
export type HexBigInt = `0x${string}`; export type HexBigInt = `0x${string}`;
export type TypedClaim<TBody extends OrdJson = OrdJson, TKey extends ClaimKey = ClaimKey> = Claim & { export type TypedClaim<TBody extends OrdJson = OrdJson, TKey extends ClaimKey = ClaimKey> = Claim & {
@ -22,7 +24,23 @@ export type ResolvedChildOp = {
ChildOp: 0; ChildOp: 0;
}; };
export type ResolvedOp = ResolvedOperation | ResolvedSlotOp | ResolvedExecOp | ResolvedChildOp; export type ResolvedLockOp = {
LockOp: {
lockId: string;
};
};
export type ResolvedUnlockOp = {
UnlockOp: 0;
};
export type ResolvedOp =
| ResolvedOperation
| ResolvedSlotOp
| ResolvedExecOp
| ResolvedChildOp
| ResolvedLockOp
| ResolvedUnlockOp;
export type PreparedExecOp = { export type PreparedExecOp = {
ExecOp: { ExecOp: {
@ -30,7 +48,20 @@ export type PreparedExecOp = {
}; };
}; };
export type PreparedOp = PreparedOperation | PreparedExecOp; export type PreparedLockOp = {
LockOp: {
lockId: string;
keys: LockedKey[];
};
};
export type PreparedUnlockOp = {
UnlockOp: {
lockId: string;
};
};
export type PreparedOp = PreparedOperation | PreparedExecOp | PreparedLockOp | PreparedUnlockOp;
export type Task = { export type Task = {
op: PreparedOp; op: PreparedOp;

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="0x8c89cb42634cfe892290845d907af8ec2d482cead42afaef3ccc3cea4446fce5" VITE_CONTRACT_ADDRESS="0xbf9ca1687e3441bed1afd495405778a9c06c2f5173829d47d9b87dfb150063d8"