Skip to content

Commit c73c143

Browse files
committed
updated exclusivity logic
1 parent e03be54 commit c73c143

File tree

3 files changed

+47
-26
lines changed

3 files changed

+47
-26
lines changed

contracts/ExclusiveGeyser.sol

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@
22
pragma solidity 0.7.6;
33
pragma abicoder v2;
44

5+
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
56
import {IUniversalVault} from "./UniversalVault.sol";
67
import {Geyser} from "./Geyser.sol";
78

89
/// @title ExclusiveGeyser
9-
/// @notice A special extension of GeyserV2 which allows staking in,
10-
/// at most one distribution program in any given time, for a given staking token.
10+
/// @notice A special extension of GeyserV2 which limits staking tokens,
11+
/// only distribution program at any given time.
1112
/// @dev Security contact: dev-support@ampleforth.org
1213
contract ExclusiveGeyser is Geyser {
1314
/// @inheritdoc Geyser
@@ -16,21 +17,31 @@ contract ExclusiveGeyser is Geyser {
1617
uint256 amount,
1718
bytes calldata permission
1819
) public override {
19-
// verify that vault has NOT locked staking token in other programs
20-
_enforceExclusiveStake(IUniversalVault(vault));
20+
// verify that vault isn't staking the same tokens in multiple programs
21+
_enforceExclusiveStake(IUniversalVault(vault), amount);
2122

2223
// continue with regular stake
2324
super.stake(vault, amount, permission);
2425
}
2526

26-
function _enforceExclusiveStake(IUniversalVault vault) private view {
27+
/// @dev Enforces that the vault can't use tokens which have already been staked.
28+
function _enforceExclusiveStake(IUniversalVault vault, uint256 amount) private view {
29+
require(amount <= computeAvailableStakingBalance(vault), "ExclusiveGeyser: expected exclusive stake");
30+
}
31+
32+
/// @notice Computes the amount of staking tokens in the vault available to be staked exclusively.
33+
function computeAvailableStakingBalance(IUniversalVault vault) public view returns (uint256) {
34+
// Iterates through the vault's locks to compute the total "stakingToken" balance staked across all geysers.
2735
address stakingToken = super.getGeyserData().stakingToken;
36+
uint256 vaultBal = IERC20(stakingToken).balanceOf(address(vault));
37+
uint256 totalStakedBal = 0;
2838
uint256 lockCount = vault.getLockSetCount();
2939
for (uint256 i = 0; i < lockCount; i++) {
3040
IUniversalVault.LockData memory lock = vault.getLockAt(i);
3141
if (lock.token == stakingToken) {
32-
require(lock.delegate == address(this), "ExclusiveGeyser: expected exclusive stake");
42+
totalStakedBal += lock.balance;
3343
}
3444
}
45+
return vaultBal - totalStakedBal;
3546
}
3647
}

frontend/src/utils/stakingToken.ts

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -141,12 +141,10 @@ const uniswapV2Pair = async (
141141
const totalSupply: BigNumber = await contract.totalSupply()
142142
const totalSupplyNumber = parseFloat(formatUnits(totalSupply, decimals))
143143

144-
const tokenCompositions = await getTokenCompositions(
145-
[token0Address, token1Address],
146-
address,
147-
signerOrProvider,
148-
[0.5, 0.5],
149-
)
144+
const tokenCompositions = await getTokenCompositions([token0Address, token1Address], address, signerOrProvider, [
145+
0.5,
146+
0.5,
147+
])
150148
const [token0Symbol, token1Symbol] = tokenCompositions.map((c) => c.symbol)
151149
const marketCap = getMarketCap(tokenCompositions)
152150

@@ -183,12 +181,10 @@ const getMooniswap = async (tokenAddress: string, signerOrProvider: SignerOrProv
183181

184182
const totalSupplyNumber = parseFloat(formatUnits(totalSupply, decimals))
185183

186-
const tokenCompositions = await getTokenCompositions(
187-
[token0Address, token1Address],
188-
address,
189-
signerOrProvider,
190-
[0.5, 0.5],
191-
)
184+
const tokenCompositions = await getTokenCompositions([token0Address, token1Address], address, signerOrProvider, [
185+
0.5,
186+
0.5,
187+
])
192188
const marketCap = getMarketCap(tokenCompositions)
193189

194190
return {

test/ExclusiveGeyser.ts

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1031,8 +1031,9 @@ describe('ExclusiveGeyser', function () {
10311031
})
10321032
describe('with insufficient balance', function () {
10331033
it('should fail', async function () {
1034+
// Exclusive stake condition fails first
10341035
await expect(stake(user, geyser, vault, stakingToken, stakeAmount.mul(2))).to.be.revertedWith(
1035-
'UniversalVault: insufficient balance',
1036+
'ExclusiveGeyser: expected exclusive stake',
10361037
)
10371038
})
10381039
})
@@ -1123,7 +1124,7 @@ describe('ExclusiveGeyser', function () {
11231124
}
11241125
})
11251126
it('should fail', async function () {
1126-
await expect(stake(user, geyser, vault, stakingToken, stakeAmount.div(quantity))).to.be.revertedWith(
1127+
await expect(stake(user, geyser, vault, stakingToken, 1)).to.be.revertedWith(
11271128
'Geyser: MAX_STAKES_PER_VAULT reached',
11281129
)
11291130
})
@@ -1176,18 +1177,31 @@ describe('ExclusiveGeyser', function () {
11761177
]
11771178
otherGeyser = await deployGeyser(args, 'Geyser')
11781179
await otherGeyser.connect(admin).registerVaultFactory(vaultFactory.address)
1179-
await stake(user, otherGeyser, vault, stakingToken, 1)
1180+
await stake(user, otherGeyser, vault, stakingToken, stakeAmount)
11801181
})
11811182
it('should fail', async function () {
11821183
await expect(stake(user, geyser, vault, stakingToken, stakeAmount)).to.be.revertedWith(
11831184
'ExclusiveGeyser: expected exclusive stake',
11841185
)
1186+
expect(await geyser.computeAvailableStakingBalance(vault.address)).to.eq(0)
1187+
expect(await vault.checkBalances()).to.eq(true)
11851188
})
1186-
it('should not fail when there are no locks', async function () {
1187-
await unstakeAndClaim(user, otherGeyser, vault, stakingToken, 1)
1188-
await expect(stake(user, geyser, vault, stakingToken, stakeAmount)).not.to.be.revertedWith(
1189-
'ExclusiveGeyser: expected exclusive stake',
1190-
)
1189+
1190+
it('should NOT fail when there is some unlocked amount', async function () {
1191+
await unstakeAndClaim(user, otherGeyser, vault, stakingToken, 15)
1192+
await expect(stake(user, geyser, vault, stakingToken, 14)).not.to.be.reverted
1193+
expect(await geyser.computeAvailableStakingBalance(vault.address)).to.eq(1)
1194+
expect(await vault.checkBalances()).to.eq(true)
1195+
await expect(stake(user, geyser, vault, stakingToken, 1)).not.to.be.reverted
1196+
expect(await geyser.computeAvailableStakingBalance(vault.address)).to.eq(0)
1197+
expect(await vault.checkBalances()).to.eq(true)
1198+
})
1199+
1200+
it('should NOT fail when there are no locks', async function () {
1201+
await unstakeAndClaim(user, otherGeyser, vault, stakingToken, stakeAmount)
1202+
await expect(stake(user, geyser, vault, stakingToken, stakeAmount)).not.to.be.reverted
1203+
expect(await geyser.computeAvailableStakingBalance(vault.address)).to.eq(0)
1204+
expect(await vault.checkBalances()).to.eq(true)
11911205
})
11921206
})
11931207
})

0 commit comments

Comments
 (0)