Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: improve live revert tests #225

Merged
merged 6 commits into from
Aug 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion contracts/governance/InterchainGovernance.sol
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ contract InterchainGovernance is AxelarExecutable, TimeLock, Caller, IInterchain
}

/**
* @notice Making contact able to receive native value
* @notice Allow contract to receive native gas token
*/
receive() external payable {}
}
153 changes: 94 additions & 59 deletions test/AxelarGateway.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const {
constants: { AddressZero, HashZero },
} = ethers;
const { expect } = chai;
const { isHardhat, getChainId, getEVMVersion, getGasOptions, getRandomString } = require('./utils');
const { isHardhat, getChainId, getEVMVersion, getGasOptions, getRandomString, expectRevert } = require('./utils');
const { getBytecodeHash } = require('@axelar-network/axelar-contract-deployments');

const {
Expand Down Expand Up @@ -110,19 +110,20 @@ describe('AxelarGateway', () => {
};

describe('constructor checks', () => {
before(async () => {
await deployGateway();
});

it('should revert if auth module is not a contract', async () => {
await expect(gatewayFactory.deploy(owner.address, tokenDeployer.address)).to.be.revertedWithCustomError(
gateway,
await expectRevert(
(gasOptions) => gatewayFactory.deploy(owner.address, externalToken.address, gasOptions),
gatewayFactory,
'InvalidAuthModule',
);
});

it('should revert if token deployer is not a contract', async () => {
await expect(gatewayFactory.deploy(auth.address, owner.address)).to.be.revertedWithCustomError(gateway, 'InvalidTokenDeployer');
await expectRevert(
(gasOptions) => gatewayFactory.deploy(externalToken.address, owner.address, gasOptions),
gatewayFactory,
'InvalidTokenDeployer',
);
});
});

Expand Down Expand Up @@ -160,27 +161,27 @@ describe('AxelarGateway', () => {
});

it('should fail on external call to deployToken', async () => {
await expect(gateway.deployToken(params, HashZero)).to.be.revertedWithCustomError(gateway, 'NotSelf');
await expectRevert((gasOptions) => gateway.deployToken(params, HashZero, gasOptions), gateway, 'NotSelf');
});

it('should fail on external call to mintToken', async () => {
await expect(gateway.mintToken(params, HashZero)).to.be.revertedWithCustomError(gateway, 'NotSelf');
await expectRevert((gasOptions) => gateway.mintToken(params, HashZero, gasOptions), gateway, 'NotSelf');
});

it('should fail on external call to burnToken', async () => {
await expect(gateway.burnToken(params, HashZero)).to.be.revertedWithCustomError(gateway, 'NotSelf');
await expectRevert((gasOptions) => gateway.burnToken(params, HashZero, gasOptions), gateway, 'NotSelf');
});

it('should fail on external call to approveContractCall', async () => {
await expect(gateway.approveContractCall(params, HashZero)).to.be.revertedWithCustomError(gateway, 'NotSelf');
await expectRevert((gasOptions) => gateway.approveContractCall(params, HashZero, gasOptions), gateway, 'NotSelf');
});

it('should fail on external call to approveContractCallWithMint', async () => {
await expect(gateway.approveContractCallWithMint(params, HashZero)).to.be.revertedWithCustomError(gateway, 'NotSelf');
await expectRevert((gasOptions) => gateway.approveContractCallWithMint(params, HashZero, gasOptions), gateway, 'NotSelf');
});

it('should fail on external call to transferOperatorship', async () => {
await expect(gateway.transferOperatorship(params, HashZero)).to.be.revertedWithCustomError(gateway, 'NotSelf');
await expectRevert((gasOptions) => gateway.transferOperatorship(params, HashZero, gasOptions), gateway, 'NotSelf');
});
});

Expand Down Expand Up @@ -267,24 +268,28 @@ describe('AxelarGateway', () => {
const limit = getRandomInt(Number.MAX_SAFE_INTEGER);
const limits = symbols.map(() => limit);

await expect(gateway.connect(notGovernance).setTokenMintLimits(symbols, limits)).to.be.revertedWithCustomError(
await expectRevert(
(gasOptions) => gateway.connect(notGovernance).setTokenMintLimits(symbols, limits, gasOptions),
gateway,
'NotMintLimiter',
);

const invalidLimits = [...limits];
invalidLimits.pop();

await expect(gateway.connect(governance).setTokenMintLimits(symbols, invalidLimits)).to.be.revertedWithCustomError(
await expectRevert(
(gasOptions) => gateway.connect(governance).setTokenMintLimits(symbols, invalidLimits, gasOptions),
gateway,
'InvalidSetMintLimitsParams',
);

const invalidSymbols = ['TokenX', 'TokenY'];

await expect(gateway.connect(governance).setTokenMintLimits(invalidSymbols, limits))
.to.be.revertedWithCustomError(gateway, 'TokenDoesNotExist')
.withArgs(invalidSymbols[0]);
await expectRevert(
(gasOptions) => gateway.connect(governance).setTokenMintLimits(invalidSymbols, limits, gasOptions),
gateway,
'TokenDoesNotExist',
);

await gateway
.connect(governance)
Expand All @@ -306,12 +311,14 @@ describe('AxelarGateway', () => {
});

it('should allow transferring governance', async () => {
await expect(gateway.connect(notGovernance).transferGovernance(governance.address)).to.be.revertedWithCustomError(
await expectRevert(
(gasOptions) => gateway.connect(notGovernance).transferGovernance(governance.address, gasOptions),
gateway,
'NotGovernance',
);

await expect(gateway.connect(governance).transferGovernance(AddressZero)).to.be.revertedWithCustomError(
await expectRevert(
(gasOptions) => gateway.connect(governance).transferGovernance(AddressZero, gasOptions),
gateway,
'InvalidGovernance',
);
Expand All @@ -320,7 +327,8 @@ describe('AxelarGateway', () => {
.to.emit(gateway, 'GovernanceTransferred')
.withArgs(governance.address, notGovernance.address);

await expect(gateway.connect(governance).transferGovernance(governance.address)).to.be.revertedWithCustomError(
await expectRevert(
(gasOptions) => gateway.connect(governance).transferGovernance(governance.address, gasOptions),
gateway,
'NotGovernance',
);
Expand All @@ -331,12 +339,14 @@ describe('AxelarGateway', () => {
it('should allow transferring mint limiter', async () => {
const notMintLimiter = notGovernance;

await expect(gateway.connect(notMintLimiter).transferMintLimiter(notMintLimiter.address)).to.be.revertedWithCustomError(
await expectRevert(
(gasOptions) => gateway.connect(notMintLimiter).transferMintLimiter(notMintLimiter.address, gasOptions),
gateway,
'NotMintLimiter',
);

await expect(gateway.connect(mintLimiter).transferMintLimiter(AddressZero)).to.be.revertedWithCustomError(
await expectRevert(
(gasOptions) => gateway.connect(mintLimiter).transferMintLimiter(AddressZero, gasOptions),
gateway,
'InvalidMintLimiter',
);
Expand Down Expand Up @@ -364,15 +374,16 @@ describe('AxelarGateway', () => {
const newGatewayImplementationCodeHash = await getBytecodeHash(newGatewayImplementation, network.config.id);
const params = '0x';

await expect(
gateway.connect(notGovernance).upgrade(newGatewayImplementation.address, newGatewayImplementationCodeHash, params),
).to.be.revertedWithCustomError(gateway, 'NotGovernance');
await expectRevert(
(gasOptions) =>
gateway
.connect(notGovernance)
.upgrade(newGatewayImplementation.address, newGatewayImplementationCodeHash, params, gasOptions),
gateway,
'NotGovernance',
);

await expect(
gateway
.connect(governance)
.upgrade(newGatewayImplementation.address, newGatewayImplementationCodeHash, params, getGasOptions()),
)
await expect(gateway.connect(governance).upgrade(newGatewayImplementation.address, newGatewayImplementationCodeHash, params))
.to.emit(gateway, 'Upgraded')
.withArgs(newGatewayImplementation.address)
.to.not.emit(gateway, 'GovernanceTransferred')
Expand All @@ -382,12 +393,16 @@ describe('AxelarGateway', () => {
it('should allow governance to upgrade to the correct implementation with new governance and mint limiter', async () => {
const newGatewayImplementation = await gatewayFactory.deploy(auth.address, tokenDeployer.address).then((d) => d.deployed());
const newGatewayImplementationCodeHash = await getBytecodeHash(newGatewayImplementation, network.config.id);

let params = '0x';

await expect(
gateway.connect(notGovernance).upgrade(newGatewayImplementation.address, newGatewayImplementationCodeHash, params),
).to.be.revertedWithCustomError(gateway, 'NotGovernance');
await expectRevert(
(gasOptions) =>
gateway
.connect(notGovernance)
.upgrade(newGatewayImplementation.address, newGatewayImplementationCodeHash, params, gasOptions),
gateway,
'NotGovernance',
);

params = getWeightedProxyDeployParams(notGovernance.address, notGovernance.address, []);

Expand Down Expand Up @@ -493,17 +508,24 @@ describe('AxelarGateway', () => {

const params = getWeightedProxyDeployParams(governance.address, mintLimiter.address, newOperatorAddresses, Array(2).fill(1), 2);

await expect(
gateway.connect(notGovernance).upgrade(newGatewayImplementation.address, wrongCodeHash, params),
).to.be.revertedWithCustomError(gateway, 'NotGovernance');
await expectRevert(
(gasOptions) => gateway.connect(notGovernance).upgrade(newGatewayImplementation.address, wrongCodeHash, params, gasOptions),
gateway,
'NotGovernance',
);

await expect(
gateway.connect(governance).upgrade(newGatewayImplementation.address, wrongCodeHash, params),
).to.be.revertedWithCustomError(gateway, 'InvalidCodeHash');
await expectRevert(
(gasOptions) => gateway.connect(governance).upgrade(newGatewayImplementation.address, wrongCodeHash, params, gasOptions),
gateway,
'InvalidCodeHash',
);

await expect(
gateway.connect(governance).upgrade(wrongImplementation.address, wrongImplementationCodeHash, params),
).to.be.revertedWithCustomError(gateway, 'InvalidImplementation');
await expectRevert(
(gasOptions) =>
gateway.connect(governance).upgrade(wrongImplementation.address, wrongImplementationCodeHash, params, gasOptions),
gateway,
'InvalidImplementation',
);
});

it('should not allow calling the setup function directly', async () => {
Expand All @@ -515,7 +537,7 @@ describe('AxelarGateway', () => {

const implementation = gatewayFactory.attach(await gateway.implementation());

await expect(implementation.connect(governance).setup(params)).to.be.revertedWithCustomError(implementation, 'NotProxy');
await expectRevert((gasOptions) => implementation.connect(governance).setup(params, gasOptions), gateway, 'NotProxy');
});

it('should not allow malicious proxy to call setup function directly and transfer governance or mint limiter', async () => {
Expand All @@ -540,9 +562,14 @@ describe('AxelarGateway', () => {

const implementation = gatewayFactory.attach(await gateway.implementation());

await expect(
implementation.connect(notGovernance).upgrade(newGatewayImplementation.address, newGatewayImplementationCodeHash, params),
).to.be.revertedWithCustomError(implementation, 'NotGovernance');
await expectRevert(
(gasOptions) =>
implementation
.connect(notGovernance)
.upgrade(newGatewayImplementation.address, newGatewayImplementationCodeHash, params, gasOptions),
gateway,
'NotGovernance',
);
});

it('should revert on upgrade if setup fails for any reason', async () => {
Expand All @@ -552,9 +579,14 @@ describe('AxelarGateway', () => {
// invalid setup params
const params = '0x1234';

await expect(
gateway.connect(governance).upgrade(newGatewayImplementation.address, newGatewayImplementationCodeHash, params),
).to.be.revertedWithCustomError(gateway, 'SetupFailed');
await expectRevert(
(gasOptions) =>
gateway
.connect(governance)
.upgrade(newGatewayImplementation.address, newGatewayImplementationCodeHash, params, gasOptions),
gateway,
'SetupFailed',
);
});
});

Expand Down Expand Up @@ -585,7 +617,7 @@ describe('AxelarGateway', () => {
operators.slice(0, threshold),
);

await expect(gateway.execute(input)).to.be.revertedWithCustomError(gateway, 'InvalidChainId');
await expectRevert((gasOptions) => gateway.execute(input, gasOptions), gateway, 'InvalidChainId');
});
});

Expand Down Expand Up @@ -1688,7 +1720,7 @@ describe('AxelarGateway', () => {
operators.slice(0, threshold),
);

await expect(gateway.execute(input)).to.be.revertedWithCustomError(gateway, 'InvalidCommands');
await expectRevert((gasOptions) => gateway.execute(input, gasOptions), gateway, 'InvalidCommands');

data = buildCommandBatch(
await getChainId(),
Expand All @@ -1699,7 +1731,7 @@ describe('AxelarGateway', () => {

input = await getSignedWeightedExecuteInput(data, operators, getWeights(operators), threshold, operators.slice(0, threshold));

await expect(gateway.execute(input)).to.be.revertedWithCustomError(gateway, 'InvalidCommands');
await expectRevert((gasOptions) => gateway.execute(input, gasOptions), gateway, 'InvalidCommands');
});

it('should batch execute multiple commands and skip any unknown commands', async () => {
Expand Down Expand Up @@ -1821,9 +1853,11 @@ describe('AxelarGateway', () => {
const destination = '0xb7900E8Ec64A1D1315B6D4017d4b1dcd36E6Ea88';
const payload = defaultAbiCoder.encode(['address', 'address'], [owner.address, destination]);

await expect(
gateway.callContractWithToken(chain, destination, payload, invalidTokenSymbol, amount),
).to.be.revertedWithCustomError(gateway, 'TokenDoesNotExist');
await expectRevert(
(gasOptions) => gateway.callContractWithToken(chain, destination, payload, invalidTokenSymbol, amount, gasOptions),
gateway,
'TokenDoesNotExist',
);
});

it('should revert if token amount is invalid', async () => {
Expand All @@ -1832,7 +1866,8 @@ describe('AxelarGateway', () => {
const destination = '0xb7900E8Ec64A1D1315B6D4017d4b1dcd36E6Ea88';
const payload = defaultAbiCoder.encode(['address', 'address'], [owner.address, destination]);

await expect(gateway.callContractWithToken(chain, destination, payload, tokenSymbol, amount)).to.be.revertedWithCustomError(
await expectRevert(
(gasOptions) => gateway.callContractWithToken(chain, destination, payload, tokenSymbol, amount, gasOptions),
gateway,
'InvalidAmount',
);
Expand Down
11 changes: 11 additions & 0 deletions test/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const {
} = ethers;
const { network } = require('hardhat');
const { sortBy } = require('lodash');
const { expect } = require('chai');

const getRandomInt = (max) => {
return Math.floor(Math.random() * max);
Expand Down Expand Up @@ -76,6 +77,14 @@ const getEVMVersion = () => {
return config.solidity.compilers[0].settings.evmVersion;
};

const expectRevert = async (txFunc, contract, error) => {
if (network.config.contracts?.skipRevertTests) {
await expect(txFunc(getGasOptions())).to.be.reverted;
} else {
await expect(txFunc(null)).to.be.revertedWithCustomError(contract, error);
}
};

module.exports = {
getChainId: async () => await network.provider.send('eth_chainId'),

Expand All @@ -93,6 +102,8 @@ module.exports = {

waitFor,

expectRevert,

getSignedMultisigExecuteInput: async (data, operators, signers) =>
defaultAbiCoder.encode(['bytes', 'bytes'], [data, await getSignaturesProof(data, operators, signers)]),

Expand Down
Loading