diff --git a/src/conditions/base/contract.ts b/src/conditions/base/contract.ts index 44f13c9d4..3160612b7 100644 --- a/src/conditions/base/contract.ts +++ b/src/conditions/base/contract.ts @@ -67,9 +67,13 @@ const functionAbiSchema = z export type FunctionAbiProps = z.infer; +export const ContractConditionType = 'contract'; + export const contractConditionSchema = rpcConditionSchema .extend({ - conditionType: z.literal('contract').default('contract'), + conditionType: z + .literal(ContractConditionType) + .default(ContractConditionType), contractAddress: z.string().regex(ETH_ADDRESS_REGEXP), standardContractType: z.enum(['ERC20', 'ERC721']).optional(), method: z.string(), diff --git a/src/conditions/base/index.ts b/src/conditions/base/index.ts index b17914f34..512dbd641 100644 --- a/src/conditions/base/index.ts +++ b/src/conditions/base/index.ts @@ -35,6 +35,10 @@ export class TimeCondition extends Condition { } } -export { type ContractConditionProps } from './contract'; -export { type RpcConditionProps } from './rpc'; -export { type TimeConditionProps } from './time'; +export { type ContractConditionProps, ContractConditionType } from './contract'; +export { type RpcConditionProps, RpcConditionType } from './rpc'; +export { + type TimeConditionProps, + TimeConditionType, + TimeConditionMethod, +} from './time'; diff --git a/src/conditions/base/rpc.ts b/src/conditions/base/rpc.ts index ea232d50f..da06cbeb0 100644 --- a/src/conditions/base/rpc.ts +++ b/src/conditions/base/rpc.ts @@ -5,8 +5,10 @@ import createUnionSchema from '../zod'; import { EthAddressOrUserAddressSchema, returnValueTestSchema } from './shared'; +export const RpcConditionType = 'rpc'; + export const rpcConditionSchema = z.object({ - conditionType: z.literal('rpc').default('rpc'), + conditionType: z.literal(RpcConditionType).default(RpcConditionType), chain: createUnionSchema(SUPPORTED_CHAIN_IDS), method: z.enum(['eth_getBalance', 'balanceOf']), parameters: z.array(EthAddressOrUserAddressSchema), diff --git a/src/conditions/base/time.ts b/src/conditions/base/time.ts index 0196b728c..7e43ae9a1 100644 --- a/src/conditions/base/time.ts +++ b/src/conditions/base/time.ts @@ -6,10 +6,13 @@ import { rpcConditionSchema } from './rpc'; // eslint-disable-next-line @typescript-eslint/no-unused-vars const { parameters: _, ...restShape } = rpcConditionSchema.shape; +export const TimeConditionType = 'time'; +export const TimeConditionMethod = 'blocktime'; + export const timeConditionSchema = z.object({ ...restShape, - conditionType: z.literal('time').default('time'), - method: z.literal('blocktime').default('blocktime'), + conditionType: z.literal(TimeConditionType).default(TimeConditionType), + method: z.literal(TimeConditionMethod).default(TimeConditionMethod), }); export type TimeConditionProps = z.infer; diff --git a/src/conditions/compound-condition.ts b/src/conditions/compound-condition.ts index 2c88e0bfe..531827ba4 100644 --- a/src/conditions/compound-condition.ts +++ b/src/conditions/compound-condition.ts @@ -4,8 +4,12 @@ import { contractConditionSchema } from './base/contract'; import { rpcConditionSchema } from './base/rpc'; import { timeConditionSchema } from './base/time'; +export const CompoundConditionType = 'compound'; + export const compoundConditionSchema: z.ZodSchema = z.object({ - conditionType: z.literal('compound').default('compound'), + conditionType: z + .literal(CompoundConditionType) + .default(CompoundConditionType), operator: z.enum(['and', 'or']), operands: z .array( diff --git a/src/conditions/condition.ts b/src/conditions/condition.ts index b16444ef1..b69eb0c36 100644 --- a/src/conditions/condition.ts +++ b/src/conditions/condition.ts @@ -6,17 +6,40 @@ import { CompoundCondition, ContractCondition, ContractConditionProps, + ContractConditionType, RpcCondition, RpcConditionProps, + RpcConditionType, TimeCondition, TimeConditionProps, + TimeConditionType, } from './base'; -import { CompoundConditionProps } from './compound-condition'; +import { + CompoundConditionProps, + CompoundConditionType, +} from './compound-condition'; import { USER_ADDRESS_PARAM } from './const'; type ConditionSchema = z.ZodSchema; export type ConditionProps = z.infer; +class ConditionFactory { + public static conditionFromProps(obj: ConditionProps): Condition { + switch (obj.conditionType) { + case RpcConditionType: + return new RpcCondition(obj as RpcConditionProps); + case TimeConditionType: + return new TimeCondition(obj as TimeConditionProps); + case ContractConditionType: + return new ContractCondition(obj as ContractConditionProps); + case CompoundConditionType: + return new CompoundCondition(obj as CompoundConditionProps); + default: + throw new Error(`Invalid conditionType: ${obj.conditionType}`); + } + } +} + export class Condition { constructor( public readonly schema: ConditionSchema, @@ -55,23 +78,8 @@ export class Condition { return data; } - private static conditionFromObject(obj: ConditionProps): Condition { - switch (obj.conditionType) { - case 'rpc': - return new RpcCondition(obj as RpcConditionProps); - case 'time': - return new TimeCondition(obj as TimeConditionProps); - case 'contract': - return new ContractCondition(obj as ContractConditionProps); - case 'compound': - return new CompoundCondition(obj as CompoundConditionProps); - default: - throw new Error(`Invalid conditionType: ${obj.conditionType}`); - } - } - public static fromObj(obj: ConditionProps): Condition { - return Condition.conditionFromObject(obj); + return ConditionFactory.conditionFromProps(obj); } public equals(other: Condition) { diff --git a/src/conditions/index.ts b/src/conditions/index.ts index ac71ad556..9c37634ac 100644 --- a/src/conditions/index.ts +++ b/src/conditions/index.ts @@ -8,4 +8,7 @@ export { } from './condition-expr'; export { ConditionContext, type CustomContextParam } from './context'; export { Condition, type ConditionProps } from './condition'; -export { type CompoundConditionProps } from './compound-condition'; +export { + type CompoundConditionProps, + CompoundConditionType, +} from './compound-condition'; diff --git a/src/conditions/predefined/erc721.ts b/src/conditions/predefined/erc721.ts index 9fcfe22d5..1a81b8456 100644 --- a/src/conditions/predefined/erc721.ts +++ b/src/conditions/predefined/erc721.ts @@ -1,4 +1,5 @@ import { ContractCondition, ContractConditionProps } from '../base'; +import { ContractConditionType } from '../base/contract'; import { USER_ADDRESS_PARAM } from '../const'; // TODO: Rewrite these using Zod schemas? @@ -9,7 +10,7 @@ const ERC721OwnershipDefaults: Omit< ContractConditionProps, ERC721OwnershipFields > = { - conditionType: 'contract', + conditionType: ContractConditionType, method: 'ownerOf', standardContractType: 'ERC721', returnValueTest: { @@ -29,7 +30,7 @@ type ERC721BalanceFields = 'contractAddress' | 'chain'; const ERC721BalanceDefaults: Omit = { - conditionType: 'contract', + conditionType: ContractConditionType, method: 'balanceOf', parameters: [USER_ADDRESS_PARAM], standardContractType: 'ERC721', diff --git a/test/unit/conditions/base/time.test.ts b/test/unit/conditions/base/time.test.ts index 0ace6466f..a2db870ab 100644 --- a/test/unit/conditions/base/time.test.ts +++ b/test/unit/conditions/base/time.test.ts @@ -3,7 +3,11 @@ import { TimeConditionProps, } from '../../../../src/conditions/base'; import { ReturnValueTestProps } from '../../../../src/conditions/base/shared'; -import { timeConditionSchema } from '../../../../src/conditions/base/time'; +import { + TimeConditionMethod, + timeConditionSchema, + TimeConditionType, +} from '../../../../src/conditions/base/time'; describe('validation', () => { const returnValueTest: ReturnValueTestProps = { @@ -14,9 +18,9 @@ describe('validation', () => { it('accepts a valid schema', () => { const conditionObj: TimeConditionProps = { + conditionType: TimeConditionType, returnValueTest, - conditionType: 'time', - method: 'blocktime', + method: TimeConditionMethod, chain: 1, }; const result = TimeCondition.validate(timeConditionSchema, conditionObj); @@ -27,7 +31,7 @@ describe('validation', () => { it('rejects an invalid schema', () => { const badObj = { - conditionType: 'time', + conditionType: TimeConditionType, // Intentionally replacing `returnValueTest` with an invalid test returnValueTest: { ...returnValueTest, diff --git a/test/unit/conditions/compound-condition.test.ts b/test/unit/conditions/compound-condition.test.ts index 2f81a8bc8..fd34d92c4 100644 --- a/test/unit/conditions/compound-condition.test.ts +++ b/test/unit/conditions/compound-condition.test.ts @@ -1,6 +1,9 @@ import { Condition } from '../../../src/conditions'; import { CompoundCondition } from '../../../src/conditions/base'; -import { compoundConditionSchema } from '../../../src/conditions/compound-condition'; +import { + compoundConditionSchema, + CompoundConditionType, +} from '../../../src/conditions/compound-condition'; import { testContractConditionObj, testRpcConditionObj, @@ -18,7 +21,7 @@ describe('validation', () => { expect(result.error).toBeUndefined(); expect(result.data).toEqual({ ...conditionObj, - conditionType: 'compound', + conditionType: CompoundConditionType, }); }); @@ -35,7 +38,7 @@ describe('validation', () => { expect(result.error).toBeUndefined(); expect(result.data).toEqual({ ...conditionObj, - conditionType: 'compound', + conditionType: CompoundConditionType, }); }); @@ -104,14 +107,14 @@ describe('validation', () => { ); expect(result.error).toBeUndefined(); expect(result.data).toEqual({ - conditionType: 'compound', + conditionType: CompoundConditionType, operator: 'and', operands: [ testContractConditionObj, testTimeConditionObj, testRpcConditionObj, { - conditionType: 'compound', + conditionType: CompoundConditionType, operator: 'or', operands: [testTimeConditionObj, testContractConditionObj], }, diff --git a/test/unit/conditions/condition-expr.test.ts b/test/unit/conditions/condition-expr.test.ts index ff53730e1..409f47a78 100644 --- a/test/unit/conditions/condition-expr.test.ts +++ b/test/unit/conditions/condition-expr.test.ts @@ -9,6 +9,7 @@ import { TimeCondition, TimeConditionProps, } from '../../../src/conditions/base'; +import { RpcConditionType } from '../../../src/conditions/base/rpc'; import { USER_ADDRESS_PARAM } from '../../../src/conditions/const'; import { ERC721Balance } from '../../../src/conditions/predefined'; import { objectEquals, toJSON } from '../../../src/utils'; @@ -232,7 +233,7 @@ describe('condition set', () => { it('rejects a mismatched condition type', () => { const conditionObj = { ...testTimeConditionObj, - conditionType: 'rpc', + conditionType: RpcConditionType, } as unknown as TimeConditionProps; expect(() => { ConditionExpression.fromObj({ diff --git a/test/unit/testVariables.ts b/test/unit/testVariables.ts index 1acfe86eb..c0fbf3488 100644 --- a/test/unit/testVariables.ts +++ b/test/unit/testVariables.ts @@ -3,8 +3,16 @@ import { RpcConditionProps, TimeConditionProps, } from '../../src/conditions/base'; -import { FunctionAbiProps } from '../../src/conditions/base/contract'; +import { + ContractConditionType, + FunctionAbiProps, +} from '../../src/conditions/base/contract'; +import { RpcConditionType } from '../../src/conditions/base/rpc'; import { ReturnValueTestProps } from '../../src/conditions/base/shared'; +import { + TimeConditionMethod, + TimeConditionType, +} from '../../src/conditions/base/time'; export const aliceSecretKeyBytes = new Uint8Array([ 55, 82, 190, 189, 203, 164, 60, 148, 36, 86, 46, 123, 63, 152, 215, 113, 174, @@ -28,18 +36,18 @@ export const testReturnValueTest: ReturnValueTestProps = { }; export const testTimeConditionObj: TimeConditionProps = { - conditionType: 'time', + conditionType: TimeConditionType, returnValueTest: { index: 0, comparator: '>', value: '100', }, - method: 'blocktime', + method: TimeConditionMethod, chain: 5, }; export const testRpcConditionObj: RpcConditionProps = { - conditionType: 'rpc', + conditionType: RpcConditionType, chain: TEST_CHAIN_ID, method: 'eth_getBalance', parameters: ['0x1e988ba4692e52Bc50b375bcC8585b95c48AaD77'], @@ -47,7 +55,7 @@ export const testRpcConditionObj: RpcConditionProps = { }; export const testContractConditionObj: ContractConditionProps = { - conditionType: 'contract', + conditionType: ContractConditionType, contractAddress: '0x0000000000000000000000000000000000000000', chain: 5, standardContractType: 'ERC20',