Skip to content

Commit

Permalink
Add swap test
Browse files Browse the repository at this point in the history
  • Loading branch information
DimaStebaev committed Jan 30, 2025
1 parent 64d0126 commit 7e31c2f
Show file tree
Hide file tree
Showing 3 changed files with 218 additions and 13 deletions.
61 changes: 61 additions & 0 deletions contracts/test/ExecutionLayer/SwapMock.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// SPDX-License-Identifier: AGPL-3.0-only

/**
* SwapMock.sol - SKALE Interchain Messaging Agent
* Copyright (C) 2021-Present SKALE Labs
* @author Dmytro Stebaiev
*
* SKALE IMA is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* SKALE IMA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with SKALE IMA. If not, see <https://www.gnu.org/licenses/>.
*/


pragma solidity 0.8.27;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";

contract SwapMock {
IERC20 tokenA;
IERC20 tokenB;

error UnknownToken(IERC20 token);

function swap(IERC20 token, uint256 amount) external returns (uint256 resultAmount) {
IERC20 anotherToken = getAnotherToken(token);
address user = msg.sender;
token.transferFrom(user, address(this), amount);
anotherToken.transfer(user, amount);
return amount;
}

function setTokenA(IERC20 token) external {
tokenA = token;
}

function setTokenB(IERC20 token) external {
tokenB = token;
}

// public

function getAnotherToken(IERC20 token) public view returns (IERC20 anotherToken) {
require(tokenA == token || tokenB == token, UnknownToken(token));
if (token == tokenA) {
return tokenB;
} else if (token == tokenB) {
return tokenA;
} else {
revert UnknownToken(token);
}
}
}
58 changes: 58 additions & 0 deletions contracts/test/ExecutionLayer/executors/SwapMockSwap.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// SPDX-License-Identifier: AGPL-3.0-only

/**
* Executor.sol - SKALE Interchain Messaging Agent
* Copyright (C) 2024-Present SKALE Labs
* @author Dmytro Stebaiev
*
* SKALE IMA is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* SKALE IMA is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with SKALE IMA. If not, see <https://www.gnu.org/licenses/>.
*/

pragma solidity 0.8.27;

import "hardhat/console.sol";

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {ExecutorId} from "@skalenetwork/ima-interfaces/schain/ExecutionLayer/IExecutor.sol";
import {TokenInfo} from "@skalenetwork/ima-interfaces/schain/ExecutionLayer/IActionExecutor.sol";

import {Executor} from "../../../schain/ExecutionLayer/Executor.sol";
import {SwapMock} from "../SwapMock.sol";

contract SwapMockSwap is Executor {
ExecutorId public constant ID = ExecutorId.wrap(keccak256("SwapMockSwap"));
SwapMock exchange;

function setExchange(SwapMock exchangeAddress) external {
exchange = exchangeAddress;
}

function execute(
TokenInfo[] memory inputTokens,
bytes memory
)
external
override
returns (TokenInfo[] memory outputTokens)
{
outputTokens = new TokenInfo[](inputTokens.length);
for (uint256 i = 0; i < inputTokens.length; ++i) {
IERC20 token = IERC20(getTokenAddress(inputTokens[i]));
IERC20 anotherToken = exchange.getAnotherToken(token);
uint256 anotherValue = exchange.swap(token, inputTokens[i].value);
outputTokens[i].token = address(anotherToken);
outputTokens[i].value = anotherValue;
}
}
}
112 changes: 99 additions & 13 deletions test/ExecutionManager.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { SignerWithAddress } from "@nomicfoundation/hardhat-ethers/signers";
import { ethers } from "hardhat";
import { ethers, upgrades } from "hardhat";
import { ExecutionManager, MessageProxyForSchain, Protocol, TokenManagerERC20, TokenManagerLinker } from "../typechain";
import { deployExecutionManager } from "./utils/deploy/schain/executionManager";
import { AgentMock } from "./utils/agent/AgentMock";
Expand Down Expand Up @@ -111,18 +111,6 @@ describe("ExecutionManager", () => {
assert(sourceExecutionManager);
assert(targetExecutionManager);

// const metaAction = {
// targetChainHash: ethers.id(targetSchainName),
// actions: "0x",
// nextMetaAction: await sourceExecutionManager.encodeMetaAction({
// targetChainHash: ethers.id(targetSchainName),
// actions: "0x",
// nextMetaAction: "0x",
// postActions: "0x"
// }),
// postActions: "0x"
// }

const metaAction = (await sourceExecutionManager.createMetaAction(
targetSchainHash,
[]
Expand Down Expand Up @@ -235,4 +223,102 @@ describe("ExecutionManager", () => {
expect(await clone.balanceOf(user)).to.be.equal(0n);
expect(await token.balanceOf(user)).to.be.equal(value);
});

it.only("should transfer from Chain A to Chain B with execution of a swap on Chain B", async() => {
const schains = await setupMultipleSchains(2);
const agent = new AgentMock();
for (const [schainName, schainSetup] of schains) {
await agent.registerSchain(schainName, schainSetup.messageProxy);
}

const [sourceSchainName, targetSchainName] = [...schains.keys()];
const sourceSchainHash = ethers.id(sourceSchainName);
const targetSchainHash = ethers.id(targetSchainName);

const sourceExecutionManager = schains.get(sourceSchainName)?.executionManager;
const targetExecutionManager = schains.get(targetSchainName)?.executionManager;

assert(sourceExecutionManager);
assert(targetExecutionManager);

const sourceTokenManager = schains.get(sourceSchainName)?.tokenManager;
const targetTokenManager = schains.get(targetSchainName)?.tokenManager;

assert(sourceTokenManager);
assert(targetTokenManager);

// Create tokens on chain B

const token = await deployERC20OnChain("D2", "D2");
const value = ethers.parseEther("1");
await token.mint(user, value);

const token2 = await deployERC20OnChain("D2", "D2");
await token2.mint(user, value);

// Setup exchange

const exchange = await upgrades.deployProxy(await ethers.getContractFactory("SwapMock"));
await exchange.setTokenA(token);
await exchange.setTokenB(token2);
await token2.connect(user).transfer(exchange, value);

// Transfer the token to chain A

await token.connect(user).approve(targetTokenManager, value);
await targetTokenManager.connect(user).transferToSchainERC20(
sourceSchainName,
token, value
);

await agent.deliverMessages();

const cloneAddress = await sourceTokenManager.clonesErc20(targetSchainHash, token);
const clone = await ethers.getContractAt("ERC20OnChain", cloneAddress);

// Setup executors

const swapMockSwap = await ethers.deployContract("SwapMockSwap");
await swapMockSwap.setExchange(exchange);
await targetExecutionManager.setExecutor(await swapMockSwap.ID(), swapMockSwap);

const send = await ethers.getContractAt(
"Send",
await sourceExecutionManager.getExecutor(
ethers.id("Send")
)
);

// Send tokens and swap then

expect(await token.balanceOf(user)).to.be.equal(0n);
expect(await token2.balanceOf(user)).to.be.equal(0n);
expect(await clone.balanceOf(user)).to.be.equal(value);

const metaAction = (await sourceExecutionManager.createMetaAction(
targetSchainHash,
[
{
executor: ethers.id("SwapMockSwap"),
arguments: "0x"
},
{
executor: ethers.id("Send"),
arguments: await send.encodeArguments(user)
}
]
)).toObject();

await clone.connect(user).approve(sourceExecutionManager, value);
await sourceExecutionManager.connect(user).execute(
metaAction,
[{token: clone, value: value, origin: token}]
);

await agent.deliverMessages();

expect(await token.balanceOf(user)).to.be.equal(0n);
expect(await token2.balanceOf(user)).to.be.equal(value);
expect(await clone.balanceOf(user)).to.be.equal(0n);
});
});

0 comments on commit 7e31c2f

Please sign in to comment.