@xyo-network/chain-bridge 1.15.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +165 -0
- package/README.md +96 -0
- package/dist/node/driver/index.d.ts +2 -0
- package/dist/node/driver/index.d.ts.map +1 -0
- package/dist/node/driver/indexer/ChainBlockIteration/ChainHashIterationService.d.ts +24 -0
- package/dist/node/driver/indexer/ChainBlockIteration/ChainHashIterationService.d.ts.map +1 -0
- package/dist/node/driver/indexer/ChainBlockIteration/index.d.ts +2 -0
- package/dist/node/driver/indexer/ChainBlockIteration/index.d.ts.map +1 -0
- package/dist/node/driver/indexer/ChainBlocksObservable.d.ts +5 -0
- package/dist/node/driver/indexer/ChainBlocksObservable.d.ts.map +1 -0
- package/dist/node/driver/indexer/ChainHydratedBlocksObservable.d.ts +11 -0
- package/dist/node/driver/indexer/ChainHydratedBlocksObservable.d.ts.map +1 -0
- package/dist/node/driver/indexer/index.d.ts +2 -0
- package/dist/node/driver/indexer/index.d.ts.map +1 -0
- package/dist/node/driver/indexer/spec/ChainBlocksObservable.spec.d.ts +2 -0
- package/dist/node/driver/indexer/spec/ChainBlocksObservable.spec.d.ts.map +1 -0
- package/dist/node/driver/indexer/spec/ChainHydratedBlocksObservable.spec.d.ts +2 -0
- package/dist/node/driver/indexer/spec/ChainHydratedBlocksObservable.spec.d.ts.map +1 -0
- package/dist/node/driver/mongo/MongoMap.d.ts +17 -0
- package/dist/node/driver/mongo/MongoMap.d.ts.map +1 -0
- package/dist/node/driver/mongo/index.d.ts +2 -0
- package/dist/node/driver/mongo/index.d.ts.map +1 -0
- package/dist/node/driver/mongo/spec/MongoMap.spec.d.ts +2 -0
- package/dist/node/driver/mongo/spec/MongoMap.spec.d.ts.map +1 -0
- package/dist/node/index.d.ts +2 -0
- package/dist/node/index.d.ts.map +1 -0
- package/dist/node/index.mjs +751 -0
- package/dist/node/index.mjs.map +1 -0
- package/dist/node/interface/index.d.ts +5 -0
- package/dist/node/interface/index.d.ts.map +1 -0
- package/dist/node/interface/interface/ChainBridgeRelayInterface.d.ts +9 -0
- package/dist/node/interface/interface/ChainBridgeRelayInterface.d.ts.map +1 -0
- package/dist/node/interface/interface/IntentIndexerInterface.d.ts +6 -0
- package/dist/node/interface/interface/IntentIndexerInterface.d.ts.map +1 -0
- package/dist/node/interface/interface/LockingProcessorInterface.d.ts +9 -0
- package/dist/node/interface/interface/LockingProcessorInterface.d.ts.map +1 -0
- package/dist/node/interface/interface/ObservationIndexerInterface.d.ts +7 -0
- package/dist/node/interface/interface/ObservationIndexerInterface.d.ts.map +1 -0
- package/dist/node/interface/interface/Params.d.ts +26 -0
- package/dist/node/interface/interface/Params.d.ts.map +1 -0
- package/dist/node/interface/interface/RelayInterface.d.ts +8 -0
- package/dist/node/interface/interface/RelayInterface.d.ts.map +1 -0
- package/dist/node/interface/interface/index.d.ts +7 -0
- package/dist/node/interface/interface/index.d.ts.map +1 -0
- package/dist/node/interface/repository/RepositoryInterface.d.ts +22 -0
- package/dist/node/interface/repository/RepositoryInterface.d.ts.map +1 -0
- package/dist/node/interface/repository/index.d.ts +2 -0
- package/dist/node/interface/repository/index.d.ts.map +1 -0
- package/dist/node/interface/service/ChainBridgeRelay/ChainBridgeRelayInterface.d.ts +9 -0
- package/dist/node/interface/service/ChainBridgeRelay/ChainBridgeRelayInterface.d.ts.map +1 -0
- package/dist/node/interface/service/ChainBridgeRelay/ChainBridgeRelayService.d.ts +31 -0
- package/dist/node/interface/service/ChainBridgeRelay/ChainBridgeRelayService.d.ts.map +1 -0
- package/dist/node/interface/service/ChainBridgeRelay/index.d.ts +2 -0
- package/dist/node/interface/service/ChainBridgeRelay/index.d.ts.map +1 -0
- package/dist/node/interface/service/ChainBridgeRelay/spec/ChainBridgeRelayService.spec.d.ts +2 -0
- package/dist/node/interface/service/ChainBridgeRelay/spec/ChainBridgeRelayService.spec.d.ts.map +1 -0
- package/dist/node/interface/service/Observer/Observer.d.ts +15 -0
- package/dist/node/interface/service/Observer/Observer.d.ts.map +1 -0
- package/dist/node/interface/service/Observer/index.d.ts +2 -0
- package/dist/node/interface/service/Observer/index.d.ts.map +1 -0
- package/dist/node/interface/service/index.d.ts +3 -0
- package/dist/node/interface/service/index.d.ts.map +1 -0
- package/dist/node/interface/util/getBridgeIntentIdentifier.d.ts +5 -0
- package/dist/node/interface/util/getBridgeIntentIdentifier.d.ts.map +1 -0
- package/dist/node/interface/util/index.d.ts +2 -0
- package/dist/node/interface/util/index.d.ts.map +1 -0
- package/dist/node/manifest/getLocator.d.ts +14 -0
- package/dist/node/manifest/getLocator.d.ts.map +1 -0
- package/dist/node/manifest/getNode.d.ts +15 -0
- package/dist/node/manifest/getNode.d.ts.map +1 -0
- package/dist/node/manifest/index.d.ts +6 -0
- package/dist/node/manifest/index.d.ts.map +1 -0
- package/dist/node/manifest/nodeManifest.d.ts +6 -0
- package/dist/node/manifest/nodeManifest.d.ts.map +1 -0
- package/dist/node/manifest/private/index.d.ts +5 -0
- package/dist/node/manifest/private/index.d.ts.map +1 -0
- package/dist/node/manifest/public/index.d.ts +14 -0
- package/dist/node/manifest/public/index.d.ts.map +1 -0
- package/dist/node/manifest/public/spec/Node.spec.d.ts +2 -0
- package/dist/node/manifest/public/spec/Node.spec.d.ts.map +1 -0
- package/dist/node/server/app.d.ts +4 -0
- package/dist/node/server/app.d.ts.map +1 -0
- package/dist/node/server/index.d.ts +11 -0
- package/dist/node/server/index.d.ts.map +1 -0
- package/dist/node/server/instrumentation.d.ts +9 -0
- package/dist/node/server/instrumentation.d.ts.map +1 -0
- package/dist/node/server/routes/addRoutes.d.ts +3 -0
- package/dist/node/server/routes/addRoutes.d.ts.map +1 -0
- package/dist/node/server/routes/address/AddressPathParams.d.ts +4 -0
- package/dist/node/server/routes/address/AddressPathParams.d.ts.map +1 -0
- package/dist/node/server/routes/address/addNodeRoutes.d.ts +3 -0
- package/dist/node/server/routes/address/addNodeRoutes.d.ts.map +1 -0
- package/dist/node/server/routes/address/get/get.d.ts +4 -0
- package/dist/node/server/routes/address/get/get.d.ts.map +1 -0
- package/dist/node/server/routes/address/get/index.d.ts +2 -0
- package/dist/node/server/routes/address/get/index.d.ts.map +1 -0
- package/dist/node/server/routes/address/index.d.ts +2 -0
- package/dist/node/server/routes/address/index.d.ts.map +1 -0
- package/dist/node/server/routes/address/post/getQueryConfig.d.ts +6 -0
- package/dist/node/server/routes/address/post/getQueryConfig.d.ts.map +1 -0
- package/dist/node/server/routes/address/post/index.d.ts +2 -0
- package/dist/node/server/routes/address/post/index.d.ts.map +1 -0
- package/dist/node/server/routes/address/post/post.d.ts +8 -0
- package/dist/node/server/routes/address/post/post.d.ts.map +1 -0
- package/dist/node/server/routes/dataLake/addDataLakeRoutes.d.ts +3 -0
- package/dist/node/server/routes/dataLake/addDataLakeRoutes.d.ts.map +1 -0
- package/dist/node/server/routes/dataLake/archivistMiddleware.d.ts +10 -0
- package/dist/node/server/routes/dataLake/archivistMiddleware.d.ts.map +1 -0
- package/dist/node/server/routes/dataLake/index.d.ts +2 -0
- package/dist/node/server/routes/dataLake/index.d.ts.map +1 -0
- package/dist/node/server/routes/healthz/get.d.ts +3 -0
- package/dist/node/server/routes/healthz/get.d.ts.map +1 -0
- package/dist/node/server/routes/healthz/index.d.ts +2 -0
- package/dist/node/server/routes/healthz/index.d.ts.map +1 -0
- package/dist/node/server/routes/index.d.ts +5 -0
- package/dist/node/server/routes/index.d.ts.map +1 -0
- package/dist/node/server/routes/rpc/index.d.ts +2 -0
- package/dist/node/server/routes/rpc/index.d.ts.map +1 -0
- package/dist/node/server/routes/rpc/routes/addRpcRoutes.d.ts +3 -0
- package/dist/node/server/routes/rpc/routes/addRpcRoutes.d.ts.map +1 -0
- package/dist/node/server/routes/rpc/routes/index.d.ts +2 -0
- package/dist/node/server/routes/rpc/routes/index.d.ts.map +1 -0
- package/dist/node/server/server.d.ts +11 -0
- package/dist/node/server/server.d.ts.map +1 -0
- package/package.json +125 -0
- package/src/driver/index.ts +1 -0
- package/src/driver/indexer/ChainBlockIteration/ChainHashIterationService.ts +87 -0
- package/src/driver/indexer/ChainBlockIteration/index.ts +1 -0
- package/src/driver/indexer/ChainBlocksObservable.ts +47 -0
- package/src/driver/indexer/ChainHydratedBlocksObservable.ts +23 -0
- package/src/driver/indexer/index.ts +1 -0
- package/src/driver/indexer/spec/ChainBlocksObservable.spec.ts +58 -0
- package/src/driver/indexer/spec/ChainHydratedBlocksObservable.spec.ts +58 -0
- package/src/driver/mongo/MongoMap.ts +62 -0
- package/src/driver/mongo/index.ts +1 -0
- package/src/driver/mongo/spec/MongoMap.spec.ts +67 -0
- package/src/index.ts +1 -0
- package/src/interface/index.ts +4 -0
- package/src/interface/interface/ChainBridgeRelayInterface.ts +9 -0
- package/src/interface/interface/IntentIndexerInterface.ts +7 -0
- package/src/interface/interface/LockingProcessorInterface.ts +10 -0
- package/src/interface/interface/ObservationIndexerInterface.ts +12 -0
- package/src/interface/interface/Params.ts +26 -0
- package/src/interface/interface/RelayInterface.ts +8 -0
- package/src/interface/interface/index.ts +6 -0
- package/src/interface/repository/RepositoryInterface.ts +28 -0
- package/src/interface/repository/index.ts +1 -0
- package/src/interface/service/ChainBridgeRelay/ChainBridgeRelayInterface.ts +11 -0
- package/src/interface/service/ChainBridgeRelay/ChainBridgeRelayService.ts +116 -0
- package/src/interface/service/ChainBridgeRelay/index.ts +1 -0
- package/src/interface/service/ChainBridgeRelay/spec/ChainBridgeRelayService.spec.ts +264 -0
- package/src/interface/service/Observer/Observer.ts +48 -0
- package/src/interface/service/Observer/index.ts +1 -0
- package/src/interface/service/index.ts +2 -0
- package/src/interface/util/getBridgeIntentIdentifier.ts +18 -0
- package/src/interface/util/index.ts +1 -0
- package/src/manifest/getLocator.ts +105 -0
- package/src/manifest/getNode.ts +32 -0
- package/src/manifest/index.ts +5 -0
- package/src/manifest/node.json +17 -0
- package/src/manifest/nodeManifest.ts +8 -0
- package/src/manifest/private/index.ts +4 -0
- package/src/manifest/public/Chain.json +138 -0
- package/src/manifest/public/Pending.json +35 -0
- package/src/manifest/public/index.ts +20 -0
- package/src/manifest/public/spec/Node.spec.ts +32 -0
- package/src/server/app.ts +37 -0
- package/src/server/index.ts +13 -0
- package/src/server/instrumentation.ts +15 -0
- package/src/server/routes/addRoutes.ts +11 -0
- package/src/server/routes/address/AddressPathParams.ts +3 -0
- package/src/server/routes/address/addNodeRoutes.ts +21 -0
- package/src/server/routes/address/get/get.ts +33 -0
- package/src/server/routes/address/get/index.ts +1 -0
- package/src/server/routes/address/index.ts +1 -0
- package/src/server/routes/address/post/getQueryConfig.ts +23 -0
- package/src/server/routes/address/post/index.ts +1 -0
- package/src/server/routes/address/post/post.ts +77 -0
- package/src/server/routes/dataLake/addDataLakeRoutes.ts +9 -0
- package/src/server/routes/dataLake/archivistMiddleware.ts +86 -0
- package/src/server/routes/dataLake/index.ts +1 -0
- package/src/server/routes/healthz/get.ts +20 -0
- package/src/server/routes/healthz/index.ts +1 -0
- package/src/server/routes/index.ts +5 -0
- package/src/server/routes/rpc/index.ts +1 -0
- package/src/server/routes/rpc/routes/addRpcRoutes.ts +22 -0
- package/src/server/routes/rpc/routes/index.ts +1 -0
- package/src/server/server.ts +59 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { assertEx } from '@xylabs/assert'
|
|
2
|
+
import {
|
|
3
|
+
AbstractCreatable, creatable, CreatableParams,
|
|
4
|
+
} from '@xylabs/creatable'
|
|
5
|
+
import { BaseMongoSdk } from '@xylabs/mongo'
|
|
6
|
+
import { isNull } from '@xylabs/typeof'
|
|
7
|
+
import { AsynchronousMap } from '@xyo-network/chain-protocol'
|
|
8
|
+
import {
|
|
9
|
+
Document, Filter, OptionalUnlessRequiredId, WithId,
|
|
10
|
+
} from 'mongodb'
|
|
11
|
+
|
|
12
|
+
export interface MongoMapParams<TData extends Document = Document> extends CreatableParams {
|
|
13
|
+
sdk: BaseMongoSdk<TData>
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function stripMongoId<V extends Document>(doc: WithId<V>): V {
|
|
17
|
+
const { _id, ...rest } = doc
|
|
18
|
+
return rest as unknown as V
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
@creatable()
|
|
22
|
+
export class MongoMap<K = string, V extends Document = Document>
|
|
23
|
+
extends AbstractCreatable<MongoMapParams<V>>
|
|
24
|
+
implements AsynchronousMap<K, V> {
|
|
25
|
+
get sdk(): BaseMongoSdk<V> {
|
|
26
|
+
return assertEx(this.params.sdk, () => 'No sdk specified')
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async clear(): Promise<void> {
|
|
30
|
+
await this.sdk.deleteMany({})
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async delete(id: K): Promise<boolean> {
|
|
34
|
+
const filter = { _id: id } as Filter<V>
|
|
35
|
+
const result = await this.sdk.deleteOne(filter)
|
|
36
|
+
return result.deletedCount > 0
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async get(id: K): Promise<V | undefined> {
|
|
40
|
+
const filter = { _id: id } as Filter<V>
|
|
41
|
+
const doc = await this.sdk.findOne(filter)
|
|
42
|
+
return isNull(doc) ? undefined : stripMongoId(doc)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async has(id: K): Promise<boolean> {
|
|
46
|
+
const filter = { _id: id } as Filter<V>
|
|
47
|
+
const exists = await this.sdk.findOne(filter)
|
|
48
|
+
return isNull(exists) ? false : true
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async set(id: K, data: V): Promise<this> {
|
|
52
|
+
const filter = { _id: id } as Filter<V>
|
|
53
|
+
const value = { ...data, _id: id } as OptionalUnlessRequiredId<V>
|
|
54
|
+
await this.sdk.replaceOne(filter, value, { upsert: true })
|
|
55
|
+
return this
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
override async startHandler(): Promise<void> {
|
|
59
|
+
await super.startHandler()
|
|
60
|
+
// TODO: Ensure index
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './MongoMap.ts'
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { BaseMongoSdk } from '@xylabs/mongo'
|
|
2
|
+
import { getBaseMongoSdkPrivateConfig, hasMongoDBConfig } from '@xyo-network/module-abstract-mongodb'
|
|
3
|
+
import {
|
|
4
|
+
beforeAll, beforeEach, describe, expect, it,
|
|
5
|
+
} from 'vitest'
|
|
6
|
+
|
|
7
|
+
import { MongoMap } from '../MongoMap.ts'
|
|
8
|
+
|
|
9
|
+
describe.runIf(hasMongoDBConfig())('MongoMap', () => {
|
|
10
|
+
interface TestDoc {
|
|
11
|
+
name: string
|
|
12
|
+
value: number
|
|
13
|
+
}
|
|
14
|
+
const collection = 'test_mongo_map'
|
|
15
|
+
let sdk: BaseMongoSdk<TestDoc>
|
|
16
|
+
let map: MongoMap<string, TestDoc>
|
|
17
|
+
beforeAll(async () => {
|
|
18
|
+
const payloadSdkConfig = getBaseMongoSdkPrivateConfig()
|
|
19
|
+
sdk = new BaseMongoSdk<TestDoc>({ ...payloadSdkConfig, collection })
|
|
20
|
+
map = await MongoMap.create<MongoMap<string, TestDoc>>({ sdk })
|
|
21
|
+
await map.start()
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
beforeEach(async () => {
|
|
25
|
+
await map.clear()
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
it('can set and get a value', async () => {
|
|
29
|
+
await map.set('foo', { name: 'Test', value: 42 })
|
|
30
|
+
const result = await map.get('foo')
|
|
31
|
+
expect(result).toEqual({ name: 'Test', value: 42 })
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
it('returns undefined for missing key', async () => {
|
|
35
|
+
const result = await map.get('missing')
|
|
36
|
+
expect(result).toBeUndefined()
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
it('can detect if key exists', async () => {
|
|
40
|
+
await map.set('exists', { name: 'Check', value: 100 })
|
|
41
|
+
const hasExists = await map.has('exists')
|
|
42
|
+
const hasMissing = await map.has('does-not-exist')
|
|
43
|
+
expect(hasExists).toBe(true)
|
|
44
|
+
expect(hasMissing).toBe(false)
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
it('can delete a key', async () => {
|
|
48
|
+
await map.set('delete-me', { name: 'ToDelete', value: 1 })
|
|
49
|
+
const deleted = await map.delete('delete-me')
|
|
50
|
+
const stillExists = await map.has('delete-me')
|
|
51
|
+
expect(deleted).toBe(true)
|
|
52
|
+
expect(stillExists).toBe(false)
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
it('delete returns false for non-existent key', async () => {
|
|
56
|
+
const deleted = await map.delete('non-existent')
|
|
57
|
+
expect(deleted).toBe(false)
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
it('can clear all entries', async () => {
|
|
61
|
+
await map.set('one', { name: 'A', value: 1 })
|
|
62
|
+
await map.set('two', { name: 'B', value: 2 })
|
|
63
|
+
await map.clear()
|
|
64
|
+
expect(await map.has('one')).toBe(false)
|
|
65
|
+
expect(await map.has('two')).toBe(false)
|
|
66
|
+
})
|
|
67
|
+
})
|
package/src/index.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './server/index.ts'
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { BridgeDestinationObservation, BridgeIntent } from '@xyo-network/xl1-protocol'
|
|
2
|
+
|
|
3
|
+
import type { AsynchronousRelayInterface, SynchronousRelayInterface } from './RelayInterface.ts'
|
|
4
|
+
|
|
5
|
+
export interface AsynchronousChainBridgeRelayInterface extends AsynchronousRelayInterface<BridgeIntent, BridgeDestinationObservation> {}
|
|
6
|
+
|
|
7
|
+
export interface SynchronousChainBridgeRelayInterface extends SynchronousRelayInterface<BridgeIntent, BridgeDestinationObservation> {}
|
|
8
|
+
|
|
9
|
+
export interface ChainBridgeRelayInterface extends AsynchronousChainBridgeRelayInterface, SynchronousChainBridgeRelayInterface {}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Address } from '@xylabs/hex'
|
|
2
|
+
|
|
3
|
+
export interface IntentIndexerInterface<T> {
|
|
4
|
+
// TODO: Hex or string instead to handle alternative addressing schemes
|
|
5
|
+
getIntent(src: Address, nonce: string): Promise<T | null>
|
|
6
|
+
getIntents(src: Address): Promise<T[]>
|
|
7
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Address } from '@xylabs/hex'
|
|
2
|
+
|
|
3
|
+
export interface LockingProcessorInterface<T> {
|
|
4
|
+
isLocked(intent: T): Promise<Address | null>
|
|
5
|
+
lock(processor: Address, intent: T): Promise<boolean>
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface UnlockingProcessorInterface<T> extends LockingProcessorInterface<T> {
|
|
9
|
+
unlock(processor: Address, intent: T): Promise<boolean>
|
|
10
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
BridgeDestinationObservation, BridgeIntent, BridgeSourceObservation,
|
|
3
|
+
} from '@xyo-network/xl1-protocol'
|
|
4
|
+
|
|
5
|
+
export interface ObservationIndexerInterface<
|
|
6
|
+
TObservation extends BridgeSourceObservation | BridgeDestinationObservation,
|
|
7
|
+
TIntent extends BridgeIntent = BridgeIntent,
|
|
8
|
+
> {
|
|
9
|
+
addObservation(observation: TObservation, intent: TIntent): Promise<boolean>
|
|
10
|
+
getIntentForObservation(observation: TObservation): Promise<TIntent | null>
|
|
11
|
+
getObservationForIntent(intent: TIntent): Promise<TObservation | null>
|
|
12
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type BaseAccountableServiceParams } from '@xyo-network/chain-services'
|
|
2
|
+
import type {
|
|
3
|
+
BridgeDestinationObservation, BridgeIntent, BridgeSourceObservation,
|
|
4
|
+
} from '@xyo-network/xl1-protocol'
|
|
5
|
+
|
|
6
|
+
import type { ChainBridgeRelayInterface } from './ChainBridgeRelayInterface.ts'
|
|
7
|
+
import type { IntentIndexerInterface } from './IntentIndexerInterface.ts'
|
|
8
|
+
import type { LockingProcessorInterface, UnlockingProcessorInterface } from './LockingProcessorInterface.ts'
|
|
9
|
+
import type { ObservationIndexerInterface } from './ObservationIndexerInterface.ts'
|
|
10
|
+
|
|
11
|
+
export interface BridgeIntentIndexerInterface extends IntentIndexerInterface<BridgeIntent> {}
|
|
12
|
+
export interface BridgeSourceObservationIndexerInterface extends ObservationIndexerInterface<BridgeSourceObservation> {}
|
|
13
|
+
export interface BridgeDestinationObservationIndexerInterface extends ObservationIndexerInterface<BridgeDestinationObservation> {}
|
|
14
|
+
export interface LockingBridgeIntentProcessorInterface extends LockingProcessorInterface<BridgeIntent> {}
|
|
15
|
+
export interface UnlockingBridgeIntentProcessorInterface extends UnlockingProcessorInterface<BridgeIntent> {}
|
|
16
|
+
|
|
17
|
+
export type BridgeServiceCollection = {
|
|
18
|
+
destinationObservations: BridgeDestinationObservationIndexerInterface
|
|
19
|
+
destinationRelay: ChainBridgeRelayInterface
|
|
20
|
+
intentProcessed: LockingBridgeIntentProcessorInterface
|
|
21
|
+
intentProcessing: UnlockingBridgeIntentProcessorInterface
|
|
22
|
+
intents: BridgeIntentIndexerInterface
|
|
23
|
+
sourceObservations: BridgeSourceObservationIndexerInterface
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export type BridgeServiceParams = BaseAccountableServiceParams & BridgeServiceCollection
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Address } from '@xylabs/hex'
|
|
2
|
+
import type {
|
|
3
|
+
BridgeDestinationObservation, BridgeIntent, BridgeSourceObservation,
|
|
4
|
+
} from '@xyo-network/xl1-protocol'
|
|
5
|
+
|
|
6
|
+
export interface IntentRepository {
|
|
7
|
+
getBridgeIntents(src: Address, nonce?: string): Promise<BridgeIntent[]>
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface SourceObservationRepository {
|
|
11
|
+
getBridgeSourceObservation(intent: BridgeIntent): Promise<BridgeSourceObservation[]>
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface DestinationObservationRepository {
|
|
15
|
+
getBridgeDestinationObservation(intent: BridgeIntent): Promise<BridgeDestinationObservation[]>
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface IntentProcessingRepository {
|
|
19
|
+
isIntentProcessing(intent: BridgeIntent): Promise<boolean>
|
|
20
|
+
markIntentProcessed(intent: BridgeIntent): Promise<void>
|
|
21
|
+
markIntentProcessing(intent: BridgeIntent): Promise<void>
|
|
22
|
+
unmarkIntentProcessing(intent: BridgeIntent): Promise<void>
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface IntentProcessedRepository {
|
|
26
|
+
isIntentProcessed(intent: BridgeIntent): Promise<boolean>
|
|
27
|
+
markIntentProcessed(intent: BridgeIntent): Promise<void>
|
|
28
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './RepositoryInterface.ts'
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { BridgeDestinationObservation, BridgeIntent } from '@xyo-network/xl1-protocol'
|
|
2
|
+
|
|
3
|
+
import type { AsynchronousRelayInterface, SynchronousRelayInterface } from '../../interface/index.ts'
|
|
4
|
+
|
|
5
|
+
export interface BlockingChainBridgeRelay extends SynchronousRelayInterface<BridgeIntent, BridgeDestinationObservation> {}
|
|
6
|
+
|
|
7
|
+
export interface ChainBridgeRelay extends AsynchronousRelayInterface<BridgeIntent, BridgeDestinationObservation> {}
|
|
8
|
+
|
|
9
|
+
export interface ChainBridgeRelayInterface extends
|
|
10
|
+
AsynchronousRelayInterface<BridgeIntent, BridgeDestinationObservation>,
|
|
11
|
+
SynchronousRelayInterface<BridgeIntent, BridgeDestinationObservation> {}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { assertEx } from '@xylabs/assert'
|
|
2
|
+
import { isNull } from '@xylabs/typeof'
|
|
3
|
+
import { BaseAccountableService } from '@xyo-network/chain-services'
|
|
4
|
+
import type { BridgeDestinationObservation, BridgeIntent } from '@xyo-network/xl1-protocol'
|
|
5
|
+
|
|
6
|
+
import type {
|
|
7
|
+
BridgeDestinationObservationIndexerInterface, BridgeIntentIndexerInterface,
|
|
8
|
+
BridgeServiceParams,
|
|
9
|
+
BridgeSourceObservationIndexerInterface, ChainBridgeRelayInterface, LockingBridgeIntentProcessorInterface,
|
|
10
|
+
UnlockingBridgeIntentProcessorInterface,
|
|
11
|
+
} from '../../interface/index.ts'
|
|
12
|
+
|
|
13
|
+
export class ChainBridgeRelayService<TParams extends BridgeServiceParams = BridgeServiceParams>
|
|
14
|
+
extends BaseAccountableService<TParams> implements ChainBridgeRelayInterface {
|
|
15
|
+
protected get account() {
|
|
16
|
+
return assertEx(this.params.account, () => 'account is required')
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
protected get destinationObservations(): BridgeDestinationObservationIndexerInterface {
|
|
20
|
+
return assertEx(this.params.destinationObservations, () => 'destinationObservations is required')
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
protected get destinationRelay(): ChainBridgeRelayInterface {
|
|
24
|
+
return assertEx(this.params.destinationRelay, () => 'destinationRelay is required')
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
protected get intentProcessed(): LockingBridgeIntentProcessorInterface {
|
|
28
|
+
return assertEx(this.params.intentProcessed, () => 'intentProcessed is required')
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
protected get intentProcessing(): UnlockingBridgeIntentProcessorInterface {
|
|
32
|
+
return assertEx(this.params.intentProcessing, () => 'intentProcessing is required')
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
protected get intentResourceAccess(): BridgeIntentIndexerInterface {
|
|
36
|
+
return assertEx(this.params.intents, () => 'intents is required')
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
protected get sourceObservations(): BridgeSourceObservationIndexerInterface {
|
|
40
|
+
return assertEx(this.params.sourceObservations, () => 'sourceObservations is required')
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Begins the relay process for a given BridgeIntent.
|
|
45
|
+
* @param bridgeIntent The bridgeIntent to begin relaying
|
|
46
|
+
* @returns True if the relay was started, false otherwise
|
|
47
|
+
*/
|
|
48
|
+
async beginRelay(bridgeIntent: BridgeIntent): Promise<boolean> {
|
|
49
|
+
try {
|
|
50
|
+
// Ensure source observation exists
|
|
51
|
+
const bridgeSourceObservation = await this.sourceObservations.getObservationForIntent(bridgeIntent)
|
|
52
|
+
if (!isNull(bridgeSourceObservation)) {
|
|
53
|
+
const canProcess = await this.intentProcessing.lock(this.account.address, bridgeIntent)
|
|
54
|
+
if (canProcess) {
|
|
55
|
+
await this.destinationRelay.beginRelay(bridgeIntent)
|
|
56
|
+
return true
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
} catch {
|
|
60
|
+
await this.intentProcessing.unlock(this.account.address, bridgeIntent)
|
|
61
|
+
}
|
|
62
|
+
return false
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Handles a BridgeDestinationObservation.
|
|
67
|
+
* @param bridgeDestinationObservation The BridgeDestinationObservation to process
|
|
68
|
+
* @returns True if the observation was processed as a completion for an outstanding relay, false otherwise
|
|
69
|
+
*/
|
|
70
|
+
async onDestinationObservation(bridgeDestinationObservation: BridgeDestinationObservation): Promise<boolean> {
|
|
71
|
+
// Ensure intent exists
|
|
72
|
+
const bridgeIntent = await this.destinationObservations.getIntentForObservation(bridgeDestinationObservation)
|
|
73
|
+
if (!isNull(bridgeIntent)) {
|
|
74
|
+
// Ensure not already processed
|
|
75
|
+
const processed = await this.intentProcessed.isLocked(bridgeIntent)
|
|
76
|
+
if (isNull(processed)) {
|
|
77
|
+
// Ensure we are the processor
|
|
78
|
+
const processor = await this.intentProcessing.isLocked(bridgeIntent)
|
|
79
|
+
if (!isNull(processor) && processor === this.account.address) {
|
|
80
|
+
// Mark as completed
|
|
81
|
+
await this.intentProcessed.lock(this.account.address, bridgeIntent)
|
|
82
|
+
await this.intentProcessing.unlock(this.account.address, bridgeIntent)
|
|
83
|
+
return true
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return false
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Relays a given BridgeIntent.
|
|
92
|
+
* @param bridgeIntent The BridgeIntent to relay
|
|
93
|
+
* @returns Relays the intent in a blocking manner, returning the resulting BridgeDestinationObservation if successful
|
|
94
|
+
*/
|
|
95
|
+
async relaySync(bridgeIntent: BridgeIntent): Promise<BridgeDestinationObservation | null> {
|
|
96
|
+
let canProcess = isNull(await this.intentProcessing.isLocked(bridgeIntent))
|
|
97
|
+
try {
|
|
98
|
+
canProcess = await this.intentProcessing.lock(this.account.address, bridgeIntent)
|
|
99
|
+
if (canProcess) {
|
|
100
|
+
const result = await this.destinationRelay.relaySync(bridgeIntent)
|
|
101
|
+
if (!isNull(result)) {
|
|
102
|
+
// TODO: How to handle partial success here where relay succeeded but we didn't record result successfully?
|
|
103
|
+
// Prefer async observer pattern instead?
|
|
104
|
+
await this.destinationObservations.addObservation(result, bridgeIntent)
|
|
105
|
+
await this.intentProcessed.lock(this.account.address, bridgeIntent)
|
|
106
|
+
return result
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
} finally {
|
|
110
|
+
if (!isNull(bridgeIntent)) {
|
|
111
|
+
await this.intentProcessing.unlock(this.account.address, bridgeIntent)
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return null
|
|
115
|
+
}
|
|
116
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './ChainBridgeRelayService.ts'
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
import { toAddress, toHex } from '@xylabs/hex'
|
|
2
|
+
import { HDWallet } from '@xyo-network/wallet'
|
|
3
|
+
import type { WalletInstance } from '@xyo-network/wallet-model'
|
|
4
|
+
import type {
|
|
5
|
+
BridgeDestinationObservation, BridgeIntent, BridgeSourceObservation, Chain,
|
|
6
|
+
} from '@xyo-network/xl1-protocol'
|
|
7
|
+
import {
|
|
8
|
+
AttoXL1ConvertFactor, BridgeDestinationObservationSchema, BridgeIntentSchema,
|
|
9
|
+
BridgeSourceObservationSchema,
|
|
10
|
+
} from '@xyo-network/xl1-protocol'
|
|
11
|
+
import type { Config } from '@xyo-network/xl1-protocol-sdk'
|
|
12
|
+
import { getDefaultConfig } from '@xyo-network/xl1-protocol-sdk'
|
|
13
|
+
import {
|
|
14
|
+
beforeAll, describe, expect, it, vi,
|
|
15
|
+
} from 'vitest'
|
|
16
|
+
|
|
17
|
+
import type {
|
|
18
|
+
BridgeDestinationObservationIndexerInterface, BridgeIntentIndexerInterface, BridgeServiceParams,
|
|
19
|
+
BridgeSourceObservationIndexerInterface,
|
|
20
|
+
ChainBridgeRelayInterface,
|
|
21
|
+
LockingBridgeIntentProcessorInterface, UnlockingBridgeIntentProcessorInterface,
|
|
22
|
+
} from '../../../interface/index.ts'
|
|
23
|
+
import { ChainBridgeRelayService } from '../ChainBridgeRelayService.ts'
|
|
24
|
+
|
|
25
|
+
describe('ChainBridgeRelayService', () => {
|
|
26
|
+
let account: WalletInstance
|
|
27
|
+
let config: Config
|
|
28
|
+
let destinationObservations: BridgeDestinationObservationIndexerInterface
|
|
29
|
+
let destinationRelay: ChainBridgeRelayInterface
|
|
30
|
+
let intentProcessed: LockingBridgeIntentProcessorInterface
|
|
31
|
+
let intentProcessing: UnlockingBridgeIntentProcessorInterface
|
|
32
|
+
let intents: BridgeIntentIndexerInterface
|
|
33
|
+
let sourceObservations: BridgeSourceObservationIndexerInterface
|
|
34
|
+
let relay: ChainBridgeRelayService
|
|
35
|
+
|
|
36
|
+
const srcAmount = toHex(100n * AttoXL1ConvertFactor.xl1) // 100 XL1 in AttoXL1
|
|
37
|
+
const destAmount = srcAmount // 1:1 for test
|
|
38
|
+
|
|
39
|
+
const xl1ChainId: Chain = toHex('dd381fbb392c85160d8b0453e446757b12384046')
|
|
40
|
+
const ethChainId = toHex('0x1')
|
|
41
|
+
|
|
42
|
+
const xl1Address = toAddress('1111111111111111111111111111111111111111')
|
|
43
|
+
const ethAddress = toAddress('0x2222222222222222222222222222222222222222')
|
|
44
|
+
|
|
45
|
+
const bridgeableTokenContract = toHex('0x3333333333333333333333333333333333333333')
|
|
46
|
+
|
|
47
|
+
const nonce = 'd5eeff33-d5bc-4aca-9ecb-3406c02a5dc4'
|
|
48
|
+
|
|
49
|
+
const xl1TxHash = toHex('0x4444444444444444444444444444444444444444444444444444444444444444') // Some XL1 tx hash
|
|
50
|
+
const ethTxHash = toHex('0x5555555555555555555555555555555555555555555555555555555555555555') // Some Eth tx hash
|
|
51
|
+
|
|
52
|
+
const intent: BridgeIntent = {
|
|
53
|
+
// Source
|
|
54
|
+
src: xl1ChainId, // From XL1
|
|
55
|
+
srcAddress: xl1Address, // From XL1 sender
|
|
56
|
+
srcAmount,
|
|
57
|
+
srcToken: xl1ChainId, // In XL1
|
|
58
|
+
|
|
59
|
+
// Destination
|
|
60
|
+
dest: ethChainId, // To Ethereum
|
|
61
|
+
destAddress: ethAddress,
|
|
62
|
+
destAmount,
|
|
63
|
+
destToken: bridgeableTokenContract,
|
|
64
|
+
|
|
65
|
+
// Details
|
|
66
|
+
nonce,
|
|
67
|
+
|
|
68
|
+
schema: BridgeIntentSchema,
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const srcObservation: BridgeSourceObservation = {
|
|
72
|
+
// Source
|
|
73
|
+
src: xl1ChainId, // From XL1
|
|
74
|
+
srcAddress: xl1Address, // From XL1 sender
|
|
75
|
+
srcAmount,
|
|
76
|
+
srcToken: xl1ChainId, // In XL1
|
|
77
|
+
|
|
78
|
+
// Destination
|
|
79
|
+
dest: ethChainId, // To Ethereum
|
|
80
|
+
destAddress: ethAddress,
|
|
81
|
+
destAmount,
|
|
82
|
+
destToken: bridgeableTokenContract,
|
|
83
|
+
|
|
84
|
+
// Observation
|
|
85
|
+
srcConfirmation: xl1TxHash,
|
|
86
|
+
|
|
87
|
+
schema: BridgeSourceObservationSchema,
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const destObservation: BridgeDestinationObservation = {
|
|
91
|
+
// Source
|
|
92
|
+
src: xl1ChainId, // From XL1
|
|
93
|
+
srcAddress: xl1Address, // From XL1 sender
|
|
94
|
+
srcAmount,
|
|
95
|
+
srcToken: xl1ChainId, // In XL1
|
|
96
|
+
|
|
97
|
+
// Destination
|
|
98
|
+
dest: ethChainId, // To Ethereum
|
|
99
|
+
destAddress: ethAddress,
|
|
100
|
+
destAmount,
|
|
101
|
+
destToken: bridgeableTokenContract,
|
|
102
|
+
|
|
103
|
+
// Observation
|
|
104
|
+
destConfirmation: ethTxHash,
|
|
105
|
+
|
|
106
|
+
schema: BridgeDestinationObservationSchema,
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
beforeAll(async () => {
|
|
110
|
+
account = await HDWallet.random()
|
|
111
|
+
config = getDefaultConfig()
|
|
112
|
+
|
|
113
|
+
intents = {
|
|
114
|
+
getIntent: vi.fn().mockResolvedValue(null),
|
|
115
|
+
getIntents: vi.fn().mockResolvedValue([]),
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
sourceObservations = {
|
|
119
|
+
addObservation: vi.fn().mockResolvedValue(true),
|
|
120
|
+
getIntentForObservation: vi.fn().mockResolvedValue(null),
|
|
121
|
+
getObservationForIntent: vi.fn().mockResolvedValue(null),
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
destinationObservations = {
|
|
125
|
+
addObservation: vi.fn().mockResolvedValue(true),
|
|
126
|
+
getIntentForObservation: vi.fn().mockResolvedValue(null),
|
|
127
|
+
getObservationForIntent: vi.fn().mockResolvedValue(null),
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
intentProcessed = {
|
|
131
|
+
isLocked: vi.fn().mockResolvedValue(null),
|
|
132
|
+
lock: vi.fn().mockResolvedValue(true),
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
intentProcessing = {
|
|
136
|
+
isLocked: vi.fn().mockResolvedValue(null),
|
|
137
|
+
lock: vi.fn().mockResolvedValue(true),
|
|
138
|
+
unlock: vi.fn().mockResolvedValue(true),
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
destinationRelay = {
|
|
142
|
+
beginRelay: vi.fn().mockResolvedValue(true),
|
|
143
|
+
relaySync: vi.fn().mockResolvedValue(null),
|
|
144
|
+
onDestinationObservation: vi.fn().mockResolvedValue(false),
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const params: BridgeServiceParams = {
|
|
148
|
+
account,
|
|
149
|
+
config,
|
|
150
|
+
destinationObservations,
|
|
151
|
+
destinationRelay,
|
|
152
|
+
intentProcessed,
|
|
153
|
+
intentProcessing,
|
|
154
|
+
intents,
|
|
155
|
+
sourceObservations,
|
|
156
|
+
}
|
|
157
|
+
relay = await ChainBridgeRelayService.create(params)
|
|
158
|
+
})
|
|
159
|
+
describe('beginRelay', () => {
|
|
160
|
+
it('should do nothing if source observation is null', async () => {
|
|
161
|
+
// Arrange
|
|
162
|
+
sourceObservations.getObservationForIntent = vi.fn().mockResolvedValue(null)
|
|
163
|
+
|
|
164
|
+
// Act
|
|
165
|
+
const result = await relay.beginRelay(intent)
|
|
166
|
+
|
|
167
|
+
// Assert
|
|
168
|
+
expect(intentProcessing.lock).not.toHaveBeenCalled()
|
|
169
|
+
expect(result).toBe(false)
|
|
170
|
+
})
|
|
171
|
+
|
|
172
|
+
it('should lock and call destinationRelay.beginRelay if observation exists', async () => {
|
|
173
|
+
// Arrange
|
|
174
|
+
sourceObservations.getObservationForIntent = vi.fn().mockResolvedValue(srcObservation)
|
|
175
|
+
intentProcessing.lock = vi.fn().mockResolvedValue(true)
|
|
176
|
+
destinationRelay.beginRelay = vi.fn().mockResolvedValue(true)
|
|
177
|
+
|
|
178
|
+
// Act
|
|
179
|
+
const result = await relay.beginRelay(intent)
|
|
180
|
+
|
|
181
|
+
// Assert
|
|
182
|
+
expect(sourceObservations.getObservationForIntent).toHaveBeenCalledWith(intent)
|
|
183
|
+
expect(intentProcessing.lock).toHaveBeenCalledWith(account.address, intent)
|
|
184
|
+
expect(destinationRelay.beginRelay).toHaveBeenCalledWith(intent)
|
|
185
|
+
expect(result).toBe(true)
|
|
186
|
+
})
|
|
187
|
+
|
|
188
|
+
it('should unlock if an exception is thrown', async () => {
|
|
189
|
+
// Arrange
|
|
190
|
+
sourceObservations.getObservationForIntent = vi.fn().mockResolvedValue(srcObservation)
|
|
191
|
+
intentProcessing.lock = vi.fn().mockResolvedValue(true)
|
|
192
|
+
destinationRelay.beginRelay = vi.fn().mockImplementation(() => {
|
|
193
|
+
throw new Error('boom')
|
|
194
|
+
})
|
|
195
|
+
intentProcessing.unlock = vi.fn()
|
|
196
|
+
|
|
197
|
+
// Act
|
|
198
|
+
const result = await relay.beginRelay(intent)
|
|
199
|
+
|
|
200
|
+
// Assert
|
|
201
|
+
expect(intentProcessing.unlock).toHaveBeenCalledWith(account.address, intent)
|
|
202
|
+
expect(result).toBe(false)
|
|
203
|
+
})
|
|
204
|
+
})
|
|
205
|
+
describe('onBridgeDestinationObservation', () => {
|
|
206
|
+
it('should return false if no intent exists for observation', async () => {
|
|
207
|
+
// Arrange
|
|
208
|
+
destinationObservations.getIntentForObservation = vi.fn().mockResolvedValue(null)
|
|
209
|
+
|
|
210
|
+
// Act
|
|
211
|
+
const result = await relay.onDestinationObservation(destObservation)
|
|
212
|
+
|
|
213
|
+
// Assert
|
|
214
|
+
expect(intentProcessed.isLocked).not.toHaveBeenCalled()
|
|
215
|
+
expect(result).toBe(false)
|
|
216
|
+
})
|
|
217
|
+
|
|
218
|
+
it('should return false if already processed', async () => {
|
|
219
|
+
// Arrange
|
|
220
|
+
const other = await HDWallet.random()
|
|
221
|
+
destinationObservations.getIntentForObservation = vi.fn().mockResolvedValue(intent)
|
|
222
|
+
intentProcessed.isLocked = vi.fn().mockResolvedValue(other.address)
|
|
223
|
+
|
|
224
|
+
// Act
|
|
225
|
+
const result = await relay.onDestinationObservation(destObservation)
|
|
226
|
+
|
|
227
|
+
// Assert
|
|
228
|
+
expect(intentProcessed.isLocked).toHaveBeenCalledWith(intent)
|
|
229
|
+
expect(result).toBe(false)
|
|
230
|
+
})
|
|
231
|
+
|
|
232
|
+
it('should return false if not the processor', async () => {
|
|
233
|
+
// Arrange
|
|
234
|
+
const other = await HDWallet.random()
|
|
235
|
+
destinationObservations.getIntentForObservation = vi.fn().mockResolvedValue(intent)
|
|
236
|
+
intentProcessed.isLocked = vi.fn().mockResolvedValue(null)
|
|
237
|
+
intentProcessing.isLocked = vi.fn().mockResolvedValue(other.address)
|
|
238
|
+
|
|
239
|
+
// Act
|
|
240
|
+
const result = await relay.onDestinationObservation(destObservation)
|
|
241
|
+
|
|
242
|
+
// Assert
|
|
243
|
+
expect(intentProcessing.isLocked).toHaveBeenCalledWith(intent)
|
|
244
|
+
expect(result).toBe(false)
|
|
245
|
+
})
|
|
246
|
+
|
|
247
|
+
it('should process if we are the processor', async () => {
|
|
248
|
+
// Arrange
|
|
249
|
+
destinationObservations.getIntentForObservation = vi.fn().mockResolvedValue(intent)
|
|
250
|
+
intentProcessed.isLocked = vi.fn().mockResolvedValue(null)
|
|
251
|
+
intentProcessing.isLocked = vi.fn().mockResolvedValue(account.address)
|
|
252
|
+
intentProcessed.lock = vi.fn().mockResolvedValue(true)
|
|
253
|
+
intentProcessing.unlock = vi.fn().mockResolvedValue(true)
|
|
254
|
+
|
|
255
|
+
// Act
|
|
256
|
+
const result = await relay.onDestinationObservation(destObservation)
|
|
257
|
+
|
|
258
|
+
// Assert
|
|
259
|
+
expect(intentProcessed.lock).toHaveBeenCalledWith(account.address, intent)
|
|
260
|
+
expect(intentProcessing.unlock).toHaveBeenCalledWith(account.address, intent)
|
|
261
|
+
expect(result).toBe(true)
|
|
262
|
+
})
|
|
263
|
+
})
|
|
264
|
+
})
|