https://velog.io/@dnjs0397/Remix-vscode-연결
sudo npm install -g @remix-project/remixd
cd contracts
sudo remixd -s . --remix-ide https://remix.ethereum.org
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract MintToken is ERC20 {
constructor(uint256 initialSupply, string memory _name, string memory _symbol) ERC20(_name, _symbol) {
_mint(msg.sender, initialSupply * 10 ** 18);
}
}
INITIALSUPPLY: uint256(발행량)
_NAME: string
_SYMBOL: string
발행!
https://velog.io/@kich555/Ether-Gwei-Wei-Gas
https://eips.ethereum.org/EIPS/eip-20
https://www.openzeppelin.com/contracts
제3자의 토큰을 보내는 건데 권한이 필요. 그 권한을 설정할 수 있는 게 Approve함수. "누구에게 얼마를 보낼 수 있는 권한을 주겠다!"
실제 서비스 쓸 때는 조심해야 함. 우리의 권한을 넘기는 것이기 때문.
: 내 토큰을 전송하는 함수. From이 필요없음. 실행시키는 사람이 나니까. 누구에게 얼마를 줄 것인가.
: 누구의 토큰을 누구에게 얼마를 보낼 것인가. 권한 설정을 잘 해야 함. 권한이 없는 상태에서 막 전송하면 안 되기 때문. 제 3자의 코인을 제 3자에게 전달할 수 있는 함수. 그러려면 권한을 부여해야 함.
: 그 사람이 권한을 갖고 있는지 체크하는 게 위 함수.
: 주소 입력하면 이 토큰을 얼마나 들고 있냐를 확인할 수 있는 함수
git clone https://github.com/h662/cra-tailwind-template-2.git .
npm install
npm run start
npm i @metamask/sdk-react
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
import { MetaMaskProvider } from "@metamask/sdk-react";
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
<MetaMaskProvider
sdkOptions={{
dappMetadata: "ERC20 Practice",
url: window.location.host,
}}
>
<App />
</MetaMaskProvider>
</React.StrictMode>
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
import { useSDK } from "@metamask/sdk-react";
import { useState } from "react";
const App = () => {
const [account, setAccount] = useState("");
const { sdk } = useSDK();
const onClickMetaMask = async () => {
try {
const accounts = await sdk?.connect();
setAccount(accounts[0]);
} catch (error) {
console.error(error);
}
};
return (
<div className="bg-red-100 min-h-screen flex flex-col justify-center items-center">
{account ? (
<>
<div>
Hello, {account.substring(0, 7)}...
{account.substring(account.length - 5)}
</div>
<button onClick={() => setAccount("")}>🦊 MetaMask Login</button>
</>
) : (
<button onClick={onClickMetaMask}>🦊 MetaMask Login</button>
)}
</div>
);
};
export default App;
npm i web3 (web3 library 설치)
Remix의 compiler 탭에서 가장 하단 Abi 복사해서 abi file에 붙여넣기
import { useSDK } from "@metamask/sdk-react";
import { useEffect, useState } from "react";
import { useSpring, animated } from "react-spring";
import Web3 from "web3";
import "animate.css";
import contractAddress from "./contractAddress.json";
import TokenCard from "./components/TokenCard";
const App = () => {
const [account, setAccount] = useState("");
const [web3, setWeb3] = useState();
const { sdk, provider } = useSDK();
const onClickMetaMask = async () => {
try {
const accounts = await sdk?.connect();
setAccount(accounts[0]);
} catch (error) {
console.error(error);
}
};
useEffect(() => {
if (!provider) return;
setWeb3(new Web3(provider));
}, [provider]);
const buttonAnimation = useSpring({
from: { transform: "scale(0.8)", opacity: 0 },
to: { transform: "scale(1)", opacity: 1 },
config: { tension: 300, friction: 10 },
});
return (
<div className="bg-gradient-to-r from-black to-purple-800 text-white min-h-screen flex flex-col justify-center items-center">
{account ? (
<>
<div>
{account.substring(0, 7)}...
{account.substring(account.length - 5)}
</div>
{contractAddress.map((v, i) => (
<TokenCard
key={i}
account={account}
web3={web3}
address={v.address}
owner={v.owner}
walletAccount={v.walletAccount}
/>
))}
<button
className="whitespace-pre mt-4"
onClick={() => setAccount("")}
>
🦊 Logout
</button>
</>
) : (
<>
<button className="mb-4 text-7xl">
<animated.button style={buttonAnimation}>
<div className="animate__animated animate__bounce">🦊</div>
</animated.button>
</button>
<button
onClick={onClickMetaMask}
className="bg-gradient-to-r from-blue-700 to-purple-700 px-8 py-4 text-black font-semibold font-serif rounded-md hover:from-blue-600 hover:to-purple-600"
>
<animated.button style={buttonAnimation}>
MetaMask login
</animated.button>
</button>
</>
)}
</div>
);
};
export default App;
import { useEffect, useState } from "react";
import mintTokenAbi from "../mintTokenAbi.json";
import contractAddress from "../contractAddress.json";
import OptionCard from "./OptionCard";
const TokenCard = ({ account, web3, address, owner, walletAccount }) => {
const [name, setName] = useState("TOKEN");
const [symbol, setSymbol] = useState("TOKEN");
const [balance, setBalance] = useState(0);
const [contract, setContract] = useState();
const [inputAccount, setInputAccount] = useState("");
const [inputValue, setInputValue] = useState("0");
const getName = async () => {
try {
const response = await contract.methods.name().call();
setName(response);
} catch (error) {
console.error(error);
}
};
const getSymbol = async () => {
try {
const response = await contract.methods.symbol().call();
setSymbol(response);
} catch (error) {
console.error(error);
}
};
const getBalanceOf = async () => {
try {
const response = await contract.methods.balanceOf(account).call();
setBalance(Number(web3.utils.fromWei(response, "ether")));
} catch (error) {
console.log(error);
}
};
const onSubmitSend = async (e) => {
try {
e.preventDefault();
await contract.methods
.transfer(inputAccount, web3.utils.toWei(inputValue, "ether"))
.send({
from: account,
});
getBalanceOf();
setInputAccount("");
setInputValue("0");
alert("성공적으로 토큰을 전송하였습니다.");
} catch (error) {
console.log(error);
}
};
const onClickClipBoard = async () => {
try {
await navigator.clipboard.writeText(walletAccount);
} catch (error) {
console.error(error);
}
};
useEffect(() => {
if (!contract || !account) return;
getName();
getSymbol();
getBalanceOf();
}, [contract, account]);
useEffect(() => {
if (!web3) return;
setContract(new web3.eth.Contract(mintTokenAbi, address));
}, [web3]);
useEffect(() => console.log(inputAccount), [inputAccount]);
return (
<li className="flex flex-col justify-center items-center gap-1 mt-5">
<div>
<button className="text-blue-500 underline" onClick={onClickClipBoard}>
{owner}
</button>
가 발행한 코인
</div>
<div className="flex">
<span className="flex justify-center bg-black w-48">{name}</span>
<span className="flex justify-center bg-black w-60">
{balance.toFixed(4)}
</span>
<span className="flex justify-center bg-black w-20">{symbol}</span>
<form className="flex" onSubmit={onSubmitSend}>
<select
value={inputAccount}
onChange={(e) => setInputAccount(e.target.value)}
>
<option value=""></option>
{contractAddress.map((v, i) => (
<OptionCard
key={i}
owner={v.owner}
walletAccount={v.walletAccount}
/>
))}
</select>
<input
className="bg-black w-32"
type="text"
value={inputValue}
onChange={(e) => setInputValue(e.target.value)}
/>
<button className="bg-black" type="submit">
Send
</button>
</form>
</div>
</li>
);
};
export default TokenCard;
const OptionCard = ({ walletAccount, owner }) => {
return <option value={walletAccount}>{owner}</option>;
};
export default OptionCard;
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
-> compile버전 명시, 오픈제플린 코드 참고
import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
-> 오픈제플린 라이브러리 활용해서 erc20 모델 갖다 씀.
contract MintToken is ERC20 {
constructor(uint256 initialSupply, string memory _name, string memory _symbol) ERC20(_name, _symbol) {
_mint(msg.sender, initialSupply * 10 ** 18);
}
}
-> solidity는 contract 단위로 코드를 짬.
앞에는 contract로 시작.
오픈제플린 라이브러리를 상속받아서 씀. 그래서 approve, transfer, transferfrom 등의 기능이 담겨있는 것.
constructor는 스마트 컨트랙트가 배포될 때 실행되는 특별한 함수
_mint(msg.sender, initialSupply * 10 ** 18)
-> token이 생성되는 함수. 초기 한 번만 할 수 있고 추가발행 안 됨.
추가발행을 원하면 function 추가발행() 이런 식으로 함수를 더 써서 추가발행을 했어야 함.
소각 모델은 burn 함수
이 코드는 한 번 발행되면 수정배포가 불가능함.
수정한다는 사실 자체가 프로젝트에 독으로 작용할 것.
수정을 할 수 없으니 커뮤니티합의만 이뤄지면 컨트랙트 새로 만들고 새로 만든 컨트랙트를 진짜로 인정하면 되는 것. 수정배포가 아니라 다시 배포->커뮤 인정
ERC20(_name, _symbol)
-> erc20 이용해서 이름과 심볼 사용해라
uint256 initialSupply, string memory _name, string memory _symbol
-> 바깥 deploy탭에서 받아온 것. 효과적이라 토큰 발행 여러개도 하기 수월.