Skip to content

Commit

Permalink
Merge pull request #17 from jbx-protocol/experiment/exact-swap-out
Browse files Browse the repository at this point in the history
feat: extra eth handling
  • Loading branch information
simon-something authored Sep 30, 2023
2 parents 9188f09 + 85cd53c commit 4a76e59
Show file tree
Hide file tree
Showing 18 changed files with 2,318 additions and 7,750 deletions.
2 changes: 1 addition & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"solidity.compileUsingRemoteVersion": "v0.8.16+commit.07a7930e"
"solidity.compileUsingRemoteVersion": "v0.8.20+commit.a1b79de6"
}
9 changes: 7 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

Provides a datasource and delegate which maximise the project token received by the contributor when they call `pay` on the terminal. In order to do so, the delegate will either mint new tokens ("vanilla" path, bypassing the delegate) or swap existing token in an Uniswap V3 pool ("buyback" path), depending on the best quote available at the time of the call.

This first iteration is only compatible with ETH terminals.
This first iteration is only compatible with 18-decimals token terminals.

## Design
### Flow
Expand All @@ -29,4 +29,9 @@ Maximizing the project token received by the contributor while leveling the fund
## Risk & trade-offs
- This delegate is, for now, only compatible with ETH as terminal token.
- This delegate relies on the liquidity available in an Uniswap V3. If LP migrate to a new pool or another DEX, this delegate would need to be redeployed.
- A low liquidity might, if the max slippage isn't set properly, lead to an actual amount of token received lower than expected.
- A low liquidity might, if the max slippage isn't set properly, lead to an actual amount of token received lower than expected.

## Future work
- Non-18 decimals token should use a non fixed decimals (ln167, ln199, ln284 - `_data.amount.decimals`) and the tests should then use this same decimals (or the terminal decimal for forked tests, `IJBSingleTokenPaymentTerminal(address(jbEthPaymentTerminal)).decimals()`)
- Invariant are only partially tested (total supply hold in mint case, pool needfs additional tooling as we rely on hardcoded pool hash for create2)
- A first version was designed to be used as a BBD for an unique project (project token, pool, etc being immutables), in order to keep the gas cost as low as possible. This might be resumed and further tested if a need arises.
776 changes: 415 additions & 361 deletions contracts/JBBuybackDelegate.sol

Large diffs are not rendered by default.

644 changes: 0 additions & 644 deletions contracts/JBGenericBuybackDelegate.sol

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -12,63 +12,61 @@ import {IUniswapV3SwapCallback} from "@uniswap/v3-core/contracts/interfaces/call

import {IWETH9} from "./external/IWETH9.sol";

interface IJBGenericBuybackDelegate is IJBPayDelegate3_1_1, IJBFundingCycleDataSource3_1_1, IUniswapV3SwapCallback {
interface IJBBuybackDelegate is IJBPayDelegate3_1_1, IJBFundingCycleDataSource3_1_1, IUniswapV3SwapCallback {
/////////////////////////////////////////////////////////////////////
// Errors //
/////////////////////////////////////////////////////////////////////

error JuiceBuyback_MaximumSlippage();
error JuiceBuyback_InsufficientPayAmount();
error JuiceBuyback_NotEnoughTokensReceived();
error JuiceBuyback_NewSecondsAgoTooLow();
error JuiceBuyback_NoProjectToken();
error JuiceBuyback_PoolAlreadySet();
error JuiceBuyback_TransferFailed();
error JuiceBuyback_InvalidTwapDelta();
error JuiceBuyback_InvalidTwapPeriod();
error JuiceBuyback_InvalidTwapSlippageTolerance();
error JuiceBuyback_InvalidTwapWindow();
error JuiceBuyback_Unauthorized();

/////////////////////////////////////////////////////////////////////
// Events //
/////////////////////////////////////////////////////////////////////

event BuybackDelegate_Swap(uint256 indexed projectId, uint256 amountEth, uint256 amountOut);
event BuybackDelegate_Mint(uint256 indexed projectId);
event BuybackDelegate_SecondsAgoChanged(uint256 indexed projectId, uint256 oldSecondsAgo, uint256 newSecondsAgo);
event BuybackDelegate_TwapDeltaChanged(uint256 indexed projectId, uint256 oldTwapDelta, uint256 newTwapDelta);
event BuybackDelegate_PendingSweep(address indexed beneficiary, address indexed token, uint256 amount);
event BuybackDelegate_PoolAdded(uint256 indexed projectId, address indexed terminalToken, address newPool);
event BuybackDelegate_Swap(uint256 indexed projectId, uint256 amountIn, IUniswapV3Pool pool, uint256 amountOut, address caller);
event BuybackDelegate_Mint(uint256 indexed projectId, uint256 amountIn, uint256 tokenCount, address caller);
event BuybackDelegate_TwapWindowChanged(uint256 indexed projectId, uint256 oldSecondsAgo, uint256 newSecondsAgo, address caller);
event BuybackDelegate_TwapSlippageToleranceChanged(uint256 indexed projectId, uint256 oldTwapDelta, uint256 newTwapDelta, address caller);
event BuybackDelegate_PoolAdded(uint256 indexed projectId, address indexed terminalToken, address newPool, address caller);

/////////////////////////////////////////////////////////////////////
// Getters //
/////////////////////////////////////////////////////////////////////

function SLIPPAGE_DENOMINATOR() external view returns (uint256);
function MIN_TWAP_DELTA() external view returns (uint256);
function MAX_TWAP_DELTA() external view returns (uint256);
function MIN_SECONDS_AGO() external view returns (uint256);
function MAX_SECONDS_AGO() external view returns (uint256);
function MIN_TWAP_SLIPPAGE_TOLERANCE() external view returns (uint256);
function MAX_TWAP_SLIPPAGE_TOLERANCE() external view returns (uint256);
function MIN_TWAP_WINDOW() external view returns (uint256);
function MAX_TWAP_WINDOW() external view returns (uint256);
function UNISWAP_V3_FACTORY() external view returns (address);
function DIRECTORY() external view returns (IJBDirectory);
function CONTROLLER() external view returns (IJBController3_1);
function PROJECTS() external view returns (IJBProjects);
function WETH() external view returns (IWETH9);
function delegateId() external view returns (bytes4);
function poolOf(uint256 _projectId, address _terminalToken) external view returns (IUniswapV3Pool _pool);
function secondsAgoOf(uint256 _projectId) external view returns (uint32 _seconds);
function twapDeltaOf(uint256 _projectId) external view returns (uint256 _delta);
function projectTokenOf(uint256 _projectId) external view returns (address projectTokenOf);
function sweepBalanceOf(address _beneficiary, address _token) external view returns (uint256 _balance);
function totalSweepBalance(address _token) external view returns (uint256 _contractBalance);
function DELEGATE_ID() external view returns (bytes4);
function poolOf(uint256 projectId, address terminalToken) external view returns (IUniswapV3Pool pool);
function twapWindowOf(uint256 projectId) external view returns (uint32 window);
function twapSlippageToleranceOf(uint256 projectId) external view returns (uint256 slippageTolerance);
function projectTokenOf(uint256 projectId) external view returns (address projectTokenOf);

/////////////////////////////////////////////////////////////////////
// State-changing functions //
/////////////////////////////////////////////////////////////////////

function setPoolFor(uint256 _projectId, uint24 _fee, uint32 _secondsAgo, uint256 _twapDelta, address _terminalToken)
function setPoolFor(uint256 projectId, uint24 fee, uint32 twapWindow, uint256 twapSlippageTolerance, address terminalToken)
external
returns (IUniswapV3Pool _newPool);
returns (IUniswapV3Pool newPool);

function changeSecondsAgo(uint256 _projectId, uint32 _newSecondsAgo) external;
function setTwapWindowOf(uint256 projectId, uint32 newWindow) external;

function setTwapDelta(uint256 _projectId, uint256 _newDelta) external;
function sweep(address _beneficiary, address _token) external;
function setTwapSlippageToleranceOf(uint256 projectId, uint256 newSlippageTolerance) external;
}
30 changes: 18 additions & 12 deletions contracts/scripts/Deploy.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,9 @@ pragma solidity ^0.8.20;

import "forge-std/Script.sol";

import "../JBGenericBuybackDelegate.sol";
import "../JBBuybackDelegate.sol";

contract DeployGeneric is Script {

uint256 _chainId = block.chainid;
string _network;

Expand All @@ -26,33 +25,40 @@ contract DeployGeneric is Script {
bytes4 constant _delegateId = bytes4("BUYB");

function setUp() public {
if(_chainId == 1) {
if (_chainId == 1) {
_network = "mainnet";
_weth = _wethMainnet;
_factory = _factoryMainnet;
}
else if(_chainId == 5) {
} else if (_chainId == 5) {
_network = "goerli";
_weth = _wethGoerli;
_factory = _factoryGoerli;
}
else if(_chainId == 1337) {
} else if (_chainId == 1337) {
_network = "sepolia";
_weth = _wethSepolia;
_factory = _factorySepolia;
} else {
revert("Invalid RPC / no juice contracts deployed on this network");
}
else revert("Invalid RPC / no juice contracts deployed on this network");

_directory = IJBDirectory(
stdJson.readAddress(
vm.readFile(string.concat("node_modules/@jbx-protocol/juice-contracts-v3/deployments/", _network, "/JBDirectory.json")),
vm.readFile(
string.concat(
"node_modules/@jbx-protocol/juice-contracts-v3/deployments/", _network, "/JBDirectory.json"
)
),
".address"
)
);

_controller = IJBController3_1(
stdJson.readAddress(
vm.readFile(string.concat("node_modules/@jbx-protocol/juice-contracts-v3/deployments/", _network, "/JBController3_1.json")),
vm.readFile(
string.concat(
"node_modules/@jbx-protocol/juice-contracts-v3/deployments/", _network, "/JBController3_1.json"
)
),
".address"
)
);
Expand All @@ -70,7 +76,7 @@ contract DeployGeneric is Script {
console.log(address(_controller));

vm.startBroadcast();
JBGenericBuybackDelegate _delegate = new JBGenericBuybackDelegate(
JBBuybackDelegate _delegate = new JBBuybackDelegate(
_weth,
_factory,
_directory,
Expand All @@ -81,4 +87,4 @@ contract DeployGeneric is Script {
console.log("Delegate deployed at:");
console.log(address(_delegate));
}
}
}
Loading

0 comments on commit 4a76e59

Please sign in to comment.