2021-06-25 02:33:40 -07:00
|
|
|
|
# # ⚠ Warning
|
|
|
|
|
#
|
|
|
|
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
|
|
|
|
|
# LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
|
|
|
|
|
# NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
|
|
|
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
|
|
|
# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
|
#
|
|
|
|
|
# [🥭 Mango Markets](https://mango.markets/) support is available at:
|
|
|
|
|
# [Docs](https://docs.mango.markets/)
|
|
|
|
|
# [Discord](https://discord.gg/67jySBhxrg)
|
|
|
|
|
# [Twitter](https://twitter.com/mangomarkets)
|
|
|
|
|
# [Github](https://github.com/blockworks-foundation)
|
|
|
|
|
# [Email](mailto:hello@blockworks.foundation)
|
|
|
|
|
|
|
|
|
|
import typing
|
|
|
|
|
|
|
|
|
|
from datetime import datetime
|
|
|
|
|
from decimal import Decimal
|
|
|
|
|
from solana.publickey import PublicKey
|
|
|
|
|
|
|
|
|
|
from .accountinfo import AccountInfo
|
|
|
|
|
from .addressableaccount import AddressableAccount
|
|
|
|
|
from .context import Context
|
|
|
|
|
from .layouts import layouts
|
2021-07-01 13:24:08 -07:00
|
|
|
|
from .metadata import Metadata
|
2021-06-25 02:33:40 -07:00
|
|
|
|
from .version import Version
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# # 🥭 NodeBank class
|
|
|
|
|
#
|
2021-07-21 08:35:38 -07:00
|
|
|
|
# `NodeBank` stores details of deposits/borrows and vault.
|
2021-06-25 02:33:40 -07:00
|
|
|
|
#
|
|
|
|
|
class NodeBank(AddressableAccount):
|
|
|
|
|
def __init__(self, account_info: AccountInfo, version: Version, meta_data: Metadata,
|
2021-07-12 02:57:05 -07:00
|
|
|
|
deposits: Decimal, borrows: Decimal, vault: PublicKey):
|
2021-06-25 02:33:40 -07:00
|
|
|
|
super().__init__(account_info)
|
|
|
|
|
self.version: Version = version
|
|
|
|
|
|
|
|
|
|
self.meta_data: Metadata = meta_data
|
2021-07-12 02:57:05 -07:00
|
|
|
|
self.deposits: Decimal = deposits
|
|
|
|
|
self.borrows: Decimal = borrows
|
2021-06-25 02:33:40 -07:00
|
|
|
|
self.vault: PublicKey = vault
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
2021-08-27 12:37:23 -07:00
|
|
|
|
def from_layout(layout: typing.Any, account_info: AccountInfo, version: Version) -> "NodeBank":
|
2021-06-25 02:33:40 -07:00
|
|
|
|
meta_data: Metadata = layout.meta_data
|
2021-07-12 02:57:05 -07:00
|
|
|
|
deposits: Decimal = layout.deposits
|
|
|
|
|
borrows: Decimal = layout.borrows
|
2021-06-25 02:33:40 -07:00
|
|
|
|
vault: PublicKey = layout.vault
|
|
|
|
|
|
2021-07-12 02:57:05 -07:00
|
|
|
|
return NodeBank(account_info, version, meta_data, deposits, borrows, vault)
|
2021-06-25 02:33:40 -07:00
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
def parse(account_info: AccountInfo) -> "NodeBank":
|
|
|
|
|
data = account_info.data
|
|
|
|
|
if len(data) != layouts.NODE_BANK.sizeof():
|
|
|
|
|
raise Exception(
|
2021-07-12 02:57:05 -07:00
|
|
|
|
f"NodeBank data length ({len(data)}) does not match expected size ({layouts.NODE_BANK.sizeof()})")
|
2021-06-25 02:33:40 -07:00
|
|
|
|
|
|
|
|
|
layout = layouts.NODE_BANK.parse(data)
|
|
|
|
|
return NodeBank.from_layout(layout, account_info, Version.V1)
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
def load(context: Context, address: PublicKey) -> "NodeBank":
|
|
|
|
|
account_info = AccountInfo.load(context, address)
|
|
|
|
|
if account_info is None:
|
|
|
|
|
raise Exception(f"NodeBank account not found at address '{address}'")
|
|
|
|
|
return NodeBank.parse(account_info)
|
|
|
|
|
|
2021-08-01 10:03:46 -07:00
|
|
|
|
def __str__(self) -> str:
|
2021-06-25 02:33:40 -07:00
|
|
|
|
return f"""« 𝙽𝚘𝚍𝚎𝙱𝚊𝚗𝚔 [{self.version}] {self.address}
|
|
|
|
|
{self.meta_data}
|
2021-08-01 10:03:46 -07:00
|
|
|
|
Deposit: {self.deposits}
|
|
|
|
|
Borrow: {self.borrows}
|
2021-06-25 02:33:40 -07:00
|
|
|
|
Vault: {self.vault}
|
|
|
|
|
»"""
|
|
|
|
|
|
|
|
|
|
def __repr__(self) -> str:
|
|
|
|
|
return f"{self}"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# # 🥭 RootBank class
|
|
|
|
|
#
|
2021-07-30 12:31:17 -07:00
|
|
|
|
# `RootBank` stores details of how to reach `NodeBank`.
|
2021-06-25 02:33:40 -07:00
|
|
|
|
#
|
|
|
|
|
class RootBank(AddressableAccount):
|
|
|
|
|
def __init__(self, account_info: AccountInfo, version: Version, meta_data: Metadata,
|
2021-10-05 02:47:41 -07:00
|
|
|
|
optimal_util: Decimal, optimal_rate: Decimal, max_rate: Decimal,
|
2021-06-25 02:33:40 -07:00
|
|
|
|
node_banks: typing.Sequence[PublicKey], deposit_index: Decimal,
|
|
|
|
|
borrow_index: Decimal, last_updated: datetime):
|
|
|
|
|
super().__init__(account_info)
|
|
|
|
|
self.version: Version = version
|
|
|
|
|
|
|
|
|
|
self.meta_data: Metadata = meta_data
|
2021-10-05 02:47:41 -07:00
|
|
|
|
|
|
|
|
|
self.optimal_util: Decimal = optimal_util
|
|
|
|
|
self.optimal_rate: Decimal = optimal_rate
|
|
|
|
|
self.max_rate: Decimal = max_rate
|
|
|
|
|
|
2021-06-25 02:33:40 -07:00
|
|
|
|
self.node_banks: typing.Sequence[PublicKey] = node_banks
|
|
|
|
|
self.deposit_index: Decimal = deposit_index
|
|
|
|
|
self.borrow_index: Decimal = borrow_index
|
|
|
|
|
self.last_updated: datetime = last_updated
|
|
|
|
|
|
|
|
|
|
def pick_node_bank(self, context: Context) -> NodeBank:
|
|
|
|
|
node_bank_account_infos = AccountInfo.load_multiple(context, self.node_banks)
|
|
|
|
|
return list(map(NodeBank.parse, node_bank_account_infos))[0]
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
2021-08-27 12:37:23 -07:00
|
|
|
|
def from_layout(layout: typing.Any, account_info: AccountInfo, version: Version) -> "RootBank":
|
2021-08-03 10:32:12 -07:00
|
|
|
|
meta_data: Metadata = Metadata.from_layout(layout.meta_data)
|
2021-10-05 02:47:41 -07:00
|
|
|
|
|
|
|
|
|
optimal_util: Decimal = layout.optimal_util
|
|
|
|
|
optimal_rate: Decimal = layout.optimal_rate
|
|
|
|
|
max_rate: Decimal = layout.max_rate
|
|
|
|
|
|
2021-06-25 02:33:40 -07:00
|
|
|
|
num_node_banks: Decimal = layout.num_node_banks
|
|
|
|
|
node_banks: typing.Sequence[PublicKey] = layout.node_banks[0:int(num_node_banks)]
|
|
|
|
|
deposit_index: Decimal = layout.deposit_index
|
|
|
|
|
borrow_index: Decimal = layout.borrow_index
|
|
|
|
|
last_updated: datetime = layout.last_updated
|
|
|
|
|
|
2021-10-05 02:47:41 -07:00
|
|
|
|
return RootBank(account_info, version, meta_data, optimal_util, optimal_rate, max_rate, node_banks, deposit_index, borrow_index, last_updated)
|
2021-06-25 02:33:40 -07:00
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
def parse(account_info: AccountInfo) -> "RootBank":
|
|
|
|
|
data = account_info.data
|
|
|
|
|
if len(data) != layouts.ROOT_BANK.sizeof():
|
|
|
|
|
raise Exception(
|
2021-07-12 02:57:05 -07:00
|
|
|
|
f"RootBank data length ({len(data)}) does not match expected size ({layouts.ROOT_BANK.sizeof()})")
|
2021-06-25 02:33:40 -07:00
|
|
|
|
|
|
|
|
|
layout = layouts.ROOT_BANK.parse(data)
|
|
|
|
|
return RootBank.from_layout(layout, account_info, Version.V1)
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
def load(context: Context, address: PublicKey) -> "RootBank":
|
|
|
|
|
account_info = AccountInfo.load(context, address)
|
|
|
|
|
if account_info is None:
|
|
|
|
|
raise Exception(f"RootBank account not found at address '{address}'")
|
|
|
|
|
return RootBank.parse(account_info)
|
|
|
|
|
|
2021-07-01 13:24:08 -07:00
|
|
|
|
@staticmethod
|
|
|
|
|
def load_multiple(context: Context, addresses: typing.Sequence[PublicKey]) -> typing.Sequence["RootBank"]:
|
|
|
|
|
account_infos = AccountInfo.load_multiple(context, addresses)
|
|
|
|
|
root_banks = []
|
|
|
|
|
for account_info in account_infos:
|
|
|
|
|
root_bank = RootBank.parse(account_info)
|
|
|
|
|
root_banks += [root_bank]
|
|
|
|
|
|
|
|
|
|
return root_banks
|
|
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
|
def find_by_address(values: typing.Sequence["RootBank"], address: PublicKey) -> "RootBank":
|
|
|
|
|
found = [value for value in values if value.address == address]
|
|
|
|
|
if len(found) == 0:
|
|
|
|
|
raise Exception(f"RootBank '{address}' not found in root banks: {values}")
|
|
|
|
|
|
|
|
|
|
if len(found) > 1:
|
|
|
|
|
raise Exception(f"RootBank '{address}' matched multiple root banks in: {values}")
|
|
|
|
|
|
|
|
|
|
return found[0]
|
|
|
|
|
|
2021-08-01 10:03:46 -07:00
|
|
|
|
def __str__(self) -> str:
|
2021-06-25 02:33:40 -07:00
|
|
|
|
return f"""« 𝚁𝚘𝚘𝚝𝙱𝚊𝚗𝚔 [{self.version}] {self.address}
|
|
|
|
|
{self.meta_data}
|
2021-10-05 03:21:13 -07:00
|
|
|
|
Optimal Util: {self.optimal_util:,.4f}
|
|
|
|
|
Optimal Rate: {self.optimal_rate:,.4f}
|
2021-10-05 02:47:41 -07:00
|
|
|
|
Max Rate: {self.max_rate}
|
2021-06-25 02:33:40 -07:00
|
|
|
|
Node Banks:
|
|
|
|
|
{self.node_banks}
|
|
|
|
|
Deposit Index: {self.deposit_index}
|
|
|
|
|
Borrow Index: {self.borrow_index}
|
|
|
|
|
Last Updated: {self.last_updated}
|
|
|
|
|
»"""
|
|
|
|
|
|
|
|
|
|
def __repr__(self) -> str:
|
|
|
|
|
return f"{self}"
|