ApeWorX / ape

The smart contract development tool for Pythonistas, Data Scientists, and Security Professionals
https://apeworx.io
Apache License 2.0
866 stars 133 forks source link

Export Solidity functions as Python functions #2178

Open gabrielfior opened 1 month ago

gabrielfior commented 1 month ago

Overview

I want to generate Python functions from a smart contract's ABI definitions.

Specification

I would expect this to be part of the ape.contracts package.

Dependencies

For reference, I found some (old) implementations that don't actually do what I'm thinking. https://github.com/Consensys/python-solidity-parser/tree/master (last commit 3 years ago)

This also goes in the right direction -> https://github.com/ethereum/web3.py/issues/3016

linear[bot] commented 1 month ago

APE-1785 Export Solidity functions as Python functions

gabrielfior commented 1 month ago

APE-1785 Export Solidity functions as Python functions

No access

fubuloubu commented 1 month ago

This would take a larger refactor to move to a more functional, metaclass-style method for generating ContractContainer singleton classes from ethpm_types.ContractType models which can be instantiated (and enhanced with other information from ContractType such as NatSpec docs, etc.)

@BobTheBuidler might find this idea interesting, he was looking for a way to avoid having multiple instances of ContractType models to reduce the memory usage of Ape with large amounts of instances with the same contract type

cc @antazoey @bitwise-constructs who are thinking about overall improvements to contract ergonomics

antazoey commented 1 month ago

I just want to have some things to visualize for us to discuss, as this might be something we want to consider for 0.9.

Currently:

>>> from ape import project
>>> project.MyContract

Note: currently, all contract-types are the same class and not individual classes (not generated at runtime via ABI)

Proposition:

>>> from ape.contracts import MyContract

The new MyContract is something generated at runtime via the ABI. All methods are readidly available. Structs are the same, we already use meta-classes for those, same with errors.

^ That last sentence is a good point: we are already using metaclass / runtime type generation for errors and structs, so one more step to ContractType feels right.

Anyway, the downside is the metaclass are weird, but I think one can just expect that... like you can't use issubclass and some other stuff like that.

fubuloubu commented 1 month ago

Proposition:

>>> from ape.contracts import MyContract

As an overall note, this level of change (migrating from using ape.project.MyContract to ape.contracts.MyContract) would be a v1.0 type of breaking change


Further, I don't think I like automatically generating imports. That's what Brownie used to do, and it's very difficult because you pollute the namespace of the module. It can be much more simple that project.__getattr__ uses keys from the current project's known contract types and generates custom classes using ContractContainer more as a metaclass (or we can get rid of ContractContainer entirely). Still, like you said it would be a breaking change to remove ContractContainer from the class heirarchy (and ContractInstance, which subclasses ape.api.address.BaseAddress) but there are ways of just applying a common parent class when forming the metaclass to maintain that heirarchy for all the generated subclasses.

In a similar vein, we can probably do the same with MethodABI objects to generate custom method handlers as metaclasses and insert them into the ContractInstance objects (closer to what this issue's OP is talking about) This might also be a breaking v1.0 level change due to the change of metaclasses, type annotation support, etc. (I think I am using ContractMethodHandler inside of ape-safe and the multicall module)