GithubHelp home page GithubHelp logo

apeworx / silverback Goto Github PK

View Code? Open in Web Editor NEW
72.0 5.0 6.0 5.45 MB

Blockchain automation library, and SDK for the (upcoming) Silverback platform

Home Page: https://www.apeworx.io/silverback

License: Apache License 2.0

Python 99.03% Dockerfile 0.97%
ape apeworx python3 silverback

silverback's Introduction

Quick Start

Silverback lets you create and deploy your own Python bots that respond to on-chain events. The Silverback library leverages the Ape development framework as well as it's ecosystem of plugins and packages to enable you to develop simple-yet-sophisticated automated applications that can listen and respond to live chain data.

Silverback applications are excellent for use cases that involve continuously monitoring and responding to on-chain events, such as newly confirmed blocks or contract event logs.

Some examples of these types of applications:

  • Monitoring new pool creations, and depositing liquidity
  • Measuring trading activity of popular pools
  • Listening for large swaps to update a telegram group

Documentation

Please read the development userguide for more information on how to develop an application.

Dependencies

  • python3 version 3.10 or greater, python3-dev

Installation

Silverback relies heavily on the Ape development framework, so it's worth it to familarize yourself with how to install Ape and it's plugins using the Ape installation userguide.

It is suggested that you use a virtual environment of your choosing, and then install the Silverback package via one of the following options.

via pip

You can install the latest release via pip:

pip install silverback

via setuptools

You can clone the repository and use setuptools for the most up-to-date version:

git clone https://github.com/ApeWorX/silverback.git silverback
cd silverback
python3 setup.py install

Quick Usage

Checkout the example to see how to use the library.

The example makes use of the [Ape Tokens](https://github.com/ApeWorX/ape-tokens) plugin.
Be sure to properly configure your environment for the USDC and YFI tokens on Ethereum mainnet.

To run your bot against a live network, this SDK includes a simple runner command you can use via:

$ silverback run "example:app" --network :mainnet:alchemy
This runner uses an in-memory task broker by default.
If you want to learn more about what that means, please visit the [development userguide](https://docs.apeworx.io/silverback/stable/userguides/development.html).

Docker Usage

$ docker run --volume $PWD:/home/harambe/project --volume ~/.tokenlists:/home/harambe/.tokenlists apeworx/silverback:latest run "example:app" --network :mainnet
The Docker image we publish uses Python 3.11.

Setting Up Your Environment

Running the Quick Usage and Docker Usage with the provided example will fail if you do not have a fully-configured environment. Most common issues when using the SDK stem from the proper configuration of Ape plugins to unlock the behavior you desire.

You should use a provider that supports websockets to run silverback. If you want to use a hosted provider with websocket support like Alchemy to run this example, you will need a Alchemy API key for Ethereum mainnet. If you attempt to run the Docker Usage command without supplying this key, you will get the following error:

$ docker run --volume $PWD:/home/harambe/project --volume ~/.tokenlists:/home/harambe/.tokenlists apeworx/silverback:latest run "example:app" --network :mainnet:alchemy
Traceback (most recent call last):
  ...
ape_alchemy.exceptions.MissingProjectKeyError: Must set one of $WEB3_ALCHEMY_PROJECT_ID, $WEB3_ALCHEMY_API_KEY, $WEB3_ETHEREUM_MAINNET_ALCHEMY_PROJECT_ID, $WEB3_ETHEREUM_MAINNET_ALCHEMY_API_KEY.

Go to Alchemy, create an account, then create an application in their dashboard, and copy the API Key.

Another requirement for the command from Docker Usage to run the given example is that it uses ape-tokens plugin to look up token interfaces by symbol. In order for this to work, you should have installed and configured that plugin using a token list that includes both YFI and USDC on Ethereum mainnet. Doing this will give you a ~/.tokenlists hidden folder in your home folder that you must mount into the docker container with the following flag:

... --volume ~/.tokenlists:/home/harambe/.tokenlists ...
It is suggested to install the 1inch tokenlist via `ape tokens install tokens.1inch.eth`.
See the [ape-tokens](https://github.com/ApeWorX/ape-tokens?tab=readme-ov-file#quick-usage) README for more information.

To check that both of the tokens exist in your configured tokenlist, you can execute this command:

$ ape tokens token-info YFI
      Symbol: YFI
        Name: yearn.finance
    Chain ID: 1
     Address: 0x0bc529c00C6401aEF6D220BE8C6Ea1667F6Ad93e
    Decimals: 18

$ ape tokens token-info USDC
      Symbol: USDC
        Name: Circle USD
    Chain ID: 1
     Address: 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48
    Decimals: 6
If you want, you can comment out the two functions `exec_event1` and `exec_event2` that handle the contract log events from these contracts if you do not have the configured tokenlist, then your command should work.

Development

This project is under active development in preparation of the release of the Silverback Platform. Things might not be in their final state and breaking changes may occur. Comments, questions, criticisms and pull requests are welcomed.

See Contributing for more information.

silverback's People

Contributors

fubuloubu avatar mikeshultz avatar antazoey avatar sabotagebeats avatar dtdang avatar notpeopling2day avatar johnson2427 avatar

Stargazers

Lidne avatar Frank Mullenger avatar TizzyTee avatar Toko avatar Barabazs avatar Manuel Montenegro avatar Ekrem Seren avatar De Clercq Wentzel avatar Alexey Shekhirin avatar Mihai avatar Renzo avatar jpgonzalezra avatar Romel avatar Eloi Manuel avatar noid avatar Eshaan Bansal avatar Vyom Sharma avatar Lubomir Anastasov avatar steven avatar Lawrence Forman avatar  avatar Tola David avatar Fran Algaba avatar Saikat Karmakar avatar ded comfy avatar  avatar Shawn Koh avatar Andrew avatar Spartan117 avatar Zachary Thielemann avatar agBabbbyCakes (ApeWorX, LTD) avatar  avatar kaleb avatar  avatar  avatar Valerii Lavrushko avatar  avatar  avatar Yixun Chen avatar Ryan Berckmans avatar  avatar  avatar ehart004 avatar Raghav Bansal avatar ryanycw.eth avatar indigo avatar Simple Man avatar  avatar  avatar uArtApe avatar ABC10 avatar Aurelia.eth avatar  avatar  avatar egornomic avatar Adil Kazani avatar disco chuck avatar Viki Val avatar Sandalots avatar 0xYYY avatar sudo rm -rf --no-preserve-root / avatar Not Jeremy Liu avatar Roman Sivakov avatar crzypatchwork avatar chubbykat avatar m0ham3dx avatar Nikolay avatar 0xRizzo avatar sam bacha avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar agBabbbyCakes (ApeWorX, LTD) avatar

silverback's Issues

[SBK-176] bug: KeyError: Symbol 'USDC' is not a known token symbol

Environment information

  • ape and plugin versions:
$ docker run -it --entrypoint /bin/bash --volume $PWD:/home/harambe/project --entrypoint /bin/bash apeworx/silverback:latest 
harambe@ce9cf7333104:~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~/project$ ape --version
0.6.11
harambe@ce9cf7333104:~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~/project$ ape plugins list
Installed Plugins:
  foundry      0.6.9
  alchemy      0.6.2
  infura       0.6.2
  solidity     0.6.6
  template     0.6.1
  hardhat      0.6.8
  ens          0.6.1
  tokens       0.6.1
  vyper        0.6.8
  etherscan    0.6.6
  • Python Version:
$ python --version
Python 3.9.17
  • OS: docker linux apeworx/ape:0.6.11

What went wrong?

$ docker run -it --volume $PWD:/home/harambe/project apeworx/silverback:latest run exa
mple:app
INFO: Loading Silverback App with settings:
  BROKER_CLASS="taskiq:InMemoryBroker"
  NETWORK_CHOICE="ethereum"
INFO: Loaded Silverback App:
  NETWORK="ethereum:local"
  SIGNER=None
Traceback (most recent call last):
  File "/usr/local/lib/python3.9/site-packages/ape_tokens/managers.py", line 121, in __getitem__
    token_info = self._manager.get_token_info(symbol)
  File "/usr/local/lib/python3.9/site-packages/tokenlists/manager.py", line 106, in get_token_info
    tokenlist = self.get_tokenlist(token_listname)
  File "/usr/local/lib/python3.9/site-packages/tokenlists/manager.py", line 82, in get_tokenlist
    raise ValueError("Default token list has not been set.")
ValueError: Default token list has not been set.

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/usr/local/bin/silverback", line 8, in <module>
    sys.exit(cli())
  File "/usr/local/lib/python3.9/site-packages/click/core.py", line 1130, in __call__
    return self.main(*args, **kwargs)
  File "/usr/local/lib/python3.9/site-packages/click/core.py", line 1055, in main
    rv = self.invoke(ctx)
  File "/usr/local/lib/python3.9/site-packages/click/core.py", line 1657, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/usr/local/lib/python3.9/site-packages/ape/cli/commands.py", line 18, in invoke
    super().invoke(ctx)
  File "/usr/local/lib/python3.9/site-packages/click/core.py", line 1404, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/usr/local/lib/python3.9/site-packages/click/core.py", line 760, in invoke
    return __callback(*args, **kwargs)
  File "/usr/local/lib/python3.9/site-packages/silverback/_cli.py", line 28, in run
    app = import_from_string(path)
  File "/usr/local/lib/python3.9/site-packages/silverback/_importer.py", line 55, in import_from_string
    module = importlib.import_module(module_str)
  File "/usr/local/lib/python3.9/importlib/__init__.py", line 127, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1030, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1007, in _find_and_load
  File "<frozen importlib._bootstrap>", line 986, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 680, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 850, in exec_module
  File "<frozen importlib._bootstrap>", line 228, in _call_with_frames_removed
  File "/home/harambe/project/example.py", line 12, in <module>
    USDC = tokens["USDC"]
  File "/usr/local/lib/python3.9/site-packages/ape_tokens/managers.py", line 124, in __getitem__
    raise KeyError(f"Symbol '{symbol}' is not a known token symbol") from e
KeyError: "Symbol 'USDC' is not a known token symbol"

How can it be fixed?

Fill this in if you have ideas on how the bug could be fixed.

From SyncLinear.com | SBK-176

Inconsistent branding on casing, `Silverback` versus `SilverBack`

Overview

Sometimes it is written as Silverback and sometimes SilverBack. The actual gorilla is Silverback so that is the one I would prefer. But if you look at some spots, like SilverBackException, the casing is inconsistenct.

Specification

  • pick one or the other
  • change everywhere to the one.

Dependencies

Include links to any open issues that must be resolved before this feature can be implemented.

bug: websockets.exceptions.ConnectionClosedError: no close frame received or sent

What went wrong?

Traceback (most recent call last):
  File "/usr/local/lib/python3.11/site-packages/silverback_platform/runner.py", line 238, in _process_events
    async for raw_event in self.subscriptions.get_subscription_data(sub_id):
  File "/usr/local/lib/python3.11/site-packages/silverback/subscriptions.py", line 123, in get_subscription_data
    await self.__anext__()
  File "/usr/local/lib/python3.11/site-packages/silverback/subscriptions.py", line 49, in __anext__
    message = await self.connection.recv()
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/local/lib/python3.11/site-packages/websockets/legacy/protocol.py", line 568, in recv
    await self.ensure_open()
  File "/usr/local/lib/python3.11/site-packages/websockets/legacy/protocol.py", line 939, in ensure_open
    raise self.connection_closed_exc()
websockets.exceptions.ConnectionClosedError: no close frame received or sent

How can it be fixed?

Would be good to have better error handling and recovery for Websockets. For long running processes, these errors are inevitable.

More dynamic broker configuration

Overview

I'd like to be able to use more dynamic broker configuration on initialization. Right now, we can kind of sort of provide one string as the first argument to __init__() here:

# TODO: Not all brokers share a common arg signature.
broker = broker_class(self.BROKER_URI or None)

But broker don't all share the same signature and some may have more complex configuration options. For instance, this PR in taskiq-sqs that adds two extra params to configure how it interacts with SQS.

Specification

I'm not entirely sure how we'd want to go about this, which is why I'm creating an issue. Either we'd have to somehow hack together some way to dynamically provide this config via env vars, or maybe we need a way to provide the broker to the application without using pydantic settings at all.

Thoughts?

feat: use -v DEBUG in silverback run

Overview

Provide a simple overview of what you wish to see added. Please include:

  • What you are trying to do
    see debugs
  • Why Ape's current functionality is inadequate to address your goal
    silverback not importing ape-cli-context

Specification

Describe the syntax and semantics of how you would like to see this feature implemented. The more detailed the better!

Remember, your feature is much more likely to be included if it does not involve any breaking changes.

Dependencies

Include links to any open issues that must be resolved before this feature can be implemented.

bug: `none is not an allowed value (type=type_error.none.not_allowed)`

Environment information

  • ape and plugin versions:
$ ape --version
0.6.10

$ ape plugins list
Installed Plugins:
  infura         0.6.1
  tokens         0.6.0
  cairo          0.6.1
  avalanche      0.6.2
  vyper          0.6.7
  foundry        0.6.8
  solidity       0.6.5
  hardhat        0.6.7
  optimism       0.6.0
  template       0.6.0
  arbitrum       0.6.0
  etherscan      0.6.5
  fantom         0.6.0
  ens            0.6.0
  alchemy        0.6.1
  addressbook    0.6.0
  • Python Version:
$ python --version
Python 3.10.1
  • OS: osx/linux/win
    ubuntu LTS win11 WSL2

What went wrong?

Please include information like:

  • what command you ran

$ silverback run example:app --network http://3.86.11.61:8545

  • the code that caused the failure (see this link for help with formatting code)

  • full output of the error you received

WARNING: Connecting Geth plugin to non-Geth client 'anvil'.
INFO: Loading Silverback App with settings: BROKER_CLASS='taskiq:InMemoryBroker' BROKER_URI='' ENABLE_METRICS=False RESULT_BACKEND_CLASS='' RESULT_BACKEND_URI='' NETWORK_CHOICE='http://3.86.11.61:8545' SIGNER_ALIAS=''
Traceback (most recent call last):
  File "/home/doge/.pyenv/versions/silverback/bin/silverback", line 33, in <module>
    sys.exit(load_entry_point('silverback', 'console_scripts', 'silverback')())
  File "/home/doge/.pyenv/versions/3.10.1/envs/silverback/lib/python3.10/site-packages/click/core.py", line 1130, in __call__
    return self.main(*args, **kwargs)
  File "/home/doge/.pyenv/versions/3.10.1/envs/silverback/lib/python3.10/site-packages/click/core.py", line 1055, in main
    rv = self.invoke(ctx)
  File "/home/doge/.pyenv/versions/3.10.1/envs/silverback/lib/python3.10/site-packages/click/core.py", line 1657, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/home/doge/.pyenv/versions/3.10.1/envs/silverback/lib/python3.10/site-packages/ape/cli/commands.py", line 18, in invoke
    super().invoke(ctx)
  File "/home/doge/.pyenv/versions/3.10.1/envs/silverback/lib/python3.10/site-packages/click/core.py", line 1404, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/home/doge/.pyenv/versions/3.10.1/envs/silverback/lib/python3.10/site-packages/click/core.py", line 760, in invoke
    return __callback(*args, **kwargs)
  File "/home/doge/ape/silverback/silverback/_cli.py", line 23, in run
    app = import_from_string(path)
  File "/home/doge/ape/silverback/silverback/_importer.py", line 55, in import_from_string
    module = importlib.import_module(module_str)
  File "/home/doge/.pyenv/versions/3.10.1/lib/python3.10/importlib/__init__.py", line 126, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1050, in _gcd_import
  File "<frozen importlib._bootstrap>", line 1027, in _find_and_load
  File "<frozen importlib._bootstrap>", line 1006, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 688, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 883, in exec_module
  File "<frozen importlib._bootstrap>", line 241, in _call_with_frames_removed
  File "/home/doge/ape/silverback/example.py", line 9, in <module>
    app = SilverBackApp()
  File "/home/doge/ape/silverback/silverback/application.py", line 25, in __init__
    self.broker = settings.get_broker()
  File "/home/doge/ape/silverback/silverback/settings.py", line 43, in get_broker
    middlewares: List[TaskiqMiddleware] = [SilverbackMiddleware()]
  File "/home/doge/ape/silverback/silverback/middlewares.py", line 18, in __init__
    self.block_time = self.chain_manager.provider.network.block_time or compute_block_time()
  File "/home/doge/ape/silverback/silverback/middlewares.py", line 10, in compute_block_time
    genesis = self.chain_manager.blocks[0]
  File "/home/doge/.pyenv/versions/3.10.1/envs/silverback/lib/python3.10/site-packages/ape/managers/chain.py", line 91, in __getitem__
    return self._get_block(block_number)
  File "/home/doge/.pyenv/versions/3.10.1/envs/silverback/lib/python3.10/site-packages/ape/managers/chain.py", line 389, in _get_block
    return self.provider.get_block(block_id)
  File "/home/doge/.pyenv/versions/3.10.1/envs/silverback/lib/python3.10/site-packages/ape/api/providers.py", line 784, in get_block
    return self.network.ecosystem.decode_block(block_data)
  File "/home/doge/.pyenv/versions/3.10.1/envs/silverback/lib/python3.10/site-packages/ape_ethereum/ecosystem.py", line 357, in decode_block
    return Block.parse_obj(data)
  File "pydantic/main.py", line 526, in pydantic.main.BaseModel.parse_obj
  File "pydantic/main.py", line 341, in pydantic.main.BaseModel.__init__
pydantic.error_wrappers.ValidationError: 1 validation error for Block
baseFeePerGas
  none is not an allowed value (type=type_error.none.not_allowed)

How can it be fixed?

Fill this in if you have ideas on how the bug could be fixed.

feat request: block checkpointing

Overview

Prevent blocks from being re-checked.
Both a perf and can help prevent duplicating actions you really don't want to duplicate.

Specification

Either just do this by default and have a checkpointing kwargument or something else or some combination of things that achieve the same goal. Make it good tho plz.

Dependencies

Include links to any open issues that must be resolved before this feature can be implemented.

[SBK-314] Add REST API for command/control/monitoring

Overview

When running an app, often it would be very useful to have some basic C&C capabilities, including being able to monitor the results of the tasks as they are running, and the failure rate (among other things). It also would be really handy to be able to restart/stop the bot in case the user decides the bot is not working as intented. Lastly, a really cool feature would be the ability to adjust parameters of the bot's execution as you are running live, such as things like filter parameters, limits, and more

Specification

Integrate a FastAPI app into the SilverbackApp class. Expose ends points for being able to query the TaskIQ task queue status (jobs in progress, failures/successes, etc) and results backend (if one is configured). Allow the ability for users to add their own custom endpoints for command/control inputs into the bot.

Consider adding a default control panel (built with Textualize) that can be extended by end users (as a demo for what we will build ourselves for the platform, as well as allows easier local development)

Dependencies

SPIKE needed to identify the proper way to secure the REST API (specifically C&C inputs) for exposure to cloud services, as well as best way to integrate into metrics gathering platforms

SBK-314

[FEAT] Add custom block time [SBK-262]

Overview

For testing my setup, I'd like to run my environment on a local chain like anvil, hardhat, or even ape networks run. However, due to how block time is calculated, it's quite difficult.

Exact issue

I have an anvil local chain running that I have run with :

anvil -b 2

The -b 2 gives it a block time of 2.

When I run the following command:

silverback run "monitoring:app" --network http://127.0.0.1:8545

I get the following stack trace

WARNING: Connecting Geth plugin to non-Geth client 'anvil'.
INFO: Loading Silverback App with settings:
  BROKER_CLASS="taskiq:InMemoryBroker"
  NETWORK_CHOICE="http://127.0.0.1:8545"
WARNING: Connecting Geth plugin to non-Geth client 'anvil'.
INFO: Loaded Silverback App:
  NETWORK="ethereum:adhoc"
  SIGNER=None
INFO: Using LiveRunner: max_exceptions=3
INFO: `ape-cache` database has not been initialized
INFO: block[block=0x45a75375d3e5e314597b98c678d592d35512fe26e4da0f6cd6c5f13aa6a92a9b] - Started
INFO: block[block=0x45a75375d3e5e314597b98c678d592d35512fe26e4da0f6cd6c5f13aa6a92a9b] - 0.010s (0.5%)
Traceback (most recent call last):
  File "/PATH/venv/bin/silverback", line 8, in <module>
    sys.exit(cli())
  File "/PATH/venv/lib/python3.10/site-packages/click/core.py", line 1157, in __call__
    return self.main(*args, **kwargs)
  File "/PATH/venv/lib/python3.10/site-packages/click/core.py", line 1078, in main
    rv = self.invoke(ctx)
  File "/PATH/venv/lib/python3.10/site-packages/click/core.py", line 1688, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/PATH/venv/lib/python3.10/site-packages/click/core.py", line 1434, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/PATH/venv/lib/python3.10/site-packages/click/core.py", line 783, in invoke
    return __callback(*args, **kwargs)
  File "/PATH/venv/lib/python3.10/site-packages/click/decorators.py", line 92, in new_func
    return ctx.invoke(f, obj, *args, **kwargs)
  File "/PATH/venv/lib/python3.10/site-packages/click/core.py", line 783, in invoke
    return __callback(*args, **kwargs)
  File "/PATH/venv/lib/python3.10/site-packages/silverback/_cli.py", line 59, in run
    asyncio.run(runner.run())
  File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/asyncio/runners.py", line 44, in run
    return loop.run_until_complete(main)
  File "/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/asyncio/base_events.py", line 646, in run_until_complete
    return future.result()
  File "/PATH/venv/lib/python3.10/site-packages/silverback/runner.py", line 61, in run
    await asyncio.gather(*tasks)
  File "/PATH/venv/lib/python3.10/site-packages/silverback/runner.py", line 87, in _block_task
    async for block in async_wrap_iter(
  File "/PATH/venv/lib/python3.10/site-packages/silverback/utils.py", line 21, in yield_queue_items
    raise exception
  File "/PATH/venv/lib/python3.10/site-packages/silverback/utils.py", line 26, in iter_to_queue
    for item in it:
  File "/PATH/venv/lib/python3.10/site-packages/ape/managers/chain.py", line 346, in poll_blocks
    _try_timeout()
  File "/PATH/venv/lib/python3.10/site-packages/ape/managers/chain.py", line 326, in _try_timeout
    raise ChainError(message)
ape.exceptions.ChainError: Timed out waiting for new block (time_waited=0.0144).

This is because the timeout variable in ape/managers/chain.py::poll_blocks is set as such:

timeout = (
            (
                10.0
                if network_name == LOCAL_NETWORK_NAME or network_name.endswith("-fork")
                else 50 * block_time
            )
            if new_block_timeout is None
            else new_block_timeout
        )

I'm not sure how to tell silverback that the anvil chain block time is 2.

Request / Ask

Is there a way to tell silverback/ape that the ad-hoc's block time is X?

`silverback worker` does not use independent processes per worker

What went wrong?

When testing with silverback worker -w 3 we noticed that it does not use separate process IDs per worker:

$ silverback worker -w 3 example:app ...
...
SUCCESS: Loaded Silverback App:
  ...
worker_startup process ID 128903
worker_startup process ID 128903
worker_startup process ID 128903

(using os.getpid to print the process ID)

How can it be fixed?

Debug cli implementation of silverback worker

def worker(cli_ctx, account, workers, max_exceptions, shutdown_timeout, path):
app = import_from_string(path)
asyncio.run(run_worker(app.broker, worker_count=workers, shutdown_timeout=shutdown_timeout))

feat: add support for defining tools and settings in toml

Overview

Want the ability to add ape and silverback related variables and install definitions in the pyproject.toml file

Specification

[[tool.ape.plugins]]
name = "aws"

[tool.silverback]
signer_alias = "aws-alias"

Dependencies

None

[SBK-182] [SBK-153] "fix: verbosity fixes in CLI" (SilverBackLtd/silverback #9)

What I did

Requires the release of Ape with this PR: ApeWorX/ape#1486

How I did it

How to verify it

Checklist

  • Passes all linting checks (pre-commit and CI jobs)
  • New test cases have been added and are passing
  • Documentation has been updated
  • PR title follows Conventional Commit standard (will be automatically included in the changelog)

SilverBackLtd/silverback #9 by @antazoey on GitHub

via LinearSync

From SyncLinear.com | SBK-153

SBK-182

bug: BlockNotFoundError: Missing latest block. Reason: when sending a str, it must be a hex string. Got: 'latest'

Environment information

Should be latest everything. Seen in the spamalot bot.

What went wrong?

Traceback (most recent call last):
  File "/usr/local/lib/python3.10/site-packages/ape/api/providers.py", line 997, in get_block
    block_data = dict(self.web3.eth.get_block(block_id))
  File "/usr/local/lib/python3.10/site-packages/web3/eth/eth.py", line 390, in get_block
    return self._get_block(block_identifier, full_transactions)
  File "/usr/local/lib/python3.10/site-packages/web3/module.py", line 64, in caller
    (method_str, params), response_formatters = method.process_params(
  File "/usr/local/lib/python3.10/site-packages/web3/method.py", line 214, in process_params
    _apply_request_formatters(params, self.request_formatters(method)),
  File "/usr/local/lib/python3.10/site-packages/eth_utils/functional.py", line 45, in inner
    return callback(fn(*args, **kwargs))
  File "/usr/local/lib/python3.10/site-packages/web3/method.py", line 55, in _apply_request_formatters
    formatted_params = pipe(params, request_formatters)
  File "cytoolz/functoolz.pyx", line 680, in cytoolz.functoolz.pipe
  File "cytoolz/functoolz.pyx", line 655, in cytoolz.functoolz.c_pipe
  File "cytoolz/functoolz.pyx", line 263, in cytoolz.functoolz.curry.__call__
  File "/usr/local/lib/python3.10/site-packages/web3/_utils/abi.py", line 746, in map_abi_data
    return pipe(data, *pipeline)
  File "cytoolz/functoolz.pyx", line 680, in cytoolz.functoolz.pipe
  File "cytoolz/functoolz.pyx", line 655, in cytoolz.functoolz.c_pipe
  File "cytoolz/functoolz.pyx", line 263, in cytoolz.functoolz.curry.__call__
  File "/usr/local/lib/python3.10/site-packages/web3/_utils/abi.py", line 781, in data_tree_map
    return recursive_map(map_to_typed_data, data_tree)
  File "/usr/local/lib/python3.10/site-packages/web3/_utils/decorators.py", line 30, in wrapped
    wrapped_val = to_wrap(*args)
  File "/usr/local/lib/python3.10/site-packages/web3/_utils/formatters.py", line 84, in recursive_map
    items_mapped = map_collection(recurse, data)
  File "/usr/local/lib/python3.10/site-packages/web3/_utils/formatters.py", line 68, in map_collection
    return datatype(map(func, collection))
  File "/usr/local/lib/python3.10/site-packages/web3/_utils/formatters.py", line 82, in recurse
    return recursive_map(func, item)
  File "/usr/local/lib/python3.10/site-packages/web3/_utils/decorators.py", line 30, in wrapped
    wrapped_val = to_wrap(*args)
  File "/usr/local/lib/python3.10/site-packages/web3/_utils/formatters.py", line 85, in recursive_map
    return func(items_mapped)
  File "/usr/local/lib/python3.10/site-packages/web3/_utils/abi.py", line 777, in map_to_typed_data
    return ABITypedData(func(*elements))
  File "/usr/local/lib/python3.10/site-packages/web3/_utils/normalizers.py", line 80, in wrapper
    modified = to_wrap(type_str, data)
  File "/usr/local/lib/python3.10/site-packages/web3/_utils/normalizers.py", line 135, in new_normalizer
    return old_normalizer(abi_type, type_str, data)
  File "/usr/local/lib/python3.10/site-packages/web3/_utils/normalizers.py", line 148, in abi_bytes_to_hex
    bytes_data = hexstr_if_str(to_bytes, data)
  File "cytoolz/functoolz.pyx", line 263, in cytoolz.functoolz.curry.__call__
  File "/usr/local/lib/python3.10/site-packages/web3/_utils/encoding.py", line 172, in hexstr_if_str
    raise ValueError(
ValueError: when sending a str, it must be a hex string. Got: 'latest'
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
  File "/usr/local/bin/silverback", line 8, in <module>
    sys.exit(cli())
  File "/usr/local/lib/python3.10/site-packages/click/core.py", line 1157, in __call__
    return self.main(*args, **kwargs)
  File "/usr/local/lib/python3.10/site-packages/click/core.py", line 1078, in main
    rv = self.invoke(ctx)
  File "/usr/local/lib/python3.10/site-packages/click/core.py", line 1688, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/usr/local/lib/python3.10/site-packages/click/core.py", line 1434, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/usr/local/lib/python3.10/site-packages/click/core.py", line 783, in invoke
    return __callback(*args, **kwargs)
  File "/usr/local/lib/python3.10/site-packages/click/decorators.py", line 92, in new_func
    return ctx.invoke(f, obj, *args, **kwargs)
  File "/usr/local/lib/python3.10/site-packages/click/core.py", line 783, in invoke
    return __callback(*args, **kwargs)
  File "/usr/local/lib/python3.10/site-packages/silverback/_cli.py", line 83, in run
    asyncio.run(runner.run())
  File "/usr/local/lib/python3.10/asyncio/runners.py", line 44, in run
    return loop.run_until_complete(main)
  File "/usr/local/lib/python3.10/asyncio/base_events.py", line 649, in run_until_complete
    return future.result()
  File "/usr/local/lib/python3.10/site-packages/silverback/runner.py", line 129, in run
    await asyncio.gather(*tasks)
  File "/usr/local/lib/python3.10/site-packages/silverback/runner.py", line 235, in _block_task
    async for block in async_wrap_iter(
  File "/usr/local/lib/python3.10/site-packages/silverback/utils.py", line 23, in yield_queue_items
    raise exception
  File "/usr/local/lib/python3.10/site-packages/silverback/utils.py", line 28, in iter_to_queue
    for item in it:
  File "/usr/local/lib/python3.10/site-packages/ape/managers/chain.py", line 324, in poll_blocks
    confirmable_block_number = self.height - required_confirmations
  File "/usr/local/lib/python3.10/site-packages/ape/managers/chain.py", line 67, in height
    if self.head.number is None:
  File "/usr/local/lib/python3.10/site-packages/ape/managers/chain.py", line 60, in head
    return self._get_block("latest")
  File "/usr/local/lib/python3.10/site-packages/ape/managers/chain.py", line 395, in _get_block
    return self.provider.get_block(block_id)
  File "/usr/local/lib/python3.10/site-packages/ape/api/providers.py", line 999, in get_block
    raise BlockNotFoundError(block_id, reason=str(err)) from err
ape.exceptions.BlockNotFoundError: Missing latest block. Reason: when sending a str, it must be a hex string. Got: 'latest'

How can it be fixed?

Just fix it

feat: Logging results to console

Overview

Results returned from handlers are currently intended to be stored in a DB (from docs), but it would be great to also see these results in console logs as the bot is running.

Specification

Using example.py, potentially log what would be stored in a results DB to the console. So, the event handler

# This is how we trigger off of events
# Set new_block_timeout to adjust the expected block time.
@app.on_(USDC.Transfer, start_block=18588777, new_block_timeout=25)
# NOTE: Typing isn't required
def exec_event1(log):
    if log.log_index % 7 == 3:
        # If you ever want the app to shutdown under some scenario, call this exception
        raise CircuitBreaker("Oopsie!")
    return {"amount": log.amount}

would log {"amount": log.amount} for each log handled.

Managed Parameters

Overview

When designing a Silverback app, it is typically very useful to design parameterization into the logic of the app so that you can adjust this behavior. We should have a deeper integration for updating and managing these parameters, both when running live or when starting/restarting the app in a cloud environment.

Specification

We need some way for a user to describe a parameter, and add a method for it to get added to state, so that all worker tasks can see the parameter update:

@app.parameter("something", int, lower_limit=200)  # or `upper_limit=`, defaults to None
def handle_parameter(value, state):
    # NOTE: Raises if not using the same parameter name above
    state.something = value  # or, could generate a more complex handler
    # no return, but task handle will automatically return the update 

# OR, more simply:

app.add_parameter("something", int, lower_limit=...)
# basically the same as above, no ability to handle it specially

After this is done, the parameter becomes a part of both the App's environment variables (e.g. SILVERBACK_APP_something=...) as well as accepts updates triggered by the platform or management dashboard for the app (see #39)

Lastly, when accepting parameter updates, it should be possible to accept a batch of updates and process them at the same time, scheduling the tasks to take effect all before any other tasks are allowed to proceed using the new variables (all updates are atomic when batched)

Dependencies

No dependencies

Track `app.signer` transactions in `TaskResult`

Overview

We need a hook to show whether a task triggered a transaction by app.signer for deeper integration with platform monitoring

Specification

The app.signer should be a part of the system registration flow, so that we can reference transactions by app.signer's .nonce in the task recorder interface, which will allow us to index further downstream what transactions that a signer has made during execution (after it has fully settled).

Since it may be the case that we have auto-update functionality in "smart receipts" further down the road, and also that transactions may not actually be fully "mined" by the time the task is completed, it seems smartest to reference by .nonce in this fashion and to wait until a finalization threshold to record properties from the final transaction result

Dependencies

n/a

bug: TypeError: run() got multiple values for argument 'network'

Environment information

  • ape and plugin versions:
$ ape --version
0.6.11.dev15+g816a7778

$ ape plugins list
Installed Plugins:
  template       0.6.0
  addressbook    0.6.0
  fantom         0.6.0
  etherscan      0.6.5
  cairo          0.6.1
  hardhat        0.6.7
  avalanche      0.6.2
  arbitrum       0.6.0
  tokens         0.6.0
  infura         0.6.1
  solidity       0.6.5
  alchemy        0.6.1
  vyper          0.6.7
  optimism       0.6.0
  foundry        0.6.8
  ens            0.6.0

$ python --version
Python 3.10.1
  • OS: win 11 wsl2 ubuntu LTS

What went wrong?

  • what command you ran
$ silverback run example:app --network http://35.175.202.145:8545
WARNING: Connecting Geth plugin to non-Geth client 'anvil'.
Traceback (most recent call last):
  File "/home/doge/.pyenv/versions/silverback/bin/silverback", line 8, in <module>
    sys.exit(cli())
  File "/home/doge/.pyenv/versions/3.10.1/envs/silverback/lib/python3.10/site-packages/click/core.py", line 1130, in __call__
    return self.main(*args, **kwargs)
  File "/home/doge/.pyenv/versions/3.10.1/envs/silverback/lib/python3.10/site-packages/click/core.py", line 1055, in main
    rv = self.invoke(ctx)
  File "/home/doge/.pyenv/versions/3.10.1/envs/silverback/lib/python3.10/site-packages/click/core.py", line 1657, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/home/doge/ape/ape/src/ape/cli/commands.py", line 18, in invoke
    super().invoke(ctx)
  File "/home/doge/.pyenv/versions/3.10.1/envs/silverback/lib/python3.10/site-packages/click/core.py", line 1404, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/home/doge/.pyenv/versions/3.10.1/envs/silverback/lib/python3.10/site-packages/click/core.py", line 760, in invoke
    return __callback(*args, **kwargs)
  File "/home/doge/.pyenv/versions/3.10.1/envs/silverback/lib/python3.10/site-packages/click/decorators.py", line 84, in new_func
    return ctx.invoke(f, obj, *args, **kwargs)
  File "/home/doge/.pyenv/versions/3.10.1/envs/silverback/lib/python3.10/site-packages/click/core.py", line 760, in invoke
    return __callback(*args, **kwargs)
TypeError: run() got multiple values for argument 'network'

How can it be fixed?

discussion: start_block parameter in decorator [SBK-360]

Problem

Currently the start_block param of the on_() decorator only has an affect on the PollingRunner.

start_block: Optional[int] = None,

I think this may be a point of confusion since it behaves differently depending on the runner. It sure surprised me seeing blocks from 1m blocks ago during testing. I think it's worth considering either documenting, removing, or somehow supporting in the WebsocketRunner.

Possible solutions

I think there's a few options here.

  • Document the current behavior, and maybe show a warning when using websockets
  • Remove it completely. This would be easy. We wouldn't need to consider historical events. For isntance, the ApePay use case does event backlog processing during the Silverback startup event, so maybe historical processing is unnecessary in Silverback.
  • Using it as a filter in WebsocketRunner. Don't process events from before start_block. This could be used as a kind of "deploy code before available on chain" type thing.
  • Backfill events and blocks by polling the missed block range even when using websockets. This would be RPC-expensive but would be more akin to a "traditional" listener that can pickup where it left off. The complexity is an ongoing issue with this implementation, however.

Allow filtering for Event Log tasks

Overview

Allow specifying indexed parameters to reduce the amount of events grabbed by an event log task

Specification

@app.on_(USDC.Transfer, to=app.signer)
def receive_task(log):
    ... # Do something on receiving USDC to the app signer account

Note that it should be pretty nice to be able to use un-converted objects to use filter with, but sometimes the name of indexed event arguments are not valid Python identifiers such as from, so an alternative method should be specified to work around that and potential name conflicts with other kwargs to this method e.g. start_block (perhaps a kwarg filter_args={...} that gets merged with the remaining **kwargs on app.on_)

Dependencies

May require updates to Core first

#55

Refactor Runner to be stateless

Elevator pitch:

When initializing the worker task, the runner task should not have to be aware of how the application logic is specified, such that we can run a generic version of the task runner for any app

Runtime Data Acquistion

Overview

It would be very useful for Silverback to automate the collection of runtime datapoints deemed important by the app creator for the purposes of monitoring the operation of their bots, for either live monitoring or historical analysis purposes (when properly stored). TaskIQ defines an ability to return a TaskiqResult data item from the execution of a task, so that the original task kicker can read and handle that data according to it's own needs.

Currently, the Runner class does this handling of the results, and with #45 it can also support updating a database, but makes no restrictions on the types of data that can be returned and stored. The Silverback Platform will have a feature for displaying different timeseries metrics and events that occur with each bot, so we need a way to allow users to specify data items they wish to populate into the platform.

Specification

Restrict the types of data that a Silverback app task can return to the following:

silverback/types.py:

from datetime import datetime
from decimal import Decimal
from typing import Annotated, Literal

from pydantic import BaseModel, Field


class Datapoint(BaseModel):
    type: str  # discriminator

    # default value ensures we don't have to set this manually
    time: Annotated[datetime, Field(default_factory=lambda: datetime.now(timezone.utc))]


class ScalarDatapoint(Datapoint):
    type: Literal["scalar"]

    # supported scalar value types:
    data: bool | int | float | Decimal


# This is what a Silverback app task must return to integrate properly with our data acq system
Result = dict[str, Datapoint]
# Otherwise, log a warning and ignore the return value

A user can return one of the basic types supported by ScalarDatapoint.data directly from their task, and Silverback task runner will convert those data items into an instance of the ScalarDatapoint class for proper integration with the platform without having to import and use a more cumbersome type.


For future support of new Datapoint subclasses, the user will have to import that type in order to use it effectively. For example, let's say we design a new Datapoint type that supports graphing candlestick chart datapoints, then the model for that datapoint might look something like this:

class CandlestickDatapoint(Datapoint):
    type: Literal["candlestick"]

    high: float
    low: float
    open: float
    close: float

    # datapoints can also have computed properties
    @property
    def closed_higher(self) -> bool:
        return self.open < self.close

The data structure will be appropriately serialized to work with the data acquisition system to be stored as a complex timeseries data point, which can be graphed either on the supporting chart type (which makes use of all the parameters by default) OR can be mapped to another chart type by exposing one or more of the inner data items (e.g. .high, .low, etc.) to what that chart expects.

If the user changes the str label applied to the result of a specific datapoint (the key in the Result dictionary), that will change the labeled dataset where the datapoint is associated. It is not recommended to change the class of the datapoint type associated with a specific label, but a user can make this change (even though it may cause inconsistent results when displaying the timeseries data chart).

A user can also selectively decide to return a specific labeled datapoint as part of a set, all that will mean is that sampling frequency of that data item is inconsistent, and updates in the chart display may display poorly as a result.

Dependencies

Somewhat related to #39

Add autosign to runner

Overview

Most people wanting to run a bot locally will probably use a KeyfileAccount that requires opt-ing into automatic signing, which is probably annoying.

Specification

Add a flag to silverback run and silverback worker that allows opt-ing out of autosigning as a default e.g. --account my-alias --no-autosign

Dependencies

n/a

[SBK-153] "fix: verbosity fixes in CLI" (SilverBackLtd/silverback #9)

What I did

Requires the release of Ape with this PR: ApeWorX/ape#1486

How I did it

How to verify it

Checklist

  • Passes all linting checks (pre-commit and CI jobs)
  • New test cases have been added and are passing
  • Documentation has been updated
  • PR title follows Conventional Commit standard (will be automatically included in the changelog)

SilverBackLtd/silverback #9 by @antazoey on GitHub

via LinearSync

From SyncLinear.com | SBK-153

Testing webhook [Test]

Overview

Provide a simple overview of what you wish to see added. Please include:

  • What you are trying to do
  • Why Ape's current functionality is inadequate to address your goal

Specification

Describe the syntax and semantics of how you would like to see this feature implemented. The more detailed the better!

Remember, your feature is much more likely to be included if it does not involve any breaking changes.

Dependencies

Include links to any open issues that must be resolved before this feature can be implemented.

[SBK-251] Store the last successfully processed block for fast restart

Overview

If the script restarts, you might want to know what the last successfully processed block was so you can continue from that point

Specification

Would be useful for a pattern like this

@app.on_startup()
def process_event(state):
    data = contract.MyEvent.query("*", start_block=state.last_block)
    ...

Dependencies

Include links to any open issues that must be resolved before this feature can be implemented.

SBK-251

Not a valid TransactionType error with goerli/sepolia testnet

Environment information

  • ape and plugin versions:
$ ape --version
0.7.7

$ ape plugins list
Installed Plugins
  alchemy      0.7.1
  etherscan    0.7.0
  foundry      0.7.3
  solidity     0.7.1
  • Python Version: 3.11.0
  • OS: osx
silverback[dev]==0.3.0

What went wrong?

Attempted to run the example.py file renamed as main.py

$ silverback run "main:app" --network ethereum:sepolia:alchemy

And obtained the following error

ValueError: 3 is not a valid TransactionType

when attempting for both sepolia and goerli. ethereum:mainnet:alchemy runs the example app fine for me.

Full output

$ silverback run "main:app" --network ethereum:sepolia:alchemy
INFO: Loading Silverback App with settings:
  INSTANCE="default"
  BROKER_CLASS="taskiq:InMemoryBroker"
  NETWORK_CHOICE="ethereum:sepolia:alchemy"
INFO: Loaded Silverback App:
  NETWORK="ethereum:sepolia"
  SIGNER=None
WARNING: The polling runner makes a significant amount of requests. Do not use in production over long time periods unless you know what you're doing.
INFO: silverback_startup - Started
INFO: silverback_startup - 0.000s (0.0%)
INFO: `ape-cache` database has not been initialized
Traceback (most recent call last):
  File "~/v1-arbitrageur-bot/.venv/bin/silverback", line 8, in <module>
    sys.exit(cli())
             ^^^^^
  File "~/v1-arbitrageur-bot/.venv/lib/python3.11/site-packages/click/core.py", line 1157, in __call__
    return self.main(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "~/v1-arbitrageur-bot/.venv/lib/python3.11/site-packages/click/core.py", line 1078, in main
    rv = self.invoke(ctx)
         ^^^^^^^^^^^^^^^^
  File "~/v1-arbitrageur-bot/.venv/lib/python3.11/site-packages/click/core.py", line 1688, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "~/v1-arbitrageur-bot/.venv/lib/python3.11/site-packages/ape/cli/commands.py", line 96, in invoke
    return self._invoke(ctx, provider=provider)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "~/v1-arbitrageur-bot/.venv/lib/python3.11/site-packages/ape/cli/commands.py", line 133, in _invoke
    return ctx.invoke(self.callback or (lambda: None), **ctx.params)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "~/v1-arbitrageur-bot/.venv/lib/python3.11/site-packages/click/core.py", line 783, in invoke
    return __callback(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "~/v1-arbitrageur-bot/.venv/lib/python3.11/site-packages/click/decorators.py", line 92, in new_func
    return ctx.invoke(f, obj, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "~/v1-arbitrageur-bot/.venv/lib/python3.11/site-packages/click/core.py", line 783, in invoke
    return __callback(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "~/v1-arbitrageur-bot/.venv/lib/python3.11/site-packages/silverback/_cli.py", line 100, in run
    asyncio.run(runner.run())
  File "~/.pyenv/versions/3.11.0/lib/python3.11/asyncio/runners.py", line 190, in run
    return runner.run(main)
           ^^^^^^^^^^^^^^^^
  File "~/.pyenv/versions/3.11.0/lib/python3.11/asyncio/runners.py", line 118, in run
    return self._loop.run_until_complete(task)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "~/.pyenv/versions/3.11.0/lib/python3.11/asyncio/base_events.py", line 650, in run_until_complete
    return future.result()
           ^^^^^^^^^^^^^^^
  File "~/v1-arbitrageur-bot/.venv/lib/python3.11/site-packages/silverback/runner.py", line 129, in run
    await asyncio.gather(*tasks)
  File "~/v1-arbitrageur-bot/.venv/lib/python3.11/site-packages/silverback/runner.py", line 241, in _block_task
    block_task = await block_handler.kiq(block)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "~/v1-arbitrageur-bot/.venv/lib/python3.11/site-packages/taskiq/decor.py", line 99, in kiq
    return await self.kicker().kiq(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "~/v1-arbitrageur-bot/.venv/lib/python3.11/site-packages/taskiq/kicker.py", line 130, in kiq
    f"Kicking {self.task_name} with args={args} and kwargs={kwargs}.",
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "~/v1-arbitrageur-bot/.venv/lib/python3.11/site-packages/pydantic/main.py", line 916, in __repr__
    return f'{self.__repr_name__()}({self.__repr_str__(", ")})'
                                     ^^^^^^^^^^^^^^^^^^^^^^^
  File "~/v1-arbitrageur-bot/.venv/lib/python3.11/site-packages/pydantic/_internal/_repr.py", line 55, in __repr_str__
    return join_str.join(repr(v) if a is None else f'{a}={v!r}' for a, v in self.__repr_args__())
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "~/v1-arbitrageur-bot/.venv/lib/python3.11/site-packages/pydantic/_internal/_repr.py", line 55, in <genexpr>
    return join_str.join(repr(v) if a is None else f'{a}={v!r}' for a, v in self.__repr_args__())
                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "~/v1-arbitrageur-bot/.venv/lib/python3.11/site-packages/pydantic/main.py", line 935, in __repr_args__
    yield from ((k, getattr(self, k)) for k, v in self.model_computed_fields.items() if v.repr)
  File "~/v1-arbitrageur-bot/.venv/lib/python3.11/site-packages/pydantic/main.py", line 935, in <genexpr>
    yield from ((k, getattr(self, k)) for k, v in self.model_computed_fields.items() if v.repr)
                    ^^^^^^^^^^^^^^^^
  File "~/.pyenv/versions/3.11.0/lib/python3.11/functools.py", line 1001, in __get__
    val = self.func(instance)
          ^^^^^^^^^^^^^^^^^^^
  File "~/v1-arbitrageur-bot/.venv/lib/python3.11/site-packages/ape/api/providers.py", line 87, in transactions
    return cast(List[TransactionAPI], list(self.query_manager.query(query)))
                                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "~/v1-arbitrageur-bot/.venv/lib/python3.11/site-packages/ape_ethereum/provider.py", line 529, in get_transactions_by_block
    yield self.network.ecosystem.create_transaction(**transaction)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "~/v1-arbitrageur-bot/.venv/lib/python3.11/site-packages/ape_ethereum/ecosystem.py", line 779, in create_transaction
    version = TransactionType(tx_data["type"])
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "~/.pyenv/versions/3.11.0/lib/python3.11/enum.py", line 695, in __call__
    return cls.__new__(cls, value)
           ^^^^^^^^^^^^^^^^^^^^^^^
  File "~/.pyenv/versions/3.11.0/lib/python3.11/enum.py", line 1111, in __new__
    raise ve_exc
ValueError: 3 is not a valid TransactionType

How can it be fixed?

Unsure if this is an ape issue or a silverback issue given the error seems to stem from ape_ethereum plugin.

Gracefully resolve shutdown

It looks to me like we have 1 connection to the message broker per application. If so, then we need to add a task to the task queue that handles the connection termination prior to the application terminating so we don't have accidental open connections remaining.

import signal

class SigtermHandler:

    def __init__(self):
        self.should_terminate = False
        signal.signal(signal.SIGTERM, self._set_terminate)

    def _set_terminate(self):
        self.should_terminate = True

    def check_quit_signal(self):
        return self.should_terminate
def __init__(self):
    ...
    self.sigterm_handler = SigtermHandler

async def close_conn(self): # Add this method to the list of tasks that you run in the gather
    while not self.sigterm_handler.check_quit_signal():
        await.sleep(1)
    terminate_connection()  # some function to terminate connection to message broker

This is the basic idea of this, I'll keep reviewing to make sure I'm not completely off base here. But this is what I've done with Kafka and with RabbitMQ in Python

That seems reasonable

Just to lay out what we have going on here, we have a couple of different things in motion that need to gracefully shutdown:

  • broker needs to stop producing tasks (tasks are created based on RPC subscriptions module)
  • RPC subscriptions should be independently unsubscribed (so they stop producing new tasks)
  • websocket connections (that provide conduict for RPC subscriptions to come in) should be disconnected
  • all pending tasks should be finished (using the silverback worker command this is a separate process)
  • finally, we can exit Runner.run() method

@mikeshultz is working on some of this for the cluster (chat more offline about what the needs are there e.g. upgrading worker process to a new revision of the container), so I think it'd be great to collab more about what a "proper shutdown" scenario looks like, both for local dev (here in this SDK) and for the cluster (talk more about that outside of github)

Originally posted by @fubuloubu in #65 (comment)

bug(client): Transaction processing error on approve() #228 [SBK-342]

Seen when testing on Sepolia:

I did see a 503 on an eth_getTransactionReceipt RPC request. Perhaps that caused wagmi to return a failure. Might be good to handle these if possible (if wagmi doesn't make that difficult), and maybe surface the issue to the user if there's nothing we can do about it.

The transaction did end up succeeding. This returned an error before MM showed the tx as completed.

CC @alexisnsns


Automatically created from Linear (#342)

via LinearSync

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.