Skip to content
Open
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
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,8 @@
"editor.defaultFormatter": "esbenp.prettier-vscode",
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[solidity]": {
"editor.defaultFormatter": "JuanBlanco.solidity"
}
}
36 changes: 33 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,46 @@ npx hardhat node
npx hardhat ignition deploy ./ignition/modules/Lock.js
```

# DEPLOYMENTS.md
# DEPLOYMENTS

## Sepolia Network

- **Swap Contract**: `0x3cb29089715Dc67d73228D39073B6bf21Dd8cf2b`
- **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 \
<deployed_contract_address> \
<Uniswap-SwapRouter-Address> \
<DAI-Address> \
<WETH9-Address>
```

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

To verify a new smart contract, execute the following command:
```
node scripts/verify.js sepolia <Contract Address>
```

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
```
71 changes: 56 additions & 15 deletions contracts/Swap.sol
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -17,38 +18,78 @@ contract Swap {
WETH9 = _WETH9;
}

// Function to transfer WETH to this contract (user must approve the transfer)
function transferWETH9ToContract(uint256 amountIn) internal {
TransferHelper.safeTransferFrom(WETH9, msg.sender, address(this), amountIn);
TransferHelper.safeTransferFrom(
WETH9,
msg.sender,
address(this),
amountIn
);
}

// 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 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 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 executeSwap(ISwapRouter.ExactInputSingleParams memory params) internal returns (uint256) {
// 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,
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
});
}

// Execute the swap transaction
function executeSwap(
ISwapRouter.ExactInputSingleParams memory params
) internal returns (uint256) {
return swapRouter.exactInputSingle(params);
}

function swapWETHForDAI(uint256 amountIn) external returns (uint256 amountOut) {
// 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);
ISwapRouter.ExactInputSingleParams memory params = prepareSwapParams(amountIn, minOut, priceLimit);

// Prepare swap parameters
ISwapRouter.ExactInputSingleParams memory params = prepareSwapParams(
amountIn,
minOut,
priceLimit
);

// Execute the swap and return the output
amountOut = executeSwap(params);
}
}
4 changes: 3 additions & 1 deletion hardhat.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
22 changes: 22 additions & 0 deletions scripts/get-weth-balance.js
Original file line number Diff line number Diff line change
@@ -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 = "0x9FD6b6693BE719E2169f4E81B97d1E59325305dD";

// 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.formatUnits(balance, 18)); // WETH is typically 18 decimals
};

main().catch((err) => console.log(err));
77 changes: 77 additions & 0 deletions scripts/transfer-funds.js
Original file line number Diff line number Diff line change
@@ -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));
36 changes: 36 additions & 0 deletions scripts/verify.js
Original file line number Diff line number Diff line change
@@ -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);