From 2fd86552b7a2b7d5143f652ce351216d34b251ec Mon Sep 17 00:00:00 2001
From: Hayden Shively <17186559+haydenshively@users.noreply.github.com>
Date: Mon, 23 Oct 2023 18:05:41 -0500
Subject: [PATCH] Fix multicall in BorrowerNFT by making mint payable, and
outsource URI
---
periphery/script/Deploy.s.sol | 17 +-
periphery/src/boost/BoostNFT.txt | 151 ------------
periphery/src/boost/NFTDescriptor.sol | 158 ------------
periphery/src/boost/NFTSVG.sol | 269 ---------------------
periphery/src/boost/SafeERC20Namer.sol | 94 -------
periphery/src/borrower-nft/BorrowerNFT.sol | 16 +-
6 files changed, 23 insertions(+), 682 deletions(-)
delete mode 100644 periphery/src/boost/BoostNFT.txt
delete mode 100644 periphery/src/boost/NFTDescriptor.sol
delete mode 100644 periphery/src/boost/NFTSVG.sol
delete mode 100644 periphery/src/boost/SafeERC20Namer.sol
diff --git a/periphery/script/Deploy.s.sol b/periphery/script/Deploy.s.sol
index 62412343..d8e96baa 100644
--- a/periphery/script/Deploy.s.sol
+++ b/periphery/script/Deploy.s.sol
@@ -9,24 +9,30 @@ import {BorrowerLens} from "src/BorrowerLens.sol";
import {LenderLens} from "src/LenderLens.sol";
import {Router, IPermit2} from "src/Router.sol";
-import {BorrowerNFT} from "src/borrower-nft/BorrowerNFT.sol";
+import {BorrowerNFT, IBorrowerURISource} from "src/borrower-nft/BorrowerNFT.sol";
import {BoostManager} from "src/managers/BoostManager.sol";
import {FrontendManager} from "src/managers/FrontendManager.sol";
import {SimpleManager} from "src/managers/SimpleManager.sol";
import {UniswapNFTManager, INFTManager} from "src/managers/UniswapNFTManager.sol";
-bytes32 constant TAG = bytes32(uint256(0xA10EBE1A2));
+bytes32 constant TAG = bytes32(uint256(0xA10EBE1A3));
contract DeployScript is Script {
string[] chains = ["optimism", "arbitrum", "base"];
Factory[] factories = [
- Factory(0x3A0a11A7829bfB34400cE338a0442877FBC8582e),
- Factory(0x3A0a11A7829bfB34400cE338a0442877FBC8582e),
+ Factory(0x000000F00500f8BC2a68D90723AC00fCf4AD4b4D),
+ Factory(0x000000F00500f8BC2a68D90723AC00fCf4AD4b4D),
Factory(0x00000006d6C0519e0eB953CFfeb7007e5200680B)
];
+ IBorrowerURISource[] uriSources = [
+ IBorrowerURISource(0x0000000000000000000000000000000000000000),
+ IBorrowerURISource(0x0000000000000000000000000000000000000000),
+ IBorrowerURISource(0x0000000000000000000000000000000000000000)
+ ];
+
INFTManager[] uniswapNfts = [
INFTManager(0xC36442b4a4522E871399CD717aBDD847Ab11FE88),
INFTManager(0xC36442b4a4522E871399CD717aBDD847Ab11FE88),
@@ -42,6 +48,7 @@ contract DeployScript is Script {
function run() external {
for (uint256 i = 0; i < chains.length; i++) {
Factory factory = factories[i];
+ IBorrowerURISource uriSource = uriSources[i];
INFTManager uniswapNft = uniswapNfts[i];
IPermit2 permit2 = permit2s[i];
@@ -52,7 +59,7 @@ contract DeployScript is Script {
new LenderLens{salt: TAG}();
new Router{salt: TAG}(permit2);
- BorrowerNFT borrowerNft = new BorrowerNFT{salt: TAG}(factory);
+ BorrowerNFT borrowerNft = new BorrowerNFT{salt: TAG}(factory, uriSource);
new BoostManager{salt: TAG}(factory, address(borrowerNft), uniswapNft);
new FrontendManager{salt: TAG}(factory);
diff --git a/periphery/src/boost/BoostNFT.txt b/periphery/src/boost/BoostNFT.txt
deleted file mode 100644
index 66a3cc1c..00000000
--- a/periphery/src/boost/BoostNFT.txt
+++ /dev/null
@@ -1,151 +0,0 @@
-// SPDX-License-Identifier: AGPL-3.0-only
-pragma solidity 0.8.17;
-
-import {ERC721} from "solmate/tokens/ERC721.sol";
-import {IUniswapV3Pool} from "v3-core/contracts/interfaces/IUniswapV3Pool.sol";
-
-import {Borrower, IManager, ERC20} from "aloe-ii-core/Borrower.sol";
-import {Factory} from "aloe-ii-core/Factory.sol";
-
-import {NFTDescriptor} from "./NFTDescriptor.sol";
-import {SafeERC20Namer} from "./SafeERC20Namer.sol";
-
-contract BoostNFT is ERC721 {
- struct NFTAttributes {
- Borrower borrower;
- bool isGeneralized;
- }
-
- Factory public immutable FACTORY;
-
- address public owner;
-
- IManager public boostManager;
-
- mapping(uint256 => NFTAttributes) public attributesOf;
-
- Borrower[] internal _freeBorrowers;
-
- /*//////////////////////////////////////////////////////////////
- CONSTRUCTOR
- //////////////////////////////////////////////////////////////*/
-
- constructor(address owner_, Factory factory) ERC721("Uniswap V3 - Aloe Edition", "UNI-V3-ALOE") {
- owner = owner_;
- FACTORY = factory;
- }
-
- /*//////////////////////////////////////////////////////////////
- GOVERNANCE
- //////////////////////////////////////////////////////////////*/
-
- function setOwner(address owner_) external {
- require(msg.sender == owner);
- owner = owner_;
- }
-
- function setBoostManager(IManager boostManager_) external {
- require(msg.sender == owner);
- boostManager = boostManager_;
- }
-
- function createBorrower(IUniswapV3Pool pool) external {
- _freeBorrowers.push(Borrower(FACTORY.createBorrower(pool, address(this), 0)));
- }
-
- /*//////////////////////////////////////////////////////////////
- MINT
- //////////////////////////////////////////////////////////////*/
-
- function mint(IUniswapV3Pool pool, bytes memory initializationData, uint40 oracleSeed) public payable {
- uint256 id = uint256(keccak256(abi.encodePacked(msg.sender, balanceOf(msg.sender))));
-
- Borrower borrower = _nextBorrower(pool);
- attributesOf[id] = NFTAttributes(borrower, false);
- _mint(msg.sender, id);
-
- initializationData = abi.encode(msg.sender, 0, initializationData);
- borrower.modify{value: msg.value}(boostManager, initializationData, oracleSeed);
- }
-
- /*//////////////////////////////////////////////////////////////
- BORROWER MODIFY
- //////////////////////////////////////////////////////////////*/
-
- function modify(uint256 id, uint8 action, IManager manager, bytes memory data, uint40 oracleSeed) public payable {
- require(msg.sender == _ownerOf[id], "Aloe: only NFT owner can modify");
-
- NFTAttributes memory attributes = attributesOf[id];
-
- if (address(manager) == address(0)) {
- manager = boostManager;
- } else if (!attributes.isGeneralized) {
- attributesOf[id].isGeneralized = true;
- }
-
- data = abi.encode(msg.sender, action, data);
- attributes.borrower.modify{value: msg.value}(manager, data, oracleSeed);
- }
-
- function modify(uint256 id, uint8 action, bytes calldata data, uint40 oracleSeed) external payable {
- modify(id, action, IManager(address(0)), data, oracleSeed);
- }
-
- /*//////////////////////////////////////////////////////////////
- NFT DESCRIPTION
- //////////////////////////////////////////////////////////////*/
-
- function tokenURI(uint256 id) public view virtual override returns (string memory) {
- NFTAttributes memory attributes = attributesOf[id];
-
- int24 tickLower;
- int24 tickUpper;
- int24 tickCurrent;
- int24 tickSpacing;
- IUniswapV3Pool poolAddress = attributes.borrower.UNISWAP_POOL();
-
- if (!attributes.isGeneralized) {
- int24[] memory positions = attributes.borrower.getUniswapPositions();
- tickLower = positions[0];
- tickUpper = positions[1];
- (, tickCurrent, , , , , ) = poolAddress.slot0();
- tickSpacing = poolAddress.tickSpacing();
- }
-
- ERC20 token0 = attributes.borrower.TOKEN0();
- ERC20 token1 = attributes.borrower.TOKEN1();
- return
- NFTDescriptor.constructTokenURI(
- NFTDescriptor.ConstructTokenURIParams({
- tokenId: id,
- token0: address(token0),
- token1: address(token1),
- symbol0: SafeERC20Namer.tokenSymbol(address(token0)),
- symbol1: SafeERC20Namer.tokenSymbol(address(token1)),
- tickLower: tickLower,
- tickUpper: tickUpper,
- tickCurrent: tickCurrent,
- fee: poolAddress.fee(),
- poolAddress: address(poolAddress),
- borrowerAddress: address(attributes.borrower),
- isGeneralized: attributes.isGeneralized
- })
- );
- }
-
- /*//////////////////////////////////////////////////////////////
- HELPERS
- //////////////////////////////////////////////////////////////*/
-
- function _nextBorrower(IUniswapV3Pool pool) private returns (Borrower borrower) {
- unchecked {
- uint256 count = _freeBorrowers.length;
- if (count > 0) {
- borrower = _freeBorrowers[count - 1];
- _freeBorrowers.pop();
- } else {
- borrower = Borrower(FACTORY.createBorrower(pool, address(this), 0));
- }
- }
- }
-}
diff --git a/periphery/src/boost/NFTDescriptor.sol b/periphery/src/boost/NFTDescriptor.sol
deleted file mode 100644
index 5d74d551..00000000
--- a/periphery/src/boost/NFTDescriptor.sol
+++ /dev/null
@@ -1,158 +0,0 @@
-// SPDX-License-Identifier: AGPL-3.0-only
-pragma solidity 0.8.17;
-
-import {Base64} from "solady/utils/Base64.sol";
-import {LibString} from "solady/utils/LibString.sol";
-
-import {NFTSVG} from "./NFTSVG.sol";
-
-library NFTDescriptor {
- using LibString for string;
- using LibString for uint256;
-
- struct ConstructTokenURIParams {
- address poolAddress;
- uint24 fee;
- address token0;
- address token1;
- string symbol0;
- string symbol1;
- address borrowerAddress;
- uint256 counter;
- uint24 health;
- bool hasAnte;
- }
-
- function constructTokenURI(ConstructTokenURIParams memory params) internal pure returns (string memory) {
- // Pool-specific parameters
- string memory poolString = _addressToString(params.poolAddress);
- string memory feeString = _feeToString(params.fee);
- string memory token0 = _addressToString(params.token0);
- string memory token1 = _addressToString(params.token1);
- params.symbol0 = params.symbol0.escapeJSON();
- params.symbol1 = params.symbol1.escapeJSON();
- // Borrower-specific parameters
- string memory borrowerString = _addressToString(params.borrowerAddress);
- string memory counter = params.counter.toString();
-
- string memory name = _generateName(params);
- string memory description = _generateDescription(
- poolString,
- feeString,
- token0,
- token1,
- params.symbol0,
- params.symbol1,
- borrowerString,
- counter
- );
- string memory image = Base64.encode(bytes(_generateSVGImage(params, counter, token0, token1, feeString)));
-
- return
- string.concat(
- "data:application/json;base64,",
- Base64.encode(
- bytes(
- string.concat(
- '{"name":"',
- name,
- '", "description":"',
- description,
- '", "image": "',
- "data:image/svg+xml;base64,",
- image,
- '"}'
- )
- )
- )
- );
- }
-
- function _generateName(ConstructTokenURIParams memory params) private pure returns (string memory) {
- return string.concat(params.symbol0, "/", params.symbol1, " Borrower");
- }
-
- function _generateDescription(
- string memory poolString,
- string memory feeString,
- string memory token0,
- string memory token1,
- string memory symbol0,
- string memory symbol1,
- string memory borrowerString,
- string memory counter
- ) private pure returns (string memory) {
- return
- string.concat(
- "This NFT grants its owner control of an Aloe II Borrower in the ",
- symbol0,
- "-",
- symbol1,
- " market.\\n",
- "\\nBorrower: ",
- borrowerString,
- "\\n\\n",
- symbol0,
- ": ",
- token0,
- "\\n\\n",
- symbol1,
- ": ",
- token1,
- "\\n\\nUniswap Pool: ",
- poolString,
- " (",
- feeString,
- " Fee Tier)",
- "\\n\\nToken ID: ",
- counter,
- "\\n\\n",
- unicode"⚠️ DISCLAIMER: Due diligence is imperative when assessing this NFT. Make sure token addresses match the expected tokens, as token symbols may be imitated."
- );
- }
-
- function _generateSVGImage(
- ConstructTokenURIParams memory params,
- string memory tokenId,
- string memory token0,
- string memory token1,
- string memory feeString
- ) private pure returns (string memory) {
- NFTSVG.SVGParams memory svgParams = NFTSVG.SVGParams(
- tokenId.slice(0, 16),
- token0,
- token1,
- params.symbol0,
- params.symbol1,
- feeString,
- params.health,
- params.hasAnte,
- _tokenToColor(params.token0, 60),
- _tokenToColor(params.token1, 32),
- _tokenToColor(params.token0, 80),
- _tokenToColor(params.token1, 36)
- );
-
- return NFTSVG.generateSVG(svgParams);
- }
-
- /*//////////////////////////////////////////////////////////////
- HELPERS
- //////////////////////////////////////////////////////////////*/
-
- function _addressToString(address addr) private pure returns (string memory) {
- return LibString.toHexStringChecksummed(addr);
- }
-
- function _feeToString(uint24 fee) private pure returns (string memory) {
- if (fee == 100) return "0.01%";
- if (fee == 500) return "0.05%";
- if (fee == 3000) return "0.3%";
- if (fee == 10000) return "1.0%";
- return "";
- }
-
- function _tokenToColor(address token, uint256 offset) private pure returns (string memory) {
- return string.concat("#", uint256((uint160(token) >> offset) % (1 << 24)).toHexStringNoPrefix(3));
- }
-}
diff --git a/periphery/src/boost/NFTSVG.sol b/periphery/src/boost/NFTSVG.sol
deleted file mode 100644
index 6de932ad..00000000
--- a/periphery/src/boost/NFTSVG.sol
+++ /dev/null
@@ -1,269 +0,0 @@
-// SPDX-License-Identifier: AGPL-3.0-only
-pragma solidity 0.8.17;
-
-import {LibString} from "solady/utils/LibString.sol";
-
-/// @title NFTSVG
-/// @notice Generates SVG for a boosted Uniswap position
-library NFTSVG {
- using LibString for uint256;
- using LibString for int256;
-
- struct SVGParams {
- string tokenId;
- string token0;
- string token1;
- string symbol0;
- string symbol1;
- string feeTier;
- uint24 health;
- bool hasAnte;
- string color0;
- string color1;
- string color2;
- string color3;
- }
-
- function generateSVG(SVGParams memory params) internal pure returns (string memory svg) {
- return
- string.concat(
- _generateSVGDefs(),
- // card effects wrappers
- '',
- // card background
- '',
- _generateSVGHeaderText(params.symbol0, params.symbol1, params.feeTier),
- '" : ' filter="url(#grayscale)">',
- _generate3X3Quilt(params.color0, "rgb(242,245,238)", params.color1, params.color2, params.color3),
- _generateAnimatedText(params.token0, params.token1, params.symbol0, params.symbol1),
- "",
- _generatePositionDataText(params.tokenId, params.health, true),
- ""
- );
- }
-
- function _generateSVGDefs() private pure returns (string memory) {
- return
- string.concat(
- '