Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

A0-1766: Sketch out the pricing script #852

Merged
merged 7 commits into from
Jan 13, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions scripts/pricing/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
Pricing script
==============

The `./run.py` script in this directory will deploy some contracts and print a summary of how much some basic operations
on them cost.

It requires `python3` and an Ink 4-compatible version of `cargo contract`, to install:

```bash
$ cargo install cargo-contract --version 2.0.0-beta.1
```

Afterwards, install the python deps and run the script:

```bash
$ pip install -r requirements.txt
$ ./run.py
```

For more info on options see:

```bash
$ ./run.py --help
```
1 change: 1 addition & 0 deletions scripts/pricing/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
tabulate==0.9.0
96 changes: 96 additions & 0 deletions scripts/pricing/run.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
#!/usr/bin/python3

import argparse
import random
import subprocess
import json
from subprocess import PIPE
from tabulate import tabulate
import urllib.request

AZERO = 1_000_000_000_000


parser = argparse.ArgumentParser(
description='Check the prices of some common contract operations')
parser.add_argument('--url', type=str,
default='ws://localhost:9944', help='URL of the node to connect to')
parser.add_argument('--suri', type=str, default='//Alice',
help='Secret key URI to use for calls')
parser.add_argument('--adder-dir', type=str,
help='Directory of the adder contract', default='../../contracts/adder')

args = parser.parse_args()

COMMON_ARGS = ['--suri', args.suri, '--url',
args.url, '--skip-confirm', '--output-json']


def random_salt():
return ''.join(random.choice('0123456789abcdef') for _ in range(10))


def deploy(directory):
res = subprocess.run(['cargo', 'contract', 'instantiate', '--salt',
obrok marked this conversation as resolved.
Show resolved Hide resolved
random_salt()] + COMMON_ARGS, cwd=directory, check=True, stdout=PIPE)
return json.loads(res.stdout.decode('utf-8'))


def call(directory, contract, message, *args):
args = [x for a in args for x in ['--args', a]]
res = subprocess.run(['cargo', 'contract', 'call', '--contract', contract,
'--message', message] + args + COMMON_ARGS, cwd=directory, check=True, stdout=PIPE)
return json.loads(res.stdout.decode('utf-8'))


def event_field(event, field):
for f in [f for f in event['fields'] if f['name'] == field]:
obrok marked this conversation as resolved.
Show resolved Hide resolved
return f['value']


def account_id(value):
match value:
case {'Literal': account_id}: return account_id
case _: raise ValueError(f'Invalid account id: {value}')


def uint(value):
match value:
case {'UInt': value}: return value
case _: raise ValueError(f'Invalid uint: {value}')


def find_fee(events, by_whom):
fee_event = next(filter(lambda e: e['name'] == 'TransactionFeePaid' and account_id(
event_field(e, 'who')) == by_whom, events), None)
return uint(event_field(fee_event, 'actual_fee'))


with urllib.request.urlopen('https://api.coingecko.com/api/v3/simple/price?ids=aleph-zero&vs_currencies=usd') as response:
data = json.load(response)
aleph_usd = data['aleph-zero']['usd']
obrok marked this conversation as resolved.
Show resolved Hide resolved


def format_fee(fee):
return "%f AZERO ($%f)" % (fee / AZERO, fee / AZERO * aleph_usd)


deploy_result = deploy(args.adder_dir)

adder_address = deploy_result['contract']
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This approach will return invalid address if there are contracts instantiated in the constructor use-ink/cargo-contract#777

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, given it's a bug in cargo contract, I say ignore until they fix it, then update cargo contract, no?

Copy link
Contributor

@deuszx deuszx Jan 18, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or you can inspect the .events[] field in the JSON and use the last element where .pallet == "Contracts" and .name = "Instantiated".

You will handle it however you want, I'm just raising a problem this code has with some contracts.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll keep that in mind if I run into this problem.

setup_event = next(filter(
lambda e: e['name'] == 'Transfer' and account_id(event_field(e, 'to')) == adder_address, deploy_result['events']), None)
obrok marked this conversation as resolved.
Show resolved Hide resolved

suri_address = account_id(event_field(setup_event, 'from'))
instantiate_fee = find_fee(deploy_result['events'], suri_address)

events = call(args.adder_dir, adder_address, 'add', '42')
add_fee = find_fee(events, suri_address)

headers = ['Operation', 'Fee']
prices = [
["Instantiate contract with single storage value",
format_fee(instantiate_fee)],
["Call contract with single storage update", format_fee(add_fee)]
]
print(tabulate(prices, headers=headers, tablefmt="github"))