mirror of https://github.com/nucypher/nucypher.git
Updates worklock API to work-based unlock without allocation deployments.
parent
bb55ac05d7
commit
bbcb9e649c
|
@ -38,7 +38,7 @@ from nucypher.blockchain.eth.constants import (
|
|||
)
|
||||
from nucypher.blockchain.eth.decorators import validate_checksum_address
|
||||
from nucypher.blockchain.eth.interfaces import BlockchainInterface, BlockchainInterfaceFactory
|
||||
from nucypher.blockchain.eth.registry import AllocationRegistry, BaseContractRegistry, IndividualAllocationRegistry
|
||||
from nucypher.blockchain.eth.registry import AllocationRegistry, BaseContractRegistry
|
||||
from nucypher.blockchain.eth.utils import epoch_to_period
|
||||
from nucypher.crypto.api import sha256_digest
|
||||
|
||||
|
@ -613,8 +613,8 @@ class StakingEscrowAgent(EthereumContractAgent):
|
|||
result = missing_confirmations
|
||||
return result
|
||||
|
||||
def get_completed_work(self, allocation_address: str):
|
||||
total_completed_work = self.contract.functions.getCompletedWork(allocation_address).call()
|
||||
def get_completed_work(self, bidder_address: str):
|
||||
total_completed_work = self.contract.functions.getCompletedWork(bidder_address).call()
|
||||
return total_completed_work
|
||||
|
||||
|
||||
|
@ -958,28 +958,56 @@ class WorkLockAgent(EthereumContractAgent):
|
|||
|
||||
registry_contract_name = "WorkLock"
|
||||
|
||||
#
|
||||
# Transactions
|
||||
#
|
||||
|
||||
def bid(self, value: int, bidder_address: str) -> dict:
|
||||
"""
|
||||
Bid for NU tokens with ETH.
|
||||
"""
|
||||
"""Bid for NU tokens with ETH."""
|
||||
contract_function = self.contract.functions.bid()
|
||||
receipt = self.blockchain.send_transaction(contract_function=contract_function,
|
||||
sender_address=bidder_address,
|
||||
receipt = self.blockchain.send_transaction(contract_function=contract_function, sender_address=bidder_address,
|
||||
payload={'value': value})
|
||||
return receipt
|
||||
|
||||
def cancel_bid(self, bidder_address: str) -> dict:
|
||||
"""Cancel bid and refund deposited ETH."""
|
||||
contract_function = self.contract.functions.cancelBid()
|
||||
receipt = self.blockchain.send_transaction(contract_function=contract_function, sender_address=bidder_address)
|
||||
return receipt
|
||||
|
||||
def claim(self, bidder_address: str) -> dict:
|
||||
"""
|
||||
Claim tokens - will be deposited and locked as stake in the StakingEscrow contract.
|
||||
This function produces a new deployment or PreAllocationEscrow for the claimee.
|
||||
"""
|
||||
contract_function = self.contract.functions.claim()
|
||||
receipt = self.blockchain.send_transaction(contract_function=contract_function, sender_address=bidder_address)
|
||||
|
||||
return receipt
|
||||
|
||||
def burn_unclaimed(self, sender_address: str) -> dict:
|
||||
"""
|
||||
Burn unclaimed tokens - Out of the goodness of your heart...
|
||||
of course the caller must pay for the transaction gas.
|
||||
"""
|
||||
contract_function = self.contract.functions.burnUnclaimed()
|
||||
receipt = self.blockchain.send_transaction(contract_function=contract_function, sender_address=sender_address)
|
||||
return receipt
|
||||
|
||||
def refund(self, bidder_address: str) -> dict:
|
||||
"""Refund ETH for completed work."""
|
||||
contract_function = self.contract.functions.refund()
|
||||
receipt = self.blockchain.send_transaction(contract_function=contract_function, sender_address=bidder_address)
|
||||
return receipt
|
||||
|
||||
#
|
||||
# Calls
|
||||
#
|
||||
|
||||
def get_bid(self, checksum_address: str) -> int:
|
||||
current_bid = self.contract.functions.workInfo(checksum_address).call()[0]
|
||||
return current_bid
|
||||
|
||||
def get_allocation_from_bidder(self, bidder_address: str) -> str:
|
||||
allocation_address = self.contract.functions.workInfo(bidder_address).call()[2]
|
||||
return allocation_address
|
||||
|
||||
def get_bidder_from_allocation(self, allocation_address: str) -> str:
|
||||
bidder = self.contract.functions.depositors(allocation_address).call()
|
||||
return bidder
|
||||
|
||||
@property
|
||||
def lot_value(self) -> int:
|
||||
"""
|
||||
|
@ -989,82 +1017,17 @@ class WorkLockAgent(EthereumContractAgent):
|
|||
supply = self.contract.functions.tokenSupply().call()
|
||||
return supply
|
||||
|
||||
def cancel_bid(self, bidder_address: str) -> dict:
|
||||
"""
|
||||
Cancel bid and refund deposited ETH.
|
||||
"""
|
||||
contract_function = self.contract.functions.cancelBid()
|
||||
receipt = self.blockchain.send_transaction(contract_function=contract_function,
|
||||
sender_address=bidder_address)
|
||||
return receipt
|
||||
|
||||
def _make_allocation_registry(self, bidder_address: str) -> IndividualAllocationRegistry:
|
||||
preallocation_address = self.get_allocation_from_bidder(bidder_address=bidder_address)
|
||||
allocation_registry = IndividualAllocationRegistry(beneficiary_address=bidder_address,
|
||||
contract_address=preallocation_address)
|
||||
return allocation_registry
|
||||
|
||||
def available_refund(self, bidder_address: str = None, allocation_address: str = None) -> int:
|
||||
if not (bool(bidder_address) ^ bool(allocation_address)):
|
||||
raise ValueError(f"Pass bidder address or allocation address, got '{bidder_address}' and '{allocation_address}'.")
|
||||
if bidder_address:
|
||||
allocation_address = self.get_allocation_from_bidder(bidder_address=bidder_address)
|
||||
else:
|
||||
bidder_address = self.get_bidder_from_allocation(allocation_address=allocation_address)
|
||||
|
||||
def available_refund(self, bidder_address: str) -> int:
|
||||
staking_agent = ContractAgency.get_agent(StakingEscrowAgent, registry=self.registry)
|
||||
|
||||
# Calculate
|
||||
total_completed_work = staking_agent.get_completed_work(allocation_address=allocation_address)
|
||||
total_completed_work = staking_agent.get_completed_work(bidder_address=bidder_address)
|
||||
refunded_work = self.contract.functions.workInfo(bidder_address).call()[1]
|
||||
completed_work = total_completed_work - refunded_work
|
||||
refund_eth = self.contract.functions.workToETH(completed_work).call()
|
||||
|
||||
return refund_eth
|
||||
|
||||
def claim(self, bidder_address: str) -> dict:
|
||||
"""
|
||||
Claim tokens - will be deposited and locked as stake in the StakingEscrow contract.
|
||||
This function produces a new deployment or PreAllocationEscrow for the claimee.
|
||||
"""
|
||||
contract_function = self.contract.functions.claim()
|
||||
receipt = self.blockchain.send_transaction(contract_function=contract_function,
|
||||
sender_address=bidder_address)
|
||||
|
||||
return receipt
|
||||
|
||||
def burn_unclaimed(self, sender_address: str) -> dict:
|
||||
"""
|
||||
Burn unclaimed tokens -
|
||||
Out of the goodness of your heart - of course the caller must pay for the gas...
|
||||
"""
|
||||
contract_function = self.contract.functions.burnUnclaimed()
|
||||
receipt = self.blockchain.send_transaction(contract_function=contract_function,
|
||||
sender_address=sender_address)
|
||||
return receipt
|
||||
|
||||
def refund(self, beneficiary_address: str, allocation_address: str = None) -> dict:
|
||||
"""
|
||||
Refund ETH for completed work to the beneficiary address (must be the owner of the allocation).
|
||||
"""
|
||||
if not allocation_address:
|
||||
allocation_address = self.get_allocation_from_bidder(bidder_address=beneficiary_address)
|
||||
if allocation_address == BlockchainInterface.NULL_ADDRESS:
|
||||
raise ValueError(f"Unable to locate allocation for {beneficiary_address}")
|
||||
contract_function = self.contract.functions.refund(allocation_address)
|
||||
receipt = self.blockchain.send_transaction(contract_function=contract_function,
|
||||
sender_address=beneficiary_address)
|
||||
return receipt
|
||||
|
||||
def get_remaining_work(self, bidder_address: str = None, allocation_address: str = None) -> int:
|
||||
"""
|
||||
Get remaining work periods until full refund for the target address.
|
||||
"""
|
||||
if not (bool(bidder_address) ^ bool(allocation_address)):
|
||||
raise ValueError(f"Pass bidder address or allocation address, got '{bidder_address}' and '{allocation_address}'.")
|
||||
if bidder_address:
|
||||
allocation_address = self.get_allocation_from_bidder(bidder_address=bidder_address)
|
||||
result = self.contract.functions.getRemainingWork(allocation_address).call()
|
||||
def get_remaining_work(self, bidder_address: str) -> int:
|
||||
"""Get remaining work periods until full refund for the target address."""
|
||||
result = self.contract.functions.getRemainingWork(bidder_address).call()
|
||||
return result
|
||||
|
||||
def get_eth_supply(self) -> int:
|
||||
|
@ -1073,7 +1036,9 @@ class WorkLockAgent(EthereumContractAgent):
|
|||
|
||||
def get_refund_rate(self) -> int:
|
||||
f = self.contract.functions
|
||||
refund_rate = self.get_deposit_rate() * f.SLOWING_REFUND().call() / f.boostingRefund().call()
|
||||
slowing_refund = f.SLOWING_REFUND().call()
|
||||
boosting_refund = f.boostingRefund().call()
|
||||
refund_rate = self.get_deposit_rate() * slowing_refund / boosting_refund
|
||||
return refund_rate
|
||||
|
||||
def get_deposit_rate(self) -> int:
|
||||
|
@ -1093,7 +1058,7 @@ class WorkLockAgent(EthereumContractAgent):
|
|||
'startBidDate',
|
||||
'endBidDate',
|
||||
'boostingRefund',
|
||||
'lockingDuration',
|
||||
'stakingPeriods',
|
||||
)
|
||||
|
||||
def _call_function_by_name(name: str):
|
||||
|
|
|
@ -1094,7 +1094,6 @@ class WorklockDeployer(BaseContractDeployer):
|
|||
# Deploy
|
||||
constructor_args = (self.token_agent.contract_address,
|
||||
self.staking_agent.contract_address,
|
||||
interface_router.address,
|
||||
*self.economics.worklock_deployment_parameters)
|
||||
|
||||
worklock_contract, receipt = self.blockchain.deploy_contract(self.deployer_address,
|
||||
|
|
|
@ -19,7 +19,6 @@ along with nucypher. If not, see <https://www.gnu.org/licenses/>.
|
|||
import collections
|
||||
import os
|
||||
import pprint
|
||||
import time
|
||||
from typing import List
|
||||
from typing import Tuple
|
||||
from typing import Union
|
||||
|
@ -27,6 +26,7 @@ from urllib.parse import urlparse
|
|||
|
||||
import click
|
||||
import requests
|
||||
import time
|
||||
from constant_sorrow.constants import (
|
||||
NO_BLOCKCHAIN_CONNECTION,
|
||||
NO_COMPILATION_PERFORMED,
|
||||
|
@ -35,7 +35,7 @@ from constant_sorrow.constants import (
|
|||
READ_ONLY_INTERFACE
|
||||
)
|
||||
from eth_tester import EthereumTester
|
||||
from eth_utils import to_checksum_address, is_checksum_address
|
||||
from eth_utils import to_checksum_address
|
||||
from twisted.logger import Logger
|
||||
from web3 import Web3, WebsocketProvider, HTTPProvider, IPCProvider
|
||||
from web3.contract import ContractConstructor, Contract
|
||||
|
@ -58,7 +58,6 @@ from nucypher.blockchain.eth.providers import (
|
|||
)
|
||||
from nucypher.blockchain.eth.registry import BaseContractRegistry
|
||||
from nucypher.blockchain.eth.sol.compile import SolidityCompiler
|
||||
from nucypher.characters.control.emitters import StdoutEmitter
|
||||
|
||||
Web3Providers = Union[IPCProvider, WebsocketProvider, HTTPProvider, EthereumTester]
|
||||
|
||||
|
@ -588,7 +587,7 @@ class BlockchainDeployerInterface(BlockchainInterface):
|
|||
deploy_transaction.update({'gas': gas_limit})
|
||||
|
||||
pprint_args = ', '.join(list(map(str, constructor_args)) + list(f"{k}={v}" for k, v in constructor_kwargs.items()))
|
||||
pprint_args = pprint_args.replace("{", "{{").replace("}", "}}") # See #724
|
||||
pprint_args = pprint_args.replace("{", "{{").replace("}", "}}") # TODO: See #724
|
||||
|
||||
contract_factory = self.get_contract_factory(contract_name=contract_name, version=contract_version)
|
||||
self.log.info(f"Deploying contract {contract_name}:{contract_factory.version} with "
|
||||
|
|
|
@ -775,7 +775,6 @@ def paint_worklock_participant_status(emitter, registry, bidder_address):
|
|||
Allocations
|
||||
=====================================================
|
||||
Bidder ............... {bidder_address}
|
||||
Allocation Contract... {WORKLOCK_AGENT.get_allocation_from_bidder(bidder_address)}
|
||||
Available Refund ..... {WORKLOCK_AGENT.available_refund(bidder_address=bidder_address)}
|
||||
Remaining Work ....... {WORKLOCK_AGENT.get_remaining_work(bidder_address=bidder_address)}
|
||||
"""
|
||||
|
@ -816,11 +815,10 @@ Accept worklock terms and node operator obligation?"""
|
|||
return
|
||||
|
||||
|
||||
def paint_worklock_claim(emitter, bidder_address: str, allocation_address: str):
|
||||
def paint_worklock_claim(emitter, bidder_address: str):
|
||||
message = f"""
|
||||
|
||||
Successfully claimed WorkLock tokens for {bidder_address}.
|
||||
***New Allocation Contract Deployed at {allocation_address}***
|
||||
|
||||
Next Steps for Worklock Winners
|
||||
===============================
|
||||
|
@ -828,7 +826,7 @@ Next Steps for Worklock Winners
|
|||
See the nucypher official documentation for a comprehensive guide!
|
||||
|
||||
Create a stake with your allocation contract:
|
||||
'nucypher stake create --provider <URI> --staking-address {allocation_address} --beneficiary-address {bidder_address}'
|
||||
'nucypher stake create --provider <URI> --staking-address {bidder_address}'
|
||||
|
||||
Bond a worker to your stake: 'nucypher stake set-worker --worker-address <WORKER ADDRESS>'
|
||||
|
||||
|
|
|
@ -38,10 +38,6 @@ option_bidder_address = click.option('--bidder-address',
|
|||
help="Bidder's checksum address.",
|
||||
type=EIP55_CHECKSUM_ADDRESS)
|
||||
|
||||
option_allocation_address = click.option('--allocation-address',
|
||||
help="Worklock allocation contract address",
|
||||
type=EIP55_CHECKSUM_ADDRESS)
|
||||
|
||||
|
||||
def _setup_emitter(general_config):
|
||||
emitter = general_config.emitter
|
||||
|
@ -54,9 +50,8 @@ class WorkLockOptions:
|
|||
|
||||
__option_name__ = 'worklock_options'
|
||||
|
||||
def __init__(self, bidder_address, allocation_address):
|
||||
def __init__(self, bidder_address: str):
|
||||
self.bidder_address = bidder_address
|
||||
self.allocation_address = allocation_address
|
||||
|
||||
def create_agent(self, registry):
|
||||
agent = ContractAgency.get_agent(WorkLockAgent, registry=registry)
|
||||
|
@ -65,8 +60,7 @@ class WorkLockOptions:
|
|||
|
||||
group_worklock_options = group_options(
|
||||
WorkLockOptions,
|
||||
bidder_address=option_bidder_address,
|
||||
allocation_address=option_allocation_address)
|
||||
bidder_address=option_bidder_address)
|
||||
|
||||
|
||||
@click.group()
|
||||
|
@ -154,8 +148,7 @@ def claim(general_config, worklock_options, registry_options, force):
|
|||
worklock_agent = worklock_options.create_agent(registry=registry)
|
||||
receipt = worklock_agent.claim(bidder_address=worklock_options.bidder_address)
|
||||
paint_receipt_summary(receipt=receipt, emitter=emitter, chain_name=worklock_agent.blockchain.client.chain_name)
|
||||
allocation_address = worklock_agent.get_allocation_from_bidder(bidder_address=worklock_options.bidder_address)
|
||||
paint_worklock_claim(emitter, bidder_address=worklock_options.bidder_address, allocation_address=allocation_address)
|
||||
paint_worklock_claim(emitter, bidder_address=worklock_options.bidder_address)
|
||||
return # Exit
|
||||
|
||||
|
||||
|
@ -190,7 +183,7 @@ def refund(general_config, worklock_options, registry_options, force):
|
|||
emitter.message("Submitting WorkLock refund request...")
|
||||
registry = registry_options.get_registry(emitter, general_config.debug)
|
||||
worklock_agent = worklock_options.create_agent(registry=registry)
|
||||
receipt = worklock_agent.refund(beneficiary_address=worklock_options.bidder_address)
|
||||
receipt = worklock_agent.refund(bidder_address=worklock_options.bidder_address)
|
||||
paint_receipt_summary(receipt=receipt, emitter=emitter, chain_name=worklock_agent.blockchain.client.chain_name)
|
||||
return # Exit
|
||||
|
||||
|
|
|
@ -59,11 +59,11 @@ def test_cancel_bid(testerchain, agency, token_economics, test_registry):
|
|||
_receipt = agent.cancel_bid(bidder)
|
||||
|
||||
|
||||
def test_get_remaining_work_before_bidding_ends(testerchain, agency, token_economics, test_registry):
|
||||
def test_get_remaining_work(testerchain, agency, token_economics, test_registry):
|
||||
agent = ContractAgency.get_agent(WorkLockAgent, registry=test_registry)
|
||||
bidder = testerchain.unassigned_accounts[0]
|
||||
remaining = agent.get_remaining_work(bidder_address=bidder)
|
||||
assert remaining == 0
|
||||
assert remaining == 35905203136136849607983
|
||||
|
||||
|
||||
def test_early_claim(testerchain, agency, token_economics, test_registry):
|
||||
|
@ -87,11 +87,3 @@ def test_successful_claim(testerchain, agency, token_economics, test_registry):
|
|||
# Cant claim more than once
|
||||
with pytest.raises(TransactionFailed):
|
||||
_receipt = agent.claim(bidder_address=bidder)
|
||||
|
||||
|
||||
def test_lookup_bidders_and_allocations(testerchain, agency, token_economics, test_registry):
|
||||
bidder = testerchain.unassigned_accounts[0]
|
||||
agent = ContractAgency.get_agent(WorkLockAgent, registry=test_registry)
|
||||
allocation_address = agent.get_allocation_from_bidder(bidder)
|
||||
recovered_bidder = agent.get_bidder_from_allocation(allocation_address)
|
||||
assert recovered_bidder == bidder
|
||||
|
|
|
@ -148,17 +148,12 @@ def test_refund(click_runner, testerchain, agency, test_registry, token_economic
|
|||
#
|
||||
|
||||
worklock_agent = ContractAgency.get_agent(WorkLockAgent, registry=test_registry)
|
||||
allocation_address = worklock_agent.get_allocation_from_bidder(bidder_address=bidder)
|
||||
individual_allocation = IndividualAllocationRegistry(beneficiary_address=bidder,
|
||||
contract_address=allocation_address)
|
||||
|
||||
staker = Staker(is_me=True,
|
||||
registry=test_registry,
|
||||
individual_allocation=individual_allocation)
|
||||
staker = Staker(is_me=True, checksum_address=bidder, registry=test_registry)
|
||||
|
||||
# Create a new stake with the new allocation
|
||||
new_stake = staker.initialize_stake(entire_balance=True, lock_periods=30)
|
||||
assert new_stake
|
||||
# new_stake = staker.initialize_stake(entire_balance=True, lock_periods=30)
|
||||
# assert new_stake
|
||||
|
||||
# Bond the worker
|
||||
receipt = staker.set_worker(worker_address=worker_address)
|
||||
|
@ -166,7 +161,7 @@ def test_refund(click_runner, testerchain, agency, test_registry, token_economic
|
|||
|
||||
worker = Ursula(is_me=True,
|
||||
registry=test_registry,
|
||||
checksum_address=allocation_address,
|
||||
checksum_address=bidder,
|
||||
worker_address=worker_address,
|
||||
rest_host=MOCK_IP_ADDRESS,
|
||||
rest_port=select_test_port())
|
||||
|
|
Loading…
Reference in New Issue