Skip to content

Commit

Permalink
UniV3 routing logic (#254)
Browse files Browse the repository at this point in the history
* UniV3 routing logic

* Fix comments

* Unused contracts

* Add scripts and script test

* remove comments

* Add blast manager and script

* fix natspec

* remove unused

* Add uni to abi exports

* fix blast deployment

* fix deployment script

* audit return 1

* fix comments
  • Loading branch information
maxencerb authored Mar 20, 2024
1 parent 7a9bc5b commit 94765c1
Show file tree
Hide file tree
Showing 66 changed files with 3,764 additions and 40 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Next version

- feat: add `UniSwap` routing logic, manager, and univ3 logics

# 2.1.0-6

- Add view function to `SmartKandel` to get the current logics
Expand Down
2 changes: 2 additions & 0 deletions config.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ exports.abi_exports = [
"AbstractRoutingLogic",
"SimpleAaveLogic",
"MangroveAmplifier",
"UniswapV3Manager",
"UniswapV3RoutingLogic",
"OrbitLogic",
"OrbitLogicStorage",
"SmartKandelSeeder",
Expand Down
50 changes: 50 additions & 0 deletions copyUniBytecode.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
const fs = require("fs");

/**
* Outputs the compiler output to a file.
* @typedef {Object} CompilerOutput
* @property {string} bytecode The bytecode of the contract.
*/

const files = [
"node_modules/@uniswap/v3-core/artifacts/contracts/UniswapV3Factory.sol/UniswapV3Factory.json",
"node_modules/@uniswap/v3-periphery/artifacts/contracts/NonfungibleTokenPositionDescriptor.sol/NonfungibleTokenPositionDescriptor.json",
"node_modules/@uniswap/v3-periphery/artifacts/contracts/NonfungiblePositionManager.sol/NonfungiblePositionManager.json",
];
const outputs = [
__dirname + "/uni-out/UniswapV3Factory.txt",
__dirname + "/uni-out/NonfungibleTokenPositionDescriptor.txt",
__dirname + "/uni-out/NonfungiblePositionManager.txt",
];

async function main() {
await Promise.all(
files.map(async (file, idx) => {
const fileContent = await new Promise((resolve, reject) => {
fs.readFile(file, "utf8", (err, data) => {
if (err) reject(err);
resolve(data);
});
});
/**
* @type {CompilerOutput}
*/
const data = JSON.parse(fileContent);
const bytecode = data.bytecode.replace(
/__\$[a-fA-F0-9]+\$__/gm,
"0".repeat(40),
);

await new Promise((resolve, reject) => {
fs.writeFile(outputs[idx], bytecode, (err) => {
if (err) reject(err);
resolve();
});
});
}),
);
}

main()
.then(() => console.log("done"))
.catch(console.error);
2 changes: 1 addition & 1 deletion foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ out='out'
libs=['lib']
cache_path='cache'
evm_version='paris'
fs_permissions = [{ access = "read-write", path = "./addresses/"}, { access = "read-write", path = "./analytics/"}, { access = "read", path = "./out/" }, {access = "read", path = "./node_modules/@mangrovedao/mangrove-core/"}, { access = "read", path = "./mgvConfig.json" }]
fs_permissions = [{ access = "read-write", path = "./addresses/"}, { access = "read-write", path = "./analytics/"}, { access = "read", path = "./out/" }, {access = "read", path = "./node_modules/@mangrovedao/mangrove-core/"}, {access = "read", path = "./uni-out/"}, { access = "read", path = "./mgvConfig.json" }]
solc_version="0.8.20"
ffi=true
optimizer=false
Expand Down
7 changes: 5 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@
"prepack": "pinst --disable && yarn build",
"postpack": "pinst --enable",
"postinstall": "husky install",
"build": "yarn run copyDeploymentAddresses && yarn run copyContextAddresses && forge build && node copyArtifacts && node buildIndex && node checkNatspec",
"build": "yarn run copyDeploymentAddresses && yarn run copyContextAddresses && yarn run copyUniBytecode && forge build && node copyArtifacts && node buildIndex && node checkNatspec",
"copyDeploymentAddresses": "node copyDeploymentAddresses",
"copyContextAddresses": "node copyContextAddresses",
"copyUniBytecode": "node copyUniBytecode",
"clean": "forge clean; rimraf index.js dist",
"test": "forge test -vvv",
"gas-measurement": "GAS_MATCH_PATH='*.gasreq.*' bash ./node_modules/@mangrovedao/mangrove-core/gas-measurement.sh"
Expand All @@ -33,7 +34,9 @@
"/README.md"
],
"dependencies": {
"@mangrovedao/mangrove-core": "^2.1.1"
"@mangrovedao/mangrove-core": "^2.1.1",
"@uniswap/v3-core": "^1.0.1",
"@uniswap/v3-periphery": "^1.4.4"
},
"devDependencies": {
"@mangrovedao/context-addresses": "^1.3.4",
Expand Down
9 changes: 8 additions & 1 deletion remappings.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,12 @@ ds-test/=node_modules/@mangrovedao/mangrove-core/lib/forge-std/lib/ds-test/src
@mgv-strats/test/=test/
@mgv-strats/script/=script/

@uniswap/v3-core/=node_modules/@uniswap/v3-core/
@uniswap/v2-core/=node_modules/@uniswap/v2-core/
@uniswap/v3-periphery/=node_modules/@uniswap/v3-periphery/
@uniswap/lib/=node_modules/@uniswap/lib/

base64-sol/=node_modules/base64-sol/

@openzeppelin/contracts/=lib/openzeppelin/contracts/
@orbit-protocol/contracts/=lib/orbit-protocol/contracts/
@orbit-protocol/contracts/=lib/orbit-protocol/contracts/
6 changes: 1 addition & 5 deletions script/deployers/MumbaiActivateMarket.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ import {MgvReader, Market} from "@mgv/src/periphery/MgvReader.sol";
* TOKEN1_IN_USD=$(cast ff 8 0.9) \
* forge script --fork-url mumbai MumbaiActivateMarket
*/

contract MumbaiActivateMarket is Deployer {
uint maticPrice;

Expand Down Expand Up @@ -74,10 +73,7 @@ contract MumbaiActivateMarket is Deployer {
tokens[0] = IERC20(market.tkn0);
tokens[1] = IERC20(market.tkn1);

new ActivateMangroveOrder().innerRun({
mgvOrder: mangroveOrder,
tokens: tokens
});
new ActivateMangroveOrder().innerRun({mgvOrder: mangroveOrder, tokens: tokens});
}

function smokeTest(Market memory market) internal view {
Expand Down
1 change: 0 additions & 1 deletion script/strategies/kandel/KandelShutdown.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import {toFixed} from "@mgv/lib/Test2.sol";
/**
* @notice Populate Kandel's distribution on Mangrove
*/

contract KandelShutdown is Deployer {
function run() public {
innerRun({kdl: GeometricKandel(envAddressOrName("KANDEL"))});
Expand Down
1 change: 0 additions & 1 deletion script/strategies/kandel/KandelSower.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ import {console2 as console} from "@mgv/forge-std/Script.sol";
* @dev since the max number of price slot Kandel can use is an immutable, one should deploy Kandel on a large price range.
* @dev Example: WRITE_DEPLOY=true ON_AAVE=true SHARING=false BASE=CRV QUOTE=WBTC TICK_SPACING=1 forge script --fork-url $LOCALHOST_URL KandelSower --broadcast --private-key $MUMBAI_PRIVATE_KEY
*/

contract KandelSower is Deployer, MangroveTest {
function run() public {
bool onAave = vm.envBool("ON_AAVE");
Expand Down
1 change: 0 additions & 1 deletion script/strategies/kandel/KandelStatus.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import {toFixed} from "@mgv/lib/Test2.sol";
/**
* @notice Populate Kandel's distribution on Mangrove
*/

contract KandelStatus is Deployer {
function run() public view {
innerRun({kdl: GeometricKandel(envAddressOrName("KANDEL"))});
Expand Down
1 change: 0 additions & 1 deletion script/strategies/kandel/deployers/KandelDeployer.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import {KandelSower} from "../KandelSower.s.sol";
* @dev since the max number of price slot Kandel can use is an immutable, one should deploy Kandel on a large price range.
* @dev Example: WRITE_DEPLOY=true BASE=WETH QUOTE=USDC forge script --fork-url $LOCALHOST_URL KandelDeployer --broadcast --private-key $MUMBAI_PRIVATE_KEY
*/

contract KandelDeployer is Deployer {
Kandel public current;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

import {Script, console} from "@mgv/forge-std/Script.sol";
import {BlastUniswapV3Manager} from "@mgv-strats/src/strategies/chains/blast/routing_logic/BlastUniswapV3Manager.sol";
import {INonfungiblePositionManager} from
"@mgv-strats/src/strategies/vendor/uniswap/v3/periphery/interfaces/INonfungiblePositionManager.sol";
import {RouterProxyFactory} from "@mgv-strats/src/strategies/routers/RouterProxyFactory.sol";
import {AbstractRouter} from "@mgv-strats/src/strategies/routers/abstract/AbstractRouter.sol";
import {UniswapV3RoutingLogic} from
"@mgv-strats/src/strategies/routing_logic/restaking/uni-v3/UniswapV3RoutingLogic.sol";
import {Deployer} from "@mgv/script/lib/Deployer.sol";
import {IERC20Rebasing} from "@mgv-strats/src/strategies/vendor/blast/IERC20Rebasing.sol";
import {IBlast} from "@mgv/src/chains/blast/interfaces/IBlast.sol";
import {IBlastPoints} from "@mgv/src/chains/blast/interfaces/IBlastPoints.sol";

/* Deploys a UniswapV3Logic instance */
contract BlastUniV3RoutingLogicDeployer is Deployer {
function run() public {
address[] memory tokens = vm.envAddress("REBASING_TOKENS", ",");
IERC20Rebasing[] memory rebasingTokens = new IERC20Rebasing[](tokens.length);
for (uint i = 0; i < tokens.length; i++) {
rebasingTokens[i] = IERC20Rebasing(tokens[i]);
}
innerRun({
positionManager: INonfungiblePositionManager(vm.envAddress("UNISWAP_V3_POSITION_MANAGER")),
factory: RouterProxyFactory(envAddressOrName("ROUTER_PROXY_FACTORY", "RouterProxyFactory")),
implementation: AbstractRouter(envAddressOrName("SMART_ROUTER_IMPLEMENTATION", "MangroveOrder-Router")),
forkName: vm.envString("FORK_NAME"),
tokens: rebasingTokens,
admin: vm.envAddress("ADMIN"),
blastContract: IBlast(vm.envAddress("BLAST")),
pointsContract: IBlastPoints(vm.envAddress("BLAST_POINTS")),
pointsOperator: vm.envAddress("BLAST_POINTS_OPERATOR"),
blastGovernor: vm.envAddress("BLAST_GOVERNOR")
});
outputDeployment();
}

function innerRun(
INonfungiblePositionManager positionManager,
RouterProxyFactory factory,
AbstractRouter implementation,
string memory forkName,
IERC20Rebasing[] memory tokens,
address admin,
IBlast blastContract,
IBlastPoints pointsContract,
address pointsOperator,
address blastGovernor
) public {
broadcast();
BlastUniswapV3Manager uniswapV3Manager = new BlastUniswapV3Manager(
tokens,
admin,
positionManager,
factory,
implementation,
pointsContract,
pointsOperator,
blastContract,
blastGovernor
);
string memory managerName = string.concat("UniswapV3Manager-", forkName);
fork.set(managerName, address(uniswapV3Manager));
console.log("UniswapV3Manager deployed", address(uniswapV3Manager));

broadcast();
UniswapV3RoutingLogic uniswapV3RoutingLogic = new UniswapV3RoutingLogic(uniswapV3Manager);
string memory logicName = string.concat("UniswapV3RoutingLogic-", forkName);
fork.set(logicName, address(uniswapV3RoutingLogic));
console.log("UniswapV3RoutingLogic deployed", address(uniswapV3RoutingLogic));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

import {Script, console} from "@mgv/forge-std/Script.sol";
import {UniswapV3Manager} from "@mgv-strats/src/strategies/routing_logic/restaking/uni-v3/UniswapV3Manager.sol";
import {INonfungiblePositionManager} from
"@mgv-strats/src/strategies/vendor/uniswap/v3/periphery/interfaces/INonfungiblePositionManager.sol";
import {RouterProxyFactory} from "@mgv-strats/src/strategies/routers/RouterProxyFactory.sol";
import {AbstractRouter} from "@mgv-strats/src/strategies/routers/abstract/AbstractRouter.sol";
import {UniswapV3RoutingLogic} from
"@mgv-strats/src/strategies/routing_logic/restaking/uni-v3/UniswapV3RoutingLogic.sol";
import {Deployer} from "@mgv/script/lib/Deployer.sol";

/* Deploys a UniswapV3Logic instance */
contract UniV3RoutingLogicDeployer is Deployer {
function run() public {
innerRun({
positionManager: INonfungiblePositionManager(vm.envAddress("UNISWAP_V3_POSITION_MANAGER")),
factory: RouterProxyFactory(envAddressOrName("ROUTER_PROXY_FACTORY", "RouterProxyFactory")),
implementation: AbstractRouter(envAddressOrName("SMART_ROUTER_IMPLEMENTATION", "MangroveOrder-Router")),
forkName: vm.envString("FORK_NAME")
});
outputDeployment();
}

function innerRun(
INonfungiblePositionManager positionManager,
RouterProxyFactory factory,
AbstractRouter implementation,
string memory forkName
) public {
broadcast();
UniswapV3Manager uniswapV3Manager = new UniswapV3Manager(positionManager, factory, implementation);
string memory managerName = string.concat("UniswapV3Manager-", forkName);
fork.set(managerName, address(uniswapV3Manager));
console.log("UniswapV3Manager deployed", address(uniswapV3Manager));

broadcast();
UniswapV3RoutingLogic uniswapV3RoutingLogic = new UniswapV3RoutingLogic(uniswapV3Manager);
string memory logicName = string.concat("UniswapV3RoutingLogic-", forkName);
fork.set(logicName, address(uniswapV3RoutingLogic));
console.log("UniswapV3RoutingLogic deployed", address(uniswapV3RoutingLogic));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import {
UniswapV3Manager,
INonfungiblePositionManager,
RouterProxyFactory,
AbstractRouter
} from "@mgv-strats/src/strategies/routing_logic/restaking/uni-v3/UniswapV3Manager.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {IERC20Rebasing, YieldMode} from "@mgv-strats/src/strategies/vendor/blast/IERC20Rebasing.sol";
import {IBlastPoints} from "@mgv/src/chains/blast/interfaces/IBlastPoints.sol";
import {IBlast} from "@mgv/src/chains/blast/interfaces/IBlast.sol";

/// @title BlastUniswapV3Manager
/// @author Mangrove
/// @notice A UniswapV3Manager that can handle rebasing tokens from Blast
contract BlastUniswapV3Manager is UniswapV3Manager, Ownable {
/// @notice Constructor
/// @param _initTokens The tokens to initialize
/// @param admin the admin address
/// @param positionManager the position manager
/// @param factory the router proxy factory
/// @param implementation the router implementation
constructor(
IERC20Rebasing[] memory _initTokens,
address admin,
INonfungiblePositionManager positionManager,
RouterProxyFactory factory,
AbstractRouter implementation,
IBlastPoints pointsContract,
address pointsOperator,
IBlast blastContract,
address blastGovernor
) UniswapV3Manager(positionManager, factory, implementation) Ownable(admin) {
for (uint i = 0; i < _initTokens.length; i++) {
_initRebasingToken(_initTokens[i]);
}
pointsContract.configurePointsOperator(pointsOperator);
blastContract.configureClaimableGas();
blastContract.configureGovernor(blastGovernor);
}

/// @notice Initializes a rebasing token with the correct yield mode
/// @param token The token to configure
function _initRebasingToken(IERC20Rebasing token) internal {
token.configure(YieldMode.CLAIMABLE);
}

/// @notice Initializes a rebasing token with the correct yield mode
/// @param token The token to configure
function initRebasingToken(IERC20Rebasing token) external onlyOwner {
_initRebasingToken(token);
}

/// @notice Claims yield from a rebasing token
/// @param token The token to claim from
/// @param recipient The recipient of the claim
/// @param amount The amount to claim
function claim(IERC20Rebasing token, address recipient, uint amount) external onlyOwner {
token.claim(recipient, amount);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,16 @@ contract AaveKandelSeeder is AbstractKandelSeeder {
// allowing owner to be modified by Kandel's admin would require approval from owner's address controller
address owner = liquiditySharing ? msg.sender : address(0);

kandel = new AaveKandel(MGV, olKeyBaseQuote, KANDEL_GASREQ, Direct.RouterParams({
routerImplementation: AAVE_ROUTER, // using aave pooled router to source liquidity
fundOwner: owner,
strict: liquiditySharing
}));
kandel = new AaveKandel(
MGV,
olKeyBaseQuote,
KANDEL_GASREQ,
Direct.RouterParams({
routerImplementation: AAVE_ROUTER, // using aave pooled router to source liquidity
fundOwner: owner,
strict: liquiditySharing
})
);
// Allowing newly deployed Kandel to bind to the AaveRouter
AAVE_ROUTER.bind(address(kandel));
emit NewAaveKandel(msg.sender, olKeyBaseQuote.hash(), olKeyBaseQuote.flipped().hash(), address(kandel), owner);
Expand Down
4 changes: 2 additions & 2 deletions src/strategies/routing_logic/orbit/OrbitLogic.sol
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ contract OrbitLogic is AbstractRoutingLogic, ExponentialNoError {
returns (uint pulled)
{
OErc20 overlyingToken = overlying(token);
// compute amout to be pulled (currently taking stored axchange rate and not the current one)
// compute amout to be pulled (currently taking stored exchange rate and not the current one)
Exp memory exchangeRate = Exp({mantissa: overlyingToken.exchangeRateStored()});
uint toPull = div_(amount, exchangeRate);
// Pull the amount of oTokens from the fundOwner
Expand All @@ -50,7 +50,7 @@ contract OrbitLogic is AbstractRoutingLogic, ExponentialNoError {
);
// redeem the oTokens to get the underlying tokens
overlyingToken.redeemUnderlying(amount);
// send the underlying tokens to the fundOwner
// send the underlying tokens to the msg.sender (maker contract)
pulled = token.balanceOf(address(this));
require(TransferLib.transferToken(token, msg.sender, pulled), "OrbitLogic: Transfer failed");
uint balance = overlyingToken.balanceOf(address(this));
Expand Down
Loading

0 comments on commit 94765c1

Please sign in to comment.