Exercise - Enhance Hello World SCORE

In the last tutorial we made a JSON-RPC call to the hello function, let’s make some modifications. This time we’ll give the contract a name and return something more interesting.

# 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()}'

We declared a new method name , it is a public method (@external) and it is read only. This returns the SCORE name of your choice.

We also modified the hello function to greet you back. msg is a built-in property that holds two information, msg.sender and msg.value . Sender is the wallet executing this transaction and value is the amount of ICX the sender attempts to transfer.

We also declared the function with return type hint of str, this is good practice and will be generated in the API spec, so we will know the function returns a string instead of being just a function name.

Your file should look like this, save it.

We learned in the last tutorial, all SCORE updates need to be deployed again with ‘-m update’ parameter.

# Update the SCORE in our localhost node

$ tbears deploy my_first_score -m update -o cx4f1ac3681a51dbbca949c5b411e6a6dadcfd6d2b

As usual, always check txresult to see if the transaction is successfully performed.

$ tbears txresult 0x68a09e20431961ac1be74efc1f546a70730d2e84b5d729ec425f62698498d634

Let’s try the same call.json, and see what we get

$ tbears call call.json

And we should get a nice greeting back from the contract.

response : {

"jsonrpc": "2.0",

"result": "Hello, hxe7af5fcfd8dfc67530a01a0e403882687528dfcb. My name is HelloWorld",

"id": 1

}

Deploying a Contract to Testnet

So far we have been working on the localhost node, we’ll eventually need to deploy the contract to the real ICON network for everyone to use.

In order to deploy our contract to the testnet, unlike local transactions, we’ll need valid signatures to make requests. This is done via providing our keystore to sign the transactions.

To make things more clear, let’s initiate a brand new SCORE template.

$ cd ..

$ mkdir SCORE_testnet && cd SCORE_testnet

$ tbears init my_testnet_score TestnetScore

Let’s make a new wallet for testnet use, instructions are the same from Part 1, you can either use the SDK or ICONex to create the wallet, we’ll use the SDK here.

# 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()

It is important to note that your ICX balances will reside on different networks, depending on where the transactions came from, so always make sure which node you’re on, ie. localhost or testnet.

The wallet is newly created so it has 0 balance across all nodes, let’s populate it with some ICX on ICX Testnet Faucet with the wallet address we just printed.

Now check the balance, this time we specify the network API.

# 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

The faucet app made the transfer on the bicon.net testnet, so naturally your balances are reflected there. Now our data is on testnet, so we’re able to check transactions through the live tracker: https://bicon.tracker.solidwallet.io/

Now let’s reconfigure our default config file and point our URI to the testnet, so we don’t need to specify the network API on every call.

# 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",

Our default stepLimit was also too low for testnet deployment, we’ll need a slightly higher stepLimit. You can use this tool to play with the settings you need: Unit Converter

Before we make any changes, let’s take a closer look at step calculation, I found this fairly confusing, particularly having to convert between all the units from super long decimals and hex, setting stepLimit was a bit of a guess work. We briefly covered step calculation in part 1 on a more theoretical level, with a formula. Let’s look into it and understand clearly how to properly configure stepLimit.

Step Calculation

Step = max ( [ ( ∑ βiSi+ C ), C ] )

This formula says, number of steps required has two scenarios,

Scenario 1: the transaction involves some SCORE operations, each operation has a different weight and cost. The formula ∑ βiSi+ C will sum up all the cost of these operations + a minimum fee of 100,000 steps.

Scenario 2: the transaction is a pure ICX transfer with no SCORE operations, so there’s only a minimum fee of 100,000 steps.

The formula will pick the higher value of the two scenarios as your step cost.

Let's further examine this with numbers,

In the first example, we assumed our SCORE is 512 Bytes, the size of Contract Set (size of generated/updated smart contract code in Bytes) is 512. We’d multiply by its weight of 30000 as our first SCORE operation cost. Second part is Contract Create (number of times to call the smart contract code generation function), in this case it’s one time only, so the cost is 1 times its weight of 1,000,000,000. Then we add the minimum transaction step cost which is 100,000. This operation falls in scenario 1 and will be picked by the system as the transaction cost.

In the second example, similar calculations were applied. In this example, we only made a contract call (a SCORE invoking an external function in another SCORE) which has a weight of 25,000.

The SCORE operations involved in transaction fee calculation can be found in the ICON Transaction Fee Yellowpaper

Now we have a fairly good idea how much each transaction might cost, we can set the stepLimits accordingly. For a contract deployment it’ll cost ~1 billion steps, let’s give it a bit of room with 1.5 billion steps as stepLimit. From our Unit Coverter, this is 0x59682f00 in hex value.

*Note the stepLimit in ‘deploy’ is using unit ‘step’ not ‘loop’, and it takes a hex value, so from the converter you should use Step to Hex.

We also know that ICX transfers right now cost 100,000 steps, let’s also give it a bit of a buffer with 200,000 stepLimit. This is 0x30d40 in hex value.

Open the default config file again,

# tbears_cli_config.json # Change deploy stepLimit to

"stepLimit": "0x59682f00", # Change transfer stepLimit to

"stepLimit": "0x200000" # save the file

Units Revisited, in Words

I’m not sure if it’s just me or if people get confused by big numbers, let’s translate these numbers into words see if that helps,

1 quintillion (1 with 18 zeroes) loops = 1 ICX

100 million steps = 1 ICX

1 step = 10 billion loops

Ok that didn’t really help, whatevers let’s move on.

Now we the configurations ready, with URL pointing to testnet and estimated stepLimits, we’re ready to deploy.

# Deploy contract to testnet $ tbears deploy my_testnet_score -k iconkeystore3

Check transaction to see if everything went smoothly,

$ tbears txresult 0x3e23091f9f17afafdd241f7730bbbb64543dab75732b574865ee6a06a0ff61d3

Let’s also check the transaction result on the live tracker this time, https://bicon.tracker.solidwallet.io/transaction/0x3e23091f9f17afafdd241f7730bbbb64543dab75732b574865ee6a06a0ff61d3

Step Limit is as we set it, 1.5 billion steps and actual steps consumed by the transaction was around 1 billion, also very close to our estimate.

Actual TxFee is simply Step used by Txt * Step Price which is ~10 ICX. Step Price here is the minimum 100,000 steps which is 0.00000001 ICX. When running on mainnet, ICX is the real ICX exchange rate, to get the transaction cost in USD value ($6.778 USD in this example).

You can click ‘Contract Created’ to see our the SCORE address, copy this down, this is where our SCORE lives on testnet. Let’s try to invoke the hello function on testnet, create a new call.json file.

# call.json # Change # iconkeyscore 3 address "from": "hx5638ee91e18574a1f0a29b4813578389f0e142a7" # to the scoreAddress you just copied, ie. SCORE address on testnet "to": "cxfe1cf1c1230cd32b0a95a54ce87e5b9009044343",

Execute it,

$ tbears call call.json

The keystore address we use doesn’t really matter in this case, as this is a read-only operation that doesn’t require a keystore file. But for other operations that involve ICX transactions, we will need to use an actual keystore to sign the transaction.

We should get a nice greeting back from the testnet node!

response : {

"jsonrpc": "2.0",

"result": "Hello, hx5638ee91e18574a1f0a29b4813578389f0e142a7. My name is HelloWorld",

"id": 1

}

At this point you should be comfortable with SCORE development. We have discussed SCORE in principle, basic syntax and its limitations. We have tried to deploy our SCORE to different networks, looked into step limits, step calculation and other configurations. We are now ready to build a more complicated SCORE project.

Before our next exercise, let’s spend a few minutes to learn about token standards, because we’ll be building our own token, we should understand what they are first.

ERC-20 should be a familiar term even for non-devs, you should all have at one point invested in an ICO with ETH and received custom ERC-20 tokens in return. This includes ICON presale, which was conducted on the Ethereum network, with ERC-20 ICX tokens.

What is ERC-20 exactly?

ERC-20 is a technical standard for smart contracts, there are only 6 functions in the standard. totalSupply for total token supply. balanceOf to get account balance of another account. transfer to transfer tokens directly with a value. transferFrom to transfer from an address to another. approve to allow balance withdrawl from your account. allowance that checks how much left can still be withdrawn.

This simple specification defines pretty much all your needs in a custom token, it has a finite supply, facilitates token transfers and keeps balance information.

Shortcomings of ERC-20

ERC-20 works pretty well, for the most part. It was (and still is?) the de facto token specification for almost all token sales, but what are the shortcomings?

There are minor shortcomings such as not being able to handle transactions through a receiver contract, taking 2 step process for transfers when only 1 is needed etc. These are however minor problems compared to the biggest shortcoming that can cause catastrophic economic losses — sending tokens to a contract that does not allow tokens to be withdrawn or reverting the action.

What is the fix? ERC-223.

ERC-223 addresses this specific problem by enforcing the transfer function to throw an error on invalid transfers and cancel the transaction so no funds are lost. The transfer function in ERC-223 also checks to see whether the receiving address is a contract, if it is, it will assume there’s a tokenFallback method to call it back. We’ll learn more about this soon in our crowdsale exercise.

Why did we just explain ERC-20 and ERC-223? Because ICON has its own token standard, the ICON Token Standard IRC-2. The standard adopts ERC-223 standard, along with its fallback mechanism. This is likely going to be the token standard for all upcoming ICON ICOs that plan to raise funds in ICX, in return you’d get custom IRC-2 tokens at a predefined ratio, pretty much like ETH<->ERC-20 token. The custom IRC-2 tokens can then be re-used by third parties or exchanges, just like how all other tokens work.

There’s also another token standard, ICON IRC-3 NFTs, which is based on ERC-721, you can read more here: ICON Non-Fungible Token Standard

Now let’s build our custom IRC-2 token.