Skip to content

Commit

Permalink
Merge pull request #6512 from aarmoa/feat/injective_spot_offline_vaults
Browse files Browse the repository at this point in the history
Feat/injective spot offline vaults
  • Loading branch information
cardosofede committed Aug 10, 2023
2 parents f7b1aea + 621308b commit 4b02c52
Show file tree
Hide file tree
Showing 34 changed files with 4,066 additions and 572 deletions.
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -91,3 +91,4 @@ coverage.xml

# External SDK files
/**/.chain_cookie
/**/.injective_cookie
2 changes: 1 addition & 1 deletion Dockerfile
Expand Up @@ -73,4 +73,4 @@ SHELL [ "/bin/bash", "-lc" ]

# Set the default command to run when starting the container

CMD conda activate hummingbot && ./bin/hummingbot_quickstart.py
CMD conda activate hummingbot && ./bin/hummingbot_quickstart.py 2>./logs/standard_error_output.txt
4 changes: 2 additions & 2 deletions hummingbot/client/command/connect_command.py
Expand Up @@ -133,6 +133,7 @@ async def validate_n_connect_connector(

async def _perform_connect(self, connector_config: ClientConfigAdapter, previous_keys: Optional[Dict] = None):
connector_name = connector_config.connector
original_config = connector_config.full_copy()
await self.prompt_for_model_config(connector_config)
self.app.change_prompt(prompt=">>> ")
if self.app.to_stop_config:
Expand All @@ -145,5 +146,4 @@ async def _perform_connect(self, connector_config: ClientConfigAdapter, previous
else:
self.notify(f"\nError: {err_msg}")
if previous_keys is not None:
previous_config = ClientConfigAdapter(connector_config.hb_config.__class__(**previous_keys))
Security.update_secure_config(previous_config)
Security.update_secure_config(original_config)
30 changes: 25 additions & 5 deletions hummingbot/client/config/config_helpers.py
Expand Up @@ -244,9 +244,9 @@ def generate_yml_output_str_with_comments(self) -> str:
def validate_model(self) -> List[str]:
results = validate_model(type(self._hb_config), json.loads(self._hb_config.json()))
conf_dict = results[0]
self._decrypt_secrets(conf_dict)
for key, value in conf_dict.items():
self.setattr_no_validation(key, value)
self._decrypt_all_internal_secrets()
errors = results[2]
validation_errors = []
if errors is not None:
Expand All @@ -261,6 +261,9 @@ def setattr_no_validation(self, attr: str, value: Any):
with self._disable_validation():
setattr(self, attr, value)

def full_copy(self):
return self.__class__(hb_config=self._hb_config.copy(deep=True))

@contextlib.contextmanager
def _disable_validation(self):
self._hb_config.Config.validate_assignment = False
Expand All @@ -285,20 +288,22 @@ def _is_union(t: Type) -> bool:
return is_union

def _dict_in_conf_order(self) -> Dict[str, Any]:
d = {}
conf_dict = {}
for attr in self._hb_config.__fields__.keys():
value = getattr(self, attr)
if isinstance(value, ClientConfigAdapter):
value = value._dict_in_conf_order()
d[attr] = value
return d
conf_dict[attr] = value
self._encrypt_secrets(conf_dict)
return conf_dict

def _encrypt_secrets(self, conf_dict: Dict[str, Any]):
from hummingbot.client.config.security import Security # avoids circular import
for attr, value in conf_dict.items():
attr_type = self._hb_config.__fields__[attr].type_
if attr_type == SecretStr:
conf_dict[attr] = Security.secrets_manager.encrypt_secret_value(attr, value.get_secret_value())
clear_text_value = value.get_secret_value() if isinstance(value, SecretStr) else value
conf_dict[attr] = Security.secrets_manager.encrypt_secret_value(attr, clear_text_value)

def _decrypt_secrets(self, conf_dict: Dict[str, Any]):
from hummingbot.client.config.security import Security # avoids circular import
Expand All @@ -308,6 +313,21 @@ def _decrypt_secrets(self, conf_dict: Dict[str, Any]):
decrypted_value = Security.secrets_manager.decrypt_secret_value(attr, value.get_secret_value())
conf_dict[attr] = SecretStr(decrypted_value)

def _decrypt_all_internal_secrets(self):
from hummingbot.client.config.security import Security # avoids circular import

for traversal_item in self.traverse():
if traversal_item.type_ == SecretStr:
encrypted_value = traversal_item.value
if isinstance(encrypted_value, SecretStr):
encrypted_value = encrypted_value.get_secret_value()
decrypted_value = Security.secrets_manager.decrypt_secret_value(traversal_item.attr, encrypted_value)
parent_attributes = traversal_item.config_path.split(".")[:-1]
config = self
for parent_attribute in parent_attributes:
config = getattr(config, parent_attribute)
setattr(config, traversal_item.attr, decrypted_value)

def _generate_title(self) -> str:
title = f"{self._hb_config.Config.title}"
title = self._adorn_title(title)
Expand Down
5 changes: 5 additions & 0 deletions hummingbot/client/settings.py
Expand Up @@ -283,6 +283,11 @@ def conn_init_parameters(
params["trading_pairs"] = trading_pairs
params["trading_required"] = trading_required
params["client_config_map"] = client_config_map
if (self.config_keys is not None
and type(self.config_keys) is not dict
and "receive_connector_configuration" in self.config_keys.__fields__
and self.config_keys.receive_connector_configuration):
params["connector_configuration"] = self.config_keys

return params

Expand Down
2 changes: 1 addition & 1 deletion hummingbot/connector/client_order_tracker.py
Expand Up @@ -235,7 +235,7 @@ async def process_order_not_found(self, client_order_id: str):
self._order_not_found_records[client_order_id] += 1
if self._order_not_found_records[client_order_id] > self._lost_order_count_limit:
# Only mark the order as failed if it has not been marked as done already asynchronously
if not tracked_order.is_done:
if tracked_order.current_state not in [OrderState.CANCELED, OrderState.FILLED, OrderState.FAILED]:
self.logger().warning(
f"The order {client_order_id}({tracked_order.exchange_order_id}) will be "
f"considered lost. Please check its status in the exchange."
Expand Down
Empty file.
31 changes: 24 additions & 7 deletions hummingbot/connector/exchange/injective_v2/README.md
Expand Up @@ -2,16 +2,33 @@

This is a spot connector created by **[Injective Labs](https://injectivelabs.org/)**.
The difference with `injective` connector is that v2 is a pure Python connector. That means that the user does not need to configure and run a Gateway instance to use the connector.
Also, `injective_v2` has been implemented to use delegated accounts. That means that the account used to send the transactions to the chain for trading is not the account holding the funds.
The connector supports two different account modes:
- Trading with delegate accounts
- Trading through off-chain vault contracts

### Delegate account mode
When configuring the connector with this mode, the account used to send the transactions to the chain for trading is not the account holding the funds.
The user will need to have one portfolio account and at least one trading account. And permissions should be granted with the portfolio account to the trading account for it to operate using the portfolio account's funds.

### Trading permissions grant
#### Trading permissions grant
To grant permissions from a portfolio account to a trading account to operate using the portfolio account funds please refer to the script `account_delegation_script.py`

### Connector parameters
#### Mode parameters
When configuring a new instance of the connector in Hummingbot the following parameters are required:

- **private_key**: the private key of the trading account (grantee account)
- **subaccount_index**: the index (decimal number) of the subaccount from the trading account that the connector will be operating with
- **granter_address**: the public key (injective format address) of the portfolio account
- **granter_subaccount_index**: the index (decimal number) of the subaccount from the portfolio account (the subaccount holding the funds)


### Off-chain vault mode
When configuring the connector with this mode, all the operations are sent to be executed by a vault contract in the chain.
The user will need to have a vault contract deployed on chain, and use the vault's admin account to configure this mode's parameters.

#### Mode parameters
When configuring a new instance of the connector in Hummingbot the following parameters are required:

- **injective_private_key**: the private key of the trading account (grantee account)
- **injective_subaccount_index**: the index (decimal number) of the subaccount from the trading account that the connector will be operating with
- **injective_granter_address**: the public key (injective format address) of the portfolio account
- **injective_granter_subaccount_index**: the index (decimal number) of the subaccount from the portfolio account (the subaccount holding the funds)
- **private_key**: the vault's admin account private key
- **subaccount_index**: the index (decimal number) of the subaccount from the vault's admin account
- **vault_contract_address**: the address in the chain for the vault contract
Expand Up @@ -34,7 +34,7 @@ async def main() -> None:
granter_public_key = granter_private_key.to_public_key()
granter_address = granter_public_key.to_address()
account = await client.get_account(granter_address.to_acc_bech32()) # noqa: F841
granter_subaccount_id = granter_address.get_subaccount_id(index=0)
granter_subaccount_id = granter_address.get_subaccount_id(index=GRANTER_SUBACCOUNT_INDEX)

msg = composer.MsgGrantTyped(
granter = granter_address.to_acc_bech32(),
Expand Down
Empty file.

0 comments on commit 4b02c52

Please sign in to comment.