Skip to content

Commit 57e5bd4

Browse files
fishronsage流戈
authored andcommitted
basic implementation of fungible asset client
1 parent e5e1ecb commit 57e5bd4

File tree

2 files changed

+414
-1
lines changed

2 files changed

+414
-1
lines changed

aptos_sdk/aptos_token_client.py

Lines changed: 174 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
from __future__ import annotations
55

6-
from typing import Any, List, Tuple
6+
from typing import Any, List, Optional, Tuple
77

88
from .account import Account
99
from .account_address import AccountAddress
@@ -298,13 +298,83 @@ def parse(resource: dict[str, Any]) -> PropertyMap:
298298
return PropertyMap(properties)
299299

300300

301+
class FAConcurrentSupply:
302+
value: int
303+
max_value: int
304+
305+
struct_tag = "0x1::fungible_asset::ConcurrentSupply"
306+
307+
def __init__(self, value: int, max_value: int) -> None:
308+
self.value = value
309+
self.max_value = max_value
310+
311+
def __str__(self) -> str:
312+
return f"FAConcurrentSupply[value: {self.value}, max_value: {self.max_value}]"
313+
314+
@staticmethod
315+
def parse(resource: dict[str, Any]) -> FAConcurrentSupply:
316+
return FAConcurrentSupply(
317+
int(resource["current"]["value"]), int(resource["current"]["max_value"])
318+
)
319+
320+
321+
class FAMetadata:
322+
name: str
323+
symbol: str
324+
decimals: int
325+
icon_uri: str
326+
project_uri: str
327+
328+
def __init__(
329+
self, name: str, symbol: str, decimals: int, icon_uri: str, project_uri: str
330+
) -> None:
331+
self.name = name
332+
self.symbol = symbol
333+
self.decimals = decimals
334+
self.icon_uri = icon_uri
335+
self.project_uri = project_uri
336+
337+
def __str__(self) -> str:
338+
return f"FAMetadata[name: {self.name}, symbol: {self.symbol}, decimals: {self.decimals}, icon_uri: {self.icon_uri}, project_uri: {self.project_uri}]"
339+
340+
@staticmethod
341+
def parse(resource: dict[str, Any]) -> FAMetadata:
342+
return FAMetadata(
343+
resource["name"],
344+
resource["symbol"],
345+
int(resource["decimals"]),
346+
resource["icon_uri"],
347+
resource["project_uri"],
348+
)
349+
350+
351+
class FungibleStore:
352+
balance: int
353+
frozen: bool
354+
355+
struct_tag = "0x1::fungible_asset::FungibleStore"
356+
357+
def __init__(self, balance: int, frozen: bool) -> None:
358+
self.balance = balance
359+
self.frozen = frozen
360+
361+
def __str__(self):
362+
return f"FungibleStore[balance: {self.balance}, frozen: {self.frozen}]"
363+
364+
@staticmethod
365+
def parse(resource: dict[str, Any]) -> FungibleStore:
366+
return FungibleStore(int(resource["balance"]), resource["frozen"])
367+
368+
301369
class ReadObject:
302370
resource_map: dict[str, Any] = {
303371
Collection.struct_tag: Collection,
304372
Object.struct_tag: Object,
305373
PropertyMap.struct_tag: PropertyMap,
306374
Royalty.struct_tag: Royalty,
307375
Token.struct_tag: Token,
376+
FAConcurrentSupply.struct_tag: FAConcurrentSupply,
377+
FungibleStore.struct_tag: FungibleStore,
308378
}
309379

310380
resources: dict[Any, Any]
@@ -631,3 +701,106 @@ async def tokens_minted_from_transaction(
631701
continue
632702
mints.append(AccountAddress.from_str_relaxed(event["data"]["token"]))
633703
return mints
704+
705+
706+
class FungibleAssetClient:
707+
"""A wrapper around reading and mutating Fungible Assets"""
708+
709+
def __init__(self, rest_client: RestClient):
710+
self.client = rest_client
711+
712+
async def __primary_store_view(
713+
self,
714+
function: str,
715+
args: List[TransactionArgument],
716+
ledger_version: Optional[int] = None,
717+
) -> Any:
718+
module = "0x1::primary_fungible_store"
719+
ty_args = [TypeTag(StructTag.from_str("0x1::fungible_asset::Metadata"))]
720+
return await self.client.view_bcs_payload(
721+
module, function, ty_args, args, ledger_version
722+
)
723+
724+
async def read_object(self, address: AccountAddress) -> ReadObject:
725+
resources = {}
726+
read_resources = await self.client.account_resources(address)
727+
for resource in read_resources:
728+
if resource["type"] in ReadObject.resource_map:
729+
resource_obj = ReadObject.resource_map[resource["type"]]
730+
resources[resource_obj] = resource_obj.parse(resource["data"])
731+
return ReadObject(resources)
732+
733+
async def transfer(
734+
self,
735+
sender: Account,
736+
metadata_address: AccountAddress,
737+
receiver_address: AccountAddress,
738+
amount: int,
739+
sequence_number: Optional[int] = None,
740+
) -> str:
741+
"""Transfer amount of fungible asset from sender's primary store to receiver's primary store."""
742+
payload = EntryFunction.natural(
743+
"0x1::primary_fungible_store",
744+
"transfer",
745+
[TypeTag(StructTag.from_str("0x1::fungible_asset::Metadata"))],
746+
[
747+
TransactionArgument(metadata_address, Serializer.struct),
748+
TransactionArgument(receiver_address, Serializer.struct),
749+
TransactionArgument(amount, Serializer.u64),
750+
],
751+
)
752+
signed_transaction = await self.client.create_bcs_signed_transaction(
753+
sender, TransactionPayload(payload), sequence_number=sequence_number
754+
)
755+
return await self.client.submit_bcs_transaction(signed_transaction)
756+
757+
async def balance(
758+
self,
759+
metadata_address: AccountAddress,
760+
address: AccountAddress,
761+
ledger_version: Optional[int] = None,
762+
) -> int:
763+
"""Get the balance of account's primary store."""
764+
resp = await self.__primary_store_view(
765+
"balance",
766+
[
767+
TransactionArgument(address, Serializer.struct),
768+
TransactionArgument(metadata_address, Serializer.struct),
769+
],
770+
ledger_version,
771+
)
772+
return int(resp[0])
773+
774+
async def is_frozen(
775+
self,
776+
metadata_address: AccountAddress,
777+
address: AccountAddress,
778+
ledger_version: Optional[int] = None,
779+
) -> bool:
780+
"""Return whether the given account's primary store is frozen."""
781+
resp = await self.__primary_store_view(
782+
"is_frozen",
783+
[
784+
TransactionArgument(address, Serializer.struct),
785+
TransactionArgument(metadata_address, Serializer.struct),
786+
],
787+
ledger_version,
788+
)
789+
return resp[0]
790+
791+
async def primary_store_address(
792+
self,
793+
metadata_address,
794+
address: AccountAddress,
795+
ledger_version: Optional[int] = None,
796+
) -> str:
797+
"""Get the address of the primary store for the given account."""
798+
resp = await self.__primary_store_view(
799+
"primary_store_address",
800+
[
801+
TransactionArgument(address, Serializer.struct),
802+
TransactionArgument(metadata_address, Serializer.struct),
803+
],
804+
ledger_version,
805+
)
806+
return resp[0]

0 commit comments

Comments
 (0)