오늘 순서
(1) ERC-20 transfer 구현
(2) ERC-30 trasnferFrom, Approved 구현
(3) WETH
9개의 method
2개의 event
정의 - event, 실행 - emit
// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;
contract Token {
// 대부분 사용하는 dataType = mapping임
//9개의 method
// 1. name("Wrapped Ether") = getValue 형식
// 2. symbol("WETH") = getValue
// 3. decimals(18) // 자릿수
// 4. totalSupply // 지금까지 얼마나 토큰이 발행되었는가
// 5. balaceOf() // 계정마다 얼마나 토큰을 갖고 있는지 input address -> output uint
// 6. transfer() // 보내는 사람은 무조건 나, 내가 남한테 얼마큼 보낼 것이다 **(중요) 내 토큰 얼마나 전송했어
// 7. transferFrom() // 대리인이 내 코인을 전송하는 기능, 권한을 주면 유니스왑에서 내 토큰을 알아서 가져가는 것
// 8. approve() // 권한을 주는 함수 ** (중요)
// 9. allowance() // 얼마큼 위임되어있냐, 누구에게 얼마큼 권한이 있는지 확인 input: owner, Spender(사용할 사람) -> output amount(uint)
// name symbol decimals 어떻게 저장되어야 할까?
// mapping타입의 balances 변수 , 특정 유저가 얼마만큼의 토큰을 보유하고 있는가
// function name() public view returns (string memory) {
// return "doyeon Token";
// } 변수 선언과 같은 의미
string public name = "doyeon Token";
string public symbol = "DYT";
uint public decimals = 18;
uint public totalSupply = 0; //민팅된게 없음
mapping(address owner => uint amount) public balances; // key는 주소, value는 갖고있는 토큰의 amount
mapping(address owner => mapping(address spender => uint amount)) public allowances;
//allowances[myEOA][uniswap Pair] = 1000;
// 이벤트 선언, indexed 하면 더 빨라짐!! value = amount
event Transfer(address indexed _from, address indexed _to, uint _amout); // transfer(), transferFrom() 에서 emit
event Approval(address indexed _owner, address indexed _spender, uint _value); //approve() 에서 emit
// 지갑 안에 토큰이 있는게 아닌가?
// 우리 account에는 논스 밸런스 스토리지 코드해시만 있는데, 토큰을 홀딩하고있는 정보는 다른 컨트랙에 있음
// 컨트랙트 주소에는 스토리지가 있음, [stroage] balances[dokite.eth] = 100;
// 느낌상 내 지갑에서 나가는 것 같지만, 나와 상관 없는 컨트랙에서 숫자가 빠져나갈 뿐인 것 ???? 이해가 안 감
// transfer의 실행 주체는 owner다 : address owner = msg.sender;
function transfer(address _to, uint _value) public returns (bool success) {
address owner = msg.sender;
// 발생하지 말아야 할 예외처리
// 1. 잔고보다 더 많이 보내려 한다면? : 이 사람이 갖고있는 토큰 개수가 보내려는 토큰보다 많은가? - require 사용
require( balances[owner] >= _value);
// 2. 실제 전송: 내 잔액에서 (-) , 받는 사람 잔액 (+)
balances[owner] -= _value;
balances[_to] += _value;
// Transfer Event Emit 실행
emit Transfer(owner, _to, _value);
// return true 끝
return true;
// **만약**
// 100개를 전송한다 할때 , 90개는 전송하고 10개는 내가 갖도록 구현(커뮤니티에 주도록)한다면??
// address feeAccount = 0x ; // 커뮤니티 주소
// address owner = msg.sender;
// require( balances[owner] >= _value);
// balances[owner] -= _value;
// uint fee = amount / 10;
// uint amountWithoutFee = amount - fee;
// balance[feeAccount] += fee;
// balance[to] += amountWithoutFee;
// emit Transfer(owner, _to, _value);
// return true;
}
// transferFrom의 실행 주체는 owner가 아닌 spender(uniswap의 weth/pepe exchange(pair) 이 토큰들을 스왑하는 컨트랙트,
// exchange의 동작 방식, 유저가 유니스왑 컨트랙트에 스왑하고싶다고 어떤 함수 호츨,
// 이 토큰을 직접 보내는게 아닌, 유니스왑 컨트랙이 가져갈 수 있도록 권한을 주는 거임 -> 유니스왑이 transferFrom을 실행함
// 실행 주체 : sender
function transferFrom(
address _from, // 내 eoa 계정(owner)
address _to, // exchange
uint _value // amount
) public returns (bool success) {
// 발생하지 말아야할 예외처리
// 1. spender에게 권한이 있는가 = owner가 spender한테 권한을 줬는가? = 할당받은 양이 보내려는 amount보다 많아야 한다,
require(allowances[_from][_to] >= _value); // spender = msg.sender임, owner가 spender에게 할당한 양이 _value보다 많은가, msg.sender가 함수 실행 주체??
// 2. owner(from)의 잔액이 충분한가?
require(balances[_from] >= _value);
// 데이터 업데이트
// 1. 잔고 업데이트 from(-) to(+)
balances[_from] -= _value;
balances[_to] += _value;
// 2. allowances양 변경 - 1000개 권한중 200 개 쓰면 800권한이 남았다
allowances[_from][_to] -= _value;
// Event Emit
emit Transfer(_from, _to, _value);
// return true
return true;
}
// approve는 언제 씀?
// 스왑할때
// 1. 내 토큰을 eth로 바꿀때, approved -> max값 넣고 next -> approved 완 - uniswap 디앱은 pepe토큰의 approve 함수를 호출한다
// 2. 유니스왑의 컨트랙트가 pepe의 transferFrom을 호츨해서 내 거에서 pepe를 가져감
// 실행 주체 : owner
function approve(
address _to,
uint _value
) public returns (bool success) {
address owner = msg.sender;
allowances[owner][_to] = _value;
emit Approval(owner, _to, _value);
return true;
}
function balanceOf(
address _owner
) public view returns (uint balance) {
return balances[_owner];
}
function allowance(
address _owner,
address _spender
) public view returns(uint remaining){
return allowances[_owner][_spender];
}
// WETH - wrapped ether는 왜 나왔는가? erc-20 + deposit, withdraw
// deposit 이더리움을 넣으면 민팅한다 - balance오름,이벤트 발생, total supply늘어남
// withdraw
// 이더를 보낸다 address.transfer 뿐
// erc 20 에서는 contract.transfer, contract.transferFrom
// ether to token
// 기본적으로 이더를 받아야하고
function deposit() public payable returns (bool success) {
// msg.value = 내가 바꾸고 싶은 값
// msg.sender = 바꾸는 주체, 토큰을 받을 주체
uint amount = msg.value;
address owner = msg.sender;
// 0.5ETH를 넣으면 0.5WETH를 발행(민팅)
// 1. 잔액 변경
balances[owner] += amount;
// 2. totalSupply 조정
totalSupply += amount;
// 3. 이벤트 발생
emit Transfer(address(0), owner, amount);
// 4. Vault Balance 0.5ETH
return true;
}
// token to ether
// 특정 개수의 수량으로 이더를 출금하는 함수
function withdraw(uint amount) public returns (bool success) {
// amount WETH -> ETH로 바꿈
// owner의 밸런스가 amount보다 많은가?
address owner = msg.sender;
require(balances[owner] >= amount);
// 1. WETH를 받는다 = balances 감소됨
// 선택의 문제
// WETH를 소각한다는 관점 - owner의 balance를 줄인다 = totalSupply를 줄인다
// WETH의 소유자를 컨트랙트로 바꾸는 관점 - weth의 소유자를 컨트랙트 자기 자신(address(this))으로 바꾼다, totalSupply는 바뀌지 않음?????
balances[owner] -= amount;
// 2. totalSupply 발행량 조절
totalSupply -= amount;
// 3. transfer이벤트 발생
emit Transfer(owner,address(0), amount);
// 4. ETH 출금 : payable(msg.sender).transfer()
payable(owner).transfer(amount);
// 5. return true
return true;
}
//ERC-721 구현해보기***
//ERC-1155 코드 보기
}