-
Notifications
You must be signed in to change notification settings - Fork 1
SOV-4106 Feat: Fee sharing collector with direct transfer #2
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
base: bobDevelopment
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| // SPDX-License-Identifier: GPL-2.0-or-later | ||
| pragma solidity 0.8.19; | ||
|
|
||
| /// @title Minimal ERC20 interface for Uniswap | ||
| /// @notice Contains a subset of the full ERC20 interface that is used in Uniswap V3 | ||
| interface IFeeProtocolCollector { | ||
| function transferTokens(address _token, uint96 _amount) external; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -7,6 +7,8 @@ import '../libraries/TokenFlow.sol'; | |
| import '../libraries/SafeCast.sol'; | ||
| import './StorageLayout.sol'; | ||
|
|
||
| import '../interfaces/IFeeProtocolCollector.sol'; | ||
|
|
||
| /* @title Protocol Account Mixin | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the ProtocolAccount contract is inherited in WarmPath, but seems not to be used there. |
||
| * @notice Tracks and pays out the accumulated protocol fees across the entire exchange | ||
| * These are the fees belonging to the SdexSwap protocol, not the liquidity | ||
|
|
@@ -16,6 +18,9 @@ import './StorageLayout.sol'; | |
| contract ProtocolAccount is StorageLayout { | ||
| using SafeCast for uint256; | ||
| using TokenFlow for address; | ||
|
|
||
| address public constant PROTOCOL_FEES_RECEIVER_HASH = | ||
| address(uint160(uint256(keccak256("PROTOCOL_FEES_RECEIVER_HASH")))); | ||
|
|
||
| /* @notice Called at the completion of a swap event, incrementing any protocol | ||
| * fees accumulated in the swap. */ | ||
|
|
@@ -35,14 +40,27 @@ contract ProtocolAccount is StorageLayout { | |
| } | ||
|
|
||
| /* @notice Pays out the earned, but unclaimed protocol fees in the pool. | ||
| * @param recv - The receiver of the protocol fees. | ||
| * @param token - The token address of the quote token. */ | ||
| function disburseProtocolFees (address recv, address token) internal { | ||
| uint128 collected = feesAccum_[token]; | ||
| feesAccum_[token] = 0; | ||
| if (collected > 0) { | ||
| bytes32 payoutKey = keccak256(abi.encode(recv, token)); | ||
| userBals_[payoutKey].surplusCollateral_ += collected; | ||
| * @param tokens - The token address of the quote token. | ||
| */ | ||
| function disburseProtocolFees (address[] memory tokens) internal { | ||
| for(uint256 i = 0; i < tokens.length; i++) { | ||
| address token = tokens[i]; | ||
| uint128 collected = feesAccum_[token]; | ||
| feesAccum_[token] = 0; | ||
| if (collected > 0) { | ||
| /** | ||
| * directly deposit token to fee protocol collector | ||
| */ | ||
| bytes32 payoutKey = keccak256(abi.encode(PROTOCOL_FEES_RECEIVER_HASH, token)); | ||
| userBals_[payoutKey].surplusCollateral_ += collected; | ||
| require(userBals_[payoutKey].surplusCollateral_ <= type(uint96).max, "Value exceeds uint96 range"); | ||
|
|
||
| uint256 amountToTransfer = uint96(userBals_[payoutKey].surplusCollateral_); | ||
|
|
||
| IERC20Minimal(token).approve(treasury_, amountToTransfer); | ||
| IFeeProtocolCollector(treasury_).transferTokens(token, uint96(amountToTransfer)); | ||
| userBals_[payoutKey].surplusCollateral_ = 0; | ||
| } | ||
| } | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -148,44 +148,39 @@ describe('Pool Governance', () => { | |
| }) | ||
|
|
||
|
|
||
| it("collect treasury", async() => { | ||
| it("successfully collect treasury without time delay", async() => { | ||
| await test.testRevisePool(feeRate, 128, 1) // Turn on protocol fee | ||
| await pool.connect(await test.auth).protocolCmd(test.COLD_PROXY, transferCmd(policy.address), true) | ||
| await pool.connect(await test.auth).protocolCmd(test.COLD_PROXY, transferCmd(policy2.address), true) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why is policy replaced with policy2 here? |
||
| await test.testMintAmbient(10000) | ||
| await test.testSwap(true, false, 100000, MAX_PRICE) | ||
|
|
||
| await treasury.treasuryResolution(pool.address, test.COLD_PROXY, treasurySetCmd(policy2.address), true) | ||
| await hre.ethers.provider.send("evm_increaseTime", [3600*24*7+1]) // 7 days | ||
| await treasury2.treasuryResolution(pool.address, test.COLD_PROXY, treasurySetCmd(treasury2.address), true) | ||
|
|
||
| // Unauthorized attempts to collect treasury | ||
| await expect(pool.protocolCmd(test.COLD_PROXY, collectCmd(), true)).to.be.reverted | ||
| await expect(ops.opsResolution(pool.address, test.COLD_PROXY, collectCmd())).to.be.reverted | ||
| await expect(ops.treasuryResolution(pool.address, test.COLD_PROXY, collectCmd(), true)).to.be.reverted | ||
| await expect(treasury.userCmd(pool.address, test.COLD_PROXY, collectCmd())).to.be.revertedWith("Only Treasury") | ||
|
|
||
| // Successful treasury payout | ||
| let snap = await (await test.query).querySurplus(policy2.address, baseToken.address) | ||
| await treasury.treasuryResolution(pool.address, test.COLD_PROXY, collectCmd(), true) | ||
| expect(await (await test.query).querySurplus(policy2.address, baseToken.address)).to.gt(snap); | ||
| let snap = await (await test.query).querySurplus(treasury2.address, baseToken.address) | ||
| await treasury2.userCmd(pool.address, test.COLD_PROXY, collectCmd()) | ||
| expect(await (await test.query).querySurplus(treasury2.address, baseToken.address)).to.gt(snap); | ||
| }) | ||
|
|
||
| it("collect treasury time delay", async() => { | ||
| it("successfully collect treasury with time delay", async() => { | ||
| await test.testRevisePool(feeRate, 128, 1) // Turn on protocol fee | ||
| await pool.connect(await test.auth).protocolCmd(test.COLD_PROXY, transferCmd(policy.address), true) | ||
| await pool.connect(await test.auth).protocolCmd(test.COLD_PROXY, transferCmd(policy2.address), true) | ||
| await test.testMintAmbient(10000) | ||
| await test.testSwap(true, false, 100000, MAX_PRICE) | ||
|
|
||
| await treasury.treasuryResolution(pool.address, test.COLD_PROXY, treasurySetCmd(policy2.address), true) | ||
| await treasury2.treasuryResolution(pool.address, test.COLD_PROXY, treasurySetCmd(treasury2.address), true) | ||
|
|
||
| // Will fail because treasury can only be collected 7 days after treasury address is set | ||
| await expect(treasury.treasuryResolution(pool.address, test.COLD_PROXY, collectCmd(), true)).to.be.reverted | ||
| await hre.ethers.provider.send("evm_increaseTime", [3600*24*6]) // 6 days | ||
| await expect(treasury.treasuryResolution(pool.address, test.COLD_PROXY, collectCmd(), true)).to.be.reverted | ||
| await hre.ethers.provider.send("evm_increaseTime", [3600*24+1]) // One more day... treasury valid | ||
| // Unauthorized attempts to collect treasury | ||
| await expect(treasury.userCmd(pool.address, test.COLD_PROXY, collectCmd())).to.be.revertedWith("Only Treasury") | ||
|
|
||
| await hre.ethers.provider.send("evm_increaseTime", [3600*24*6]) // 6 days | ||
| // Successful treasury payout | ||
| let snap = await (await test.query).querySurplus(policy2.address, baseToken.address) | ||
| await treasury.treasuryResolution(pool.address, test.COLD_PROXY, collectCmd(), true) | ||
| expect(await (await test.query).querySurplus(policy2.address, baseToken.address)).to.gt(snap); | ||
| let snap = await (await test.query).querySurplus(treasury2.address, baseToken.address) | ||
| await treasury2.userCmd(pool.address, test.COLD_PROXY, collectCmd()) | ||
| expect(await (await test.query).querySurplus(treasury2.address, baseToken.address)).to.gt(snap); | ||
| }) | ||
|
|
||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
to keep it universal i suggest to restore the original code
but replace
7 dayswith a constantTREASURY_START_TIME_OFFSETto have this option if needed on other chains.The BOB and default implementation should have it set to 0.