Skip to content

Commit 2ab9836

Browse files
authored
Merge pull request #67 from tinymanorg/liquid-staking
Liquid Staking
2 parents ea4bdf1 + 84539f0 commit 2ab9836

12 files changed

Lines changed: 1187 additions & 7 deletions

File tree

.github/workflows/tests.yml

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,5 @@ jobs:
3131
- name: Run flake8
3232
run: flake8 ${{ github.workspace }} --ignore=E501,F403,F405,E126,E121,W503,E203
3333

34-
- name: Run Black
35-
run: black ${{ github.workspace }} --check
36-
3734
- name: Run Unit tests
3835
run: python -m unittest

tinyman/liquid_staking/__init__.py

Whitespace-only changes.
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
import time
2+
from base64 import b64decode, b64encode
3+
4+
from algosdk import transaction
5+
from algosdk.logic import get_application_address
6+
7+
# TODO: move struct to parent.
8+
from tinyman.liquid_staking.struct import get_struct, get_box_costs
9+
from tinyman.utils import get_global_state, TransactionGroup
10+
11+
12+
class BaseClient():
13+
def __init__(self, algod, app_id, user_address, user_sk) -> None:
14+
self.algod = algod
15+
self.app_id = app_id
16+
self.application_address = get_application_address(self.app_id)
17+
self.user_address = user_address
18+
self.keys = {}
19+
self.add_key(user_address, user_sk)
20+
self.current_timestamp = None
21+
self.simulate = False
22+
23+
def get_suggested_params(self):
24+
return self.algod.suggested_params()
25+
26+
def get_current_timestamp(self):
27+
return self.current_timestamp or time.time()
28+
29+
def _submit(self, transactions, additional_fees=0):
30+
transactions = self.flatten_transactions(transactions)
31+
fee = transactions[0].fee
32+
n = 0
33+
for txn in transactions:
34+
if txn.fee == fee:
35+
txn.fee = 0
36+
n += 1
37+
transactions[0].fee = (n + additional_fees) * fee
38+
txn_group = TransactionGroup(transactions)
39+
for address, key in self.keys.items():
40+
if isinstance(key, transaction.LogicSigAccount):
41+
txn_group.sign_with_logicsig(key, address=address)
42+
else:
43+
txn_group.sign_with_private_key(address, key)
44+
if self.simulate:
45+
txn_info = self.algod.simulate_raw_transactions(txn_group.signed_transactions)
46+
else:
47+
txn_info = txn_group.submit(self.algod, wait=True)
48+
return txn_info
49+
50+
def flatten_transactions(self, txns):
51+
result = []
52+
if isinstance(txns, transaction.Transaction):
53+
result = [txns]
54+
elif isinstance(txns, list):
55+
for txn in txns:
56+
result += self.flatten_transactions(txn)
57+
return result
58+
59+
def calculate_min_balance(self, accounts=0, assets=0, boxes=None):
60+
cost = 0
61+
cost += accounts * 100_000
62+
cost += assets * 100_000
63+
cost += get_box_costs(boxes or {})
64+
return cost
65+
66+
def add_key(self, address, key):
67+
self.keys[address] = key
68+
69+
def get_globals(self, app_id=None):
70+
app_id = app_id or self.app_id
71+
gs = self.algod.application_info(app_id)["params"]["global-state"]
72+
global_state = {s["key"]: s["value"] for s in gs}
73+
state = {}
74+
for key in global_state:
75+
k = b64decode(key)
76+
value = global_state[key]
77+
if value["type"] == 2:
78+
state[k] = value["uint"]
79+
else:
80+
state[k] = b64decode(value["bytes"])
81+
state = dict(sorted(state.items(), key=lambda x: x[0]))
82+
return state
83+
84+
def get_global(self, key, default=None, app_id=None):
85+
app_id = app_id or self.app_id
86+
global_state = {s["key"]: s["value"] for s in self.algod.application_info(app_id)["params"]["global-state"]}
87+
key = b64encode(key).decode()
88+
if key in global_state:
89+
value = global_state[key]
90+
if value["type"] == 2:
91+
return value["uint"]
92+
else:
93+
return b64decode(value["bytes"])
94+
else:
95+
return default
96+
97+
def get_global_state(self, app_id=None):
98+
app_id = app_id or self.app_id
99+
100+
return get_global_state(self.algod, app_id)
101+
102+
def get_box(self, box_name, struct_name, app_id=None):
103+
app_id = app_id or self.app_id
104+
box_value = b64decode(self.algod.application_box_by_name(app_id, box_name)["value"])
105+
struct_class = get_struct(struct_name)
106+
struct = struct_class(box_value)
107+
return struct
108+
109+
def box_exists(self, box_name, app_id=None):
110+
app_id = app_id or self.app_id
111+
try:
112+
self.algod.application_box_by_name(app_id, box_name)
113+
return True
114+
except Exception:
115+
return False
116+
117+
def get_reward_slot(self, staking_asset_id, reward_asset_id):
118+
asset_box = self.get_asset_box(staking_asset_id)
119+
for i in range(8):
120+
if asset_box.reward_slots[i].asset_id == reward_asset_id:
121+
return i
122+
123+
def is_opted_in(self, address, asset_id):
124+
try:
125+
self.algod.account_asset_info(address, asset_id)
126+
return True
127+
except Exception:
128+
return False
129+
130+
def get_optin_if_needed_txn(self, sender, asset_id):
131+
if not self.is_opted_in(sender, asset_id):
132+
txn = transaction.AssetOptInTxn(
133+
sender=sender,
134+
sp=self.get_suggested_params(),
135+
index=asset_id,
136+
)
137+
return txn
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
TESTNET_TALGO_APP_ID = 724519988
2+
MAINNET_TALGO_APP_ID = 2537013674
3+
4+
TESTNET_TALGO_STAKING_APP_ID = 724676904
5+
MAINNET_TALGO_STAKING_APP_ID = 2537022861
6+
7+
TESTNET_TALGO_ASSET_ID = 724519992
8+
MAINNET_TALGO_ASSET_ID = 2537013734
9+
10+
TESTNET_STALGO_ASSET_ID = 724676936
11+
MAINNET_STALGO_ASSET_ID = 2537023208
12+
13+
# App Constants
14+
15+
APP_LOCAL_INTS = 3
16+
APP_LOCAL_BYTES = 1
17+
APP_GLOBAL_INTS = 16
18+
APP_GLOBAL_BYTES = 16
19+
EXTRA_PAGES = 3
20+
21+
VAULT_APP_ID_KEY = b"vault_app_id"
22+
TINY_ASSET_ID_KEY = b"tiny_asset_id"
23+
TALGO_ASSET_ID_KEY = b"talgo_asset_id"
24+
STALGO_ASSET_ID_KEY = b"stalgo_asset_id"
25+
26+
TOTAL_REWARD_AMOUNT_SUM_KEY = b"total_reward_amount_sum"
27+
TOTAL_CLAIMED_REWARD_AMOUNT_KEY = b"total_claimed_reward_amount"
28+
CURRENT_REWARD_RATE_PER_TIME_KEY = b"current_reward_rate_per_time"
29+
CURRENT_REWARD_RATE_PER_TIME_END_TIMESTAMP_KEY = b"current_reward_rate_per_time_end_timestamp"
30+
31+
TINY_POWER_THRESHOLD_KEY = b"tiny_power_threshold"
32+
LAST_UPDATE_TIMESTAMP_KEY = b"last_update_timestamp"
33+
ACCUMULATED_REWARDS_PER_UNIT = b"accumulated_rewards_per_unit"
34+
TOTAL_STAKED_AMOUNT_KEY = b"total_staked_amount"
35+
TOTAL_STAKER_COUNT_KEY = b"total_staker_count"
36+
37+
PROPOSED_MANAGER_KEY = b"proposed_manager"
38+
MANAGER_KEY = b"manager"
39+
40+
MAX_UINT64 = 18446744073709551615
41+
RATE_SCALER = 1_000_000_000_000

0 commit comments

Comments
 (0)