Skip to content

Commit

Permalink
feat: add support for Soroban. (#59)
Browse files Browse the repository at this point in the history
  • Loading branch information
overcat committed Dec 6, 2023
1 parent cb5e2de commit 72b07c7
Show file tree
Hide file tree
Showing 12 changed files with 735 additions and 1 deletion.
4 changes: 4 additions & 0 deletions stellar_model/model/horizon/asset_stat.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from decimal import Decimal
from typing import Optional

from pydantic import BaseModel, Field

Expand Down Expand Up @@ -41,6 +42,7 @@ class AssetStat(BaseModel):
asset_code: str = Field(description="This asset's code")
asset_issuer: str = Field(description="The Stellar address of this asset's issuer.")
paging_token: str = Field(description="A cursor value for use in pagination.")
contract_id: Optional[str] = Field(description="The contract ID of this asset.")
num_accounts: int = Field(
description="The numnber of accounts that have issued a trustline to this asset. If the **auth_required** flag for this asset's issuer is set to **true**, this number only includes the accounts who have both set up a trustline to the asset and have been authorized to hold the asset."
)
Expand All @@ -50,6 +52,7 @@ class AssetStat(BaseModel):
num_liquidity_pools: int = Field(
description="The current number of liquidity_pools for this asset."
)
num_contracts: int
amount: Decimal = Field(description="The number of units issued for this asset.")
accounts: AssetStatAccounts = Field(
description="The number of accounts grouped by each trustline flag state."
Expand All @@ -60,6 +63,7 @@ class AssetStat(BaseModel):
liquidity_pools_amount: Decimal = Field(
description="The number of units in liquidity pools for this asset."
)
contracts_amount: Decimal
balances: AssetStatBalances = Field(
description="The number of units issued for this asset grouped by each trustline flag state."
)
Expand Down
54 changes: 54 additions & 0 deletions stellar_model/model/horizon/effects.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@
"LiquidityPoolCreatedEffect",
"LiquidityPoolRemovedEffect",
"LiquidityPoolRevokedEffect",
"EffectContractCreditedEffect",
"EffectContractDebitedEffect",
]

from stellar_model.model.horizon.liquidity_pool_asset_amount import (
Expand Down Expand Up @@ -749,6 +751,54 @@ class LiquidityPoolRevokedEffect(BaseEffect):
shares_revoked: Decimal


class EffectContractCreditedEffect(BaseEffect):
"""
Occurs when a contract receives balance.
type: contract_credited
type_i: 96
"""

asset_type: str
asset_code: Optional[str] = None
asset_issuer: Optional[str] = None
contract: str
amount: Decimal


class EffectContractDebitedEffect(BaseEffect):
"""
Occurs when a contract sends balance.
type: contract_debited
type_i: 97
"""

asset_type: str
asset_code: Optional[str] = None
asset_issuer: Optional[str] = None
contract: str
amount: Decimal


# class EffectBumpFootprintExpirationEffect(BaseEffect):
# """
# Occurs when bumps contract's footprint expiration.
#
# type: bump_footprint_expiration
# type_i: 98
# """
#
#
# class EffectRestoreFootprintEffect(BaseEffect):
# """
# Occurs when restores contract's footprint.
#
# type: restore_footprint
# type_i: 99
# """


_EFFECT_TYPE_I_MAP = {
0: AccountCreatedEffect,
1: AccountRemovedEffect,
Expand Down Expand Up @@ -801,6 +851,8 @@ class LiquidityPoolRevokedEffect(BaseEffect):
93: LiquidityPoolCreatedEffect,
94: LiquidityPoolRemovedEffect,
95: LiquidityPoolRevokedEffect,
96: EffectContractCreditedEffect,
97: EffectContractDebitedEffect,
}

_EFFECT_TYPE_UNION = Union[
Expand Down Expand Up @@ -855,4 +907,6 @@ class LiquidityPoolRevokedEffect(BaseEffect):
LiquidityPoolCreatedEffect,
LiquidityPoolRemovedEffect,
LiquidityPoolRevokedEffect,
EffectContractCreditedEffect,
EffectContractDebitedEffect,
]
65 changes: 64 additions & 1 deletion stellar_model/model/horizon/operations.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from decimal import Decimal
from typing import List, Optional, Union

from pydantic import BaseModel, Field
from pydantic import BaseModel, ConfigDict, Field

from stellar_model.model.horizon.asset import Asset
from stellar_model.model.horizon.claimable_balance import Claimant
Expand Down Expand Up @@ -38,6 +38,9 @@
"SetTrustLineFlagsOperation",
"LiquidityPoolDepositOperation",
"LiquidityPoolWithdrawOperation",
"InvokeHostFunctionOperation",
"BumpFootprintExpirationOperation",
"RestoreFootprintOperation",
]


Expand Down Expand Up @@ -690,6 +693,60 @@ class LiquidityPoolWithdrawOperation(BaseOperation):
reserves_received: List[LiquidityPoolAssetAmount]


class HostFunctionParameter(BaseModel):
value: str
type: str


class AssetContractBalanceChange(BaseModel):
asset_type: str
asset_code: Optional[str] = None
asset_issuer: Optional[str] = None
type: str
from_: Optional[str] = Field(
alias="from",
default=None,
)
to: Optional[str] = None
amount: Decimal
model_config = ConfigDict(populate_by_name=True)


class InvokeHostFunctionOperation(BaseOperation):
"""
Represents a single operations whose type is InvokeHostFunction.
type: invoke_host_function
type_i: 24
"""

function: str
parameters: List[HostFunctionParameter]
address: str
salt: str
asset_balance_changes: List[AssetContractBalanceChange]


class BumpFootprintExpirationOperation(BaseOperation):
"""
Represents a single operations whose type is BumpFootprintExpiration.
type: bump_footprint_expiration
type_i: 25
"""

extend_to: int


class RestoreFootprintOperation(BaseOperation):
"""
Represents a single operations whose type is RestoreFootprint.
type: restore_footprint
type_i: 26
"""


_OPERATION_TYPE_UNION = Union[
CreateAccountOperation,
PaymentOperation,
Expand All @@ -715,6 +772,9 @@ class LiquidityPoolWithdrawOperation(BaseOperation):
SetTrustLineFlagsOperation,
LiquidityPoolDepositOperation,
LiquidityPoolWithdrawOperation,
InvokeHostFunctionOperation,
BumpFootprintExpirationOperation,
RestoreFootprintOperation,
]

_PAYMENT_TYPE_UNION = Union[
Expand Down Expand Up @@ -750,4 +810,7 @@ class LiquidityPoolWithdrawOperation(BaseOperation):
21: SetTrustLineFlagsOperation,
22: LiquidityPoolDepositOperation,
23: LiquidityPoolWithdrawOperation,
24: InvokeHostFunctionOperation,
25: BumpFootprintExpirationOperation,
26: RestoreFootprintOperation,
}
40 changes: 40 additions & 0 deletions tests/model/horizon/test_effects.py
Original file line number Diff line number Diff line change
Expand Up @@ -1139,3 +1139,43 @@ def test_liquidity_pool_removed(self):

def test_liquidity_pool_revoked(self):
pass

def test_contract_credited(self):
raw_data = load_horizon_file("effects/contract_credited.json")
parsed_data = EffectContractCreditedEffect.model_validate(raw_data)
self.assertEqual(parsed_data.id, "0000021517786157057-0000000002")
self.assertEqual(parsed_data.paging_token, "21517786157057-2")
self.assertEqual(
parsed_data.account,
"GDAT5HWTGIU4TSSZ4752OUC4SABDLTLZFRPZUJ3D6LKBNEPA7V2CIG54",
)
self.assertEqual(parsed_data.type, "contract_credited")
self.assertEqual(parsed_data.type_i, 96)
self.assertEqual(
parsed_data.created_at,
datetime.datetime(2023, 9, 19, 5, 43, 12, tzinfo=datetime.timezone.utc),
)
self.assertEqual(parsed_data.asset_type, "native")
self.assertEqual(parsed_data.asset_code, None)
self.assertEqual(parsed_data.asset_issuer, None)
self.assertEqual(parsed_data.amount, Decimal("100"))

def test_contract_debited(self):
raw_data = load_horizon_file("effects/contract_debited.json")
parsed_data = EffectContractCreditedEffect.model_validate(raw_data)
self.assertEqual(parsed_data.id, "0000021517786157057-0000000002")
self.assertEqual(parsed_data.paging_token, "21517786157057-2")
self.assertEqual(
parsed_data.account,
"GDAT5HWTGIU4TSSZ4752OUC4SABDLTLZFRPZUJ3D6LKBNEPA7V2CIG54",
)
self.assertEqual(parsed_data.type, "contract_debited")
self.assertEqual(parsed_data.type_i, 97)
self.assertEqual(
parsed_data.created_at,
datetime.datetime(2023, 9, 19, 5, 43, 12, tzinfo=datetime.timezone.utc),
)
self.assertEqual(parsed_data.asset_type, "native")
self.assertEqual(parsed_data.asset_code, None)
self.assertEqual(parsed_data.asset_issuer, None)
self.assertEqual(parsed_data.amount, Decimal("100"))
108 changes: 108 additions & 0 deletions tests/model/horizon/test_operations.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
from decimal import Decimal

from stellar_model.model.horizon.operations import *
from stellar_model.model.horizon.operations import (
AssetContractBalanceChange,
HostFunctionParameter,
)
from tests.model.horizon import load_horizon_file

"""
Expand Down Expand Up @@ -998,3 +1002,107 @@ def test_valid_liquidity_pool_withdraw_operation(self):
self.assertEqual(
parsed_data.reserves_received[1].amount, Decimal("100.0000000")
)

def test_bump_footprint_expiration_operation(self):
raw_data = load_horizon_file("operations/bump_footprint_expiration.json")
parsed_data = BumpFootprintExpirationOperation.model_validate(raw_data)
self.assertEqual(parsed_data.id, "2224793063426")
self.assertEqual(parsed_data.paging_token, "2224793063426")
self.assertEqual(parsed_data.transaction_successful, True)
self.assertEqual(
parsed_data.source_account,
"GDAT5HWTGIU4TSSZ4752OUC4SABDLTLZFRPZUJ3D6LKBNEPA7V2CIG54",
)
self.assertEqual(parsed_data.type, "bump_footprint_expiration")
self.assertEqual(parsed_data.type_i, 25)
self.assertEqual(
parsed_data.created_at,
datetime.datetime(2023, 7, 20, 10, 44, 56, tzinfo=datetime.timezone.utc),
)
self.assertEqual(
parsed_data.transaction_hash,
"c452cd9d1ff9692499d0d2aa2f8e898b8c38025300c0f293f4a2adde7295c82f",
)
self.assertEqual(parsed_data.extend_to, 2343241)

def test_restore_footprint_operation(self):
raw_data = load_horizon_file("operations/restore_footprint.json")
parsed_data = RestoreFootprintOperation.model_validate(raw_data)
self.assertEqual(parsed_data.id, "2224793063427")
self.assertEqual(parsed_data.paging_token, "2224793063427")
self.assertEqual(parsed_data.transaction_successful, True)
self.assertEqual(
parsed_data.source_account,
"GDAT5HWTGIU4TSSZ4752OUC4SABDLTLZFRPZUJ3D6LKBNEPA7V2CIG54",
)
self.assertEqual(parsed_data.type, "restore_footprint")
self.assertEqual(parsed_data.type_i, 26)
self.assertEqual(
parsed_data.created_at,
datetime.datetime(2023, 7, 20, 10, 44, 56, tzinfo=datetime.timezone.utc),
)
self.assertEqual(
parsed_data.transaction_hash,
"b6932dacb25e05ca8e3d006d2a5a119683602f70474cc9f5de9fc53e99f627f8",
)

def test_invoke_host_function_operation(self):
raw_data = load_horizon_file("operations/invoke_host_function.json")
parsed_data = InvokeHostFunctionOperation.model_validate(raw_data)
self.assertEqual(parsed_data.id, "2224793063425")
self.assertEqual(parsed_data.paging_token, "2224793063425")
self.assertEqual(parsed_data.transaction_successful, True)
self.assertEqual(
parsed_data.source_account,
"GDAT5HWTGIU4TSSZ4752OUC4SABDLTLZFRPZUJ3D6LKBNEPA7V2CIG54",
)
self.assertEqual(parsed_data.type, "invoke_host_function")
self.assertEqual(parsed_data.type_i, 24)
self.assertEqual(
parsed_data.created_at,
datetime.datetime(2023, 7, 20, 10, 44, 56, tzinfo=datetime.timezone.utc),
)
self.assertEqual(
parsed_data.transaction_hash,
"4ef3d81fba4b7db959080e4894cb8b2575418b8da9aa484f6306a79a3f63de3d",
)
self.assertEqual(
parsed_data.function, "HostFunctionTypeHostFunctionTypeInvokeContract"
)
self.assertEqual(
parsed_data.parameters,
[
HostFunctionParameter(
value="AAAAEgAAAAGw7oy+G8a9SeTIE5E/EuJYl5JfwF0eZJWk8S7LmE7fwA==",
type="Address",
),
HostFunctionParameter(value="AAAADwAAAAh0cmFuc2Zlcg==", type="Sym"),
HostFunctionParameter(
value="AAAAEgAAAAAAAAAAwT6e0zIpycpZ5/unUFyQAjXNeSxfmidj8tQWkeD9dCQ=",
type="Address",
),
HostFunctionParameter(
value="AAAAEgAAAAAAAAAAWLfEosjyl6qPPSRxKB/fzOyv5I5WYzE+wY4Spz7KmKE=",
type="Address",
),
HostFunctionParameter(
value="AAAACgAAAAAAAAAAAAAAASoF8gA=", type="I128"
),
],
)
self.assertEqual(parsed_data.address, "")
self.assertEqual(parsed_data.salt, "")
self.assertEqual(
parsed_data.asset_balance_changes,
[
AssetContractBalanceChange(
asset_type="credit_alphanum12",
asset_code="Hello",
asset_issuer="GDJKBIYIPBE2NC5XIZX6GCFZHVWFUA7ONMQUOOVTLIM3BESTI4BYADAN",
type="transfer",
from_="GDAT5HWTGIU4TSSZ4752OUC4SABDLTLZFRPZUJ3D6LKBNEPA7V2CIG54",
to="GBMLPRFCZDZJPKUPHUSHCKA737GOZL7ERZLGGMJ6YGHBFJZ6ZKMKCZTM",
amount=Decimal(500),
)
],
)
3 changes: 3 additions & 0 deletions tests/resources/model/horizon/asset_stat.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@
"asset_code": "ZX2",
"asset_issuer": "GDBSQA2GDRBRZVWOMRLZ6V4ZN2UESRQAEIHGKCZDR3YO4QIMC3CLHPR3",
"paging_token": "ZX2_GDBSQA2GDRBRZVWOMRLZ6V4ZN2UESRQAEIHGKCZDR3YO4QIMC3CLHPR3_credit_alphanum4",
"contract_id": "CCCPUUUK5HZCTPW6RM2EY5IXGDEOML672CJ7VDCCSLSQLT2Z5R755QHD",
"num_accounts": 1,
"num_claimable_balances": 0,
"num_liquidity_pools": 0,
"num_contracts": 2,
"amount": "1001.0000000",
"accounts": {
"authorized": 1,
Expand All @@ -19,6 +21,7 @@
},
"claimable_balances_amount": "0.0000000",
"liquidity_pools_amount": "0.0000000",
"contracts_amount": "123.2232",
"balances": {
"authorized": "1001.0000000",
"authorized_to_maintain_liabilities": "0.0000000",
Expand Down
Loading

0 comments on commit 72b07c7

Please sign in to comment.