Skip to content

Commit

Permalink
added option to load contracts using abi. which can be a string, List…
Browse files Browse the repository at this point in the history
… or file
  • Loading branch information
Aviksaikat committed Aug 29, 2023
1 parent b45bf22 commit f7b280e
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 0 deletions.
15 changes: 15 additions & 0 deletions docs/userguides/contracts.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,21 @@ from ape import Contract
contract = Contract("v2.registry.ychad.eth")
```

## From ABIs

You can load contracts using their ABIs:

```python
from ape import Contract

address = "0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45"
abi = '[{"inputs":[{"internalType":"contract MultiWrapper"...]

contract = Contract(address, abi=abi)
```

This will create the Contract instance from the given ABI.

## From Previous Deployment

Ape keeps track of your deployments for you so you can always refer back to a version that you deployed previously.
Expand Down
43 changes: 43 additions & 0 deletions src/ape/managers/chain.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import pandas as pd
from ethpm_types import ContractType
from ethpm_types.abi import ABI
from rich import get_console
from rich.console import Console as RichConsole

Expand Down Expand Up @@ -1140,6 +1141,9 @@ def instance_at(
address: Union[str, AddressType],
contract_type: Optional[ContractType] = None,
txn_hash: Optional[str] = None,
abi: Optional[
Union[List[ABI], str, Path]
] = None, # ABI can be string, List[ABI] or a Path object
) -> ContractInstance:
"""
Get a contract at the given address. If the contract type of the contract is known,
Expand All @@ -1159,6 +1163,8 @@ def instance_at(
in case it is not already known.
txn_hash (Optional[str]): The hash of the transaction responsible for deploying the
contract, if known. Useful for publishing. Defaults to ``None``.
abi (Union[Optional[List[ethpm_types.abi.ABI], str, Path]] = None): Will load the contract
from abi provided like Contract(address, abi=abi) by the user.
Returns:
:class:`~ape.contracts.base.ContractInstance`
Expand All @@ -1182,6 +1188,43 @@ def instance_at(
else:
raise # Current exception

if abi:
# if abi is type string then convert it to json object
if isinstance(abi, str):
try:
# convert the abi from string to list
list_abi = ContractType.parse_raw(abi)
# Create a new ContractType with the provided ABI
contract_type = ContractType(abi=list_abi)
except Exception as err:
if contract_type:
# If a default contract type was provided, don't error and use it.
logger.error(str(err))
else:
raise # Current exception
# if it's of type List[ABI] pass it directly to ContractType Class
elif isinstance(abi, list):
# Use the provided abi directly
contract_type = ContractType(abi=abi)
elif isinstance(abi, Path):
# Handle both absolute and relative paths
if not abi.is_absolute():
project_folder = Path.cwd()
abi = project_folder / abi
try:
with abi.open() as f:
list_abi = ContractType.parse_raw(f)
# Create a new ContractType with the provided ABI from the file
contract_type = ContractType(abi=list_abi)
except Exception as err:
if contract_type:
# If a default contract type was provided, don't error and use it.
logger.error(str(err))
else:
raise # Current exception
else:
raise TypeError("Invalid ABI type, expecting str, List[ABI] or a json file")

if not contract_type:
raise ContractNotFoundError(
contract_address,
Expand Down
15 changes: 15 additions & 0 deletions tests/functional/test_chain.py
Original file line number Diff line number Diff line change
Expand Up @@ -669,3 +669,18 @@ def test_get_contract_receipt(chain, vyper_contract_instance):
chain.mine()
receipt = chain.contracts.get_creation_receipt(address)
assert receipt.contract_address == address


def test_load_solidity_contract_from_abi():
# USDC contract
abi = '[{"constant":false,"inputs":[{"name":"newImplementation","type":"address"}],"name":"upgradeTo","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"newImplementation","type":"address"},{"name":"data","type":"bytes"}],"name":"upgradeToAndCall","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[],"name":"implementation","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"newAdmin","type":"address"}],"name":"changeAdmin","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"admin","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"_implementation","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":false,"name":"previousAdmin","type":"address"},{"indexed":false,"name":"newAdmin","type":"address"}],"name":"AdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"implementation","type":"address"}],"name":"Upgraded","type":"event"}]'
address = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"

contract = ape.Contract(address, abi=abi)

assert isinstance(contract, ContractInstance)
assert contract.address == address


def test_load_vyper_contract_from_abi():
pass

0 comments on commit f7b280e

Please sign in to comment.