[TIL] Part2 - DAY47 Solidity - 은행앱(TEST9), ERC20 학습

박동우·2023년 5월 25일

블록체인 스쿨 3기

목록 보기
44/72

[TIL] 2023-05-25 DAY47


Solidity

GIT 주소 : https://github.com/dowoo303/SOLIDITY


오늘 배운것들

  1. TEST9
    1) 은행 앱 만들어보기
    2) msg sender 이해
  2. ERC20 학습

각종 팁

회고

  1. DAY46은 일때매 바빠서 pass함 ..
  2. 미리 공부하고 싶다?
    슈노르 서명(Schnorr signature)
    세그잇(SegWit)
    탭루트(Taproot)
    올디너스(Ordinals) 프로토콜(Protocol)
    참고



💻 Solidity 코딩

✅ TEST9(은행 앱 만들어보기)

approve 개념: A계좌에서 B계좌로 30원을 보낸다고 가정했을 때, 진짜 30원을 보내는 것처럼 보이지만 사실은 은행이 단순히 +-를 했을 뿐이다. 따라서 보내는 코드를 구성할 것이 아니라 30원이 +되고 -되는 것을 approve해주는 코드를 구성하기만 하면 된다.

코인 지갑들도 사실 지갑에 돈을 들고 있다고 표현하기는 애매하다.
비트 - 시드->프라이빗키->퍼블릭키->주소->UTXO들의 합
이더 - 머클 패트리시아 트리로 잔고 관리

bank라는 컨트랙트를 만들고 bank를 여러개 인스턴스화 시켜서 사용하면 각 인스턴스화가 은행이 되고 그 은행안의 balance라는 mapping이 각자 개인의 계좌가 되는 개념이다.

+수수료 부과하는법: modifier을 하나 만들어서 애초에 처음부터 수수료를 포함한 돈을 받으면 됨

🟥 코드

// SPDX-License-Identifier: GPL-3.0

pragma solidity >=0.7.0 <0.9.0;

// 은행에 입금을 하면 진짜 돈이 빠져나가는 case

contract APP {
    mapping(address=>uint) balance; // 개인 잔액
    // mapping(address=>mapping(address=>uint)) bankAccounts; // 은행 잔고
    receive() external payable{}    // 특정 함수를 통하지 않고 이 컨트랙트도 돈을 받을 일이 있음

    modifier balCheck(address _a, uint _amount) {
        balance[_a] >= _amount;
        _;
    }

    // app 이용 수수료 부과하기
    modifier Fee(uint _amount){
        require(msg.value == _amount*101/100);
        _;
    }


    // 입금 - 해당 App에게 얼만큼의 돈을 입금하겠다.
    function depositToApp() public payable {
        balance[msg.sender] += msg.value;
    }

    // 입금 - 특정 은행에게 얼만큼의 돈을 입금하겠다.
    function depositToBank(address payable _bank, uint _amount) public payable Fee(_amount) {
        Bank targetBank = Bank(_bank);
        require(balance[msg.sender] >= _amount);
        payable(targetBank).transfer(_amount);      // App에서 뱅크로 트랜스퍼
        balance[msg.sender] -= _amount;
        targetBank.Deposit(msg.sender, _amount);    // 특정 은행의 명령자의 계좌에 _amount만큼 입금
    }

    function _depositToBank(address payable _bank, address _to, uint _amount) public {
        Bank targetBank = Bank(_bank);
        require(balance[msg.sender] >= _amount);
        payable(targetBank).transfer(_amount);
        balance[msg.sender] -= _amount;
        targetBank.Deposit(_to, _amount);
    }


    // 인출 - 해당 App에게 얼만큼의 돈을 출금하겠다.
    function withdrawFromApp(uint _amount) public {
        balance[msg.sender] -= _amount;
        payable(msg.sender).transfer(_amount);
    }

    // 인출 - 특정 은행에서 얼만큼의 돈을 출금하겠다.
    function withdrawFromBank(address payable _bank, uint _amount) public {
        Bank targetBank = Bank(_bank);
        targetBank.Withdraw(msg.sender, _amount);
        balance[msg.sender] += _amount;    
    }

    // 송금 - A가 B에게 돈을 보내는 것(직접)
    function transferTo(address _bAccount, uint _amount) public balCheck(msg.sender, _amount) {
        balance[msg.sender] -= _amount;
        balance[_bAccount] += _amount;
    }

    // 은행송금 - A의 은행에서 B의 은행에게 돈을 보내는 것(은행끼리)
    // 사용자의 A bank -> App -> B bank(받는사람)
    function transferWire(address payable _aBank, address _bAccount, address payable _bBank, uint _amount) public {
        withdrawFromBank(_aBank, _amount);
        _depositToBank(_bBank, _bAccount, _amount);
    }
}



contract Bank {
    mapping(address=>uint) private balance; // 보안을 위하여 mapping(address=>uint) public balance;를 프라이빗으로
    receive() payable external{}

    // 잔고확인
    function balanceOf() public view returns(uint) {
        return balance[msg.sender];       // 내 지갑주소만 확인 가능
    }


    // 입금 - 얼만큼의 돈을 입금하겠다.
    function deposit() public payable {
        balance[msg.sender] += msg.value;
    }

    // 밑 타행송금에서 deposit을 하면 자기자신에게 돈을 입금하는 상황이 되므로 주소를 따로 받을 수 있는 _deposit생성
    function _deposit(address _account, uint _amount) internal {
        balance[_account] += _amount;
    }
    
    // 위 _deposit함수를 ERC20 형식으로 분리
    function Deposit(address _account, uint _amount) public {
        require(_amount != 0, "Amount should not be zero");
        _deposit(_account, _amount);
    }


    // 인출 - 얼만큼의 돈을 출금하겠다.
    function withdraw(uint _amount) public {
        balance[msg.sender] -= _amount;
        payable(msg.sender).transfer(_amount);
    }

    // 밑 타행송금에서 withdraw을 하면 자기자신에게 돈을 입금하는 상황이 되므로 주소를 따로 받을 수 있는 _withdraw생성
    function _withdraw(address _account, uint _amount) internal {
        balance[_account] -= _amount;
        payable(msg.sender).transfer(_amount);
    }
    function _withdraw2(address _account, uint _amount) internal {
        balance[_account] -= _amount;
        payable(_account).transfer(_amount);    // 위와 다르게 개인지갑으로 다이렉트
    }
    
    // 위 _withdraw함수를 ERC20 형식으로 분리
    function Withdraw(address _account, uint _amount) public {
        require(balance[_account] >= _amount, "Not enough money");
        _withdraw(_account, _amount);
    }



    // 송금 - A가 B에게 돈을 보내는 것(동행)
    function transferTo(address _bAccount, uint _amount) public {
        balance[msg.sender] -= _amount;
        balance[_bAccount] += _amount;
    }

    // 타행송금 - A가 B의 다른 은행에게 돈을 보내는 것(타행)
    function transferWire(address _bAccount, address payable anotherBank, uint _amount) public {
        Bank B = Bank(anotherBank);
        balance[msg.sender] -= _amount;
        payable(B).transfer(_amount);       // B뱅크 컨트랙트로 트랜스퍼하겠다
        B.Deposit(_bAccount,_amount);       // B뱅크의 b계좌에 _amount만큼 입금
    }
}



✅ OpenZeppelin ERC20

  1. 사이트: https://github.com/OpenZeppelin/openzeppelin-contracts/tree/master/contracts/token/ERC20
  2. 설명서: https://docs.openzeppelin.com/contracts/4.x/api/token/erc20#erc20
  3. https://github.com/ethereum/EIPs

1번 ERC20 살펴보기

  • virtual: 나중에 덮어 쓸 수 있도록 만들어줌

  • override: 기존 함수를 받아와서 덮어 씌움

  • Context 안에 _msgSender가 들어있음

  • 기본적으로 transfer, _transfer 처럼 안전을 위하여 꼭 경로를 한번 더 거치도록 코딩을 해놨다.

  • balancesOf: 누군가의 잔액을 보여줌

  • emit: 이벤트를 발생시킴(로그)

  • allowances: 일정량의 돈(currentAllowance)에 접근할 수 있도록 허락해주는 mapping

  • USDC 주소를 받아와서 ierc20에 있는 transferFrom 함수를 호출

allowance를 쓰는 이유: 일정량의 돈을 어디다가 맡기고 game을 할 수가 없기 때문에 얼마까지 돈을 가져갈 수 있는 권리를 allowance를 통해 설정(더블 매핑: _allowances)하고 그 제한된 범위 내에서만 transfer가 가능하도록 만듬.(제 3자의 역할을 스마트 컨트랙트를 통해 구현)

profile
HELLO!

0개의 댓글