ICON DAPP From A-Z Part 2: SCORE

2infiniti
2infiniti
Oct 30, 2018 · 19 min read

What is a Smart Contract

ICON Smart Contract — SCORE Overview

State Transitions

A SCORE’s state transitions are logged in the State DB

Data Formats

SCORE Coding

from iconservice import *

TAG = 'FirstScore'

class FirstScore(IconScoreBase):

def __init__(self, db: IconScoreDatabase) -> None:
super().__init__(db)

def on_install(self) -> None:
super().on_install()

def on_update(self) -> None:
super().on_update()

@external(readonly=True)
def hello(self) -> str:
Logger.debug(f'Hello, world!', TAG)
return "Hello"
# def receive_funds is removed because it was our custom method not part of the generated template.

Exercise - Enhance Hello World SCORE

# my_first_score/my_first_score.py...@external(readonly=True)
def name(self) -> str:
return "HelloWorld"

@external(readonly=True)
def hello(self) -> str:
return f'Hello, {self.msg.sender}. My name is {self.name()}'
# Update the SCORE in our localhost node
$ tbears deploy my_first_score -m update -o cx4f1ac3681a51dbbca949c5b411e6a6dadcfd6d2b
$ tbears txresult 0x68a09e20431961ac1be74efc1f546a70730d2e84b5d729ec425f62698498d634
$ tbears call call.json
response : {
"jsonrpc": "2.0",
"result": "Hello, hxe7af5fcfd8dfc67530a01a0e403882687528dfcb. My name is HelloWorld",
"id": 1
}
$ cd ..
$ mkdir SCORE_testnet && cd SCORE_testnet
$ tbears init my_testnet_score TestnetScore
# Enter interactive mode
$ python
# Create a wallet
>>> from iconsdk.wallet.wallet import KeyWallet
>>> wallet = KeyWallet.create()
# Check the wallet address
>>> wallet.get_address()
# Now let's create a keystore
>>> wallet.store('./iconkeystore3', '@icon333')
>>> exit()
# Check localhost first
$ tbears balance -u http://127.0.0.1:9000/api/v3 hx5638ee91e18574a1f0a29b4813578389f0e142a7
# balance in decimal: 0 # Then check testnet
$ tbears balance -u https://bicon.net.solidwallet.io/api/v3 hx5638ee91e18574a1f0a29b4813578389f0e142a7
# balance in decimal: 20000000000000000000
# tbears_cli_config.json# Change uri from"uri": "http://127.0.0.1:9000/api/v3",# to "uri": "https://bicon.net.solidwallet.io/api/v3",
# tbears_cli_config.json# Change deploy stepLimit to 
"stepLimit": "0x59682f00",
# Change transfer stepLimit to
"stepLimit": "0x200000"
# save the file
# Deploy contract to testnet$ tbears deploy my_testnet_score -k iconkeystore3
$ tbears txresult 0x3e23091f9f17afafdd241f7730bbbb64543dab75732b574865ee6a06a0ff61d3
# call.json# Change# iconkeyscore 3 address"from": "hx5638ee91e18574a1f0a29b4813578389f0e142a7"# to the scoreAddress you just copied, ie. SCORE address on testnet "to": "cxfe1cf1c1230cd32b0a95a54ce87e5b9009044343",
$ tbears call call.json
response : {
"jsonrpc": "2.0",
"result": "Hello, hx5638ee91e18574a1f0a29b4813578389f0e142a7. My name is HelloWorld",
"id": 1
}

Exercise — Creating a Sample Token (IRC-2) & Sample Crowdsale

# Make a new directory, we'll work from a fresh SCORE template
$ cd ..
$ mkdir SCORE_token && cd SCORE_token
# Initialize a new SCORE project
$ tbears init my_sample_token MySampleToken
# my_sample_token/my_sample_token.pyfrom iconservice import *

TAG = 'MySampleToken'
class TokenStandard(ABC):
@abstractmethod
def name(self) -> str:
pass

@abstractmethod
def symbol(self) -> str:
pass

@abstractmethod
def decimals(self) -> int:
pass

@abstractmethod
def totalSupply(self) -> int:
pass

@abstractmethod
def balanceOf(self, _owner: Address) -> int:
pass

@abstractmethod
def transfer(self, _to: Address, _value: int, _data: bytes=None):
pass
class MySampleToken(IconScoreBase, TokenStandard):    # Declare some class variables that we'll need
_BALANCES = 'balances'
_TOTAL_SUPPLY = 'total_supply'
_DECIMALS = 'decimals'

def __init__
(self, db: IconScoreDatabase) -> None:
super().__init__(db)
# Remember we have 3 types of wrappers for State DB
# VarDB is for simple key value, DictDB for
# dictionaries and ArrayDB when you need to maintain order
self._total_supply = VarDB(self._TOTAL_SUPPLY, db, value_type=int)
self._decimals = VarDB(self._DECIMALS, db, value_type=int)
self._balances = DictDB(self._BALANCES, db, value_type=int)

def on_install(self, initialSupply: int, decimals: int) -> None:
super().on_install()
# Token's total supply is using {decimals} places, so
# actual supply will need to be multiplied by 10^decimals
total_supply = initialSupply * 10 ** decimals
Logger.debug(f'on_install: total_supply={total_supply}', TAG)
# Set the total supply and decimals of this contract
self._total_supply.set(total_supply)
self._decimals.set(decimals)
self._balances[self.msg.sender] = total_supply

def on_update(self) -> None:
super().on_update()
# my_sample_token/my_sample_token.pyclass MySampleToken(IconScoreBase, TokenStandard):
...
@external(readonly=True)
def name(self) -> str:
return "MySampleToken"
# Symbol is the ticker symbol you normally see on exchanges
@external(readonly=True)
def symbol(self) -> str:
return "MST"

@external(readonly=True)
def decimals(self) -> int:
return self._decimals.get()

@external(readonly=True)
def totalSupply(self) -> int:
return self._total_supply.get()

@external(readonly=True)
def balanceOf(self, _owner: Address) -> int:
return self._balances[_owner]

@external
def transfer(self, _to: Address, _value: int, _data: bytes=None):
if _data is None:
_data = b'None'
self._transfer(self.msg.sender, _to, _value, _data)
def _transfer(self, _from: Address, _to: Address, _value: int, _data: bytes): # check to see if the sender has enough balance
if self._balances[_from] < _value:
revert("Out of balance")

# substract the sending value from sender balance
self._balances[_from] = self._balances[_from] - _value
# add balance to recipient address
self._balances[_to] = self._balances[_to] + _value

# if recipient is a contract, we'll handle differently
# with tokenFallback method, this method will be defined
# in our actual crowdsale contract.
if _to
.is_contract:
recipient_score = self.create_interface_score(_to, TokenFallbackInterface)
recipient_score.tokenFallback(_from, _value, _data)
self.Transfer(_from, _to, _value, _data)
Logger.debug(f'Transfer({_from}, {_to}, {_value}, {_data})', TAG)
# Log the transfer data event
@eventlog(indexed=3)
def Transfer(self, _from: Address, _to: Address, _value: int, _data: bytes):
pass
my_sample_token/my_sample_token.py
# tbears_cli_config.json
{
"uri": "http://127.0.0.1:9000/api/v3",
"nid": "0x3",
"keyStore": null,
"from": "hxe7af5fcfd8dfc67530a01a0e403882687528dfcb",
"to": "cx0000000000000000000000000000000000000000",
"stepLimit": "0x3000000",
"deploy": {
"contentType": "zip",
"mode": "install",
"scoreParams": {
"initialSupply": "0x2fb60ce0",
"decimals": "0x12"
}
}
}
$ tbears deploy my_sample_token -k keystore_test1
$ tbears txresult 0x16d7aaa8b69f5d73e7afdfe312b3f3f96bb3b178f6aa15d1f2bf7d77485a823d
gettotalsupply.json{
"jsonrpc": "2.0",
"method": "icx_call",
"params": {
"from": "hxe7af5fcfd8dfc67530a01a0e403882687528dfcb",
"to": "cx4bb1165dc33a6b7e70d1d5f46d33286095c5a56b",
"dataType": "call",
"data": {
"method": "totalSupply"
}
},
"id": 1
}
$ tbears call gettotalsupply.json
response : {
"jsonrpc": "2.0",
"result": "0x2961fff8ca4a62327800000",
"id": 1
}
populate balanceOf with the keystore address you used to deploy the contract
# Create a new SCORE template
$ cd ..
$ mkdir SCORE_crowdsale && cd SCORE_crowdsale
$ tbears init my_sample_crowdsale MySampleCrowdsale
my_sample_crowdsale/my_sample_crowdsale.py
# tbears_cli_config_testnet.json
{
"uri": "https://bicon.net.solidwallet.io/api/v3",
"nid": "0x3",
"keyStore": null,
"from": "hx5638ee91e18574a1f0a29b4813578389f0e142a7",
"to": "cx0000000000000000000000000000000000000000",
"deploy": {
"stepLimit": "0x77359400",
"contentType": "zip",
"mode": "install",
"scoreParams": {
"fundingGoalInIcx": "0x2fb60ce0",
"tokenScore": "cx3d929fd4a9c32ccba8834a72503d27197627517e",
"durationInBlocks": "0x1f4"
}
},
"txresult": {},
"transfer": {
"stepLimit": "0x300000"
}
}
$ tbears deploy my_sample_crowdsale -k iconkeystore3 -c tbears_cli_config_testnet.json


Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade