이전글 - [Reboot 이타인클럽] #7 홈페이지 개편 완료. 이타인 얼굴공개, 스팀잇연동](https://steemit.com/hive-196917/@etainclub/reboot-7)
이타인클럽 리부트 중입니다. 많은 관심과 응원, 참여 부탁드립니다.
- 홈페이지 개편 (https://etain.club)
- 스마트 컨트랙트 토큰 적용
- 도움 요청 분야 세분화
- 영상 통화를 이용한 도움 받기
왜 ERC777 토큰을 사용할까요?
ERC777이 ERC20보다 좋은 점이 있습니다. 사실 이더리움 블록체인 앱들의 문제는 바로 gas 비용입니다. 스마트 컨트랙트의 기능을 호출할 때마다 호출하는 사람이 gas 비용을 지불해야만 합니다.
- gas비용을 지불하려면 어떤 과정을 거치게 되죠?
네, 그렇습니다. 이더가 필요합니다. 앱은 이더와 상관없는 토큰을 위한 것이지만, 정작 토큰 관련 트랙잭션을 블록에 기록하려면 gas비를 내야 합니다.
이 문제가 이더리움 블록체인을 사용하는 앱들의 가장 큰 허들일 것입니다. 앱 사용자들이 이더를 보충해 놔야 하니까요. ERC20토큰에서는 이러한 문제를 해결할 수 없었는데, ERC777은 관리자가 대신 gas비를 낼 수 있도록 했습니다. 사용자입장에서는 이더를 구매할 필요가 없으니 사용성이 확 올라가겠죠!
ERC777의 operatorSend
ERC777에는 관리자의 역할로 operator라는 것이 있습니다. operator가 송금 권한을 이임받아서 대신 송금하게 됩니다. 그러면 관리자가 스마트 컨트랙트를 호출하는 방식으로 하면 gas비를 operator가 대신 낼 수 있습니다. 이타인클럽 앱 제작에도 매우 유용한 기능입니다.
이 기능이 없다면... 생각하기도 싫습니다. 사용자가 직접 이더를 채굴하던지, 구매를 하던지 해야 합니다. 물론 보다 쉬운 방법은 관리자가 이더 수도(faucet)를 만들어서 사용자에게 나눠 주는 방식도 있을 수 있지만, 그것마저도 복잡합니다. 사용자 입장에서는 그런 것도 신경쓰기 싫고, 블록체인 자체에 대한 지식이 없어도 서비스만 잘 돌아가는게 장땡이겠죠.
- ERC777의 단점
단점이라기 보다는 개발할 때, 정보가 너무 없는게 단점입니다. 그리고 solidity 0.6.0 버전에 대해서는 정보가 별로 없습니다.
다음과 같이 API는 잘 정리되어 있는데, ERC777 사용 케이스가 없어도 너무 없습니다.
https://docs.openzeppelin.com/contracts/3.x/api/token/erc777#IERC777-operatorSend-address-address-uint256-bytes-bytes-
스마트 컨트랙트 수정
고생고생하여 operatorSend 테스트를 완료했습니다. 이런 이유로 이전에 올렸던 토큰 컨트랙트를 다음과 같이 수정합니다.
pragma solidity ^0.6.0;
//import "../node_modules/@openzeppelin/contracts/token/ERC777/ERC777.sol";
import "@openzeppelin/contracts/token/ERC777/ERC777.sol";
contract EtainClubToken is ERC777 {
// constructor
constructor(uint256 initialSupply, address[] memory defaultOperators)
public
ERC777("EtainClubToken", "ECT", defaultOperators)
{
// create initial supply tokens and then assigns them to the account
address account = msg.sender;
_mint(account, initialSupply, "", "");
}
}
- ERC777 생성자의 마지막 인자에 주목
- defaultOperators를 입력 받게 되어 있음
- defaultOperators는 기본적인 operators 주소 배열을 설정
- ERC777 생성자 내부에선 _mint 함수로 초기 토큰 개수만큼 생성
deploy 코드 수정
변경한 토큰 컨트랙트를 배포하기 위한 코드도 다음과 같이 수정합니다.
var EtainToken = artifacts.require("EtainClubToken");
require("@openzeppelin/test-helpers/configure")({
provider: web3.currentProvider,
environment: "truffle",
});
const { singletons } = require("@openzeppelin/test-helpers");
module.exports = async function (deployer, network, accounts) {
await singletons.ERC1820Registry(accounts[0]);
await deployer.deploy(EtainToken, 1000, [accounts[0]]);
};
- accounts를 사용하기 위해 ERC1820Registry 사용
- 컨트랙트 deploy하기 위한 인자
- EtainToken: 토큰 컨트랙트 instance
- 1000: 초기 supply 토큰 개수
- [accounts[0]]: defaultOperators로 accounts[0]을 지정
Compile & Migrate
- 수정된 컨트랙트를 다음과 같이 컴파일
$ truffle compile
Compiling your contracts...
===========================
✔ Fetching solc version list from solc-bin. Attempt #1
> Compiling ./contracts/EtainToken.sol
✔ Fetching solc version list from solc-bin. Attempt #1
> Artifacts written to /home/etain/devel/erc-test/build/contracts
> Compiled successfully using:
- solc: 0.6.11+commit.5ef660b1.Emscripten.clang
- migration하기 전에 ganache 블록체인 네트워크 기동
$ ganache-cli -p 7545 -i 5777
Ganache CLI v6.10.0-beta.2 (ganache-core: 2.11.0-beta.0)
Available Accounts
==================
(0) 0x0A8FCf5F743ACE81Fc377136961De926cF30eA5C (100 ETH)
(1) 0x92BF115EFb86D1bC7B703926BFf27F51af8672bD (100 ETH)
...
- 수정된 migrate 코드로 migrate
$ truffle migrate --network ganache
...
2_deploy_contract.js
====================
Replacing 'EtainClubToken'
...
migration까지 잘 됩니다.
테스트
그러면 truffle console을 이용하여 operatorSend 테스트를 해보겠습니다.
- 다음과 같이 truffle console을 실행
$ truffle console --network ganache
1 초기 토큰 개수 확인
truffle(ganache)> EtainClubToken.deployed().then(instance => instance.totalSupply())
BN {
negative: 0,
words: [ 1000, <1 empty item> ],
length: 1,
red: null
}
2 operator 확인
truffle(ganache)> EtainClubToken.deployed().then(instance => instance.isOperatorFor(accounts[0], accounts[1]))
true
truffle(ganache)> EtainClubToken.deployed().then(instance => instance.isOperatorFor(accounts[1], accounts[2]))
false
- accounts[0]은 모든 다른 accounts에 대한 operator임
3 accounts[1]에서 accounts[2]로 토큰 50개 송금
여기서는 먼저 accounts[0]에서 accounts[1]로 100개를 송금하고 나서 진행하는 부분입니다.
truffle(ganache)> EtainClubToken.deployed().then(instance => instance.operatorSend(accounts[1], accounts[2], 50, '0x', '0x'))
{
tx: '0x294aa06213f384a4f47034ceb17569b19c546c604058d9996df6d67ad412d04a',
receipt: {
드디어 나왔네요. opertorSend 사용법입니다.
사용법 참고:https://github.com/0xjac/ERC777/blob/devel/test/utils/operatorSend.js
위와 같이 별도로 컨트랙트를 호출하는 계정을 지정하지 않으면 accounts[0]이 사용됩니다.
위 코드는 accounts[0]이 accounts[1]의 operator로서, accounts[1]을 대신하여 accounts[2]로 토큰 50개를 보내는 얘입니다. 호출할 때, 4번째, 5번째는 추가적인 데이터를 전달할 수 있는데 여기서는 0을 전달하고 있습니다.
- 계좌 잔액 확인
truffle(ganache)> EtainClubToken.deployed().then(instance => instance.balanceOf(accounts[2]))
BN { negative: 0, words: [ 50, <1 empty item> ], length: 1, red: null }
truffle(ganache)> EtainClubToken.deployed().then(instance => instance.balanceOf(accounts[1]))
BN { negative: 0, words: [ 50, <1 empty item> ], length: 1, red: null }
- 컨트랙트 호출자를 명시하기 위해 다음과 같이 호출할 수도 있습니다.
truffle(ganache)> EtainClubToken.deployed().then(instance => instance.operatorSend(accounts[1], accounts[2], 10, '0x', '0x').send({from: accounts[0]})
- 참고 사이트에서는 다음과 같이 함수 끝에 .send함수를 호출하도록 되어 있는데, 다음과 같이 오류 메시지가 보입니다.
EtainClubToken.deployed().then(instance => instance.operatorSend(accounts[1], accounts[2], 10, '0x', '0x').send({from: accounts[0]})
Thrown:
TypeError: instance.operatorSend(...).send is not a function
truffle에서 에러가 발생했지만, 실제 트랜잭션은 정상적으로 이루어집니다.
이상으로 ERC777의 operatorSend함수를 이용해서 대신해서 토큰을 송금하는 것을 테스트해봤습니다. 앱 서비스하는 입자에서는 매우 편리한 기능이 아닐 수 없습니다.
ERC777이 보다 널리 쓰이는데 조금이라도 도움이 되었길 바랍니다!