Skip to content
Closed
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
7c5b02a
test commit
maxencerb Feb 10, 2025
84111f4
wip:erc4626 kandel
Solthodox Feb 14, 2025
ca6c5bd
fix:kandle seeder tests
Solthodox Feb 17, 2025
be7961c
test erc4626 kandel + remove shares logic from erc4626 router
Solthodox Feb 17, 2025
2270331
cleanup
Solthodox Feb 17, 2025
d212f0b
restore foundry config
Solthodox Feb 17, 2025
d69876f
test morpho kandel + use require reverts in router
Solthodox Feb 19, 2025
a41f5e4
remove puller logic + admin token rescue + skip deposit if not vault …
Solthodox Feb 25, 2025
2370e49
setVault function for kandel
Solthodox Feb 26, 2025
9a976b7
make kandel router admin
Solthodox Feb 28, 2025
2f99fa8
fixes
Solthodox Feb 28, 2025
dcf708c
doc + redeposit + use transferLib
Solthodox Feb 28, 2025
dffb583
fix:add missing submodules
Solthodox Mar 3, 2025
4943db1
fix:add missing submodules
Solthodox Mar 3, 2025
f23dc1a
Update CHANGELOG.md with submodule configuration fixes
Solthodox Mar 3, 2025
d049849
fix: added 0 vault bugfix + view function (#294)
maxencerb Mar 5, 2025
b43b8fc
add erc4646 deployment scripts + fix stack too deep
Solthodox Mar 11, 2025
ecda391
fix:router deployment in erc4626seeder + create morpho seeder
Solthodox Mar 11, 2025
d80ac58
remove unnecesary storage + fix script
Solthodox Mar 11, 2025
2c13414
fix:remove unused imports
Solthodox Mar 12, 2025
b47bc89
fix:remove redundant function override
Solthodox Mar 12, 2025
b3e5f17
fix:wrong argument in event emission
Solthodox Mar 12, 2025
3a997a9
fix:include fees in erc4626 shares valuation
Solthodox Mar 12, 2025
34e0c91
chore:refactor redundant condition
Solthodox Mar 12, 2025
d4beedc
chore:add missing event emissions
Solthodox Mar 12, 2025
245eccf
feat:check vault asset in {setVaultForToken}
Solthodox Mar 13, 2025
7baf0f3
feat: set vault event emission + doc
Solthodox Mar 13, 2025
2d9e736
fix:add slippage protection for vault exit
Solthodox Mar 13, 2025
939bfee
chore:add warning notes in doc
Solthodox Mar 13, 2025
f90f4bc
fix:remove unused import in morpho router
Solthodox Mar 13, 2025
af0fa91
fix:remove duplicated comments
Solthodox Mar 13, 2025
862271c
fix:remove unused event
Solthodox Mar 13, 2025
46cea0c
fix:morpho rewards receiver
Solthodox Mar 13, 2025
7604704
fix:use forceApprove in erc4626 router
Solthodox Mar 13, 2025
9c8826d
fix:withdraw rounding issues + code refactor
Solthodox Mar 13, 2025
221ab02
fix:confusing doc
Solthodox Mar 13, 2025
a1dc9aa
fix:revert to old transferLib + OZ credits
Solthodox Mar 14, 2025
3e2c501
feat:minSharesOut param in setVault
Solthodox Mar 14, 2025
7ff06e7
feat:hardode kandel as owner in event emission
Solthodox Mar 14, 2025
9694099
Merge pull request #295 from mangrovedao/cantina-fixes
Solthodox Mar 14, 2025
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
2 changes: 2 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ jobs:
- name: Foundry Setup
uses: mangrovedao/.github/.github/actions/foundry-setup@master

- run: forge install

- run: yarn install --immutable

- name: Build
Expand Down
17 changes: 17 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,6 +1,23 @@
[submodule "lib/orbit-protocol"]
path = lib/orbit-protocol
url = https://github.com/orbit-protocol/contracts.git

[submodule "lib/openzeppelin"]
path = lib/openzeppelin
url = https://github.com/OpenZeppelin/openzeppelin-contracts.git

[submodule "lib/mangrove-core"]
path = lib/mangrove-core
url = https://github.com/mangrovedao/mangrove-core.git

[submodule "lib/v2-core"]
path = lib/v2-core
url = https://github.com/Uniswap/v2-core.git

[submodule "lib/v3-core"]
path = lib/v3-core
url = https://github.com/Uniswap/v3-core.git

[submodule "lib/v3-periphery"]
path = lib/v3-periphery
url = https://github.com/Uniswap/v3-periphery.git
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Next version

- Change licenses to MIT for code mistakenly assigned BSD-2-Clause or AGPL-3.0
- Fix submodule configurations for mangrove-core, v2-core, v3-core, and v3-periphery

# 2.1.0-7

Expand Down
1 change: 1 addition & 0 deletions lib/mangrove-core
Submodule mangrove-core added at 7bd43d
1 change: 1 addition & 0 deletions lib/v2-core
Submodule v2-core added at 4dd590
1 change: 1 addition & 0 deletions lib/v3-core
Submodule v3-core added at e3589b
1 change: 1 addition & 0 deletions lib/v3-periphery
Submodule v3-periphery added at 80f26c
2 changes: 1 addition & 1 deletion script/strategies/kandel/KandelSower.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ contract KandelSower is Deployer, MangroveTest {
innerRun({
kandelSeeder: AbstractKandelSeeder(
envAddressOrName("KANDEL_SEEDER", onAave ? fork.get("AaveKandelSeeder") : fork.get("KandelSeeder"))
),
),
olKeyBaseQuote: OLKey(envAddressOrName("BASE"), envAddressOrName("QUOTE"), vm.envUint("TICK_SPACING")),
sharing: vm.envBool("SHARING"),
onAave: onAave,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import {
AaveKandelSeeder,
IPoolAddressesProvider
} from "@mgv-strats/src/strategies/offer_maker/market_making/kandel/AaveKandelSeeder.sol";
import {ERC4626KandelSeeder} from "@mgv-strats/src/strategies/offer_maker/market_making/kandel/ERC4626KandelSeeder.sol";

import {Deployer} from "@mgv/script/lib/Deployer.sol";
import {KandelSeederDeployer, IERC20} from "./KandelSeederDeployer.s.sol";

Expand All @@ -17,16 +19,25 @@ contract ArbitrumKandelSeederDeployer is Deployer {
outputDeployment();
}

function runWithChainSpecificParams() public returns (KandelSeeder seeder, AaveKandelSeeder aaveSeeder) {
return new KandelSeederDeployer().innerRun({
function runWithChainSpecificParams()
public
returns (KandelSeeder seeder, AaveKandelSeeder aaveSeeder, ERC4626KandelSeeder erc4626Seeder)
{
// Create the DeploymentParams struct
KandelSeederDeployer.DeploymentParams memory params = KandelSeederDeployer.DeploymentParams({
mgv: IMangrove(fork.get("Mangrove")),
addressesProvider: IPoolAddressesProvider(fork.get("AaveAddressProvider")),
aaveKandelGasreq: 628_000,
erc4626KandelGasreq: 628_000,
kandelGasreq: 200_000,
deployKandel: true,
deployAaveKandel: true,
deployERC4626Kandel: true,
deployKandel: true,
testBase: IERC20(fork.get("WETH.e")),
testQuote: IERC20(fork.get("DAI.e"))
});

// Pass the struct to innerRun
return new KandelSeederDeployer().innerRun(params);
}
}
55 changes: 55 additions & 0 deletions script/strategies/kandel/deployers/ERC4626KandelDeployer.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

import {Script, console} from "@mgv/forge-std/Script.sol";
import {ERC4626Kandel} from "@mgv-strats/src/strategies/offer_maker/market_making/kandel/ERC4626Kandel.sol";
import {AbstractRouter, RL} from "@mgv-strats/src/strategies/routers/abstract/AbstractRouter.sol";
import {Direct} from "@mgv-strats/src/strategies/offer_maker/abstract/Direct.sol";
import {OLKey} from "@mgv/src/core/MgvLib.sol";
import {IMangrove} from "@mgv/src/IMangrove.sol";
import {Deployer} from "@mgv/script/lib/Deployer.sol";
import {KandelSower} from "../KandelSower.s.sol";

/**
* @notice deploys a Kandel instance on a given market
* @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 ERC4626KandelDeployer --broadcast --private-key $MUMBAI_PRIVATE_KEY
*/
contract ERC4626KandelDeployer is Deployer {
ERC4626Kandel public current;

function run() public {
innerRun({
mgv: IMangrove(envAddressOrName("MGV", "Mangrove")),
olKeyBaseQuote: OLKey(envAddressOrName("BASE"), envAddressOrName("QUOTE"), vm.envUint("TICK_SPACING")),
gasreq: 200_000,
name: envHas("NAME") ? vm.envString("NAME") : "",
routerParams: Direct.RouterParams({
routerImplementation: AbstractRouter(vm.envAddress("ERC4626_ROUTER")),
fundOwner: vm.envAddress("FUND_OWNER"),
strict: true
})
});
outputDeployment();
}

/**
* @param mgv The Mangrove deployment.
* @param olKeyBaseQuote The OLKey for the outbound_tkn base and inbound_tkn quote offer list Kandel will act on, the flipped OLKey is used for the opposite offer list.
* @param gasreq the gas required for the offer logic
* @param name The name to register the deployed Kandel instance under. If empty, a name will be generated
*/
function innerRun(
IMangrove mgv,
OLKey memory olKeyBaseQuote,
uint gasreq,
string memory name,
Direct.RouterParams memory routerParams
) public {
broadcast();
current = new ERC4626Kandel(mgv, olKeyBaseQuote, gasreq, routerParams);

string memory kandelName = new KandelSower().getName(name, olKeyBaseQuote, false);
fork.set(kandelName, address(current));
}
}
151 changes: 114 additions & 37 deletions script/strategies/kandel/deployers/KandelSeederDeployer.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ import {
AaveKandel,
IPoolAddressesProvider
} from "@mgv-strats/src/strategies/offer_maker/market_making/kandel/AaveKandelSeeder.sol";
import {
ERC4626Kandel,
ERC4626KandelSeeder
} from "@mgv-strats/src/strategies/offer_maker/market_making/kandel/ERC4626KandelSeeder.sol";
import {AbstractKandelSeeder} from
"@mgv-strats/src/strategies/offer_maker/market_making/kandel/abstract/AbstractKandelSeeder.sol";
import {CoreKandel} from "@mgv-strats/src/strategies/offer_maker/market_making/kandel/abstract/CoreKandel.sol";
Expand All @@ -26,60 +30,89 @@ import {OLKey} from "@mgv/src/core/MgvLib.sol";
contract KandelSeederDeployer is Deployer, Test2 {
function run() public {
bool deployAaveKandel = true;
bool deployERC4626Kandel = true;
bool deployKandel = true;

try vm.envBool("DEPLOY_AAVE_KANDEL") returns (bool deployAaveKandel_) {
deployAaveKandel = deployAaveKandel_;
} catch {}

try vm.envBool("DEPLOY_ERC4626_KANDEL") returns (bool deployERC4626Kandel_) {
deployERC4626Kandel = deployERC4626Kandel_;
} catch {}

try vm.envBool("DEPLOY_KANDEL") returns (bool deployKandel_) {
deployKandel = deployKandel_;
} catch {}
innerRun({

// Create the parameters struct
DeploymentParams memory params = DeploymentParams({
mgv: IMangrove(envAddressOrName("MGV", "Mangrove")),
addressesProvider: IPoolAddressesProvider(envAddressOrName("AAVE_ADDRESS_PROVIDER", "AaveAddressProvider")),
aaveKandelGasreq: 628_000,
erc4626KandelGasreq: 628_000,
kandelGasreq: 128_000,
deployAaveKandel: deployAaveKandel,
deployERC4626Kandel: deployERC4626Kandel,
deployKandel: deployKandel,
testBase: IERC20(envAddressOrName("TEST_BASE")),
testQuote: IERC20(envAddressOrName("TEST_QUOTE"))
});

innerRun(params);
outputDeployment();
}

function innerRun(
IMangrove mgv,
IPoolAddressesProvider addressesProvider,
uint aaveKandelGasreq,
uint kandelGasreq,
bool deployAaveKandel,
bool deployKandel,
IERC20 testBase,
IERC20 testQuote
) public returns (KandelSeeder seeder, AaveKandelSeeder aaveSeeder) {
struct DeploymentParams {
IMangrove mgv;
IPoolAddressesProvider addressesProvider;
uint aaveKandelGasreq;
uint erc4626KandelGasreq;
uint kandelGasreq;
bool deployAaveKandel;
bool deployERC4626Kandel;
bool deployKandel;
IERC20 testBase;
IERC20 testQuote;
}

function innerRun(DeploymentParams memory params)
public
returns (KandelSeeder seeder, AaveKandelSeeder aaveSeeder, ERC4626KandelSeeder erc4626Seeder)
{
// Tick spacing is irrelevant, only used to deploy for verification and to use as a library
uint tickSpacing = 1;
OLKey memory olKeyBaseQuote = OLKey(address(testBase), address(testQuote), tickSpacing);
OLKey memory olKeyBaseQuote = OLKey(address(params.testBase), address(params.testQuote), tickSpacing);

if (deployKandel) {
if (params.deployKandel) {
prettyLog("Deploying Kandel seeder...");
broadcast();
seeder = new KandelSeeder(mgv, kandelGasreq);
seeder = new KandelSeeder(params.mgv, params.kandelGasreq);
fork.set("KandelSeeder", address(seeder));

console.log("Deploying Kandel instance for code verification and to use as proxy for KandelLib...");
broadcast();
Kandel kandel = new Kandel(mgv, olKeyBaseQuote, 1);
Kandel kandel = new Kandel(params.mgv, olKeyBaseQuote, 1);
// Write the kandel's address so it can be used as a library to call createGeometricDistribution
fork.set("KandelLib", address(kandel));
smokeTest(mgv, olKeyBaseQuote, seeder, AbstractRouter(address(0)));

smokeTest(
SmokeTestParams({
mgv: params.mgv,
olKeyBaseQuote: olKeyBaseQuote,
kandelSeeder: seeder,
expectedRouter: AbstractRouter(address(0))
})
);
}
if (deployAaveKandel) {

if (params.deployAaveKandel) {
prettyLog("Deploying AaveKandel seeder...");
// Bug workaround: Foundry has a bug where the nonce is not incremented when AaveKandelSeeder is deployed.
// We therefore ensure that this happens.
uint64 nonce = vm.getNonce(broadcaster());
broadcast();
aaveSeeder = new AaveKandelSeeder(mgv, addressesProvider, aaveKandelGasreq);
aaveSeeder = new AaveKandelSeeder(params.mgv, params.addressesProvider, params.aaveKandelGasreq);
// Bug workaround: See comment above `nonce` further up
if (nonce == vm.getNonce(broadcaster())) {
vm.setNonce(broadcaster(), nonce + 1);
Expand All @@ -93,49 +126,93 @@ contract KandelSeederDeployer is Deployer, Test2 {
console.log("Seeder's router:", address(router));
broadcast();
new AaveKandel(
mgv,
params.mgv,
olKeyBaseQuote,
params.aaveKandelGasreq,
Direct.RouterParams({routerImplementation: router, fundOwner: address(0), strict: true})
);
smokeTest(
SmokeTestParams({
mgv: params.mgv,
olKeyBaseQuote: olKeyBaseQuote,
kandelSeeder: aaveSeeder,
expectedRouter: aaveSeeder.AAVE_ROUTER()
})
);
}

if (params.deployERC4626Kandel) {
prettyLog("Deploying ERC4626Kandel seeder...");
// Bug workaround: Foundry has a bug where the nonce is not incremented when AaveKandelSeeder is deployed.
// We therefore ensure that this happens.
uint64 nonce = vm.getNonce(broadcaster());
broadcast();
erc4626Seeder = new ERC4626KandelSeeder(params.mgv, params.erc4626KandelGasreq);
// Bug workaround: See comment above `nonce` further up
if (nonce == vm.getNonce(broadcaster())) {
vm.setNonce(broadcaster(), nonce + 1);
}
fork.set("ERC4626KandelSeeder", address(erc4626Seeder));
fork.set("ERC4626Rounter", address(erc4626Seeder.ERC4626_ROUTER()));

console.log("Deploying ERC4626Kandel instance for code verification...");
prettyLog("Deploying ERC4626Kandel instance...");
AbstractRouter router = AbstractRouter(address(erc4626Seeder.ERC4626_ROUTER()));
console.log("Seeder's router:", address(router));
broadcast();
new ERC4626Kandel(
params.mgv,
olKeyBaseQuote,
aaveKandelGasreq,
params.aaveKandelGasreq,
Direct.RouterParams({routerImplementation: router, fundOwner: address(0), strict: true})
);
smokeTest(mgv, olKeyBaseQuote, aaveSeeder, aaveSeeder.AAVE_ROUTER());
smokeTest(
SmokeTestParams({
mgv: params.mgv,
olKeyBaseQuote: olKeyBaseQuote,
kandelSeeder: erc4626Seeder,
expectedRouter: erc4626Seeder.ERC4626_ROUTER()
})
);
}

console.log("Deployed!");
}

function smokeTest(
IMangrove mgv,
OLKey memory olKeyBaseQuote,
AbstractKandelSeeder kandelSeeder,
AbstractRouter expectedRouter
) internal {
// Ensure that WETH/DAI market is open on Mangrove
vm.startPrank(mgv.governance());
mgv.activate(olKeyBaseQuote, 0, 1, 1);
mgv.activate(olKeyBaseQuote.flipped(), 0, 1, 1);
struct SmokeTestParams {
IMangrove mgv;
OLKey olKeyBaseQuote;
AbstractKandelSeeder kandelSeeder;
AbstractRouter expectedRouter;
}

function smokeTest(SmokeTestParams memory params) internal {
// Ensure that market is open on Mangrove
vm.startPrank(params.mgv.governance());
params.mgv.activate(params.olKeyBaseQuote, 0, 1, 1);
params.mgv.activate(params.olKeyBaseQuote.flipped(), 0, 1, 1);
vm.stopPrank();

CoreKandel kandel = kandelSeeder.sow({olKeyBaseQuote: olKeyBaseQuote, liquiditySharing: true});
CoreKandel kandel = params.kandelSeeder.sow({olKeyBaseQuote: params.olKeyBaseQuote, liquiditySharing: true});

require(kandel.router() == expectedRouter, "Incorrect router address");
require(kandel.router() == params.expectedRouter, "Incorrect router address");
require(kandel.admin() == address(this), "Incorrect admin");
if (address(expectedRouter) == address(0)) {
if (address(params.expectedRouter) == address(0)) {
require(kandel.FUND_OWNER() == address(kandel), "Incorrect id");
} else {
require(kandel.FUND_OWNER() == kandel.admin(), "Incorrect id");
// starting smoke test with 10 inbound on Kandel
deal({to: address(kandel), token: olKeyBaseQuote.inbound_tkn, give: 10});
deal({to: address(kandel), token: params.olKeyBaseQuote.inbound_tkn, give: 10});

vm.startPrank(address(kandel));
// push should take 5 inbound (out of 10) from kandel and send it to router
uint pushed = kandel.router().push(
RL.createOrder({token: IERC20(olKeyBaseQuote.inbound_tkn), fundOwner: kandel.FUND_OWNER()}), 5
RL.createOrder({token: IERC20(params.olKeyBaseQuote.inbound_tkn), fundOwner: kandel.FUND_OWNER()}), 5
);
require(pushed == 5, "smoke test: push failed");
// pull should take 1 outbound from router and send it to kandel
uint pulled = kandel.router().pull(
RL.createOrder({token: IERC20(olKeyBaseQuote.inbound_tkn), fundOwner: kandel.FUND_OWNER()}), 1, true
RL.createOrder({token: IERC20(params.olKeyBaseQuote.inbound_tkn), fundOwner: kandel.FUND_OWNER()}), 1, true
);
require(pulled == 1, "smoke test: pull failed");
vm.stopPrank();
Expand Down
Loading