Skip to content

Commit

Permalink
Merge pull request #115 from vzotova/commitment
Browse files Browse the repository at this point in the history
TACo commitment
  • Loading branch information
cygnusv authored Sep 8, 2023
2 parents 3456fdc + 7f3f369 commit 74ac5ea
Show file tree
Hide file tree
Showing 5 changed files with 180 additions and 3 deletions.
2 changes: 2 additions & 0 deletions ape-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ deployments:
pre_percentage_penalty_coefficient: 100000
reward_duration: 604800 # one week in seconds
deauthorization_duration: 5184000 # 60 days in seconds
commitment_duration_1: 15552000 # 180 days in seconds
commitment_duration_2: 31104000 # 360 days in seconds
verify: False
rinkeby:
- nu_token: "0x78D591D90a4a768B9D2790deA465D472b6Fe0f18"
Expand Down
60 changes: 58 additions & 2 deletions contracts/contracts/TACoApplication.sol
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,13 @@ contract TACoApplication is IApplication, ITACoChildToRoot, OwnableUpgradeable {
uint256 startTimestamp
);

/**
* @notice Signals that a staking provider made a commitment
* @param stakingProvider Staking provider address
* @param endCommitment End of commitment
*/
event CommitmentMade(address indexed stakingProvider, uint256 endCommitment);

struct StakingProviderInfo {
address operator;
bool operatorConfirmed;
Expand All @@ -137,13 +144,19 @@ contract TACoApplication is IApplication, ITACoChildToRoot, OwnableUpgradeable {
uint64 endDeauthorization;
uint96 tReward;
uint96 rewardPerTokenPaid;
uint64 endCommitment;
}

uint96 public immutable minimumAuthorization;
uint256 public immutable minOperatorSeconds;
uint256 public immutable rewardDuration;
uint256 public immutable deauthorizationDuration;

uint64 public immutable commitmentDurationOption1;
uint64 public immutable commitmentDurationOption2;
uint64 public immutable commitmentDurationOption3;
uint64 public immutable commitmentDurationOption4;

IStaking public immutable tStaking;
IERC20 public immutable token;

Expand All @@ -169,19 +182,23 @@ contract TACoApplication is IApplication, ITACoChildToRoot, OwnableUpgradeable {
* @param _minOperatorSeconds Min amount of seconds while an operator can't be changed
* @param _rewardDuration Duration of one reward cycle in seconds
* @param _deauthorizationDuration Duration of decreasing authorization in seconds
* @param _commitmentDurationOptions Options for commitment duration
*/
constructor(
IERC20 _token,
IStaking _tStaking,
uint96 _minimumAuthorization,
uint256 _minOperatorSeconds,
uint256 _rewardDuration,
uint256 _deauthorizationDuration
uint256 _deauthorizationDuration,
uint64[] memory _commitmentDurationOptions
) {
require(
_rewardDuration != 0 &&
_tStaking.authorizedStake(address(this), address(this)) == 0 &&
_token.totalSupply() > 0,
_token.totalSupply() > 0 &&
_commitmentDurationOptions.length >= 1 &&
_commitmentDurationOptions.length <= 4,
"Wrong input parameters"
);
rewardDuration = _rewardDuration;
Expand All @@ -190,6 +207,16 @@ contract TACoApplication is IApplication, ITACoChildToRoot, OwnableUpgradeable {
token = _token;
tStaking = _tStaking;
minOperatorSeconds = _minOperatorSeconds;
commitmentDurationOption1 = _commitmentDurationOptions[0];
commitmentDurationOption2 = _commitmentDurationOptions.length >= 2
? _commitmentDurationOptions[1]
: 0;
commitmentDurationOption3 = _commitmentDurationOptions.length >= 3
? _commitmentDurationOptions[2]
: 0;
commitmentDurationOption4 = _commitmentDurationOptions.length >= 4
? _commitmentDurationOptions[3]
: 0;
_disableInitializers();
}

Expand Down Expand Up @@ -440,6 +467,10 @@ contract TACoApplication is IApplication, ITACoChildToRoot, OwnableUpgradeable {
_toAmount == 0 || _toAmount >= minimumAuthorization,
"Resulting authorization will be less than minimum"
);
require(
info.endCommitment <= block.timestamp,
"Can't request deauthorization before end of commitment"
);
if (info.operatorConfirmed) {
resynchronizeAuthorizedOverall(info, _fromAmount);
}
Expand Down Expand Up @@ -475,6 +506,7 @@ contract TACoApplication is IApplication, ITACoChildToRoot, OwnableUpgradeable {
info.authorized = toAmount;
info.deauthorizing = 0;
info.endDeauthorization = 0;
info.endCommitment = 0;

if (info.authorized == 0) {
_stakingProviderFromOperator[info.operator] = address(0);
Expand Down Expand Up @@ -513,6 +545,30 @@ contract TACoApplication is IApplication, ITACoChildToRoot, OwnableUpgradeable {
_updateAuthorization(_stakingProvider, info);
}

/**
* @notice Make a commitment to not request authorization decrease for specified duration
* @param _stakingProvider Staking provider address
* @param _commitmentDuration Duration of commitment
*/
function makeCommitment(
address _stakingProvider,
uint64 _commitmentDuration
) external onlyOwnerOrStakingProvider(_stakingProvider) {
require(
_commitmentDuration > 0 &&
(_commitmentDuration == commitmentDurationOption1 ||
_commitmentDuration == commitmentDurationOption2 ||
_commitmentDuration == commitmentDurationOption3 ||
_commitmentDuration == commitmentDurationOption4),
"Commitment duration must be equal to one of options"
);
StakingProviderInfo storage info = stakingProviderInfo[_stakingProvider];
require(info.endDeauthorization == 0, "Commitment can't be made during deauthorization");
require(info.endCommitment == 0, "Commitment already made");
info.endCommitment = uint64(block.timestamp) + _commitmentDuration;
emit CommitmentMade(_stakingProvider, info.endCommitment);
}

//-------------------------Main-------------------------
/**
* @notice Returns staking provider for specified operator
Expand Down
4 changes: 4 additions & 0 deletions scripts/deploy_taco_application.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@ def main(account_id=None):
deployments_config.get("pre_min_operator_seconds"),
deployments_config.get("reward_duration"),
deployments_config.get("deauthorization_duration"),
[
deployments_config.get("commitment_duration_1"),
deployments_config.get("commitment_duration_2"),
],
sender=deployer,
publish=deployments_config.get("verify"),
)
Expand Down
4 changes: 4 additions & 0 deletions tests/application/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@
REWARD_DURATION = 60 * 60 * 24 * 7 # one week in seconds
DEAUTHORIZATION_DURATION = 60 * 60 * 24 * 60 # 60 days in seconds
TOTAL_SUPPLY = Web3.to_wei(1_000_000_000, "ether") # TODO NU(1_000_000_000, 'NU').to_units()
COMMITMENT_DURATION_1 = 182 * 60 * 24 * 60 # 182 days in seconds
COMMITMENT_DURATION_2 = 2 * COMMITMENT_DURATION_1 # 365 days in seconds
COMMITMENT_DURATION_3 = 3 * COMMITMENT_DURATION_1 # 365 days in seconds

DEPENDENCY = project.dependencies["openzeppelin"]["4.9.1"]

Expand Down Expand Up @@ -75,6 +78,7 @@ def taco_application(project, creator, token, threshold_staking):
MIN_OPERATOR_SECONDS,
REWARD_DURATION,
DEAUTHORIZATION_DURATION,
[COMMITMENT_DURATION_1, COMMITMENT_DURATION_2, COMMITMENT_DURATION_3],
)

proxy_admin = DEPENDENCY.ProxyAdmin.deploy(sender=creator)
Expand Down
113 changes: 112 additions & 1 deletion tests/application/test_authorization.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
AUTHORIZATION_SLOT = 3
DEAUTHORIZING_SLOT = 4
END_DEAUTHORIZATION_SLOT = 5
END_COMMITMENT_SLOT = 8
MIN_AUTHORIZATION = Web3.to_wei(40_000, "ether")
DEAUTHORIZATION_DURATION = 60 * 60 * 24 * 60 # 60 days in seconds

Expand Down Expand Up @@ -403,10 +404,29 @@ def test_authorization_decrease_request(
# Request decrease without syncing with child app
taco_application.setChildApplication(ZERO_ADDRESS, sender=creator)
threshold_staking.authorizationDecreaseRequested(
staking_provider, value // 2, value // 2, sender=creator
staking_provider, value // 2, 0, sender=creator
)
assert child_application.authorizedStake(staking_provider) == 0

# Try to request decrease before ending of commitment
chain.pending_timestamp += deauthorization_duration
taco_application.finishAuthorizationDecrease(staking_provider, sender=creator)
threshold_staking.authorizationIncreased(staking_provider, 0, value, sender=creator)
commitment_duration = taco_application.commitmentDurationOption1()
taco_application.makeCommitment(staking_provider, commitment_duration, sender=staking_provider)

# Commitment is still active
with ape.reverts("Can't request deauthorization before end of commitment"):
threshold_staking.authorizationDecreaseRequested(
staking_provider, value, value // 2, sender=creator
)
chain.pending_timestamp += commitment_duration

# Now decrease can be requested
threshold_staking.authorizationDecreaseRequested(
staking_provider, value, value // 2, sender=creator
)


def test_finish_authorization_decrease(
accounts, threshold_staking, taco_application, child_application, chain
Expand Down Expand Up @@ -663,3 +683,94 @@ def test_resync(accounts, threshold_staking, taco_application, child_application
assert child_application.authorizedStake(staking_provider) == value
assert taco_application.getOperatorFromStakingProvider(staking_provider) == ZERO_ADDRESS
assert child_application.operatorFromStakingProvider(staking_provider) == staking_provider


def test_commitment(accounts, threshold_staking, taco_application, chain):
"""
Tests for authorization method: makeCommitment
"""

creator, staking_provider, another_staking_provider = accounts[0:3]
deauthorization_duration = DEAUTHORIZATION_DURATION
minimum_authorization = MIN_AUTHORIZATION
value = 2 * minimum_authorization
commitment_duration_1 = taco_application.commitmentDurationOption1()
commitment_duration_2 = taco_application.commitmentDurationOption2()
commitment_duration_3 = taco_application.commitmentDurationOption3()

# Commitment can be made only for authorized staking provider
with ape.reverts("Not owner or provider"):
taco_application.makeCommitment(
staking_provider, commitment_duration_1, sender=staking_provider
)

# Prepare staking provider
threshold_staking.authorizationIncreased(staking_provider, 0, value, sender=creator)

# Begin deauthorization
threshold_staking.authorizationDecreaseRequested(
staking_provider, value, minimum_authorization, sender=creator
)

# Commitment can't be made during deauthorization
with ape.reverts("Commitment can't be made during deauthorization"):
taco_application.makeCommitment(
staking_provider, commitment_duration_3, sender=staking_provider
)

# Finish deauthorization
chain.pending_timestamp += deauthorization_duration
taco_application.finishAuthorizationDecrease(staking_provider, sender=creator)

# Commitment can be made only by staking provider
with ape.reverts("Not owner or provider"):
taco_application.makeCommitment(
staking_provider, commitment_duration_3, sender=another_staking_provider
)

# Commitment duration must be equal to one of options
with ape.reverts("Commitment duration must be equal to one of options"):
taco_application.makeCommitment(staking_provider, 0, sender=staking_provider)
with ape.reverts("Commitment duration must be equal to one of options"):
taco_application.makeCommitment(
staking_provider, commitment_duration_1 + 1, sender=staking_provider
)

# And make a commitment for shorter duration
tx = taco_application.makeCommitment(
staking_provider, commitment_duration_1, sender=staking_provider
)
timestamp = chain.pending_timestamp - 1
end_commitment = timestamp + commitment_duration_1
assert (
taco_application.stakingProviderInfo(staking_provider)[END_COMMITMENT_SLOT]
== end_commitment
)
assert tx.events == [
taco_application.CommitmentMade(
stakingProvider=staking_provider, endCommitment=end_commitment
)
]

# Commitment can't be made twice
with ape.reverts("Commitment already made"):
taco_application.makeCommitment(
staking_provider, commitment_duration_2, sender=staking_provider
)

# Another staking provider makes a commitment for longer period of time
threshold_staking.authorizationIncreased(another_staking_provider, 0, value, sender=creator)
tx = taco_application.makeCommitment(
another_staking_provider, commitment_duration_3, sender=another_staking_provider
)
timestamp = chain.pending_timestamp - 1
end_commitment = timestamp + commitment_duration_3
assert (
taco_application.stakingProviderInfo(another_staking_provider)[END_COMMITMENT_SLOT]
== end_commitment
)
assert tx.events == [
taco_application.CommitmentMade(
stakingProvider=another_staking_provider, endCommitment=end_commitment
)
]

0 comments on commit 74ac5ea

Please sign in to comment.