class StdTx(JsonSerializable, JsonDeserializable): __schema__ = S.OBJECT( type=S.STRING_WITH_PATTERN(r"^core/StdTx\Z"), value=S.OBJECT( fee=StdFee.__schema__, msg=S.ARRAY(S.ANY(*(mt.__schema__ for mt in MSG_TYPES.values()))), signatures=S.ARRAY(StdSignature.__schema__), memo=S.STRING, ), ) # NOTE: msg is not plural, and is NOT a typo. This may change later for consistency. fee: Optional[StdFee] = None msg: List[StdMsg] = field(default_factory=list) signatures: List[StdSignature] = field(default_factory=list) memo: str = "" def to_data(self) -> dict: return { "type": "core/StdTx", "value": dict(self.__dict__), } @classmethod def from_data(cls, data: dict) -> StdTx: data = data["value"] fee = StdFee.from_data(data["fee"]) # deserialize the messages msg = [] for m in data["msg"]: msg_type = MSG_TYPES[m["type"]] msg.append(msg_type.from_data(m)) signatures = [StdSignature.from_data(s) for s in data["signatures"]] return cls(fee=fee, msg=msg, signatures=signatures, memo=data["memo"])
class MsgSubmitProposal(StdMsg): type = "gov/MsgSubmitProposal" action = "submit_proposal" __schema__ = S.OBJECT( type=S.STRING_WITH_PATTERN(r"^gov/MsgSubmitProposal\Z"), value=S.OBJECT( content=S.ANY(*(pt.__schema__ for pt in PROPOSAL_TYPES.values())), initial_deposit=Coins.__schema__, proposer=S.ACC_ADDRESS, ), ) content: Type[Content] initial_deposit: Coins proposer: AccAddress def __post_init__(self): self.proposer = validate_acc_address(self.proposer) self.initial_deposit = Coins(self.initial_deposit) @classmethod def from_data(cls, data: dict) -> MsgSubmitProposal: data = data["value"] p_type = PROPOSAL_TYPES[data["content"]["type"]] content = p_type.from_data(data["content"]) return cls( content=content, initial_deposit=Coins.from_data(data["initial_deposit"]), proposer=data["proposer"], )
class PublicKey(JsonSerializable, JsonDeserializable): __schema__ = S.OBJECT( type=S.STRING, value=S.ANY( S.STRING, S.OBJECT( threshold=S.STRING_INTEGER, pubkeys=S.ARRAY(S.OBJECT(type=S.STRING, value=S.STRING)), ), ), ) type: str = "tendermint/PubKeySecp256k1" value: Any = None @classmethod def from_data(cls, data: dict) -> PublicKey: # TODO: apply None-coalescing feature to root JsonDeserializable # viz: JsonDeserializable if data is None: return None return cls(type=data["type"], value=data["value"])
class Coin(JsonSerializable, JsonDeserializable, Generic[T]): __schema__ = S.OBJECT(denom=S.STRING, amount=S.ANY(S.STRING_INTEGER, Dec.__schema__)) denom: str amount: T # all get converted to int or Dec def __post_init__(self): s = self if (isinstance(s.amount, Dec) or isinstance(s.amount, Decimal) or isinstance(s.amount, float) or (isinstance(s.amount, str) and "." in str(s.amount) or "E" in str(s.amount))): object.__setattr__(s, "amount", Dec( s.amount)) # must do this due to immutability else: try: object.__setattr__(s, "amount", int(s.amount)) except ValueError: raise ValueError( f"unacceptable value for Coin amount: {s.amount}") def __repr__(self) -> str: return f"Coin('{self.denom}', {self.amount!r})" @property def int_coin(self) -> Coin[int]: return Coin(self.denom, int(self.amount)) @property def dec_coin(self) -> Coin[Dec]: return Coin(self.denom, Dec(self.amount)) def __str__(self) -> str: return f"{self.amount}{self.denom}" @classmethod def from_str(cls, string: str) -> Coin: pattern = r"^(\-?[0-9]+(\.[0-9]+)?)([a-zA-Z]+)$" match = re.match(pattern, string) if match: return cls(match.group(3), match.group(1)) else: raise ValueError(f"{string} could not be parsed into Coin") def __eq__(self, other: object) -> bool: if isinstance(other, Coin): return self.denom == other.denom and self.amount == other.amount else: return False def __add__(self, other) -> Union[Coin, Coins]: if other == 0: return Coin(self.denom, self.amount) if isinstance(other, Coins): return other + self if not isinstance(other, Coin): raise TypeError( f"unsupported operand types for +: 'Coin' and '{type(other)}'") if self.denom != other.denom: raise DenomIncompatibleError( f"unsupported Coin denoms for +: '{self.denom}' and '{other.denom}'" ) cast = int if isinstance(self.amount, Dec) or isinstance(other.amount, Dec): cast = Dec return Coin(denom=self.denom, amount=cast(self.amount + other.amount)) def __radd__(self, other) -> Union[Coin, Coins]: return self + other def __sub__(self, other: Coin) -> Coin[Union[int, Dec]]: if not isinstance(other, Coin): raise TypeError( f"unsupported operand types for -: 'Coin' and '{type(other)}'") if self.denom != other.denom: raise DenomIncompatibleError( f"unsupported Coin denoms for -: '{self.denom}' and '{other.denom}'" ) cast = int if isinstance(self.amount, Dec) or isinstance(other.amount, Dec): cast = Dec return Coin(denom=self.denom, amount=cast(self.amount - other.amount)) def __mul__(self, other) -> Coin[T]: return Coin(denom=self.denom, amount=(self.amount * other)) def __rmul__(self, other) -> Coin[T]: return self * other def __truediv__(self, other) -> Coin[T]: cast = type(self.amount) return Coin(denom=self.denom, amount=cast(self.amount / other)) def __floordiv__(self, other) -> Coin[T]: cast = type(self.amount) return Coin(denom=self.denom, amount=cast(self.amount // other)) def __neg__(self) -> Coin[T]: return Coin(denom=self.denom, amount=(-self.amount)) def __abs__(self) -> Coin[T]: return Coin(denom=self.denom, amount=abs(self.amount)) def __pos__(self) -> Coin[T]: return abs(self) def __lt__(self, other: Coin) -> bool: if self.denom != other.denom: raise DenomIncompatibleError( f"incompatible Coin denoms for <: '{self.denom}' and '{other.denom}'" ) return self.amount < other.amount def __gt__(self, other: Coin) -> bool: if self.denom != other.denom: raise DenomIncompatibleError( f"incompatible Coin denoms for >: '{self.denom}' and '{other.denom}'" ) return self.amount > other.amount def __ge__(self, other: Coin) -> bool: return self > other or self == other def __le__(self, other: Coin) -> bool: return self < other or self == other def to_data(self) -> dict: return {"denom": str(self.denom), "amount": str(self.amount)} def _pretty_repr_(self, path="") -> str: return str(self) @classmethod def from_data(cls, data: Dict[str, str]) -> Coin: return cls(denom=data["denom"], amount=data["amount"])