-
-
Notifications
You must be signed in to change notification settings - Fork 131
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
test: test and document proxy system [APE-197] (#1507)
- Loading branch information
Showing
12 changed files
with
170 additions
and
35 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
# Proxy Contracts | ||
|
||
Ape is able to detect proxy contracts so that it uses the target interface when interacting with a contract. | ||
The following proxies are supporting in `ape-ethereum`: | ||
|
||
| Proxy Type | Short Description | | ||
| ------------ | --------------------------------- | | ||
| Minimal | EIP-1167 | | ||
| Standard | EIP-1967 | | ||
| Beacon | EIP-1967 | | ||
| UUPS | EIP-1822 | | ||
| Vyper | vyper \<0.2.9 create_forwarder_to | | ||
| Clones | 0xsplits clones | | ||
| Safe | Formerly Gnosis Safe | | ||
| OpenZeppelin | OZ Upgradable | | ||
| Delegate | EIP-897 | | ||
| ZeroAge | A minimal proxy | | ||
| SoladyPush0 | Uses PUSH0 | | ||
|
||
Proxy detection occurs when attempting to retrieve contract types in Ape. | ||
Ape uses various sources to find contract types, such as explorer APIs. | ||
See [this guide](./contracts.html) to learn more about initializing contracts. | ||
|
||
```python | ||
from ape import Contract | ||
|
||
my_contract = Contract("0x...") | ||
``` | ||
|
||
Ape will check the address you give it and detect if hosts a proxy contract. | ||
In the case where it determines the address is a proxy contract, it resolves the address of the implementation (every proxy is different) and returns the interface for the implementation contract. | ||
This allows you to still call methods as you normally do on proxy contracts. | ||
|
||
```python | ||
# `my_contract` address points to a proxy with no methods in the interface | ||
# However, Ape detected the implementation type and can find methods to call that way. | ||
my_contract.my_method(sender=account) | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
from enum import IntEnum | ||
|
||
from ethpm_types import ContractType | ||
from lazyasd import LazyObject # type: ignore | ||
|
||
from ape.api.networks import ProxyInfoAPI | ||
from ape.contracts import ContractContainer | ||
|
||
MINIMAL_PROXY_BYTES = ( | ||
"0x3d602d80600a3d3981f3363d3d373d3d3d363d73" | ||
"bebebebebebebebebebebebebebebebebebebebe5af43d82803e903d91602b57fd5bf3" | ||
) | ||
|
||
|
||
class ProxyType(IntEnum): | ||
Minimal = 0 # eip-1167 minimal proxy contract | ||
Standard = 1 # eip-1967 standard proxy storage slots | ||
Beacon = 2 # eip-1967 beacon proxy | ||
UUPS = 3 # # eip-1822 universal upgradeable proxy standard | ||
Vyper = 4 # vyper <0.2.9 create_forwarder_to | ||
Clones = 5 # 0xsplits clones | ||
GnosisSafe = 6 | ||
OpenZeppelin = 7 # openzeppelin upgradeability proxy | ||
Delegate = 8 # eip-897 delegate proxy | ||
ZeroAge = 9 # a more-minimal proxy | ||
SoladyPush0 = 10 # solady push0 minimal proxy | ||
|
||
|
||
class ProxyInfo(ProxyInfoAPI): | ||
type: ProxyType | ||
|
||
|
||
def _make_minimal_proxy() -> ContractContainer: | ||
bytecode = {"bytecode": MINIMAL_PROXY_BYTES} | ||
contract_type = ContractType(abi=[], deploymentBytecode=bytecode) | ||
return ContractContainer(contract_type=contract_type) | ||
|
||
|
||
minimal_proxy = LazyObject(_make_minimal_proxy, globals(), "minimal_proxy") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
{"abi":[{"inputs":[{"internalType":"address","name":"_addr","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"implementation","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"}],"ast":{"ast_type":"SourceUnit","children":[{"ast_type":"ImportDirective","children":[],"classification":0,"col_offset":-1,"end_col_offset":-1,"end_lineno":-1,"lineno":-1,"src":{"contract_id":1,"jump_code":"","length":58}},{"ast_type":"ContractDefinition","children":[{"ast_type":"InheritanceSpecifier","children":[{"ast_type":"IdentifierPath","children":[],"classification":0,"col_offset":-1,"end_col_offset":-1,"end_lineno":-1,"lineno":-1,"name":"IBeacon","src":{"contract_id":1,"jump_code":"","length":7,"start":79}}],"classification":0,"col_offset":-1,"end_col_offset":-1,"end_lineno":-1,"lineno":-1,"src":{"contract_id":1,"jump_code":"","length":7,"start":79}},{"ast_type":"VariableDeclaration","children":[{"ast_type":"ElementaryTypeName","children":[],"classification":0,"col_offset":-1,"end_col_offset":-1,"end_lineno":-1,"lineno":-1,"name":"address","src":{"contract_id":1,"jump_code":"","length":7,"start":93}}],"classification":0,"col_offset":-1,"end_col_offset":-1,"end_lineno":-1,"lineno":-1,"name":"addr","src":{"contract_id":1,"jump_code":"","length":12,"start":93}},{"ast_type":"FunctionDefinition","children":[{"ast_type":"Block","children":[{"ast_type":"ExpressionStatement","children":[{"ast_type":"Assignment","children":[{"ast_type":"Identifier","children":[],"classification":0,"col_offset":-1,"end_col_offset":-1,"end_lineno":-1,"lineno":-1,"name":"addr","src":{"contract_id":1,"jump_code":"","length":4,"start":149}},{"ast_type":"Identifier","children":[],"classification":0,"col_offset":-1,"end_col_offset":-1,"end_lineno":-1,"lineno":-1,"name":"_addr","src":{"contract_id":1,"jump_code":"","length":5,"start":156}}],"classification":0,"col_offset":-1,"end_col_offset":-1,"end_lineno":-1,"lineno":-1,"src":{"contract_id":1,"jump_code":"","length":12,"start":149}}],"classification":0,"col_offset":-1,"end_col_offset":-1,"end_lineno":-1,"lineno":-1,"src":{"contract_id":1,"jump_code":"","length":12,"start":149}}],"classification":0,"col_offset":-1,"end_col_offset":-1,"end_lineno":-1,"lineno":-1,"src":{"contract_id":1,"jump_code":"","length":29,"start":139}},{"ast_type":"ParameterList","children":[{"ast_type":"VariableDeclaration","children":[{"ast_type":"ElementaryTypeName","children":[],"classification":0,"col_offset":-1,"end_col_offset":-1,"end_lineno":-1,"lineno":-1,"name":"address","src":{"contract_id":1,"jump_code":"","length":7,"start":124}}],"classification":0,"col_offset":-1,"end_col_offset":-1,"end_lineno":-1,"lineno":-1,"name":"_addr","src":{"contract_id":1,"jump_code":"","length":13,"start":124}}],"classification":0,"col_offset":-1,"end_col_offset":-1,"end_lineno":-1,"lineno":-1,"src":{"contract_id":1,"jump_code":"","length":15,"start":123}},{"ast_type":"ParameterList","children":[],"classification":0,"col_offset":-1,"end_col_offset":-1,"end_lineno":-1,"lineno":-1,"src":{"contract_id":1,"jump_code":"","start":139}}],"classification":1,"col_offset":-1,"end_col_offset":-1,"end_lineno":-1,"lineno":-1,"name":"","src":{"contract_id":1,"jump_code":"","length":56,"start":112}},{"ast_type":"FunctionDefinition","children":[{"ast_type":"Block","children":[{"ast_type":"Return","children":[{"ast_type":"Identifier","children":[],"classification":0,"col_offset":-1,"end_col_offset":-1,"end_lineno":-1,"lineno":-1,"name":"addr","src":{"contract_id":1,"jump_code":"","length":4,"start":249}}],"classification":0,"col_offset":-1,"end_col_offset":-1,"end_lineno":-1,"lineno":-1,"src":{"contract_id":1,"jump_code":"","length":11,"start":242}}],"classification":0,"col_offset":-1,"end_col_offset":-1,"end_lineno":-1,"lineno":-1,"src":{"contract_id":1,"jump_code":"","length":28,"start":232}},{"ast_type":"ParameterList","children":[],"classification":0,"col_offset":-1,"end_col_offset":-1,"end_lineno":-1,"lineno":-1,"src":{"contract_id":1,"jump_code":"","length":2,"start":197}},{"ast_type":"ParameterList","children":[{"ast_type":"VariableDeclaration","children":[{"ast_type":"ElementaryTypeName","children":[],"classification":0,"col_offset":-1,"end_col_offset":-1,"end_lineno":-1,"lineno":-1,"name":"address","src":{"contract_id":1,"jump_code":"","length":7,"start":223}}],"classification":0,"col_offset":-1,"end_col_offset":-1,"end_lineno":-1,"lineno":-1,"name":"","src":{"contract_id":1,"jump_code":"","length":7,"start":223}}],"classification":0,"col_offset":-1,"end_col_offset":-1,"end_lineno":-1,"lineno":-1,"src":{"contract_id":1,"jump_code":"","length":9,"start":222}}],"classification":1,"col_offset":-1,"end_col_offset":-1,"end_lineno":-1,"lineno":-1,"name":"implementation","src":{"contract_id":1,"jump_code":"","length":86,"start":174}}],"classification":0,"col_offset":-1,"end_col_offset":-1,"end_lineno":-1,"lineno":-1,"name":"Beacon","src":{"contract_id":1,"jump_code":"","length":202,"start":60}}],"classification":0,"col_offset":-1,"end_col_offset":-1,"end_lineno":-1,"lineno":-1,"src":{"contract_id":1,"jump_code":"","length":263}},"contractName":"Beacon","deploymentBytecode":{"bytecode":"0x608060405234801561001057600080fd5b5060405161011438038061011483398101604081905261002f91610054565b600080546001600160a01b0319166001600160a01b0392909216919091179055610084565b60006020828403121561006657600080fd5b81516001600160a01b038116811461007d57600080fd5b9392505050565b6082806100926000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c80635c60da1b14602d575b600080fd5b600054604080516001600160a01b039092168252519081900360200190f3fea2646970667358221220f690a74086006b293a82ea58c45ab6814cd9d5774d31a6cd70c9a398b85ee28864736f6c63430008140033"},"devdoc":{"kind":"dev","methods":{"implementation()":{"details":"Must return an address that can be used as a delegate call target. {BeaconProxy} will check that this address is a contract."}},"version":1},"runtimeBytecode":{"bytecode":"0x6080604052348015600f57600080fd5b506004361060285760003560e01c80635c60da1b14602d575b600080fd5b600054604080516001600160a01b039092168252519081900360200190f3fea2646970667358221220f690a74086006b293a82ea58c45ab6814cd9d5774d31a6cd70c9a398b85ee28864736f6c63430008140033"},"sourceId":"Beacon.sol","sourcemap":"60:202:1:-:0;;;112:56;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;149:4;:12;;-1:-1:-1;;;;;;149:12:1;-1:-1:-1;;;;;149:12:1;;;;;;;;;;60:202;;14:290:2;84:6;137:2;125:9;116:7;112:23;108:32;105:52;;;153:1;150;143:12;105:52;179:16;;-1:-1:-1;;;;;224:31:2;;214:42;;204:70;;270:1;267;260:12;204:70;293:5;14:290;-1:-1:-1;;;14:290:2:o;:::-;60:202:1;;;;;;","userdoc":{"kind":"user","methods":{},"version":1}} |
1 change: 1 addition & 0 deletions
1
tests/functional/data/contracts/ethereum/local/beacon_proxy.json
Large diffs are not rendered by default.
Oops, something went wrong.
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
import pytest | ||
from ethpm_types import HexBytes | ||
|
||
from ape.contracts import ContractContainer | ||
from ape_ethereum.proxies import ProxyType | ||
from ape_ethereum.proxies import minimal_proxy as minimal_proxy_container | ||
from tests.conftest import geth_process_test | ||
|
||
|
||
@pytest.fixture | ||
def minimal_proxy(owner): | ||
return owner.deploy(minimal_proxy_container) | ||
|
||
|
||
@pytest.fixture | ||
def standard_proxy(owner, get_contract_type, geth_vyper_contract): | ||
_type = get_contract_type("eip1967") | ||
contract = ContractContainer(_type) | ||
target = geth_vyper_contract.address | ||
return owner.deploy(contract, target, HexBytes("")) | ||
|
||
|
||
@pytest.fixture | ||
def beacon(owner, get_contract_type, geth_provider, vyper_contract_instance): | ||
_type = get_contract_type("beacon") | ||
contract = ContractContainer(_type) | ||
return owner.deploy(contract, vyper_contract_instance) | ||
|
||
|
||
@pytest.fixture | ||
def beacon_proxy(owner, get_contract_type, beacon, geth_provider): | ||
_type = get_contract_type("beacon_proxy") | ||
contract = ContractContainer(_type) | ||
return owner.deploy(contract, beacon, HexBytes("")) | ||
|
||
|
||
def test_minimal_proxy(ethereum, minimal_proxy): | ||
actual = ethereum.get_proxy_info(minimal_proxy.address) | ||
assert actual is not None | ||
assert actual.type == ProxyType.Minimal | ||
# It is the placeholder value still. | ||
assert actual.target == "0xBEbeBeBEbeBebeBeBEBEbebEBeBeBebeBeBebebe" | ||
|
||
|
||
@geth_process_test | ||
def test_standard_proxy(ethereum, standard_proxy, geth_provider, geth_vyper_contract): | ||
""" | ||
NOTE: Geth is used here because EthTester does not implement getting storage slots. | ||
""" | ||
actual = ethereum.get_proxy_info(standard_proxy.address) | ||
assert actual is not None | ||
assert actual.type == ProxyType.Standard | ||
assert actual.target == geth_vyper_contract.address | ||
|
||
|
||
@geth_process_test | ||
def test_beacon_proxy(ethereum, beacon_proxy, geth_provider, vyper_contract_instance): | ||
""" | ||
NOTE: Geth is used here because EthTester does not implement getting storage slots. | ||
""" | ||
actual = ethereum.get_proxy_info(beacon_proxy.address) | ||
assert actual is not None | ||
assert actual.type == ProxyType.Beacon | ||
assert actual.target == vyper_contract_instance.address |