BlockChain

Upgrade ERC20 - Owner 추가 및 Token Lock 재사용

es_0409 2022. 4. 6. 13:32
이번에는 ERC20토큰 발행에 사용했던 라이브러리인 OwnerHelper를 이용해 Owner를 여러 명으로 추가하고, Token Lock 기능을 일회성이 아닌 재사용이 가능하도록 수정해보았다.

 

<기존의 OwnerHelper>

abstract contract OwnerHelper {
  	address private _owner;

  	event OwnershipTransferred(address indexed preOwner, address indexed nextOwner);

  	modifier onlyOwner {
		require(msg.sender == _owner, "OwnerHelper: caller is not owner");
		_;
  	}

  	constructor() {
		_owner = msg.sender;
  	}

  	function owner() public view virtual returns (address) {
		return _owner;
  	}

  	function transferOwnership(address newOwner) onlyOwner public {
		require(newOwner != _owner);
		require(newOwner != address(0x0));
		_owner = newOwner;
		emit OwnershipTransferred(_owner, newOwner);
  	}
}

기존의 ownerHelper에는 컨트랙트를 생성한 owner만 특정 함수들을 이용할 수 있도록 제어해주는 modifier와, 새로운 owner로 owner를 변경시켜주는 transferOwnership 함수가 들어 있다.

오늘의 핵심은 바로 이 ownerHelper를 사용하여 owner를 인원 제한 없이 늘린 후에, ERC20 token발행 이후 필요한 안건이 있으면 회의를 하도록 하며 owner 집단에서 투표를 통해 안건을 통과시킬지, 말지를  만장일치 제도로 통과시키도록 하는것이다.

 

먼저 변경된 ownerHelper의 전체 코드를 첨부한다. 현장의 생생함(?)을 위해 삽질 기록인 무수한 주석을 삭제하지 않았다..

abstract contract OwnerHelper{
  	// address private _owner;
    using SafeMath for uint;

    // struct Agenda {
    //     bytes name;
    //     address[] voters; 
    //     // address[] voter;
    // }

    // [Try] owner를 private 으로 선언해야한다.
    mapping (address => address) public _owner;
    mapping (string => uint) public _ownercnt; 
    // agenda id(투표안건 0번)
    // agenda voter id 투표안건 0번의 투표자 0번
    // agenda voter id's address 
    mapping (string => mapping(address => address)) public _agenda;
    mapping (string => uint256) public _voters;

    // address[2] private _owner;

  	event OwnershipTransferred(address indexed preOwner, address indexed nextOwner);

  	modifier onlyOwner {
		require(msg.sender == _owner[msg.sender], "OwnerHelper: caller is not owner");
		_;
  	}
  	constructor() {
            _owner[msg.sender] = msg.sender;
            _ownercnt["ownercnt"] = 1;
  	}

    //    function owner() public view virtual returns (address) {
    //        return _owner;
    //    }

  	function transferOwnership(address preOwner, address newOwner) onlyOwner public {
            require(newOwner != _owner[newOwner]);
            require(preOwner == _owner[preOwner]);
            require(newOwner != address(0x0));
            // PreOwner의 키-밸류를 newOwner의 키-밸류로 변경
    	    // preOwner 삭제 -> 삭제를 하면 키-밸류가 사라지는게 아니라 0으로 초기화됩니다.
            delete _owner[preOwner];
            _owner[newOwner] = newOwner;
    	    emit OwnershipTransferred(preOwner, newOwner);
  	}

<변수 설명>

_owner : 기존의 ownerHelper에서 owner를 추가하기 위해서 _owner를 mapping을 활용해 객체 형태로 받아 올 필요가 있었다.

address형 키에 address를 맵핑해서, 다른 함수에서 msg.sender를 _owner 맵핑의 키값으로 활용하여 owner로 등록된 address인지 확인하려는 의도로 address를 키값으로 사용하였다.

_ownercnt : 관리자의 수를 의미한다.

_agenda : 투표 안건을 의미한다. 

안건을 기획할 때 가장 많은 시행착오가 있었다. 솔리디티에 익숙하지 않아 초반에는 구조체 내부에 맵핑을 사용하여 투표자들 명단을 받으려고 했었다. 하지만 이 시도는 계속해서 실패했고 오랜 시간 고민 끝에 이중 맵핑을 사용하여 문제를 해결했다.

_voters : 특정 안건에 투표한 owner의 숫자를 의미한다.


<onlyOwner>

단일 관리자로 구성되어있을 때에 반해서 관리자가 다수로 늘어났기 때문에, 맵핑변수를 활용해서 owner를 체크해줄 필요가 있었다.

modifier onlyOwner {
		// msg.sender를 키값으로 넣어 owner 맵핑에 존재하는지 확인
		require(msg.sender == _owner[msg.sender], "OwnerHelper: caller is not owner");
		_;
  	}

<transferOwnership, giveOwnership 함수>

function transferOwnership(address preOwner, address newOwner) onlyOwner public {
            require(newOwner != _owner[newOwner]);
            require(preOwner == _owner[preOwner]);
            require(newOwner != address(0x0));
            // PreOwner의 키-밸류를 newOwner의 키-밸류로 변경
    	    // [try] preOwner 삭제 -> 삭제를 하면 키-밸류가 사라지는게 아니라 0으로 초기화됩니다.
            delete _owner[preOwner];
            _owner[newOwner] = newOwner;
    	    emit OwnershipTransferred(preOwner, newOwner);
  	}

       // 관리자 권한 부여 함수
    function giveOwnership(address newOwner) onlyOwner public{
        // _owner 객체에 추가 (newOwner 어드레스를)
        // 유효한 주소인지 검사
        // 이미 관리자로 등록이 되었는지
    require(newOwner != address(0x0));
    require(newOwner != _owner[newOwner]);
    // _newOwner가 이미 3개의 키-값 이 있는지?? -> map 의 키 개수를 계산할 수는 없다
    _owner[newOwner] = newOwner;
    _ownercnt["ownercnt"] = _ownercnt["ownercnt"].add(1); // safeMath add 이용
    }

transferOwnership : preOwner는 이제 _owner에 맵핑된 형태로 존재하므로, 각자의 주소를 받아서 ownership을 상속해줄 필요가 있었다. 따라서 맵핑 변수에서 _preOwner를 찾아 삭제하고, newOwner로 변경시켜줄 방안을 모색했는데, delete 키워드를 찾아냈다. 하지만 아쉬운 점은, delete를 사용하면 preOwner가 차지하고 있던 데이터 공간 자체를 삭제시켜주는것이 아니라, 해당 데이터를 0으로 초기화시켜버린다. 따라서 실제로 깨끗하게 모든게 삭제된다고 보긴 어렵다. 

giveOwnership : 관리자 권한 부여를 위해 onlyOwner 제어자를 붙여줬다. newOwner의 address를 유효한 주소인지 검사한 후 이미 관리자로 등록이 됐는지 확인한다. 그런 후에 _owner 맵핑에 추가해준다. _ownercnt의 경우 관리자의 권한이 변경될 때는 추가해 줄 필요가 없지만 (총 관리자 인원이 증가한 게 아니므로) giveOwnership에서는 꼭 추가해줘야한다. SafeMath 라이브러리를 사용했다.


이렇게 하면 OwnerHelper 편집이 끝났다. 본격적으로 토큰 컨트랙트에 대해서 설명하겠다.

먼저 전체 코드를 첨부한다.

 

전체 코드

// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.13;

interface ERC20Interface {
    function totalSupply() external view returns (uint256);
    function balanceOf(address account) external view returns (uint256);
    function transfer(address recipient, uint256 amount) external returns (bool);
    function approve(address spender, uint256 amount) external returns (bool);
    function allowance(address owner, address spender) external view returns (uint256);
    function transferFrom(address spender, address recipient, uint256 amount) external returns (bool);
    
    event Transfer(address indexed from, address indexed to, uint256 amount);
    event Transfer(address indexed spender, address indexed from, address indexed to, uint256 amount);
    event Approval(address indexed owner, address indexed spender, uint256 oldAmount, uint256 amount);
}

library SafeMath {
  	function mul(uint256 a, uint256 b) internal pure returns (uint256) {
		uint256 c = a * b;
		assert(a == 0 || c / a == b);
		return c;
  	}

  	function div(uint256 a, uint256 b) internal pure returns (uint256) {
	    uint256 c = a / b;
		return c;
  	}

  	function sub(uint256 a, uint256 b) internal pure returns (uint256) {
		assert(b <= a);
		return a - b;
  	}

  	function add(uint256 a, uint256 b) internal pure returns (uint256) {
		uint256 c = a + b;
		assert(c >= a);
		return c;
	}
}

abstract contract OwnerHelper{
  	// address private _owner;
    using SafeMath for uint;

    // struct Agenda {
    //     bytes name;
    //     address[] voters; 
    //     // address[] voter;
    // }

    // [Try] owner를 private 으로 선언해야한다.
    mapping (address => address) public _owner;
    mapping (string => uint) public _ownercnt; 
    // agenda id(투표안건 0번)
    // agenda voter id 투표안건 0번의 투표자 0번
    // agenda voter id's address 
    mapping (string => mapping(address => address)) public _agenda;
    mapping (string => uint256) public _voters;

    // address[2] private _owner;

  	event OwnershipTransferred(address indexed preOwner, address indexed nextOwner);

  	modifier onlyOwner {
		require(msg.sender == _owner[msg.sender], "OwnerHelper: caller is not owner");
		_;
  	}
  	constructor() {
            _owner[msg.sender] = msg.sender;
            _ownercnt["ownercnt"] = 1;
  	}

    //    function owner() public view virtual returns (address) {
    //        return _owner;
    //    }

  	function transferOwnership(address preOwner, address newOwner) onlyOwner public {
            require(newOwner != _owner[newOwner]);
            require(preOwner == _owner[preOwner]);
            require(newOwner != address(0x0));
            // PreOwner의 키-밸류를 newOwner의 키-밸류로 변경
    	    // [try] preOwner 삭제 -> 삭제를 하면 키-밸류가 사라지는게 아니라 0으로 초기화됩니다~!~!
            delete _owner[preOwner];
            _owner[newOwner] = newOwner;
    	    emit OwnershipTransferred(preOwner, newOwner);
  	}

       // 관리자 권한 부여 함수
    function giveOwnership(address newOwner) onlyOwner public{
        // _owner 객체에 추가 (newOwner 어드레스를)
        // 유효한 주소인지 검사
        // 이미 관리자로 등록이 되었는지
    require(newOwner != address(0x0));
    require(newOwner != _owner[newOwner]);
    // _newOwner가 이미 3개의 키-값 이 있는지?? -> map 의 키 개수를 계산할 수는 없다
    _owner[newOwner] = newOwner;
    _ownercnt["ownercnt"] = _ownercnt["ownercnt"].add(1); //SafeMath.add로 고쳐보기
    }
}

contract SimpleToken is ERC20Interface,OwnerHelper {
    using SafeMath for uint256;
    mapping (address => uint256) private _balances;
    mapping (address => mapping (address => uint256)) public _allowances;
    mapping (address => bool) public _personalTokenLock;

    uint256 public _totalSupply;
    string public _name;
    string public _symbol;
    uint8 public _decimals;
    bool public _tokenLock;

    constructor(string memory getName, string memory getSymbol) {
        _name = getName;
        _symbol = getSymbol;
        _decimals = 18;
        _totalSupply = 100000000e18;
        _balances[msg.sender] = _totalSupply;
        _tokenLock = true;
    }

// myStruct = Struct(number);
// myStruct.myMap[key] = value; 

    // 안건 올리는 함수
    function listenMyopinion (string memory agenda) onlyOwner public{
        require(msg.sender == _owner[msg.sender]);  // 관리자만 투표 참여
        // Agenda a = Agenda(agenda,voters[msg.sender] = msg.sender);
        _agenda[agenda][msg.sender] = msg.sender;
        // _agenda[agenda]["voters"] =1;
        _voters[agenda] = 1;

    }

    function voteYouropinion (string memory agenda) onlyOwner public{
        require(msg.sender == _owner[msg.sender]);  // 관리자만 투표 참여
        require(msg.sender != _agenda[agenda][msg.sender]); // 이미 투표했는지 검사
        _agenda[agenda][msg.sender] = msg.sender;
        _voters[agenda] = _voters[agenda].add(1);
    }

    function voteCheck (string memory agenda) public view returns (bool result){ //view옵션 : storage 데이터 읽기 전용, pure : starage 데이터 읽지도 못함(매개변수만 사용)
        if(_voters[agenda] == _ownercnt["ownercnt"]){
            result = true;
        }
        else{
            result = false;
        }
    }

    function isTokenLock(address from, address to) public view returns (bool lock) {
        lock = false;

        if(_tokenLock == true)
        {
             lock = true;
        }
        if(_personalTokenLock[from] == false || _personalTokenLock[to] == false) {
             lock = true;
        }
    }

    // 재사용 함수 또한 Owner만 권한을 부여하는 방향으로,,
    // 토큰락이 false -> true인 흐름으로 설정해주면 되지 않을까..?

    function applyTokenLock () onlyOwner public {
        require(_tokenLock == false);
        _tokenLock = true;
    }

    function applyPersonalTokenLock (address _who) onlyOwner public {
        require(_personalTokenLock[_who] == true);
        _personalTokenLock[_who] = false;
    }
    // -------------------------------------------- //

    function removeTokenLock() onlyOwner public {
        require(voteCheck("removeTokenLock")==true);
        require(_tokenLock == true);
        _tokenLock = false;
    }

    function removePersonalTokenLock(address _who) onlyOwner public {
        require(voteCheck("removePersonalTokenLock")==true);
        require(_personalTokenLock[_who] == false);
        _personalTokenLock[_who] = true;
    }

    
    
    function name() public view returns (string memory) {
        return _name;
    }
    
    function symbol() public view returns (string memory) {
        return _symbol;
    }
    
    function decimals() public view returns (uint8) {
        return _decimals;
    }
    
    function totalSupply() external view virtual override returns (uint256) {
        return _totalSupply;
    }
    
    function balanceOf(address account) external view virtual override returns (uint256) {
        return _balances[account];
    }
    
    function transfer(address recipient, uint amount) public virtual override returns (bool) {
        _transfer(msg.sender, recipient, amount);
        emit Transfer(msg.sender, recipient, amount);
        return true;
    }
    
    function allowance(address owner, address spender) external view override returns (uint256) {
        return _allowances[owner][spender];
    }
    
    function approve(address spender, uint amount) external virtual override returns (bool) {
        uint256 currentAllownace = _allowances[msg.sender][spender];
        require(currentAllownace >= amount, "ERC20: Transfer amount exceeds allowance");
        _approve(msg.sender, spender, currentAllownace, amount);
        return true;
    }
    
    function transferFrom(address sender, address recipient, uint256 amount) external virtual override returns (bool) {
        _transfer(sender, recipient, amount);
        emit Transfer(msg.sender, sender, recipient, amount);
        uint256 currentAllowance = _allowances[sender][msg.sender];
        require(currentAllowance >= amount, "ERC20: transfer amount exceeds allowance");
        _approve(sender, msg.sender, currentAllowance, currentAllowance.sub(amount));
        return true;
    }
    
    function _transfer(address sender, address recipient, uint256 amount) internal virtual {
        require(sender != address(0), "ERC20: transfer from the zero address");
        require(recipient != address(0), "ERC20: transfer to the zero address");
        require(isTokenLock(sender, recipient) == false, "TokenLock: invalid token transfer");
        uint256 senderBalance = _balances[sender];
        require(senderBalance >= amount, "ERC20: transfer amount exceeds balance");
        _balances[sender] = senderBalance.sub(amount);
        _balances[recipient] = _balances[recipient].add(amount);
    }
    
    function _approve(address owner, address spender, uint256 currentAmount, uint256 amount) internal virtual {
        require(owner != address(0), "ERC20: approve from the zero address");
        require(spender != address(0), "ERC20: approve to the zero address");
        require(currentAmount == _allowances[owner][spender], "ERC20: invalid currentAmount");
        _allowances[owner][spender] = amount;
        emit Approval(owner, spender, currentAmount, amount);
    }
}

컨트랙트 코드가 조금 길지만, 기존의 ERC20 컨트랙트와 다를게 없으므로 주로 편집된 함수 위주로 설명하겠다.

가장 핵심적인 기능은 관리자만 참여할 수 있는 각종 함수들이 투표를 통해 실행될 수 있도록 변경된 점이다.


투표 기능

 // 안건 올리는 함수
    function listenMyopinion (string memory agenda) onlyOwner public{
        require(msg.sender == _owner[msg.sender]);  // 관리자만 투표 참여
        // Agenda a = Agenda(agenda,voters[msg.sender] = msg.sender);
        _agenda[agenda][msg.sender] = msg.sender;
        // _agenda[agenda]["voters"] =1;
        _voters[agenda] = 1;

    }

    function voteYouropinion (string memory agenda) onlyOwner public{
        require(msg.sender == _owner[msg.sender]);  // 관리자만 투표 참여
        require(msg.sender != _agenda[agenda][msg.sender]); // 이미 투표했는지 검사
        _agenda[agenda][msg.sender] = msg.sender;
        _voters[agenda] = _voters[agenda].add(1);
    }

    function voteCheck (string memory agenda) public view returns (bool result){ //view옵션 : storage 데이터 읽기 전용, pure : starage 데이터 읽지도 못함(매개변수만 사용)
        if(_voters[agenda] == _ownercnt["ownercnt"]){
            result = true;
        }
        else{
            result = false;
        }
    }

listenMyopnion : 안건을 올리는 함수이다. 관리자만 참여할 수 있으며, 이 때 안건으로 등록될 수 있는 string은 아래 두 가지로 테스트하였다. 이 함수를 이용해 아래 안건 중 하나를 올리면, _voters또한 해당 안건에 대하여 1이 올라간다.

  • removeTokenLock
  • removePersonalTokenLock

onlyOwner 모디파이어가 붙어 있는 모든 함수에 위 기능을 집어넣고 싶었지만, 테스트를 위해 2개로 제한하였다. 같은 매커니즘으로 동작하므로, 같은 방식으로 추가해주기만 하면 된다.

 

voteYouropinion : 안건에 투표하는 함수이다. 마찬가지로 관리자만 참여할 수 있으며 _agenda 맵핑에 투표자 주소를 키-밸류 형태로 저장하고, _voters 맵핑 또한 1 올려준다. 이런 방식을 통해 특정 안건에 대한 투표자의 수를 알 수 있다. 

 

voteCheck : voteCheck를 이용해 특정 안건에 등록된 투표자의 수(_voters['특정 안건'])가 _ownercnt와 같은지 비교할 수 있다. 이 함수의 결과가 true를 반환해야만 실행되는 함수를 살펴보자.

 function removeTokenLock() onlyOwner public {
        require(voteCheck("removeTokenLock")==true);
        require(_tokenLock == true);
        _tokenLock = false;
    }

위 함수는 TokenLock을 해제하는 함수이다. 만장 일치로 모든 관리자가 안건에 동의한 경우에만 실행된다.


<TokenLock 재활용>

 // 토큰락 제거 함수
    function removeTokenLock() onlyOwner public {
        require(voteCheck("removeTokenLock")==true);
        require(_tokenLock == true);
        _tokenLock = false;
    }

    function removePersonalTokenLock(address _who) onlyOwner public {
        require(voteCheck("removePersonalTokenLock")==true);
        require(_personalTokenLock[_who] == false);
        _personalTokenLock[_who] = true;
    }

// 토큰락 적용 함수
// 재사용 함수 또한 Owner만 권한을 부여하는 방향으로,,
// 토큰락이 false -> true인 흐름으로 설정해주면 되지 않을까..?

    function applyTokenLock () onlyOwner public {
        require(_tokenLock == false);
        _tokenLock = true;
    }

    function applyPersonalTokenLock (address _who) onlyOwner public {
        require(_personalTokenLock[_who] == true);
        _personalTokenLock[_who] = false;
    }

토큰락 재활용은 단순한 흐름으로 생각하였다.

한번 해제하고나서 끝나지 않게 하려면 가장 단순한 아이디어는 다시 토큰락을 거는 함수를 추가하는것이다.

apply* 함수들은 토큰락을 적용하는 함수이다. 따라서 가장 먼저 토큰락이 해제되어 있는지 검사한다. 그런 후에 반대의 상태로 설정해주면 끝이다.

여기서 주의할 점은, TokenLock의 경우 초기값(해제된 값)이 false로 설정되어 있지만, personalTokenLock의 경우는 초기가 true로 되어있다. 따라서 함수 내에서 동작의 흐름이 반대로 되어있다. 


실행 모습

테스트 환경 : remix + ganache 로컬 테스트넷

테스트를 위해 3개의 계정을 준비

초기 세팅

account2 : 컨트랙트 발행자 (초기 관리자)

account3, account4 : 컨트랙트에 관리자로 추가될 계정

관리자 추가

먼저 account3, account4를 관리자로 추가해준다.

안건올리기

그런 뒤 관리자 계정으로 listenMyopinion 함수를 사용해, removeTokenLock을 진행하자는 투표 안건을 올린다.

올린 안건 투표자 체크하기 (1명으로 되어 있음)

투표 안건을 막 올리고 나서 _voters[removeTokneLock] 을 확인해보면, 1명으로 등록되어 있다.

 

안건에 투표 후 투표자 인원 체크

남은 관리자 계정을 이용해 모두 voteYouropnion 함수를 이용해 removeTokenLock에 투표해준 후 투표자 인원을 체크하면 3인으로 등록되어 있다.

투표 완료 후 상태 확인

모든 관리자가 투표를 완료했으므로, voteCheck함수를 통해 removeTokenLock 이라는 안건이 결과적으로 통과가 되었는지 확인한다. true를 반환하고 있으므로 이 함수는 실행 가능하다.


Etherscan에서 확인하기

컨트랙트 주소 : 0xf51ab7C7193dC0c424CdCc271f3cFaeec54c518E

https://ropsten.etherscan.io/address/0xf51ab7c7193dc0c424cdcc271f3cfaeec54c518e

 

Contract Address 0xf51ab7c7193dc0c424cdcc271f3cfaeec54c518e | Etherscan

The Contract Address 0xf51ab7c7193dc0c424cdcc271f3cfaeec54c518e page allows users to view the source code, transactions, balances, and analytics for the contract address. Users can also interact and make transactions to the contract directly on Etherscan.

ropsten.etherscan.io

 


마치며..

 

아쉬운 점

만장일치 제도로 컨트랙트를 기획했지만, 만장일치로 할지, 다수결 제도로 할지 또한 관리자들간의 투표로 정할 수 있었다면 더 좋았겠다는 아쉬움이 남는다. 이것 외에도 owner만 실행할 수 있는 함수가 여러 개 있었지만, 테스트를 위해 일부 함수만 ownerOnly로 실험해본것이 완성도 측면에서 조금 아쉽다.

또한 컨트랙트를 작성하며 초기값으로 설정된 personalTokenLock, TokenLock을 미처 통일할 생각을 하지 못했는데, 둘의 초기값을 통일해서 가독성을 높여줬으면 싶다. 이 외에도 전체적으로 가독성이 떨어지는것 또한 아쉬운점이다. 코딩스킬이 더 많이 늘었으면 좋겠다.

가장 큰 문제점이라고 꼽는것은 owner를 public 으로 선언하는건 옳지 못하다. '일단 되는 것'을 만들기 위해 그런 방법을 선택했지만, 컨트랙트 상속과 변수 선언에 대해 조금 더 이해가 있었더라면 더 좋은 코드가 나오지 않았을까 싶다.

 

좋았던 점

함수의 여러 옵션을 만져볼 수 있어서 좋았다. 그에 따라 자연스럽게 이더리움의 가스비에 대해 생각하게 된 것 같다. 예를 들면, view함수에서는 읽기 전용이기 때문에 가스비가 상대적으로 덜 들고, memory타입 변수를 인자로 사용하는 등 솔리디티에 대해 약간 더 심도 있게 이해할 수 있었다. 

이 컨트랙트를 작성하면서 가장 많이 헤맸던 부분은 맵핑이었는데, 그동안 자바스크립트에서 참조형 변수 중 배열을 주로 사용하면서 자연스럽게 솔리디티에서도 그렇게 접근을 하려고 했고, 진행에 차질이 있어 구글링을 하던 중 배열로 데이터를 처리하는것이 그렇게 권장되지는 않는다는것을 알게 됐다. 그래서 이중 맵핑으로 대체하게 되었는데, 직접 구현해보지 않았으면 절대 몰랐을 정보들을 습득해서 뿌듯했다.

또한 상속에 대해서도 공부할 수 있었다. 함수와 변수의 선언을 전부 private으로 했다가 배포 후 테스트할 때 함수가 나타나지 않아 당황했었는데, 직접 부딪히지 않으면 몰랐을 것 같다.

너무 즐거운 코딩이었다. 정말 온종일 푹 빠져서 이 컨트랙트만 만졌던 것 같다. 솔리디티의 매력을 느낄 수 있는 유익한 시간이었다.   지금 보니 아쉬운점 투성이지만 처음으로 스마트 컨트랙트를 입문하며 작성해본 컨트랙트로써 큰 의미를 두게 되어 포스팅하게 되었다. 

 

개선할 점

아쉬운 점에서 나열했던 요소들을 수정한다면 충분히 쓸만한 컨트랙트가 되지 않을까 싶다. 전체적으로 가독성 좋게 코드를 수정하고, openzeppelin을 사용해서 토큰 관련 코드를 많이 줄이면 더욱 보기 좋을것같다. 

위에서 꼽았던 가장 큰 문제인 public owner를 꼭 개선해보고싶다. 코드를 짜면서도 정말 개운하지 못했던 부분이라. 가장 아쉽게 남았고 가장 큰 오점이라고 생각한다. 

'BlockChain' 카테고리의 다른 글

OpenSea Clone Coding Project - Whale  (0) 2022.04.18
KAS를 사용해서 간단한 서버-클라이언트 프로젝트 만들기  (0) 2022.04.06
Eggman  (0) 2022.03.30
commit reveal scheme  (0) 2022.03.22
Dapp 질의응답  (0) 2022.03.15