from os import path
import pytest
from tools.client_regression import ClientRegression
from tools import paths
from tools.utils import (
assert_run_failure,
assert_storage_contains,
bake,
init_with_transfer,
assert_balance,
)
from tools.constants import IDENTITIES
from .contract_paths import OPCODES_CONTRACT_PATH, MINI_SCENARIOS_CONTRACT_PATH
from . import protocol
KEY1 = 'foo'
KEY2 = 'bar'
@pytest.mark.incremental
@pytest.mark.slow
@pytest.mark.contract
@pytest.mark.regression
class TestContractOnchainOpcodes:
"""Tests for individual opcodes that requires origination."""
def test_gen_keys(self, client_regtest_scrubbed: ClientRegression):
"""Add keys used by later tests."""
client = client_regtest_scrubbed
client.gen_key(KEY1)
client.gen_key(KEY2)
def test_store_input(self, client_regtest_scrubbed: ClientRegression):
client = client_regtest_scrubbed
client.transfer(1000, "bootstrap1", KEY1, ['--burn-cap', '0.257'])
bake(client)
client.transfer(2000, "bootstrap1", KEY2, ['--burn-cap', '0.257'])
bake(client)
assert_balance(client, KEY1, 1000)
assert_balance(client, KEY2, 2000)
# Create a contract and transfer 100 ꜩ to it
init_with_transfer(
client,
path.join(OPCODES_CONTRACT_PATH, 'store_input.tz'),
'""',
100,
'bootstrap1',
)
client.transfer(
100,
"bootstrap1",
"store_input",
["-arg", '"abcdefg"', '--burn-cap', '10'],
)
bake(client)
assert_balance(client, "store_input", 200)
assert_storage_contains(client, "store_input", '"abcdefg"')
client.transfer(
100,
"bootstrap1",
"store_input",
["-arg", '"xyz"', '--burn-cap', '10'],
)
bake(client)
assert_storage_contains(client, "store_input", '"xyz"')
def test_transfer_amount(self, client_regtest_scrubbed: ClientRegression):
client = client_regtest_scrubbed
init_with_transfer(
client,
path.join(OPCODES_CONTRACT_PATH, 'transfer_amount.tz'),
'0',
100,
'bootstrap1',
)
client.transfer(
500,
"bootstrap1",
'transfer_amount',
['-arg', 'Unit', '--burn-cap', '10'],
)
bake(client)
assert_storage_contains(client, "transfer_amount", '500000000')
def test_now(self, client_regtest_scrubbed: ClientRegression):
# Regtest is disabled for this test, since one would need to
# scrub storage for this one as it changes (the timestamp)
# on every run.
client = client_regtest_scrubbed
client.set_regtest(None)
init_with_transfer(
client,
path.join(OPCODES_CONTRACT_PATH, 'store_now.tz'),
'"2017-07-13T09:19:01Z"',
100,
'bootstrap1',
)
client.transfer(
500, "bootstrap1", 'store_now', ['-arg', 'Unit', '--burn-cap', '10']
)
bake(client)
assert_storage_contains(
client, 'store_now', f'"{protocol.get_now(client)}"'
)
def test_transfer_tokens(self, client_regtest_scrubbed: ClientRegression):
"""Tests TRANSFER_TOKENS."""
client = client_regtest_scrubbed
client.originate(
'test_transfer_account1',
100,
'bootstrap1',
path.join(OPCODES_CONTRACT_PATH, 'noop.tz'),
['--burn-cap', '10'],
)
bake(client)
client.originate(
'test_transfer_account2',
20,
'bootstrap1',
path.join(OPCODES_CONTRACT_PATH, 'noop.tz'),
['--burn-cap', '10'],
)
bake(client)
init_with_transfer(
client,
path.join(OPCODES_CONTRACT_PATH, 'transfer_tokens.tz'),
'Unit',
1000,
'bootstrap1',
)
assert_balance(client, 'test_transfer_account1', 100)
account1_addr = client.get_contract_address('test_transfer_account1')
client.transfer(
100,
'bootstrap1',
'transfer_tokens',
['-arg', f'"{account1_addr}"', '--burn-cap', '10'],
)
bake(client)
# Why isn't this 200 ꜩ? Baking fee?
assert_balance(client, 'test_transfer_account1', 200)
account2_addr = client.get_contract_address('test_transfer_account2')
client.transfer(
100,
'bootstrap1',
'transfer_tokens',
['-arg', f'"{account2_addr}"', '--burn-cap', '10'],
)
bake(client)
assert_balance(client, 'test_transfer_account2', 120)
def test_self(self, client_regtest_scrubbed: ClientRegression):
# Regtest is disabled for this test, since one would need to
# scrub storage for this one as it changes (the contract
# address) on every run.
client = client_regtest_scrubbed
client.set_regtest(None)
init_with_transfer(
client,
path.join(OPCODES_CONTRACT_PATH, 'self.tz'),
'"tz1KqTpEZ7Yob7QbPE4Hy4Wo8fHG8LhKxZSx"',
1000,
'bootstrap1',
)
client.transfer(0, 'bootstrap1', 'self', ['--burn-cap', '10'])
bake(client)
self_addr = client.get_contract_address('self')
assert_storage_contains(client, 'self', f'"{self_addr}"')
def test_contract_fails(self, client_regtest_scrubbed: ClientRegression):
client = client_regtest_scrubbed
client.set_regtest(None)
init_with_transfer(
client,
path.join(OPCODES_CONTRACT_PATH, 'contract.tz'),
'Unit',
1000,
'bootstrap1',
)
client.transfer(0, 'bootstrap1', 'self', ['--burn-cap', '10'])
bake(client)
addr = client.get_contract_address('contract')
with assert_run_failure(r'script reached FAILWITH instruction'):
client.transfer(
0,
'bootstrap1',
'contract',
['-arg', f'"{addr}"', '--burn-cap', '10'],
)
def test_init_proxy(self, client_regtest_scrubbed: ClientRegression):
client = client_regtest_scrubbed
init_with_transfer(
client,
path.join(OPCODES_CONTRACT_PATH, 'proxy.tz'),
'Unit',
1000,
'bootstrap1',
)
def test_source(self, client_regtest_scrubbed: ClientRegression):
client = client_regtest_scrubbed
init_store = IDENTITIES['bootstrap4']['identity']
init_with_transfer(
client,
path.join(OPCODES_CONTRACT_PATH, 'source.tz'),
f'"{init_store}"',
1000,
'bootstrap1',
)
# direct transfer to the contract
client.transfer(0, 'bootstrap2', 'source', ['--burn-cap', '10'])
bake(client)
source_addr = IDENTITIES['bootstrap2']['identity']
assert_storage_contains(client, 'source', f'"{source_addr}"')
# indirect transfer to the contract through proxy
contract_addr = client.get_contract_address('source')
client.transfer(
0,
'bootstrap2',
'proxy',
['--burn-cap', '10', '--arg', f'"{contract_addr}"'],
)
bake(client)
assert_storage_contains(client, 'source', f'"{source_addr}"')
def test_sender(self, client_regtest_scrubbed: ClientRegression):
client = client_regtest_scrubbed
client.set_regtest(None)
init_store = IDENTITIES['bootstrap4']['identity']
init_with_transfer(
client,
path.join(OPCODES_CONTRACT_PATH, 'sender.tz'),
f'"{init_store}"',
1000,
'bootstrap1',
)
# direct transfer to the contract
client.transfer(0, 'bootstrap2', 'sender', ['--burn-cap', '10'])
bake(client)
sender_addr = IDENTITIES['bootstrap2']['identity']
assert_storage_contains(client, 'sender', f'"{sender_addr}"')
# indirect transfer to the contract through proxy
contract_addr = client.get_contract_address('sender')
proxy_addr = client.get_contract_address('proxy')
client.transfer(
0,
'bootstrap2',
'proxy',
['--burn-cap', '10', '--arg', f'"{contract_addr}"'],
)
bake(client)
assert_storage_contains(client, 'sender', f'"{proxy_addr}"')
def test_slice(self, client_regtest_scrubbed: ClientRegression):
client = client_regtest_scrubbed
init_with_transfer(
client,
path.join(OPCODES_CONTRACT_PATH, 'slices.tz'),
'"sppk7dBPqMPjDjXgKbb5f7V3PuKUrA4Zuwc3c3H7XqQerqPUWbK7Hna"',
1000,
'bootstrap1',
)
@pytest.mark.parametrize(
'contract_arg',
[
line.rstrip('\n')
for line in open(
f'{paths.TEZOS_HOME}'
+ '/tests_python/tests_013/'
+ 'test_slice_fails_params.txt'
)
],
)
def test_slice_fails(
self, client_regtest_scrubbed: ClientRegression, contract_arg: str
):
client = client_regtest_scrubbed
with assert_run_failure(r'script reached FAILWITH instruction'):
client.transfer(
0,
'bootstrap1',
'slices',
['-arg', contract_arg, '--burn-cap', '10'],
)
# bake(client)
@pytest.mark.parametrize(
'contract_arg',
[
line.rstrip('\n')
for line in open(
f'{paths.TEZOS_HOME}'
+ '/tests_python/tests_013/'
+ 'test_slice_success_params.txt'
)
],
)
def test_slice_success(
self, client_regtest_scrubbed: ClientRegression, contract_arg: str
):
client = client_regtest_scrubbed
client.transfer(
0,
'bootstrap1',
'slices',
['-arg', contract_arg, '--burn-cap', '10'],
)
bake(client)
def test_split_string(self, client_regtest_scrubbed: ClientRegression):
client = client_regtest_scrubbed
init_with_transfer(
client,
path.join(OPCODES_CONTRACT_PATH, 'split_string.tz'),
'{}',
1000,
'bootstrap1',
)
client.transfer(
0,
'bootstrap1',
'split_string',
['-arg', '"abc"', '--burn-cap', '10'],
)
bake(client)
assert_storage_contains(client, 'split_string', '{ "a" ; "b" ; "c" }')
client.transfer(
0,
'bootstrap1',
'split_string',
['-arg', '"def"', '--burn-cap', '10'],
)
bake(client)
assert_storage_contains(
client, 'split_string', '{ "a" ; "b" ; "c" ; "d" ; "e" ; "f" }'
)
def test_split_bytes(self, client_regtest_scrubbed: ClientRegression):
client = client_regtest_scrubbed
init_with_transfer(
client,
path.join(OPCODES_CONTRACT_PATH, 'split_bytes.tz'),
'{}',
1000,
'bootstrap1',
)
client.transfer(
0,
'bootstrap1',
'split_bytes',
['-arg', '0xaabbcc', '--burn-cap', '10'],
)
bake(client)
assert_storage_contains(client, 'split_bytes', '{ 0xaa ; 0xbb ; 0xcc }')
client.transfer(
0,
'bootstrap1',
'split_bytes',
['-arg', '0xddeeff', '--burn-cap', '10'],
)
bake(client)
assert_storage_contains(
client, 'split_bytes', '{ 0xaa ; 0xbb ; 0xcc ; 0xdd ; 0xee ; 0xff }'
)
def test_set_delegate(self, client_regtest_scrubbed: ClientRegression):
client = client_regtest_scrubbed
init_with_transfer(
client,
path.join(OPCODES_CONTRACT_PATH, 'set_delegate.tz'),
'Unit',
1000,
'bootstrap1',
)
bake(client)
assert client.get_delegate('set_delegate').delegate is None
addr = IDENTITIES['bootstrap5']['identity']
client.transfer(
0, 'bootstrap1', 'set_delegate', ['-arg', f'(Some "{addr}")']
)
bake(client)
assert client.get_delegate('set_delegate').delegate == addr
client.transfer(0, 'bootstrap1', 'set_delegate', ['-arg', 'None'])
bake(client)
assert client.get_delegate('set_delegate').delegate is None
@pytest.mark.parametrize(
'contract',
[
'compare_big_type.tz',
'compare_big_type2.tz',
],
)
def test_trace_origination(self, client_regtest_scrubbed, contract):
client = client_regtest_scrubbed
init_with_transfer(
client,
path.join(OPCODES_CONTRACT_PATH, contract),
'Unit',
1000,
'bootstrap1',
)
bake(client)
@pytest.mark.incremental
class TestTickets:
"""Tests for tickets."""
def test_ticket_user_forge(self, client):
bake(client)
init_with_transfer(
client,
path.join(OPCODES_CONTRACT_PATH, 'ticket_store-2.tz'),
'None',
100,
'bootstrap1',
'storer',
)
# Create parameter by hand with a ticket type but no ticket in it
client.transfer(
100, 'bootstrap1', 'storer', ['-arg', 'None', '--burn-cap', '10']
)
with assert_run_failure(r'Unexpected forged value'):
# Create parameter by hand with a ticket in it
client.transfer(
100,
'bootstrap1',
'storer',
['-arg', 'Some 1', '--burn-cap', '10'],
)
with assert_run_failure(r'Unexpected forged value'):
# Create storage by hand with a ticket in it
init_with_transfer(
client,
path.join(OPCODES_CONTRACT_PATH, 'ticket_bad.tz'),
'1',
100,
'bootstrap1',
'ticket_bad',
)
def test_ticket_user_big_forge(self, client):
bake(client)
contract_name = 'big_storer'
init_with_transfer(
client,
path.join(OPCODES_CONTRACT_PATH, 'ticket_big_store.tz'),
'{}',
100,
'bootstrap1',
contract_name,
)
bake(client)
client.transfer(
100, 'bootstrap1', contract_name, ['-arg', '42', '--burn-cap', '10']
)
bake(client)
storage = client.get_storage(contract_name)
with assert_run_failure(r'Unexpected forged value'):
# Create a storage with the ID of a big map that has tickets in it
init_with_transfer(
client,
path.join(OPCODES_CONTRACT_PATH, 'ticket_big_store.tz'),
storage,
100,
'bootstrap1',
'thief',
)
with assert_run_failure(r'Unexpected forged value'):
# Create a storage with the ID of a big map that has tickets in it
init_with_transfer(
client,
path.join(OPCODES_CONTRACT_PATH, 'ticket_big_store.tz'),
'(Pair ' + storage + ' {})',
100,
'bootstrap1',
'thief',
)
def test_ticket_read(self, client):
"""Test TICKETS"""
init_with_transfer(
client,
path.join(OPCODES_CONTRACT_PATH, 'ticketer.tz'),
'42',
100,
'bootstrap1',
'ticketer_read',
)
bake(client)
ticketer_addr = client.get_contract_address('ticketer_read')
init_with_transfer(
client,
path.join(OPCODES_CONTRACT_PATH, 'ticket_read.tz'),
'"' + ticketer_addr + '"',
100,
'bootstrap1',
'reader',
)
bake(client)
reader_addr = client.get_contract_address('reader')
client.transfer(
100,
'bootstrap1',
'ticketer_read',
['-arg', '"' + reader_addr + '"', '--burn-cap', '10'],
)
bake(client)
assert_storage_contains(client, "reader", '"' + ticketer_addr + '"')
def test_bad_ticket(self, client):
init_with_transfer(
client,
path.join(OPCODES_CONTRACT_PATH, 'ticketer.tz'),
'42',
100,
'bootstrap1',
'ticketer_bad',
)
bake(client)
ticketer_addr = client.get_contract_address('ticketer_bad')
init_with_transfer(
client,
path.join(OPCODES_CONTRACT_PATH, 'ticket_read.tz'),
'"' + ticketer_addr + '"',
100,
'bootstrap1',
'reader_bad',
)
bake(client)
with assert_run_failure(r'Unexpected forged value'):
client.transfer(
100,
'bootstrap1',
'reader_bad',
['-arg', '1', '--burn-cap', '10'],
)
bake(client)
def test_ticket_utxo(self, client):
"""Test UTXOs"""
init_with_transfer(
client,
path.join(OPCODES_CONTRACT_PATH, 'utxor.tz'),
'42',
100,
'bootstrap1',
)
bake(client)
utxor_addr = client.get_contract_address('utxor')
init_with_transfer(
client,
path.join(OPCODES_CONTRACT_PATH, 'utxo_read.tz'),
'"' + utxor_addr + '"',
100,
'bootstrap1',
"reader_a",
)
bake(client)
reader_a_addr = client.get_contract_address('reader_a')
utxor_addr = client.get_contract_address('utxor')
init_with_transfer(
client,
path.join(OPCODES_CONTRACT_PATH, 'utxo_read.tz'),
'"' + utxor_addr + '"',
100,
'bootstrap1',
"reader_b",
)
bake(client)
reader_b_addr = client.get_contract_address('reader_b')
client.transfer(
100,
'bootstrap1',
'utxor',
[
'-arg',
'(Pair "' + reader_a_addr + '" "' + reader_b_addr + '")',
'--burn-cap',
'10',
],
)
bake(client)
def test_ticket_split(self, client):
def ticket(target_addr, param, utxo_amount):
param = (
'(Pair (Pair "'
+ target_addr
+ '" '
+ str(param)
+ ') '
+ str(utxo_amount)
+ ')'
)
client.transfer(
100,
'bootstrap1',
'ticketer',
['-arg', param, '--burn-cap', '10'],
)
init_with_transfer(
client,
path.join(OPCODES_CONTRACT_PATH, 'ticketer-2.tz'),
'Unit',
100,
'bootstrap1',
'ticketer',
)
init_with_transfer(
client,
path.join(OPCODES_CONTRACT_PATH, 'ticket_split.tz'),
'Unit',
100,
'bootstrap1',
'splitter',
)
bake(client)
splitter_addr = client.get_contract_address('splitter')
ticket(splitter_addr, 42, 3)
with assert_run_failure(r'script reached FAILWITH instruction'):
# Wrong Split Amount
ticket(splitter_addr, 42, 4)
bake(client)
def test_ticket_join(self, client):
"""Test JOIN"""
def params(target_addr, utxo_amount, param):
return (
'(Pair (Pair "'
+ target_addr
+ '" '
+ str(param)
+ ') '
+ str(utxo_amount)
+ ')'
)
init_with_transfer(
client,
path.join(OPCODES_CONTRACT_PATH, 'ticketer-2.tz'),
'Unit',
100,
'bootstrap1',
'ticketer_a',
)
init_with_transfer(
client,
path.join(OPCODES_CONTRACT_PATH, 'ticketer-2.tz'),
'Unit',
100,
'bootstrap1',
'ticketer_b',
)
init_with_transfer(
client,
path.join(OPCODES_CONTRACT_PATH, 'ticket_join.tz'),
'None',
100,
'bootstrap1',
'joiner',
)
bake(client)
joiner_addr = client.get_contract_address('joiner')
client.transfer(
100,
'bootstrap1',
'ticketer_a',
['-arg', params(joiner_addr, 42, 1), '--burn-cap', '10'],
)
bake(client)
client.transfer(
100,
'bootstrap1',
'ticketer_a',
['-arg', params(joiner_addr, 144, 1), '--burn-cap', '10'],
)
bake(client)
with assert_run_failure(r'script reached FAILWITH instruction'):
# Different Ticketer
client.transfer(
100,
'bootstrap1',
'ticketer_b',
['-arg', params(joiner_addr, 23, 1), '--burn-cap', '10'],
)
with assert_run_failure(r'script reached FAILWITH instruction'):
# Different Content
client.transfer(
100,
'bootstrap1',
'ticketer_a',
['-arg', params(joiner_addr, 21, 23), '--burn-cap', '10'],
)
def test_ticket_fungible_originations(self, client, session):
"""Test the origination of builder and wallet contracts for fungible
tokens implemented using tickets."""
builder_path = path.join(
MINI_SCENARIOS_CONTRACT_PATH, 'ticket_builder_fungible.tz'
)
wallet_path = path.join(
MINI_SCENARIOS_CONTRACT_PATH, 'ticket_wallet_fungible.tz'
)
manager_address = IDENTITIES['bootstrap1']['identity']
builders = {}
wallets = {}
# Helper functions
def originate_builder(name):
"""Create a fungible token contract managed by bootstrap1."""
origination = client.originate(
contract_name=f'builder_{name}',
amount="0",
sender='bootstrap1',
contract=builder_path,
args=['--init', f'"{manager_address}"', '--burn-cap', "10"],
)
builders[name] = origination.contract
bake(client)
def originate_wallet(name):
"""Create a fungible token wallet managed by bootstrap1."""
origination = client.originate(
contract_name=f'wallet_{name}',
amount="0",
sender='bootstrap1',
contract=wallet_path,
args=[
'--init',
f'Pair "{manager_address}" {{}}',
'--burn-cap',
"10",
],
)
wallets[name] = origination.contract
bake(client)
# Create 3 token contracts "A", "B", and "C".
originate_builder("A")
originate_builder("B")
originate_builder("C")
# Create 2 wallets "Alice" and "Bob".
originate_wallet("Alice")
originate_wallet("Bob")
session['fungible_builders'] = builders
session['fungible_wallets'] = wallets
def test_ticket_fungible_transfers(self, client, session):
"""Test the life cycle of fungible tokens implemented using tickets."""
manager_address = IDENTITIES['bootstrap1']['identity']
builders = session['fungible_builders']
wallets = session['fungible_wallets']
def mint(builder, wallet, amount):
"""Mint fungible tokens."""
wallet_address = wallets[wallet]
parameter = f'(Pair "{wallet_address}%receive" {amount})'
client.transfer(
amount=0,
giver=manager_address,
receiver=f'builder_{builder}',
args=[
'--burn-cap',
'2',
'--entrypoint',
'mint',
'--arg',
parameter,
],
)
bake(client)
def burn(builder, wallet, amount):
"""Burn fungible tokens."""
builder_addr = builders[builder]
parameter = f'Pair "{builder_addr}%burn" {amount} "{builder_addr}"'
client.transfer(
amount=0,
giver=manager_address,
receiver=f'wallet_{wallet}',
args=[
'--burn-cap',
'2',
'--entrypoint',
'send',
'--arg',
parameter,
],
)
bake(client)
def transfer(builder, source_wallet, destination_wallet, amount):
"""Transfer fungible tokens."""
builder_addr = builders[builder]
dest_addr = wallets[destination_wallet]
parameter = f'Pair "{dest_addr}%receive" {amount} "{builder_addr}"'
client.transfer(
amount=0,
giver=manager_address,
receiver=f'wallet_{source_wallet}',
args=[
'--burn-cap',
'2',
'--entrypoint',
'send',
'--arg',
parameter,
],
)
bake(client)
# 100A --> Alice
mint(builder="A", wallet="Alice", amount=100)
# 100B --> Alice
mint(builder="B", wallet="Alice", amount=100)
# Fail: Alice --1C--> Bob
with assert_run_failure(r'script reached FAILWITH instruction'):
transfer(
builder="C",
source_wallet="Alice",
destination_wallet="Bob",
amount=1,
)
# Fail: Alice --0C--> Bob
with assert_run_failure(r'script reached FAILWITH instruction'):
transfer(
builder="C",
source_wallet="Alice",
destination_wallet="Bob",
amount=0,
)
# Fail: Alice --150A--> Bob
with assert_run_failure(r'script reached FAILWITH instruction'):
transfer(
builder="A",
source_wallet="Alice",
destination_wallet="Bob",
amount=150,
)
# Fail: Bob --50A--> Alice
with assert_run_failure(r'script reached FAILWITH instruction'):
transfer(
builder="A",
source_wallet="Bob",
destination_wallet="Alice",
amount=50,
)
# Alice --50A--> Bob
transfer(
builder="A",
source_wallet="Alice",
destination_wallet="Bob",
amount=50,
)
# Alice --50A--> Bob
transfer(
builder="A",
source_wallet="Alice",
destination_wallet="Bob",
amount=50,
)
# Alice --0A--> Bob
transfer(
builder="A",
source_wallet="Alice",
destination_wallet="Bob",
amount=0,
)
# Fail: Alice --1A--> Bob
with assert_run_failure(r'script reached FAILWITH instruction'):
transfer(
builder="A",
source_wallet="Alice",
destination_wallet="Bob",
amount=1,
)
# Bob --100A--> Bob
transfer(
builder="A",
source_wallet="Bob",
destination_wallet="Bob",
amount=100,
)
# Fail: Bob --150A--> Bob
with assert_run_failure(r'script reached FAILWITH instruction'):
transfer(
builder="A",
source_wallet="Bob",
destination_wallet="Bob",
amount=150,
)
# Bob --100A-->
burn(builder="A", wallet="Bob", amount=100)
# Bob --0A-->
burn(builder="A", wallet="Bob", amount=0)
# Fail: Bob --1A-->
with assert_run_failure(r'script reached FAILWITH instruction'):
burn(builder="A", wallet="Bob", amount=1)
def test_ticket_non_fungible_originations(self, client, session):
"""Test the origination of builder and wallet contracts for
non-fungible tokens implemented using tickets."""
builder_path = path.join(
MINI_SCENARIOS_CONTRACT_PATH, 'ticket_builder_non_fungible.tz'
)
wallet_path = path.join(
MINI_SCENARIOS_CONTRACT_PATH, 'ticket_wallet_non_fungible.tz'
)
manager_address = IDENTITIES['bootstrap1']['identity']
builders = {}
wallets = {}
# Helper functions
def originate_builder(name):
"""Create a non-fungible token contract managed by bootstrap1."""
storage = f'(Pair "{manager_address}" 0)'
origination = client.originate(
contract_name=f'nft_builder_{name}',
amount="0",
sender='bootstrap1',
contract=builder_path,
args=['--init', storage, '--burn-cap', "10"],
)
builders[name] = origination.contract
bake(client)
def originate_wallet(name):
"""Create a non-fungible token wallet managed by bootstrap1."""
origination = client.originate(
contract_name=f'nft_wallet_{name}',
amount="0",
sender='bootstrap1',
contract=wallet_path,
args=[
'--init',
f'Pair "{manager_address}" {{}}',
'--burn-cap',
"10",
],
)
wallets[name] = origination.contract
bake(client)
# Create 3 token contracts "A", "B", and "C".
originate_builder("A")
originate_builder("B")
originate_builder("C")
# Create 2 wallets "Alice" and "Bob".
originate_wallet("Alice")
originate_wallet("Bob")
session['non_fungible_builders'] = builders
session['non_fungible_wallets'] = wallets
def test_ticket_non_fungible_transfers(self, client, session):
"""Test the life cycle of non-fungible tokens implemented using
tickets."""
manager_address = IDENTITIES['bootstrap1']['identity']
builders = session['non_fungible_builders']
wallets = session['non_fungible_wallets']
def mint(builder, wallet, token_id):
"""Mint a non-fungible token and assert that it has the expected
id."""
builder_alias = f'nft_builder_{builder}'
expected_builder_storage = f'Pair "{manager_address}" {token_id}'
actual_builder_storage = client.get_storage(builder_alias)
assert expected_builder_storage == actual_builder_storage
wallet_address = wallets[wallet]
parameter = f'"{wallet_address}%receive"'
client.transfer(
amount=0,
giver=manager_address,
receiver=builder_alias,
args=[
'--burn-cap',
'2',
'--entrypoint',
'mint_destination',
'--arg',
parameter,
],
)
bake(client)
def burn(builder, wallet, token_id):
"""Burn a non-fungible token."""
builder_addr = builders[builder]
parameter = (
f'Pair "{builder_addr}%burn" "{builder_addr}" {token_id}'
)
client.transfer(
amount=0,
giver=manager_address,
receiver=f'nft_wallet_{wallet}',
args=[
'--burn-cap',
'2',
'--entrypoint',
'send',
'--arg',
parameter,
],
)
bake(client)
def transfer(builder, source_wallet, destination_wallet, token_id):
"""Transfer fungible tokens."""
builder_addr = builders[builder]
dest_addr = wallets[destination_wallet]
parameter = (
f'Pair "{dest_addr}%receive" "{builder_addr}" {token_id}'
)
client.transfer(
amount=0,
giver=manager_address,
receiver=f'nft_wallet_{source_wallet}',
args=[
'--burn-cap',
'2',
'--entrypoint',
'send',
'--arg',
parameter,
],
)
bake(client)
# A0 --> Alice
mint(builder="A", wallet="Alice", token_id=0)
# A1 --> Alice
mint(builder="A", wallet="Alice", token_id=1)
# B0 --> Alice
mint(builder="B", wallet="Alice", token_id=0)
# Fail: Alice --C0--> Bob
with assert_run_failure(r'script reached FAILWITH instruction'):
transfer(
builder="C",
source_wallet="Alice",
destination_wallet="Bob",
token_id=0,
)
# Fail: Alice --A2--> Bob
with assert_run_failure(r'script reached FAILWITH instruction'):
transfer(
builder="A",
source_wallet="Alice",
destination_wallet="Bob",
token_id=2,
)
# Fail: Bob --A0--> Alice
with assert_run_failure(r'script reached FAILWITH instruction'):
transfer(
builder="A",
source_wallet="Bob",
destination_wallet="Alice",
token_id=0,
)
# Fail: Bob --A1--> Bob
with assert_run_failure(r'script reached FAILWITH instruction'):
transfer(
builder="A",
source_wallet="Bob",
destination_wallet="Bob",
token_id=1,
)
# Alice --A1--> Bob
transfer(
builder="A",
source_wallet="Alice",
destination_wallet="Bob",
token_id=1,
)
# Alice --A0--> Bob
transfer(
builder="A",
source_wallet="Alice",
destination_wallet="Bob",
token_id=0,
)
# Fail: Alice --A1--> Bob
with assert_run_failure(r'script reached FAILWITH instruction'):
transfer(
builder="A",
source_wallet="Alice",
destination_wallet="Bob",
token_id=1,
)
# Bob --A0--> Bob
transfer(
builder="A",
source_wallet="Bob",
destination_wallet="Bob",
token_id=0,
)
# Bob --A0-->
burn(builder="A", wallet="Bob", token_id=0)
# Bob --A1-->
burn(builder="A", wallet="Bob", token_id=1)
# Fail: Bob --B0-->
with assert_run_failure(r'script reached FAILWITH instruction'):
burn(builder="B", wallet="Bob", token_id=0)
# Alice --B0-->
burn(builder="B", wallet="Alice", token_id=0)
ORIGINATE_BIG_MAP_FILE = path.join(
OPCODES_CONTRACT_PATH, 'originate_big_map.tz'
)
@pytest.mark.incremental
@pytest.mark.contract
@pytest.mark.regression
class TestContractBigMapOrigination:
def test_big_map_origination_literal(self, client_regtest_scrubbed):
client = client_regtest_scrubbed
# originate a first version of the contract from a literal so
# that a big_map with id 0 exists
init_with_transfer(
client,
ORIGINATE_BIG_MAP_FILE,
'{Elt 0 0}',
1000,
'bootstrap1',
contract_name='originate_big_map_literal',
)
def test_big_map_origination_id(self, client_regtest_scrubbed):
client = client_regtest_scrubbed
# originate again the same script from the big-map id 0
with assert_run_failure(r'Unexpected forged value'):
init_with_transfer(
client,
ORIGINATE_BIG_MAP_FILE,
'0',
1000,
'bootstrap1',
contract_name='originate_big_map_id',
)
def test_big_map_origination_diff(self, client_regtest_scrubbed):
client = client_regtest_scrubbed
# originate again the same script from a big-diff
with assert_run_failure(r'Unexpected forged value'):
init_with_transfer(
client,
ORIGINATE_BIG_MAP_FILE,
'Pair 0 {Elt 1 (Some 4)}',
1000,
'bootstrap1',
contract_name='originate_big_map_diff',
)
def test_big_map_transfer_id(self, client_regtest_scrubbed):
client = client_regtest_scrubbed
# call the first contract, passing an id as parameter
with assert_run_failure(r'Unexpected forged value'):
client.call(
source='bootstrap1',
destination='originate_big_map_literal',
args=['--arg', '0'],
)
def test_big_map_transfer_diff(self, client_regtest_scrubbed):
client = client_regtest_scrubbed
# call the first contract, passing a diff as parameter
with assert_run_failure(r'Unexpected forged value'):
client.call(
source='bootstrap1',
destination='originate_big_map_literal',
args=['--arg', 'Pair 0 {Elt 1 (Some 4)}'],
)
@pytest.mark.incremental
@pytest.mark.slow
@pytest.mark.contract
@pytest.mark.regression
class TestContractOnchainLevel:
"""Onchain tests for LEVEL."""
# This test needs to be in a separate class to not depend on the number
# of operations happening before
def test_level(self, client_regtest_scrubbed: ClientRegression):
client = client_regtest_scrubbed
init_with_transfer(
client,
path.join(OPCODES_CONTRACT_PATH, 'level.tz'),
'9999999',
100,
'bootstrap1',
)
bake(client)
client.transfer(
500, "bootstrap1", 'level', ['-arg', 'Unit', '--burn-cap', '10']
)
bake(client)
level = client.get_level()
slevel = str(level)
assert_storage_contains(client, 'level', slevel)
bake(client)
bake(client)
# checks the storage hasn't changed even though the current level has
assert_storage_contains(client, 'level', slevel)
# Run again to check the storage gets updated
client.transfer(
500, "bootstrap1", 'level', ['-arg', 'Unit', '--burn-cap', '10']
)
bake(client)
assert_storage_contains(client, 'level', str(level + 3))