diff --git a/contracts/common/uniswap/IWETH.sol b/contracts/common/IWETH.sol similarity index 100% rename from contracts/common/uniswap/IWETH.sol rename to contracts/common/IWETH.sol diff --git a/contracts/common/ReferenceForTest.sol b/contracts/common/ReferenceForTest.sol new file mode 100644 index 0000000..0f89805 --- /dev/null +++ b/contracts/common/ReferenceForTest.sol @@ -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 {} \ No newline at end of file diff --git a/contracts/upgradeable-Bridge/FiberRouter.sol b/contracts/upgradeable-Bridge/FiberRouter.sol index b504712..12512b4 100644 --- a/contracts/upgradeable-Bridge/FiberRouter.sol +++ b/contracts/upgradeable-Bridge/FiberRouter.sol @@ -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"; /** @@ -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, @@ -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( @@ -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 @@ -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 diff --git a/contracts/upgradeable-Bridge/FundManager.sol b/contracts/upgradeable-Bridge/FundManager.sol index 7c8a768..9ff6fa7 100644 --- a/contracts/upgradeable-Bridge/FundManager.sol +++ b/contracts/upgradeable-Bridge/FundManager.sol @@ -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 { @@ -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 *************** diff --git a/hardhat.config.js b/hardhat.config.js index cd05f5a..897f17d 100644 --- a/hardhat.config.js +++ b/hardhat.config.js @@ -16,6 +16,15 @@ module.exports = { }, }, }, + { + version: "0.8.12", + settings: { + optimizer: { + enabled: true, + runs: 200, + }, + }, + }, { version: "0.8.17", settings: { diff --git a/package.json b/package.json index d2a553b..7df0a83 100644 --- a/package.json +++ b/package.json @@ -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": { @@ -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", diff --git a/scripts/deploy/deployFundManager.js b/scripts/deploy/deployFundManager.js index 1a6e099..1878af6 100644 --- a/scripts/deploy/deployFundManager.js +++ b/scripts/deploy/deployFundManager.js @@ -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 diff --git a/scripts/deploy/deployRouter.js b/scripts/deploy/deployRouter.js index 8189294..42b971d 100644 --- a/scripts/deploy/deployRouter.js +++ b/scripts/deploy/deployRouter.js @@ -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; });