Skip to content

Commit 665cfbe

Browse files
authored
Merge branch 'main' into release-agents-v1.7.0
2 parents 08d04af + 2ed21c9 commit 665cfbe

33 files changed

Lines changed: 991 additions & 215 deletions

.changeset/many-clouds-decide.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@hyperlane-xyz/utils": minor
3+
---
4+
5+
Fix bug in the address detection logic that prevented radix localnet addresses to be recognized as valid radix addresses

.changeset/pink-beds-act.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
"@hyperlane-xyz/cosmos-sdk": minor
3+
"@hyperlane-xyz/radix-sdk": minor
4+
"@hyperlane-xyz/utils": minor
5+
"@hyperlane-xyz/cli": minor
6+
"@hyperlane-xyz/sdk": minor
7+
---
8+
9+
add new methods for altvm interface

.github/workflows/test.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,10 @@ jobs:
376376
test:
377377
# Core Commands
378378
- core-deploy
379+
# Warp Commands
380+
- warp-deploy
381+
- warp-apply-ownership-updates
382+
- warp-apply-route-extension
379383
steps:
380384
- uses: actions/checkout@v5
381385
with:

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ To read more about interchain applications, how the protocol works, and how to i
3030

3131
#### Install `jq`
3232

33-
You need `jq` installed on your machine. You can download it from [official page](https://jqlang.github.io/jq/download/) or use a package manager of your choice.
33+
You need `jq` installed on your machine. You can download it from [official page](https://jqlang.org/download/) or use a package manager of your choice.
3434

3535
#### Install `gitleaks`
3636

@@ -52,7 +52,7 @@ Then run `foundryup` to install `forge`, `cast`, `anvil` and `chisel`.
5252
foundryup
5353
```
5454

55-
Check out the [Foundry Book](https://book.getfoundry.sh/getting-started/installation) for more information.
55+
Check out the [Foundry Book](https://getfoundry.sh/introduction/installation/) for more information.
5656

5757
#### Node
5858

typescript/cli/src/context/altvm.ts

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -203,20 +203,10 @@ export class AltVMSignerFactory
203203
for (const chain of chains) {
204204
const metadata = metadataManager.getChainMetadata(chain);
205205

206-
if (metadata.protocol === ProtocolType.Ethereum) {
207-
continue;
208-
}
209-
210-
if (metadata.protocol === ProtocolType.Sealevel) {
211-
continue;
212-
}
213-
214206
const protocol = ALT_VM_SUPPORTED_PROTOCOLS[metadata.protocol];
215207

216208
if (!protocol) {
217-
throw new Error(
218-
`Chain ${chain} with protocol type ${metadata.protocol} not supported in AltVM`,
219-
);
209+
continue;
220210
}
221211

222212
const privateKey = await AltVMSignerFactory.loadPrivateKey(
@@ -245,10 +235,7 @@ export class AltVMSignerFactory
245235

246236
const factories: ProtocolMap<Record<string, SubmitterFactory>> = {};
247237

248-
if (
249-
protocol === ProtocolType.Ethereum ||
250-
protocol === ProtocolType.Sealevel
251-
) {
238+
if (!ALT_VM_SUPPORTED_PROTOCOLS[protocol]) {
252239
return factories;
253240
}
254241

typescript/cli/src/tests/nodes.ts

Lines changed: 6 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@ import {
66
} from 'testcontainers';
77
import { fileURLToPath } from 'url';
88

9-
import { RadixSigner } from '@hyperlane-xyz/radix-sdk';
10-
import { assert, retryAsync, sleep } from '@hyperlane-xyz/utils';
9+
import { assert, sleep } from '@hyperlane-xyz/utils';
1110

12-
import { HYP_KEY_BY_PROTOCOL, TestChainMetadata } from './constants.js';
11+
import { TestChainMetadata } from './constants.js';
12+
import { deployHyperlaneRadixPackageDefinition } from './radix/utils.js';
1313

1414
const __filename = fileURLToPath(import.meta.url);
1515
const __dirname = dirname(__filename);
@@ -93,32 +93,10 @@ export async function runRadixNode(
9393
console.log(`Waiting on the gateway API to sync for ${chainMetadata.name}`);
9494
await sleep(10_000);
9595

96-
// Adding dummy package address to avoid the signer crashing because
97-
// no Hyperlane package is deployed on the new node
98-
chainMetadata.packageAddress = 'not-yet-deployed';
99-
const signer = (await RadixSigner.connectWithSigner(
100-
chainMetadata.rpcUrls.map((rpc) => rpc.http),
101-
HYP_KEY_BY_PROTOCOL.radix,
102-
{
103-
metadata: chainMetadata,
104-
},
105-
)) as RadixSigner;
106-
107-
// Fund the account with the internal signer
108-
// Use retryAsync to handle transient errors (e.g., epoch expiry)
109-
await retryAsync(
110-
() => signer['signer'].getTestnetXrd(),
111-
3, // attempts
112-
1000, // base retry delay (1 second)
113-
);
114-
console.log(
115-
`Funded test account on ${chainMetadata.name} before publishing the hyperlane package`,
96+
await deployHyperlaneRadixPackageDefinition(
97+
chainMetadata,
98+
hyperlanePackageArtifacts,
11699
);
117-
const packageAddress = await signer.publishPackage({
118-
code: hyperlanePackageArtifacts.code,
119-
packageDefinition: hyperlanePackageArtifacts.packageDefinition,
120-
});
121-
chainMetadata.packageAddress = packageAddress;
122100

123101
return environment;
124102
}

typescript/cli/src/tests/radix/e2e-test.setup.ts

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import {
1212
} from '../constants.js';
1313
import { runRadixNode } from '../nodes.js';
1414

15+
import { deployHyperlaneRadixPackageDefinition } from './utils.js';
16+
1517
const HYPERLANE_RADIX_GIT = 'https://github.com/hyperlane-xyz/hyperlane-radix';
1618
const HYPERLANE_RADIX_VERSION = '1.1.0';
1719

@@ -74,11 +76,25 @@ before(async function () {
7476
packageDefinition: new Uint8Array(packageDefinition),
7577
});
7678

77-
// Write back to registry file so CLI can read the package address field injected
78-
// when starting the node
79-
const metadataPath = TEST_CHAIN_METADATA_PATH_BY_PROTOCOL.radix.CHAIN_NAME_1;
80-
const updatedMetadata = TEST_CHAIN_METADATA_BY_PROTOCOL.radix.CHAIN_NAME_1;
81-
writeYamlOrJson(metadataPath, updatedMetadata);
79+
const t = Object.keys(
80+
TEST_CHAIN_METADATA_PATH_BY_PROTOCOL.radix,
81+
) as (keyof typeof TEST_CHAIN_METADATA_PATH_BY_PROTOCOL.radix)[];
82+
83+
for (const chain of t) {
84+
await deployHyperlaneRadixPackageDefinition(
85+
TEST_CHAIN_METADATA_BY_PROTOCOL.radix[chain],
86+
{
87+
code: new Uint8Array(code),
88+
packageDefinition: new Uint8Array(packageDefinition),
89+
},
90+
);
91+
92+
// Write back to registry file so CLI can read the package address field injected
93+
// when starting the node
94+
const metadataPath = TEST_CHAIN_METADATA_PATH_BY_PROTOCOL.radix[chain];
95+
const updatedMetadata = TEST_CHAIN_METADATA_BY_PROTOCOL.radix[chain];
96+
writeYamlOrJson(metadataPath, updatedMetadata);
97+
}
8298
});
8399

84100
// Reset the test registry for each test invocation
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { RadixSigner } from '@hyperlane-xyz/radix-sdk';
2+
import { retryAsync } from '@hyperlane-xyz/utils';
3+
4+
import { HYP_KEY_BY_PROTOCOL, TestChainMetadata } from '../constants.js';
5+
6+
export async function deployHyperlaneRadixPackageDefinition(
7+
chainMetadata: TestChainMetadata,
8+
hyperlanePackageArtifacts: {
9+
code: Uint8Array;
10+
packageDefinition: Uint8Array;
11+
},
12+
) {
13+
// Adding dummy package address to avoid the signer crashing because
14+
// no Hyperlane package is deployed on the new node
15+
chainMetadata.packageAddress = 'not-yet-deployed';
16+
const signer = (await RadixSigner.connectWithSigner(
17+
chainMetadata.rpcUrls.map((rpc) => rpc.http),
18+
HYP_KEY_BY_PROTOCOL.radix,
19+
{
20+
metadata: chainMetadata,
21+
},
22+
)) as RadixSigner;
23+
24+
// Fund the account with the internal signer
25+
// Use retryAsync to handle transient errors (e.g., epoch expiry)
26+
await retryAsync(
27+
() => signer['signer'].getTestnetXrd(),
28+
3, // attempts
29+
1000, // base retry delay (1 second)
30+
);
31+
console.log(
32+
`Funded test account on ${chainMetadata.name} before publishing the hyperlane package`,
33+
);
34+
const packageAddress = await signer.publishPackage({
35+
code: hyperlanePackageArtifacts.code,
36+
packageDefinition: hyperlanePackageArtifacts.packageDefinition,
37+
});
38+
chainMetadata.packageAddress = packageAddress;
39+
}
Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
import { expect } from 'chai';
2+
3+
import { ChainAddresses } from '@hyperlane-xyz/registry';
4+
import {
5+
DerivedWarpRouteDeployConfig,
6+
TokenType,
7+
WarpRouteDeployConfig,
8+
} from '@hyperlane-xyz/sdk';
9+
import { Address, ProtocolType, assert } from '@hyperlane-xyz/utils';
10+
11+
import { readYamlOrJson, writeYamlOrJson } from '../../../utils/files.js';
12+
import { HyperlaneE2ECoreTestCommands } from '../../commands/core.js';
13+
import { HyperlaneE2EWarpTestCommands } from '../../commands/warp.js';
14+
import {
15+
BURN_ADDRESS_BY_PROTOCOL,
16+
CORE_CONFIG_PATH_BY_PROTOCOL,
17+
CORE_READ_CONFIG_PATH_BY_PROTOCOL,
18+
DEFAULT_E2E_TEST_TIMEOUT,
19+
HYP_DEPLOYER_ADDRESS_BY_PROTOCOL,
20+
HYP_KEY_BY_PROTOCOL,
21+
REGISTRY_PATH,
22+
TEST_CHAIN_METADATA_BY_PROTOCOL,
23+
TEST_CHAIN_NAMES_BY_PROTOCOL,
24+
WARP_READ_OUTPUT_PATH,
25+
getWarpCoreConfigPath,
26+
getWarpDeployConfigPath,
27+
getWarpId,
28+
} from '../../constants.js';
29+
30+
describe('hyperlane warp apply (Radix E2E tests)', async function () {
31+
this.timeout(DEFAULT_E2E_TEST_TIMEOUT);
32+
33+
const nativeTokenData =
34+
TEST_CHAIN_METADATA_BY_PROTOCOL.radix.CHAIN_NAME_1.nativeToken;
35+
assert(
36+
nativeTokenData,
37+
`Expected native token data to be defined for chain ${TEST_CHAIN_NAMES_BY_PROTOCOL.radix.CHAIN_NAME_1}`,
38+
);
39+
const nativeTokenAddress = nativeTokenData.denom;
40+
assert(
41+
nativeTokenAddress,
42+
`Expected native token address to be defined for ${TEST_CHAIN_NAMES_BY_PROTOCOL.radix.CHAIN_NAME_1}`,
43+
);
44+
45+
let chain1CoreAddress: ChainAddresses;
46+
const hyperlaneCore1 = new HyperlaneE2ECoreTestCommands(
47+
ProtocolType.Radix,
48+
TEST_CHAIN_NAMES_BY_PROTOCOL.radix.CHAIN_NAME_1,
49+
REGISTRY_PATH,
50+
CORE_CONFIG_PATH_BY_PROTOCOL.radix,
51+
CORE_READ_CONFIG_PATH_BY_PROTOCOL.radix.CHAIN_NAME_1,
52+
);
53+
54+
let chain2CoreAddress: ChainAddresses;
55+
const hyperlaneCore2 = new HyperlaneE2ECoreTestCommands(
56+
ProtocolType.Radix,
57+
TEST_CHAIN_NAMES_BY_PROTOCOL.radix.CHAIN_NAME_2,
58+
REGISTRY_PATH,
59+
CORE_CONFIG_PATH_BY_PROTOCOL.radix,
60+
CORE_READ_CONFIG_PATH_BY_PROTOCOL.radix.CHAIN_NAME_2,
61+
);
62+
63+
const WARP_CORE_PATH = getWarpCoreConfigPath(nativeTokenData.symbol, [
64+
TEST_CHAIN_NAMES_BY_PROTOCOL.radix.CHAIN_NAME_1,
65+
]);
66+
const WARP_DEPLOY_PATH = getWarpDeployConfigPath(nativeTokenData.symbol, [
67+
TEST_CHAIN_NAMES_BY_PROTOCOL.radix.CHAIN_NAME_1,
68+
]);
69+
const WARP_ROUTE_ID = getWarpId(nativeTokenData.symbol, [
70+
TEST_CHAIN_NAMES_BY_PROTOCOL.radix.CHAIN_NAME_1,
71+
]);
72+
73+
const hyperlaneWarp = new HyperlaneE2EWarpTestCommands(
74+
ProtocolType.Radix,
75+
REGISTRY_PATH,
76+
WARP_CORE_PATH,
77+
);
78+
79+
before(async function () {
80+
[chain1CoreAddress, chain2CoreAddress] = await Promise.all([
81+
hyperlaneCore1.deployOrUseExistingCore(HYP_KEY_BY_PROTOCOL.radix),
82+
hyperlaneCore2.deployOrUseExistingCore(HYP_KEY_BY_PROTOCOL.radix),
83+
]);
84+
});
85+
86+
let warpDeployConfig: WarpRouteDeployConfig;
87+
beforeEach(async () => {
88+
warpDeployConfig = {
89+
[TEST_CHAIN_NAMES_BY_PROTOCOL.radix.CHAIN_NAME_1]: {
90+
type: TokenType.collateral,
91+
token: nativeTokenAddress,
92+
mailbox: chain1CoreAddress.mailbox,
93+
owner: HYP_DEPLOYER_ADDRESS_BY_PROTOCOL.radix,
94+
name: nativeTokenData.name,
95+
symbol: nativeTokenData.symbol,
96+
decimals: nativeTokenData.decimals,
97+
},
98+
[TEST_CHAIN_NAMES_BY_PROTOCOL.radix.CHAIN_NAME_2]: {
99+
type: TokenType.synthetic,
100+
mailbox: chain2CoreAddress.mailbox,
101+
owner: HYP_DEPLOYER_ADDRESS_BY_PROTOCOL.radix,
102+
name: nativeTokenData.name,
103+
symbol: nativeTokenData.symbol,
104+
decimals: nativeTokenData.decimals,
105+
},
106+
};
107+
108+
writeYamlOrJson(WARP_DEPLOY_PATH, warpDeployConfig);
109+
await hyperlaneWarp.deployRaw({
110+
warpRouteId: WARP_ROUTE_ID,
111+
skipConfirmationPrompts: true,
112+
privateKey: HYP_KEY_BY_PROTOCOL.radix,
113+
});
114+
});
115+
116+
it('should not update if there are no owner changes', async () => {
117+
const output = await hyperlaneWarp.applyRaw({
118+
warpRouteId: WARP_ROUTE_ID,
119+
hypKey: HYP_KEY_BY_PROTOCOL.radix,
120+
});
121+
122+
expect(output.text()).to.include(
123+
'Warp config is the same as target. No updates needed.',
124+
);
125+
});
126+
127+
const testCases: {
128+
description: string;
129+
chain1TokenOwner: string;
130+
chain2TokenOwner: string;
131+
}[] = [
132+
{
133+
description: 'should burn owner address on chain 1',
134+
chain1TokenOwner: BURN_ADDRESS_BY_PROTOCOL.radix,
135+
chain2TokenOwner: HYP_DEPLOYER_ADDRESS_BY_PROTOCOL.radix,
136+
},
137+
{
138+
description: 'should burn owner address on chain 2',
139+
chain1TokenOwner: HYP_DEPLOYER_ADDRESS_BY_PROTOCOL.radix,
140+
chain2TokenOwner: BURN_ADDRESS_BY_PROTOCOL.radix,
141+
},
142+
{
143+
description: 'should transfer ownership of all the tokens',
144+
chain1TokenOwner: BURN_ADDRESS_BY_PROTOCOL.radix,
145+
chain2TokenOwner: BURN_ADDRESS_BY_PROTOCOL.radix,
146+
},
147+
];
148+
149+
for (const { description, chain1TokenOwner, chain2TokenOwner } of testCases) {
150+
it(description, async function () {
151+
const expectedChain1TokenOwner: Address = chain1TokenOwner;
152+
const expectedChain2TokenOwner: Address = chain2TokenOwner;
153+
154+
warpDeployConfig[TEST_CHAIN_NAMES_BY_PROTOCOL.radix.CHAIN_NAME_1].owner =
155+
chain1TokenOwner;
156+
warpDeployConfig[TEST_CHAIN_NAMES_BY_PROTOCOL.radix.CHAIN_NAME_2].owner =
157+
chain2TokenOwner;
158+
159+
writeYamlOrJson(WARP_DEPLOY_PATH, warpDeployConfig);
160+
161+
await hyperlaneWarp.applyRaw({
162+
warpRouteId: WARP_ROUTE_ID,
163+
hypKey: HYP_KEY_BY_PROTOCOL.radix,
164+
});
165+
166+
await hyperlaneWarp.readRaw({
167+
warpRouteId: WARP_ROUTE_ID,
168+
outputPath: WARP_READ_OUTPUT_PATH,
169+
});
170+
171+
const updatedWarpDeployConfig: DerivedWarpRouteDeployConfig =
172+
readYamlOrJson(WARP_READ_OUTPUT_PATH);
173+
174+
expect(
175+
updatedWarpDeployConfig[TEST_CHAIN_NAMES_BY_PROTOCOL.radix.CHAIN_NAME_1]
176+
.owner,
177+
).to.eq(expectedChain1TokenOwner);
178+
expect(
179+
updatedWarpDeployConfig[TEST_CHAIN_NAMES_BY_PROTOCOL.radix.CHAIN_NAME_2]
180+
.owner,
181+
).to.eq(expectedChain2TokenOwner);
182+
});
183+
}
184+
});

0 commit comments

Comments
 (0)