diff --git a/README.md b/README.md
index 83159329b..42f1ce27f 100644
--- a/README.md
+++ b/README.md
@@ -9,7 +9,8 @@
Made by COZ.IO
-
-
-
- Status |
- Release |
- Converts |
- Example Code |
- Contract Example Test |
-
-
-
- ✅ |
- v0.3 |
- Local variable declarations and assignments |
-
-
-
- def func():
- foo: int = 42
- bar = foo
-
-
- |
-
- List of examples
- |
-
-
- ✅ |
- v0.3 |
- Global variable declarations and assignments |
-
-
-
- foo: int = 42
- bar = foo
-
-
- |
-
- List of examples
- |
-
-
- ✅ |
- v0.4 |
- Global keyword |
-
-
-
- foo: int = 42
- bar = foo
-
-
- def func():
- global foo
- foo = 1
-
-
- |
-
- List of examples
- |
-
-
- ✅ |
- v0.3 |
- Arithmetic operations |
-
-
-
- +, -, *, //, %
-
-
- |
-
- List of examples
- |
-
-
- ✅ |
- v0.8 |
- Arithmetic operations |
-
-
-
- **
-
-
- |
-
- List of examples
- |
-
-
- 🔜 |
- backlog |
- Arithmetic operations |
-
-
-
- /
-
-
- |
-
- |
-
-
- ✅ |
- v0.3 |
- Arithmetic augmented assignment operators |
-
-
-
- +=, -=, *=, //=, %=
-
-
- |
-
- List of examples
- |
-
-
- ✅ |
- v0.8 |
- Arithmetic augmented assignment operators |
-
-
-
- **=
-
-
- |
-
- List of examples
- |
-
-
- 🔜 |
- backlog |
- Arithmetic augmented assignment operators |
-
-
-
- /=
-
-
- |
-
- |
-
-
- ✅ |
- v0.3 |
- Relational operations |
-
-
-
- ==, !=, <, <=, >, >=,
- is None, is not None
-
-
- |
-
- List of examples
- |
-
-
- ✅ |
- v0.8.3 |
- Relational operations |
-
-
-
- is, is not
-
-
- |
-
- List of examples
- |
-
-
- ✅ |
- v0.3 |
- Bitwise operations |
-
-
-
- &, |, ~, ^, <<, >>
-
-
- |
-
- List of examples
- |
-
-
- ✅ |
- v0.8.3 |
- Bitwise augmented assignment operators |
-
-
-
- &=, |=, ~=, ^=, <<=, >>=
-
-
- |
-
- List of examples
- |
-
-
- ✅ |
- v0.3 |
- Boolean logic operations |
-
-
-
- and, or, not
-
-
- |
-
- List of examples
- |
-
-
- ✅ |
- v0.3 |
- Tuple type |
-
-
-
- a = ('1', '2', '3')
-
-
- |
-
- List of examples
- |
-
-
- ✅ |
- v0.3 |
- List type |
-
-
-
- a = ['1', '2', '3']
-
-
- |
-
- List of examples
- |
-
-
- ✅ |
- v0.4 |
- List type |
-
-
-
- a.pop()
-
-
- |
-
- List of examples
- |
-
-
- ✅ |
- v0.7 |
- List type |
-
-
-
- a.remove(1)
- a.insert('example', 2)
-
-
- |
-
- List of examples
- |
-
-
- ✅ |
- v0.3 |
- Dict type |
-
-
-
- a = {1:'1', 2:'2', 3:'3'}
-
-
- |
-
- List of examples
- |
-
-
- 🔜 |
- backlog |
- Set type |
-
-
-
- a = {'1', '2', '3'}
-
-
- |
-
- |
-
-
- ✅ |
- v0.3 |
- Bytes type |
-
-
-
- a = b'\x01\x02\x03\x04'
-
-
- |
-
- List of examples
- |
-
-
- ✅ |
- v0.3 |
- Bytearray type |
-
-
-
- a = bytearray(b'\x01\x02\x03\x04')
-
-
- |
-
- List of examples
- |
-
-
- ✅ |
- v0.8.2 |
- Optional type |
-
-
-
- a: Optional[int] = 5
- a = 142
- a = None
-
-
- |
-
- List of examples
- |
-
-
- ✅ |
- v0.6.1 |
- Union type |
-
-
-
- a: Union[int, str] = 5
- a = 142
- a = 'example'
-
-
- |
-
- List of examples
- |
-
-
- ✅ |
- v0.3 |
- While statement |
-
-
-
- foo = 0
- while condition:
- foo = foo + 2
-
-
- |
-
- List of examples
- |
-
-
- ✅ |
- v0.3 |
- If, elif, else statements |
-
-
-
- if condition1:
- foo = 0
- elif condition2:
- foo = 1
- else:
- bar = 2
-
-
- |
-
- List of examples
- |
-
-
- ✅ |
- v0.3 |
- For statement |
-
-
-
- for x in (1, 2, 3):
- ...
-
-
- |
-
- List of examples
- |
-
-
- ✅ |
- v0.3 |
- Function call |
-
-
-
- def Main(num: int):
- a = foo(num)
- ...
-
-
- def foo(num: int) -> int:
- ...
-
-
- |
-
- List of examples
- |
-
-
- ✅ |
- v0.3 |
- Built in function |
-
-
-
- a = len('hello')
-
-
- |
-
- List of examples
- |
-
-
- ✅ |
- v0.4 |
- Built in function |
-
-
-
- a = range(1, 5, 2)
- b = isinstance(5, str)
- print(42)
-
-
- |
-
- List of examples
- |
-
-
- ✅ |
- v0.7 |
- Built in function |
-
-
-
- a = max(7, 12)
- b = min(1, 6)
-
-
- |
-
- List of examples
- |
-
-
- ✅ |
- v0.8 |
- Built in function |
-
-
-
- a = abs(-5)
-
-
- |
-
- List of examples
- |
-
-
- ✅ |
- v0.8.1 |
- Built in function |
-
-
-
- a = sum(list_of_num, 0)
-
-
- |
-
- List of examples
- |
-
-
- ✅ |
- v0.8.3 |
- Built in function |
-
-
-
- a = max(7, 0, 12, 8)
- b = min(1, 6, 2)
- c = reversed([1, 2, 3, 4])
-
-
- |
-
- List of examples
- |
-
-
- ✅ |
- v0.11.0 |
- Built in function |
-
-
-
- a = pow(2, 2)
-
-
- |
-
- List of examples
- |
-
-
- ✅ |
- v0.3 |
- Multiple expressions in the same line |
-
-
-
- i = i + h; a = 1; b = 3 + a; count = 0
-
-
- |
-
- List of examples
- |
-
-
- ✅ |
- v0.4 |
- Chained assignment |
-
-
-
- x = y = foo()
-
-
- |
-
- |
-
-
- ✅ |
- v0.3 |
- Sequence slicing |
-
-
-
- x = 'example'[2:4]
- x = [1, 2, 3][:2]
- x = 'example'[4:]
- x = (1, 2, 3)[:]
- x = 'example'[-4:-2]
- x = 'example'[:-4]
-
-
- |
-
- List of examples
- |
-
-
- ✅ |
- v0.10.0 |
- Sequence slicing |
-
-
-
- x = 'example'[2:4:2]
- x = 'example'[::2]
-
-
- |
-
- List of examples
- |
-
-
- ✅ |
- v0.3 |
- Assert |
-
-
-
- assert x % 2 == 0
- assert x % 3 != 2, 'error message'
-
-
- |
-
- List of examples
- |
-
-
- ✅ |
- v0.4 |
- Try except |
-
-
-
- try:
- a = foo(b)
- except Exception as e:
- a = foo(b)
-
-
- |
-
- List of examples
- |
-
-
- ✅ |
- v0.5 |
- Try except with finally |
-
-
-
- try:
- a = foo(b)
- except Exception as e:
- a = zubs(b)
- finally:
- b = zubs(a)
-
-
- |
-
- List of examples
- |
-
-
- ✅ |
- v0.4 |
- Continue, break |
-
- |
-
- |
-
-
- ✅ |
- v0.11.2 |
- Pass |
-
- |
-
- List of examples
- |
-
-
- ✅ |
- v0.3 |
- Import |
- Support to boa3.builtin packages. |
-
- List of examples
- |
-
-
- ✅ |
- v0.8.3 |
- Import |
- Support to user created modules. |
-
- List of examples
- |
-
-
- ✅ |
- v0.10.0 |
- Class |
-
-
-
- class Foo:
- def __init__(self, bar: Any):
- pass
-
-
- |
-
- List of examples
- |
-
-
-
-
## Neo Python Suite Projects
- **[Neo3-boa](https://github.com/CityOfZion/neo3-boa)**: Python smart contracts' compiler.
diff --git a/boa3/builtin/compile_time/__init__.py b/boa3/builtin/compile_time/__init__.py
index 23061f8d9..d3302e8ea 100644
--- a/boa3/builtin/compile_time/__init__.py
+++ b/boa3/builtin/compile_time/__init__.py
@@ -16,6 +16,14 @@ def CreateNewEvent(arguments: List[Tuple[str, type]] = [], event_name: str = '')
"""
Creates a new event.
+ >>> new_event: Event = CreateNewEvent(
+ ... [
+ ... ('name', str),
+ ... ('amount', int)
+ ... ],
+ ... 'New Event'
+ ... )
+
:param arguments: the list of the events args' names and types
:type arguments: List[Tuple[str, type]]
:param event_name: custom name of the event. It's filled with the variable name if not specified
@@ -30,6 +38,39 @@ def public(name: str = None, safe: bool = True, *args, **kwargs):
"""
This decorator identifies which methods should be included in the abi file.
+ >>> @public # this method will be added to the abi
+ ... def callable_function() -> bool:
+ ... return True
+ {
+ "name": "callable_function",
+ "offset": 0,
+ "parameters": [],
+ "safe": false,
+ "returntype": "Boolean"
+ }
+
+ >>> @public(name='callableFunction') # the method will be added with the different name to the abi
+ ... def callable_function() -> bool:
+ ... return True
+ {
+ "name": "callableFunction",
+ "offset": 0,
+ "parameters": [],
+ "safe": false,
+ "returntype": "Boolean"
+ }
+
+ >>> @public(safe=True) # the method will be added with the safe flag to the abi
+ ... def callable_function() -> bool:
+ ... return True
+ {
+ "name": "callable_function",
+ "offset": 0,
+ "parameters": [],
+ "safe": true,
+ "returntype": "Boolean"
+ }
+
:param name: Identifier for this method that'll be used on the abi. If not specified, it'll be the same
identifier from Python method definition
:type name: str
@@ -45,6 +86,12 @@ def metadata(*args):
"""
This decorator identifies the function that returns the metadata object of the smart contract.
This can be used to only one function. Using this decorator in multiple functions will raise a compiler error.
+
+ >>> @metadata # this indicates that this function will have information about the smart contract
+ ... def neo_metadata() -> NeoMetadata: # needs to return a NeoMetadata
+ ... meta = NeoMetadata()
+ ... meta.name = 'NewContractName'
+ ... return meta
"""
pass
@@ -53,6 +100,15 @@ def contract(script_hash: ByteString):
"""
This decorator identifies a class that should be interpreted as an interface to an existing contract.
+ >>> @contract('0xd2a4cff31913016155e38e474a2c06d08be276cf')
+ ... class GASInterface:
+ ... @staticmethod
+ ... def symbol() -> str:
+ ... pass
+ ... @public
+ ... def main() -> str:
+ ... return "Symbol is " + GASInterface.symbol()
+
:param script_hash: Script hash of the interfaced contract
:type script_hash: str or bytes
"""
@@ -73,6 +129,16 @@ def display_name(name: str):
This decorator identifies which methods from a contract interface should have a different identifier from the one
interfacing it. It only works in contract interface classes.
+ >>> @contract('0xd2a4cff31913016155e38e474a2c06d08be276cf')
+ ... class GASInterface:
+ ... @staticmethod
+ ... @display_name('totalSupply')
+ ... def total_supply() -> int: # the smart contract will call "totalSupply", but when writing the script you can call this method whatever you want to
+ ... pass
+ ... @public
+ ... def main() -> int:
+ ... return GASInterface.total_supply()
+
:param name: Method identifier from the contract manifest.
:type name: str
"""
@@ -83,6 +149,15 @@ class NeoMetadata:
"""
This class stores the smart contract manifest information.
+ >>> @metadata
+ ... def neo_metadata() -> NeoMetadata:
+ ... meta = NeoMetadata()
+ ... meta.name = 'NewContractName'
+ ... meta.add_permission(methods=['onNEP17Payment'])
+ ... meta.add_trusted_source("0x1234567890123456789012345678901234567890")
+ ... meta.date = "2023/05/30" # this property will end up inside the extra property
+ ... return meta
+
:ivar name: the smart contract name. Will be the name of the file by default;
:vartype type name: str
:ivar supported_standards: Neo standards supported by this smart contract. Empty by default;
@@ -152,6 +227,12 @@ def add_trusted_source(self, hash_or_address: str):
"""
Adds a valid contract hash, valid group public key, or the '*' wildcard to trusts.
+ >>> self.add_trusted_source("0x1234567890123456789012345678901234abcdef")
+
+ >>> self.add_trusted_source("035a928f201639204e06b4368b1a93365462a8ebbff0b8818151b74faab3a2b61a")
+
+ >>> self.add_trusted_source("*")
+
:param hash_or_address: a contract hash, group public key or '*'
:type hash_or_address: str
"""
@@ -179,6 +260,9 @@ def add_group(self, pub_key: str, signature: str):
"""
Adds a pair of public key and signature to the groups in the manifest.
+ >>> self.add_group("031f64da8a38e6c1e5423a72ddd6d4fc4a777abe537e5cb5aa0425685cda8e063b",
+ ... "fhsOJNF3N5Pm3oV1b7wYTx0QVelYNu7whwXMi8GsNGFKUnu3ZG8z7oWLfzzEz9pbnzwQe8WFCALEiZhLD1jG/w==")
+
:param pub_key: public key of the group
:type pub_key: str
:param signature: signature of the contract hash encoded in Base64
@@ -204,6 +288,12 @@ def add_permission(self, *, contract: str = IMPORT_WILDCARD, methods: Union[List
"""
Adds a valid contract and a valid methods to the permissions in the manifest.
+ >>> self.add_permission(methods=['onNEP17Payment'])
+
+ >>> self.add_permission(contract='0x3846a4aa420d9831044396dd3a56011514cd10e3', methods=['get_object'])
+
+ >>> self.add_permission(contract='0333b24ee50a488caa5deec7e021ff515f57b7993b93b45d7df901e23ee3004916')
+
:param contract: a contract hash, group public key or '*'
:type contract: str
:param methods: a list of methods or '*'
diff --git a/boa3/builtin/contract/__init__.py b/boa3/builtin/contract/__init__.py
index 215810b88..0e4d76f85 100644
--- a/boa3/builtin/contract/__init__.py
+++ b/boa3/builtin/contract/__init__.py
@@ -24,6 +24,18 @@
The NEP-5 transfer event that will be triggered whenever a token is transferred, minted or burned. It needs the
addresses of the sender, receiver and the amount transferred.
+>>> Nep5TransferEvent(b'\\xd1\\x17\\x92\\x82\\x12\\xc6\\xbe\\xfa\\x05\\xa0\\x23\\x07\\xa1\\x12\\x55\\x41\\x06\\x55\\x10\\xe6', # when calling, it will return None, but the event will be triggered
+... b'\\x18\\xb7\\x30\\x14\\xdf\\xcb\\xee\\x01\\x30\\x00\\x13\\x9b\\x8d\\xa0\\x13\\xfb\\x96\\xac\\xd1\\xc0', 100)
+{
+ 'name': 'transfer',
+ 'script hash': b'\\xee\\xc3\\x12\\xfd\\x12\\x95\\x84\\44\\x7f\\xb8\\xed\\x41\\xdc\\x86\\x33\\x95\\x10\\x10\\x9f\\x85',
+ 'state': {
+ 'from': b'\\xd1\\x17\\x92\\x82\\x12\\xc6\\xbe\\xfa\\x05\\xa0\\x23\\x07\\xa1\\x12\\x55\\x41\\x06\\x55\\x10\\xe6',
+ 'to': b'\\x18\\xb7\\x30\\x14\\xdf\\xcb\\xee\\x01\\x30\\x00\\x13\\x9b\\x8d\\xa0\\x13\\xfb\\x96\\xac\\xd1\\xc0',
+ 'amount': 100
+ }
+}
+
:meta hide-value:
"""
@@ -40,6 +52,19 @@
The NEP-11 Transfer event that will be triggered whenever a token is transferred, minted or burned. It needs the
addresses of the sender, receiver, amount transferred and the id of the token.
+>>> Nep11TransferEvent(b'\\xd1\\x17\\x92\\x82\\x12\\xc6\\xbe\\xfa\\x05\\xa0\\x23\\x07\\xa1\\x12\\x55\\x41\\x06\\x55\\x10\\xe6', # when calling, it will return None, but the event will be triggered
+... b'\\x18\\xb7\\x30\\x14\\xdf\\xcb\\xee\\x01\\x30\\x00\\x13\\x9b\\x8d\\xa0\\x13\\xfb\\x96\\xac\\xd1\\xc0', 1, '01')
+{
+ 'name': 'Transfer',
+ 'script hash': b'\\x13\\xb4\\x51\\xa2\\x1c\\x10\\x12\\xd6\\x13\\x12\\x19\\x0c\\x15\\x61\\x9b\\x1b\\xd1\\xa2\\xf4\\xb2',
+ 'state': {
+ 'from': b'\\xd1\\x17\\x92\\x82\\x12\\xc6\\xbe\\xfa\\x05\\xa0\\x23\\x07\\xa1\\x12\\x55\\x41\\x06\\x55\\x10\\xe6',
+ 'to': b'\\x18\\xb7\\x30\\x14\\xdf\\xcb\\xee\\x01\\x30\\x00\\x13\\x9b\\x8d\\xa0\\x13\\xfb\\x96\\xac\\xd1\\xc0',
+ 'amount': 1,
+ 'tokenId': '01'
+ }
+}
+
:meta hide-value:
"""
@@ -56,6 +81,18 @@
The NEP-17 Transfer event that will be triggered whenever a token is transferred, minted or burned. It needs the
addresses of the sender, receiver and the amount transferred.
+>>> Nep17TransferEvent(b'\\xd1\\x17\\x92\\x82\\x12\\xc6\\xbe\\xfa\\x05\\xa0\\x23\\x07\\xa1\\x12\\x55\\x41\\x06\\x55\\x10\\xe6', # when calling, it will return None, but the event will be triggered
+... b'\\x18\\xb7\\x30\\x14\\xdf\\xcb\\xee\\x01\\x30\\x00\\x13\\x9b\\x8d\\xa0\\x13\\xfb\\x96\\xac\\xd1\\xc0', 100)
+{
+ 'name': 'Transfer',
+ 'script hash': b'\\x17\\xe3\\xca\\x91\\xca\\xb7\\xaf\\xdd\\xe6\\xba\\x07\\xaa\\xba\\xa1\\x66\\xab\\xcf\\x00\\x04\\x50',
+ 'state': {
+ 'from': b'\\xd1\\x17\\x92\\x82\\x12\\xc6\\xbe\\xfa\\x05\\xa0\\x23\\x07\\xa1\\x12\\x55\\x41\\x06\\x55\\x10\\xe6',
+ 'to': b'\\x18\\xb7\\x30\\x14\\xdf\\xcb\\xee\\x01\\x30\\x00\\x13\\x9b\\x8d\\xa0\\x13\\xfb\\x96\\xac\\xd1\\xc0',
+ 'amount': 100
+ }
+}
+
:meta hide-value:
"""
@@ -63,6 +100,10 @@
def abort():
"""
Aborts the execution of a smart contract.
+
+ >>> abort() # abort doesn't return anything by itself, but the execution will stop and the VMState will be FAULT
+ VMState.FAULT
+
"""
pass
@@ -90,6 +131,12 @@ def to_script_hash(data_bytes: Any) -> bytes:
"""
Converts a data to a script hash.
+ >>> to_script_hash(ECPoint(bytes(range(33))))
+ b'\\x12\\xc8z\\xfb3k\\x1e4>\\xb3\\x83\\tK\\xc7\\xdch\\xe5\\xee\\xc7\\x98'
+
+ >>> to_script_hash(b'1234567891')
+ b'\\x4b\\x56\\x34\\x17\\xed\\x99\\x7f\\x13\\x22\\x67\\x40\\x79\\x36\\x8b\\xa2\\xcd\\x72\\x41\\x25\\x6d'
+
:param data_bytes: data to hash
:type data_bytes: Any
:return: the script hash of the data
diff --git a/boa3/builtin/interop/blockchain/__init__.py b/boa3/builtin/interop/blockchain/__init__.py
index 47dc0af35..b5d1c99fc 100644
--- a/boa3/builtin/interop/blockchain/__init__.py
+++ b/boa3/builtin/interop/blockchain/__init__.py
@@ -28,6 +28,23 @@ def get_contract(hash: UInt160) -> Contract:
"""
Gets a contract with a given hash.
+ >>> get_contract(UInt160(b'\\xcfv\\xe2\\x8b\\xd0\\x06,JG\\x8e\\xe3Ua\\x01\\x13\\x19\\xf3\\xcf\\xa4\\xd2')) # GAS script hash
+ {
+ 'id': -6,
+ 'update_counter': 0,
+ 'hash': b'\\xcfv\\xe2\\x8b\\xd0\\x06,JG\\x8e\\xe3Ua\\x01\\x13\\x19\\xf3\\xcf\\xa4\\xd2',
+ 'nef': b'NEF3neo-core-v3.0\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00#\\x10A\\x1a\\xf7{g@\\x10A\\x1a\\xf7{g@\\x10A\\x1a\\xf7{g@\\x10A\\x1a\\xf7{g@\\x10A\\x1a\\xf7{g@QA\\xc7\\x9e',
+ 'manifest': {
+ 'name': 'GasToken',
+ 'group': [],
+ 'supported_standards': ['NEP-17'],
+ 'abi': [[['balanceOf', [['account', 20]], 17, 0, True], ['decimals', [], 17, 7, True], ['symbol', [], 19, 14, True], ['totalSupply', [], 17, 21, True], ['transfer', [['from', 20], ['to', 20], ['amount', 17], ['data', 0]], 16, 28, False]], [['Transfer', [['from', 20], ['to', 20], ['amount', 17]]]]],
+ 'permissions': [[None, None]],
+ 'trusts': [],
+ 'extras': 'null'
+ },
+ }
+
:param hash: a smart contract hash
:type hash: UInt160
:return: a contract
@@ -42,6 +59,40 @@ def get_block(index_or_hash: Union[int, UInt256]) -> Block:
"""
Gets the block with the given index or hash.
+ >>> get_block(0) # first block
+ {
+ 'hash': b"S{\\xed'\\x85&\\xf5\\x93U=\\xc1\\xbf'\\x95\\xc4/\\x80X\\xdb\\xd5\\xa1-\\x97q\\x85\\xe3I\\xe5\\x99cd\\x04",
+ 'version': 0,
+ 'previous_hash': '\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00',
+ 'merkle_root': '\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00',
+ 'timestamp': 1468595301000,
+ 'nonce': 2083236893,
+ 'index': 0,
+ 'primary_index': 0,
+ 'next_consensus': b'\\xa6\\xea\\xb0\\xae\\xaf\\xb4\\x96\\xa1\\x1b\\xb0|\\x88\\x17\\xcar\\xa5J\\x00\\x12\\x04',
+ 'transaction_count': 0,
+ }
+
+ >>> get_block(UInt256(b"S{\\xed'\\x85&\\xf5\\x93U=\\xc1\\xbf'\\x95\\xc4/\\x80X\\xdb\\xd5\\xa1-\\x97q\\x85\\xe3I\\xe5\\x99cd\\x04")) # first block
+ {
+ 'hash': b"S{\\xed'\\x85&\\xf5\\x93U=\\xc1\\xbf'\\x95\\xc4/\\x80X\\xdb\\xd5\\xa1-\\x97q\\x85\\xe3I\\xe5\\x99cd\\x04",
+ 'version': 0,
+ 'previous_hash': '\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00',
+ 'merkle_root': '\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00',
+ 'timestamp': 1468595301000,
+ 'nonce': 2083236893,
+ 'index': 0,
+ 'primary_index': 0,
+ 'next_consensus': b'\\xa6\\xea\\xb0\\xae\\xaf\\xb4\\x96\\xa1\\x1b\\xb0|\\x88\\x17\\xcar\\xa5J\\x00\\x12\\x04',
+ 'transaction_count': 0,
+ }
+
+ >>> get_block(9999999) # block doesn't exist
+ None
+
+ >>> get_block(UInt256(bytes(32))) # block doesn't exist
+ None
+
:param index_or_hash: index or hash identifier of the block
:type index_or_hash: int or UInt256
:return: the desired block, if exists. None otherwise
@@ -54,6 +105,21 @@ def get_transaction(hash_: UInt256) -> Transaction:
"""
Gets a transaction with the given hash.
+ >>> get_transaction(UInt256(b'\\xff\\x7f\\x18\\x99\\x8c\\x1d\\x10X{bA\\xc2\\xe3\\xdf\\xc8\\xb0\\x9f>\\xd0\\xd2G\\xe3\\xba\\xd8\\x96\\xb9\\x0e\\xc1iS\\xcdr'))
+ {
+ 'hash': b'\\xff\\x7f\\x18\\x99\\x8c\\x1d\\x10X{bA\\xc2\\xe3\\xdf\\xc8\\xb0\\x9f>\\xd0\\xd2G\\xe3\\xba\\xd8\\x96\\xb9\\x0e\\xc1iS\\xcdr',
+ 'version': 0,
+ 'nonce': 2025056010,
+ 'sender': b'\\xa6\\xea\\xb0\\xae\\xaf\\xb4\\x96\\xa1\\x1b\\xb0|\\x88\\x17\\xcar\\xa5J\\x00\\x12\\x04',
+ 'system_fee': 2028330,
+ 'network_fee': 1206580,
+ 'valid_until_block': 5761,
+ 'script': b'\\x0c\\x14\\xa6\\xea\\xb0\\xae\\xaf\\xb4\\x96\\xa1\\x1b\\xb0|\\x88\\x17\\xcar\\xa5J\\x00\\x12\\x04\\x11\\xc0\\x1f\\x0c\\tbalanceOf\\x0c\\x14\\xcfv\\xe2\\x8b\\xd0\\x06,JG\\x8e\\xe3Ua\\x01\\x13\\x19\\xf3\\xcf\\xa4\\xd2Ab}[R',
+ }
+
+ >>> get_transaction(UInt256(bytes(32))) # transaction doesn't exist
+ None
+
:param hash_: hash identifier of the transaction
:type hash_: UInt256
:return: the Transaction, if exists. None otherwise
@@ -65,6 +131,30 @@ def get_transaction_from_block(block_hash_or_height: Union[UInt256, int], tx_ind
"""
Gets a transaction from a block.
+ >>> get_transaction_from_block(1, 0)
+ {
+ 'hash': b'\\xff\\x7f\\x18\\x99\\x8c\\x1d\\x10X{bA\\xc2\\xe3\\xdf\\xc8\\xb0\\x9f>\\xd0\\xd2G\\xe3\\xba\\xd8\\x96\\xb9\\x0e\\xc1iS\\xcdr',
+ 'version': 0,
+ 'nonce': 2025056010,
+ 'sender': b'\\xa6\\xea\\xb0\\xae\\xaf\\xb4\\x96\\xa1\\x1b\\xb0|\\x88\\x17\\xcar\\xa5J\\x00\\x12\\x04',
+ 'system_fee': 2028330,
+ 'network_fee': 1206580,
+ 'valid_until_block': 5761,
+ 'script': b'\\x0c\\x14\\xa6\\xea\\xb0\\xae\\xaf\\xb4\\x96\\xa1\\x1b\\xb0|\\x88\\x17\\xcar\\xa5J\\x00\\x12\\x04\\x11\\xc0\\x1f\\x0c\\tbalanceOf\\x0c\\x14\\xcfv\\xe2\\x8b\\xd0\\x06,JG\\x8e\\xe3Ua\\x01\\x13\\x19\\xf3\\xcf\\xa4\\xd2Ab}[R',
+ }
+
+ >>> get_transaction_from_block(UInt256(b'\\x29\\x41\\x06\\xdb\\x4c\\xf3\\x84\\xa7\\x20\\x4d\\xba\\x0a\\x04\\x03\\x72\\xb3\\x27\\x76\\xf2\\x6e\\xd3\\x87\\x49\\x88\\xd0\\x3e\\xff\\x5d\\xa9\\x93\\x8c\\xa3'), 0)
+ {
+ 'hash': b'\\xff\\x7f\\x18\\x99\\x8c\\x1d\\x10X{bA\\xc2\\xe3\\xdf\\xc8\\xb0\\x9f>\\xd0\\xd2G\\xe3\\xba\\xd8\\x96\\xb9\\x0e\\xc1iS\\xcdr',
+ 'version': 0,
+ 'nonce': 2025056010,
+ 'sender': b'\\xa6\\xea\\xb0\\xae\\xaf\\xb4\\x96\\xa1\\x1b\\xb0|\\x88\\x17\\xcar\\xa5J\\x00\\x12\\x04',
+ 'system_fee': 2028330,
+ 'network_fee': 1206580,
+ 'valid_until_block': 5761,
+ 'script': b'\\x0c\\x14\\xa6\\xea\\xb0\\xae\\xaf\\xb4\\x96\\xa1\\x1b\\xb0|\\x88\\x17\\xcar\\xa5J\\x00\\x12\\x04\\x11\\xc0\\x1f\\x0c\\tbalanceOf\\x0c\\x14\\xcfv\\xe2\\x8b\\xd0\\x06,JG\\x8e\\xe3Ua\\x01\\x13\\x19\\xf3\\xcf\\xa4\\xd2Ab}[R',
+ }
+
:param block_hash_or_height: a block identifier
:type block_hash_or_height: UInt256 or int
:param tx_index: the transaction identifier in the block
@@ -78,6 +168,15 @@ def get_transaction_height(hash_: UInt256) -> int:
"""
Gets the height of a transaction.
+ >>> get_transaction_height(UInt256(b'\\x28\\x89\\x4f\\xb6\\x10\\x62\\x9d\\xea\\x4c\\xcd\\x00\\x2e\\x9e\\x11\\xa6\\xd0\\x3d\\x28\\x90\\xc0\\xe5\\xd4\\xfc\\x8f\\xc6\\x4f\\xcc\\x32\\x53\\xb5\\x48\\x01'))
+ 2108703
+
+ >>> get_transaction_height(UInt256(b'\\x29\\x41\\x06\\xdb\\x4c\\xf3\\x84\\xa7\\x20\\x4d\\xba\\x0a\\x04\\x03\\x72\\xb3\\x27\\x76\\xf2\\x6e\\xd3\\x87\\x49\\x88\\xd0\\x3e\\xff\\x5d\\xa9\\x93\\x8c\\xa3'))
+ 10
+
+ >>> get_transaction_height(UInt256(bytes(32))) # transaction doesn't exist
+ -1
+
:param hash_: hash identifier of the transaction
:type hash_: UInt256
:return: height of the transaction
@@ -89,6 +188,17 @@ def get_transaction_signers(hash_: UInt256) -> List[Signer]:
"""
Gets the VM state of a transaction.
+ >>> get_transaction_signers(UInt256(b'\\x29\\x41\\x06\\xdb\\x4c\\xf3\\x84\\xa7\\x20\\x4d\\xba\\x0a\\x04\\x03\\x72\\xb3\\x27\\x76\\xf2\\x6e\\xd3\\x87\\x49\\x88\\xd0\\x3e\\xff\\x5d\\xa9\\x93\\x8c\\xa3'))
+ [
+ {
+ "account": b'\\xa6\\xea\\xb0\\xae\\xaf\\xb4\\x96\\xa1\\x1b\\xb0|\\x88\\x17\\xcar\\xa5J\\x00\\x12\\x04',
+ "scopes": 1,
+ "allowed_contracts": [],
+ "allowed_groups": [],
+ "rules": [],
+ },
+ ]
+
:param hash_: hash identifier of the transaction
:type hash_: UInt256
:return: VM state of the transaction
@@ -100,6 +210,9 @@ def get_transaction_vm_state(hash_: UInt256) -> VMState:
"""
Gets the VM state of a transaction.
+ >>> get_transaction_vm_state(UInt256(b'\\x29\\x41\\x06\\xdb\\x4c\\xf3\\x84\\xa7\\x20\\x4d\\xba\\x0a\\x04\\x03\\x72\\xb3\\x27\\x76\\xf2\\x6e\\xd3\\x87\\x49\\x88\\xd0\\x3e\\xff\\x5d\\xa9\\x93\\x8c\\xa3'))
+ VMState.HALT
+
:param hash_: hash identifier of the transaction
:type hash_: UInt256
:return: VM state of the transaction
@@ -111,6 +224,9 @@ def get_transaction_vm_state(hash_: UInt256) -> VMState:
"""
Gets the hash of the current block.
+>>> current_hash
+b'\\x3e\\x65\\xe5\\x4d\\x75\\x5a\\x94\\x90\\xd6\\x98\\x3a\\x77\\xe4\\x82\\xaf\\x7a\\x38\\xc9\\x8c\\x1a\\xc6\\xd9\\xda\\x48\\xbd\\x7c\\x22\\xb3\\x2a\\x9e\\x34\\xea'
+
:meta hide-value:
"""
@@ -118,5 +234,14 @@ def get_transaction_vm_state(hash_: UInt256) -> VMState:
"""
Gets the index of the current block.
+>>> current_index
+10908937
+
+>>> current_index
+2108690
+
+>>> current_index
+3529755
+
:meta hide-value:
"""
diff --git a/boa3/builtin/interop/contract/__init__.py b/boa3/builtin/interop/contract/__init__.py
index daf82a857..f33383ae2 100644
--- a/boa3/builtin/interop/contract/__init__.py
+++ b/boa3/builtin/interop/contract/__init__.py
@@ -2,6 +2,14 @@
'CallFlags',
'Contract',
'ContractManifest',
+ 'ContractPermission',
+ 'ContractPermissionDescriptor',
+ 'ContractGroup',
+ 'ContractAbi',
+ 'ContractMethodDescriptor',
+ 'ContractEventDescriptor',
+ 'ContractParameterDefinition',
+ 'ContractParameterType',
'call_contract',
'create_contract',
'update_contract',
@@ -19,7 +27,8 @@
from boa3.builtin.interop.contract.callflagstype import CallFlags
from boa3.builtin.interop.contract.contract import Contract
-from boa3.builtin.interop.contract.contractmanifest import ContractManifest
+from boa3.builtin.interop.contract.contractmanifest import ContractManifest, ContractPermission, ContractPermissionDescriptor, \
+ ContractGroup, ContractAbi, ContractMethodDescriptor, ContractEventDescriptor, ContractParameterDefinition, ContractParameterType
from boa3.builtin.type import ECPoint, UInt160
@@ -27,6 +36,9 @@ def call_contract(script_hash: UInt160, method: str, args: Sequence = (), call_f
"""
Calls a smart contract given the method and the arguments.
+ >>> call_contract(NEO, 'balanceOf', UInt160(b'\\xcfv\\xe2\\x8b\\xd0\\x06,JG\\x8e\\xe3Ua\\x01\\x13\\x19\\xf3\\xcf\\xa4\\xd2'))
+ 100
+
:param script_hash: the target smart contract's script hash
:type script_hash: UInt160
:param method: the name of the method to be executed
@@ -49,6 +61,24 @@ def create_contract(nef_file: bytes, manifest: bytes, data: Any = None) -> Contr
"""
Creates a smart contract given the script and the manifest.
+ >>> nef_file_ = get_script(); manifest_ = get_manifest() # get the script and manifest somehow
+ ... create_contract(nef_file_, manifest_, None) # smart contract will be deployed
+ {
+ 'id': 2,
+ 'update_counter': 0,
+ 'hash': b'\\x92\\x8f+1q\\x86z_@\\x94\\xf5pE\\xcb\\xb8 \\x0f\\\\`Z',
+ 'nef': b'NEF3neo3-boa by COZ-_unit_tests_\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x07W\\x00\\x02xy\\x9e@\\xf9\\7b\\xbb\\xcc',
+ 'manifest': {
+ 'name': 'TestContract',
+ 'group': [],
+ 'supported_standards': [],
+ 'abi': [[['test', [['a', 17], ['b', 17]], 17, 0, False]], []],
+ 'permissions': [],
+ 'trusts': [],
+ 'extras': 'null'
+ },
+ }
+
:param nef_file: the target smart contract's compiled nef
:type nef_file: bytes
:param manifest: the manifest.json that describes how the script should behave
@@ -68,6 +98,10 @@ def update_contract(nef_file: bytes, manifest: bytes, data: Any = None):
"""
Updates the executing smart contract given the script and the manifest.
+ >>> nef_file_ = get_script(); manifest_ = get_manifest() # get the script and manifest somehow
+ ... update_contract(nef_file_, manifest_, None) # smart contract will be updated
+ None
+
:param nef_file: the new smart contract's compiled nef
:type nef_file: bytes
:param manifest: the new smart contract's manifest
@@ -84,6 +118,10 @@ def update_contract(nef_file: bytes, manifest: bytes, data: Any = None):
def destroy_contract():
"""
Destroy the executing smart contract.
+
+ >>> destroy_contract()
+ None
+
"""
pass
@@ -92,6 +130,9 @@ def get_minimum_deployment_fee() -> int:
"""
Gets the minimum fee of contract deployment.
+ >>> get_minimum_deployment_fee()
+ 1000000000
+
:return: the minimum fee of contract deployment
"""
pass
@@ -100,6 +141,9 @@ def get_minimum_deployment_fee() -> int:
def get_call_flags() -> CallFlags:
"""
Gets the CallFlags in the current context.
+
+ >>> get_call_flags()
+ CallFlags.READ_ONLY
"""
pass
@@ -108,6 +152,9 @@ def create_standard_account(pub_key: ECPoint) -> UInt160:
"""
Calculates the script hash from a public key.
+ >>> create_standard_account(ECPoint(b'\\x03\\x5a\\x92\\x8f\\x20\\x16\\x39\\x20\\x4e\\x06\\xb4\\x36\\x8b\\x1a\\x93\\x36\\x54\\x62\\xa8\\xeb\\xbf\\xf0\\xb8\\x81\\x81\\x51\\xb7\\x4f\\xaa\\xb3\\xa2\\xb6\\x1a'))
+ b'\\r\\xa9g\\xa4\\x00C+\\xf2\\x7f\\x8e\\x8e\\xb4o\\xe8\\xace\\x9e\\xcc\\xde\\x04'
+
:param pub_key: the given public key
:type pub_key: ECPoint
@@ -121,6 +168,9 @@ def create_multisig_account(m: int, pub_keys: List[ECPoint]) -> UInt160:
"""
Calculates corresponding multisig account script hash for the given public keys.
+ >>> create_multisig_account(1, [ECPoint(b'\\x03\\x5a\\x92\\x8f\\x20\\x16\\x39\\x20\\x4e\\x06\\xb4\\x36\\x8b\\x1a\\x93\\x36\\x54\\x62\\xa8\\xeb\\xbf\\xf0\\xb8\\x81\\x81\\x51\\xb7\\x4f\\xaa\\xb3\\xa2\\xb6\\x1a')])
+ b'"5,\\xd2\\x9e\\xe7\\xb4\\x02\\x08b\\xdbd\\x1e\\xedx\\x82\\x8fU(m'
+
:param m: the minimum number of correct signatures need to be provided in order for the verification to pass.
:type m: int
:param pub_keys: the public keys of the account
@@ -136,6 +186,9 @@ def create_multisig_account(m: int, pub_keys: List[ECPoint]) -> UInt160:
"""
NEO's token script hash.
+>>> NEO
+b'\\xf5c\\xea@\\xbc(=M\\x0e\\x05\\xc4\\x8e\\xa3\\x05\\xb3\\xf2\\xa0s@\\xef'
+
:meta hide-value:
"""
@@ -143,5 +196,8 @@ def create_multisig_account(m: int, pub_keys: List[ECPoint]) -> UInt160:
"""
GAS' token script hash.
+>>> GAS
+b'\\xcfv\\xe2\\x8b\\xd0\\x06,JG\\x8e\\xe3Ua\\x01\\x13\\x19\\xf3\\xcf\\xa4\\xd2'
+
:meta hide-value:
"""
diff --git a/boa3/builtin/interop/crypto/__init__.py b/boa3/builtin/interop/crypto/__init__.py
index db339357f..6816354b1 100644
--- a/boa3/builtin/interop/crypto/__init__.py
+++ b/boa3/builtin/interop/crypto/__init__.py
@@ -27,6 +27,12 @@ def sha256(key: Any) -> bytes:
"""
Encrypts a key using SHA-256.
+ >>> sha256('unit test')
+ b'\\xdau1>J\\xc2W\\xf8LN\\xfb2\\x0f\\xbd\\x01\\x1cr@<\\xf5\\x93<\\x90\\xd2\\xe3\\xb8$\\xd6H\\x96\\xf8\\x9a'
+
+ >>> sha256(10)
+ b'\\x9c\\x82r\\x01\\xb9@\\x19\\xb4/\\x85pk\\xc4\\x9cY\\xff\\x84\\xb5`M\\x11\\xca\\xaf\\xb9\\n\\xb9HV\\xc4\\xe1\\xddz'
+
:param key: the key to be encrypted
:type key: Any
:return: a byte value that represents the encrypted key
@@ -39,6 +45,12 @@ def ripemd160(key: Any) -> bytes:
"""
Encrypts a key using RIPEMD-160.
+ >>> ripemd160('unit test')
+ b'H\\x8e\\xef\\xf4Zh\\x89:\\xe6\\xf1\\xdc\\x08\\xdd\\x8f\\x01\\rD\\n\\xbdH'
+
+ >>> ripemd160(10)
+ b'\\xc0\\xda\\x02P8\\xed\\x83\\xc6\\x87\\xdd\\xc40\\xda\\x98F\\xec\\xb9\\x7f9\\x98'
+
:param key: the key to be encrypted
:type key: Any
:return: a byte value that represents the encrypted key
@@ -51,6 +63,12 @@ def hash160(key: Any) -> bytes:
"""
Encrypts a key using HASH160.
+ >>> hash160('unit test')
+ b'#Q\\xc9\\xaf+c\\x12\\xb1\\xb9\\x9e\\xa1\\x89t\\xa228g\\xec\\x0eF'
+
+ >>> hash160(10)
+ b'\\x89\\x86D\\x19\\xa8\\xc3v%\\x00\\xfe\\x9a\\x98\\xaf\\x8f\\xbbO3u\\x08\\xf0'
+
:param key: the key to be encrypted
:type key: Any
:return: a byte value that represents the encrypted key
@@ -63,6 +81,12 @@ def hash256(key: Any) -> bytes:
"""
Encrypts a key using HASH256.
+ >>> hash256('unit test')
+ b'\\xdau1>J\\xc2W\\xf8LN\\xfb2\\x0f\\xbd\\x01\\x1cr@<\\xf5\\x93<\\x90\\xd2\\xe3\\xb8$\\xd6H\\x96\\xf8\\x9a'
+
+ >>> hash256(10)
+ b'\\x9c\\x82r\\x01\\xb9@\\x19\\xb4/\\x85pk\\xc4\\x9cY\\xff\\x84\\xb5`M\\x11\\xca\\xaf\\xb9\\n\\xb9HV\\xc4\\xe1\\xddz'
+
:param key: the key to be encrypted
:type key: Any
:return: a byte value that represents the encrypted key
@@ -75,6 +99,10 @@ def check_sig(pub_key: ECPoint, signature: bytes) -> bool:
"""
Checks the signature for the current script container.
+ >>> check_sig(ECPoint(b'\\x03\\x5a\\x92\\x8f\\x20\\x16\\x39\\x20\\x4e\\x06\\xb4\\x36\\x8b\\x1a\\x93\\x36\\x54\\x62\\xa8\\xeb\\xbf\\xf0\\xb8\\x81\\x81\\x51\\xb7\\x4f\\xaa\\xb3\\xa2\\xb6\\x1a'),
+ ... b'wrongsignature')
+ False
+
:param pub_key: the public key of the account
:type pub_key: ECPoint
:param signature: the signature of the current script container
@@ -89,6 +117,11 @@ def check_multisig(pubkeys: List[ECPoint], signatures: List[bytes]) -> bool:
"""
Checks the signatures for the current script container.
+ >>> check_multisig([ECPoint(b"\\x03\\xcd\\xb0\\x67\\xd9\\x30\\xfd\\x5a\\xda\\xa6\\xc6\\x85\\x45\\x01\\x60\\x44\\xaa\\xdd\\xec\\x64\\xba\\x39\\xe5\\x48\\x25\\x0e\\xae\\xa5\\x51\\x17\\x2e\\x53\\x5c"),
+ ... ECPoint(b"\\x03l\\x841\\xccx\\xb31w\\xa6\\x0bK\\xcc\\x02\\xba\\xf6\\r\\x05\\xfe\\xe5\\x03\\x8es9\\xd3\\xa6\\x88\\xe3\\x94\\xc2\\xcb\\xd8C")],
+ ... [b'wrongsignature1', b'wrongsignature2'])
+ False
+
:param pubkeys: a list of public keys
:type pubkeys: List[ECPoint]
:param signatures: a list of signatures
@@ -103,6 +136,10 @@ def verify_with_ecdsa(message: ByteString, pubkey: ECPoint, signature: ByteStrin
"""
Using the elliptic curve, it checks if the signature of the message was originally produced by the public key.
+ >>> verify_with_ecdsa('unit test', ECPoint(b'\\x03\\x5a\\x92\\x8f\\x20\\x16\\x39\\x20\\x4e\\x06\\xb4\\x36\\x8b\\x1a\\x93\\x36\\x54\\x62\\xa8\\xeb\\xbf\\xf0\\xb8\\x81\\x81\\x51\\xb7\\x4f\\xaa\\xb3\\xa2\\xb6\\x1a'),
+ ... b'wrong_signature', NamedCurve.SECP256R1)
+ False
+
:param message: the encrypted message
:type message: bytes
:param pubkey: the public key that might have created the item
@@ -121,6 +158,9 @@ def murmur32(data: ByteString, seed: int) -> ByteString:
"""
Computes the hash value for the specified byte array using the murmur32 algorithm.
+ >>> murmur32('unit test', 0)
+ b"\\x90D'G"
+
:param data: the input to compute the hash code for
:type data: ByteString
:param seed: the seed of the murmur32 hash function
diff --git a/boa3/builtin/interop/iterator/__init__.py b/boa3/builtin/interop/iterator/__init__.py
index cdecd2216..df3cc3b1a 100644
--- a/boa3/builtin/interop/iterator/__init__.py
+++ b/boa3/builtin/interop/iterator/__init__.py
@@ -27,6 +27,11 @@ def next(self) -> bool:
"""
Advances the iterator to the next element of the collection.
+ >>> from boa3.builtin.interop import storage
+ ... iterator = storage.find('prefix')
+ ... iterator.next()
+ True
+
:return: true if it advanced, false if there isn't a next element
:rtype: bool
"""
diff --git a/boa3/builtin/interop/json/__init__.py b/boa3/builtin/interop/json/__init__.py
index 6fe41e252..2accbe200 100644
--- a/boa3/builtin/interop/json/__init__.py
+++ b/boa3/builtin/interop/json/__init__.py
@@ -11,6 +11,9 @@ def json_serialize(item: Any) -> str:
"""
Serializes an item into a json.
+ >>> json_serialize({'one': 1, 'two': 2, 'three': 3})
+ '{"one":1,"two":2,"three":3}'
+
:param item: The item that will be serialized
:type item: Any
:return: The serialized item
@@ -26,6 +29,9 @@ def json_deserialize(json: str) -> Any:
"""
Deserializes a json into some valid type.
+ >>> json_deserialize('{"one":1,"two":2,"three":3}')
+ {'one': 1, 'three': 3, 'two': 2}
+
:param json: A json that will be deserialized
:type json: str
:return: The deserialized json
diff --git a/boa3/builtin/interop/policy/__init__.py b/boa3/builtin/interop/policy/__init__.py
index f246b6207..f5b9b60bc 100644
--- a/boa3/builtin/interop/policy/__init__.py
+++ b/boa3/builtin/interop/policy/__init__.py
@@ -14,6 +14,9 @@ def get_exec_fee_factor() -> int:
Gets the execution fee factor. This is a multiplier that can be adjusted by the committee to adjust the system fees
for transactions.
+ >>> get_exec_fee_factor()
+ 30
+
:return: the execution fee factor
:rtype: int
"""
@@ -24,6 +27,9 @@ def get_fee_per_byte() -> int:
"""
Gets the network fee per transaction byte.
+ >>> get_fee_per_byte()
+ 1000
+
:return: the network fee per transaction byte
:rtype: int
"""
@@ -34,6 +40,9 @@ def get_storage_price() -> int:
"""
Gets the storage price.
+ >>> get_storage_price()
+ 100000
+
:return: the snapshot used to read data
:rtype: int
"""
@@ -44,6 +53,9 @@ def is_blocked(account: UInt160) -> bool:
"""
Determines whether the specified account is blocked.
+ >>> is_blocked(UInt160(b'\\xcfv\\xe2\\x8b\\xd0\\x06,JG\\x8e\\xe3Ua\\x01\\x13\\x19\\xf3\\xcf\\xa4\\xd2'))
+ False
+
:param account: the account to be checked
:type account: UInt160
diff --git a/boa3/builtin/interop/role/__init__.py b/boa3/builtin/interop/role/__init__.py
index a4bfa7262..850fd14dd 100644
--- a/boa3/builtin/interop/role/__init__.py
+++ b/boa3/builtin/interop/role/__init__.py
@@ -12,6 +12,9 @@ def get_designated_by_role(role: Role, index: int) -> ECPoint:
"""
Gets the list of nodes for the specified role.
+ >>> get_designated_by_role(Role.ORACLE, 0)
+ []
+
:param role: the type of the role
:type role: Role
:param index: the index of the block to be queried
diff --git a/boa3/builtin/interop/runtime/__init__.py b/boa3/builtin/interop/runtime/__init__.py
index f0c7eea11..43282ed7c 100644
--- a/boa3/builtin/interop/runtime/__init__.py
+++ b/boa3/builtin/interop/runtime/__init__.py
@@ -34,6 +34,12 @@ def check_witness(hash_or_pubkey: Union[UInt160, ECPoint]) -> bool:
"""
Verifies that the transactions or block of the calling contract has validated the required script hash.
+ >>> check_witness(calling_script_hash)
+ True
+
+ >>> check_witness(UInt160(bytes(20)))
+ False
+
:param hash_or_pubkey: script hash or public key to validate
:type hash_or_pubkey: UInt160 or ECPoint
:return: a boolean value that represents whether the script hash was verified
@@ -46,6 +52,14 @@ def notify(state: Any, notification_name: str = None):
"""
Notifies the client from the executing smart contract.
+ >>> var = 10
+ ... notify(var) # An event will be triggered
+ None
+
+ >>> var = 10
+ ... notify(var, 'custom event name') # An event will be triggered
+ None
+
:param state: the notification message
:type state: Any
:param notification_name: name that'll be linked to the notification
@@ -58,6 +72,9 @@ def log(message: str):
"""
Show log messages to the client from the executing smart contract.
+ >>> log('log sent') # An event that can be shown on the CLI
+ log sent
+
:param message: the log message
:type message: str
"""
@@ -68,6 +85,9 @@ def get_trigger() -> TriggerType:
"""
Return the smart contract trigger type.
+ >>> get_trigger()
+ TriggerType.APPLICATION
+
:return: a value that represents the contract trigger type
:rtype: TriggerType
"""
@@ -78,6 +98,26 @@ def get_notifications(script_hash: UInt160 = UInt160()) -> List[Notification]:
"""
This method gets current invocation notifications from specific 'script_hash'.
+ >>> notify(1); notify(2); notify(3)
+ ... get_notifications(UInt160(b'\\xcfv\\xe2\\x8b\\xd0\\x06,JG\\x8e\\xe3Ua\\x01\\x13\\x19\\xf3\\xcf\\xa4\\xd2'))
+ [
+ [
+ b'8\\xfe\\x11\\n\\xff7J\\xb8}\\xe9x6@\\xea\\x0b\\x00\\xf1|\\x82v',
+ 'notify',
+ [1]
+ ],
+ [
+ b'8\\xfe\\x11\\n\\xff7J\\xb8}\\xe9x6@\\xea\\x0b\\x00\\xf1|\\x82v',
+ 'notify',
+ [2]
+ ],
+ [
+ b'8\\xfe\\x11\\n\\xff7J\\xb8}\\xe9x6@\\xea\\x0b\\x00\\xf1|\\x82v',
+ 'notify',
+ [3]
+ ]
+ ]
+
:param script_hash: must have 20 bytes, but if it's all zero 0000...0000 it refers to all existing notifications
(like a * wildcard)
:type script_hash: UInt160
@@ -91,6 +131,9 @@ def get_network() -> int:
"""
Gets the magic number of the current network.
+ >>> get_network()
+ 860243278
+
:return: the magic number of the current network
:rtype: int
"""
@@ -101,6 +144,9 @@ def burn_gas(gas: int):
"""
Burns GAS to benefit the NEO ecosystem.
+ >>> burn_gas(1000)
+ None
+
:param gas: the amount of GAS that will be burned
:type gas: int
@@ -113,6 +159,15 @@ def get_random() -> int:
"""
Gets the next random number.
+ >>> get_random()
+ 191320526825634396960813166838892720709
+
+ >>> get_random()
+ 99083669484001682562631729023191545809
+
+ >>> get_random()
+ 328056213623902365838800581788496514419
+
:return: the next random number
:rtype: int
"""
@@ -122,6 +177,12 @@ def get_random() -> int:
def load_script(script: ByteString, args: Sequence = (), flags: CallFlags = CallFlags.NONE) -> Any:
"""
Loads a script at runtime.
+
+ >>> from typing import cast
+ ... from boa3.builtin.vm import Opcode
+ ... cast(int, load_script(Opcode.ADD, [10, 2]))
+ 12
+
"""
pass
@@ -130,6 +191,9 @@ def load_script(script: ByteString, args: Sequence = (), flags: CallFlags = Call
"""
Gets the address version of the current network.
+>>> address_version
+53
+
:meta hide-value:
"""
@@ -138,6 +202,9 @@ def load_script(script: ByteString, args: Sequence = (), flags: CallFlags = Call
"""
Gets the script hash of the current context.
+>>> executing_script_hash
+b'^b]\\x90#\\xbf\\xcc\\x1f\\xd8\\x9e\\xe3\\xa4zd\\x14\\xa4\\xf0\\x96\\x9f`'
+
:meta hide-value:
"""
@@ -145,6 +212,9 @@ def load_script(script: ByteString, args: Sequence = (), flags: CallFlags = Call
"""
Gets the script hash of the calling contract.
+>>> calling_script_hash
+b'\\x05\\x7f\\xc2\\x9d\\xba\\xb2\\xc1x\\xf5\\x81\\x83\\xbf\\xcb\\x87/\\xc3!\\xca\\xe1\\xd0'
+
:meta hide-value:
"""
@@ -152,6 +222,9 @@ def load_script(script: ByteString, args: Sequence = (), flags: CallFlags = Call
"""
Gets the timestamp of the current block.
+>>> time
+1685395697108
+
:meta hide-value:
"""
@@ -159,6 +232,9 @@ def load_script(script: ByteString, args: Sequence = (), flags: CallFlags = Call
"""
Gets the remaining GAS that can be spent in order to complete the execution.
+>>> gas_left
+1999015490
+
:meta hide-value:
"""
@@ -166,6 +242,9 @@ def load_script(script: ByteString, args: Sequence = (), flags: CallFlags = Call
"""
Gets the name of the current platform.
+>>> platform
+'NEO'
+
:meta hide-value:
"""
@@ -173,6 +252,9 @@ def load_script(script: ByteString, args: Sequence = (), flags: CallFlags = Call
"""
Gets the number of times the current contract has been called during the execution.
+>>> invocation_counter
+1
+
:meta hide-value:
"""
@@ -180,6 +262,9 @@ def load_script(script: ByteString, args: Sequence = (), flags: CallFlags = Call
"""
Gets the script hash of the entry context.
+>>> entry_script_hash
+b'\\tK\\xb31\\xa8\\x13\\x80`\\xad\\xf6\\xda\\xdf\\xc6R\\x9b\\xfdB\\xbf\\x83\\x8f'
+
:meta hide-value:
"""
@@ -187,5 +272,31 @@ def load_script(script: ByteString, args: Sequence = (), flags: CallFlags = Call
"""
Gets the current script container.
+>>> script_container
+[
+ b'\\xf1y\\xc2\\xd6\\x1c\\xb6\\x98\\xa4\\xdc\\xf3\\xd67s\\xd7E\\xf0<;\\x98+\\xa2T\\x03P,T\\xe8\\xc6{ \\x101',
+ 0,
+ 442907905,
+ '\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00',
+ 0,
+ 0,
+ 0,
+ ''
+]
+
+>>> script_container
+[
+ b"S{\\xed'\\x85&\\xf5\\x93U=\\xc1\\xbf'\\x95\\xc4/\\x80X\\xdb\\xd5\\xa1-\\x97q\\x85\\xe3I\\xe5\\x99cd\\x04",
+ 0,
+ '\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00',
+ '\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00',
+ 1468595301000,
+ 2083236893,
+ 0,
+ 0,
+ b'\\xa6\\xea\\xb0\\xae\\xaf\\xb4\\x96\\xa1\\x1b\\xb0|\\x88\\x17\\xcar\\xa5J\\x00\\x12\\x04',
+ 0,
+]
+
:meta hide-value:
"""
diff --git a/boa3/builtin/interop/stdlib/__init__.py b/boa3/builtin/interop/stdlib/__init__.py
index ed358f431..ce21ef74f 100644
--- a/boa3/builtin/interop/stdlib/__init__.py
+++ b/boa3/builtin/interop/stdlib/__init__.py
@@ -23,6 +23,9 @@ def base58_encode(key: bytes) -> str:
"""
Encodes a bytes value using base58.
+ >>> base58_encode(b'unit test')
+ b"2VhL46g69A1mu"
+
:param key: bytes value to be encoded
:type key: bytes
:return: the encoded string
@@ -35,6 +38,9 @@ def base58_decode(key: str) -> bytes:
"""
Decodes a string value encoded with base58.
+ >>> base58_decode('2VhL46g69A1mu')
+ b"unit test"
+
:param key: string value to be decoded
:type key: str
:return: the decoded bytes
@@ -48,6 +54,9 @@ def base58_check_encode(key: bytes) -> str:
Converts a bytes value to its equivalent str representation that is encoded with base-58 digits. The encoded str
contains the checksum of the binary data.
+ >>> base58_check_encode(b'unit test')
+ b"AnJcKqvgBwKxsjX75o"
+
:param key: bytes value to be encoded
:type key: bytes
:return: the encoded string
@@ -61,6 +70,9 @@ def base58_check_decode(key: str) -> bytes:
Converts the specified str, which encodes binary data as base-58 digits, to an equivalent bytes value. The encoded
str contains the checksum of the binary data.
+ >>> base58_check_decode('AnJcKqvgBwKxsjX75o')
+ b"unit test"
+
:param key: string value to be decoded
:type key: str
:return: the decoded bytes
@@ -73,6 +85,9 @@ def base64_encode(key: bytes) -> str:
"""
Encodes a bytes value using base64.
+ >>> base64_encode(b'unit test')
+ b"dW5pdCB0ZXN0"
+
:param key: bytes value to be encoded
:type key: bytes
:return: the encoded string
@@ -85,6 +100,9 @@ def base64_decode(key: str) -> bytes:
"""
Decodes a string value encoded with base64.
+ >>> base64_decode("dW5pdCB0ZXN0")
+ b"unit test"
+
:param key: string value to be decoded
:type key: str
:return: the decoded bytes
@@ -97,6 +115,18 @@ def serialize(item: Any) -> bytes:
"""
Serializes the given value into its bytes representation.
+ >>> serialize('42')
+ b'(\x0242'
+
+ >>> serialize(42)
+ b'!\x01*'
+
+ >>> serialize([2, 3, 5, 7])
+ b'@\x04!\x01\x02!\x01\x03!\x01\x05!\x01\x07'
+
+ >>> serialize({1: 1, 2: 1, 3: 2})
+ b'H\x03!\x01\x01!\x01\x01!\x01\x02!\x01\x01!\x01\x03!\x01\x02'
+
:param item: value to be serialized
:type item: Any
:return: the serialized value
@@ -111,6 +141,18 @@ def deserialize(data: bytes) -> Any:
"""
Deserializes the given bytes value.
+ >>> deserialize(b'(\x0242')
+ '42'
+
+ >>> deserialize(b'!\x01*')
+ 42
+
+ >>> deserialize(b'@\x04!\x01\x02!\x01\x03!\x01\x05!\x01\x07')
+ [2, 3, 5, 7]
+
+ >>> deserialize(b'H\x03!\x01\x01!\x01\x01!\x01\x02!\x01\x01!\x01\x03!\x01\x02')
+ {1: 1, 2: 1, 3: 2}
+
:param data: serialized value
:type data: bytes
:return: the deserialized result
@@ -125,6 +167,18 @@ def atoi(value: str, base: int = 10) -> int:
"""
Converts a character string to a specific base value, decimal or hexadecimal. The default is decimal.
+ >>> atoi('10')
+ 10
+
+ >>> atoi('123')
+ 123
+
+ >>> atoi('1f', 16)
+ 31
+
+ >>> atoi('ff', 16)
+ -1
+
:param value: the int value as a string
:type value: str
:param base: the value base
@@ -141,6 +195,18 @@ def itoa(value: int, base: int = 10) -> str:
"""
Converts the specific type of value to a decimal or hexadecimal string. The default is decimal.
+ >>> itoa(10)
+ '10'
+
+ >>> itoa(123)
+ '123'
+
+ >>> itoa(-1, 16)
+ 'f'
+
+ >>> itoa(15, 16)
+ '0f'
+
:param value: the int value
:type value: int
:param base: the value's base
@@ -155,6 +221,12 @@ def memory_search(mem: ByteString, value: ByteString, start: int = 0, backward:
"""
Searches for a given value in a given memory.
+ >>> memory_search('abcde', 'a', 0)
+ 0
+
+ >>> memory_search('abcde', 'e', 0)
+ 4
+
:param mem: the memory
:type mem: bytes or str
:param value: the value
@@ -174,6 +246,15 @@ def memory_compare(mem1: ByteString, mem2: ByteString) -> int:
"""
Compares a memory with another one.
+ >>> memory_compare('abc', 'abc')
+ 0
+
+ >>> memory_compare('ABC', 'abc')
+ -1
+
+ >>> memory_compare('abc', 'ABC')
+ 1
+
:param mem1: a memory to be compared to another one
:type mem1: bytes or str
:param mem2: a memory that will be compared with another one
diff --git a/boa3/builtin/interop/storage/__init__.py b/boa3/builtin/interop/storage/__init__.py
index ccf6d8bb4..298dcd2c4 100644
--- a/boa3/builtin/interop/storage/__init__.py
+++ b/boa3/builtin/interop/storage/__init__.py
@@ -24,6 +24,13 @@ def get(key: ByteString, context: StorageContext = None) -> bytes:
"""
Gets a value from the persistent store based on the given key.
+ >>> put('unit', 'test')
+ ... get('unit')
+ 'test'
+
+ >>> get('fake_key')
+ ''
+
:param key: value identifier in the store
:type key: str or bytes
:param context: storage context to be used
@@ -38,6 +45,9 @@ def get_context() -> StorageContext:
"""
Gets current storage context.
+ >>> get_context() # StorageContext cannot be read outside the blockchain
+ _InteropInterface
+
:return: the current storage context
:rtype: StorageContext
"""
@@ -48,6 +58,9 @@ def get_read_only_context() -> StorageContext:
"""
Gets current read only storage context.
+ >>> get_context() # StorageContext cannot be read outside the blockchain
+ _InteropInterface
+
:return: the current read only storage context
:rtype: StorageContext
"""
@@ -58,6 +71,9 @@ def put(key: ByteString, value: Union[int, ByteString], context: StorageContext
"""
Inserts a given value in the key-value format into the persistent storage.
+ >>> put('unit', 'test')
+ None
+
:param key: the identifier in the store for the new value
:type key: str or bytes
:param value: value to be stored
@@ -72,6 +88,11 @@ def delete(key: ByteString, context: StorageContext = None):
"""
Removes a given key from the persistent storage if exists.
+ >>> put('unit', 'test')
+ ... delete()
+ ... get('unit')
+ ''
+
:param key: the identifier in the store for the new value
:type key: str or bytes
:param context: storage context to be used
@@ -86,6 +107,17 @@ def find(prefix: ByteString,
"""
Searches in the storage for keys that start with the given prefix.
+ >>> put('a1', 'one')
+ ... put('a2', 'two')
+ ... put('a3', 'three')
+ ... put('b4', 'four')
+ ... findIterator = find('a')
+ ... findResults = []
+ ... while findIterator.next():
+ ... findResults.append(findIterator.value)
+ ... findResults
+ ['one', 'two', 'three']
+
:param prefix: prefix to find the storage keys
:type prefix: str or bytes
:param context: storage context to be used
diff --git a/boa3/builtin/math.py b/boa3/builtin/math.py
index 114963c3c..8f8765ccf 100644
--- a/boa3/builtin/math.py
+++ b/boa3/builtin/math.py
@@ -3,6 +3,9 @@ def ceil(x: int, decimals: int) -> int:
Return the ceiling of x given the amount of decimals.
This is the smallest integer >= x.
+ >>> ceil(12345, 3)
+ 13000
+
:param x: any integer number
:type x: int
:param decimals: number of decimals
@@ -20,6 +23,9 @@ def floor(x: int, decimals: int) -> int:
Return the floor of x given the amount of decimals.
This is the largest integer <= x.
+ >>> floor(12345, 3)
+ 12000
+
:param x: any integer number
:type x: int
:param decimals: number of decimals
@@ -36,6 +42,15 @@ def sqrt(x: int) -> int:
"""
Gets the square root of a number.
+ >>> sqrt(1)
+ 1
+
+ >>> sqrt(10)
+ 3
+
+ >>> sqrt(25)
+ 5
+
:param x: a non-negative number
:type x: int
:return: the square root of a number
diff --git a/boa3/builtin/nativecontract/contractmanagement.py b/boa3/builtin/nativecontract/contractmanagement.py
index f6e554ff4..11b03f6fe 100644
--- a/boa3/builtin/nativecontract/contractmanagement.py
+++ b/boa3/builtin/nativecontract/contractmanagement.py
@@ -22,6 +22,9 @@ def get_minimum_deployment_fee(cls) -> int:
"""
Gets the minimum fee of contract deployment.
+ >>> ContractManagement.get_minimum_deployment_fee()
+ 1000000000
+
:return: the minimum fee of contract deployment
"""
pass
@@ -31,6 +34,23 @@ def get_contract(cls, script_hash: UInt160) -> Contract:
"""
Gets a contract with a given hash.
+ >>> ContractManagement.get_contract(UInt160(b'\\xcfv\\xe2\\x8b\\xd0\\x06,JG\\x8e\\xe3Ua\\x01\\x13\\x19\\xf3\\xcf\\xa4\\xd2')) # GAS script hash
+ {
+ 'id': -6,
+ 'update_counter': 0,
+ 'hash': b'\\xcfv\\xe2\\x8b\\xd0\\x06,JG\\x8e\\xe3Ua\\x01\\x13\\x19\\xf3\\xcf\\xa4\\xd2',
+ 'nef': b'NEF3neo-core-v3.0\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00#\\x10A\\x1a\\xf7{g@\\x10A\\x1a\\xf7{g@\\x10A\\x1a\\xf7{g@\\x10A\\x1a\\xf7{g@\\x10A\\x1a\\xf7{g@QA\\xc7\\x9e',
+ 'manifest': {
+ 'name': 'GasToken',
+ 'group': [],
+ 'supported_standards': ['NEP-17'],
+ 'abi': [[['balanceOf', [['account', 20]], 17, 0, True], ['decimals', [], 17, 7, True], ['symbol', [], 19, 14, True], ['totalSupply', [], 17, 21, True], ['transfer', [['from', 20], ['to', 20], ['amount', 17], ['data', 0]], 16, 28, False]], [['Transfer', [['from', 20], ['to', 20], ['amount', 17]]]]],
+ 'permissions': [[None, None]],
+ 'trusts': [],
+ 'extras': 'null'
+ },
+ }
+
:param script_hash: a smart contract hash
:type script_hash: UInt160
:return: a contract
@@ -45,6 +65,18 @@ def has_method(cls, hash: UInt160, method: str, parameter_count: int) -> bool:
"""
Check if a method exists in a contract.
+ >>> ContractManagement.has_method(UInt160(b'\\xcfv\\xe2\\x8b\\xd0\\x06,JG\\x8e\\xe3Ua\\x01\\x13\\x19\\xf3\\xcf\\xa4\\xd2'),
+ ... 'balanceOf', 1) # GAS script hash
+ True
+
+ >>> ContractManagement.has_method(UInt160(b'\\xcfv\\xe2\\x8b\\xd0\\x06,JG\\x8e\\xe3Ua\\x01\\x13\\x19\\xf3\\xcf\\xa4\\xd2'),
+ ... 'balanceOf', 10) # GAS script hash
+ False
+
+ >>> ContractManagement.has_method(UInt160(b'\\xcfv\\xe2\\x8b\\xd0\\x06,JG\\x8e\\xe3Ua\\x01\\x13\\x19\\xf3\\xcf\\xa4\\xd2'),
+ ... 'invalid', 1) # GAS script hash
+ False
+
:param hash: The hash of the deployed contract
:type hash: UInt160
:param method: The name of the method
@@ -63,6 +95,24 @@ def deploy(cls, nef_file: bytes, manifest: bytes, data: Any = None) -> Contract:
"""
Creates a smart contract given the script and the manifest.
+ >>> nef_file_ = get_script(); manifest_ = get_manifest() # get the script and manifest somehow
+ ... ContractManagement.deploy(nef_file_, manifest_, None) # smart contract will be deployed
+ {
+ 'id': 2,
+ 'update_counter': 0,
+ 'hash': b'\\x92\\x8f+1q\\x86z_@\\x94\\xf5pE\\xcb\\xb8 \\x0f\\\\`Z',
+ 'nef': b'NEF3neo3-boa by COZ-_unit_tests_\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x07W\\x00\\x02xy\\x9e@\\xf9\\7b\\xbb\\xcc',
+ 'manifest': {
+ 'name': 'TestContract',
+ 'group': [],
+ 'supported_standards': [],
+ 'abi': [[['test', [['a', 17], ['b', 17]], 17, 0, False]], []],
+ 'permissions': [],
+ 'trusts': [],
+ 'extras': 'null'
+ },
+ }
+
:param nef_file: the target smart contract's compiled nef
:type nef_file: bytes
:param manifest: the manifest.json that describes how the script should behave
@@ -82,6 +132,10 @@ def update(cls, nef_file: bytes, manifest: bytes, data: Any = None):
"""
Updates the executing smart contract given the script and the manifest.
+ >>> nef_file_ = get_script(); manifest_ = get_manifest() # get the script and manifest somehow
+ ... ContractManagement.update(nef_file_, manifest_, None) # smart contract will be updated
+ None
+
:param nef_file: the new smart contract's compiled nef
:type nef_file: bytes
:param manifest: the new smart contract's manifest
@@ -98,5 +152,9 @@ def update(cls, nef_file: bytes, manifest: bytes, data: Any = None):
def destroy(cls):
"""
Destroy the executing smart contract.
+
+ >>> destroy_contract()
+ None
+
"""
pass
diff --git a/boa3/builtin/nativecontract/cryptolib.py b/boa3/builtin/nativecontract/cryptolib.py
index cb5fa0d7f..d8160aaae 100644
--- a/boa3/builtin/nativecontract/cryptolib.py
+++ b/boa3/builtin/nativecontract/cryptolib.py
@@ -21,6 +21,9 @@ def murmur32(cls, data: ByteString, seed: int) -> ByteString:
"""
Computes the hash value for the specified byte array using the murmur32 algorithm.
+ >>> CryptoLib.murmur32('unit test', 0)
+ b"\\x90D'G"
+
:param data: the input to compute the hash code for
:type data: ByteString
:param seed: the seed of the murmur32 hash function
@@ -35,6 +38,12 @@ def sha256(cls, key: Any) -> bytes:
"""
Encrypts a key using SHA-256.
+ >>> CryptoLib.sha256('unit test')
+ b'\\xdau1>J\\xc2W\\xf8LN\\xfb2\\x0f\\xbd\\x01\\x1cr@<\\xf5\\x93<\\x90\\xd2\\xe3\\xb8$\\xd6H\\x96\\xf8\\x9a'
+
+ >>> CryptoLib.sha256(10)
+ b'\\x9c\\x82r\\x01\\xb9@\\x19\\xb4/\\x85pk\\xc4\\x9cY\\xff\\x84\\xb5`M\\x11\\xca\\xaf\\xb9\\n\\xb9HV\\xc4\\xe1\\xddz'
+
:param key: the key to be encrypted
:type key: Any
:return: a byte value that represents the encrypted key
@@ -47,6 +56,12 @@ def ripemd160(cls, key: Any) -> bytes:
"""
Encrypts a key using RIPEMD-160.
+ >>> CryptoLib.ripemd160('unit test')
+ b'H\\x8e\\xef\\xf4Zh\\x89:\\xe6\\xf1\\xdc\\x08\\xdd\\x8f\\x01\\rD\\n\\xbdH'
+
+ >>> CryptoLib.ripemd160(10)
+ b'\\xc0\\xda\\x02P8\\xed\\x83\\xc6\\x87\\xdd\\xc40\\xda\\x98F\\xec\\xb9\\x7f9\\x98'
+
:param key: the key to be encrypted
:type key: Any
:return: a byte value that represents the encrypted key
@@ -59,6 +74,10 @@ def verify_with_ecdsa(cls, message: ByteString, pubkey: ECPoint, signature: Byte
"""
Using the elliptic curve, it checks if the signature of the message was originally produced by the public key.
+ >>> CryptoLib.verify_with_ecdsa('unit test', ECPoint(b'\\x03\\x5a\\x92\\x8f\\x20\\x16\\x39\\x20\\x4e\\x06\\xb4\\x36\\x8b\\x1a\\x93\\x36\\x54\\x62\\xa8\\xeb\\xbf\\xf0\\xb8\\x81\\x81\\x51\\xb7\\x4f\\xaa\\xb3\\xa2\\xb6\\x1a'),
+ ... b'wrong_signature', NamedCurve.SECP256R1)
+ False
+
:param message: the encrypted message
:type message: bytes
:param pubkey: the public key that might have created the item
diff --git a/boa3/builtin/nativecontract/gas.py b/boa3/builtin/nativecontract/gas.py
index f4fffce5f..279717c88 100644
--- a/boa3/builtin/nativecontract/gas.py
+++ b/boa3/builtin/nativecontract/gas.py
@@ -20,6 +20,9 @@ def symbol(cls) -> str:
"""
Gets the symbol of GAS.
+ >>> GAS.symbol()
+ 'GAS'
+
:return: the GAS string.
:rtype: str
"""
@@ -30,6 +33,9 @@ def decimals(cls) -> int:
"""
Gets the amount of decimals used by GAS.
+ >>> GAS.decimals()
+ 8
+
:return: the number 8.
:rtype: int
"""
@@ -40,6 +46,12 @@ def totalSupply(cls) -> int:
"""
Gets the total token supply deployed in the system.
+ >>> GAS.totalSupply()
+ 5199999098939320
+
+ >>> GAS.totalSupply()
+ 5522957322800300
+
:return: the total token supply deployed in the system.
:rtype: int
"""
@@ -50,6 +62,12 @@ def balanceOf(cls, account: UInt160) -> int:
"""
Get the current balance of an address.
+ >>> GAS.balanceOf(UInt160(bytes(20)))
+ 0
+
+ >>> GAS.balanceOf(UInt160(b'\\xabv\\xe2\\xcb\\xb0\\x16,vG\\x2f\\x44Va\\x10\\x14\\x19\\xf3\\xff\\xa1\\xe6'))
+ 1000000000
+
:param account: the account's address to retrieve the balance for
:type account: UInt160
:return: the account's balance
@@ -65,6 +83,21 @@ def transfer(cls, from_address: UInt160, to_address: UInt160, amount: int, data:
If the method succeeds, it will fire the `Transfer` event and must return true, even if the amount is 0,
or from and to are the same address.
+ >>> GAS.transfer(UInt160(b'\\xc9F\\x17\\xba!\\x99\\x07\\xc1\\xc5\\xd6\t#\\xe1\\x9096\\x89U\\xac\\x13'), # this script hash needs to have signed the transaction or block
+ ... UInt160(b'\\xabv\\xe2\\xcb\\xb0\\x16,vG\\x2f\\x44Va\\x10\\x14\\x19\\xf3\\xff\\xa1\\xe6'),
+ ... 10000, None)
+ True
+
+ >>> GAS.transfer(UInt160(bytes(20)),
+ ... UInt160(b'\\xabv\\xe2\\xcb\\xb0\\x16,vG\\x2f\\x44Va\\x10\\x14\\x19\\xf3\\xff\\xa1\\xe6'),
+ ... 10000, None)
+ False
+
+ >>> GAS.transfer(UInt160(b'\\xc9F\\x17\\xba!\\x99\\x07\\xc1\\xc5\\xd6\t#\\xe1\\x9096\\x89U\\xac\\x13'),
+ ... UInt160(b'\\xabv\\xe2\\xcb\\xb0\\x16,vG\\x2f\\x44Va\\x10\\x14\\x19\\xf3\\xff\\xa1\\xe6'),
+ ... -1, None)
+ False
+
:param from_address: the address to transfer from
:type from_address: UInt160
:param to_address: the address to transfer to
diff --git a/boa3/builtin/nativecontract/ledger.py b/boa3/builtin/nativecontract/ledger.py
index 0ec692979..3866338c8 100644
--- a/boa3/builtin/nativecontract/ledger.py
+++ b/boa3/builtin/nativecontract/ledger.py
@@ -21,6 +21,37 @@ def get_block(cls, index_or_hash: Union[int, UInt256]) -> Block:
"""
Gets the block with the given index or hash.
+ >>> Ledger.get_block(0) # first block
+ {
+ 'hash': b"S{\\xed'\\x85&\\xf5\\x93U=\\xc1\\xbf'\\x95\\xc4/\\x80X\\xdb\\xd5\\xa1-\\x97q\\x85\\xe3I\\xe5\\x99cd\\x04",
+ 'version': 0,
+ 'previous_hash': '\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00',
+ 'merkle_root': '\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00',
+ 'timestamp': 1468595301000,
+ 'nonce': 2083236893,
+ 'index': 0,
+ 'primary_index': 0,
+ 'next_consensus': b'\\xa6\\xea\\xb0\\xae\\xaf\\xb4\\x96\\xa1\\x1b\\xb0|\\x88\\x17\\xcar\\xa5J\\x00\\x12\\x04',
+ 'transaction_count': 0,
+ }
+
+ >>> Ledger.get_block(UInt256(b"S{\\xed'\\x85&\\xf5\\x93U=\\xc1\\xbf'\\x95\\xc4/\\x80X\\xdb\\xd5\\xa1-\\x97q\\x85\\xe3I\\xe5\\x99cd\\x04")) # first block
+ {
+ 'hash': b"S{\\xed'\\x85&\\xf5\\x93U=\\xc1\\xbf'\\x95\\xc4/\\x80X\\xdb\\xd5\\xa1-\\x97q\\x85\\xe3I\\xe5\\x99cd\\x04",
+ 'version': 0,
+ 'previous_hash': '\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00',
+ 'merkle_root': '\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00',
+ 'timestamp': 1468595301000,
+ 'nonce': 2083236893,
+ 'index': 0,
+ 'primary_index': 0,
+ 'next_consensus': b'\\xa6\\xea\\xb0\\xae\\xaf\\xb4\\x96\\xa1\\x1b\\xb0|\\x88\\x17\\xcar\\xa5J\\x00\\x12\\x04',
+ 'transaction_count': 0,
+ }
+
+ >>> Ledger.get_block(9999999) # block doesn't exist
+ None
+
:param index_or_hash: index or hash identifier of the block
:type index_or_hash: int or UInt256
:return: the desired block, if exists. None otherwise
@@ -33,6 +64,15 @@ def get_current_index(cls) -> int:
"""
Gets the index of the current block.
+ >>> Ledger.get_current_index()
+ 10908937
+
+ >>> Ledger.get_current_index()
+ 2108690
+
+ >>> Ledger.get_current_index()
+ 3529755
+
:return: the index of the current block
:rtype: int
"""
@@ -43,6 +83,21 @@ def get_transaction(cls, hash_: UInt256) -> Transaction:
"""
Gets a transaction with the given hash.
+ >>> Ledger.get_transaction(UInt256(b'\\xff\\x7f\\x18\\x99\\x8c\\x1d\\x10X{bA\\xc2\\xe3\\xdf\\xc8\\xb0\\x9f>\\xd0\\xd2G\\xe3\\xba\\xd8\\x96\\xb9\\x0e\\xc1iS\\xcdr'))
+ {
+ 'hash': b'\\xff\\x7f\\x18\\x99\\x8c\\x1d\\x10X{bA\\xc2\\xe3\\xdf\\xc8\\xb0\\x9f>\\xd0\\xd2G\\xe3\\xba\\xd8\\x96\\xb9\\x0e\\xc1iS\\xcdr',
+ 'version': 0,
+ 'nonce': 2025056010,
+ 'sender': b'\\xa6\\xea\\xb0\\xae\\xaf\\xb4\\x96\\xa1\\x1b\\xb0|\\x88\\x17\\xcar\\xa5J\\x00\\x12\\x04',
+ 'system_fee': 2028330,
+ 'network_fee': 1206580,
+ 'valid_until_block': 5761,
+ 'script': b'\\x0c\\x14\\xa6\\xea\\xb0\\xae\\xaf\\xb4\\x96\\xa1\\x1b\\xb0|\\x88\\x17\\xcar\\xa5J\\x00\\x12\\x04\\x11\\xc0\\x1f\\x0c\\tbalanceOf\\x0c\\x14\\xcfv\\xe2\\x8b\\xd0\\x06,JG\\x8e\\xe3Ua\\x01\\x13\\x19\\xf3\\xcf\\xa4\\xd2Ab}[R',
+ }
+
+ >>> Ledger.get_transaction(UInt256(bytes(32))) # transaction doesn't exist
+ None
+
:param hash_: hash identifier of the transaction
:type hash_: UInt256
:return: the Transaction, if exists. None otherwise
@@ -54,6 +109,30 @@ def get_transaction_from_block(cls, block_hash_or_height: Union[UInt256, int], t
"""
Gets a transaction from a block.
+ >>> Ledger.get_transaction_from_block(1, 0)
+ {
+ 'hash': b'\\xff\\x7f\\x18\\x99\\x8c\\x1d\\x10X{bA\\xc2\\xe3\\xdf\\xc8\\xb0\\x9f>\\xd0\\xd2G\\xe3\\xba\\xd8\\x96\\xb9\\x0e\\xc1iS\\xcdr',
+ 'version': 0,
+ 'nonce': 2025056010,
+ 'sender': b'\\xa6\\xea\\xb0\\xae\\xaf\\xb4\\x96\\xa1\\x1b\\xb0|\\x88\\x17\\xcar\\xa5J\\x00\\x12\\x04',
+ 'system_fee': 2028330,
+ 'network_fee': 1206580,
+ 'valid_until_block': 5761,
+ 'script': b'\\x0c\\x14\\xa6\\xea\\xb0\\xae\\xaf\\xb4\\x96\\xa1\\x1b\\xb0|\\x88\\x17\\xcar\\xa5J\\x00\\x12\\x04\\x11\\xc0\\x1f\\x0c\\tbalanceOf\\x0c\\x14\\xcfv\\xe2\\x8b\\xd0\\x06,JG\\x8e\\xe3Ua\\x01\\x13\\x19\\xf3\\xcf\\xa4\\xd2Ab}[R',
+ }
+
+ >>> Ledger.get_transaction_from_block(UInt256(b'\\x21|\\xc2~U\\t\\x89^\\x0c\\xc0\\xd29wl\\x0b\\xad d\\xe1\\xf5U\\xd7\\xf5B\\xa5/\\x8b\\x8f\\x8b\\x22\\x24\\x80'), 0)
+ {
+ 'hash': b'\\xff\\x7f\\x18\\x99\\x8c\\x1d\\x10X{bA\\xc2\\xe3\\xdf\\xc8\\xb0\\x9f>\\xd0\\xd2G\\xe3\\xba\\xd8\\x96\\xb9\\x0e\\xc1iS\\xcdr',
+ 'version': 0,
+ 'nonce': 2025056010,
+ 'sender': b'\\xa6\\xea\\xb0\\xae\\xaf\\xb4\\x96\\xa1\\x1b\\xb0|\\x88\\x17\\xcar\\xa5J\\x00\\x12\\x04',
+ 'system_fee': 2028330,
+ 'network_fee': 1206580,
+ 'valid_until_block': 5761,
+ 'script': b'\\x0c\\x14\\xa6\\xea\\xb0\\xae\\xaf\\xb4\\x96\\xa1\\x1b\\xb0|\\x88\\x17\\xcar\\xa5J\\x00\\x12\\x04\\x11\\xc0\\x1f\\x0c\\tbalanceOf\\x0c\\x14\\xcfv\\xe2\\x8b\\xd0\\x06,JG\\x8e\\xe3Ua\\x01\\x13\\x19\\xf3\\xcf\\xa4\\xd2Ab}[R',
+ }
+
:param block_hash_or_height: a block identifier
:type block_hash_or_height: UInt256 or int
:param tx_index: the transaction identifier in the block
@@ -67,6 +146,15 @@ def get_transaction_height(cls, hash_: UInt256) -> int:
"""
Gets the height of a transaction.
+ >>> Ledger.get_transaction_height(UInt256(b'\\x28\\x89\\x4f\\xb6\\x10\\x62\\x9d\\xea\\x4c\\xcd\\x00\\x2e\\x9e\\x11\\xa6\\xd0\\x3d\\x28\\x90\\xc0\\xe5\\xd4\\xfc\\x8f\\xc6\\x4f\\xcc\\x32\\x53\\xb5\\x48\\x01'))
+ 2108703
+
+ >>> Ledger.get_transaction_height(UInt256(b'\\x29\\x41\\x06\\xdb\\x4c\\xf3\\x84\\xa7\\x20\\x4d\\xba\\x0a\\x04\\x03\\x72\\xb3\\x27\\x76\\xf2\\x6e\\xd3\\x87\\x49\\x88\\xd0\\x3e\\xff\\x5d\\xa9\\x93\\x8c\\xa3'))
+ 10
+
+ >>> Ledger.get_transaction_height(UInt256(bytes(32))) # transaction doesn't exist
+ -1
+
:param hash_: hash identifier of the transaction
:type hash_: UInt256
:return: height of the transaction
@@ -78,6 +166,17 @@ def get_transaction_signers(cls, hash_: UInt256) -> List[Signer]:
"""
Gets the VM state of a transaction.
+ >>> Ledger.get_transaction_signers(UInt256(b'\\x29\\x41\\x06\\xdb\\x4c\\xf3\\x84\\xa7\\x20\\x4d\\xba\\x0a\\x04\\x03\\x72\\xb3\\x27\\x76\\xf2\\x6e\\xd3\\x87\\x49\\x88\\xd0\\x3e\\xff\\x5d\\xa9\\x93\\x8c\\xa3'))
+ [
+ {
+ "account": b'\\xa6\\xea\\xb0\\xae\\xaf\\xb4\\x96\\xa1\\x1b\\xb0|\\x88\\x17\\xcar\\xa5J\\x00\\x12\\x04',
+ "scopes": 1,
+ "allowed_contracts": [],
+ "allowed_groups": [],
+ "rules": [],
+ },
+ ]
+
:param hash_: hash identifier of the transaction
:type hash_: UInt256
:return: VM state of the transaction
@@ -89,6 +188,9 @@ def get_transaction_vm_state(cls, hash_: UInt256) -> VMState:
"""
Gets the VM state of a transaction.
+ >>> Ledger.get_transaction_vm_state(UInt256(b'\\x29\\x41\\x06\\xdb\\x4c\\xf3\\x84\\xa7\\x20\\x4d\\xba\\x0a\\x04\\x03\\x72\\xb3\\x27\\x76\\xf2\\x6e\\xd3\\x87\\x49\\x88\\xd0\\x3e\\xff\\x5d\\xa9\\x93\\x8c\\xa3'))
+ VMState.HALT
+
:param hash_: hash identifier of the transaction
:type hash_: UInt256
:return: VM state of the transaction
diff --git a/boa3/builtin/nativecontract/neo.py b/boa3/builtin/nativecontract/neo.py
index 6604ed663..a60295167 100644
--- a/boa3/builtin/nativecontract/neo.py
+++ b/boa3/builtin/nativecontract/neo.py
@@ -22,6 +22,9 @@ def symbol(cls) -> str:
"""
Gets the symbol of NEO.
+ >>> NEO.symbol()
+ 'NEO'
+
:return: the NEO string.
:rtype: str
"""
@@ -32,7 +35,10 @@ def decimals(cls) -> int:
"""
Gets the amount of decimals used by NEO.
- :return: the number 8.
+ >>> NEO.decimals()
+ 0
+
+ :return: the number 0.
:rtype: int
"""
pass
@@ -42,6 +48,9 @@ def totalSupply(cls) -> int:
"""
Gets the total token supply deployed in the system.
+ >>> NEO.totalSupply()
+ 100000000
+
:return: the total token supply deployed in the system.
:rtype: int
"""
@@ -52,6 +61,12 @@ def balanceOf(cls, account: UInt160) -> int:
"""
Get the current balance of an address.
+ >>> NEO.balanceOf(UInt160(bytes(20)))
+ 0
+
+ >>> NEO.balanceOf(UInt160(b'\\xabv\\xe2\\xcb\\xb0\\x16,vG\\x2f\\x44Va\\x10\\x14\\x19\\xf3\\xff\\xa1\\xe6'))
+ 100
+
:param account: the account's address to retrieve the balance for
:type account: UInt160
:return: the account's balance
@@ -67,6 +82,22 @@ def transfer(cls, from_address: UInt160, to_address: UInt160, amount: int, data:
If the method succeeds, it will fire the `Transfer` event and must return true, even if the amount is 0,
or from and to are the same address.
+ >>> NEO.transfer(UInt160(b'\\xc9F\\x17\\xba!\\x99\\x07\\xc1\\xc5\\xd6\t#\\xe1\\x9096\\x89U\\xac\\x13'), # this script hash needs to have signed the transaction or block
+ ... UInt160(b'\\xabv\\xe2\\xcb\\xb0\\x16,vG\\x2f\\x44Va\\x10\\x14\\x19\\xf3\\xff\\xa1\\xe6'),
+ ... 10, None)
+ True
+
+ >>> NEO.transfer(UInt160(bytes(20)),
+ ... UInt160(b'\\xabv\\xe2\\xcb\\xb0\\x16,vG\\x2f\\x44Va\\x10\\x14\\x19\\xf3\\xff\\xa1\\xe6'),
+ ... 10, None)
+ False
+
+ >>> NEO.transfer(UInt160(b'\\xc9F\\x17\\xba!\\x99\\x07\\xc1\\xc5\\xd6\t#\\xe1\\x9096\\x89U\\xac\\x13'),
+ ... UInt160(b'\\xabv\\xe2\\xcb\\xb0\\x16,vG\\x2f\\x44Va\\x10\\x14\\x19\\xf3\\xff\\xa1\\xe6'),
+ ... -1, None)
+ False
+
+
:param from_address: the address to transfer from
:type from_address: UInt160
:param to_address: the address to transfer to
@@ -87,6 +118,9 @@ def get_gas_per_block(cls) -> int:
"""
Gets the amount of GAS generated in each block.
+ >>> NEO.get_gas_per_block()
+ 500000000
+
:return: the amount of GAS generated
:rtype: int
"""
@@ -97,6 +131,12 @@ def unclaimed_gas(cls, account: UInt160, end: int) -> int:
"""
Gets the amount of unclaimed GAS in the specified account.
+ >>> NEO.unclaimed_gas(UInt160(b'\\xc9F\\x17\\xba!\\x99\\x07\\xc1\\xc5\\xd6\t#\\xe1\\x9096\\x89U\\xac\\x13'), 0)
+ 100000000
+
+ >>> NEO.unclaimed_gas(UInt160(bytes(20), 0)
+ 100000000
+
:param account: the account to check
:type account: UInt160
:param end: the block index used when calculating GAS
@@ -109,6 +149,9 @@ def register_candidate(cls, pubkey: ECPoint) -> bool:
"""
Registers as a candidate.
+ >>> NEO.register_candidate(ECPoint(b'\\x03\\x5a\\x92\\x8f\\x20\\x16\\x39\\x20\\x4e\\x06\\xb4\\x36\\x8b\\x1a\\x93\\x36\\x54\\x62\\xa8\\xeb\\xbf\\xf0\\xb8\\x81\\x81\\x51\\xb7\\x4f\\xaa\\xb3\\xa2\\xb6\\x1a'))
+ False
+
:param pubkey: The public key of the account to be registered
:type pubkey: ECPoint
:return: whether the registration was a success or not
@@ -121,6 +164,9 @@ def unregister_candidate(cls, pubkey: ECPoint) -> bool:
"""
Unregisters as a candidate.
+ >>> NEO.unregister_candidate(ECPoint(b'\\x03\\x5a\\x92\\x8f\\x20\\x16\\x39\\x20\\x4e\\x06\\xb4\\x36\\x8b\\x1a\\x93\\x36\\x54\\x62\\xa8\\xeb\\xbf\\xf0\\xb8\\x81\\x81\\x51\\xb7\\x4f\\xaa\\xb3\\xa2\\xb6\\x1a'))
+ False
+
:param pubkey: The public key of the account to be unregistered
:type pubkey: ECPoint
:return: whether the unregistration was a success or not
@@ -133,6 +179,9 @@ def vote(cls, account: UInt160, vote_to: ECPoint) -> bool:
"""
Votes for a candidate.
+ >>> NEO.vote(UInt160(b'\\xc9F\\x17\\xba!\\x99\\x07\\xc1\\xc5\\xd6\t#\\xe1\\x9096\\x89U\\xac\\x13'), ECPoint(b'\\x03\\x5a\\x92\\x8f\\x20\\x16\\x39\\x20\\x4e\\x06\\xb4\\x36\\x8b\\x1a\\x93\\x36\\x54\\x62\\xa8\\xeb\\xbf\\xf0\\xb8\\x81\\x81\\x51\\xb7\\x4f\\xaa\\xb3\\xa2\\xb6\\x1a'))
+ False
+
:param account: the account that is voting
:type account: UInt160
:param vote_to: the public key of the one being voted
@@ -145,6 +194,9 @@ def get_all_candidates(cls) -> Iterator:
"""
Gets the registered candidates iterator.
+ >>> NEO.get_all_candidates()
+ []
+
:return: all registered candidates
:rtype: Iterator
"""
@@ -155,6 +207,9 @@ def un_vote(cls, account: UInt160) -> bool:
"""
Removes the vote of the candidate voted. It would be the same as calling vote(account, None).
+ >>> NEO.un_vote(UInt160(b'\\xc9F\\x17\\xba!\\x99\\x07\\xc1\\xc5\\xd6\t#\\xe1\\x9096\\x89U\\xac\\x13'))
+ False
+
:param account: the account that is removing the vote
:type account: UInt160
"""
@@ -165,6 +220,9 @@ def get_candidates(cls) -> List[Tuple[ECPoint, int]]:
"""
Gets the list of all registered candidates.
+ >>> NEO.get_candidates()
+ []
+
:return: all registered candidates
:rtype: List[Tuple[ECPoint, int]]
"""
@@ -175,6 +233,12 @@ def get_candidate_vote(cls, pubkey: ECPoint) -> int:
"""
Gets votes from specific candidate.
+ >>> NEO.get_candidate_vote(ECPoint(b'\\x03\\x5a\\x92\\x8f\\x20\\x16\\x39\\x20\\x4e\\x06\\xb4\\x36\\x8b\\x1a\\x93\\x36\\x54\\x62\\xa8\\xeb\\xbf\\xf0\\xb8\\x81\\x81\\x51\\xb7\\x4f\\xaa\\xb3\\xa2\\xb6\\x1a'))
+ 100
+
+ >>> NEO.get_candidate_vote(ECPoint(bytes(32)))
+ -1
+
:return: Votes or -1 if it was not found.
:rtype: int
"""
@@ -185,6 +249,9 @@ def get_committee(cls) -> List[ECPoint]:
"""
Gets all committee members list.
+ >>> NEO.get_committee()
+ [ b'\\x02|\\x84\\xb0V\\xc2j{$XG\\x1em\\xcfgR\\xed\\xd9k\\x96\\x88}x34\\xe3Q\\xdd\\xfe\\x13\\xc4\\xbc\\xa2' ]
+
:return: all committee members
:rtype: List[ECPoint]
"""
@@ -195,6 +262,9 @@ def get_next_block_validators(cls) -> List[ECPoint]:
"""
Gets validators list of the next block.
+ >>> NEO.get_next_block_validators()
+ [ b'\\x02|\\x84\\xb0V\\xc2j{$XG\\x1em\\xcfgR\\xed\\xd9k\\x96\\x88}x34\\xe3Q\\xdd\\xfe\\x13\\xc4\\xbc\\xa2' ]
+
:return: the public keys of the validators
:rtype: List[ECPoint]
"""
@@ -205,6 +275,13 @@ def get_account_state(cls, account: UInt160) -> NeoAccountState:
"""
Gets the latest votes of the specified account.
+ >>> NEO.get_account_state(UInt160(b'\\xc9F\\x17\\xba!\\x99\\x07\\xc1\\xc5\\xd6\t#\\xe1\\x9096\\x89U\\xac\\x13'))
+ {
+ 'balance': 100,
+ 'height': 2,
+ 'vote_to': None,
+ }
+
:param account: the specified account
:type account: UInt160
:return: the state of the account
diff --git a/boa3/builtin/nativecontract/oracle.py b/boa3/builtin/nativecontract/oracle.py
index 8922b4eb6..83e9acf5a 100644
--- a/boa3/builtin/nativecontract/oracle.py
+++ b/boa3/builtin/nativecontract/oracle.py
@@ -23,6 +23,10 @@ def request(cls, url: str, request_filter: Union[str, None], callback: str, user
This method just requests data from the oracle, it won't return the result.
+ >>> Oracle.request('https://dora.coz.io/api/v1/neo3/testnet/asset/0xef4073a0f2b305a38ec4050e4d3d28bc40ea63f5',
+ ... '', 'callback_name', None, 10 * 10 ** 8)
+ None
+
:param url: External url to retrieve the data
:type url: str
:param request_filter:
@@ -56,6 +60,9 @@ def get_price(cls) -> int:
"""
Gets the price for an Oracle request.
+ >>> Oracle.get_price()
+ 50000000
+
:return: the price for an Oracle request
"""
pass
diff --git a/boa3/builtin/nativecontract/policy.py b/boa3/builtin/nativecontract/policy.py
index 88198d95b..9715dca54 100644
--- a/boa3/builtin/nativecontract/policy.py
+++ b/boa3/builtin/nativecontract/policy.py
@@ -18,6 +18,9 @@ def get_fee_per_byte(cls) -> int:
"""
Gets the network fee per transaction byte.
+ >>> Policy.get_fee_per_byte()
+ 1000
+
:return: the network fee per transaction byte
:rtype: int
"""
@@ -29,6 +32,9 @@ def get_exec_fee_factor(cls) -> int:
Gets the execution fee factor. This is a multiplier that can be adjusted by the committee to adjust the system fees
for transactions.
+ >>> Policy.get_exec_fee_factor()
+ 30
+
:return: the execution fee factor
:rtype: int
"""
@@ -39,6 +45,9 @@ def get_storage_price(cls) -> int:
"""
Gets the storage price.
+ >>> Policy.get_storage_price()
+ 100000
+
:return: the snapshot used to read data
:rtype: int
"""
@@ -49,6 +58,9 @@ def is_blocked(cls, account: UInt160) -> bool:
"""
Determines whether the specified account is blocked.
+ >>> Policy.is_blocked(UInt160(b'\\xcfv\\xe2\\x8b\\xd0\\x06,JG\\x8e\\xe3Ua\\x01\\x13\\x19\\xf3\\xcf\\xa4\\xd2'))
+ False
+
:param account: the account to be checked
:type account: UInt160
diff --git a/boa3/builtin/nativecontract/rolemanagement.py b/boa3/builtin/nativecontract/rolemanagement.py
index f0be0046c..46662d0ca 100644
--- a/boa3/builtin/nativecontract/rolemanagement.py
+++ b/boa3/builtin/nativecontract/rolemanagement.py
@@ -20,6 +20,9 @@ def get_designated_by_role(cls, role: Role, index: int) -> ECPoint:
"""
Gets the list of nodes for the specified role.
+ >>> get_designated_by_role(Role.ORACLE, 0)
+ []
+
:param role: the type of the role
:type role: Role
:param index: the index of the block to be queried
diff --git a/boa3/builtin/nativecontract/stdlib.py b/boa3/builtin/nativecontract/stdlib.py
index 9583e5fc4..f20f671e8 100644
--- a/boa3/builtin/nativecontract/stdlib.py
+++ b/boa3/builtin/nativecontract/stdlib.py
@@ -20,6 +20,18 @@ def serialize(cls, item: Any) -> bytes:
"""
Serializes the given value into its bytes representation.
+ >>> StdLib.serialize('42')
+ b'(\\x0242'
+
+ >>> StdLib.serialize(42)
+ b'!\\x01*'
+
+ >>> StdLib.serialize([2, 3, 5, 7])
+ b'@\\x04!\\x01\\x02!\\x01\\x03!\\x01\\x05!\\x01\\x07'
+
+ >>> StdLib.serialize({1: 1, 2: 1, 3: 2})
+ b'H\\x03!\\x01\\x01!\\x01\\x01!\\x01\\x02!\\x01\\x01!\\x01\\x03!\\x01\\x02'
+
:param item: value to be serialized
:type item: Any
:return: the serialized value
@@ -34,6 +46,18 @@ def deserialize(cls, data: bytes) -> Any:
"""
Deserializes the given bytes value.
+ >>> StdLib.deserialize(b'(\\x0242')
+ '42'
+
+ >>> StdLib.deserialize(b'!\\x01*')
+ 42
+
+ >>> StdLib.deserialize(b'@\\x04!\\x01\\x02!\\x01\\x03!\\x01\\x05!\\x01\\x07')
+ [2, 3, 5, 7]
+
+ >>> StdLib.deserialize(b'H\\x03!\\x01\\x01!\\x01\\x01!\\x01\\x02!\\x01\\x01!\\x01\\x03!\\x01\\x02')
+ {1: 1, 2: 1, 3: 2}
+
:param data: serialized value
:type data: bytes
:return: the deserialized result
@@ -48,6 +72,9 @@ def json_serialize(cls, item: Any) -> str:
"""
Serializes an item into a json.
+ >>> StdLib.json_serialize({'one': 1, 'two': 2, 'three': 3})
+ '{"one":1,"two":2,"three":3}'
+
:param item: The item that will be serialized
:type item: Any
:return: The serialized item
@@ -63,6 +90,9 @@ def json_deserialize(cls, json: str) -> Any:
"""
Deserializes a json into some valid type.
+ >>> StdLib.json_deserialize('{"one":1,"two":2,"three":3}')
+ {'one': 1, 'three': 3, 'two': 2}
+
:param json: A json that will be deserialized
:type json: str
:return: The deserialized json
@@ -77,6 +107,9 @@ def base64_decode(cls, key: str) -> bytes:
"""
Decodes a string value encoded with base64.
+ >>> StdLib.base64_decode("dW5pdCB0ZXN0")
+ b"unit test"
+
:param key: string value to be decoded
:type key: str
:return: the decoded string
@@ -89,6 +122,9 @@ def base64_encode(cls, key: bytes) -> str:
"""
Encodes a bytes value using base64.
+ >>> StdLib.base64_encode(b'unit test')
+ b"dW5pdCB0ZXN0"
+
:param key: bytes value to be encoded
:type key: bytes
:return: the encoded string
@@ -101,6 +137,9 @@ def base58_decode(cls, key: str) -> bytes:
"""
Decodes a string value encoded with base58.
+ >>> StdLib.base58_decode('2VhL46g69A1mu')
+ b"unit test"
+
:param key: string value to be decoded
:type key: str
:return: the decoded bytes
@@ -113,6 +152,9 @@ def base58_encode(cls, key: bytes) -> str:
"""
Encodes a bytes value using base58.
+ >>> StdLib.base58_encode(b'unit test')
+ b"2VhL46g69A1mu"
+
:param key: bytes value to be encoded
:type key: bytes
:return: the encoded string
@@ -126,6 +168,9 @@ def base58_check_decode(cls, key: str) -> bytes:
Converts the specified str, which encodes binary data as base-58 digits, to an equivalent bytes value. The encoded
str contains the checksum of the binary data.
+ >>> StdLib.base58_check_decode('AnJcKqvgBwKxsjX75o')
+ b"unit test"
+
:param key: string value to be decoded
:type key: str
:return: the decoded bytes
@@ -139,6 +184,9 @@ def base58_check_encode(cls, key: bytes) -> str:
Converts a bytes value to its equivalent str representation that is encoded with base-58 digits. The encoded str
contains the checksum of the binary data.
+ >>> StdLib.base58_check_encode(b'unit test')
+ b"AnJcKqvgBwKxsjX75o"
+
:param key: bytes value to be encoded
:type key: bytes
:return: the encoded string
@@ -151,6 +199,18 @@ def itoa(cls, value: int, base: int = 10) -> str:
"""
Converts the specific type of value to a decimal or hexadecimal string. The default is decimal.
+ >>> StdLib.itoa(10)
+ '10'
+
+ >>> StdLib.itoa(123)
+ '123'
+
+ >>> StdLib.itoa(-1, 16)
+ 'f'
+
+ >>> StdLib.itoa(15, 16)
+ '0f'
+
:param value: the int value
:type value: int
:param base: the value's base
@@ -165,6 +225,18 @@ def atoi(cls, value: str, base: int = 10) -> int:
"""
Converts a character string to a specific base value, decimal or hexadecimal. The default is decimal.
+ >>> StdLib.atoi('10')
+ 10
+
+ >>> StdLib.atoi('123')
+ 123
+
+ >>> StdLib.atoi('1f', 16)
+ 31
+
+ >>> StdLib.atoi('ff', 16)
+ -1
+
:param value: the int value as a string
:type value: str
:param base: the value base
@@ -181,6 +253,15 @@ def memory_compare(cls, mem1: ByteString, mem2: ByteString) -> int:
"""
Compares a memory with another one.
+ >>> StdLib.memory_compare('abc', 'abc')
+ 0
+
+ >>> StdLib.memory_compare('ABC', 'abc')
+ -1
+
+ >>> StdLib.memory_compare('abc', 'ABC')
+ 1
+
:param mem1: a memory to be compared to another one
:type mem1: bytes or str
:param mem2: a memory that will be compared with another one
@@ -196,6 +277,12 @@ def memory_search(cls, mem: ByteString, value: ByteString, start: int = 0, backw
"""
Searches for a given value in a given memory.
+ >>> StdLib.memory_search('abcde', 'a', 0)
+ 0
+
+ >>> StdLib.memory_search('abcde', 'e', 0)
+ 4
+
:param mem: the memory
:type mem: bytes or str
:param value: the value
diff --git a/boa3/builtin/type/__init__.py b/boa3/builtin/type/__init__.py
index dc8d94902..ffa15c845 100644
--- a/boa3/builtin/type/__init__.py
+++ b/boa3/builtin/type/__init__.py
@@ -20,6 +20,15 @@
class Event:
"""
Describes an action that happened in the blockchain.
+
+ >>> from boa3.builtin.compile_time import CreateNewEvent
+ ... new_event: Event = CreateNewEvent( # create a new Event with the CreateNewEvent method
+ ... [
+ ... ('name', str),
+ ... ('amount', int)
+ ... ],
+ ... 'New Event'
+ ... )
"""
def __call__(self, *args, **kwargs):
diff --git a/docs/source/boa3/builtin/interop/blockchain/boa3-builtin-interop-blockchain.rst b/docs/source/boa3/builtin/interop/blockchain/boa3-builtin-interop-blockchain.rst
index ae9d7c008..a31848456 100644
--- a/docs/source/boa3/builtin/interop/blockchain/boa3-builtin-interop-blockchain.rst
+++ b/docs/source/boa3/builtin/interop/blockchain/boa3-builtin-interop-blockchain.rst
@@ -2,13 +2,3 @@ blockchain
==========
.. automodule:: boa3.builtin.interop.blockchain
-
-Block
------
-
-.. automodule:: boa3.builtin.interop.blockchain.block
-
-Transaction
------------
-
-.. automodule:: boa3.builtin.interop.blockchain.transaction
diff --git a/docs/source/boa3/builtin/interop/boa3-builtin-interop.rst b/docs/source/boa3/builtin/interop/boa3-builtin-interop.rst
index 6ed10c10c..cc4df47ea 100644
--- a/docs/source/boa3/builtin/interop/boa3-builtin-interop.rst
+++ b/docs/source/boa3/builtin/interop/boa3-builtin-interop.rst
@@ -13,6 +13,9 @@ Subpackages
crypto/boa3-builtin-interop-crypto
iterator/boa3-builtin-interop-iterator
json/boa3-builtin-interop-json
+ oracle/boa3-builtin-interop-oracle
+ policy/boa3-builtin-interop-policy
+ role/boa3-builtin-interop-role
runtime/boa3-builtin-interop-runtime
stdlib/boa3-builtin-interop-stdlib
storage/boa3-builtin-interop-storage
diff --git a/docs/source/boa3/builtin/interop/contract/boa3-builtin-interop-contract-callflags.rst b/docs/source/boa3/builtin/interop/contract/boa3-builtin-interop-contract-callflags.rst
deleted file mode 100644
index a4aef5443..000000000
--- a/docs/source/boa3/builtin/interop/contract/boa3-builtin-interop-contract-callflags.rst
+++ /dev/null
@@ -1,4 +0,0 @@
-callflagstype
--------------
-
-.. automodule:: boa3.builtin.interop.contract.callflagstype
diff --git a/docs/source/boa3/builtin/interop/contract/boa3-builtin-interop-contract-contract.rst b/docs/source/boa3/builtin/interop/contract/boa3-builtin-interop-contract-contract.rst
deleted file mode 100644
index 5f588f2ee..000000000
--- a/docs/source/boa3/builtin/interop/contract/boa3-builtin-interop-contract-contract.rst
+++ /dev/null
@@ -1,4 +0,0 @@
-contract
---------
-
-.. automodule:: boa3.builtin.interop.contract.contract
diff --git a/docs/source/boa3/builtin/interop/contract/boa3-builtin-interop-contract-contractmanifest.rst b/docs/source/boa3/builtin/interop/contract/boa3-builtin-interop-contract-contractmanifest.rst
deleted file mode 100644
index d2b4006cb..000000000
--- a/docs/source/boa3/builtin/interop/contract/boa3-builtin-interop-contract-contractmanifest.rst
+++ /dev/null
@@ -1,4 +0,0 @@
-contractmanifest
-----------------
-
-.. automodule:: boa3.builtin.interop.contract.contractmanifest
diff --git a/docs/source/boa3/builtin/interop/contract/boa3-builtin-interop-contract.rst b/docs/source/boa3/builtin/interop/contract/boa3-builtin-interop-contract.rst
index 6d6fde8f2..c12f1fb5a 100644
--- a/docs/source/boa3/builtin/interop/contract/boa3-builtin-interop-contract.rst
+++ b/docs/source/boa3/builtin/interop/contract/boa3-builtin-interop-contract.rst
@@ -2,12 +2,3 @@ contract
========
.. automodule:: boa3.builtin.interop.contract
-
-Subpackages
------------
-
-.. toctree::
-
- boa3-builtin-interop-contract-callflags
- boa3-builtin-interop-contract-contract
- boa3-builtin-interop-contract-contractmanifest
diff --git a/docs/source/boa3/builtin/interop/oracle/boa3-builtin-interop-oracle.rst b/docs/source/boa3/builtin/interop/oracle/boa3-builtin-interop-oracle.rst
new file mode 100644
index 000000000..b02afb78a
--- /dev/null
+++ b/docs/source/boa3/builtin/interop/oracle/boa3-builtin-interop-oracle.rst
@@ -0,0 +1,4 @@
+oracle
+======
+
+.. automodule:: boa3.builtin.interop.oracle
diff --git a/docs/source/boa3/builtin/interop/policy/boa3-builtin-interop-policy.rst b/docs/source/boa3/builtin/interop/policy/boa3-builtin-interop-policy.rst
new file mode 100644
index 000000000..fcc65831b
--- /dev/null
+++ b/docs/source/boa3/builtin/interop/policy/boa3-builtin-interop-policy.rst
@@ -0,0 +1,4 @@
+policy
+======
+
+.. automodule:: boa3.builtin.interop.policy
diff --git a/docs/source/boa3/builtin/interop/role/boa3-builtin-interop-role.rst b/docs/source/boa3/builtin/interop/role/boa3-builtin-interop-role.rst
new file mode 100644
index 000000000..b38527ed0
--- /dev/null
+++ b/docs/source/boa3/builtin/interop/role/boa3-builtin-interop-role.rst
@@ -0,0 +1,4 @@
+role
+====
+
+.. automodule:: boa3.builtin.interop.role
diff --git a/docs/source/boa3/builtin/interop/runtime/boa3-builtin-interop-runtime.rst b/docs/source/boa3/builtin/interop/runtime/boa3-builtin-interop-runtime.rst
index 663583f59..024013de4 100644
--- a/docs/source/boa3/builtin/interop/runtime/boa3-builtin-interop-runtime.rst
+++ b/docs/source/boa3/builtin/interop/runtime/boa3-builtin-interop-runtime.rst
@@ -2,13 +2,3 @@ runtime
=======
.. automodule:: boa3.builtin.interop.runtime
-
-Notification
-------------
-
-.. automodule:: boa3.builtin.interop.runtime.notification
-
-Trigger
--------
-
-.. automodule:: boa3.builtin.interop.runtime.triggertype
diff --git a/docs/source/boa3/builtin/interop/storage/boa3-builtin-interop-storage.rst b/docs/source/boa3/builtin/interop/storage/boa3-builtin-interop-storage.rst
index 2ae510bdf..b145b1236 100644
--- a/docs/source/boa3/builtin/interop/storage/boa3-builtin-interop-storage.rst
+++ b/docs/source/boa3/builtin/interop/storage/boa3-builtin-interop-storage.rst
@@ -2,13 +2,3 @@ storage
=======
.. automodule:: boa3.builtin.interop.storage
-
-StorageContext
---------------
-
-.. automodule:: boa3.builtin.interop.storage.storagecontext
-
-StorageMap
-----------
-
-.. automodule:: boa3.builtin.interop.storage.storagemap
diff --git a/docs/source/calling-smart-contracts.md b/docs/source/calling-smart-contracts.md
new file mode 100644
index 000000000..7808e23bb
--- /dev/null
+++ b/docs/source/calling-smart-contracts.md
@@ -0,0 +1,125 @@
+# Calling smart contracts
+Currently, there are 2 different ways to call another smart contract on the blockchain: using a `call_contract` method
+or using an interface, but internally they work both the same way.
+
+Let us suppose the following contract was deployed on Neo:
+```python
+from boa3.builtin.compile_time import public
+
+
+@public
+def hello_stranger(name: str) -> str:
+ return "Hello " + name
+```
+To call the `hello_stranger` method, first you'll need to know the smart contract's [script hash](https://developers.neo.org/docs/n3/develop/deploy/deploy#the-contract-scripthash),
+Let us also assume it is `0x000102030405060708090A0B0C0D0E0F10111213`.
+
+## with call_contract
+Use the `call_contract` method from `boa3.builtin.interop.contract` on your smart contract. You'll need to use the
+script hash, followed by the name of the function you want to call, followed by the arguments of said function. You'll
+also need to type cast the return, otherwise it will be considered as `Any`.
+```python
+# calling_with_call_contract.py
+from boa3.builtin.compile_time import public
+from boa3.builtin.interop.contract import call_contract
+from boa3.builtin.type import UInt160
+
+@public
+def calling_other_contract() -> str:
+ greetings: str = call_contract(UInt160(b'\x13\x12\x11\x10\x0F\x0E\x0D\x0C\x0B\x0A\x09\x08\x07\x06\x05\x04\x03\x02\x01\x00'), # usually, script hashes that starts with "0x" means that they are using big endian, so when using `bytes` you'll need to revert the order
+ 'hello_stranger', # it's the function's name
+ 'John Doe' # the parameter of 'hello_stranger'
+ )
+ return greetings
+```
+
+> Note: If you are going to call a contract only once, then it's ok to use `call_contract`, however, it might be hard to
+keep track of a lot of `call_contract`s on the same file. It's pretty much always better to use an interface when dealing with other
+smart contracts.
+
+## with Interface
+Use the `contract` decorator from `boa3.builtin.compile_time` using the script hash and create a class that have the
+same methods you want call.
+```python
+# calling_with_interface.py
+from boa3.builtin.compile_time import public, contract
+
+@public
+def calling_other_contract() -> str:
+ greetings = HelloStrangerContract.hello_stranger('John Doe')
+ return greetings
+
+
+@contract('0x000102030405060708090A0B0C0D0E0F10111213')
+class HelloStrangerContract:
+ @staticmethod
+ def hello_stranger(name: str) -> str:
+ pass
+
+```
+
+### Calling native contracts
+Neo3-boa already has interfaces for all the [native contracts](https://docs.neo.org/docs/en-us/reference/scapi/framework/native.html)
+that you can import from `boa3.builtin.nativecontract`
+```python
+# calling_native_contract.py
+from boa3.builtin.compile_time import public
+from boa3.builtin.nativecontract import NEO
+
+@public
+def calling_other_contract() -> str:
+ neo_symbol = NEO.symbol()
+ return neo_symbol
+```
+
+### Automate with CPM
+Instead of manually writing the smart contract interface, you can use [CPM](https://github.com/CityOfZion/cpm/tree/master#readme)
+to generate it automatically. After installing Neo3-boa, you can install CPM by typing `install_cpm` on CLI (without the
+`neo3-boa` prefix). Then, you'll need to create a [cpm.yaml config file](https://github.com/CityOfZion/cpm/blob/master/docs/config.md)
+and put the smart contract information there, and [run cpm](https://github.com/CityOfZion/cpm#example-commands).
+
+For example, if you use CPM to create a [dice smart contract](https://dora.coz.io/contract/neo3/mainnet/0x4380f2c1de98bb267d3ea821897ec571a04fe3e0)
+interface, the following file will be generated:
+```python
+# cpm_out/python/dice/contract.py
+from boa3.builtin.type import UInt160, UInt256, ECPoint
+from boa3.builtin.compile_time import contract, display_name
+from typing import cast, Any
+
+
+@contract('0x4380f2c1de98bb267d3ea821897ec571a04fe3e0')
+class Dice:
+
+ @staticmethod
+ def rand_between(start: int, end: int) -> int:
+ pass
+
+ @staticmethod
+ def map_bytes_onto_range(start: int, end: int, entropy: bytes) -> int:
+ pass
+
+ @staticmethod
+ def roll_die(die: str) -> int:
+ pass
+
+ @staticmethod
+ def roll_dice_with_entropy(die: str, precision: int, entropy: bytes) -> list:
+ pass
+
+ @staticmethod
+ def update(script: bytes, manifest: bytes, data: Any) -> None:
+ pass
+```
+
+Then, all you need to do is import this class onto your smart contract.
+```python
+# calling_with_cpm.py
+from boa3.builtin.compile_time import public
+from cpm_out.python.dice.contract import Dice
+
+
+@public
+def calling_other_contract() -> str:
+ d6_roll = Dice.rand_between(1, 6)
+ return "Die result is " + str(d6_roll)
+```
diff --git a/docs/source/code-reference.rst b/docs/source/code-reference.rst
deleted file mode 100644
index c90581d07..000000000
--- a/docs/source/code-reference.rst
+++ /dev/null
@@ -1,311 +0,0 @@
-4. Code Reference
-#################
-
-4.1 Examples
-============
-
-For an extensive collection of examples:
-
-- `Smart Contract Examples