Skip to content

Commit 3faea53

Browse files
Merge pull request #492 from autonomys/update-messenger
feat: improve XDM messenger developer experience
2 parents 8c566fa + 7a2ef07 commit 3faea53

File tree

4 files changed

+194
-13
lines changed

4 files changed

+194
-13
lines changed

integration-tests/helpers/xdm.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { setupWallet, signAndSendTx, type ApiPromise } from '@autonomys/auto-utils'
2-
import { chainAllowlist, channels, nextChannelId } from '@autonomys/auto-xdm'
2+
import { chainAllowlist, channels, initiateChannel, nextChannelId } from '@autonomys/auto-xdm'
33
import { waitForBlocks, waitUntil } from './chain'
44

55
/**
@@ -74,8 +74,7 @@ export const setupXDM = async (
7474

7575
if (!channelExists) {
7676
console.log(`Step 3/3: Initiating channel to domain ${domainId}`)
77-
// Use ChainId format (lowercase 'domain') for transaction
78-
const callInitiateChannel = consensusApi.tx.messenger.initiateChannel({ domain: domainId })
77+
const callInitiateChannel = initiateChannel(consensusApi, { domainId })
7978
await signAndSendTx(owner.keyringPair, callInitiateChannel, {}, [], false)
8079

8180
// Wait for channel to be open (similar to official Subspace test pattern)
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
import { setupWallet, signAndSendTx } from '@autonomys/auto-utils'
2+
import {
3+
chainAllowlist,
4+
channels,
5+
closeChannel,
6+
initiateChannel,
7+
nextChannelId,
8+
type Chain,
9+
} from '@autonomys/auto-xdm'
10+
import { cleanupChains, setupChains, setupXDM, waitUntil } from '../../helpers'
11+
12+
describe('XDM Messenger Functions', () => {
13+
let apis: Awaited<ReturnType<typeof setupChains>>
14+
let ownerWallet: ReturnType<typeof setupWallet>
15+
16+
beforeAll(async () => {
17+
apis = await setupChains()
18+
ownerWallet = setupWallet({ uri: '//Alice' })
19+
20+
if (!ownerWallet.keyringPair) {
21+
throw new Error('Owner keyring pair not initialized')
22+
}
23+
24+
// Setup XDM between consensus and domain before running tests
25+
await setupXDM(apis.consensus, apis.domain, 0)
26+
}, 300000) // Increased timeout to allow chains to start and XDM to be set up
27+
28+
afterAll(async () => {
29+
await cleanupChains(apis)
30+
}, 10000)
31+
32+
describe('initiateChannel()', () => {
33+
test('should successfully initiate and open a channel', async () => {
34+
// Verify destination is in allowlist first
35+
const allowlist = await chainAllowlist(apis.consensus)
36+
const canOpenToDomain0 = allowlist.some(
37+
(chain) => chain !== 'consensus' && chain.domainId === 0,
38+
)
39+
expect(canOpenToDomain0).toBe(true)
40+
41+
// Check current nextChannelId to find what channel ID will be created
42+
const nextIdBefore = await nextChannelId(apis.consensus, { domainId: 0 })
43+
const expectedChannelId = Number(nextIdBefore.toString())
44+
45+
// Create and submit transaction using wrapper function
46+
const destination: Chain = { domainId: 0 }
47+
const tx = initiateChannel(apis.consensus, destination)
48+
49+
// Submit the transaction
50+
await signAndSendTx(ownerWallet.keyringPair!, tx, {}, [], false)
51+
52+
// Wait for channel to open
53+
await waitUntil(async () => {
54+
const channel = await channels(apis.consensus, { domainId: 0 }, expectedChannelId)
55+
return channel !== null && channel.state === 'Open'
56+
})
57+
58+
// Verify channel was created and is open
59+
const channel = await channels(apis.consensus, { domainId: 0 }, expectedChannelId)
60+
expect(channel).not.toBeNull()
61+
expect(channel?.state).toBe('Open')
62+
}, 300000)
63+
})
64+
65+
describe('closeChannel()', () => {
66+
test('should successfully close an open channel', async () => {
67+
const nextId = await nextChannelId(apis.consensus, { domainId: 0 })
68+
const channelCount = Number(nextId.toString())
69+
70+
// If no channels exist, skip test
71+
if (channelCount === 0) {
72+
console.log('No channels exist to close, skipping test')
73+
return
74+
}
75+
76+
// Find the first open channel
77+
let channelIdToClose: number | null = null
78+
for (let i = 0; i < channelCount; i++) {
79+
const channel = await channels(apis.consensus, { domainId: 0 }, i)
80+
if (channel && channel.state === 'Open') {
81+
channelIdToClose = i
82+
break
83+
}
84+
}
85+
86+
if (channelIdToClose === null) {
87+
console.log('No open channels found to close, skipping test')
88+
return
89+
}
90+
91+
// Close the channel using wrapper function
92+
const destination: Chain = { domainId: 0 }
93+
const tx = closeChannel(apis.consensus, destination, channelIdToClose)
94+
await signAndSendTx(ownerWallet.keyringPair!, tx, {}, [], false)
95+
96+
// Wait for channel to close
97+
await waitUntil(async () => {
98+
const channel = await channels(apis.consensus, { domainId: 0 }, channelIdToClose!)
99+
return channel?.state === 'Closed'
100+
})
101+
102+
// Verify channel is closed
103+
const channelAfter = await channels(apis.consensus, { domainId: 0 }, channelIdToClose)
104+
expect(channelAfter).not.toBeNull()
105+
expect(channelAfter?.state).toBe('Closed')
106+
}, 300000)
107+
})
108+
})

packages/auto-xdm/src/messenger.ts

Lines changed: 81 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,85 @@
1-
// file: src/transfer.ts
1+
import type { ApiPromise, ISubmittableResult, SubmittableExtrinsic } from '@autonomys/auto-utils'
2+
import { chainToChainIdCodec } from './transforms'
3+
import type { Chain } from './types'
24

3-
import type { ApiPromise, Codec } from '@autonomys/auto-utils'
4-
5-
export const initiateChannel = async (api: ApiPromise, destination: Codec) => {
6-
return await api.tx.messenger.initiateChannel(destination)
5+
/**
6+
* Creates a transaction to initiate a channel to a destination chain.
7+
*
8+
* This function creates a transaction but does not submit it. The returned transaction
9+
* must be signed and sent using `signAndSendTx` or similar methods.
10+
*
11+
* Before initiating a channel, ensure the destination chain is in the allowlist.
12+
* Use `chainAllowlist()` to check which chains can open channels with the current chain.
13+
*
14+
* @param api - The API instance for the source chain
15+
* @param destination - The destination chain: 'consensus' or { domainId: number }
16+
* @returns A transaction that can be signed and submitted
17+
*
18+
* @example
19+
* ```typescript
20+
* // Initiate channel to consensus chain
21+
* const tx = initiateChannel(api, 'consensus')
22+
* await signAndSendTx(keyringPair, tx, {}, [], false)
23+
*
24+
* // Initiate channel to domain 0
25+
* const tx = initiateChannel(api, { domainId: 0 })
26+
* await signAndSendTx(keyringPair, tx, {}, [], false)
27+
*
28+
* // Check if destination is allowed before initiating
29+
* const allowlist = await chainAllowlist(api)
30+
* const canOpenToDomain0 = allowlist.some(chain => chain !== 'consensus' && chain.domainId === 0)
31+
* if (canOpenToDomain0) {
32+
* const tx = initiateChannel(api, { domainId: 0 })
33+
* await signAndSendTx(keyringPair, tx, {}, [], false)
34+
* }
35+
* ```
36+
*/
37+
export const initiateChannel = (
38+
api: ApiPromise,
39+
destination: Chain,
40+
): SubmittableExtrinsic<'promise', ISubmittableResult> => {
41+
const destinationChainId = chainToChainIdCodec(api, destination)
42+
return api.tx.messenger.initiateChannel(destinationChainId)
743
}
844

9-
export const closeChannel = async (api: ApiPromise, chainId: Codec, channelId: string) => {
10-
return await api.tx.messenger.closeChannel(chainId, channelId)
45+
/**
46+
* Creates a transaction to close an existing channel to a destination chain.
47+
*
48+
* This function creates a transaction but does not submit it. The returned transaction
49+
* must be signed and sent using `signAndSendTx` or similar methods.
50+
*
51+
* Closing a channel stops further message exchanges on that channel. You can query
52+
* existing channels using `channels()` to find the channel ID to close.
53+
*
54+
* @param api - The API instance for the chain where the channel exists
55+
* @param destination - The destination chain of the channel: 'consensus' or { domainId: number }
56+
* @param channelId - The channel ID to close (number, string, or bigint)
57+
* @returns A transaction that can be signed and submitted
58+
*
59+
* @example
60+
* ```typescript
61+
* // Close channel 0 to domain 0
62+
* const tx = closeChannel(api, { domainId: 0 }, 0)
63+
* await signAndSendTx(keyringPair, tx, {}, [], false)
64+
*
65+
* // Close channel 1 to consensus chain
66+
* const tx = closeChannel(api, 'consensus', 1)
67+
* await signAndSendTx(keyringPair, tx, {}, [], false)
68+
*
69+
* // Query channel before closing to verify it exists
70+
* const channel = await channels(api, { domainId: 0 }, 0)
71+
* if (channel && channel.state === 'Open') {
72+
* const tx = closeChannel(api, { domainId: 0 }, 0)
73+
* await signAndSendTx(keyringPair, tx, {}, [], false)
74+
* }
75+
* ```
76+
*/
77+
export const closeChannel = (
78+
api: ApiPromise,
79+
destination: Chain,
80+
channelId: number | string | bigint,
81+
): SubmittableExtrinsic<'promise', ISubmittableResult> => {
82+
const destinationChainId = chainToChainIdCodec(api, destination)
83+
const channelIdCodec = api.createType('U256', channelId)
84+
return api.tx.messenger.closeChannel(destinationChainId, channelIdCodec)
1185
}

packages/auto-xdm/src/transfer.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
// file: src/transfer.ts
2-
31
import type { StringNumberOrBigInt } from '@autonomys/auto-consensus'
42
import {
53
ApiPromise,
@@ -8,6 +6,8 @@ import {
86
createDomainsChainIdType,
97
createMultiAccountIdType,
108
createTransporterLocationType,
9+
type ISubmittableResult,
10+
type SubmittableExtrinsic,
1111
} from '@autonomys/auto-utils'
1212
import type { Chain, TransferAccount } from './types'
1313

@@ -40,7 +40,7 @@ export const transporterTransfer = (
4040
destination: Chain,
4141
account: TransferAccount,
4242
amount: StringNumberOrBigInt,
43-
) => {
43+
): SubmittableExtrinsic<'promise', ISubmittableResult> => {
4444
// Create chain ID codec
4545
const chainId =
4646
destination === 'consensus'

0 commit comments

Comments
 (0)