Skip to content

Commit 4011a45

Browse files
authored
feat: allow ism to be set to 0 address in warp apply (#7235)
1 parent 604cb4d commit 4011a45

File tree

10 files changed

+395
-72
lines changed

10 files changed

+395
-72
lines changed

.changeset/cuddly-lamps-hide.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
"@hyperlane-xyz/cli": minor
3+
"@hyperlane-xyz/sdk": minor
4+
---
5+
6+
Fix bug that prevented the warp route ism to be set to the 0 address and include logic to update a pausable ism as it was missing

.github/workflows/test.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,7 @@ jobs:
313313
# Warp Commands
314314
- warp-apply-1
315315
- warp-apply-2
316+
- warp-apply-ism-updates
316317
- warp-apply-submitters
317318
- warp-bridge-1
318319
- warp-bridge-2

typescript/cli/src/tests/ethereum/commands/helpers.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,12 @@ import path from 'path';
33
import { $ } from 'zx';
44

55
import {
6+
AbstractCcipReadIsm,
7+
AbstractCcipReadIsm__factory,
68
ERC20Test,
79
ERC20Test__factory,
810
ERC4626Test__factory,
11+
TestCcipReadIsm__factory,
912
XERC20LockboxTest,
1013
XERC20LockboxTest__factory,
1114
XERC20VSTest,
@@ -266,6 +269,29 @@ export async function deployXERC20LockboxToken(
266269
return lockboxToken;
267270
}
268271

272+
export async function deployTestOffchainLookupISM(
273+
privateKey: string,
274+
chain: string,
275+
urls: string[] = [],
276+
): Promise<AbstractCcipReadIsm> {
277+
const { multiProvider } = await getContext({
278+
registryUris: [REGISTRY_PATH],
279+
key: privateKey,
280+
});
281+
282+
multiProvider.setSigner(chain, new ethers.Wallet(privateKey));
283+
284+
const testIsm = await new TestCcipReadIsm__factory(
285+
multiProvider.getSigner(chain),
286+
).deploy(urls);
287+
await testIsm.deployed();
288+
289+
return AbstractCcipReadIsm__factory.connect(
290+
testIsm.address,
291+
multiProvider.getSigner(chain),
292+
);
293+
}
294+
269295
// Verifies if the IS_CI var is set and generates the correct prefix for running the command
270296
// in the current env
271297
export function localTestRunCmdPrefix() {
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
import { expect } from 'chai';
2+
import { ethers } from 'ethers';
3+
4+
import {
5+
IsmConfig,
6+
IsmType,
7+
WarpRouteDeployConfig,
8+
normalizeConfig,
9+
} from '@hyperlane-xyz/sdk';
10+
11+
import { TokenType } from '../../../../../utils/dist/altvm.js';
12+
import { writeYamlOrJson } from '../../../utils/files.js';
13+
import { deployOrUseExistingCore } from '../commands/core.js';
14+
import { deployTestOffchainLookupISM } from '../commands/helpers.js';
15+
import {
16+
hyperlaneWarpApplyRaw,
17+
hyperlaneWarpDeploy,
18+
readWarpConfig,
19+
} from '../commands/warp.js';
20+
import {
21+
ANVIL_DEPLOYER_ADDRESS,
22+
ANVIL_KEY,
23+
CHAIN_NAME_2,
24+
CORE_CONFIG_PATH,
25+
DEFAULT_E2E_TEST_TIMEOUT,
26+
TEMP_PATH,
27+
WARP_CORE_CONFIG_PATH_2,
28+
WARP_DEPLOY_2_ID,
29+
} from '../consts.js';
30+
31+
describe('hyperlane warp apply E2E (ISM updates)', async function () {
32+
this.timeout(2 * DEFAULT_E2E_TEST_TIMEOUT);
33+
34+
before(async function () {
35+
await deployOrUseExistingCore(CHAIN_NAME_2, CORE_CONFIG_PATH, ANVIL_KEY);
36+
});
37+
38+
const testCases: {
39+
description: string;
40+
initialIsmConfig?: IsmConfig;
41+
targetIsmConfig: IsmConfig;
42+
}[] = [
43+
{
44+
description: 'should allow updating the default ism to a new ism',
45+
// Use the default ism
46+
targetIsmConfig: {
47+
type: IsmType.PAUSABLE,
48+
owner: ANVIL_DEPLOYER_ADDRESS,
49+
paused: false,
50+
},
51+
},
52+
{
53+
description:
54+
'should allow updating the ism configuration to the default ism (0 address)',
55+
targetIsmConfig: ethers.constants.AddressZero,
56+
initialIsmConfig: {
57+
type: IsmType.PAUSABLE,
58+
owner: ANVIL_DEPLOYER_ADDRESS,
59+
paused: false,
60+
},
61+
},
62+
{
63+
description: 'should pause the pausable ISM',
64+
initialIsmConfig: {
65+
type: IsmType.PAUSABLE,
66+
owner: ANVIL_DEPLOYER_ADDRESS,
67+
paused: false,
68+
},
69+
targetIsmConfig: {
70+
type: IsmType.PAUSABLE,
71+
owner: ANVIL_DEPLOYER_ADDRESS,
72+
paused: true,
73+
},
74+
},
75+
{
76+
description: 'should update the offchain lookup ism',
77+
targetIsmConfig: {
78+
type: IsmType.OFFCHAIN_LOOKUP,
79+
owner: ANVIL_DEPLOYER_ADDRESS,
80+
urls: [
81+
'https://new-server.hyperlane.xyz/api',
82+
'https://backup-server.hyperlane.xyz/api',
83+
],
84+
},
85+
initialIsmConfig: {
86+
type: IsmType.OFFCHAIN_LOOKUP,
87+
owner: ANVIL_DEPLOYER_ADDRESS,
88+
urls: ['https://server.hyperlane.xyz/api'],
89+
},
90+
},
91+
{
92+
description:
93+
'should update the offchain lookup ism if the urls are not in the same order',
94+
targetIsmConfig: {
95+
type: IsmType.OFFCHAIN_LOOKUP,
96+
owner: ANVIL_DEPLOYER_ADDRESS,
97+
urls: [
98+
'https://new-server.hyperlane.xyz/api',
99+
'https://backup-server.hyperlane.xyz/api',
100+
],
101+
},
102+
initialIsmConfig: {
103+
type: IsmType.OFFCHAIN_LOOKUP,
104+
owner: ANVIL_DEPLOYER_ADDRESS,
105+
urls: [
106+
'https://backup-server.hyperlane.xyz/api',
107+
'https://new-server.hyperlane.xyz/api',
108+
],
109+
},
110+
},
111+
];
112+
113+
for (const { description, targetIsmConfig, initialIsmConfig } of testCases) {
114+
it(description, async () => {
115+
const warpDeployPath = `${TEMP_PATH}/warp-route-deployment-2.yaml`;
116+
117+
// CLI does not support deploying offchain lookup isms so we do it here
118+
let ismDeployConfig = initialIsmConfig;
119+
if (
120+
typeof initialIsmConfig !== 'string' &&
121+
initialIsmConfig?.type === IsmType.OFFCHAIN_LOOKUP
122+
) {
123+
const testOffchainLookupIsm = await deployTestOffchainLookupISM(
124+
ANVIL_KEY,
125+
CHAIN_NAME_2,
126+
initialIsmConfig.urls,
127+
);
128+
129+
ismDeployConfig = testOffchainLookupIsm.address;
130+
}
131+
132+
const warpDeployConfig: WarpRouteDeployConfig = {
133+
[CHAIN_NAME_2]: {
134+
type: TokenType.native,
135+
owner: ANVIL_DEPLOYER_ADDRESS,
136+
interchainSecurityModule: ismDeployConfig,
137+
},
138+
};
139+
140+
await writeYamlOrJson(warpDeployPath, warpDeployConfig);
141+
await hyperlaneWarpDeploy(warpDeployPath, WARP_DEPLOY_2_ID);
142+
143+
// Write the updated config
144+
warpDeployConfig[CHAIN_NAME_2].interchainSecurityModule = targetIsmConfig;
145+
await writeYamlOrJson(warpDeployPath, warpDeployConfig);
146+
147+
// Apply the changes
148+
await hyperlaneWarpApplyRaw({
149+
warpDeployPath: warpDeployPath,
150+
warpCorePath: WARP_CORE_CONFIG_PATH_2,
151+
});
152+
153+
// Read back the config to verify changes
154+
const updatedConfig = await readWarpConfig(
155+
CHAIN_NAME_2,
156+
WARP_CORE_CONFIG_PATH_2,
157+
warpDeployPath,
158+
);
159+
160+
expect(
161+
normalizeConfig(updatedConfig[CHAIN_NAME_2].interchainSecurityModule),
162+
).to.deep.equal(normalizeConfig(targetIsmConfig));
163+
});
164+
}
165+
});

0 commit comments

Comments
 (0)