@hyperlane-xyz/sdk 41.3.1 → 42.0.0
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/dist/ccip/consts.d.ts.map +1 -1
- package/dist/ccip/consts.js +20 -0
- package/dist/ccip/consts.js.map +1 -1
- package/dist/consts/multisigIsm.d.ts.map +1 -1
- package/dist/consts/multisigIsm.js +20 -0
- package/dist/consts/multisigIsm.js.map +1 -1
- package/dist/contracts/contracts.d.ts.map +1 -1
- package/dist/contracts/contracts.js +15 -0
- package/dist/contracts/contracts.js.map +1 -1
- package/dist/contracts/contracts.test.d.ts +2 -0
- package/dist/contracts/contracts.test.d.ts.map +1 -0
- package/dist/contracts/contracts.test.js +88 -0
- package/dist/contracts/contracts.test.js.map +1 -0
- package/dist/core/types.d.ts +80 -2602
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js +19 -1
- package/dist/core/types.js.map +1 -1
- package/dist/core/types.test.js +58 -0
- package/dist/core/types.test.js.map +1 -1
- package/dist/deploy/warp.d.ts +119 -3
- package/dist/deploy/warp.d.ts.map +1 -1
- package/dist/deploy/warp.js +696 -33
- package/dist/deploy/warp.js.map +1 -1
- package/dist/deploy/warp.test.d.ts +2 -0
- package/dist/deploy/warp.test.d.ts.map +1 -0
- package/dist/deploy/warp.test.js +358 -0
- package/dist/deploy/warp.test.js.map +1 -0
- package/dist/deploy/warpHybridPlan.d.ts +104 -0
- package/dist/deploy/warpHybridPlan.d.ts.map +1 -0
- package/dist/deploy/warpHybridPlan.js +178 -0
- package/dist/deploy/warpHybridPlan.js.map +1 -0
- package/dist/deploy/warpHybridPlan.test.d.ts +2 -0
- package/dist/deploy/warpHybridPlan.test.d.ts.map +1 -0
- package/dist/deploy/warpHybridPlan.test.js +141 -0
- package/dist/deploy/warpHybridPlan.test.js.map +1 -0
- package/dist/hook/EvmHookModule.d.ts +1 -1
- package/dist/hook/EvmHookModule.d.ts.map +1 -1
- package/dist/hook/EvmHookModule.js +16 -3
- package/dist/hook/EvmHookModule.js.map +1 -1
- package/dist/hook/EvmHookReader.d.ts +10 -0
- package/dist/hook/EvmHookReader.d.ts.map +1 -1
- package/dist/hook/EvmHookReader.js +92 -11
- package/dist/hook/EvmHookReader.js.map +1 -1
- package/dist/hook/EvmHookReader.test.js +155 -1
- package/dist/hook/EvmHookReader.test.js.map +1 -1
- package/dist/hook/types.d.ts +107 -1983
- package/dist/hook/types.d.ts.map +1 -1
- package/dist/hook/types.js +49 -0
- package/dist/hook/types.js.map +1 -1
- package/dist/hook/types.test.js +21 -1
- package/dist/hook/types.test.js.map +1 -1
- package/dist/hook/updates.d.ts +1 -0
- package/dist/hook/updates.d.ts.map +1 -1
- package/dist/hook/updates.js +2 -2
- package/dist/hook/updates.js.map +1 -1
- package/dist/hook/utils.d.ts +28 -1
- package/dist/hook/utils.d.ts.map +1 -1
- package/dist/hook/utils.js +89 -0
- package/dist/hook/utils.js.map +1 -1
- package/dist/ica/types.d.ts +4 -674
- package/dist/ica/types.d.ts.map +1 -1
- package/dist/index.d.ts +6 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -5
- package/dist/index.js.map +1 -1
- package/dist/ism/EvmIsmModule.d.ts +28 -2
- package/dist/ism/EvmIsmModule.d.ts.map +1 -1
- package/dist/ism/EvmIsmModule.hardhat-test.js +478 -3
- package/dist/ism/EvmIsmModule.hardhat-test.js.map +1 -1
- package/dist/ism/EvmIsmModule.js +224 -10
- package/dist/ism/EvmIsmModule.js.map +1 -1
- package/dist/ism/EvmIsmReader.d.ts.map +1 -1
- package/dist/ism/EvmIsmReader.js +77 -1
- package/dist/ism/EvmIsmReader.js.map +1 -1
- package/dist/ism/EvmIsmReader.test.js +245 -4
- package/dist/ism/EvmIsmReader.test.js.map +1 -1
- package/dist/ism/HyperlaneIsmFactory.d.ts +14 -4
- package/dist/ism/HyperlaneIsmFactory.d.ts.map +1 -1
- package/dist/ism/HyperlaneIsmFactory.hardhat-test.js +372 -3
- package/dist/ism/HyperlaneIsmFactory.hardhat-test.js.map +1 -1
- package/dist/ism/HyperlaneIsmFactory.js +80 -6
- package/dist/ism/HyperlaneIsmFactory.js.map +1 -1
- package/dist/ism/delayedFlow.d.ts +31 -0
- package/dist/ism/delayedFlow.d.ts.map +1 -0
- package/dist/ism/delayedFlow.js +33 -0
- package/dist/ism/delayedFlow.js.map +1 -0
- package/dist/ism/types.d.ts +135 -4
- package/dist/ism/types.d.ts.map +1 -1
- package/dist/ism/types.js +263 -11
- package/dist/ism/types.js.map +1 -1
- package/dist/ism/types.test.js +433 -1
- package/dist/ism/types.test.js.map +1 -1
- package/dist/ism/utils.d.ts.map +1 -1
- package/dist/ism/utils.js +113 -3
- package/dist/ism/utils.js.map +1 -1
- package/dist/ism/utils.test.js +44 -0
- package/dist/ism/utils.test.js.map +1 -1
- package/dist/providers/MultiProviderAdapter.d.ts +3 -3
- package/dist/providers/MultiProviderAdapter.d.ts.map +1 -1
- package/dist/providers/MultiProviderAdapter.js +2 -1
- package/dist/providers/MultiProviderAdapter.js.map +1 -1
- package/dist/providers/SmartProvider/HyperlaneJsonRpcProvider.d.ts.map +1 -1
- package/dist/providers/SmartProvider/HyperlaneJsonRpcProvider.js +4 -0
- package/dist/providers/SmartProvider/HyperlaneJsonRpcProvider.js.map +1 -1
- package/dist/providers/transactionFeeEstimators.d.ts +16 -9
- package/dist/providers/transactionFeeEstimators.d.ts.map +1 -1
- package/dist/providers/transactionFeeEstimators.js +66 -17
- package/dist/providers/transactionFeeEstimators.js.map +1 -1
- package/dist/providers/transactionFeeEstimators.test.js +153 -1
- package/dist/providers/transactionFeeEstimators.test.js.map +1 -1
- package/dist/providers/transactions/submitter/ethersV5/EV5JsonRpcTxSubmitter.d.ts +14 -1
- package/dist/providers/transactions/submitter/ethersV5/EV5JsonRpcTxSubmitter.d.ts.map +1 -1
- package/dist/providers/transactions/submitter/ethersV5/EV5JsonRpcTxSubmitter.js +46 -5
- package/dist/providers/transactions/submitter/ethersV5/EV5JsonRpcTxSubmitter.js.map +1 -1
- package/dist/providers/transactions/submitter/ethersV5/EV5JsonRpcTxSubmitter.test.d.ts +2 -0
- package/dist/providers/transactions/submitter/ethersV5/EV5JsonRpcTxSubmitter.test.d.ts.map +1 -0
- package/dist/providers/transactions/submitter/ethersV5/EV5JsonRpcTxSubmitter.test.js +125 -0
- package/dist/providers/transactions/submitter/ethersV5/EV5JsonRpcTxSubmitter.test.js.map +1 -0
- package/dist/router/HyperlaneRouterDeployer.d.ts +3 -1
- package/dist/router/HyperlaneRouterDeployer.d.ts.map +1 -1
- package/dist/router/HyperlaneRouterDeployer.js +4 -2
- package/dist/router/HyperlaneRouterDeployer.js.map +1 -1
- package/dist/router/types.d.ts +6 -1011
- package/dist/router/types.d.ts.map +1 -1
- package/dist/test/testUtils.d.ts +1 -1
- package/dist/test/testUtils.d.ts.map +1 -1
- package/dist/test/testUtils.js +17 -0
- package/dist/test/testUtils.js.map +1 -1
- package/dist/test/testUtils.test.d.ts +2 -0
- package/dist/test/testUtils.test.d.ts.map +1 -0
- package/dist/test/testUtils.test.js +28 -0
- package/dist/test/testUtils.test.js.map +1 -0
- package/dist/token/EvmWarpModule.d.ts +44 -8
- package/dist/token/EvmWarpModule.d.ts.map +1 -1
- package/dist/token/EvmWarpModule.hardhat-test.js +574 -4
- package/dist/token/EvmWarpModule.hardhat-test.js.map +1 -1
- package/dist/token/EvmWarpModule.js +237 -25
- package/dist/token/EvmWarpModule.js.map +1 -1
- package/dist/token/configUtils.d.ts +17 -7
- package/dist/token/configUtils.d.ts.map +1 -1
- package/dist/token/configUtils.js +108 -0
- package/dist/token/configUtils.js.map +1 -1
- package/dist/token/configUtils.test.js +155 -1
- package/dist/token/configUtils.test.js.map +1 -1
- package/dist/token/deploy.d.ts +3 -1
- package/dist/token/deploy.d.ts.map +1 -1
- package/dist/token/deploy.hardhat-test.js +454 -3
- package/dist/token/deploy.hardhat-test.js.map +1 -1
- package/dist/token/deploy.js +25 -6
- package/dist/token/deploy.js.map +1 -1
- package/dist/token/types.d.ts +169 -2931
- package/dist/token/types.d.ts.map +1 -1
- package/dist/token/types.test.js +48 -0
- package/dist/token/types.test.js.map +1 -1
- package/dist/token/warpCheck.d.ts.map +1 -1
- package/dist/token/warpCheck.js +36 -1
- package/dist/token/warpCheck.js.map +1 -1
- package/dist/utils/ism.d.ts +167 -1
- package/dist/utils/ism.d.ts.map +1 -1
- package/dist/utils/ism.js +412 -2
- package/dist/utils/ism.js.map +1 -1
- package/dist/utils/ism.test.d.ts +2 -0
- package/dist/utils/ism.test.d.ts.map +1 -0
- package/dist/utils/ism.test.js +372 -0
- package/dist/utils/ism.test.js.map +1 -0
- package/package.json +10 -10
|
@@ -3,16 +3,18 @@ import chai, { expect } from 'chai';
|
|
|
3
3
|
import chaiAsPromised from 'chai-as-promised';
|
|
4
4
|
import hre from 'hardhat';
|
|
5
5
|
import sinon from 'sinon';
|
|
6
|
-
import { BlacklistIsm__factory, RateLimitedIsm__factory, StaticAggregationIsm__factory, TestLegacyBlacklistIsm__factory, } from '@hyperlane-xyz/core';
|
|
7
|
-
import { eqAddress, rootLogger } from '@hyperlane-xyz/utils';
|
|
6
|
+
import { BlacklistIsm__factory, DefaultIsm__factory, DelayedFlowRouterHookIsm__factory, HypERC20__factory, NetFlowRateLimitedHookIsm__factory, RateLimitedIsm__factory, StaticAggregationIsm__factory, TestLegacyBlacklistIsm__factory, } from '@hyperlane-xyz/core';
|
|
7
|
+
import { addressToBytes32, eqAddress, rootLogger, } from '@hyperlane-xyz/utils';
|
|
8
8
|
import { TestChainName, testChains } from '../consts/testChains.js';
|
|
9
9
|
import { TestCoreDeployer } from '../core/TestCoreDeployer.js';
|
|
10
10
|
import { HyperlaneProxyFactoryDeployer } from '../deploy/HyperlaneProxyFactoryDeployer.js';
|
|
11
|
+
import { buildDelayedFlowEnrollmentTxs, } from '../deploy/warp.js';
|
|
11
12
|
import { MultiProvider } from '../providers/MultiProvider.js';
|
|
12
13
|
import { EvmEventLogsReader } from '../rpc/evm/EvmEventLogsReader.js';
|
|
13
14
|
import { randomAddress, randomIsmConfig, randomMultisigIsmConfig, } from '../test/testUtils.js';
|
|
14
|
-
import { normalizeConfig } from '../utils/ism.js';
|
|
15
|
+
import { collectHybridIsmNodes, normalizeConfig } from '../utils/ism.js';
|
|
15
16
|
import { EvmIsmModule } from './EvmIsmModule.js';
|
|
17
|
+
import { EvmIsmReader } from './EvmIsmReader.js';
|
|
16
18
|
import { HyperlaneIsmFactory } from './HyperlaneIsmFactory.js';
|
|
17
19
|
import { IsmType, ModuleType, } from './types.js';
|
|
18
20
|
chai.use(chaiAsPromised);
|
|
@@ -31,6 +33,8 @@ describe('EvmIsmModule', async () => {
|
|
|
31
33
|
const legacyDeploymentBlocks = new Map();
|
|
32
34
|
let legacyExplorerMetadataStub;
|
|
33
35
|
let legacyDeploymentBlockStub;
|
|
36
|
+
// paired TokenRouter required by the warp-route hybrid hook/ISM constructors
|
|
37
|
+
let warpRouterAddress;
|
|
34
38
|
before(async () => {
|
|
35
39
|
const [signer, funder] = await hre.ethers.getSigners();
|
|
36
40
|
fundingAccount = funder;
|
|
@@ -47,6 +51,7 @@ describe('EvmIsmModule', async () => {
|
|
|
47
51
|
const legacyIsmFactory = new HyperlaneIsmFactory(contractsMap, multiProvider);
|
|
48
52
|
// mailbox
|
|
49
53
|
mailboxAddress = (await new TestCoreDeployer(multiProvider, legacyIsmFactory).deployApp()).getContracts(chain).mailbox.address;
|
|
54
|
+
warpRouterAddress = (await new HypERC20__factory(signer).deploy(18, 1, 1, mailboxAddress)).address;
|
|
50
55
|
});
|
|
51
56
|
beforeEach(async () => {
|
|
52
57
|
// Reset the MultiProvider for each test
|
|
@@ -153,6 +158,29 @@ describe('EvmIsmModule', async () => {
|
|
|
153
158
|
},
|
|
154
159
|
});
|
|
155
160
|
}
|
|
161
|
+
/**
|
|
162
|
+
* Hybrid hook/ISMs are only valid inside a mandatory aggregation beside an
|
|
163
|
+
* authenticating ISM, which is also how a warp route always installs them.
|
|
164
|
+
* The wrapper holds `hybrid` by reference, so tests can keep mutating the
|
|
165
|
+
* node in place between create and update.
|
|
166
|
+
*/
|
|
167
|
+
function hybridAgg(hybrid) {
|
|
168
|
+
return {
|
|
169
|
+
type: IsmType.AGGREGATION,
|
|
170
|
+
threshold: 2,
|
|
171
|
+
modules: [
|
|
172
|
+
{ type: IsmType.TRUSTED_RELAYER, relayer: mailboxAddress },
|
|
173
|
+
hybrid,
|
|
174
|
+
],
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
/** Address of the hybrid leaf inside the module's installed tree. */
|
|
178
|
+
async function hybridLeaf(ism) {
|
|
179
|
+
const derived = await new EvmIsmReader(multiProvider, chain).deriveIsmConfig(ism.serialize().deployedIsm);
|
|
180
|
+
const [leaf] = collectHybridIsmNodes(derived);
|
|
181
|
+
assert(leaf && 'address' in leaf && typeof leaf.address === 'string', 'expected a deployed hybrid leaf');
|
|
182
|
+
return leaf.address;
|
|
183
|
+
}
|
|
156
184
|
describe('create', async () => {
|
|
157
185
|
it('deploys a simple ism', async () => {
|
|
158
186
|
const config = randomMultisigIsmConfig(3, 5);
|
|
@@ -241,6 +269,45 @@ describe('EvmIsmModule', async () => {
|
|
|
241
269
|
multiProvider,
|
|
242
270
|
})).to.be.rejectedWith(BLACKLIST_COMPOSITION_ERROR);
|
|
243
271
|
});
|
|
272
|
+
it('deploys a mailbox default ism', async () => {
|
|
273
|
+
const config = {
|
|
274
|
+
type: IsmType.MAILBOX_DEFAULT,
|
|
275
|
+
};
|
|
276
|
+
await createIsm(config);
|
|
277
|
+
});
|
|
278
|
+
it('deploys a net flow rate limited hook ism and transfers ownership to non-deployer', async () => {
|
|
279
|
+
const owner = randomAddress();
|
|
280
|
+
const config = {
|
|
281
|
+
type: IsmType.NET_FLOW_RATE_LIMITED,
|
|
282
|
+
warpRouter: warpRouterAddress,
|
|
283
|
+
thresholdBps: 500,
|
|
284
|
+
duration: 86400n,
|
|
285
|
+
owner,
|
|
286
|
+
};
|
|
287
|
+
const { ism } = await createIsm(hybridAgg(config));
|
|
288
|
+
const netFlowIsm = NetFlowRateLimitedHookIsm__factory.connect(await hybridLeaf(ism), multiProvider.getProvider(chain));
|
|
289
|
+
expect((await netFlowIsm.owner()).toLowerCase()).to.equal(owner.toLowerCase());
|
|
290
|
+
expect((await netFlowIsm.warpRouter()).toLowerCase()).to.equal(warpRouterAddress.toLowerCase());
|
|
291
|
+
});
|
|
292
|
+
it('deploys a delayed flow router hook ism, enrolling routers before transferring ownership', async () => {
|
|
293
|
+
// owner is a non-deployer EOA: the deploy only succeeds if the
|
|
294
|
+
// owner-gated enrollment happens BEFORE the ownership transfer
|
|
295
|
+
const owner = randomAddress();
|
|
296
|
+
const remoteRouter = addressToBytes32(randomAddress()).toLowerCase();
|
|
297
|
+
const config = {
|
|
298
|
+
type: IsmType.DELAYED_FLOW_ROUTER,
|
|
299
|
+
warpRouter: warpRouterAddress,
|
|
300
|
+
thresholdBps: 10000,
|
|
301
|
+
maxDelay: 3600,
|
|
302
|
+
duration: 86400n,
|
|
303
|
+
owner,
|
|
304
|
+
remoteIsms: { [TestChainName.test2]: remoteRouter },
|
|
305
|
+
};
|
|
306
|
+
const { ism } = await createIsm(hybridAgg(config));
|
|
307
|
+
const delayedIsm = DelayedFlowRouterHookIsm__factory.connect(await hybridLeaf(ism), multiProvider.getProvider(chain));
|
|
308
|
+
expect(await delayedIsm.routers(multiProvider.getDomainId(TestChainName.test2))).to.equal(remoteRouter);
|
|
309
|
+
expect((await delayedIsm.owner()).toLowerCase()).to.equal(owner.toLowerCase());
|
|
310
|
+
});
|
|
244
311
|
for (let i = 0; i < 16; i++) {
|
|
245
312
|
it(`deploys a random ism config #${i}`, async () => {
|
|
246
313
|
const config = randomIsmConfig();
|
|
@@ -765,6 +832,414 @@ describe('EvmIsmModule', async () => {
|
|
|
765
832
|
expect(blacklistFound).to.be.true;
|
|
766
833
|
});
|
|
767
834
|
});
|
|
835
|
+
it('no changes to an existing mailbox default ism means no redeployment or updates', async () => {
|
|
836
|
+
const config = {
|
|
837
|
+
type: IsmType.MAILBOX_DEFAULT,
|
|
838
|
+
};
|
|
839
|
+
const { ism, initialIsmAddress } = await createIsm(config);
|
|
840
|
+
await expectTxsAndUpdate(ism, config, 0);
|
|
841
|
+
expect(eqAddress(initialIsmAddress, ism.serialize().deployedIsm)).to.be
|
|
842
|
+
.true;
|
|
843
|
+
});
|
|
844
|
+
it('redeploys a mailbox default ism that defers to a different mailbox', async () => {
|
|
845
|
+
const config = {
|
|
846
|
+
type: IsmType.MAILBOX_DEFAULT,
|
|
847
|
+
};
|
|
848
|
+
const { initialIsmAddress } = await createIsm(config);
|
|
849
|
+
// A DefaultIsm's mailbox is an immutable constructor arg the reader
|
|
850
|
+
// omits, so this module — pointed at the same instance but at a
|
|
851
|
+
// different mailbox — sees an identical `{ type }` config while the
|
|
852
|
+
// instance routes through the wrong mailbox's default ISM.
|
|
853
|
+
const otherMailbox = warpRouterAddress;
|
|
854
|
+
const reboundModule = new EvmIsmModule(multiProvider, {
|
|
855
|
+
chain,
|
|
856
|
+
config,
|
|
857
|
+
addresses: {
|
|
858
|
+
...factoryAddresses,
|
|
859
|
+
mailbox: otherMailbox,
|
|
860
|
+
deployedIsm: initialIsmAddress,
|
|
861
|
+
},
|
|
862
|
+
});
|
|
863
|
+
// redeploys internally (MAILBOX_DEFAULT is immutable), emitting no txs
|
|
864
|
+
expect(await reboundModule.update(config)).to.deep.equal([]);
|
|
865
|
+
const redeployedIsm = reboundModule.serialize().deployedIsm;
|
|
866
|
+
expect(eqAddress(redeployedIsm, initialIsmAddress)).to.be.false;
|
|
867
|
+
expect(eqAddress(await DefaultIsm__factory.connect(redeployedIsm, multiProvider.getProvider(chain)).mailbox(), otherMailbox)).to.be.true;
|
|
868
|
+
});
|
|
869
|
+
it('redeploys when changing a mailbox default ism to another type', async () => {
|
|
870
|
+
const { ism, initialIsmAddress } = await createIsm({
|
|
871
|
+
type: IsmType.MAILBOX_DEFAULT,
|
|
872
|
+
});
|
|
873
|
+
const trustedRelayerConfig = {
|
|
874
|
+
type: IsmType.TRUSTED_RELAYER,
|
|
875
|
+
relayer: randomAddress(),
|
|
876
|
+
};
|
|
877
|
+
// keep testConfig in sync for the afterEach read-back assertion
|
|
878
|
+
testConfig = trustedRelayerConfig;
|
|
879
|
+
// update() redeploys internally and emits no txs
|
|
880
|
+
await expectTxsAndUpdate(ism, trustedRelayerConfig, 0);
|
|
881
|
+
expect(eqAddress(initialIsmAddress, ism.serialize().deployedIsm)).to.be
|
|
882
|
+
.false;
|
|
883
|
+
});
|
|
884
|
+
it('transfers netFlowRateLimitedHookIsm ownership in-place on owner change', async () => {
|
|
885
|
+
const signerAddress = await multiProvider.getSignerAddress(chain);
|
|
886
|
+
const netFlowConfig = {
|
|
887
|
+
type: IsmType.NET_FLOW_RATE_LIMITED,
|
|
888
|
+
warpRouter: warpRouterAddress,
|
|
889
|
+
thresholdBps: 500,
|
|
890
|
+
duration: 86400n,
|
|
891
|
+
owner: signerAddress,
|
|
892
|
+
};
|
|
893
|
+
const { ism, initialIsmAddress } = await createIsm(hybridAgg(netFlowConfig));
|
|
894
|
+
const newOwner = randomAddress();
|
|
895
|
+
// mutate in-place so testConfig (same reference) stays in sync for afterEach
|
|
896
|
+
netFlowConfig.owner = newOwner;
|
|
897
|
+
// NET_FLOW_RATE_LIMITED is mutable — update() transfers ownership in-place (1 tx)
|
|
898
|
+
await expectTxsAndUpdate(ism, hybridAgg(netFlowConfig), 1);
|
|
899
|
+
// same contract address — no redeploy
|
|
900
|
+
expect(eqAddress(initialIsmAddress, ism.serialize().deployedIsm)).to.be
|
|
901
|
+
.true;
|
|
902
|
+
const netFlowIsm = NetFlowRateLimitedHookIsm__factory.connect(await hybridLeaf(ism), multiProvider.getProvider(chain));
|
|
903
|
+
expect((await netFlowIsm.owner()).toLowerCase()).to.equal(newOwner.toLowerCase());
|
|
904
|
+
});
|
|
905
|
+
it('redeploys a new netFlowRateLimitedHookIsm on thresholdBps change (immutable)', async () => {
|
|
906
|
+
const signerAddress = await multiProvider.getSignerAddress(chain);
|
|
907
|
+
const netFlowConfig = {
|
|
908
|
+
type: IsmType.NET_FLOW_RATE_LIMITED,
|
|
909
|
+
warpRouter: warpRouterAddress,
|
|
910
|
+
thresholdBps: 500,
|
|
911
|
+
duration: 86400n,
|
|
912
|
+
owner: signerAddress,
|
|
913
|
+
};
|
|
914
|
+
const { ism, initialIsmAddress } = await createIsm(hybridAgg(netFlowConfig));
|
|
915
|
+
// thresholdBps is immutable on-chain; changing it must redeploy
|
|
916
|
+
netFlowConfig.thresholdBps = 750;
|
|
917
|
+
// update() redeploys internally and emits no txs
|
|
918
|
+
await expectTxsAndUpdate(ism, hybridAgg(netFlowConfig), 0);
|
|
919
|
+
// different contract address — redeployed
|
|
920
|
+
expect(eqAddress(initialIsmAddress, ism.serialize().deployedIsm)).to.be
|
|
921
|
+
.false;
|
|
922
|
+
const netFlowIsm = NetFlowRateLimitedHookIsm__factory.connect(await hybridLeaf(ism), multiProvider.getProvider(chain));
|
|
923
|
+
expect((await netFlowIsm.thresholdBps()).toString()).to.equal('750');
|
|
924
|
+
});
|
|
925
|
+
it('updates netFlowRateLimitedHookIsm owner in-place inside an aggregation ism without redeploying the container', async () => {
|
|
926
|
+
// the contract mandates composing NetFlowRateLimitedHookIsm under an
|
|
927
|
+
// authenticating ISM, so aggregation-wrapped is the primary shape
|
|
928
|
+
const signerAddress = await multiProvider.getSignerAddress(chain);
|
|
929
|
+
const netFlowConfig = {
|
|
930
|
+
type: IsmType.NET_FLOW_RATE_LIMITED,
|
|
931
|
+
warpRouter: warpRouterAddress,
|
|
932
|
+
thresholdBps: 500,
|
|
933
|
+
duration: 86400n,
|
|
934
|
+
owner: signerAddress,
|
|
935
|
+
};
|
|
936
|
+
const aggregationConfig = {
|
|
937
|
+
type: IsmType.AGGREGATION,
|
|
938
|
+
modules: [netFlowConfig, randomMultisigIsmConfig(3, 5)],
|
|
939
|
+
threshold: 2,
|
|
940
|
+
};
|
|
941
|
+
const { ism, initialIsmAddress } = await createIsm(aggregationConfig);
|
|
942
|
+
// mutate in-place so testConfig (same reference) stays in sync for afterEach
|
|
943
|
+
const newOwner = randomAddress();
|
|
944
|
+
netFlowConfig.owner = newOwner;
|
|
945
|
+
// owner is the netFlow's only mutable field: expect a single in-place
|
|
946
|
+
// transferOwnership tx, with the aggregation container untouched
|
|
947
|
+
await expectTxsAndUpdate(ism, aggregationConfig, 1);
|
|
948
|
+
expect(eqAddress(initialIsmAddress, ism.serialize().deployedIsm)).to.be
|
|
949
|
+
.true;
|
|
950
|
+
});
|
|
951
|
+
it('no changes to an existing delayedFlowRouterHookIsm means no redeployment or updates', async () => {
|
|
952
|
+
const signerAddress = await multiProvider.getSignerAddress(chain);
|
|
953
|
+
const delayedConfig = {
|
|
954
|
+
type: IsmType.DELAYED_FLOW_ROUTER,
|
|
955
|
+
warpRouter: warpRouterAddress,
|
|
956
|
+
thresholdBps: 10000,
|
|
957
|
+
maxDelay: 3600,
|
|
958
|
+
duration: 86400n,
|
|
959
|
+
owner: signerAddress,
|
|
960
|
+
remoteIsms: {
|
|
961
|
+
[TestChainName.test2]: addressToBytes32(randomAddress()).toLowerCase(),
|
|
962
|
+
},
|
|
963
|
+
};
|
|
964
|
+
const { ism, initialIsmAddress } = await createIsm(hybridAgg(delayedConfig));
|
|
965
|
+
await expectTxsAndUpdate(ism, hybridAgg(delayedConfig), 0);
|
|
966
|
+
expect(eqAddress(initialIsmAddress, ism.serialize().deployedIsm)).to.be
|
|
967
|
+
.true;
|
|
968
|
+
});
|
|
969
|
+
it('does not redeploy a delayedFlowRouterHookIsm when the target omits warpRouter', async () => {
|
|
970
|
+
const signerAddress = await multiProvider.getSignerAddress(chain);
|
|
971
|
+
const delayedConfig = {
|
|
972
|
+
type: IsmType.DELAYED_FLOW_ROUTER,
|
|
973
|
+
warpRouter: warpRouterAddress,
|
|
974
|
+
thresholdBps: 10000,
|
|
975
|
+
maxDelay: 3600,
|
|
976
|
+
duration: 86400n,
|
|
977
|
+
owner: signerAddress,
|
|
978
|
+
remoteIsms: {},
|
|
979
|
+
};
|
|
980
|
+
const { ism, initialIsmAddress } = await createIsm(hybridAgg(delayedConfig));
|
|
981
|
+
// warp-route-context configs omit warpRouter (implicitly the token);
|
|
982
|
+
// the update must treat it as unchanged, not as an immutable-field change
|
|
983
|
+
const targetWithoutWarpRouter = {
|
|
984
|
+
type: IsmType.DELAYED_FLOW_ROUTER,
|
|
985
|
+
thresholdBps: 10000,
|
|
986
|
+
maxDelay: 3600,
|
|
987
|
+
duration: 86400n,
|
|
988
|
+
owner: signerAddress,
|
|
989
|
+
remoteIsms: {},
|
|
990
|
+
};
|
|
991
|
+
await expectTxsAndUpdate(ism, hybridAgg(targetWithoutWarpRouter), 0);
|
|
992
|
+
expect(eqAddress(initialIsmAddress, ism.serialize().deployedIsm)).to.be
|
|
993
|
+
.true;
|
|
994
|
+
});
|
|
995
|
+
it('enrolls and unenrolls remote routers on an existing delayedFlowRouterHookIsm', async () => {
|
|
996
|
+
const signerAddress = await multiProvider.getSignerAddress(chain);
|
|
997
|
+
const test2Router = addressToBytes32(randomAddress()).toLowerCase();
|
|
998
|
+
const test3Router = addressToBytes32(randomAddress()).toLowerCase();
|
|
999
|
+
const delayedConfig = {
|
|
1000
|
+
type: IsmType.DELAYED_FLOW_ROUTER,
|
|
1001
|
+
warpRouter: warpRouterAddress,
|
|
1002
|
+
thresholdBps: 10000,
|
|
1003
|
+
maxDelay: 3600,
|
|
1004
|
+
duration: 86400n,
|
|
1005
|
+
owner: signerAddress,
|
|
1006
|
+
remoteIsms: { [TestChainName.test2]: test2Router },
|
|
1007
|
+
};
|
|
1008
|
+
const { ism, initialIsmAddress } = await createIsm(hybridAgg(delayedConfig));
|
|
1009
|
+
// replace test2 with test3: 1 enroll tx + 1 unenroll tx
|
|
1010
|
+
delayedConfig.remoteIsms = { [TestChainName.test3]: test3Router };
|
|
1011
|
+
await expectTxsAndUpdate(ism, hybridAgg(delayedConfig), 2);
|
|
1012
|
+
expect(eqAddress(initialIsmAddress, ism.serialize().deployedIsm)).to.be
|
|
1013
|
+
.true;
|
|
1014
|
+
const delayedIsm = DelayedFlowRouterHookIsm__factory.connect(await hybridLeaf(ism), multiProvider.getProvider(chain));
|
|
1015
|
+
const test3Domain = multiProvider.getDomainId(TestChainName.test3);
|
|
1016
|
+
expect(await delayedIsm.routers(test3Domain)).to.equal(test3Router);
|
|
1017
|
+
const enrolledDomains = await delayedIsm.domains();
|
|
1018
|
+
expect(enrolledDomains.map((domain) => Number(domain))).to.deep.equal([
|
|
1019
|
+
test3Domain,
|
|
1020
|
+
]);
|
|
1021
|
+
});
|
|
1022
|
+
it('unenrolls a delayed flow domain missing from provider metadata', async () => {
|
|
1023
|
+
const signerAddress = await multiProvider.getSignerAddress(chain);
|
|
1024
|
+
const delayedConfig = {
|
|
1025
|
+
type: IsmType.DELAYED_FLOW_ROUTER,
|
|
1026
|
+
warpRouter: warpRouterAddress,
|
|
1027
|
+
thresholdBps: 10000,
|
|
1028
|
+
maxDelay: 3600,
|
|
1029
|
+
duration: 86400n,
|
|
1030
|
+
owner: signerAddress,
|
|
1031
|
+
remoteIsms: {},
|
|
1032
|
+
};
|
|
1033
|
+
const { ism } = await createIsm(hybridAgg(delayedConfig));
|
|
1034
|
+
const leafAddress = await hybridLeaf(ism);
|
|
1035
|
+
const delayedIsm = DelayedFlowRouterHookIsm__factory.connect(leafAddress, multiProvider.getSigner(chain));
|
|
1036
|
+
const unknownDomain = 987654;
|
|
1037
|
+
expect(multiProvider.tryGetChainName(unknownDomain)).to.be.null;
|
|
1038
|
+
await delayedIsm.enrollRemoteRouters([unknownDomain], [addressToBytes32(randomAddress())]);
|
|
1039
|
+
const leafModule = new EvmIsmModule(multiProvider, {
|
|
1040
|
+
chain,
|
|
1041
|
+
config: delayedConfig,
|
|
1042
|
+
addresses: {
|
|
1043
|
+
...factoryAddresses,
|
|
1044
|
+
mailbox: mailboxAddress,
|
|
1045
|
+
deployedIsm: leafAddress,
|
|
1046
|
+
},
|
|
1047
|
+
});
|
|
1048
|
+
const txs = await leafModule.updateDeployedInstance(delayedConfig);
|
|
1049
|
+
expect(txs).to.have.length(1);
|
|
1050
|
+
for (const tx of txs) {
|
|
1051
|
+
await multiProvider.sendTransaction(chain, tx);
|
|
1052
|
+
}
|
|
1053
|
+
expect(await delayedIsm.domains()).to.deep.equal([]);
|
|
1054
|
+
});
|
|
1055
|
+
it('updateDeployedInstance reconciles the hybrid leaf but rejects a composed tree', async () => {
|
|
1056
|
+
const signerAddress = await multiProvider.getSignerAddress(chain);
|
|
1057
|
+
const delayedConfig = {
|
|
1058
|
+
type: IsmType.DELAYED_FLOW_ROUTER,
|
|
1059
|
+
warpRouter: warpRouterAddress,
|
|
1060
|
+
thresholdBps: 10000,
|
|
1061
|
+
maxDelay: 3600,
|
|
1062
|
+
duration: 86400n,
|
|
1063
|
+
owner: signerAddress,
|
|
1064
|
+
remoteIsms: {},
|
|
1065
|
+
};
|
|
1066
|
+
const { ism } = await createIsm(hybridAgg(delayedConfig));
|
|
1067
|
+
// Same module shape buildDelayedFlowEnrollmentTxs builds: pointed at the
|
|
1068
|
+
// deployed hybrid leaf, not at the aggregation above it.
|
|
1069
|
+
const leafModule = new EvmIsmModule(multiProvider, {
|
|
1070
|
+
chain,
|
|
1071
|
+
config: delayedConfig,
|
|
1072
|
+
addresses: {
|
|
1073
|
+
...factoryAddresses,
|
|
1074
|
+
mailbox: mailboxAddress,
|
|
1075
|
+
deployedIsm: await hybridLeaf(ism),
|
|
1076
|
+
},
|
|
1077
|
+
});
|
|
1078
|
+
// The escape hatch skips the composition rules, so it must not accept a
|
|
1079
|
+
// config that composes anything.
|
|
1080
|
+
await expect(leafModule.updateDeployedInstance(hybridAgg(delayedConfig))).to.be.rejectedWith('composed');
|
|
1081
|
+
// ...while the leaf node it exists for still reconciles.
|
|
1082
|
+
delayedConfig.remoteIsms = {
|
|
1083
|
+
[TestChainName.test2]: addressToBytes32(randomAddress()).toLowerCase(),
|
|
1084
|
+
};
|
|
1085
|
+
const txs = await leafModule.updateDeployedInstance(delayedConfig);
|
|
1086
|
+
expect(txs.length).to.equal(1);
|
|
1087
|
+
for (const tx of txs) {
|
|
1088
|
+
await multiProvider.sendTransaction(chain, tx);
|
|
1089
|
+
}
|
|
1090
|
+
});
|
|
1091
|
+
it('transfers delayedFlowRouterHookIsm ownership after enrollment updates', async () => {
|
|
1092
|
+
const signerAddress = await multiProvider.getSignerAddress(chain);
|
|
1093
|
+
const delayedConfig = {
|
|
1094
|
+
type: IsmType.DELAYED_FLOW_ROUTER,
|
|
1095
|
+
warpRouter: warpRouterAddress,
|
|
1096
|
+
thresholdBps: 10000,
|
|
1097
|
+
maxDelay: 3600,
|
|
1098
|
+
duration: 86400n,
|
|
1099
|
+
owner: signerAddress,
|
|
1100
|
+
remoteIsms: {},
|
|
1101
|
+
};
|
|
1102
|
+
const { ism, initialIsmAddress } = await createIsm(hybridAgg(delayedConfig));
|
|
1103
|
+
// enroll a router AND hand over ownership in the same update: the txs
|
|
1104
|
+
// only execute successfully if enrollment (owner-gated) precedes the
|
|
1105
|
+
// ownership transfer
|
|
1106
|
+
const newOwner = randomAddress();
|
|
1107
|
+
delayedConfig.remoteIsms = {
|
|
1108
|
+
[TestChainName.test2]: addressToBytes32(randomAddress()).toLowerCase(),
|
|
1109
|
+
};
|
|
1110
|
+
delayedConfig.owner = newOwner;
|
|
1111
|
+
await expectTxsAndUpdate(ism, hybridAgg(delayedConfig), 2);
|
|
1112
|
+
expect(eqAddress(initialIsmAddress, ism.serialize().deployedIsm)).to.be
|
|
1113
|
+
.true;
|
|
1114
|
+
const delayedIsm = DelayedFlowRouterHookIsm__factory.connect(await hybridLeaf(ism), multiProvider.getProvider(chain));
|
|
1115
|
+
expect((await delayedIsm.owner()).toLowerCase()).to.equal(newOwner.toLowerCase());
|
|
1116
|
+
});
|
|
1117
|
+
it('redeploys a new delayedFlowRouterHookIsm on maxDelay change (immutable)', async () => {
|
|
1118
|
+
const signerAddress = await multiProvider.getSignerAddress(chain);
|
|
1119
|
+
const delayedConfig = {
|
|
1120
|
+
type: IsmType.DELAYED_FLOW_ROUTER,
|
|
1121
|
+
warpRouter: warpRouterAddress,
|
|
1122
|
+
thresholdBps: 10000,
|
|
1123
|
+
maxDelay: 3600,
|
|
1124
|
+
duration: 86400n,
|
|
1125
|
+
owner: signerAddress,
|
|
1126
|
+
remoteIsms: {},
|
|
1127
|
+
};
|
|
1128
|
+
const { ism, initialIsmAddress } = await createIsm(hybridAgg(delayedConfig));
|
|
1129
|
+
// maxDelay is immutable on-chain; changing it must redeploy
|
|
1130
|
+
delayedConfig.maxDelay = 7200;
|
|
1131
|
+
// update() redeploys internally and emits no txs
|
|
1132
|
+
await expectTxsAndUpdate(ism, hybridAgg(delayedConfig), 0);
|
|
1133
|
+
// different contract address — redeployed
|
|
1134
|
+
expect(eqAddress(initialIsmAddress, ism.serialize().deployedIsm)).to.be
|
|
1135
|
+
.false;
|
|
1136
|
+
const delayedIsm = DelayedFlowRouterHookIsm__factory.connect(await hybridLeaf(ism), multiProvider.getProvider(chain));
|
|
1137
|
+
expect(await delayedIsm.maxDelay()).to.equal(7200);
|
|
1138
|
+
});
|
|
1139
|
+
});
|
|
1140
|
+
describe('delayed flow router cross-chain enrollment', async () => {
|
|
1141
|
+
const remoteChain = TestChainName.test2;
|
|
1142
|
+
const manualChain = TestChainName.test3;
|
|
1143
|
+
function registryAddresses() {
|
|
1144
|
+
return { [chain]: { ...factoryAddresses, mailbox: mailboxAddress } };
|
|
1145
|
+
}
|
|
1146
|
+
async function enrollmentTxs(allTargets) {
|
|
1147
|
+
return buildDelayedFlowEnrollmentTxs({
|
|
1148
|
+
chain,
|
|
1149
|
+
multiProvider,
|
|
1150
|
+
registryAddresses: registryAddresses(),
|
|
1151
|
+
warpRouter: warpRouterAddress,
|
|
1152
|
+
target: allTargets[chain],
|
|
1153
|
+
allTargets,
|
|
1154
|
+
});
|
|
1155
|
+
}
|
|
1156
|
+
async function applyTxs(txs) {
|
|
1157
|
+
for (const tx of txs) {
|
|
1158
|
+
await multiProvider.sendTransaction(chain, tx);
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
1161
|
+
it('enrolls the pairing derived from the route and converges on re-run', async () => {
|
|
1162
|
+
const signerAddress = await multiProvider.getSignerAddress(chain);
|
|
1163
|
+
const delayedConfig = {
|
|
1164
|
+
type: IsmType.DELAYED_FLOW_ROUTER,
|
|
1165
|
+
warpRouter: warpRouterAddress,
|
|
1166
|
+
thresholdBps: 10000,
|
|
1167
|
+
maxDelay: 3600,
|
|
1168
|
+
duration: 86400n,
|
|
1169
|
+
owner: signerAddress,
|
|
1170
|
+
};
|
|
1171
|
+
const { ism } = await createIsm(hybridAgg(delayedConfig));
|
|
1172
|
+
const remoteIsmAddress = randomAddress();
|
|
1173
|
+
const allTargets = {
|
|
1174
|
+
[chain]: {
|
|
1175
|
+
ismAddress: await hybridLeaf(ism),
|
|
1176
|
+
userNode: delayedConfig,
|
|
1177
|
+
},
|
|
1178
|
+
[remoteChain]: {
|
|
1179
|
+
ismAddress: remoteIsmAddress,
|
|
1180
|
+
userNode: delayedConfig,
|
|
1181
|
+
},
|
|
1182
|
+
};
|
|
1183
|
+
const txs = await enrollmentTxs(allTargets);
|
|
1184
|
+
expect(txs.length).to.equal(1);
|
|
1185
|
+
await applyTxs(txs);
|
|
1186
|
+
expect((await enrollmentTxs(allTargets)).length).to.equal(0);
|
|
1187
|
+
const delayedIsm = DelayedFlowRouterHookIsm__factory.connect(await hybridLeaf(ism), multiProvider.getProvider(chain));
|
|
1188
|
+
expect(await delayedIsm.domains()).to.deep.equal([
|
|
1189
|
+
multiProvider.getDomainId(remoteChain),
|
|
1190
|
+
]);
|
|
1191
|
+
// keep testConfig (same reference) in sync for the afterEach read-back
|
|
1192
|
+
delayedConfig.remoteIsms = {
|
|
1193
|
+
[remoteChain]: addressToBytes32(remoteIsmAddress).toLowerCase(),
|
|
1194
|
+
};
|
|
1195
|
+
});
|
|
1196
|
+
it('enrolls a configured external remoteIsms alongside the derived pairing and converges on re-run', async () => {
|
|
1197
|
+
const signerAddress = await multiProvider.getSignerAddress(chain);
|
|
1198
|
+
const delayedConfig = {
|
|
1199
|
+
type: IsmType.DELAYED_FLOW_ROUTER,
|
|
1200
|
+
warpRouter: warpRouterAddress,
|
|
1201
|
+
thresholdBps: 10000,
|
|
1202
|
+
maxDelay: 3600,
|
|
1203
|
+
duration: 86400n,
|
|
1204
|
+
owner: signerAddress,
|
|
1205
|
+
remoteIsms: {},
|
|
1206
|
+
};
|
|
1207
|
+
const { ism } = await createIsm(hybridAgg(delayedConfig));
|
|
1208
|
+
// An operator-authored pairing names a chain the route never derives.
|
|
1209
|
+
// It is retained alongside the route-managed counterpart.
|
|
1210
|
+
const manualRouter = addressToBytes32(randomAddress()).toLowerCase();
|
|
1211
|
+
delayedConfig.remoteIsms = { [manualChain]: manualRouter };
|
|
1212
|
+
const derivedRouterAddress = randomAddress();
|
|
1213
|
+
const derivedRouter = addressToBytes32(derivedRouterAddress).toLowerCase();
|
|
1214
|
+
const allTargets = {
|
|
1215
|
+
[chain]: {
|
|
1216
|
+
ismAddress: await hybridLeaf(ism),
|
|
1217
|
+
userNode: delayedConfig,
|
|
1218
|
+
},
|
|
1219
|
+
[remoteChain]: {
|
|
1220
|
+
ismAddress: derivedRouterAddress,
|
|
1221
|
+
userNode: delayedConfig,
|
|
1222
|
+
},
|
|
1223
|
+
};
|
|
1224
|
+
const txs = await enrollmentTxs(allTargets);
|
|
1225
|
+
expect(txs.length).to.equal(1);
|
|
1226
|
+
await applyTxs(txs);
|
|
1227
|
+
expect((await enrollmentTxs(allTargets)).length).to.equal(0);
|
|
1228
|
+
const delayedIsm = DelayedFlowRouterHookIsm__factory.connect(await hybridLeaf(ism), multiProvider.getProvider(chain));
|
|
1229
|
+
const manualDomain = multiProvider.getDomainId(manualChain);
|
|
1230
|
+
const derivedDomain = multiProvider.getDomainId(remoteChain);
|
|
1231
|
+
expect(await delayedIsm.domains()).to.have.members([
|
|
1232
|
+
manualDomain,
|
|
1233
|
+
derivedDomain,
|
|
1234
|
+
]);
|
|
1235
|
+
expect(await delayedIsm.routers(manualDomain)).to.equal(manualRouter);
|
|
1236
|
+
expect(await delayedIsm.routers(derivedDomain)).to.equal(derivedRouter);
|
|
1237
|
+
// keep testConfig (same reference) in sync for the afterEach read-back
|
|
1238
|
+
delayedConfig.remoteIsms = {
|
|
1239
|
+
[manualChain]: manualRouter,
|
|
1240
|
+
[remoteChain]: derivedRouter,
|
|
1241
|
+
};
|
|
1242
|
+
});
|
|
768
1243
|
});
|
|
769
1244
|
});
|
|
770
1245
|
//# sourceMappingURL=EvmIsmModule.hardhat-test.js.map
|