Skip to main content

2 posts tagged with "ethereum"

View All Tags

Analysing Ethereum contract gas costs during development

· 5 min read
Dominik Harz
Co-Founder BOB

Updating the state of a smart contract in Ethereum costs money. In this post I will go a bit into detail why this is necessary in Ethereum and how to check easily (with truffle test) your gas costs during local development. For this we will use a little pet example, you can use for your own deployments.

Gas cost - why?

Ethereum smart contracts are deployed at an address in the Ethereum network. They are identifiable by the fact that the code field in the address is occupied by the EVM bytecode. One example is the Maker Dai contract. Any contract in Ethereum is deployed that way and implements a range of functions that can update its own state and the state of the blockchain.

The own state is defined within the contract and refers to any contract wide variables you define like uint, mapping, or string. The global state of the network is affected when you make a transaction to a contract, which, if everything goes right, will include one more transaction in the ever-growing list of transactions.

Everytime somebody invokes a state update (i.e. sends a transaction), the network reaches consensus over this operation. If 50% + 1 decide that the state transition is valid, the network state gets updated. However, this means that if you have a function that does 5 + 4 and stores the result to a contract variable, every node in the network need to verify that the result is in fact 9. Assuming that Ethereum has more than 12,000 nodes, this is quite costly.

Moreover, assume that any of these operations are free. One could simply create a function that runs indefinitely to block all nodes verifying the network. This is the equivalent to a distributed denial-of-service attack. It would be nice if we would know in advance if a function terminates. However, the Halting problem prevents us from knowing this. Hence, we have to charge partial function execution and forcefully terminate execution when it becomes to burdensome for the network.

Practical implications

Operations executed by the EVM cost gas depending how "heavy" they are on the network. Gas is a way of determining the cost for an operation. The current gas price on the other hand determines how much 1 gas costs in Ether. That way, the gas cost can be adjusted depending on the network load. For example, while the gas cost for a single operation is constant, the gas price can go up when the network is congested. An overview of the gas costs are found in appendix G of the yellow paper.

Local execution and global execution

Any operation on a smart contract costs gas. It is important to note a difference though. Calls are only executed locally. So for example the function below measures its gas, but the you will not be charged executing such a function as it is a read-only function.

function balanceOf(address owner) public view returns (uint256) {
return _balances[owner];
}

However, transactions will deduct Ether from your balance as those are executed by the global network and miners need to have an incentive to include your transaction in a block. So the function below is from an ERC20 and executes a transfer. This updates the contract state, and thus requires a transaction.

function _transfer(address from, address to, uint256 value) internal {
require(value <= _balances[from]);
require(to != address(0));

_balances[from] = _balances[from].sub(value);
_balances[to] = _balances[to].add(value);
emit Transfer(from, to, value);
}

Measuring gas costs

I will assume here that you are familiar with Truffle and Ganache. When you develop your new great smart contracts, I like to create end-to-end tests that also check for gas costs.

Assuming your project folder looks something like this:

contracts/
migrations/
test/

I am creating a folder mkdir experiments where I will store the number of transactions each of my interactions require and the amount of gas that this operation is using.

Next, I'm creating a test script demo.js in the test folder. I like to store my experiments in a CSV format so I can import it to other tools. First, the required imports:

var fs = require("fs");
var csvWriter = require('csv-write-stream');
var writer = csvWriter();

// your contract
const ERC20 = artifacts.require("./ERC20.sol");

You will write your tests based on the Truffle/Mocha/Chai interface. For this, I'm creating the variables I am using to store the measurements. Also, I am using the before construct to setup the CSV writer. During tests I would update the gas and number of transaction related counters.

After the tests are finished, the data is written to a CSV file. To USD conversion uses a helper function.

contract('ERC20', async (accounts) => {
// gas price conversion
const gas_limit = 7988288;

// experiment related vars
var transfer_success_gas = 0;
var transfer_fail_gas = 0;
var transfer_success_tx = 0;
var transfer_fail_tx = 0;

// any other const or vars
// ...

before('Create writer for experiments', async () => {
writer.pipe(fs.createWriteStream(('./experiments/ERC20.csv')));
})

after('Write experiment data to file', async () => {
let transfer_success_usd = convertToUsd(transfer_success_gas);
let transfer_fail_usd = convertToUsd(transfer_fail_gas);

writer.write(
{
Transfer: transfer_success_gas,
TransferFail: transfer_fail_gas,
});
writer.write(
{
Transfer: transfer_success_usd,
TransferFail: transfer_fail_usd,
});
writer.write(
{
Transfer: transfer_success_tx,
TransferFail: transfer_fail_tx,
});
writer.end();
})

// your tests
// it("Transfer Success")
// it("Transfer Fail")
})

The USD conversion function works like this. I have this in a separate file called helpers.js that I export/import as a module.

convertToUsd: function (gasCost) {
// gas price conversion
const gas_price = web3.toWei(5, "gwei");
const eth_usd = 200; // USD

return gasCost * web3.fromWei(gas_price, "ether") * eth_usd;
}

That's it. You can run the experiment with truffle test path/to/the/file.js.

Integrating Python and Ethereum

· 6 min read
Dominik Harz
Co-Founder BOB

In Ethereum and other blockchains there are still a lot of proof of concept implementation and developers trying out how to cope with the new concepts. As part of the dInvest post series I was also looking into Ethereum and trying to implement a hedge fund in a blockchain. In a previous post I discussed how to get a quantitative framework in python up and running. In this post I will write how to integrate python programs with Ethereum smart contracts. For one reason or another you might be also faced with the issue, that although Ethereum offers a Turing-complete language not everything is actually doable there.

Let's say you have created one of the simple tutorial contracts in Ethereum and now want to look at something more advanced. I personally liked the Hitchhiker's Guide to Smart Contracts by Manuel Aráoz to get started with more complex code, setup testrpc, and truffle. Take a look at it.

dInvest smart contract

dInvest is composed of one smart contract that is responsible for making investments, verifying investment criteria and distribution of returns. The contract exposes public functions to create new investments and for withdrawal which will act as main functions of a hedge fund. Users of the hedge fund are identified by their Ethereum address which is equivalent for the public key. Suggestion of investment strategies and strategy execution are done in different agents that also have Ethereum addresses. These agents are set by the contract creator only. When a user is creating an investment it is possible to specify a list of industry sectors identified by a two digit number based on the Standard Industrial Classification codes. These sectors will be identified as a black list when making the investments. Therefore user have the ability control the sectors which the hedge fund will invest on.

The contract can be found in the GitHub repo.

Interaction with smart contracts

To interact with smart contracts, there are a couple of option including RPC or a JavaScript API. I found the easiest way to interact with Ethereum smart contracts from other programs (like python programs) was using their web3 JavaScript API. As the majority of dInvest is written in python, I wanted to stick to the language and not include JS as well. Luckily, there is a web3 implementation in python. To get it up and running for the dInvest setting I switched to the virtualenv, where I also installed zipline and then install web3 simply with pip install web3.

Using web3, there are three steps to get you up and running to interact with your smart contract:

  1. Getting your ABI
  2. Setup the RPC connection
  3. Interact with the smart contract

In the next sections, I will go into detail how to achieve the three steps. I am using this mostly as a python module for other programs. In the end our python module structure might look like this:

contract
|-- __init__.py
|-- ContractHandler.py
|-- your-contract-name.json

Getting your ABI

Now, to interact with any smart contract you need the Application Binary Interface(ABI) defined by the contract. The ABI is a static, strongly typed interface. Whenever you create a new contract or change an existing one, chances are your ABI changes as well. In my experience the easiest way to get the current ABI of a smart contract (which might be yours or any contract you have the source code available) is to go to https://ethereum.github.io/browser-solidity/ and copy/paste your code there. Then press the "Compile" button on the upper right side and copy the entire string in the "Interface" field into a your-contract-name.json file. Once you have that JSON, your web3 interface will know how to interact with the contract.

Setting up the RPC provider

As a next step you will need to connect to the RPC provider. In your python file (e.g. ContractHandler.py) include those lines of code:

from web3 import Web3, TestRPCProvider

class ContractHandler:
def __init__(self):
self.web3 = Web3(RPCProvider(host='localhost', port='8545'))
with open(str(path.join(dir_path, 'contract_abi.json')), 'r') as abi_definition:
self.abi = json.load(abi_definition)
self.contract_address = your_contract_address
self.contract = self.web3.eth.contract(self.abi, self.contract_address)

I prefer having my configurations in a separate file. There are many ways to do it and it seems like there is no standard in python. I guess using a txt file is not the best option though and I plan to switch to yml soon. See also here https://martin-thoma.com/configuration-files-in-python/. Make sure to run your favorite Ethereum client before starting your program (e.g. geth --rpc).

Interacting with the smart contract

Note: Before interacting with your own account you need to unlock it first. This is achieved in web3 via:

self.web3.personal.unlockAccount(your_ethereum_account, your_ethereum_password)

There are some standard web3 calls you can make, like getting the current balance of an account in wei:

wei_balance = self.web3.eth.getBalance(some_ethereum_address)

In case you want to call a function in the contract you can do this by calling the command as defined by the contract ABI. In our dInvest example there is a contract call which returns the blacklisted companies for our sustainable investment. It is callable with:

blacklist = self.contract.call().blackListCompanies()

There are some more examples in the GitHub code available.

Final note

As a final note, I would like to point out that there are other blockchain solutions like Hyperledger Fabric or Tendermint that aim to solve issues around compatibility with other programming language, transaction throughput etc. As they are permissioned blockchains I haven't yet given them a try, but might be interesting to take a look at.