def tally_for(self, proposal_id: str) -> Union[ApiResponse, Dict[str, Coin]]: res = self._api_get(f"/gov/proposals/{proposal_id}/tally") denoms = res or [] tally = JiguBox({}) for denom in denoms: tally[denom] = Coin(uLuna, int(res[denom])) return project(res, JiguBox(tally))
def from_data(cls, data: dict) -> Block: header = BlockHeader.from_data(data["block_meta"]) txs = (data["block"]["data"]["txs"] or []) # these will be Amino-encoded tx strings evidence = JiguBox(data["block"].get("evidence")) last_commit = JiguBox(data["block"].get("last_commit")) return cls(txs=txs, evidence=evidence, last_commit=last_commit, **header.__dict__)
def to_signing_info(si): box = JiguBox( si, box_recast={ "start_height": int, "index_offset": int, # "jailed_until": Timestamp, "missed_blocks_counter": int, }, ) box.jailed_until = Timestamp.from_data(box.jailed_until) return box
def params(self, key: Optional[str] = None) -> Union[ApiResponse, Dec, dict]: res = self._api_get("/market/parameters") p = JiguBox(res) p["pool_recovery_period"] = int(p["pool_recovery_period"]) p["base_pool"] = Dec.deserialize(p["base_pool"]) p["min_spread"] = Dec.deserialize(p["min_spread"]) p["tobin_tax"] = Dec.deserialize(p["tobin_tax"]) ill = p["illiquid_tobin_tax_list"] p["illiquid_tobin_tax_list"] = JiguBox({}) for item in ill: p["illiquid_tobin_tax_list"][item["denom"]] = Dec(item["tax_rate"]) return project(res, p[key] if key else p)
def node_info(self) -> Union[ApiResponse, JiguBox]: """Get information for the node. Returns: Node information with the following schema. """ res = self._api_get("/node_info", unwrap=False) return project(res, JiguBox(res))
def signing_info_by_address(self, address: str = None): if address: validate_val_consaddress(address) res = self._api_get("/slashing/signing_infos") infos = [to_signing_info(r) for r in res] by_address = JiguBox({info.address: info for info in infos}) return project(res, by_address[address] if address else by_address)
def _process_block_msg(self, message) -> JiguBox: """Will process the response from a NewBlock subscription and turn it into a block's context.""" data = message["data"]["value"] height = data["block"]["header"]["height"] block = self.terra.blocks.at(height) begin_block_events = [{ "type": e["type"], "attributes": self._decode_kvs_b64(e["attributes"]) } for e in data["result_begin_block"].get("events", [])] end_block_events = [{ "type": e["type"], "attributes": self._decode_kvs_b64(e["attributes"]) } for e in data["result_end_block"].get("events", [])] return JiguBox({ "block": block, "events": { "begin_block": EventsQuery(Event.deserialize(e) for e in begin_block_events), "end_block": EventsQuery(Event.deserialize(e) for e in end_block_events), }, })
def votes_for(self, proposal_id: str) -> Union[ApiResponse, dict]: res = self._api_get(f"/gov/proposals/{proposal_id}/votes") vs = res or [] votes = JiguBox({}) for v in vs: votes[v["voter"]] = v["option"] return project(res, votes)
def __init__(self, changes: dict): for k, v in changes.items(): m = symbol.match(k) if not m: raise InvalidParamChange( f"Parameter change subspace could not be parsed: {k}") if not isinstance(v, dict): raise InvalidParamChange( f"Parameter change value should be a dict but got: '{type(v)}' for {k}" ) for sk, sv in v.items(): sm = symbol.match(sk) if not sm: raise InvalidParamChange( f"Parameter change key could not be parsed - {k}: '{sk}'" ) self.changes = JiguBox(changes)
def params( self, key: Optional[str] = None) -> Union[ApiResponse, dict, Dec, bool]: res = self._api_get("/distribution/parameters") p = res p["community_tax"] = Dec(p["community_tax"]) p["base_proposer_reward"] = Dec(p["base_proposer_reward"]) p["bonus_proposer_reward"] = Dec(p["bonus_proposer_reward"]) return project(res, p[key] if key else JiguBox(p))
def deposit_params( self, key: str = None ) -> Union[ApiResponse, Dict[str, Union[int, Coins]]]: res = self._api_get(f"/gov/parameters/deposit") p = JiguBox(res) p["min_deposit"] = Coins.deserialize(p["min_deposit"]) p["max_deposit_period"] = int(p["max_deposit_period"]) return project(res, p[key] if key else p)
def deposits_for(self, proposal_id: int) -> Union[ApiResponse, Dict[str, Coins]]: """Get the proposal's deposits.""" res = self._api_get(f"/gov/proposals/{proposal_id}/deposits") ds = res or [] deposits = JiguBox({}) for d in ds: deposits[d["depositor"]] = Coins.deserialize(d["amount"]) return project(res, deposits)
def tally_params(self, key: str = None) -> Union[ApiResponse, Dict[str, Dec]]: res = self._api_get(f"/gov/parameters/tallying") p = JiguBox(res, box_recast={ "quorum": Dec, "threshold": Dec, "veto": Dec }) return project(res, p[key] if key else p)
def pool( self, key: Optional[str] = None ) -> Union[ApiResponse, Coin, JiguBox[str, Coin]]: res = self._api_get("/staking/pool") pool = JiguBox( { "bonded": Coin(uLuna, res["bonded_tokens"]), "not_bonded": Coin(uLuna, res["not_bonded_tokens"]), } ) return project(res, pool[key] if key else pool)
def from_data(cls, data: list) -> ParamChanges: changes = JiguBox(default_box=True) for p in data: subspace = p["subspace"] key = cls._get_key( subspace, p["key"], inverse=True ) # p["key"] is paramstore key, we are using json-name keys inside Jigu value = cls._unmarshal_value(subspace, p["key"], json.loads(p["value"])) changes[subspace][key] = value return cls(changes)
def delegates(self, height=None): all_validators = self.staking.validators() vset = self._tendermint.validator_set(height=height) # join validators using pubkey by_pubkey = {x["pub_key"]: x for x in vset} return JiguBox( { v.operator_address: {"info": v, **by_pubkey[v.consensus_pubkey]} for v in all_validators if v.consensus_pubkey in by_pubkey } )
def __init__(self, msg: StdMsg, success: bool, log: dict, events: EventsQuery): wrapt.ObjectProxy.__init__(self, msg) try: log = json.loads(log) if type(log) == dict: log = JiguBox(log) except: log = None self._self_success = success self._self_log = log self._self_events = events self._self_pretty_data = None
def params(self, key: str = None): """Puts all the parameters together.""" deposit = self.deposit_params() voting = self.voting_params() tally = self.tally_params() p = JiguBox({ "deposit_params": deposit, "voting_params": voting, "tally_params": tally }) return project( # use response information of last entry, even if there is a delay tally, p[key] if key else p)
def from_data(cls, data: dict) -> BlockHeader: header = data["header"] hashes = JiguBox({ "last_commit": header["last_commit_hash"], "data": header["data_hash"], "validators": header["validators_hash"], "next_validators": header["next_validators_hash"], "consensus": header["consensus_hash"], "app": header["app_hash"], "last_results": header["last_results_hash"], "evidence": header["evidence_hash"], }) return cls( chain_id=header["chain_id"], height=int(header["height"]), time=Timestamp.from_data(header["time"]), num_txs=int(header["num_txs"]), total_txs=int(header["total_txs"]), hashes=hashes, proposer=header["proposer_address"], block_id=JiguBox(data["block_id"]), last_block_id=JiguBox(header["last_block_id"]), )
def rewards_for(self, delegator: AccAddress) -> Union[ApiResponse, dict]: """Get an account's delegation rewards.""" delegator = validate_acc_address(delegator) res = self._api_get(f"/distribution/delegators/{delegator}/rewards") rewards = res["rewards"] or [] total = Coins.from_data(res["total"]) result = JiguBox({ "rewards": { r["validator_address"]: Coins.deserialize(r["reward"]) for r in rewards }, "total": total, }) return project(res, result)
def params(self, key: Optional[str] = None) -> Union[int, Dec, dict]: res = self._api_get("/slashing/parameters") p = JiguBox( res, box_recast={ "max_evidence_age": int, "signed_blocks_window": int, "min_signed_per_window": Dec, "downtime_jail_duration": int, "slash_fraction_double_sign": Dec, "slash_fraction_downtime": Dec, }, ) return project(res, p[key] if key else p)
def val_rewards_for( self, validator: ValAddress, key: Optional[str] = None ) -> Union[ApiResponse, Coins, Dict[str, Coins]]: validator = validate_val_address(validator) res = self._api_get(f"/distribution/validators/{validator}") rewards = JiguBox({ "self_bond": Coins.deserialize(res["self_bond_rewards"]), "commission": Coins.deserialize(res["val_commission"]), }) return project(res, rewards[key] if key else rewards)
def block(self, height=None) -> Union[ApiResponse, dict]: """Get the raw block data at a height on the blockchain. Args: height (int, optional): block height Returns: - The block at the provided height. If no height was specified, get the latest block at the time of calling. """ if height: res = self._api_get(f"/blocks/{height}", unwrap=False) else: res = self._api_get("/blocks/latest", unwrap=False) return project(res, JiguBox(res))
def params( self, key: Optional[str] = None) -> Union[ApiResponse, Dec, int, dict]: res = self._api_get("/treasury/parameters") p = JiguBox( res, box_recast={ "seigniorage_burden_target": Dec, "mining_increment": Dec, "window_short": int, "window_long": int, "window_probation": int, }, ) p["tax_policy"] = PolicyConstraints.deserialize(p["tax_policy"]) p["reward_policy"] = PolicyConstraints.deserialize(p["reward_policy"]) return project(res, p[key] if key else p)
def from_data(cls, data: dict) -> Proposal: final_tally_result = data["final_tally_result"] for key in final_tally_result: final_tally_result[key] = Coin(uLuna, int(final_tally_result[key])) p_type = PROPOSAL_TYPES[data["content"]["type"]] content = p_type.from_data(data["content"]) return cls( content=content, id=int(data["id"]), proposal_status=ProposalStatus(data["proposal_status"]), final_tally_result=JiguBox(final_tally_result), submit_time=Timestamp.from_data(data["submit_time"]), deposit_end_time=Timestamp.from_data(data["deposit_end_time"]), total_deposit=Coins.from_data(data["total_deposit"]), voting_start_time=Timestamp.from_data(data["voting_start_time"]), voting_end_time=Timestamp.from_data(data["voting_end_time"]), )
def params( self, key: Optional[str] = None ) -> Union[ApiResponse, dict, int, Dec, List[str]]: res = self._api_get("/oracle/parameters") p = JiguBox( res, box_recast={ "vote_period": int, "vote_threshold": Dec, "reward_band": Dec, "reward_distribution_window": int, "slash_fraction": Dec, "slash_window": int, "min_valid_per_window": Dec, }, ) return project(res, p[key] if key else p)
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, )
def validator_set(self, height=None) -> Union[ApiResponse, dict]: """Retrieves the current set of validators in the actively validating set. ## Arguments - **height** `int` *optional* block height ## Returns List of `dict` with the following keys: - **address** `ValConsAddress` validator's consensus address - **pub_key** `ValConsPubKey` validator's consensus public key - **proposer_priority** `int` - **voting_power** `int` """ if height: res = self._api_get(f"/validatorsets/{height}") else: res = self._api_get("/validatorsets/latest") vs = res["validators"] results = [] for v in vs: results.append( JiguBox({ "address": v["address"], "pub_key": v["pub_key"], "proposer_priority": int(v["proposer_priority"]), "voting_power": int(v["voting_power"]), })) return project(res, results)
class ParamChanges(JsonSerializable, JsonDeserializable): __schema__ = S.ARRAY(ParamChangeSchema) def __init__(self, changes: dict): for k, v in changes.items(): m = symbol.match(k) if not m: raise InvalidParamChange( f"Parameter change subspace could not be parsed: {k}") if not isinstance(v, dict): raise InvalidParamChange( f"Parameter change value should be a dict but got: '{type(v)}' for {k}" ) for sk, sv in v.items(): sm = symbol.match(sk) if not sm: raise InvalidParamChange( f"Parameter change key could not be parsed - {k}: '{sk}'" ) self.changes = JiguBox(changes) def __repr__(self) -> str: return f"<ParamChanges {self.changes!r}>" def __getattr__(self, name: str) -> JiguBox: return self.changes[name] def __getitem__(self, item) -> JiguBox: return self.changes[item] @staticmethod def _get_key(subspace, key, inverse=False): """Each parameter has a special key in the ParamStore that might not correspond to the parameter's JSON-name. We use this function to look up the ParamStore key for the JSON-name, which we are familiar with. Set `inverse` to `True` to look up from the opposite direction for deserialization. """ try: table = PARAMSTORE_KEY_LOOKUP_TABLE[subspace] if inverse: table = table.inverse return table[key] except KeyError: return key @staticmethod def _marshal_value(subspace, key, value): """Formats the value for param change. Terra node expects all the values to be JSON-encoded, and Amino:JSON int/int64/uint/uint64 expects quoted values for JavaScript numeric support, and int16/uint16 need `int`. """ try: if key not in PARAM_DEFNS[subspace]: # if not JSON-name # try paramstore name key = ParamChanges._get_key(subspace, key, inverse=True) if len(PARAM_DEFNS[subspace][key]) == 3: value = PARAM_DEFNS[subspace][key][2](value) except KeyError: pass return serialize_to_json(value) @staticmethod def _unmarshal_value(subspace, key, value): """Looks up the correct type to decode the right type for the parameter change.""" if subspace in DES_LOOKUP_TABLE and key in DES_LOOKUP_TABLE[subspace]: if DES_LOOKUP_TABLE[subspace][key] is not None: return DES_LOOKUP_TABLE[subspace][key](value) return value @property def pretty_data(self): return self.changes.items() def to_data(self) -> list: param_changes = [] for subspace, v in self.changes.items(): for key, value in v.items(): param_changes.append({ "subspace": subspace, "key": self._get_key(subspace, key), "value": self._marshal_value(subspace, key, value), }) return param_changes @classmethod def from_data(cls, data: list) -> ParamChanges: changes = JiguBox(default_box=True) for p in data: subspace = p["subspace"] key = cls._get_key( subspace, p["key"], inverse=True ) # p["key"] is paramstore key, we are using json-name keys inside Jigu value = cls._unmarshal_value(subspace, p["key"], json.loads(p["value"])) changes[subspace][key] = value return cls(changes)
def params(self, key: Optional[str] = None) -> Union[ApiResponse, JiguBox]: res = self._api_get("/staking/parameters") p = JiguBox(res, box_recast={"unbonding_time": int}) return project(res, p[key] if key else p)