Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: extract proxy factory from gnosis safe and test it #121

Merged
merged 8 commits into from
Oct 2, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -347,4 +347,5 @@ Pipfile.lock
# ignore packages/fetchai, but useful to have them locally
# so to be able to run tests
packages/fetchai
!packages/valory/contracts/gnosis_safe/build
!packages/valory/contracts/gnosis_safe/build
!packages/valory/contracts/gnosis_safe_proxy_factory/build
7 changes: 4 additions & 3 deletions packages/hashes.csv
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
valory/agents/counter,QmbEhbjyHS1W3vZnWgpKoqMr2ugKLXp5d84H8cSNBDmzEc
valory/agents/counter_client,QmNzaAny4auEo4cDXq6pM7kdyY8HZ35wgCiXcinqs3e39e
valory/agents/price_estimation,QmSH2mV2D3Qakyzeozd2sAYScf56S4E6ZQkxKFAhuBHXqh
valory/agents/price_estimation,QmZcmxo49NGEca2Hjp7eT4kECAiiDBGjeFrjaAXULgeVdH
valory/connections/abci,QmRYgXTxs4sqnBMF6GufZtczTiKv8LLk8ASjU3UefJArwh
valory/contracts/gnosis_safe,QmZ2J3sUtTKaXriLepUAzuV1WscRn1ykAcUChSWidETQwk
valory/contracts/gnosis_safe,QmUajWwLd1pv6VTW1AmiaShk8sm6fwPYE7meBuc2Q1mB5F
valory/contracts/gnosis_safe_proxy_factory,QmWfgDNYxYx6PGDi9Cbjt5nnoT2YFMgPFAUhSAUZAurqhT
valory/protocols/abci,QmY9LyAMRBPSUiWnC821ZMczFLpw7nXsQKVqvLK14gj2dv
valory/skills/abstract_abci,QmQeyVZThnkfxmXkUjBa1T55Kixssz3Lv82upCH5wHtayb
valory/skills/abstract_round_abci,QmVSzSFKHQUQTkKjubyN4MicW5BK2MjpR2D9cNr5dNUAmm
valory/skills/counter,QmSmt61iahp11PaYT6oNEzijMU8n15b1A4fty6HXmxi2gW
valory/skills/counter_client,QmYcfMC1nzgeMMys5NHUXJTB3KKxwJFBM4tryBMpNggEHt
valory/skills/price_estimation_abci,QmexxkTzbG1ADjZdT6yQ5YM5fv9Mj2cmSi5cAfePV2DmGt
valory/skills/price_estimation_abci,QmQK8oyGnnrwhYxBAwpiehoaoWRPSM6P4xP6EwKmLdgfRW
4 changes: 3 additions & 1 deletion packages/valory/agents/price_estimation/aea-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ connections:
- fetchai/http_client:0.22.0
- fetchai/ledger:0.18.0
- valory/abci:0.1.0
contracts: []
contracts:
- valory/gnosis_safe:0.1.0
- valory/gnosis_safe_proxy_factory:0.1.0
protocols:
- fetchai/contract_api:1.0.0
- fetchai/default:1.0.0
Expand Down
2 changes: 1 addition & 1 deletion packages/valory/contracts/gnosis_safe/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,4 @@
#
# ------------------------------------------------------------------------------

"""This module contains the support resources for the Fetch oracle contract."""
"""This module contains the support resources for the gnosis_safe (GnosisSafeL2) contract."""
105 changes: 24 additions & 81 deletions packages/valory/contracts/gnosis_safe/contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,18 @@
import logging
import secrets
from enum import Enum
from pathlib import Path
from typing import Any, Dict, List, Optional, Tuple, cast

from aea.common import JSONLike
from aea.configurations.base import PublicId
from aea.contracts.base import Contract, contract_registry
from aea.contracts.base import Contract
from aea.crypto.base import LedgerApi
from aea_ledger_ethereum import EthereumApi
from eth_typing import ChecksumAddress, HexAddress, HexStr
from hexbytes import HexBytes
from packaging.version import Version
from py_eth_sig_utils.eip712 import encode_typed_data
from web3.types import Nonce, TxParams, Wei
from web3.types import TxParams, Wei


PUBLIC_ID = PublicId.from_str("valory/gnosis_safe:0.1.0")
Expand Down Expand Up @@ -113,13 +112,17 @@ def get_deploy_transaction(
owners = kwargs.pop("owners")
threshold = kwargs.pop("threshold")
salt_nonce = kwargs.pop("salt_nonce", None)
gas = kwargs.pop("gas", None)
gas_price = kwargs.pop("gas_price", None)
ledger_api = cast(EthereumApi, ledger_api)
tx_params, contract_address = cls._get_deploy_transaction(
ledger_api,
deployer_address,
owners=owners,
threshold=threshold,
salt_nonce=salt_nonce,
gas=gas,
gas_price=gas_price,
)
result = dict(cast(Dict, tx_params))
# piggyback the contract address
Expand All @@ -134,6 +137,8 @@ def _get_deploy_transaction( # pylint: disable=too-many-locals,too-many-argumen
owners: List[str],
threshold: int,
salt_nonce: Optional[int] = None,
gas: Optional[int] = None,
gas_price: Optional[int] = None,
) -> Tuple[TxParams, str]:
"""
Get the deployment transaction of the new Safe.
Expand All @@ -146,6 +151,8 @@ def _get_deploy_transaction( # pylint: disable=too-many-locals,too-many-argumen
:param owners: a list of public keys
:param threshold: the signature threshold
:param salt_nonce: Use a custom nonce for the deployment. Defaults to random nonce.
:param gas: gas cost
:param gas_price: Gas price that should be used for the payment calculation

:return: transaction params and contract address
"""
Expand Down Expand Up @@ -194,9 +201,7 @@ def _get_deploy_transaction( # pylint: disable=too-many-locals,too-many-argumen
fallback_handler,
salt_nonce,
)
safe_contract = cls.get_safe_contract_instance(
ledger_api, safe_contract_address
)
safe_contract = cls.get_instance(ledger_api, safe_contract_address)
safe_creation_tx_data = HexBytes(
safe_contract.functions.setup(
owners,
Expand All @@ -221,89 +226,27 @@ def _get_deploy_transaction( # pylint: disable=too-many-locals,too-many-argumen
)
if nonce is None:
raise ValueError("No nonce returned.")
tx_params, contract_address = cls._build_tx_deploy_proxy_contract_with_nonce(
# TOFIX: lazy import until contract dependencies supported in AEA
from packages.valory.contracts.gnosis_safe_proxy_factory.contract import ( # pylint: disable=import-outside-toplevel
GnosisSafeProxyFactoryContract,
)

(
tx_params,
contract_address,
) = GnosisSafeProxyFactoryContract.build_tx_deploy_proxy_contract_with_nonce(
ledger_api,
Comment on lines +237 to 238
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

contracts packages can depend on others. The framework doesn't currently account for this. We need to add an issue on aea repo and support this!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

proxy_factory_address,
safe_contract_address,
account_address,
safe_creation_tx_data,
salt_nonce,
nonce=nonce,
gas=gas,
gas_price=gas_price,
)
return tx_params, contract_address

@classmethod
def get_safe_contract_instance(
cls, ledger_api: LedgerApi, safe_contract_address: str
) -> Any:
"""
Get an instance of the safe contract.

:param ledger_api: ledger API object
:param safe_contract_address: the address of the safe contract
:return: an instance of the safe contract
"""
# hack as we currently cannot register multiple contracts :/
contract = contract_registry.make(str(cls.contract_id))
full_path = Path(
str(contract.configuration.directory), "build/GnosisSafe_V1_3_0.json"
)
safe_contract_interface = ledger_api.load_contract_interface(full_path)
safe_contract = ledger_api.get_contract_instance(
safe_contract_interface, safe_contract_address
)
return safe_contract

@classmethod
def _build_tx_deploy_proxy_contract_with_nonce( # pylint: disable=too-many-arguments
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extracted to the contract gnosis_safe_proxy_factory

cls,
ledger_api: LedgerApi,
proxy_factory_address: str,
master_copy: str,
address: str,
initializer: bytes,
salt_nonce: int,
gas: Optional[int] = None,
gas_price: Optional[int] = None,
nonce: Optional[int] = None,
) -> Tuple[TxParams, str]:
"""
Deploy proxy contract via Proxy Factory using `createProxyWithNonce` (create2)

:param ledger_api: ledger API object
:param proxy_factory_address: the address of the proxy factory
:param address: Ethereum address
:param master_copy: Address the proxy will point at
:param initializer: Data for safe creation
:param salt_nonce: Uint256 for `create2` salt
:param gas: Gas
:param gas_price: Gas Price
:param nonce: Nonce
:return: Tuple(tx-hash, tx, deployed contract address)
"""
proxy_factory_contract = cls.get_instance(ledger_api, proxy_factory_address)

create_proxy_fn = proxy_factory_contract.functions.createProxyWithNonce(
master_copy, initializer, salt_nonce
)

tx_parameters = TxParams({"from": address})
contract_address = create_proxy_fn.call(tx_parameters)

if gas_price is not None:
tx_parameters["gasPrice"] = Wei(gas_price)

if gas is not None:
tx_parameters["gas"] = Wei(gas)

if nonce is not None:
tx_parameters["nonce"] = Nonce(nonce)

transaction_dict = create_proxy_fn.buildTransaction(tx_parameters)
# Auto estimation of gas does not work. We use a little more gas just in case
transaction_dict["gas"] = Wei(transaction_dict["gas"] + 50000)
return transaction_dict, contract_address

@classmethod
def get_raw_safe_transaction_hash( # pylint: disable=too-many-arguments,too-many-locals
cls,
Expand Down Expand Up @@ -344,7 +287,7 @@ def get_raw_safe_transaction_hash( # pylint: disable=too-many-arguments,too-man
:param chain_id: Ethereum network chain_id is used in hash calculation for Safes >= 1.3.0. If not provided, it will be retrieved from the provided ethereum_client
:return: the hash of the raw Safe transaction
"""
safe_contract = cls.get_safe_contract_instance(ledger_api, contract_address)
safe_contract = cls.get_instance(ledger_api, contract_address)
if safe_nonce is None:
safe_nonce = safe_contract.functions.nonce().call(block_identifier="latest")
if safe_version is None:
Expand Down Expand Up @@ -459,7 +402,7 @@ def get_raw_safe_transaction( # pylint: disable=too-many-arguments,too-many-loc
signatures += signature_bytes
# Packed signature data ({bytes32 r}{bytes32 s}{uint8 v})

safe_contract = cls.get_safe_contract_instance(ledger_api, contract_address)
safe_contract = cls.get_instance(ledger_api, contract_address)

if safe_nonce is None:
safe_nonce = safe_contract.functions.nonce().call(block_identifier="latest")
Expand Down
9 changes: 4 additions & 5 deletions packages/valory/contracts/gnosis_safe/contract.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,17 @@ name: gnosis_safe
author: valory
version: 0.1.0
type: contract
description: Gnosis Safe contract
description: Gnosis Safe (GnosisSafeL2) contract
license: Apache-2.0
aea_version: '>=1.0.0, <2.0.0'
fingerprint:
README.md: Qmd5NcJnij2d19rhtNJgsTSBU7ErTdYVH2c621j6TKN7Qz
__init__.py: QmS2MEY7dgz18icnFs975zxCxMmYQzVkeFfe2TjnTDeDiL
__init__.py: QmWLx43KXUA8iq4uRo1VDhFPqd6dFF7xfdiMpQLAoBBMoD
build/GnosisSafe_V1_3_0.json: QmafMmPcVqiTLykozgjGwNL2S8b1g5bmgMP3z6EdecgMYh
build/ProxyFactory_V1_3_0.json: QmRKHfF1hYrrSZNZmec2AS3xSPWdB29uyEWAYdAS5cKHdL
contract.py: QmXXR3N6Uu5mqqwDhoNaVfdo6xsz5P69V9vZC28LEWoHJg
contract.py: QmWTLNL8a7Vqsyw7wwCzyiJ9gHsoYQBc8TnJzJ4Qecpybp
fingerprint_ignore_patterns: []
class_name: GnosisSafeContract
contract_interface_paths:
ethereum: build/ProxyFactory_V1_3_0.json
ethereum: build/GnosisSafe_V1_3_0.json
dependencies:
py-eth-sig-utils: {}
6 changes: 6 additions & 0 deletions packages/valory/contracts/gnosis_safe_proxy_factory/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Gnosis Safe Proxy Factory contract

## Description

## Functions

20 changes: 20 additions & 0 deletions packages/valory/contracts/gnosis_safe_proxy_factory/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# ------------------------------------------------------------------------------
#
# Copyright 2021 Valory AG
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# ------------------------------------------------------------------------------

"""This module contains the support resources for the gnosis_safe_proxy_contract (GnosisSafeProxyFactory) contract."""
Loading