Skip to content

Commit

Permalink
Search for %metadata big_map in nested sturctures (#285)
Browse files Browse the repository at this point in the history
  • Loading branch information
m-kus authored Feb 28, 2022
1 parent 5f5b5b5 commit edd0e38
Show file tree
Hide file tree
Showing 8 changed files with 85 additions and 9 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: Release
on:
push:
tags:
- '*.*.*'
- '*.*.0'

jobs:
build:
Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## 3.3.5 - 2022-02-27

### Fixed

* Metadata big map can be located in a nested structure, not necessarily on the top-level (as per TZIP-16)

## 3.3.4 - 2021-12-23

### Fixed
Expand Down
14 changes: 12 additions & 2 deletions src/pytezos/contract/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
from pytezos.michelson.program import MichelsonProgram
from pytezos.michelson.sections import ViewSection
from pytezos.michelson.types.base import generate_pydoc
from pytezos.michelson.types import BigMapType, BytesType
from pytezos.operation.group import OperationGroup
from pytezos.rpc import ShellQuery

Expand Down Expand Up @@ -402,10 +403,19 @@ def _get_token_metadata_from_view(self, token_id: int) -> Optional[ContractToken
@cached_property
def metadata_url(self) -> Optional[str]:
try:
return self.storage['metadata']['']().decode()
metadata = self.storage.data.find(lambda x: x.field_name == 'metadata' and isinstance(x, BigMapType))
if metadata is not None:
metadata_url = metadata['']
if isinstance(metadata_url, BytesType):
return metadata_url.value.decode()
else:
self._logger.info('Empty string key is not found in metadata big map')
else:
self._logger.info('Metadata big map not found')
# FIXME: Dirty
except (KeyError, AssertionError):
return None
self._logger.info('Failed to get metadata URI')
return None

@property
def parameter(self) -> ContractEntrypoint:
Expand Down
23 changes: 22 additions & 1 deletion src/pytezos/michelson/types/base.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
from copy import copy, deepcopy
from typing import Any, List, Optional, Tuple, Type, Union, cast
from collections.abc import Iterable
from typing import (
Any,
List,
Optional,
Tuple,
Type,
Union,
cast,
Callable,
)

from pytezos.context.abstract import AbstractContext # type: ignore
from pytezos.michelson.forge import forge_micheline, unforge_micheline
Expand Down Expand Up @@ -214,6 +224,17 @@ def aggregate_lazy_diff(self, lazy_diff: List[dict], mode='readable') -> 'Michel
assert len(self.args) == 0 or self.prim in ['contract', 'lambda', 'ticket', 'set']
return copy(self)

def find(self, predicate: Callable[['MichelsonType'], bool]) -> Optional['MichelsonType']:
if predicate(self):
return self
if isinstance(self, Iterable):
for item in self:
if isinstance(item, MichelsonType):
res = item.find(predicate)
if res is not None:
return res
return None

def forge(self, mode='readable') -> bytes:
val_expr = self.to_micheline_value(mode=mode)
return forge_micheline(val_expr)
Expand Down
16 changes: 14 additions & 2 deletions src/pytezos/michelson/types/big_map.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
from copy import copy, deepcopy
from typing import Dict, Generator, List, Optional, Tuple, Type, Union

from typing import (
Callable,
Generator,
List,
Optional,
Tuple,
Type,
Union,
)
from pytezos.context.abstract import AbstractContext # type: ignore
from pytezos.michelson.forge import forge_script_expr
from pytezos.michelson.micheline import Micheline, MichelineLiteral, MichelineSequence, parse_micheline_literal
Expand Down Expand Up @@ -192,6 +199,11 @@ def make_update(key: MichelsonType, val: Optional[MichelsonType]) -> dict:
res.context = self.context
return res

def find(self, predicate: Callable[['MichelsonType'], bool]) -> Optional['MichelsonType']:
if predicate(self):
return self
return None

def attach_context(self, context: AbstractContext, big_map_copy=False):
assert self.context is None, f'context already attached'
self.context = context
Expand Down
19 changes: 17 additions & 2 deletions src/pytezos/michelson/types/map.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
from typing import Generator, List, Optional, Tuple, Type

from typing import (
Generator,
List,
Optional,
Tuple,
Type,
Callable,
)
from pytezos.context.abstract import AbstractContext # type: ignore
from pytezos.michelson.micheline import Micheline, MichelineSequence, parse_micheline_value
from pytezos.michelson.types.base import MichelsonType
Expand Down Expand Up @@ -124,6 +130,15 @@ def aggregate_lazy_diff(self, lazy_diff: List[dict], mode='readable'):
items = [(key, val.aggregate_lazy_diff(lazy_diff, mode=mode)) for key, val in self.items]
return type(self)(items)

def find(self, predicate: Callable[['MichelsonType'], bool]) -> Optional['MichelsonType']:
if predicate(self):
return self
for _, item in self:
res = item.find(predicate)
if res:
return res
return None

def attach_context(self, context: AbstractContext, big_map_copy=False):
for _, val in self.items:
val.attach_context(context, big_map_copy=big_map_copy)
Expand Down
9 changes: 8 additions & 1 deletion src/pytezos/michelson/types/option.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import List, Optional, Type
from typing import List, Optional, Type, Callable

from pytezos.context.abstract import AbstractContext # type: ignore
from pytezos.michelson.micheline import Micheline, parse_micheline_value
Expand Down Expand Up @@ -113,3 +113,10 @@ def aggregate_lazy_diff(self, lazy_diff: List[dict], mode='readable') -> 'Michel
def attach_context(self, context: AbstractContext, big_map_copy=False):
if not self.is_none():
self.item.attach_context(context, big_map_copy=big_map_copy) # type: ignore

def find(self, predicate: Callable[['MichelsonType'], bool]) -> Optional['MichelsonType']:
if predicate(self):
return self
if self.item is not None:
return self.item.find(predicate)
return None
5 changes: 5 additions & 0 deletions tests/integration_tests/test_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,8 @@ def test_1(self):
contract = client.contract("KT1Tg6WFuTU47Z8rPeBhDbgx1DmX1SWCZcZq")
balance = contract.metadata.getBalance(("tz1WkYQMi1LSvge2ksaWtx5t4eqqLqgEskUM", 0)).storage_view()
assert int(balance) > 0

def test_domains(self):
td = pytezos.using('mainnet').contract('KT1GBZmSxmnKJXGMdMLbugPfLyUPmuLSMwKS')
res = td.metadata.resolveAddress('tz1PN9FWDGoASBTvgppaQbbPdGhkrnTNmcVz').storage_view()
self.assertEqual('tz1PN9FWDGoASBTvgppaQbbPdGhkrnTNmcVz', res['address'])

0 comments on commit edd0e38

Please sign in to comment.