⛓️ Web3

솔리디티 문해력 특강_ 3강

meLR 2023. 9. 7. 20:47

디사이퍼에서 진행한 솔리디티 문해력 특강을 개인적으로 기록으로 남기기 위해 정리한 글입니다.

해당 특강은 아래 링크에서 확인 가능합니다. 강의를 진행해주신 안수찬님께 감사드립니다.

 

안수찬님 블로그 : https://solidity.ansuchan.com/

디사이퍼 솔리디티 문해력 특강 유튜브 : https://www.youtube.com/playlist?list=PLOY0jYV3zWiElk6lAXhuyRJ8dMDqelU_r    


EIP-20

https://eips.ethereum.org/EIPS/eip-20#methods

 

ERC20

data+9 Mathods+2 Event

 

ERC20 Mathods

  1. name(”My First Token”)
  2. symbol(”MFT”)
  3. decimals(18; default)
  4. total supply
  5. transfer (to, amount) → emit Transfer
  6. transferFrom(from, to, amount) → emit Transfer
  7. approve → emit Approval
  8. allowance
  9. balanceOf

ERC20 Events

  1. Transfer
  2. Approval

ERC20 구현

Public으로 선언하면 기본적으로 Getter 함수를 만들어 준다.

contract MyFirstToken {
    string public name = "My First Contract";
    string public symbol = "MFT";
    uint public decimals = 18;
		uint public totalSupply = 0;
}

 

balance

 

balance를 구현 하려면 어떻게 해야할까? 🤔

 

mapping을 통한 키와 밸류 쌍으로 저장 할수있을것이다. 그렇다면 이를 초기화하는 방법은?

 

→ 새컨트랙트를 새로운 연도의 버전으로 재배포를 하거나

→ 단일 컨트랙트로 이중 mapping을 통해 이전연도 올해 연도 두가지로 나눠서 진행 할수도 있을것이다

ex)

mapping(address owner => uint amount)public balances;
mapping(uint year => mapping(address owner => uint amount)) balancesOfYears;

balancesOfYears[2022][mer.eth] = 100;
balancesOfYears[2023][mer.eth] = 0;

 

일반적인 balances 구현

mapping(address owner => uint amount)public balances;

 

balancesOf

function balancesOf(address owner) public view returns (uint amount){
    return balances[owner];
}

 

Transfer

네가지 단계로 함수를 구현할때 생각해볼수 있다.

  1. error
  2. data update
  3. event
  4. return
event Transfer (address indexed from, address indexed to, uint256 amount);

function transfer(
        address to, 
        uint amount
    ) public returns (bool success){
        address owner = msg.sender;
        require(balances[msg.sender]>=amount,"Not Enough");
        balances[msg.sender]-=amount;
        balances[to]+=amount;
        emit Transfer(owner,to, amount);
        return true;
    }

우리가 흔히 내 EOA 계정에 토큰이 들어와 있는 줄 알지만 사실은 해당 토큰의 CA에 키와 밸류 값으로 mapping 되어있다.

만약 토큰을 보내고 싶다면 해당 토큰의 CA 에서 transfer 함수를 호출해서 매핑 되어있는 밸류 값을 알맞게 바꿔주고 true를 리턴하는 식으로 작동을 한다.

 

transferFrom

중요한것은 우선 이 함수를 불러내는 실행주체는 spender라는 사실이다.

spender는 owner가 일정량을 위임해 줌으로써 위임해준 양만큼 해당 토큰을 옮길수있는 주체를 말한다.

 

그렇기에 mapping을 통해 위임해준 토큰양을 저장해두고

Error에서는 owner가 가진양과 spender가 사용 할수있는 양이 모두 보내려는 양보다 큰지 체크해야할것이다.

또한 데이터도 owner의 토큰 잔액뿐만 아니라 spender에게 위임해준양까지 고려하여 위임양을 차감해주어야 모든 데이터 업데이트가 끝났다고 볼수있다.

 

//누가 누구에게 얼마만큼 인출 권한을 주었는가?
    mapping(address owner => mapping(address sender => uint)) public allowances;		

//실행주체 : spender (uniswap pair/exchange)
    function transferFrom(
        address owner,
        address to,
        uint amount
    ) public returns (bool success){
        address spender = msg.sender;
        //1.Error
        //1) 잔액이 충분한가?
        require(balances[owner]>= amount);
        //2) 권한이 있는가?
        require(allowances[owner][spender]>= amount);
        //2. Data update
        balances[owner]-=amount;
        balances[to]+=amount;
        allowances[owner][spender]-=amount;
        //3.Event
        emit Transfer(owner,to, amount);
        //4.Return
        return true;
    }

 

approve

function approve(
        address spender,
        uint amount
    ) public returns (bool success) {
        address owner =msg.sender;
        allowances[owner][spender]=amount;
        emit Approval(owner,spender,amount);
        return true;
    }
💡 이더스캔에서 내가 approve 한 항목들을 살펴볼수있다.

 

allowance

function allowance(
        address owner,
        address spender
    ) public view returns (uint amount) {
        return allowances[owner][spender];
    }

 

Wrapped Ether

두가지 함수 deposit withdraw를 생성해보자

 

deposit

function deposit()public payable returns (bool success){
        address owner = msg.sender;
        uint amount = msg.value;
        balances[owner] += amount;
        totalSupply += amount;
        emit Transfer(address(0), owner, amount);
        return true;
    }

 

withdraw

withdraw 구현을 할때 다양한 관점으로 접근할수있는데

밑의 함수는 토큰 소각후 이더로 변환이라는 방법을 따랐다.

function withdraw(
        uint amount
    ) public returns(bool success){
        address owner = msg.sender;
        balances[owner]-=amount;
        totalSupply-=amount;
        payable(owner).transfer(amount);
        emit Transfer(owner, address(0), amount);
        return true;
    }

 

💡어떤 기능을 볼때 함수 내부구현이 어떻게 되어있는지 살펴보자
💡 실행주체가 누구인지 항상 생각하며 코드짜기

 

Uniswap

  1. 유니스왑은 swap이라는 함수안에 transferFrom이 들어가있다.
  2. swap을 사용하기 위해선 approve해서 토큰을 위임해줘야 하기 때문에 처음에 항상 approve과정이 필요하다.
  3. 결국 유니스왑 토큰도 ERC20을 따르고 있다. 유동성을 공급하거나 제거하면 해당 유니스왑 LP 토큰을 소각한다.