-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from aboutcircles/mt/factory
Latest LBP design implementation for metri testing
- Loading branch information
Showing
25 changed files
with
921 additions
and
920 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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\"}]}}}" | ||
}' | ||
*/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
} | ||
} |
Oops, something went wrong.