Skip to content

Commit

Permalink
Merge pull request #1 from aboutcircles/mt/factory
Browse files Browse the repository at this point in the history
Latest LBP design implementation for metri testing
  • Loading branch information
roleengineer authored Jan 12, 2025
2 parents eb90bcc + 4063931 commit d40bec8
Show file tree
Hide file tree
Showing 25 changed files with 921 additions and 920 deletions.
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,6 @@
[submodule "lib/safe-smart-account"]
path = lib/safe-smart-account
url = https://github.com/safe-global/safe-smart-account
[submodule "lib/contracts"]
path = lib/contracts
url = https://github.com/cowprotocol/contracts
1 change: 1 addition & 0 deletions lib/contracts
Submodule contracts added at ba5738
1 change: 1 addition & 0 deletions remappings.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ halmos-cheatcodes/=lib/openzeppelin-contracts/lib/halmos-cheatcodes/src/
openzeppelin-contracts/=lib/openzeppelin-contracts/
safe-smart-account/=lib/safe-smart-account/
solmate/=lib/solmate/src/
@cowprotocol/contracts/=lib/contracts/src/contracts
45 changes: 45 additions & 0 deletions script/DeployFactory.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.28;

import {Script, console} from "forge-std/Script.sol";
import {CirclesBackingFactory} from "src/factory/CirclesBackingFactory.sol";

contract DeployFactory is Script {
address deployer = address(0x6BF173798733623cc6c221eD52c010472247d861);
CirclesBackingFactory public circlesBackingFactory; // 0xD608978aD1e1473fa98BaD368e767C5b11e3b3cE

function setUp() public {}

function run() public {
vm.startBroadcast(deployer);

circlesBackingFactory = new CirclesBackingFactory(deployer, 1);

vm.stopBroadcast();
console.log(address(circlesBackingFactory), "CirclesBackingFactory");
}
}

/*
curl -X 'POST' \
'https://api.cow.fi/xdai/api/v1/orders' \
-H 'accept: application/json' \
-H 'Content-Type: application/json' \
-d '{
"sellToken": "0x2a22f9c3b484c3629090FeED35F17Ff8F88f76F0",
"buyToken": "0x6A023CCd1ff6F2045C3309768eAd9E68F978f6e1",
"receiver": "0xe75F06c807038D7D38e4f9716FF953eA1dA39157",
"sellAmount": "1000000",
"buyAmount": "1",
"validTo": 1894324190,
"feeAmount": "0",
"kind": "sell",
"partiallyFillable": false,
"sellTokenBalance": "erc20",
"buyTokenBalance": "erc20",
"signingScheme": "presign",
"signature": "0x",
"from": "0xe75F06c807038D7D38e4f9716FF953eA1dA39157",
"appData": "{\"version\":\"1.1.0\",\"appCode\":\"Circles backing powered by AboutCircles\",\"metadata\":{\"hooks\":{\"version\":\"0.1.0\",\"post\":[{\"target\":\"0xe75f06c807038d7d38e4f9716ff953ea1da39157\",\"callData\":\"0x13e8f89f\",\"gasLimit\":\"6000000\"}]}}}"
}'
*/
8 changes: 4 additions & 4 deletions script/DeployMintPolicy.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,20 @@
pragma solidity ^0.8.28;

import {Script, console} from "forge-std/Script.sol";
import {TestLBPMintPolicy} from "src/policy/TestLBPMintPolicy.sol";
import {CreateTestProxyLBPMintPolicy} from "src/proxy/CreateTestProxyLBPMintPolicy.sol";
import {MintPolicy} from "circles-contracts-v2/groups/BaseMintPolicy.sol";

contract DeployMintPolicy is Script {
address deployer = address(0x6BF173798733623cc6c221eD52c010472247d861);
TestLBPMintPolicy public mintPolicy; // 0xCb10eC7A4D9D764b1DcfcB9c2EBa675B1e756C96
CreateTestProxyLBPMintPolicy public proxyDeployer; // 0x777f78921890Df5Db755e77CbA84CBAdA5DB56D2
MintPolicy public mintPolicy;
CreateTestProxyLBPMintPolicy public proxyDeployer;

function setUp() public {}

function run() public {
vm.startBroadcast(deployer);

mintPolicy = new TestLBPMintPolicy();
mintPolicy = new MintPolicy();
proxyDeployer = new CreateTestProxyLBPMintPolicy(address(mintPolicy));

vm.stopBroadcast();
Expand Down
25 changes: 0 additions & 25 deletions script/DeployModuleFactory.s.sol

This file was deleted.

22 changes: 22 additions & 0 deletions script/DeployOrderCreator.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.28;

import {Script, console} from "forge-std/Script.sol";
import {OrderCreator} from "src/prototype/OrderCreator.sol";

contract DeployPrototype is Script {
address deployer = address(0x6BF173798733623cc6c221eD52c010472247d861);
OrderCreator public orderCreator;

function setUp() public {}

function run() public {
vm.startBroadcast(deployer);

orderCreator = new OrderCreator();
orderCreator.createOrder();

vm.stopBroadcast();
console.log(address(orderCreator), "orderCreator");
}
}
158 changes: 158 additions & 0 deletions src/CirclesBacking.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity ^0.8.28;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {ICowswapSettlement} from "src/interfaces/ICowswapSettlement.sol";
import {IFactory} from "src/interfaces/IFactory.sol";
import {ILBP} from "src/interfaces/ILBP.sol";
import {IVault} from "src/interfaces/IVault.sol";

contract CirclesBacking {
// Errors
/// Already initialized.
error AlreadyInitialized();
/// Function must be called only by Cowswap posthook.
error OrderNotFilledYet();
/// LBP is already created.
error AlreadyCreated();
/// Cowswap solver must transfer the swap result before calling posthook.
error InsufficientBackingAssetBalance();
/// Unauthorized access.
error NotBacker();
/// Balancer Pool Tokens are still locked until `timestamp`.
error TokensLockedUntilTimestamp(uint256 timestamp);

// Events
/// @notice Emitted when Cowswap order is created, logging order uid.
event OrderCreated(bytes orderUid);

// Constants
/// @notice Gnosis Protocol v2 Settlement Contract.
ICowswapSettlement public constant COWSWAP_SETTLEMENT =
ICowswapSettlement(address(0x9008D19f58AAbD9eD0D60971565AA8510560ab41));
/// @notice Gnosis Protocol v2 Vault Relayer Contract.
address public constant VAULT_RELAY = 0xC92E8bdf79f0507f65a392b0ab4667716BFE0110;
/// @dev Circles Backing Factory.
IFactory internal immutable FACTORY;
/// @dev LBP token weight 50%.
uint256 internal constant WEIGHT_50 = 0.5 ether;
/// @dev Update weight duration and lbp lock period is set to 1 year.
uint256 internal constant UPDATE_WEIGHT_DURATION = 365 days;

// Storage
/// @notice Address of circles avatar, which has backed his personal circles.
address public backer;
/// @notice Address of one of supported assets, which was used to back circles.
address public backingAsset;
/// @notice Address of ERC20 stable circles version (InflationaryCircles), which is used as underlying asset in lbp.
address public personalCircles;
/// @notice Address of created Liquidity Bootstrapping Pool, which represents backing liquidity.
address public lbp;
uint256 stableCirclesAmount;
/// @notice Timestamp, when locked balancer pool tokens are allowed to be claimed by backer.
uint256 public balancerPoolTokensUnlockTimestamp;
/// @notice Cowswap order uid.
bytes public storedOrderUid;

constructor() {
FACTORY = IFactory(msg.sender);
}

// Backing logic

/// @notice Initiates core values and backing process, approves Cowswap to spend USDC and presigns order.
function initiateBacking(
address _backer,
address _backingAsset,
address _personalCircles,
bytes memory orderUid,
address usdc,
uint256 tradeAmount,
uint256 stableCRCAmount
) external {
if (backer != address(0)) revert AlreadyInitialized();
// init
backer = _backer;
backingAsset = _backingAsset;
personalCircles = _personalCircles;
stableCirclesAmount = stableCRCAmount;

// Approve USDC to Vault Relay contract
IERC20(usdc).approve(VAULT_RELAY, tradeAmount);

// Store the order UID
storedOrderUid = orderUid;

// Place the order using "setPreSignature"
COWSWAP_SETTLEMENT.setPreSignature(orderUid, true);

// Emit event with the order UID
emit OrderCreated(orderUid);
}

/// @notice Method, which should be used as Cowswap posthook interaction.
/// Creates preconfigured LBP and provides liquidity to it.
function createLBP() external {
// Check if the order has been filled on the CowSwap settlement contract
uint256 filledAmount = COWSWAP_SETTLEMENT.filledAmount(storedOrderUid);
if (filledAmount == 0) revert OrderNotFilledYet();
if (lbp != address(0)) revert AlreadyCreated();

// Backing asset balance of the contract
uint256 backingAssetBalance = IERC20(backingAsset).balanceOf(address(this));
if (backingAssetBalance == 0) revert InsufficientBackingAssetBalance();

// Create LBP
bytes32 poolId;
IVault.JoinPoolRequest memory request;
address vault;
(lbp, poolId, request, vault) =
FACTORY.createLBP(personalCircles, stableCirclesAmount, backingAsset, backingAssetBalance);

// approve vault
IERC20(personalCircles).approve(vault, stableCirclesAmount);
IERC20(backingAsset).approve(vault, backingAssetBalance);

// provide liquidity into lbp
IVault(vault).joinPool(
poolId,
address(this), // sender
address(this), // recipient
request
);

// update weight gradually
uint256 timestampInYear = block.timestamp + UPDATE_WEIGHT_DURATION;
ILBP(lbp).updateWeightsGradually(block.timestamp, timestampInYear, _endWeights());

// set bpt unlock
balancerPoolTokensUnlockTimestamp = timestampInYear;
}

// Balancer pool tokens

/// @notice Method allows backer to claim balancer pool tokens after lock period or in case of global release.
/// @param receiver Address, which will receive balancer pool tokens.
function releaseBalancerPoolTokens(address receiver) external {
if (msg.sender != backer) revert NotBacker();

if (FACTORY.releaseTimestamp() > uint32(block.timestamp)) {
if (balancerPoolTokensUnlockTimestamp > block.timestamp) {
revert TokensLockedUntilTimestamp(balancerPoolTokensUnlockTimestamp);
}
}
// zeroed timestamp
balancerPoolTokensUnlockTimestamp = 0;

uint256 bptAmount = IERC20(lbp).balanceOf(address(this));
IERC20(lbp).transfer(receiver, bptAmount);
}

// Internal functions

function _endWeights() internal pure returns (uint256[] memory endWeights) {
endWeights = new uint256[](2);
endWeights[0] = WEIGHT_50;
endWeights[1] = WEIGHT_50;
}
}
Loading

0 comments on commit d40bec8

Please sign in to comment.