# Contracts You can interact with contracts pythonically using ape! First, we need to obtain a contract instance. One way to do this is to deploy a contract. The other way is to initialize an already-deployed contract using its address. ## From Deploy Deploy contracts from your project using the `project` root-level object. You deploy contracts using Python functions such as [AccountAPI.deploy](../methoddocs/api.html#ape.api.accounts.AccountAPI.deploy) or [ContractContainer.deploy](../methoddocs/contracts.html#ape.contracts.base.ContractContainer.deploy). **NOTE**: You can run Ape's deploy functions from anywhere you run Python! You need both an account and a contract in order to deploy a contract, as the deployment process requires a transaction to submit the contract data to the blockchain. To learn about accounts and how to use them, see the [Accounts Guide](./accounts.html). You also need the contract. You can access contract types from Ape's root-level `project` object (e.g. `project.MyContract`) and their types are [ContractContainer](../methoddocs/contracts.html#ape.contracts.base.ContractContainer). Let's assume you have a Vyper contract like this: ```vyper contract MySmartContract: owner: public(address) balance: public(uint256) @public @payable @public def __init__(arg1: uint256, arg2: address): self.owner = arg2 self.balance = arg1 ``` Before you can deploy this contract, you must ensure it was compiled. To learn about compiling in Ape, please see [this guide](./compile.html). After it is compiled, you can deploy it. Here is a basic example of Python code to deploy a contract: ```python from ape import accounts, project # You need an account to deploy, as it requires a transaction. account = accounts.load("") # NOTE: refers to your account alias! contract = project.MyContract.deploy(1, account, sender=account) # NOTE: You can also do it this way: contract2 = account.deploy(project.MyContract, 1, account) ``` The arguments to the constructor (`1, account`) can be in Python form. Ape will automatically convert values in your transactions, thus allowing you to provide higher-level objects or abstractions as input types. That is why, as you can see, the second argument is an `AccountAPI` object for the type `address` in the contract. Notice in the example, we use `project.MyContract` to access the contract type. To avoid naming collisions with other properties on the `project` object, you can alternatively use the [get_contract()](../methoddocs/managers.html#ape.managers.project.manager.ProjectManager.get_contract) method to retrieve contract containers. ```python from ape import project contract = project.get_contract("MyContract") # Same as `project.MyContract`. ``` Notice when deploying, we have to specify the `sender=` kwarg because `deploy` operations are transactions. To learn more about contract interaction via transactions, see the [Contract Interaction](#contract-interaction) section below and the [guide on transactions](./transactions.html). ### Deploy Scripts Often time, the deployment process may be unique or complex. Or possibly, you need to run the deploy-logic from CI or in a repeatable fashion. Or perhaps, you just want to avoid having to invoking Python directly. In those cases, you can use Ape's scripting system to save time and store your deployment logic. Simply copy your Python logic into an Ape script and run it via: ```shell ape run ``` Learn how to do this and scripting in its entirity by reviewing [the scripting user-guide](./scripts.html). **There is no root `ape` command to deploy contracts; only the scripting-system, the `console`, or merely using Ape as a Python library**. If your deployment process is simple or only needs to happen once, it is easy to use `ape console` to achieve a deployment. More information on how to use `ape console` can be found [here](./console.html). ### Publishing You can also publish the contract source code to an explorer upon deployment using the `publish=` kwarg on the deploy methods. More information on publishing contracts can be found in [this guide](./publishing.html). ## From Project Contract Address You can also use the [at() method](../methoddocs/contracts.html#ape.contracts.base.ContractContainer.at) from the same top-level project manager when you know the address of an already-deployed contract: ```python from ape import project contract = project.MyContract.at("0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45") ``` ## From Any Address If you already know the address of a contract, you can create instances of it using the `Contract` top-level factory: ```python from ape import Contract contract = Contract("0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45") ``` It will fetch the `contract-type` using the explorer plugin from the active network, such as [ape-etherscan](https://github.com/ApeWorX/ape-etherscan). If you have the [ENS plugin](https://github.com/ApeWorX/ape-ens) installed, you can use `.eth` domain names as the argument: ```python 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" # Using a JSON str: contract = Contract( address, abi='[{"name":"foo","type":"fallback", "stateMutability":"nonpayable"}]' ) # Using a JSON file path: contract = Contract(address, abi="abi.json") # Using a Python dictionary from JSON: contract = Contract( address, abi=[{"name":"foo","type":"fallback", "stateMutability":"nonpayable"}] ) ``` 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. On live networks, this history of deployments is saved; on local networks, this history lasts for the duration of your script. Let's say you previously deployed a smart contract called `MyContract` on the rinkeby test network. You could then refer back to it like so: ```python from ape import project, chain def main(): my_contract = chain.contracts.get_deployments(project.MyContract)[-1] ``` or ```python from ape import project def main(): my_contract = project.MyContract.deployments[-1] ``` `my_contract` will be of type `ContractInstance`. `get_deployments` returns a list of deployments you made of that contract type. ## Contract Interaction Then, after you have a contract instance, you can call methods on the contract. For example, let's say you have a Vyper contract containing some functions: ```python @pure @external def get_static_list() -> DynArray[uint256, 3]: return [1, 2, 3] @external def set_number(num: uint256): assert msg.sender == self.owner, "!authorized" self.prevNumber = self.myNumber self.myNumber = num ``` Notice the contract has both an external pure method and an external method that modifies state. In EVM languages, methods that modify state require a transaction to execute because they cost money. Modifying the storage of a contract requires gas and thus requires a sender with enough funding. Contract calls, on the other hand, are read-operations and do not cost anything. Thus, calls do not require specifying a `sender=` in Ape. At the RPC level, Ethereum calls are performed using the `eth_call` RPC and transactions are performed using the `eth_sendTransaction` or `eth_sendRawTransaction` RPCs. ### Transactions The following example demonstrates invoking a contract's method in Ape as a transaction. However, take note that there is a [separate guide](./transactions.html) which fully covers transactions in Ape. ```python from ape import accounts, Contract account = accounts.load("") contract = Contract("0x...") # Assume is deployed version of code above # Transaction: Invoke the `set_number()` function, which costs Ether receipt = contract.set_number(sender=account) assert not receipt.failed # The receipt contains data such as `gas_used`. print(receipt.gas_used) ``` Notice that transacting returns a [ReceiptAPI](../methoddocs/api.html#ape.api.transactions.ReceiptAPI) object which contains all the receipt data, such as `gas_used`. **NOTE**: If you need the `return_value` from a transaction, you have to either treat transaction as a call (see the section below!) or use a provider with tracing-features enabled (such as `ape-foundry` or `ape-geth`) and access the [return_value](../methoddocs/api.html#ape.api.transactions.ReceiptAPI.return_value) property on the receipt. ```python assert receipt.return_value == 123 ``` For more general information on transactions in the Ape framework, see [this guide](./transactions.html). ### Calls In the Vyper code at the beginning of this section, the function `get_static_list()` is decorated as `@pure` indicating that it's read-only. (Also in Vyper, `@view` methods are read-only). Since `get_static_list()` is read-only, we can successfully call it without a `sender=` kwarg; no funds are required. Here is an example of making a call by checking the result of `get_static_list()`: ```python from ape import accounts, Contract account = accounts.load("") contract = Contract("0x...") # CALL: A sender is not required for calls! assert contract.get_static_list() == [1, 2, 3] ``` ### Calling Transactions and Transacting Calls You can treat transactions as calls and vice-versa. For example, let's say we have a Solidity function: ```solidity function addBalance(uint256 new_bal) external returns(uint256) { balances[msg.sender] = new_bal; return balances[msg.sender]; } ``` To simulate the transaction without actually modifying any state, use the `.call` method from the contract transaction handler: ```python from ape import Contract contract = Contract("0x...") result = contract.addBalance.call(123) assert result == "123" # The return value gets forwarded from the contract. ``` Similarly, you may want to measure a call as if it were a transaction, in which case you can use the `.transact` attribute on the contract call handler: Given the Solidity function: ```solidity function getModifiedBalance() external view returns(uint256) { return balances[msg.sender] + 123; } ``` You can treat it like a transaction by doing: ```python from ape import accounts, Contract account = accounts.load("") contract = Contract("0x...") receipt = contract.getModifiedBalance.transact(sender=account) assert not receipt.failed # Transactions return `ReceiptAPI` objects. print(receipt.gas_used) # Analyze receipt gas from calls. ``` ### Default, Fallback, and Direct Calls To directly call an address, such as invoking a contract's `fallback` or `receive` method, call a contract instance directly: ```python from ape import Contract, accounts sender = accounts.load("") # NOTE: refers to your account alias! contract = Contract("0x123...") # Call the contract's fallback method. receipt = contract(sender=sender, gas=40000, data="0x123") ``` ### Private Transactions If you are using a provider that allows private mempool transactions, you are able to use the `private=True` kwarg to publish your transaction into a private mempool. For example, EVM providers likely will use the `eth_sendPrivateTransaction` RPC to achieve this. To send a private transaction, do the following: ```python receipt = contract.set_number(sender=dev, private=True) ``` The `private=True` is available on all contract interactions. ## Decoding and Encoding Inputs If you want to separately decode and encode inputs without sending a transaction or making a call, you can achieve this with Ape. If you know the method you want to use when decoding or encoding, you can call methods `encode_input()` or `decode_input()` on the method handler from a contract: ```python from ape import Contract # HexBytes(0x3fb5c1cb00000000000000000000000000000000000000000000000000000000000000de) contract = Contract("0x...") bytes_value = contract.my_method.encode_input(0, 1, 2) ``` In the example above, the bytes value returned contains the method ID selector prefix `3fb5c1c`. Alternatively, you can decode input: ```python from eth_pydantic_types import HexBytes from ape import Contract contract = Contract("0x...") selector_str, input_dict = contract.my_method.decode_input(HexBytes("0x123...")) ``` In the example above, `selector_str` is the string version of the method ID, e.g. `my_method(unit256,uint256)`. The input dict is a mapping of input names to their decoded values, e.g `{"foo": 2, "owner": "0x123..."}`. If an input does not have a name, its key is its stringified input index. If you don't know the method's ABI and you have calldata, you can use a `ContractInstance` or `ContractContainer` directly: ```python import ape # Fetch a contract contract = ape.Contract("0x...") # Alternative, use a contract container from ape.project # contract = ape.project.MyContract # Only works if unique amount of args. bytes_value = contract.encode_input(0, 1, 2, 4, 5) method_id, input_dict = contract.decode_input(bytes_value) ``` ## Contract Interface Introspection There may be times you need to figure out ABI selectors and method or event identifiers for a contract. A contract instance provides properties to make this easy. For instance, if you have a 4-byte hex method ID, you can return the ABI type for that method: ```python import ape usdc = ape.Contract("0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48") # ABI type for a hex method ID assert usdc.identifier_lookup['0x70a08231'].selector == 'balanceOf(address)' # Also, selectors from method and event signatures assert usdc.selector_identifiers["balances(address)"] == "0x27e235e3" # Or dump all selectors and IDs for identifier, abi_type in usdc.identifier_lookup.items(): print(identifier, abi_type) # 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef type='event' name='Transfer' inputs=... # ... ``` These include methods and error IDs, as well as event topics. ## Multi-Call and Multi-Transaction The `ape_ethereum` core plugin comes with a `multicall` module containing tools for interacting with the [multicall3 smart contract](https://github.com/mds1/multicall). Multicall allows you to group function calls and transactions into a single call or transaction. Here is an example of how you can use the multicall module: ```python import ape from ape_ethereum import multicall ADDRESSES = ("0xF4b8A02D4e8D76070bD7092B54D2cBbe90fa72e9", "0x80067013d7F7aF4e86b3890489AcAFe79F31a4Cb") POOLS = [ape.project.IPool.at(a) for a in ADDRESSES] def main(): # Use multi-call. call = multicall.Call() for pool in POOLS: call.add(pool.getReserves) print(list(call())) # Use multi-transaction. tx = multicall.Transaction() for pool in POOLS: tx.add(pool.ApplyDiscount, 123) acct = ape.accounts.load("signer") for result in tx(sender=acct): print(result) ```