Big renaming of Context properties to make them more consistent and appropriate.

This commit is contained in:
Geoff Taylor 2021-08-26 10:31:02 +01:00
parent b89a072cdd
commit 261848f325
54 changed files with 273 additions and 280 deletions

View File

@ -2,7 +2,6 @@
"cells": [ "cells": [
{ {
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {},
"source": [ "source": [
"# ⚠ Warning\n", "# ⚠ Warning\n",
"\n", "\n",
@ -11,11 +10,11 @@
"[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gl/OpinionatedGeek%2Fmango-explorer/HEAD?filepath=Pandas.ipynb) _🏃 To run this notebook press the ⏩ icon in the toolbar above._\n", "[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gl/OpinionatedGeek%2Fmango-explorer/HEAD?filepath=Pandas.ipynb) _🏃 To run this notebook press the ⏩ icon in the toolbar above._\n",
"\n", "\n",
"[🥭 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)" "[🥭 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)"
] ],
"metadata": {}
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {},
"source": [ "source": [
"# 🥭 Mango + Pandas 🐼🐼\n", "# 🥭 Mango + Pandas 🐼🐼\n",
"\n", "\n",
@ -24,31 +23,28 @@
"The `DataFrame` is then queried for the total assets and liabilities, the Top 10 margin accounts with the most assets and the most liabilities, and then the Top 10 margin accounts closest to liquidation.\n", "The `DataFrame` is then queried for the total assets and liabilities, the Top 10 margin accounts with the most assets and the most liabilities, and then the Top 10 margin accounts closest to liquidation.\n",
"\n", "\n",
"The data remains in the `DataFrame` called `df` so you can easily add your own queries and analyses." "The data remains in the `DataFrame` called `df` so you can easily add your own queries and analyses."
] ],
"metadata": {}
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": null, "execution_count": null,
"metadata": {
"jupyter": {
"source_hidden": true
}
},
"outputs": [],
"source": [ "source": [
"import logging\n", "import logging\n",
"import mango\n", "import mango\n",
"import pandas as pd\n", "import pandas as pd\n",
"import time\n" "import time\n"
] ],
"outputs": [],
"metadata": {
"jupyter": {
"source_hidden": true
}
}
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": null, "execution_count": null,
"metadata": {
"tags": []
},
"outputs": [],
"source": [ "source": [
"start_time = time.time()\n", "start_time = time.time()\n",
"context = mango.Context.default()\n", "context = mango.Context.default()\n",
@ -62,7 +58,7 @@
"print(f\"Done. Time taken: {time.time() - start_time}\")\n", "print(f\"Done. Time taken: {time.time() - start_time}\")\n",
"\n", "\n",
"print(\"Loading margin accounts...\")\n", "print(\"Loading margin accounts...\")\n",
"margin_accounts = mango.MarginAccount.load_all_for_group_with_open_orders(context, context.program_id, group)\n", "margin_accounts = mango.MarginAccount.load_all_for_group_with_open_orders(context, context.mango_program_address, group)\n",
"print(f\"Done. {len(margin_accounts)} accounts. Time taken: {time.time() - start_time}\")\n", "print(f\"Done. {len(margin_accounts)} accounts. Time taken: {time.time() - start_time}\")\n",
"\n", "\n",
"print(\"Loading pandas dataframe...\")\n", "print(\"Loading pandas dataframe...\")\n",
@ -113,20 +109,22 @@
" }\n", " }\n",
" all_formats.update(sheet_formats)\n", " all_formats.update(sheet_formats)\n",
" return df.style.format(all_formats)\n" " return df.style.format(all_formats)\n"
] ],
"outputs": [],
"metadata": {
"tags": []
}
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {},
"source": [ "source": [
"# 🔥 Total Assets + Liabilities" "# 🔥 Total Assets + Liabilities"
] ],
"metadata": {}
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": null, "execution_count": null,
"metadata": {},
"outputs": [],
"source": [ "source": [
"print(f\"\"\"\n", "print(f\"\"\"\n",
"Total Assets: ${df['Assets'].sum():>15,.2f}\n", "Total Assets: ${df['Assets'].sum():>15,.2f}\n",
@ -135,112 +133,114 @@
"Liquidatable: {len(df[(df[\"Collateral Ratio\"] != 0) & (df[\"Collateral Ratio\"] <= 1.1)]):>15,}\n", "Liquidatable: {len(df[(df[\"Collateral Ratio\"] != 0) & (df[\"Collateral Ratio\"] <= 1.1)]):>15,}\n",
"🥭 Ripe Mangoes: {len(df[(df[\"Collateral Ratio\"] > 1.1) & (df[\"Collateral Ratio\"] < 1.2)]):>15,}\n", "🥭 Ripe Mangoes: {len(df[(df[\"Collateral Ratio\"] > 1.1) & (df[\"Collateral Ratio\"] < 1.2)]):>15,}\n",
"\"\"\")" "\"\"\")"
] ],
"outputs": [],
"metadata": {}
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {},
"source": [ "source": [
"# 🔝 Top 10 Greatest Assets\n", "# 🔝 Top 10 Greatest Assets\n",
"\n", "\n",
"The top 10 margin accounts with most assets" "The top 10 margin accounts with most assets"
] ],
"metadata": {}
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": null, "execution_count": null,
"metadata": {},
"outputs": [],
"source": [ "source": [
"render_styled(df.sort_values(\"Assets\", ascending=False).head(10))" "render_styled(df.sort_values(\"Assets\", ascending=False).head(10))"
] ],
"outputs": [],
"metadata": {}
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {},
"source": [ "source": [
"# 🔝 Top 10 Greatest Liabilities\n", "# 🔝 Top 10 Greatest Liabilities\n",
"\n", "\n",
"The top 10 margin accounts with most liabilities" "The top 10 margin accounts with most liabilities"
] ],
"metadata": {}
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": null, "execution_count": null,
"metadata": {},
"outputs": [],
"source": [ "source": [
"render_styled(df.sort_values(\"Liabilities\", ascending=False).head(10))" "render_styled(df.sort_values(\"Liabilities\", ascending=False).head(10))"
] ],
"outputs": [],
"metadata": {}
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {},
"source": [ "source": [
"# 🔝 Top 10 Least Collateralised\n", "# 🔝 Top 10 Least Collateralised\n",
"\n", "\n",
"The top 10 least collateralised margin accounts\n", "The top 10 least collateralised margin accounts\n",
"\n", "\n",
"Collect all margin accounts that have a non-zero Collateral Ratio (so have some liabilities). Then sort them from least-collateralised to most-collateralised." "Collect all margin accounts that have a non-zero Collateral Ratio (so have some liabilities). Then sort them from least-collateralised to most-collateralised."
] ],
"metadata": {}
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": null, "execution_count": null,
"metadata": {},
"outputs": [],
"source": [ "source": [
"nonzero = df[df[\"Collateral Ratio\"] != 0]\n", "nonzero = df[df[\"Collateral Ratio\"] != 0]\n",
"render_styled(nonzero.sort_values(\"Collateral Ratio\", ascending=True).head(10))" "render_styled(nonzero.sort_values(\"Collateral Ratio\", ascending=True).head(10))"
] ],
"outputs": [],
"metadata": {}
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {},
"source": [ "source": [
"# 💧 Liquidatable\n", "# 💧 Liquidatable\n",
"\n", "\n",
"An account is 'liquidatable' when its available collateral falls below the group's maintenance collataeral threshold.\n", "An account is 'liquidatable' when its available collateral falls below the group's maintenance collataeral threshold.\n",
"\n", "\n",
"This code shows all liquidatable margin accounts, sorted by the available collateral (_not_ the collateral ratio)." "This code shows all liquidatable margin accounts, sorted by the available collateral (_not_ the collateral ratio)."
] ],
"metadata": {}
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": null, "execution_count": null,
"metadata": {},
"outputs": [],
"source": [ "source": [
"simplified = nonzero.drop([\"Settled Assets\", \"Unsettled Assets\"], axis=1)\n", "simplified = nonzero.drop([\"Settled Assets\", \"Unsettled Assets\"], axis=1)\n",
"liquidatable = simplified[simplified[\"Collateral Ratio\"] < group.maint_coll_ratio].copy()\n", "liquidatable = simplified[simplified[\"Collateral Ratio\"] < group.maint_coll_ratio].copy()\n",
"\n", "\n",
"print(f\"There are {len(liquidatable)} liquidatable accounts.\")\n", "print(f\"There are {len(liquidatable)} liquidatable accounts.\")\n",
"render_styled(liquidatable.sort_values(\"Available Collateral\", ascending=False).head(len(liquidatable)))" "render_styled(liquidatable.sort_values(\"Available Collateral\", ascending=False).head(len(liquidatable)))"
] ],
"outputs": [],
"metadata": {}
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {},
"source": [ "source": [
"# 🥭 Ripe Mangoes\n", "# 🥭 Ripe Mangoes\n",
"\n", "\n",
"'Ripe mangoes' are margin accounts that are below the group's initial margin requirements but have not yet fallen below the liquidation threshold.\n", "'Ripe mangoes' are margin accounts that are below the group's initial margin requirements but have not yet fallen below the liquidation threshold.\n",
"\n", "\n",
"This code shows all ripe mangoes, sorted by the available collateral (_not_ the collateral ratio)." "This code shows all ripe mangoes, sorted by the available collateral (_not_ the collateral ratio)."
] ],
"metadata": {}
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": null, "execution_count": null,
"metadata": {},
"outputs": [],
"source": [ "source": [
"ripe = simplified[simplified[\"Collateral Ratio\"] < group.init_coll_ratio]\n", "ripe = simplified[simplified[\"Collateral Ratio\"] < group.init_coll_ratio]\n",
"only_ripe = ripe[ripe[\"Collateral Ratio\"] >= group.maint_coll_ratio].copy()\n", "only_ripe = ripe[ripe[\"Collateral Ratio\"] >= group.maint_coll_ratio].copy()\n",
"\n", "\n",
"print(f\"There are {len(only_ripe)} 🥭 ripe mangoes.\")\n", "print(f\"There are {len(only_ripe)} 🥭 ripe mangoes.\")\n",
"render_styled(only_ripe.sort_values(\"Available Collateral\", ascending=False).head(len(only_ripe)))" "render_styled(only_ripe.sort_values(\"Available Collateral\", ascending=False).head(len(only_ripe)))"
] ],
"outputs": [],
"metadata": {}
} }
], ],
"metadata": { "metadata": {

View File

@ -2,7 +2,6 @@
"cells": [ "cells": [
{ {
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {},
"source": [ "source": [
"# ⚠ Warning\n", "# ⚠ Warning\n",
"\n", "\n",
@ -11,11 +10,11 @@
"[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gl/OpinionatedGeek%2Fmango-explorer/HEAD?filepath=ShowAllMarginAccounts.ipynb) _🏃 To run this notebook press the ⏩ icon in the toolbar above._\n", "[![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gl/OpinionatedGeek%2Fmango-explorer/HEAD?filepath=ShowAllMarginAccounts.ipynb) _🏃 To run this notebook press the ⏩ icon in the toolbar above._\n",
"\n", "\n",
"[🥭 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)" "[🥭 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)"
] ],
"metadata": {}
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {},
"source": [ "source": [
"# 🥭 Show All Margin Accounts\n", "# 🥭 Show All Margin Accounts\n",
"\n", "\n",
@ -24,26 +23,21 @@
"It fetches the data from Solana, parses it, and then prints it.\n", "It fetches the data from Solana, parses it, and then prints it.\n",
"\n", "\n",
"Note: this can take a long time to run." "Note: this can take a long time to run."
] ],
"metadata": {}
}, },
{ {
"cell_type": "markdown", "cell_type": "markdown",
"metadata": {},
"source": [ "source": [
"## How To Use This Page\n", "## How To Use This Page\n",
"\n", "\n",
"Theo code should be runnable as-is. Just click the >> button in the toolbar above, and you should see output appear below the code." "Theo code should be runnable as-is. Just click the >> button in the toolbar above, and you should see output appear below the code."
] ],
"metadata": {}
}, },
{ {
"cell_type": "code", "cell_type": "code",
"execution_count": null, "execution_count": null,
"metadata": {
"tags": [
"outputPrepend"
]
},
"outputs": [],
"source": [ "source": [
"if __name__ == \"__main__\":\n", "if __name__ == \"__main__\":\n",
" import mango\n", " import mango\n",
@ -57,7 +51,7 @@
" print(f\"Done loading group. Time taken: {time.time() - start_time}\")\n", " print(f\"Done loading group. Time taken: {time.time() - start_time}\")\n",
"\n", "\n",
" print(\"Loading margin accounts...\")\n", " print(\"Loading margin accounts...\")\n",
" margin_accounts = mango.MarginAccount.load_all_for_group_with_open_orders(context, context.program_id, group)\n", " margin_accounts = mango.MarginAccount.load_all_for_group_with_open_orders(context, context.mango_program_address, group)\n",
" print(f\"Done loading {len(margin_accounts)} account(s). Total time taken: {time.time() - start_time}\")\n", " print(f\"Done loading {len(margin_accounts)} account(s). Total time taken: {time.time() - start_time}\")\n",
"\n", "\n",
" print(*margin_accounts, sep=\"\\n\")\n", " print(*margin_accounts, sep=\"\\n\")\n",
@ -67,7 +61,13 @@
" # import cProfile\n", " # import cProfile\n",
" # import pstats\n", " # import pstats\n",
" # cProfile.run(\"show_all_accounts()\", sort=pstats.SortKey.TIME)\n" " # cProfile.run(\"show_all_accounts()\", sort=pstats.SortKey.TIME)\n"
] ],
"outputs": [],
"metadata": {
"tags": [
"outputPrepend"
]
}
} }
], ],
"metadata": { "metadata": {

View File

@ -25,7 +25,7 @@ logging.warning(mango.WARNING_DISCLAIMER_TEXT)
context = mango.ContextBuilder.from_command_line_parameters(args) context = mango.ContextBuilder.from_command_line_parameters(args)
wallet = mango.Wallet.from_command_line_parameters_or_raise(args) wallet = mango.Wallet.from_command_line_parameters_or_raise(args)
group = mango.Group.load(context, context.group_id) group = mango.Group.load(context, context.group_address)
account = mango.Account.load_for_owner_by_index(context, wallet.address, group, args.account_index) account = mango.Account.load_for_owner_by_index(context, wallet.address, group, args.account_index)
market_symbol = args.market.upper() market_symbol = args.market.upper()

View File

@ -33,7 +33,7 @@ logging.warning(mango.WARNING_DISCLAIMER_TEXT)
context = mango.ContextBuilder.from_command_line_parameters(args) context = mango.ContextBuilder.from_command_line_parameters(args)
wallet = mango.Wallet.from_command_line_parameters_or_raise(args) wallet = mango.Wallet.from_command_line_parameters_or_raise(args)
group = mango.Group.load(context, context.group_id) group = mango.Group.load(context, context.group_address)
account = mango.Account.load_for_owner_by_index(context, wallet.address, group, args.account_index) account = mango.Account.load_for_owner_by_index(context, wallet.address, group, args.account_index)
market_symbol = args.market.upper() market_symbol = args.market.upper()

View File

@ -29,7 +29,7 @@ logging.warning(mango.WARNING_DISCLAIMER_TEXT)
context = mango.ContextBuilder.from_command_line_parameters(args) context = mango.ContextBuilder.from_command_line_parameters(args)
logging.info(f"Context: {context}") logging.info(f"Context: {context}")
wallet = mango.Wallet.from_command_line_parameters_or_raise(args) wallet = mango.Wallet.from_command_line_parameters_or_raise(args)
group = mango.Group.load(context, context.group_id) group = mango.Group.load(context, context.group_address)
account = mango.Account.load_for_owner_by_index(context, wallet.address, group, args.account_index) account = mango.Account.load_for_owner_by_index(context, wallet.address, group, args.account_index)
logging.info(f"Wallet address: {wallet.address}") logging.info(f"Wallet address: {wallet.address}")

View File

@ -27,7 +27,7 @@ logging.warning(mango.WARNING_DISCLAIMER_TEXT)
context = mango.ContextBuilder.from_command_line_parameters(args) context = mango.ContextBuilder.from_command_line_parameters(args)
wallet = mango.Wallet.from_command_line_parameters_or_raise(args) wallet = mango.Wallet.from_command_line_parameters_or_raise(args)
group = mango.Group.load(context, context.group_id) group = mango.Group.load(context, context.group_address)
accounts = mango.Account.load_all_for_owner(context, wallet.address, group) accounts = mango.Account.load_all_for_owner(context, wallet.address, group)
if len(accounts) == 0: if len(accounts) == 0:
raise Exception(f"Could not find any Mango accounts for '{wallet.address}'.") raise Exception(f"Could not find any Mango accounts for '{wallet.address}'.")

View File

@ -42,7 +42,7 @@ logging.warning(mango.WARNING_DISCLAIMER_TEXT)
context = mango.ContextBuilder.from_command_line_parameters(args) context = mango.ContextBuilder.from_command_line_parameters(args)
wallet = mango.Wallet.from_command_line_parameters_or_raise(args) wallet = mango.Wallet.from_command_line_parameters_or_raise(args)
group = mango.Group.load(context, context.group_id) group = mango.Group.load(context, context.group_address)
account = mango.Account.load_for_owner_by_index(context, wallet.address, group, args.account_index) account = mango.Account.load_for_owner_by_index(context, wallet.address, group, args.account_index)
disposer = mango.DisposePropagator() disposer = mango.DisposePropagator()

View File

@ -21,7 +21,7 @@ logging.warning(mango.WARNING_DISCLAIMER_TEXT)
context = mango.ContextBuilder.from_command_line_parameters(args) context = mango.ContextBuilder.from_command_line_parameters(args)
wallet = mango.Wallet.from_command_line_parameters_or_raise(args) wallet = mango.Wallet.from_command_line_parameters_or_raise(args)
group = mango.Group.load(context, context.group_id) group = mango.Group.load(context, context.group_address)
signers: mango.CombinableInstructions = mango.CombinableInstructions.from_wallet(wallet) signers: mango.CombinableInstructions = mango.CombinableInstructions.from_wallet(wallet)
init = mango.build_create_account_instructions(context, wallet, group) init = mango.build_create_account_instructions(context, wallet, group)

View File

@ -74,7 +74,7 @@ health_check = mango.HealthCheck()
disposer.add_disposable(health_check) disposer.add_disposable(health_check)
wallet = mango.Wallet.from_command_line_parameters_or_raise(args) wallet = mango.Wallet.from_command_line_parameters_or_raise(args)
group = mango.Group.load(context, context.group_id) group = mango.Group.load(context, context.group_address)
account = mango.Account.load_for_owner_by_index(context, wallet.address, group, args.account_index) account = mango.Account.load_for_owner_by_index(context, wallet.address, group, args.account_index)
market_symbol = args.market.upper() market_symbol = args.market.upper()

View File

@ -35,7 +35,7 @@ if account_info is None:
else: else:
account_sols = account_info.lamports / mango.SOL_DECIMAL_DIVISOR account_sols = account_info.lamports / mango.SOL_DECIMAL_DIVISOR
if account_sols < args.minimum_sol_balance: if account_sols < args.minimum_sol_balance:
report = f"Account \"{args.name} [{args.address}]\" on {context.client.cluster} has only {account_sols} SOL, which is below the minimum required balance of {args.minimum_sol_balance} SOL." report = f"Account \"{args.name} [{args.address}]\" on {context.client.cluster_name} has only {account_sols} SOL, which is below the minimum required balance of {args.minimum_sol_balance} SOL."
for notify in args.notify: for notify in args.notify:
notify.send(report) notify.send(report)
print(f"Notification sent: {report}") print(f"Notification sent: {report}")

View File

@ -32,7 +32,7 @@ logging.warning(mango.WARNING_DISCLAIMER_TEXT)
context = mango.ContextBuilder.from_command_line_parameters(args) context = mango.ContextBuilder.from_command_line_parameters(args)
wallet = mango.Wallet.from_command_line_parameters_or_raise(args) wallet = mango.Wallet.from_command_line_parameters_or_raise(args)
group = mango.Group.load(context, context.group_id) group = mango.Group.load(context, context.group_address)
account = mango.Account.load_for_owner_by_index(context, wallet.address, group, args.account_index) account = mango.Account.load_for_owner_by_index(context, wallet.address, group, args.account_index)
market_symbol = args.market.upper() market_symbol = args.market.upper()

View File

@ -20,7 +20,7 @@ def report_accrued(basket_token: mango.AccountBasketBaseToken):
def load_perp_market(context: mango.Context, group: mango.Group, group_basket_market: mango.GroupBasketMarket): def load_perp_market(context: mango.Context, group: mango.Group, group_basket_market: mango.GroupBasketMarket):
perp_market_details = mango.PerpMarketDetails.load(context, group_basket_market.perp_market_info.address, group) perp_market_details = mango.PerpMarketDetails.load(context, group_basket_market.perp_market_info.address, group)
perp_market = mango.PerpMarket(context.program_id, group_basket_market.perp_market_info.address, perp_market = mango.PerpMarket(context.mango_program_address, group_basket_market.perp_market_info.address,
group_basket_market.base_token_info.token, group_basket_market.base_token_info.token,
group_basket_market.quote_token_info.token, perp_market_details) group_basket_market.quote_token_info.token, perp_market_details)
@ -70,7 +70,7 @@ if (not args.all) and (args.market is None):
context = mango.ContextBuilder.from_command_line_parameters(args) context = mango.ContextBuilder.from_command_line_parameters(args)
wallet = mango.Wallet.from_command_line_parameters_or_raise(args) wallet = mango.Wallet.from_command_line_parameters_or_raise(args)
group = mango.Group.load(context, context.group_id) group = mango.Group.load(context, context.group_address)
mngo = group.find_token_info_by_symbol("MNGO") mngo = group.find_token_info_by_symbol("MNGO")
account = mango.Account.load_for_owner_by_index(context, wallet.address, group, args.account_index) account = mango.Account.load_for_owner_by_index(context, wallet.address, group, args.account_index)

View File

@ -27,7 +27,7 @@ logging.warning(mango.WARNING_DISCLAIMER_TEXT)
context = mango.ContextBuilder.from_command_line_parameters(args) context = mango.ContextBuilder.from_command_line_parameters(args)
wallet = mango.Wallet.from_command_line_parameters_or_raise(args) wallet = mango.Wallet.from_command_line_parameters_or_raise(args)
group = mango.Group.load(context, context.group_id) group = mango.Group.load(context, context.group_address)
account = mango.Account.load_for_owner_by_index(context, wallet.address, group, args.account_index) account = mango.Account.load_for_owner_by_index(context, wallet.address, group, args.account_index)
logging.info(f"Context: {context}") logging.info(f"Context: {context}")

View File

@ -29,6 +29,6 @@ if address is None:
wallet = mango.Wallet.from_command_line_parameters_or_raise(args) wallet = mango.Wallet.from_command_line_parameters_or_raise(args)
address = wallet.address address = wallet.address
group = mango.Group.load(context, context.group_id) group = mango.Group.load(context, context.group_address)
mango_accounts = mango.Account.load_all_for_owner(context, address, group) mango_accounts = mango.Account.load_all_for_owner(context, address, group)
print(mango_accounts) print(mango_accounts)

View File

@ -26,7 +26,7 @@ logging.warning(mango.WARNING_DISCLAIMER_TEXT)
context = mango.ContextBuilder.from_command_line_parameters(args) context = mango.ContextBuilder.from_command_line_parameters(args)
wallet = mango.Wallet.from_command_line_parameters_or_raise(args) wallet = mango.Wallet.from_command_line_parameters_or_raise(args)
group = mango.Group.load(context, context.group_id) group = mango.Group.load(context, context.group_address)
account = mango.Account.load_for_owner_by_index(context, wallet.address, group, args.account_index) account = mango.Account.load_for_owner_by_index(context, wallet.address, group, args.account_index)
market_symbol = args.market.upper() market_symbol = args.market.upper()

View File

@ -25,7 +25,7 @@ logging.warning(mango.WARNING_DISCLAIMER_TEXT)
context = mango.ContextBuilder.from_command_line_parameters(args) context = mango.ContextBuilder.from_command_line_parameters(args)
wallet = mango.Wallet.from_command_line_parameters_or_raise(args) wallet = mango.Wallet.from_command_line_parameters_or_raise(args)
group = mango.Group.load(context, context.group_id) group = mango.Group.load(context, context.group_address)
account = mango.Account.load_for_owner_by_index(context, wallet.address, group, args.account_index) account = mango.Account.load_for_owner_by_index(context, wallet.address, group, args.account_index)
market_symbol = args.market.upper() market_symbol = args.market.upper()

View File

@ -25,7 +25,7 @@ logging.warning(mango.WARNING_DISCLAIMER_TEXT)
context = mango.ContextBuilder.from_command_line_parameters(args) context = mango.ContextBuilder.from_command_line_parameters(args)
wallet = mango.Wallet.from_command_line_parameters_or_raise(args) wallet = mango.Wallet.from_command_line_parameters_or_raise(args)
group = mango.Group.load(context, context.group_id) group = mango.Group.load(context, context.group_address)
account = mango.Account.load_for_owner_by_index(context, wallet.address, group, args.account_index) account = mango.Account.load_for_owner_by_index(context, wallet.address, group, args.account_index)
market_symbol = args.market.upper() market_symbol = args.market.upper()

View File

@ -32,7 +32,7 @@ if not isinstance(market, mango.SerumMarket):
raise Exception(f"Market {market_symbol} is not a Serum market: {market}") raise Exception(f"Market {market_symbol} is not a Serum market: {market}")
all_open_orders_for_market = mango.OpenOrders.load_for_market_and_owner( all_open_orders_for_market = mango.OpenOrders.load_for_market_and_owner(
context, market.address, wallet.address, context.dex_program_id, market.base.decimals, market.quote.decimals) context, market.address, wallet.address, context.serum_program_address, market.base.decimals, market.quote.decimals)
print(f"Found {len(all_open_orders_for_market)} OpenOrders account(s) for market {market.symbol}.") print(f"Found {len(all_open_orders_for_market)} OpenOrders account(s) for market {market.symbol}.")
for open_orders in all_open_orders_for_market: for open_orders in all_open_orders_for_market:
print(open_orders) print(open_orders)

View File

@ -43,7 +43,7 @@ logging.warning(mango.WARNING_DISCLAIMER_TEXT)
try: try:
context = mango.ContextBuilder.from_command_line_parameters(args) context = mango.ContextBuilder.from_command_line_parameters(args)
wallet = mango.Wallet.from_command_line_parameters_or_raise(args) wallet = mango.Wallet.from_command_line_parameters_or_raise(args)
group = mango.Group.load(context, context.group_id) group = mango.Group.load(context, context.group_address)
account = mango.Account.load_for_owner_by_index(context, wallet.address, group, args.account_index) account = mango.Account.load_for_owner_by_index(context, wallet.address, group, args.account_index)
market_symbol = args.market.upper() market_symbol = args.market.upper()

View File

@ -55,7 +55,7 @@ def sols_from_lamports(lamports: Decimal) -> Decimal:
def notifier(name: str) -> typing.Callable[[mango.AccountInfo], None]: def notifier(name: str) -> typing.Callable[[mango.AccountInfo], None]:
def notify(account_info: mango.AccountInfo) -> None: def notify(account_info: mango.AccountInfo) -> None:
account_sols = sols_from_lamports(account_info.lamports) account_sols = sols_from_lamports(account_info.lamports)
report = f"Account \"{name} [{account_info.address}]\" on {context.client.cluster} has only {account_sols} SOL, which is below the minimum required balance of {args.minimum_sol_balance} SOL." report = f"Account \"{name} [{account_info.address}]\" on {context.client.cluster_name} has only {account_sols} SOL, which is below the minimum required balance of {args.minimum_sol_balance} SOL."
send_balance_notification(report) send_balance_notification(report)
print(f"Notification sent: {report}") print(f"Notification sent: {report}")
return notify return notify

View File

@ -29,7 +29,7 @@ logging.warning(mango.WARNING_DISCLAIMER_TEXT)
context = mango.ContextBuilder.from_command_line_parameters(args) context = mango.ContextBuilder.from_command_line_parameters(args)
wallet = mango.Wallet.from_command_line_parameters_or_raise(args) wallet = mango.Wallet.from_command_line_parameters_or_raise(args)
group = mango.Group.load(context, context.group_id) group = mango.Group.load(context, context.group_address)
accounts = mango.Account.load_all_for_owner(context, wallet.address, group) accounts = mango.Account.load_all_for_owner(context, wallet.address, group)
if len(accounts) == 0: if len(accounts) == 0:
raise Exception(f"Could not find any margin accounts for '{wallet.address}'.") raise Exception(f"Could not find any margin accounts for '{wallet.address}'.")

View File

@ -28,7 +28,7 @@ while :
do do
cancel-my-orders --name "WSMM ${MARKET} (cancel)" --market $MARKET --log-level ERROR cancel-my-orders --name "WSMM ${MARKET} (cancel)" --market $MARKET --log-level ERROR
CURRENT_PRICE=$(fetch-price --provider serum --symbol $ORACLE_MARKET --log-level ERROR --cluster mainnet | cut -d"'" -f 2 | sed 's/,//') CURRENT_PRICE=$(fetch-price --provider serum --symbol $ORACLE_MARKET --log-level ERROR --cluster-name mainnet | cut -d"'" -f 2 | sed 's/,//')
place-order --name "WSMM ${MARKET} (buy)" --market $MARKET --order-type LIMIT \ place-order --name "WSMM ${MARKET} (buy)" --market $MARKET --order-type LIMIT \
--log-level ERROR --side BUY --quantity $FIXED_POSITION_SIZE --price $(echo "$CURRENT_PRICE - $FIXED_SPREAD" | bc) --log-level ERROR --side BUY --quantity $FIXED_POSITION_SIZE --price $(echo "$CURRENT_PRICE - $FIXED_SPREAD" | bc)
place-order --name "WSMM ${MARKET} (sell)" --market $MARKET --order-type LIMIT \ place-order --name "WSMM ${MARKET} (sell)" --market $MARKET --order-type LIMIT \

View File

@ -206,7 +206,7 @@ class Account(AddressableAccount):
) )
] ]
results = context.client.get_program_accounts(context.program_id, memcmp_opts=filters) results = context.client.get_program_accounts(context.mango_program_address, memcmp_opts=filters)
accounts = [] accounts = []
for account_data in results: for account_data in results:
address = PublicKey(account_data["pubkey"]) address = PublicKey(account_data["pubkey"])

View File

@ -134,10 +134,10 @@ UnspecifiedEncoding = "unspecified"
# some common operations better from our point of view. # some common operations better from our point of view.
# #
class CompatibleClient: class CompatibleClient:
def __init__(self, name: str, cluster: str, cluster_url: str, commitment: Commitment, skip_preflight: bool, instruction_reporter: InstructionReporter): def __init__(self, name: str, cluster_name: str, cluster_url: str, commitment: Commitment, skip_preflight: bool, instruction_reporter: InstructionReporter):
self.logger: logging.Logger = logging.getLogger(self.__class__.__name__) self.logger: logging.Logger = logging.getLogger(self.__class__.__name__)
self.name: str = name self.name: str = name
self.cluster: str = cluster self.cluster_name: str = cluster_name
self.cluster_url: str = cluster_url self.cluster_url: str = cluster_url
self.commitment: Commitment = commitment self.commitment: Commitment = commitment
self.skip_preflight: bool = skip_preflight self.skip_preflight: bool = skip_preflight
@ -334,7 +334,7 @@ class CompatibleClient:
return self._build_options(commitment, encoding_to_use, data_slice) return self._build_options(commitment, encoding_to_use, data_slice)
def __str__(self) -> str: def __str__(self) -> str:
return f"« 𝙲𝚘𝚖𝚙𝚊𝚝𝚒𝚋𝚕𝚎𝙲𝚕𝚒𝚎𝚗𝚝 [{self.cluster}]: {self.cluster_url} »" return f"« 𝙲𝚘𝚖𝚙𝚊𝚝𝚒𝚋𝚕𝚎𝙲𝚕𝚒𝚎𝚗𝚝 [{self.cluster_name}]: {self.cluster_url} »"
def __repr__(self) -> str: def __repr__(self) -> str:
return f"{self}" return f"{self}"
@ -351,12 +351,12 @@ class BetterClient:
8), Decimal(16), Decimal(20), Decimal(30)] 8), Decimal(16), Decimal(20), Decimal(30)]
@property @property
def cluster(self) -> str: def cluster_name(self) -> str:
return self.compatible_client.cluster return self.compatible_client.cluster_name
@cluster.setter @cluster_name.setter
def cluster(self, value: str) -> None: def cluster_name(self, value: str) -> None:
self.compatible_client.cluster = value self.compatible_client.cluster_name = value
@property @property
def cluster_url(self) -> str: def cluster_url(self) -> str:
@ -399,8 +399,8 @@ class BetterClient:
self.compatible_client.instruction_reporter = value self.compatible_client.instruction_reporter = value
@staticmethod @staticmethod
def from_configuration(name: str, cluster: str, cluster_url: str, commitment: Commitment, skip_preflight: bool, instruction_reporter: InstructionReporter) -> "BetterClient": def from_configuration(name: str, cluster_name: str, cluster_url: str, commitment: Commitment, skip_preflight: bool, instruction_reporter: InstructionReporter) -> "BetterClient":
compatible = CompatibleClient(name, cluster, cluster_url, commitment, skip_preflight, instruction_reporter) compatible = CompatibleClient(name, cluster_name, cluster_url, commitment, skip_preflight, instruction_reporter)
return BetterClient(compatible) return BetterClient(compatible)
def is_node_healthy(self) -> bool: def is_node_healthy(self) -> bool:
@ -480,7 +480,7 @@ class BetterClient:
return all_confirmed return all_confirmed
def __str__(self) -> str: def __str__(self) -> str:
return f"« 𝙱𝚎𝚝𝚝𝚎𝚛𝙲𝚕𝚒𝚎𝚗𝚝 [{self.cluster}]: {self.cluster_url} »" return f"« 𝙱𝚎𝚝𝚝𝚎𝚛𝙲𝚕𝚒𝚎𝚗𝚝 [{self.cluster_name}]: {self.cluster_url} »"
def __repr__(self) -> str: def __repr__(self) -> str:
return f"{self}" return f"{self}"

View File

@ -40,17 +40,19 @@ _pool_scheduler = ThreadPoolScheduler(multiprocessing.cpu_count())
class Context: class Context:
def __init__(self, name: str, cluster: str, cluster_url: str, skip_preflight: bool, program_id: PublicKey, dex_program_id: PublicKey, def __init__(self, name: str, cluster_name: str, cluster_url: str, skip_preflight: bool, mango_program_address: PublicKey,
group_name: str, group_id: PublicKey, token_lookup: TokenLookup, market_lookup: MarketLookup): serum_program_address: PublicKey, group_name: str, group_address: PublicKey,
token_lookup: TokenLookup, market_lookup: MarketLookup):
self.logger: logging.Logger = logging.getLogger(self.__class__.__name__) self.logger: logging.Logger = logging.getLogger(self.__class__.__name__)
self.name: str = name self.name: str = name
instruction_reporter: InstructionReporter = CompoundInstructionReporter.from_ids(program_id, dex_program_id) instruction_reporter: InstructionReporter = CompoundInstructionReporter.from_addresses(
mango_program_address, serum_program_address)
self.client: BetterClient = BetterClient.from_configuration( self.client: BetterClient = BetterClient.from_configuration(
name, cluster, cluster_url, Commitment("processed"), skip_preflight, instruction_reporter) name, cluster_name, cluster_url, Commitment("processed"), skip_preflight, instruction_reporter)
self.program_id: PublicKey = program_id self.mango_program_address: PublicKey = mango_program_address
self.dex_program_id: PublicKey = dex_program_id self.serum_program_address: PublicKey = serum_program_address
self.group_name: str = group_name self.group_name: str = group_name
self.group_id: PublicKey = group_id self.group_address: PublicKey = group_address
self.token_lookup: TokenLookup = token_lookup self.token_lookup: TokenLookup = token_lookup
self.market_lookup: MarketLookup = market_lookup self.market_lookup: MarketLookup = market_lookup
@ -74,19 +76,19 @@ class Context:
def lookup_group_name(self, group_address: PublicKey) -> str: def lookup_group_name(self, group_address: PublicKey) -> str:
group_address_str = str(group_address) group_address_str = str(group_address)
for group in MangoConstants["groups"]: for group in MangoConstants["groups"]:
if group["cluster"] == self.client.cluster and group["publicKey"] == group_address_str: if group["cluster"] == self.client.cluster_name and group["publicKey"] == group_address_str:
return group["name"] return group["name"]
return "« Unknown Group »" return "« Unknown Group »"
def __str__(self) -> str: def __str__(self) -> str:
return f"""« 𝙲𝚘𝚗𝚝𝚎𝚡𝚝 '{self.name}': return f"""« 𝙲𝚘𝚗𝚝𝚎𝚡𝚝 '{self.name}':
Cluster: {self.client.cluster} Cluster Name: {self.client.cluster_name}
Cluster URL: {self.client.cluster_url} Cluster URL: {self.client.cluster_url}
Program ID: {self.program_id}
DEX Program ID: {self.dex_program_id}
Group Name: {self.group_name} Group Name: {self.group_name}
Group ID: {self.group_id} Group Address: {self.group_address}
Mango Program Address: {self.mango_program_address}
Serum Program Address: {self.serum_program_address}
»""" »"""
def __repr__(self) -> str: def __repr__(self) -> str:

View File

@ -43,7 +43,7 @@ from .tokenlookup import TokenLookup, CompoundTokenLookup
# * CLUSTER_URL # * CLUSTER_URL
# * GROUP_NAME # * GROUP_NAME
# * GROUP_ADDRESS # * GROUP_ADDRESS
# * PROGRAM_ADDRESS # * MANGO_PROGRAM_ADDRESS
# * SERUM_PROGRAM_ADDRESS # * SERUM_PROGRAM_ADDRESS
@ -61,13 +61,13 @@ class ContextBuilder:
def add_command_line_parameters(parser: argparse.ArgumentParser, logging_default=logging.INFO) -> None: def add_command_line_parameters(parser: argparse.ArgumentParser, logging_default=logging.INFO) -> None:
parser.add_argument("--name", type=str, default="Mango Explorer", parser.add_argument("--name", type=str, default="Mango Explorer",
help="Name of the program (used in reports and alerts)") help="Name of the program (used in reports and alerts)")
parser.add_argument("--cluster", type=str, default=None, help="Solana RPC cluster name") parser.add_argument("--cluster-name", type=str, default=None, help="Solana RPC cluster name")
parser.add_argument("--cluster-url", type=str, default=None, help="Solana RPC cluster URL") parser.add_argument("--cluster-url", type=str, default=None, help="Solana RPC cluster URL")
parser.add_argument("--skip-preflight", default=False, action="store_true", help="Skip pre-flight checks")
parser.add_argument("--program-id", type=PublicKey, default=None, help="Mango program ID/address")
parser.add_argument("--dex-program-id", type=PublicKey, default=None, help="DEX program ID/address")
parser.add_argument("--group-name", type=str, default=None, help="Mango group name") parser.add_argument("--group-name", type=str, default=None, help="Mango group name")
parser.add_argument("--group-id", type=PublicKey, default=None, help="Mango group ID/address") parser.add_argument("--group-address", type=PublicKey, default=None, help="Mango group address")
parser.add_argument("--mango-program-address", type=PublicKey, default=None, help="Mango program address")
parser.add_argument("--serum-program-address", type=PublicKey, default=None, help="Serum program address")
parser.add_argument("--skip-preflight", default=False, action="store_true", help="Skip pre-flight checks")
parser.add_argument("--token-data-file", type=str, default=SplTokenLookup.DefaultDataFilepath, parser.add_argument("--token-data-file", type=str, default=SplTokenLookup.DefaultDataFilepath,
help="data file that contains token symbols, names, mints and decimals (format is same as https://raw.githubusercontent.com/solana-labs/token-list/main/src/tokens/solana.tokenlist.json)") help="data file that contains token symbols, names, mints and decimals (format is same as https://raw.githubusercontent.com/solana-labs/token-list/main/src/tokens/solana.tokenlist.json)")
@ -85,25 +85,17 @@ class ContextBuilder:
# #
@staticmethod @staticmethod
def from_command_line_parameters(args: argparse.Namespace) -> "Context": def from_command_line_parameters(args: argparse.Namespace) -> "Context":
# Here we should have values for all our parameters (because they'll either be specified
# on the command-line or will be the default_* value) but we may be in the situation where
# a group name is specified but not a group ID, and in that case we want to look up the
# group ID.
#
# In that situation, the group_name will not be default_group_name but the group_id will
# still be default_group_id. In that situation we want to override what we were passed
# as the group_id.
name: typing.Optional[str] = args.name name: typing.Optional[str] = args.name
group_name: typing.Optional[str] = args.group_name cluster_name: typing.Optional[str] = args.cluster_name
cluster: typing.Optional[str] = args.cluster
cluster_url: typing.Optional[str] = args.cluster_url cluster_url: typing.Optional[str] = args.cluster_url
group_name: typing.Optional[str] = args.group_name
group_address: typing.Optional[PublicKey] = args.group_address
mango_program_address: typing.Optional[PublicKey] = args.mango_program_address
serum_program_address: typing.Optional[PublicKey] = args.serum_program_address
skip_preflight: bool = bool(args.skip_preflight) skip_preflight: bool = bool(args.skip_preflight)
group_id: typing.Optional[PublicKey] = args.group_id
program_id: typing.Optional[PublicKey] = args.program_id
dex_program_id: typing.Optional[PublicKey] = args.dex_program_id
token_filename: str = args.token_data_file token_filename: str = args.token_data_file
return ContextBuilder._build(name, cluster, cluster_url, skip_preflight, group_name, group_id, program_id, dex_program_id, token_filename) return ContextBuilder._build(name, cluster_name, cluster_url, skip_preflight, group_name, group_address, mango_program_address, serum_program_address, token_filename)
@staticmethod @staticmethod
def default(): def default():
@ -111,21 +103,21 @@ class ContextBuilder:
@staticmethod @staticmethod
def from_group_name(context: Context, group_name: str) -> Context: def from_group_name(context: Context, group_name: str) -> Context:
return ContextBuilder._build(context.name, context.client.cluster, context.client.cluster_url, return ContextBuilder._build(context.name, context.client.cluster_name, context.client.cluster_url,
context.client.skip_preflight, group_name, None, context.client.skip_preflight, group_name, None,
None, None, SplTokenLookup.DefaultDataFilepath) None, None, SplTokenLookup.DefaultDataFilepath)
@staticmethod @staticmethod
def forced_to_devnet(context: Context) -> Context: def forced_to_devnet(context: Context) -> Context:
cluster: str = "devnet" cluster_name: str = "devnet"
cluster_url: str = MangoConstants["cluster_urls"][cluster] cluster_url: str = MangoConstants["cluster_urls"][cluster_name]
return ContextBuilder._build(context.name, cluster, cluster_url, context.client.skip_preflight, context.group_name, context.group_id, context.program_id, context.dex_program_id, SplTokenLookup.DefaultDataFilepath) return ContextBuilder._build(context.name, cluster_name, cluster_url, context.client.skip_preflight, context.group_name, context.group_address, context.mango_program_address, context.serum_program_address, SplTokenLookup.DefaultDataFilepath)
@staticmethod @staticmethod
def forced_to_mainnet_beta(context: Context) -> Context: def forced_to_mainnet_beta(context: Context) -> Context:
cluster: str = "mainnet" cluster_name: str = "mainnet"
cluster_url: str = MangoConstants["cluster_urls"][cluster] cluster_url: str = MangoConstants["cluster_urls"][cluster_name]
return ContextBuilder._build(context.name, cluster, cluster_url, context.client.skip_preflight, context.group_name, context.group_id, context.program_id, context.dex_program_id, SplTokenLookup.DefaultDataFilepath) return ContextBuilder._build(context.name, cluster_name, cluster_url, context.client.skip_preflight, context.group_name, context.group_address, context.mango_program_address, context.serum_program_address, SplTokenLookup.DefaultDataFilepath)
# This function is the converse of `add_command_line_parameters()` - it takes # This function is the converse of `add_command_line_parameters()` - it takes
# an argument of parsed command-line parameters and expects to see the ones it added # an argument of parsed command-line parameters and expects to see the ones it added
@ -134,7 +126,7 @@ class ContextBuilder:
# It then uses those parameters to create a properly-configured `Context` object. # It then uses those parameters to create a properly-configured `Context` object.
# #
@staticmethod @staticmethod
def _build(name: typing.Optional[str], cluster: typing.Optional[str], cluster_url: typing.Optional[str], def _build(name: typing.Optional[str], cluster_name: typing.Optional[str], cluster_url: typing.Optional[str],
skip_preflight: bool, group_name: typing.Optional[str], group_address: typing.Optional[PublicKey], skip_preflight: bool, group_name: typing.Optional[str], group_address: typing.Optional[PublicKey],
program_address: typing.Optional[PublicKey], serum_program_address: typing.Optional[PublicKey], program_address: typing.Optional[PublicKey], serum_program_address: typing.Optional[PublicKey],
token_filename: str) -> "Context": token_filename: str) -> "Context":
@ -144,7 +136,7 @@ class ContextBuilder:
return None return None
default_group_data = MangoConstants["groups"][0] default_group_data = MangoConstants["groups"][0]
actual_name: str = name or os.environ.get("NAME") or "Mango Explorer" actual_name: str = name or os.environ.get("NAME") or "Mango Explorer"
actual_cluster: str = cluster or os.environ.get("CLUSTER") or default_group_data["cluster"] actual_cluster: str = cluster_name or os.environ.get("CLUSTER") or default_group_data["cluster"]
actual_cluster_url: str = cluster_url or os.environ.get( actual_cluster_url: str = cluster_url or os.environ.get(
"CLUSTER_URL") or MangoConstants["cluster_urls"][actual_cluster] "CLUSTER_URL") or MangoConstants["cluster_urls"][actual_cluster]
actual_skip_preflight: bool = skip_preflight actual_skip_preflight: bool = skip_preflight
@ -156,12 +148,12 @@ class ContextBuilder:
found_group_data = group found_group_data = group
if found_group_data is None: if found_group_data is None:
raise Exception(f"Could not find group named '{actual_group_name}' in cluster '{actual_cluster}.") raise Exception(f"Could not find group named '{actual_group_name}' in cluster_name '{actual_cluster}.")
actual_group_address: PublicKey = group_address or public_key_or_none(os.environ.get( actual_group_address: PublicKey = group_address or public_key_or_none(os.environ.get(
"GROUP_ADDRESS")) or PublicKey(found_group_data["publicKey"]) "GROUP_ADDRESS")) or PublicKey(found_group_data["publicKey"])
actual_program_address: PublicKey = program_address or public_key_or_none(os.environ.get( actual_program_address: PublicKey = program_address or public_key_or_none(os.environ.get(
"PROGRAM_ADDRESS")) or PublicKey(found_group_data["mangoProgramId"]) "MANGO_PROGRAM_ADDRESS")) or PublicKey(found_group_data["mangoProgramId"])
actual_serum_program_address: PublicKey = serum_program_address or public_key_or_none(os.environ.get( actual_serum_program_address: PublicKey = serum_program_address or public_key_or_none(os.environ.get(
"SERUM_PROGRAM_ADDRESS")) or PublicKey(found_group_data["serumProgramId"]) "SERUM_PROGRAM_ADDRESS")) or PublicKey(found_group_data["serumProgramId"])

View File

@ -82,7 +82,7 @@ class Group(AddressableAccount):
basket_indices: typing.Sequence[bool], basket_indices: typing.Sequence[bool],
basket: typing.Sequence[GroupBasketMarket], basket: typing.Sequence[GroupBasketMarket],
signer_nonce: Decimal, signer_key: PublicKey, signer_nonce: Decimal, signer_key: PublicKey,
admin: PublicKey, dex_program_id: PublicKey, cache: PublicKey, valid_interval: Decimal, admin: PublicKey, serum_program_address: PublicKey, cache: PublicKey, valid_interval: Decimal,
dao_vault: PublicKey, srm_vault: PublicKey, msrm_vault: PublicKey): dao_vault: PublicKey, srm_vault: PublicKey, msrm_vault: PublicKey):
super().__init__(account_info) super().__init__(account_info)
self.version: Version = version self.version: Version = version
@ -95,7 +95,7 @@ class Group(AddressableAccount):
self.signer_nonce: Decimal = signer_nonce self.signer_nonce: Decimal = signer_nonce
self.signer_key: PublicKey = signer_key self.signer_key: PublicKey = signer_key
self.admin: PublicKey = admin self.admin: PublicKey = admin
self.dex_program_id: PublicKey = dex_program_id self.serum_program_address: PublicKey = serum_program_address
self.cache: PublicKey = cache self.cache: PublicKey = cache
self.valid_interval: Decimal = valid_interval self.valid_interval: Decimal = valid_interval
self.dao_vault: PublicKey = dao_vault self.dao_vault: PublicKey = dao_vault
@ -151,14 +151,14 @@ class Group(AddressableAccount):
signer_nonce: Decimal = layout.signer_nonce signer_nonce: Decimal = layout.signer_nonce
signer_key: PublicKey = layout.signer_key signer_key: PublicKey = layout.signer_key
admin: PublicKey = layout.admin admin: PublicKey = layout.admin
dex_program_id: PublicKey = layout.dex_program_id serum_program_address: PublicKey = layout.serum_program_address
cache: PublicKey = layout.cache cache: PublicKey = layout.cache
valid_interval: Decimal = layout.valid_interval valid_interval: Decimal = layout.valid_interval
dao_vault: PublicKey = layout.dao_vault dao_vault: PublicKey = layout.dao_vault
srm_vault: PublicKey = layout.srm_vault srm_vault: PublicKey = layout.srm_vault
msrm_vault: PublicKey = layout.msrm_vault msrm_vault: PublicKey = layout.msrm_vault
return Group(account_info, version, name, meta_data, quote_token_info, in_basket, basket, signer_nonce, signer_key, admin, dex_program_id, cache, valid_interval, dao_vault, srm_vault, msrm_vault) return Group(account_info, version, name, meta_data, quote_token_info, in_basket, basket, signer_nonce, signer_key, admin, serum_program_address, cache, valid_interval, dao_vault, srm_vault, msrm_vault)
@staticmethod @staticmethod
def parse(context: Context, account_info: AccountInfo) -> "Group": def parse(context: Context, account_info: AccountInfo) -> "Group":
@ -186,7 +186,7 @@ class Group(AddressableAccount):
@staticmethod @staticmethod
def load(context: Context, address: typing.Optional[PublicKey] = None) -> "Group": def load(context: Context, address: typing.Optional[PublicKey] = None) -> "Group":
group_address: PublicKey = address or context.group_id group_address: PublicKey = address or context.group_address
account_info = AccountInfo.load(context, group_address) account_info = AccountInfo.load(context, group_address)
if account_info is None: if account_info is None:
raise Exception(f"Group account not found at address '{group_address}'") raise Exception(f"Group account not found at address '{group_address}'")
@ -239,7 +239,7 @@ class Group(AddressableAccount):
Name: {self.name} Name: {self.name}
Signer [Nonce: {self.signer_nonce}]: {self.signer_key} Signer [Nonce: {self.signer_nonce}]: {self.signer_key}
Admin: {self.admin} Admin: {self.admin}
DEX Program ID: {self.dex_program_id} DEX Program ID: {self.serum_program_address}
Cache: {self.cache} Cache: {self.cache}
DAO Vault: {self.dao_vault} DAO Vault: {self.dao_vault}
SRM Vault: {self.srm_vault} SRM Vault: {self.srm_vault}

View File

@ -45,20 +45,20 @@ class IdsJsonMarketType(enum.Enum):
class IdsJsonMarketLookup(MarketLookup): class IdsJsonMarketLookup(MarketLookup):
def __init__(self, cluster: str) -> None: def __init__(self, cluster_name: str) -> None:
super().__init__() super().__init__()
self.cluster: str = cluster self.cluster_name: str = cluster_name
@staticmethod @staticmethod
def _from_dict(market_type: IdsJsonMarketType, program_id: PublicKey, group_address: PublicKey, data: typing.Dict, tokens: typing.Sequence[Token], quote_symbol: str) -> Market: def _from_dict(market_type: IdsJsonMarketType, mango_program_address: PublicKey, group_address: PublicKey, data: typing.Dict, tokens: typing.Sequence[Token], quote_symbol: str) -> Market:
base_symbol = data["baseSymbol"] base_symbol = data["baseSymbol"]
base = Token.find_by_symbol(tokens, base_symbol) base = Token.find_by_symbol(tokens, base_symbol)
quote = Token.find_by_symbol(tokens, quote_symbol) quote = Token.find_by_symbol(tokens, quote_symbol)
address = PublicKey(data["publicKey"]) address = PublicKey(data["publicKey"])
if market_type == IdsJsonMarketType.PERP: if market_type == IdsJsonMarketType.PERP:
return PerpMarketStub(program_id, address, base, quote, group_address) return PerpMarketStub(mango_program_address, address, base, quote, group_address)
else: else:
return SpotMarketStub(program_id, address, base, quote, group_address) return SpotMarketStub(mango_program_address, address, base, quote, group_address)
@staticmethod @staticmethod
def _load_tokens(data: typing.Dict) -> typing.Sequence[Token]: def _load_tokens(data: typing.Dict) -> typing.Sequence[Token]:
@ -80,51 +80,51 @@ class IdsJsonMarketLookup(MarketLookup):
check_spots = False # Skip spot markets because we're explicitly told it's a perp check_spots = False # Skip spot markets because we're explicitly told it's a perp
for group in MangoConstants["groups"]: for group in MangoConstants["groups"]:
if group["cluster"] == self.cluster: if group["cluster"] == self.cluster_name:
group_address: PublicKey = PublicKey(group["publicKey"]) group_address: PublicKey = PublicKey(group["publicKey"])
program_id: PublicKey = PublicKey(group["mangoProgramId"]) mango_program_address: PublicKey = PublicKey(group["mangoProgramId"])
if check_perps: if check_perps:
for market_data in group["perpMarkets"]: for market_data in group["perpMarkets"]:
if market_data["name"].upper() == symbol.upper(): if market_data["name"].upper() == symbol.upper():
tokens = IdsJsonMarketLookup._load_tokens(group["tokens"]) tokens = IdsJsonMarketLookup._load_tokens(group["tokens"])
return IdsJsonMarketLookup._from_dict(IdsJsonMarketType.PERP, program_id, group_address, market_data, tokens, group["quoteSymbol"]) return IdsJsonMarketLookup._from_dict(IdsJsonMarketType.PERP, mango_program_address, group_address, market_data, tokens, group["quoteSymbol"])
if check_spots: if check_spots:
for market_data in group["spotMarkets"]: for market_data in group["spotMarkets"]:
if market_data["name"].upper() == symbol.upper(): if market_data["name"].upper() == symbol.upper():
tokens = IdsJsonMarketLookup._load_tokens(group["tokens"]) tokens = IdsJsonMarketLookup._load_tokens(group["tokens"])
return IdsJsonMarketLookup._from_dict(IdsJsonMarketType.SPOT, program_id, group_address, market_data, tokens, group["quoteSymbol"]) return IdsJsonMarketLookup._from_dict(IdsJsonMarketType.SPOT, mango_program_address, group_address, market_data, tokens, group["quoteSymbol"])
return None return None
def find_by_address(self, address: PublicKey) -> typing.Optional[Market]: def find_by_address(self, address: PublicKey) -> typing.Optional[Market]:
for group in MangoConstants["groups"]: for group in MangoConstants["groups"]:
if group["cluster"] == self.cluster: if group["cluster"] == self.cluster_name:
group_address: PublicKey = PublicKey(group["publicKey"]) group_address: PublicKey = PublicKey(group["publicKey"])
program_id: PublicKey = PublicKey(group["mangoProgramId"]) mango_program_address: PublicKey = PublicKey(group["mangoProgramId"])
for market_data in group["perpMarkets"]: for market_data in group["perpMarkets"]:
if market_data["key"] == str(address): if market_data["key"] == str(address):
tokens = IdsJsonMarketLookup._load_tokens(group["tokens"]) tokens = IdsJsonMarketLookup._load_tokens(group["tokens"])
return IdsJsonMarketLookup._from_dict(IdsJsonMarketType.PERP, program_id, group_address, market_data, tokens, group["quoteSymbol"]) return IdsJsonMarketLookup._from_dict(IdsJsonMarketType.PERP, mango_program_address, group_address, market_data, tokens, group["quoteSymbol"])
for market_data in group["spotMarkets"]: for market_data in group["spotMarkets"]:
if market_data["key"] == str(address): if market_data["key"] == str(address):
tokens = IdsJsonMarketLookup._load_tokens(group["tokens"]) tokens = IdsJsonMarketLookup._load_tokens(group["tokens"])
return IdsJsonMarketLookup._from_dict(IdsJsonMarketType.SPOT, program_id, group_address, market_data, tokens, group["quoteSymbol"]) return IdsJsonMarketLookup._from_dict(IdsJsonMarketType.SPOT, mango_program_address, group_address, market_data, tokens, group["quoteSymbol"])
return None return None
def all_markets(self) -> typing.Sequence[Market]: def all_markets(self) -> typing.Sequence[Market]:
markets = [] markets = []
for group in MangoConstants["groups"]: for group in MangoConstants["groups"]:
if group["cluster"] == self.cluster: if group["cluster"] == self.cluster_name:
group_address: PublicKey = PublicKey(group["publicKey"]) group_address: PublicKey = PublicKey(group["publicKey"])
program_id: PublicKey = PublicKey(group["mangoProgramId"]) mango_program_address: PublicKey = PublicKey(group["mangoProgramId"])
for market_data in group["perpMarkets"]: for market_data in group["perpMarkets"]:
tokens = IdsJsonMarketLookup._load_tokens(group["tokens"]) tokens = IdsJsonMarketLookup._load_tokens(group["tokens"])
market = IdsJsonMarketLookup._from_dict( market = IdsJsonMarketLookup._from_dict(
IdsJsonMarketType.PERP, program_id, group_address, market_data, tokens, group["quoteSymbol"]) IdsJsonMarketType.PERP, mango_program_address, group_address, market_data, tokens, group["quoteSymbol"])
markets = [market] markets = [market]
for market_data in group["spotMarkets"]: for market_data in group["spotMarkets"]:
tokens = IdsJsonMarketLookup._load_tokens(group["tokens"]) tokens = IdsJsonMarketLookup._load_tokens(group["tokens"])
market = IdsJsonMarketLookup._from_dict( market = IdsJsonMarketLookup._from_dict(
IdsJsonMarketType.SPOT, program_id, group_address, market_data, tokens, group["quoteSymbol"]) IdsJsonMarketType.SPOT, mango_program_address, group_address, market_data, tokens, group["quoteSymbol"])
markets = [market] markets = [market]
return markets return markets

View File

@ -29,14 +29,14 @@ from .tokenlookup import TokenLookup
# #
class IdsJsonTokenLookup(TokenLookup): class IdsJsonTokenLookup(TokenLookup):
def __init__(self, cluster: str, group_name: str) -> None: def __init__(self, cluster_name: str, group_name: str) -> None:
super().__init__() super().__init__()
self.cluster: str = cluster self.cluster_name: str = cluster_name
self.group_name: str = group_name self.group_name: str = group_name
def find_by_symbol(self, symbol: str) -> typing.Optional[Token]: def find_by_symbol(self, symbol: str) -> typing.Optional[Token]:
for group in MangoConstants["groups"]: for group in MangoConstants["groups"]:
if group["cluster"] == self.cluster and group["name"] == self.group_name: if group["cluster"] == self.cluster_name and group["name"] == self.group_name:
for token in group["tokens"]: for token in group["tokens"]:
if token["symbol"] == symbol: if token["symbol"] == symbol:
return Token(token["symbol"], token["symbol"], PublicKey(token["mintKey"]), Decimal(token["decimals"])) return Token(token["symbol"], token["symbol"], PublicKey(token["mintKey"]), Decimal(token["decimals"]))
@ -45,7 +45,7 @@ class IdsJsonTokenLookup(TokenLookup):
def find_by_mint(self, mint: PublicKey) -> typing.Optional[Token]: def find_by_mint(self, mint: PublicKey) -> typing.Optional[Token]:
mint_str = str(mint) mint_str = str(mint)
for group in MangoConstants["groups"]: for group in MangoConstants["groups"]:
if group["cluster"] == self.cluster and group["name"] == self.group_name: if group["cluster"] == self.cluster_name and group["name"] == self.group_name:
for token in group["tokens"]: for token in group["tokens"]:
if token["mintKey"] == mint_str: if token["mintKey"] == mint_str:
return Token(token["symbol"], token["symbol"], PublicKey(token["mintKey"]), Decimal(token["decimals"])) return Token(token["symbol"], token["symbol"], PublicKey(token["mintKey"]), Decimal(token["decimals"]))

View File

@ -48,11 +48,11 @@ class InstructionReporter:
# The `SerumInstructionParser` class knows a bit more about Serum instructions. # The `SerumInstructionParser` class knows a bit more about Serum instructions.
# #
class SerumInstructionReporter(InstructionReporter): class SerumInstructionReporter(InstructionReporter):
def __init__(self, dex_program_id: PublicKey): def __init__(self, serum_program_address: PublicKey):
self.dex_program_id: PublicKey = dex_program_id self.serum_program_address: PublicKey = serum_program_address
def matches(self, instruction: TransactionInstruction) -> bool: def matches(self, instruction: TransactionInstruction) -> bool:
return instruction.program_id == self.dex_program_id return instruction.program_id == self.serum_program_address
def report(self, instruction: TransactionInstruction) -> str: def report(self, instruction: TransactionInstruction) -> str:
initial = layouts.SERUM_INSTRUCTION_VARIANT_FINDER.parse(instruction.data) initial = layouts.SERUM_INSTRUCTION_VARIANT_FINDER.parse(instruction.data)
@ -65,11 +65,11 @@ class SerumInstructionReporter(InstructionReporter):
# The `MangoInstructionReporter` class knows a bit more about Mango instructions. # The `MangoInstructionReporter` class knows a bit more about Mango instructions.
# #
class MangoInstructionReporter(InstructionReporter): class MangoInstructionReporter(InstructionReporter):
def __init__(self, program_id: PublicKey): def __init__(self, mango_program_address: PublicKey):
self.program_id: PublicKey = program_id self.mango_program_address: PublicKey = mango_program_address
def matches(self, instruction: TransactionInstruction) -> bool: def matches(self, instruction: TransactionInstruction) -> bool:
return instruction.program_id == self.program_id return instruction.program_id == self.mango_program_address
def report(self, instruction: TransactionInstruction) -> str: def report(self, instruction: TransactionInstruction) -> str:
initial = layouts.MANGO_INSTRUCTION_VARIANT_FINDER.parse(instruction.data) initial = layouts.MANGO_INSTRUCTION_VARIANT_FINDER.parse(instruction.data)
@ -107,8 +107,8 @@ class CompoundInstructionReporter(InstructionReporter):
f"Could not find instruction reporter for instruction {instruction}.") f"Could not find instruction reporter for instruction {instruction}.")
@staticmethod @staticmethod
def from_ids(program_id: PublicKey, dex_program_id: PublicKey) -> InstructionReporter: def from_addresses(mango_program_address: PublicKey, serum_program_address: PublicKey) -> InstructionReporter:
base: InstructionReporter = InstructionReporter() base: InstructionReporter = InstructionReporter()
serum: InstructionReporter = SerumInstructionReporter(dex_program_id) serum: InstructionReporter = SerumInstructionReporter(serum_program_address)
mango: InstructionReporter = MangoInstructionReporter(program_id) mango: InstructionReporter = MangoInstructionReporter(mango_program_address)
return CompoundInstructionReporter([mango, serum, base]) return CompoundInstructionReporter([mango, serum, base])

View File

@ -66,11 +66,11 @@ from .wallet import Wallet
# necesary. # necesary.
# #
def build_create_solana_account_instructions(context: Context, wallet: Wallet, program_id: PublicKey, size: int, lamports: int = 0) -> CombinableInstructions: def build_create_solana_account_instructions(context: Context, wallet: Wallet, mango_program_address: PublicKey, size: int, lamports: int = 0) -> CombinableInstructions:
minimum_balance = context.client.get_minimum_balance_for_rent_exemption(size) minimum_balance = context.client.get_minimum_balance_for_rent_exemption(size)
account = SolanaAccount() account = SolanaAccount()
create_instruction = create_account( create_instruction = create_account(
CreateAccountParams(wallet.address, account.public_key(), lamports + minimum_balance, size, program_id)) CreateAccountParams(wallet.address, account.public_key(), lamports + minimum_balance, size, mango_program_address))
return CombinableInstructions(signers=[account], instructions=[create_instruction]) return CombinableInstructions(signers=[account], instructions=[create_instruction])
@ -177,7 +177,7 @@ def build_serum_consume_events_instructions(context: Context, market_address: Pu
AccountMeta(pubkey=pubkey, is_signer=False, is_writable=True) AccountMeta(pubkey=pubkey, is_signer=False, is_writable=True)
for pubkey in [*open_orders_addresses, market_address, event_queue_address] for pubkey in [*open_orders_addresses, market_address, event_queue_address]
], ],
program_id=context.dex_program_id, program_id=context.serum_program_address,
data=PYSERUM_INSTRUCTIONS_LAYOUT.build( data=PYSERUM_INSTRUCTIONS_LAYOUT.build(
dict(instruction_type=PySerumInstructionType.CONSUME_EVENTS, args=dict(limit=limit)) dict(instruction_type=PySerumInstructionType.CONSUME_EVENTS, args=dict(limit=limit))
), ),
@ -260,7 +260,7 @@ def build_spot_settle_instructions(context: Context, wallet: Wallet, account: Ac
AccountMeta(is_signer=False, is_writable=True, pubkey=group.cache), AccountMeta(is_signer=False, is_writable=True, pubkey=group.cache),
AccountMeta(is_signer=True, is_writable=False, pubkey=wallet.address), AccountMeta(is_signer=True, is_writable=False, pubkey=wallet.address),
AccountMeta(is_signer=False, is_writable=True, pubkey=account.address), AccountMeta(is_signer=False, is_writable=True, pubkey=account.address),
AccountMeta(is_signer=False, is_writable=False, pubkey=context.dex_program_id), AccountMeta(is_signer=False, is_writable=False, pubkey=context.serum_program_address),
AccountMeta(is_signer=False, is_writable=True, pubkey=market.state.public_key()), AccountMeta(is_signer=False, is_writable=True, pubkey=market.state.public_key()),
AccountMeta(is_signer=False, is_writable=True, pubkey=open_orders_address), AccountMeta(is_signer=False, is_writable=True, pubkey=open_orders_address),
AccountMeta(is_signer=False, is_writable=False, pubkey=group.signer_key), AccountMeta(is_signer=False, is_writable=False, pubkey=group.signer_key),
@ -275,7 +275,7 @@ def build_spot_settle_instructions(context: Context, wallet: Wallet, account: Ac
AccountMeta(is_signer=False, is_writable=False, pubkey=vault_signer), AccountMeta(is_signer=False, is_writable=False, pubkey=vault_signer),
AccountMeta(is_signer=False, is_writable=False, pubkey=TOKEN_PROGRAM_ID) AccountMeta(is_signer=False, is_writable=False, pubkey=TOKEN_PROGRAM_ID)
], ],
program_id=context.program_id, program_id=context.mango_program_address,
data=layouts.SETTLE_FUNDS.build(dict()) data=layouts.SETTLE_FUNDS.build(dict())
) )
@ -352,7 +352,7 @@ def build_cancel_perp_order_instructions(context: Context, wallet: Wallet, accou
AccountMeta(is_signer=False, is_writable=True, pubkey=perp_market_details.bids), AccountMeta(is_signer=False, is_writable=True, pubkey=perp_market_details.bids),
AccountMeta(is_signer=False, is_writable=True, pubkey=perp_market_details.asks) AccountMeta(is_signer=False, is_writable=True, pubkey=perp_market_details.asks)
], ],
program_id=context.program_id, program_id=context.mango_program_address,
data=data data=data
) )
] ]
@ -399,7 +399,7 @@ def build_place_perp_order_instructions(context: Context, wallet: Wallet, group:
*list([AccountMeta(is_signer=False, is_writable=False, *list([AccountMeta(is_signer=False, is_writable=False,
pubkey=oo_address or SYSTEM_PROGRAM_ADDRESS) for oo_address in account.spot_open_orders]) pubkey=oo_address or SYSTEM_PROGRAM_ADDRESS) for oo_address in account.spot_open_orders])
], ],
program_id=context.program_id, program_id=context.mango_program_address,
data=layouts.PLACE_PERP_ORDER.build( data=layouts.PLACE_PERP_ORDER.build(
{ {
"price": native_price, "price": native_price,
@ -435,7 +435,7 @@ def build_mango_consume_events_instructions(context: Context, group: Group, perp
*list([AccountMeta(is_signer=False, is_writable=True, *list([AccountMeta(is_signer=False, is_writable=True,
pubkey=account_address) for account_address in account_addresses]) pubkey=account_address) for account_address in account_addresses])
], ],
program_id=context.program_id, program_id=context.mango_program_address,
data=layouts.CONSUME_EVENTS.build( data=layouts.CONSUME_EVENTS.build(
{ {
"limit": limit, "limit": limit,
@ -447,7 +447,7 @@ def build_mango_consume_events_instructions(context: Context, group: Group, perp
def build_create_account_instructions(context: Context, wallet: Wallet, group: Group) -> CombinableInstructions: def build_create_account_instructions(context: Context, wallet: Wallet, group: Group) -> CombinableInstructions:
create_account_instructions = build_create_solana_account_instructions( create_account_instructions = build_create_solana_account_instructions(
context, wallet, context.program_id, layouts.MANGO_ACCOUNT.sizeof()) context, wallet, context.mango_program_address, layouts.MANGO_ACCOUNT.sizeof())
mango_account_address = create_account_instructions.signers[0].public_key() mango_account_address = create_account_instructions.signers[0].public_key()
# /// 0. `[]` mango_group_ai - Group that this mango account is for # /// 0. `[]` mango_group_ai - Group that this mango account is for
@ -460,7 +460,7 @@ def build_create_account_instructions(context: Context, wallet: Wallet, group: G
AccountMeta(is_signer=False, is_writable=True, pubkey=mango_account_address), AccountMeta(is_signer=False, is_writable=True, pubkey=mango_account_address),
AccountMeta(is_signer=True, is_writable=False, pubkey=wallet.address) AccountMeta(is_signer=True, is_writable=False, pubkey=wallet.address)
], ],
program_id=context.program_id, program_id=context.mango_program_address,
data=layouts.INIT_MANGO_ACCOUNT.build({}) data=layouts.INIT_MANGO_ACCOUNT.build({})
) )
return create_account_instructions + CombinableInstructions(signers=[], instructions=[init]) return create_account_instructions + CombinableInstructions(signers=[], instructions=[init])
@ -496,7 +496,7 @@ def build_deposit_instructions(context: Context, wallet: Wallet, group: Group, a
AccountMeta(is_signer=False, is_writable=False, pubkey=TOKEN_PROGRAM_ID), AccountMeta(is_signer=False, is_writable=False, pubkey=TOKEN_PROGRAM_ID),
AccountMeta(is_signer=False, is_writable=True, pubkey=token_account.address) AccountMeta(is_signer=False, is_writable=True, pubkey=token_account.address)
], ],
program_id=context.program_id, program_id=context.mango_program_address,
data=layouts.DEPOSIT.build({ data=layouts.DEPOSIT.build({
"quantity": value "quantity": value
}) })
@ -544,7 +544,7 @@ def build_withdraw_instructions(context: Context, wallet: Wallet, group: Group,
*list([AccountMeta(is_signer=False, is_writable=False, *list([AccountMeta(is_signer=False, is_writable=False,
pubkey=oo_address or SYSTEM_PROGRAM_ADDRESS) for oo_address in account.spot_open_orders]) pubkey=oo_address or SYSTEM_PROGRAM_ADDRESS) for oo_address in account.spot_open_orders])
], ],
program_id=context.program_id, program_id=context.mango_program_address,
data=layouts.WITHDRAW.build({ data=layouts.WITHDRAW.build({
"quantity": value, "quantity": value,
"allow_borrow": allow_borrow "allow_borrow": allow_borrow
@ -557,7 +557,7 @@ def build_withdraw_instructions(context: Context, wallet: Wallet, group: Group,
def build_spot_openorders_instructions(context: Context, wallet: Wallet, group: Group, account: Account, market: PySerumMarket) -> CombinableInstructions: def build_spot_openorders_instructions(context: Context, wallet: Wallet, group: Group, account: Account, market: PySerumMarket) -> CombinableInstructions:
instructions: CombinableInstructions = CombinableInstructions.empty() instructions: CombinableInstructions = CombinableInstructions.empty()
create_open_orders = build_create_solana_account_instructions( create_open_orders = build_create_solana_account_instructions(
context, wallet, context.dex_program_id, layouts.OPEN_ORDERS.sizeof()) context, wallet, context.serum_program_address, layouts.OPEN_ORDERS.sizeof())
instructions += create_open_orders instructions += create_open_orders
open_orders_address = create_open_orders.signers[0].public_key() open_orders_address = create_open_orders.signers[0].public_key()
@ -567,13 +567,13 @@ def build_spot_openorders_instructions(context: Context, wallet: Wallet, group:
AccountMeta(is_signer=False, is_writable=False, pubkey=group.address), AccountMeta(is_signer=False, is_writable=False, pubkey=group.address),
AccountMeta(is_signer=False, is_writable=True, pubkey=account.address), AccountMeta(is_signer=False, is_writable=True, pubkey=account.address),
AccountMeta(is_signer=True, is_writable=False, pubkey=wallet.address), AccountMeta(is_signer=True, is_writable=False, pubkey=wallet.address),
AccountMeta(is_signer=False, is_writable=False, pubkey=context.dex_program_id), AccountMeta(is_signer=False, is_writable=False, pubkey=context.serum_program_address),
AccountMeta(is_signer=False, is_writable=True, pubkey=open_orders_address), AccountMeta(is_signer=False, is_writable=True, pubkey=open_orders_address),
AccountMeta(is_signer=False, is_writable=False, pubkey=market.state.public_key()), AccountMeta(is_signer=False, is_writable=False, pubkey=market.state.public_key()),
AccountMeta(is_signer=False, is_writable=False, pubkey=group.signer_key), AccountMeta(is_signer=False, is_writable=False, pubkey=group.signer_key),
AccountMeta(is_signer=False, is_writable=False, pubkey=SYSVAR_RENT_PUBKEY) AccountMeta(is_signer=False, is_writable=False, pubkey=SYSVAR_RENT_PUBKEY)
], ],
program_id=context.program_id, program_id=context.mango_program_address,
data=layouts.INIT_SPOT_OPEN_ORDERS.build(dict()) data=layouts.INIT_SPOT_OPEN_ORDERS.build(dict())
) )
instructions += CombinableInstructions(signers=[], instructions=[initialise_open_orders_instruction]) instructions += CombinableInstructions(signers=[], instructions=[initialise_open_orders_instruction])
@ -666,7 +666,7 @@ def build_spot_place_order_instructions(context: Context, wallet: Wallet, group:
AccountMeta(is_signer=False, is_writable=True, pubkey=account.address), AccountMeta(is_signer=False, is_writable=True, pubkey=account.address),
AccountMeta(is_signer=True, is_writable=False, pubkey=wallet.address), AccountMeta(is_signer=True, is_writable=False, pubkey=wallet.address),
AccountMeta(is_signer=False, is_writable=False, pubkey=group.cache), AccountMeta(is_signer=False, is_writable=False, pubkey=group.cache),
AccountMeta(is_signer=False, is_writable=False, pubkey=context.dex_program_id), AccountMeta(is_signer=False, is_writable=False, pubkey=context.serum_program_address),
AccountMeta(is_signer=False, is_writable=True, pubkey=market.state.public_key()), AccountMeta(is_signer=False, is_writable=True, pubkey=market.state.public_key()),
AccountMeta(is_signer=False, is_writable=True, pubkey=market.state.bids()), AccountMeta(is_signer=False, is_writable=True, pubkey=market.state.bids()),
AccountMeta(is_signer=False, is_writable=True, pubkey=market.state.asks()), AccountMeta(is_signer=False, is_writable=True, pubkey=market.state.asks()),
@ -690,7 +690,7 @@ def build_spot_place_order_instructions(context: Context, wallet: Wallet, group:
pubkey=oo_address or SYSTEM_PROGRAM_ADDRESS) for oo_address in account.spot_open_orders]), pubkey=oo_address or SYSTEM_PROGRAM_ADDRESS) for oo_address in account.spot_open_orders]),
*fee_discount_address_meta *fee_discount_address_meta
], ],
program_id=context.program_id, program_id=context.mango_program_address,
data=layouts.PLACE_SPOT_ORDER.build( data=layouts.PLACE_SPOT_ORDER.build(
dict( dict(
side=serum_side, side=serum_side,
@ -736,7 +736,7 @@ def build_cancel_spot_order_instructions(context: Context, wallet: Wallet, group
AccountMeta(is_signer=False, is_writable=False, pubkey=group.address), AccountMeta(is_signer=False, is_writable=False, pubkey=group.address),
AccountMeta(is_signer=True, is_writable=False, pubkey=wallet.address), AccountMeta(is_signer=True, is_writable=False, pubkey=wallet.address),
AccountMeta(is_signer=False, is_writable=False, pubkey=account.address), AccountMeta(is_signer=False, is_writable=False, pubkey=account.address),
AccountMeta(is_signer=False, is_writable=False, pubkey=context.dex_program_id), AccountMeta(is_signer=False, is_writable=False, pubkey=context.serum_program_address),
AccountMeta(is_signer=False, is_writable=True, pubkey=market.state.public_key()), AccountMeta(is_signer=False, is_writable=True, pubkey=market.state.public_key()),
AccountMeta(is_signer=False, is_writable=True, pubkey=market.state.bids()), AccountMeta(is_signer=False, is_writable=True, pubkey=market.state.bids()),
AccountMeta(is_signer=False, is_writable=True, pubkey=market.state.asks()), AccountMeta(is_signer=False, is_writable=True, pubkey=market.state.asks()),
@ -744,7 +744,7 @@ def build_cancel_spot_order_instructions(context: Context, wallet: Wallet, group
AccountMeta(is_signer=False, is_writable=False, pubkey=group.signer_key), AccountMeta(is_signer=False, is_writable=False, pubkey=group.signer_key),
AccountMeta(is_signer=False, is_writable=True, pubkey=market.state.event_queue()) AccountMeta(is_signer=False, is_writable=True, pubkey=market.state.event_queue())
], ],
program_id=context.program_id, program_id=context.mango_program_address,
data=layouts.CANCEL_SPOT_ORDER.build( data=layouts.CANCEL_SPOT_ORDER.build(
{ {
"order_id": order.id, "order_id": order.id,
@ -815,7 +815,7 @@ def build_redeem_accrued_mango_instructions(context: Context, wallet: Wallet, pe
AccountMeta(is_signer=False, is_writable=False, pubkey=group.signer_key), AccountMeta(is_signer=False, is_writable=False, pubkey=group.signer_key),
AccountMeta(is_signer=False, is_writable=False, pubkey=TOKEN_PROGRAM_ID) AccountMeta(is_signer=False, is_writable=False, pubkey=TOKEN_PROGRAM_ID)
], ],
program_id=context.program_id, program_id=context.mango_program_address,
data=layouts.REDEEM_MNGO.build(dict()) data=layouts.REDEEM_MNGO.build(dict())
) )
return CombinableInstructions(signers=[], instructions=[redeem_accrued_mango_instruction]) return CombinableInstructions(signers=[], instructions=[redeem_accrued_mango_instruction])

View File

@ -450,7 +450,7 @@ PERP_MARKET_INFO = construct.Struct(
# pub signer_nonce: u64, # pub signer_nonce: u64,
# pub signer_key: Pubkey, # pub signer_key: Pubkey,
# pub admin: Pubkey, // Used to add new markets and adjust risk params # pub admin: Pubkey, // Used to add new markets and adjust risk params
# pub dex_program_id: Pubkey, // Consider allowing more # pub serum_program_address: Pubkey, // Consider allowing more
# pub mango_cache: Pubkey, # pub mango_cache: Pubkey,
# pub valid_interval: u64, # pub valid_interval: u64,
# #
@ -473,7 +473,7 @@ GROUP = construct.Struct(
"signer_nonce" / DecimalAdapter(), "signer_nonce" / DecimalAdapter(),
"signer_key" / PublicKeyAdapter(), "signer_key" / PublicKeyAdapter(),
"admin" / PublicKeyAdapter(), "admin" / PublicKeyAdapter(),
"dex_program_id" / PublicKeyAdapter(), "serum_program_address" / PublicKeyAdapter(),
"cache" / PublicKeyAdapter(), "cache" / PublicKeyAdapter(),
"valid_interval" / DecimalAdapter(), "valid_interval" / DecimalAdapter(),
"dao_vault" / PublicKeyAdapter(), "dao_vault" / PublicKeyAdapter(),

View File

@ -39,11 +39,10 @@ class InventorySource(enum.Enum):
# #
# This class describes a crypto market. It *must* have an address, a base token and a quote token. # This class describes a crypto market. It *must* have an address, a base token and a quote token.
# #
class Market(metaclass=abc.ABCMeta): class Market(metaclass=abc.ABCMeta):
def __init__(self, program_id: PublicKey, address: PublicKey, inventory_source: InventorySource, base: Token, quote: Token, lot_size_converter: LotSizeConverter): def __init__(self, program_address: PublicKey, address: PublicKey, inventory_source: InventorySource, base: Token, quote: Token, lot_size_converter: LotSizeConverter):
self.logger: logging.Logger = logging.getLogger(self.__class__.__name__) self.logger: logging.Logger = logging.getLogger(self.__class__.__name__)
self.program_id: PublicKey = program_id self.program_address: PublicKey = program_address
self.address: PublicKey = address self.address: PublicKey = address
self.inventory_source: InventorySource = inventory_source self.inventory_source: InventorySource = inventory_source
self.base: Token = base self.base: Token = base

View File

@ -76,7 +76,7 @@ def _polling_serum_model_state_builder_factory(context: mango.Context, wallet: m
raise Exception( raise Exception(
f"Could not find token account owned by {wallet.address} for quote token {market.quote}.") f"Could not find token account owned by {wallet.address} for quote token {market.quote}.")
all_open_orders = mango.OpenOrders.load_for_market_and_owner( all_open_orders = mango.OpenOrders.load_for_market_and_owner(
context, market.address, wallet.address, context.dex_program_id, market.base.decimals, market.quote.decimals) context, market.address, wallet.address, context.serum_program_address, market.base.decimals, market.quote.decimals)
if len(all_open_orders) == 0: if len(all_open_orders) == 0:
raise Exception( raise Exception(
f"Could not find serum openorders account owned by {wallet.address} for market {market.symbol}.") f"Could not find serum openorders account owned by {wallet.address} for market {market.symbol}.")

View File

@ -36,14 +36,14 @@ from .version import Version
class OpenOrders(AddressableAccount): class OpenOrders(AddressableAccount):
def __init__(self, account_info: AccountInfo, version: Version, program_id: PublicKey, def __init__(self, account_info: AccountInfo, version: Version, program_address: PublicKey,
account_flags: AccountFlags, market: PublicKey, owner: PublicKey, account_flags: AccountFlags, market: PublicKey, owner: PublicKey,
base_token_free: Decimal, base_token_total: Decimal, quote_token_free: Decimal, base_token_free: Decimal, base_token_total: Decimal, quote_token_free: Decimal,
quote_token_total: Decimal, placed_orders: typing.Sequence[PlacedOrder], quote_token_total: Decimal, placed_orders: typing.Sequence[PlacedOrder],
referrer_rebate_accrued: Decimal): referrer_rebate_accrued: Decimal):
super().__init__(account_info) super().__init__(account_info)
self.version: Version = version self.version: Version = version
self.program_id: PublicKey = program_id self.program_address: PublicKey = program_address
self.account_flags: AccountFlags = account_flags self.account_flags: AccountFlags = account_flags
self.market: PublicKey = market self.market: PublicKey = market
self.owner: PublicKey = owner self.owner: PublicKey = owner
@ -62,7 +62,7 @@ class OpenOrders(AddressableAccount):
def from_layout(layout: layouts.OPEN_ORDERS, account_info: AccountInfo, def from_layout(layout: layouts.OPEN_ORDERS, account_info: AccountInfo,
base_decimals: Decimal, quote_decimals: Decimal) -> "OpenOrders": base_decimals: Decimal, quote_decimals: Decimal) -> "OpenOrders":
account_flags = AccountFlags.from_layout(layout.account_flags) account_flags = AccountFlags.from_layout(layout.account_flags)
program_id = account_info.owner program_address = account_info.owner
base_divisor = 10 ** base_decimals base_divisor = 10 ** base_decimals
quote_divisor = 10 ** quote_decimals quote_divisor = 10 ** quote_decimals
@ -75,7 +75,7 @@ class OpenOrders(AddressableAccount):
if account_flags.initialized: if account_flags.initialized:
placed_orders = PlacedOrder.build_from_open_orders_data( placed_orders = PlacedOrder.build_from_open_orders_data(
layout.free_slot_bits, layout.is_bid_bits, layout.orders, layout.client_ids) layout.free_slot_bits, layout.is_bid_bits, layout.orders, layout.client_ids)
return OpenOrders(account_info, Version.UNSPECIFIED, program_id, account_flags, layout.market, return OpenOrders(account_info, Version.UNSPECIFIED, program_address, account_flags, layout.market,
layout.owner, base_token_free, base_token_total, quote_token_free, layout.owner, base_token_free, base_token_total, quote_token_free,
quote_token_total, placed_orders, layout.referrer_rebate_accrued) quote_token_total, placed_orders, layout.referrer_rebate_accrued)
@ -98,7 +98,7 @@ class OpenOrders(AddressableAccount):
] ]
results = context.client.get_program_accounts( results = context.client.get_program_accounts(
group.dex_program_id, data_size=layouts.OPEN_ORDERS.sizeof(), memcmp_opts=filters) group.serum_program_address, data_size=layouts.OPEN_ORDERS.sizeof(), memcmp_opts=filters)
account_infos = list(map(lambda pair: AccountInfo._from_response_values(pair[0], pair[1]), [ account_infos = list(map(lambda pair: AccountInfo._from_response_values(pair[0], pair[1]), [
(result["account"], PublicKey(result["pubkey"])) for result in results])) (result["account"], PublicKey(result["pubkey"])) for result in results]))
account_infos_by_address = {key: value for key, value in [ account_infos_by_address = {key: value for key, value in [
@ -113,7 +113,7 @@ class OpenOrders(AddressableAccount):
return OpenOrders.parse(open_orders_account, base_decimals, quote_decimals) return OpenOrders.parse(open_orders_account, base_decimals, quote_decimals)
@staticmethod @staticmethod
def load_for_market_and_owner(context: Context, market: PublicKey, owner: PublicKey, program_id: PublicKey, base_decimals: Decimal, quote_decimals: Decimal): def load_for_market_and_owner(context: Context, market: PublicKey, owner: PublicKey, program_address: PublicKey, base_decimals: Decimal, quote_decimals: Decimal):
filters = [ filters = [
MemcmpOpts( MemcmpOpts(
offset=layouts.ACCOUNT_FLAGS.sizeof() + 5, offset=layouts.ACCOUNT_FLAGS.sizeof() + 5,
@ -126,7 +126,7 @@ class OpenOrders(AddressableAccount):
] ]
results = context.client.get_program_accounts( results = context.client.get_program_accounts(
program_id, data_size=layouts.OPEN_ORDERS.sizeof(), memcmp_opts=filters) program_address, data_size=layouts.OPEN_ORDERS.sizeof(), memcmp_opts=filters)
accounts = map(lambda result: AccountInfo._from_response_values( accounts = map(lambda result: AccountInfo._from_response_values(
result["account"], PublicKey(result["pubkey"])), results) result["account"], PublicKey(result["pubkey"])), results)
return list(map(lambda acc: OpenOrders.parse(acc, base_decimals, quote_decimals), accounts)) return list(map(lambda acc: OpenOrders.parse(acc, base_decimals, quote_decimals), accounts))
@ -136,7 +136,7 @@ class OpenOrders(AddressableAccount):
return f"""« OpenOrders [{self.address}]: return f"""« OpenOrders [{self.address}]:
Flags: {self.account_flags} Flags: {self.account_flags}
Program ID: {self.program_id} Program ID: {self.program_address}
Market: {self.market} Market: {self.market}
Owner: {self.owner} Owner: {self.owner}
Base Token: {self.base_token_free:,.8f} of {self.base_token_total:,.8f} Base Token: {self.base_token_free:,.8f} of {self.base_token_total:,.8f}

View File

@ -110,7 +110,7 @@ class PythOracle(Oracle):
class PythOracleProvider(OracleProvider): class PythOracleProvider(OracleProvider):
def __init__(self, context: Context) -> None: def __init__(self, context: Context) -> None:
self.address: PublicKey = PYTH_MAINNET_MAPPING_ROOT if context.client.cluster == "mainnet" else PYTH_DEVNET_MAPPING_ROOT self.address: PublicKey = PYTH_MAINNET_MAPPING_ROOT if context.client.cluster_name == "mainnet" else PYTH_DEVNET_MAPPING_ROOT
super().__init__(f"Pyth Oracle Factory [{self.address}]") super().__init__(f"Pyth Oracle Factory [{self.address}]")
self.context: Context = context self.context: Context = context

View File

@ -74,7 +74,7 @@ class SerumOracle(Oracle):
context.client.skip_preflight, context.client.skip_preflight,
context.client.instruction_reporter) context.client.instruction_reporter)
mainnet_serum_market_lookup: SerumMarketLookup = SerumMarketLookup.load( mainnet_serum_market_lookup: SerumMarketLookup = SerumMarketLookup.load(
context.dex_program_id, SplTokenLookup.DefaultDataFilepath) context.serum_program_address, SplTokenLookup.DefaultDataFilepath)
adjusted_market = self.market adjusted_market = self.market
mainnet_adjusted_market: typing.Optional[Market] = mainnet_serum_market_lookup.find_by_symbol( mainnet_adjusted_market: typing.Optional[Market] = mainnet_serum_market_lookup.find_by_symbol(
self.market.symbol) self.market.symbol)
@ -111,7 +111,7 @@ class SerumOracleProvider(OracleProvider):
def oracle_for_market(self, context: Context, market: Market) -> typing.Optional[Oracle]: def oracle_for_market(self, context: Context, market: Market) -> typing.Optional[Oracle]:
loaded_market: Market = ensure_market_loaded(context, market) loaded_market: Market = ensure_market_loaded(context, market)
if isinstance(loaded_market, SpotMarket): if isinstance(loaded_market, SpotMarket):
serum_market = SerumMarket(context.dex_program_id, loaded_market.address, loaded_market.base, serum_market = SerumMarket(context.serum_program_address, loaded_market.address, loaded_market.base,
loaded_market.quote, loaded_market.underlying_serum_market) loaded_market.quote, loaded_market.underlying_serum_market)
return SerumOracle(serum_market) return SerumOracle(serum_market)
elif isinstance(loaded_market, SerumMarket): elif isinstance(loaded_market, SerumMarket):

View File

@ -34,8 +34,8 @@ from .token import Token
# This class encapsulates our knowledge of a Mango perps market. # This class encapsulates our knowledge of a Mango perps market.
# #
class PerpMarket(Market): class PerpMarket(Market):
def __init__(self, program_id: PublicKey, address: PublicKey, base: Token, quote: Token, underlying_perp_market: PerpMarketDetails): def __init__(self, mango_program_address: PublicKey, address: PublicKey, base: Token, quote: Token, underlying_perp_market: PerpMarketDetails):
super().__init__(program_id, address, InventorySource.ACCOUNT, base, quote, RaisingLotSizeConverter()) super().__init__(mango_program_address, address, InventorySource.ACCOUNT, base, quote, RaisingLotSizeConverter())
self.underlying_perp_market: PerpMarketDetails = underlying_perp_market self.underlying_perp_market: PerpMarketDetails = underlying_perp_market
self.lot_size_converter: LotSizeConverter = LotSizeConverter( self.lot_size_converter: LotSizeConverter = LotSizeConverter(
base, underlying_perp_market.base_lot_size, quote, underlying_perp_market.quote_lot_size) base, underlying_perp_market.base_lot_size, quote, underlying_perp_market.quote_lot_size)
@ -81,7 +81,7 @@ class PerpMarket(Market):
def __str__(self) -> str: def __str__(self) -> str:
underlying: str = f"{self.underlying_perp_market}".replace("\n", "\n ") underlying: str = f"{self.underlying_perp_market}".replace("\n", "\n ")
return f"""« 𝙿𝚎𝚛𝚙𝙼𝚊𝚛𝚔𝚎𝚝 {self.symbol} {self.address} [{self.program_id}] return f"""« 𝙿𝚎𝚛𝚙𝙼𝚊𝚛𝚔𝚎𝚝 {self.symbol} {self.address} [{self.program_address}]
{underlying} {underlying}
»""" »"""
@ -91,18 +91,18 @@ class PerpMarket(Market):
# This class holds information to load a `PerpMarket` object but doesn't automatically load it. # This class holds information to load a `PerpMarket` object but doesn't automatically load it.
# #
class PerpMarketStub(Market): class PerpMarketStub(Market):
def __init__(self, program_id: PublicKey, address: PublicKey, base: Token, quote: Token, group_address: PublicKey): def __init__(self, mango_program_address: PublicKey, address: PublicKey, base: Token, quote: Token, group_address: PublicKey):
super().__init__(program_id, address, InventorySource.ACCOUNT, base, quote, RaisingLotSizeConverter()) super().__init__(mango_program_address, address, InventorySource.ACCOUNT, base, quote, RaisingLotSizeConverter())
self.group_address: PublicKey = group_address self.group_address: PublicKey = group_address
def load(self, context: Context, group: typing.Optional[Group] = None) -> PerpMarket: def load(self, context: Context, group: typing.Optional[Group] = None) -> PerpMarket:
actual_group: Group = group or Group.load(context, self.group_address) actual_group: Group = group or Group.load(context, self.group_address)
underlying_perp_market: PerpMarketDetails = PerpMarketDetails.load(context, self.address, actual_group) underlying_perp_market: PerpMarketDetails = PerpMarketDetails.load(context, self.address, actual_group)
return PerpMarket(self.program_id, self.address, self.base, self.quote, underlying_perp_market) return PerpMarket(self.program_address, self.address, self.base, self.quote, underlying_perp_market)
@property @property
def symbol(self) -> str: def symbol(self) -> str:
return f"{self.base.symbol}-PERP" return f"{self.base.symbol}-PERP"
def __str__(self) -> str: def __str__(self) -> str:
return f"« 𝙿𝚎𝚛𝚙𝙼𝚊𝚛𝚔𝚎𝚝𝚂𝚝𝚞𝚋 {self.symbol} {self.address} [{self.program_id}] »" return f"« 𝙿𝚎𝚛𝚙𝙼𝚊𝚛𝚔𝚎𝚝𝚂𝚝𝚞𝚋 {self.symbol} {self.address} [{self.program_address}] »"

View File

@ -34,8 +34,8 @@ from .token import Token
# This class encapsulates our knowledge of a Serum spot market. # This class encapsulates our knowledge of a Serum spot market.
# #
class SerumMarket(Market): class SerumMarket(Market):
def __init__(self, program_id: PublicKey, address: PublicKey, base: Token, quote: Token, underlying_serum_market: PySerumMarket): def __init__(self, serum_program_address: PublicKey, address: PublicKey, base: Token, quote: Token, underlying_serum_market: PySerumMarket):
super().__init__(program_id, address, InventorySource.SPL_TOKENS, base, quote, RaisingLotSizeConverter()) super().__init__(serum_program_address, address, InventorySource.SPL_TOKENS, base, quote, RaisingLotSizeConverter())
self.underlying_serum_market: PySerumMarket = underlying_serum_market self.underlying_serum_market: PySerumMarket = underlying_serum_market
self.lot_size_converter: LotSizeConverter = LotSizeConverter( self.lot_size_converter: LotSizeConverter = LotSizeConverter(
base, underlying_serum_market.state.base_lot_size, quote, underlying_serum_market.state.quote_lot_size) base, underlying_serum_market.state.base_lot_size, quote, underlying_serum_market.state.quote_lot_size)
@ -54,7 +54,7 @@ class SerumMarket(Market):
return list(map(Order.from_serum_order, itertools.chain(bids_orderbook.orders(), asks_orderbook.orders()))) return list(map(Order.from_serum_order, itertools.chain(bids_orderbook.orders(), asks_orderbook.orders())))
def __str__(self) -> str: def __str__(self) -> str:
return f"""« 𝚂𝚎𝚛𝚞𝚖𝙼𝚊𝚛𝚔𝚎𝚝 {self.symbol} {self.address} [{self.program_id}] return f"""« 𝚂𝚎𝚛𝚞𝚖𝙼𝚊𝚛𝚔𝚎𝚝 {self.symbol} {self.address} [{self.program_address}]
Event Queue: {self.underlying_serum_market.state.event_queue()} Event Queue: {self.underlying_serum_market.state.event_queue()}
Request Queue: {self.underlying_serum_market.state.request_queue()} Request Queue: {self.underlying_serum_market.state.request_queue()}
Bids: {self.underlying_serum_market.state.bids()} Bids: {self.underlying_serum_market.state.bids()}
@ -69,13 +69,13 @@ class SerumMarket(Market):
# This class holds information to load a `SerumMarket` object but doesn't automatically load it. # This class holds information to load a `SerumMarket` object but doesn't automatically load it.
# #
class SerumMarketStub(Market): class SerumMarketStub(Market):
def __init__(self, program_id: PublicKey, address: PublicKey, base: Token, quote: Token): def __init__(self, serum_program_address: PublicKey, address: PublicKey, base: Token, quote: Token):
super().__init__(program_id, address, InventorySource.SPL_TOKENS, base, quote, RaisingLotSizeConverter()) super().__init__(serum_program_address, address, InventorySource.SPL_TOKENS, base, quote, RaisingLotSizeConverter())
def load(self, context: Context) -> SerumMarket: def load(self, context: Context) -> SerumMarket:
underlying_serum_market: PySerumMarket = PySerumMarket.load( underlying_serum_market: PySerumMarket = PySerumMarket.load(
context.client.compatible_client, self.address, context.dex_program_id) context.client.compatible_client, self.address, context.serum_program_address)
return SerumMarket(self.program_id, self.address, self.base, self.quote, underlying_serum_market) return SerumMarket(self.program_address, self.address, self.base, self.quote, underlying_serum_market)
def __str__(self) -> str: def __str__(self) -> str:
return f"« 𝚂𝚎𝚛𝚞𝚖𝙼𝚊𝚛𝚔𝚎𝚝𝚂𝚝𝚞𝚋 {self.symbol} {self.address} [{self.program_id}] »" return f"« 𝚂𝚎𝚛𝚞𝚖𝙼𝚊𝚛𝚔𝚎𝚝𝚂𝚝𝚞𝚋 {self.symbol} {self.address} [{self.program_address}] »"

View File

@ -56,7 +56,7 @@ class SerumMarketInstructionBuilder(MarketInstructionBuilder):
@staticmethod @staticmethod
def load(context: Context, wallet: Wallet, serum_market: SerumMarket) -> "SerumMarketInstructionBuilder": def load(context: Context, wallet: Wallet, serum_market: SerumMarket) -> "SerumMarketInstructionBuilder":
raw_market: PySerumMarket = PySerumMarket.load( raw_market: PySerumMarket = PySerumMarket.load(
context.client.compatible_client, serum_market.address, context.dex_program_id) context.client.compatible_client, serum_market.address, context.serum_program_address)
fee_discount_token_address: typing.Optional[PublicKey] = None fee_discount_token_address: typing.Optional[PublicKey] = None
srm_token = context.token_lookup.find_by_symbol("SRM") srm_token = context.token_lookup.find_by_symbol("SRM")
@ -68,7 +68,7 @@ class SerumMarketInstructionBuilder(MarketInstructionBuilder):
open_orders_address: typing.Optional[PublicKey] = None open_orders_address: typing.Optional[PublicKey] = None
all_open_orders = OpenOrders.load_for_market_and_owner( all_open_orders = OpenOrders.load_for_market_and_owner(
context, serum_market.address, wallet.address, context.dex_program_id, serum_market.base.decimals, serum_market.quote.decimals) context, serum_market.address, wallet.address, context.serum_program_address, serum_market.base.decimals, serum_market.quote.decimals)
if len(all_open_orders) > 0: if len(all_open_orders) > 0:
open_orders_address = all_open_orders[0].address open_orders_address = all_open_orders[0].address

View File

@ -50,16 +50,16 @@ from .token import Token
# there is a name-value pair for the particular market we're interested in. Also, the # there is a name-value pair for the particular market we're interested in. Also, the
# current file only lists USDC and USDT markets, so that's all we can support this way. # current file only lists USDC and USDT markets, so that's all we can support this way.
class SerumMarketLookup(MarketLookup): class SerumMarketLookup(MarketLookup):
def __init__(self, program_id: PublicKey, token_data: typing.Dict) -> None: def __init__(self, serum_program_address: PublicKey, token_data: typing.Dict) -> None:
super().__init__() super().__init__()
self.program_id: PublicKey = program_id self.serum_program_address: PublicKey = serum_program_address
self.token_data: typing.Dict = token_data self.token_data: typing.Dict = token_data
@staticmethod @staticmethod
def load(program_id: PublicKey, token_data_filename: str) -> "SerumMarketLookup": def load(serum_program_address: PublicKey, token_data_filename: str) -> "SerumMarketLookup":
with open(token_data_filename) as json_file: with open(token_data_filename) as json_file:
token_data = json.load(json_file) token_data = json.load(json_file)
return SerumMarketLookup(program_id, token_data) return SerumMarketLookup(serum_program_address, token_data)
@staticmethod @staticmethod
def _find_data_by_symbol(symbol: str, token_data: typing.Dict) -> typing.Optional[typing.Dict]: def _find_data_by_symbol(symbol: str, token_data: typing.Dict) -> typing.Optional[typing.Dict]:
@ -121,7 +121,7 @@ class SerumMarketLookup(MarketLookup):
f"Could not find market with quote token '{quote.symbol}'. Only markets based on USDC or USDT are supported.") f"Could not find market with quote token '{quote.symbol}'. Only markets based on USDC or USDT are supported.")
return None return None
return SerumMarketStub(self.program_id, market_address, base, quote) return SerumMarketStub(self.serum_program_address, market_address, base, quote)
def find_by_address(self, address: PublicKey) -> typing.Optional[Market]: def find_by_address(self, address: PublicKey) -> typing.Optional[Market]:
address_string: str = str(address) address_string: str = str(address)
@ -138,7 +138,7 @@ class SerumMarketLookup(MarketLookup):
raise Exception("Could not load token data for USDC (which should always be present).") raise Exception("Could not load token data for USDC (which should always be present).")
quote = Token(quote_data["symbol"], quote_data["name"], PublicKey( quote = Token(quote_data["symbol"], quote_data["name"], PublicKey(
quote_data["address"]), Decimal(quote_data["decimals"])) quote_data["address"]), Decimal(quote_data["decimals"]))
return SerumMarketStub(self.program_id, market_address, base, quote) return SerumMarketStub(self.serum_program_address, market_address, base, quote)
if "serumV3Usdt" in token_data["extensions"]: if "serumV3Usdt" in token_data["extensions"]:
if token_data["extensions"]["serumV3Usdt"] == address_string: if token_data["extensions"]["serumV3Usdt"] == address_string:
market_address_string = token_data["extensions"]["serumV3Usdt"] market_address_string = token_data["extensions"]["serumV3Usdt"]
@ -150,7 +150,7 @@ class SerumMarketLookup(MarketLookup):
raise Exception("Could not load token data for USDT (which should always be present).") raise Exception("Could not load token data for USDT (which should always be present).")
quote = Token(quote_data["symbol"], quote_data["name"], PublicKey( quote = Token(quote_data["symbol"], quote_data["name"], PublicKey(
quote_data["address"]), Decimal(quote_data["decimals"])) quote_data["address"]), Decimal(quote_data["decimals"]))
return SerumMarketStub(self.program_id, market_address, base, quote) return SerumMarketStub(self.serum_program_address, market_address, base, quote)
return None return None
def all_markets(self) -> typing.Sequence[Market]: def all_markets(self) -> typing.Sequence[Market]:
@ -165,12 +165,12 @@ class SerumMarketLookup(MarketLookup):
market_address = PublicKey(market_address_string) market_address = PublicKey(market_address_string)
base = Token(token_data["symbol"], token_data["name"], PublicKey( base = Token(token_data["symbol"], token_data["name"], PublicKey(
token_data["address"]), Decimal(token_data["decimals"])) token_data["address"]), Decimal(token_data["decimals"]))
all_markets += [SerumMarketStub(self.program_id, market_address, base, usdc)] all_markets += [SerumMarketStub(self.serum_program_address, market_address, base, usdc)]
if "serumV3Usdt" in token_data["extensions"]: if "serumV3Usdt" in token_data["extensions"]:
market_address_string = token_data["extensions"]["serumV3Usdt"] market_address_string = token_data["extensions"]["serumV3Usdt"]
market_address = PublicKey(market_address_string) market_address = PublicKey(market_address_string)
base = Token(token_data["symbol"], token_data["name"], PublicKey( base = Token(token_data["symbol"], token_data["name"], PublicKey(
token_data["address"]), Decimal(token_data["decimals"])) token_data["address"]), Decimal(token_data["decimals"]))
all_markets += [SerumMarketStub(self.program_id, market_address, base, usdt)] all_markets += [SerumMarketStub(self.serum_program_address, market_address, base, usdt)]
return all_markets return all_markets

View File

@ -35,8 +35,8 @@ from .token import Token
# This class encapsulates our knowledge of a Serum spot market. # This class encapsulates our knowledge of a Serum spot market.
# #
class SpotMarket(Market): class SpotMarket(Market):
def __init__(self, program_id: PublicKey, address: PublicKey, base: Token, quote: Token, group: Group, underlying_serum_market: PySerumMarket): def __init__(self, serum_program_address: PublicKey, address: PublicKey, base: Token, quote: Token, group: Group, underlying_serum_market: PySerumMarket):
super().__init__(program_id, address, InventorySource.ACCOUNT, base, quote, RaisingLotSizeConverter()) super().__init__(serum_program_address, address, InventorySource.ACCOUNT, base, quote, RaisingLotSizeConverter())
self.group: Group = group self.group: Group = group
self.underlying_serum_market: PySerumMarket = underlying_serum_market self.underlying_serum_market: PySerumMarket = underlying_serum_market
self.lot_size_converter: LotSizeConverter = LotSizeConverter( self.lot_size_converter: LotSizeConverter = LotSizeConverter(
@ -56,7 +56,7 @@ class SpotMarket(Market):
return list(map(Order.from_serum_order, itertools.chain(bids_orderbook.orders(), asks_orderbook.orders()))) return list(map(Order.from_serum_order, itertools.chain(bids_orderbook.orders(), asks_orderbook.orders())))
def __str__(self) -> str: def __str__(self) -> str:
return f"""« 𝚂𝚙𝚘𝚝𝙼𝚊𝚛𝚔𝚎𝚝 {self.symbol} {self.address} [{self.program_id}] return f"""« 𝚂𝚙𝚘𝚝𝙼𝚊𝚛𝚔𝚎𝚝 {self.symbol} {self.address} [{self.program_address}]
Event Queue: {self.underlying_serum_market.state.event_queue()} Event Queue: {self.underlying_serum_market.state.event_queue()}
Request Queue: {self.underlying_serum_market.state.request_queue()} Request Queue: {self.underlying_serum_market.state.request_queue()}
Bids: {self.underlying_serum_market.state.bids()} Bids: {self.underlying_serum_market.state.bids()}
@ -73,15 +73,15 @@ class SpotMarket(Market):
class SpotMarketStub(Market): class SpotMarketStub(Market):
def __init__(self, program_id: PublicKey, address: PublicKey, base: Token, quote: Token, group_address: PublicKey): def __init__(self, serum_program_address: PublicKey, address: PublicKey, base: Token, quote: Token, group_address: PublicKey):
super().__init__(program_id, address, InventorySource.ACCOUNT, base, quote, RaisingLotSizeConverter()) super().__init__(serum_program_address, address, InventorySource.ACCOUNT, base, quote, RaisingLotSizeConverter())
self.group_address: PublicKey = group_address self.group_address: PublicKey = group_address
def load(self, context: Context, group: typing.Optional[Group]) -> SpotMarket: def load(self, context: Context, group: typing.Optional[Group]) -> SpotMarket:
actual_group: Group = group or Group.load(context, self.group_address) actual_group: Group = group or Group.load(context, self.group_address)
underlying_serum_market: PySerumMarket = PySerumMarket.load( underlying_serum_market: PySerumMarket = PySerumMarket.load(
context.client.compatible_client, self.address, context.dex_program_id) context.client.compatible_client, self.address, context.serum_program_address)
return SpotMarket(self.program_id, self.address, self.base, self.quote, actual_group, underlying_serum_market) return SpotMarket(self.program_address, self.address, self.base, self.quote, actual_group, underlying_serum_market)
def __str__(self) -> str: def __str__(self) -> str:
return f"« 𝚂𝚙𝚘𝚝𝙼𝚊𝚛𝚔𝚎𝚝𝚂𝚝𝚞𝚋 {self.symbol} {self.address} [{self.program_id}] »" return f"« 𝚂𝚙𝚘𝚝𝙼𝚊𝚛𝚔𝚎𝚝𝚂𝚝𝚞𝚋 {self.symbol} {self.address} [{self.program_address}] »"

View File

@ -58,7 +58,7 @@ class SpotMarketInstructionBuilder(MarketInstructionBuilder):
@staticmethod @staticmethod
def load(context: Context, wallet: Wallet, group: Group, account: Account, spot_market: SpotMarket) -> "SpotMarketInstructionBuilder": def load(context: Context, wallet: Wallet, group: Group, account: Account, spot_market: SpotMarket) -> "SpotMarketInstructionBuilder":
raw_market: PySerumMarket = PySerumMarket.load( raw_market: PySerumMarket = PySerumMarket.load(
context.client.compatible_client, spot_market.address, context.dex_program_id) context.client.compatible_client, spot_market.address, context.serum_program_address)
fee_discount_token_address: typing.Optional[PublicKey] = None fee_discount_token_address: typing.Optional[PublicKey] = None
srm_token = context.token_lookup.find_by_symbol("SRM") srm_token = context.token_lookup.find_by_symbol("SRM")

View File

@ -220,7 +220,7 @@ def fetch_all_recent_transaction_signatures(context: Context) -> typing.Sequence
before = None before = None
signature_results: typing.List[str] = [] signature_results: typing.List[str] = []
while not all_fetched: while not all_fetched:
signatures = context.client.get_confirmed_signatures_for_address2(context.group_id, before=before) signatures = context.client.get_confirmed_signatures_for_address2(context.group_address, before=before)
signature_results += signatures signature_results += signatures
if (len(signatures) == 0): if (len(signatures) == 0):
all_fetched = True all_fetched = True
@ -232,7 +232,7 @@ def fetch_all_recent_transaction_signatures(context: Context) -> typing.Sequence
def mango_instruction_from_response(context: Context, all_accounts: typing.Sequence[PublicKey], instruction_data: typing.Dict) -> typing.Optional["MangoInstruction"]: def mango_instruction_from_response(context: Context, all_accounts: typing.Sequence[PublicKey], instruction_data: typing.Dict) -> typing.Optional["MangoInstruction"]:
program_account_index = instruction_data["programIdIndex"] program_account_index = instruction_data["programIdIndex"]
if all_accounts[program_account_index] != context.program_id: if all_accounts[program_account_index] != context.mango_program_address:
# It's an instruction, it's just not a Mango one. # It's an instruction, it's just not a Mango one.
return None return None

View File

@ -92,7 +92,7 @@ def build_spot_open_orders_watcher(context: Context, manager: WebSocketSubscript
def build_serum_open_orders_watcher(context: Context, manager: WebSocketSubscriptionManager, health_check: HealthCheck, serum_market: SerumMarket, wallet: Wallet) -> Watcher[PlacedOrdersContainer]: def build_serum_open_orders_watcher(context: Context, manager: WebSocketSubscriptionManager, health_check: HealthCheck, serum_market: SerumMarket, wallet: Wallet) -> Watcher[PlacedOrdersContainer]:
all_open_orders = OpenOrders.load_for_market_and_owner( all_open_orders = OpenOrders.load_for_market_and_owner(
context, serum_market.address, wallet.address, context.dex_program_id, serum_market.base.decimals, serum_market.quote.decimals) context, serum_market.address, wallet.address, context.serum_program_address, serum_market.base.decimals, serum_market.quote.decimals)
if len(all_open_orders) > 0: if len(all_open_orders) > 0:
initial_serum_open_orders: OpenOrders = all_open_orders[0] initial_serum_open_orders: OpenOrders = all_open_orders[0]
open_orders_address = initial_serum_open_orders.address open_orders_address = initial_serum_open_orders.address

View File

@ -10,7 +10,7 @@ do
cancel-my-orders --name "Random Taker ${MARKET} (cancel)" --market $MARKET --log-level ERROR cancel-my-orders --name "Random Taker ${MARKET} (cancel)" --market $MARKET --log-level ERROR
RANDOM_POSITION_SIZE=$(echo "scale=4; ($(echo "$RANDOM % 1000" | bc) / 1000) * $POSITION_SIZE_CEILING" | bc) RANDOM_POSITION_SIZE=$(echo "scale=4; ($(echo "$RANDOM % 1000" | bc) / 1000) * $POSITION_SIZE_CEILING" | bc)
CURRENT_PRICE=$(fetch-price --provider serum --symbol $ORACLE_MARKET --log-level ERROR --cluster mainnet | cut -d"'" -f 2 | sed 's/,//') CURRENT_PRICE=$(fetch-price --provider serum --symbol $ORACLE_MARKET --log-level ERROR --cluster-name mainnet | cut -d"'" -f 2 | sed 's/,//')
place-order --name "Random Taker ${MARKET} (place buy)" --market $MARKET --order-type IOC --log-level ERROR \ place-order --name "Random Taker ${MARKET} (place buy)" --market $MARKET --order-type IOC --log-level ERROR \
--side BUY --quantity $RANDOM_POSITION_SIZE --price $(echo "$CURRENT_PRICE + $IMMEDIATE_BUY_ADJUSTMENT" | bc) --side BUY --quantity $RANDOM_POSITION_SIZE --price $(echo "$CURRENT_PRICE + $IMMEDIATE_BUY_ADJUSTMENT" | bc)
@ -19,7 +19,7 @@ do
sleep ${PAUSE_FOR} sleep ${PAUSE_FOR}
RANDOM_POSITION_SIZE=$(echo "scale=4; ($(echo "$RANDOM % 1000" | bc) / 1000) * $POSITION_SIZE_CEILING" | bc) RANDOM_POSITION_SIZE=$(echo "scale=4; ($(echo "$RANDOM % 1000" | bc) / 1000) * $POSITION_SIZE_CEILING" | bc)
CURRENT_PRICE=$(fetch-price --provider serum --symbol $ORACLE_MARKET --log-level ERROR --cluster mainnet | cut -d"'" -f 2 | sed 's/,//') CURRENT_PRICE=$(fetch-price --provider serum --symbol $ORACLE_MARKET --log-level ERROR --cluster-name mainnet | cut -d"'" -f 2 | sed 's/,//')
place-order --name "Random Taker ${MARKET} (place sell)" --market $MARKET --order-type IOC --log-level ERROR \ place-order --name "Random Taker ${MARKET} (place sell)" --market $MARKET --order-type IOC --log-level ERROR \
--side SELL --quantity $RANDOM_POSITION_SIZE --price $(echo "$CURRENT_PRICE - $IMMEDIATE_BUY_ADJUSTMENT" | bc) --side SELL --quantity $RANDOM_POSITION_SIZE --price $(echo "$CURRENT_PRICE - $IMMEDIATE_BUY_ADJUSTMENT" | bc)

View File

@ -10,7 +10,7 @@ while :
do do
cancel-my-orders --name "WSMM ${MARKET} (cancel)" --market $MARKET --log-level ERROR cancel-my-orders --name "WSMM ${MARKET} (cancel)" --market $MARKET --log-level ERROR
CURRENT_PRICE=$(fetch-price --provider serum --symbol $ORACLE_MARKET --log-level ERROR --cluster mainnet | cut -d"'" -f 2 | sed 's/,//') CURRENT_PRICE=$(fetch-price --provider serum --symbol $ORACLE_MARKET --log-level ERROR --cluster-name mainnet | cut -d"'" -f 2 | sed 's/,//')
place-order --name "WSMM ${MARKET} (buy)" --market $MARKET --order-type LIMIT \ place-order --name "WSMM ${MARKET} (buy)" --market $MARKET --order-type LIMIT \
--log-level ERROR --side BUY --quantity $FIXED_POSITION_SIZE --price $(echo "$CURRENT_PRICE - $FIXED_SPREAD" | bc) --log-level ERROR --side BUY --quantity $FIXED_POSITION_SIZE --price $(echo "$CURRENT_PRICE - $FIXED_SPREAD" | bc)
place-order --name "WSMM ${MARKET} (sell)" --market $MARKET --order-type LIMIT \ place-order --name "WSMM ${MARKET} (sell)" --market $MARKET --order-type LIMIT \

View File

@ -53,13 +53,13 @@ def fake_token_info() -> mango.TokenInfo:
def fake_context() -> mango.Context: def fake_context() -> mango.Context:
context = mango.Context(name="Mango Test", context = mango.Context(name="Mango Test",
cluster="test", cluster_name="test",
cluster_url="http://localhost", cluster_url="http://localhost",
skip_preflight=False, skip_preflight=False,
program_id=fake_seeded_public_key("program ID"), mango_program_address=fake_seeded_public_key("Mango program address"),
dex_program_id=fake_seeded_public_key("DEX program ID"), serum_program_address=fake_seeded_public_key("Serum program address"),
group_name="TEST_GROUP", group_name="TEST_GROUP",
group_id=fake_seeded_public_key("group ID"), group_address=fake_seeded_public_key("group ID"),
token_lookup=mango.NullTokenLookup(), token_lookup=mango.NullTokenLookup(),
market_lookup=mango.NullMarketLookup()) market_lookup=mango.NullMarketLookup())
context.client = MockClient() context.client = MockClient()

View File

@ -37,7 +37,7 @@ def test_group_layout():
assert group.signer_nonce == 0 assert group.signer_nonce == 0
assert group.signer_key == PublicKey("9GXvznfEep9yEsvH4CQzqNy5GH81FNk1HDeAR8UjefSf") assert group.signer_key == PublicKey("9GXvznfEep9yEsvH4CQzqNy5GH81FNk1HDeAR8UjefSf")
assert group.admin == PublicKey("Cwg1f6m4m3DGwMEbmsbAfDtUToUf5jRdKrJSGD7GfZCB") assert group.admin == PublicKey("Cwg1f6m4m3DGwMEbmsbAfDtUToUf5jRdKrJSGD7GfZCB")
assert group.dex_program_id == PublicKey("DESVgJVGajEgKGXhb6XmqDHGz3VjdgP7rEVESBgxmroY") assert group.serum_program_address == PublicKey("DESVgJVGajEgKGXhb6XmqDHGz3VjdgP7rEVESBgxmroY")
assert group.cache == PublicKey("PJhM2enPpZH7E9wgw7Sqt8S2p4mr3Bc7SycawQwfY7b") assert group.cache == PublicKey("PJhM2enPpZH7E9wgw7Sqt8S2p4mr3Bc7SycawQwfY7b")
assert group.valid_interval == 5 assert group.valid_interval == 5
assert group.dao_vault == PublicKey("14gfuPWjUQnYXpsxs4WgsjafUrJctKkR9AMFH7fjvTgR") assert group.dao_vault == PublicKey("14gfuPWjUQnYXpsxs4WgsjafUrJctKkR9AMFH7fjvTgR")

View File

@ -31,13 +31,13 @@ def mock_group():
signer_nonce = Decimal(1) signer_nonce = Decimal(1)
signer_key = fake_seeded_public_key("signer key") signer_key = fake_seeded_public_key("signer key")
admin_key = fake_seeded_public_key("admin key") admin_key = fake_seeded_public_key("admin key")
dex_program_id = fake_seeded_public_key("DEX program ID") serum_program_address = fake_seeded_public_key("DEX program ID")
cache_key = fake_seeded_public_key("cache key") cache_key = fake_seeded_public_key("cache key")
valid_interval = Decimal(7) valid_interval = Decimal(7)
return mango.Group(account_info, mango.Version.V1, name, meta_data, token_infos, return mango.Group(account_info, mango.Version.V1, name, meta_data, token_infos,
spot_markets, perp_markets, oracles, signer_nonce, signer_key, spot_markets, perp_markets, oracles, signer_nonce, signer_key,
admin_key, dex_program_id, cache_key, valid_interval) admin_key, serum_program_address, cache_key, valid_interval)
def mock_prices(prices: typing.Sequence[str]): def mock_prices(prices: typing.Sequence[str]):
@ -53,11 +53,11 @@ def mock_prices(prices: typing.Sequence[str]):
def mock_open_orders(base_token_free: Decimal = Decimal(0), base_token_total: Decimal = Decimal(0), quote_token_free: Decimal = Decimal(0), quote_token_total: Decimal = Decimal(0), referrer_rebate_accrued: Decimal = Decimal(0)): def mock_open_orders(base_token_free: Decimal = Decimal(0), base_token_total: Decimal = Decimal(0), quote_token_free: Decimal = Decimal(0), quote_token_total: Decimal = Decimal(0), referrer_rebate_accrued: Decimal = Decimal(0)):
account_info = fake_account_info() account_info = fake_account_info()
program_id = fake_seeded_public_key("program ID") program_address = fake_seeded_public_key("program address")
market = fake_seeded_public_key("market") market = fake_seeded_public_key("market")
owner = fake_seeded_public_key("owner") owner = fake_seeded_public_key("owner")
flags = mango.AccountFlags(mango.Version.V1, True, False, True, False, False, False, False, False) flags = mango.AccountFlags(mango.Version.V1, True, False, True, False, False, False, False, False)
return mango.OpenOrders(account_info, mango.Version.V1, program_id, flags, market, return mango.OpenOrders(account_info, mango.Version.V1, program_address, flags, market,
owner, base_token_free, base_token_total, quote_token_free, owner, base_token_free, base_token_total, quote_token_free,
quote_token_total, [], referrer_rebate_accrued) quote_token_total, [], referrer_rebate_accrued)

View File

@ -4,10 +4,10 @@ from solana.publickey import PublicKey
def context_has_default_values(ctx: mango.Context): def context_has_default_values(ctx: mango.Context):
assert ctx.program_id == PublicKey("mv3ekLzLbnVPNxjSKvqBpU3ZeZXPQdEC3bp5MDEBG68") assert ctx.mango_program_address == PublicKey("mv3ekLzLbnVPNxjSKvqBpU3ZeZXPQdEC3bp5MDEBG68")
assert ctx.dex_program_id == PublicKey("9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin") assert ctx.serum_program_address == PublicKey("9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin")
assert ctx.group_name == "mainnet.1" assert ctx.group_name == "mainnet.1"
assert ctx.group_id == PublicKey("98pjRuQjK3qA6gXts96PqZT4Ze5QmnCmt3QYjhbUSPue") assert ctx.group_address == PublicKey("98pjRuQjK3qA6gXts96PqZT4Ze5QmnCmt3QYjhbUSPue")
def test_context_default_exists(): def test_context_default_exists():
@ -22,21 +22,21 @@ def test_context_default_values():
# def test_new_from_cluster(): # def test_new_from_cluster():
# context_has_default_values(mango.ContextBuilder.default()) # context_has_default_values(mango.ContextBuilder.default())
# derived = mango.ContextBuilder.default().new_from_cluster("mainnet") # derived = mango.ContextBuilder.default().new_from_cluster("mainnet")
# assert derived.cluster == "mainnet" # assert derived.cluster_name == "mainnet"
# assert derived.cluster_url == "https://solana-api.projectserum.com" # assert derived.cluster_url == "https://solana-api.projectserum.com"
# assert derived.program_id == PublicKey("mv3ekLzLbnVPNxjSKvqBpU3ZeZXPQdEC3bp5MDEBG68") # assert derived.mango_program_address == PublicKey("mv3ekLzLbnVPNxjSKvqBpU3ZeZXPQdEC3bp5MDEBG68")
# assert derived.dex_program_id == PublicKey("9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin") # assert derived.serum_program_address == PublicKey("9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin")
# assert derived.group_name == "mainnet.0" # assert derived.group_name == "mainnet.0"
# assert derived.group_id == PublicKey("98pjRuQjK3qA6gXts96PqZT4Ze5QmnCmt3QYjhbUSPue") # assert derived.group_address == PublicKey("98pjRuQjK3qA6gXts96PqZT4Ze5QmnCmt3QYjhbUSPue")
# context_has_default_values(mango.ContextBuilder.default()) # context_has_default_values(mango.ContextBuilder.default())
def test_new_from_group_name(): def test_new_from_group_name():
context_has_default_values(mango.ContextBuilder.default()) context_has_default_values(mango.ContextBuilder.default())
derived = mango.ContextBuilder.from_group_name(mango.ContextBuilder.default(), "mainnet.0") derived = mango.ContextBuilder.from_group_name(mango.ContextBuilder.default(), "mainnet.0")
assert derived.program_id == PublicKey("mv3ekLzLbnVPNxjSKvqBpU3ZeZXPQdEC3bp5MDEBG68") assert derived.mango_program_address == PublicKey("mv3ekLzLbnVPNxjSKvqBpU3ZeZXPQdEC3bp5MDEBG68")
assert derived.dex_program_id == PublicKey("9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin") assert derived.serum_program_address == PublicKey("9xQeWvG816bUx9EPjHmaT23yvVM2ZWbrrpZb9PusVFin")
# Should update both of these values on new group name. # Should update both of these values on new group name.
assert derived.group_name == "mainnet.0" assert derived.group_name == "mainnet.0"
assert derived.group_id == PublicKey("4yJ2Vx3kZnmHTNCrHzdoj5nCwriF2kVhfKNvqC6gU8tr") assert derived.group_address == PublicKey("4yJ2Vx3kZnmHTNCrHzdoj5nCwriF2kVhfKNvqC6gU8tr")

View File

@ -15,7 +15,7 @@ def test_construction():
signer_nonce = Decimal(1) signer_nonce = Decimal(1)
signer_key = fake_seeded_public_key("signer key") signer_key = fake_seeded_public_key("signer key")
admin_key = fake_seeded_public_key("admin key") admin_key = fake_seeded_public_key("admin key")
dex_program_id = fake_seeded_public_key("DEX program ID") serum_program_address = fake_seeded_public_key("Serum program ID")
cache_key = fake_seeded_public_key("cache key") cache_key = fake_seeded_public_key("cache key")
valid_interval = Decimal(7) valid_interval = Decimal(7)
dao_vault = fake_seeded_public_key("insurance vault") dao_vault = fake_seeded_public_key("insurance vault")
@ -24,7 +24,7 @@ def test_construction():
actual = mango.Group(account_info, mango.Version.V1, name, meta_data, shared_quote_token, actual = mango.Group(account_info, mango.Version.V1, name, meta_data, shared_quote_token,
in_basket, basket_markets, signer_nonce, signer_key, admin_key, in_basket, basket_markets, signer_nonce, signer_key, admin_key,
dex_program_id, cache_key, valid_interval, dao_vault, srm_vault, msrm_vault) serum_program_address, cache_key, valid_interval, dao_vault, srm_vault, msrm_vault)
assert actual is not None assert actual is not None
assert actual.logger is not None assert actual.logger is not None
@ -36,7 +36,7 @@ def test_construction():
assert actual.signer_nonce == signer_nonce assert actual.signer_nonce == signer_nonce
assert actual.signer_key == signer_key assert actual.signer_key == signer_key
assert actual.admin == admin_key assert actual.admin == admin_key
assert actual.dex_program_id == dex_program_id assert actual.serum_program_address == serum_program_address
assert actual.cache == cache_key assert actual.cache == cache_key
assert actual.valid_interval == valid_interval assert actual.valid_interval == valid_interval
assert actual.dao_vault == dao_vault assert actual.dao_vault == dao_vault
@ -55,7 +55,7 @@ def test_construction():
# context = fake_context() # context = fake_context()
# ids_json_token_lookup: mango.TokenLookup = mango.IdsJsonTokenLookup("devnet", mango.default_group_name) # ids_json_token_lookup: mango.TokenLookup = mango.IdsJsonTokenLookup("devnet", mango.default_group_name)
# context.token_lookup = ids_json_token_lookup # context.token_lookup = ids_json_token_lookup
# # context.cluster = "devnet" # Needed for devnet token lookups. # # context.cluster_name = "devnet" # Needed for devnet token lookups.
# group = mango.Group.parse(context, group_account_info) # group = mango.Group.parse(context, group_account_info)
# assert group.address == group_public_key # assert group.address == group_public_key
@ -81,7 +81,7 @@ def test_construction():
# assert group.signer_nonce == 0 # assert group.signer_nonce == 0
# assert group.signer_key == PublicKey("HzpyPmuz8C4P6ExinCDndujSAy68MeMNHNzYLpmMAGMS") # assert group.signer_key == PublicKey("HzpyPmuz8C4P6ExinCDndujSAy68MeMNHNzYLpmMAGMS")
# assert group.admin == PublicKey("Cwg1f6m4m3DGwMEbmsbAfDtUToUf5jRdKrJSGD7GfZCB") # assert group.admin == PublicKey("Cwg1f6m4m3DGwMEbmsbAfDtUToUf5jRdKrJSGD7GfZCB")
# assert group.dex_program_id == PublicKey("DESVgJVGajEgKGXhb6XmqDHGz3VjdgP7rEVESBgxmroY") # assert group.serum_program_address == PublicKey("DESVgJVGajEgKGXhb6XmqDHGz3VjdgP7rEVESBgxmroY")
# assert group.cache == PublicKey("D2zSEAJTJADF46eCm1SrsSP3k4QDfv33nneTpbHow3DM") # assert group.cache == PublicKey("D2zSEAJTJADF46eCm1SrsSP3k4QDfv33nneTpbHow3DM")
# assert group.valid_interval == 5 # assert group.valid_interval == 5
# assert group.dao_vault is None # assert group.dao_vault is None

View File

@ -6,12 +6,12 @@ from decimal import Decimal
def test_constructor(): def test_constructor():
account_info = fake_account_info() account_info = fake_account_info()
program_id = fake_public_key() program_address = fake_public_key()
market = fake_public_key() market = fake_public_key()
owner = fake_public_key() owner = fake_public_key()
flags = mango.AccountFlags(mango.Version.V1, True, False, True, False, False, False, False, False) flags = mango.AccountFlags(mango.Version.V1, True, False, True, False, False, False, False, False)
actual = mango.OpenOrders(account_info, mango.Version.V1, program_id, flags, market, actual = mango.OpenOrders(account_info, mango.Version.V1, program_address, flags, market,
owner, Decimal(0), Decimal(0), Decimal(0), Decimal(0), [], Decimal(0)) owner, Decimal(0), Decimal(0), Decimal(0), Decimal(0), [], Decimal(0))
assert actual is not None assert actual is not None
assert actual.logger is not None assert actual.logger is not None

View File

@ -5,16 +5,16 @@ from decimal import Decimal
def test_spot_market_stub_constructor(): def test_spot_market_stub_constructor():
program_id = fake_seeded_public_key("program ID") program_address = fake_seeded_public_key("program address")
address = fake_seeded_public_key("spot market address") address = fake_seeded_public_key("spot market address")
base = mango.Token("BASE", "Base Token", fake_seeded_public_key("base token"), Decimal(7)) base = mango.Token("BASE", "Base Token", fake_seeded_public_key("base token"), Decimal(7))
quote = mango.Token("QUOTE", "Quote Token", fake_seeded_public_key("quote token"), Decimal(9)) quote = mango.Token("QUOTE", "Quote Token", fake_seeded_public_key("quote token"), Decimal(9))
group_address = fake_seeded_public_key("group address") group_address = fake_seeded_public_key("group address")
actual = mango.SpotMarketStub(program_id, address, base, quote, group_address) actual = mango.SpotMarketStub(program_address, address, base, quote, group_address)
assert actual is not None assert actual is not None
assert actual.logger is not None assert actual.logger is not None
assert actual.base == base assert actual.base == base
assert actual.quote == quote assert actual.quote == quote
assert actual.address == address assert actual.address == address
assert actual.program_id == program_id assert actual.program_address == program_address
assert actual.group_address == group_address assert actual.group_address == group_address