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

[Tests] Unit Tests Guide #171

Merged
merged 13 commits into from
Mar 26, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,6 @@ contract CallContractWithToken is AxelarExecutable {
for (uint256 i = 0; i < recipients.length; i++) {
IERC20(tokenAddress).transfer(recipients[i], sentAmount);
}

emit Executed();
}
}
5 changes: 5 additions & 0 deletions examples/evm/call-contract-with-token/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ The aforementioned command pertains to specifying the intended environment for a

An example of its usage is demonstrated as follows: `npm run deploy evm/call-contract-with-token local` or `npm run deploy evm/call-contract-with-token testnet`.

### Test

`cd` into `evm` folder
Run the command ` npx hardhat test call-contract-with-token/tests/call-contract-with-token.test.js`

### Execution

To execute the example, use the following command:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
const { utils } = require('ethers');
const { createNetwork, relay, deployContract } = require('@axelar-network/axelar-local-dev');
const CallContractWithToken = require('../../../../artifacts/examples/evm/call-contract-with-token/CallContractWithToken.sol/CallContractWithToken.json');

const { expect } = require('chai');

describe('Call Contract With Token', async () => {
let polygon;
let avalanche;

let polygonUserWallet;
let avalancheUserWallet;
let avalancheUserWalletTwo;

let deployedContractPolygon;
let deployedContractAvalanche;

let aUSDCPolygon;
let aUSDCAvalanche;

before(async () => {
// Initialize a Polygon network
polygon = await createNetwork({
name: 'Polygon',
});

// Initialize an Avalanche network
avalanche = await createNetwork({
name: 'Avalanche',
});

// Extract user wallets for both networks
[polygonUserWallet] = polygon.userWallets;
[avalancheUserWallet, avalancheUserWalletTwo] = avalanche.userWallets;

// Deploy USDC token on the Polygon network
await polygon.deployToken('USDC', 'aUSDC', 6, BigInt(100_000e6));

// Deploy USDC token on the Fantom network
await avalanche.deployToken('USDC', 'aUSDC', 6, BigInt(100_000e6));

// Get token contracts for both chains
aUSDCPolygon = await polygon.getTokenContract('aUSDC');
aUSDCAvalanche = await avalanche.getTokenContract('aUSDC');

await polygon.giveToken(polygonUserWallet.address, 'aUSDC', BigInt(100e6));
});

describe('src chain', async () => {
beforeEach(async () => {
deployedContractPolygon = await deployContract(polygonUserWallet, CallContractWithToken, [
polygon.gateway.address,
polygon.gasService.address,
]);
deployedContractAvalanche = await deployContract(avalancheUserWallet, CallContractWithToken, [
avalanche.gateway.address,
avalanche.gasService.address,
]);
await aUSDCPolygon.connect(polygonUserWallet).approve(deployedContractPolygon.address, (100e18).toString());
});
afterEach(async () => {
await relay();
});
it('should set correct gateway and gas service addresses on src chain', async () => {
expect(await deployedContractPolygon.gateway()).to.equal(polygon.gateway.address);
expect(await deployedContractPolygon.gasService()).to.equal(polygon.gasService.address);
});
it('should deduct funds from msg.sender', async () => {
const totalSupplyBefore = await aUSDCPolygon.totalSupply();
const myBalanceBefore = await aUSDCPolygon.balanceOf(polygonUserWallet.address);

await deployedContractPolygon.sendToMany(
avalanche.name,
deployedContractAvalanche.address,
[avalancheUserWallet.address, avalancheUserWalletTwo.address],
'aUSDC',
6e6,
{
value: (1e18).toString(),
},
);

const myBalanceAfter = await aUSDCPolygon.balanceOf(polygonUserWallet.address);
const totalSupplyAfter = await aUSDCPolygon.totalSupply();

expect(myBalanceAfter).to.equal(myBalanceBefore - 6e6);
expect(totalSupplyAfter).to.equal(totalSupplyBefore - 6e6); //token was removed by the gateway on the src chain
});
it('should successfully trigger interchain tx', async () => {
const payload = utils.defaultAbiCoder.encode(['address[]'], [[avalancheUserWallet.address, avalancheUserWalletTwo.address]]);
const hashedPayload = utils.keccak256(payload);
await expect(
deployedContractPolygon.sendToMany(
avalanche.name,
deployedContractAvalanche.address,
[avalancheUserWallet.address, avalancheUserWalletTwo.address],
'aUSDC',
6e6,
{
value: (1e18).toString(),
},
),
)
.to.emit(polygon.gateway, 'ContractCallWithToken')
.withArgs(
deployedContractPolygon.address,
avalanche.name,
deployedContractAvalanche.address,
hashedPayload,
payload,
'aUSDC',
6e6,
);
});

it('should pay gas via axelar gas service', async () => {
const payload = utils.defaultAbiCoder.encode(['address[]'], [[avalancheUserWallet.address, avalancheUserWalletTwo.address]]);
const hashedPayload = utils.keccak256(payload);
await expect(
deployedContractPolygon.sendToMany(
avalanche.name,
deployedContractAvalanche.address,
[avalancheUserWallet.address, avalancheUserWalletTwo.address],
'aUSDC',
6e6,
{
value: (1e18).toString(),
},
),
)
.to.emit(polygon.gasService, 'NativeGasPaidForContractCallWithToken')
.withArgs(
deployedContractPolygon.address,
avalanche.name,
deployedContractAvalanche.address,
hashedPayload,
'aUSDC',
6e6,
(1e18).toString(),
polygonUserWallet.address,
);
});
});

describe('dest chain', async () => {
beforeEach(async () => {
deployedContractPolygon = await deployContract(polygonUserWallet, CallContractWithToken, [
polygon.gateway.address,
polygon.gasService.address,
]);
deployedContractAvalanche = await deployContract(avalancheUserWallet, CallContractWithToken, [
avalanche.gateway.address,
avalanche.gasService.address,
]);

await aUSDCPolygon.connect(polygonUserWallet).approve(deployedContractPolygon.address, (100e18).toString());
});
afterEach(async () => {
await relay();
});
it('should set correct gateway addresses and gas service addresses on dest chain', async () => {
expect(await deployedContractAvalanche.gateway()).to.equal(avalanche.gateway.address);
expect(await deployedContractAvalanche.gasService()).to.equal(avalanche.gasService.address);
});

it('should distribute token evenly', async () => {
const receiverOneBalanceBefore = await aUSDCAvalanche.balanceOf(avalancheUserWalletTwo.address);
const receiverTwoBalanceBefore = await aUSDCAvalanche.balanceOf(avalancheUserWalletTwo.address);

await deployedContractPolygon.sendToMany(
avalanche.name,
deployedContractAvalanche.address,
[avalancheUserWallet.address, avalancheUserWalletTwo.address],
'aUSDC',
6e6,
{
value: (1e18).toString(),
},
);

await relay();

const receiverOneBalanceAfter = await aUSDCAvalanche.balanceOf(avalancheUserWallet.address);
expect(receiverOneBalanceAfter).to.equal(parseInt(receiverOneBalanceBefore) + 3e6);

const receiverTwoBalanceAfter = await aUSDCAvalanche.balanceOf(avalancheUserWalletTwo.address);
expect(receiverTwoBalanceAfter).to.equal(parseInt(receiverTwoBalanceBefore) + 3e6);
});
it('should emit Executed event', async () => {
const ExecutedEvent = deployedContractAvalanche.filters.Executed();

const blockNumberBefore = await avalanche.lastRelayedBlock;
const blockInfoBefore = await avalanche.provider.getLogs(blockNumberBefore);
const eventsBefore = await deployedContractAvalanche.queryFilter(ExecutedEvent, blockInfoBefore.hash);

await deployedContractPolygon.sendToMany(
avalanche.name,
deployedContractAvalanche.address,
[avalancheUserWallet.address, avalancheUserWalletTwo.address],
'aUSDC',
6e6,
{
value: (1e18).toString(),
},
);

await relay();

const blockNumberAfter = await avalanche.lastRelayedBlock;
const blockInfoAfter = await avalanche.provider.getLogs(blockNumberAfter);

const eventsAfter = await deployedContractAvalanche.queryFilter(ExecutedEvent, blockInfoAfter.hash);

expect(eventsBefore.length + 1).to.equal(eventsAfter.length);

for (const events in eventsAfter) {
const event = eventsAfter[events];
expect(event.event).to.equal('Executed');
}
});
});
});
12 changes: 3 additions & 9 deletions examples/evm/call-contract/CallContract.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import { IAxelarGateway } from '@axelar-network/axelar-gmp-sdk-solidity/contract
import { IAxelarGasService } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IAxelarGasService.sol';
import { IERC20 } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IERC20.sol';


/**
* @title CallContract
* @notice Send a message from chain A to chain B and stores gmp message
Expand All @@ -20,15 +19,14 @@ contract CallContract is AxelarExecutable {
event Executed(string _from, string _message);

/**
*
*
* @param _gateway address of axl gateway on deployed chain
* @param _gasReceiver address of axl gas service on deployed chain
*/
constructor(address _gateway, address _gasReceiver) AxelarExecutable(_gateway) {
gasService = IAxelarGasService(_gasReceiver);
}


/**
* @notice Send message from chain A to chain B
* @dev message param is passed in as gmp message
Expand Down Expand Up @@ -57,15 +55,11 @@ contract CallContract is AxelarExecutable {
/**
* @notice logic to be executed on dest chain
* @dev this is triggered automatically by relayer
* @param _sourceChain blockchain where tx is originating from
* @param _sourceChain blockchain where tx is originating from
* @param _sourceAddress address on src chain where tx is originating from
* @param _payload encoded gmp message sent from src chain
*/
function _execute(
string calldata _sourceChain,
string calldata _sourceAddress,
bytes calldata _payload
) internal override {
function _execute(string calldata _sourceChain, string calldata _sourceAddress, bytes calldata _payload) internal override {
(message) = abi.decode(_payload, (string));
sourceChain = _sourceChain;
sourceAddress = _sourceAddress;
Expand Down
9 changes: 7 additions & 2 deletions examples/evm/call-contract/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,14 @@ To deploy the contract, use the following command:
npm run deploy evm/call-contract [local|testnet]
```

The aforementioned command pertains to specifying the intended environment for a project to execute on. It provides the option to choose between local and testnet environments by appending either `local` or `testnet` after the command.
The aforementioned command pertains to specifying the intended environment for a project to execute on. It provides the option to choose between local and testnet environments by appending either `local` or `testnet` after the command.

An example of its usage is demonstrated as follows: `npm run deploy evm/call-contract local` or `npm run deploy evm/call-contract testnet`.
An example of its usage is demonstrated as follows: `npm run deploy evm/call-contract local` or `npm run deploy evm/call-contract testnet`.

### Test

`cd` into `evm` folder
Run the command ` npx hardhat test call-contract/tests/call-contract.test.js`

### Execution

Expand Down
Loading
Loading