Skip to content

Commit a414235

Browse files
committed
feat: enhance EventFlow and EventSignal to support multiple addresses; refactor test cases and improve debugging capabilities
1 parent 569013b commit a414235

File tree

17 files changed

+216
-82
lines changed

17 files changed

+216
-82
lines changed

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@
4747
"publish": "nr prepublishOnly && esno scripts/publish.ts",
4848
"release": "esno scripts/release.ts",
4949
"start": "esno src/index.ts",
50-
"test": "DEBUG=DEBUG:ora-stack:* vitest",
50+
"test": "vitest",
51+
"test:debug": "DEBUG=DEBUG:ora-stack:* vitest",
5152
"typecheck": "tsc --noEmit"
5253
},
5354
"devDependencies": {

packages/orap/examples/declarativeDemo/app.ts

Lines changed: 31 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import type { ContractEventPayload } from 'ethers'
33
import { Logger, objectKeys, randomStr, redisStore } from '@ora-io/utils'
44
import type { ListenOptions, ToKeyFn } from '../../src'
5-
import { CheckTransactionStatus, Orap, StoreManager, getMiddlewareContext } from '../../src'
5+
import { Orap, StoreManager, getMiddlewareContext } from '../../src'
66
import ABI from './erc20.abi.json'
77

88
const MAINNET_USDT_ADDR = '0xdAC17F958D2ee523a2206206994597C13D831ec7'
@@ -24,7 +24,12 @@ export function startDemo(options: ListenOptions, storeConfig?: any) {
2424

2525
const toKey: ToKeyFn = (from: string, _to: string, _amount: number) => `${from}_${randomStr(4)}`
2626

27-
const event = orap.event(eventSignalParam.address, eventSignalParam.abi, eventSignalParam.eventName)
27+
const event = orap.event({
28+
address: eventSignalParam.address,
29+
abi: eventSignalParam.abi,
30+
eventName: eventSignalParam.eventName,
31+
enableSubscribe: false,
32+
})
2833
.crosscheck({
2934
store,
3035
storeKeyPrefix: 'ora-stack:orap:demo:cc:',
@@ -43,27 +48,34 @@ export function startDemo(options: ListenOptions, storeConfig?: any) {
4348
.key(toKey)
4449
.prefix('ora-stack:orap:demo:TransferTask:', 'ora-stack:orap:demo:Done-TransferTask:')
4550
.ttl({ taskTtl: 120000, doneTtl: 60000 })
46-
.use(CheckTransactionStatus(options.wsProvider))
51+
// .use(CheckTransactionStatus(options.wsProvider))
4752
.handle(handleTask)
4853
// add another task
49-
.another()
50-
.task()
51-
.prefix('ora-stack:orap:demo:AnotherTask:', 'ora-stack:orap:demo:Done-AnotherTask:')
52-
.cache(sm) // rm to use mem by default
53-
.ttl({ taskTtl: 20000, doneTtl: 20000 })
54-
.handle(handleTask_2)
55-
56-
event.stop()
54+
// .another()
55+
// .task()
56+
// .prefix('ora-stack:orap:demo:AnotherTask:', 'ora-stack:orap:demo:Done-AnotherTask:')
57+
// .cache(sm) // rm to use mem by default
58+
// .ttl({ taskTtl: 20000, doneTtl: 20000 })
59+
// .handle(handleTask_2)
5760

5861
// start signal listener
5962
orap.listen(
6063
options,
6164
() => { logger.log('listening on provider.network') },
6265
)
66+
67+
setTimeout(() => {
68+
logger.log('[+] add another address')
69+
event.addresses([
70+
eventSignalParam.address,
71+
'0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',
72+
])
73+
event.restart()
74+
}, 10 * 1000)
6375
}
6476

65-
async function handleTask(from: string, to: string, amount: number, _event: ContractEventPayload) {
66-
logger.log('[+] handleTask: from =', from, 'to =', to, 'amount =', amount)
77+
async function handleTask(from: string, to: string, amount: number, event: ContractEventPayload) {
78+
logger.log('[+] handleTask: from =', from, 'to =', to, 'amount =', amount, 'address =', event.log.address)
6779
const args = objectKeys(arguments).map(k => arguments[k])
6880

6981
const { next } = getMiddlewareContext(...args)
@@ -76,10 +88,10 @@ async function newEventSignalHook(from: string, to: string, amount: number, even
7688
return true // true to continue handle tasks, false to hijack the process.
7789
}
7890

79-
async function handleTask_2(from: string, to: string, amount: number) {
80-
logger.log('[+] handleTask_2: from =', from, 'to =', to, 'amount =', amount)
81-
const args = objectKeys(arguments).map(k => arguments[k])
91+
// async function handleTask_2(from: string, to: string, amount: number) {
92+
// logger.log('[+] handleTask_2: from =', from, 'to =', to, 'amount =', amount)
93+
// const args = objectKeys(arguments).map(k => arguments[k])
8294

83-
const { next } = getMiddlewareContext(...args)
84-
await next()
85-
}
95+
// const { next } = getMiddlewareContext(...args)
96+
// await next()
97+
// }

packages/orap/src/flow/event.test.ts

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { ethers } from 'ethers'
22
import { beforeEach, describe, expect, it, vi } from 'vitest'
33
import { EventVerse } from '../verse/event'
4+
import { ERC20_ABI, USDT_ADDRESS } from '../../tests/config'
45
import { OrapFlow } from './orap'
56
import { EventFlow } from './event'
67

@@ -10,7 +11,7 @@ describe('EventFlow', () => {
1011

1112
beforeEach(() => {
1213
orapFlow = new OrapFlow()
13-
eventFlow = new EventFlow(orapFlow)
14+
eventFlow = new EventFlow(orapFlow, { address: USDT_ADDRESS, abi: ERC20_ABI, eventName: 'Transfer' })
1415
})
1516

1617
it('should create a task flow', () => {
@@ -56,4 +57,36 @@ describe('EventFlow', () => {
5657
eventFlow.stop()
5758
expect(stopFn).toHaveBeenCalled()
5859
})
60+
61+
describe('address', () => {
62+
it('should set the address', () => {
63+
const eventFlow = new EventFlow(orapFlow, { address: USDT_ADDRESS, abi: ERC20_ABI, eventName: 'Transfer' })
64+
eventFlow.address('0x1234567890123456789012345678901234567890')
65+
expect(eventFlow.params.address).toEqual(['0x1234567890123456789012345678901234567890'])
66+
})
67+
68+
it.only('should set the array address', () => {
69+
const eventFlow = new EventFlow(orapFlow, { address: [USDT_ADDRESS], abi: ERC20_ABI, eventName: 'Transfer' })
70+
eventFlow.address(1, '0x1234567890123456789012345678901234567890')
71+
expect(eventFlow.params.address).toContainEqual('0x1234567890123456789012345678901234567890')
72+
})
73+
74+
it('should set the address with number', () => {
75+
const eventFlow = new EventFlow(orapFlow, { address: USDT_ADDRESS, abi: ERC20_ABI, eventName: 'Transfer' })
76+
eventFlow.address(1, '0x1234567890123456789012345678901234567890')
77+
expect(eventFlow.params.address).toEqual(['0x1234567890123456789012345678901234567890'])
78+
})
79+
80+
it('should set the addresses with array', () => {
81+
const eventFlow = new EventFlow(orapFlow, { address: USDT_ADDRESS, abi: ERC20_ABI, eventName: 'Transfer' })
82+
eventFlow.addresses(['0x1234567890123456789012345678901234567890'])
83+
expect(eventFlow.params.address).toEqual(['0x1234567890123456789012345678901234567890'])
84+
})
85+
86+
it('should set the addresses with array and number', () => {
87+
const eventFlow = new EventFlow(orapFlow, { address: USDT_ADDRESS, abi: ERC20_ABI, eventName: 'Transfer' })
88+
eventFlow.addresses([USDT_ADDRESS, '0x1234567890123456789012345678901234567890'])
89+
expect(eventFlow.params.address).toEqual([USDT_ADDRESS, '0x1234567890123456789012345678901234567890'])
90+
})
91+
})
5992
})

packages/orap/src/flow/event.ts

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import type { AutoCrossCheckParam, Providers } from '@ora-io/reku'
2+
import type { ContractAddress } from '@ora-io/utils/src'
23
import type { Context } from '../task'
34
import type { TaskFlowParams } from '../flow/task'
45
import { TaskFlow } from '../flow/task'
@@ -19,16 +20,24 @@ export class EventFlow implements Flow {
1920
private _crosscheckProvider?: Providers
2021

2122
private _verse: EventVerse = new EventVerse(this)
23+
private _addresses: ContractAddress[] = []
24+
private _params: EventSignalRegisterParams
2225

2326
get verse() {
2427
return this._verse
2528
}
2629

30+
get params() {
31+
return this._params
32+
}
33+
2734
constructor(
28-
private parentFlow?: OrapFlow,
29-
public params?: EventSignalRegisterParams,
35+
private parentFlow: OrapFlow,
36+
params: EventSignalRegisterParams,
3037
handleFn?: HandleFn, // return: succ & continue if true, stop if false
3138
) {
39+
this._params = params
40+
this._addresses = Array.isArray(this.params.address) ? this.params.address : [this.params.address]
3241
// Default handleFn
3342
this.handleFn = handleFn ?? (async (..._args: Array<any>) => {
3443
return true
@@ -58,6 +67,7 @@ export class EventFlow implements Flow {
5867
tf = new TaskFlow(this, sm)
5968
}
6069
this._taskFlows.push(tf)
70+
this._verse.setTaskVerses(this._taskFlows.map(flow => flow.verse))
6171
return tf
6272
}
6373

@@ -95,14 +105,40 @@ export class EventFlow implements Flow {
95105
}
96106

97107
another(): OrapFlow {
98-
return this.parentFlow!
108+
return this.parentFlow
99109
}
100110

101-
stop() {
111+
stop(): this {
102112
this._verse.stop()
113+
return this
103114
}
104115

105-
restart() {
116+
restart(): this {
106117
this._verse.restart()
118+
return this
119+
}
120+
121+
address(_index: number, _address: ContractAddress): this
122+
address(address: ContractAddress): this
123+
address(_first: ContractAddress | number, _second?: ContractAddress): this {
124+
if (typeof _first === 'number') {
125+
if (!_second)
126+
throw new Error('address is required')
127+
if (Array.isArray(this._params.address))
128+
Reflect.set(this._addresses, _first, _second)
129+
130+
else this._addresses = [_second!]
131+
}
132+
else
133+
if (Array.isArray(this._params.address)) { this._addresses = [...new Set([...this._params.address, _first])] }
134+
else { this._addresses = [_first] }
135+
this._params.address = this._addresses
136+
return this
137+
}
138+
139+
addresses(addresses: ContractAddress[]): this {
140+
this._addresses = addresses
141+
this._params.address = this._addresses
142+
return this
107143
}
108144
}

packages/orap/src/flow/orap.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ export class OrapFlow implements Flow {
5151
* @param options
5252
* @param onListenFn
5353
*/
54-
listen(options: ListenOptions, onListenFn?: Fn) {
54+
listen(options: ListenOptions, onListenFn?: Fn): this {
5555
for (const eventFlow of this.subflows.event) {
5656
eventFlow.setSubscribeProvider(options.wsProvider)
5757
if (options.httpProvider)
@@ -65,14 +65,17 @@ export class OrapFlow implements Flow {
6565

6666
this._verse.play()
6767
this.onListenFn()
68+
return this
6869
}
6970

70-
stop() {
71+
stop(): this {
7172
this._verse.stop()
73+
return this
7274
}
7375

74-
restart() {
76+
restart(): this {
7577
this._verse.restart()
78+
return this
7679
}
7780

7881
assemble(): OrapVerse {

packages/orap/src/flow/task.test.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
import { beforeEach, describe, expect, it, vi } from 'vitest'
22
import { memoryStore } from '@ora-io/utils'
33
import { StoreManager } from '../store'
4-
import { EventFlow, TaskFlow } from '.'
4+
import { ERC20_ABI, USDT_ADDRESS } from '../../tests/config'
5+
import { EventFlow, OrapFlow, TaskFlow } from '.'
56

67
describe('TaskFlow', () => {
78
let parentFlow: any
89
let taskFlow: TaskFlow
910

1011
beforeEach(() => {
11-
parentFlow = new EventFlow()
12+
parentFlow = new EventFlow(new OrapFlow(), { address: USDT_ADDRESS, abi: ERC20_ABI, eventName: 'Transfer' })
1213
taskFlow = new TaskFlow(parentFlow)
1314
})
1415

packages/orap/src/flow/task.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,11 +144,13 @@ export class TaskFlow implements Flow {
144144
return new TaskVerse(this)
145145
}
146146

147-
stop() {
147+
stop(): this {
148148
this._verse.stop()
149+
return this
149150
}
150151

151-
restart() {
152+
restart(): this {
152153
this._verse.restart()
154+
return this
153155
}
154156
}

packages/orap/src/signal/event.test.ts

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { ethers } from 'ethers'
22
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
33
import type { AutoCrossCheckParam } from '@ora-io/reku'
44
import { AutoCrossChecker, RekuProviderManager } from '@ora-io/reku'
5+
import type { ContractAddress } from '@ora-io/utils/src'
56
import { ERC20_ABI, MAINNET_WSS, USDT_ADDRESS } from '../../tests/config'
67
import type { EventSignalCallback, EventSignalRegisterParams } from './event'
78
import { EventSignal } from './event'
@@ -30,11 +31,6 @@ describe('EventSignal', () => {
3031
expect(eventSignal).toBeInstanceOf(EventSignal)
3132
})
3233

33-
it('should set the contract property', () => {
34-
expect(eventSignal.contract).toBeInstanceOf(ethers.Contract)
35-
expect(eventSignal.contract.interface).toEqual(new ethers.Interface(ERC20_ABI))
36-
})
37-
3834
it('should set the eventFragment property', () => {
3935
expect(eventSignal.eventFragment).toBeInstanceOf(ethers.EventFragment)
4036
expect(eventSignal.eventFragment.name).toBe(params.eventName)
@@ -91,29 +87,37 @@ describe('EventSignal', () => {
9187
const provider = {} as any
9288

9389
beforeEach(() => {
94-
eventSignal.contract.connect = vi.fn().mockReturnValue({
95-
on: vi.fn(),
96-
})
90+
// Mock the contract in the contractMap
91+
const mockContract = {
92+
connect: vi.fn().mockReturnValue({
93+
on: vi.fn(),
94+
}),
95+
removeListener: vi.fn(),
96+
} as any
97+
eventSignal.contractMap.set(params.address as ContractAddress, mockContract)
9798
})
9899

99100
it('should add the contract to the provider if provider is an instance of RekuProviderManager', () => {
100101
rekuProviderManager.addContract = vi.fn()
101102
rekuProviderManager.addListener = vi.fn()
102103
eventSignal.startEventListener(rekuProviderManager)
103-
expect(rekuProviderManager.addContract).toHaveBeenCalledWith(params.address, eventSignal.contract)
104+
expect(rekuProviderManager.addContract).toHaveBeenCalledWith(params.address, eventSignal.contractMap.get(params.address as ContractAddress))
104105
expect(rekuProviderManager.addListener).toHaveBeenCalledWith(params.address, params.eventName, eventSignal.subscribeCallback)
105106
})
106107

107108
it('should connect the contract to the provider if provider is not an instance of RekuProviderManager', () => {
108109
eventSignal.startEventListener(provider)
109-
expect(eventSignal.contract.connect).toHaveBeenCalledWith(provider)
110+
expect(eventSignal.contractMap.get(params.address as ContractAddress)?.connect).toHaveBeenCalledWith(provider)
110111
})
111112

112113
it('should call the on method of the listener with the eventName and subscribeCallback', () => {
113114
const listener = {
114115
on: vi.fn(),
115116
}
116-
eventSignal.contract.connect = vi.fn().mockReturnValue(listener)
117+
const mockContract = eventSignal.contractMap.get(params.address as ContractAddress)
118+
if (mockContract)
119+
mockContract.connect = vi.fn().mockReturnValue(listener)
120+
117121
eventSignal.startEventListener(provider)
118122
expect(listener.on).toHaveBeenCalledWith(params.eventName, eventSignal.subscribeCallback)
119123
})

0 commit comments

Comments
 (0)