The backend component to interface with the smart contract.
Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

107 linhas
3.9 KiB

  1. import asyncio
  2. from stellar_sdk import Keypair, Network, SorobanServer, TransactionBuilder, xdr as stellar_xdr
  3. from stellar_sdk.exceptions import PrepareTransactionException
  4. from stellar_sdk.soroban_rpc import GetTransactionStatus, SendTransactionStatus
  5. from stellar_sdk.xdr import SCVal, SCValType
  6. class SingletonMeta(type):
  7. _instances = {}
  8. def __call__(cls, *args, **kwargs):
  9. if cls not in cls._instances:
  10. instance = super().__call__(*args, **kwargs)
  11. cls._instances[cls] = instance
  12. return cls._instances[cls]
  13. class SmartContract(metaclass=SingletonMeta):
  14. contract_id: str
  15. user_key: str
  16. def __init__(self, contract_id: str, user_key: str):
  17. self.contract_id = contract_id
  18. self.user_key = user_key
  19. async def _execute_procedure(self, procedure_name: str, parameters=None):
  20. source_keypair = Keypair.from_secret(self.user_key)
  21. soroban_server = SorobanServer('https://soroban-testnet.stellar.org')
  22. source_account = soroban_server.load_account(source_keypair.public_key)
  23. built_transaction = (
  24. TransactionBuilder(
  25. source_account=source_account,
  26. base_fee=100,
  27. network_passphrase=Network.TESTNET_NETWORK_PASSPHRASE,
  28. )
  29. .append_invoke_contract_function_op(
  30. contract_id=self.contract_id,
  31. function_name=procedure_name,
  32. parameters=parameters,
  33. )
  34. .set_timeout(30)
  35. .build()
  36. )
  37. try:
  38. prepared_transaction = soroban_server.prepare_transaction(built_transaction)
  39. except PrepareTransactionException as e:
  40. print(f"Exception preparing transaction: {e}\n{e.simulate_transaction_response.error}")
  41. raise e
  42. prepared_transaction.sign(source_keypair)
  43. send_response = soroban_server.send_transaction(prepared_transaction)
  44. if send_response.status != SendTransactionStatus.PENDING:
  45. raise Exception("sending transaction failed")
  46. while True:
  47. get_response = soroban_server.get_transaction(send_response.hash)
  48. if get_response.status != GetTransactionStatus.NOT_FOUND:
  49. break
  50. await asyncio.sleep(2)
  51. print(f"get_transaction response: {get_response}")
  52. if get_response.status == GetTransactionStatus.SUCCESS:
  53. assert get_response.result_meta_xdr is not None
  54. transaction_meta = stellar_xdr.TransactionMeta.from_xdr(
  55. get_response.result_meta_xdr
  56. )
  57. return_value = transaction_meta.v3.soroban_meta.return_value
  58. output = translate_soroban_value(return_value)
  59. return output
  60. else:
  61. print(f"Transaction failed: {get_response.result_xdr}")
  62. def translate_soroban_value(val: SCVal) -> int or str or bool or list[int or str or bool] or None:
  63. def sanitize(k: str) -> str:
  64. return k.lstrip('b\'').rstrip('\'')
  65. type_handlers = {
  66. SCValType.SCV_U32: lambda v: v.u32.uint32,
  67. SCValType.SCV_I32: lambda v: v.i32.int32,
  68. SCValType.SCV_U64: lambda v: v.u64.uint64,
  69. SCValType.SCV_I64: lambda v: v.i64.int64,
  70. SCValType.SCV_BOOL: lambda v: v.bool.boolean,
  71. SCValType.SCV_STRING: lambda v: sanitize(str(v.str.sc_string)),
  72. SCValType.SCV_SYMBOL: lambda v: sanitize(str(v.sym.sc_symbol)),
  73. SCValType.SCV_BYTES: lambda v: bytes(v.bytes.sc_bytes),
  74. SCValType.SCV_VEC: lambda v: [translate_soroban_value(item) for item in v.vec.sc_vec],
  75. SCValType.SCV_MAP: lambda v: {translate_soroban_value(item.key): translate_soroban_value(item.val) for item in v.map.sc_map},
  76. }
  77. if val.type == SCValType.SCV_VOID:
  78. return None
  79. handler = type_handlers.get(val.type)
  80. if handler:
  81. return handler(val)
  82. else:
  83. raise ValueError(f"Unsupported SCVal type: {val.type}")