class MsgUndelegate(StdMsg): type = "staking/MsgUndelegate" action = "begin_unbonding" __schema__ = S.OBJECT( type=S.STRING_WITH_PATTERN(r"^staking/MsgUndelegate\Z"), value=S.OBJECT( delegator_address=S.ACC_ADDRESS, validator_address=S.VAL_ADDRESS, amount=Coin.__schema__, ), ) delegator_address: AccAddress validator_address: ValAddress amount: Coin def __post_init__(self): self.delegator_address = validate_acc_address(self.delegator_address) self.validator_address = validate_val_address(self.validator_address) @classmethod def from_data(cls, data: dict) -> MsgUndelegate: data = data["value"] return cls( delegator_address=data["delegator_address"], validator_address=data["validator_address"], amount=Coin.from_data(data["amount"]), )
class CommunityPoolSpendProposal(Content): type = "distribution/CommunityPoolSpendProposal" __schema__ = S.OBJECT( type=S.STRING_WITH_PATTERN( r"^distribution/CommunityPoolSpendProposal\Z"), value=S.OBJECT( title=S.STRING, description=S.STRING, recipient=S.ACC_ADDRESS, amount=Coins.__schema__, ), ) title: str description: str recipient: AccAddress amount: Coins @classmethod def from_data(cls, data: dict) -> CommunityPoolSpendProposal: data = data["value"] return cls( title=data["title"], description=data["description"], recipient=data["recipient"], amount=Coins.from_data(data["amount"]), )
class MsgSwap(StdMsg): type = "market/MsgSwap" action = "swap" __schema__ = S.OBJECT( type=S.STRING_WITH_PATTERN(r"^market/MsgSwap\Z"), value=S.OBJECT(trader=S.ACC_ADDRESS, offer_coin=Coin.__schema__, ask_denom=S.STRING), ) trader: AccAddress offer_coin: Coin ask_denom: str def __post_init__(self): self.trader = validate_acc_address(self.trader) @classmethod def from_data(cls, data: dict) -> MsgSwap: data = data["value"] return cls( trader=data["trader"], offer_coin=Coin.from_data(data["offer_coin"]), ask_denom=data["ask_denom"], )
class ParameterChangeProposal(Content): type = "params/ParameterChangeProposal" ParamChanges = ParamChanges # alias for easy access __schema__ = S.OBJECT( type=S.STRING_WITH_PATTERN(r"^params/ParameterChangeProposal\Z"), value=S.OBJECT( title=S.STRING, description=S.STRING, changes=ParamChanges.__schema__, ), ) title: str description: str changes: ParamChanges = field(default_factory=ParamChanges) def __post_init__(self): # validation checks for key if isinstance(self.changes, dict): self.changes = ParamChanges(self.changes) @classmethod def from_data(cls, data: dict) -> ParameterChangeProposal: data = data["value"] return cls( title=data["title"], description=data["description"], changes=ParamChanges.from_data(data["changes"]), )
class MsgSend(StdMsg): type = "bank/MsgSend" action = "send" __schema__ = S.OBJECT( type=S.STRING_WITH_PATTERN(r"^bank/MsgSend\Z"), value=S.OBJECT( from_address=S.ACC_ADDRESS, to_address=S.ACC_ADDRESS, amount=Coins.__schema__, ), ) from_address: AccAddress to_address: AccAddress amount: Union[Coins, List[Coin]] def __post_init__(self): self.from_address = validate_acc_address(self.from_address) self.to_address = validate_acc_address(self.to_address) self.amount = Coins(self.amount) @classmethod def from_data(cls, data: dict) -> MsgSend: data = data["value"] amount = Coins.from_data(data["amount"]) return cls( from_address=data["from_address"], to_address=data["to_address"], amount=amount, )
class MsgExchangeRatePrevote(StdMsg): type = "oracle/MsgExchangeRatePrevote" action = "exchangerateprevote" __schema__ = S.OBJECT( type=S.STRING_WITH_PATTERN(r"^oracle/MsgExchangeRatePrevote\Z"), value=S.OBJECT( hash=S.STRING, denom=S.STRING, feeder=S.ACC_ADDRESS, validator=S.VAL_ADDRESS, ), ) hash: str denom: str feeder: AccAddress validator: ValAddress def __post_init__(self): self.feeder = AccAddress(self.feeder) self.validator = ValAddress(self.validator) @classmethod def from_data(cls, data: dict) -> MsgExchangeRatePrevote: data = data["value"] return cls( hash=data["hash"], denom=data["denom"], feeder=data["feeder"], validator=data["validator"], )
class MsgExchangeRateVote(StdMsg): type = "oracle/MsgExchangeRateVote" action = "exchangeratevote" __schema__ = S.OBJECT( type=S.STRING_WITH_PATTERN(r"^oracle/MsgExchangeRateVote\Z"), value=S.OBJECT( exchange_rate=Dec.__schema__, salt=S.STRING, denom=S.STRING, feeder=S.ACC_ADDRESS, validator=S.VAL_ADDRESS, ), ) exchange_rate: Union[str, Type[Decimal], Coin, int] salt: str denom: str feeder: AccAddress validator: ValAddress def __post_init__(self): self.feeder = validate_acc_address(self.feeder) self.validator = validate_val_address(self.validator) if not isinstance(self.exchange_rate, Coin): self.exchange_rate = Coin(self.denom, self.exchange_rate) else: validate_same_denom(self.exchange_rate.denom, self.denom) def msg_value(self) -> dict: d = dict(self.__dict__) d["exchange_rate"] = str(self.exchange_rate.amount) return d @property def vote_hash(self): return vote_hash(self.salt, self.exchange_rate.amount, self.denom, self.validator) @property def prevote(self): return MsgExchangeRatePrevote( hash=self.vote_hash, denom=self.denom, feeder=self.feeder, validator=self.validator, ) @classmethod def from_data(cls, data: dict) -> MsgExchangeRateVote: data = data["value"] xr = Coin(data["denom"], data["exchange_rate"]) return cls( exchange_rate=xr, salt=data["salt"], denom=xr.denom, feeder=data["feeder"], validator=data["validator"], )
class MsgDeposit(StdMsg): type = "gov/MsgDeposit" action = "deposit" __schema__ = S.OBJECT( type=S.STRING_WITH_PATTERN(r"^gov/MsgDeposit\Z"), value=S.OBJECT( proposal_id=S.STRING_INTEGER, depositor=S.ACC_ADDRESS, amount=Coins.__schema__, ), ) proposal_id: int depositor: AccAddress amount: Coins def __post_init__(self): self.depositor = validate_acc_address(self.depositor) self.amount = Coins(self.amount) def msg_value(self) -> dict: d = dict(self.__dict__) d["proposal_id"] = str(self.proposal_id) return d @classmethod def from_data(cls, data: dict) -> MsgDeposit: data = data["value"] return cls( proposal_id=int(data["proposal_id"]), depositor=data["depositor"], amount=Coins.from_data(data["amount"]), )
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 MsgWithdrawDelegationReward(StdMsg): type = "distribution/MsgWithdrawDelegationReward" action = "withdraw_delegation_reward" schema = S.OBJECT( type=S.STRING_WITH_PATTERN(r"^distribution/MsgWithdrawDelegationReward\Z"), value=S.OBJECT( delegator_address=S.ACC_ADDRESS, validator_address=S.VAL_ADDRESS ), ) delegator_address: AccAddress validator_address: ValAddress def __post_init__(self): self.delegator_address = validate_acc_address(self.delegator_address) self.validator_address = validate_val_address(self.validator_address) @classmethod def from_data(cls, data: dict) -> MsgWithdrawDelegationReward: data = data["value"] return cls( delegator_address=data["delegator_address"], validator_address=data["validator_address"], )
class MsgMultiSend(StdMsg): type = "bank/MsgMultiSend" action = "multisend" Input = Input # alias Output = Output # alias __schema__ = S.OBJECT( type=S.STRING_WITH_PATTERN(r"^bank/MsgMultiSend\Z"), value=S.OBJECT(inputs=S.ARRAY(Input.__schema__), outputs=S.ARRAY(Output.__schema__)), ) inputs: List[Union[Input, dict]] outputs: List[Union[Output, dict]] def __init__( self, inputs: List[Input], outputs: List[Output], ): self.inputs = [] self.outputs = [] for i in inputs: if isinstance(i, dict): self.inputs.append( Input(address=i["address"], coins=i["coins"])) elif isinstance(i, Input): self.inputs.append(i) else: raise TypeError( f"invalid item {type(i)} encountered in MsgMultiSend.inputs, can only accept 'Input' or 'dict'." ) for o in outputs: if isinstance(o, dict): self.outputs.append( Output(address=o["address"], coins=o["coins"])) elif isinstance(o, Output): self.outputs.append(o) else: raise TypeError( f"invalid item {type(o)} encountered in MsgMultiSend.outputs, can only accept 'Output' or 'dict'." ) @classmethod def from_data(cls, data: dict) -> MsgMultiSend: data = data["value"] return cls( inputs=[Input.from_data(i) for i in data["inputs"]], outputs=[Output.from_data(o) for o in data["outputs"]], )
class MsgCreateValidator(StdMsg): type = "staking/MsgCreateValidator" action = "create_validator" __schema__ = S.OBJECT( type=S.STRING_WITH_PATTERN(r"^staking/MsgCreateValidator\Z"), value=S.OBJECT( description=Description.__schema__, commission=CommissionRates.__schema__, min_self_delegation=S.STRING_INTEGER, delegator_address=S.ACC_ADDRESS, validator_address=S.VAL_ADDRESS, pubkey=S.VAL_CONSPUBKEY, value=Coin.__schema__, ), ) description: Description commission: CommissionRates min_self_delegation: int delegator_address: AccAddress validator_address: ValAddress pubkey: ValConsPubKey value: Coin def __post_init__(self): self.delegator_address = validate_acc_address(self.delegator_address) self.validator_address = validate_val_address(self.validator_address) validate_val_conspubkey(self.pubkey) self.min_self_delegation = int(self.min_self_delegation) def msg_value(self) -> dict: d = dict(self.__dict__) msd = self.min_self_delegation d["min_self_delegation"] = str(msd) return d @classmethod def from_data(cls, data: dict) -> MsgCreateValidator: data = data["value"] return cls( description=Description.from_data(data["description"]), commission=CommissionRates.from_data(data["commission"]), min_self_delegation=int(data["min_self_delegation"]), delegator_address=data["delegator_address"], validator_address=data["validator_address"], pubkey=data["pubkey"], value=Coin.from_data(data["value"]), )
class TextProposal(Content): type = "gov/TextProposal" __schema__ = S.OBJECT( type=S.STRING_WITH_PATTERN(r"^gov/TextProposal\Z"), value=S.OBJECT(title=S.STRING, description=S.STRING), ) title: str description: str @classmethod def from_data(cls, data: dict) -> TextProposal: data = data["value"] return cls(title=data["title"], description=data["description"])
class Account(JsonSerializable, JsonDeserializable): __schema__ = S.OBJECT( type=S.STRING_WITH_PATTERN(r"^core/Account\Z"), value=S.OBJECT( address=S.ACC_ADDRESS, coins=Coins.__schema__, public_key=S.OPTIONAL(PublicKey.__schema__), account_number=S.STRING_INTEGER, sequence=S.STRING_INTEGER, ), ) address: str coins: Coins public_key: Optional[PublicKey] account_number: int sequence: int def __post_init__(self): if self.address: self.address = validate_acc_address(self.address) if self.coins: self.coins = Coins(self.coins) self.account_number = int(self.account_number) self.sequence = int(self.sequence) def to_data(self) -> dict: d = dict(self.__dict__) d["account_number"] = str(self.account_number) d["sequence"] = str(self.sequence) return {"type": "core/Account", "value": d} @classmethod def from_data(cls, data: dict) -> Account: if "type" in data: data = data["value"] pk = data["public_key"] if pk is not None: pk = PublicKey.deserialize(data["public_key"]) return cls( address=data["address"], coins=Coins.from_data(data["coins"]), public_key=pk, account_number=data["account_number"], sequence=data["sequence"], )
class MsgEditValidator(StdMsg): type = "staking/MsgEditValidator" action = "edit_validator" __schema__ = S.OBJECT( type=S.STRING_WITH_PATTERN(r"^staking/MsgEditValidator\Z"), value=S.OBJECT( Description=Description.__schema__, address=S.VAL_ADDRESS, commission_rate=S.OPTIONAL(Dec.__schema__), min_self_delegation=S.OPTIONAL(S.STRING_INTEGER), ), ) Description: Description address: ValAddress commission_rate: Optional[Dec] = None min_self_delegation: Optional[int] = None def __post_init__(self): if self.commission_rate is not None: self.commission_rate = Dec(self.commission_rate) if self.min_self_delegation is not None: self.min_self_delegation = int(self.min_self_delegation) self.address = validate_val_address(self.address) def msg_value(self) -> dict: d = dict(self.__dict__) msd = self.min_self_delegation if msd is not None: msd = str(msd) d["min_self_delegation"] = msd return d @classmethod def from_data(cls, data: dict) -> MsgEditValidator: data = data["value"] msd = int(data["min_self_delegation"]) if data["min_self_delegation"] else None cr = Dec(data["commission_rate"]) if data["commission_rate"] else None return cls( Description=data["Description"], address=data["address"], commission_rate=cr, min_self_delegation=msd, )
class MsgUnjail(StdMsg): type = "cosmos/MsgUnjail" action = "unjail" __schema__ = S.OBJECT( type=S.STRING_WITH_PATTERN(r"^cosmos/MsgUnjail\Z"), value=S.OBJECT(address=S.VAL_ADDRESS), ) address: ValAddress def __post_init__(self): self.address = validate_val_address(self.address) @classmethod def from_data(cls, data: dict) -> MsgUnjail: data = data["value"] return cls(address=data["address"])
class MsgWithdrawValidatorCommission(StdMsg): type = "distribution/MsgWithdrawValidatorCommission" action = "withdraw_validator_commission" __schema__ = S.OBJECT( type=S.STRING_WITH_PATTERN(r"^distribution/MsgWithdrawValidatorCommission\Z"), value=S.OBJECT(validator_address=S.VAL_ADDRESS), ) validator_address: ValAddress def __post_init__(self): self.validator_address = validate_val_address(self.validator_address) @classmethod def from_data(cls, data: dict) -> MsgWithdrawValidatorCommission: data = data["value"] return cls(validator_address=data["validator_address"])
class MsgDelegateFeedConsent(StdMsg): type = "oracle/MsgDelegateFeedConsent" action = "delegatefeeder" __schema__ = S.OBJECT( type=S.STRING_WITH_PATTERN(r"^oracle/MsgDelegateFeedConsent\Z"), value=S.OBJECT(operator=S.VAL_ADDRESS, delegate=S.ACC_ADDRESS), ) operator: ValAddress delegate: AccAddress def __post_init__(self): self.operator = validate_val_address(self.operator) self.delegate = validate_acc_address(self.delegate) @classmethod def from_data(cls, data: dict) -> MsgDelegateFeedConsent: data = data["value"] return cls(operator=data["operator"], delegate=data["delegate"])
class MsgVote(StdMsg): type = "gov/MsgVote" action = "vote" __schema__ = S.OBJECT( type=S.STRING_WITH_PATTERN(r"^gov/MsgVote\Z"), value=S.OBJECT( proposal_id=S.STRING_INTEGER, voter=S.ACC_ADDRESS, option=S.ONE(S.STRING, S.INTEGER), # signing is different ), ) EMPTY = "Empty" YES = "Yes" ABSTAIN = "Abstain" NO = "No" NO_WITH_VETO = "NoWithVeto" proposal_id: int voter: AccAddress option: str def __post_init__(self): self.voter = validate_acc_address(self.voter) def msg_value(self) -> dict: d = dict(self.__dict__) d["proposal_id"] = str(self.proposal_id) return d @classmethod def from_data(cls, data: dict) -> MsgVote: data = data["value"] return cls( proposal_id=int(data["proposal_id"]), voter=data["voter"], option=data["option"], )
class TaxRateUpdateProposal(Content): type = "treasury/TaxRateUpdateProposal" __schema__ = S.OBJECT( type=S.STRING_WITH_PATTERN(r"^treasury/TaxRateUpdateProposal\Z"), value=S.OBJECT(title=S.STRING, description=S.STRING, tax_rate=Dec.__schema__), ) title: str description: str tax_rate: Dec @classmethod def from_data(cls, data: dict) -> TaxRateUpdateProposal: data = data["value"] return cls( title=data["title"], description=data["description"], tax_rate=Dec(data["tax_rate"]), )
class RewardWeightUpdateProposal(Content): type = "treasury/RewardWeightUpdateProposal" __schema__ = S.OBJECT( type=S.STRING_WITH_PATTERN(r"^treasury/RewardWeightUpdateProposal\Z"), value=S.OBJECT(title=S.STRING, description=S.STRING, reward_weight=Dec.__schema__), ) title: str description: str reward_weight: Dec @classmethod def from_data(cls, data: dict) -> RewardWeightUpdateProposal: data = data["value"] return cls( title=data["title"], description=data["description"], reward_weight=Dec(data["reward_weight"]), )
class MsgModifyWithdrawAddress(StdMsg): type = "distribution/MsgModifyWithdrawAddress" action = "set_withdraw_address" __schema__ = S.OBJECT( type=S.STRING_WITH_PATTERN(r"^distribution/MsgModifyWithdrawAddress\Z"), value=S.OBJECT(delegator_address=S.ACC_ADDRESS, withdraw_address=S.ACC_ADDRESS), ) delegator_address: AccAddress withdraw_address: AccAddress def __post_init__(self): self.delegator_address = validate_acc_address(self.delegator_address) self.withdraw_address = validate_acc_address(self.withdraw_address) @classmethod def from_data(cls, data: dict) -> MsgModifyWithdrawAddress: data = data["value"] return cls( delegator_address=data["delegator_address"], withdraw_address=data["withdraw_address"], )
class Timestamp(wrapt.ObjectProxy, JsonSerializable, JsonDeserializable): # we have 2 patterns, 1 for serdes-checking in tests and 1 for checking if it works # python's builtin datetime pattern = re.compile( r"^(0001|[1-9]\d{3})-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])T([0-1]\d|2[0-3]):([0-5]\d):([0-5]\d)(\.\d+)?Z\Z" ) __schema__ = S.STRING_WITH_PATTERN( r"^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(\.\d+)?Z\Z") def __init__(self, year, month, day, hour, minute, second, ns=0, precision=0): wrapt.ObjectProxy.__init__( self, datetime(year, month, day, hour, minute, second, ns // 1000)) self._self_ns = ns self._self_precision = min(9, precision) @property def ns(self): return self._self_ns @property def precision(self): return self._self_precision @property def nanosecond(self): return int(self.ns % 1000) def __str__(self) -> str: Y = str(self.year).rjust(4, "0") M = str(self.month).rjust(2, "0") D = str(self.day).rjust(2, "0") h = str(self.hour).rjust(2, "0") m = str(self.minute).rjust(2, "0") s = str(self.second).rjust(2, "0") r = f"{Y}-{M}-{D}T{h}:{m}:{s}" if self.precision > 0: r += "." r += str(self.microsecond * 1000 + self.nanosecond).rjust( 9, "0")[:self.precision] return r + "Z" def __repr__(self) -> str: s = self p = f", {s.precision}" if s.precision != 0 else "" return f"Timestamp({s.year}, {s.month}, {s.day}, {s.hour}, {s.minute}, {s.second}, {s.ns}{p})" def to_data(self) -> str: return str(self) def _pretty_repr_(self, path: str = ""): return self.strftime("%a %b %d '%y - %H:%M:%S.%f") @classmethod def from_datetime(cls, dt: datetime) -> Timestamp: return cls( year=dt.year, month=dt.month, day=dt.day, hour=dt.hour, minute=dt.minute, second=dt.second, ns=dt.microsecond * 1000, precision=6 if dt.microsecond > 0 else 0, ) @classmethod def from_data(cls, data: str) -> Timestamp: m = cls.pattern.match(data) # m = re.match(cls.__schema__["pattern"], data) if m: year, month, day, hour, minute, second = [ int(m.group(x)) for x in range(1, 7) ] if m.group(7) is not None: # keep only nanosecond precision ns = int(Decimal(m.group(7)) * 1_000_000_000) precision = min(9, len(m.group(7)) - 1) else: ns = 0 precision = 0 return cls(year, month, day, hour, minute, second, ns, precision) else: raise ValueError(f"Timestamp could not deserialize: {data}") @classmethod def from_str(cls, string: str) -> Timestamp: return cls.from_data(string) # TODO: write datetime analogous functions for things that return datetime # Not sure if this is too high priority as Timestamp is not a replacement for datetime. def __eq__(self, other): if isinstance(other, Timestamp): return self.__wrapped__ == other.__wrapped__ and self.ns == other.ns return self.__wrapped__ == other and self.nanosecond == 0 def __ne__(self, other): return not self.__eq__(other) def __lt__(self, other): if isinstance(other, Timestamp): if self.__wrapped__ == other.__wrapped__: return self.ns < other.ns else: return self.__wrapped__ < other.__wrapped__ return self.__wrapped__ < other def __le__(self, other): return self == other or self < other def __gt__(self, other): if isinstance(other, Timestamp): if self.__wrapped__ == other.__wrapped__: return self.ns > other.ns else: return self.__wrapped__ > other.__wrapped__ if self.__wrapped__ == other: return self.ns > 0 else: return self.__wrapped__ > other def __ge__(self, other): return self == other or self > other
class Dec(JsonSerializable, JsonDeserializable): __schema__ = S.STRING_WITH_PATTERN(SDK_DEC_REGEX_PATTERN) def __init__(self, arg): """BigInt-based Decimal representation with basic arithmetic operations with compatible Python numeric types (int, float, Decimal). Does not work with NaN, Infinity, +0, -0, etc. Serializes as a string with 18 points of decimal precision. """ if isinstance(arg, float): arg = str("%f" % arg) if isinstance(arg, str): parts = PATTERN.match(arg) if parts is None: raise ValueError( f"Unable to parse Dec object from string: {arg}") self.i = int(parts.group(2)) * SDK_DEC_UNIT # integer part if parts.group(3): fraction = (parts.group(3).lstrip(".")[0:SDK_DEC_PREC].ljust( SDK_DEC_PREC, "0")) self.i += int(fraction) if parts.group(1) is not None: self.i *= -1 elif isinstance(arg, int): self.i = arg * SDK_DEC_UNIT elif isinstance(arg, Dec): self.i = arg.i elif isinstance(arg, Decimal): whole = int(arg) fraction = arg % 1 self.i = (whole * SDK_DEC_UNIT) + (fraction * SDK_DEC_UNIT) else: raise TypeError( f"Unable to create Dec object from given argument {arg}") # guarantee int self.i = int(self.i) @property def short_str(self): parity = "-" if self.i < 0 else "" frac = self.frac.rstrip("0") dot = "." if len(frac) > 0 else "" return f"{parity}{self.whole}{dot}{frac}" def __repr__(self): return f"Dec({self.short_str!r})" # short representation def __str__(self) -> str: if self.i == 0: return "0." + SDK_DEC_PREC * "0" parity = "-" if self.i < 0 else "" return f"{parity}{self.whole}.{self.frac}" def __int__(self) -> int: int_part = abs(self.i) // SDK_DEC_UNIT int_part *= -1 if self.i < 0 else 1 return int_part def __float__(self) -> float: # NOTE: This is not robust enough for: float(Dec(float)) to give the same output, # and should mainly be used as getting a rough value from the Dec object. return self.i / SDK_DEC_UNIT @property def parity(self) -> int: return -1 if self.i < 0 else 1 @property def whole(self) -> str: return str(abs(self.i) // SDK_DEC_UNIT) @property def frac(self) -> str: return str(abs(self.i) % SDK_DEC_UNIT).rjust(SDK_DEC_PREC, "0") def to_data(self) -> str: return str(self) def _pretty_repr_(self, path: str = ""): return self.short_str def __eq__(self, other) -> bool: try: return self._i_binop(other, int.__eq__, "==") except TypeError: return False def __lt__(self, other) -> bool: if isinstance(other, Dec): return self.i < other.i return (Decimal(self.i) / SDK_DEC_UNIT) < other def __le__(self, other) -> bool: return self < other or self == other def __gt__(self, other) -> bool: if isinstance(other, Dec): return self.i > other.i return (Decimal(self.i) / SDK_DEC_UNIT) > other def __ge__(self, other) -> bool: return self > other or self == other def __add__(self, other) -> Dec: new_val = self._i_binop(other, int.__add__, "+") nd = Dec(0) nd.i = new_val return nd def __radd__(self, other): return Dec.__add__(Dec(other), self) def __sub__(self, other) -> Dec: new_val = self._i_binop(other, int.__sub__, "-") nd = Dec(0) nd.i = new_val return nd def __rsub__(self, other): return Dec.__sub__(Dec(other), self) def __mul__(self, other): if isinstance(other, Dec): new_val = self.i * other.i elif (isinstance(other, int) or isinstance(other, float) or isinstance(other, Decimal)): new_val = self.i * Dec(other).i else: raise TypeError( f"unsupported operand types for *: 'Dec' and '{type(other)}'") nd = Dec(0) nd.i = new_val // SDK_DEC_UNIT return nd def __rmul__(self, other): return Dec.__mul__(Dec(other), self) def __truediv__(self, other): try: # we need the decimal value's better precision. d1 = Decimal(self.i) d2 = Decimal(other.i) new_val = d1 / d2 except AttributeError: new_val = self._i_binop(other, int.__truediv__, "/") nd = Dec(0) nd.i = int(new_val * SDK_DEC_UNIT) return nd def __rtruediv__(self, other): return Dec.__truediv__(Dec(other), self) def __floordiv__(self, other): return Dec(int(self / Dec(other))) def __neg__(self): x = Dec(self) x.i *= -1 return x def __abs__(self): x = Dec(self) x.i = abs(x.i) return x def __pos__(self): return abs(self) @classmethod def from_data(cls, data: str) -> Dec: return cls(data) @classmethod def with_prec(cls, i: int, prec: int) -> Dec: """Replicates Dec.with_prec(i, prec)""" d = cls(0) i = int(i) d.i = i * 10**(SDK_DEC_PREC - int(prec)) return d def _i_binop(self, other, binop, binop_name): """Helper method that tries to work with compatible number types by first converting them into Dec and working with their internal BigInt representation. """ if isinstance(other, Dec): new_val = binop(self.i, other.i) elif (isinstance(other, int) or isinstance(other, float) or isinstance(other, Decimal)): new_val = binop(self.i, Dec(other).i) else: raise TypeError( f"unsupported operand types for {binop_name}: 'Dec' and '{type(other)}'" ) return new_val
class LazyGradedVestingAccount(Account): __schema__ = S.OBJECT( type=S.STRING_WITH_PATTERN(r"^core/LazyGradedVestingAccount\Z"), value=S.OBJECT( BaseVestingAccount=S.OBJECT( BaseAccount=Account.__schema__["properties"]["value"], original_vesting=Coins.__schema__, delegated_free=Coins.__schema__, delegated_vesting=Coins.__schema__, end_time=S.STRING_INTEGER, ), vesting_schedules=S.ARRAY( S.OBJECT(denom=S.STRING, schedules=S.ARRAY(VestingScheduleEntry.__schema__))), ), ) original_vesting: Coins delegated_free: Coins delegated_vesting: Coins end_time: int vesting_schedules: Dict[str, List[VestingScheduleEntry]] def __init__( self, base_account: Account, original_vesting: Coins, delegated_free: Coins, delegated_vesting: Coins, end_time: int, vesting_schedules: Dict[str, List[VestingScheduleEntry]], ): Account.__init__( self, address=base_account.address, coins=base_account.coins, public_key=base_account.public_key, account_number=base_account.account_number, sequence=base_account.sequence, ) self._base_account = base_account self.original_vesting = original_vesting self.delegated_free = delegated_free self.delegated_vesting = delegated_vesting self.end_time = end_time self.vesting_schedules = vesting_schedules def to_data(self): return { "type": "core/LazyGradedVestingAccount", "value": { "BaseVestingAccount": { "BaseAccount": Account.to_data(self._base_account)["value"], "original_vesting": self.original_vesting, "delegated_free": self.delegated_free, "delegated_vesting": self.delegated_vesting, "end_time": str(self.end_time), }, "vesting_schedules": [{ "denom": denom, "schedules": self.vesting_schedules[denom] } for denom in self.vesting_schedules], }, } @property def pretty_data(self): d = dict(self.__dict__) d.pop("_base_account") return d.items() @classmethod def from_data(cls, data: dict) -> LazyGradedVestingAccount: if "type" in data: data = data["value"] # disregard type bva = data[ "BaseVestingAccount"] # value = { bva { ... } vesting_schedules { } } original_vesting = Coins.from_data(bva["original_vesting"]) delegated_free = Coins.from_data(bva["delegated_free"]) delegated_vesting = Coins.from_data(bva["delegated_vesting"]) vesting_schedules = JiguBox({}) for s in data["vesting_schedules"]: vesting_schedules[s["denom"]] = [ VestingScheduleEntry.deserialize(e) for e in s["schedules"] ] return cls( base_account=Account.from_data(bva["BaseAccount"]), original_vesting=original_vesting, delegated_free=delegated_free, delegated_vesting=delegated_vesting, end_time=int(bva["end_time"]), vesting_schedules=vesting_schedules, )