feat: finish challenge
Some checks failed
Lint / ci (lts/*, ubuntu-latest) (push) Has been cancelled

This commit is contained in:
han
2026-01-23 19:56:48 +07:00
parent e7b2a69f2a
commit 2ad0e8c238
9 changed files with 2413 additions and 20 deletions

View File

@@ -15,6 +15,8 @@ contract DEX {
/* ========== GLOBAL VARIABLES ========== */ /* ========== GLOBAL VARIABLES ========== */
IERC20 token; //instantiates the imported contract IERC20 token; //instantiates the imported contract
uint256 public totalLiquidity;
mapping (address => uint256) public liquidity;
/* ========== EVENTS ========== */ /* ========== EVENTS ========== */
@@ -57,13 +59,27 @@ contract DEX {
* @return totalLiquidity is the number of LPTs minting as a result of deposits made to DEX contract * @return totalLiquidity is the number of LPTs minting as a result of deposits made to DEX contract
* NOTE: since ratio is 1:1, this is fine to initialize the totalLiquidity (wrt to balloons) as equal to eth balance of contract. * NOTE: since ratio is 1:1, this is fine to initialize the totalLiquidity (wrt to balloons) as equal to eth balance of contract.
*/ */
function init(uint256 tokens) public payable returns (uint256) {} function init(uint256 tokens) public payable returns (uint256) {
require(totalLiquidity == 0, "DEX: init - already has liquidity");
totalLiquidity = address(this).balance;
liquidity[msg.sender] = totalLiquidity;
require(token.transferFrom(msg.sender, address(this), tokens), "DEX: init - transfer did not transact");
return totalLiquidity;
}
/** /**
* @notice returns yOutput, or yDelta for xInput (or xDelta) * @notice returns yOutput, or yDelta for xInput (or xDelta)
* @dev Follow along with the [original tutorial](https://medium.com/@austin_48503/%EF%B8%8F-minimum-viable-exchange-d84f30bd0c90) Price section for an understanding of the DEX's pricing model and for a price function to add to your contract. You may need to update the Solidity syntax (e.g. use + instead of .add, * instead of .mul, etc). Deploy when you are done. * @dev Follow along with the [original tutorial](https://medium.com/@austin_48503/%EF%B8%8F-minimum-viable-exchange-d84f30bd0c90) Price section for an understanding of the DEX's pricing model and for a price function to add to your contract. You may need to update the Solidity syntax (e.g. use + instead of .add, * instead of .mul, etc). Deploy when you are done.
*/ */
function price(uint256 xInput, uint256 xReserves, uint256 yReserves) public pure returns (uint256 yOutput) {} function price(uint256 xInput, uint256 xReserves, uint256 yReserves) public pure returns (uint256 yOutput) {
uint256 feePercentage = 997;
uint256 feeDenominator = 1000;
uint256 xInputAfterFee = xInput * feePercentage;
uint256 numerator = xInputAfterFee * yReserves;
uint256 denominator = (xReserves * feeDenominator) + xInputAfterFee;
return (numerator / denominator);
}
/** /**
* @notice returns liquidity for a user. * @notice returns liquidity for a user.
@@ -71,17 +87,41 @@ contract DEX {
* NOTE: if you are using a mapping liquidity, then you can use `return liquidity[lp]` to get the liquidity for a user. * NOTE: if you are using a mapping liquidity, then you can use `return liquidity[lp]` to get the liquidity for a user.
* NOTE: if you will be submitting the challenge make sure to implement this function as it is used in the tests. * NOTE: if you will be submitting the challenge make sure to implement this function as it is used in the tests.
*/ */
function getLiquidity(address lp) public view returns (uint256) {} function getLiquidity(address lp) public view returns (uint256) {
return liquidity[lp];
}
/** /**
* @notice sends Ether to DEX in exchange for $BAL * @notice sends Ether to DEX in exchange for $BAL
*/ */
function ethToToken() public payable returns (uint256 tokenOutput) {} function ethToToken() public payable returns (uint256 tokenOutput) {
require(msg.value > 0, "cannot swap 0 ETH");
uint256 ethReserve = address(this).balance - msg.value;
uint256 tokenReserve = token.balanceOf(address(this));
tokenOutput = price(msg.value, ethReserve, tokenReserve);
require(token.transfer(msg.sender, tokenOutput), "ethToToken(): reverted swap.");
emit EthToTokenSwap(msg.sender, tokenOutput, msg.value);
return tokenOutput;
}
/** /**
* @notice sends $BAL tokens to DEX in exchange for Ether * @notice sends $BAL tokens to DEX in exchange for Ether
*/ */
function tokenToEth(uint256 tokenInput) public returns (uint256 ethOutput) {} function tokenToEth(uint256 tokenInput) public returns (uint256 ethOutput) {
require(tokenInput > 0, "cannot swap 0 tokens");
require(token.balanceOf(msg.sender) >= tokenInput, "insufficient token balance");
require(token.allowance(msg.sender, address(this)) >= tokenInput, "insufficient allowance");
uint256 ethReserve = address(this).balance;
uint256 tokenReserve = token.balanceOf(address(this));
ethOutput = price(tokenInput, tokenReserve, ethReserve);
require(token.transferFrom(msg.sender, address(this), tokenInput), "tokenToEth(): reverted swap.");
(bool success,) = msg.sender.call{value: ethOutput}("");
require(success, "tokenToEth: reverted in transferring eth to you!");
emit TokenToEthSwap(msg.sender, tokenInput, ethOutput);
return ethOutput;
}
/** /**
* @notice allows deposits of $BAL and $ETH to liquidity pool * @notice allows deposits of $BAL and $ETH to liquidity pool
@@ -89,11 +129,45 @@ contract DEX {
* NOTE: user has to make sure to give DEX approval to spend their tokens on their behalf by calling approve function prior to this function call. * NOTE: user has to make sure to give DEX approval to spend their tokens on their behalf by calling approve function prior to this function call.
* NOTE: Equal parts of both assets will be removed from the user's wallet with respect to the price outlined by the AMM. * NOTE: Equal parts of both assets will be removed from the user's wallet with respect to the price outlined by the AMM.
*/ */
function deposit() public payable returns (uint256 tokensDeposited) {} function deposit() public payable returns (uint256 tokensDeposited) {
require(msg.value > 0, "Must send value when depositing");
uint256 ethReserve = address(this).balance - msg.value;
uint256 tokenReserve = token.balanceOf(address(this));
uint256 tokenDeposit;
tokenDeposit = (msg.value * tokenReserve / ethReserve) + 1;
require(token.balanceOf(msg.sender) >= tokenDeposit, "insufficient token balance");
require(token.allowance(msg.sender, address(this)) >= tokenDeposit, "insufficient allowance");
uint256 liquidityMinted = msg.value * totalLiquidity / ethReserve;
liquidity[msg.sender] += liquidityMinted;
totalLiquidity += liquidityMinted;
require(token.transferFrom(msg.sender, address(this), tokenDeposit));
emit LiquidityProvided(msg.sender, liquidityMinted, msg.value, tokenDeposit);
return tokenDeposit;
}
/** /**
* @notice allows withdrawal of $BAL and $ETH from liquidity pool * @notice allows withdrawal of $BAL and $ETH from liquidity pool
* NOTE: with this current code, the msg caller could end up getting very little back if the liquidity is super low in the pool. I guess they could see that with the UI. * NOTE: with this current code, the msg caller could end up getting very little back if the liquidity is super low in the pool. I guess they could see that with the UI.
*/ */
function withdraw(uint256 amount) public returns (uint256 ethAmount, uint256 tokenAmount) {} function withdraw(uint256 amount) public returns (uint256 ethAmount, uint256 tokenAmount) {
require(liquidity[msg.sender] >= amount, "withdraw: sender does not have enough liquidity to withdraw.");
uint256 ethReserve = address(this).balance;
uint256 tokenReserve = token.balanceOf(address(this));
uint256 ethWithdrawn;
ethWithdrawn = amount * ethReserve / totalLiquidity;
uint256 tokenAmount = amount * tokenReserve / totalLiquidity;
liquidity[msg.sender] -= amount;
totalLiquidity -= amount;
(bool sent, ) = payable(msg.sender).call{value: ethWithdrawn}("");
require(sent, "withdraw(): revert in transferring eth to you!");
require(token.transfer(msg.sender, tokenAmount));
emit LiquidityRemoved(msg.sender, amount, tokenAmount, ethWithdrawn);
return (ethWithdrawn, tokenAmount);
}
} }

View File

@@ -50,19 +50,19 @@ const deployYourContract: DeployFunction = async function (hre: HardhatRuntimeEn
const dex = (await hre.ethers.getContract("DEX", deployer)) as DEX; const dex = (await hre.ethers.getContract("DEX", deployer)) as DEX;
// // paste in your front-end address here to get 10 balloons on deploy: // // paste in your front-end address here to get 10 balloons on deploy:
// await balloons.transfer("YOUR_FRONTEND_ADDRESS", "" + 10 * 10 ** 18); await balloons.transfer("0x0d76b138023D1B901a155f491CE245736729893a", "" + 10 * 10 ** 18);
// // uncomment to init DEX on deploy: // // uncomment to init DEX on deploy:
// const dexAddress = await dex.getAddress(); const dexAddress = await dex.getAddress();
// console.log("Approving DEX (" + dexAddress + ") to take Balloons from main account..."); console.log("Approving DEX (" + dexAddress + ") to take Balloons from main account...");
// // If you are going to the testnet make sure your deployer account has enough ETH // If you are going to the testnet make sure your deployer account has enough ETH
// await balloons.approve(dexAddress, hre.ethers.parseEther("100")); await balloons.approve(dexAddress, hre.ethers.parseEther("100"));
// console.log("INIT exchange..."); console.log("INIT exchange...");
// await dex.init(hre.ethers.parseEther("5"), { await dex.init(hre.ethers.parseEther("0.01"), {
// value: hre.ethers.parseEther("5"), value: hre.ethers.parseEther("0.01"),
// gasLimit: 200000, gasLimit: 200000,
// }); });
}; };
export default deployYourContract; export default deployYourContract;

View File

@@ -0,0 +1 @@
11155111

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@@ -15,7 +15,7 @@ export const DEFAULT_ALCHEMY_API_KEY = "oKxs-03sij-U_N0iOlrSsZFr29-IqbuF";
const scaffoldConfig = { const scaffoldConfig = {
// The networks on which your DApp is live // The networks on which your DApp is live
targetNetworks: [chains.hardhat], targetNetworks: [chains.sepolia], //chains.hardhat],
// The interval at which your front-end polls the RPC servers for new data (it has no effect if you only target the local network (default is 4000)) // The interval at which your front-end polls the RPC servers for new data (it has no effect if you only target the local network (default is 4000))
pollingInterval: 30000, pollingInterval: 30000,
// This is ours Alchemy's default API key. // This is ours Alchemy's default API key.
@@ -34,7 +34,7 @@ const scaffoldConfig = {
// It's recommended to store it in an env variable: // It's recommended to store it in an env variable:
// .env.local for local testing, and in the Vercel/system env config for live apps. // .env.local for local testing, and in the Vercel/system env config for live apps.
walletConnectProjectId: process.env.NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID || "3a8170812b534d0ff9d794f19a901d64", walletConnectProjectId: process.env.NEXT_PUBLIC_WALLET_CONNECT_PROJECT_ID || "3a8170812b534d0ff9d794f19a901d64",
onlyLocalBurnerWallet: true, onlyLocalBurnerWallet: false,
} as const satisfies ScaffoldConfig; } as const satisfies ScaffoldConfig;
export default scaffoldConfig; export default scaffoldConfig;