챌린지를 수행하며 기억에 남은 내용만 간단히 회고한다.
계약 자체는 @openzeppelin
라이브러리를 임포트하여 간편하게 작성했다.
ERC20
스펙에 맞춰 토큰을 생성decimals()
는 소수점 거래를 위한 유효숫자 정의: 기본 18 (e.g. wei
)pragma solidity 0.8.4; //Do not change the solidity version as it negativly impacts submission grading
// SPDX-License-Identifier: MIT
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract YourToken is ERC20 {
uint256 private ONE_ETHER_IN_WEI = 10 ** decimals();
constructor() ERC20("Gold", "GLD") {
_mint(msg.sender, 1000 * ONE_ETHER_IN_WEI);
}
}
Vendor
계약 생성pragma solidity 0.8.4; //Do not change the solidity version as it negativly impacts submission grading
// SPDX-License-Identifier: MIT
import "@openzeppelin/contracts/access/Ownable.sol";
import "./YourToken.sol";
interface IVendor {
function buyTokens() external payable;
function withdraw() external;
function sellTokens(uint256 _amount) external;
}
contract Vendor is IVendor, Ownable {
event BuyTokens(address buyer, uint256 amountOfETH, uint256 amountOfTokens);
event SellTokens(
address seller,
uint256 amountOfTokens,
uint256 amountOfETH
);
YourToken public yourToken;
constructor(address tokenAddress) {
yourToken = YourToken(tokenAddress);
}
function buyTokens() external payable override {
uint256 amountOfTokens = msg.value * tokensPerEth();
yourToken.transfer(msg.sender, amountOfTokens);
emit BuyTokens(msg.sender, msg.value, amountOfTokens);
}
function withdraw() external override {
require(msg.sender == owner(), "Only the owner can withdraw");
payable(owner()).transfer(address(this).balance);
}
function sellTokens(uint256 _amount) external override {
yourToken.transferFrom(msg.sender, address(this), _amount);
uint256 amountOfTokens = _amount / tokensPerEth();
payable(msg.sender).transfer(amountOfTokens);
emit SellTokens(msg.sender, _amount, amountOfTokens);
}
function tokensPerEth() public pure returns (uint256) {
return 100;
}
}
YourToken
: 0x41f50A95Ac5b173FF0d100DcfC7Ff3338b3478b3
Vendor
: 0x7b14ccc3719084D55A3435B947184ac084a14a8d
❯ yarn deploy
Nothing to compile
No need to generate any newer typings.
deploying "YourToken" (tx: 0xf650b921837e178c72495168230d6977926e5f2405258b11b8dbd78d15c7a5af)...: deployed at 0x41f50A95Ac5b173FF0d100DcfC7Ff3338b3478b3 with 642328 gas
deploying "Vendor" (tx: 0xd2757cfcad443b7c0547bb19096849dfc5ae15634f6dd22d8e598b9839e1991a)...: deployed at 0x7b14ccc3719084D55A3435B947184ac084a14a8d with 435777 gas
📝 Updated TypeScript contract definition file on ../nextjs/contracts/deployedContracts.ts
YourToken
과 Vendor
계약의 주소를 파악하고, yarn hardhat-verify <Vendor-Address> "<YourToken-Address>"
로 검증yarn hardhat-verify 0x7b14ccc3719084D55A3435B947184ac084a14a8d "0x41f50A95Ac5b173FF0d100DcfC7Ff3338b3478b3"
이번에는 프론트엔드 코드에 대한 내용도 다뤘다.
ethers 라이브러리를 통해, 계약에는 명시되지 않지만, 배포 이후에 수행할 작업을 수정했다.
import { HardhatRuntimeEnvironment } from "hardhat/types";
import { DeployFunction } from "hardhat-deploy/types";
import { Contract } from "ethers";
import { Vendor } from "../typechain-types";
/**
* Deploys a contract named "Vendor" using the deployer account and
* constructor arguments set to the deployer address
*
* @param hre HardhatRuntimeEnvironment object.
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const deployVendor: DeployFunction = async function (hre: HardhatRuntimeEnvironment) {
// Deploy Vendor
const { deployer } = await hre.getNamedAccounts();
const { deploy } = hre.deployments;
const yourToken = await hre.ethers.getContract<Contract>("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<Contract>("Vendor", deployer);
const vendorAddress = await vendor.getAddress();
// Transfer tokens to Vendor
await yourToken.transfer(vendorAddress, hre.ethers.parseEther("1000"));
// Transfer contract ownership to your frontend address
await vendor.transferOwnership(process.env.ADMIN_ADDRESS!);
};
export default deployVendor;
마지막 부분의 두 줄이 핵심인데, 생성해둔 1000개의 토큰은 Vendor
로 옮기고, Vendor
의 소유권을 임의의 운영자 계정으로 옮겨서 eth
인출이 가능하도록 한다.
await yourToken.transfer(vendorAddress, hre.ethers.parseEther("1000"));
await vendor.transferOwnership(process.env.ADMIN_ADDRESS!);