Update tests to new TACo nucypher contracts

This commit is aimed to update test to the following changes on
nucypher-contracts repo:
  - Update SimplePREApplication with TACoApplication
  - Replace ThresholdStakingForPREApplicationMock contract with
    ThresholdStakingForTACoApplicationMock
  - Use the new Coordinator contract
  - Add the deployment of StakeInfo contract

The use of these new contracts made necessary the following changes:
  - Add the deployment of a new Token for ritual fees: RitualToken. This
    contract has been added as local deployment.
  - Add the deployment of local contract ConditionNFT

Additionally, this commit updates the key character for arguments in
ape-config.yaml. From '::variable::' to <variable>, which results in a
more legible code.

Finally, this commit solves some linting and formatting issues.

Co-authored-by: LunarBytes <kieran@nucypher.com>
pull/3213/head
Manuel Montenegro 2023-08-29 19:31:28 +02:00 committed by derekpierre
parent 5bdb6a8709
commit fe75f75998
8 changed files with 177 additions and 42 deletions

View File

@ -38,8 +38,8 @@ from nucypher.blockchain.eth.constants import (
ETH_ADDRESS_BYTE_LENGTH,
NUCYPHER_TOKEN_CONTRACT_NAME,
NULL_ADDRESS,
PRE_APPLICATION_CONTRACT_NAME,
SUBSCRIPTION_MANAGER_CONTRACT_NAME,
TACO_APPLICATION_CONTRACT_NAME,
)
from nucypher.blockchain.eth.decorators import contract_api
from nucypher.blockchain.eth.interfaces import BlockchainInterfaceFactory
@ -384,7 +384,7 @@ class AdjudicatorAgent(EthereumContractAgent):
class PREApplicationAgent(EthereumContractAgent):
contract_name: str = PRE_APPLICATION_CONTRACT_NAME
contract_name: str = TACO_APPLICATION_CONTRACT_NAME
DEFAULT_PROVIDERS_PAGINATION_SIZE_LIGHT_NODE = int(os.environ.get(NUCYPHER_ENVVAR_STAKING_PROVIDERS_PAGINATION_SIZE_LIGHT_NODE, default=30))
DEFAULT_PROVIDERS_PAGINATION_SIZE = int(os.environ.get(NUCYPHER_ENVVAR_STAKING_PROVIDERS_PAGINATION_SIZE, default=1000))
@ -843,7 +843,7 @@ class ContractAgency:
if name == NUCYPHER_TOKEN_CONTRACT_NAME:
# TODO: Perhaps rename NucypherTokenAgent
name = "NucypherToken"
if name == PRE_APPLICATION_CONTRACT_NAME:
if name == TACO_APPLICATION_CONTRACT_NAME:
name = "PREApplication" # TODO not needed once full PRE Application is used
agent_name = f"{name}Agent"
return agent_name

View File

@ -4,20 +4,20 @@
# Contract Names
#
DISPATCHER_CONTRACT_NAME = 'Dispatcher'
NUCYPHER_TOKEN_CONTRACT_NAME = 'NuCypherToken'
STAKING_ESCROW_CONTRACT_NAME = 'StakingEscrow'
STAKING_ESCROW_STUB_CONTRACT_NAME = 'StakingEscrowStub'
ADJUDICATOR_CONTRACT_NAME = 'Adjudicator'
PRE_APPLICATION_CONTRACT_NAME = 'SimplePREApplication' # TODO: Use the real PREApplication
SUBSCRIPTION_MANAGER_CONTRACT_NAME = 'SubscriptionManager'
DISPATCHER_CONTRACT_NAME = "Dispatcher"
NUCYPHER_TOKEN_CONTRACT_NAME = "NuCypherToken"
STAKING_ESCROW_CONTRACT_NAME = "StakingEscrow"
STAKING_ESCROW_STUB_CONTRACT_NAME = "StakingEscrowStub"
ADJUDICATOR_CONTRACT_NAME = "Adjudicator"
TACO_APPLICATION_CONTRACT_NAME = "TACoApplication" # TODO: Use the real PREApplication
SUBSCRIPTION_MANAGER_CONTRACT_NAME = "SubscriptionManager"
NUCYPHER_CONTRACT_NAMES = (
NUCYPHER_TOKEN_CONTRACT_NAME,
STAKING_ESCROW_CONTRACT_NAME,
ADJUDICATOR_CONTRACT_NAME,
DISPATCHER_CONTRACT_NAME,
PRE_APPLICATION_CONTRACT_NAME,
TACO_APPLICATION_CONTRACT_NAME,
SUBSCRIPTION_MANAGER_CONTRACT_NAME
)

View File

@ -20,19 +20,35 @@ solidity:
deployments:
ethereum:
local:
- contract_type: RitualToken
address: '0x1e59ce931B4CFea3fe4B875411e280e173cB7A9C' # test account at index 0
total_supply: 1000000000000000000000000000
- contract_type: TToken
address: '0x1e59ce931B4CFea3fe4B875411e280e173cB7A9C' # test account at index 0
total_supply: 1000000000000000000000000000
- contract_type: NuCypherToken
address: '0x1e59ce931B4CFea3fe4B875411e280e173cB7A9C' # test account at index 0
nu_token_supply: 1_000_000_000
- contract_type: SimplePREApplication
- contract_type: StakeInfo
address: '0x1e59ce931B4CFea3fe4B875411e280e173cB7A9C' # test account at index 0
threshold_staking: '::ThresholdStakingForPREApplicationMock.address::'
updaters:
- <address.0>
- contract_type: TACoApplication
address: '0x1e59ce931B4CFea3fe4B875411e280e173cB7A9C' # test account at index 0
t_token: <TToken.address>
threshold_staking: <ThresholdStakingForTACoApplicationMock.address>
pre_min_authorization: 40000000000000000000000
pre_min_operator_seconds: 86400 # one day in seconds
reward_duration: 604800
deauthorization_duration: 5184000
- contract_type: Coordinator
address: '0x1e59ce931B4CFea3fe4B875411e280e173cB7A9C' # test account at index 0
app: '::SimplePREApplication.address::'
stake_info: <StakeInfo.address>
ritual_timeout: 3600
max_dkg_size: 8
admin: <address.0>
currency: <RitualToken.address>
app: <TACoApplication.address>
test:
mnemonic: test test test test test test test test test test test junk

View File

@ -67,11 +67,11 @@ def erc20_evm_condition_balanceof(testerchain, test_registry):
@pytest.fixture
def erc721_contract(accounts, project, test_registry):
def erc721_contract(accounts, project):
account = accounts[0]
# deploy contract
deployed_contract = account.deploy(project.ConditionNFT)
deployed_contract = project.ConditionNFT
# mint nft with token id = 1
deployed_contract.mint(account.address, 1, sender=account)

View File

@ -65,6 +65,11 @@ def mock_multichain_configuration(module_mocker, testerchain):
@pytest.fixture(scope='session', autouse=True)
def test_contracts(project):
return project.contracts
@pytest.fixture(scope="session", autouse=True)
def nucypher_contracts(project):
nucypher_contracts_dependency_api = project.dependencies["nucypher-contracts"]
# simply use first entry - could be from github ('main') or local ('local')
@ -74,9 +79,11 @@ def nucypher_contracts(project):
@pytest.fixture(scope='module', autouse=True)
def deploy_contracts(nucypher_contracts, accounts):
def deploy_contracts(nucypher_contracts, test_contracts, accounts):
deployments = ape_deploy_contracts(
nucypher_contracts=nucypher_contracts, accounts=accounts
nucypher_contracts=nucypher_contracts,
test_contracts=test_contracts,
accounts=accounts,
)
return deployments

View File

@ -0,0 +1,14 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
/**
* @notice Contract for testing the application contract
*/
contract RitualToken is ERC20("RitualToken", "RT") {
constructor(uint256 _totalSupplyOfTokens) {
_mint(msg.sender, _totalSupplyOfTokens);
}
}

View File

@ -17,7 +17,11 @@ from nucypher.config.constants import (
#
MOCK_STAKING_CONTRACT_NAME = 'ThresholdStakingForPREApplicationMock'
MOCK_STAKING_CONTRACT_NAME = "ThresholdStakingForTACoApplicationMock"
RITUAL_TOKEN = "RitualToken"
T_TOKEN = "TToken"
STAKE_INFO = "StakeInfo"
CONDITION_NFT = "ConditionNFT"
#
# Ursula

View File

@ -1,10 +1,14 @@
import json
from copy import deepcopy
from pathlib import Path
from typing import Any, Dict, Tuple
from typing import Dict, List, Tuple, Union
from ape import config as ape_config
from ape import project
from ape.api import AccountAPI, DependencyAPI
from ape.contracts.base import ContractInstance
from ape_test.accounts import TestAccount
from eth_typing import ChecksumAddress
from eth_utils import to_checksum_address
from nucypher.blockchain.eth.agents import (
@ -14,46 +18,120 @@ from nucypher.blockchain.eth.agents import (
SubscriptionManagerAgent,
)
from nucypher.blockchain.eth.registry import InMemoryContractRegistry
from tests.constants import MOCK_STAKING_CONTRACT_NAME
from tests.constants import (
CONDITION_NFT,
MOCK_STAKING_CONTRACT_NAME,
RITUAL_TOKEN,
STAKE_INFO,
T_TOKEN,
)
# order sensitive
_CONTRACTS_TO_DEPLOY_ON_TESTERCHAIN = (
RITUAL_TOKEN,
T_TOKEN,
NucypherTokenAgent.contract_name,
STAKE_INFO,
MOCK_STAKING_CONTRACT_NAME,
PREApplicationAgent.contract_name,
SubscriptionManagerAgent.contract_name,
CoordinatorAgent.contract_name,
CONDITION_NFT,
)
VARIABLE_PREFIX_SYMBOL = "<"
VARIABLE_SUFIX_SYMBOL = ">"
def get_ape_project_build_path(project) -> Path:
build_path = Path(project.path) / '.build'
return build_path
def _is_variable(param: Union[str, int, List[Union[str, int]]]) -> bool:
"""Check if param is a ape-config variable"""
return isinstance(param, str) and (
param.startswith(VARIABLE_PREFIX_SYMBOL)
and param.endswith(VARIABLE_SUFIX_SYMBOL)
)
def _resolve_variable(
param: str,
contract_name: str,
deployments: Dict[str, ContractInstance],
accounts: List[TestAccount],
) -> Union[ChecksumAddress, str, int]:
"""Resolve a ape-config variable to a literal"""
dependency_expression = param.strip(VARIABLE_PREFIX_SYMBOL).strip(
VARIABLE_SUFIX_SYMBOL
)
dependency_name, attribute_name = dependency_expression.split(".")
if dependency_name == "address":
try:
account = accounts[int(attribute_name)]
except ValueError:
raise ValueError(
f"Ape account must be accessed by an index; got '{attribute_name}'."
)
address = ChecksumAddress(account.address)
return address
try:
param = getattr(deployments[dependency_name], attribute_name)
except KeyError:
raise ValueError(f"Contract {contract_name} not found in deployments")
except AttributeError:
raise ValueError(f"Attribute {attribute_name} not found in {dependency_name}")
return param
def process_deployment_params(
contract_name, params, deployments, symbol: str = "::"
) -> Dict[str, Any]:
contract_name: str,
params: Dict[str, Union[str, int, list]],
deployments: Dict[str, ContractInstance],
accounts: List[TestAccount],
) -> Dict[str, Union[ChecksumAddress, str, int]]:
"""Process deployment params for a contract."""
processed_params = dict()
for k, v in params.items():
if isinstance(v, str) and (v.startswith(symbol) and v.endswith(symbol)):
dependency_expression = v.strip(symbol)
dependency_name, attribute_name = dependency_expression.split(".")
try:
v = getattr(deployments[dependency_name], attribute_name)
except KeyError:
raise ValueError(f"Contract {contract_name} not found in deployments")
except AttributeError:
raise ValueError(
f"Attribute {attribute_name} not found in {dependency_name}"
)
processed_params[k] = v
for param_name, param_value in params.items():
if _is_variable(param_value):
param_value = _resolve_variable(
param=param_value,
contract_name=contract_name,
deployments=deployments,
accounts=accounts,
)
processed_params[param_name] = param_value
continue
elif isinstance(param_value, list):
value_list = list()
for param in param_value:
if _is_variable(param):
param = _resolve_variable(
param=param,
contract_name=contract_name,
deployments=deployments,
accounts=accounts,
)
value_list.append(param)
processed_params[param_name] = value_list
continue
else:
# this parameter is a literal
processed_params[param_name] = param_value
continue
return processed_params
def get_deployment_params(
contract_name, config, accounts, deployments
contract_name: str,
config: Dict[str, Union[str, list]],
accounts: List[TestAccount],
deployments: Dict[str, ContractInstance],
) -> Tuple[Dict, AccountAPI]:
"""
Get deployment params for a contract.
@ -64,14 +142,23 @@ def get_deployment_params(
deployer_address = accounts[params.pop("address")]
name = params.pop("contract_type")
if name == contract_name:
params = process_deployment_params(contract_name, params, deployments)
params = process_deployment_params(
contract_name=contract_name,
params=params,
deployments=deployments,
accounts=accounts,
)
return params, deployer_address
else:
# there are no deployment params for this contract; default to account at index 0
return dict(), accounts[0]
def deploy_contracts(nucypher_contracts: DependencyAPI, accounts):
def deploy_contracts(
nucypher_contracts: DependencyAPI,
test_contracts: DependencyAPI,
accounts: List[TestAccount],
):
"""Deploy contracts o via ape's API for testing."""
config = ape_config.get_config("deployments")["ethereum"]["local"]
deployments = dict()
@ -79,13 +166,20 @@ def deploy_contracts(nucypher_contracts: DependencyAPI, accounts):
params, deployer_account = get_deployment_params(
name, deployments=deployments, config=config, accounts=accounts
)
dependency_contract = getattr(nucypher_contracts, name)
deployed_contract = deployer_account.deploy(dependency_contract, *params.values())
try:
# this contract is a dependency
contract = getattr(nucypher_contracts, name)
except AttributeError:
# this contract is local to this project
contract = getattr(project, name)
deployed_contract = deployer_account.deploy(contract, *params.values())
deployments[name] = deployed_contract
return deployments
def registry_from_ape_deployments(nucypher_contracts: DependencyAPI, deployments: Dict) -> InMemoryContractRegistry:
def registry_from_ape_deployments(
nucypher_contracts: DependencyAPI, deployments: Dict[str, ContractInstance]
) -> InMemoryContractRegistry:
"""Creates a registry from ape deployments."""
# Get the raw abi from the cached manifest