Skip to content

Commit 91cb0ac

Browse files
committed
workflow testing (active and passive)
1 parent a8ee58b commit 91cb0ac

File tree

5 files changed

+115
-132
lines changed

5 files changed

+115
-132
lines changed

contracts/crosschain/mocks/AxelarGatewayMock.sol

Lines changed: 17 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -8,45 +8,24 @@ import {BitMaps} from "@openzeppelin/contracts/utils/structs/BitMaps.sol";
88
import {Strings} from "@openzeppelin/contracts/utils/Strings.sol";
99
import {StringsUnreleased} from "../../utils/Strings.sol";
1010

11-
contract AxelarGatewayActiveMock {
12-
using Strings for address;
13-
14-
function callContract(
15-
string calldata destinationChain,
16-
string calldata destinationContractAddress,
17-
bytes calldata payload
18-
) external {
19-
// TODO: check that destination chain is local
20-
21-
emit IAxelarGateway.ContractCall(
22-
msg.sender,
23-
destinationChain,
24-
destinationContractAddress,
25-
keccak256(payload),
26-
payload
27-
);
28-
29-
// NOTE:
30-
// - no commandId in this mock
31-
// - source chain and destination chain are the same in this mock
32-
address target = StringsUnreleased.parseAddress(destinationContractAddress);
33-
IAxelarExecutable(target).execute(bytes32(0), destinationChain, msg.sender.toHexString(), payload);
34-
}
35-
}
36-
37-
contract AxelarGatewayPassiveMock {
11+
contract AxelarGatewayMock {
3812
using Strings for address;
3913
using BitMaps for BitMaps.BitMap;
4014

15+
bool private activeMode;
4116
BitMaps.BitMap private pendingCommandIds;
4217

43-
event NewCommandId(
18+
event CommandIdPending(
4419
bytes32 indexed commandId,
4520
string destinationChain,
4621
string destinationContractAddress,
4722
bytes payload
4823
);
4924

25+
function setActive(bool enabled) public {
26+
activeMode = enabled;
27+
}
28+
5029
function callContract(
5130
string calldata destinationChain,
5231
string calldata destinationContractAddress,
@@ -69,7 +48,14 @@ contract AxelarGatewayPassiveMock {
6948
require(!pendingCommandIds.get(uint256(commandId)));
7049
pendingCommandIds.set(uint256(commandId));
7150

72-
emit NewCommandId(commandId, destinationChain, destinationContractAddress, payload);
51+
emit CommandIdPending(commandId, destinationChain, destinationContractAddress, payload);
52+
53+
if (activeMode) {
54+
// NOTE:
55+
// - source chain and destination chain are the same in this mock
56+
address target = StringsUnreleased.parseAddress(destinationContractAddress);
57+
IAxelarExecutable(target).execute(commandId, destinationChain, msg.sender.toHexString(), payload);
58+
}
7359
}
7460

7561
function validateContractCall(
@@ -81,6 +67,8 @@ contract AxelarGatewayPassiveMock {
8167
if (pendingCommandIds.get(uint256(commandId))) {
8268
pendingCommandIds.unset(uint256(commandId));
8369

70+
emit IAxelarGateway.ContractCallExecuted(commandId);
71+
8472
return
8573
commandId == keccak256(abi.encode(sourceChain, sourceAddress, msg.sender.toHexString(), payloadHash));
8674
} else return false;
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
const { ethers } = require('hardhat');
2+
const { expect } = require('chai');
3+
const { loadFixture } = require('@nomicfoundation/hardhat-network-helpers');
4+
const { anyValue } = require('@nomicfoundation/hardhat-chai-matchers/withArgs');
5+
6+
const getAddress = account => (account.target ?? account.address ?? account).toLowerCase();
7+
8+
async function fixture() {
9+
const [owner, sender, ...accounts] = await ethers.getSigners();
10+
11+
const { chainId } = await ethers.provider.getNetwork();
12+
const CAIP2 = `eip155:${chainId}`;
13+
const asCAIP10 = account => `eip155:${chainId}:${getAddress(account)}`;
14+
15+
const axelar = await ethers.deployContract('$AxelarGatewayMock');
16+
const srcGateway = await ethers.deployContract('$AxelarGatewaySource', [ owner, axelar ]);
17+
const dstGateway = await ethers.deployContract('$AxelarGatewayDestination', [ owner, axelar, axelar ]);
18+
const receiver = await ethers.deployContract('$GatewayReceiverMock', [ dstGateway ]);
19+
20+
await srcGateway.registerCAIP2Equivalence(CAIP2, 'local');
21+
await dstGateway.registerCAIP2Equivalence(CAIP2, 'local');
22+
await srcGateway.registerRemoteGateway(CAIP2, getAddress(dstGateway));
23+
await dstGateway.registerRemoteGateway(CAIP2, getAddress(srcGateway));
24+
25+
return { owner, sender, accounts, CAIP2, asCAIP10, axelar, srcGateway, dstGateway, receiver };
26+
}
27+
28+
describe('AxelarGateway', function () {
29+
beforeEach(async function () {
30+
Object.assign(this, await loadFixture(fixture));
31+
});
32+
33+
it('initial setup', async function () {
34+
expect(await this.srcGateway.localGateway()).to.equal(this.axelar);
35+
expect(await this.srcGateway.fromCAIP2(this.CAIP2)).to.equal('local');
36+
expect(await this.srcGateway.getRemoteGateway(this.CAIP2)).to.equal(getAddress(this.dstGateway));
37+
38+
expect(await this.dstGateway.localGateway()).to.equal(this.axelar);
39+
expect(await this.dstGateway.fromCAIP2(this.CAIP2)).to.equal('local');
40+
expect(await this.dstGateway.getRemoteGateway(this.CAIP2)).to.equal(getAddress(this.srcGateway));
41+
});
42+
43+
describe('Active mode', function () {
44+
beforeEach(async function () {
45+
await this.axelar.setActive(true);
46+
});
47+
48+
it('workflow', async function () {
49+
const srcCAIP10 = this.asCAIP10(this.sender);
50+
const dstCAIP10 = this.asCAIP10(this.receiver);
51+
const payload = ethers.randomBytes(128);
52+
const attributes = [];
53+
const package = ethers.AbiCoder.defaultAbiCoder().encode([ 'string', 'string', 'bytes', 'bytes[]' ], [ srcCAIP10, dstCAIP10, payload, attributes ]);
54+
55+
const tx = await this.srcGateway.connect(this.sender).sendMessage(this.CAIP2, getAddress(this.receiver), payload, attributes);
56+
await expect(tx)
57+
.to.emit(this.srcGateway, 'MessageCreated').withArgs(ethers.ZeroHash, srcCAIP10, dstCAIP10, payload, attributes)
58+
.to.emit(this.axelar, 'ContractCall').withArgs(this.srcGateway, 'local', getAddress(this.dstGateway), ethers.keccak256(package), package)
59+
.to.emit(this.axelar, 'ContractCallExecuted').withArgs(anyValue)
60+
.to.emit(this.receiver, 'MessageReceived').withArgs(anyValue, this.CAIP2, getAddress(this.sender), payload, attributes);
61+
});
62+
});
63+
64+
describe('Passive mode', function () {
65+
beforeEach(async function () {
66+
await this.axelar.setActive(false);
67+
});
68+
69+
it('workflow', async function () {
70+
const srcCAIP10 = this.asCAIP10(this.sender);
71+
const dstCAIP10 = this.asCAIP10(this.receiver);
72+
const payload = ethers.randomBytes(128);
73+
const attributes = [];
74+
const package = ethers.AbiCoder.defaultAbiCoder().encode([ 'string', 'string', 'bytes', 'bytes[]' ], [ srcCAIP10, dstCAIP10, payload, attributes ]);
75+
76+
const tx = await this.srcGateway.connect(this.sender).sendMessage(this.CAIP2, getAddress(this.receiver), payload, attributes);
77+
await expect(tx)
78+
.to.emit(this.srcGateway, 'MessageCreated').withArgs(ethers.ZeroHash, srcCAIP10, dstCAIP10, payload, attributes)
79+
.to.emit(this.axelar, 'ContractCall').withArgs(this.srcGateway, 'local', getAddress(this.dstGateway), ethers.keccak256(package), package)
80+
.to.emit(this.axelar, 'CommandIdPending').withArgs(anyValue, 'local', getAddress(this.dstGateway), package);
81+
82+
const { logs } = await tx.wait();
83+
const commandIdEvent = logs.find(({ address, topics }) => address == this.axelar.target && topics[0] == this.axelar.interface.getEvent('CommandIdPending').topicHash);
84+
const [ commandId ] = this.axelar.interface.decodeEventLog('CommandIdPending', commandIdEvent.data, commandIdEvent.topics);
85+
86+
await expect(this.receiver.receiveMessage(
87+
this.dstGateway,
88+
commandId, // bytes32 is already self-encoded
89+
this.CAIP2,
90+
getAddress(this.sender),
91+
payload,
92+
attributes,
93+
))
94+
.to.emit(this.axelar, 'ContractCallExecuted').withArgs(commandId)
95+
.to.emit(this.receiver, 'MessageReceived').withArgs(commandId, this.CAIP2, getAddress(this.sender), payload, attributes);
96+
});
97+
});
98+
});

test/crosschain/axelar/AxelarGatewayBase.test.js

Lines changed: 0 additions & 67 deletions
This file was deleted.

test/crosschain/axelar/AxelarGatewayDestination.test.js

Lines changed: 0 additions & 18 deletions
This file was deleted.

test/crosschain/axelar/AxelarGatewaySource.test.js

Lines changed: 0 additions & 18 deletions
This file was deleted.

0 commit comments

Comments
 (0)