feat: finish challenge
Some checks failed
Lint / ci (lts/*, ubuntu-latest) (push) Has been cancelled
Some checks failed
Lint / ci (lts/*, ubuntu-latest) (push) Has been cancelled
This commit is contained in:
@@ -10,18 +10,26 @@ contract Vendor is Ownable {
|
|||||||
/////////////////
|
/////////////////
|
||||||
|
|
||||||
// Errors go here...
|
// Errors go here...
|
||||||
|
error InvalidEthAmount();
|
||||||
|
error InsufficientVendorTokenBalance(uint256 available, uint256 required);
|
||||||
|
error EthTransferFailed(address to, uint256 amount);
|
||||||
|
error InvalidTokenAmount();
|
||||||
|
error InsufficientVendorEthBalance(uint256 available, uint256 required);
|
||||||
|
|
||||||
//////////////////////
|
//////////////////////
|
||||||
/// State Variables //
|
/// State Variables //
|
||||||
//////////////////////
|
//////////////////////
|
||||||
|
|
||||||
YourToken public immutable yourToken;
|
YourToken public immutable yourToken;
|
||||||
|
uint256 public constant tokensPerEth = 100;
|
||||||
|
|
||||||
////////////////
|
////////////////
|
||||||
/// Events /////
|
/// Events /////
|
||||||
////////////////
|
////////////////
|
||||||
|
|
||||||
// Events go here...
|
// Events go here...
|
||||||
|
event BuyTokens(address indexed buyer, uint256 amountOfETH, uint256 amountOfTokens);
|
||||||
|
event SellTokens(address indexed seller, uint256 amountOfTokens, uint256 amountOfETH);
|
||||||
|
|
||||||
///////////////////
|
///////////////////
|
||||||
/// Constructor ///
|
/// Constructor ///
|
||||||
@@ -35,9 +43,29 @@ contract Vendor is Ownable {
|
|||||||
/// Functions /////
|
/// Functions /////
|
||||||
///////////////////
|
///////////////////
|
||||||
|
|
||||||
function buyTokens() external payable {}
|
function buyTokens() external payable {
|
||||||
|
if (msg.value == 0) revert InvalidEthAmount();
|
||||||
|
uint256 receivedToken = msg.value * tokensPerEth;
|
||||||
|
uint256 availableToken = yourToken.balanceOf(address(this));
|
||||||
|
if (availableToken < receivedToken) revert InsufficientVendorTokenBalance(availableToken, receivedToken);
|
||||||
|
yourToken.transfer(msg.sender, receivedToken);
|
||||||
|
emit BuyTokens(msg.sender, msg.value, receivedToken);
|
||||||
|
}
|
||||||
|
|
||||||
function withdraw() public onlyOwner {}
|
function withdraw() public onlyOwner {
|
||||||
|
address ownerAddr = payable(owner());
|
||||||
|
(bool sent,) = ownerAddr.call{value: address(this).balance}("");
|
||||||
|
if (!sent) revert EthTransferFailed(ownerAddr, address(this).balance);
|
||||||
|
}
|
||||||
|
|
||||||
function sellTokens(uint256 amount) public {}
|
function sellTokens(uint256 amount) public {
|
||||||
|
if (amount == 0) revert InvalidTokenAmount();
|
||||||
|
yourToken.transferFrom(msg.sender, address(this), amount);
|
||||||
|
uint256 receivedEth = amount / tokensPerEth;
|
||||||
|
uint256 contractEth = address(this).balance;
|
||||||
|
if (contractEth < receivedEth) revert InsufficientVendorEthBalance(contractEth, receivedEth);
|
||||||
|
(bool sent,) = payable(msg.sender).call{value: receivedEth}("");
|
||||||
|
if (!sent) revert EthTransferFailed(msg.sender, receivedEth);
|
||||||
|
emit SellTokens(msg.sender, amount, receivedEth);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,5 +6,7 @@ import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
|
|||||||
// learn more: https://docs.openzeppelin.com/contracts/5.x/erc20
|
// learn more: https://docs.openzeppelin.com/contracts/5.x/erc20
|
||||||
|
|
||||||
contract YourToken is ERC20 {
|
contract YourToken is ERC20 {
|
||||||
constructor() ERC20("Gold", "GLD") {}
|
constructor() ERC20("Gold", "GLD") {
|
||||||
|
_mint(msg.sender, 1000 * (10 ** 18));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,14 +27,14 @@ const deployVendor: DeployFunction = async function (hre: HardhatRuntimeEnvironm
|
|||||||
* Student TODO:
|
* Student TODO:
|
||||||
* - Put the address you’re using in the frontend here (leave "" to default to the deployer)
|
* - Put the address you’re using in the frontend here (leave "" to default to the deployer)
|
||||||
*/
|
*/
|
||||||
const FRONTEND_ADDRESS: string = "";
|
const FRONTEND_ADDRESS: string = "0x253F67DF832aBADb8A54aAA9b4A2635fbeDa4Ccf";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mode switch:
|
* Mode switch:
|
||||||
* - If true: deploy Vendor and seed it with the token balance
|
* - If true: deploy Vendor and seed it with the token balance
|
||||||
* - If false: send tokens to your frontend address (or deployer if unset)
|
* - If false: send tokens to your frontend address (or deployer if unset)
|
||||||
*/
|
*/
|
||||||
const SEND_TOKENS_TO_VENDOR = false; // Don't switch until Checkpoint 2!
|
const SEND_TOKENS_TO_VENDOR = true; // Don't switch until Checkpoint 2!
|
||||||
|
|
||||||
const recipientAddress = FRONTEND_ADDRESS && FRONTEND_ADDRESS.trim().length > 0 ? FRONTEND_ADDRESS : deployer;
|
const recipientAddress = FRONTEND_ADDRESS && FRONTEND_ADDRESS.trim().length > 0 ? FRONTEND_ADDRESS : deployer;
|
||||||
|
|
||||||
|
|||||||
1
packages/hardhat/deployments/sepolia/.chainId
Normal file
1
packages/hardhat/deployments/sepolia/.chainId
Normal file
@@ -0,0 +1 @@
|
|||||||
|
11155111
|
||||||
363
packages/hardhat/deployments/sepolia/Vendor.json
Normal file
363
packages/hardhat/deployments/sepolia/Vendor.json
Normal file
File diff suppressed because one or more lines are too long
530
packages/hardhat/deployments/sepolia/YourToken.json
Normal file
530
packages/hardhat/deployments/sepolia/YourToken.json
Normal file
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
@@ -37,7 +37,7 @@ const config: HardhatUserConfig = {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
defaultNetwork: "localhost",
|
defaultNetwork: "sepolia", //"localhost",
|
||||||
namedAccounts: {
|
namedAccounts: {
|
||||||
deployer: {
|
deployer: {
|
||||||
// By default, it will take the first Hardhat account as the deployer
|
// By default, it will take the first Hardhat account as the deployer
|
||||||
|
|||||||
@@ -12,11 +12,11 @@ const Events: NextPage = () => {
|
|||||||
eventName: "BuyTokens",
|
eventName: "BuyTokens",
|
||||||
});
|
});
|
||||||
|
|
||||||
// // SellTokens Events
|
// SellTokens Events
|
||||||
// const { data: sellTokenEvents, isLoading: isSellEventsLoading } = useScaffoldEventHistory({
|
const { data: sellTokenEvents, isLoading: isSellEventsLoading } = useScaffoldEventHistory({
|
||||||
// contractName: "Vendor",
|
contractName: "Vendor",
|
||||||
// eventName: "SellTokens",
|
eventName: "SellTokens",
|
||||||
// });
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center flex-col flex-grow pt-10">
|
<div className="flex items-center flex-col flex-grow pt-10">
|
||||||
@@ -66,7 +66,7 @@ const Events: NextPage = () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* SellTokens Events */}
|
{/* SellTokens Events */}
|
||||||
{/* <div className="mt-14">
|
<div className="mt-14">
|
||||||
<div className="text-center mb-4">
|
<div className="text-center mb-4">
|
||||||
<span className="block text-2xl font-bold">Sell Token Events</span>
|
<span className="block text-2xl font-bold">Sell Token Events</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -108,7 +108,7 @@ const Events: NextPage = () => {
|
|||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div> */}
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -33,18 +33,18 @@ const TokenVendor: NextPage = () => {
|
|||||||
const { writeContractAsync: writeVendorAsync } = useScaffoldWriteContract({ contractName: "Vendor" });
|
const { writeContractAsync: writeVendorAsync } = useScaffoldWriteContract({ contractName: "Vendor" });
|
||||||
const { writeContractAsync: writeYourTokenAsync } = useScaffoldWriteContract({ contractName: "YourToken" });
|
const { writeContractAsync: writeYourTokenAsync } = useScaffoldWriteContract({ contractName: "YourToken" });
|
||||||
|
|
||||||
// const { data: vendorTokenBalance } = useScaffoldReadContract({
|
const { data: vendorTokenBalance } = useScaffoldReadContract({
|
||||||
// contractName: "YourToken",
|
contractName: "YourToken",
|
||||||
// functionName: "balanceOf",
|
functionName: "balanceOf",
|
||||||
// args: [vendorContractData?.address],
|
args: [vendorContractData?.address],
|
||||||
// });
|
});
|
||||||
|
|
||||||
// const { data: vendorEthBalance } = useWatchBalance({ address: vendorContractData?.address });
|
const { data: vendorEthBalance } = useWatchBalance({ address: vendorContractData?.address });
|
||||||
|
|
||||||
// const { data: tokensPerEth } = useScaffoldReadContract({
|
const { data: tokensPerEth } = useScaffoldReadContract({
|
||||||
// contractName: "Vendor",
|
contractName: "Vendor",
|
||||||
// functionName: "tokensPerEth",
|
functionName: "tokensPerEth",
|
||||||
// });
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -57,8 +57,7 @@ const TokenVendor: NextPage = () => {
|
|||||||
<span className="font-bold ml-1">{yourTokenSymbol}</span>
|
<span className="font-bold ml-1">{yourTokenSymbol}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/* Vendor Balances */}
|
<hr className="w-full border-secondary my-3" />
|
||||||
{/* <hr className="w-full border-secondary my-3" />
|
|
||||||
<div>
|
<div>
|
||||||
Vendor token balance:{" "}
|
Vendor token balance:{" "}
|
||||||
<div className="inline-flex items-center justify-center">
|
<div className="inline-flex items-center justify-center">
|
||||||
@@ -69,11 +68,10 @@ const TokenVendor: NextPage = () => {
|
|||||||
<div>
|
<div>
|
||||||
Vendor eth balance: {Number(formatEther(vendorEthBalance?.value || 0n)).toFixed(4)}
|
Vendor eth balance: {Number(formatEther(vendorEthBalance?.value || 0n)).toFixed(4)}
|
||||||
<span className="font-bold ml-1">ETH</span>
|
<span className="font-bold ml-1">ETH</span>
|
||||||
</div> */}
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Buy Tokens */}
|
<div className="flex flex-col items-center space-y-4 bg-base-100 shadow-lg shadow-secondary border-8 border-secondary rounded-xl p-6 mt-8 w-full max-w-lg">
|
||||||
{/* <div className="flex flex-col items-center space-y-4 bg-base-100 shadow-lg shadow-secondary border-8 border-secondary rounded-xl p-6 mt-8 w-full max-w-lg">
|
|
||||||
<div className="text-xl">Buy tokens</div>
|
<div className="text-xl">Buy tokens</div>
|
||||||
<div>{tokensPerEth?.toString() || 0} tokens per ETH</div>
|
<div>{tokensPerEth?.toString() || 0} tokens per ETH</div>
|
||||||
|
|
||||||
@@ -98,7 +96,7 @@ const TokenVendor: NextPage = () => {
|
|||||||
>
|
>
|
||||||
Buy Tokens
|
Buy Tokens
|
||||||
</button>
|
</button>
|
||||||
</div> */}
|
</div>
|
||||||
|
|
||||||
{!!yourTokenBalance && (
|
{!!yourTokenBalance && (
|
||||||
<div className="flex flex-col items-center space-y-4 bg-base-100 shadow-lg shadow-secondary border-8 border-secondary rounded-xl p-6 mt-8 w-full max-w-lg">
|
<div className="flex flex-col items-center space-y-4 bg-base-100 shadow-lg shadow-secondary border-8 border-secondary rounded-xl p-6 mt-8 w-full max-w-lg">
|
||||||
@@ -132,7 +130,7 @@ const TokenVendor: NextPage = () => {
|
|||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Sell Tokens */}
|
{/* Sell Tokens */}
|
||||||
{/* {!!yourTokenBalance && (
|
{!!yourTokenBalance && (
|
||||||
<div className="flex flex-col items-center space-y-4 bg-base-100 shadow-lg shadow-secondary border-8 border-secondary rounded-xl p-6 mt-8 w-full max-w-lg">
|
<div className="flex flex-col items-center space-y-4 bg-base-100 shadow-lg shadow-secondary border-8 border-secondary rounded-xl p-6 mt-8 w-full max-w-lg">
|
||||||
<div className="text-xl">Sell tokens</div>
|
<div className="text-xl">Sell tokens</div>
|
||||||
<div>{tokensPerEth?.toString() || 0} tokens per ETH</div>
|
<div>{tokensPerEth?.toString() || 0} tokens per ETH</div>
|
||||||
@@ -180,7 +178,7 @@ const TokenVendor: NextPage = () => {
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)} */}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
1
packages/nextjs/next-env.d.ts
vendored
1
packages/nextjs/next-env.d.ts
vendored
@@ -1,6 +1,5 @@
|
|||||||
/// <reference types="next" />
|
/// <reference types="next" />
|
||||||
/// <reference types="next/image-types/global" />
|
/// <reference types="next/image-types/global" />
|
||||||
/// <reference path="./.next/types/routes.d.ts" />
|
|
||||||
|
|
||||||
// NOTE: This file should not be edited
|
// NOTE: This file should not be edited
|
||||||
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export const DEFAULT_ALCHEMY_API_KEY = "cR4WnXePioePZ5fFrnSiR";
|
|||||||
|
|
||||||
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;
|
||||||
|
|||||||
Reference in New Issue
Block a user