|
|
@@ -0,0 +1,106 @@ |
|
|
|
import asyncio |
|
|
|
|
|
|
|
from stellar_sdk import Keypair, Network, SorobanServer, TransactionBuilder, xdr as stellar_xdr |
|
|
|
from stellar_sdk.exceptions import PrepareTransactionException |
|
|
|
from stellar_sdk.soroban_rpc import GetTransactionStatus, SendTransactionStatus |
|
|
|
from stellar_sdk.xdr import SCVal, SCValType |
|
|
|
|
|
|
|
|
|
|
|
class SingletonMeta(type): |
|
|
|
_instances = {} |
|
|
|
|
|
|
|
def __call__(cls, *args, **kwargs): |
|
|
|
if cls not in cls._instances: |
|
|
|
instance = super().__call__(*args, **kwargs) |
|
|
|
cls._instances[cls] = instance |
|
|
|
return cls._instances[cls] |
|
|
|
|
|
|
|
|
|
|
|
class SmartContract(metaclass=SingletonMeta): |
|
|
|
contract_id: str |
|
|
|
user_key: str |
|
|
|
def __init__(self, contract_id: str, user_key: str): |
|
|
|
self.contract_id = contract_id |
|
|
|
self.user_key = user_key |
|
|
|
|
|
|
|
async def _execute_procedure(self, procedure_name: str, parameters=None): |
|
|
|
source_keypair = Keypair.from_secret(self.user_key) |
|
|
|
|
|
|
|
soroban_server = SorobanServer('https://soroban-testnet.stellar.org') |
|
|
|
|
|
|
|
source_account = soroban_server.load_account(source_keypair.public_key) |
|
|
|
|
|
|
|
built_transaction = ( |
|
|
|
TransactionBuilder( |
|
|
|
source_account=source_account, |
|
|
|
base_fee=100, |
|
|
|
network_passphrase=Network.TESTNET_NETWORK_PASSPHRASE, |
|
|
|
) |
|
|
|
.append_invoke_contract_function_op( |
|
|
|
contract_id=self.contract_id, |
|
|
|
function_name=procedure_name, |
|
|
|
parameters=parameters, |
|
|
|
) |
|
|
|
.set_timeout(30) |
|
|
|
.build() |
|
|
|
) |
|
|
|
|
|
|
|
try: |
|
|
|
prepared_transaction = soroban_server.prepare_transaction(built_transaction) |
|
|
|
except PrepareTransactionException as e: |
|
|
|
print(f"Exception preparing transaction: {e}\n{e.simulate_transaction_response.error}") |
|
|
|
raise e |
|
|
|
|
|
|
|
prepared_transaction.sign(source_keypair) |
|
|
|
|
|
|
|
send_response = soroban_server.send_transaction(prepared_transaction) |
|
|
|
|
|
|
|
if send_response.status != SendTransactionStatus.PENDING: |
|
|
|
raise Exception("sending transaction failed") |
|
|
|
|
|
|
|
while True: |
|
|
|
get_response = soroban_server.get_transaction(send_response.hash) |
|
|
|
if get_response.status != GetTransactionStatus.NOT_FOUND: |
|
|
|
break |
|
|
|
await asyncio.sleep(2) |
|
|
|
|
|
|
|
print(f"get_transaction response: {get_response}") |
|
|
|
|
|
|
|
if get_response.status == GetTransactionStatus.SUCCESS: |
|
|
|
assert get_response.result_meta_xdr is not None |
|
|
|
|
|
|
|
transaction_meta = stellar_xdr.TransactionMeta.from_xdr( |
|
|
|
get_response.result_meta_xdr |
|
|
|
) |
|
|
|
return_value = transaction_meta.v3.soroban_meta.return_value |
|
|
|
output = translate_soroban_value(return_value) |
|
|
|
return output |
|
|
|
else: |
|
|
|
print(f"Transaction failed: {get_response.result_xdr}") |
|
|
|
|
|
|
|
|
|
|
|
def translate_soroban_value(val: SCVal) -> int or str or bool or list[int or str or bool] or None: |
|
|
|
def sanitize(k: str) -> str: |
|
|
|
return k.lstrip('b\'').rstrip('\'') |
|
|
|
|
|
|
|
type_handlers = { |
|
|
|
SCValType.SCV_U32: lambda v: v.u32.uint32, |
|
|
|
SCValType.SCV_I32: lambda v: v.i32.int32, |
|
|
|
SCValType.SCV_U64: lambda v: v.u64.uint64, |
|
|
|
SCValType.SCV_I64: lambda v: v.i64.int64, |
|
|
|
SCValType.SCV_BOOL: lambda v: v.bool.boolean, |
|
|
|
SCValType.SCV_STRING: lambda v: sanitize(str(v.str.sc_string)), |
|
|
|
SCValType.SCV_SYMBOL: lambda v: sanitize(str(v.sym.sc_symbol)), |
|
|
|
SCValType.SCV_BYTES: lambda v: bytes(v.bytes.sc_bytes), |
|
|
|
SCValType.SCV_VEC: lambda v: [translate_soroban_value(item) for item in v.vec.sc_vec], |
|
|
|
SCValType.SCV_MAP: lambda v: {translate_soroban_value(item.key): translate_soroban_value(item.val) for item in v.map.sc_map}, |
|
|
|
} |
|
|
|
|
|
|
|
if val.type == SCValType.SCV_VOID: |
|
|
|
return None |
|
|
|
|
|
|
|
handler = type_handlers.get(val.type) |
|
|
|
if handler: |
|
|
|
return handler(val) |
|
|
|
else: |
|
|
|
raise ValueError(f"Unsupported SCVal type: {val.type}") |