@hyperlane-xyz/sdk
Version:
The official SDK for the Hyperlane Network
168 lines • 8.25 kB
JavaScript
import { ChildToParentMessageStatus } from '@arbitrum/sdk';
import { expect } from 'chai';
import { BigNumber } from 'ethers';
import hre from 'hardhat';
import { before } from 'mocha';
import sinon from 'sinon';
import { ArbL2ToL1Hook__factory, ArbL2ToL1Ism__factory, MockArbBridge__factory, MockArbSys__factory, } from '@hyperlane-xyz/core';
import { bytes32ToAddress, objMap, } from '@hyperlane-xyz/utils';
import { testChains } from '../../consts/testChains.js';
import { TestCoreDeployer } from '../../core/TestCoreDeployer.js';
import { TestRecipientDeployer } from '../../core/TestRecipientDeployer.js';
import { HyperlaneProxyFactoryDeployer } from '../../deploy/HyperlaneProxyFactoryDeployer.js';
import { EvmHookModule } from '../../hook/EvmHookModule.js';
import { HookType } from '../../hook/types.js';
import { MultiProvider } from '../../providers/MultiProvider.js';
import { EvmIsmReader } from '../EvmIsmReader.js';
import { HyperlaneIsmFactory } from '../HyperlaneIsmFactory.js';
import { ArbL2ToL1MetadataBuilder } from './arbL2ToL1.js';
describe('ArbL2ToL1MetadataBuilder', () => {
const origin = 'test4';
const destination = 'test2';
let core;
let ismFactory;
let hookConfig;
let arbL2ToL1Hook;
let arbL2ToL1Ism;
let arbBridge;
let testRecipients;
let proxyFactoryAddresses;
let factoryContracts;
let relayer;
let metadataBuilder;
let context;
function setArbitrumBridgeStatus(status) {
sinon
.stub(metadataBuilder, 'getArbitrumBridgeStatus')
.callsFake(async () => {
return status;
});
}
before(async () => {
[relayer] = await hre.ethers.getSigners();
const multiProvider = MultiProvider.createTestMultiProvider({
signer: relayer,
});
const ismFactoryDeployer = new HyperlaneProxyFactoryDeployer(multiProvider);
const contractsMap = await ismFactoryDeployer.deploy(multiProvider.mapKnownChains(() => ({})));
ismFactory = new HyperlaneIsmFactory(contractsMap, multiProvider);
const coreDeployer = new TestCoreDeployer(multiProvider, ismFactory);
const recipientDeployer = new TestRecipientDeployer(multiProvider);
testRecipients = objMap(await recipientDeployer.deploy(Object.fromEntries(testChains.map((c) => [c, {}]))), (_, { testRecipient }) => testRecipient);
core = await coreDeployer.deployApp();
const mockArbSys = await multiProvider.handleDeploy(origin, new MockArbSys__factory(), []);
hookConfig = {
test4: {
type: HookType.ARB_L2_TO_L1,
arbSys: mockArbSys.address,
destinationChain: destination,
childHook: {
type: HookType.INTERCHAIN_GAS_PAYMASTER,
beneficiary: relayer.address,
owner: relayer.address,
oracleKey: relayer.address,
overhead: {
[destination]: 200000,
},
oracleConfig: {
[destination]: {
gasPrice: '20',
tokenExchangeRate: '10000000000',
},
},
},
},
};
factoryContracts = contractsMap.test4;
proxyFactoryAddresses = Object.keys(factoryContracts).reduce((acc, key) => {
acc[key] =
contractsMap[origin][key].address;
return acc;
}, {});
arbBridge = await multiProvider.handleDeploy(origin, new MockArbBridge__factory(), []);
hookConfig.test4.bridge = arbBridge.address;
const hookModule = await EvmHookModule.create({
chain: origin,
config: hookConfig.test4,
proxyFactoryFactories: proxyFactoryAddresses,
coreAddresses: core.getAddresses(origin),
multiProvider,
});
const hookAddress = hookModule.serialize().deployedHook;
arbL2ToL1Hook = ArbL2ToL1Hook__factory.connect(hookAddress, relayer);
metadataBuilder = new ArbL2ToL1MetadataBuilder(core);
});
describe('#build', () => {
let metadata;
beforeEach(async () => {
const testRecipient = testRecipients[destination];
arbL2ToL1Ism = ArbL2ToL1Ism__factory.connect(bytes32ToAddress(await arbL2ToL1Hook.ism()), relayer);
await testRecipient.setInterchainSecurityModule(arbL2ToL1Ism.address);
const { dispatchTx, message } = await core.sendMessage(origin, destination, testRecipient.address, '0xdeadbeef', arbL2ToL1Hook.address);
const derivedIsm = await new EvmIsmReader(core.multiProvider, destination).deriveIsmConfig(arbL2ToL1Ism.address);
context = {
hook: { ...hookConfig[origin], address: arbL2ToL1Hook.address },
ism: derivedIsm,
message,
dispatchTx,
};
sinon
.stub(metadataBuilder, 'getArbitrumOutboxProof')
.callsFake(async () => {
await arbBridge.setL2ToL1Sender(arbL2ToL1Hook.address);
return [];
});
});
it(`should build valid metadata using direct executeTransaction call`, async () => {
setArbitrumBridgeStatus(ChildToParentMessageStatus.CONFIRMED);
metadata = await metadataBuilder.build(context);
await arbL2ToL1Ism.verify(metadata, context.message.message);
});
it(`should throw an error if the message has already been relayed`, async () => {
setArbitrumBridgeStatus(ChildToParentMessageStatus.EXECUTED);
try {
await metadataBuilder.build(context);
expect.fail('Expected an error to be thrown');
}
catch (error) {
expect(error.message).to.include('Arbitrum L2ToL1 message has already been executed');
}
await expect(arbL2ToL1Ism.verify(metadata, context.message.message)).to.be.revertedWith('ArbL2ToL1Ism: invalid message id');
});
it(`should throw an error if the challenge period hasn't passed`, async () => {
setArbitrumBridgeStatus(ChildToParentMessageStatus.UNCONFIRMED);
// stub waiting period to 10 blocks
sinon
.stub(metadataBuilder, 'getWaitingBlocksUntilReady')
.callsFake(async () => {
return BigNumber.from(10); // test waiting period
});
try {
await metadataBuilder.build(context);
expect.fail('Expected an error to be thrown');
}
catch (error) {
expect(error.message).to.include("Arbitrum L2ToL1 message isn't ready for relay. Wait 10 blocks until the challenge period before relaying again");
}
await expect(arbL2ToL1Ism.verify(metadata, context.message.message)).to.be.revertedWith('ArbL2ToL1Ism: invalid message id');
});
it(`should build valid metadata if already preverified by 3rd party relayer`, async () => {
setArbitrumBridgeStatus(ChildToParentMessageStatus.CONFIRMED);
const calldata = await metadataBuilder.buildArbitrumBridgeCalldata(context);
metadata = ArbL2ToL1MetadataBuilder.encodeArbL2ToL1Metadata(calldata);
await arbBridge.executeTransaction(calldata.proof, calldata.position, calldata.caller, calldata.destination, calldata.arbBlockNum, calldata.ethBlockNum, calldata.timestamp, BigNumber.from(0), // msg.value
calldata.data);
metadata = await metadataBuilder.build(context);
await arbL2ToL1Ism.verify(metadata, context.message.message);
});
it(`should decode metadata`, async () => {
setArbitrumBridgeStatus(ChildToParentMessageStatus.CONFIRMED);
metadata = await metadataBuilder.build(context);
ArbL2ToL1MetadataBuilder.decode(metadata, context);
});
});
afterEach(() => {
sinon.restore();
});
});
//# sourceMappingURL=arbL2ToL1.hardhat-test.js.map