From f25dfa25164be70110da328ebf8027711847942c Mon Sep 17 00:00:00 2001 From: antonio Date: Wed, 26 Mar 2025 16:13:12 +0100 Subject: [PATCH 1/4] fix hardhat.config.js --- README.md | 26 +++++++++++++++++++++++--- hardhat.config.js | 4 +++- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 780a820..8db2357 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ npx hardhat node npx hardhat ignition deploy ./ignition/modules/Lock.js ``` -# DEPLOYMENTS.md +# DEPLOYMENTS ## Sepolia Network @@ -18,8 +18,28 @@ npx hardhat ignition deploy ./ignition/modules/Lock.js - **Deployer Address**: `0x25b...` - **Uniswap Router**: `0x3bF...` -| Address | Purpose | Security Level | -| -------------------------------------- | ------------------------------------------------------ | -------------- | +| Address | Purpose | Security Level | +| ----------------------------- | ------------------------------------------------------ | -------------- | | `0x3cb...` (Swap Contract) | Your deployed contract address (share publicly) | 🔵 **Public** | | `0x25b...` (Deployer Address) | Your EOA (Externally Owned Account) - **keep private** | 🔴 **Private** | | `0x3bF...` (Uniswap Router) | Sepolia's Uniswap router (public infrastructure) | 🔵 **Public** | + +## Verify your smart contract + +The command `npx hardhat verify --network sepolia` is used to verify your smart contract on the Etherscan block explorer for the Sepolia network. + +``` +npx hardhat verify --network sepolia \ + \ + \ + \ + +``` + +For reference, please see the deployed contract at: +https://sepolia.etherscan.io/address/0xc90F75633540CFA9A5cD34e9456E3c509A61a107#code + +Where: +- Contract Address: 0xc90F75633540CFA9A5cD34e9456E3c509A61a107 +- Blockchain: Ethereum Sepolia Testnet +- Access: Full source code verification available via Etherscan diff --git a/hardhat.config.js b/hardhat.config.js index 7f48cb7..16080b2 100644 --- a/hardhat.config.js +++ b/hardhat.config.js @@ -10,7 +10,9 @@ module.exports = { }, }, etherscan: { - apiKey: process.env.ETHERSCAN_API_KEY, + apiKey: { + sepolia: process.env.ETHERSCAN_API_KEY, // Replace with your Sepolia API key + }, }, sourcify: { enabled: true, From 146ceac7331b8a2a4003df394aec18904cc37d21 Mon Sep 17 00:00:00 2001 From: antonio Date: Wed, 26 Mar 2025 16:45:17 +0100 Subject: [PATCH 2/4] add script to verify contract --- .vscode/settings.json | 3 +++ README.md | 5 ++++ contracts/Swap.sol | 52 ++++++++++++++++++++++++++----------- scripts/get-weth-balance.js | 22 ++++++++++++++++ scripts/verify.js | 36 +++++++++++++++++++++++++ 5 files changed, 103 insertions(+), 15 deletions(-) create mode 100644 scripts/get-weth-balance.js create mode 100644 scripts/verify.js diff --git a/.vscode/settings.json b/.vscode/settings.json index 6838bab..1068fb4 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -22,5 +22,8 @@ "editor.defaultFormatter": "esbenp.prettier-vscode", "[javascript]": { "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[solidity]": { + "editor.defaultFormatter": "JuanBlanco.solidity" } } diff --git a/README.md b/README.md index 8db2357..9c90ed7 100644 --- a/README.md +++ b/README.md @@ -43,3 +43,8 @@ Where: - Contract Address: 0xc90F75633540CFA9A5cD34e9456E3c509A61a107 - Blockchain: Ethereum Sepolia Testnet - Access: Full source code verification available via Etherscan + +To verify a new smart contract, execute the following command: +``` +node scripts/verify.js sepolia +``` \ No newline at end of file diff --git a/contracts/Swap.sol b/contracts/Swap.sol index 5ca5208..80c8273 100644 --- a/contracts/Swap.sol +++ b/contracts/Swap.sol @@ -18,37 +18,59 @@ contract Swap { } function transferWETH9ToContract(uint256 amountIn) internal { - TransferHelper.safeTransferFrom(WETH9, msg.sender, address(this), amountIn); + TransferHelper.safeTransferFrom( + WETH9, + msg.sender, + address(this), + amountIn + ); } function approveSwapRouter(uint256 amountIn) internal { TransferHelper.safeApprove(WETH9, address(swapRouter), amountIn); } - function prepareSwapParams(uint256 amountIn, uint256 minOut, uint160 priceLimit) internal view returns (ISwapRouter.ExactInputSingleParams memory) { - return ISwapRouter.ExactInputSingleParams({ - tokenIn: WETH9, - tokenOut: DAI, - fee: feeTier, - recipient: msg.sender, - deadline: block.timestamp, - amountIn: amountIn, - amountOutMinimum: minOut, - sqrtPriceLimitX96: priceLimit - }); + function getWETHBalance() external view returns (uint256) { + return IERC20(WETH9).balanceOf(address(this)); // Get the WETH balance of the contract } - function executeSwap(ISwapRouter.ExactInputSingleParams memory params) internal returns (uint256) { + function prepareSwapParams( + uint256 amountIn, + uint256 minOut, + uint160 priceLimit + ) internal view returns (ISwapRouter.ExactInputSingleParams memory) { + return + ISwapRouter.ExactInputSingleParams({ + tokenIn: WETH9, + tokenOut: DAI, + fee: feeTier, + recipient: msg.sender, + deadline: block.timestamp, + amountIn: amountIn, + amountOutMinimum: minOut, + sqrtPriceLimitX96: priceLimit + }); + } + + function executeSwap( + ISwapRouter.ExactInputSingleParams memory params + ) internal returns (uint256) { return swapRouter.exactInputSingle(params); } - function swapWETHForDAI(uint256 amountIn) external returns (uint256 amountOut) { + function swapWETHForDAI( + uint256 amountIn + ) external returns (uint256 amountOut) { uint256 minOut = 0; // Calculate minimum output as needed uint160 priceLimit = 0; // Calculate price limit as needed transferWETH9ToContract(amountIn); approveSwapRouter(amountIn); - ISwapRouter.ExactInputSingleParams memory params = prepareSwapParams(amountIn, minOut, priceLimit); + ISwapRouter.ExactInputSingleParams memory params = prepareSwapParams( + amountIn, + minOut, + priceLimit + ); amountOut = executeSwap(params); } } diff --git a/scripts/get-weth-balance.js b/scripts/get-weth-balance.js new file mode 100644 index 0000000..68b5cbb --- /dev/null +++ b/scripts/get-weth-balance.js @@ -0,0 +1,22 @@ +require("dotenv").config(); +const { ethers } = require("hardhat"); + +const main = async () => { + const [signer] = await ethers.getSigners(); // Get the signer (your wallet) + console.log("Using account:", signer.address); + + // Contract address of the deployed contract + const contractAddress = "0xc90F75633540CFA9A5cD34e9456E3c509A61a107"; + + // ABI with the getWETHBalance function + const abi = ["function getWETHBalance() external view returns (uint256)"]; + + // Connect to the contract + const contract = new ethers.Contract(contractAddress, abi, signer); + + // Call the getWETHBalance function + const balance = await contract.getWETHBalance(); + console.log("WETH Balance in Contract:", ethers.utils.formatUnits(balance, 18)); // WETH is typically 18 decimals +} + +main().catch((err) => console.log(err)); diff --git a/scripts/verify.js b/scripts/verify.js new file mode 100644 index 0000000..e914170 --- /dev/null +++ b/scripts/verify.js @@ -0,0 +1,36 @@ +const { exec } = require("child_process"); +const { getNetworkConfig } = require("../utils/config"); + +const verifyContract = async (network, deployedContractAddress) => { + if (!network || !deployedContractAddress) { + return logError("Please provide both the network and deployed contract address."); + } + + const networkConfig = await getNetworkConfig(network); + const { swapRouter, dai, weth } = networkConfig; + const command = buildCommand(network, deployedContractAddress, swapRouter, dai, weth); + + console.log(`Running command: ${command}`); + executeCommand(command); +}; + +const buildCommand = (network, deployedContractAddress, swapRouter, dai, weth) => + `npx hardhat verify --network ${network} ${deployedContractAddress} ${swapRouter} ${dai} ${weth}`; + +const executeCommand = (command) => { + exec(command, (stdout, stderr) => { + if (stderr) return handleError(stderr); + console.log(`stdout: ${stdout}`); + }); +}; + +const handleError = (stderr) => { + throw new Error(`stderr: ${stderr}`); +}; + +const logError = (message) => console.log(message); + +const network = process.argv[2]; // Network parameter (sepolia, mainnet, etc.) +const deployedContractAddress = process.argv[3]; // Deployed contract address + +verifyContract(network, deployedContractAddress); From 98ba264d4f79b90669fb53f52315cb24e5a6c027 Mon Sep 17 00:00:00 2001 From: antonio Date: Wed, 26 Mar 2025 16:50:41 +0100 Subject: [PATCH 3/4] fix scripts/get-weth-balance.js --- scripts/get-weth-balance.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/scripts/get-weth-balance.js b/scripts/get-weth-balance.js index 68b5cbb..e8837c1 100644 --- a/scripts/get-weth-balance.js +++ b/scripts/get-weth-balance.js @@ -6,7 +6,7 @@ const main = async () => { console.log("Using account:", signer.address); // Contract address of the deployed contract - const contractAddress = "0xc90F75633540CFA9A5cD34e9456E3c509A61a107"; + const contractAddress = "0x9FD6b6693BE719E2169f4E81B97d1E59325305dD"; // ABI with the getWETHBalance function const abi = ["function getWETHBalance() external view returns (uint256)"]; @@ -16,7 +16,7 @@ const main = async () => { // Call the getWETHBalance function const balance = await contract.getWETHBalance(); - console.log("WETH Balance in Contract:", ethers.utils.formatUnits(balance, 18)); // WETH is typically 18 decimals -} + console.log("WETH Balance in Contract:", ethers.formatUnits(balance, 18)); // WETH is typically 18 decimals +}; main().catch((err) => console.log(err)); From 7b7775195df7643b72deb64371f1ded25f1c4412 Mon Sep 17 00:00:00 2001 From: antonio Date: Wed, 26 Mar 2025 17:24:48 +0100 Subject: [PATCH 4/4] add scripts/transfer-funds.js --- README.md | 5 +++ contracts/Swap.sol | 19 ++++++++++ scripts/transfer-funds.js | 77 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 101 insertions(+) create mode 100644 scripts/transfer-funds.js diff --git a/README.md b/README.md index 9c90ed7..1a3fad2 100644 --- a/README.md +++ b/README.md @@ -47,4 +47,9 @@ Where: To verify a new smart contract, execute the following command: ``` node scripts/verify.js sepolia +``` + +To retrieve the balance stored within the smart contract, execute the following command in your terminal: +``` +npx hardhat run scripts/get-weth-balance.js --network sepolia ``` \ No newline at end of file diff --git a/contracts/Swap.sol b/contracts/Swap.sol index 80c8273..a4bf15c 100644 --- a/contracts/Swap.sol +++ b/contracts/Swap.sol @@ -4,6 +4,7 @@ pragma abicoder v2; import "@uniswap/v3-periphery/contracts/interfaces/ISwapRouter.sol"; import "@uniswap/v3-periphery/contracts/libraries/TransferHelper.sol"; +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; contract Swap { ISwapRouter public immutable swapRouter; @@ -17,6 +18,7 @@ contract Swap { WETH9 = _WETH9; } + // Function to transfer WETH to this contract (user must approve the transfer) function transferWETH9ToContract(uint256 amountIn) internal { TransferHelper.safeTransferFrom( WETH9, @@ -26,14 +28,22 @@ contract Swap { ); } + // Function to allow this contract to approve WETH transfer to the swap router function approveSwapRouter(uint256 amountIn) internal { TransferHelper.safeApprove(WETH9, address(swapRouter), amountIn); } + // Function to receive ETH (if you want to store ETH directly in the contract) + receive() external payable { + // You could emit an event here for logging + } + + // Function to check the WETH balance of the contract function getWETHBalance() external view returns (uint256) { return IERC20(WETH9).balanceOf(address(this)); // Get the WETH balance of the contract } + // Preparing the swap parameters for Uniswap V3 function prepareSwapParams( uint256 amountIn, uint256 minOut, @@ -52,25 +62,34 @@ contract Swap { }); } + // Execute the swap transaction function executeSwap( ISwapRouter.ExactInputSingleParams memory params ) internal returns (uint256) { return swapRouter.exactInputSingle(params); } + // Function to swap WETH for DAI function swapWETHForDAI( uint256 amountIn ) external returns (uint256 amountOut) { uint256 minOut = 0; // Calculate minimum output as needed uint160 priceLimit = 0; // Calculate price limit as needed + // Transfer WETH to the contract transferWETH9ToContract(amountIn); + + // Approve the swap router to spend WETH approveSwapRouter(amountIn); + + // Prepare swap parameters ISwapRouter.ExactInputSingleParams memory params = prepareSwapParams( amountIn, minOut, priceLimit ); + + // Execute the swap and return the output amountOut = executeSwap(params); } } diff --git a/scripts/transfer-funds.js b/scripts/transfer-funds.js new file mode 100644 index 0000000..5ca5c15 --- /dev/null +++ b/scripts/transfer-funds.js @@ -0,0 +1,77 @@ +require("dotenv").config(); +const { ethers } = require("ethers"); + +// Load the .env variables +const privateKey = process.env.PRIVATE_KEY; +const provider = new ethers.JsonRpcProvider(process.env.INFURA_URL); +const wallet = new ethers.Wallet(privateKey, provider); + +// Contract addresses +const contractAddress = process.env.CONTRACT_ADDRESS; // Your deployed contract address +const WETH_ADDRESS = process.env.WETH_ADDRESS; // WETH contract address + +// ABI for the WETH contract to approve transfer, transfer WETH, and wrap ETH +const WETH_ABI = [ + "function approve(address spender, uint256 amount) public returns (bool)", + "function balanceOf(address account) external view returns (uint256)", + "function transfer(address recipient, uint256 amount) public returns (bool)", + "function deposit() public payable", // Wrap ETH into WETH + "function withdraw(uint256 wad) public" // Withdraw WETH to ETH +]; + +const main = async () => { + const [, , amountIn] = process.argv; + + if (!amountIn) { + console.error("Please provide the amount of WETH to transfer."); + return; + } + + const amount = ethers.parseUnits(amountIn, 18); // WETH has 18 decimals + + console.log(`Transferring ${amountIn} WETH to the contract at ${contractAddress}`); + + // Create the WETH contract instance + const wethContract = new ethers.Contract(WETH_ADDRESS, WETH_ABI, wallet); + + // Ensure the wallet has enough ETH to wrap into WETH + const ethBalance = await provider.getBalance(wallet.address); + console.log('ETH Balance:', ethBalance.toString()); // Log ETH balance to see its value + + if (ethBalance.lt(amount)) { + console.error("Insufficient ETH to wrap into WETH."); + return; + } + + // Wrap ETH into WETH if needed + if (ethBalance.gte(amount)) { + console.log(`Wrapping ${ethers.formatUnits(amount, 18)} ETH into WETH...`); + const wrapTx = await wethContract.deposit({ value: amount }); + await wrapTx.wait(); + console.log("ETH successfully wrapped into WETH."); + } + + // Ensure the wallet has enough WETH + const balance = await wethContract.balanceOf(wallet.address); + console.log('WETH Balance:', balance.toString()); // Log WETH balance to see its value + + if (balance.lt(amount)) { + console.error("Insufficient WETH balance to transfer."); + return; + } + + // Approve the contract to transfer WETH on behalf of the user + console.log("Approving the contract to spend WETH..."); + const approveTx = await wethContract.approve(contractAddress, amount); + await approveTx.wait(); + + console.log("Approval granted. Transferring WETH to the contract..."); + + // Transfer the WETH to the contract + const transferTx = await wethContract.transfer(contractAddress, amount); + await transferTx.wait(); + + console.log(`${amountIn} WETH successfully transferred to the contract.`); +}; + +main().catch((error) => console.error(error));