From b9cf1280860cbdec16de25326d9e08b165156b74 Mon Sep 17 00:00:00 2001 From: Gleb Karavatski Date: Mon, 1 Jul 2024 19:20:01 +0300 Subject: [PATCH 01/17] [TON-431] API v2 Transactions documentation --- ton-http-api/pyTON/api/api_v2/endpoints/common.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ton-http-api/pyTON/api/api_v2/endpoints/common.py b/ton-http-api/pyTON/api/api_v2/endpoints/common.py index 6670db8..d159417 100644 --- a/ton-http-api/pyTON/api/api_v2/endpoints/common.py +++ b/ton-http-api/pyTON/api/api_v2/endpoints/common.py @@ -374,7 +374,7 @@ async def get_try_locate_tx( tonlib: TonlibManager = Depends(tonlib_dep) ): """ - Locate outcoming transaction of *destination* address by incoming message. + Locate `incoming` transaction by `outgoing` message. """ return await tonlib.tryLocateTxByIncomingMessage(source, destination, created_lt) @@ -388,7 +388,7 @@ async def get_try_locate_result_tx( tonlib: TonlibManager = Depends(tonlib_dep) ): """ - Same as previous. Locate outcoming transaction of *destination* address by incoming message + Same as previous. Locate `incoming` transaction by `outgoing` message. """ return await tonlib.tryLocateTxByIncomingMessage(source, destination, created_lt) @@ -402,7 +402,7 @@ async def get_try_locate_source_tx( tonlib: TonlibManager = Depends(tonlib_dep) ): """ - Locate incoming transaction of *source* address by outcoming message. + Locate `outgoing` transaction by `incoming` message. """ return await tonlib.tryLocateTxByOutcomingMessage(source, destination, created_lt) From bc583131ffbfe01bdcd8282da5311ab7fab0e4ce Mon Sep 17 00:00:00 2001 From: Gleb Karavatski Date: Tue, 2 Jul 2024 15:20:07 +0300 Subject: [PATCH 02/17] add getBlockTransactions response description --- .../pyTON/api/api_v2/endpoints/common.py | 7 ++-- ton-http-api/pyTON/schemas/__init__.py | 3 +- ton-http-api/pyTON/schemas/http.py | 32 +++++++++++++++++-- 3 files changed, 36 insertions(+), 6 deletions(-) diff --git a/ton-http-api/pyTON/api/api_v2/endpoints/common.py b/ton-http-api/pyTON/api/api_v2/endpoints/common.py index d159417..67e837e 100644 --- a/ton-http-api/pyTON/api/api_v2/endpoints/common.py +++ b/ton-http-api/pyTON/api/api_v2/endpoints/common.py @@ -16,7 +16,8 @@ from pyTON.schemas import ( TonResponse, DeprecatedTonResponseJsonRPC, - TonRequestJsonRPC + TonRequestJsonRPC, + TonResponseGetBlockTransactions, ) from pyTON.core.tonlib.manager import TonlibManager from pyTON.api.deps.ton import tonlib_dep, settings_dep @@ -303,7 +304,7 @@ async def get_shards( """ return await tonlib.getShards(seqno) -@router.get('/getBlockTransactions', response_model=TonResponse, response_model_exclude_none=True, tags=['blocks','transactions']) +@router.get('/getBlockTransactions', response_model=TonResponseGetBlockTransactions, response_model_exclude_none=True, tags=['blocks','transactions']) @json_rpc('getBlockTransactions') @wrap_result async def get_block_transactions( @@ -318,7 +319,7 @@ async def get_block_transactions( tonlib: TonlibManager = Depends(tonlib_dep) ): """ - Get transactions of the given block. + Get transactions descriptions of the given block. """ return await tonlib.getBlockTransactions(workchain, shard, seqno, count, root_hash, file_hash, after_lt, after_hash) diff --git a/ton-http-api/pyTON/schemas/__init__.py b/ton-http-api/pyTON/schemas/__init__.py index ae75d85..5fcd800 100644 --- a/ton-http-api/pyTON/schemas/__init__.py +++ b/ton-http-api/pyTON/schemas/__init__.py @@ -3,7 +3,8 @@ TonResponse, TonResponseGeneric, TonResponseJsonRPC, - DeprecatedTonResponseJsonRPC + DeprecatedTonResponseJsonRPC, + TonResponseGetBlockTransactions ) from .ton import ( BlockId, diff --git a/ton-http-api/pyTON/schemas/http.py b/ton-http-api/pyTON/schemas/http.py index 13b5e75..a8f3d46 100644 --- a/ton-http-api/pyTON/schemas/http.py +++ b/ton-http-api/pyTON/schemas/http.py @@ -1,6 +1,6 @@ -from typing import Optional, TypeVar, Union +from typing import Optional, TypeVar, Union, Literal, List from pydantic.generics import GenericModel, Generic -from pydantic import BaseModel +from pydantic import BaseModel, Field ResultT = TypeVar('ResultT') @@ -16,6 +16,34 @@ class TonResponseGeneric(GenericModel, Generic[ResultT]): class TonResponse(TonResponseGeneric[Union[str, list, dict, None]]): pass +class ShortTransaction(BaseModel): + type: Literal['blocks.shortTxId'] = Field(alias="@type") + mode: int + account: str + lt: str + hash: str + +class Id(BaseModel): + type: Literal['ton.blockIdExt'] = Field(alias="@type") + workchain: int + shard: str + seqno: int + root_hash: str + file_hash: str + +class ResponseShortTransaction(BaseModel): + type: Literal['blocks.transactions'] = Field(alias="@type") + id: Id + req_count: int + incomplete: bool + transactions: List[ShortTransaction] + type: Literal['string'] = Field(alias="@@xtra") + +class TonResponseGetBlockTransactions(BaseModel): + ok: bool + result: Optional[ResponseShortTransaction] + error: Optional[str] = None + code: Optional[int] = None class TonResponseJsonRPC(BaseModel): id: str From c89d8c5db477901bcf4e578e252760f8c6a358ef Mon Sep 17 00:00:00 2001 From: Aliaksandr Bahdanau Date: Tue, 2 Jul 2024 15:42:29 +0300 Subject: [PATCH 03/17] document send methods --- .../pyTON/api/api_v2/endpoints/common.py | 17 ++++--- ton-http-api/pyTON/schemas/__init__.py | 5 ++- ton-http-api/pyTON/schemas/http.py | 44 +++++++++++++++++-- 3 files changed, 55 insertions(+), 11 deletions(-) diff --git a/ton-http-api/pyTON/api/api_v2/endpoints/common.py b/ton-http-api/pyTON/api/api_v2/endpoints/common.py index 6670db8..cad61be 100644 --- a/ton-http-api/pyTON/api/api_v2/endpoints/common.py +++ b/ton-http-api/pyTON/api/api_v2/endpoints/common.py @@ -16,7 +16,10 @@ from pyTON.schemas import ( TonResponse, DeprecatedTonResponseJsonRPC, - TonRequestJsonRPC + TonRequestJsonRPC, + SendBocReturnHashResponse, + EstimateFeeResponse, + OkResponse ) from pyTON.core.tonlib.manager import TonlibManager from pyTON.api.deps.ton import tonlib_dep, settings_dep @@ -432,7 +435,7 @@ def send_boc_to_external_endpoint(boc): return False -@router.post('/sendBoc', response_model=TonResponse, response_model_exclude_none=True, tags=['send']) +@router.post('/sendBoc', response_model=OkResponse, response_model_exclude_none=True, tags=['send']) @json_rpc('sendBoc') @wrap_result async def send_boc( @@ -451,7 +454,7 @@ async def send_boc( background_tasks.add_task(send_boc_to_external_endpoint, base64.b64encode(boc).decode('utf8')) return res -@router.post('/sendBocReturnHash', response_model=TonResponse, response_model_exclude_none=True, tags=['send']) +@router.post('/sendBocReturnHash', response_model=SendBocReturnHashResponse, response_model_exclude_none=True, tags=['send']) @json_rpc('sendBocReturnHash') @wrap_result async def send_boc_return_hash( @@ -517,12 +520,12 @@ async def send_cell( raise HTTPException(status_code=400, detail="Error while parsing cell") return await tonlib.raw_send_message(boc) -@router.post('/sendQuery', response_model=TonResponse, response_model_exclude_none=True, tags=['send']) +@router.post('/sendQuery', response_model=OkResponse, response_model_exclude_none=True, tags=['send']) @json_rpc('sendQuery') @wrap_result async def send_query( - address: str = Body(..., description="Address in any format"), - body: str = Body(..., description="b64-encoded boc-serialized cell with message body"), + address: str = Body(..., description="Address in any format", examples=[ADDRESS_EXAMPLE]), + body: str = Body(..., description="b64-encoded boc-serialized cell with message body", examples=[ADDRESS_EXAMPLE]), init_code: str = Body(default='', description="b64-encoded boc-serialized cell with init-code"), init_data: str = Body(default='', description="b64-encoded boc-serialized cell with init-data"), tonlib: TonlibManager = Depends(tonlib_dep) @@ -561,7 +564,7 @@ async def send_query_cell( raise HTTPException(status_code=400, detail="Error while parsing cell object") return await tonlib.raw_create_and_send_query(address, body, init_code=qcode, init_data=qdata) -@router.post('/estimateFee', response_model=TonResponse, response_model_exclude_none=True, tags=['send']) +@router.post('/estimateFee', response_model=EstimateFeeResponse, response_model_exclude_none=True, tags=['send']) @json_rpc('estimateFee') @wrap_result async def estimate_fee( diff --git a/ton-http-api/pyTON/schemas/__init__.py b/ton-http-api/pyTON/schemas/__init__.py index ae75d85..7507208 100644 --- a/ton-http-api/pyTON/schemas/__init__.py +++ b/ton-http-api/pyTON/schemas/__init__.py @@ -3,7 +3,10 @@ TonResponse, TonResponseGeneric, TonResponseJsonRPC, - DeprecatedTonResponseJsonRPC + DeprecatedTonResponseJsonRPC, + OkResponse, + SendBocReturnHashResponse, + EstimateFeeResponse ) from .ton import ( BlockId, diff --git a/ton-http-api/pyTON/schemas/http.py b/ton-http-api/pyTON/schemas/http.py index 13b5e75..02a470c 100644 --- a/ton-http-api/pyTON/schemas/http.py +++ b/ton-http-api/pyTON/schemas/http.py @@ -1,7 +1,6 @@ -from typing import Optional, TypeVar, Union +from typing import Optional, TypeVar, Union, Literal, List from pydantic.generics import GenericModel, Generic -from pydantic import BaseModel - +from pydantic import BaseModel, Field ResultT = TypeVar('ResultT') @@ -13,6 +12,14 @@ class TonResponseGeneric(GenericModel, Generic[ResultT]): code: Optional[int] = None +ResultTypeT = TypeVar('ResultTypeT') + + +class TonResponseResultGeneric(GenericModel, Generic[ResultTypeT]): + type: ResultTypeT = Field(alias="@type") + extra: str = Field(alias="@extra") + + class TonResponse(TonResponseGeneric[Union[str, list, dict, None]]): pass @@ -39,3 +46,34 @@ class TonRequestJsonRPC(BaseModel): params: dict = {} id: Optional[str] = None jsonrpc: Optional[str] = None + + +class OkResponse(TonResponseGeneric[TonResponseResultGeneric[Literal['ok']]]): + pass + + +class SendBocReturnHashResult(TonResponseResultGeneric[Literal['raw.extMessageInfo']]): + hash: str = Field(example="65+BlkfroywqXyM+POVpMpFiC6XYMQyBvHXw12XiFzc=") + + +class SendBocReturnHashResponse(TonResponseGeneric[SendBocReturnHashResult]): + pass + + +class Fees(BaseModel): + type: Literal['fees'] = Field(alias="@type") + in_fwd_fee: int + storage_fee: int + gas_fee: int + fwd_fee: int + + +class EstimateFeeResponseResult(BaseModel): + type: Literal['query.fees'] = Field(alias="@type") + source_fees: Fees + destination_fees: List[Fees] + extra: str = Field(alias="@extra") + + +class EstimateFeeResponse(TonResponseGeneric[EstimateFeeResponseResult]): + pass From 391c6c669a9b1362fe37a095442565e93a0a0767 Mon Sep 17 00:00:00 2001 From: Gleb Karavatski Date: Tue, 2 Jul 2024 15:46:17 +0300 Subject: [PATCH 04/17] add trx descr --- .../pyTON/api/api_v2/endpoints/common.py | 3 +- ton-http-api/pyTON/schemas/__init__.py | 3 +- ton-http-api/pyTON/schemas/http.py | 55 ++++++++++++++++++- 3 files changed, 58 insertions(+), 3 deletions(-) diff --git a/ton-http-api/pyTON/api/api_v2/endpoints/common.py b/ton-http-api/pyTON/api/api_v2/endpoints/common.py index 67e837e..7864f6a 100644 --- a/ton-http-api/pyTON/api/api_v2/endpoints/common.py +++ b/ton-http-api/pyTON/api/api_v2/endpoints/common.py @@ -18,6 +18,7 @@ DeprecatedTonResponseJsonRPC, TonRequestJsonRPC, TonResponseGetBlockTransactions, + TonResponseGetTransactions ) from pyTON.core.tonlib.manager import TonlibManager from pyTON.api.deps.ton import tonlib_dep, settings_dep @@ -159,7 +160,7 @@ async def get_wallet_information( wallet_handler["data_extractor"](res, result) return res -@router.get('/getTransactions', response_model=TonResponse, response_model_exclude_none=True, tags=['accounts', 'transactions']) +@router.get('/getTransactions', response_model=TonResponseGetTransactions, response_model_exclude_none=True, tags=['accounts', 'transactions']) @json_rpc('getTransactions') @wrap_result async def get_transactions( diff --git a/ton-http-api/pyTON/schemas/__init__.py b/ton-http-api/pyTON/schemas/__init__.py index 5fcd800..dc2bcb9 100644 --- a/ton-http-api/pyTON/schemas/__init__.py +++ b/ton-http-api/pyTON/schemas/__init__.py @@ -4,7 +4,8 @@ TonResponseGeneric, TonResponseJsonRPC, DeprecatedTonResponseJsonRPC, - TonResponseGetBlockTransactions + TonResponseGetBlockTransactions, + TonResponseGetTransactions ) from .ton import ( BlockId, diff --git a/ton-http-api/pyTON/schemas/http.py b/ton-http-api/pyTON/schemas/http.py index a8f3d46..cba3cff 100644 --- a/ton-http-api/pyTON/schemas/http.py +++ b/ton-http-api/pyTON/schemas/http.py @@ -37,7 +37,7 @@ class ResponseShortTransaction(BaseModel): req_count: int incomplete: bool transactions: List[ShortTransaction] - type: Literal['string'] = Field(alias="@@xtra") + type: Literal['string'] = Field(alias="@extra") class TonResponseGetBlockTransactions(BaseModel): ok: bool @@ -45,6 +45,59 @@ class TonResponseGetBlockTransactions(BaseModel): error: Optional[str] = None code: Optional[int] = None +class Address(BaseModel): + type: Literal['accountAddress'] = Field(alias="@type") + account_address: str + +class TransactionId(BaseModel): + type: Literal['internal.transactionId'] = Field(alias="@type") + account_address: str + lt: str + hash: str + +class MsgData(BaseModel): + type: Literal['msg.dataRaw'] = Field(alias="@type") + body: str + init_state: str + +class Msg(BaseModel): + type: Literal['raw.message'] = Field(alias="@type") + source: str + destination: str + value: str + fwd_fee: str + ihr_fee: str + created_lt: str + body_hash: str + msg_data: MsgData + message: str + + +class ResponseAddressTransaction(BaseModel): + type: Literal['raw.transaction'] = Field(alias="@type") + address: Address + utime: int + data: str + transaction_id: TransactionId + fee: str + storage_fee: str + other_fee: str + in_msg: Msg + out_msgs: List[Msg] + + +class TonResponseGetTransactions(BaseModel): + ok: bool + result: Optional[List[ResponseAddressTransaction]] + error: Optional[str] = None + code: Optional[int] = None + +class TonTryLocateTx(BaseModel): + ok: bool + result: Optional[List[ResponseAddressTransaction]] + error: Optional[str] = None + code: Optional[int] = None + class TonResponseJsonRPC(BaseModel): id: str jsonrpc: str = "2.0" From 2782f27724e2dcd512a119d816f990bd4b712b5f Mon Sep 17 00:00:00 2001 From: Gleb Karavatski Date: Tue, 2 Jul 2024 17:09:21 +0300 Subject: [PATCH 05/17] add locate trx desrcs --- ton-http-api/pyTON/api/api_v2/endpoints/common.py | 15 ++++++++------- ton-http-api/pyTON/schemas/__init__.py | 3 ++- ton-http-api/pyTON/schemas/http.py | 5 ++--- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/ton-http-api/pyTON/api/api_v2/endpoints/common.py b/ton-http-api/pyTON/api/api_v2/endpoints/common.py index 7864f6a..8378e55 100644 --- a/ton-http-api/pyTON/api/api_v2/endpoints/common.py +++ b/ton-http-api/pyTON/api/api_v2/endpoints/common.py @@ -18,7 +18,8 @@ DeprecatedTonResponseJsonRPC, TonRequestJsonRPC, TonResponseGetBlockTransactions, - TonResponseGetTransactions + TonResponseGetTransactions, + TonTryLocateTx ) from pyTON.core.tonlib.manager import TonlibManager from pyTON.api.deps.ton import tonlib_dep, settings_dep @@ -366,7 +367,7 @@ async def get_token_data( address = prepare_address(address) return await tonlib.get_token_data(address) -@router.get('/tryLocateTx', response_model=TonResponse, response_model_exclude_none=True, tags=['transactions']) +@router.get('/tryLocateTx', response_model=TonTryLocateTx, response_model_exclude_none=True, tags=['transactions']) @json_rpc('tryLocateTx') @wrap_result async def get_try_locate_tx( @@ -376,11 +377,11 @@ async def get_try_locate_tx( tonlib: TonlibManager = Depends(tonlib_dep) ): """ - Locate `incoming` transaction by `outgoing` message. + Locate transaction for destination address by incoming message. """ return await tonlib.tryLocateTxByIncomingMessage(source, destination, created_lt) -@router.get('/tryLocateResultTx', response_model=TonResponse, response_model_exclude_none=True, tags=['transactions']) +@router.get('/tryLocateResultTx', response_model=TonTryLocateTx, response_model_exclude_none=True, tags=['transactions']) @json_rpc('tryLocateResultTx') @wrap_result async def get_try_locate_result_tx( @@ -390,11 +391,11 @@ async def get_try_locate_result_tx( tonlib: TonlibManager = Depends(tonlib_dep) ): """ - Same as previous. Locate `incoming` transaction by `outgoing` message. + Same as previous. Locate transaction for destination address by incoming message. """ return await tonlib.tryLocateTxByIncomingMessage(source, destination, created_lt) -@router.get('/tryLocateSourceTx', response_model=TonResponse, response_model_exclude_none=True, tags=['transactions']) +@router.get('/tryLocateSourceTx', response_model=TonTryLocateTx, response_model_exclude_none=True, tags=['transactions']) @json_rpc('tryLocateSourceTx') @wrap_result async def get_try_locate_source_tx( @@ -404,7 +405,7 @@ async def get_try_locate_source_tx( tonlib: TonlibManager = Depends(tonlib_dep) ): """ - Locate `outgoing` transaction by `incoming` message. + Locate transaction for source address by outcoming message. """ return await tonlib.tryLocateTxByOutcomingMessage(source, destination, created_lt) diff --git a/ton-http-api/pyTON/schemas/__init__.py b/ton-http-api/pyTON/schemas/__init__.py index dc2bcb9..56edc6b 100644 --- a/ton-http-api/pyTON/schemas/__init__.py +++ b/ton-http-api/pyTON/schemas/__init__.py @@ -5,7 +5,8 @@ TonResponseJsonRPC, DeprecatedTonResponseJsonRPC, TonResponseGetBlockTransactions, - TonResponseGetTransactions + TonResponseGetTransactions, + TonTryLocateTx ) from .ton import ( BlockId, diff --git a/ton-http-api/pyTON/schemas/http.py b/ton-http-api/pyTON/schemas/http.py index cba3cff..136c9c7 100644 --- a/ton-http-api/pyTON/schemas/http.py +++ b/ton-http-api/pyTON/schemas/http.py @@ -51,7 +51,7 @@ class Address(BaseModel): class TransactionId(BaseModel): type: Literal['internal.transactionId'] = Field(alias="@type") - account_address: str + account_address: Optional[str] lt: str hash: str @@ -94,7 +94,7 @@ class TonResponseGetTransactions(BaseModel): class TonTryLocateTx(BaseModel): ok: bool - result: Optional[List[ResponseAddressTransaction]] + result: Optional[ResponseAddressTransaction] error: Optional[str] = None code: Optional[int] = None @@ -105,7 +105,6 @@ class TonResponseJsonRPC(BaseModel): error: Optional[str] = None code: Optional[int] = None - class DeprecatedTonResponseJsonRPC(BaseModel): ok: bool result: Optional[ResultT] From 27d6c41387df692bf73c96877c8354743e4e383f Mon Sep 17 00:00:00 2001 From: Aliaksandr Bahdanau Date: Thu, 4 Jul 2024 15:39:54 +0300 Subject: [PATCH 06/17] document runGetMethod --- .../pyTON/api/api_v2/endpoints/common.py | 9 ++-- ton-http-api/pyTON/schemas/__init__.py | 3 +- ton-http-api/pyTON/schemas/http.py | 45 +++++++++++++++++-- 3 files changed, 49 insertions(+), 8 deletions(-) diff --git a/ton-http-api/pyTON/api/api_v2/endpoints/common.py b/ton-http-api/pyTON/api/api_v2/endpoints/common.py index 6670db8..4194029 100644 --- a/ton-http-api/pyTON/api/api_v2/endpoints/common.py +++ b/ton-http-api/pyTON/api/api_v2/endpoints/common.py @@ -16,7 +16,8 @@ from pyTON.schemas import ( TonResponse, DeprecatedTonResponseJsonRPC, - TonRequestJsonRPC + TonRequestJsonRPC, + RunGetMethodResponse ) from pyTON.core.tonlib.manager import TonlibManager from pyTON.api.deps.ton import tonlib_dep, settings_dep @@ -609,12 +610,12 @@ async def estimate_fee_cell( if settings.webserver.get_methods: - @router.post('/runGetMethod', response_model=TonResponse, response_model_exclude_none=True, tags=["run method"]) + @router.post('/runGetMethod', response_model=RunGetMethodResponse, response_model_exclude_none=True, tags=["run method"]) @json_rpc('runGetMethod') @wrap_result async def run_get_method( - address: str = Body(..., description='Contract address'), - method: Union[str, int] = Body(..., description='Method name or method id'), + address: str = Body(..., description='Contract address'), + method: Union[str, int] = Body(..., description='Method name or method id'), stack: List[List[Any]] = Body(..., description="Array of stack elements: `[['num',3], ['cell', cell_object], ['slice', slice_object]]`"), tonlib: TonlibManager = Depends(tonlib_dep) ): diff --git a/ton-http-api/pyTON/schemas/__init__.py b/ton-http-api/pyTON/schemas/__init__.py index ae75d85..306d766 100644 --- a/ton-http-api/pyTON/schemas/__init__.py +++ b/ton-http-api/pyTON/schemas/__init__.py @@ -3,7 +3,8 @@ TonResponse, TonResponseGeneric, TonResponseJsonRPC, - DeprecatedTonResponseJsonRPC + DeprecatedTonResponseJsonRPC, + RunGetMethodResponse, ) from .ton import ( BlockId, diff --git a/ton-http-api/pyTON/schemas/http.py b/ton-http-api/pyTON/schemas/http.py index 13b5e75..37a64e0 100644 --- a/ton-http-api/pyTON/schemas/http.py +++ b/ton-http-api/pyTON/schemas/http.py @@ -1,7 +1,6 @@ -from typing import Optional, TypeVar, Union +from typing import Optional, TypeVar, Union, Literal, List, Tuple from pydantic.generics import GenericModel, Generic -from pydantic import BaseModel - +from pydantic import BaseModel, Field ResultT = TypeVar('ResultT') @@ -39,3 +38,43 @@ class TonRequestJsonRPC(BaseModel): params: dict = {} id: Optional[str] = None jsonrpc: Optional[str] = None + + +class TvmStackEntry(BaseModel): + type: str = Field(alias='@type') + + +class TvmTuple(BaseModel): + type: Literal['tvm.tuple'] = Field(alias='@type') + elements: List[TvmStackEntry] + + +class BlockIdExt(BaseModel): + type: Literal['smc.blockIdExt'] = Field(alias='@type') + workchain: int + shard: str + seqno: int + root_hash: str + file_hash: str + + +class TransactionId(BaseModel): + type: Literal['internal.transactionId'] = Field(alias='@type') + lt: str + hash: str + + +TVMStackEntryType = Literal['cell', 'slice', 'num', 'tuple', 'list'] + +class RunGetMethodResult(BaseModel): + type: Literal['smc.runResult'] = Field(alias='@type') + gas_used: int + stack: List[List[Union[TVMStackEntryType, Union[str, TvmTuple]]]] = Field(example=[['num', '0x1']]) + exit_code: int + extra: str = Field(alias='@extra') + block_id: BlockIdExt + last_transaction_id: TransactionId + + +class RunGetMethodResponse(TonResponseGeneric[RunGetMethodResult]): + pass From 58d14163f022c24deeb75a0ac4dd3ac5131a3ef6 Mon Sep 17 00:00:00 2001 From: Gleb Karavatski Date: Thu, 4 Jul 2024 16:13:12 +0300 Subject: [PATCH 07/17] add blocks api descr --- .../pyTON/api/api_v2/endpoints/common.py | 32 +++++++---- ton-http-api/pyTON/schemas/__init__.py | 6 +++ ton-http-api/pyTON/schemas/http.py | 4 +- ton-http-api/pyTON/schemas/ton.py | 53 ++++++++++++++++++- 4 files changed, 80 insertions(+), 15 deletions(-) diff --git a/ton-http-api/pyTON/api/api_v2/endpoints/common.py b/ton-http-api/pyTON/api/api_v2/endpoints/common.py index 6670db8..754c0bb 100644 --- a/ton-http-api/pyTON/api/api_v2/endpoints/common.py +++ b/ton-http-api/pyTON/api/api_v2/endpoints/common.py @@ -16,7 +16,17 @@ from pyTON.schemas import ( TonResponse, DeprecatedTonResponseJsonRPC, - TonRequestJsonRPC + TonRequestJsonRPC, + TonResponseGeneric, + # TonResponseGenericExtra, + MasterchainInfo, + MasterchainSignatures, + ShardBlockProof, + BlockId, + ConsensusBlock, + Shards, + ShortTransactions, + BlockHeader ) from pyTON.core.tonlib.manager import TonlibManager from pyTON.api.deps.ton import tonlib_dep, settings_dep @@ -229,7 +239,7 @@ async def unpack_address( """ return _detect_address(address)["raw_form"] -@router.get('/getMasterchainInfo', response_model=TonResponse, response_model_exclude_none=True, tags=['blocks']) +@router.get('/getMasterchainInfo', response_model=TonResponseGeneric[MasterchainInfo], response_model_exclude_none=True, tags=['blocks']) @json_rpc('getMasterchainInfo') @wrap_result async def get_masterchain_info(tonlib: TonlibManager=Depends(tonlib_dep)): @@ -238,7 +248,7 @@ async def get_masterchain_info(tonlib: TonlibManager=Depends(tonlib_dep)): """ return await tonlib.getMasterchainInfo() -@router.get('/getMasterchainBlockSignatures', response_model=TonResponse, response_model_exclude_none=True, tags=['blocks']) +@router.get('/getMasterchainBlockSignatures', response_model=TonResponseGeneric[MasterchainSignatures], response_model_exclude_none=True, tags=['blocks']) @json_rpc('getMasterchainBlockSignatures') @wrap_result async def get_masterchain_block_signatures( @@ -250,7 +260,7 @@ async def get_masterchain_block_signatures( """ return await tonlib.getMasterchainBlockSignatures(seqno) -@router.get('/getShardBlockProof', response_model=TonResponse, response_model_exclude_none=True, tags=['blocks']) +@router.get('/getShardBlockProof', response_model=TonResponseGeneric[ShardBlockProof], response_model_exclude_none=True, tags=['blocks']) @json_rpc('getShardBlockProof') @wrap_result async def get_shard_block_proof( @@ -266,7 +276,7 @@ async def get_shard_block_proof( return await tonlib.getShardBlockProof(workchain, shard, seqno, from_seqno) -@router.get('/getConsensusBlock', response_model=TonResponse, response_model_exclude_none=True, tags=['blocks']) +@router.get('/getConsensusBlock', response_model=TonResponseGeneric[ConsensusBlock], response_model_exclude_none=True, tags=['blocks']) @json_rpc('getConsensusBlock') @wrap_result async def get_consensus_block(tonlib: TonlibManager=Depends(tonlib_dep)): @@ -275,7 +285,7 @@ async def get_consensus_block(tonlib: TonlibManager=Depends(tonlib_dep)): """ return await tonlib.getConsensusBlock() -@router.get('/lookupBlock', response_model=TonResponse, response_model_exclude_none=True, tags=['blocks']) +@router.get('/lookupBlock', response_model=TonResponseGeneric[BlockId], response_model_exclude_none=True, tags=['blocks']) @json_rpc('lookupBlock') @wrap_result async def lookup_block( @@ -291,7 +301,7 @@ async def lookup_block( """ return await tonlib.lookupBlock(workchain, shard, seqno, lt, unixtime) -@router.get('/shards', response_model=TonResponse, response_model_exclude_none=True, tags=['blocks']) +@router.get('/shards', response_model=TonResponseGeneric[Shards], response_model_exclude_none=True, tags=['blocks']) @json_rpc('shards') @wrap_result async def get_shards( @@ -303,7 +313,7 @@ async def get_shards( """ return await tonlib.getShards(seqno) -@router.get('/getBlockTransactions', response_model=TonResponse, response_model_exclude_none=True, tags=['blocks','transactions']) +@router.get('/getBlockTransactions', response_model=TonResponseGeneric[ShortTransactions], response_model_exclude_none=True, tags=['blocks','transactions']) @json_rpc('getBlockTransactions') @wrap_result async def get_block_transactions( @@ -318,11 +328,11 @@ async def get_block_transactions( tonlib: TonlibManager = Depends(tonlib_dep) ): """ - Get transactions of the given block. + Get transactions metadata of the given block. """ return await tonlib.getBlockTransactions(workchain, shard, seqno, count, root_hash, file_hash, after_lt, after_hash) -@router.get('/getBlockHeader', response_model=TonResponse, response_model_exclude_none=True, tags=['blocks']) +@router.get('/getBlockHeader', response_model=TonResponseGeneric[BlockHeader], response_model_exclude_none=True, tags=['blocks']) @json_rpc('getBlockHeader') @wrap_result async def get_block_header( @@ -334,7 +344,7 @@ async def get_block_header( tonlib: TonlibManager = Depends(tonlib_dep) ): """ - Get metadata of a given block. + Get metadata of the given block. """ return await tonlib.getBlockHeader(workchain, shard, seqno, root_hash, file_hash) diff --git a/ton-http-api/pyTON/schemas/__init__.py b/ton-http-api/pyTON/schemas/__init__.py index ae75d85..f1b0a9a 100644 --- a/ton-http-api/pyTON/schemas/__init__.py +++ b/ton-http-api/pyTON/schemas/__init__.py @@ -2,6 +2,7 @@ TonRequestJsonRPC, TonResponse, TonResponseGeneric, + # TonResponseGenericExtra, TonResponseJsonRPC, DeprecatedTonResponseJsonRPC ) @@ -18,6 +19,11 @@ Message, TransactionId, Transaction, + MasterchainSignatures, + ShardBlockProof, + ConsensusBlock, + Shards, + ShortTransactions, check_tonlib_type, address_state ) diff --git a/ton-http-api/pyTON/schemas/http.py b/ton-http-api/pyTON/schemas/http.py index 13b5e75..0c31502 100644 --- a/ton-http-api/pyTON/schemas/http.py +++ b/ton-http-api/pyTON/schemas/http.py @@ -1,6 +1,6 @@ -from typing import Optional, TypeVar, Union +from typing import Optional, TypeVar, Union, Literal from pydantic.generics import GenericModel, Generic -from pydantic import BaseModel +from pydantic import BaseModel, Field ResultT = TypeVar('ResultT') diff --git a/ton-http-api/pyTON/schemas/ton.py b/ton-http-api/pyTON/schemas/ton.py index f63cb23..9ad5911 100644 --- a/ton-http-api/pyTON/schemas/ton.py +++ b/ton-http-api/pyTON/schemas/ton.py @@ -1,7 +1,6 @@ from typing import List, Optional, Literal -from pydantic import BaseModel - +from pydantic import BaseModel, Field from pytonlib.utils.wallet import wallets as known_wallets, sha256 @@ -21,6 +20,7 @@ def address_state(account_info): class BlockId(BaseModel): + type: Literal['ton.blockIdExt'] = Field(alias="@type") workchain: int shard: str seqno: int @@ -40,6 +40,7 @@ def build(tl_obj: dict): class BlockHeader(BaseModel): + type: Literal['blocks.header'] = Field(alias="@type") id: BlockId global_id: int version: int @@ -152,6 +153,7 @@ def build(raw: dict): class MasterchainInfo(BaseModel): + type: Literal['blocks.masterchainInfo'] = Field(alias="@type") last: BlockId state_root_hash: str init: BlockId @@ -164,6 +166,53 @@ def build(tl_obj: dict): state_root_hash=tl_obj['state_root_hash'] ) +class BlockSignature(BaseModel): + type: Literal['blocks.signature'] = Field(alias="@type") + node_id_short: str + signature: str + +class MasterchainSignatures(BaseModel): + type: Literal['blocks.blockSignatures'] = Field(alias="@type") + id: BlockId + signatures: List[BlockSignature] + +class Proof(BaseModel): + type: Literal['blocks.blockLinkBack'] = Field(alias="@type") + to_key_block: bool + from_id: BlockId = Field(alias="from") + to: BlockId + dest_proof: str + proof: str + state_proof: str + +class ShardBlockProof(BaseModel): + type: Literal['blocks.blockSignatures'] = Field(alias="@type") + from_id: BlockId = Field(alias="from") + mc_id: BlockId + links: List[str] + mc_proof: List[Proof] + +class Shards(BaseModel): + type: Literal['blocks.shards'] = Field(alias="@type") + shards: List[BlockId] + +class ConsensusBlock(BaseModel): + consensus_block: int + timestamp: int + +class ShortTransaction(BaseModel): + type: Literal['blocks.shortTxId'] = Field(alias="@type") + mode: int + account: str + lt: str + hash: str + +class ShortTransactions(BaseModel): + type: Literal['blocks.transactions'] = Field(alias="@type") + id: BlockId + req_count: int + incomplete: bool + transactions: List[ShortTransaction] class ExternalMessage(BaseModel): msg_hash: str From 000eac5f868de7f6502cbf8f5ccab926d42c2a9c Mon Sep 17 00:00:00 2001 From: Aliaksandr Bahdanau Date: Thu, 4 Jul 2024 16:49:26 +0300 Subject: [PATCH 08/17] document jsonRPC handler --- ton-http-api/pyTON/api/api_v3/endpoints/common.py | 3 ++- ton-http-api/pyTON/schemas/http.py | 10 +++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/ton-http-api/pyTON/api/api_v3/endpoints/common.py b/ton-http-api/pyTON/api/api_v3/endpoints/common.py index d83a3eb..83d21ed 100644 --- a/ton-http-api/pyTON/api/api_v3/endpoints/common.py +++ b/ton-http-api/pyTON/api/api_v3/endpoints/common.py @@ -613,7 +613,8 @@ async def jsonrpc_handler(json_rpc: TonRequestJsonRPC, background_tasks: BackgroundTasks, tonlib: TonlibManager = Depends(tonlib_dep)): """ - All methods in the API are available through JSON-RPC protocol ([spec](https://www.jsonrpc.org/specification)). + All methods in the API are available through JSON-RPC protocol ([spec](https://www.jsonrpc.org/specification)). + The type of the "result" field is the same as `result` field in original method. """ params = json_rpc.params method = json_rpc.method diff --git a/ton-http-api/pyTON/schemas/http.py b/ton-http-api/pyTON/schemas/http.py index 13b5e75..c2556b8 100644 --- a/ton-http-api/pyTON/schemas/http.py +++ b/ton-http-api/pyTON/schemas/http.py @@ -1,6 +1,6 @@ -from typing import Optional, TypeVar, Union +from typing import Optional, TypeVar, Union, Literal from pydantic.generics import GenericModel, Generic -from pydantic import BaseModel +from pydantic import BaseModel, Field ResultT = TypeVar('ResultT') @@ -31,11 +31,11 @@ class DeprecatedTonResponseJsonRPC(BaseModel): error: Optional[str] = None code: Optional[int] = None id: str - jsonrpc: str = "2.0" + jsonrpc: Literal['2.0'] = "2.0" class TonRequestJsonRPC(BaseModel): - method: str - params: dict = {} + method: str = Field(example='runGetMethod') + params: dict = Field({}, example={"address": "kQAl8r8c6Pg-0MD9c-onqsdwk83PkAx1Cwcd9_sCiOAZsoUE", "method": "get_jetton_data", "stack": [] } ) id: Optional[str] = None jsonrpc: Optional[str] = None From 38bac22b8b2c67b247767e66a1d5a843ae9b9f9a Mon Sep 17 00:00:00 2001 From: Gleb Karavatski Date: Thu, 4 Jul 2024 16:53:12 +0300 Subject: [PATCH 09/17] refactor objects --- .../pyTON/api/api_v2/endpoints/common.py | 20 ++--- .../pyTON/api/api_v3/endpoints/common.py | 5 +- ton-http-api/pyTON/schemas/__init__.py | 7 +- ton-http-api/pyTON/schemas/http.py | 81 ------------------- ton-http-api/pyTON/schemas/ton.py | 43 +++++++--- 5 files changed, 51 insertions(+), 105 deletions(-) diff --git a/ton-http-api/pyTON/api/api_v2/endpoints/common.py b/ton-http-api/pyTON/api/api_v2/endpoints/common.py index 8378e55..cb4e1d2 100644 --- a/ton-http-api/pyTON/api/api_v2/endpoints/common.py +++ b/ton-http-api/pyTON/api/api_v2/endpoints/common.py @@ -14,12 +14,14 @@ from fastapi.exceptions import HTTPException from pyTON.schemas import ( - TonResponse, + TonResponse, + TonResponseGeneric, DeprecatedTonResponseJsonRPC, TonRequestJsonRPC, - TonResponseGetBlockTransactions, - TonResponseGetTransactions, - TonTryLocateTx + ShortTransactions, + TransactionWAddressId, + TransactionId, + Transaction ) from pyTON.core.tonlib.manager import TonlibManager from pyTON.api.deps.ton import tonlib_dep, settings_dep @@ -161,7 +163,7 @@ async def get_wallet_information( wallet_handler["data_extractor"](res, result) return res -@router.get('/getTransactions', response_model=TonResponseGetTransactions, response_model_exclude_none=True, tags=['accounts', 'transactions']) +@router.get('/getTransactions', response_model=TonResponseGeneric[List[Transaction[TransactionWAddressId]]], response_model_exclude_none=True, tags=['accounts', 'transactions']) @json_rpc('getTransactions') @wrap_result async def get_transactions( @@ -306,7 +308,7 @@ async def get_shards( """ return await tonlib.getShards(seqno) -@router.get('/getBlockTransactions', response_model=TonResponseGetBlockTransactions, response_model_exclude_none=True, tags=['blocks','transactions']) +@router.get('/getBlockTransactions', response_model=TonResponseGeneric[ShortTransactions], response_model_exclude_none=True, tags=['blocks','transactions']) @json_rpc('getBlockTransactions') @wrap_result async def get_block_transactions( @@ -367,7 +369,7 @@ async def get_token_data( address = prepare_address(address) return await tonlib.get_token_data(address) -@router.get('/tryLocateTx', response_model=TonTryLocateTx, response_model_exclude_none=True, tags=['transactions']) +@router.get('/tryLocateTx', response_model=TonResponseGeneric[Transaction[TransactionId]], response_model_exclude_none=True, tags=['transactions']) @json_rpc('tryLocateTx') @wrap_result async def get_try_locate_tx( @@ -381,7 +383,7 @@ async def get_try_locate_tx( """ return await tonlib.tryLocateTxByIncomingMessage(source, destination, created_lt) -@router.get('/tryLocateResultTx', response_model=TonTryLocateTx, response_model_exclude_none=True, tags=['transactions']) +@router.get('/tryLocateResultTx', response_model=TonResponseGeneric[Transaction[TransactionId]], response_model_exclude_none=True, tags=['transactions']) @json_rpc('tryLocateResultTx') @wrap_result async def get_try_locate_result_tx( @@ -395,7 +397,7 @@ async def get_try_locate_result_tx( """ return await tonlib.tryLocateTxByIncomingMessage(source, destination, created_lt) -@router.get('/tryLocateSourceTx', response_model=TonTryLocateTx, response_model_exclude_none=True, tags=['transactions']) +@router.get('/tryLocateSourceTx', response_model=TonResponseGeneric[Transaction[TransactionId]], response_model_exclude_none=True, tags=['transactions']) @json_rpc('tryLocateSourceTx') @wrap_result async def get_try_locate_source_tx( diff --git a/ton-http-api/pyTON/api/api_v3/endpoints/common.py b/ton-http-api/pyTON/api/api_v3/endpoints/common.py index d83a3eb..a70e74b 100644 --- a/ton-http-api/pyTON/api/api_v3/endpoints/common.py +++ b/ton-http-api/pyTON/api/api_v3/endpoints/common.py @@ -22,7 +22,8 @@ BlockHeader, check_tonlib_type, SerializedBoc, - Transaction + Transaction, + TransactionWAddressId ) from pyTON.core.tonlib.manager import TonlibManager from pyTON.api.deps.ton import tonlib_dep, settings_dep @@ -166,7 +167,7 @@ async def get_contract_information( # wallet_handler["data_extractor"](res, result) # return res -@router.get('/getTransactions', response_model=List[Transaction], response_model_exclude_none=True, tags=['accounts', 'transactions']) +@router.get('/getTransactions', response_model=List[Transaction[TransactionWAddressId]], response_model_exclude_none=True, tags=['accounts', 'transactions']) @json_rpc('getTransactions') async def get_transactions( address: str = Query(..., description="Identifier of target TON account in any form."), diff --git a/ton-http-api/pyTON/schemas/__init__.py b/ton-http-api/pyTON/schemas/__init__.py index 56edc6b..efd9ad1 100644 --- a/ton-http-api/pyTON/schemas/__init__.py +++ b/ton-http-api/pyTON/schemas/__init__.py @@ -3,10 +3,7 @@ TonResponse, TonResponseGeneric, TonResponseJsonRPC, - DeprecatedTonResponseJsonRPC, - TonResponseGetBlockTransactions, - TonResponseGetTransactions, - TonTryLocateTx + DeprecatedTonResponseJsonRPC ) from .ton import ( BlockId, @@ -21,6 +18,8 @@ Message, TransactionId, Transaction, + ShortTransactions, + TransactionWAddressId, check_tonlib_type, address_state ) diff --git a/ton-http-api/pyTON/schemas/http.py b/ton-http-api/pyTON/schemas/http.py index 136c9c7..5191c18 100644 --- a/ton-http-api/pyTON/schemas/http.py +++ b/ton-http-api/pyTON/schemas/http.py @@ -16,87 +16,6 @@ class TonResponseGeneric(GenericModel, Generic[ResultT]): class TonResponse(TonResponseGeneric[Union[str, list, dict, None]]): pass -class ShortTransaction(BaseModel): - type: Literal['blocks.shortTxId'] = Field(alias="@type") - mode: int - account: str - lt: str - hash: str - -class Id(BaseModel): - type: Literal['ton.blockIdExt'] = Field(alias="@type") - workchain: int - shard: str - seqno: int - root_hash: str - file_hash: str - -class ResponseShortTransaction(BaseModel): - type: Literal['blocks.transactions'] = Field(alias="@type") - id: Id - req_count: int - incomplete: bool - transactions: List[ShortTransaction] - type: Literal['string'] = Field(alias="@extra") - -class TonResponseGetBlockTransactions(BaseModel): - ok: bool - result: Optional[ResponseShortTransaction] - error: Optional[str] = None - code: Optional[int] = None - -class Address(BaseModel): - type: Literal['accountAddress'] = Field(alias="@type") - account_address: str - -class TransactionId(BaseModel): - type: Literal['internal.transactionId'] = Field(alias="@type") - account_address: Optional[str] - lt: str - hash: str - -class MsgData(BaseModel): - type: Literal['msg.dataRaw'] = Field(alias="@type") - body: str - init_state: str - -class Msg(BaseModel): - type: Literal['raw.message'] = Field(alias="@type") - source: str - destination: str - value: str - fwd_fee: str - ihr_fee: str - created_lt: str - body_hash: str - msg_data: MsgData - message: str - - -class ResponseAddressTransaction(BaseModel): - type: Literal['raw.transaction'] = Field(alias="@type") - address: Address - utime: int - data: str - transaction_id: TransactionId - fee: str - storage_fee: str - other_fee: str - in_msg: Msg - out_msgs: List[Msg] - - -class TonResponseGetTransactions(BaseModel): - ok: bool - result: Optional[List[ResponseAddressTransaction]] - error: Optional[str] = None - code: Optional[int] = None - -class TonTryLocateTx(BaseModel): - ok: bool - result: Optional[ResponseAddressTransaction] - error: Optional[str] = None - code: Optional[int] = None class TonResponseJsonRPC(BaseModel): id: str diff --git a/ton-http-api/pyTON/schemas/ton.py b/ton-http-api/pyTON/schemas/ton.py index f63cb23..9774f24 100644 --- a/ton-http-api/pyTON/schemas/ton.py +++ b/ton-http-api/pyTON/schemas/ton.py @@ -1,9 +1,11 @@ -from typing import List, Optional, Literal -from pydantic import BaseModel +from typing import List, Optional, Literal, TypeVar +from pydantic import BaseModel, Field +from pydantic.generics import GenericModel, Generic from pytonlib.utils.wallet import wallets as known_wallets, sha256 +ResultT = TypeVar('ResultT') def check_tonlib_type(tl_obj: dict, expected_type: str): tl_type = tl_obj.get('@type', '') @@ -21,6 +23,7 @@ def address_state(account_info): class BlockId(BaseModel): + type: Literal['ton.blockIdExt'] = Field(alias="@type") workchain: int shard: str seqno: int @@ -184,6 +187,7 @@ def build_from_config(tl_obj: dict): class MsgDataRaw(BaseModel): + type: Literal['msg.dataRaw'] = Field(alias="@type") body: str init_state: str @@ -194,6 +198,7 @@ def build(tl_obj: dict): class Message(BaseModel): + type: Literal['raw.message'] = Field(alias="@type") source: str destination: str value: int @@ -219,9 +224,9 @@ def build(tl_obj: dict): comment=tl_obj.get('comment'), op=tl_obj.get('op'), ) - - + class TransactionId(BaseModel): + type: Literal['internal.transactionId'] = Field(alias="@type") lt: int hash: str @@ -230,12 +235,19 @@ def build(tl_obj: dict): return TransactionId(lt=int(tl_obj['lt']), hash=tl_obj['hash']) +class TransactionWAddressId(TransactionId): + account_address: str + +class Address(BaseModel): + type: Literal['accountAddress'] = Field(alias="@type") + account_address: str -class Transaction(BaseModel): +class Transaction(GenericModel, Generic[ResultT]): + type: Literal['raw.transaction'] = Field(alias="@type") + address: Address utime: int data: str - hash: str - lt: str + transaction_id: ResultT fee: int storage_fee: int other_fee: int @@ -247,11 +259,24 @@ def build(tl_obj: dict): return Transaction(utime=int(tl_obj['utime']), data=tl_obj['data'], - hash=tl_obj['transaction_id']['hash'], - lt=tl_obj['transaction_id']['lt'], + transaction_id=tl_obj['transaction_id'], fee=int(tl_obj['fee']), storage_fee=int(tl_obj['storage_fee']), other_fee=int(tl_obj['other_fee']), in_msg=Message.build(tl_obj.get('in_msg')) if tl_obj.get('in_msg') else None, out_msgs=[Message.build(m) for m in tl_obj['out_msgs']] ) + +class ShortTransaction(BaseModel): + type: Literal['blocks.shortTxId'] = Field(alias="@type") + mode: int + account: str + lt: str + hash: str + +class ShortTransactions(BaseModel): + type: Literal['blocks.transactions'] = Field(alias="@type") + id: BlockId + req_count: int + incomplete: bool + transactions: List[ShortTransaction] From 4f6eae20e05f86a150a247fe7a710dc1cb825b3a Mon Sep 17 00:00:00 2001 From: Aliaksandr Bahdanau Date: Fri, 5 Jul 2024 11:05:13 +0300 Subject: [PATCH 10/17] document config params --- ton-http-api/pyTON/api/api_v2/endpoints/common.py | 6 ++++-- ton-http-api/pyTON/schemas/__init__.py | 3 ++- ton-http-api/pyTON/schemas/http.py | 5 +++++ ton-http-api/pyTON/schemas/ton.py | 13 ++++++++++++- 4 files changed, 23 insertions(+), 4 deletions(-) diff --git a/ton-http-api/pyTON/api/api_v2/endpoints/common.py b/ton-http-api/pyTON/api/api_v2/endpoints/common.py index 6670db8..77c1c1d 100644 --- a/ton-http-api/pyTON/api/api_v2/endpoints/common.py +++ b/ton-http-api/pyTON/api/api_v2/endpoints/common.py @@ -16,7 +16,8 @@ from pyTON.schemas import ( TonResponse, DeprecatedTonResponseJsonRPC, - TonRequestJsonRPC + TonRequestJsonRPC, + GetConfigParamResponse, ) from pyTON.core.tonlib.manager import TonlibManager from pyTON.api.deps.ton import tonlib_dep, settings_dep @@ -338,7 +339,7 @@ async def get_block_header( """ return await tonlib.getBlockHeader(workchain, shard, seqno, root_hash, file_hash) -@router.get('/getConfigParam', response_model=TonResponse, response_model_exclude_none=True, tags=['get config']) +@router.get('/getConfigParam', response_model=GetConfigParamResponse, response_model_exclude_none=True, tags=['get config']) @json_rpc('getConfigParam') @wrap_result async def get_config_param( @@ -348,6 +349,7 @@ async def get_config_param( ): """ Get config by id. + Information about cell content can be found [in TL-B definitions](https://github.com/ton-blockchain/ton/blob/5c392e0f2d946877bb79a09ed35068f7b0bd333a/crypto/block/block.tlb#L593). """ return await tonlib.get_config_param(config_id, seqno) diff --git a/ton-http-api/pyTON/schemas/__init__.py b/ton-http-api/pyTON/schemas/__init__.py index ae75d85..ebaaef6 100644 --- a/ton-http-api/pyTON/schemas/__init__.py +++ b/ton-http-api/pyTON/schemas/__init__.py @@ -3,7 +3,8 @@ TonResponse, TonResponseGeneric, TonResponseJsonRPC, - DeprecatedTonResponseJsonRPC + DeprecatedTonResponseJsonRPC, + GetConfigParamResponse ) from .ton import ( BlockId, diff --git a/ton-http-api/pyTON/schemas/http.py b/ton-http-api/pyTON/schemas/http.py index 13b5e75..a91126f 100644 --- a/ton-http-api/pyTON/schemas/http.py +++ b/ton-http-api/pyTON/schemas/http.py @@ -2,6 +2,7 @@ from pydantic.generics import GenericModel, Generic from pydantic import BaseModel +from .ton import ConfigInfo ResultT = TypeVar('ResultT') @@ -39,3 +40,7 @@ class TonRequestJsonRPC(BaseModel): params: dict = {} id: Optional[str] = None jsonrpc: Optional[str] = None + + +class GetConfigParamResponse(TonResponseGeneric[ConfigInfo]): + pass diff --git a/ton-http-api/pyTON/schemas/ton.py b/ton-http-api/pyTON/schemas/ton.py index f63cb23..f6f6794 100644 --- a/ton-http-api/pyTON/schemas/ton.py +++ b/ton-http-api/pyTON/schemas/ton.py @@ -1,6 +1,6 @@ from typing import List, Optional, Literal -from pydantic import BaseModel +from pydantic import BaseModel, Field from pytonlib.utils.wallet import wallets as known_wallets, sha256 @@ -255,3 +255,14 @@ def build(tl_obj: dict): in_msg=Message.build(tl_obj.get('in_msg')) if tl_obj.get('in_msg') else None, out_msgs=[Message.build(m) for m in tl_obj['out_msgs']] ) + + +class TVMCell(BaseModel): + type: Literal['tvm.cell'] = Field(alias='@type') + bytes: str + + +class ConfigInfo(BaseModel): + type: Literal['configInfo'] = Field(alias='@type') + config: TVMCell + extra: str = Field(alias='@extra') From 4b31bdf82b09d263f82f7ad3595a963d87f19c6f Mon Sep 17 00:00:00 2001 From: Full-Hat Date: Mon, 8 Jul 2024 13:52:13 +0300 Subject: [PATCH 11/17] Add responses schemas and use them for API v2 --- .../pyTON/api/api_v2/endpoints/common.py | 437 +++++++++++------- ton-http-api/pyTON/schemas/__init__.py | 13 +- ton-http-api/pyTON/schemas/http.py | 240 +++++++++- 3 files changed, 522 insertions(+), 168 deletions(-) diff --git a/ton-http-api/pyTON/api/api_v2/endpoints/common.py b/ton-http-api/pyTON/api/api_v2/endpoints/common.py index 6670db8..ed103ae 100644 --- a/ton-http-api/pyTON/api/api_v2/endpoints/common.py +++ b/ton-http-api/pyTON/api/api_v2/endpoints/common.py @@ -14,9 +14,20 @@ from fastapi.exceptions import HTTPException from pyTON.schemas import ( - TonResponse, + TonResponse, DeprecatedTonResponseJsonRPC, - TonRequestJsonRPC + TonRequestJsonRPC, + get_get_address_information_error_responses, + GetAddressInformationResponse, + GetExtendedAddressInformationResponse, + GetWalletInformationResponse, + GetTransactionsResponse, + GetAddressBalanceResponse, + GetAddressStateResponse, + PackAddressResponse, + UnpackAddressResponse, + GetTokenDataResponse, + DetectAddressResponse ) from pyTON.core.tonlib.manager import TonlibManager from pyTON.api.deps.ton import tonlib_dep, settings_dep @@ -29,7 +40,6 @@ from loguru import logger - router = APIRouter() settings = settings_dep() @@ -63,11 +73,13 @@ def wrap_result(func): async def wrapper(*args, **kwargs): result = await asyncio.wait_for(func(*args, **kwargs), settings.tonlib.request_timeout) return TonResponse(ok=True, result=result) + return wrapper json_rpc_methods = {} + def json_rpc(method): def g(func): @wraps(func) @@ -77,7 +89,7 @@ def f(**kwargs): # Add function's default value parameters to kwargs. if k not in kwargs and v.default is not inspect._empty: default_val = v.default - + if isinstance(default_val, Param) or isinstance(default_val, Body): if default_val.default == ...: raise TypeError("Non-optional argument expected") @@ -97,16 +109,21 @@ def f(**kwargs): json_rpc_methods[method] = f return func + return g -@router.get('/getAddressInformation', response_model=TonResponse, response_model_exclude_none=True, tags=['accounts']) +@router.get('/getAddressInformation', + response_model=GetAddressInformationResponse, + responses=get_get_address_information_error_responses(), + response_model_exclude_none=True, + tags=['accounts']) @json_rpc('getAddressInformation') @wrap_result async def get_address_information( - address: str = Query(..., description="Identifier of target TON account in any form."), - tonlib: TonlibManager = Depends(tonlib_dep) - ): + address: str = Query(..., description="Identifier of target TON account in any form."), + tonlib: TonlibManager = Depends(tonlib_dep) +): """ Get basic information about the address: balance, code, data, last_transaction_id. """ @@ -119,13 +136,18 @@ async def get_address_information( result["suspended"] = True return result -@router.get('/getExtendedAddressInformation', response_model=TonResponse, response_model_exclude_none=True, tags=['accounts']) + +@router.get('/getExtendedAddressInformation', + response_model=GetExtendedAddressInformationResponse, + responses=get_get_address_information_error_responses(), + response_model_exclude_none=True, + tags=['accounts']) @json_rpc('getExtendedAddressInformation') @wrap_result async def get_extended_address_information( - address: str = Query(..., description="Identifier of target TON account in any form."), - tonlib: TonlibManager = Depends(tonlib_dep) - ): + address: str = Query(..., description="Identifier of target TON account in any form."), + tonlib: TonlibManager = Depends(tonlib_dep) +): """ Similar to previous one but tries to parse additional information for known contract types. This method is based on tonlib's function *getAccountState*. For detecting wallets we recommend to use *getWalletInformation*. """ @@ -133,13 +155,18 @@ async def get_extended_address_information( result = await tonlib.generic_get_account_state(address) return result -@router.get('/getWalletInformation', response_model=TonResponse, response_model_exclude_none=True, tags=['accounts']) + +@router.get('/getWalletInformation', + response_model=GetWalletInformationResponse, + responses=get_get_address_information_error_responses(), + response_model_exclude_none=True, + tags=['accounts']) @json_rpc('getWalletInformation') @wrap_result async def get_wallet_information( - address: str = Query(..., description="Identifier of target TON account in any form."), - tonlib: TonlibManager = Depends(tonlib_dep) - ): + address: str = Query(..., description="Identifier of target TON account in any form."), + tonlib: TonlibManager = Depends(tonlib_dep) +): """ Retrieve wallet information. This method parses contract state and currently supports more wallet types than getExtendedAddressInformation: simple wallet, standart wallet, v3 wallet, v4 wallet. """ @@ -158,31 +185,47 @@ async def get_wallet_information( wallet_handler["data_extractor"](res, result) return res -@router.get('/getTransactions', response_model=TonResponse, response_model_exclude_none=True, tags=['accounts', 'transactions']) + +@router.get('/getTransactions', + response_model=GetTransactionsResponse, + responses=get_get_address_information_error_responses(), + response_model_exclude_none=True, + tags=['accounts', 'transactions']) @json_rpc('getTransactions') @wrap_result async def get_transactions( - address: str = Query(..., description="Identifier of target TON account in any form."), - limit: Optional[int] = Query(default=10, description="Maximum number of transactions in response.", gt=0, le=100), - lt: Optional[int] = Query(default=None, description="Logical time of transaction to start with, must be sent with *hash*."), - hash: Optional[str] = Query(default=None, description="Hash of transaction to start with, in *base64* or *hex* encoding , must be sent with *lt*."), - to_lt: Optional[int] = Query(default=0, description="Logical time of transaction to finish with (to get tx from *lt* to *to_lt*)."), - archival: bool = Query(default=False, description="By default getTransaction request is processed by any available liteserver. If *archival=true* only liteservers with full history are used."), - tonlib: TonlibManager = Depends(tonlib_dep) - ): + address: str = Query(..., description="Identifier of target TON account in any form."), + limit: Optional[int] = Query(default=10, description="Maximum number of transactions in response.", gt=0, + le=100), + lt: Optional[int] = Query(default=None, + description="Logical time of transaction to start with, must be sent with *hash*."), + hash: Optional[str] = Query(default=None, + description="Hash of transaction to start with, in *base64* or *hex* encoding , must be sent with *lt*."), + to_lt: Optional[int] = Query(default=0, + description="Logical time of transaction to finish with (to get tx from *lt* to *to_lt*)."), + archival: bool = Query(default=False, + description="By default getTransaction request is processed by any available liteserver. If *archival=true* only liteservers with full history are used."), + tonlib: TonlibManager = Depends(tonlib_dep) +): """ Get transaction history of a given address. """ address = prepare_address(address) - return await tonlib.get_transactions(address, from_transaction_lt=lt, from_transaction_hash=hash, to_transaction_lt=to_lt, limit=limit, archival=archival) + return await tonlib.get_transactions(address, from_transaction_lt=lt, from_transaction_hash=hash, + to_transaction_lt=to_lt, limit=limit, archival=archival) + -@router.get('/getAddressBalance', response_model=TonResponse, response_model_exclude_none=True, tags=['accounts']) +@router.get('/getAddressBalance', + response_model=GetAddressBalanceResponse, + responses=get_get_address_information_error_responses(), + response_model_exclude_none=True, + tags=['accounts']) @json_rpc('getAddressBalance') @wrap_result async def get_address_balance( - address: str = Query(..., description="Identifier of target TON account in any form."), - tonlib: TonlibManager = Depends(tonlib_dep) - ): + address: str = Query(..., description="Identifier of target TON account in any form."), + tonlib: TonlibManager = Depends(tonlib_dep) +): """ Get balance (in nanotons) of a given address. """ @@ -192,13 +235,18 @@ async def get_address_balance( result["balance"] = 0 return result["balance"] -@router.get('/getAddressState', response_model=TonResponse, response_model_exclude_none=True, tags=['accounts']) + +@router.get('/getAddressState', + response_model=GetAddressStateResponse, + responses=get_get_address_information_error_responses(), + response_model_exclude_none=True, + tags=['accounts']) @json_rpc('getAddressState') @wrap_result async def get_address( - address: str = Query(..., description="Identifier of target TON account in any form."), - tonlib: TonlibManager = Depends(tonlib_dep) - ): + address: str = Query(..., description="Identifier of target TON account in any form."), + tonlib: TonlibManager = Depends(tonlib_dep) +): """ Get state of a given address. State can be either *unitialized*, *active* or *frozen*. """ @@ -206,60 +254,77 @@ async def get_address( result = await tonlib.raw_get_account_state(address) return address_state(result) -@router.get('/packAddress', response_model=TonResponse, response_model_exclude_none=True, tags=['accounts']) + +@router.get('/packAddress', + response_model=PackAddressResponse, + responses=get_get_address_information_error_responses(), + response_model_exclude_none=True, + tags=['accounts']) @json_rpc('packAddress') @wrap_result async def pack_address( - address: str = Query(..., description="Identifier of target TON account in raw form.", example="0:83DFD552E63729B472FCBCC8C45EBCC6691702558B68EC7527E1BA403A0F31A8"), - tonlib: TonlibManager = Depends(tonlib_dep) - ): + address: str = Query(..., description="Identifier of target TON account in raw form.", + example="0:83DFD552E63729B472FCBCC8C45EBCC6691702558B68EC7527E1BA403A0F31A8"), + tonlib: TonlibManager = Depends(tonlib_dep) +): """ Convert an address from raw to human-readable format. """ return prepare_address(address) -@router.get('/unpackAddress', response_model=TonResponse, response_model_exclude_none=True, tags=['accounts']) + +@router.get('/unpackAddress', + response_model=UnpackAddressResponse, + responses=get_get_address_information_error_responses(), + response_model_exclude_none=True, + tags=['accounts']) @json_rpc('unpackAddress') @wrap_result async def unpack_address( - address: str = Query(..., description="Identifier of target TON account in user-friendly form", example="EQCD39VS5jcptHL8vMjEXrzGaRcCVYto7HUn4bpAOg8xqB2N") - ): + address: str = Query(..., description="Identifier of target TON account in user-friendly form", + example="EQCD39VS5jcptHL8vMjEXrzGaRcCVYto7HUn4bpAOg8xqB2N") +): """ Convert an address from human-readable to raw format. """ return _detect_address(address)["raw_form"] + @router.get('/getMasterchainInfo', response_model=TonResponse, response_model_exclude_none=True, tags=['blocks']) @json_rpc('getMasterchainInfo') @wrap_result -async def get_masterchain_info(tonlib: TonlibManager=Depends(tonlib_dep)): +async def get_masterchain_info(tonlib: TonlibManager = Depends(tonlib_dep)): """ Get up-to-date masterchain state. """ return await tonlib.getMasterchainInfo() -@router.get('/getMasterchainBlockSignatures', response_model=TonResponse, response_model_exclude_none=True, tags=['blocks']) + +@router.get('/getMasterchainBlockSignatures', response_model=TonResponse, response_model_exclude_none=True, + tags=['blocks']) @json_rpc('getMasterchainBlockSignatures') @wrap_result async def get_masterchain_block_signatures( - seqno: int, - tonlib: TonlibManager=Depends(tonlib_dep) - ): + seqno: int, + tonlib: TonlibManager = Depends(tonlib_dep) +): """ Get up-to-date masterchain state. """ return await tonlib.getMasterchainBlockSignatures(seqno) + @router.get('/getShardBlockProof', response_model=TonResponse, response_model_exclude_none=True, tags=['blocks']) @json_rpc('getShardBlockProof') @wrap_result async def get_shard_block_proof( - workchain: int = Query(..., description="Block workchain id"), - shard: int = Query(..., description="Block shard id"), - seqno: int = Query(..., description="Block seqno"), - from_seqno: Optional[int] = Query(None, description="Seqno of masterchain block starting from which proof is required. If not specified latest masterchain block is used."), - tonlib: TonlibManager=Depends(tonlib_dep) - ): + workchain: int = Query(..., description="Block workchain id"), + shard: int = Query(..., description="Block shard id"), + seqno: int = Query(..., description="Block seqno"), + from_seqno: Optional[int] = Query(None, + description="Seqno of masterchain block starting from which proof is required. If not specified latest masterchain block is used."), + tonlib: TonlibManager = Depends(tonlib_dep) +): """ Get merkle proof of shardchain block. """ @@ -269,149 +334,169 @@ async def get_shard_block_proof( @router.get('/getConsensusBlock', response_model=TonResponse, response_model_exclude_none=True, tags=['blocks']) @json_rpc('getConsensusBlock') @wrap_result -async def get_consensus_block(tonlib: TonlibManager=Depends(tonlib_dep)): +async def get_consensus_block(tonlib: TonlibManager = Depends(tonlib_dep)): """ Get consensus block and its update timestamp. """ return await tonlib.getConsensusBlock() + @router.get('/lookupBlock', response_model=TonResponse, response_model_exclude_none=True, tags=['blocks']) @json_rpc('lookupBlock') @wrap_result async def lookup_block( - workchain: int = Query(..., description="Workchain id to look up block in"), - shard: int = Query(..., description="Shard id to look up block in"), - seqno: Optional[int] = Query(None, description="Block's height"), - lt: Optional[int] = Query(None, description="Block's logical time"), - unixtime: Optional[int] = Query(None, description="Block's unixtime"), - tonlib: TonlibManager = Depends(tonlib_dep) - ): + workchain: int = Query(..., description="Workchain id to look up block in"), + shard: int = Query(..., description="Shard id to look up block in"), + seqno: Optional[int] = Query(None, description="Block's height"), + lt: Optional[int] = Query(None, description="Block's logical time"), + unixtime: Optional[int] = Query(None, description="Block's unixtime"), + tonlib: TonlibManager = Depends(tonlib_dep) +): """ Look up block by either *seqno*, *lt* or *unixtime*. """ return await tonlib.lookupBlock(workchain, shard, seqno, lt, unixtime) + @router.get('/shards', response_model=TonResponse, response_model_exclude_none=True, tags=['blocks']) @json_rpc('shards') @wrap_result async def get_shards( - seqno: int = Query(..., description="Masterchain seqno to fetch shards of."), - tonlib: TonlibManager = Depends(tonlib_dep) - ): + seqno: int = Query(..., description="Masterchain seqno to fetch shards of."), + tonlib: TonlibManager = Depends(tonlib_dep) +): """ Get shards information. """ return await tonlib.getShards(seqno) -@router.get('/getBlockTransactions', response_model=TonResponse, response_model_exclude_none=True, tags=['blocks','transactions']) + +@router.get('/getBlockTransactions', response_model=TonResponse, response_model_exclude_none=True, + tags=['blocks', 'transactions']) @json_rpc('getBlockTransactions') @wrap_result async def get_block_transactions( - workchain: int, - shard: int, - seqno: int, - root_hash: Optional[str] = None, - file_hash: Optional[str] = None, - after_lt: Optional[int] = None, - after_hash: Optional[str] = None, - count: int = 40, - tonlib: TonlibManager = Depends(tonlib_dep) - ): + workchain: int, + shard: int, + seqno: int, + root_hash: Optional[str] = None, + file_hash: Optional[str] = None, + after_lt: Optional[int] = None, + after_hash: Optional[str] = None, + count: int = 40, + tonlib: TonlibManager = Depends(tonlib_dep) +): """ Get transactions of the given block. """ return await tonlib.getBlockTransactions(workchain, shard, seqno, count, root_hash, file_hash, after_lt, after_hash) + @router.get('/getBlockHeader', response_model=TonResponse, response_model_exclude_none=True, tags=['blocks']) @json_rpc('getBlockHeader') @wrap_result async def get_block_header( - workchain: int, - shard: int, - seqno: int, - root_hash: Optional[str] = None, - file_hash: Optional[str] = None, - tonlib: TonlibManager = Depends(tonlib_dep) - ): + workchain: int, + shard: int, + seqno: int, + root_hash: Optional[str] = None, + file_hash: Optional[str] = None, + tonlib: TonlibManager = Depends(tonlib_dep) +): """ Get metadata of a given block. """ return await tonlib.getBlockHeader(workchain, shard, seqno, root_hash, file_hash) + @router.get('/getConfigParam', response_model=TonResponse, response_model_exclude_none=True, tags=['get config']) @json_rpc('getConfigParam') @wrap_result async def get_config_param( - config_id: int = Query(..., description="Config id"), - seqno: Optional[int] = Query(None, description="Masterchain seqno. If not specified, latest blockchain state will be used."), - tonlib: TonlibManager = Depends(tonlib_dep) - ): + config_id: int = Query(..., description="Config id"), + seqno: Optional[int] = Query(None, + description="Masterchain seqno. If not specified, latest blockchain state will be used."), + tonlib: TonlibManager = Depends(tonlib_dep) +): """ Get config by id. """ return await tonlib.get_config_param(config_id, seqno) -@router.get('/getTokenData', response_model=TonResponse, response_model_exclude_none=True, tags=['accounts']) + +@router.get('/getTokenData', + response_model=GetTokenDataResponse, + responses=get_get_address_information_error_responses(), + response_model_exclude_none=True, + tags=['accounts']) @json_rpc('getTokenData') @wrap_result async def get_token_data( - address: str = Query(..., description="Address of NFT collection/item or Jetton master/wallet smart contract"), - tonlib: TonlibManager = Depends(tonlib_dep) - ): + address: str = Query(..., description="Address of NFT collection/item or Jetton master/wallet smart contract"), + tonlib: TonlibManager = Depends(tonlib_dep) +): """ Get NFT or Jetton information. """ address = prepare_address(address) return await tonlib.get_token_data(address) + @router.get('/tryLocateTx', response_model=TonResponse, response_model_exclude_none=True, tags=['transactions']) @json_rpc('tryLocateTx') @wrap_result async def get_try_locate_tx( - source: str, - destination: str, - created_lt: int, - tonlib: TonlibManager = Depends(tonlib_dep) - ): + source: str, + destination: str, + created_lt: int, + tonlib: TonlibManager = Depends(tonlib_dep) +): """ Locate outcoming transaction of *destination* address by incoming message. """ return await tonlib.tryLocateTxByIncomingMessage(source, destination, created_lt) + @router.get('/tryLocateResultTx', response_model=TonResponse, response_model_exclude_none=True, tags=['transactions']) @json_rpc('tryLocateResultTx') @wrap_result async def get_try_locate_result_tx( - source: str, - destination: str, - created_lt: int, - tonlib: TonlibManager = Depends(tonlib_dep) - ): + source: str, + destination: str, + created_lt: int, + tonlib: TonlibManager = Depends(tonlib_dep) +): """ Same as previous. Locate outcoming transaction of *destination* address by incoming message """ return await tonlib.tryLocateTxByIncomingMessage(source, destination, created_lt) + @router.get('/tryLocateSourceTx', response_model=TonResponse, response_model_exclude_none=True, tags=['transactions']) @json_rpc('tryLocateSourceTx') @wrap_result async def get_try_locate_source_tx( - source: str, - destination: str, - created_lt: int, - tonlib: TonlibManager = Depends(tonlib_dep) - ): + source: str, + destination: str, + created_lt: int, + tonlib: TonlibManager = Depends(tonlib_dep) +): """ Locate incoming transaction of *source* address by outcoming message. """ return await tonlib.tryLocateTxByOutcomingMessage(source, destination, created_lt) -@router.get('/detectAddress', response_model=TonResponse, response_model_exclude_none=True, tags=['accounts']) + +@router.get('/detectAddress', + response_model=DetectAddressResponse, + responses=get_get_address_information_error_responses(), + response_model_exclude_none=True, + tags=['accounts']) @json_rpc('detectAddress') @wrap_result async def detect_address( - address: str = Query(..., description="Identifier of target TON account in any form.") - ): + address: str = Query(..., description="Identifier of target TON account in any form.") +): """ Get all possible address forms. """ @@ -436,10 +521,10 @@ def send_boc_to_external_endpoint(boc): @json_rpc('sendBoc') @wrap_result async def send_boc( - background_tasks: BackgroundTasks, - boc: str = Body(..., embed=True, description="b64 encoded bag of cells"), - tonlib: TonlibManager = Depends(tonlib_dep), - ): + background_tasks: BackgroundTasks, + boc: str = Body(..., embed=True, description="b64 encoded bag of cells"), + tonlib: TonlibManager = Depends(tonlib_dep), +): """ Send serialized boc file: fully packed and serialized external message to blockchain. """ @@ -451,14 +536,15 @@ async def send_boc( background_tasks.add_task(send_boc_to_external_endpoint, base64.b64encode(boc).decode('utf8')) return res + @router.post('/sendBocReturnHash', response_model=TonResponse, response_model_exclude_none=True, tags=['send']) @json_rpc('sendBocReturnHash') @wrap_result async def send_boc_return_hash( - background_tasks: BackgroundTasks, - boc: str = Body(..., embed=True, description="b64 encoded bag of cells"), - tonlib: TonlibManager = Depends(tonlib_dep) - ): + background_tasks: BackgroundTasks, + boc: str = Body(..., embed=True, description="b64 encoded bag of cells"), + tonlib: TonlibManager = Depends(tonlib_dep) +): """ Send serialized boc file: fully packed and serialized external message to blockchain. The method returns message hash. """ @@ -470,6 +556,7 @@ async def send_boc_return_hash( background_tasks.add_task(send_boc_to_external_endpoint, base64.b64encode(boc).decode('utf8')) return res + async def send_boc_unsafe_task(boc_bytes: bytes, tonlib: TonlibManager): send_interval = 5 send_duration = 60 @@ -484,14 +571,16 @@ async def send_boc_unsafe_task(boc_bytes: bytes, tonlib: TonlibManager): pass await asyncio.sleep(send_interval) -@router.post('/sendBocUnsafe', response_model=TonResponse, response_model_exclude_none=True, include_in_schema=False, tags=['send']) + +@router.post('/sendBocUnsafe', response_model=TonResponse, response_model_exclude_none=True, include_in_schema=False, + tags=['send']) @json_rpc('sendBocUnsafe') @wrap_result async def send_boc_unsafe( - background_tasks: BackgroundTasks, - boc: str = Body(..., embed=True, description="b64 encoded bag of cells"), - tonlib: TonlibManager = Depends(tonlib_dep) - ): + background_tasks: BackgroundTasks, + boc: str = Body(..., embed=True, description="b64 encoded bag of cells"), + tonlib: TonlibManager = Depends(tonlib_dep) +): """ Unsafe send serialized boc file: fully packed and serialized external message to blockchain. This method creates background task that sends boc to network every 5 seconds for 1 minute. @@ -500,13 +589,15 @@ async def send_boc_unsafe( background_tasks.add_task(send_boc_unsafe_task, boc, tonlib) return {'@type': 'ok', '@extra': '0:0:0'} -@router.post('/sendCellSimple', response_model=TonResponse, response_model_exclude_none=True, include_in_schema=False, tags=['send']) + +@router.post('/sendCellSimple', response_model=TonResponse, response_model_exclude_none=True, include_in_schema=False, + tags=['send']) @json_rpc('sendCellSimple') @wrap_result async def send_cell( - cell: Dict[str, Any] = Body(..., embed=True, description="Cell serialized as object"), - tonlib: TonlibManager = Depends(tonlib_dep) - ): + cell: Dict[str, Any] = Body(..., embed=True, description="Cell serialized as object"), + tonlib: TonlibManager = Depends(tonlib_dep) +): """ (Deprecated) Send cell as object: `{"data": {"b64": "...", "len": int }, "refs": [...subcells...]}`, that is fully packed but not serialized external message. """ @@ -517,16 +608,17 @@ async def send_cell( raise HTTPException(status_code=400, detail="Error while parsing cell") return await tonlib.raw_send_message(boc) + @router.post('/sendQuery', response_model=TonResponse, response_model_exclude_none=True, tags=['send']) @json_rpc('sendQuery') @wrap_result async def send_query( - address: str = Body(..., description="Address in any format"), - body: str = Body(..., description="b64-encoded boc-serialized cell with message body"), - init_code: str = Body(default='', description="b64-encoded boc-serialized cell with init-code"), - init_data: str = Body(default='', description="b64-encoded boc-serialized cell with init-data"), - tonlib: TonlibManager = Depends(tonlib_dep) - ): + address: str = Body(..., description="Address in any format"), + body: str = Body(..., description="b64-encoded boc-serialized cell with message body"), + init_code: str = Body(default='', description="b64-encoded boc-serialized cell with init-code"), + init_data: str = Body(default='', description="b64-encoded boc-serialized cell with init-data"), + tonlib: TonlibManager = Depends(tonlib_dep) +): """ Send query - unpacked external message. This method takes address, body and init-params (if any), packs it to external message and sends to network. All params should be boc-serialized. """ @@ -536,16 +628,21 @@ async def send_query( data = codecs.decode(codecs.encode(init_data, "utf-8"), 'base64') return await tonlib.raw_create_and_send_query(address, body, init_code=code, init_data=data) -@router.post('/sendQuerySimple', response_model=TonResponse, response_model_exclude_none=True, include_in_schema=False, tags=['send']) + +@router.post('/sendQuerySimple', response_model=TonResponse, response_model_exclude_none=True, include_in_schema=False, + tags=['send']) @json_rpc('sendQuerySimple') @wrap_result async def send_query_cell( - address: str = Body(..., description="Address in any format"), - body: str = Body(..., description='Body cell as object: `{"data": {"b64": "...", "len": int }, "refs": [...subcells...]}`'), - init_code: Optional[Dict[str, Any]] = Body(default=None, description='init-code cell as object: `{"data": {"b64": "...", "len": int }, "refs": [...subcells...]}`'), - init_data: Optional[Dict[str, Any]] = Body(default=None, description='init-data cell as object: `{"data": {"b64": "...", "len": int }, "refs": [...subcells...]}`'), - tonlib: TonlibManager = Depends(tonlib_dep) - ): + address: str = Body(..., description="Address in any format"), + body: str = Body(..., + description='Body cell as object: `{"data": {"b64": "...", "len": int }, "refs": [...subcells...]}`'), + init_code: Optional[Dict[str, Any]] = Body(default=None, + description='init-code cell as object: `{"data": {"b64": "...", "len": int }, "refs": [...subcells...]}`'), + init_data: Optional[Dict[str, Any]] = Body(default=None, + description='init-data cell as object: `{"data": {"b64": "...", "len": int }, "refs": [...subcells...]}`'), + tonlib: TonlibManager = Depends(tonlib_dep) +): """ (Deprecated) Send query - unpacked external message. This method gets address, body and init-params (if any), packs it to external message and sends to network. Body, init-code and init-data should be passed as objects. """ @@ -561,17 +658,19 @@ async def send_query_cell( raise HTTPException(status_code=400, detail="Error while parsing cell object") return await tonlib.raw_create_and_send_query(address, body, init_code=qcode, init_data=qdata) + @router.post('/estimateFee', response_model=TonResponse, response_model_exclude_none=True, tags=['send']) @json_rpc('estimateFee') @wrap_result async def estimate_fee( - address: str = Body(..., description='Address in any format'), - body: str = Body(..., description='b64-encoded cell with message body'), - init_code: str = Body(default='', description='b64-encoded cell with init-code'), - init_data: str = Body(default='', description='b64-encoded cell with init-data'), - ignore_chksig: bool = Body(default=True, description='If true during test query processing assume that all chksig operations return True'), - tonlib: TonlibManager = Depends(tonlib_dep) - ): + address: str = Body(..., description='Address in any format'), + body: str = Body(..., description='b64-encoded cell with message body'), + init_code: str = Body(default='', description='b64-encoded cell with init-code'), + init_data: str = Body(default='', description='b64-encoded cell with init-data'), + ignore_chksig: bool = Body(default=True, + description='If true during test query processing assume that all chksig operations return True'), + tonlib: TonlibManager = Depends(tonlib_dep) +): """ Estimate fees required for query processing. *body*, *init-code* and *init-data* accepted in serialized format (b64-encoded). """ @@ -581,17 +680,23 @@ async def estimate_fee( data = codecs.decode(codecs.encode(init_data, "utf-8"), 'base64') return await tonlib.raw_estimate_fees(address, body, init_code=code, init_data=data, ignore_chksig=ignore_chksig) -@router.post('/estimateFeeSimple', response_model=TonResponse, response_model_exclude_none=True, include_in_schema=False, tags=['send']) + +@router.post('/estimateFeeSimple', response_model=TonResponse, response_model_exclude_none=True, + include_in_schema=False, tags=['send']) @json_rpc('estimateFeeSimple') @wrap_result async def estimate_fee_cell( - address: str = Body(..., description='Address in any format'), - body: Dict[str, Any] = Body(..., description='Body cell as object: `{"data": {"b64": "...", "len": int }, "refs": [...subcells...]}`'), - init_code: Optional[Dict[str, Any]] = Body(default=None, description='init-code cell as object: `{"data": {"b64": "...", "len": int }, "refs": [...subcells...]}`'), - init_data: Optional[Dict[str, Any]] = Body(default=None, description='init-data cell as object: `{"data": {"b64": "...", "len": int }, "refs": [...subcells...]}`'), - ignore_chksig: bool = Body(default=True, description='If true during test query processing assume that all chksig operations return True'), - tonlib: TonlibManager = Depends(tonlib_dep) - ): + address: str = Body(..., description='Address in any format'), + body: Dict[str, Any] = Body(..., + description='Body cell as object: `{"data": {"b64": "...", "len": int }, "refs": [...subcells...]}`'), + init_code: Optional[Dict[str, Any]] = Body(default=None, + description='init-code cell as object: `{"data": {"b64": "...", "len": int }, "refs": [...subcells...]}`'), + init_data: Optional[Dict[str, Any]] = Body(default=None, + description='init-data cell as object: `{"data": {"b64": "...", "len": int }, "refs": [...subcells...]}`'), + ignore_chksig: bool = Body(default=True, + description='If true during test query processing assume that all chksig operations return True'), + tonlib: TonlibManager = Depends(tonlib_dep) +): """ (Deprecated) Estimate fees required for query processing. *body*, *init-code* and *init-data* accepted in unserialized format (as objects). """ @@ -613,27 +718,28 @@ async def estimate_fee_cell( @json_rpc('runGetMethod') @wrap_result async def run_get_method( - address: str = Body(..., description='Contract address'), - method: Union[str, int] = Body(..., description='Method name or method id'), - stack: List[List[Any]] = Body(..., description="Array of stack elements: `[['num',3], ['cell', cell_object], ['slice', slice_object]]`"), - tonlib: TonlibManager = Depends(tonlib_dep) - ): + address: str = Body(..., description='Contract address'), + method: Union[str, int] = Body(..., description='Method name or method id'), + stack: List[List[Any]] = Body(..., + description="Array of stack elements: `[['num',3], ['cell', cell_object], ['slice', slice_object]]`"), + tonlib: TonlibManager = Depends(tonlib_dep) + ): """ Run get method on smart contract. """ address = prepare_address(address) return await tonlib.raw_run_method(address, method, stack) - if settings.webserver.json_rpc: - @router.post('/jsonRPC', response_model=DeprecatedTonResponseJsonRPC, response_model_exclude_none=True, tags=['json rpc']) - async def jsonrpc_handler(json_rpc: TonRequestJsonRPC, - request: Request, - response: Response, + @router.post('/jsonRPC', response_model=DeprecatedTonResponseJsonRPC, response_model_exclude_none=True, + tags=['json rpc']) + async def jsonrpc_handler(json_rpc: TonRequestJsonRPC, + request: Request, + response: Response, background_tasks: BackgroundTasks, tonlib: TonlibManager = Depends(tonlib_dep)): """ - All methods in the API are available through JSON-RPC protocol ([spec](https://www.jsonrpc.org/specification)). + All methods in the API are available through JSON-RPC protocol ([spec](https://www.jsonrpc.org/specification)). """ params = json_rpc.params method = json_rpc.method @@ -656,5 +762,6 @@ async def jsonrpc_handler(json_rpc: TonRequestJsonRPC, except TypeError as e: response.status_code = status.HTTP_422_UNPROCESSABLE_ENTITY return DeprecatedTonResponseJsonRPC(ok=False, error=f'TypeError: {e}', id=_id) - - return DeprecatedTonResponseJsonRPC(ok=result.ok, result=result.result, error=result.error, code=result.code, id=_id) + + return DeprecatedTonResponseJsonRPC(ok=result.ok, result=result.result, error=result.error, code=result.code, + id=_id) diff --git a/ton-http-api/pyTON/schemas/__init__.py b/ton-http-api/pyTON/schemas/__init__.py index ae75d85..2d7ac00 100644 --- a/ton-http-api/pyTON/schemas/__init__.py +++ b/ton-http-api/pyTON/schemas/__init__.py @@ -3,7 +3,18 @@ TonResponse, TonResponseGeneric, TonResponseJsonRPC, - DeprecatedTonResponseJsonRPC + DeprecatedTonResponseJsonRPC, + get_get_address_information_error_responses, + GetAddressInformationResponse, + GetExtendedAddressInformationResponse, + GetWalletInformationResponse, + GetTransactionsResponse, + GetAddressBalanceResponse, + GetAddressStateResponse, + PackAddressResponse, + UnpackAddressResponse, + GetTokenDataResponse, + DetectAddressResponse ) from .ton import ( BlockId, diff --git a/ton-http-api/pyTON/schemas/http.py b/ton-http-api/pyTON/schemas/http.py index 13b5e75..1e58202 100644 --- a/ton-http-api/pyTON/schemas/http.py +++ b/ton-http-api/pyTON/schemas/http.py @@ -1,7 +1,7 @@ -from typing import Optional, TypeVar, Union +from typing import Optional, TypeVar, Union, List from pydantic.generics import GenericModel, Generic from pydantic import BaseModel - +from pydantic import BaseModel, Field ResultT = TypeVar('ResultT') @@ -17,6 +17,242 @@ class TonResponse(TonResponseGeneric[Union[str, list, dict, None]]): pass +class ErrorGetAddressInformationResponses422(BaseModel): + ok: bool = Field(False) + error: str + code: int = Field(422) + + @staticmethod + def get_response(): + return {422: {"model": ErrorGetAddressInformationResponses422, 'description': 'Validation Error'}} + + +class ErrorGetAddressInformationResponses504(BaseModel): + ok: bool = Field(False) + error: str + code: int = Field(504) + + @staticmethod + def get_response(): + return {504: {"model": ErrorGetAddressInformationResponses504, 'description': 'Lite Server Timeout'}} + + +def get_get_address_information_error_responses(): + response = ErrorGetAddressInformationResponses422.get_response() + response.update(ErrorGetAddressInformationResponses504.get_response()) + return response + + +class GetAddressInformationResponse(BaseModel): + ok: bool = Field(True, description='Always true if there is no error') + + class AddressResult(BaseModel): + type: str = Field(alias="@type") + balance: str + code: str + data: str + + class LastTransactionId(BaseModel): + type: str = Field(alias="@type") + lt: str + hash: str + + last_transaction_id: LastTransactionId + + class BlockId(BaseModel): + type: str = Field(alias="@type") + workchain: int + shard: str + seqno: int + root_hash: str + file_hash: str + + block_id: BlockId + frozen_hash: str + sync_utime: int + extra: str = Field(alias="@extra") + state: str + + result: AddressResult + + +class GetExtendedAddressInformationResponse(BaseModel): + ok: bool = Field(True, description='Hello') + + class Result(BaseModel): + type: str = Field(alias="@type") + + class Address(BaseModel): + type: str = Field(alias="@type") + account_address: str + + address: Address + balance: str + + class LastTransactionId(BaseModel): + type: str = Field(alias="@type") + lt: str + hash: str + + last_transaction_id: LastTransactionId + + class BlockId(BaseModel): + type: str = Field(alias="@type") + workchain: int + shard: str + seqno: int + root_hash: str + file_hash: str + + block_id: BlockId + + sync_utime: int + + class AccountState(BaseModel): + type: str = Field(alias="@type") + wallet_id: str + seqno: int + + account_state: AccountState + + revision: int + extra: str = Field(alias="@extra") + + result: Result + + +class GetWalletInformationResponse(BaseModel): + ok: bool = Field(True) + + class Result(BaseModel): + wallet: bool + balance: str + account_state: str + wallet_type: str + seqno: int + + class LastTransactionId(BaseModel): + type: str = Field(alias="@type") + lt: str + hash: str + + last_transaction_id: LastTransactionId + wallet_id: int + + result: Result + + +class GetTransactionsResponse(BaseModel): + ok: bool = Field(True) + + class Transaction(BaseModel): + type: str = Field(alias="@type") + address: dict + utime: int + data: str + + class TransactionId(BaseModel): + type: str = Field(alias="@type") + lt: str + hash: str + + transaction_id: TransactionId + fee: str + storage_fee: str + other_fee: str + + class Message(BaseModel): + type: str = Field(alias="@type") + source: str + destination: str + value: str + fwd_fee: str + ihr_fee: str + created_lt: str + body_hash: str + + class MessageData(BaseModel): + type: str = Field(alias="@type") + body: str + init_state: Optional[str] = None + + msg_data: MessageData + message: Optional[str] = None + + in_msg: Message + out_msgs: List[Message] + + result: List[Transaction] + + +class GetAddressBalanceResponse(BaseModel): + ok: bool = Field(True) + result: str = Field('1234', description='str representation of number, balance of the contract') + + +class GetAddressStateResponse(BaseModel): + ok: bool = Field(True) + result: str = Field('nonexist/uninit/active/frozen', + description='State of the address, visit https://docs.ton.org/learn/overviews/addresses#addresses-state for more') + + +class PackAddressResponse(BaseModel): + ok: bool = Field(True) + result: str = Field('EQCD39VS5jcptHL8vMjEXrzGaRcCVYto7HUn4bpAOg8xqB2N', description='Packed address') + + +class UnpackAddressResponse(BaseModel): + ok: bool = Field(True) + result: str = Field('0:83dfd552e63729b472fcbcc8c45ebcc6691702558b68ec7527e1ba403a0f31a8', + description='Unpacked address') + + +class GetTokenDataResponse(BaseModel): + ok: bool = Field(True) + + class Result(BaseModel): + total_supply: int + mintable: bool + admin_address: str + + class JettonContent(BaseModel): + type: str = Field(alias="@type") + + class Data(BaseModel): + image: str + name: str + symbol: str + description: str + decimals: str + + data: Data + + jetton_content: JettonContent + + jetton_wallet_code: str + contract_type: str + + result: Result + + +class DetectAddressResponse(BaseModel): + ok: bool = Field(True) + + class Result(BaseModel): + raw_form: str + + class Bouncable(BaseModel): + b64: str + b64url: str + + bounceable: Bouncable + non_bounceable: Bouncable + given_type: str + test_only: bool + + result: Result + + class TonResponseJsonRPC(BaseModel): id: str jsonrpc: str = "2.0" From 9b699b62c1618dcb1f596cee0057a5515ac4a8cb Mon Sep 17 00:00:00 2001 From: Full-Hat Date: Mon, 8 Jul 2024 14:50:16 +0300 Subject: [PATCH 12/17] Add generics --- .../pyTON/api/api_v2/endpoints/common.py | 23 +- ton-http-api/pyTON/schemas/__init__.py | 3 +- ton-http-api/pyTON/schemas/http.py | 252 ++++++++---------- 3 files changed, 128 insertions(+), 150 deletions(-) diff --git a/ton-http-api/pyTON/api/api_v2/endpoints/common.py b/ton-http-api/pyTON/api/api_v2/endpoints/common.py index ed103ae..e46838b 100644 --- a/ton-http-api/pyTON/api/api_v2/endpoints/common.py +++ b/ton-http-api/pyTON/api/api_v2/endpoints/common.py @@ -27,7 +27,8 @@ PackAddressResponse, UnpackAddressResponse, GetTokenDataResponse, - DetectAddressResponse + DetectAddressResponse, + TonResponse200Generic ) from pyTON.core.tonlib.manager import TonlibManager from pyTON.api.deps.ton import tonlib_dep, settings_dep @@ -114,7 +115,7 @@ def f(**kwargs): @router.get('/getAddressInformation', - response_model=GetAddressInformationResponse, + response_model=TonResponse200Generic[GetAddressInformationResponse], responses=get_get_address_information_error_responses(), response_model_exclude_none=True, tags=['accounts']) @@ -138,7 +139,7 @@ async def get_address_information( @router.get('/getExtendedAddressInformation', - response_model=GetExtendedAddressInformationResponse, + response_model=TonResponse200Generic[GetExtendedAddressInformationResponse], responses=get_get_address_information_error_responses(), response_model_exclude_none=True, tags=['accounts']) @@ -157,7 +158,7 @@ async def get_extended_address_information( @router.get('/getWalletInformation', - response_model=GetWalletInformationResponse, + response_model=TonResponse200Generic[GetWalletInformationResponse], responses=get_get_address_information_error_responses(), response_model_exclude_none=True, tags=['accounts']) @@ -187,7 +188,7 @@ async def get_wallet_information( @router.get('/getTransactions', - response_model=GetTransactionsResponse, + response_model=TonResponse200Generic[List[GetTransactionsResponse]], responses=get_get_address_information_error_responses(), response_model_exclude_none=True, tags=['accounts', 'transactions']) @@ -216,7 +217,7 @@ async def get_transactions( @router.get('/getAddressBalance', - response_model=GetAddressBalanceResponse, + response_model=TonResponse200Generic[GetAddressBalanceResponse], responses=get_get_address_information_error_responses(), response_model_exclude_none=True, tags=['accounts']) @@ -237,7 +238,7 @@ async def get_address_balance( @router.get('/getAddressState', - response_model=GetAddressStateResponse, + response_model=TonResponse200Generic[GetAddressStateResponse], responses=get_get_address_information_error_responses(), response_model_exclude_none=True, tags=['accounts']) @@ -256,7 +257,7 @@ async def get_address( @router.get('/packAddress', - response_model=PackAddressResponse, + response_model=TonResponse200Generic[PackAddressResponse], responses=get_get_address_information_error_responses(), response_model_exclude_none=True, tags=['accounts']) @@ -274,7 +275,7 @@ async def pack_address( @router.get('/unpackAddress', - response_model=UnpackAddressResponse, + response_model=TonResponse200Generic[UnpackAddressResponse], responses=get_get_address_information_error_responses(), response_model_exclude_none=True, tags=['accounts']) @@ -425,7 +426,7 @@ async def get_config_param( @router.get('/getTokenData', - response_model=GetTokenDataResponse, + response_model=TonResponse200Generic[GetTokenDataResponse], responses=get_get_address_information_error_responses(), response_model_exclude_none=True, tags=['accounts']) @@ -488,7 +489,7 @@ async def get_try_locate_source_tx( @router.get('/detectAddress', - response_model=DetectAddressResponse, + response_model=TonResponse200Generic[DetectAddressResponse], responses=get_get_address_information_error_responses(), response_model_exclude_none=True, tags=['accounts']) diff --git a/ton-http-api/pyTON/schemas/__init__.py b/ton-http-api/pyTON/schemas/__init__.py index 2d7ac00..7643c81 100644 --- a/ton-http-api/pyTON/schemas/__init__.py +++ b/ton-http-api/pyTON/schemas/__init__.py @@ -14,7 +14,8 @@ PackAddressResponse, UnpackAddressResponse, GetTokenDataResponse, - DetectAddressResponse + DetectAddressResponse, + TonResponse200Generic ) from .ton import ( BlockId, diff --git a/ton-http-api/pyTON/schemas/http.py b/ton-http-api/pyTON/schemas/http.py index 1e58202..5ed655e 100644 --- a/ton-http-api/pyTON/schemas/http.py +++ b/ton-http-api/pyTON/schemas/http.py @@ -13,6 +13,11 @@ class TonResponseGeneric(GenericModel, Generic[ResultT]): code: Optional[int] = None +class TonResponse200Generic(GenericModel, Generic[ResultT]): + ok: bool = Field(True) + result: Optional[ResultT] + + class TonResponse(TonResponseGeneric[Union[str, list, dict, None]]): pass @@ -44,145 +49,126 @@ def get_get_address_information_error_responses(): class GetAddressInformationResponse(BaseModel): - ok: bool = Field(True, description='Always true if there is no error') + type: str = Field(alias="@type") + balance: str + code: str + data: str - class AddressResult(BaseModel): + class LastTransactionId(BaseModel): type: str = Field(alias="@type") - balance: str - code: str - data: str - - class LastTransactionId(BaseModel): - type: str = Field(alias="@type") - lt: str - hash: str + lt: str + hash: str - last_transaction_id: LastTransactionId + last_transaction_id: LastTransactionId - class BlockId(BaseModel): - type: str = Field(alias="@type") - workchain: int - shard: str - seqno: int - root_hash: str - file_hash: str - - block_id: BlockId - frozen_hash: str - sync_utime: int - extra: str = Field(alias="@extra") - state: str + class BlockId(BaseModel): + type: str = Field(alias="@type") + workchain: int + shard: str + seqno: int + root_hash: str + file_hash: str - result: AddressResult + block_id: BlockId + frozen_hash: str + sync_utime: int + extra: str = Field(alias="@extra") + state: str class GetExtendedAddressInformationResponse(BaseModel): - ok: bool = Field(True, description='Hello') + type: str = Field(alias="@type") - class Result(BaseModel): + class Address(BaseModel): type: str = Field(alias="@type") + account_address: str - class Address(BaseModel): - type: str = Field(alias="@type") - account_address: str + address: Address + balance: str - address: Address - balance: str - - class LastTransactionId(BaseModel): - type: str = Field(alias="@type") - lt: str - hash: str - - last_transaction_id: LastTransactionId + class LastTransactionId(BaseModel): + type: str = Field(alias="@type") + lt: str + hash: str - class BlockId(BaseModel): - type: str = Field(alias="@type") - workchain: int - shard: str - seqno: int - root_hash: str - file_hash: str + last_transaction_id: LastTransactionId - block_id: BlockId + class BlockId(BaseModel): + type: str = Field(alias="@type") + workchain: int + shard: str + seqno: int + root_hash: str + file_hash: str - sync_utime: int + block_id: BlockId - class AccountState(BaseModel): - type: str = Field(alias="@type") - wallet_id: str - seqno: int + sync_utime: int - account_state: AccountState + class AccountState(BaseModel): + type: str = Field(alias="@type") + wallet_id: str + seqno: int - revision: int - extra: str = Field(alias="@extra") + account_state: AccountState - result: Result + revision: int + extra: str = Field(alias="@extra") class GetWalletInformationResponse(BaseModel): - ok: bool = Field(True) - - class Result(BaseModel): - wallet: bool - balance: str - account_state: str - wallet_type: str - seqno: int - - class LastTransactionId(BaseModel): - type: str = Field(alias="@type") - lt: str - hash: str + wallet: bool + balance: str + account_state: str + wallet_type: str + seqno: int - last_transaction_id: LastTransactionId - wallet_id: int + class LastTransactionId(BaseModel): + type: str = Field(alias="@type") + lt: str + hash: str - result: Result + last_transaction_id: LastTransactionId + wallet_id: int +# should be list class GetTransactionsResponse(BaseModel): - ok: bool = Field(True) + type: str = Field(alias="@type") + address: dict + utime: int + data: str - class Transaction(BaseModel): + class TransactionId(BaseModel): type: str = Field(alias="@type") - address: dict - utime: int - data: str + lt: str + hash: str - class TransactionId(BaseModel): - type: str = Field(alias="@type") - lt: str - hash: str + transaction_id: TransactionId + fee: str + storage_fee: str + other_fee: str - transaction_id: TransactionId - fee: str - storage_fee: str - other_fee: str - - class Message(BaseModel): + class Message(BaseModel): + type: str = Field(alias="@type") + source: str + destination: str + value: str + fwd_fee: str + ihr_fee: str + created_lt: str + body_hash: str + + class MessageData(BaseModel): type: str = Field(alias="@type") - source: str - destination: str - value: str - fwd_fee: str - ihr_fee: str - created_lt: str - body_hash: str + body: str + init_state: Optional[str] = None - class MessageData(BaseModel): - type: str = Field(alias="@type") - body: str - init_state: Optional[str] = None + msg_data: MessageData + message: Optional[str] = None - msg_data: MessageData - message: Optional[str] = None - - in_msg: Message - out_msgs: List[Message] - - result: List[Transaction] + in_msg: Message + out_msgs: List[Message] class GetAddressBalanceResponse(BaseModel): @@ -208,49 +194,39 @@ class UnpackAddressResponse(BaseModel): class GetTokenDataResponse(BaseModel): - ok: bool = Field(True) - - class Result(BaseModel): - total_supply: int - mintable: bool - admin_address: str - - class JettonContent(BaseModel): - type: str = Field(alias="@type") + total_supply: int + mintable: bool + admin_address: str - class Data(BaseModel): - image: str - name: str - symbol: str - description: str - decimals: str + class JettonContent(BaseModel): + type: str = Field(alias="@type") - data: Data + class Data(BaseModel): + image: str + name: str + symbol: str + description: str + decimals: str - jetton_content: JettonContent + data: Data - jetton_wallet_code: str - contract_type: str + jetton_content: JettonContent - result: Result + jetton_wallet_code: str + contract_type: str class DetectAddressResponse(BaseModel): - ok: bool = Field(True) - - class Result(BaseModel): - raw_form: str - - class Bouncable(BaseModel): - b64: str - b64url: str + raw_form: str - bounceable: Bouncable - non_bounceable: Bouncable - given_type: str - test_only: bool + class Bouncable(BaseModel): + b64: str + b64url: str - result: Result + bounceable: Bouncable + non_bounceable: Bouncable + given_type: str + test_only: bool class TonResponseJsonRPC(BaseModel): From 6067625ae15867cc0f4695ce08e5fbdaebd68516 Mon Sep 17 00:00:00 2001 From: Aliaksandr Bahdanau Date: Wed, 10 Jul 2024 12:50:50 +0300 Subject: [PATCH 13/17] fix api --- .../pyTON/api/api_v2/endpoints/common.py | 17 +++++------------ ton-http-api/pyTON/schemas/http.py | 15 +++++++++------ ton-http-api/pyTON/schemas/ton.py | 18 +++++++++++++----- 3 files changed, 27 insertions(+), 23 deletions(-) diff --git a/ton-http-api/pyTON/api/api_v2/endpoints/common.py b/ton-http-api/pyTON/api/api_v2/endpoints/common.py index 9f4ee98..bf8d861 100644 --- a/ton-http-api/pyTON/api/api_v2/endpoints/common.py +++ b/ton-http-api/pyTON/api/api_v2/endpoints/common.py @@ -15,10 +15,7 @@ from pyTON.schemas import ( TonResponse, - TonResponse, - TonResponseGeneric, DeprecatedTonResponseJsonRPC, - TonRequestJsonRPC, get_get_address_information_error_responses, GetAddressInformationResponse, GetExtendedAddressInformationResponse, @@ -37,19 +34,15 @@ BlockId, ConsensusBlock, Shards, - ShortTransactions, BlockHeader, RunGetMethodResponse, AddressForms, - TonRequestJsonRPC, SendBocReturnHashResponse, EstimateFeeResponse, OkResponse, TonRequestJsonRPC, ShortTransactions, - TransactionWAddressId, TransactionId, - Transaction, ) from pyTON.core.tonlib.manager import TonlibManager from pyTON.api.deps.ton import tonlib_dep, settings_dep @@ -245,7 +238,7 @@ async def get_wallet_information( @router.get( "/getTransactions", - response_model=TonResponseGeneric[List[RawTransaction[TransactionWAddressId]]], + response_model=TonResponseGeneric[List[RawTransaction[TransactionId]]], response_model_exclude_none=True, tags=["accounts", "transactions"], ) @@ -295,7 +288,7 @@ async def get_transactions( @router.get( "/getAddressBalance", - response_model=TonResponse200Generic[GetAddressBalanceResponse], + response_model=GetAddressBalanceResponse, responses=get_get_address_information_error_responses(), response_model_exclude_none=True, tags=["accounts"], @@ -320,7 +313,7 @@ async def get_address_balance( @router.get( "/getAddressState", - response_model=TonResponse200Generic[GetAddressStateResponse], + response_model=GetAddressStateResponse, responses=get_get_address_information_error_responses(), response_model_exclude_none=True, tags=["accounts"], @@ -343,7 +336,7 @@ async def get_address( @router.get( "/packAddress", - response_model=TonResponse200Generic[PackAddressResponse], + response_model=PackAddressResponse, responses=get_get_address_information_error_responses(), response_model_exclude_none=True, tags=["accounts"], @@ -366,7 +359,7 @@ async def pack_address( @router.get( "/unpackAddress", - response_model=TonResponse200Generic[UnpackAddressResponse], + response_model=UnpackAddressResponse, responses=get_get_address_information_error_responses(), response_model_exclude_none=True, tags=["accounts"], diff --git a/ton-http-api/pyTON/schemas/http.py b/ton-http-api/pyTON/schemas/http.py index d6d07ca..7a9658e 100644 --- a/ton-http-api/pyTON/schemas/http.py +++ b/ton-http-api/pyTON/schemas/http.py @@ -12,11 +12,10 @@ AccountState, AddressShort, JettonContent, - Transaction, - RawTransaction, ) ResultT = TypeVar("ResultT") +ResultT2 = TypeVar("ResultT2") class TonResponseGeneric(GenericModel, Generic[ResultT]): @@ -26,9 +25,9 @@ class TonResponseGeneric(GenericModel, Generic[ResultT]): code: Optional[int] = None -class TonResponse200Generic(GenericModel, Generic[ResultT]): +class TonResponse200Generic(GenericModel, Generic[ResultT2]): ok: bool = Field(True) - result: Optional[ResultT] + result: Optional[ResultT2] ResultTypeT = TypeVar("ResultTypeT") @@ -99,6 +98,7 @@ class GetExtendedAddressInformationResponse(BaseModel): last_transaction_id: TransactionId block_id: BlockId sync_utime: int + # TODO: ACCOOUNT STATE INVALID (can be raw.accountState {code, data, frozenHash} (for different account differnt state) account_state: AccountState revision: int extra: str = Field(alias="@extra") @@ -109,9 +109,9 @@ class GetWalletInformationResponse(BaseModel): balance: str account_state: str wallet_type: str - seqno: int + seqno: Optional[int] last_transaction_id: TransactionId - wallet_id: int + wallet_id: Optional[int] class GetAddressBalanceResponse(BaseModel): @@ -145,6 +145,9 @@ class UnpackAddressResponse(BaseModel): ) + + +# TODO nft also can be class GetTokenDataResponse(BaseModel): total_supply: int mintable: bool diff --git a/ton-http-api/pyTON/schemas/ton.py b/ton-http-api/pyTON/schemas/ton.py index a1af621..cfbca1f 100644 --- a/ton-http-api/pyTON/schemas/ton.py +++ b/ton-http-api/pyTON/schemas/ton.py @@ -1,4 +1,4 @@ -from typing import List, Optional, Literal, TypeVar +from typing import List, Optional, Literal, TypeVar, Union from pydantic import BaseModel, Field from pydantic.generics import GenericModel, Generic @@ -200,7 +200,7 @@ class Proof(BaseModel): class ShardBlockProof(BaseModel): - type: Literal["blocks.blockSignatures"] = Field(alias="@type") + type: Literal["blocks.shardBlockProof"] = Field(alias="@type") from_id: BlockId = Field(alias="from") mc_id: BlockId links: List[str] @@ -261,6 +261,11 @@ def build(tl_obj: dict): return MsgDataRaw(body=tl_obj["body"], init_state=tl_obj["init_state"]) +class MsgDataText(BaseModel): + type: Literal["msg.dataText"] = Field(alias="@type") + text: str + + class Message(BaseModel): source: str destination: str @@ -269,7 +274,7 @@ class Message(BaseModel): ihr_fee: int created_lt: int body_hash: str - msg_data: MsgDataRaw + msg_data: Union[MsgDataRaw, MsgDataText] comment: Optional[str] op: Optional[int] @@ -338,12 +343,15 @@ def build(tl_obj: dict): ) -class RawTransaction(GenericModel, Generic[ResultT]): +RawResultT = TypeVar("RawResultT") + + +class RawTransaction(GenericModel, Generic[RawResultT]): type: Literal["raw.transaction"] = Field(alias="@type") address: Address utime: int data: str - transaction_id: ResultT + transaction_id: RawResultT fee: int storage_fee: int other_fee: int From 900007b723962daee2451e1e7dd3f75471c8b247 Mon Sep 17 00:00:00 2001 From: Full-Hat Date: Wed, 10 Jul 2024 15:43:33 +0300 Subject: [PATCH 14/17] Update GetExtendedAddressInformation & getTokenData responses TODO: add jetton wallet response for getTokenData endpoint --- .../pyTON/api/api_v2/endpoints/common.py | 4 +- ton-http-api/pyTON/schemas/__init__.py | 2 +- ton-http-api/pyTON/schemas/http.py | 24 +++---- ton-http-api/pyTON/schemas/ton.py | 62 ++++++++++++++++--- 4 files changed, 65 insertions(+), 27 deletions(-) diff --git a/ton-http-api/pyTON/api/api_v2/endpoints/common.py b/ton-http-api/pyTON/api/api_v2/endpoints/common.py index bf8d861..b8657af 100644 --- a/ton-http-api/pyTON/api/api_v2/endpoints/common.py +++ b/ton-http-api/pyTON/api/api_v2/endpoints/common.py @@ -24,7 +24,7 @@ GetAddressStateResponse, PackAddressResponse, UnpackAddressResponse, - GetTokenDataResponse, + TonGetTokenDataResponse, TonResponse200Generic, GetConfigParamResponse, TonResponseGeneric, @@ -564,7 +564,7 @@ async def get_config_param( @router.get( "/getTokenData", - response_model=TonResponse200Generic[GetTokenDataResponse], + response_model=TonGetTokenDataResponse, responses=get_get_address_information_error_responses(), response_model_exclude_none=True, tags=["accounts"], diff --git a/ton-http-api/pyTON/schemas/__init__.py b/ton-http-api/pyTON/schemas/__init__.py index c5c406a..b999efb 100644 --- a/ton-http-api/pyTON/schemas/__init__.py +++ b/ton-http-api/pyTON/schemas/__init__.py @@ -12,7 +12,7 @@ GetAddressStateResponse, PackAddressResponse, UnpackAddressResponse, - GetTokenDataResponse, + TonGetTokenDataResponse, TonResponse200Generic, GetConfigParamResponse, RunGetMethodResponse, diff --git a/ton-http-api/pyTON/schemas/http.py b/ton-http-api/pyTON/schemas/http.py index 7a9658e..1593098 100644 --- a/ton-http-api/pyTON/schemas/http.py +++ b/ton-http-api/pyTON/schemas/http.py @@ -3,15 +3,19 @@ from pydantic import BaseModel, Field from .ton import ( + AccountStateRow, + AccountStateUninited, + AccountStateWallet, ConfigInfo, TVMStackEntryType, TvmTuple, BlockIdExt, TransactionId, BlockId, - AccountState, AddressShort, - JettonContent, + JettonMasterData, + NftCollectionData, + NftItemData ) ResultT = TypeVar("ResultT") @@ -98,8 +102,7 @@ class GetExtendedAddressInformationResponse(BaseModel): last_transaction_id: TransactionId block_id: BlockId sync_utime: int - # TODO: ACCOOUNT STATE INVALID (can be raw.accountState {code, data, frozenHash} (for different account differnt state) - account_state: AccountState + account_state: Union[AccountStateWallet, AccountStateRow, AccountStateUninited] revision: int extra: str = Field(alias="@extra") @@ -145,16 +148,9 @@ class UnpackAddressResponse(BaseModel): ) - - -# TODO nft also can be -class GetTokenDataResponse(BaseModel): - total_supply: int - mintable: bool - admin_address: str - jetton_content: JettonContent - jetton_wallet_code: str - contract_type: str +class TonGetTokenDataResponse(BaseModel): + ok: bool = Field(True) + result: Union[JettonMasterData, NftCollectionData, NftItemData] class TonResponseJsonRPC(BaseModel): diff --git a/ton-http-api/pyTON/schemas/ton.py b/ton-http-api/pyTON/schemas/ton.py index cfbca1f..7a7658b 100644 --- a/ton-http-api/pyTON/schemas/ton.py +++ b/ton-http-api/pyTON/schemas/ton.py @@ -407,25 +407,67 @@ class BlockIdExt(BaseModel): TVMStackEntryType = Literal["cell", "slice", "num", "tuple", "list"] -class AccountState(BaseModel): - type: str = Field(alias="@type") +class AccountStateWallet(BaseModel): + type: str = Field('wallet.version.accountState', alias="@type") wallet_id: str seqno: int +class AccountStateRow(BaseModel): + type: str = Field('raw.accountState', alias="@type") + code: str + data: str + frozen_hash: str + + +class AccountStateUninited(BaseModel): + type: str = Field('uninited.accountState', alias="@type") + frozen_hash: str + + class AddressShort(BaseModel): type: str = Field(alias="@type") account_address: str -class JettonContent(BaseModel): +class JettonMasterData(BaseModel): + total_supply: int + mintable: bool + admin_address: str + + class JettonContent(BaseModel): + type: str = Field(alias="@type") + + class Data(BaseModel): + image: str + name: str + symbol: str + description: str + decimals: str + + data: Data + + jetton_content: JettonContent + jetton_wallet_code: str + contract_type: str = Field('jetton_master') + + +class NftContent(BaseModel): type: str = Field(alias="@type") + data: str + + +class NftCollectionData(BaseModel): + next_item_index: int + collection_content: NftContent + owner_address: str + contract_type: str = Field('nft_collection') - class Data(BaseModel): - image: str - name: str - symbol: str - description: str - decimals: str - data: Data +class NftItemData(BaseModel): + init: bool + index: int + owner_address: str + collection_address: str + content: NftContent + contract_type: str = Field('nft_item') From a017e65bdaed6a0f35e13e80b52741db3102f801 Mon Sep 17 00:00:00 2001 From: Full-Hat Date: Wed, 10 Jul 2024 16:01:21 +0300 Subject: [PATCH 15/17] Add Any to unions & update response model reference --- .../pyTON/api/api_v2/endpoints/common.py | 25 +++++++++---------- ton-http-api/pyTON/schemas/__init__.py | 1 - ton-http-api/pyTON/schemas/http.py | 12 ++++----- 3 files changed, 18 insertions(+), 20 deletions(-) diff --git a/ton-http-api/pyTON/api/api_v2/endpoints/common.py b/ton-http-api/pyTON/api/api_v2/endpoints/common.py index b8657af..9cbf0e3 100644 --- a/ton-http-api/pyTON/api/api_v2/endpoints/common.py +++ b/ton-http-api/pyTON/api/api_v2/endpoints/common.py @@ -27,7 +27,6 @@ TonGetTokenDataResponse, TonResponse200Generic, GetConfigParamResponse, - TonResponseGeneric, MasterchainInfo, MasterchainSignatures, ShardBlockProof, @@ -238,7 +237,7 @@ async def get_wallet_information( @router.get( "/getTransactions", - response_model=TonResponseGeneric[List[RawTransaction[TransactionId]]], + response_model=TonResponse200Generic[List[RawTransaction[TransactionId]]], response_model_exclude_none=True, tags=["accounts", "transactions"], ) @@ -381,7 +380,7 @@ async def unpack_address( @router.get( "/getMasterchainInfo", - response_model=TonResponseGeneric[MasterchainInfo], + response_model=TonResponse200Generic[MasterchainInfo], response_model_exclude_none=True, tags=["blocks"], ) @@ -396,7 +395,7 @@ async def get_masterchain_info(tonlib: TonlibManager = Depends(tonlib_dep)): @router.get( "/getMasterchainBlockSignatures", - response_model=TonResponseGeneric[MasterchainSignatures], + response_model=TonResponse200Generic[MasterchainSignatures], response_model_exclude_none=True, tags=["blocks"], ) @@ -413,7 +412,7 @@ async def get_masterchain_block_signatures( @router.get( "/getShardBlockProof", - response_model=TonResponseGeneric[ShardBlockProof], + response_model=TonResponse200Generic[ShardBlockProof], response_model_exclude_none=True, tags=["blocks"], ) @@ -437,7 +436,7 @@ async def get_shard_block_proof( @router.get( "/getConsensusBlock", - response_model=TonResponseGeneric[ConsensusBlock], + response_model=TonResponse200Generic[ConsensusBlock], response_model_exclude_none=True, tags=["blocks"], ) @@ -452,7 +451,7 @@ async def get_consensus_block(tonlib: TonlibManager = Depends(tonlib_dep)): @router.get( "/lookupBlock", - response_model=TonResponseGeneric[BlockId], + response_model=TonResponse200Generic[BlockId], response_model_exclude_none=True, tags=["blocks"], ) @@ -474,7 +473,7 @@ async def lookup_block( @router.get( "/shards", - response_model=TonResponseGeneric[Shards], + response_model=TonResponse200Generic[Shards], response_model_exclude_none=True, tags=["blocks"], ) @@ -492,7 +491,7 @@ async def get_shards( @router.get( "/getBlockTransactions", - response_model=TonResponseGeneric[ShortTransactions], + response_model=TonResponse200Generic[ShortTransactions], response_model_exclude_none=True, tags=["blocks", "transactions"], ) @@ -519,7 +518,7 @@ async def get_block_transactions( @router.get( "/getBlockHeader", - response_model=TonResponseGeneric[BlockHeader], + response_model=TonResponse200Generic[BlockHeader], response_model_exclude_none=True, tags=["blocks"], ) @@ -587,7 +586,7 @@ async def get_token_data( @router.get( "/tryLocateTx", - response_model=TonResponseGeneric[RawTransaction[TransactionId]], + response_model=TonResponse200Generic[RawTransaction[TransactionId]], response_model_exclude_none=True, tags=["transactions"], ) @@ -607,7 +606,7 @@ async def get_try_locate_tx( @router.get( "/tryLocateResultTx", - response_model=TonResponseGeneric[RawTransaction[TransactionId]], + response_model=TonResponse200Generic[RawTransaction[TransactionId]], response_model_exclude_none=True, tags=["transactions"], ) @@ -627,7 +626,7 @@ async def get_try_locate_result_tx( @router.get( "/tryLocateSourceTx", - response_model=TonResponseGeneric[RawTransaction[TransactionId]], + response_model=TonResponse200Generic[RawTransaction[TransactionId]], response_model_exclude_none=True, tags=["transactions"], ) diff --git a/ton-http-api/pyTON/schemas/__init__.py b/ton-http-api/pyTON/schemas/__init__.py index b999efb..a94b670 100644 --- a/ton-http-api/pyTON/schemas/__init__.py +++ b/ton-http-api/pyTON/schemas/__init__.py @@ -1,7 +1,6 @@ from .http import ( TonRequestJsonRPC, TonResponse, - TonResponseGeneric, TonResponseJsonRPC, DeprecatedTonResponseJsonRPC, get_get_address_information_error_responses, diff --git a/ton-http-api/pyTON/schemas/http.py b/ton-http-api/pyTON/schemas/http.py index 1593098..016619e 100644 --- a/ton-http-api/pyTON/schemas/http.py +++ b/ton-http-api/pyTON/schemas/http.py @@ -1,4 +1,4 @@ -from typing import Optional, TypeVar, Union, List, Literal, Tuple +from typing import Optional, TypeVar, Union, List, Literal, Tuple, Any from pydantic.generics import GenericModel, Generic from pydantic import BaseModel, Field @@ -24,14 +24,14 @@ class TonResponseGeneric(GenericModel, Generic[ResultT]): ok: bool - result: Optional[ResultT] + result: Optional[Union[ResultT, Any]] error: Optional[str] = None code: Optional[int] = None class TonResponse200Generic(GenericModel, Generic[ResultT2]): ok: bool = Field(True) - result: Optional[ResultT2] + result: Optional[Union[ResultT2, Any]] ResultTypeT = TypeVar("ResultTypeT") @@ -150,20 +150,20 @@ class UnpackAddressResponse(BaseModel): class TonGetTokenDataResponse(BaseModel): ok: bool = Field(True) - result: Union[JettonMasterData, NftCollectionData, NftItemData] + result: Union[JettonMasterData, NftCollectionData, NftItemData, Any] class TonResponseJsonRPC(BaseModel): id: str jsonrpc: str = "2.0" - result: Optional[ResultT] + result: Optional[Union[ResultT, Any]] error: Optional[str] = None code: Optional[int] = None class DeprecatedTonResponseJsonRPC(BaseModel): ok: bool - result: Optional[ResultT] + result: Optional[Union[ResultT, Any]] error: Optional[str] = None code: Optional[int] = None id: str From a6f29ac1f00b6044f20da6d0f44827b8944cdc7a Mon Sep 17 00:00:00 2001 From: Full-Hat Date: Wed, 10 Jul 2024 16:18:49 +0300 Subject: [PATCH 16/17] Add jetton wallet response case --- ton-http-api/pyTON/schemas/http.py | 5 +++-- ton-http-api/pyTON/schemas/ton.py | 8 ++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/ton-http-api/pyTON/schemas/http.py b/ton-http-api/pyTON/schemas/http.py index 016619e..0609184 100644 --- a/ton-http-api/pyTON/schemas/http.py +++ b/ton-http-api/pyTON/schemas/http.py @@ -14,6 +14,7 @@ BlockId, AddressShort, JettonMasterData, + JettonWalletData, NftCollectionData, NftItemData ) @@ -102,7 +103,7 @@ class GetExtendedAddressInformationResponse(BaseModel): last_transaction_id: TransactionId block_id: BlockId sync_utime: int - account_state: Union[AccountStateWallet, AccountStateRow, AccountStateUninited] + account_state: Union[AccountStateWallet, AccountStateRow, AccountStateUninited, Any] revision: int extra: str = Field(alias="@extra") @@ -150,7 +151,7 @@ class UnpackAddressResponse(BaseModel): class TonGetTokenDataResponse(BaseModel): ok: bool = Field(True) - result: Union[JettonMasterData, NftCollectionData, NftItemData, Any] + result: Union[JettonMasterData, NftCollectionData, NftItemData, JettonWalletData, Any] class TonResponseJsonRPC(BaseModel): diff --git a/ton-http-api/pyTON/schemas/ton.py b/ton-http-api/pyTON/schemas/ton.py index 7a7658b..a34f8e7 100644 --- a/ton-http-api/pyTON/schemas/ton.py +++ b/ton-http-api/pyTON/schemas/ton.py @@ -452,6 +452,14 @@ class Data(BaseModel): contract_type: str = Field('jetton_master') +class JettonWalletData(BaseModel): + balance: int + owner: str + jetton: str + jetton_wallet_code: str + contract_type: str = Field('jetton_wallet') + + class NftContent(BaseModel): type: str = Field(alias="@type") data: str From 89606aff12defe5db69b43da577f1dd7850d5c0c Mon Sep 17 00:00:00 2001 From: Full-Hat Date: Wed, 10 Jul 2024 16:53:22 +0300 Subject: [PATCH 17/17] Format code --- ton-http-api/pyTON/schemas/http.py | 6 ++++-- ton-http-api/pyTON/schemas/ton.py | 18 +++++++++--------- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/ton-http-api/pyTON/schemas/http.py b/ton-http-api/pyTON/schemas/http.py index 0609184..8f0aa44 100644 --- a/ton-http-api/pyTON/schemas/http.py +++ b/ton-http-api/pyTON/schemas/http.py @@ -16,7 +16,7 @@ JettonMasterData, JettonWalletData, NftCollectionData, - NftItemData + NftItemData, ) ResultT = TypeVar("ResultT") @@ -151,7 +151,9 @@ class UnpackAddressResponse(BaseModel): class TonGetTokenDataResponse(BaseModel): ok: bool = Field(True) - result: Union[JettonMasterData, NftCollectionData, NftItemData, JettonWalletData, Any] + result: Union[ + JettonMasterData, NftCollectionData, NftItemData, JettonWalletData, Any + ] class TonResponseJsonRPC(BaseModel): diff --git a/ton-http-api/pyTON/schemas/ton.py b/ton-http-api/pyTON/schemas/ton.py index a34f8e7..65c1ece 100644 --- a/ton-http-api/pyTON/schemas/ton.py +++ b/ton-http-api/pyTON/schemas/ton.py @@ -408,20 +408,20 @@ class BlockIdExt(BaseModel): class AccountStateWallet(BaseModel): - type: str = Field('wallet.version.accountState', alias="@type") + type: str = Field("wallet.version.accountState", alias="@type") wallet_id: str seqno: int class AccountStateRow(BaseModel): - type: str = Field('raw.accountState', alias="@type") + type: str = Field("raw.accountState", alias="@type") code: str data: str frozen_hash: str class AccountStateUninited(BaseModel): - type: str = Field('uninited.accountState', alias="@type") + type: str = Field("uninited.accountState", alias="@type") frozen_hash: str @@ -434,7 +434,7 @@ class JettonMasterData(BaseModel): total_supply: int mintable: bool admin_address: str - + class JettonContent(BaseModel): type: str = Field(alias="@type") @@ -446,10 +446,10 @@ class Data(BaseModel): decimals: str data: Data - + jetton_content: JettonContent jetton_wallet_code: str - contract_type: str = Field('jetton_master') + contract_type: str = Field("jetton_master") class JettonWalletData(BaseModel): @@ -457,7 +457,7 @@ class JettonWalletData(BaseModel): owner: str jetton: str jetton_wallet_code: str - contract_type: str = Field('jetton_wallet') + contract_type: str = Field("jetton_wallet") class NftContent(BaseModel): @@ -469,7 +469,7 @@ class NftCollectionData(BaseModel): next_item_index: int collection_content: NftContent owner_address: str - contract_type: str = Field('nft_collection') + contract_type: str = Field("nft_collection") class NftItemData(BaseModel): @@ -478,4 +478,4 @@ class NftItemData(BaseModel): owner_address: str collection_address: str content: NftContent - contract_type: str = Field('nft_item') + contract_type: str = Field("nft_item")