Skip to content

Commit 502fa51

Browse files
committed
staking,delegation: make undelegate to work like unstake
1 parent edadbb0 commit 502fa51

File tree

4 files changed

+80
-32
lines changed

4 files changed

+80
-32
lines changed

contracts/staking/Staking.sol

Lines changed: 68 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -503,6 +503,24 @@ contract Staking is StakingV1Storage, GraphUpgradeable, IStaking {
503503
return indexerStake.tokensAvailableWithDelegation(tokensDelegated);
504504
}
505505

506+
/**
507+
* @dev Returns amount of delegated tokens ready to be withdrawn after unbonding period.
508+
* @param _delegation Delegation of tokens from delegator to indexer
509+
* @return Amount of tokens to withdraw
510+
*/
511+
function getWithdraweableDelegatedTokens(Delegation memory _delegation)
512+
public
513+
view
514+
returns (uint256)
515+
{
516+
// There must be locked tokens and period passed
517+
uint256 currentEpoch = epochManager().currentEpoch();
518+
if (_delegation.tokensLockedUntil > 0 && currentEpoch >= _delegation.tokensLockedUntil) {
519+
return _delegation.tokensLocked;
520+
}
521+
return 0;
522+
}
523+
506524
/**
507525
* @dev Authorize an address to be an operator.
508526
* @param _operator Address to authorize
@@ -665,38 +683,14 @@ contract Staking is StakingV1Storage, GraphUpgradeable, IStaking {
665683
/**
666684
* @dev Withdraw delegated tokens once the unbonding period has passed.
667685
* @param _indexer Withdraw available tokens delegated to indexer
668-
* @param _newIndexer Re-delegate to indexer address if non-zero, withdraw if zero address
686+
* @param _delegateToIndexer Re-delegate to indexer address if non-zero, withdraw if zero address
669687
*/
670-
function withdrawDelegated(address _indexer, address _newIndexer) external override notPaused {
671-
address delegator = msg.sender;
672-
673-
// Get the delegation pool of the indexer
674-
DelegationPool storage pool = delegationPools[_indexer];
675-
Delegation storage delegation = pool.delegators[delegator];
676-
677-
// There must be locked tokens and period passed
678-
uint256 currentEpoch = epochManager().currentEpoch();
679-
require(
680-
delegation.tokensLockedUntil > 0 && currentEpoch >= delegation.tokensLockedUntil,
681-
"No tokens available to withdraw"
682-
);
683-
684-
// Get tokens available for withdrawal
685-
uint256 tokensToWithdraw = delegation.tokensLocked;
686-
687-
// Reset lock
688-
delegation.tokensLocked = 0;
689-
delegation.tokensLockedUntil = 0;
690-
691-
emit StakeDelegatedWithdrawn(_indexer, delegator, tokensToWithdraw);
692-
693-
if (_newIndexer != address(0)) {
694-
// Re-delegate tokens to a new indexer
695-
_delegate(delegator, _newIndexer, tokensToWithdraw);
696-
} else {
697-
// Return tokens to the delegator
698-
require(graphToken().transfer(delegator, tokensToWithdraw), "!transfer");
699-
}
688+
function withdrawDelegated(address _indexer, address _delegateToIndexer)
689+
external
690+
override
691+
notPaused
692+
{
693+
_withdrawDelegated(msg.sender, _indexer, _delegateToIndexer);
700694
}
701695

702696
/**
@@ -1162,6 +1156,11 @@ contract Staking is StakingV1Storage, GraphUpgradeable, IStaking {
11621156
// Delegator need to have enough shares in the pool to undelegate
11631157
require(delegation.shares >= _shares, "Delegator does not have enough shares");
11641158

1159+
// Withdraw tokens if available
1160+
if (getWithdraweableDelegatedTokens(delegation) > 0) {
1161+
_withdrawDelegated(_delegator, _indexer, address(0));
1162+
}
1163+
11651164
// Calculate tokens to get in exchange for the shares
11661165
uint256 tokens = _shares.mul(pool.tokens).div(pool.shares);
11671166

@@ -1185,6 +1184,44 @@ contract Staking is StakingV1Storage, GraphUpgradeable, IStaking {
11851184
return tokens;
11861185
}
11871186

1187+
/**
1188+
* @dev Withdraw delegated tokens once the unbonding period has passed.
1189+
* @param _delegator Delegator that is withdrawing tokens
1190+
* @param _indexer Withdraw available tokens delegated to indexer
1191+
* @param _delegateToIndexer Re-delegate to indexer address if non-zero, withdraw if zero address
1192+
*/
1193+
function _withdrawDelegated(
1194+
address _delegator,
1195+
address _indexer,
1196+
address _delegateToIndexer
1197+
) internal returns (uint256) {
1198+
// Get the delegation pool of the indexer
1199+
DelegationPool storage pool = delegationPools[_indexer];
1200+
Delegation storage delegation = pool.delegators[_delegator];
1201+
1202+
// Validation
1203+
uint256 tokensToWithdraw = getWithdraweableDelegatedTokens(delegation);
1204+
require(tokensToWithdraw > 0, "No tokens available to withdraw");
1205+
1206+
// Reset lock
1207+
delegation.tokensLocked = 0;
1208+
delegation.tokensLockedUntil = 0;
1209+
1210+
emit StakeDelegatedWithdrawn(_indexer, _delegator, tokensToWithdraw);
1211+
1212+
// -- Effects --
1213+
1214+
if (_delegateToIndexer != address(0)) {
1215+
// Re-delegate tokens to a new indexer
1216+
_delegate(_delegator, _delegateToIndexer, tokensToWithdraw);
1217+
} else {
1218+
// Return tokens to the delegator
1219+
require(graphToken().transfer(_delegator, tokensToWithdraw), "!transfer");
1220+
}
1221+
1222+
return tokensToWithdraw;
1223+
}
1224+
11881225
/**
11891226
* @dev Collect the delegation rewards for query fees.
11901227
* This function will assign the collected fees to the delegation pool.

graph.config.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,10 @@ contracts:
7373
maxAllocationEpochs: 24 # Based on epoch length this is ~1 day (in epochs)
7474
- fn: "setThawingPeriod"
7575
thawingPeriod: 275 # ~1 hour (in blocks)
76+
- fn: "setDelegationUnbondingPeriod"
77+
delegationUnbondingPeriod: 2 # epochs
7678
- fn: "setDelegationRatio"
77-
delegationRatio: 1 # 100% (multiplier)
79+
delegationRatio: 100 # 100x (multiplier)
7880
- fn: "setProtocolPercentage"
7981
protocolPercentage: 10000 # 1% (in basis points)
8082
- fn: "setRebateRatio"

test/lib/deployment.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ export const defaults = {
4040
channelDisputeEpochs: 1,
4141
maxAllocationEpochs: 5,
4242
thawingPeriod: 20, // in blocks
43+
delegationUnbondingPeriod: 1, // in epochs
4344
alphaNumerator: 85,
4445
alphaDenominator: 100,
4546
},
@@ -211,6 +212,9 @@ export async function deployStaking(deployer: Signer, controller: string): Promi
211212
await staking.connect(deployer).setChannelDisputeEpochs(defaults.staking.channelDisputeEpochs)
212213
await staking.connect(deployer).setMaxAllocationEpochs(defaults.staking.maxAllocationEpochs)
213214
await staking.connect(deployer).setThawingPeriod(defaults.staking.thawingPeriod)
215+
await staking
216+
.connect(deployer)
217+
.setDelegationUnbondingPeriod(defaults.staking.delegationUnbondingPeriod)
214218
await staking
215219
.connect(deployer)
216220
.setRebateRatio(defaults.staking.alphaNumerator, defaults.staking.alphaDenominator)

test/staking/delegation.test.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -381,6 +381,11 @@ describe('Staking::Delegation', () => {
381381
await advanceToNextEpoch(epochManager)
382382
await shouldUndelegate(delegator, toGRT('25'))
383383
})
384+
385+
it('should undelegate and withdraw freed tokens from unbonding period', async function () {
386+
await shouldDelegate(delegator, toGRT('100'))
387+
await shouldUndelegate(delegator2, toGRT('50'))
388+
})
384389
})
385390

386391
describe('withdraw', function () {

0 commit comments

Comments
 (0)