본문 바로가기
블록체인/[파공이]파이썬으로 공부하는 이더리움(Web3)

[web3]PYTHON web3를 활용하여 Smart Contract 배포하기!!(4) - Token 제작

by 일등박사 2022. 9. 25.

2022.08.17 - [일등박사의 생각/블록체인] - [web3] Python Web3.py 패키지를 활용하여 데이터 주고받기!

2022.08.17 - [일등박사의 생각/블록체인] - [web3]PYTHON web3를 활용하여 Smart Contract 배포하기!!(1)

2022.08.20 - [일등박사의 생각/블록체인] - [web3]PYTHON web3를 활용하여 Smart Contract 배포하기!!(2)

2022.08.21 - [일등박사의 생각/블록체인] - [web3]PYTHON web3를 활용하여 Smart Contract 배포하기!!(3)

 


 안녕하세요!!

 

이번 포스팅에서는 Python web3를 활용하여

ERC20 토큰을 만들고,

만든 토큰을 사용자끼리 주고받는 기능을 구현해 보겠습니다!!

 

 

Ethereum Machine에 데이터를 저장하는 함수를 생성해보겠습니다!

우선 토큰을 만들  솔리티디 코드는 아래와 같습니다!

 

Symbol DLAB의 DATALAB이라는, 발행량 10만개의 토큰을 만들 예정입니다.

내부의 함수로는

- Token 심볼 조회하기(symbol)

- 지갑의 잔액 조회하기(balanceOf)

- 총 발행량 조회하기(totalSupply)

- 보내기(transfer) 

의 기능기 구성되어있습니다!!

pragma solidity ^0.4.24;

// ----------------------------------------------------------------------------
// Sample token contract
//
// Symbol        : DLAB
// Name          : DATALAB Token
// Total supply  : 100000
// Decimals      : 2
// Owner Account : 0xD56a953478DeF80377e5717e6a4bf1C70174A7dA
//
// Enjoy.
//
// (c) by Idea Inven Doohee 2021. DM Licence.
// ----------------------------------------------------------------------------


// ----------------------------------------------------------------------------
// Lib: Safe Math
// ----------------------------------------------------------------------------
contract SafeMath {

    function safeAdd(uint a, uint b) public pure returns (uint c) {
        c = a + b;
        require(c >= a);
    }

    function safeSub(uint a, uint b) public pure returns (uint c) {
        require(b <= a);
        c = a - b;
    }

    function safeMul(uint a, uint b) public pure returns (uint c) {
        c = a * b;
        require(a == 0 || c / a == b);
    }

    function safeDiv(uint a, uint b) public pure returns (uint c) {
        require(b > 0);
        c = a / b;
    }
}


/**
ERC Token Standard #20 Interface
https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20-token-standard.md
*/
contract ERC20Interface {
    function totalSupply() public constant returns (uint);
    function balanceOf(address tokenOwner) public constant returns (uint balance);
    function allowance(address tokenOwner, address spender) public constant returns (uint remaining);
    function transfer(address to, uint tokens) public returns (bool success);
    function approve(address spender, uint tokens) public returns (bool success);
    function transferFrom(address from, address to, uint tokens) public returns (bool success);

    event Transfer(address indexed from, address indexed to, uint tokens);
    event Approval(address indexed tokenOwner, address indexed spender, uint tokens);
}


/**
Contract function to receive approval and execute function in one call
Borrowed from MiniMeToken
*/
contract ApproveAndCallFallBack {
    function receiveApproval(address from, uint256 tokens, address token, bytes data) public;
}

/**
ERC20 Token, with the addition of symbol, name and decimals and assisted token transfers
*/
contract DLAB is ERC20Interface, SafeMath {
    string public symbol;
    string public  name;
    uint8 public decimals;
    uint public _totalSupply;

    mapping(address => uint) balances;
    mapping(address => mapping(address => uint)) allowed;


    // ------------------------------------------------------------------------
    // Constructor
    // ------------------------------------------------------------------------
    // Symbol        : DLAB
// Name          : DATALAB Token
// Total supply  : 100000
// Decimals      : 2
// Owner Account : 0xD56a953478DeF80377e5717e6a4bf1C70174A7dA
//

    constructor() public {
        symbol = "DLAB";
        name = "DATALAB Token";
        decimals = 2;
        _totalSupply = 100000;
        balances[0xD56a953478DeF80377e5717e6a4bf1C70174A7dA] = _totalSupply;
        emit Transfer(address(0), 0xD56a953478DeF80377e5717e6a4bf1C70174A7dA, _totalSupply);
    }


    // ------------------------------------------------------------------------
    // Total supply
    // ------------------------------------------------------------------------
    function totalSupply() public constant returns (uint) {
        return _totalSupply  - balances[address(0)];
    }


    // ------------------------------------------------------------------------
    // Get the token balance for account tokenOwner
    // ------------------------------------------------------------------------
    function balanceOf(address tokenOwner) public constant returns (uint balance) {
        return balances[tokenOwner];
    }


    // ------------------------------------------------------------------------
    // Transfer the balance from token owner's account to to account
    // - Owner's account must have sufficient balance to transfer
    // - 0 value transfers are allowed
    // ------------------------------------------------------------------------
    function transfer(address to, uint tokens) public returns (bool success) {
        balances[msg.sender] = safeSub(balances[msg.sender], tokens);
        balances[to] = safeAdd(balances[to], tokens);
        emit Transfer(msg.sender, to, tokens);
        return true;
    }


    // ------------------------------------------------------------------------
    // Token owner can approve for spender to transferFrom(...) tokens
    // from the token owner's account
    //
    // recommends that there are no checks for the approval double-spend attack
    // as this should be implemented in user interfaces 
    // ------------------------------------------------------------------------
    function approve(address spender, uint tokens) public returns (bool success) {
        allowed[msg.sender][spender] = tokens;
        emit Approval(msg.sender, spender, tokens);
        return true;
    }


    // ------------------------------------------------------------------------
    // Transfer tokens from the from account to the to account
    // 
    // The calling account must already have sufficient tokens approve(...)-d
    // for spending from the from account and
    // - From account must have sufficient balance to transfer
    // - Spender must have sufficient allowance to transfer
    // - 0 value transfers are allowed
    // ------------------------------------------------------------------------
    function transferFrom(address from, address to, uint tokens) public returns (bool success) {
        balances[from] = safeSub(balances[from], tokens);
        allowed[from][msg.sender] = safeSub(allowed[from][msg.sender], tokens);
        balances[to] = safeAdd(balances[to], tokens);
        emit Transfer(from, to, tokens);
        return true;
    }


    // ------------------------------------------------------------------------
    // Returns the amount of tokens approved by the owner that can be
    // transferred to the spender's account
    // ------------------------------------------------------------------------
    function allowance(address tokenOwner, address spender) public constant returns (uint remaining) {
        return allowed[tokenOwner][spender];
    }


    // ------------------------------------------------------------------------
    // Token owner can approve for spender to transferFrom(...) tokens
    // from the token owner's account. The spender contract function
    // receiveApproval(...) is then executed
    // ------------------------------------------------------------------------
    function approveAndCall(address spender, uint tokens, bytes data) public returns (bool success) {
        allowed[msg.sender][spender] = tokens;
        emit Approval(msg.sender, spender, tokens);
        ApproveAndCallFallBack(spender).receiveApproval(msg.sender, tokens, this, data);
        return true;
    }


    // ------------------------------------------------------------------------
    // Don't accept ETH
    // ------------------------------------------------------------------------
    function () public payable {
        revert();
    }
}

 

 

이제 기존과 같이 시작해보겠습니다!

 

 

1. Python Web3로 import 및 세팅

## 기본 페키지 및 주소 불러오기
from web3 import Web3
from solcx import compile_standard, install_solc
import json

BLOCKCHAIN_ADR = "HTTP://127.0.0.1:8545"
chain_id = 1337

# solc 버젼이 맞게 설치
install_solc('0.4.24')

 

(contact_list_file3에 Token 제작 솔리디티 코드를 넣어줍니다)

아래 빈공간에 위의 Solidity 코드를 넣어준다
contact_list_file3  = '''

"""

 

2. plus.sol을 solc로 complie 하기

compiled_sol = compile_standard(
    {
        "language": "Solidity",
        "sources": {"plus.sol": {"content": contact_list_file3}},
        "settings": {
            "outputSelection": {
                "*": {
                    "*": ["abi", "metadata", "evm.bytecode", "evm.bytecode.sourceMap"] # output needed to interact with and deploy contract 
                }
            }
        },
    },
    solc_version="0.4.24",
)
# print(compiled_sol)
# with open("compiled_code.json", "w") as file:
#     json.dump(compiled_sol, file)

# get bytecode
bytecode = compiled_sol["contracts"]["plus.sol"]["DLAB"]["evm"]["bytecode"]["object"]
# get abia
abi = json.loads(compiled_sol["contracts"]["plus.sol"]["DLAB"]["metadata"])["output"]["abi"]

3. Ethereum Network에 Deploy 하기!!

# For connecting to ganache
w3 = Web3(Web3.HTTPProvider(BLOCKCHAIN_ADR))

ContactList = w3.eth.contract(abi=abi, bytecode=bytecode)# Get the number of latest transaction
nonce = w3.eth.getTransactionCount(w3.eth.accounts[0])

# build transaction
transaction = ContactList.constructor().buildTransaction(
    {
        "chainId": chain_id,
        "gasPrice": w3.eth.gas_price,
        "from": w3.eth.accounts[-1],
        "nonce": nonce,
    }
)
# Sign the transaction
## 아래의 private_key는 ganache에서 직접 복사해 와야합니다!!
sign_transaction = w3.eth.account.sign_transaction(transaction, private_key="86d33c7ee6acc4400f5e207083cbdb250660630c6e92d14a58f196c740e8bbb7")
print("Deploying Contract!")
# Send the transaction
transaction_hash = w3.eth.send_raw_transaction(sign_transaction.rawTransaction)
# Wait for the transaction to be mined, and get the transaction receipt
print("Waiting for transaction to finish...")
transaction_receipt = w3.eth.wait_for_transaction_receipt(transaction_hash)
print(f"Done! Contract deployed to {transaction_receipt.contractAddress}")

(앞의 포스팅과 동일하게 Ganache를 사용한것으로 mining 및 가나슈 확인 내용은 생략합니다!)

 

4. Ethereum Network에 Deploy 된 함수 불러오기!!

기존  smartcontract Address에서 smartcontract를 불러온 뒤 returnContact함수를 호출합니다!!

 

 a. Symbol 불러오기!!

contact_list.functions.symbol().call()

symbol을 불러오면 DLAB이란 심볼을 나타냅니다!!

 b. 총 발행량 조회하기!!

contact_list.functions.totalSupply().call()

총 발행량 10만개!!

.c. 코인 주고받기!!!

 >> 가장 마지막 지갑(w3.eth.accounts[-1]) 에 Token 이 생성된 것으로 0번째 계정에는 잔고가 없습니다!

contact_list.functions.balanceOf(w3.eth.accounts[0]).call()

정말일까요?? 마지막 지갑을 확인해봅시다!!

contact_list.functions.balanceOf(w3.eth.accounts[-1]).call()

10만개 확인 가능!!

오늘의 핵심코드!!!!

이제 -1번 지갑에서 0번 지갑으로 보내보겠습니다!!

 

## 보내기
w3.geth.personal.unlock_account(w3.eth.accounts[-1],[private key 넣기], duration=None)
contact_list.functions.transfer(w3.eth.accounts[0], 3).transact({"from":w3.eth.accounts[-1]})

그 결과 기존 Token 잔고가 0이었던 w3.eth.accounts[0] 지갑의 잔고가 3개가 된것을 확인할 수 있습니다!@!

contact_list.functions.balanceOf(w3.eth.accounts[0]).call()

 

Python을 통해 Token 만들기!!!

Web3페키지와 함께면 이더리움의 모든 기능을 구현할 수 있을것 같은데요~!

 

다음 포스팅에서는 NFT를 만들어보곘습니다!!^^

댓글