diff --git a/packages/hardhat/contracts/Vendor.sol b/packages/hardhat/contracts/Vendor.sol index cb39809..9669211 100644 --- a/packages/hardhat/contracts/Vendor.sol +++ b/packages/hardhat/contracts/Vendor.sol @@ -1,21 +1,32 @@ pragma solidity 0.8.20; //Do not change the solidity version as it negatively impacts submission grading // SPDX-License-Identifier: MIT -// import "@openzeppelin/contracts/access/Ownable.sol"; +import "@openzeppelin/contracts/access/Ownable.sol"; import "./YourToken.sol"; -contract Vendor { - // event BuyTokens(address buyer, uint256 amountOfETH, uint256 amountOfTokens); +contract Vendor is Ownable{ + event BuyTokens(address buyer, uint256 amountOfETH, uint256 amountOfTokens); + event SellTokens(address seller, uint256 amountOfETH, uint256 amountOfTokens); YourToken public yourToken; + uint public constant tokensPerEth = 100; - constructor(address tokenAddress) { + constructor(address tokenAddress) Ownable (msg.sender) { yourToken = YourToken(tokenAddress); } // ToDo: create a payable buyTokens() function: + function buyTokens() payable public { + // msg.sender is the buyer of the tokens, they will pay some ETH in msg.data + uint ethInput = msg.value; + uint256 tokensBought = tokensPerEth * ethInput; + // check if vendor has enough + require(tokensBought<= yourToken.balanceOf(address(this)), " insuffcient tokens to dispense"); + yourToken.transfer(msg.sender, tokensBought); //transfer token from + emit BuyTokens(msg.sender, ethInput, tokensBought); + } // ToDo: create a withdraw() function that lets the owner withdraw ETH - + // function withdraw // ToDo: create a sellTokens(uint256 _amount) function: } diff --git a/packages/hardhat/contracts/YourToken.sol b/packages/hardhat/contracts/YourToken.sol index ad7fd73..2951e38 100644 --- a/packages/hardhat/contracts/YourToken.sol +++ b/packages/hardhat/contracts/YourToken.sol @@ -2,11 +2,17 @@ pragma solidity 0.8.20; //Do not change the solidity version as it negatively im // SPDX-License-Identifier: MIT import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; +import "@openzeppelin/contracts/access/Ownable.sol"; // learn more: https://docs.openzeppelin.com/contracts/4.x/erc20 contract YourToken is ERC20 { - constructor() ERC20("Gold", "GLD") { + + constructor() ERC20("Group9", "G9") { //_mint( ~~~YOUR FRONTEND ADDRESS HERE~~~~ , 1000 * 10 ** 18); + address initialTokenOwner = msg.sender; + _mint(initialTokenOwner, 1000* (10**18)); // 1000 tokens, msg sender aka minter will be initial Owner of all tokens } + + } diff --git a/packages/hardhat/deploy/01_deploy_vendor.ts b/packages/hardhat/deploy/01_deploy_vendor.ts index f6c6e67..19cdd84 100644 --- a/packages/hardhat/deploy/01_deploy_vendor.ts +++ b/packages/hardhat/deploy/01_deploy_vendor.ts @@ -1,6 +1,6 @@ import { HardhatRuntimeEnvironment } from "hardhat/types"; import { DeployFunction } from "hardhat-deploy/types"; -// import { Contract } from "ethers"; +import { Contract } from "ethers"; /** * Deploys a contract named "Vendor" using the deployer account and @@ -21,25 +21,25 @@ const deployVendor: DeployFunction = async function (hre: HardhatRuntimeEnvironm You can run the `yarn account` command to check your balance in every network. */ // // Deploy Vendor - // const { deployer } = await hre.getNamedAccounts(); - // const { deploy } = hre.deployments; - // const yourToken = await hre.ethers.getContract("YourToken", deployer); - // const yourTokenAddress = await yourToken.getAddress(); - // await deploy("Vendor", { - // from: deployer, - // // Contract constructor arguments - // args: [yourTokenAddress], - // log: true, - // // autoMine: can be passed to the deploy function to make the deployment process faster on local networks by - // // automatically mining the contract deployment transaction. There is no effect on live networks. - // autoMine: true, - // }); - // const vendor = await hre.ethers.getContract("Vendor", deployer); - // const vendorAddress = await vendor.getAddress(); - // // Transfer tokens to Vendor - // await yourToken.transfer(vendorAddress, hre.ethers.parseEther("1000")); + const { deployer } = await hre.getNamedAccounts(); + const { deploy } = hre.deployments; + const yourToken = await hre.ethers.getContract("YourToken", deployer); + const yourTokenAddress = await yourToken.getAddress(); + await deploy("Vendor", { + from: deployer, + // Contract constructor arguments + args: [yourTokenAddress], + log: true, + // autoMine: can be passed to the deploy function to make the deployment process faster on local networks by + // automatically mining the contract deployment transaction. There is no effect on live networks. + autoMine: true, + }); + const vendor = await hre.ethers.getContract("Vendor", deployer); + const vendorAddress = await vendor.getAddress(); + // // Transfer tokens to Vendor from msg sender + await yourToken.transfer(vendorAddress, hre.ethers.parseEther("1000")); // // Transfer contract ownership to your frontend address - // await vendor.transferOwnership("**YOUR FRONTEND ADDRESS**"); + await vendor.transferOwnership("0x173696F944B398d00D2176E595677102dE63C59e"); }; export default deployVendor; diff --git a/packages/nextjs/app/token-vendor/page.tsx b/packages/nextjs/app/token-vendor/page.tsx index 5c43af3..9346d91 100644 --- a/packages/nextjs/app/token-vendor/page.tsx +++ b/packages/nextjs/app/token-vendor/page.tsx @@ -40,10 +40,10 @@ const TokenVendor: NextPage = () => { // const { data: vendorEthBalance } = useWatchBalance({ address: vendorContractData?.address }); - // const { data: tokensPerEth } = useScaffoldReadContract({ - // contractName: "Vendor", - // functionName: "tokensPerEth", - // }); + const { data: tokensPerEth } = useScaffoldReadContract({ + contractName: "Vendor", + functionName: "tokensPerEth", + }); return ( <> @@ -72,7 +72,7 @@ const TokenVendor: NextPage = () => { {/* Buy Tokens */} - {/*
+
Buy tokens
{tokensPerEth?.toString() || 0} tokens per ETH
@@ -97,7 +97,7 @@ const TokenVendor: NextPage = () => { > Buy Tokens -
*/} +
{!!yourTokenBalance && (
diff --git a/packages/nextjs/contracts/deployedContracts.ts b/packages/nextjs/contracts/deployedContracts.ts index 008d4eb..e73a33e 100644 --- a/packages/nextjs/contracts/deployedContracts.ts +++ b/packages/nextjs/contracts/deployedContracts.ts @@ -4,6 +4,491 @@ */ import { GenericContractsDeclaration } from "~~/utils/scaffold-eth/contract"; -const deployedContracts = {} as const; +const deployedContracts = { + 31337: { + Vendor: { + address: "0xA51c1fc2f0D1a1b8494Ed1FE312d7C3a78Ed91C0", + abi: [ + { + inputs: [ + { + internalType: "address", + name: "tokenAddress", + type: "address", + }, + ], + stateMutability: "nonpayable", + type: "constructor", + }, + { + inputs: [ + { + internalType: "address", + name: "owner", + type: "address", + }, + ], + name: "OwnableInvalidOwner", + type: "error", + }, + { + inputs: [ + { + internalType: "address", + name: "account", + type: "address", + }, + ], + name: "OwnableUnauthorizedAccount", + type: "error", + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: "address", + name: "buyer", + type: "address", + }, + { + indexed: false, + internalType: "uint256", + name: "amountOfETH", + type: "uint256", + }, + { + indexed: false, + internalType: "uint256", + name: "amountOfTokens", + type: "uint256", + }, + ], + name: "BuyTokens", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "previousOwner", + type: "address", + }, + { + indexed: true, + internalType: "address", + name: "newOwner", + type: "address", + }, + ], + name: "OwnershipTransferred", + type: "event", + }, + { + inputs: [], + name: "buyTokens", + outputs: [], + stateMutability: "payable", + type: "function", + }, + { + inputs: [], + name: "owner", + outputs: [ + { + internalType: "address", + name: "", + type: "address", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "renounceOwnership", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [], + name: "tokensPerEth", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "newOwner", + type: "address", + }, + ], + name: "transferOwnership", + outputs: [], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [], + name: "yourToken", + outputs: [ + { + internalType: "contract YourToken", + name: "", + type: "address", + }, + ], + stateMutability: "view", + type: "function", + }, + ], + inheritedFunctions: { + owner: "@openzeppelin/contracts/access/Ownable.sol", + renounceOwnership: "@openzeppelin/contracts/access/Ownable.sol", + transferOwnership: "@openzeppelin/contracts/access/Ownable.sol", + }, + }, + YourToken: { + address: "0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e", + abi: [ + { + inputs: [], + stateMutability: "nonpayable", + type: "constructor", + }, + { + inputs: [ + { + internalType: "address", + name: "spender", + type: "address", + }, + { + internalType: "uint256", + name: "allowance", + type: "uint256", + }, + { + internalType: "uint256", + name: "needed", + type: "uint256", + }, + ], + name: "ERC20InsufficientAllowance", + type: "error", + }, + { + inputs: [ + { + internalType: "address", + name: "sender", + type: "address", + }, + { + internalType: "uint256", + name: "balance", + type: "uint256", + }, + { + internalType: "uint256", + name: "needed", + type: "uint256", + }, + ], + name: "ERC20InsufficientBalance", + type: "error", + }, + { + inputs: [ + { + internalType: "address", + name: "approver", + type: "address", + }, + ], + name: "ERC20InvalidApprover", + type: "error", + }, + { + inputs: [ + { + internalType: "address", + name: "receiver", + type: "address", + }, + ], + name: "ERC20InvalidReceiver", + type: "error", + }, + { + inputs: [ + { + internalType: "address", + name: "sender", + type: "address", + }, + ], + name: "ERC20InvalidSender", + type: "error", + }, + { + inputs: [ + { + internalType: "address", + name: "spender", + type: "address", + }, + ], + name: "ERC20InvalidSpender", + type: "error", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "owner", + type: "address", + }, + { + indexed: true, + internalType: "address", + name: "spender", + type: "address", + }, + { + indexed: false, + internalType: "uint256", + name: "value", + type: "uint256", + }, + ], + name: "Approval", + type: "event", + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: "address", + name: "from", + type: "address", + }, + { + indexed: true, + internalType: "address", + name: "to", + type: "address", + }, + { + indexed: false, + internalType: "uint256", + name: "value", + type: "uint256", + }, + ], + name: "Transfer", + type: "event", + }, + { + inputs: [ + { + internalType: "address", + name: "owner", + type: "address", + }, + { + internalType: "address", + name: "spender", + type: "address", + }, + ], + name: "allowance", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "spender", + type: "address", + }, + { + internalType: "uint256", + name: "value", + type: "uint256", + }, + ], + name: "approve", + outputs: [ + { + internalType: "bool", + name: "", + type: "bool", + }, + ], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "account", + type: "address", + }, + ], + name: "balanceOf", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "decimals", + outputs: [ + { + internalType: "uint8", + name: "", + type: "uint8", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "name", + outputs: [ + { + internalType: "string", + name: "", + type: "string", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "symbol", + outputs: [ + { + internalType: "string", + name: "", + type: "string", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [], + name: "totalSupply", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256", + }, + ], + stateMutability: "view", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "to", + type: "address", + }, + { + internalType: "uint256", + name: "value", + type: "uint256", + }, + ], + name: "transfer", + outputs: [ + { + internalType: "bool", + name: "", + type: "bool", + }, + ], + stateMutability: "nonpayable", + type: "function", + }, + { + inputs: [ + { + internalType: "address", + name: "from", + type: "address", + }, + { + internalType: "address", + name: "to", + type: "address", + }, + { + internalType: "uint256", + name: "value", + type: "uint256", + }, + ], + name: "transferFrom", + outputs: [ + { + internalType: "bool", + name: "", + type: "bool", + }, + ], + stateMutability: "nonpayable", + type: "function", + }, + ], + inheritedFunctions: { + allowance: "@openzeppelin/contracts/token/ERC20/ERC20.sol", + approve: "@openzeppelin/contracts/token/ERC20/ERC20.sol", + balanceOf: "@openzeppelin/contracts/token/ERC20/ERC20.sol", + decimals: "@openzeppelin/contracts/token/ERC20/ERC20.sol", + name: "@openzeppelin/contracts/token/ERC20/ERC20.sol", + symbol: "@openzeppelin/contracts/token/ERC20/ERC20.sol", + totalSupply: "@openzeppelin/contracts/token/ERC20/ERC20.sol", + transfer: "@openzeppelin/contracts/token/ERC20/ERC20.sol", + transferFrom: "@openzeppelin/contracts/token/ERC20/ERC20.sol", + }, + }, + }, +} as const; export default deployedContracts satisfies GenericContractsDeclaration;