# SDK Primitives

# Decimal Numbers

jigu.core.Dec

Terra Core uses the sdk.Dec data structure provided by Cosmos-SDK for representing decimal values (18 points of precision). Jigu's Dec replicates this functionality and serializes in a form compatible with Terra.

You can create a Dec from int, float, decimal.Decimal, or str. Dec will try to capture as much precision. Anything beyond 18 decimal digits has no guarantee. Negative values are supported, and non-standard values such as NaN or +/- Infinity are not supported.

from decimal import Decimal
from jigu.core import Dec

Dec(5)
Dec(5.2482)
Dec(Decimal("5.24821242"))
Dec("5.248212422039101232")

You can also convert a Dec into int, float, and str using type-casting.

D = Dec("5.5")
int(D) # 5
float(D) # 5.5
str(D) # 5.500000000000000000

# Operation compatibility

Dec behaves like a numeric type, and can be compared, added/subtracted, multiplied, and divided against int, float, decimal.Decimal, and other Dec objects. This will result in the result becoming a Dec type.

Dec(5) + 4.2 # Dec('9.2')
Dec(7.5) * Dec("23.22") # Dec('174.15')
1 / Dec(5.5) # Dec('0.181818181818181824')
Dec(55.5) // 2 # Dec('27')

# Coin

jigu.core.Coin

Terra Core represents all of its tokens (both Luna and Terra stablecoins) with either sdk.Coin or sdk.DecCoin, composite types that keep an amount alongside the coin denomination. Jigu's Coin serves to unmarshal both, distinguished by the type of their amount attribute (int or Dec). From here, we will call them integer Coin and decimal Coin.

from jigu.core import Coin

Coin("ukrw", 450) # integer Coin
Coin("uluna", 5.3) # decimal Coin

NOTE

sdk.DecCoin is rarely used in Terra Core except for representing fractional Coin values that are sensitive about precision, such as gas prices, Oracle price votes and Community Pool rewards. In normal usage, an account cannot hold or spend non-integral Coin amounts.

# Coin operations

A Coin can be added and subtracted with other Coin objects only if they share the same denomination. If any of the two operands are decimal Coins, the result will be a decimal Coin.

Coin("ukrw", 6) + Coin("ukrw", 5) # Coin('ukrw', 11)
Coin("ukrw", 6.0) - Coin("ukrw", 5) # Coin('ukrw', Dec('1'))
Coin("ukrw", 1) - Coin("uluna", 2) # raises jigu.error.DenomIncompatibleError

A Coin can also be multiplied by numeric types, however the resulting Coin's amount will share the same type as the original (e.g. you cannot multiply an integer Coin by a Dec and get a decimal Coin as a result).

Coin("ukrw", 5) * 5.3 # Coin('ukrw', 26)
Coin("ukrw", 5.0) * 5.3 # Coin('ukrw', Dec('26.5'))

Coins can only be compared (<, <=, >, >=) if they are of the same denomination.

Coin("ukrw", 5.0) <= Coin("ukrw", 7) # True
Coin("ukrw", 5.0) > Coin("uluna", 7) # raises jigu.error.DenomIncompatibleError

NOTE

Equality == can be used for Coins of other denominations without an error, as well as other types of objects. Equality will not distinguish between an integer Coin and decimal Coin.

# Coins

jigu.core.Coins

A Coins object is analagous to sdk.Coins and sdk.DecCoins, and represents a collection of Coin objects.

You may prefer to use Coins over something like List[Coin] because it offers several functions and behaviors that make it more convenient to work with collections of Coin objects.

You can pass any Iterable[Coin] such as a list of Coin objects to the constructor of Coins:

from jigu.core import Coins

coins = Coins([
    Coin("ukrw", 1234),
    Coin("uluna", 1500),
    Coin("umnt", 1600)
])

Alternatively, Coins provides a more readable and convenient notation using keyword arguments. The following code creates the exact same object as above.

# Method #2

coins = Coins(ukrw=1234, uluna=1500, umnt=1600)

# Indexable by denomination

Every Coins object can be indexed like a dictionary using the desired denomination as the key.

coins["uluna"]
# Coin("uluna", 1500)

This means that there can be at most one Coin of that denomination in the collection. When multiple coins of a similar denomination are provided in the list used to instatiate Coins, their values are added together.

# Addition

You can add two Coins objects together to combine their sets together. If there are 2 Coin objects of a similar denomination, they will get combined into one Coin.

A = Coins(uusd=1000, ukrw=1500)
B = Coins(uluna=1000, ukrw=3000)
A + B
# Coins(ukrw=4500, uluna=1000, uusd=1000)

# Iterable

A Coins object implements the Iterable interface, which allows you to iterate over the collection in alphabetic order of denomination.

for coin in Coins(uusd=2000, uluna=1000):
    print(coin.denom)
# uluna
# uusd

# Filterable

You can get a subset of Coins that satisfy some condition. Use Coins.filter() with a predicate.

A = Coins(uusd=0, ukrw=1500, uluna=2500, ukrw=23.52)

# take out zero values
A.filter(lambda c: c.amount != 0)

# get only decimal coins
A.filter(lambda c: type(c.amount) != int)

# Timestamp

jigu.core.Timestamp

Temporal values in Terra Core are serialized as an ISO-8601 formatted string with up to nanosecond precision (9 digits). Jigu uses Timestamp in lieu of datetime to preserve this precision during unmarshaling.

You likely will never have to create Timestamp values directly as they exist mainly as data structures to capture timestamp values from chain data and are automatically generated by the Jigu API.

# Conversion from str

You can unmarshal ISO-8601 formatted strings to Timestamp.

from jigu.core import Timestamp

ts = Timestamp.from_str("2019-12-17T12:13:37.956878188Z")
ts.hour # -> 12

# Conversion from datetime

You can convert Python's datetime.datetime objects to Timestamp.

from datetime import datetime
from jigu.core import Timestamp

dt = datetime.now()
ts = Timestamp.from_datetime(dt)

dt == ts # True

# Interoperability with datetime

You can use Timestamp anywhere that expects a datetime. It inherits all of the methods that are defined by datetime, but loses the nanosecond accuracy whenever you call a method that returns a datetime, such as adding a timedelta.

from datetime import timedelta

ts = Timestamp.from_datetime(datetime.now())
delta = timedelta(days=2)

ts + delta # -> datetime

# Comparison

You can compare Timestamp to other Timestamp and datetime objects, and it will consider nanosecond differences.

# (from test_timestamp.py)
ts0 = Timestamp(2004, 12, 18, 19, 54, 22, 333333000, 9)
ts1 = Timestamp(2004, 12, 18, 19, 54, 22, 333333333, 9)
dt = datetime(2004, 12, 18, 19, 54, 22, 333333)
dt2 = datetime(2004, 12, 18, 19, 54, 22, 433333)

assert dt == ts0
assert dt != ts1
assert ts1 > dt
assert dt2 > ts0

# JiguBox

jigu.util.serdes.JiguBox

Replaces the Python standard library's dict, based on python-box, but provides pretty-printing facilities and is indexable through attribute.

# Pretty-printing

Python 3.7.6 (default, Dec 30 2019, 19:38:26)
>>> from jigu import Terra
>>> terra = Terra("columbus-3", "https://lcd.terra.dev/")
>>> terra.market.params()._pp
╒═════════════════════════╤═════════════════╕
│ pool_recovery_period    │ 14400           │
├─────────────────────────┼─────────────────┤
│ base_pool               │ 250000000000    │
├─────────────────────────┼─────────────────┤
│ min_spread              │ 0.02            │
├─────────────────────────┼─────────────────┤
│ tobin_tax               │ 0.0025          │
├─────────────────────────┼─────────────────┤
│ illiquid_tobin_tax_list │ ╒══════╤══════╕ │
│                         │ │ umnt │ 0.02 │ │
│                         │ ╘══════╧══════╛ │
╘═════════════════════════╧═════════════════╛

# Indexable through attributes

>>> data = {"a": 5000, "b": "bell"}
>>> data["a"]
5000
>>> data.a
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'dict' object has no attribute 'a'
>>> jb = JiguBox(data)
>>> jb["a"]
5000
>>> jb.a
5000
Updated on: 3/4/2020, 8:25:51 AM