티스토리 뷰

총 여섯개 코스인데 다섯번째를 시작한다.
대충 설명을 읽어보니 여기서는 토큰이랑, ERC721 표준이랑, 크립토 에셋 등을 다룬다고 한다.
좀비를,, 친구들과 교환할 수 있는.. 그런 환경을 갖추기 위한 것들을 배우나보다..
내 좀비.. 이거 배우고 나면 OpenSea에 올려서 팔 수 있는거야..? 여튼..
Chapter 1: Tokens on Ethereum
토큰에 대해 이야기해본다.. 나는 토큰 == 코인 인 줄 알았는데 아닌가보다.
만일 이더리움에 대해 조금 관심이 있었다면, 아마 사람들이 토큰, 특히 ERC20 토큰에 대해 이야기하는걸 들어봤을거라고 써있는데,
나는 사실 관심이 생긴지 얼마 되지 않아서, 거의 처음 듣는 것 같다. 전에 ERC 뒤에 어떤 숫자 붙은걸 본 적이 있는듯 한데 그 뒤 붙은 숫자가 20인지 뭐였는지 기억도 안난다.
어제는 EIP-3009에 대한 글을 읽었는데, 이거랑은 상관 없는 듯 하고. 어제 본 내용은 다음에 기회가 되면 적어봐야지. 하지만 이왕 말이 나온김에.. EIP-3009 이건 뭐냐.. EIP는 Ethereum Improvement Proposal의 첫 알파벳을 따서 만든 단어다. EIP-3009는 Ethereum Improvement Proposal 3009이게 풀 네임인데, 이더리움 기반의 토큰 (USDC - 코인베이스에서 만드는 USD 달러 가치에 페깅된 스테이블 코인)을 사용하여 결제를 간편하게 처리하는 기술 표준. 생각해보니 어제 본 글이 이더리움 트윗이었는데 링크로 코인베이스가 있었다. 이쪽 소식은 X (구 트위터)가 빠른 것 같다.
다시 돌아가서..
ERC-20 의미는 Ethereum Request for Comments 20이다.
이더리움 토큰은 공통의 규칙을 따르는 스마트 컨트랙트다. transfer(), balanceOf() 이런 기능을 가지고 있고, 내부적으로는 mapping(address => uint256) balnce를 통해서 각 주소가 얼만큼의 balance를 가지고 있는지도 기록한다.
모든 ERC20 토큰은 같은 이름으로 같은 기능을 하는 함수들을 공유한다. 그래서 같은 방식으로 서로 서로 interact 할 수 있다.
예를 들어서, 내가 어떤 ERC20 토큰과 interact 할 수 있는 애플리케이션을 만들었다면, 이 말은 내 앞은 다른 어떤 ERC20 토큰과도 interact 할 수 있다는 의미이다.
다르게 표현하면, ERC-20는 스마트 컨트랙트가 구현해야 할 일련의 규칙과 함수들을 정의한다. transfer는 토큰 전송, balanceOf 하면 잔액 조회, approve 하면 토큰 사용 권한 부여 같은 것들이 그 예시이다. 이 덕분에 ERC-20 토큰들은 동일한 방식으로 작동돼서, 다른 어떤 dApp과도 문제없이 호환이 가능하다.
ERC-20는 이더리움 블록체인에서 대체 가능한 (fungible) 토큰을 만들기 위한 기술 규약이라고 한다. 예를 들어 1USDC 토큰은 다른 1USDC 토큰과 완전히 같다. 반면 NFT (Non-Fungible Token) 이거는 대체가 안된다. 이거는 ERC-721 같은 다른 표준을 사용하며 각 토큰이 고유한 가치를 지닌다고 한다.
그래서 내가 만들면서 배우고 있는 이 좀비 유니버스에서는 ERC-721 표준이 더 적합하다. 왜냐면 0.26 이더를 전송하는건 오케이, 감사합니다, 이지만, 내 귀여운 좀비 0.7 보낼게, 이건 말이 안되고 의미도 없다. 0.7 좀비란 무엇인가? 무엇을 제하면 0.7이 되나.. 등등..
그래서 ERC-721에서는 오직 하나의 완전체 유닛 단위로만 교환이 가능하다고 한다. 그리고 각 유닛은 고유한 ID를 가진다.
난 좀비 오너로서, ERC-721 규약을 따를 유인이 충분한데, 왜냐면 따로 직접 좀비를 사고 팔겠다고 경매나 에스크로를 내가 개발할 필요가 없다. 이미 ERC-721 표준에 정의돼 있기 떄문에, 그것에 맞춰서 컨트랙트를 만들기만 하면 된다.
게다가 이 규약을 따르면 이미 존재하는 NFT 플랫폼 (유명한 OpenSea, Rarible 등)이 내 좀비를 자동으로 인식하고 거래를 지원할 수 있게 된다고 한다. 거대하고 탄탄한 인프라가 이미 갖춰진 셈이다. 그러니 나는 좀비 자체에만! 더 집중할 수 있게 되는, 그런 아름다운 이야기.
Chapter 2: ERC721 Standard, Multiple Inheritance
여러개의 컨트랙트로부터 상속이 가능하다.
pragma solidity >=0.5.0 <0.6.0;
import "./zombieattack.sol";
// Import file here
import "./erc721.sol";
// Declare ERC721 inheritance here
contract ZombieOwnership is ZombieAttack, ERC721 {
}
여러 컨트랙트는 컴마로 구분하면 됨.
Chapter 3: balanceOf & ownerOf
function balanceOf(address _owner) external view returns (uint256) {
return ownerZombieCount[_owner];
}
function ownerOf(uint256 _tokenId) external view returns (address) {
return zombieToOwner[_tokenId];
}
Chapter 4: Refactoring
ERC-721에 따라서 함수 이름을 리팩토링 한다.
// 1. Change modifier name to `onlyOwnerOf`
modifier onlyOwnerOf(uint _zombieId) {
require(msg.sender == zombieToOwner[_zombieId]);
_;
}
Chapter 5: ERC721: Transfer Logic
토큰(좀비) 소유권을 이전하는 두 가지 방식을 소개한다. 첫 번째는 소유자가 직접 전송하는거다. transferFrom(address _from, address _to, uint256 _tokenId) 이런 식이다. from 자리에는 내 주소 msg.sender가 들어가고, to 자리에는 받을 사람의 주소가 들어간다.
두 번째 방식은 승인된 제 3자, 혹은 제3의 컨트랙트가 전송할 수 있도록 하는거다. 먼저 approve(address _approved, uint256 _toeknId) 이런식으로 토큰의 원래 소유가자 이 함수를 호출한다. 그러면 컨트랙트는 누가 토큰을 받을 수 있게 승인됐는지 매핑 형식 mapping (uint256 => address) 형식으로 기록한다. 그러고 나서 원래의 토큰 소유주 혹은 승인된 사람이 transferFrom을 호출하면 컨트랙트가 msg.sender가 소유주인지 승인받은 주소인지 체크해서 소유권을 이전한다.
결국 같은 트랜스퍼 로직이 들어간걸 알수 있다. 그래서 _transfer 함수를 만들어서 transferFrom에서 호출해 쓸 수 있게 한다.
function _transfer(address _from, address _to, uint256 _tokenId) private {
ownerZombieCount[_to]++;
ownerZombieCount[_from]--;
zombieToOwner[_tokenId] = _to;
emit Transfer(_from, _to, _tokenId);
}
Chapter 6: ERC721: Transfer Cont'd
이제 앞에서 이야기한 소유권 이전하는 방식 두 개를 코드로 만든다.
_transfer 함수를 만들어놔서 간단할거다.
mapping (uint => address) zombieApprovals;
function transferFrom(address _from, address _to, uint256 _tokenId) external payable {
require (zombieToOwner[_tokenId] == msg.sender || zombieApprovals[_tokenId] == msg.sender);
_transfer(_from, _to, _tokenId);
}
zombieApprovals 라는 매핑을 새로 정의하고, _transfer 함수를 이용하는 transferFrom 함수를 만들었다.
Chapter 7: ERC721: Approve
여기서는 approve 를 만들건가보다.
zombieownership.sol 파일에서 approve 함수를 만들었다.
function approve(address _approved, uint256 _tokenId) external payable onlyOwnerOf(_tokenId) {
zombieApprovals[_tokenId] = _approved;
}
Chapter 8: ERC721: Approve
ERC721 스펙에는 Approval 이벤트가 있어니 이걸 approve 함수 안에 추가해 줄거다.
erc721.sol 파일을 참고해서
event Approval(address indexed _owner, address indexed _approved, uint256 indexed _tokenId);
Approval 이벤트가 위와 같은 인자를 받는걸 확인하고,
function approve(address _approved, uint256 _tokenId) external payable onlyOwnerOf(_tokenId) {
zombieApprovals[_tokenId] = _approved;
emit Approval(msg.sender, _approved, _tokenId);
}
이렇게 이벤트 한 줄 추가했다.
Chapter 9: Preventing Overflows
이 코스는 아무래도 간단히 핵심을 알려주는거같다. 핵심일수도 있고 진짜 최소한만 알려주는것. 그래야 계속할테니까. 너무 많이 알려주면 무서워서 도망갈 수도 있으니까.
transfer에 위와 같은 기능 뿐만 아니라 다른 피쳐도 추가할 수 있다. 실수로 좀비를 0 주소를 가진데에 보내지 않도록 한 번 더 체크해 주는 기능이라던지, 아님 기본적인 경매 로직을 추가한다던지 등등.
더 자세한 구현을 보고싶으면 OpenZeppelin ERC721 contract를 이 튜토리얼 마치고 보라고 한다. 어디보자..
https://docs.openzeppelin.com/contracts/4.x/erc721
ERC721 - OpenZeppelin Docs
We’ve discussed how you can make a fungible token using ERC20, but what if not all tokens are alike? This comes up in situations like real estate, voting rights, or collectibles, where some items are valued more than others, due to their usefulness, rari
docs.openzeppelin.com
스마트 컨트랙트 작성시에 오버플로우랑 언더플로우를 예방하는것이 필요하다.
OpenZeppelin에서는 이러한 언더플로우, 오버플로우를 사전에 예방할 수 있게 도와주는 SafeMath라는 라이브러리를 만들어놨다고 한다.
근데 찾아보니, 솔리디티 버전이 올라가면서 기본적으로 체크가 되게 바뀌었다고 한다?
https://forum.openzeppelin.com/t/was-safemath-sol-replaced-by-math-sol/42033/3
Was SafeMath.sol replaced by Math.sol?
Yes, from Solidity 0.8.0, all arithmetic is checked by default, and you can find that SafeMath is still in the repository, athough it is now just a wrapper over the built-in overflow checks. so I think you can use Solidity 0.8.x to remove SafeMath from you
forum.openzeppelin.com

여기 자료에서 배우는건 0.5.0 이상 0.6.0 미만이라서 버전 차이가 좀 있나보다.
pragma solidity >=0.5.0 <0.6.0;
import "./zombieattack.sol";
import "./erc721.sol";
여튼 배울건 배우고 넘어가야지~
현 시점 최신은 Solidity 0.8.30~ 인 것 같다. Release 0.8.31 이라는 말도 보이는군.

여튼 다시 돌아와서, 이번 챕터에서 시키는대로 SafeMath 솔리디티 라이브러리를 사용하기 위해, 파일 임포트도 하고, using [라이브러리이름] for [타입] 이런식으로 적었다.
pragma solidity >=0.5.0 <0.6.0;
import "./ownable.sol";
// 1. Import here
import "./safemath.sol";
contract ZombieFactory is Ownable {
// 2. Declare using safemath here
using SafeMath for uint256;
Chapter 10: SafeMath Part2
function _transfer(address _from, address _to, uint256 _tokenId) private {
// 1. Replace with SafeMath's `add`
ownerZombieCount[_to]=ownerZombieCount[_to].add(1);
// 2. Replace with SafeMath's `sub`
ownerZombieCount[_from] = ownerZombieCount[_from].sub(1);
zombieToOwner[_tokenId] = _to;
emit Transfer(_from, _to, _tokenId);
}
그래서 원래 zobie++; 이런식으로 한 코드를 zombie=zombie.add(1); 이런 방식으로 SafeMath를 사용하도록 변경했다.
Chapter 11: SafeMath Part 3
그냥 SafeMath 같은 경우 uint256을 인자로 받고 수행해서, uint16으로 정의된 변수에 add, sub, 이런 식의 연산을 수행하면 그 결과가 uint256으로 전환된다. 이런거 때문에 여기서는 SafeMath16 for uint16, SafeMath32 for uint32도 만들었다.
ibrary SafeMath32 {
function mul(uint32 a, uint32 b) internal pure returns (uint32) {
if (a == 0) {
return 0;
}
uint32 c = a * b;
assert(c / a == b);
return c;
}
function div(uint32 a, uint32 b) internal pure returns (uint32) {
// assert(b > 0); // Solidity automatically throws when dividing by 0
uint32 c = a / b;
// assert(a == b * c + a % b); // There is no case in which this doesn't hold
return c;
}
function sub(uint32 a, uint32 b) internal pure returns (uint32) {
assert(b <= a);
return a - b;
}
function add(uint32 a, uint32 b) internal pure returns (uint32) {
uint32 c = a + b;
assert(c >= a);
return c;
}
}
같은 식으로 SafeMath16도 있음.
Chapter 12: SafeMath Part 4
여기서는 ++ 연산 SafeMath의 add로 다 바꿔줌.
Chapter 13: Comments
솔리디티 커뮤니티에서 사용되는 스탠다느 코멘트 포맷은 natspec 이라고 한다. 다음과 같이 생겼다.
/// @title A contract for basic math operations
/// @author H4XF13LD MORRIS 💯💯😎💯💯
/// @notice For now, this contract just adds a multiply function
contract Math {
/// @notice Multiplies 2 numbers together
/// @param x the first uint.
/// @param y the second uint.
/// @return z the product of (x * y)
/// @dev This function does not currently check for overflows
function multiply(uint x, uint y) returns (uint z) {
// This is just a normal comment, and won't get picked up by natspec
z = x * y;
}
}
@title, @auther 는 당연히 알겠고.
@notice는 사용자에게 이 컨트랙트/함수가 뭘 하는건지 설명한다.
@dev 는 개발자에게 추가로 자세한 설명을 전할 때 사용하고
@param 그리고 @return 은 함수의 인자와 그리고 반환값을 알려준다.
모든건 옵션이지만, 그래도 최소한 @dev는 남겨두는게 좋겠다.
My CryptoZombie Army Has Grown!
I just completed CryptoZombies Lesson 5, and got a level 10 H4XF13LD MORRIS 💯💯😎💯💯 zombie OMG! Thanks @CryptoZombiesHQ.
share.cryptozombies.io
끝~!
- Total
- Today
- Yesterday
- 크립토좀비
- 길리
- 플러터
- zksync
- gili
- 패들보트
- 세상만사새옹지마
- web2
- 오블완
- 선라이즈 패들보트
- 공부좀열심히해라
- web3
- 티스토리챌린지
- til
- contains
- 솔리디티
- 스파르타코딩클럽
- 김영한
- 터틀포인트
- 세폴리아
- 찾아봄
- web2web3비교
- setState
- 길리여행
- prettier
- vscode
- DART
- Flutter
- 테스트넷
- 이더리움
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | ||||
| 4 | 5 | 6 | 7 | 8 | 9 | 10 |
| 11 | 12 | 13 | 14 | 15 | 16 | 17 |
| 18 | 19 | 20 | 21 | 22 | 23 | 24 |
| 25 | 26 | 27 | 28 | 29 | 30 | 31 |