From 44c612fcc1f46344b90d0fd631100fbb21c6332a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Obrok?= Date: Mon, 9 Jan 2023 17:52:11 +0100 Subject: [PATCH 1/4] Sketch out the pricing script --- scripts/pricing/README.md | 24 ++++++++ scripts/pricing/requirements.txt | 1 + scripts/pricing/run.py | 96 ++++++++++++++++++++++++++++++++ 3 files changed, 121 insertions(+) create mode 100644 scripts/pricing/README.md create mode 100644 scripts/pricing/requirements.txt create mode 100755 scripts/pricing/run.py diff --git a/scripts/pricing/README.md b/scripts/pricing/README.md new file mode 100644 index 0000000000..b526b1b651 --- /dev/null +++ b/scripts/pricing/README.md @@ -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 +``` diff --git a/scripts/pricing/requirements.txt b/scripts/pricing/requirements.txt new file mode 100644 index 0000000000..a00a1bbfbe --- /dev/null +++ b/scripts/pricing/requirements.txt @@ -0,0 +1 @@ +tabulate==0.9.0 diff --git a/scripts/pricing/run.py b/scripts/pricing/run.py new file mode 100755 index 0000000000..de57913573 --- /dev/null +++ b/scripts/pricing/run.py @@ -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', + 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]: + 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'] + + +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'] +setup_event = next(filter( + lambda e: e['name'] == 'Transfer' and account_id(event_field(e, 'to')) == adder_address, deploy_result['events']), None) + +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) + +prices = [ + ["Operation", "Fee"], + ["Instantiate contract with single storage value", + format_fee(instantiate_fee)], + ["Call contract with single storage update", format_fee(add_fee)] +] +print(tabulate(prices)) From 531066373e5c5041cee6fd641a108bcda80e1a6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Obrok?= Date: Mon, 9 Jan 2023 18:02:46 +0100 Subject: [PATCH 2/4] Format the pricing table prettier --- scripts/pricing/run.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/scripts/pricing/run.py b/scripts/pricing/run.py index de57913573..2fd15e721a 100755 --- a/scripts/pricing/run.py +++ b/scripts/pricing/run.py @@ -87,10 +87,10 @@ def format_fee(fee): events = call(args.adder_dir, adder_address, 'add', '42') add_fee = find_fee(events, suri_address) +headers = ['Operation', 'Fee'] prices = [ - ["Operation", "Fee"], ["Instantiate contract with single storage value", format_fee(instantiate_fee)], ["Call contract with single storage update", format_fee(add_fee)] ] -print(tabulate(prices)) +print(tabulate(prices, headers=headers, tablefmt="github")) From 7b8b5962be69b1968dc5f586047b1fb59afe6e56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Obrok?= Date: Wed, 11 Jan 2023 12:17:42 +0100 Subject: [PATCH 3/4] Improve cosmetics --- scripts/pricing/run.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/scripts/pricing/run.py b/scripts/pricing/run.py index 2fd15e721a..0f6e626ecb 100755 --- a/scripts/pricing/run.py +++ b/scripts/pricing/run.py @@ -44,8 +44,9 @@ def call(directory, contract, message, *args): def event_field(event, field): - for f in [f for f in event['fields'] if f['name'] == field]: - return f['value'] + for f in event['fields']: + if f['name'] == field: + return f['value'] def account_id(value): @@ -93,4 +94,5 @@ def format_fee(fee): format_fee(instantiate_fee)], ["Call contract with single storage update", format_fee(add_fee)] ] + print(tabulate(prices, headers=headers, tablefmt="github")) From 8cba8dadc922b09d7eab8195df128c3599fd12f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Obrok?= Date: Fri, 13 Jan 2023 16:55:38 +0100 Subject: [PATCH 4/4] Improve cosmetics --- scripts/pricing/run.py | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/scripts/pricing/run.py b/scripts/pricing/run.py index 0f6e626ecb..3b65e25ffa 100755 --- a/scripts/pricing/run.py +++ b/scripts/pricing/run.py @@ -4,7 +4,6 @@ import random import subprocess import json -from subprocess import PIPE from tabulate import tabulate import urllib.request @@ -31,16 +30,16 @@ def random_salt(): def deploy(directory): - res = subprocess.run(['cargo', 'contract', 'instantiate', '--salt', - random_salt()] + COMMON_ARGS, cwd=directory, check=True, stdout=PIPE) - return json.loads(res.stdout.decode('utf-8')) + res = subprocess.check_output(['cargo', 'contract', 'instantiate', '--salt', + random_salt()] + COMMON_ARGS, cwd=directory) + return json.loads(res.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')) + res = subprocess.check_output(['cargo', 'contract', 'call', '--contract', contract, + '--message', message] + args + COMMON_ARGS, cwd=directory) + return json.loads(res.decode('utf-8')) def event_field(event, field): @@ -49,6 +48,13 @@ def event_field(event, field): return f['value'] +def deployer_account_id(deploy_result): + setup_event = next(filter( + lambda e: e['name'] == 'Transfer' and account_id(event_field(e, 'to')) == adder_address, deploy_result['events']), None) + + return account_id(event_field(setup_event, 'from')) + + def account_id(value): match value: case {'Literal': account_id}: return account_id @@ -79,10 +85,7 @@ def format_fee(fee): deploy_result = deploy(args.adder_dir) adder_address = deploy_result['contract'] -setup_event = next(filter( - lambda e: e['name'] == 'Transfer' and account_id(event_field(e, 'to')) == adder_address, deploy_result['events']), None) - -suri_address = account_id(event_field(setup_event, 'from')) +suri_address = deployer_account_id(deploy_result) instantiate_fee = find_fee(deploy_result['events'], suri_address) events = call(args.adder_dir, adder_address, 'add', '42')