├── README.md ├── LICENSE ├── transaction.py ├── deploy.py └── src └── abi.py /README.md: -------------------------------------------------------------------------------- 1 | # Smart-Contract-Example 2 | 3 | Simple python code for deploying & interacting with smart contract. 4 | 5 | This is a term project I finished in [class](https://sites.google.com/site/ntublock2019/home). The contract (provided by TA) can be found [here](https://github.com/yenchihliao/BlockchainIntroduction). 6 | 7 | 8 | ## Dependency 9 | 10 | - web3 11 | - solc (see [this issue](https://github.com/ethereum/py-solc/issues/61) if you have problem installing this) 12 | 13 | 14 | 15 | ## Content 16 | 17 | - [transaction.py](transaction.py) 18 | 19 | Submit student ID / token / hash (first argument) to the contract 20 | 21 | - [deploy.py](deploy.py) 22 | 23 | Deploy a new contract (hello world) to ethereum testnet 24 | 25 | 26 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Alexander H. Liu 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /transaction.py: -------------------------------------------------------------------------------- 1 | # Reference 2 | # - https://hackernoon.com/ethereum-smart-contracts-in-python-a-comprehensive-ish-guide-771b03990988 3 | # - https://web3py.readthedocs.io/en/stable/web3.eth.account.html#sign-a-contract-transaction 4 | import time 5 | import sys 6 | from web3 import Web3, HTTPProvider 7 | from src.abi import abi 8 | 9 | # The url for node connecting to Ropsten testnet (the chain), you may apply it at https://infura.io/ 10 | w3 = Web3(HTTPProvider("https://ropsten.infura.io/v3/ID_FROM_INFURA")) 11 | 12 | # Contract address provided by TA 13 | contract_address = "CONTRACT_ADDRESS" 14 | 15 | # Private key of wallet on testnet 16 | wallet_private_key = "YOUR_PRIVATE_KEY" 17 | 18 | # Address of wallet on testnet 19 | wallet_address = "YOUR_WALLET_ADDRESS" 20 | nonce = w3.eth.getTransactionCount(wallet_address) 21 | 22 | # Value to send, make sure you have enough value in account (must > 3gwei) 23 | value_to_send = 0.1 24 | amount_in_wei = w3.toWei(value_to_send,'ether') 25 | 26 | # Parse contract 27 | contract = w3.eth.contract(contract_address, abi=abi) 28 | 29 | # Prepare transaction 30 | txn_dict = { 31 | 'value': amount_in_wei, 32 | 'gas': 2000000, 33 | 'gasPrice': w3.toWei('40', 'gwei'), 34 | 'nonce': nonce, 35 | 'chainId': 3, 36 | } 37 | 38 | # Prepare contract function 39 | txn_hash = contract.functions.Problem2("hello world",sys.argv[1])\ 40 | .buildTransaction(txn_dict) 41 | 42 | # Sign & send txn 43 | signed_txn = w3.eth.account.signTransaction(txn_hash, wallet_private_key) 44 | txn_hash = w3.eth.sendRawTransaction(signed_txn.rawTransaction) 45 | 46 | # Wait for result 47 | txn_receipt = None 48 | count = 0 49 | while txn_receipt is None and (count < 30): 50 | try: 51 | txn_receipt = w3.eth.getTransactionReceipt(txn_hash) 52 | print(txn_receipt) 53 | except: 54 | print('Waiting for transaction to be approved ... ({})'.format(count),end='\r') 55 | count+=1 56 | time.sleep(5) 57 | if txn_receipt is None: 58 | print('Failed.') 59 | else: 60 | print('Done.') 61 | -------------------------------------------------------------------------------- /deploy.py: -------------------------------------------------------------------------------- 1 | # Reference 2 | # - https://ethereum.stackexchange.com/questions/44614/how-to-connect-to-infura-and-deploy-contract-use-web3-py 3 | # - https://medium.com/@w_n1c01a5/hello-world-on-solidity-ethereum-b6a4de6a4258 4 | import time 5 | import sys 6 | from web3 import Web3, HTTPProvider 7 | from solc import compile_source 8 | 9 | # ABI for parsing contract 10 | contract_source_code = '''pragma solidity ^0.4.22; 11 | 12 | contract helloworld { 13 | function helloWorld () public pure returns (string) { 14 | return 'Hello world'; 15 | } 16 | }''' 17 | compiled_sol = compile_source(contract_source_code) 18 | contract_interface = compiled_sol[':helloworld'] 19 | 20 | # The url for node connecting to Ropsten testnet (the chain), you may apply it at https://infura.io/ 21 | w3 = Web3(HTTPProvider("https://ropsten.infura.io/v3/ID_FROM_INFURA")) 22 | 23 | # Private key of wallet on testnet 24 | wallet_private_key = "YOUR_WALLET_PRIVATE_KEY" 25 | 26 | # Address of wallet on testnet 27 | wallet_address = "YOUR_WALLET_ADDRESS" 28 | nonce = w3.eth.getTransactionCount(wallet_address) 29 | 30 | # New contract 31 | contract_ = w3.eth.contract( 32 | abi=contract_interface['abi'], 33 | bytecode=contract_interface['bin']) 34 | 35 | # Prepare your account 36 | acct = w3.eth.account.privateKeyToAccount(wallet_private_key) 37 | 38 | # Prepare transaction (i.e. deploying the contract) 39 | construct_txn = contract_.constructor().buildTransaction({ 40 | 'from': acct.address, 41 | 'nonce': nonce, 42 | 'gas': 2000000, 43 | 'gasPrice': w3.toWei('40', 'gwei'), 44 | 'chainId': 3 45 | }) 46 | 47 | # Sign & send the contract 48 | signed = acct.signTransaction(construct_txn) 49 | txn_hash = w3.eth.sendRawTransaction(signed.rawTransaction) 50 | 51 | # Wait for result 52 | txn_receipt = None 53 | count = 0 54 | while txn_receipt is None and (count < 30): 55 | try: 56 | txn_receipt = w3.eth.getTransactionReceipt(txn_hash) 57 | print(txn_receipt) 58 | except: 59 | print('Waiting for transaction to be approved ... ({})'.format(count),end='\r') 60 | count+=1 61 | time.sleep(5) 62 | if txn_receipt is None: 63 | print('Failed.') 64 | else: 65 | print('Done.') 66 | -------------------------------------------------------------------------------- /src/abi.py: -------------------------------------------------------------------------------- 1 | # This is the ABI of contract @ https://github.com/yenchihliao/BlockchainIntroduction/blob/master/PJ.sol 2 | abi = '''[ 3 | { 4 | "constant": false, 5 | "inputs": [ 6 | { 7 | "internalType": "string", 8 | "name": "ID", 9 | "type": "string" 10 | }, 11 | { 12 | "internalType": "uint8", 13 | "name": "key", 14 | "type": "uint8" 15 | } 16 | ], 17 | "name": "Bonus", 18 | "outputs": [], 19 | "payable": false, 20 | "stateMutability": "nonpayable", 21 | "type": "function" 22 | }, 23 | { 24 | "constant": false, 25 | "inputs": [ 26 | { 27 | "internalType": "string", 28 | "name": "ID", 29 | "type": "string" 30 | } 31 | ], 32 | "name": "Problem1", 33 | "outputs": [], 34 | "payable": true, 35 | "stateMutability": "payable", 36 | "type": "function" 37 | }, 38 | { 39 | "constant": false, 40 | "inputs": [ 41 | { 42 | "internalType": "string", 43 | "name": "ID", 44 | "type": "string" 45 | }, 46 | { 47 | "internalType": "string", 48 | "name": "HashedHex", 49 | "type": "string" 50 | } 51 | ], 52 | "name": "Problem2", 53 | "outputs": [], 54 | "payable": true, 55 | "stateMutability": "payable", 56 | "type": "function" 57 | }, 58 | { 59 | "constant": false, 60 | "inputs": [ 61 | { 62 | "internalType": "string", 63 | "name": "ID", 64 | "type": "string" 65 | }, 66 | { 67 | "internalType": "string", 68 | "name": "HashedHex", 69 | "type": "string" 70 | }, 71 | { 72 | "internalType": "address", 73 | "name": "yourContract", 74 | "type": "address" 75 | } 76 | ], 77 | "name": "Problem3", 78 | "outputs": [], 79 | "payable": false, 80 | "stateMutability": "nonpayable", 81 | "type": "function" 82 | }, 83 | { 84 | "constant": true, 85 | "inputs": [ 86 | { 87 | "internalType": "string", 88 | "name": "", 89 | "type": "string" 90 | } 91 | ], 92 | "name": "ID2address", 93 | "outputs": [ 94 | { 95 | "internalType": "address", 96 | "name": "", 97 | "type": "address" 98 | } 99 | ], 100 | "payable": false, 101 | "stateMutability": "view", 102 | "type": "function" 103 | }, 104 | { 105 | "constant": true, 106 | "inputs": [ 107 | { 108 | "internalType": "string", 109 | "name": "", 110 | "type": "string" 111 | } 112 | ], 113 | "name": "ID2P2Hex", 114 | "outputs": [ 115 | { 116 | "internalType": "string", 117 | "name": "", 118 | "type": "string" 119 | } 120 | ], 121 | "payable": false, 122 | "stateMutability": "view", 123 | "type": "function" 124 | }, 125 | { 126 | "constant": true, 127 | "inputs": [ 128 | { 129 | "internalType": "string", 130 | "name": "", 131 | "type": "string" 132 | } 133 | ], 134 | "name": "ID2P3Hex", 135 | "outputs": [ 136 | { 137 | "internalType": "string", 138 | "name": "", 139 | "type": "string" 140 | } 141 | ], 142 | "payable": false, 143 | "stateMutability": "view", 144 | "type": "function" 145 | }, 146 | { 147 | "constant": true, 148 | "inputs": [ 149 | { 150 | "internalType": "string", 151 | "name": "", 152 | "type": "string" 153 | } 154 | ], 155 | "name": "isBonusSubmit", 156 | "outputs": [ 157 | { 158 | "internalType": "bool", 159 | "name": "", 160 | "type": "bool" 161 | } 162 | ], 163 | "payable": false, 164 | "stateMutability": "view", 165 | "type": "function" 166 | }, 167 | { 168 | "constant": true, 169 | "inputs": [ 170 | { 171 | "internalType": "string", 172 | "name": "", 173 | "type": "string" 174 | } 175 | ], 176 | "name": "isP1Submit", 177 | "outputs": [ 178 | { 179 | "internalType": "bool", 180 | "name": "", 181 | "type": "bool" 182 | } 183 | ], 184 | "payable": false, 185 | "stateMutability": "view", 186 | "type": "function" 187 | }, 188 | { 189 | "constant": true, 190 | "inputs": [ 191 | { 192 | "internalType": "string", 193 | "name": "", 194 | "type": "string" 195 | } 196 | ], 197 | "name": "isP2Submit", 198 | "outputs": [ 199 | { 200 | "internalType": "bool", 201 | "name": "", 202 | "type": "bool" 203 | } 204 | ], 205 | "payable": false, 206 | "stateMutability": "view", 207 | "type": "function" 208 | }, 209 | { 210 | "constant": true, 211 | "inputs": [ 212 | { 213 | "internalType": "string", 214 | "name": "", 215 | "type": "string" 216 | } 217 | ], 218 | "name": "isP3Submit", 219 | "outputs": [ 220 | { 221 | "internalType": "bool", 222 | "name": "", 223 | "type": "bool" 224 | } 225 | ], 226 | "payable": false, 227 | "stateMutability": "view", 228 | "type": "function" 229 | }, 230 | { 231 | "constant": true, 232 | "inputs": [ 233 | { 234 | "internalType": "string", 235 | "name": "", 236 | "type": "string" 237 | } 238 | ], 239 | "name": "score", 240 | "outputs": [ 241 | { 242 | "internalType": "int256", 243 | "name": "", 244 | "type": "int256" 245 | } 246 | ], 247 | "payable": false, 248 | "stateMutability": "view", 249 | "type": "function" 250 | } 251 | ] 252 | ''' --------------------------------------------------------------------------------