[Reboot 이타인클럽] #8 ERC777 토큰 테스트, Operator가 대신 송금기능

in hive-196917 •  4 years ago  (edited)

이전글 - [Reboot 이타인클럽] #7 홈페이지 개편 완료. 이타인 얼굴공개, 스팀잇연동](https://steemit.com/hive-196917/@etainclub/reboot-7)

이타인클럽 리부트 중입니다. 많은 관심과 응원, 참여 부탁드립니다.

  • 홈페이지 개편 (https://etain.club)
  • 스마트 컨트랙트 토큰 적용
  • 도움 요청 분야 세분화
  • 영상 통화를 이용한 도움 받기

image.png


왜 ERC777 토큰을 사용할까요?

ERC777이 ERC20보다 좋은 점이 있습니다. 사실 이더리움 블록체인 앱들의 문제는 바로 gas 비용입니다. 스마트 컨트랙트의 기능을 호출할 때마다 호출하는 사람이 gas 비용을 지불해야만 합니다.

  • gas비용을 지불하려면 어떤 과정을 거치게 되죠?
    네, 그렇습니다. 이더가 필요합니다. 앱은 이더와 상관없는 토큰을 위한 것이지만, 정작 토큰 관련 트랙잭션을 블록에 기록하려면 gas비를 내야 합니다.

이 문제가 이더리움 블록체인을 사용하는 앱들의 가장 큰 허들일 것입니다. 앱 사용자들이 이더를 보충해 놔야 하니까요. ERC20토큰에서는 이러한 문제를 해결할 수 없었는데, ERC777은 관리자가 대신 gas비를 낼 수 있도록 했습니다. 사용자입장에서는 이더를 구매할 필요가 없으니 사용성이 확 올라가겠죠!

ERC777의 operatorSend

ERC777에는 관리자의 역할로 operator라는 것이 있습니다. operator가 송금 권한을 이임받아서 대신 송금하게 됩니다. 그러면 관리자가 스마트 컨트랙트를 호출하는 방식으로 하면 gas비를 operator가 대신 낼 수 있습니다. 이타인클럽 앱 제작에도 매우 유용한 기능입니다.

이 기능이 없다면... 생각하기도 싫습니다. 사용자가 직접 이더를 채굴하던지, 구매를 하던지 해야 합니다. 물론 보다 쉬운 방법은 관리자가 이더 수도(faucet)를 만들어서 사용자에게 나눠 주는 방식도 있을 수 있지만, 그것마저도 복잡합니다. 사용자 입장에서는 그런 것도 신경쓰기 싫고, 블록체인 자체에 대한 지식이 없어도 서비스만 잘 돌아가는게 장땡이겠죠.

스마트 컨트랙트 수정

고생고생하여 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이 보다 널리 쓰이는데 조금이라도 도움이 되었길 바랍니다!


도움 주고 받기 앱 helpus

Authors get paid when people like you upvote their post.
If you enjoyed what you read here, create your account today and start earning FREE STEEM!