Skip to content
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
4 changes: 1 addition & 3 deletions protocol-contracts/staking/contracts/OperatorStaking.sol
Original file line number Diff line number Diff line change
Expand Up @@ -59,15 +59,13 @@ contract OperatorStaking is ERC1363Upgradeable, ReentrancyGuardTransient, UUPSUp
* @dev Emitted when a redeem request is made.
* @param controller The controller address for the redeem request.
* @param owner The owner of the shares being redeemed.
* @param requestId The unique identifier for the redeem request.
* @param sender The address that initiated the redeem request.
* @param shares The number of shares requested to redeem.
* @param releaseTime The timestamp when the shares can be released.
*/
event RedeemRequest(
address indexed controller,
address indexed owner,
uint256 indexed requestId,
address sender,
uint256 shares,
uint48 releaseTime
Expand Down Expand Up @@ -217,7 +215,7 @@ contract OperatorStaking is ERC1363Upgradeable, ReentrancyGuardTransient, UUPSUp
assert(releaseTime >= lastReleaseTime); // should never happen
$._redeemRequests[controller].push(releaseTime, controllerSharesRedeemed + shares);

emit RedeemRequest(controller, ownerRedeem, 0, msg.sender, shares, releaseTime);
emit RedeemRequest(controller, ownerRedeem, msg.sender, shares, releaseTime);

return releaseTime;
}
Expand Down
75 changes: 58 additions & 17 deletions protocol-contracts/staking/test/OperatorStaking.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,6 @@ describe('OperatorStaking', function () {
.withArgs(
this.delegator1,
this.delegator1,
0,
this.delegator1,
ethers.parseEther('1'),
BigInt(currentTimestamp) + 1n + (await this.protocolStaking.unstakeCooldownPeriod()),
Expand Down Expand Up @@ -258,8 +257,16 @@ describe('OperatorStaking', function () {
it('should not redeem twice', async function () {
await this.mock.connect(this.delegator2).deposit(ethers.parseEther('5'), this.delegator2);
await this.mock.connect(this.delegator1).deposit(ethers.parseEther('10'), this.delegator1);
await this.mock.connect(this.delegator1).requestRedeem(ethers.parseEther('1'), this.delegator1, this.delegator1);
await this.mock.connect(this.delegator2).requestRedeem(ethers.parseEther('1'), this.delegator2, this.delegator2);
await expect(
this.mock.connect(this.delegator1).requestRedeem(ethers.parseEther('1'), this.delegator1, this.delegator1),
)
.to.emit(this.mock, 'RedeemRequest')
.withArgs(this.delegator1, this.delegator1, this.delegator1, ethers.parseEther('1'), anyValue);
await expect(
this.mock.connect(this.delegator2).requestRedeem(ethers.parseEther('1'), this.delegator2, this.delegator2),
)
.to.emit(this.mock, 'RedeemRequest')
.withArgs(this.delegator2, this.delegator2, this.delegator2, ethers.parseEther('1'), anyValue);

await timeIncreaseNoMine(60);

Expand All @@ -285,7 +292,11 @@ describe('OperatorStaking', function () {

it('should revert on redeem more than available', async function () {
await this.mock.connect(this.delegator1).deposit(ethers.parseEther('10'), this.delegator1);
await this.mock.connect(this.delegator1).requestRedeem(ethers.parseEther('1'), this.delegator1, this.delegator1);
await expect(
this.mock.connect(this.delegator1).requestRedeem(ethers.parseEther('1'), this.delegator1, this.delegator1),
)
.to.emit(this.mock, 'RedeemRequest')
.withArgs(this.delegator1, this.delegator1, this.delegator1, ethers.parseEther('1'), anyValue);

await timeIncreaseNoMine(10);
await expect(this.mock.connect(this.delegator1).redeem(ethers.parseEther('1'), this.delegator1, this.delegator1))
Expand All @@ -295,15 +306,23 @@ describe('OperatorStaking', function () {

it('should be able to redeem a second time', async function () {
await this.mock.connect(this.delegator1).deposit(ethers.parseEther('10'), this.delegator1);
await this.mock.connect(this.delegator1).requestRedeem(ethers.parseEther('1'), this.delegator1, this.delegator1);
await expect(
this.mock.connect(this.delegator1).requestRedeem(ethers.parseEther('1'), this.delegator1, this.delegator1),
)
.to.emit(this.mock, 'RedeemRequest')
.withArgs(this.delegator1, this.delegator1, this.delegator1, ethers.parseEther('1'), anyValue);

await timeIncreaseNoMine(60);

await expect(this.mock.connect(this.delegator1).redeem(ethers.MaxUint256, this.delegator1, this.delegator1))
.to.emit(this.token, 'Transfer')
.withArgs(this.mock, this.delegator1, ethers.parseEther('1'));

await this.mock.connect(this.delegator1).requestRedeem(ethers.parseEther('2'), this.delegator1, this.delegator1);
await expect(
this.mock.connect(this.delegator1).requestRedeem(ethers.parseEther('2'), this.delegator1, this.delegator1),
)
.to.emit(this.mock, 'RedeemRequest')
.withArgs(this.delegator1, this.delegator1, this.delegator1, ethers.parseEther('2'), anyValue);

await timeIncreaseNoMine(60);

Expand All @@ -315,7 +334,11 @@ describe('OperatorStaking', function () {
it('via separate controller', async function () {
const controller = this.accounts[0];
await this.mock.connect(this.delegator1).deposit(ethers.parseEther('10'), this.delegator1);
await this.mock.connect(this.delegator1).requestRedeem(ethers.parseEther('1'), controller, this.delegator1);
await expect(
this.mock.connect(this.delegator1).requestRedeem(ethers.parseEther('1'), controller, this.delegator1),
)
.to.emit(this.mock, 'RedeemRequest')
.withArgs(controller, this.delegator1, this.delegator1, ethers.parseEther('1'), anyValue);

await timeIncreaseNoMine(60);

Expand Down Expand Up @@ -344,7 +367,11 @@ describe('OperatorStaking', function () {
await this.mock.connect(this.delegator1).deposit(ethers.parseEther('1'), this.delegator1);
await this.mock.connect(this.delegator1).approve(approvedActor, ethers.parseEther('1'));

await this.mock.connect(approvedActor).requestRedeem(ethers.parseEther('1'), this.delegator1, this.delegator1);
await expect(
this.mock.connect(approvedActor).requestRedeem(ethers.parseEther('1'), this.delegator1, this.delegator1),
)
.to.emit(this.mock, 'RedeemRequest')
.withArgs(this.delegator1, this.delegator1, approvedActor, ethers.parseEther('1'), anyValue);
});

it('should fail via unapproved actor', async function () {
Expand All @@ -364,13 +391,23 @@ describe('OperatorStaking', function () {
await this.mock.connect(this.delegator2).deposit(ethers.parseEther('1'), this.delegator2);
await this.mock.connect(delegator3).deposit(ethers.parseEther('1'), delegator3);

await this.mock.connect(this.delegator1).requestRedeem(ethers.parseEther('1'), this.delegator1, this.delegator1);
await expect(
this.mock.connect(this.delegator1).requestRedeem(ethers.parseEther('1'), this.delegator1, this.delegator1),
)
.to.emit(this.mock, 'RedeemRequest')
.withArgs(this.delegator1, this.delegator1, this.delegator1, ethers.parseEther('1'), anyValue);
await timeIncreaseNoMine(30);

await this.mock.connect(this.delegator2).requestRedeem(ethers.parseEther('1'), this.delegator2, this.delegator2);
await expect(
this.mock.connect(this.delegator2).requestRedeem(ethers.parseEther('1'), this.delegator2, this.delegator2),
)
.to.emit(this.mock, 'RedeemRequest')
.withArgs(this.delegator2, this.delegator2, this.delegator2, ethers.parseEther('1'), anyValue);

await this.protocolStaking.connect(this.admin).setUnstakeCooldownPeriod(30);
await this.mock.connect(delegator3).requestRedeem(ethers.parseEther('1'), delegator3, delegator3);
await expect(this.mock.connect(delegator3).requestRedeem(ethers.parseEther('1'), delegator3, delegator3))
.to.emit(this.mock, 'RedeemRequest')
.withArgs(delegator3, delegator3, delegator3, ethers.parseEther('1'), anyValue);

// delegator 3 will need to wait 59 seconds

Expand All @@ -397,9 +434,11 @@ describe('OperatorStaking', function () {
});

it('should be allowed to redeem on behalf of authorized controller', async function () {
await this.mock
.connect(this.delegator1)
.requestRedeem(ethers.parseEther('1'), this.delegator1, this.delegator1);
await expect(
this.mock.connect(this.delegator1).requestRedeem(ethers.parseEther('1'), this.delegator1, this.delegator1),
)
.to.emit(this.mock, 'RedeemRequest')
.withArgs(this.delegator1, this.delegator1, this.delegator1, ethers.parseEther('1'), anyValue);

await timeIncreaseNoMine(60);

Expand All @@ -409,9 +448,11 @@ describe('OperatorStaking', function () {
});

it('should not be allowed to redeem on behalf of other controller', async function () {
await this.mock
.connect(this.delegator1)
.requestRedeem(ethers.parseEther('1'), this.delegator2, this.delegator1);
await expect(
this.mock.connect(this.delegator1).requestRedeem(ethers.parseEther('1'), this.delegator2, this.delegator1),
)
.to.emit(this.mock, 'RedeemRequest')
.withArgs(this.delegator2, this.delegator1, this.delegator1, ethers.parseEther('1'), anyValue);

await timeIncreaseNoMine(60);

Expand Down