Skip to content

Let the staking provider to refresh KEEP stake owner #103

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Aug 23, 2022
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
3 changes: 2 additions & 1 deletion contracts/staking/IStaking.sol
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@ interface IStaking {
address authorizer
) external;

/// @notice Refresh Keep stake owner. Can be called only by the old owner.
/// @notice Refresh Keep stake owner. Can be called only by the old owner
/// or their staking provider.
/// @dev The staking provider in T staking contract is the legacy KEEP
/// staking contract operator.
function refreshKeepStakeOwner(address stakingProvider) external;
Expand Down
13 changes: 7 additions & 6 deletions contracts/staking/KeepStake.sol
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,13 @@ interface IManagedGrant {
/// KEEP stakers to use their stakes in T network and weights them based
/// on KEEP<>T token ratio. KEEP stake owner is cached in T staking
/// contract and used to restrict access to all functions only owner or
/// operator should call. To cache KEEP staking contract in T staking
/// contract, it fitst needs to resolve the owner. Resolving liquid
/// KEEP stake owner is easy. Resolving token grant stake owner is
/// complicated and not possible to do on-chain from a contract external
/// to KEEP TokenStaking contract. Keep TokenStaking knows the grant ID
/// but does not expose it externally.
/// operator should call. To cache KEEP stake owner in T staking
/// contract, T staking contract first needs to resolve the owner.
///
/// Resolving liquid KEEP stake owner is easy. Resolving token grant
/// stake owner is complicated and not possible to do on-chain from
/// a contract external to KEEP TokenStaking contract. Keep TokenStaking
/// knows the grant ID but does not expose it externally.
///
/// KeepStake contract addresses this problem by exposing
/// operator-owner mappings snapshotted off-chain based on events and
Expand Down
13 changes: 7 additions & 6 deletions contracts/staking/TokenStaking.sol
Original file line number Diff line number Diff line change
Expand Up @@ -397,17 +397,18 @@ contract TokenStaking is Initializable, IStaking, Checkpoints {
);
}

/// @notice Refresh Keep stake owner. Can be called only by the old owner.
/// @notice Refresh Keep stake owner. Can be called only by the old owner
/// or their staking provider.
/// @dev The staking provider in T staking contract is the legacy KEEP
/// staking contract operator.
function refreshKeepStakeOwner(address stakingProvider) external override {
function refreshKeepStakeOwner(address stakingProvider)
external
override
onlyOwnerOrStakingProvider(stakingProvider)
{
StakingProviderInfo storage stakingProviderStruct = stakingProviders[
stakingProvider
];
require(
stakingProviderStruct.owner == msg.sender,
"Caller is not owner"
);
address newOwner = keepStake.resolveOwner(stakingProvider);

emit OwnerRefreshed(
Expand Down
10 changes: 5 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,11 @@
"eslint": "^7.27.0",
"eslint-config-keep": "github:keep-network/eslint-config-keep#0c27ade",
"ethereum-waffle": "^3.4.0",
"ethers": "^5.4.1",
"hardhat": "^2.6.8",
"hardhat-contract-sizer": "^2.1.1",
"hardhat-deploy": "^0.9.4",
"hardhat-gas-reporter": "^1.0.4",
"ethers": "^5.5.3",
"hardhat": "^2.8.3",
"hardhat-contract-sizer": "^2.5.0",
"hardhat-deploy": "^0.9.27",
"hardhat-gas-reporter": "^1.0.6",
"prettier": "^2.3.2",
"prettier-plugin-sh": "^0.7.1",
"prettier-plugin-solidity": "^1.0.0-beta.14 ",
Expand Down
190 changes: 102 additions & 88 deletions test/staking/TokenStaking.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -864,11 +864,11 @@ describe("TokenStaking", () => {
tokenStaking
.connect(stakingProvider)
.refreshKeepStakeOwner(stakingProvider.address)
).to.be.revertedWith("Caller is not owner")
).to.be.revertedWith("Not owner or provider")
})
})

context("when caller is not old owner", () => {
context("when caller is neither old owner nor staking provider", () => {
it("should revert", async () => {
await tToken
.connect(staker)
Expand All @@ -883,108 +883,122 @@ describe("TokenStaking", () => {
)
await expect(
tokenStaking
.connect(stakingProvider)
.connect(authorizer)
.refreshKeepStakeOwner(stakingProvider.address)
).to.be.revertedWith("Caller is not owner")
).to.be.revertedWith("Not owner or provider")
})
})

context("when grantee was not changed", () => {
let tx
const contextRefreshKeepStakeOwner = (getCaller) => {
context("when grantee was not changed", () => {
let tx

beforeEach(async () => {
const createdAt = 1
await keepStakingMock.setOperator(
stakingProvider.address,
staker.address,
beneficiary.address,
authorizer.address,
createdAt,
0,
initialStakerBalance
)
await keepStakingMock.setEligibility(
stakingProvider.address,
tokenStaking.address,
true
)
await tokenStaking.stakeKeep(stakingProvider.address)
beforeEach(async () => {
const createdAt = 1
await keepStakingMock.setOperator(
stakingProvider.address,
staker.address,
beneficiary.address,
authorizer.address,
createdAt,
0,
initialStakerBalance
)
await keepStakingMock.setEligibility(
stakingProvider.address,
tokenStaking.address,
true
)
await tokenStaking.stakeKeep(stakingProvider.address)

tx = await tokenStaking
.connect(staker)
.refreshKeepStakeOwner(stakingProvider.address)
})
tx = await tokenStaking
.connect(getCaller())
.refreshKeepStakeOwner(stakingProvider.address)
})

it("should not update owner", async () => {
expect(
await tokenStaking.rolesOf(stakingProvider.address)
).to.deep.equal([
staker.address,
beneficiary.address,
authorizer.address,
])
})
it("should not update owner", async () => {
expect(
await tokenStaking.rolesOf(stakingProvider.address)
).to.deep.equal([
staker.address,
beneficiary.address,
authorizer.address,
])
})

it("should emit OwnerRefreshed", async () => {
await expect(tx)
.to.emit(tokenStaking, "OwnerRefreshed")
.withArgs(stakingProvider.address, staker.address, staker.address)
it("should emit OwnerRefreshed", async () => {
await expect(tx)
.to.emit(tokenStaking, "OwnerRefreshed")
.withArgs(stakingProvider.address, staker.address, staker.address)
})
})
})

context("when grantee was changed", () => {
let tx
context("when grantee was changed", () => {
let tx

beforeEach(async () => {
const createdAt = 1
await keepStakingMock.setOperator(
stakingProvider.address,
otherStaker.address,
beneficiary.address,
authorizer.address,
createdAt,
0,
initialStakerBalance
)
await keepStakingMock.setEligibility(
stakingProvider.address,
tokenStaking.address,
true
)
await tokenStaking.stakeKeep(stakingProvider.address)
beforeEach(async () => {
const createdAt = 1
await keepStakingMock.setOperator(
stakingProvider.address,
otherStaker.address,
beneficiary.address,
authorizer.address,
createdAt,
0,
initialStakerBalance
)
await keepStakingMock.setEligibility(
stakingProvider.address,
tokenStaking.address,
true
)
await tokenStaking.stakeKeep(stakingProvider.address)

await keepStakingMock.setOperator(
stakingProvider.address,
staker.address,
beneficiary.address,
authorizer.address,
createdAt,
0,
initialStakerBalance
)
tx = await tokenStaking
.connect(otherStaker)
.refreshKeepStakeOwner(stakingProvider.address)
await keepStakingMock.setOperator(
stakingProvider.address,
staker.address,
beneficiary.address,
authorizer.address,
createdAt,
0,
initialStakerBalance
)
tx = await tokenStaking
.connect(otherStaker)
.refreshKeepStakeOwner(stakingProvider.address)
})

it("should update owner", async () => {
expect(
await tokenStaking.rolesOf(stakingProvider.address)
).to.deep.equal([
staker.address,
beneficiary.address,
authorizer.address,
])
})

it("should emit OwnerRefreshed", async () => {
await expect(tx)
.to.emit(tokenStaking, "OwnerRefreshed")
.withArgs(
stakingProvider.address,
otherStaker.address,
staker.address
)
})
})
}

it("should update owner", async () => {
expect(
await tokenStaking.rolesOf(stakingProvider.address)
).to.deep.equal([
staker.address,
beneficiary.address,
authorizer.address,
])
context("when caller is the old owner", () => {
contextRefreshKeepStakeOwner(() => {
return staker
})
})

it("should emit OwnerRefreshed", async () => {
await expect(tx)
.to.emit(tokenStaking, "OwnerRefreshed")
.withArgs(
stakingProvider.address,
otherStaker.address,
staker.address
)
context("when caller is the staking provider", () => {
contextRefreshKeepStakeOwner(() => {
return stakingProvider
})
})
})
Expand Down
Loading