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

Add ETH-based Swap and Cross-Chain Functionality #39

Merged
merged 10 commits into from
Jan 23, 2024
File renamed without changes.
12 changes: 12 additions & 0 deletions contracts/common/ReferenceForTest.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// SPDX-License-Identifier: MIT

/**
* This file is to allow refencing the imported contracts, in the test code, because it will trigger
* hardhat to create the relavant artifacts.
*/

pragma solidity ^0.8.0;

import "foundry-contracts/contracts/common/FerrumDeployer.sol";

contract FerrumDeployer_ is FerrumDeployer {}
94 changes: 86 additions & 8 deletions contracts/upgradeable-Bridge/FiberRouter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import "../common/tokenReceiveable.sol";
import "../common/SafeAmount.sol";
import "../common/oneInch/OneInchDecoder.sol";
import "../common/oneInch/IOneInchSwap.sol";
import "../common/IWETH.sol";
import "foundry-contracts/contracts/common/FerrumDeployer.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

/**
Expand All @@ -16,6 +18,7 @@ contract FiberRouter is Ownable, TokenReceivable {
using SafeERC20 for IERC20;
address public pool;
address public oneInchAggregatorRouter;
address public WETH;

event Swap(
address sourceToken,
Expand All @@ -38,14 +41,14 @@ contract FiberRouter is Ownable, TokenReceivable {
);

event WithdrawOneInch(
address,
uint256,
uint256,
address,
address,
bytes,
bytes32,
bytes
address to,
uint256 amountIn,
uint256 amountOutOneInch,
address foundryToken,
address targetToken,
bytes oneInchData,
bytes32 salt,
bytes multiSignature
);

event NonEvmSwap(
Expand Down Expand Up @@ -80,6 +83,24 @@ contract FiberRouter is Ownable, TokenReceivable {
uint256 amountOut
);


/**
* @dev Constructor that sets the WETH address, oneInchAggregator address, and the pool address.
*/
constructor() {
bytes memory initData = IFerrumDeployer(msg.sender).initData();
(WETH, oneInchAggregatorRouter, pool) = abi.decode(
initData,
(address, address, address)
);
require(WETH != address(0), "WETH address cannot be the zero address");
require(
oneInchAggregatorRouter != address(0),
"oneInchAggregator address cannot be the zero address"
);
require(pool != address(0), "Pool address cannot be the zero address");
}

/**
@notice Sets the fund manager contract.
@param _pool The fund manager
Expand Down Expand Up @@ -370,6 +391,63 @@ contract FiberRouter is Ownable, TokenReceivable {
);
}

/**
@notice Performs a local ETH swap and generates a cross-chain swap
@param amountOut Expected output amount on oneInch
@param crossTargetNetwork Target network for the cross-chain swap
@param crossTargetToken Token address on the target network
@param crossTargetAddress Address receiving the tokens on the target network
@param oneInchData Encoded data for oneInch swap
@param foundryToken Foundry token address involved in the swap
@param withdrawalData Data related to withdrawal in the swap process
*/
function swapAndCrossOneInchETH(
uint256 amountOut, // amountOut on oneInch
uint256 crossTargetNetwork,
address crossTargetToken,
address crossTargetAddress,
bytes memory oneInchData,
address foundryToken,
bytes32 withdrawalData
) external payable {
uint256 amountIn = msg.value;

// Validation checks
require(amountIn != 0, "FR: Amount in must be greater than zero");
require(amountOut != 0, "FR: Amount out must be greater than zero");
require(crossTargetToken != address(0), "FR: Cross target token address cannot be zero");
require(bytes(oneInchData).length != 0, "FR: 1inch data cannot be empty");
require(foundryToken != address(0), "FR: Foundry token address cannot be zero");
require(withdrawalData != 0, "FR: Withdraw data cannot be empty");

// Deposit ETH and get WETH
IWETH(WETH).deposit{value: amountIn}();

// Execute swap and cross-chain operation
uint256 settledAmount = _swapAndCrossOneInch(
amountIn,
amountOut,
crossTargetNetwork,
crossTargetAddress,
oneInchData,
WETH,
foundryToken
);

// Emit Swap event
emit Swap(
WETH,
crossTargetToken,
block.chainid,
crossTargetNetwork,
amountIn,
_msgSender(),
crossTargetAddress,
settledAmount,
withdrawalData
);
}

/*
@notice Withdraws funds based on a multisig
@dev For signature swapToToken must be the same as token
Expand Down
6 changes: 5 additions & 1 deletion contracts/upgradeable-Bridge/FundManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import "../common/signature/SigCheckable.sol";
import "../common/WithAdmin.sol";
import "../common/SafeAmount.sol";
import "../common/tokenReceiveable.sol";
import "foundry-contracts/contracts/common/FerrumDeployer.sol";
import "@openzeppelin/contracts/security/ReentrancyGuard.sol";

contract FundManager is SigCheckable, WithAdmin, TokenReceivable {
Expand Down Expand Up @@ -62,7 +63,10 @@ contract FundManager is SigCheckable, WithAdmin, TokenReceivable {
}

//initialize function is constructor for upgradeable smart contract
constructor() EIP712(NAME, VERSION) {}
constructor() EIP712(NAME, VERSION) {
bytes memory initData = IFerrumDeployer(msg.sender).initData();

}

/**
*************** Owner only operations ***************
Expand Down
9 changes: 9 additions & 0 deletions hardhat.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,15 @@ module.exports = {
},
},
},
{
version: "0.8.12",
settings: {
optimizer: {
enabled: true,
runs: 200,
},
},
},
{
version: "0.8.17",
settings: {
Expand Down
11 changes: 6 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,19 @@
"author": "",
"license": "ISC",
"dependencies": {
"@chainlink/contracts": "^0.5.1",
"@cosmjs/cosmwasm-stargate": "^0.29.2",
"@cosmjs/proto-signing": "^0.29.2",
"@cosmjs/stargate": "^0.29.2",
"@nomiclabs/hardhat-web3": "^2.0.0",
"@openzeppelin/contracts": "^4.0.0",
"@openzeppelin/contracts-upgradeable": "^4.8.0-rc.1",
"@openzeppelin/hardhat-upgrades": "^1.21.0",
"@openzeppelin/contracts": "^4.1.0",
"@openzeppelin/contracts-upgradeable": "4.3.2",
"@openzeppelin/hardhat-upgrades": "1.10.0",
"@openzeppelin/upgrades-core":"1.9.0",
"axios": "^1.1.3",
"dotenv": "^16.0.0",
"ethereumjs-util": "^7.1.5",
"ethers": "^5.7.2",
"foundry-contracts": "git+https://github.com/ferrumnet/foundry-contracts#develop",
"web3": "^1.8.0"
},
"devDependencies": {
Expand All @@ -35,7 +36,7 @@
"@types/chai": "^4.3.4",
"@types/mocha": "^9.1.1",
"chai": "^4.3.6",
"hardhat": "^2.14.0",
"hardhat": "^2.19.4",
"hardhat-gas-reporter": "^1.0.9",
"mocha": "^10.0.0",
"solidity-coverage": "^0.8.2",
Expand Down
46 changes: 32 additions & 14 deletions scripts/deploy/deployFundManager.js
Original file line number Diff line number Diff line change
@@ -1,28 +1,46 @@
const { ethers, upgrades } = require("hardhat");
const { ethers } = require("hardhat");

async function main() {
const FiberRouter = await hre.ethers.getContractFactory("FiberRouter");
const fiberRouter = await FiberRouter.deploy();
// Compile the contracts
await hre.run('compile');

await fiberRouter.deployed();
// Attach to the already deployed FerrumDeployer contract
const ferrumDeployerAddress = "ferrumDeployerAddress";
const FerrumDeployer = await ethers.getContractFactory("FerrumDeployer");
const ferrumDeployer = await FerrumDeployer.attach(ferrumDeployerAddress);

console.log("FiberRouter deployed to:", fiberRouter.address);
// Get the contract factory for FundManager
const FundManager = await ethers.getContractFactory("FundManager");

if (network.name == "hardhat") return;
await fundManager.deployTransaction.wait(6);
// Prepare the initialization data for FundManager
// Replace these addresses with the actual configuration data needed for FundManager
const initData = '0x';


// Compute the bytecode of FundManager
const bytecode = FundManager.bytecode;

// Compute a unique salt for deployment
const salt = ethers.utils.formatBytes32String(new Date().getTime().toString());

// Specify the owner address to which the ownership of the contract will be transferred
const ownerAddress = "0x466B45AF0B58eAF2B98Bed61E07a423ba7828E44"; // Replace with the desired owner address

// Deploy FundManager using FerrumDeployer's deployOwnable
const deploymentTx = await ferrumDeployer.deployOwnable(salt, ownerAddress, initData, bytecode);
const receipt = await deploymentTx.wait();

const fundManagerAddress = receipt.events.find((event) => event.event === 'DeployedWithData').args[0];
console.log("FundManager deployed to:", fundManagerAddress);
console.log("Verifing...");
await hre.run("verify:verify", {
address: fundManager.address,
address: fundManagerAddress,
constructorArguments: [],
});
console.log("Contract verified successfully !");
}

// npx hardhat verify --network bscMainnet 0x37D6421b1D5724421444dD33338d3043921594dB "Constructor argument 1"
// We recommend this pattern to be able to use async/await everywhere
// and properly handle errors.
main().catch((error) => {
console.error(error);
process.exitCode = 1;
console.error(error);
process.exitCode = 1;
});
//npx hardhat run --network sepolia scripts/deploy.js
61 changes: 49 additions & 12 deletions scripts/deploy/deployRouter.js
Original file line number Diff line number Diff line change
@@ -1,26 +1,63 @@
const { ethers, upgrades } = require("hardhat");
const { ethers } = require("hardhat");

async function main() {
const FiberRouter = await hre.ethers.getContractFactory("FiberRouter");
const fiberRouter = await FiberRouter.deploy();
// Compile the contracts and libraries
await hre.run('compile');

await fiberRouter.deployed();
// Deploy the OneInchDecoder library
const OneInchDecoder = await ethers.getContractFactory("OneInchDecoder");
const oneInchDecoder = await OneInchDecoder.deploy();
await oneInchDecoder.deployed();
console.log("OneInchDecoder library deployed to:", oneInchDecoder.address);

console.log("FiberRouter deployed to:", fiberRouter.address);
// Attach to the already deployed FerrumDeployer contract
const ferrumDeployerAddress = "ferrumDeployerAddress";
const FerrumDeployer = await ethers.getContractFactory("FerrumDeployer");
const ferrumDeployer = await FerrumDeployer.attach(ferrumDeployerAddress);

if (network.name == "hardhat") return;
await fiberRouter.deployTransaction.wait(21);
// Prepare the initialization data for FiberRouter
const initData = ethers.utils.defaultAbiCoder.encode(
["address", "address", "address"],
["WETH", "oneInchAggregator", "poolAddress"]
);

// Get the contract factory for FiberRouter, linking the OneInchDecoder library
const FiberRouter = await ethers.getContractFactory("FiberRouter", {
libraries: {
"contracts/common/oneInch/OneInchDecoder.sol:OneInchDecoder": oneInchDecoder.address
}
});

// Compute the bytecode of FiberRouter with initData
const bytecodeWithInitData = FiberRouter.bytecode + initData.slice(2);

// Compute a unique salt for deployment
const salt = ethers.utils.formatBytes32String(new Date().getTime().toString());

// Deploy FiberRouter using FerrumDeployer's deployOwnable function
const ownerAddress = "ownerAdddress"; // Replace with the desired owner address
const deploymentTx = await ferrumDeployer.deployOwnable(salt, ownerAddress, initData, bytecodeWithInitData);
const receipt = await deploymentTx.wait();

const fiberRouterAddress = receipt.events.find((event) => event.event === 'DeployedWithData').args[0];
console.log("FiberRouter deployed to:", fiberRouterAddress);
console.log("Verifing...");
await hre.run("verify:verify", {
address: fiberRouter.address,
address: oneInchDecoder.address,
constructorArguments: [],
});
await hre.run("verify:verify", {
address: fiberRouterAddress,
constructorArguments: [],
libraries: {
OneInchDecoder : oneInchDecoder.address,
},
});
console.log("Contract verified successfully !");
}

// We recommend this pattern to be able to use async/await everywhere
// and properly handle errors.

main().catch((error) => {
console.error(error);
process.exitCode = 1;
console.error(error);
process.exitCode = 1;
});