feat: add base mutex methods
This commit is contained in:
parent
cb4a2fb33d
commit
0d5850ec25
@ -5,12 +5,18 @@ import { addMethodHandler, ContractHandlers, executeHandler, executor } from 'cw
|
||||
import { PUBLIC_METHODS } from '../offchain/shared';
|
||||
|
||||
import { addWord } from './addWord';
|
||||
import { mutexLock } from './mutex/lock';
|
||||
import { mutexUnlock, mutexUnlockExec } from './mutex/unlock';
|
||||
|
||||
const createModule = (): ContractHandlers => {
|
||||
const module: ContractHandlers = { handlers: {} };
|
||||
|
||||
addMethodHandler(module, PUBLIC_METHODS.ADD_WORD, executor(addWord));
|
||||
|
||||
addMethodHandler(module, '_lock', mutexLock);
|
||||
addMethodHandler(module, '_unlock', mutexUnlock);
|
||||
addMethodHandler(module, '_unlock_exec', mutexUnlockExec);
|
||||
|
||||
addMethodHandler(module, SELF_REGISTER_HANDLER_NAME, selfRegisterHandler);
|
||||
|
||||
return module;
|
||||
|
||||
1
packages/contract.cm/src/onchain/mutex/access.ts
Normal file
1
packages/contract.cm/src/onchain/mutex/access.ts
Normal file
@ -0,0 +1 @@
|
||||
export const mutexAccess = () => {};
|
||||
43
packages/contract.cm/src/onchain/mutex/lock.ts
Normal file
43
packages/contract.cm/src/onchain/mutex/lock.ts
Normal file
@ -0,0 +1,43 @@
|
||||
import {
|
||||
constructClaim,
|
||||
constructClaimKey,
|
||||
constructContinueTx,
|
||||
constructContractIssuer,
|
||||
constructContractRef,
|
||||
constructStore,
|
||||
Context,
|
||||
extractContractInfo,
|
||||
getContractId,
|
||||
} from '@coinweb/contract-kit';
|
||||
|
||||
export const mutexLock = (context: Context) => {
|
||||
const { providedCweb } = extractContractInfo(context.tx);
|
||||
const issuer = constructContractIssuer(getContractId(context.tx));
|
||||
|
||||
return [
|
||||
constructContinueTx(
|
||||
context,
|
||||
[
|
||||
constructStore(
|
||||
constructClaim(constructClaimKey('mutex', context.call.txid.slice(0, 8)), { word: 'CLASH' }, '0x0')
|
||||
),
|
||||
],
|
||||
[
|
||||
{
|
||||
callInfo: {
|
||||
ref: constructContractRef(issuer, []),
|
||||
methodInfo: {
|
||||
methodName: '_unlock',
|
||||
methodArgs: [],
|
||||
},
|
||||
contractInfo: {
|
||||
providedCweb: providedCweb ? providedCweb - 1000n : 1000n,
|
||||
authenticated: null,
|
||||
},
|
||||
contractArgs: [],
|
||||
},
|
||||
},
|
||||
]
|
||||
),
|
||||
];
|
||||
};
|
||||
59
packages/contract.cm/src/onchain/mutex/unlock.ts
Normal file
59
packages/contract.cm/src/onchain/mutex/unlock.ts
Normal file
@ -0,0 +1,59 @@
|
||||
import {
|
||||
constructContinueTx,
|
||||
constructContractIssuer,
|
||||
constructContractRef,
|
||||
constructRangeRead,
|
||||
constructTake,
|
||||
Context,
|
||||
extractContractArgs,
|
||||
extractContractInfo,
|
||||
extractRead,
|
||||
getContractId,
|
||||
} from '@coinweb/contract-kit';
|
||||
|
||||
export const mutexUnlock = (context: Context) => {
|
||||
const { providedCweb } = extractContractInfo(context.tx);
|
||||
const issuer = constructContractIssuer(getContractId(context.tx));
|
||||
return [
|
||||
constructContinueTx(
|
||||
context,
|
||||
[],
|
||||
[
|
||||
{
|
||||
callInfo: {
|
||||
ref: constructContractRef(issuer, []),
|
||||
methodInfo: {
|
||||
methodName: '_unlock_exec',
|
||||
methodArgs: [],
|
||||
},
|
||||
contractInfo: {
|
||||
providedCweb: (providedCweb ? providedCweb - 1000n : 1000n) / 2n,
|
||||
authenticated: null,
|
||||
},
|
||||
contractArgs: [constructRangeRead(issuer, 'mutex', {}, 10000)],
|
||||
},
|
||||
},
|
||||
{
|
||||
callInfo: {
|
||||
ref: constructContractRef(issuer, []),
|
||||
methodInfo: {
|
||||
methodName: '_unlock',
|
||||
methodArgs: [],
|
||||
},
|
||||
contractInfo: {
|
||||
providedCweb: (providedCweb ? providedCweb - 1000n : 1000n) / 2n,
|
||||
authenticated: null,
|
||||
},
|
||||
contractArgs: [],
|
||||
},
|
||||
},
|
||||
]
|
||||
),
|
||||
];
|
||||
};
|
||||
|
||||
export const mutexUnlockExec = (context: Context) => {
|
||||
const claim = extractContractArgs(context.tx)[0]!;
|
||||
const take = extractRead(claim)![0];
|
||||
return [constructContinueTx(context, [constructTake(take.content.key)])];
|
||||
};
|
||||
26
packages/cwait/src/onchain/mutex/claims/access.ts
Normal file
26
packages/cwait/src/onchain/mutex/claims/access.ts
Normal file
@ -0,0 +1,26 @@
|
||||
import {
|
||||
constructClaim,
|
||||
constructClaimKey,
|
||||
constructStore,
|
||||
constructTake,
|
||||
CwebStore,
|
||||
GStore,
|
||||
} from '@coinweb/contract-kit';
|
||||
|
||||
import { MutexAccessResult, MutexAccessStatus } from '../types';
|
||||
|
||||
export const mutexAccessKey = 'mutex_access';
|
||||
|
||||
export const constructMutexAccessFirstPart = () => [mutexAccessKey];
|
||||
|
||||
export const constructMutexAccessClaimKey = (uniqueId: string) =>
|
||||
constructClaimKey(constructMutexAccessFirstPart(), [uniqueId]);
|
||||
|
||||
export const constructMutexAccessClaim = (uniqueId: string, status: MutexAccessStatus) =>
|
||||
constructClaim(constructMutexAccessClaimKey(uniqueId), { status } satisfies MutexAccessResult, '0x0');
|
||||
|
||||
export const constructMutexAccessClaimStore = (uniqueId: string, status: MutexAccessStatus): GStore<CwebStore> =>
|
||||
constructStore(constructMutexAccessClaim(uniqueId, status));
|
||||
|
||||
export const constructMutexAccessClaimTake = (uniqueId: string) =>
|
||||
constructTake(constructMutexAccessClaimKey(uniqueId));
|
||||
2
packages/cwait/src/onchain/mutex/claims/index.ts
Normal file
2
packages/cwait/src/onchain/mutex/claims/index.ts
Normal file
@ -0,0 +1,2 @@
|
||||
export * from './access';
|
||||
export * from './lock';
|
||||
70
packages/cwait/src/onchain/mutex/claims/lock.ts
Normal file
70
packages/cwait/src/onchain/mutex/claims/lock.ts
Normal file
@ -0,0 +1,70 @@
|
||||
import {
|
||||
constructClaim,
|
||||
constructClaimKey,
|
||||
constructContractIssuer,
|
||||
constructRangeRead,
|
||||
constructStore,
|
||||
Context,
|
||||
getContractId,
|
||||
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 = (context: Context): GRead<CwebRead> =>
|
||||
constructRangeRead(
|
||||
constructContractIssuer(getContractId(context.tx)),
|
||||
constructMutexLockFirstPart(),
|
||||
{},
|
||||
rangeReadLimit
|
||||
);
|
||||
|
||||
// export const constructMutexLockClaimTake = (lockId: string, timestamp: number) =>
|
||||
// constructTake(constructMutexLockClaimKey(lockId, timestamp));
|
||||
20
packages/cwait/src/onchain/mutex/index.ts
Normal file
20
packages/cwait/src/onchain/mutex/index.ts
Normal 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,
|
||||
};
|
||||
75
packages/cwait/src/onchain/mutex/methods/execLock.ts
Normal file
75
packages/cwait/src/onchain/mutex/methods/execLock.ts
Normal file
@ -0,0 +1,75 @@
|
||||
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 } 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: [],
|
||||
},
|
||||
},
|
||||
]
|
||||
),
|
||||
];
|
||||
};
|
||||
32
packages/cwait/src/onchain/mutex/methods/getAccess.ts
Normal file
32
packages/cwait/src/onchain/mutex/methods/getAccess.ts
Normal 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 { constructMutexAccessClaimStore } 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, [
|
||||
constructMutexAccessClaimStore(
|
||||
uniqueId,
|
||||
isLockedByOtherProcess ? MutexAccessStatus.DENIED : MutexAccessStatus.GRANTED
|
||||
),
|
||||
]),
|
||||
];
|
||||
};
|
||||
5
packages/cwait/src/onchain/mutex/methods/index.ts
Normal file
5
packages/cwait/src/onchain/mutex/methods/index.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export * from './execLock';
|
||||
export * from './getAccess';
|
||||
export * from './lock';
|
||||
export * from './names';
|
||||
export * from './unlock';
|
||||
34
packages/cwait/src/onchain/mutex/methods/lock.ts
Normal file
34
packages/cwait/src/onchain/mutex/methods/lock.ts
Normal 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(context)],
|
||||
},
|
||||
},
|
||||
]
|
||||
),
|
||||
];
|
||||
};
|
||||
4
packages/cwait/src/onchain/mutex/methods/names.ts
Normal file
4
packages/cwait/src/onchain/mutex/methods/names.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export const lockMethodName = '_mutex_lock';
|
||||
export const execLockMethodName = '_mutex_execLock';
|
||||
export const getAccessMethodName = '_mutex_get_access';
|
||||
export const unlockMethodName = '_mutex_unlock';
|
||||
38
packages/cwait/src/onchain/mutex/methods/unlock.ts
Normal file
38
packages/cwait/src/onchain/mutex/methods/unlock.ts
Normal 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: [],
|
||||
},
|
||||
},
|
||||
]
|
||||
),
|
||||
];
|
||||
};
|
||||
4
packages/cwait/src/onchain/mutex/settings.ts
Normal file
4
packages/cwait/src/onchain/mutex/settings.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export const lockFee = 5000n;
|
||||
export const unlockFee = 1000n + lockFee;
|
||||
|
||||
export const rangeReadLimit = 10000;
|
||||
22
packages/cwait/src/onchain/mutex/types.ts
Normal file
22
packages/cwait/src/onchain/mutex/types.ts
Normal file
@ -0,0 +1,22 @@
|
||||
import { ClaimKey } 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];
|
||||
1
packages/cwait/src/onchain/mutex/utils/index.ts
Normal file
1
packages/cwait/src/onchain/mutex/utils/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './isMatchLockKeys';
|
||||
@ -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;
|
||||
};
|
||||
@ -1,4 +1,4 @@
|
||||
VITE_API_URL='https://api-cloud.coinweb.io/wallet'
|
||||
VITE_EXPLORER_URL='https://explorer.coinweb.io'
|
||||
|
||||
VITE_CONTRACT_ADDRESS="0x8c89cb42634cfe892290845d907af8ec2d482cead42afaef3ccc3cea4446fce5"
|
||||
VITE_CONTRACT_ADDRESS="0xbf9ca1687e3441bed1afd495405778a9c06c2f5173829d47d9b87dfb150063d8"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user