Skip to content

Commit

Permalink
ABI Interaction Support for Python SDK (#247)
Browse files Browse the repository at this point in the history
* Start ABI JSON interaction

* Add static annoation

* Fix Method argument parsing

* Add ABI Typing to Method arguments

* [WIP] Add AtomicTransactionComposer build functions

* [WIP] Sign and send atomic transaction groups

* Add unit tests for object parsing

* Clean up method calls

* Address PR comments on JSON objects

* Refactor ABI Type to ABIType so it can be exposed to outside world

* Add cucumber steps for ABI tests and update existing implementation so it can pass these tests

* Refactor TransactionSigner to Abstract class and merge signatures when signing

* Update testing to reflect json unit tests and composer tests

* Formatting and docstring fixes

* Clean up imports

* Fix unit test for appId

* Refactor some names and add txn as an arg type

* Partially address PR comments

* Add some additional checks for safety

* Fix a step so we check for empty string instead of None

* MInclude returns tests and check for a valid returns from a ABI call

* Addressing PR comments about type hints and returning self

* Ensure group ids are zero when adding transactions
  • Loading branch information
algochoi authored and aldur committed Feb 8, 2022
1 parent af45074 commit bd0093c
Show file tree
Hide file tree
Showing 19 changed files with 1,391 additions and 33 deletions.
4 changes: 2 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
unit:
behave --tags="@unit.offline or @unit.algod or @unit.indexer or @unit.rekey or @unit.tealsign or @unit.dryrun or @unit.applications or @unit.responses or @unit.transactions or @unit.responses.231 or @unit.feetest or @unit.indexer.logs" test -f progress2
behave --tags="@unit.offline or @unit.algod or @unit.indexer or @unit.rekey or @unit.tealsign or @unit.dryrun or @unit.applications or @unit.responses or @unit.transactions or @unit.transactions.payment or @unit.responses.231 or @unit.feetest or @unit.indexer.logs or @unit.abijson or @unit.atomic_transaction_composer" test -f progress2

integration:
behave --tags="@algod or @assets or @auction or @kmd or @send or @template or @indexer or @indexer.applications or @rekey or @compile or @dryrun or @dryrun.testing or @applications or @applications.verified or @indexer.231" test -f progress2
behave --tags="@algod or @assets or @auction or @kmd or @send or @template or @indexer or @indexer.applications or @rekey or @compile or @dryrun or @dryrun.testing or @applications or @applications.verified or @indexer.231 or @abi" test -f progress2

docker-test:
./run_integration.sh
4 changes: 4 additions & 0 deletions algosdk/abi/__init__.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
from .util import type_from_string
from .uint_type import UintType
from .ufixed_type import UfixedType
from .base_type import ABIType
from .bool_type import BoolType
from .byte_type import ByteType
from .address_type import AddressType
from .string_type import StringType
from .array_dynamic_type import ArrayDynamicType
from .array_static_type import ArrayStaticType
from .tuple_type import TupleType
from .method import Method, Argument, Returns
from .interface import Interface
from .contract import Contract

name = "abi"
4 changes: 2 additions & 2 deletions algosdk/abi/address_type.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
from .base_type import Type
from .base_type import ABIType
from .byte_type import ByteType
from .tuple_type import TupleType
from .. import error

from algosdk import encoding


class AddressType(Type):
class AddressType(ABIType):
"""
Represents an Address ABI Type for encoding.
"""
Expand Down
4 changes: 2 additions & 2 deletions algosdk/abi/array_dynamic_type.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
from .base_type import ABI_LENGTH_SIZE, Type
from .base_type import ABI_LENGTH_SIZE, ABIType
from .byte_type import ByteType
from .tuple_type import TupleType
from .. import error


class ArrayDynamicType(Type):
class ArrayDynamicType(ABIType):
"""
Represents a ArrayDynamic ABI Type for encoding.
Expand Down
4 changes: 2 additions & 2 deletions algosdk/abi/array_static_type.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import math

from .base_type import Type
from .base_type import ABIType
from .bool_type import BoolType
from .byte_type import ByteType
from .tuple_type import TupleType
from .. import error


class ArrayStaticType(Type):
class ArrayStaticType(ABIType):
"""
Represents a ArrayStatic ABI Type for encoding.
Expand Down
5 changes: 2 additions & 3 deletions algosdk/abi/base_type.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
from abc import ABC, abstractmethod
from enum import IntEnum

# Globals
ABI_LENGTH_SIZE = 2 # We use 2 bytes to encode the length of a dynamic element


class Type(ABC):
class ABIType(ABC):
"""
Represents an ABI Type for encoding.
"""
Expand Down Expand Up @@ -45,7 +44,7 @@ def encode(self, value):
pass

@abstractmethod
def decode(self, value_string):
def decode(self, bytestring):
"""
Deserialize the ABI type and value from a byte string using ABI encoding rules.
"""
Expand Down
4 changes: 2 additions & 2 deletions algosdk/abi/bool_type.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from .base_type import Type
from .base_type import ABIType
from .. import error


class BoolType(Type):
class BoolType(ABIType):
"""
Represents a Bool ABI Type for encoding.
"""
Expand Down
4 changes: 2 additions & 2 deletions algosdk/abi/byte_type.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
from .base_type import Type
from .base_type import ABIType
from .. import error


class ByteType(Type):
class ByteType(ABIType):
"""
Represents a Byte ABI Type for encoding.
"""
Expand Down
47 changes: 47 additions & 0 deletions algosdk/abi/contract.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import json

from algosdk.abi.method import Method


class Contract:
"""
Represents a ABI contract description.
Args:
name (string): name of the contract
app_id (int): application id associated with the contract
methods (list): list of Method objects
"""

def __init__(self, name, app_id, methods) -> None:
self.name = name
self.app_id = int(app_id)
self.methods = methods

def __eq__(self, o) -> bool:
if not isinstance(o, Contract):
return False
return (
self.name == o.name
and self.app_id == o.app_id
and self.methods == o.methods
)

@staticmethod
def from_json(resp):
d = json.loads(resp)
return Contract.undictify(d)

def dictify(self):
d = {}
d["name"] = self.name
d["appId"] = self.app_id
d["methods"] = [m.dictify() for m in self.methods]
return d

@staticmethod
def undictify(d):
name = d["name"]
app_id = d["appId"]
method_list = [Method.undictify(method) for method in d["methods"]]
return Contract(name=name, app_id=app_id, methods=method_list)
39 changes: 39 additions & 0 deletions algosdk/abi/interface.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import json

from algosdk.abi.method import Method


class Interface:
"""
Represents a ABI interface description.
Args:
name (string): name of the interface
methods (list): list of Method objects
"""

def __init__(self, name, methods):
self.name = name
self.methods = methods

def __eq__(self, o):
if not isinstance(o, Interface):
return False
return self.name == o.name and self.methods == o.methods

@staticmethod
def from_json(resp):
d = json.loads(resp)
return Interface.undictify(d)

def dictify(self):
d = {}
d["name"] = self.name
d["methods"] = [m.dictify() for m in self.methods]
return d

@staticmethod
def undictify(d):
name = d["name"]
method_list = [Method.undictify(method) for method in d["methods"]]
return Interface(name=name, methods=method_list)
Loading

0 comments on commit bd0093c

Please sign in to comment.