|
- 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}")
|