From 30ee1da8303d9d60510a1c547c6d3c68b0bf5a9a Mon Sep 17 00:00:00 2001 From: jj_ranalli Date: Wed, 11 Jun 2025 23:00:33 +0200 Subject: [PATCH 01/28] refactor --- .../TieredDiscount/NFTDiscount/NFTDiscount.sol | 0 .../TieredDiscount/TieredDiscount.sol | 4 ++-- .../TieredDiscount/structs/CurrencyParams.sol | 0 .../TieredDiscount/structs/DiscountParams.sol | 0 .../TieredDiscount/structs/ProductDiscounts.sol | 0 src/{ => PricingStrategies}/VRGDA/LinearVRGDAPrices.sol | 4 ++-- src/{ => PricingStrategies}/VRGDA/LogisticVRGDAPrices.sol | 4 ++-- src/{ => PricingStrategies}/VRGDA/VRGDAPrices.sol | 6 +++--- .../VRGDA/structs/LinearProductParams.sol | 0 .../VRGDA/structs/LinearVRGDAParams.sol | 0 .../VRGDA/structs/LogisticProductParams.sol | 0 .../VRGDA/structs/LogisticVRGDAParams.sol | 0 .../brtmoments/BrightMomentsCafe.sol | 4 ++-- src/{ => PricingStrategies}/brtmoments/NFTDiscountFixed.sol | 2 +- src/{ => PricingStrategies}/brtmoments/OneDiscounted.sol | 4 ++-- src/{ => PricingStrategies}/brtmoments/OneForFree.sol | 4 ++-- 16 files changed, 16 insertions(+), 16 deletions(-) rename src/{ => PricingStrategies}/TieredDiscount/NFTDiscount/NFTDiscount.sol (100%) rename src/{ => PricingStrategies}/TieredDiscount/TieredDiscount.sol (96%) rename src/{ => PricingStrategies}/TieredDiscount/structs/CurrencyParams.sol (100%) rename src/{ => PricingStrategies}/TieredDiscount/structs/DiscountParams.sol (100%) rename src/{ => PricingStrategies}/TieredDiscount/structs/ProductDiscounts.sol (100%) rename src/{ => PricingStrategies}/VRGDA/LinearVRGDAPrices.sol (97%) rename src/{ => PricingStrategies}/VRGDA/LogisticVRGDAPrices.sol (98%) rename src/{ => PricingStrategies}/VRGDA/VRGDAPrices.sol (95%) rename src/{ => PricingStrategies}/VRGDA/structs/LinearProductParams.sol (100%) rename src/{ => PricingStrategies}/VRGDA/structs/LinearVRGDAParams.sol (100%) rename src/{ => PricingStrategies}/VRGDA/structs/LogisticProductParams.sol (100%) rename src/{ => PricingStrategies}/VRGDA/structs/LogisticVRGDAParams.sol (100%) rename src/{ => PricingStrategies}/brtmoments/BrightMomentsCafe.sol (96%) rename src/{ => PricingStrategies}/brtmoments/NFTDiscountFixed.sol (96%) rename src/{ => PricingStrategies}/brtmoments/OneDiscounted.sol (96%) rename src/{ => PricingStrategies}/brtmoments/OneForFree.sol (96%) diff --git a/src/TieredDiscount/NFTDiscount/NFTDiscount.sol b/src/PricingStrategies/TieredDiscount/NFTDiscount/NFTDiscount.sol similarity index 100% rename from src/TieredDiscount/NFTDiscount/NFTDiscount.sol rename to src/PricingStrategies/TieredDiscount/NFTDiscount/NFTDiscount.sol diff --git a/src/TieredDiscount/TieredDiscount.sol b/src/PricingStrategies/TieredDiscount/TieredDiscount.sol similarity index 96% rename from src/TieredDiscount/TieredDiscount.sol rename to src/PricingStrategies/TieredDiscount/TieredDiscount.sol index 114c9e1..0b9a163 100644 --- a/src/TieredDiscount/TieredDiscount.sol +++ b/src/PricingStrategies/TieredDiscount/TieredDiscount.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: Unlicense pragma solidity ^0.8.19; -import {ISliceProductPrice} from "../../utils/Slice/interfaces/utils/ISliceProductPrice.sol"; -import {IProductsModule} from "../../utils/Slice/interfaces/IProductsModule.sol"; +import {ISliceProductPrice} from "../../../utils/Slice/interfaces/utils/ISliceProductPrice.sol"; +import {IProductsModule} from "../../../utils/Slice/interfaces/IProductsModule.sol"; import {CurrencyParams} from "./structs/CurrencyParams.sol"; import {ProductDiscounts, DiscountType} from "./structs/ProductDiscounts.sol"; import {DiscountParams, NFTType} from "./structs/DiscountParams.sol"; diff --git a/src/TieredDiscount/structs/CurrencyParams.sol b/src/PricingStrategies/TieredDiscount/structs/CurrencyParams.sol similarity index 100% rename from src/TieredDiscount/structs/CurrencyParams.sol rename to src/PricingStrategies/TieredDiscount/structs/CurrencyParams.sol diff --git a/src/TieredDiscount/structs/DiscountParams.sol b/src/PricingStrategies/TieredDiscount/structs/DiscountParams.sol similarity index 100% rename from src/TieredDiscount/structs/DiscountParams.sol rename to src/PricingStrategies/TieredDiscount/structs/DiscountParams.sol diff --git a/src/TieredDiscount/structs/ProductDiscounts.sol b/src/PricingStrategies/TieredDiscount/structs/ProductDiscounts.sol similarity index 100% rename from src/TieredDiscount/structs/ProductDiscounts.sol rename to src/PricingStrategies/TieredDiscount/structs/ProductDiscounts.sol diff --git a/src/VRGDA/LinearVRGDAPrices.sol b/src/PricingStrategies/VRGDA/LinearVRGDAPrices.sol similarity index 97% rename from src/VRGDA/LinearVRGDAPrices.sol rename to src/PricingStrategies/VRGDA/LinearVRGDAPrices.sol index 929be4b..a9e18ea 100644 --- a/src/VRGDA/LinearVRGDAPrices.sol +++ b/src/PricingStrategies/VRGDA/LinearVRGDAPrices.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -import {wadLn, unsafeWadDiv, toDaysWadUnsafe} from "../../utils/SignedWadMath.sol"; -import {IProductsModule} from "../../utils/Slice/interfaces/IProductsModule.sol"; +import {wadLn, unsafeWadDiv, toDaysWadUnsafe} from "../../../utils/SignedWadMath.sol"; +import {IProductsModule} from "../../../utils/Slice/interfaces/IProductsModule.sol"; import {LinearProductParams} from "./structs/LinearProductParams.sol"; import {LinearVRGDAParams} from "./structs/LinearVRGDAParams.sol"; diff --git a/src/VRGDA/LogisticVRGDAPrices.sol b/src/PricingStrategies/VRGDA/LogisticVRGDAPrices.sol similarity index 98% rename from src/VRGDA/LogisticVRGDAPrices.sol rename to src/PricingStrategies/VRGDA/LogisticVRGDAPrices.sol index 5d771cb..beb9361 100644 --- a/src/VRGDA/LogisticVRGDAPrices.sol +++ b/src/PricingStrategies/VRGDA/LogisticVRGDAPrices.sol @@ -10,8 +10,8 @@ import { unsafeDiv, wadExp, unsafeWadMul -} from "../../utils/SignedWadMath.sol"; -import {IProductsModule} from "../../utils/Slice/interfaces/IProductsModule.sol"; +} from "../../../utils/SignedWadMath.sol"; +import {IProductsModule} from "../../../utils/Slice/interfaces/IProductsModule.sol"; import {LogisticProductParams} from "./structs/LogisticProductParams.sol"; import {LogisticVRGDAParams} from "./structs/LogisticVRGDAParams.sol"; diff --git a/src/VRGDA/VRGDAPrices.sol b/src/PricingStrategies/VRGDA/VRGDAPrices.sol similarity index 95% rename from src/VRGDA/VRGDAPrices.sol rename to src/PricingStrategies/VRGDA/VRGDAPrices.sol index f07335c..d5a1d10 100644 --- a/src/VRGDA/VRGDAPrices.sol +++ b/src/PricingStrategies/VRGDA/VRGDAPrices.sol @@ -1,9 +1,9 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -import {wadExp, wadMul, unsafeWadMul, toWadUnsafe} from "../../utils/SignedWadMath.sol"; -import {ISliceProductPrice} from "../../utils/Slice/interfaces/utils/ISliceProductPrice.sol"; -import {IProductsModule} from "../../utils/Slice/interfaces/IProductsModule.sol"; +import {wadExp, wadMul, unsafeWadMul, toWadUnsafe} from "../../../utils/SignedWadMath.sol"; +import {ISliceProductPrice} from "../../../utils/Slice/interfaces/utils/ISliceProductPrice.sol"; +import {IProductsModule} from "../../../utils/Slice/interfaces/IProductsModule.sol"; /// @title Variable Rate Gradual Dutch Auction - Slice pricing strategy /// @author jacopo diff --git a/src/VRGDA/structs/LinearProductParams.sol b/src/PricingStrategies/VRGDA/structs/LinearProductParams.sol similarity index 100% rename from src/VRGDA/structs/LinearProductParams.sol rename to src/PricingStrategies/VRGDA/structs/LinearProductParams.sol diff --git a/src/VRGDA/structs/LinearVRGDAParams.sol b/src/PricingStrategies/VRGDA/structs/LinearVRGDAParams.sol similarity index 100% rename from src/VRGDA/structs/LinearVRGDAParams.sol rename to src/PricingStrategies/VRGDA/structs/LinearVRGDAParams.sol diff --git a/src/VRGDA/structs/LogisticProductParams.sol b/src/PricingStrategies/VRGDA/structs/LogisticProductParams.sol similarity index 100% rename from src/VRGDA/structs/LogisticProductParams.sol rename to src/PricingStrategies/VRGDA/structs/LogisticProductParams.sol diff --git a/src/VRGDA/structs/LogisticVRGDAParams.sol b/src/PricingStrategies/VRGDA/structs/LogisticVRGDAParams.sol similarity index 100% rename from src/VRGDA/structs/LogisticVRGDAParams.sol rename to src/PricingStrategies/VRGDA/structs/LogisticVRGDAParams.sol diff --git a/src/brtmoments/BrightMomentsCafe.sol b/src/PricingStrategies/brtmoments/BrightMomentsCafe.sol similarity index 96% rename from src/brtmoments/BrightMomentsCafe.sol rename to src/PricingStrategies/brtmoments/BrightMomentsCafe.sol index a44b94f..a04c29d 100644 --- a/src/brtmoments/BrightMomentsCafe.sol +++ b/src/PricingStrategies/brtmoments/BrightMomentsCafe.sol @@ -1,10 +1,10 @@ // SPDX-License-Identifier: Unlicense pragma solidity ^0.8.19; -import {ISliceProductPrice} from "../../utils/Slice/interfaces/utils/ISliceProductPrice.sol"; +import {ISliceProductPrice} from "../../../utils/Slice/interfaces/utils/ISliceProductPrice.sol"; import {Ownable} from "@openzeppelin/access/Ownable.sol"; import {IERC721} from "@openzeppelin/token/ERC721/IERC721.sol"; -import {IProductsModule} from "../../utils/Slice/interfaces/IProductsModule.sol"; +import {IProductsModule} from "../../../utils/Slice/interfaces/IProductsModule.sol"; /** * @notice Slice pricing strategy for bright moments diff --git a/src/brtmoments/NFTDiscountFixed.sol b/src/PricingStrategies/brtmoments/NFTDiscountFixed.sol similarity index 96% rename from src/brtmoments/NFTDiscountFixed.sol rename to src/PricingStrategies/brtmoments/NFTDiscountFixed.sol index c20cf0c..3625ec8 100644 --- a/src/brtmoments/NFTDiscountFixed.sol +++ b/src/PricingStrategies/brtmoments/NFTDiscountFixed.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: Unlicense pragma solidity ^0.8.19; -import {ISliceProductPrice} from "../../utils/Slice/interfaces/utils/ISliceProductPrice.sol"; +import {ISliceProductPrice} from "../../../utils/Slice/interfaces/utils/ISliceProductPrice.sol"; import {Ownable} from "@openzeppelin/access/Ownable.sol"; import {IERC1155} from "@openzeppelin/token/ERC1155/IERC1155.sol"; diff --git a/src/brtmoments/OneDiscounted.sol b/src/PricingStrategies/brtmoments/OneDiscounted.sol similarity index 96% rename from src/brtmoments/OneDiscounted.sol rename to src/PricingStrategies/brtmoments/OneDiscounted.sol index 0574f98..ec9b603 100644 --- a/src/brtmoments/OneDiscounted.sol +++ b/src/PricingStrategies/brtmoments/OneDiscounted.sol @@ -1,10 +1,10 @@ // SPDX-License-Identifier: Unlicense pragma solidity ^0.8.19; -import {ISliceProductPrice} from "../../utils/Slice/interfaces/utils/ISliceProductPrice.sol"; +import {ISliceProductPrice} from "../../../utils/Slice/interfaces/utils/ISliceProductPrice.sol"; import {IERC721} from "@openzeppelin/token/ERC721/IERC721.sol"; import {IERC1155} from "@openzeppelin/token/ERC1155/IERC1155.sol"; -import {IProductsModule} from "../../utils/Slice/interfaces/IProductsModule.sol"; +import {IProductsModule} from "../../../utils/Slice/interfaces/IProductsModule.sol"; /** * @notice Slice pricing strategy to give one product for free diff --git a/src/brtmoments/OneForFree.sol b/src/PricingStrategies/brtmoments/OneForFree.sol similarity index 96% rename from src/brtmoments/OneForFree.sol rename to src/PricingStrategies/brtmoments/OneForFree.sol index fb3d84f..de7489e 100644 --- a/src/brtmoments/OneForFree.sol +++ b/src/PricingStrategies/brtmoments/OneForFree.sol @@ -1,10 +1,10 @@ // SPDX-License-Identifier: Unlicense pragma solidity ^0.8.19; -import {ISliceProductPrice} from "../../utils/Slice/interfaces/utils/ISliceProductPrice.sol"; +import {ISliceProductPrice} from "../../../utils/Slice/interfaces/utils/ISliceProductPrice.sol"; import {IERC721} from "@openzeppelin/token/ERC721/IERC721.sol"; import {IERC1155} from "@openzeppelin/token/ERC1155/IERC1155.sol"; -import {IProductsModule} from "../../utils/Slice/interfaces/IProductsModule.sol"; +import {IProductsModule} from "../../../utils/Slice/interfaces/IProductsModule.sol"; /** * @notice Slice pricing strategy to give one product for free From 3309e442e247cbd3c4192763c7eaeef75513abac Mon Sep 17 00:00:00 2001 From: jj_ranalli Date: Fri, 20 Jun 2025 16:34:53 +0200 Subject: [PATCH 02/28] contracts init --- .env.example | 4 +- .gitignore | 2 - LICENSE | 2 +- remappings.txt | 2 + .../NFTDiscount/NFTDiscount.sol | 9 +-- .../TieredDiscount/TieredDiscount.sol | 56 ++++------------- .../TieredDiscount/structs/CurrencyParams.sol | 0 .../TieredDiscount/structs/DiscountParams.sol | 0 .../structs/ProductDiscounts.sol | 0 .../VRGDA/LinearVRGDAPrices.sol | 10 ++- .../VRGDA/LogisticVRGDAPrices.sol | 11 ++-- .../VRGDA/VRGDAPrices.sol | 39 +++--------- .../VRGDA/structs/LinearProductParams.sol | 0 .../VRGDA/structs/LinearVRGDAParams.sol | 0 .../VRGDA/structs/LogisticProductParams.sol | 0 .../VRGDA/structs/LogisticVRGDAParams.sol | 0 .../brtmoments/BrightMomentsCafe.sol | 0 .../brtmoments/NFTDiscountFixed.sol | 0 .../brtmoments/OneDiscounted.sol | 0 .../brtmoments/OneForFree.sol | 0 src/utils/PricingStrategy.sol | 61 +++++++++++++++++++ {utils => src/utils}/ScriptUtils.sol | 0 {utils => src/utils}/SignedWadMath.sol | 0 .../utils}/Slice/interfaces/IFundsModule.sol | 0 .../Slice/interfaces/IProductsModule.sol | 0 .../utils}/Slice/interfaces/ISliceCore.sol | 0 .../utils}/Slice/interfaces/ISlicer.sol | 0 .../Slice/interfaces/ISlicerManager.sol | 0 .../Slice/interfaces/utils/IPriceFeed.sol | 0 .../utils}/Slice/structs/Balance.sol | 0 .../utils}/Slice/structs/Category.sol | 0 .../utils}/Slice/structs/CurrencyPrice.sol | 0 .../utils}/Slice/structs/Function.sol | 0 {utils => src/utils}/Slice/structs/Payee.sol | 0 .../utils}/Slice/structs/PoolData.sol | 0 {utils => src/utils}/Slice/structs/Price.sol | 0 .../utils}/Slice/structs/Product.sol | 0 .../utils}/Slice/structs/ProductParams.sol | 0 .../utils}/Slice/structs/ProductPrice.sol | 0 .../utils}/Slice/structs/PurchaseParams.sol | 0 .../utils}/Slice/structs/Purchases.sol | 0 .../utils}/Slice/structs/SliceParams.sol | 0 .../utils}/Slice/structs/SlicerParams.sol | 0 .../utils}/Slice/structs/SubSlicerProduct.sol | 0 .../interfaces/utils/ISliceProductPrice.sol | 13 ---- 45 files changed, 100 insertions(+), 109 deletions(-) rename src/{PricingStrategies => pricing-strategy}/TieredDiscount/NFTDiscount/NFTDiscount.sol (97%) rename src/{PricingStrategies => pricing-strategy}/TieredDiscount/TieredDiscount.sol (65%) rename src/{PricingStrategies => pricing-strategy}/TieredDiscount/structs/CurrencyParams.sol (100%) rename src/{PricingStrategies => pricing-strategy}/TieredDiscount/structs/DiscountParams.sol (100%) rename src/{PricingStrategies => pricing-strategy}/TieredDiscount/structs/ProductDiscounts.sol (100%) rename src/{PricingStrategies => pricing-strategy}/VRGDA/LinearVRGDAPrices.sol (92%) rename src/{PricingStrategies => pricing-strategy}/VRGDA/LogisticVRGDAPrices.sol (95%) rename src/{PricingStrategies => pricing-strategy}/VRGDA/VRGDAPrices.sol (76%) rename src/{PricingStrategies => pricing-strategy}/VRGDA/structs/LinearProductParams.sol (100%) rename src/{PricingStrategies => pricing-strategy}/VRGDA/structs/LinearVRGDAParams.sol (100%) rename src/{PricingStrategies => pricing-strategy}/VRGDA/structs/LogisticProductParams.sol (100%) rename src/{PricingStrategies => pricing-strategy}/VRGDA/structs/LogisticVRGDAParams.sol (100%) rename src/{PricingStrategies => pricing-strategy}/brtmoments/BrightMomentsCafe.sol (100%) rename src/{PricingStrategies => pricing-strategy}/brtmoments/NFTDiscountFixed.sol (100%) rename src/{PricingStrategies => pricing-strategy}/brtmoments/OneDiscounted.sol (100%) rename src/{PricingStrategies => pricing-strategy}/brtmoments/OneForFree.sol (100%) create mode 100644 src/utils/PricingStrategy.sol rename {utils => src/utils}/ScriptUtils.sol (100%) rename {utils => src/utils}/SignedWadMath.sol (100%) rename {utils => src/utils}/Slice/interfaces/IFundsModule.sol (100%) rename {utils => src/utils}/Slice/interfaces/IProductsModule.sol (100%) rename {utils => src/utils}/Slice/interfaces/ISliceCore.sol (100%) rename {utils => src/utils}/Slice/interfaces/ISlicer.sol (100%) rename {utils => src/utils}/Slice/interfaces/ISlicerManager.sol (100%) rename {utils => src/utils}/Slice/interfaces/utils/IPriceFeed.sol (100%) rename {utils => src/utils}/Slice/structs/Balance.sol (100%) rename {utils => src/utils}/Slice/structs/Category.sol (100%) rename {utils => src/utils}/Slice/structs/CurrencyPrice.sol (100%) rename {utils => src/utils}/Slice/structs/Function.sol (100%) rename {utils => src/utils}/Slice/structs/Payee.sol (100%) rename {utils => src/utils}/Slice/structs/PoolData.sol (100%) rename {utils => src/utils}/Slice/structs/Price.sol (100%) rename {utils => src/utils}/Slice/structs/Product.sol (100%) rename {utils => src/utils}/Slice/structs/ProductParams.sol (100%) rename {utils => src/utils}/Slice/structs/ProductPrice.sol (100%) rename {utils => src/utils}/Slice/structs/PurchaseParams.sol (100%) rename {utils => src/utils}/Slice/structs/Purchases.sol (100%) rename {utils => src/utils}/Slice/structs/SliceParams.sol (100%) rename {utils => src/utils}/Slice/structs/SlicerParams.sol (100%) rename {utils => src/utils}/Slice/structs/SubSlicerProduct.sol (100%) delete mode 100644 utils/Slice/interfaces/utils/ISliceProductPrice.sol diff --git a/.env.example b/.env.example index 8299339..c9a9206 100644 --- a/.env.example +++ b/.env.example @@ -5,6 +5,4 @@ RPC_URL_BASE_GOERLI= ETHERSCAN_KEY= -PRIVATE_KEY= -SALT_VRGDA_LOGISTIC= -SALT_VRGDA_LINEAR= \ No newline at end of file +PRIVATE_KEY= \ No newline at end of file diff --git a/.gitignore b/.gitignore index 2ab9d62..5c251c9 100644 --- a/.gitignore +++ b/.gitignore @@ -3,9 +3,7 @@ broadcast/ cache/ out/ .env - __pycache__ - # Soldeer /dependencies diff --git a/LICENSE b/LICENSE index c52d58a..100de61 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2022 Paradigm +Copyright (c) 2022 Slice Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/remappings.txt b/remappings.txt index 2b3ed4f..d2fc743 100644 --- a/remappings.txt +++ b/remappings.txt @@ -1,3 +1,5 @@ @openzeppelin-upgradeable/=dependencies/@openzeppelin-contracts-upgradeable-5.3.0/ @openzeppelin/=dependencies/@openzeppelin-contracts-5.3.0/ forge-std/=dependencies/forge-std-1.9.7/src/ + +@/=src/ \ No newline at end of file diff --git a/src/PricingStrategies/TieredDiscount/NFTDiscount/NFTDiscount.sol b/src/pricing-strategy/TieredDiscount/NFTDiscount/NFTDiscount.sol similarity index 97% rename from src/PricingStrategies/TieredDiscount/NFTDiscount/NFTDiscount.sol rename to src/pricing-strategy/TieredDiscount/NFTDiscount/NFTDiscount.sol index 82b24d2..edc07fd 100644 --- a/src/PricingStrategies/TieredDiscount/NFTDiscount/NFTDiscount.sol +++ b/src/pricing-strategy/TieredDiscount/NFTDiscount/NFTDiscount.sol @@ -2,6 +2,7 @@ pragma solidity ^0.8.19; import { + IProductsModule, CurrencyParams, DiscountParams, ProductDiscounts, @@ -13,16 +14,16 @@ import {IERC721} from "@openzeppelin/token/ERC721/IERC721.sol"; import {IERC1155} from "@openzeppelin/token/ERC1155/IERC1155.sol"; /** - * @title NFTDiscount - Slice pricing strategy with discounts based on NFT ownership - * @author Dom-Mac <@zerohex_eth> - * @author jacopo <@jj_ranalli> + * @title NFTDiscount Pricing Strategy + * @notice Discounts based on NFT ownership + * @author Slice */ contract NFTDiscount is TieredDiscount { /*////////////////////////////////////////////////////////////// CONSTRUCTOR //////////////////////////////////////////////////////////////*/ - constructor(address _productsModuleAddress) TieredDiscount(_productsModuleAddress) {} + constructor(IProductsModule _productsModule) TieredDiscount(_productsModule) {} /*////////////////////////////////////////////////////////////// FUNCTIONS diff --git a/src/PricingStrategies/TieredDiscount/TieredDiscount.sol b/src/pricing-strategy/TieredDiscount/TieredDiscount.sol similarity index 65% rename from src/PricingStrategies/TieredDiscount/TieredDiscount.sol rename to src/pricing-strategy/TieredDiscount/TieredDiscount.sol index 0b9a163..ef92153 100644 --- a/src/PricingStrategies/TieredDiscount/TieredDiscount.sol +++ b/src/pricing-strategy/TieredDiscount/TieredDiscount.sol @@ -1,36 +1,31 @@ // SPDX-License-Identifier: Unlicense pragma solidity ^0.8.19; -import {ISliceProductPrice} from "../../../utils/Slice/interfaces/utils/ISliceProductPrice.sol"; -import {IProductsModule} from "../../../utils/Slice/interfaces/IProductsModule.sol"; +import {IProductsModule, PricingStrategy} from "@/utils/PricingStrategy.sol"; import {CurrencyParams} from "./structs/CurrencyParams.sol"; import {ProductDiscounts, DiscountType} from "./structs/ProductDiscounts.sol"; import {DiscountParams, NFTType} from "./structs/DiscountParams.sol"; /** - * @notice Slice pricing strategy with discounts based on asset ownership - * @author Dom-Mac <@zerohex_eth> - * @author jacopo <@jj_ranalli> + * @notice Tiered discounts based on asset ownership + * @author Slice */ -abstract contract TieredDiscount is ISliceProductPrice { +abstract contract TieredDiscount is PricingStrategy { + /*////////////////////////////////////////////////////////////// + EVENTS + //////////////////////////////////////////////////////////////*/ + event ProductPriceSet(uint256 slicerId, uint256 productId, CurrencyParams[] params); /*////////////////////////////////////////////////////////////// ERRORS //////////////////////////////////////////////////////////////*/ - error NotProductOwner(); error WrongCurrency(); error InvalidRelativeDiscount(); error InvalidMinQuantity(); error DiscountsNotDescending(DiscountParams nft); - /*////////////////////////////////////////////////////////////// - IMMUTABLE STORAGE - //////////////////////////////////////////////////////////////*/ - - address public immutable productsModuleAddress; - /*////////////////////////////////////////////////////////////// MUTABLE STORAGE //////////////////////////////////////////////////////////////*/ @@ -42,23 +37,7 @@ abstract contract TieredDiscount is ISliceProductPrice { CONSTRUCTOR //////////////////////////////////////////////////////////////*/ - constructor(address _productsModuleAddress) { - productsModuleAddress = _productsModuleAddress; - } - - /*////////////////////////////////////////////////////////////// - MODIFIERS - //////////////////////////////////////////////////////////////*/ - - /** - * @notice Check if msg.sender is owner of a product. Used to manage access to `setProductPrice`. - */ - modifier onlyProductOwner(uint256 slicerId, uint256 productId) { - if (!IProductsModule(productsModuleAddress).isProductOwner(slicerId, productId, msg.sender)) { - revert NotProductOwner(); - } - _; - } + constructor(IProductsModule _productsModule) PricingStrategy(_productsModule) {} /*////////////////////////////////////////////////////////////// FUNCTIONS @@ -80,16 +59,7 @@ abstract contract TieredDiscount is ISliceProductPrice { } /** - * @notice Function called by Slice protocol to calculate current product price. - * - * @param slicerId ID of the slicer being queried - * @param productId ID of the product being queried - * @param currency Currency chosen for the purchase - * @param quantity Number of units purchased - * @param buyer Address of the buyer - * @param params Additional params used to calculate price - * - * @return ethPrice and currencyPrice of product. + * @notice See {ISliceProductPrice} */ function productPrice( uint256 slicerId, @@ -97,14 +67,14 @@ abstract contract TieredDiscount is ISliceProductPrice { address currency, uint256 quantity, address buyer, - bytes memory params + bytes memory data ) public view override returns (uint256 ethPrice, uint256 currencyPrice) { ProductDiscounts memory discountParams = productDiscounts[slicerId][productId][currency]; if (discountParams.basePrice == 0) { if (!discountParams.isFree) revert WrongCurrency(); } else { - return _productPrice(slicerId, productId, currency, quantity, buyer, params, discountParams); + return _productPrice(slicerId, productId, currency, quantity, buyer, data, discountParams); } } @@ -120,7 +90,7 @@ abstract contract TieredDiscount is ISliceProductPrice { address currency, uint256 quantity, address buyer, - bytes memory params, + bytes memory data, ProductDiscounts memory discountParams ) internal view virtual returns (uint256 ethPrice, uint256 currencyPrice); } diff --git a/src/PricingStrategies/TieredDiscount/structs/CurrencyParams.sol b/src/pricing-strategy/TieredDiscount/structs/CurrencyParams.sol similarity index 100% rename from src/PricingStrategies/TieredDiscount/structs/CurrencyParams.sol rename to src/pricing-strategy/TieredDiscount/structs/CurrencyParams.sol diff --git a/src/PricingStrategies/TieredDiscount/structs/DiscountParams.sol b/src/pricing-strategy/TieredDiscount/structs/DiscountParams.sol similarity index 100% rename from src/PricingStrategies/TieredDiscount/structs/DiscountParams.sol rename to src/pricing-strategy/TieredDiscount/structs/DiscountParams.sol diff --git a/src/PricingStrategies/TieredDiscount/structs/ProductDiscounts.sol b/src/pricing-strategy/TieredDiscount/structs/ProductDiscounts.sol similarity index 100% rename from src/PricingStrategies/TieredDiscount/structs/ProductDiscounts.sol rename to src/pricing-strategy/TieredDiscount/structs/ProductDiscounts.sol diff --git a/src/PricingStrategies/VRGDA/LinearVRGDAPrices.sol b/src/pricing-strategy/VRGDA/LinearVRGDAPrices.sol similarity index 92% rename from src/PricingStrategies/VRGDA/LinearVRGDAPrices.sol rename to src/pricing-strategy/VRGDA/LinearVRGDAPrices.sol index a9e18ea..cb14ca0 100644 --- a/src/PricingStrategies/VRGDA/LinearVRGDAPrices.sol +++ b/src/pricing-strategy/VRGDA/LinearVRGDAPrices.sol @@ -2,11 +2,10 @@ pragma solidity ^0.8.0; import {wadLn, unsafeWadDiv, toDaysWadUnsafe} from "../../../utils/SignedWadMath.sol"; -import {IProductsModule} from "../../../utils/Slice/interfaces/IProductsModule.sol"; import {LinearProductParams} from "./structs/LinearProductParams.sol"; import {LinearVRGDAParams} from "./structs/LinearVRGDAParams.sol"; -import {VRGDAPrices} from "./VRGDAPrices.sol"; +import {IProductsModule, VRGDAPrices} from "./VRGDAPrices.sol"; /// @title Linear Variable Rate Gradual Dutch Auction - Slice pricing strategy /// @author jacopo @@ -31,7 +30,7 @@ contract LinearVRGDAPrices is VRGDAPrices { CONSTRUCTOR //////////////////////////////////////////////////////////////*/ - constructor(address productsModuleAddress) VRGDAPrices(productsModuleAddress) {} + constructor(IProductsModule _productsModule) VRGDAPrices(_productsModule) {} /*////////////////////////////////////////////////////////////// VRGDA PARAMETERS @@ -59,8 +58,7 @@ contract LinearVRGDAPrices is VRGDAPrices { /// Get product availability and isInfinite /// @dev available units is a uint32 - (uint256 availableUnits, bool isInfinite) = - IProductsModule(_productsModuleAddress).availableUnits(slicerId, productId); + (uint256 availableUnits, bool isInfinite) = productsModule.availableUnits(slicerId, productId); // Product must not have infinite availability require(!isInfinite, "NOT_FINITE_AVAILABILITY"); @@ -118,7 +116,7 @@ contract LinearVRGDAPrices is VRGDAPrices { require(productParams.startTime != 0, "PRODUCT_UNSET"); // Get available units - (uint256 availableUnits,) = IProductsModule(_productsModuleAddress).availableUnits(slicerId, productId); + (uint256 availableUnits,) = productsModule.availableUnits(slicerId, productId); // Calculate sold units from availableUnits uint256 soldUnits = productParams.startUnits - availableUnits; diff --git a/src/PricingStrategies/VRGDA/LogisticVRGDAPrices.sol b/src/pricing-strategy/VRGDA/LogisticVRGDAPrices.sol similarity index 95% rename from src/PricingStrategies/VRGDA/LogisticVRGDAPrices.sol rename to src/pricing-strategy/VRGDA/LogisticVRGDAPrices.sol index beb9361..d5c3cfc 100644 --- a/src/PricingStrategies/VRGDA/LogisticVRGDAPrices.sol +++ b/src/pricing-strategy/VRGDA/LogisticVRGDAPrices.sol @@ -11,11 +11,9 @@ import { wadExp, unsafeWadMul } from "../../../utils/SignedWadMath.sol"; -import {IProductsModule} from "../../../utils/Slice/interfaces/IProductsModule.sol"; import {LogisticProductParams} from "./structs/LogisticProductParams.sol"; import {LogisticVRGDAParams} from "./structs/LogisticVRGDAParams.sol"; - -import {VRGDAPrices} from "./VRGDAPrices.sol"; +import {IProductsModule, VRGDAPrices} from "./VRGDAPrices.sol"; /// @title Logistic Variable Rate Gradual Dutch Auction - Slice pricing strategy /// @author jacopo @@ -40,7 +38,7 @@ contract LogisticVRGDAPrices is VRGDAPrices { CONSTRUCTOR //////////////////////////////////////////////////////////////*/ - constructor(address productsModuleAddress) VRGDAPrices(productsModuleAddress) {} + constructor(IProductsModule _productsModule) VRGDAPrices(_productsModule) {} /*////////////////////////////////////////////////////////////// VRGDA PARAMETERS @@ -68,8 +66,7 @@ contract LogisticVRGDAPrices is VRGDAPrices { require(decayConstant >= type(int184).min, "MIN_DECAY_CONSTANT_EXCEEDED"); // Get product availability and isInfinite - (uint256 availableUnits, bool isInfinite) = - IProductsModule(_productsModuleAddress).availableUnits(slicerId, productId); + (uint256 availableUnits, bool isInfinite) = productsModule.availableUnits(slicerId, productId); // Product must not have infinite availability require(!isInfinite, "NON_FINITE_AVAILABILITY"); @@ -212,7 +209,7 @@ contract LogisticVRGDAPrices is VRGDAPrices { require(productParams.startTime != 0, "PRODUCT_UNSET"); // Get available units - (uint256 availableUnits,) = IProductsModule(_productsModuleAddress).availableUnits(slicerId, productId); + (uint256 availableUnits,) = productsModule.availableUnits(slicerId, productId); // Set ethPrice or currencyPrice based on chosen currency if (currency == address(0)) { diff --git a/src/PricingStrategies/VRGDA/VRGDAPrices.sol b/src/pricing-strategy/VRGDA/VRGDAPrices.sol similarity index 76% rename from src/PricingStrategies/VRGDA/VRGDAPrices.sol rename to src/pricing-strategy/VRGDA/VRGDAPrices.sol index d5a1d10..09231ed 100644 --- a/src/PricingStrategies/VRGDA/VRGDAPrices.sol +++ b/src/pricing-strategy/VRGDA/VRGDAPrices.sol @@ -1,41 +1,20 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; -import {wadExp, wadMul, unsafeWadMul, toWadUnsafe} from "../../../utils/SignedWadMath.sol"; -import {ISliceProductPrice} from "../../../utils/Slice/interfaces/utils/ISliceProductPrice.sol"; -import {IProductsModule} from "../../../utils/Slice/interfaces/IProductsModule.sol"; - -/// @title Variable Rate Gradual Dutch Auction - Slice pricing strategy -/// @author jacopo -/// @notice Price library with configurable params for each Slice product. - -abstract contract VRGDAPrices is ISliceProductPrice { - /*////////////////////////////////////////////////////////////// - STORAGE - //////////////////////////////////////////////////////////////*/ - - address internal immutable _productsModuleAddress; +import {wadExp, wadMul, unsafeWadMul, toWadUnsafe} from "@/utils/SignedWadMath.sol"; +import {IProductsModule, PricingStrategy} from "@/utils/PricingStrategy.sol"; +/** + * @title VRGDAPrices Pricing Strategy + * @notice Variable Rate Gradual Dutch Auction + * @author Slice + */ +abstract contract VRGDAPrices is PricingStrategy { /*////////////////////////////////////////////////////////////// CONSTRUCTOR //////////////////////////////////////////////////////////////*/ - constructor(address productsModuleAddress) { - _productsModuleAddress = productsModuleAddress; - } - - /*////////////////////////////////////////////////////////////// - MODIFIERS - //////////////////////////////////////////////////////////////*/ - - /// @notice Check if msg.sender is owner of a product. Used to manage access of `setProductPrice` - /// in implementations of this contract. - modifier onlyProductOwner(uint256 slicerId, uint256 productId) { - require( - IProductsModule(_productsModuleAddress).isProductOwner(slicerId, productId, msg.sender), "NOT_PRODUCT_OWNER" - ); - _; - } + constructor(IProductsModule _productsModule) PricingStrategy(_productsModule) {} /*////////////////////////////////////////////////////////////// PRICING LOGIC diff --git a/src/PricingStrategies/VRGDA/structs/LinearProductParams.sol b/src/pricing-strategy/VRGDA/structs/LinearProductParams.sol similarity index 100% rename from src/PricingStrategies/VRGDA/structs/LinearProductParams.sol rename to src/pricing-strategy/VRGDA/structs/LinearProductParams.sol diff --git a/src/PricingStrategies/VRGDA/structs/LinearVRGDAParams.sol b/src/pricing-strategy/VRGDA/structs/LinearVRGDAParams.sol similarity index 100% rename from src/PricingStrategies/VRGDA/structs/LinearVRGDAParams.sol rename to src/pricing-strategy/VRGDA/structs/LinearVRGDAParams.sol diff --git a/src/PricingStrategies/VRGDA/structs/LogisticProductParams.sol b/src/pricing-strategy/VRGDA/structs/LogisticProductParams.sol similarity index 100% rename from src/PricingStrategies/VRGDA/structs/LogisticProductParams.sol rename to src/pricing-strategy/VRGDA/structs/LogisticProductParams.sol diff --git a/src/PricingStrategies/VRGDA/structs/LogisticVRGDAParams.sol b/src/pricing-strategy/VRGDA/structs/LogisticVRGDAParams.sol similarity index 100% rename from src/PricingStrategies/VRGDA/structs/LogisticVRGDAParams.sol rename to src/pricing-strategy/VRGDA/structs/LogisticVRGDAParams.sol diff --git a/src/PricingStrategies/brtmoments/BrightMomentsCafe.sol b/src/pricing-strategy/brtmoments/BrightMomentsCafe.sol similarity index 100% rename from src/PricingStrategies/brtmoments/BrightMomentsCafe.sol rename to src/pricing-strategy/brtmoments/BrightMomentsCafe.sol diff --git a/src/PricingStrategies/brtmoments/NFTDiscountFixed.sol b/src/pricing-strategy/brtmoments/NFTDiscountFixed.sol similarity index 100% rename from src/PricingStrategies/brtmoments/NFTDiscountFixed.sol rename to src/pricing-strategy/brtmoments/NFTDiscountFixed.sol diff --git a/src/PricingStrategies/brtmoments/OneDiscounted.sol b/src/pricing-strategy/brtmoments/OneDiscounted.sol similarity index 100% rename from src/PricingStrategies/brtmoments/OneDiscounted.sol rename to src/pricing-strategy/brtmoments/OneDiscounted.sol diff --git a/src/PricingStrategies/brtmoments/OneForFree.sol b/src/pricing-strategy/brtmoments/OneForFree.sol similarity index 100% rename from src/PricingStrategies/brtmoments/OneForFree.sol rename to src/pricing-strategy/brtmoments/OneForFree.sol diff --git a/src/utils/PricingStrategy.sol b/src/utils/PricingStrategy.sol new file mode 100644 index 0000000..1a4767c --- /dev/null +++ b/src/utils/PricingStrategy.sol @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import {IProductsModule} from "../IProductsModule.sol"; + +/** + * @notice Pricing strategy inheritable contract. + * @author Slice + */ +abstract contract PricingStrategy { + /*////////////////////////////////////////////////////////////// + ERRORS + //////////////////////////////////////////////////////////////*/ + + error NotProductOwner(); + + /*////////////////////////////////////////////////////////////// + IMMUTABLES + //////////////////////////////////////////////////////////////*/ + + IProductsModule public immutable productsModule; + + constructor(IProductsModule _productsModule) { + productsModule = _productsModule; + } + + /*////////////////////////////////////////////////////////////// + MODIFIERS + //////////////////////////////////////////////////////////////*/ + + /** + * @notice Verifies if msg.sender is the product owner. + */ + modifier onlyProductOwner(uint256 slicerId, uint256 productId) { + if (!productsModule.isProductOwner(slicerId, productId, msg.sender)) { + revert NotProductOwner(); + } + _; + } + + /** + * @notice Function called by Slice protocol to calculate current product price. + * + * @param slicerId ID of the slicer being queried + * @param productId ID of the product being queried + * @param currency Currency chosen for the purchase + * @param quantity Number of units purchased + * @param buyer Address of the buyer + * @param data Additional data used to calculate price + * + * @return ethPrice and currencyPrice of product. + */ + function productPrice( + uint256 slicerId, + uint256 productId, + address currency, + uint256 quantity, + address buyer, + bytes memory data + ) external view virtual returns (uint256 ethPrice, uint256 currencyPrice); +} diff --git a/utils/ScriptUtils.sol b/src/utils/ScriptUtils.sol similarity index 100% rename from utils/ScriptUtils.sol rename to src/utils/ScriptUtils.sol diff --git a/utils/SignedWadMath.sol b/src/utils/SignedWadMath.sol similarity index 100% rename from utils/SignedWadMath.sol rename to src/utils/SignedWadMath.sol diff --git a/utils/Slice/interfaces/IFundsModule.sol b/src/utils/Slice/interfaces/IFundsModule.sol similarity index 100% rename from utils/Slice/interfaces/IFundsModule.sol rename to src/utils/Slice/interfaces/IFundsModule.sol diff --git a/utils/Slice/interfaces/IProductsModule.sol b/src/utils/Slice/interfaces/IProductsModule.sol similarity index 100% rename from utils/Slice/interfaces/IProductsModule.sol rename to src/utils/Slice/interfaces/IProductsModule.sol diff --git a/utils/Slice/interfaces/ISliceCore.sol b/src/utils/Slice/interfaces/ISliceCore.sol similarity index 100% rename from utils/Slice/interfaces/ISliceCore.sol rename to src/utils/Slice/interfaces/ISliceCore.sol diff --git a/utils/Slice/interfaces/ISlicer.sol b/src/utils/Slice/interfaces/ISlicer.sol similarity index 100% rename from utils/Slice/interfaces/ISlicer.sol rename to src/utils/Slice/interfaces/ISlicer.sol diff --git a/utils/Slice/interfaces/ISlicerManager.sol b/src/utils/Slice/interfaces/ISlicerManager.sol similarity index 100% rename from utils/Slice/interfaces/ISlicerManager.sol rename to src/utils/Slice/interfaces/ISlicerManager.sol diff --git a/utils/Slice/interfaces/utils/IPriceFeed.sol b/src/utils/Slice/interfaces/utils/IPriceFeed.sol similarity index 100% rename from utils/Slice/interfaces/utils/IPriceFeed.sol rename to src/utils/Slice/interfaces/utils/IPriceFeed.sol diff --git a/utils/Slice/structs/Balance.sol b/src/utils/Slice/structs/Balance.sol similarity index 100% rename from utils/Slice/structs/Balance.sol rename to src/utils/Slice/structs/Balance.sol diff --git a/utils/Slice/structs/Category.sol b/src/utils/Slice/structs/Category.sol similarity index 100% rename from utils/Slice/structs/Category.sol rename to src/utils/Slice/structs/Category.sol diff --git a/utils/Slice/structs/CurrencyPrice.sol b/src/utils/Slice/structs/CurrencyPrice.sol similarity index 100% rename from utils/Slice/structs/CurrencyPrice.sol rename to src/utils/Slice/structs/CurrencyPrice.sol diff --git a/utils/Slice/structs/Function.sol b/src/utils/Slice/structs/Function.sol similarity index 100% rename from utils/Slice/structs/Function.sol rename to src/utils/Slice/structs/Function.sol diff --git a/utils/Slice/structs/Payee.sol b/src/utils/Slice/structs/Payee.sol similarity index 100% rename from utils/Slice/structs/Payee.sol rename to src/utils/Slice/structs/Payee.sol diff --git a/utils/Slice/structs/PoolData.sol b/src/utils/Slice/structs/PoolData.sol similarity index 100% rename from utils/Slice/structs/PoolData.sol rename to src/utils/Slice/structs/PoolData.sol diff --git a/utils/Slice/structs/Price.sol b/src/utils/Slice/structs/Price.sol similarity index 100% rename from utils/Slice/structs/Price.sol rename to src/utils/Slice/structs/Price.sol diff --git a/utils/Slice/structs/Product.sol b/src/utils/Slice/structs/Product.sol similarity index 100% rename from utils/Slice/structs/Product.sol rename to src/utils/Slice/structs/Product.sol diff --git a/utils/Slice/structs/ProductParams.sol b/src/utils/Slice/structs/ProductParams.sol similarity index 100% rename from utils/Slice/structs/ProductParams.sol rename to src/utils/Slice/structs/ProductParams.sol diff --git a/utils/Slice/structs/ProductPrice.sol b/src/utils/Slice/structs/ProductPrice.sol similarity index 100% rename from utils/Slice/structs/ProductPrice.sol rename to src/utils/Slice/structs/ProductPrice.sol diff --git a/utils/Slice/structs/PurchaseParams.sol b/src/utils/Slice/structs/PurchaseParams.sol similarity index 100% rename from utils/Slice/structs/PurchaseParams.sol rename to src/utils/Slice/structs/PurchaseParams.sol diff --git a/utils/Slice/structs/Purchases.sol b/src/utils/Slice/structs/Purchases.sol similarity index 100% rename from utils/Slice/structs/Purchases.sol rename to src/utils/Slice/structs/Purchases.sol diff --git a/utils/Slice/structs/SliceParams.sol b/src/utils/Slice/structs/SliceParams.sol similarity index 100% rename from utils/Slice/structs/SliceParams.sol rename to src/utils/Slice/structs/SliceParams.sol diff --git a/utils/Slice/structs/SlicerParams.sol b/src/utils/Slice/structs/SlicerParams.sol similarity index 100% rename from utils/Slice/structs/SlicerParams.sol rename to src/utils/Slice/structs/SlicerParams.sol diff --git a/utils/Slice/structs/SubSlicerProduct.sol b/src/utils/Slice/structs/SubSlicerProduct.sol similarity index 100% rename from utils/Slice/structs/SubSlicerProduct.sol rename to src/utils/Slice/structs/SubSlicerProduct.sol diff --git a/utils/Slice/interfaces/utils/ISliceProductPrice.sol b/utils/Slice/interfaces/utils/ISliceProductPrice.sol deleted file mode 100644 index 6ac9b8c..0000000 --- a/utils/Slice/interfaces/utils/ISliceProductPrice.sol +++ /dev/null @@ -1,13 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.0; - -interface ISliceProductPrice { - function productPrice( - uint256 slicerId, - uint256 productId, - address currency, - uint256 quantity, - address buyer, - bytes memory data - ) external view returns (uint256 ethPrice, uint256 currencyPrice); -} From b6bda44f91a47507668ff7d394416dcd65818fe3 Mon Sep 17 00:00:00 2001 From: jj_ranalli Date: Mon, 23 Jun 2025 11:02:25 +0200 Subject: [PATCH 03/28] remove remappings.txt --- remappings.txt | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 remappings.txt diff --git a/remappings.txt b/remappings.txt deleted file mode 100644 index d2fc743..0000000 --- a/remappings.txt +++ /dev/null @@ -1,5 +0,0 @@ -@openzeppelin-upgradeable/=dependencies/@openzeppelin-contracts-upgradeable-5.3.0/ -@openzeppelin/=dependencies/@openzeppelin-contracts-5.3.0/ -forge-std/=dependencies/forge-std-1.9.7/src/ - -@/=src/ \ No newline at end of file From 4c2973242d4cfd71da3c6c7916f586fa92dc7e30 Mon Sep 17 00:00:00 2001 From: jj_ranalli Date: Mon, 23 Jun 2025 14:04:13 +0200 Subject: [PATCH 04/28] contracts init --- .gitignore | 2 + TODO.md | 7 - foundry.toml | 13 +- script/Deploy.s.sol | 4 +- {src/utils => script}/ScriptUtils.sol | 8 +- script/WriteAddresses.s.sol | 4 +- soldeer.lock | 7 + .../NFTDiscount/NFTDiscount.sol | 6 +- .../TieredDiscount/TieredDiscount.sol | 8 +- .../{structs => types}/CurrencyParams.sol | 2 +- .../{structs => types}/DiscountParams.sol | 2 +- .../{structs => types}/ProductDiscounts.sol | 2 +- .../VRGDA/LinearVRGDAPrices.sol | 15 +- .../VRGDA/LogisticVRGDAPrices.sol | 14 +- src/pricing-strategy/VRGDA/VRGDAPrices.sol | 4 +- .../LinearProductParams.sol | 2 +- .../{structs => types}/LinearVRGDAParams.sol | 2 +- .../LogisticProductParams.sol | 2 +- .../LogisticVRGDAParams.sol | 2 +- .../brtmoments/BrightMomentsCafe.sol | 142 ------------------ .../brtmoments/NFTDiscountFixed.sol | 88 ----------- .../brtmoments/OneDiscounted.sol | 132 ---------------- .../brtmoments/OneForFree.sol | 132 ---------------- src/utils/OnchainAction.sol | 4 + src/utils/PricingStrategy.sol | 61 +------- src/utils/PricingStrategyAction.sol | 4 + src/utils/Slice/interfaces/IFundsModule.sol | 41 ----- .../Slice/interfaces/IProductsModule.sol | 70 --------- src/utils/Slice/interfaces/ISliceCore.sol | 47 ------ src/utils/Slice/interfaces/ISlicer.sol | 64 -------- src/utils/Slice/interfaces/ISlicerManager.sol | 12 -- .../Slice/interfaces/utils/IPriceFeed.sol | 34 ----- src/utils/Slice/structs/Balance.sol | 7 - src/utils/Slice/structs/Category.sol | 6 - src/utils/Slice/structs/CurrencyPrice.sol | 9 -- src/utils/Slice/structs/Function.sol | 17 --- src/utils/Slice/structs/Payee.sol | 8 - src/utils/Slice/structs/PoolData.sol | 8 - src/utils/Slice/structs/Price.sol | 9 -- src/utils/Slice/structs/Product.sol | 37 ----- src/utils/Slice/structs/ProductParams.sol | 19 --- src/utils/Slice/structs/ProductPrice.sol | 8 - src/utils/Slice/structs/PurchaseParams.sol | 10 -- src/utils/Slice/structs/Purchases.sol | 6 - src/utils/Slice/structs/SliceParams.sol | 25 --- src/utils/Slice/structs/SlicerParams.sol | 25 --- src/utils/Slice/structs/SubSlicerProduct.sol | 7 - src/utils/{ => math}/SignedWadMath.sol | 2 +- test/ERC721Discount.sol | 7 +- test/LinearVRGDA.t.sol | 7 +- test/LogisticVRGDA.t.sol | 6 +- test/correctness/LinearVRGDACorrectness.t.sol | 7 +- test/mocks/MockERC1155.sol | 4 +- test/mocks/MockERC721.sol | 4 +- test/mocks/MockLinearVRGDAPrices.sol | 6 +- test/mocks/MockLogisticVRGDAPrices.sol | 6 +- test/mocks/MockProductsModule.sol | 2 +- 57 files changed, 96 insertions(+), 1093 deletions(-) delete mode 100644 TODO.md rename {src/utils => script}/ScriptUtils.sol (98%) rename src/pricing-strategy/TieredDiscount/{structs => types}/CurrencyParams.sol (96%) rename src/pricing-strategy/TieredDiscount/{structs => types}/DiscountParams.sol (96%) rename src/pricing-strategy/TieredDiscount/{structs => types}/ProductDiscounts.sol (95%) rename src/pricing-strategy/VRGDA/{structs => types}/LinearProductParams.sol (95%) rename src/pricing-strategy/VRGDA/{structs => types}/LinearVRGDAParams.sol (93%) rename src/pricing-strategy/VRGDA/{structs => types}/LogisticProductParams.sol (95%) rename src/pricing-strategy/VRGDA/{structs => types}/LogisticVRGDAParams.sol (94%) delete mode 100644 src/pricing-strategy/brtmoments/BrightMomentsCafe.sol delete mode 100644 src/pricing-strategy/brtmoments/NFTDiscountFixed.sol delete mode 100644 src/pricing-strategy/brtmoments/OneDiscounted.sol delete mode 100644 src/pricing-strategy/brtmoments/OneForFree.sol create mode 100644 src/utils/OnchainAction.sol create mode 100644 src/utils/PricingStrategyAction.sol delete mode 100644 src/utils/Slice/interfaces/IFundsModule.sol delete mode 100644 src/utils/Slice/interfaces/IProductsModule.sol delete mode 100644 src/utils/Slice/interfaces/ISliceCore.sol delete mode 100644 src/utils/Slice/interfaces/ISlicer.sol delete mode 100644 src/utils/Slice/interfaces/ISlicerManager.sol delete mode 100644 src/utils/Slice/interfaces/utils/IPriceFeed.sol delete mode 100644 src/utils/Slice/structs/Balance.sol delete mode 100644 src/utils/Slice/structs/Category.sol delete mode 100644 src/utils/Slice/structs/CurrencyPrice.sol delete mode 100644 src/utils/Slice/structs/Function.sol delete mode 100644 src/utils/Slice/structs/Payee.sol delete mode 100644 src/utils/Slice/structs/PoolData.sol delete mode 100644 src/utils/Slice/structs/Price.sol delete mode 100644 src/utils/Slice/structs/Product.sol delete mode 100644 src/utils/Slice/structs/ProductParams.sol delete mode 100644 src/utils/Slice/structs/ProductPrice.sol delete mode 100644 src/utils/Slice/structs/PurchaseParams.sol delete mode 100644 src/utils/Slice/structs/Purchases.sol delete mode 100644 src/utils/Slice/structs/SliceParams.sol delete mode 100644 src/utils/Slice/structs/SlicerParams.sol delete mode 100644 src/utils/Slice/structs/SubSlicerProduct.sol rename src/utils/{ => math}/SignedWadMath.sol (99%) diff --git a/.gitignore b/.gitignore index 5c251c9..4453931 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,5 @@ __pycache__ # Soldeer /dependencies +remappings.txt +src/**/internal/ \ No newline at end of file diff --git a/TODO.md b/TODO.md deleted file mode 100644 index df616f6..0000000 --- a/TODO.md +++ /dev/null @@ -1,7 +0,0 @@ -- [ ] Figure out where to store slice interface contracts -- Move slice repos in a separate repo + publish to soldeer to import - -- [ ] script to get started quickly - -- [ ] Create a simple cli generator to let the user create a new pricing strategy skeleton easily - -- [ ] improve readme and fix links diff --git a/foundry.toml b/foundry.toml index d780398..150bebc 100644 --- a/foundry.toml +++ b/foundry.toml @@ -4,8 +4,17 @@ optimizer = true optimizer_runs = 1000000 no_match_test = "FFI" dynamic_test_linking = true -libs = ["dependencies"] +libs = ["dependencies", "../core/src", "../core/dependencies"] fs_permissions = [{ access = "read", path = "./src"}, { access= "read", path = "./broadcast/Deploy.s.sol/8453/run-latest.json"}, { access = "read-write", path = "./deployments"}, { access = "read", path = "./out"}] +remappings = [ + "slice/=dependencies/slice-0.0.2/", + "forge-std/=dependencies/forge-std-1.9.7/src/", + "@openzeppelin-upgradeable-5.3.0/=dependencies/@openzeppelin-contracts-upgradeable-5.3.0/", + "@openzeppelin-5.3.0/=dependencies/@openzeppelin-contracts-5.3.0/", + "@openzeppelin-upgradeable-4.8.0/=dependencies/@openzeppelin-contracts-upgradeable-4.8.0/", + "@openzeppelin-4.8.0/=dependencies/@openzeppelin-contracts-4.8.0/", + "@/=src/" +] [profile.intense] fuzz_runs = 10000 @@ -33,6 +42,8 @@ base-goerli = {key="${ETHERSCAN_KEY}", chain=84531, url="https://api-goerli.base recursive_deps = true [dependencies] +slice = "0.0.2" forge-std = "1.9.7" "@openzeppelin-contracts" = "5.3.0" "@openzeppelin-contracts-upgradeable" = "5.3.0" + diff --git a/script/Deploy.s.sol b/script/Deploy.s.sol index 5e6b27e..7bc0b3a 100644 --- a/script/Deploy.s.sol +++ b/script/Deploy.s.sol @@ -1,9 +1,9 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.0; +pragma solidity ^0.8.20; -import {BaseScript, SetUpContractsList} from "../utils/ScriptUtils.sol"; import {console} from "forge-std/console.sol"; import {VmSafe} from "forge-std/Vm.sol"; +import {BaseScript, SetUpContractsList} from "./ScriptUtils.sol"; contract DeployScript is BaseScript, SetUpContractsList { constructor() SetUpContractsList("src") {} diff --git a/src/utils/ScriptUtils.sol b/script/ScriptUtils.sol similarity index 98% rename from src/utils/ScriptUtils.sol rename to script/ScriptUtils.sol index 607784b..4514235 100644 --- a/src/utils/ScriptUtils.sol +++ b/script/ScriptUtils.sol @@ -1,12 +1,12 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.0; +pragma solidity ^0.8.20; import {console} from "forge-std/console.sol"; import {Script} from "forge-std/Script.sol"; -import {ISliceCore} from "./Slice/interfaces/ISliceCore.sol"; -import {IProductsModule} from "./Slice/interfaces/IProductsModule.sol"; -import {IFundsModule} from "./Slice/interfaces/IFundsModule.sol"; import {VmSafe} from "forge-std/Vm.sol"; +import {ISliceCore} from "slice/interfaces/ISliceCore.sol"; +import {IProductsModule} from "slice/interfaces/IProductsModule.sol"; +import {IFundsModule} from "slice/interfaces/IFundsModule.sol"; /** * Helper contract to enforce correct chain selection in scripts diff --git a/script/WriteAddresses.s.sol b/script/WriteAddresses.s.sol index 49b7f65..bb52cbd 100644 --- a/script/WriteAddresses.s.sol +++ b/script/WriteAddresses.s.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: UNLICENSED -pragma solidity >=0.8; +pragma solidity ^0.8.20; -import {SetUpContractsList} from "../utils/ScriptUtils.sol"; +import {SetUpContractsList} from "./ScriptUtils.sol"; contract WriteAddressesScript is SetUpContractsList { constructor() SetUpContractsList("src") {} diff --git a/soldeer.lock b/soldeer.lock index 92541d3..3afbcdf 100644 --- a/soldeer.lock +++ b/soldeer.lock @@ -18,3 +18,10 @@ version = "1.9.7" url = "https://soldeer-revisions.s3.amazonaws.com/forge-std/1_9_7_28-04-2025_15:55:08_forge-std-1.9.zip" checksum = "8d9e0a885fa8ee6429a4d344aeb6799119f6a94c7c4fe6f188df79b0dce294ba" integrity = "9e60fdba82bc374df80db7f2951faff6467b9091873004a3d314cf0c084b3c7d" + +[[dependencies]] +name = "slice" +version = "0.0.2" +url = "https://soldeer-revisions.s3.amazonaws.com/slice/0_0_2_23-06-2025_11:59:12_src.zip" +checksum = "d9c1daaeb72f63af5ee374420ed291d0ab8b9881309452dbd84dfc6ad268adc4" +integrity = "0a08fe5a55abc15fdc9e5a29e65db2d192d775bd19b17d06529b4178a5b0b04d" diff --git a/src/pricing-strategy/TieredDiscount/NFTDiscount/NFTDiscount.sol b/src/pricing-strategy/TieredDiscount/NFTDiscount/NFTDiscount.sol index edc07fd..e9ea6da 100644 --- a/src/pricing-strategy/TieredDiscount/NFTDiscount/NFTDiscount.sol +++ b/src/pricing-strategy/TieredDiscount/NFTDiscount/NFTDiscount.sol @@ -1,6 +1,8 @@ // SPDX-License-Identifier: Unlicense -pragma solidity ^0.8.19; +pragma solidity ^0.8.20; +import {IERC721} from "@openzeppelin-5.3.0/token/ERC721/IERC721.sol"; +import {IERC1155} from "@openzeppelin-5.3.0/token/ERC1155/IERC1155.sol"; import { IProductsModule, CurrencyParams, @@ -10,8 +12,6 @@ import { TieredDiscount, NFTType } from "../TieredDiscount.sol"; -import {IERC721} from "@openzeppelin/token/ERC721/IERC721.sol"; -import {IERC1155} from "@openzeppelin/token/ERC1155/IERC1155.sol"; /** * @title NFTDiscount Pricing Strategy diff --git a/src/pricing-strategy/TieredDiscount/TieredDiscount.sol b/src/pricing-strategy/TieredDiscount/TieredDiscount.sol index ef92153..ec644a4 100644 --- a/src/pricing-strategy/TieredDiscount/TieredDiscount.sol +++ b/src/pricing-strategy/TieredDiscount/TieredDiscount.sol @@ -1,10 +1,10 @@ // SPDX-License-Identifier: Unlicense -pragma solidity ^0.8.19; +pragma solidity ^0.8.20; import {IProductsModule, PricingStrategy} from "@/utils/PricingStrategy.sol"; -import {CurrencyParams} from "./structs/CurrencyParams.sol"; -import {ProductDiscounts, DiscountType} from "./structs/ProductDiscounts.sol"; -import {DiscountParams, NFTType} from "./structs/DiscountParams.sol"; +import {CurrencyParams} from "./types/CurrencyParams.sol"; +import {ProductDiscounts, DiscountType} from "./types/ProductDiscounts.sol"; +import {DiscountParams, NFTType} from "./types/DiscountParams.sol"; /** * @notice Tiered discounts based on asset ownership diff --git a/src/pricing-strategy/TieredDiscount/structs/CurrencyParams.sol b/src/pricing-strategy/TieredDiscount/types/CurrencyParams.sol similarity index 96% rename from src/pricing-strategy/TieredDiscount/structs/CurrencyParams.sol rename to src/pricing-strategy/TieredDiscount/types/CurrencyParams.sol index 9f058d7..afe5d51 100644 --- a/src/pricing-strategy/TieredDiscount/structs/CurrencyParams.sol +++ b/src/pricing-strategy/TieredDiscount/types/CurrencyParams.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; +pragma solidity ^0.8.20; import {DiscountParams} from "./DiscountParams.sol"; import {DiscountType} from "./ProductDiscounts.sol"; diff --git a/src/pricing-strategy/TieredDiscount/structs/DiscountParams.sol b/src/pricing-strategy/TieredDiscount/types/DiscountParams.sol similarity index 96% rename from src/pricing-strategy/TieredDiscount/structs/DiscountParams.sol rename to src/pricing-strategy/TieredDiscount/types/DiscountParams.sol index 4ec4990..26c1a2d 100644 --- a/src/pricing-strategy/TieredDiscount/structs/DiscountParams.sol +++ b/src/pricing-strategy/TieredDiscount/types/DiscountParams.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; +pragma solidity ^0.8.20; enum NFTType { ERC721, diff --git a/src/pricing-strategy/TieredDiscount/structs/ProductDiscounts.sol b/src/pricing-strategy/TieredDiscount/types/ProductDiscounts.sol similarity index 95% rename from src/pricing-strategy/TieredDiscount/structs/ProductDiscounts.sol rename to src/pricing-strategy/TieredDiscount/types/ProductDiscounts.sol index a7ddf86..497a8a2 100644 --- a/src/pricing-strategy/TieredDiscount/structs/ProductDiscounts.sol +++ b/src/pricing-strategy/TieredDiscount/types/ProductDiscounts.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; +pragma solidity ^0.8.20; import {DiscountParams} from "./DiscountParams.sol"; diff --git a/src/pricing-strategy/VRGDA/LinearVRGDAPrices.sol b/src/pricing-strategy/VRGDA/LinearVRGDAPrices.sol index cb14ca0..f447ec7 100644 --- a/src/pricing-strategy/VRGDA/LinearVRGDAPrices.sol +++ b/src/pricing-strategy/VRGDA/LinearVRGDAPrices.sol @@ -1,15 +1,14 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -import {wadLn, unsafeWadDiv, toDaysWadUnsafe} from "../../../utils/SignedWadMath.sol"; -import {LinearProductParams} from "./structs/LinearProductParams.sol"; -import {LinearVRGDAParams} from "./structs/LinearVRGDAParams.sol"; +pragma solidity ^0.8.20; +import {wadLn, unsafeWadDiv, toDaysWadUnsafe} from "@/utils/math/SignedWadMath.sol"; +import {LinearProductParams} from "./types/LinearProductParams.sol"; +import {LinearVRGDAParams} from "./types/LinearVRGDAParams.sol"; import {IProductsModule, VRGDAPrices} from "./VRGDAPrices.sol"; -/// @title Linear Variable Rate Gradual Dutch Auction - Slice pricing strategy -/// @author jacopo -/// @notice VRGDA with a linear issuance curve - Price library with different params for each Slice product. +/// @title Linear Variable Rate Gradual Dutch Auction - Slice pricing strategy +/// @notice VRGDA with a linear issuance curve - Price library with different params for each Slice product. +/// @author Slice contract LinearVRGDAPrices is VRGDAPrices { event ProductPriceSet( uint256 slicerId, diff --git a/src/pricing-strategy/VRGDA/LogisticVRGDAPrices.sol b/src/pricing-strategy/VRGDA/LogisticVRGDAPrices.sol index d5c3cfc..5dba409 100644 --- a/src/pricing-strategy/VRGDA/LogisticVRGDAPrices.sol +++ b/src/pricing-strategy/VRGDA/LogisticVRGDAPrices.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; +pragma solidity ^0.8.20; import { wadMul, @@ -10,14 +10,14 @@ import { unsafeDiv, wadExp, unsafeWadMul -} from "../../../utils/SignedWadMath.sol"; -import {LogisticProductParams} from "./structs/LogisticProductParams.sol"; -import {LogisticVRGDAParams} from "./structs/LogisticVRGDAParams.sol"; +} from "@/utils/math/SignedWadMath.sol"; +import {LogisticProductParams} from "./types/LogisticProductParams.sol"; +import {LogisticVRGDAParams} from "./types/LogisticVRGDAParams.sol"; import {IProductsModule, VRGDAPrices} from "./VRGDAPrices.sol"; -/// @title Logistic Variable Rate Gradual Dutch Auction - Slice pricing strategy -/// @author jacopo -/// @notice VRGDA with a logistic issuance curve - Price library with different params for each Slice product. +/// @title Logistic Variable Rate Gradual Dutch Auction - Slice pricing strategy +/// @notice VRGDA with a logistic issuance curve - Price library with different params for each Slice product. +/// @author Slice contract LogisticVRGDAPrices is VRGDAPrices { event ProductPriceSet( uint256 slicerId, diff --git a/src/pricing-strategy/VRGDA/VRGDAPrices.sol b/src/pricing-strategy/VRGDA/VRGDAPrices.sol index 09231ed..6624e13 100644 --- a/src/pricing-strategy/VRGDA/VRGDAPrices.sol +++ b/src/pricing-strategy/VRGDA/VRGDAPrices.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; +pragma solidity ^0.8.20; -import {wadExp, wadMul, unsafeWadMul, toWadUnsafe} from "@/utils/SignedWadMath.sol"; +import {wadExp, wadMul, unsafeWadMul, toWadUnsafe} from "@/utils/math/SignedWadMath.sol"; import {IProductsModule, PricingStrategy} from "@/utils/PricingStrategy.sol"; /** diff --git a/src/pricing-strategy/VRGDA/structs/LinearProductParams.sol b/src/pricing-strategy/VRGDA/types/LinearProductParams.sol similarity index 95% rename from src/pricing-strategy/VRGDA/structs/LinearProductParams.sol rename to src/pricing-strategy/VRGDA/types/LinearProductParams.sol index b77c04e..a15829b 100644 --- a/src/pricing-strategy/VRGDA/structs/LinearProductParams.sol +++ b/src/pricing-strategy/VRGDA/types/LinearProductParams.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; +pragma solidity ^0.8.20; import {LinearVRGDAParams} from "./LinearVRGDAParams.sol"; diff --git a/src/pricing-strategy/VRGDA/structs/LinearVRGDAParams.sol b/src/pricing-strategy/VRGDA/types/LinearVRGDAParams.sol similarity index 93% rename from src/pricing-strategy/VRGDA/structs/LinearVRGDAParams.sol rename to src/pricing-strategy/VRGDA/types/LinearVRGDAParams.sol index 7f272f4..53891e8 100644 --- a/src/pricing-strategy/VRGDA/structs/LinearVRGDAParams.sol +++ b/src/pricing-strategy/VRGDA/types/LinearVRGDAParams.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; +pragma solidity ^0.8.20; /// @param targetPrice Target price for a product, to be scaled according to sales pace. /// @param min minimum price to be paid for a token, scaled by 1e18 diff --git a/src/pricing-strategy/VRGDA/structs/LogisticProductParams.sol b/src/pricing-strategy/VRGDA/types/LogisticProductParams.sol similarity index 95% rename from src/pricing-strategy/VRGDA/structs/LogisticProductParams.sol rename to src/pricing-strategy/VRGDA/types/LogisticProductParams.sol index e016755..0b1181a 100644 --- a/src/pricing-strategy/VRGDA/structs/LogisticProductParams.sol +++ b/src/pricing-strategy/VRGDA/types/LogisticProductParams.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; +pragma solidity ^0.8.20; import {LogisticVRGDAParams} from "./LogisticVRGDAParams.sol"; diff --git a/src/pricing-strategy/VRGDA/structs/LogisticVRGDAParams.sol b/src/pricing-strategy/VRGDA/types/LogisticVRGDAParams.sol similarity index 94% rename from src/pricing-strategy/VRGDA/structs/LogisticVRGDAParams.sol rename to src/pricing-strategy/VRGDA/types/LogisticVRGDAParams.sol index 5adf031..5cea68d 100644 --- a/src/pricing-strategy/VRGDA/structs/LogisticVRGDAParams.sol +++ b/src/pricing-strategy/VRGDA/types/LogisticVRGDAParams.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; +pragma solidity ^0.8.20; /// @param targetPrice Target price for a product, to be scaled according to sales pace. /// @param min minimum price to be paid for a token, scaled by 1e18 diff --git a/src/pricing-strategy/brtmoments/BrightMomentsCafe.sol b/src/pricing-strategy/brtmoments/BrightMomentsCafe.sol deleted file mode 100644 index a04c29d..0000000 --- a/src/pricing-strategy/brtmoments/BrightMomentsCafe.sol +++ /dev/null @@ -1,142 +0,0 @@ -// SPDX-License-Identifier: Unlicense -pragma solidity ^0.8.19; - -import {ISliceProductPrice} from "../../../utils/Slice/interfaces/utils/ISliceProductPrice.sol"; -import {Ownable} from "@openzeppelin/access/Ownable.sol"; -import {IERC721} from "@openzeppelin/token/ERC721/IERC721.sol"; -import {IProductsModule} from "../../../utils/Slice/interfaces/IProductsModule.sol"; - -/** - * @notice Slice pricing strategy for bright moments - * @author jacopo <@jj_ranalli> - * @author Dom-Mac <@zerohex_eth> - */ -contract BrightMomentsCafe is ISliceProductPrice, Ownable { - /*////////////////////////////////////////////////////////////// - ERRORS - //////////////////////////////////////////////////////////////*/ - - error NotProductOwner(); - - /*////////////////////////////////////////////////////////////// - IMMUTABLE STORAGE - //////////////////////////////////////////////////////////////*/ - - address public immutable productsModuleAddress; - uint256 public constant BRTMOMENTSCAFE_STOREID = 298; - uint256 internal constant MAX_PRODUCTID = 8; - - /*////////////////////////////////////////////////////////////// - MUTABLE STORAGE - //////////////////////////////////////////////////////////////*/ - - mapping(uint256 productId => uint256 price) public usdcPrices; - mapping(address => bool) public whitelistedAddresses; - IERC721[] public nfts; - - /*////////////////////////////////////////////////////////////// - CONSTRUCTOR - //////////////////////////////////////////////////////////////*/ - - constructor(address _productsModuleAddress) Ownable(msg.sender) { - productsModuleAddress = _productsModuleAddress; - - usdcPrices[1] = 5400000; - usdcPrices[2] = 5400000; - usdcPrices[3] = 5400000; - usdcPrices[4] = 5400000; - usdcPrices[5] = 5400000; - usdcPrices[6] = 5400000; - usdcPrices[7] = 3250000; // tea - usdcPrices[8] = 2150000; // water - usdcPrices[9] = 10800000; // air - usdcPrices[10] = 54000000; // quarterly - usdcPrices[11] = 215000000; // ledger - usdcPrices[12] = 86500000; // hoodie - usdcPrices[13] = 48500000; // shirt - usdcPrices[14] = 48500000; // shirt - usdcPrices[15] = 21500000; // tote - - whitelistedAddresses[0xAe009d532328FF09e09E5d506aB5BBeC3c25c0FF] = true; - whitelistedAddresses[0xf4140f2721f5Fd76eA2A3b6864ab49e0fBa1f7d0] = true; - whitelistedAddresses[0x396D8177e5E1b9cAfb89692261f6c647Aa77f00C] = true; - - nfts.push(IERC721(0xFc30e5Ab92b78928634B4F7C6000F80d700bcE56)); - nfts.push(IERC721(0x55E8749AA336D30DF466078B9535cd97b5024dcf)); - nfts.push(IERC721(0x36FcD1b2CA01aD91D0a0680B4EAc21149F2a98Bb)); - nfts.push(IERC721(0x9D19D7F02AD521A377AafD331cCFF8162Fc52959)); - } - - /*////////////////////////////////////////////////////////////// - FUNCTIONS - //////////////////////////////////////////////////////////////*/ - - /** - * @notice Called by product owner to set base price and discounts for a product. - * - * @param productId ID of the product to set the price params for. - * @param newPrice New base price for the product. - */ - function setProductPrice(uint256 productId, uint256 newPrice) external onlyOwner { - usdcPrices[productId] = newPrice; - } - - /** - * @notice Called by product owner to set base price and discounts for a product. - * - * @param addr Address to set as whitelisted or not. - * @param isWhitelisted Whether to whitelist or not. - */ - function setWhitelistedAddress(address addr, bool isWhitelisted) external onlyOwner { - whitelistedAddresses[addr] = isWhitelisted; - } - - /** - * @notice Called by product owner to set nfts eligible for free coffee. - * - * @param _nfts Array of NFTs to set. - */ - function setNfts(IERC721[] calldata _nfts) external onlyOwner { - nfts = _nfts; - } - - /** - * @notice Function called by Slice protocol to calculate current product price. - * - * @param productId ID of the product being queried - * @param quantity Number of units purchased - * @param buyer Address of the buyer - * - * @return ethPrice and currencyPrice of product. - */ - function productPrice(uint256, uint256 productId, address, uint256 quantity, address buyer, bytes memory) - public - view - override - returns (uint256 ethPrice, uint256 currencyPrice) - { - if (whitelistedAddresses[buyer]) { - return (0, 0); - } - - if (productId <= MAX_PRODUCTID) { - for (uint256 i = 0; i < nfts.length; ++i) { - if (nfts[i].balanceOf(buyer) != 0) { - for (uint256 k = 1; k <= MAX_PRODUCTID; ++k) { - if ( - IProductsModule(productsModuleAddress).validatePurchaseUnits( - buyer, BRTMOMENTSCAFE_STOREID, k - ) != 0 - ) { - return (0, usdcPrices[productId] * quantity); - } - } - - return (0, usdcPrices[productId] * (quantity - 1)); - } - } - } - - return (0, usdcPrices[productId] * quantity); - } -} diff --git a/src/pricing-strategy/brtmoments/NFTDiscountFixed.sol b/src/pricing-strategy/brtmoments/NFTDiscountFixed.sol deleted file mode 100644 index 3625ec8..0000000 --- a/src/pricing-strategy/brtmoments/NFTDiscountFixed.sol +++ /dev/null @@ -1,88 +0,0 @@ -// SPDX-License-Identifier: Unlicense -pragma solidity ^0.8.19; - -import {ISliceProductPrice} from "../../../utils/Slice/interfaces/utils/ISliceProductPrice.sol"; -import {Ownable} from "@openzeppelin/access/Ownable.sol"; -import {IERC1155} from "@openzeppelin/token/ERC1155/IERC1155.sol"; - -struct Price { - uint128 basePrice; - uint128 discountedPrice; -} - -struct NFT { - IERC1155 nftAddress; - uint256 tokenId; -} - -contract NFTDiscountFixed is ISliceProductPrice, Ownable { - /*////////////////////////////////////////////////////////////// - MUTABLE STORAGE - //////////////////////////////////////////////////////////////*/ - - NFT[] public nfts; - mapping(uint256 productId => Price price) public usdcPrices; - - /*////////////////////////////////////////////////////////////// - FUNCTIONS - //////////////////////////////////////////////////////////////*/ - - constructor() Ownable(msg.sender) { - nfts.push(NFT(IERC1155(0x8876CD7B283b1EeB998aDfeB55a65C51D6b6f693), 1)); - - for (uint256 i = 0; i < 46; i++) { - usdcPrices[i] = Price(14_000_000, 2_000_000); - } - } - - /** - * @notice Called by product owner to set base price and discounts for a product. - * - * @param productId ID of the product to set the price params for. - * @param newPrice New base price for the product. - */ - function setProductPrice(uint256 productId, Price memory newPrice) external onlyOwner { - usdcPrices[productId] = newPrice; - } - - /** - * @notice Called by product owner to set nfts eligible for free coffee. - * - * @param _nfts Array of NFTs to set. - */ - function setNfts(NFT[] memory _nfts) external onlyOwner { - for (uint256 i = 0; i < _nfts.length; i++) { - nfts[i] = _nfts[i]; - } - - for (uint256 i = _nfts.length; i < nfts.length; i++) { - nfts.pop(); - } - } - - /** - * @notice Function called by Slice protocol to calculate current product price. - * - * @param productId ID of the product being queried - * @param quantity Number of units purchased - * @param buyer Address of the buyer - * - * @return ethPrice and currencyPrice of product. - */ - function productPrice(uint256, uint256 productId, address, uint256 quantity, address buyer, bytes memory) - public - view - override - returns (uint256 ethPrice, uint256 currencyPrice) - { - NFT memory nft; - for (uint256 i = 0; i < nfts.length; ++i) { - nft = nfts[i]; - if (nft.nftAddress.balanceOf(buyer, nft.tokenId) != 0) { - return (0, usdcPrices[productId].discountedPrice * quantity); - } - } - - return (0, usdcPrices[productId].basePrice * quantity); - } -} diff --git a/src/pricing-strategy/brtmoments/OneDiscounted.sol b/src/pricing-strategy/brtmoments/OneDiscounted.sol deleted file mode 100644 index ec9b603..0000000 --- a/src/pricing-strategy/brtmoments/OneDiscounted.sol +++ /dev/null @@ -1,132 +0,0 @@ -// SPDX-License-Identifier: Unlicense -pragma solidity ^0.8.19; - -import {ISliceProductPrice} from "../../../utils/Slice/interfaces/utils/ISliceProductPrice.sol"; -import {IERC721} from "@openzeppelin/token/ERC721/IERC721.sol"; -import {IERC1155} from "@openzeppelin/token/ERC1155/IERC1155.sol"; -import {IProductsModule} from "../../../utils/Slice/interfaces/IProductsModule.sol"; - -/** - * @notice Slice pricing strategy to give one product for free - * @author jacopo <@jj_ranalli> - */ -contract OneDiscounted is ISliceProductPrice { - /*////////////////////////////////////////////////////////////// - ERRORS - //////////////////////////////////////////////////////////////*/ - - error NotProductOwner(); - - /*////////////////////////////////////////////////////////////// - IMMUTABLE STORAGE - //////////////////////////////////////////////////////////////*/ - - address public immutable productsModuleAddress; - - /*////////////////////////////////////////////////////////////// - MUTABLE STORAGE - //////////////////////////////////////////////////////////////*/ - - struct Price { - uint256 usdcPrice; - address token; - uint88 tokenId; - TokenType tokenType; - } - - enum TokenType { - ERC721, - ERC1155 - } - - mapping(uint256 productId => Price price) public usdcPrices; - mapping(address => bool) public whitelistedAddresses; - IERC721[] public nfts; - - /*////////////////////////////////////////////////////////////// - CONSTRUCTOR - //////////////////////////////////////////////////////////////*/ - - constructor(address _productsModuleAddress) { - productsModuleAddress = _productsModuleAddress; - } - - /*////////////////////////////////////////////////////////////// - MODIFIERS - //////////////////////////////////////////////////////////////*/ - - /** - * @notice Check if msg.sender is owner of a product. Used to manage access to `setProductPrice`. - */ - modifier onlyProductOwner(uint256 slicerId, uint256 productId) { - if (!IProductsModule(productsModuleAddress).isProductOwner(slicerId, productId, msg.sender)) { - revert NotProductOwner(); - } - _; - } - - /*////////////////////////////////////////////////////////////// - FUNCTIONS - //////////////////////////////////////////////////////////////*/ - - /** - * @notice Called by product owner to set base price and discounts for a product. - * - * @param slicerId ID of the slicer to set the price for. - * @param productId ID of the product to set the price for. - * @param usdcPrice Price of the product in USDC, with 6 decimals. - * @param token Address of the token to hold to obtain a free item. Set to address(0) to bypass check. - * @param tokenId ID of the token to hold to be eligible for discount (only for ERC1155). - * @param tokenType Type of the token to hold to be eligible for discount - 1 for ERC721, 2 for ERC1155. - */ - function setProductPrice( - uint256 slicerId, - uint256 productId, - uint256 usdcPrice, - address token, - uint88 tokenId, - TokenType tokenType - ) external onlyProductOwner(slicerId, productId) { - usdcPrices[productId] = Price(usdcPrice, token, tokenId, tokenType); - } - - /** - * @notice Function called by Slice protocol to calculate current product price. - * Discount is applied only for first purchase on a slicer. - * - * @param productId ID of the product being queried - * @param quantity Number of units purchased - * @param buyer Address of the buyer - * - * @return ethPrice and currencyPrice of product. - */ - function productPrice(uint256 slicerId, uint256 productId, address, uint256 quantity, address buyer, bytes memory) - public - view - override - returns (uint256 ethPrice, uint256 currencyPrice) - { - Price memory price = usdcPrices[productId]; - - bool isEligible = price.token == address(0); - if (!isEligible) { - if (price.tokenType == TokenType.ERC721) { - isEligible = IERC721(price.token).balanceOf(buyer) != 0; - } else { - isEligible = IERC1155(price.token).balanceOf(buyer, price.tokenId) != 0; - } - } - - if (isEligible) { - for (uint256 i = 1; i <= IProductsModule(productsModuleAddress).nextProductId(slicerId); ++i) { - if (IProductsModule(productsModuleAddress).validatePurchaseUnits(buyer, slicerId, i) != 0) { - return (0, usdcPrices[productId].usdcPrice * quantity); - } - } - - return (0, usdcPrices[productId].usdcPrice * (quantity - 1)); - } - - return (0, usdcPrices[productId].usdcPrice * quantity); - } -} diff --git a/src/pricing-strategy/brtmoments/OneForFree.sol b/src/pricing-strategy/brtmoments/OneForFree.sol deleted file mode 100644 index de7489e..0000000 --- a/src/pricing-strategy/brtmoments/OneForFree.sol +++ /dev/null @@ -1,132 +0,0 @@ -// SPDX-License-Identifier: Unlicense -pragma solidity ^0.8.19; - -import {ISliceProductPrice} from "../../../utils/Slice/interfaces/utils/ISliceProductPrice.sol"; -import {IERC721} from "@openzeppelin/token/ERC721/IERC721.sol"; -import {IERC1155} from "@openzeppelin/token/ERC1155/IERC1155.sol"; -import {IProductsModule} from "../../../utils/Slice/interfaces/IProductsModule.sol"; - -/** - * @notice Slice pricing strategy to give one product for free - * @author jacopo <@jj_ranalli> - */ -contract OneForFree is ISliceProductPrice { - /*////////////////////////////////////////////////////////////// - ERRORS - //////////////////////////////////////////////////////////////*/ - - error NotProductOwner(); - - /*////////////////////////////////////////////////////////////// - IMMUTABLE STORAGE - //////////////////////////////////////////////////////////////*/ - - address public immutable productsModuleAddress; - - /*////////////////////////////////////////////////////////////// - MUTABLE STORAGE - //////////////////////////////////////////////////////////////*/ - - struct Price { - uint256 usdcPrice; - address token; - uint88 tokenId; - TokenType tokenType; - } - - enum TokenType { - ERC721, - ERC1155 - } - - mapping(uint256 productId => Price price) public usdcPrices; - mapping(address => bool) public whitelistedAddresses; - IERC721[] public nfts; - - /*////////////////////////////////////////////////////////////// - CONSTRUCTOR - //////////////////////////////////////////////////////////////*/ - - constructor(address _productsModuleAddress) { - productsModuleAddress = _productsModuleAddress; - } - - /*////////////////////////////////////////////////////////////// - MODIFIERS - //////////////////////////////////////////////////////////////*/ - - /** - * @notice Check if msg.sender is owner of a product. Used to manage access to `setProductPrice`. - */ - modifier onlyProductOwner(uint256 slicerId, uint256 productId) { - if (!IProductsModule(productsModuleAddress).isProductOwner(slicerId, productId, msg.sender)) { - revert NotProductOwner(); - } - _; - } - - /*////////////////////////////////////////////////////////////// - FUNCTIONS - //////////////////////////////////////////////////////////////*/ - - /** - * @notice Called by product owner to set base price and discounts for a product. - * - * @param slicerId ID of the slicer to set the price for. - * @param productId ID of the product to set the price for. - * @param usdcPrice Price of the product in USDC, with 6 decimals. - * @param token Address of the token to hold to obtain a free item. Set to address(0) to bypass check. - * @param tokenId ID of the token to hold to be eligible for discount (only for ERC1155). - * @param tokenType Type of the token to hold to be eligible for discount - 1 for ERC721, 2 for ERC1155. - */ - function setProductPrice( - uint256 slicerId, - uint256 productId, - uint256 usdcPrice, - address token, - uint88 tokenId, - TokenType tokenType - ) external onlyProductOwner(slicerId, productId) { - usdcPrices[productId] = Price(usdcPrice, token, tokenId, tokenType); - } - - /** - * @notice Function called by Slice protocol to calculate current product price. - * Discount is applied only for first purchase on a slicer. - * - * @param productId ID of the product being queried - * @param quantity Number of units purchased - * @param buyer Address of the buyer - * - * @return ethPrice and currencyPrice of product. - */ - function productPrice(uint256 slicerId, uint256 productId, address, uint256 quantity, address buyer, bytes memory) - public - view - override - returns (uint256 ethPrice, uint256 currencyPrice) - { - Price memory price = usdcPrices[productId]; - - bool isEligible = price.token == address(0); - if (!isEligible) { - if (price.tokenType == TokenType.ERC721) { - isEligible = IERC721(price.token).balanceOf(buyer) != 0; - } else { - isEligible = IERC1155(price.token).balanceOf(buyer, price.tokenId) != 0; - } - } - - if (isEligible) { - for (uint256 i = 1; i <= IProductsModule(productsModuleAddress).nextProductId(slicerId); ++i) { - if (IProductsModule(productsModuleAddress).validatePurchaseUnits(buyer, slicerId, i) != 0) { - return (0, usdcPrices[productId].usdcPrice * quantity); - } - } - - return (0, usdcPrices[productId].usdcPrice * (quantity - 1)); - } - - return (0, usdcPrices[productId].usdcPrice * quantity); - } -} diff --git a/src/utils/OnchainAction.sol b/src/utils/OnchainAction.sol new file mode 100644 index 0000000..9aee891 --- /dev/null +++ b/src/utils/OnchainAction.sol @@ -0,0 +1,4 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.20; + +import "slice/utils/OnchainAction.sol"; diff --git a/src/utils/PricingStrategy.sol b/src/utils/PricingStrategy.sol index 1a4767c..8996f54 100644 --- a/src/utils/PricingStrategy.sol +++ b/src/utils/PricingStrategy.sol @@ -1,61 +1,4 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.0; +pragma solidity ^0.8.20; -import {IProductsModule} from "../IProductsModule.sol"; - -/** - * @notice Pricing strategy inheritable contract. - * @author Slice - */ -abstract contract PricingStrategy { - /*////////////////////////////////////////////////////////////// - ERRORS - //////////////////////////////////////////////////////////////*/ - - error NotProductOwner(); - - /*////////////////////////////////////////////////////////////// - IMMUTABLES - //////////////////////////////////////////////////////////////*/ - - IProductsModule public immutable productsModule; - - constructor(IProductsModule _productsModule) { - productsModule = _productsModule; - } - - /*////////////////////////////////////////////////////////////// - MODIFIERS - //////////////////////////////////////////////////////////////*/ - - /** - * @notice Verifies if msg.sender is the product owner. - */ - modifier onlyProductOwner(uint256 slicerId, uint256 productId) { - if (!productsModule.isProductOwner(slicerId, productId, msg.sender)) { - revert NotProductOwner(); - } - _; - } - - /** - * @notice Function called by Slice protocol to calculate current product price. - * - * @param slicerId ID of the slicer being queried - * @param productId ID of the product being queried - * @param currency Currency chosen for the purchase - * @param quantity Number of units purchased - * @param buyer Address of the buyer - * @param data Additional data used to calculate price - * - * @return ethPrice and currencyPrice of product. - */ - function productPrice( - uint256 slicerId, - uint256 productId, - address currency, - uint256 quantity, - address buyer, - bytes memory data - ) external view virtual returns (uint256 ethPrice, uint256 currencyPrice); -} +import "slice/utils/PricingStrategy.sol"; diff --git a/src/utils/PricingStrategyAction.sol b/src/utils/PricingStrategyAction.sol new file mode 100644 index 0000000..95e834d --- /dev/null +++ b/src/utils/PricingStrategyAction.sol @@ -0,0 +1,4 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.20; + +import "slice/utils/PricingStrategyAction.sol"; diff --git a/src/utils/Slice/interfaces/IFundsModule.sol b/src/utils/Slice/interfaces/IFundsModule.sol deleted file mode 100644 index 4988f20..0000000 --- a/src/utils/Slice/interfaces/IFundsModule.sol +++ /dev/null @@ -1,41 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.0; - -import "./ISlicer.sol"; -import "./ISliceCore.sol"; - -interface IFundsModule { - function JBProjectId() external view returns (uint256 projectId); - - function sliceCore() external view returns (ISliceCore sliceCoreAddress); - - function balances(address account, address currency) - external - view - returns (uint128 accountBalance, uint128 protocolPayment); - - function depositEth(address account, uint256 protocolPayment) external payable; - - function depositTokenFromSlicer( - uint256 tokenId, - address account, - address currency, - uint256 amount, - uint256 protocolPayment - ) external; - - function withdraw(address account, address currency) external; - - function batchWithdraw(address account, address[] memory currencies) external; - - function withdrawOnRelease( - uint256 tokenId, - address account, - address currency, - uint256 amount, - uint256 protocolPayment - ) external payable; - - function batchReleaseSlicers(ISlicer[] memory slicers, address account, address currency, bool triggerWithdraw) - external; -} diff --git a/src/utils/Slice/interfaces/IProductsModule.sol b/src/utils/Slice/interfaces/IProductsModule.sol deleted file mode 100644 index 6ad2aca..0000000 --- a/src/utils/Slice/interfaces/IProductsModule.sol +++ /dev/null @@ -1,70 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.0; - -import "../structs/Function.sol"; -import "../structs/Price.sol"; -import "../structs/ProductParams.sol"; -import "../structs/PurchaseParams.sol"; -import "./ISliceCore.sol"; -import "./IFundsModule.sol"; - -interface IProductsModule { - function sliceCore() external view returns (ISliceCore sliceCoreAddress); - - function fundsModule() external view returns (IFundsModule fundsModuleAddress); - - function addProduct(uint256 slicerId, ProductParams memory params, Function memory externalCall_) external; - - function setProductInfo( - uint256 slicerId, - uint256 productId, - uint8 newMaxUnits, - bool isFree, - bool isInfinite, - uint32 newUnits, - CurrencyPrice[] memory currencyPrices - ) external; - - function removeProduct(uint256 slicerId, uint256 productId) external; - - function payProducts(address buyer, PurchaseParams[] calldata purchases) external payable; - - function releaseEthToSlicer(uint256 slicerId) external; - - // function _setCategoryAddress(uint256 categoryIndex, address newCategoryAddress) external; - - function ethBalance(uint256 slicerId) external view returns (uint256); - - function productPrice( - uint256 slicerId, - uint256 productId, - address currency, - uint256 quantity, - address buyer, - bytes memory data - ) external view returns (Price memory price); - - function validatePurchaseUnits(address account, uint256 slicerId, uint256 productId) - external - view - returns (uint256 purchases); - - function validatePurchase(uint256 slicerId, uint256 productId) - external - view - returns (uint256 purchases, bytes memory purchaseData); - - function availableUnits(uint256 slicerId, uint256 productId) - external - view - returns (uint256 units, bool isInfinite); - - function isProductOwner(uint256 slicerId, uint256 productId, address account) - external - view - returns (bool isAllowed); - - function nextProductId(uint256 slicerId) external view returns (uint256); - - // function categoryAddress(uint256 categoryIndex) external view returns (address); -} diff --git a/src/utils/Slice/interfaces/ISliceCore.sol b/src/utils/Slice/interfaces/ISliceCore.sol deleted file mode 100644 index fcc0049..0000000 --- a/src/utils/Slice/interfaces/ISliceCore.sol +++ /dev/null @@ -1,47 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.0; - -import "./ISlicerManager.sol"; -import "../structs/SliceParams.sol"; -import "../structs/SlicerParams.sol"; -import "@openzeppelin/interfaces/IERC1155.sol"; -import "@openzeppelin/interfaces/IERC2981.sol"; - -interface ISliceCore is IERC1155, IERC2981 { - function slicerManager() external view returns (ISlicerManager slicerManagerAddress); - - function slice(SliceParams calldata params) external; - - function reslice(uint256 tokenId, address payable[] calldata accounts, int32[] calldata tokensDiffs) external; - - function slicerBatchTransfer( - address from, - address[] memory recipients, - uint256 id, - uint256[] memory amounts, - bool release - ) external; - - function safeTransferFromUnreleased(address from, address to, uint256 id, uint256 amount, bytes memory data) - external; - - function setController(uint256 id, address newController) external; - - function setRoyalty(uint256 tokenId, bool isSlicer, bool isActive, uint256 royaltyPercentage) external; - - function slicers(uint256 id) external view returns (address); - - function controller(uint256 id) external view returns (address); - - function totalSupply(uint256 id) external view returns (uint256); - - function supply() external view returns (uint256); - - function exists(uint256 id) external view returns (bool); - - function owner() external view returns (address owner); - - function _setBasePath(string calldata basePath_) external; - - function _togglePause() external; -} diff --git a/src/utils/Slice/interfaces/ISlicer.sol b/src/utils/Slice/interfaces/ISlicer.sol deleted file mode 100644 index 176993f..0000000 --- a/src/utils/Slice/interfaces/ISlicer.sol +++ /dev/null @@ -1,64 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.0; - -import "@openzeppelin/token/ERC1155/IERC1155Receiver.sol"; -import "@openzeppelin/token/ERC721/IERC721Receiver.sol"; - -interface ISlicer is IERC721Receiver, IERC1155Receiver { - function release(address account, address currency, bool withdraw) external; - - function batchReleaseAccounts(address[] memory accounts, address currency, bool withdraw) external; - - function unreleased(address account, address currency) external view returns (uint256 unreleasedAmount); - - function getFee() external view returns (uint256 fee); - - function getFeeForAccount(address account) external view returns (uint256 fee); - - function slicerInfo() - external - view - returns ( - uint256 tokenId, - uint256 minimumShares, - address creator, - bool isImmutable, - bool currenciesControlled, - bool productsControlled, - bool acceptsAllCurrencies, - address[] memory currencies - ); - - function isPayeeAllowed(address payee) external view returns (bool); - - function acceptsCurrency(address currency) external view returns (bool); - - function _updatePayees( - address payable sender, - address receiver, - bool toRelease, - uint256 senderShares, - uint256 transferredShares - ) external; - - function _updatePayeesReslice(address payable[] memory accounts, int32[] memory tokensDiffs, uint32 totalSupply) - external; - - function _setChildSlicer(uint256 id, bool addChildSlicerMode) external; - - function _setTotalShares(uint256 totalShares) external; - - function _addCurrencies(address[] memory currencies) external; - - function _setCustomFee(bool customFeeActive, uint256 customFee) external; - - function _releaseFromSliceCore(address account, address currency, uint256 accountSlices) external; - - function _releaseFromFundsModule(address account, address currency) - external - returns (uint256 amount, uint256 protocolPayment); - - function _handle721Purchase(address buyer, address contractAddress, uint256 tokenId) external; - - function _handle1155Purchase(address buyer, address contractAddress, uint256 quantity, uint256 tokenId) external; -} diff --git a/src/utils/Slice/interfaces/ISlicerManager.sol b/src/utils/Slice/interfaces/ISlicerManager.sol deleted file mode 100644 index 7f6a513..0000000 --- a/src/utils/Slice/interfaces/ISlicerManager.sol +++ /dev/null @@ -1,12 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.0; - -import "../structs/SliceParams.sol"; - -interface ISlicerManager { - function implementation() external view returns (address); - - function _createSlicer(address creator, uint256 id, SliceParams calldata params) external returns (address); - - function _upgradeSlicers(address newLogicImpl) external; -} diff --git a/src/utils/Slice/interfaces/utils/IPriceFeed.sol b/src/utils/Slice/interfaces/utils/IPriceFeed.sol deleted file mode 100644 index a5fdc18..0000000 --- a/src/utils/Slice/interfaces/utils/IPriceFeed.sol +++ /dev/null @@ -1,34 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -import "../../structs/PoolData.sol"; - -interface IPriceFeed { - function uniswapV3Factory() external view returns (address uniswapV3Factory); - - function pools(address token0, address token1) - external - view - returns (address poolAddress, uint24 fee, uint48 lastUpdatedTimestamp); - - function getPool(address tokenA, address tokenB) external view returns (PoolData memory pool); - - function getQuote(uint128 baseAmount, address baseToken, address quoteToken, uint32 secondsAgo) - external - view - returns (uint256 quoteAmount); - - function getUpdatedPool(address tokenA, address tokenB, uint256 updateInterval) - external - returns (PoolData memory pool); - - function getQuoteAndUpdatePool( - uint128 baseAmount, - address baseToken, - address quoteToken, - uint32 secondsAgo, - uint256 updateInterval - ) external returns (uint256 quoteAmount); - - function updatePool(address tokenA, address tokenB) external returns (PoolData memory highestLiquidityPool); -} diff --git a/src/utils/Slice/structs/Balance.sol b/src/utils/Slice/structs/Balance.sol deleted file mode 100644 index 12bd64e..0000000 --- a/src/utils/Slice/structs/Balance.sol +++ /dev/null @@ -1,7 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -struct Balance { - uint128 account; - uint128 protocol; -} diff --git a/src/utils/Slice/structs/Category.sol b/src/utils/Slice/structs/Category.sol deleted file mode 100644 index e8b42c3..0000000 --- a/src/utils/Slice/structs/Category.sol +++ /dev/null @@ -1,6 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -struct Category { - address categoryAddress; -} diff --git a/src/utils/Slice/structs/CurrencyPrice.sol b/src/utils/Slice/structs/CurrencyPrice.sol deleted file mode 100644 index 24cf06c..0000000 --- a/src/utils/Slice/structs/CurrencyPrice.sol +++ /dev/null @@ -1,9 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -struct CurrencyPrice { - uint248 value; - bool dynamicPricing; - address externalAddress; - address currency; -} diff --git a/src/utils/Slice/structs/Function.sol b/src/utils/Slice/structs/Function.sol deleted file mode 100644 index 14e4d5d..0000000 --- a/src/utils/Slice/structs/Function.sol +++ /dev/null @@ -1,17 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -/** - * @param data data sent to `externalAddress` - * @param value Amount or percentage of ETH / token forwarded to `externalAddress` - * @param externalAddress Address to be called during external call - * @param checkFunctionSignature The timestamp when the slicer becomes releasable - * @param execFunctionSignature The timestamp when the slicer becomes transferable - */ -struct Function { - bytes data; - uint256 value; - address externalAddress; - bytes4 checkFunctionSignature; - bytes4 execFunctionSignature; -} diff --git a/src/utils/Slice/structs/Payee.sol b/src/utils/Slice/structs/Payee.sol deleted file mode 100644 index 21db3d6..0000000 --- a/src/utils/Slice/structs/Payee.sol +++ /dev/null @@ -1,8 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -struct Payee { - address account; - uint32 shares; - bool transfersAllowedWhileLocked; -} diff --git a/src/utils/Slice/structs/PoolData.sol b/src/utils/Slice/structs/PoolData.sol deleted file mode 100644 index d3a10b4..0000000 --- a/src/utils/Slice/structs/PoolData.sol +++ /dev/null @@ -1,8 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -struct PoolData { - address poolAddress; - uint24 fee; - uint48 lastUpdatedTimestamp; -} diff --git a/src/utils/Slice/structs/Price.sol b/src/utils/Slice/structs/Price.sol deleted file mode 100644 index d7fcd78..0000000 --- a/src/utils/Slice/structs/Price.sol +++ /dev/null @@ -1,9 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -struct Price { - uint256 eth; - uint256 currency; - uint256 ethExternalCall; - uint256 currencyExternalCall; -} diff --git a/src/utils/Slice/structs/Product.sol b/src/utils/Slice/structs/Product.sol deleted file mode 100644 index ab733c7..0000000 --- a/src/utils/Slice/structs/Product.sol +++ /dev/null @@ -1,37 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -import "./Function.sol"; -import "./SubSlicerProduct.sol"; -import "./ProductPrice.sol"; -import "./Purchases.sol"; - -/** - * @notice Struct related to product info. - * - * @param purchases Mapping of quantity bought by addresses - * @param prices Mapping with prices set for the product for each allowed currency - * @param subSlicerProducts Mapping with Array of subProducts - * @param externalCall `Function` struct containing the params to execute an external call during purchase - * @param data Metadata containing the public information about the product - * @param purchaseData Metadata containing the purchase information/procedure for the buyers - * @param creator Address of the account who created the product - * @param priceEditTimestamp Timestamp of last time the product price was edited - * @param availableUnits Number of available units on sale - * @param maxUnitsPerBuyer Maximum amount of units allowed to purchase for a buyer - * @param packedBooleans boolean flags ordered from the right: [IsFree, IsInfinite] - */ -struct Product { - mapping(address => Purchases) purchases; - mapping(address => ProductPrice) prices; - SubSlicerProduct[] subSlicerProducts; - Function externalCall; - bytes data; - bytes purchaseData; - address creator; - uint40 priceEditTimestamp; - uint32 availableUnits; - uint8 maxUnitsPerBuyer; - uint8 packedBooleans; -} -// uint32 categoryIndex; diff --git a/src/utils/Slice/structs/ProductParams.sol b/src/utils/Slice/structs/ProductParams.sol deleted file mode 100644 index cad8ec8..0000000 --- a/src/utils/Slice/structs/ProductParams.sol +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -import "./SubSlicerProduct.sol"; -import "./CurrencyPrice.sol"; - -struct ProductParams { - SubSlicerProduct[] subSlicerProducts; - CurrencyPrice[] currencyPrices; - bytes data; - bytes purchaseData; - uint32 availableUnits; - // uint32 categoryIndex; - uint8 maxUnitsPerBuyer; - bool isFree; - bool isInfinite; - bool isExternalCallPaymentRelative; - bool isExternalCallPreferredToken; -} diff --git a/src/utils/Slice/structs/ProductPrice.sol b/src/utils/Slice/structs/ProductPrice.sol deleted file mode 100644 index bc7d734..0000000 --- a/src/utils/Slice/structs/ProductPrice.sol +++ /dev/null @@ -1,8 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -struct ProductPrice { - uint248 value; - bool dynamicPricing; - address externalAddress; -} diff --git a/src/utils/Slice/structs/PurchaseParams.sol b/src/utils/Slice/structs/PurchaseParams.sol deleted file mode 100644 index 9a149ce..0000000 --- a/src/utils/Slice/structs/PurchaseParams.sol +++ /dev/null @@ -1,10 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -struct PurchaseParams { - uint128 slicerId; - uint32 quantity; - address currency; - uint32 productId; - bytes buyerCustomData; -} diff --git a/src/utils/Slice/structs/Purchases.sol b/src/utils/Slice/structs/Purchases.sol deleted file mode 100644 index 4b245c8..0000000 --- a/src/utils/Slice/structs/Purchases.sol +++ /dev/null @@ -1,6 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -struct Purchases { - uint32 quantity; -} diff --git a/src/utils/Slice/structs/SliceParams.sol b/src/utils/Slice/structs/SliceParams.sol deleted file mode 100644 index 71338a7..0000000 --- a/src/utils/Slice/structs/SliceParams.sol +++ /dev/null @@ -1,25 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -import "../structs/Payee.sol"; - -/** - * @param payees Addresses and shares of the initial payees - * @param minimumShares Amount of shares that gives an account access to restricted - * @param currencies Array of tokens accepted by the slicer - * @param releaseTimelock The timestamp when the slicer becomes releasable - * @param transferTimelock The timestamp when the slicer becomes transferable - * @param controller The address of the slicer controller - * @param slicerFlags See `_flags` in {Slicer} - * @param sliceCoreFlags See `flags` in {SlicerParams} struct - */ -struct SliceParams { - Payee[] payees; - uint256 minimumShares; - address[] currencies; - uint256 releaseTimelock; - uint40 transferTimelock; - address controller; - uint8 slicerFlags; - uint8 sliceCoreFlags; -} diff --git a/src/utils/Slice/structs/SlicerParams.sol b/src/utils/Slice/structs/SlicerParams.sol deleted file mode 100644 index 592295b..0000000 --- a/src/utils/Slice/structs/SlicerParams.sol +++ /dev/null @@ -1,25 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -import "../interfaces/ISlicer.sol"; - -/** - * @param slicer ISlicer instance - * @param controller Address of slicer controller - * @param transferTimelock The timestamp when the slicer becomes transferable - * @param totalSupply Total supply of slices - * @param royaltyPercentage Percentage of royalty to claim (up to 100, ie 10%) - * @param flags Boolean flags in the following order from the right: [isCustomRoyaltyActive, isRoyaltyReceiverSlicer, - * isResliceAllowed, isControlledTransferAllowed] - * @param transferAllowlist Mapping from address to permission to transfer slices during transferTimelock period - */ -struct SlicerParams { - ISlicer slicer; - address controller; - uint40 transferTimelock; - uint32 totalSupply; - uint8 royaltyPercentage; - uint8 flags; - uint8 FREE_SLOT_1; - mapping(address => bool) transferAllowlist; -} diff --git a/src/utils/Slice/structs/SubSlicerProduct.sol b/src/utils/Slice/structs/SubSlicerProduct.sol deleted file mode 100644 index eaa791c..0000000 --- a/src/utils/Slice/structs/SubSlicerProduct.sol +++ /dev/null @@ -1,7 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -struct SubSlicerProduct { - uint128 subSlicerId; - uint32 subProductId; -} diff --git a/src/utils/SignedWadMath.sol b/src/utils/math/SignedWadMath.sol similarity index 99% rename from src/utils/SignedWadMath.sol rename to src/utils/math/SignedWadMath.sol index bfe5244..14dfc8e 100644 --- a/src/utils/SignedWadMath.sol +++ b/src/utils/math/SignedWadMath.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; +pragma solidity ^0.8.20; /// @title Signed Wad Math /// @author transmissions11 diff --git a/test/ERC721Discount.sol b/test/ERC721Discount.sol index 8f9ea34..82ed9dc 100644 --- a/test/ERC721Discount.sol +++ b/test/ERC721Discount.sol @@ -1,17 +1,18 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; +pragma solidity ^0.8.20; import {Test} from "forge-std/Test.sol"; import {console2} from "forge-std/console2.sol"; import {MockProductsModule} from "./mocks/MockProductsModule.sol"; import { + IProductsModule, NFTDiscount, ProductDiscounts, DiscountType, DiscountParams, CurrencyParams, NFTType -} from "../src/TieredDiscount/NFTDiscount/NFTDiscount.sol"; +} from "@/pricing-strategy/TieredDiscount/NFTDiscount/NFTDiscount.sol"; import {MockERC721} from "./mocks/MockERC721.sol"; import {MockERC1155} from "./mocks/MockERC1155.sol"; @@ -39,7 +40,7 @@ contract NFTDiscountTest is Test { function setUp() public { productsModule = new MockProductsModule(); - erc721GatedDiscount = new NFTDiscount(address(productsModule)); + erc721GatedDiscount = new NFTDiscount(IProductsModule(address(productsModule))); nftOne.mint(buyer); } diff --git a/test/LinearVRGDA.t.sol b/test/LinearVRGDA.t.sol index 5baa92b..5a087f9 100644 --- a/test/LinearVRGDA.t.sol +++ b/test/LinearVRGDA.t.sol @@ -1,13 +1,14 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; +pragma solidity ^0.8.20; import {Test} from "forge-std/Test.sol"; import {console2} from "forge-std/console2.sol"; -import {wadLn, toWadUnsafe, toDaysWadUnsafe, fromDaysWadUnsafe} from "../utils/SignedWadMath.sol"; +import {wadLn, toWadUnsafe, toDaysWadUnsafe, fromDaysWadUnsafe} from "@/utils/math/SignedWadMath.sol"; import "./mocks/MockLinearVRGDAPrices.sol"; import {MockProductsModule} from "./mocks/MockProductsModule.sol"; +import {IProductsModule} from "@/utils/PricingStrategy.sol"; uint256 constant ONE_THOUSAND_YEARS = 356 days * 1000; @@ -26,7 +27,7 @@ contract LinearVRGDATest is Test { function setUp() public { productsModule = new MockProductsModule(); - vrgda = new MockLinearVRGDAPrices(address(productsModule)); + vrgda = new MockLinearVRGDAPrices(IProductsModule(address(productsModule))); LinearVRGDAParams[] memory linearParams = new LinearVRGDAParams[](1); linearParams[0] = LinearVRGDAParams(targetPriceConstant, min, perTimeUnit); diff --git a/test/LogisticVRGDA.t.sol b/test/LogisticVRGDA.t.sol index 01c70ab..13cacf8 100644 --- a/test/LogisticVRGDA.t.sol +++ b/test/LogisticVRGDA.t.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; +pragma solidity ^0.8.20; import {Test} from "forge-std/Test.sol"; -import {unsafeDiv, wadLn, toWadUnsafe, toDaysWadUnsafe, fromDaysWadUnsafe} from "../utils/SignedWadMath.sol"; +import {unsafeDiv, wadLn, toWadUnsafe, toDaysWadUnsafe, fromDaysWadUnsafe} from "@/utils/math/SignedWadMath.sol"; import "./mocks/MockLogisticVRGDAPrices.sol"; import {MockProductsModule} from "./mocks/MockProductsModule.sol"; @@ -27,7 +27,7 @@ contract LogisticVRGDATest is Test { function setUp() public { productsModule = new MockProductsModule(); - vrgda = new MockLogisticVRGDAPrices(address(productsModule)); + vrgda = new MockLogisticVRGDAPrices(IProductsModule(address(productsModule))); LogisticVRGDAParams[] memory logisticParams = new LogisticVRGDAParams[](1); logisticParams[0] = LogisticVRGDAParams(targetPriceConstant, min, timeScale); diff --git a/test/correctness/LinearVRGDACorrectness.t.sol b/test/correctness/LinearVRGDACorrectness.t.sol index d399bb5..aea4284 100644 --- a/test/correctness/LinearVRGDACorrectness.t.sol +++ b/test/correctness/LinearVRGDACorrectness.t.sol @@ -1,12 +1,13 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.19; +pragma solidity ^0.8.20; import {Test} from "forge-std/Test.sol"; -import {wadLn, toWadUnsafe} from "../../utils/SignedWadMath.sol"; +import {wadLn, toWadUnsafe} from "@/utils/math/SignedWadMath.sol"; import "../mocks/MockLinearVRGDAPrices.sol"; import {MockProductsModule} from "../mocks/MockProductsModule.sol"; +import {IProductsModule} from "@/utils/PricingStrategy.sol"; import {console} from "forge-std/console.sol"; import {Vm} from "forge-std/Vm.sol"; @@ -28,7 +29,7 @@ contract LinearVRGDACorrectnessTest is Test { function setUp() public { productsModule = new MockProductsModule(); - vrgda = new MockLinearVRGDAPrices(address(productsModule)); + vrgda = new MockLinearVRGDAPrices(IProductsModule(address(productsModule))); LinearVRGDAParams[] memory linearParams = new LinearVRGDAParams[](1); linearParams[0] = LinearVRGDAParams(targetPriceConstant, min, perTimeUnit); diff --git a/test/mocks/MockERC1155.sol b/test/mocks/MockERC1155.sol index 67f893f..9a3f011 100644 --- a/test/mocks/MockERC1155.sol +++ b/test/mocks/MockERC1155.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; +pragma solidity ^0.8.20; -import {ERC1155} from "@openzeppelin/token/ERC1155/ERC1155.sol"; +import {ERC1155} from "@openzeppelin-5.3.0/token/ERC1155/ERC1155.sol"; contract MockERC1155 is ERC1155 { uint256 public constant tokenId = 1; diff --git a/test/mocks/MockERC721.sol b/test/mocks/MockERC721.sol index 6665b4b..8a77d60 100644 --- a/test/mocks/MockERC721.sol +++ b/test/mocks/MockERC721.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; +pragma solidity ^0.8.20; -import {ERC721} from "@openzeppelin/token/ERC721/ERC721.sol"; +import {ERC721} from "@openzeppelin-5.3.0/token/ERC721/ERC721.sol"; contract MockERC721 is ERC721 { uint256 public tokenId; diff --git a/test/mocks/MockLinearVRGDAPrices.sol b/test/mocks/MockLinearVRGDAPrices.sol index 6418b07..71b6282 100644 --- a/test/mocks/MockLinearVRGDAPrices.sol +++ b/test/mocks/MockLinearVRGDAPrices.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; +pragma solidity ^0.8.20; -import "../../src/VRGDA/LinearVRGDAPrices.sol"; +import "@/pricing-strategy/VRGDA/LinearVRGDAPrices.sol"; contract MockLinearVRGDAPrices is LinearVRGDAPrices { - constructor(address productsModuleAddress) LinearVRGDAPrices(productsModuleAddress) {} + constructor(IProductsModule productsModule) LinearVRGDAPrices(productsModule) {} } diff --git a/test/mocks/MockLogisticVRGDAPrices.sol b/test/mocks/MockLogisticVRGDAPrices.sol index 8614dce..5901706 100644 --- a/test/mocks/MockLogisticVRGDAPrices.sol +++ b/test/mocks/MockLogisticVRGDAPrices.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; +pragma solidity ^0.8.20; -import "../../src/VRGDA/LogisticVRGDAPrices.sol"; +import "@/pricing-strategy/VRGDA/LogisticVRGDAPrices.sol"; contract MockLogisticVRGDAPrices is LogisticVRGDAPrices { - constructor(address productsModuleAddress) LogisticVRGDAPrices(productsModuleAddress) {} + constructor(IProductsModule productsModule) LogisticVRGDAPrices(productsModule) {} } diff --git a/test/mocks/MockProductsModule.sol b/test/mocks/MockProductsModule.sol index 4f6e876..4265d7d 100644 --- a/test/mocks/MockProductsModule.sol +++ b/test/mocks/MockProductsModule.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; +pragma solidity ^0.8.20; contract MockProductsModule { function isProductOwner(uint256, uint256, address account) external pure returns (bool isAllowed) { From 6eefde1c3f5c936bc63a609fcff94fc4a2636ef6 Mon Sep 17 00:00:00 2001 From: jj_ranalli Date: Mon, 23 Jun 2025 16:49:13 +0200 Subject: [PATCH 05/28] hooks wip --- README.md | 6 +++--- foundry.toml | 6 ++++-- soldeer.lock | 8 ++++---- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 97cea68..4092c00 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# Slice pricing strategies +# Slice hooks -This repo contains custom pricing strategies for products sold on [Slice](https://slice.so). +This repo contains smart contracts for pricing strategies and onchain actions, which can be used on [Slice](https://slice.so) products. Each strategy inherits the [ISliceProductPrice](/src/Slice/interfaces/utils/ISliceProductPrice.sol) interface and serves two main purposes: @@ -28,7 +28,7 @@ A discount strategy that allows a product owner to set a discount for a product ```sh cp .env.example .env -forge install +forge soldeer install ``` You will need a copy of [Foundry](https://github.com/foundry-rs/foundry) installed before proceeding. See the [installation guide](https://github.com/foundry-rs/foundry#installation) for details. diff --git a/foundry.toml b/foundry.toml index 150bebc..5832345 100644 --- a/foundry.toml +++ b/foundry.toml @@ -7,7 +7,7 @@ dynamic_test_linking = true libs = ["dependencies", "../core/src", "../core/dependencies"] fs_permissions = [{ access = "read", path = "./src"}, { access= "read", path = "./broadcast/Deploy.s.sol/8453/run-latest.json"}, { access = "read-write", path = "./deployments"}, { access = "read", path = "./out"}] remappings = [ - "slice/=dependencies/slice-0.0.2/", + "slice/=dependencies/slice-0.0.3/", "forge-std/=dependencies/forge-std-1.9.7/src/", "@openzeppelin-upgradeable-5.3.0/=dependencies/@openzeppelin-contracts-upgradeable-5.3.0/", "@openzeppelin-5.3.0/=dependencies/@openzeppelin-contracts-5.3.0/", @@ -40,9 +40,11 @@ base-goerli = {key="${ETHERSCAN_KEY}", chain=84531, url="https://api-goerli.base [soldeer] recursive_deps = true +remappings_generate = false +remappings_regenerate = false [dependencies] -slice = "0.0.2" +slice = "0.0.3" forge-std = "1.9.7" "@openzeppelin-contracts" = "5.3.0" "@openzeppelin-contracts-upgradeable" = "5.3.0" diff --git a/soldeer.lock b/soldeer.lock index 3afbcdf..37508e4 100644 --- a/soldeer.lock +++ b/soldeer.lock @@ -21,7 +21,7 @@ integrity = "9e60fdba82bc374df80db7f2951faff6467b9091873004a3d314cf0c084b3c7d" [[dependencies]] name = "slice" -version = "0.0.2" -url = "https://soldeer-revisions.s3.amazonaws.com/slice/0_0_2_23-06-2025_11:59:12_src.zip" -checksum = "d9c1daaeb72f63af5ee374420ed291d0ab8b9881309452dbd84dfc6ad268adc4" -integrity = "0a08fe5a55abc15fdc9e5a29e65db2d192d775bd19b17d06529b4178a5b0b04d" +version = "0.0.3" +url = "https://soldeer-revisions.s3.amazonaws.com/slice/0_0_3_23-06-2025_14:43:22_src.zip" +checksum = "4cab5dd357deb274edcc50b78da5e4d42f7f0bc1f7fcf33b44bd798b7c7d190d" +integrity = "45bd7975e13a29f8a71fcc54d30ced5675faa90a72b0a8094cf5acc94a512b86" From 7ac20164cccfbe83964fddcc52a589e04cf9d7b7 Mon Sep 17 00:00:00 2001 From: jj_ranalli Date: Mon, 23 Jun 2025 16:51:19 +0200 Subject: [PATCH 06/28] fix --- .gas-snapshot | 59 +++++++++++++++---------------------- .github/workflows/tests.yml | 2 +- 2 files changed, 25 insertions(+), 36 deletions(-) diff --git a/.gas-snapshot b/.gas-snapshot index d020f7d..1c75f28 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -1,46 +1,35 @@ -KeysDiscountTest:testDeploy() (gas: 5191) -KeysDiscountTest:testProductPrice__HigherDiscount() (gas: 223239) -KeysDiscountTest:testProductPrice__MinQuantity() (gas: 170349) -KeysDiscountTest:testProductPrice__MultipleBoughtQuantity() (gas: 114430) -KeysDiscountTest:testProductPrice__NotOwner() (gas: 109128) -KeysDiscountTest:testProductPrice__Relative() (gas: 111512) -KeysDiscountTest:testSetProductPrice__ERC20() (gas: 110998) -KeysDiscountTest:testSetProductPrice__ETH() (gas: 110988) -KeysDiscountTest:testSetProductPrice__Edit_Add() (gas: 241504) -KeysDiscountTest:testSetProductPrice__Edit_Remove() (gas: 222224) -KeysDiscountTest:testSetProductPrice__MultipleCurrencies() (gas: 194874) -LinearVRGDATest:testAlwaystargetPriceInRightConditions(uint256) (runs: 258, μ: 14476, ~: 14246) -LinearVRGDATest:testPricingAdjustedByQuantity() (gas: 18812) -LinearVRGDATest:testPricingBasic() (gas: 10483) -LinearVRGDATest:testPricingMin() (gas: 10447) -LinearVRGDATest:testProductPriceErc20() (gas: 24180) -LinearVRGDATest:testProductPriceEth() (gas: 24190) -LinearVRGDATest:testProductPriceMultiple() (gas: 27503) +LinearVRGDATest:testAlwaystargetPriceInRightConditions(uint256) (runs: 256, μ: 14544, ~: 14312) +LinearVRGDATest:testPricingAdjustedByQuantity() (gas: 18856) +LinearVRGDATest:testPricingBasic() (gas: 10516) +LinearVRGDATest:testPricingMin() (gas: 10480) +LinearVRGDATest:testProductPriceErc20() (gas: 24191) +LinearVRGDATest:testProductPriceEth() (gas: 24201) +LinearVRGDATest:testProductPriceMultiple() (gas: 27514) LinearVRGDATest:testSetMultiplePrices() (gas: 145200) -LinearVRGDATest:testTargetPrice() (gas: 11316) -LogisticVRGDATest:testAlwaysTargetPriceInRightConditions(uint256) (runs: 258, μ: 16740, ~: 16910) +LinearVRGDATest:testTargetPrice() (gas: 11382) +LogisticVRGDATest:testAlwaysTargetPriceInRightConditions(uint256) (runs: 256, μ: 16762, ~: 16910) LogisticVRGDATest:testGetTargetSaleTimeDoesNotRevertEarly() (gas: 6589) LogisticVRGDATest:testGetTargetSaleTimeRevertsWhenExpected() (gas: 8982) -LogisticVRGDATest:testNoOverflowForAllTokens(uint256,uint256) (runs: 258, μ: 13352, ~: 13593) -LogisticVRGDATest:testNoOverflowForMostTokens(uint256,uint256) (runs: 258, μ: 13157, ~: 13179) +LogisticVRGDATest:testNoOverflowForAllTokens(uint256,uint256) (runs: 256, μ: 13362, ~: 13593) +LogisticVRGDATest:testNoOverflowForMostTokens(uint256,uint256) (runs: 256, μ: 13172, ~: 13179) LogisticVRGDATest:testPricingAdjustedByQuantity() (gas: 23932) LogisticVRGDATest:testPricingBasic() (gas: 11432) LogisticVRGDATest:testPricingMin() (gas: 11408) LogisticVRGDATest:testProductPriceErc20() (gas: 26192) LogisticVRGDATest:testProductPriceEth() (gas: 26203) LogisticVRGDATest:testProductPriceMultiple() (gas: 32117) -LogisticVRGDATest:testSetMultiplePrices() (gas: 153283) +LogisticVRGDATest:testSetMultiplePrices() (gas: 153272) LogisticVRGDATest:testTargetPrice() (gas: 13278) -LogisticVRGDATest:test_RevertOverflow_BeyondLimitTokens(uint256,uint256) (runs: 258, μ: 14917, ~: 14902) +LogisticVRGDATest:test_RevertOverflow_BeyondLimitTokens(uint256,uint256) (runs: 256, μ: 14914, ~: 14902) NFTDiscountTest:testDeploy() (gas: 5191) -NFTDiscountTest:testProductPrice__HigherDiscount() (gas: 222739) -NFTDiscountTest:testProductPrice__MinQuantity() (gas: 150653) -NFTDiscountTest:testProductPrice__MultipleBoughtQuantity() (gas: 114030) -NFTDiscountTest:testProductPrice__NotNFTOwner() (gas: 110876) -NFTDiscountTest:testProductPrice__Relative() (gas: 111113) -NFTDiscountTest:testSetProductPrice__ERC1155() (gas: 161035) -NFTDiscountTest:testSetProductPrice__ERC20() (gas: 110588) -NFTDiscountTest:testSetProductPrice__ETH() (gas: 110611) -NFTDiscountTest:testSetProductPrice__Edit_Add() (gas: 243504) -NFTDiscountTest:testSetProductPrice__Edit_Remove() (gas: 224193) -NFTDiscountTest:testSetProductPrice__MultipleCurrencies() (gas: 194120) \ No newline at end of file +NFTDiscountTest:testProductPrice__HigherDiscount() (gas: 222761) +NFTDiscountTest:testProductPrice__MinQuantity() (gas: 150675) +NFTDiscountTest:testProductPrice__MultipleBoughtQuantity() (gas: 114052) +NFTDiscountTest:testProductPrice__NotNFTOwner() (gas: 110898) +NFTDiscountTest:testProductPrice__Relative() (gas: 111135) +NFTDiscountTest:testSetProductPrice__ERC1155() (gas: 161057) +NFTDiscountTest:testSetProductPrice__ERC20() (gas: 110610) +NFTDiscountTest:testSetProductPrice__ETH() (gas: 110633) +NFTDiscountTest:testSetProductPrice__Edit_Add() (gas: 243548) +NFTDiscountTest:testSetProductPrice__Edit_Remove() (gas: 224237) +NFTDiscountTest:testSetProductPrice__MultipleCurrencies() (gas: 194142) \ No newline at end of file diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 6037106..5d00e25 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -14,7 +14,7 @@ jobs: version: stable - name: Install dependencies - run: forge install + run: forge soldeer install - name: Check contract sizes run: forge build --sizes From 8d5f8800f8415993dfb109ef04f7aab6ddbbbd8a Mon Sep 17 00:00:00 2001 From: jj_ranalli Date: Mon, 23 Jun 2025 19:02:22 +0200 Subject: [PATCH 07/28] downgrade oz to 4.8.0 --- .gas-snapshot | 10 +++++----- foundry.toml | 4 ++-- soldeer.lock | 16 ++++++++-------- .../TieredDiscount/NFTDiscount/NFTDiscount.sol | 4 ++-- test/mocks/MockERC1155.sol | 2 +- test/mocks/MockERC721.sol | 2 +- 6 files changed, 19 insertions(+), 19 deletions(-) diff --git a/.gas-snapshot b/.gas-snapshot index 1c75f28..9662a11 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -22,14 +22,14 @@ LogisticVRGDATest:testSetMultiplePrices() (gas: 153272) LogisticVRGDATest:testTargetPrice() (gas: 13278) LogisticVRGDATest:test_RevertOverflow_BeyondLimitTokens(uint256,uint256) (runs: 256, μ: 14914, ~: 14902) NFTDiscountTest:testDeploy() (gas: 5191) -NFTDiscountTest:testProductPrice__HigherDiscount() (gas: 222761) -NFTDiscountTest:testProductPrice__MinQuantity() (gas: 150675) +NFTDiscountTest:testProductPrice__HigherDiscount() (gas: 222859) +NFTDiscountTest:testProductPrice__MinQuantity() (gas: 150773) NFTDiscountTest:testProductPrice__MultipleBoughtQuantity() (gas: 114052) NFTDiscountTest:testProductPrice__NotNFTOwner() (gas: 110898) NFTDiscountTest:testProductPrice__Relative() (gas: 111135) -NFTDiscountTest:testSetProductPrice__ERC1155() (gas: 161057) +NFTDiscountTest:testSetProductPrice__ERC1155() (gas: 160924) NFTDiscountTest:testSetProductPrice__ERC20() (gas: 110610) NFTDiscountTest:testSetProductPrice__ETH() (gas: 110633) -NFTDiscountTest:testSetProductPrice__Edit_Add() (gas: 243548) -NFTDiscountTest:testSetProductPrice__Edit_Remove() (gas: 224237) +NFTDiscountTest:testSetProductPrice__Edit_Add() (gas: 243646) +NFTDiscountTest:testSetProductPrice__Edit_Remove() (gas: 224335) NFTDiscountTest:testSetProductPrice__MultipleCurrencies() (gas: 194142) \ No newline at end of file diff --git a/foundry.toml b/foundry.toml index 5832345..bb9fd7c 100644 --- a/foundry.toml +++ b/foundry.toml @@ -46,6 +46,6 @@ remappings_regenerate = false [dependencies] slice = "0.0.3" forge-std = "1.9.7" -"@openzeppelin-contracts" = "5.3.0" -"@openzeppelin-contracts-upgradeable" = "5.3.0" +"@openzeppelin-contracts" = "4.8.0" +"@openzeppelin-contracts-upgradeable" = "4.8.0" diff --git a/soldeer.lock b/soldeer.lock index 37508e4..ab18f27 100644 --- a/soldeer.lock +++ b/soldeer.lock @@ -1,16 +1,16 @@ [[dependencies]] name = "@openzeppelin-contracts" -version = "5.3.0" -url = "https://soldeer-revisions.s3.amazonaws.com/@openzeppelin-contracts/5_3_0_10-04-2025_10:51:50_contracts.zip" -checksum = "fa2bc3db351137c4d5eb32b738a814a541b78e87fbcbfeca825e189c4c787153" -integrity = "d69addf252dfe0688dcd893a7821cbee2421f8ce53d95ca0845a59530043cfd1" +version = "4.8.0" +url = "https://soldeer-revisions.s3.amazonaws.com/@openzeppelin-contracts/4_8_0_22-01-2024_13:13:40_contracts.zip" +checksum = "932598a6426b76e315bb9fac536011eb21a76984015efe9e8167c4fc9d7e32a3" +integrity = "954367e8adec93f80c6e795012955706347cdb0359360e7c835e4dd29e5a9c2f" [[dependencies]] name = "@openzeppelin-contracts-upgradeable" -version = "5.3.0" -url = "https://soldeer-revisions.s3.amazonaws.com/@openzeppelin-contracts-upgradeable/5_3_0_10-04-2025_10:51:56_contracts-upgradeable.zip" -checksum = "4bd92f87af0cac7226b12ce367e7327f13431735fa6010508d8c8177f9d3d10f" -integrity = "fa195a69ef4dfec7fec7fbbb77f424258c821832fdd355b0a6e5fe34d2986a16" +version = "4.8.0" +url = "https://soldeer-revisions.s3.amazonaws.com/@openzeppelin-contracts-upgradeable/4_8_0_22-01-2024_13:14:55_contracts-upgradeable.zip" +checksum = "9bd3feb8a6ac529ecf2ab1927bf482bfee9abc2568533b155e567715b83ba94e" +integrity = "16aa3677eec13cfddee8ee412031773a45525d4780b741eab29c746b545afc77" [[dependencies]] name = "forge-std" diff --git a/src/pricing-strategy/TieredDiscount/NFTDiscount/NFTDiscount.sol b/src/pricing-strategy/TieredDiscount/NFTDiscount/NFTDiscount.sol index e9ea6da..7e5f431 100644 --- a/src/pricing-strategy/TieredDiscount/NFTDiscount/NFTDiscount.sol +++ b/src/pricing-strategy/TieredDiscount/NFTDiscount/NFTDiscount.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: Unlicense pragma solidity ^0.8.20; -import {IERC721} from "@openzeppelin-5.3.0/token/ERC721/IERC721.sol"; -import {IERC1155} from "@openzeppelin-5.3.0/token/ERC1155/IERC1155.sol"; +import {IERC721} from "@openzeppelin-4.8.0/token/ERC721/IERC721.sol"; +import {IERC1155} from "@openzeppelin-4.8.0/token/ERC1155/IERC1155.sol"; import { IProductsModule, CurrencyParams, diff --git a/test/mocks/MockERC1155.sol b/test/mocks/MockERC1155.sol index 9a3f011..eeb4383 100644 --- a/test/mocks/MockERC1155.sol +++ b/test/mocks/MockERC1155.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; -import {ERC1155} from "@openzeppelin-5.3.0/token/ERC1155/ERC1155.sol"; +import {ERC1155} from "@openzeppelin-4.8.0/token/ERC1155/ERC1155.sol"; contract MockERC1155 is ERC1155 { uint256 public constant tokenId = 1; diff --git a/test/mocks/MockERC721.sol b/test/mocks/MockERC721.sol index 8a77d60..c3b4cc4 100644 --- a/test/mocks/MockERC721.sol +++ b/test/mocks/MockERC721.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; -import {ERC721} from "@openzeppelin-5.3.0/token/ERC721/ERC721.sol"; +import {ERC721} from "@openzeppelin-4.8.0/token/ERC721/ERC721.sol"; contract MockERC721 is ERC721 { uint256 public tokenId; From cc38732dbb598cf4695b6c97ac3cf20664336b14 Mon Sep 17 00:00:00 2001 From: jj_ranalli Date: Mon, 23 Jun 2025 19:29:30 +0200 Subject: [PATCH 08/28] fix workflow --- .github/workflows/tests.yml | 18 +++++++++++++++++- foundry.toml | 3 ++- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 5d00e25..d351564 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -6,7 +6,9 @@ jobs: tests: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 + with: + submodules: recursive - name: Install Foundry uses: foundry-rs/foundry-toolchain@v1 @@ -16,6 +18,20 @@ jobs: - name: Install dependencies run: forge soldeer install + - name: Show Forge version + run: forge --version + + - name: Build + run: forge build + env: + FOUNDRY_PROFILE: ci + + - name: Run tests + run: forge test --isolate -vvv + env: + FOUNDRY_PROFILE: ci + FORGE_SNAPSHOT_CHECK: true + - name: Check contract sizes run: forge build --sizes diff --git a/foundry.toml b/foundry.toml index bb9fd7c..2fce551 100644 --- a/foundry.toml +++ b/foundry.toml @@ -16,7 +16,7 @@ remappings = [ "@/=src/" ] -[profile.intense] +[profile.ci] fuzz_runs = 10000 no_match_test = "FFI" @@ -26,6 +26,7 @@ match_test = "FFI" no_match_test = "a^" fuzz_runs = 1000 + [rpc_endpoints] mainnet = "${RPC_URL_MAINNET}" op = "${RPC_URL_OPTIMISM}" From a2aea9a4f0c17e7efad627c059865eaf3127f7d3 Mon Sep 17 00:00:00 2001 From: jj_ranalli Date: Mon, 23 Jun 2025 19:31:58 +0200 Subject: [PATCH 09/28] fix --- .github/workflows/tests.yml | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index d351564..25c3e34 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -30,16 +30,4 @@ jobs: run: forge test --isolate -vvv env: FOUNDRY_PROFILE: ci - FORGE_SNAPSHOT_CHECK: true - - - name: Check contract sizes - run: forge build --sizes - - - name: Check gas snapshots - run: forge snapshot --check - - - name: Run tests - run: forge test -vvv - env: - # Only fuzz intensely if we're running this action on a push to main or for a PR going into main: - FOUNDRY_PROFILE: ${{ (github.ref == 'refs/heads/main' || github.base_ref == 'main') && 'intense' }} + FORGE_SNAPSHOT_CHECK: true \ No newline at end of file From 0a52f106bfd667ef042bb9b5079dc412060056d7 Mon Sep 17 00:00:00 2001 From: jj_ranalli Date: Mon, 23 Jun 2025 19:34:02 +0200 Subject: [PATCH 10/28] fix --- foundry.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/foundry.toml b/foundry.toml index 2fce551..b3d591c 100644 --- a/foundry.toml +++ b/foundry.toml @@ -18,7 +18,6 @@ remappings = [ [profile.ci] fuzz_runs = 10000 -no_match_test = "FFI" [profile.ffi] ffi = true From 5d93a7b85a614366bf4b07a27005b53d6065bbc7 Mon Sep 17 00:00:00 2001 From: jj_ranalli Date: Tue, 24 Jun 2025 18:04:27 +0200 Subject: [PATCH 11/28] hooks refactor --- .../NFTDiscount/NFTDiscount.sol | 23 ++++-- .../TieredDiscount/TieredDiscount.sol | 26 +----- .../VRGDA/LinearVRGDAPrices.sol | 80 ++++++++----------- .../VRGDA/LogisticVRGDAPrices.sol | 41 +++++----- src/pricing-strategy/VRGDA/VRGDAPrices.sol | 21 +---- .../VRGDA/types/LinearVRGDAParams.sol | 1 + .../VRGDA/types/LogisticVRGDAParams.sol | 1 + src/utils/OnchainAction.sol | 2 +- src/utils/PricingStrategy.sol | 2 +- src/utils/PricingStrategyAction.sol | 2 +- src/utils/ProductOnchainAction.sol | 4 + src/utils/ProductPricingStrategy.sol | 4 + src/utils/ProductPricingStrategyAction.sol | 4 + test/ERC721Discount.sol | 14 ++-- test/LinearVRGDA.t.sol | 25 +++--- test/LogisticVRGDA.t.sol | 25 +++--- test/correctness/LinearVRGDACorrectness.t.sol | 7 +- 17 files changed, 116 insertions(+), 166 deletions(-) create mode 100644 src/utils/ProductOnchainAction.sol create mode 100644 src/utils/ProductPricingStrategy.sol create mode 100644 src/utils/ProductPricingStrategyAction.sol diff --git a/src/pricing-strategy/TieredDiscount/NFTDiscount/NFTDiscount.sol b/src/pricing-strategy/TieredDiscount/NFTDiscount/NFTDiscount.sol index 7e5f431..ceb6c57 100644 --- a/src/pricing-strategy/TieredDiscount/NFTDiscount/NFTDiscount.sol +++ b/src/pricing-strategy/TieredDiscount/NFTDiscount/NFTDiscount.sol @@ -5,13 +5,13 @@ import {IERC721} from "@openzeppelin-4.8.0/token/ERC721/IERC721.sol"; import {IERC1155} from "@openzeppelin-4.8.0/token/ERC1155/IERC1155.sol"; import { IProductsModule, - CurrencyParams, DiscountParams, ProductDiscounts, DiscountType, TieredDiscount, NFTType } from "../TieredDiscount.sol"; +import {CurrencyParams} from "../types/CurrencyParams.sol"; /** * @title NFTDiscount Pricing Strategy @@ -23,7 +23,16 @@ contract NFTDiscount is TieredDiscount { CONSTRUCTOR //////////////////////////////////////////////////////////////*/ - constructor(IProductsModule _productsModule) TieredDiscount(_productsModule) {} + constructor(IProductsModule productsModuleAddress) TieredDiscount(productsModuleAddress) {} + + /*////////////////////////////////////////////////////////////// + CONFIGURATION + //////////////////////////////////////////////////////////////*/ + + function pricingParamsSchema() external pure returns (string memory) { + return + "(address currency,uint240 basePrice,bool isFree,uint8 discountType,(address nft,uint80 discount,uint8 minQuantity,uint8 nftType,uint256 tokenId)[] discounts)[] allCurrencyParams"; + } /*////////////////////////////////////////////////////////////// FUNCTIONS @@ -35,13 +44,11 @@ contract NFTDiscount is TieredDiscount { * * @param slicerId ID of the slicer to set the price params for. * @param productId ID of the product to set the price params for. - * @param allCurrencyParams Array of `CurrencyParams` structs + * @param params Array of `CurrencyParams` structs */ - function _setProductPrice(uint256 slicerId, uint256 productId, CurrencyParams[] memory allCurrencyParams) - internal - virtual - override - { + function _setProductPrice(uint256 slicerId, uint256 productId, bytes memory params) internal override { + (CurrencyParams[] memory allCurrencyParams) = abi.decode(params, (CurrencyParams[])); + CurrencyParams memory currencyParams; DiscountParams[] memory newDiscounts; uint256 prevDiscountValue; diff --git a/src/pricing-strategy/TieredDiscount/TieredDiscount.sol b/src/pricing-strategy/TieredDiscount/TieredDiscount.sol index ec644a4..55c7612 100644 --- a/src/pricing-strategy/TieredDiscount/TieredDiscount.sol +++ b/src/pricing-strategy/TieredDiscount/TieredDiscount.sol @@ -2,7 +2,6 @@ pragma solidity ^0.8.20; import {IProductsModule, PricingStrategy} from "@/utils/PricingStrategy.sol"; -import {CurrencyParams} from "./types/CurrencyParams.sol"; import {ProductDiscounts, DiscountType} from "./types/ProductDiscounts.sol"; import {DiscountParams, NFTType} from "./types/DiscountParams.sol"; @@ -11,12 +10,6 @@ import {DiscountParams, NFTType} from "./types/DiscountParams.sol"; * @author Slice */ abstract contract TieredDiscount is PricingStrategy { - /*////////////////////////////////////////////////////////////// - EVENTS - //////////////////////////////////////////////////////////////*/ - - event ProductPriceSet(uint256 slicerId, uint256 productId, CurrencyParams[] params); - /*////////////////////////////////////////////////////////////// ERRORS //////////////////////////////////////////////////////////////*/ @@ -37,27 +30,12 @@ abstract contract TieredDiscount is PricingStrategy { CONSTRUCTOR //////////////////////////////////////////////////////////////*/ - constructor(IProductsModule _productsModule) PricingStrategy(_productsModule) {} + constructor(IProductsModule productsModuleAddress) PricingStrategy(productsModuleAddress) {} /*////////////////////////////////////////////////////////////// FUNCTIONS //////////////////////////////////////////////////////////////*/ - /** - * @notice Called by product owner to set base price and discounts for a product. - * - * @param slicerId ID of the slicer to set the price params for. - * @param productId ID of the product to set the price params for. - * @param params Array of `CurrencyParams` structs - */ - function setProductPrice(uint256 slicerId, uint256 productId, CurrencyParams[] memory params) - external - onlyProductOwner(slicerId, productId) - { - _setProductPrice(slicerId, productId, params); - emit ProductPriceSet(slicerId, productId, params); - } - /** * @notice See {ISliceProductPrice} */ @@ -82,8 +60,6 @@ abstract contract TieredDiscount is PricingStrategy { INTERNAL //////////////////////////////////////////////////////////////*/ - function _setProductPrice(uint256 slicerId, uint256 productId, CurrencyParams[] memory params) internal virtual; - function _productPrice( uint256 slicerId, uint256 productId, diff --git a/src/pricing-strategy/VRGDA/LinearVRGDAPrices.sol b/src/pricing-strategy/VRGDA/LinearVRGDAPrices.sol index f447ec7..e932933 100644 --- a/src/pricing-strategy/VRGDA/LinearVRGDAPrices.sol +++ b/src/pricing-strategy/VRGDA/LinearVRGDAPrices.sol @@ -10,14 +10,6 @@ import {IProductsModule, VRGDAPrices} from "./VRGDAPrices.sol"; /// @notice VRGDA with a linear issuance curve - Price library with different params for each Slice product. /// @author Slice contract LinearVRGDAPrices is VRGDAPrices { - event ProductPriceSet( - uint256 slicerId, - uint256 productId, - address[] currencies, - LinearVRGDAParams[] linearParams, - int256 priceDecayPercent - ); - /*////////////////////////////////////////////////////////////// STORAGE //////////////////////////////////////////////////////////////*/ @@ -29,26 +21,24 @@ contract LinearVRGDAPrices is VRGDAPrices { CONSTRUCTOR //////////////////////////////////////////////////////////////*/ - constructor(IProductsModule _productsModule) VRGDAPrices(_productsModule) {} + constructor(IProductsModule productsModuleAddress) VRGDAPrices(productsModuleAddress) {} /*////////////////////////////////////////////////////////////// - VRGDA PARAMETERS + CONFIGURATION //////////////////////////////////////////////////////////////*/ - /// @notice Set LinearProductParams for product. - /// @param slicerId ID of the slicer to set the price params for. - /// @param productId ID of the product to set the price params for. - /// @param currencies currencies of the product to set the price params for. - /// @param linearParams see `LinearVRGDAParams`. - /// @param priceDecayPercent The percent price decays per unit of time with no sales, scaled by 1e18. - function setProductPrice( - uint256 slicerId, - uint256 productId, - address[] calldata currencies, - LinearVRGDAParams[] calldata linearParams, - int256 priceDecayPercent - ) external onlyProductOwner(slicerId, productId) { - require(linearParams.length == currencies.length, "INVALID_INPUTS"); + function pricingParamsSchema() external pure returns (string memory) { + return + "(address currency,int128 targetPrice,uint128 min,int256 perTimeUnit)[] linearParams,int256 priceDecayPercent"; + } + + /** + * @notice Called by product owner to set base price and discounts for a product. + * @dev See {PricingStrategy} + */ + function _setProductPrice(uint256 slicerId, uint256 productId, bytes memory params) internal override { + (LinearVRGDAParams[] memory linearParams, int256 priceDecayPercent) = + abi.decode(params, (LinearVRGDAParams[], int256)); int256 decayConstant = wadLn(1e18 - priceDecayPercent); // The decay constant must be negative for VRGDAs to work. @@ -57,7 +47,7 @@ contract LinearVRGDAPrices is VRGDAPrices { /// Get product availability and isInfinite /// @dev available units is a uint32 - (uint256 availableUnits, bool isInfinite) = productsModule.availableUnits(slicerId, productId); + (uint256 availableUnits, bool isInfinite) = PRODUCTS_MODULE.availableUnits(slicerId, productId); // Product must not have infinite availability require(!isInfinite, "NOT_FINITE_AVAILABILITY"); @@ -68,37 +58,18 @@ contract LinearVRGDAPrices is VRGDAPrices { _productParams[slicerId][productId].decayConstant = int184(decayConstant); // Set currency params - for (uint256 i; i < currencies.length;) { - _productParams[slicerId][productId].pricingParams[currencies[i]] = linearParams[i]; + for (uint256 i; i < linearParams.length;) { + _productParams[slicerId][productId].pricingParams[linearParams[i].currency] = linearParams[i]; unchecked { ++i; } } - - emit ProductPriceSet(slicerId, productId, currencies, linearParams, priceDecayPercent); - } - - /*////////////////////////////////////////////////////////////// - PRICING LOGIC - //////////////////////////////////////////////////////////////*/ - - /// @dev Given a number of products sold, return the target time that number of products should be sold by. - /// @param sold A number of products sold, scaled by 1e18, to get the corresponding target sale time for. - /// @param timeFactor Time-dependent factor used to calculate target sale time. - /// @return The target time the products should be sold by, scaled by 1e18, where the time is - /// relative, such that 0 means the products should be sold immediately when the VRGDA begins. - function getTargetSaleTime(int256 sold, int256 timeFactor) public pure override returns (int256) { - return unsafeWadDiv(sold, timeFactor); } /** * @notice Function called by Slice protocol to calculate current product price. - * @param slicerId ID of the slicer being queried - * @param productId ID of the product being queried - * @param currency Currency chosen for the purchase - * @param quantity Number of units purchased - * @return ethPrice and currencyPrice of product. + * @dev See {IPricingStrategy} */ function productPrice( uint256 slicerId, @@ -115,7 +86,7 @@ contract LinearVRGDAPrices is VRGDAPrices { require(productParams.startTime != 0, "PRODUCT_UNSET"); // Get available units - (uint256 availableUnits,) = productsModule.availableUnits(slicerId, productId); + (uint256 availableUnits,) = PRODUCTS_MODULE.availableUnits(slicerId, productId); // Calculate sold units from availableUnits uint256 soldUnits = productParams.startUnits - availableUnits; @@ -143,4 +114,17 @@ contract LinearVRGDAPrices is VRGDAPrices { ); } } + + /*////////////////////////////////////////////////////////////// + INTERNAL + //////////////////////////////////////////////////////////////*/ + + /// @dev Given a number of products sold, return the target time that number of products should be sold by. + /// @param sold A number of products sold, scaled by 1e18, to get the corresponding target sale time for. + /// @param timeFactor Time-dependent factor used to calculate target sale time. + /// @return The target time the products should be sold by, scaled by 1e18, where the time is + /// relative, such that 0 means the products should be sold immediately when the VRGDA begins. + function getTargetSaleTime(int256 sold, int256 timeFactor) public pure override returns (int256) { + return unsafeWadDiv(sold, timeFactor); + } } diff --git a/src/pricing-strategy/VRGDA/LogisticVRGDAPrices.sol b/src/pricing-strategy/VRGDA/LogisticVRGDAPrices.sol index 5dba409..68cd8b7 100644 --- a/src/pricing-strategy/VRGDA/LogisticVRGDAPrices.sol +++ b/src/pricing-strategy/VRGDA/LogisticVRGDAPrices.sol @@ -38,27 +38,24 @@ contract LogisticVRGDAPrices is VRGDAPrices { CONSTRUCTOR //////////////////////////////////////////////////////////////*/ - constructor(IProductsModule _productsModule) VRGDAPrices(_productsModule) {} + constructor(IProductsModule productsModuleAddress) VRGDAPrices(productsModuleAddress) {} /*////////////////////////////////////////////////////////////// - VRGDA PARAMETERS + CONFIGURATION //////////////////////////////////////////////////////////////*/ - /// @notice Set LinearProductParams for product. - /// @param slicerId ID of the slicer to set the price params for. - /// @param productId ID of the product to set the price params for. - /// @param currencies currencies of the product to set the price params for. - /// @param logisticParams see `LogisticVRGDAParams`. - /// @param priceDecayPercent The percent price decays per unit of time with no sales, scaled by 1e18. - /// which affects how quickly we will reach the curve's asymptote, scaled by 1e18. - function setProductPrice( - uint256 slicerId, - uint256 productId, - address[] calldata currencies, - LogisticVRGDAParams[] calldata logisticParams, - int256 priceDecayPercent - ) external onlyProductOwner(slicerId, productId) { - require(logisticParams.length == currencies.length, "INVALID_INPUTS"); + function pricingParamsSchema() external pure returns (string memory) { + return + "(address currency,int128 targetPrice,uint128 min,int256 timeScale)[] logisticParams,int256 priceDecayPercent"; + } + + /** + * @notice Called by product owner to set base price and discounts for a product. + * @dev See {PricingStrategy} + */ + function _setProductPrice(uint256 slicerId, uint256 productId, bytes memory params) internal override { + (LogisticVRGDAParams[] memory logisticParams, int256 priceDecayPercent) = + abi.decode(params, (LogisticVRGDAParams[], int256)); int256 decayConstant = wadLn(1e18 - priceDecayPercent); // The decay constant must be negative for VRGDAs to work. @@ -66,7 +63,7 @@ contract LogisticVRGDAPrices is VRGDAPrices { require(decayConstant >= type(int184).min, "MIN_DECAY_CONSTANT_EXCEEDED"); // Get product availability and isInfinite - (uint256 availableUnits, bool isInfinite) = productsModule.availableUnits(slicerId, productId); + (uint256 availableUnits, bool isInfinite) = PRODUCTS_MODULE.availableUnits(slicerId, productId); // Product must not have infinite availability require(!isInfinite, "NON_FINITE_AVAILABILITY"); @@ -77,15 +74,13 @@ contract LogisticVRGDAPrices is VRGDAPrices { _productParams[slicerId][productId].decayConstant = int184(decayConstant); // Set currency params - for (uint256 i; i < currencies.length;) { - _productParams[slicerId][productId].pricingParams[currencies[i]] = logisticParams[i]; + for (uint256 i; i < logisticParams.length;) { + _productParams[slicerId][productId].pricingParams[logisticParams[i].currency] = logisticParams[i]; unchecked { ++i; } } - - emit ProductPriceSet(slicerId, productId, currencies, logisticParams, priceDecayPercent); } /*////////////////////////////////////////////////////////////// @@ -209,7 +204,7 @@ contract LogisticVRGDAPrices is VRGDAPrices { require(productParams.startTime != 0, "PRODUCT_UNSET"); // Get available units - (uint256 availableUnits,) = productsModule.availableUnits(slicerId, productId); + (uint256 availableUnits,) = PRODUCTS_MODULE.availableUnits(slicerId, productId); // Set ethPrice or currencyPrice based on chosen currency if (currency == address(0)) { diff --git a/src/pricing-strategy/VRGDA/VRGDAPrices.sol b/src/pricing-strategy/VRGDA/VRGDAPrices.sol index 6624e13..0b08709 100644 --- a/src/pricing-strategy/VRGDA/VRGDAPrices.sol +++ b/src/pricing-strategy/VRGDA/VRGDAPrices.sol @@ -14,7 +14,7 @@ abstract contract VRGDAPrices is PricingStrategy { CONSTRUCTOR //////////////////////////////////////////////////////////////*/ - constructor(IProductsModule _productsModule) PricingStrategy(_productsModule) {} + constructor(IProductsModule productsModuleAddress) PricingStrategy(productsModuleAddress) {} /*////////////////////////////////////////////////////////////// PRICING LOGIC @@ -89,23 +89,4 @@ abstract contract VRGDAPrices is PricingStrategy { } } } - - /** - * @notice Function called by Slice protocol to calculate current product price. - * @param slicerId ID of the slicer being queried - * @param productId ID of the product being queried - * @param currency Currency chosen for the purchase - * @param quantity Number of units purchased - * @param buyer Address of the buyer - * @param data Custom data sent along with the purchase transaction by the buyer - * @return ethPrice and currencyPrice of product. - */ - function productPrice( - uint256 slicerId, - uint256 productId, - address currency, - uint256 quantity, - address buyer, - bytes memory data - ) public view virtual override returns (uint256 ethPrice, uint256 currencyPrice) {} } diff --git a/src/pricing-strategy/VRGDA/types/LinearVRGDAParams.sol b/src/pricing-strategy/VRGDA/types/LinearVRGDAParams.sol index 53891e8..c3f591f 100644 --- a/src/pricing-strategy/VRGDA/types/LinearVRGDAParams.sol +++ b/src/pricing-strategy/VRGDA/types/LinearVRGDAParams.sol @@ -5,6 +5,7 @@ pragma solidity ^0.8.20; /// @param min minimum price to be paid for a token, scaled by 1e18 /// @param perTimeUnit The total number of products to target selling every full unit of time. struct LinearVRGDAParams { + address currency; int128 targetPrice; uint128 min; int256 perTimeUnit; diff --git a/src/pricing-strategy/VRGDA/types/LogisticVRGDAParams.sol b/src/pricing-strategy/VRGDA/types/LogisticVRGDAParams.sol index 5cea68d..31a3576 100644 --- a/src/pricing-strategy/VRGDA/types/LogisticVRGDAParams.sol +++ b/src/pricing-strategy/VRGDA/types/LogisticVRGDAParams.sol @@ -6,6 +6,7 @@ pragma solidity ^0.8.20; /// @param timeScale Time scale controls the steepness of the logistic curve, /// which affects how quickly we will reach the curve's asymptote. struct LogisticVRGDAParams { + address currency; int128 targetPrice; uint128 min; int256 timeScale; diff --git a/src/utils/OnchainAction.sol b/src/utils/OnchainAction.sol index 9aee891..cfe9010 100644 --- a/src/utils/OnchainAction.sol +++ b/src/utils/OnchainAction.sol @@ -1,4 +1,4 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.20; -import "slice/utils/OnchainAction.sol"; +import "slice/utils/hooks/OnchainAction.sol"; diff --git a/src/utils/PricingStrategy.sol b/src/utils/PricingStrategy.sol index 8996f54..cacf2d7 100644 --- a/src/utils/PricingStrategy.sol +++ b/src/utils/PricingStrategy.sol @@ -1,4 +1,4 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.20; -import "slice/utils/PricingStrategy.sol"; +import "slice/utils/hooks/PricingStrategy.sol"; diff --git a/src/utils/PricingStrategyAction.sol b/src/utils/PricingStrategyAction.sol index 95e834d..ed91fde 100644 --- a/src/utils/PricingStrategyAction.sol +++ b/src/utils/PricingStrategyAction.sol @@ -1,4 +1,4 @@ // SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.20; -import "slice/utils/PricingStrategyAction.sol"; +import "slice/utils/hooks/PricingStrategyAction.sol"; diff --git a/src/utils/ProductOnchainAction.sol b/src/utils/ProductOnchainAction.sol new file mode 100644 index 0000000..f9ca8f7 --- /dev/null +++ b/src/utils/ProductOnchainAction.sol @@ -0,0 +1,4 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.20; + +import "slice/utils/hooks/ProductOnchainAction.sol"; diff --git a/src/utils/ProductPricingStrategy.sol b/src/utils/ProductPricingStrategy.sol new file mode 100644 index 0000000..8b29b1f --- /dev/null +++ b/src/utils/ProductPricingStrategy.sol @@ -0,0 +1,4 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.20; + +import "slice/utils/hooks/ProductPricingStrategy.sol"; diff --git a/src/utils/ProductPricingStrategyAction.sol b/src/utils/ProductPricingStrategyAction.sol new file mode 100644 index 0000000..3aa2ee1 --- /dev/null +++ b/src/utils/ProductPricingStrategyAction.sol @@ -0,0 +1,4 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.20; + +import "slice/utils/hooks/ProductPricingStrategyAction.sol"; diff --git a/test/ERC721Discount.sol b/test/ERC721Discount.sol index 82ed9dc..10863a1 100644 --- a/test/ERC721Discount.sol +++ b/test/ERC721Discount.sol @@ -56,7 +56,7 @@ contract NFTDiscountTest is Test { currenciesParams[0] = CurrencyParams(ETH, basePrice, false, DiscountType.Absolute, discounts); vm.prank(owner); - erc721GatedDiscount.setProductPrice(slicerId, productId, currenciesParams); + erc721GatedDiscount.setProductPrice(slicerId, productId, abi.encode(currenciesParams)); } function testDeploy() public view { @@ -79,7 +79,7 @@ contract NFTDiscountTest is Test { currenciesParams[0] = CurrencyParams(ETH, basePrice, false, DiscountType.Absolute, discounts); vm.prank(owner); - erc721GatedDiscount.setProductPrice(slicerId, productId, currenciesParams); + erc721GatedDiscount.setProductPrice(slicerId, productId, abi.encode(currenciesParams)); /// check product price (uint256 ethPrice, uint256 currencyPrice) = @@ -105,7 +105,7 @@ contract NFTDiscountTest is Test { currenciesParams[0] = CurrencyParams(USDC, basePrice, false, DiscountType.Absolute, discounts); vm.prank(owner); - erc721GatedDiscount.setProductPrice(slicerId, productId, currenciesParams); + erc721GatedDiscount.setProductPrice(slicerId, productId, abi.encode(currenciesParams)); /// check product price (uint256 ethPrice, uint256 currencyPrice) = @@ -131,7 +131,7 @@ contract NFTDiscountTest is Test { currenciesParams[0] = CurrencyParams(USDC, basePrice, false, DiscountType.Absolute, discounts); vm.prank(owner); - erc721GatedDiscount.setProductPrice(slicerId, productId, currenciesParams); + erc721GatedDiscount.setProductPrice(slicerId, productId, abi.encode(currenciesParams)); /// check product price (uint256 ethPrice, uint256 currencyPrice) = @@ -176,7 +176,7 @@ contract NFTDiscountTest is Test { currenciesParams[1] = CurrencyParams(USDC, basePrice, false, DiscountType.Absolute, discountsTwo); vm.prank(owner); - erc721GatedDiscount.setProductPrice(slicerId, productId, currenciesParams); + erc721GatedDiscount.setProductPrice(slicerId, productId, abi.encode(currenciesParams)); /// check product price for ETH (uint256 ethPrice, uint256 currencyPrice) = @@ -302,7 +302,7 @@ contract NFTDiscountTest is Test { currenciesParams[0] = CurrencyParams(ETH, basePrice, false, DiscountType.Relative, discounts); vm.prank(owner); - erc721GatedDiscount.setProductPrice(slicerId, productId, currenciesParams); + erc721GatedDiscount.setProductPrice(slicerId, productId, abi.encode(currenciesParams)); /// check product price (uint256 ethPrice, uint256 currencyPrice) = @@ -328,7 +328,7 @@ contract NFTDiscountTest is Test { currenciesParams[0] = CurrencyParams(ETH, basePrice, false, DiscountType.Relative, discounts); vm.prank(owner); - erc721GatedDiscount.setProductPrice(slicerId, productId, currenciesParams); + erc721GatedDiscount.setProductPrice(slicerId, productId, abi.encode(currenciesParams)); // buy multiple products quantity = 6; diff --git a/test/LinearVRGDA.t.sol b/test/LinearVRGDA.t.sol index 5a087f9..c49b23c 100644 --- a/test/LinearVRGDA.t.sol +++ b/test/LinearVRGDA.t.sol @@ -29,16 +29,14 @@ contract LinearVRGDATest is Test { productsModule = new MockProductsModule(); vrgda = new MockLinearVRGDAPrices(IProductsModule(address(productsModule))); - LinearVRGDAParams[] memory linearParams = new LinearVRGDAParams[](1); - linearParams[0] = LinearVRGDAParams(targetPriceConstant, min, perTimeUnit); - address[] memory ethCurrency = new address[](1); - ethCurrency[0] = address(0); - address[] memory erc20Currency = new address[](1); - erc20Currency[0] = address(20); + LinearVRGDAParams[] memory linearParams = new LinearVRGDAParams[](2); + linearParams[0] = LinearVRGDAParams(address(0), targetPriceConstant, min, perTimeUnit); + linearParams[1] = LinearVRGDAParams(address(20), targetPriceConstant, min, perTimeUnit); + + bytes memory params = abi.encode(linearParams, priceDecayPercent); vm.startPrank(address(0)); - vrgda.setProductPrice(slicerId, productId, ethCurrency, linearParams, priceDecayPercent); - vrgda.setProductPrice(slicerId, productId, erc20Currency, linearParams, priceDecayPercent); + vrgda.setProductPrice(slicerId, productId, params); vm.stopPrank(); } @@ -128,14 +126,13 @@ contract LinearVRGDATest is Test { function testSetMultiplePrices() public { uint256 productId_ = 2; LinearVRGDAParams[] memory linearParams = new LinearVRGDAParams[](2); - linearParams[0] = LinearVRGDAParams(targetPriceConstant, min, perTimeUnit); - linearParams[1] = LinearVRGDAParams(targetPriceConstant, min, perTimeUnit); - address[] memory currencies = new address[](2); - currencies[0] = address(0); - currencies[1] = address(20); + linearParams[0] = LinearVRGDAParams(address(0), targetPriceConstant, min, perTimeUnit); + linearParams[1] = LinearVRGDAParams(address(20), targetPriceConstant, min, perTimeUnit); + + bytes memory params = abi.encode(linearParams, priceDecayPercent); vm.startPrank(address(0)); - vrgda.setProductPrice(slicerId, productId_, currencies, linearParams, priceDecayPercent); + vrgda.setProductPrice(slicerId, productId_, params); vm.stopPrank(); // Our VRGDA targets this number of mints at given time. diff --git a/test/LogisticVRGDA.t.sol b/test/LogisticVRGDA.t.sol index 13cacf8..069c54d 100644 --- a/test/LogisticVRGDA.t.sol +++ b/test/LogisticVRGDA.t.sol @@ -29,16 +29,14 @@ contract LogisticVRGDATest is Test { productsModule = new MockProductsModule(); vrgda = new MockLogisticVRGDAPrices(IProductsModule(address(productsModule))); - LogisticVRGDAParams[] memory logisticParams = new LogisticVRGDAParams[](1); - logisticParams[0] = LogisticVRGDAParams(targetPriceConstant, min, timeScale); - address[] memory ethCurrency = new address[](1); - ethCurrency[0] = address(0); - address[] memory erc20Currency = new address[](1); - erc20Currency[0] = address(20); + LogisticVRGDAParams[] memory logisticParams = new LogisticVRGDAParams[](2); + logisticParams[0] = LogisticVRGDAParams(address(0), targetPriceConstant, min, timeScale); + logisticParams[1] = LogisticVRGDAParams(address(20), targetPriceConstant, min, timeScale); + + bytes memory params = abi.encode(logisticParams, priceDecayPercent); vm.startPrank(address(0)); - vrgda.setProductPrice(slicerId, productId, ethCurrency, logisticParams, priceDecayPercent); - vrgda.setProductPrice(slicerId, productId, erc20Currency, logisticParams, priceDecayPercent); + vrgda.setProductPrice(slicerId, productId, params); vm.stopPrank(); } @@ -166,14 +164,13 @@ contract LogisticVRGDATest is Test { // uint256 targetPriceTest = 7.3013e18; uint256 productIdTest = 2; LogisticVRGDAParams[] memory logisticParams = new LogisticVRGDAParams[](2); - logisticParams[0] = LogisticVRGDAParams(targetPriceConstant, min, timeScale); - logisticParams[1] = LogisticVRGDAParams(targetPriceConstant, min, timeScale); - address[] memory currencies = new address[](2); - currencies[0] = address(0); - currencies[1] = address(20); + logisticParams[0] = LogisticVRGDAParams(address(0), targetPriceConstant, min, timeScale); + logisticParams[1] = LogisticVRGDAParams(address(20), targetPriceConstant, min, timeScale); + + bytes memory params = abi.encode(logisticParams, priceDecayPercent); vm.startPrank(address(0)); - vrgda.setProductPrice(slicerId, productIdTest, currencies, logisticParams, priceDecayPercent); + vrgda.setProductPrice(slicerId, productIdTest, params); vm.stopPrank(); vm.warp(block.timestamp + 10 days); diff --git a/test/correctness/LinearVRGDACorrectness.t.sol b/test/correctness/LinearVRGDACorrectness.t.sol index aea4284..8b346ef 100644 --- a/test/correctness/LinearVRGDACorrectness.t.sol +++ b/test/correctness/LinearVRGDACorrectness.t.sol @@ -32,12 +32,11 @@ contract LinearVRGDACorrectnessTest is Test { vrgda = new MockLinearVRGDAPrices(IProductsModule(address(productsModule))); LinearVRGDAParams[] memory linearParams = new LinearVRGDAParams[](1); - linearParams[0] = LinearVRGDAParams(targetPriceConstant, min, perTimeUnit); - address[] memory ethCurrency = new address[](1); - ethCurrency[0] = address(0); + linearParams[0] = LinearVRGDAParams(address(0), targetPriceConstant, min, perTimeUnit); vm.prank(address(0)); - vrgda.setProductPrice(slicerId, productId, ethCurrency, linearParams, priceDecayPercent); + bytes memory params = abi.encode(linearParams, priceDecayPercent); + vrgda.setProductPrice(slicerId, productId, params); } function testFFICorrectness() public { From 3051668934f824679477f95c94e7bacaaf44bcab Mon Sep 17 00:00:00 2001 From: jj_ranalli Date: Wed, 25 Jun 2025 16:46:04 +0200 Subject: [PATCH 12/28] hooks cleanup and fix script --- README.md | 7 + deployments/addresses.json | 91 +--------- script/ScriptUtils.sol | 155 ++++++++++++++---- script/WriteAddresses.s.sol | 4 + script/deploy.sh | 22 +-- .../NFTDiscount/NFTDiscount.sol | 0 .../TieredDiscount/TieredDiscount.sol | 0 .../TieredDiscount/types/CurrencyParams.sol | 0 .../TieredDiscount/types/DiscountParams.sol | 0 .../TieredDiscount/types/ProductDiscounts.sol | 0 .../VRGDA/LinearVRGDAPrices.sol | 0 .../VRGDA/LogisticVRGDAPrices.sol | 106 ++++++------ .../VRGDA/VRGDAPrices.sol | 0 .../VRGDA/types/LinearProductParams.sol | 0 .../VRGDA/types/LinearVRGDAParams.sol | 0 .../VRGDA/types/LogisticProductParams.sol | 0 .../VRGDA/types/LogisticVRGDAParams.sol | 0 test/ERC721Discount.sol | 2 +- test/mocks/MockLinearVRGDAPrices.sol | 2 +- test/mocks/MockLogisticVRGDAPrices.sol | 2 +- 20 files changed, 187 insertions(+), 204 deletions(-) rename src/{pricing-strategy => pricingStrategies}/TieredDiscount/NFTDiscount/NFTDiscount.sol (100%) rename src/{pricing-strategy => pricingStrategies}/TieredDiscount/TieredDiscount.sol (100%) rename src/{pricing-strategy => pricingStrategies}/TieredDiscount/types/CurrencyParams.sol (100%) rename src/{pricing-strategy => pricingStrategies}/TieredDiscount/types/DiscountParams.sol (100%) rename src/{pricing-strategy => pricingStrategies}/TieredDiscount/types/ProductDiscounts.sol (100%) rename src/{pricing-strategy => pricingStrategies}/VRGDA/LinearVRGDAPrices.sol (100%) rename src/{pricing-strategy => pricingStrategies}/VRGDA/LogisticVRGDAPrices.sol (95%) rename src/{pricing-strategy => pricingStrategies}/VRGDA/VRGDAPrices.sol (100%) rename src/{pricing-strategy => pricingStrategies}/VRGDA/types/LinearProductParams.sol (100%) rename src/{pricing-strategy => pricingStrategies}/VRGDA/types/LinearVRGDAParams.sol (100%) rename src/{pricing-strategy => pricingStrategies}/VRGDA/types/LogisticProductParams.sol (100%) rename src/{pricing-strategy => pricingStrategies}/VRGDA/types/LogisticVRGDAParams.sol (100%) diff --git a/README.md b/README.md index 4092c00..d4cf474 100644 --- a/README.md +++ b/README.md @@ -32,3 +32,10 @@ forge soldeer install ``` You will need a copy of [Foundry](https://github.com/foundry-rs/foundry) installed before proceeding. See the [installation guide](https://github.com/foundry-rs/foundry#installation) for details. + + \ No newline at end of file diff --git a/deployments/addresses.json b/deployments/addresses.json index 430ce1e..fe65f1e 100644 --- a/deployments/addresses.json +++ b/deployments/addresses.json @@ -1,90 +1,5 @@ { - "NFTDiscount": { - "addresses": [ - { - "abiProductPriceSet": { - "type": "event", - "name": "ProductPriceSet", - "inputs": [ - { - "name": "slicerId", - "type": "uint256", - "indexed": false, - "internalType": "uint256" - }, - { - "name": "productId", - "type": "uint256", - "indexed": false, - "internalType": "uint256" - }, - { - "name": "params", - "type": "tuple[]", - "indexed": false, - "internalType": "struct CurrencyParams[]", - "components": [ - { - "name": "currency", - "type": "address", - "internalType": "address" - }, - { - "name": "basePrice", - "type": "uint240", - "internalType": "uint240" - }, - { - "name": "isFree", - "type": "bool", - "internalType": "bool" - }, - { - "name": "discountType", - "type": "uint8", - "internalType": "enum DiscountType" - }, - { - "name": "discounts", - "type": "tuple[]", - "internalType": "struct DiscountParams[]", - "components": [ - { - "name": "nft", - "type": "address", - "internalType": "address" - }, - { - "name": "discount", - "type": "uint80", - "internalType": "uint80" - }, - { - "name": "minQuantity", - "type": "uint8", - "internalType": "uint8" - }, - { - "name": "nftType", - "type": "uint8", - "internalType": "enum NFTType" - }, - { - "name": "tokenId", - "type": "uint256", - "internalType": "uint256" - } - ] - } - ] - } - ], - "anonymous": false - }, - "address": "0x3bF8F042158CDdb57c2FDf5695DB9924edF65B33", - "blockNumber": 31437452, - "transactionHash": "0x45a0f87eca9bab42a174a6a32250b2ff515be2400167e1c9b8e9857ec952ef7d" - } - ] - } + "actions": {}, + "pricingStrategies": {}, + "pricingStrategyActions": {} } diff --git a/script/ScriptUtils.sol b/script/ScriptUtils.sol index 4514235..247e98c 100644 --- a/script/ScriptUtils.sol +++ b/script/ScriptUtils.sol @@ -82,7 +82,6 @@ abstract contract SetUpContractsList is Script { } struct ContractDeploymentData { - string abi; address contractAddress; uint256 blockNumber; bytes32 transactionHash; @@ -104,9 +103,54 @@ abstract contract SetUpContractsList is Script { _recordContractsOnPath(CONTRACT_PATH); } + function _updateGroupJson( + string memory existingAddresses, + string memory firstFolder, + string memory contractName, + string[] memory json + ) internal returns (string memory) { + string memory groupKey = string.concat(".", firstFolder); + string memory result; + + if (vm.keyExistsJson(existingAddresses, groupKey)) { + // For each contract in contractNames, if it belongs to this group, add its array + for (uint256 i = 0; i < contractNames.length; i++) { + (string memory folderName,) = _getFolderName(contractNames[i].path); + if (keccak256(bytes(folderName)) == keccak256(bytes(firstFolder))) { + string memory name = contractNames[i].name; + if (keccak256(bytes(name)) == keccak256(bytes(contractName))) { + // Use the new json for the contract being updated + result = vm.serializeString(contractName, name, json); + } else { + string memory contractKey = string.concat(".", firstFolder, ".", name); + if (vm.keyExistsJson(existingAddresses, contractKey)) { + // Use the existing array for other contracts + bytes memory arr = vm.parseJson(existingAddresses, contractKey); + ContractDeploymentData[] memory existingData = abi.decode(arr, (ContractDeploymentData[])); + + // Convert to string array format (similar to _buildJsonArray logic) + string[] memory arrStrings = new string[](existingData.length); + for (uint256 j = 0; j < existingData.length; j++) { + string memory idx = vm.toString(j); + vm.serializeAddress(idx, "address", existingData[j].contractAddress); + vm.serializeUint(idx, "blockNumber", existingData[j].blockNumber); + arrStrings[j] = + vm.serializeBytes32(idx, "transactionHash", existingData[j].transactionHash); + } + result = vm.serializeString(contractName, name, arrStrings); + } + } + } + } + return result; + } else { + // If the group doesn't exist, just create a new entry for contractName + return vm.serializeString(contractName, contractName, json); + } + } + function writeAddressesJson(string memory contractName) public { string memory existingAddresses = vm.readFile(ADDRESSES_PATH); - string memory newAddresses = "addresses"; Receipt[] memory receipts = _readReceipts(LAST_TX_PATH); Tx1559[] memory transactions = _readTx1559s(LAST_TX_PATH); @@ -127,7 +171,6 @@ abstract contract SetUpContractsList is Script { return; } - // TODO: Retrieve abi from contract ContractMap memory contractMap; for (uint256 i = 0; i < contractNames.length; i++) { if (keccak256(bytes(contractNames[i].name)) == keccak256(bytes(contractName))) { @@ -135,20 +178,37 @@ abstract contract SetUpContractsList is Script { break; } } - string memory abiPath = string.concat("./out/", contractName, ".sol/ProductPriceSet.json"); - string memory abiValue = vm.readFile(abiPath); - string memory key = string.concat(".", contractName, ".addresses"); - string memory addresses; + // Get the first-level and last folder name + (string memory firstFolder,) = _getFolderName(contractMap.path); + + string[] memory json = + _buildJsonArray(existingAddresses, string.concat(".", firstFolder, ".", contractName), transaction, receipt); + + // Copy all existing top-level groups + vm.serializeJson("addresses", existingAddresses); + + // Update the specific group with the new contract data + string memory updatedGroupJson = _updateGroupJson(existingAddresses, firstFolder, contractName, json); + + // Write the complete JSON with the updated group + vm.writeJson(vm.serializeString("addresses", firstFolder, updatedGroupJson), ADDRESSES_PATH); + } + + function _buildJsonArray( + string memory existingAddresses, + string memory key, + Tx1559 memory transaction, + Receipt memory receipt + ) internal returns (string[] memory json) { if (vm.keyExistsJson(existingAddresses, key)) { // Append new data to existingAddresses bytes memory contractAddressesJson = vm.parseJson(existingAddresses, key); ContractDeploymentData[] memory existingContractAddresses = abi.decode(contractAddressesJson, (ContractDeploymentData[])); - string[] memory json = new string[](existingContractAddresses.length + 1); + json = new string[](existingContractAddresses.length + 1); vm.serializeAddress("0", "address", transaction.contractAddress); - vm.serializeString("0", "abiProductPriceSet", abiValue); vm.serializeUint("0", "blockNumber", receipt.blockNumber); json[0] = vm.serializeBytes32("0", "transactionHash", transaction.hash); @@ -157,24 +217,15 @@ abstract contract SetUpContractsList is Script { string memory index = vm.toString(i + 1); vm.serializeAddress(index, "address", existingContractAddress.contractAddress); - vm.serializeString(index, "abiProductPriceSet", existingContractAddress.abi); vm.serializeUint(index, "blockNumber", existingContractAddress.blockNumber); json[i + 1] = vm.serializeBytes32(index, "transactionHash", existingContractAddress.transactionHash); } - - addresses = vm.serializeString("addresses", "addresses", json); } else { - string[] memory json = new string[](1); - vm.serializeAddress(contractName, "address", transaction.contractAddress); - vm.serializeString(contractName, "abiProductPriceSet", abiValue); - vm.serializeUint(contractName, "blockNumber", receipt.blockNumber); - json[0] = vm.serializeBytes32(contractName, "transactionHash", transaction.hash); - addresses = vm.serializeString("addresses", "addresses", json); + json = new string[](1); + vm.serializeAddress("0", "address", transaction.contractAddress); + vm.serializeUint("0", "blockNumber", receipt.blockNumber); + json[0] = vm.serializeBytes32("0", "transactionHash", transaction.hash); } - vm.serializeJson(newAddresses, existingAddresses); - newAddresses = vm.serializeString(newAddresses, contractName, addresses); - - vm.writeJson(newAddresses, ADDRESSES_PATH); } function _recordContractsOnPath(string memory path) internal { @@ -280,27 +331,65 @@ abstract contract SetUpContractsList is Script { return true; } - function _getFolderName(string memory path) internal view returns (string memory folderName) { + function _getFolderName(string memory path) + internal + view + returns (string memory firstFolderName, string memory lastFolderName) + { bytes memory pathBytes = bytes(path); uint256 lastSlash = 0; uint256 prevSlash = 0; + uint256 srcIndex = 0; + bool foundSrc = false; + // Find the index of '/src/' if present + for (uint256 i = 0; i < pathBytes.length - 3; i++) { + if ( + pathBytes[i] == 0x2f // '/' + && pathBytes[i + 1] == 0x73 // 's' + && pathBytes[i + 2] == 0x72 // 'r' + && pathBytes[i + 3] == 0x63 // 'c' + && (i + 4 == pathBytes.length || pathBytes[i + 4] == 0x2f) // '/' or end + ) { + srcIndex = i + 4; // index after '/src/' + foundSrc = true; + break; + } + } + // Find the first folder after src (or after root if no src) + uint256 start = foundSrc ? srcIndex : 0; + // skip leading slashes + while (start < pathBytes.length && pathBytes[start] == 0x2f) { + start++; + } + uint256 end = start; + while (end < pathBytes.length && pathBytes[end] != 0x2f) { + end++; + } + if (end > start) { + bytes memory firstFolderBytes = new bytes(end - start); + for (uint256 i = 0; i < end - start; i++) { + firstFolderBytes[i] = pathBytes[start + i]; + } + firstFolderName = string(firstFolderBytes); + } else { + firstFolderName = CONTRACT_PATH; + } + // Now get the last folder as before for (uint256 i = 0; i < pathBytes.length; i++) { if (pathBytes[i] == "/") { prevSlash = lastSlash; lastSlash = i; } } - // If only one slash, return the first folder after src - if (lastSlash == 0) return CONTRACT_PATH; - // Find the folder name (between prevSlash and lastSlash) - uint256 start = prevSlash == 0 ? 0 : prevSlash + 1; - uint256 len = lastSlash - start; - if (len == 0) return CONTRACT_PATH; - bytes memory folderBytes = new bytes(len); - for (uint256 i = 0; i < len; i++) { - folderBytes[i] = pathBytes[start + i]; + if (lastSlash == 0) return (firstFolderName, CONTRACT_PATH); + uint256 lastStart = prevSlash == 0 ? 0 : prevSlash + 1; + uint256 lastLen = lastSlash - lastStart; + if (lastLen == 0) return (firstFolderName, CONTRACT_PATH); + bytes memory lastFolderBytes = new bytes(lastLen); + for (uint256 i = 0; i < lastLen; i++) { + lastFolderBytes[i] = pathBytes[lastStart + i]; } - folderName = string(folderBytes); + lastFolderName = string(lastFolderBytes); } // modified from `vm.readTx1559s` to read directly from broadcast artifact diff --git a/script/WriteAddresses.s.sol b/script/WriteAddresses.s.sol index bb52cbd..f17603f 100644 --- a/script/WriteAddresses.s.sol +++ b/script/WriteAddresses.s.sol @@ -9,4 +9,8 @@ contract WriteAddressesScript is SetUpContractsList { function run(string memory contractName) external { writeAddressesJson(contractName); } + + function run() external { + writeAddressesJson("NFTDiscount"); + } } diff --git a/script/deploy.sh b/script/deploy.sh index bb7258c..71f9d6e 100755 --- a/script/deploy.sh +++ b/script/deploy.sh @@ -11,26 +11,6 @@ else forge script script/Deploy.s.sol --chain base --rpc-url base --private-key $PRIVATE_KEY --sig "run(string memory contractName)" "$contractName" --verify -vvvv --broadcast --slow fi -OUT_DIR="./out/${contractName}.sol" -ARTIFACT="${OUT_DIR}/${contractName}.json" -TARGET_EVENT="ProductPriceSet" -EVENT_JSON="${OUT_DIR}/${TARGET_EVENT}.json" - -# 1. Check if artifact exists -if [ ! -f "$ARTIFACT" ]; then - echo "Artifact not found: $ARTIFACT" - exit 1 -fi - -# 2. Extract the ABI element with name == "ProductPriceSet" -jq '.abi[] | select(.name == "'"$TARGET_EVENT"'")' "$ARTIFACT" > "$EVENT_JSON" - -if [ ! -s "$EVENT_JSON" ]; then - echo "Event $TARGET_EVENT not found in ABI." - exit 1 -fi - forge script script/WriteAddresses.s.sol --sig "run(string memory contractName)" "$contractName" -echo "Deployed contract: $contractName" -echo "Verify abi in deployments/addresses.json is correct!" \ No newline at end of file +echo "Deployed contract: $contractName" \ No newline at end of file diff --git a/src/pricing-strategy/TieredDiscount/NFTDiscount/NFTDiscount.sol b/src/pricingStrategies/TieredDiscount/NFTDiscount/NFTDiscount.sol similarity index 100% rename from src/pricing-strategy/TieredDiscount/NFTDiscount/NFTDiscount.sol rename to src/pricingStrategies/TieredDiscount/NFTDiscount/NFTDiscount.sol diff --git a/src/pricing-strategy/TieredDiscount/TieredDiscount.sol b/src/pricingStrategies/TieredDiscount/TieredDiscount.sol similarity index 100% rename from src/pricing-strategy/TieredDiscount/TieredDiscount.sol rename to src/pricingStrategies/TieredDiscount/TieredDiscount.sol diff --git a/src/pricing-strategy/TieredDiscount/types/CurrencyParams.sol b/src/pricingStrategies/TieredDiscount/types/CurrencyParams.sol similarity index 100% rename from src/pricing-strategy/TieredDiscount/types/CurrencyParams.sol rename to src/pricingStrategies/TieredDiscount/types/CurrencyParams.sol diff --git a/src/pricing-strategy/TieredDiscount/types/DiscountParams.sol b/src/pricingStrategies/TieredDiscount/types/DiscountParams.sol similarity index 100% rename from src/pricing-strategy/TieredDiscount/types/DiscountParams.sol rename to src/pricingStrategies/TieredDiscount/types/DiscountParams.sol diff --git a/src/pricing-strategy/TieredDiscount/types/ProductDiscounts.sol b/src/pricingStrategies/TieredDiscount/types/ProductDiscounts.sol similarity index 100% rename from src/pricing-strategy/TieredDiscount/types/ProductDiscounts.sol rename to src/pricingStrategies/TieredDiscount/types/ProductDiscounts.sol diff --git a/src/pricing-strategy/VRGDA/LinearVRGDAPrices.sol b/src/pricingStrategies/VRGDA/LinearVRGDAPrices.sol similarity index 100% rename from src/pricing-strategy/VRGDA/LinearVRGDAPrices.sol rename to src/pricingStrategies/VRGDA/LinearVRGDAPrices.sol diff --git a/src/pricing-strategy/VRGDA/LogisticVRGDAPrices.sol b/src/pricingStrategies/VRGDA/LogisticVRGDAPrices.sol similarity index 95% rename from src/pricing-strategy/VRGDA/LogisticVRGDAPrices.sol rename to src/pricingStrategies/VRGDA/LogisticVRGDAPrices.sol index 68cd8b7..012a6f3 100644 --- a/src/pricing-strategy/VRGDA/LogisticVRGDAPrices.sol +++ b/src/pricingStrategies/VRGDA/LogisticVRGDAPrices.sol @@ -19,14 +19,6 @@ import {IProductsModule, VRGDAPrices} from "./VRGDAPrices.sol"; /// @notice VRGDA with a logistic issuance curve - Price library with different params for each Slice product. /// @author Slice contract LogisticVRGDAPrices is VRGDAPrices { - event ProductPriceSet( - uint256 slicerId, - uint256 productId, - address[] currencies, - LogisticVRGDAParams[] logisticParams, - int256 priceDecayPercent - ); - /*////////////////////////////////////////////////////////////// STORAGE //////////////////////////////////////////////////////////////*/ @@ -83,6 +75,53 @@ contract LogisticVRGDAPrices is VRGDAPrices { } } + /** + * @notice Function called by Slice protocol to calculate current product price. + * @dev See {IPricingStrategy} + */ + function productPrice( + uint256 slicerId, + uint256 productId, + address currency, + uint256 quantity, + address, + bytes memory + ) public view override returns (uint256 ethPrice, uint256 currencyPrice) { + // Add reference for product and pricing params + LogisticProductParams storage productParams = _productParams[slicerId][productId]; + LogisticVRGDAParams memory pricingParams = productParams.pricingParams[currency]; + + require(productParams.startTime != 0, "PRODUCT_UNSET"); + + // Get available units + (uint256 availableUnits,) = PRODUCTS_MODULE.availableUnits(slicerId, productId); + + // Set ethPrice or currencyPrice based on chosen currency + if (currency == address(0)) { + ethPrice = getAdjustedVRGDALogisticPrice( + pricingParams.targetPrice, + productParams.decayConstant, + toDaysWadUnsafe(block.timestamp - productParams.startTime), + toWadUnsafe(productParams.startUnits + 1), + productParams.startUnits - availableUnits, + pricingParams.timeScale, + pricingParams.min, + quantity + ); + } else { + currencyPrice = getAdjustedVRGDALogisticPrice( + pricingParams.targetPrice, + productParams.decayConstant, + toDaysWadUnsafe(block.timestamp - productParams.startTime), + toWadUnsafe(productParams.startUnits + 1), + productParams.startUnits - availableUnits, + pricingParams.timeScale, + pricingParams.min, + quantity + ); + } + } + /*////////////////////////////////////////////////////////////// PRICING LOGIC //////////////////////////////////////////////////////////////*/ @@ -180,55 +219,4 @@ contract LogisticVRGDAPrices is VRGDAPrices { return -unsafeWadDiv(wadLn(saleFactor - 1e18), timeFactor); } } - - /** - * @notice Function called by Slice protocol to calculate current product price. - * @param slicerId ID of the slicer being queried - * @param productId ID of the product being queried - * @param currency Currency chosen for the purchase - * @param quantity Number of units purchased - * @return ethPrice and currencyPrice of product. - */ - function productPrice( - uint256 slicerId, - uint256 productId, - address currency, - uint256 quantity, - address, - bytes memory - ) public view override returns (uint256 ethPrice, uint256 currencyPrice) { - // Add reference for product and pricing params - LogisticProductParams storage productParams = _productParams[slicerId][productId]; - LogisticVRGDAParams memory pricingParams = productParams.pricingParams[currency]; - - require(productParams.startTime != 0, "PRODUCT_UNSET"); - - // Get available units - (uint256 availableUnits,) = PRODUCTS_MODULE.availableUnits(slicerId, productId); - - // Set ethPrice or currencyPrice based on chosen currency - if (currency == address(0)) { - ethPrice = getAdjustedVRGDALogisticPrice( - pricingParams.targetPrice, - productParams.decayConstant, - toDaysWadUnsafe(block.timestamp - productParams.startTime), - toWadUnsafe(productParams.startUnits + 1), - productParams.startUnits - availableUnits, - pricingParams.timeScale, - pricingParams.min, - quantity - ); - } else { - currencyPrice = getAdjustedVRGDALogisticPrice( - pricingParams.targetPrice, - productParams.decayConstant, - toDaysWadUnsafe(block.timestamp - productParams.startTime), - toWadUnsafe(productParams.startUnits + 1), - productParams.startUnits - availableUnits, - pricingParams.timeScale, - pricingParams.min, - quantity - ); - } - } } diff --git a/src/pricing-strategy/VRGDA/VRGDAPrices.sol b/src/pricingStrategies/VRGDA/VRGDAPrices.sol similarity index 100% rename from src/pricing-strategy/VRGDA/VRGDAPrices.sol rename to src/pricingStrategies/VRGDA/VRGDAPrices.sol diff --git a/src/pricing-strategy/VRGDA/types/LinearProductParams.sol b/src/pricingStrategies/VRGDA/types/LinearProductParams.sol similarity index 100% rename from src/pricing-strategy/VRGDA/types/LinearProductParams.sol rename to src/pricingStrategies/VRGDA/types/LinearProductParams.sol diff --git a/src/pricing-strategy/VRGDA/types/LinearVRGDAParams.sol b/src/pricingStrategies/VRGDA/types/LinearVRGDAParams.sol similarity index 100% rename from src/pricing-strategy/VRGDA/types/LinearVRGDAParams.sol rename to src/pricingStrategies/VRGDA/types/LinearVRGDAParams.sol diff --git a/src/pricing-strategy/VRGDA/types/LogisticProductParams.sol b/src/pricingStrategies/VRGDA/types/LogisticProductParams.sol similarity index 100% rename from src/pricing-strategy/VRGDA/types/LogisticProductParams.sol rename to src/pricingStrategies/VRGDA/types/LogisticProductParams.sol diff --git a/src/pricing-strategy/VRGDA/types/LogisticVRGDAParams.sol b/src/pricingStrategies/VRGDA/types/LogisticVRGDAParams.sol similarity index 100% rename from src/pricing-strategy/VRGDA/types/LogisticVRGDAParams.sol rename to src/pricingStrategies/VRGDA/types/LogisticVRGDAParams.sol diff --git a/test/ERC721Discount.sol b/test/ERC721Discount.sol index 10863a1..286ed6b 100644 --- a/test/ERC721Discount.sol +++ b/test/ERC721Discount.sol @@ -12,7 +12,7 @@ import { DiscountParams, CurrencyParams, NFTType -} from "@/pricing-strategy/TieredDiscount/NFTDiscount/NFTDiscount.sol"; +} from "@/pricing-strategies/TieredDiscount/NFTDiscount/NFTDiscount.sol"; import {MockERC721} from "./mocks/MockERC721.sol"; import {MockERC1155} from "./mocks/MockERC1155.sol"; diff --git a/test/mocks/MockLinearVRGDAPrices.sol b/test/mocks/MockLinearVRGDAPrices.sol index 71b6282..59e7b5a 100644 --- a/test/mocks/MockLinearVRGDAPrices.sol +++ b/test/mocks/MockLinearVRGDAPrices.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; -import "@/pricing-strategy/VRGDA/LinearVRGDAPrices.sol"; +import "@/pricing-strategies/VRGDA/LinearVRGDAPrices.sol"; contract MockLinearVRGDAPrices is LinearVRGDAPrices { constructor(IProductsModule productsModule) LinearVRGDAPrices(productsModule) {} diff --git a/test/mocks/MockLogisticVRGDAPrices.sol b/test/mocks/MockLogisticVRGDAPrices.sol index 5901706..3e3d1f9 100644 --- a/test/mocks/MockLogisticVRGDAPrices.sol +++ b/test/mocks/MockLogisticVRGDAPrices.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; -import "@/pricing-strategy/VRGDA/LogisticVRGDAPrices.sol"; +import "@/pricing-strategies/VRGDA/LogisticVRGDAPrices.sol"; contract MockLogisticVRGDAPrices is LogisticVRGDAPrices { constructor(IProductsModule productsModule) LogisticVRGDAPrices(productsModule) {} From eac65292d6892774ceafb044b5f62c561023eaed Mon Sep 17 00:00:00 2001 From: jj_ranalli Date: Thu, 26 Jun 2025 02:26:38 +0200 Subject: [PATCH 13/28] add all onchain actions and convert to new logic --- README.md | 6 +- foundry.toml | 2 + script/Deploy.s.sol | 2 +- soldeer.lock | 7 + src/actions/Allowlisted/Allowlisted.sol | 68 ++++++++ src/actions/ERC20Gated/ERC20Gated.sol | 73 ++++++++ src/actions/ERC20Gated/types/ERC20Gate.sol | 9 + src/actions/ERC20Mint/ERC20Mint.sol | 123 ++++++++++++++ src/actions/ERC20Mint/types/ERC20Data.sol | 10 ++ .../ERC20Mint/utils/ERC20Mint_BaseToken.sol | 58 +++++++ src/actions/ERC721AMint/ERC721Mint.sol | 99 +++++++++++ src/actions/ERC721AMint/types/ERC721Data.sol | 9 + .../utils/ERC721Mint_BaseToken.sol | 137 +++++++++++++++ src/actions/NFTGated/NFTGated.sol | 85 ++++++++++ src/actions/NFTGated/types/NFTGate.sol | 19 +++ src/interfaces/IOnchainAction.sol | 4 + src/interfaces/IPricingStrategy.sol | 4 + src/interfaces/IProductOnchainAction.sol | 4 + src/interfaces/IProductPricingStrategy.sol | 4 + .../FirstForFree/FirstForFree.sol | 157 ++++++++++++++++++ .../FirstForFree/types/ProductParams.sol | 12 ++ .../FirstForFree/types/TokenCondition.sol | 14 ++ .../FirstForFree/utils/ITokenERC1155.sol | 15 ++ src/productActions/BaseCafe_2.sol | 44 +++++ src/productActions/BaseGirlsScout.sol | 75 +++++++++ src/productActions/README.md | 5 + test/ERC721Discount.sol | 2 +- test/mocks/MockLinearVRGDAPrices.sol | 2 +- test/mocks/MockLogisticVRGDAPrices.sol | 2 +- 29 files changed, 1046 insertions(+), 5 deletions(-) create mode 100644 src/actions/Allowlisted/Allowlisted.sol create mode 100644 src/actions/ERC20Gated/ERC20Gated.sol create mode 100644 src/actions/ERC20Gated/types/ERC20Gate.sol create mode 100644 src/actions/ERC20Mint/ERC20Mint.sol create mode 100644 src/actions/ERC20Mint/types/ERC20Data.sol create mode 100644 src/actions/ERC20Mint/utils/ERC20Mint_BaseToken.sol create mode 100644 src/actions/ERC721AMint/ERC721Mint.sol create mode 100644 src/actions/ERC721AMint/types/ERC721Data.sol create mode 100644 src/actions/ERC721AMint/utils/ERC721Mint_BaseToken.sol create mode 100644 src/actions/NFTGated/NFTGated.sol create mode 100644 src/actions/NFTGated/types/NFTGate.sol create mode 100644 src/interfaces/IOnchainAction.sol create mode 100644 src/interfaces/IPricingStrategy.sol create mode 100644 src/interfaces/IProductOnchainAction.sol create mode 100644 src/interfaces/IProductPricingStrategy.sol create mode 100644 src/pricingStrategyActions/FirstForFree/FirstForFree.sol create mode 100644 src/pricingStrategyActions/FirstForFree/types/ProductParams.sol create mode 100644 src/pricingStrategyActions/FirstForFree/types/TokenCondition.sol create mode 100644 src/pricingStrategyActions/FirstForFree/utils/ITokenERC1155.sol create mode 100644 src/productActions/BaseCafe_2.sol create mode 100644 src/productActions/BaseGirlsScout.sol create mode 100644 src/productActions/README.md diff --git a/README.md b/README.md index d4cf474..5acb6ba 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,11 @@ You will need a copy of [Foundry](https://github.com/foundry-rs/foundry) install \ No newline at end of file diff --git a/foundry.toml b/foundry.toml index b3d591c..43bac48 100644 --- a/foundry.toml +++ b/foundry.toml @@ -13,6 +13,7 @@ remappings = [ "@openzeppelin-5.3.0/=dependencies/@openzeppelin-contracts-5.3.0/", "@openzeppelin-upgradeable-4.8.0/=dependencies/@openzeppelin-contracts-upgradeable-4.8.0/", "@openzeppelin-4.8.0/=dependencies/@openzeppelin-contracts-4.8.0/", + "@erc721a/=dependencies/erc721a-4.3.0/contracts/", "@/=src/" ] @@ -48,4 +49,5 @@ slice = "0.0.3" forge-std = "1.9.7" "@openzeppelin-contracts" = "4.8.0" "@openzeppelin-contracts-upgradeable" = "4.8.0" +erc721a = "4.3.0" diff --git a/script/Deploy.s.sol b/script/Deploy.s.sol index 7bc0b3a..7a02e82 100644 --- a/script/Deploy.s.sol +++ b/script/Deploy.s.sol @@ -21,7 +21,7 @@ contract DeployScript is BaseScript, SetUpContractsList { string memory prompt = "\nPricing strategies available to deploy:\n"; string memory lastFolder = ""; for (uint256 i = 0; i < contractNames.length; i++) { - string memory folder = _getFolderName(contractNames[i].path); + (, string memory folder) = _getFolderName(contractNames[i].path); if (i == 0 || keccak256(bytes(folder)) != keccak256(bytes(lastFolder))) { prompt = string.concat(prompt, "\n"); prompt = string.concat(prompt, folder, "\n"); diff --git a/soldeer.lock b/soldeer.lock index ab18f27..ce3469f 100644 --- a/soldeer.lock +++ b/soldeer.lock @@ -12,6 +12,13 @@ url = "https://soldeer-revisions.s3.amazonaws.com/@openzeppelin-contracts-upgrad checksum = "9bd3feb8a6ac529ecf2ab1927bf482bfee9abc2568533b155e567715b83ba94e" integrity = "16aa3677eec13cfddee8ee412031773a45525d4780b741eab29c746b545afc77" +[[dependencies]] +name = "erc721a" +version = "4.3.0" +url = "https://soldeer-revisions.s3.amazonaws.com/erc721a/4_3_0_14-03-2024_06:28:52_erc721a.zip" +checksum = "95826815148f5281311395186258940cc01f18d5d95975050a2c5f0f835d476d" +integrity = "43c1eb15351e04592630e7200eb7a8eca32aedc86be492d4f0c1825065cd70eb" + [[dependencies]] name = "forge-std" version = "1.9.7" diff --git a/src/actions/Allowlisted/Allowlisted.sol b/src/actions/Allowlisted/Allowlisted.sol new file mode 100644 index 0000000..938fef5 --- /dev/null +++ b/src/actions/Allowlisted/Allowlisted.sol @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {MerkleProof} from "@openzeppelin-4.8.0/utils/cryptography/MerkleProof.sol"; +import {IProductsModule, OnchainAction, IOnchainAction} from "@/utils/OnchainAction.sol"; + +/** + * @title Allowlisted + * @notice Action with allowlist requirement. + * @dev Implements allowlist functionality to products. + * @author Slice + */ +contract Allowlisted is OnchainAction { + /*////////////////////////////////////////////////////////////// + MUTABLE STORAGE + //////////////////////////////////////////////////////////////*/ + + mapping(uint256 slicerId => mapping(uint256 productId => bytes32 merkleRoot)) public merkleRoots; + + /*////////////////////////////////////////////////////////////// + CONSTRUCTOR + //////////////////////////////////////////////////////////////*/ + + constructor(IProductsModule productsModuleAddress) OnchainAction(productsModuleAddress) {} + + /*////////////////////////////////////////////////////////////// + CONFIGURATION + //////////////////////////////////////////////////////////////*/ + + /** + * @inheritdoc OnchainAction + * @dev Checks if the account is in the allowlist. + */ + function isPurchaseAllowed( + uint256 slicerId, + uint256 productId, + address account, + uint256, + bytes memory, + bytes memory buyerCustomData + ) public view override returns (bool isAllowed) { + // Get Merkle proof from buyerCustomData + bytes32[] memory proof = abi.decode(buyerCustomData, (bytes32[])); + + // Generate leaf from account address + bytes32 leaf = keccak256(abi.encodePacked(account)); + bytes32 root = merkleRoots[slicerId][productId]; + + // Check if Merkle proof is valid + isAllowed = MerkleProof.verify(proof, root, leaf); + } + + /** + * @inheritdoc OnchainAction + * @dev Sets the Merkle root for the allowlist. + */ + function _setProductAction(uint256 slicerId, uint256 productId, bytes memory params) internal override { + (bytes32 merkleRoot) = abi.decode(params, (bytes32)); + merkleRoots[slicerId][productId] = merkleRoot; + } + + /** + * @inheritdoc IOnchainAction + */ + function actionParamsSchema() external pure returns (string memory) { + return "bytes32 merkleRoot"; + } +} diff --git a/src/actions/ERC20Gated/ERC20Gated.sol b/src/actions/ERC20Gated/ERC20Gated.sol new file mode 100644 index 0000000..4c09f1f --- /dev/null +++ b/src/actions/ERC20Gated/ERC20Gated.sol @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {ERC20Gate} from "./types/ERC20Gate.sol"; +import {IProductsModule, OnchainAction, IOnchainAction} from "@/utils/OnchainAction.sol"; + +/** + * @title ERC20Gated + * @notice Action with ERC20 gate requirement. + * @dev Implements ERC20 gate functionality to products. + * @author Slice + */ +contract ERC20Gated is OnchainAction { + /*////////////////////////////////////////////////////////////// + MUTABLE STORAGE + //////////////////////////////////////////////////////////////*/ + + mapping(uint256 slicerId => mapping(uint256 productId => ERC20Gate[] gates)) public tokenGates; + + /*////////////////////////////////////////////////////////////// + CONSTRUCTOR + //////////////////////////////////////////////////////////////*/ + + constructor(IProductsModule productsModuleAddress) OnchainAction(productsModuleAddress) {} + + /*////////////////////////////////////////////////////////////// + CONFIGURATION + //////////////////////////////////////////////////////////////*/ + + /** + * @inheritdoc OnchainAction + * @dev Checks if `account` owns the required amount of all ERC20 tokens. + */ + function isPurchaseAllowed( + uint256 slicerId, + uint256 productId, + address account, + uint256, + bytes memory, + bytes memory + ) public view override returns (bool) { + ERC20Gate[] memory gates = tokenGates[slicerId][productId]; + + for (uint256 i = 0; i < gates.length; i++) { + ERC20Gate memory gate = gates[i]; + uint256 accountBalance = gate.erc20.balanceOf(account); + if (accountBalance < gate.amount) { + return false; + } + } + + return true; + } + + /** + * @inheritdoc OnchainAction + * @dev Sets the ERC20 gates for a product. + */ + function _setProductAction(uint256 slicerId, uint256 productId, bytes memory params) internal override { + (ERC20Gate[] memory gates) = abi.decode(params, (ERC20Gate[])); + + for (uint256 i = 0; i < gates.length; i++) { + tokenGates[slicerId][productId].push(gates[i]); + } + } + + /** + * @inheritdoc IOnchainAction + */ + function actionParamsSchema() external pure returns (string memory) { + return "(address erc20,uint256 amount)[] erc20Gates"; + } +} diff --git a/src/actions/ERC20Gated/types/ERC20Gate.sol b/src/actions/ERC20Gated/types/ERC20Gate.sol new file mode 100644 index 0000000..9b75f39 --- /dev/null +++ b/src/actions/ERC20Gated/types/ERC20Gate.sol @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {IERC20} from "@openzeppelin-4.8.0/interfaces/IERC20.sol"; + +struct ERC20Gate { + IERC20 erc20; + uint256 amount; +} diff --git a/src/actions/ERC20Mint/ERC20Mint.sol b/src/actions/ERC20Mint/ERC20Mint.sol new file mode 100644 index 0000000..db64e09 --- /dev/null +++ b/src/actions/ERC20Mint/ERC20Mint.sol @@ -0,0 +1,123 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {IProductsModule, OnchainAction, IOnchainAction} from "@/utils/OnchainAction.sol"; +import {ERC20Data} from "./types/ERC20Data.sol"; +import {ERC20Mint_BaseToken} from "./utils/ERC20Mint_BaseToken.sol"; + +/** + * @title ERC20Mint + * @notice Mints ERC20 tokens for each unit purchased. + * @dev If `revertOnMaxSupplyReached` is set to true, reverts when max supply is exceeded. + * @author Slice + */ +contract ERC20Mint is OnchainAction { + /*////////////////////////////////////////////////////////////// + ERRORS + //////////////////////////////////////////////////////////////*/ + + error MaxSupplyExceeded(); + error InvalidTokensPerUnit(); + + /*////////////////////////////////////////////////////////////// + MUTABLE STORAGE + //////////////////////////////////////////////////////////////*/ + + mapping(uint256 slicerId => mapping(uint256 productId => ERC20Data tokenData)) public tokenData; + + /*////////////////////////////////////////////////////////////// + CONSTRUCTOR + //////////////////////////////////////////////////////////////*/ + + constructor(IProductsModule productsModuleAddress) OnchainAction(productsModuleAddress) {} + + /*////////////////////////////////////////////////////////////// + CONFIGURATION + //////////////////////////////////////////////////////////////*/ + + /** + * @inheritdoc OnchainAction + * @dev If `revertOnMaxSupplyReached` is set to true, returns false when max supply is exceeded. + */ + function isPurchaseAllowed( + uint256 slicerId, + uint256 productId, + address, + uint256 quantity, + bytes memory, + bytes memory + ) public view virtual override returns (bool isAllowed) { + ERC20Data memory tokenData_ = tokenData[slicerId][productId]; + + if (tokenData_.revertOnMaxSupplyReached) { + return + tokenData_.token.totalSupply() + (quantity * tokenData_.tokensPerUnit) <= tokenData_.token.maxSupply(); + } + + return true; + } + + /** + * @inheritdoc OnchainAction + * @notice Mints tokens to the buyer. + * @dev If `revertOnMaxSupplyReached` is set to true, reverts when max supply is exceeded. + */ + function _onProductPurchase( + uint256 slicerId, + uint256 productId, + address buyer, + uint256 quantity, + bytes memory, + bytes memory + ) internal override { + ERC20Data memory tokenData_ = tokenData[slicerId][productId]; + + uint256 tokensToMint = quantity * tokenData_.tokensPerUnit; + + (bool success,) = + address(tokenData_.token).call(abi.encodeWithSelector(tokenData_.token.mint.selector, buyer, tokensToMint)); + + if (tokenData_.revertOnMaxSupplyReached) { + if (!success) revert MaxSupplyExceeded(); + } + } + + /** + * @inheritdoc OnchainAction + * @dev Sets the ERC20 data for a product. + */ + function _setProductAction(uint256 slicerId, uint256 productId, bytes memory params) internal override { + ( + string memory name, + string memory symbol, + uint256 premintAmount, + address premintReceiver, + bool revertOnMaxSupplyReached, + uint256 maxSupply, + uint256 tokensPerUnit + ) = abi.decode(params, (string, string, uint256, address, bool, uint256, uint256)); + + if (tokensPerUnit == 0) revert InvalidTokensPerUnit(); + + ERC20Mint_BaseToken token = tokenData[slicerId][productId].token; + if (address(token) == address(0)) { + token = new ERC20Mint_BaseToken(name, symbol, maxSupply); + + if (premintAmount != 0) { + token.mint(premintReceiver, premintAmount); + } + } else { + token.setMaxSupply(maxSupply); + } + + tokenData[slicerId][productId] = ERC20Data(token, revertOnMaxSupplyReached, tokensPerUnit); + } + + /** + * @inheritdoc IOnchainAction + */ + function actionParamsSchema() external pure returns (string memory) { + return + "string name,string symbol,uint256 premintAmount,address premintReceiver,uint256 maxSupply,uint256 tokensPerUnit"; + } +} diff --git a/src/actions/ERC20Mint/types/ERC20Data.sol b/src/actions/ERC20Mint/types/ERC20Data.sol new file mode 100644 index 0000000..0af5a39 --- /dev/null +++ b/src/actions/ERC20Mint/types/ERC20Data.sol @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {ERC20Mint_BaseToken} from "../utils/ERC20Mint_BaseToken.sol"; + +struct ERC20Data { + ERC20Mint_BaseToken token; + bool revertOnMaxSupplyReached; + uint256 tokensPerUnit; +} diff --git a/src/actions/ERC20Mint/utils/ERC20Mint_BaseToken.sol b/src/actions/ERC20Mint/utils/ERC20Mint_BaseToken.sol new file mode 100644 index 0000000..cea8606 --- /dev/null +++ b/src/actions/ERC20Mint/utils/ERC20Mint_BaseToken.sol @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {ERC20} from "@openzeppelin-4.8.0/token/ERC20/ERC20.sol"; + +/** + * @title ERC20Mint_BaseToken + * @notice Base ERC20 token for ERC20Mint onchain action. + */ +contract ERC20Mint_BaseToken is ERC20 { + /*////////////////////////////////////////////////////////////// + ERRORS + //////////////////////////////////////////////////////////////*/ + + error NotMinter(); + error MaxSupplyExceeded(); + + /*////////////////////////////////////////////////////////////// + STORAGE + //////////////////////////////////////////////////////////////*/ + + address public immutable minter; + uint256 public maxSupply; + + /*////////////////////////////////////////////////////////////// + CONSTRUCTOR + //////////////////////////////////////////////////////////////*/ + + constructor(string memory name_, string memory symbol_, uint256 maxSupply_) ERC20(name_, symbol_) { + minter = msg.sender; + _setMaxSupply(maxSupply_); + } + + /*////////////////////////////////////////////////////////////// + FUNCTIONS + //////////////////////////////////////////////////////////////*/ + + function mint(address to, uint256 amount) public { + if (msg.sender != minter) revert NotMinter(); + _mint(to, amount); + + if (totalSupply() > maxSupply) revert MaxSupplyExceeded(); + } + + function setMaxSupply(uint256 maxSupply_) public { + if (msg.sender != minter) revert NotMinter(); + + _setMaxSupply(maxSupply_); + } + + /*////////////////////////////////////////////////////////////// + INTERNAL FUNCTIONS + //////////////////////////////////////////////////////////////*/ + + function _setMaxSupply(uint256 maxSupply_) internal { + maxSupply = maxSupply_ == 0 ? type(uint256).max : maxSupply_; + } +} diff --git a/src/actions/ERC721AMint/ERC721Mint.sol b/src/actions/ERC721AMint/ERC721Mint.sol new file mode 100644 index 0000000..34a848d --- /dev/null +++ b/src/actions/ERC721AMint/ERC721Mint.sol @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {IProductsModule, OnchainAction, IOnchainAction} from "@/utils/OnchainAction.sol"; +import {MAX_ROYALTY, ERC721Mint_BaseToken} from "./utils/ERC721Mint_BaseToken.sol"; +import {ERC721Data} from "./types/ERC721Data.sol"; + +/** + * @title ERC721Mint + * @notice Mints ERC721 tokens for each unit purchased. + * @dev If `revertOnMaxSupplyReached` is set to true, reverts when max supply is exceeded. + * @author Slice + */ +contract ERC721Mint is OnchainAction { + /*////////////////////////////////////////////////////////////// + ERRORS + //////////////////////////////////////////////////////////////*/ + + error MaxSupplyExceeded(); + error InvalidRoyaltyFraction(); + + /*////////////////////////////////////////////////////////////// + MUTABLE STORAGE + //////////////////////////////////////////////////////////////*/ + + mapping(uint256 slicerId => mapping(uint256 productId => ERC721Data tokenData)) public tokenData; + + /*////////////////////////////////////////////////////////////// + CONSTRUCTOR + //////////////////////////////////////////////////////////////*/ + + constructor(IProductsModule productsModuleAddress) OnchainAction(productsModuleAddress) {} + + /*////////////////////////////////////////////////////////////// + CONFIGURATION + //////////////////////////////////////////////////////////////*/ + + /** + * @inheritdoc OnchainAction + * @notice Mints tokens to the buyer. + * @dev If `revertOnMaxSupplyReached` is set to true, reverts when max supply is exceeded. + */ + function _onProductPurchase( + uint256 slicerId, + uint256 productId, + address buyer, + uint256 quantity, + bytes memory, + bytes memory + ) internal override { + ERC721Data memory tokenData_ = tokenData[slicerId][productId]; + + (bool success,) = + address(tokenData_.token).call(abi.encodeWithSelector(tokenData_.token.mint.selector, buyer, quantity)); + + if (tokenData_.revertOnMaxSupplyReached) { + if (!success) revert MaxSupplyExceeded(); + } + } + + /** + * @inheritdoc OnchainAction + * @dev Sets the ERC721 data for a product. + */ + function _setProductAction(uint256 slicerId, uint256 productId, bytes memory params) internal override { + ( + string memory name_, + string memory symbol_, + address royaltyReceiver_, + uint256 royaltyFraction_, + string memory baseURI__, + string memory tokenURI__, + bool revertOnMaxSupplyReached, + uint256 maxSupply + ) = abi.decode(params, (string, string, address, uint256, string, string, bool, uint256)); + + if (royaltyFraction_ > MAX_ROYALTY) revert InvalidRoyaltyFraction(); + + ERC721Mint_BaseToken token = tokenData[slicerId][productId].token; + + if (address(token) == address(0)) { + token = new ERC721Mint_BaseToken( + name_, symbol_, maxSupply, royaltyReceiver_, royaltyFraction_, baseURI__, tokenURI__ + ); + } else { + token.setParams(maxSupply, royaltyReceiver_, royaltyFraction_, baseURI__, tokenURI__); + } + + tokenData[slicerId][productId] = ERC721Data(token, revertOnMaxSupplyReached); + } + + /** + * @inheritdoc IOnchainAction + */ + function actionParamsSchema() external pure returns (string memory) { + return + "string name,string symbol,address royaltyReceiver,uint256 royaltyFraction,string baseURI,string tokenURI,bool revertOnMaxSupplyReached,uint256 maxSupply"; + } +} diff --git a/src/actions/ERC721AMint/types/ERC721Data.sol b/src/actions/ERC721AMint/types/ERC721Data.sol new file mode 100644 index 0000000..2f57fcd --- /dev/null +++ b/src/actions/ERC721AMint/types/ERC721Data.sol @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {ERC721Mint_BaseToken} from "../utils/ERC721Mint_BaseToken.sol"; + +struct ERC721Data { + ERC721Mint_BaseToken token; + bool revertOnMaxSupplyReached; +} diff --git a/src/actions/ERC721AMint/utils/ERC721Mint_BaseToken.sol b/src/actions/ERC721AMint/utils/ERC721Mint_BaseToken.sol new file mode 100644 index 0000000..301eee2 --- /dev/null +++ b/src/actions/ERC721AMint/utils/ERC721Mint_BaseToken.sol @@ -0,0 +1,137 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {ERC721A} from "@erc721a/ERC721A.sol"; +import {IERC2981, IERC165} from "@openzeppelin-4.8.0/interfaces/IERC2981.sol"; + +uint256 constant MAX_ROYALTY = 10_000; + +/** + * @title ERC721Mint_BaseToken + * @notice Base ERC721 token for ERC721Mint onchain action. + */ +contract ERC721Mint_BaseToken is ERC721A, IERC2981 { + /*////////////////////////////////////////////////////////////// + ERRORS + //////////////////////////////////////////////////////////////*/ + + error NotMinter(); + error MaxSupplyExceeded(); + + /*////////////////////////////////////////////////////////////// + STORAGE + //////////////////////////////////////////////////////////////*/ + + address public immutable minter; + + uint256 public maxSupply; + address public royaltyReceiver; + uint256 public royaltyFraction; + string public baseURI_; + string public tokenURI_; + + /*////////////////////////////////////////////////////////////// + CONSTRUCTOR + //////////////////////////////////////////////////////////////*/ + + constructor( + string memory name_, + string memory symbol_, + uint256 maxSupply_, + address royaltyReceiver_, + uint256 royaltyFraction_, + string memory baseURI__, + string memory tokenURI__ + ) ERC721A(name_, symbol_) { + minter = msg.sender; + + _setMaxSupply(maxSupply_); + royaltyReceiver = royaltyReceiver_; + royaltyFraction = royaltyFraction_; + baseURI_ = baseURI__; + tokenURI_ = tokenURI__; + } + + /*////////////////////////////////////////////////////////////// + ACTION FUNCTIONS + //////////////////////////////////////////////////////////////*/ + + function mint(address to, uint256 amount) public { + if (msg.sender != minter) revert NotMinter(); + _mint(to, amount); + + if (totalSupply() > maxSupply) revert MaxSupplyExceeded(); + } + + function setParams( + uint256 maxSupply_, + address royaltyReceiver_, + uint256 royaltyFraction_, + string memory baseURI__, + string memory tokenURI__ + ) external { + if (msg.sender != minter) revert NotMinter(); + + royaltyReceiver = royaltyReceiver_; + royaltyFraction = royaltyFraction_; + baseURI_ = baseURI__; + tokenURI_ = tokenURI__; + _setMaxSupply(maxSupply_); + } + + /*////////////////////////////////////////////////////////////// + ERC721A FUNCTIONS + //////////////////////////////////////////////////////////////*/ + + /** + * @inheritdoc ERC721A + */ + function tokenURI(uint256 tokenId) public view virtual override returns (string memory) { + if (!_exists(tokenId)) revert URIQueryForNonexistentToken(); + + string memory baseURI = _baseURI(); + return bytes(baseURI).length != 0 ? string(abi.encodePacked(baseURI, _toString(tokenId))) : tokenURI_; + } + + /** + * @inheritdoc ERC721A + */ + function _baseURI() internal view virtual override returns (string memory) { + return baseURI_; + } + + /** + * @inheritdoc IERC2981 + */ + function royaltyInfo(uint256, uint256 salePrice) + external + view + override + returns (address _receiver, uint256 _royaltyAmount) + { + // return the receiver from storage + _receiver = royaltyReceiver; + + // calculate and return the _royaltyAmount + _royaltyAmount = (salePrice * royaltyFraction) / MAX_ROYALTY; + } + + /** + * @inheritdoc IERC165 + */ + function supportsInterface(bytes4 interfaceId) public view virtual override(ERC721A, IERC165) returns (bool) { + // The interface IDs are constants representing the first 4 bytes + // of the XOR of all function selectors in the interface. + // See: [ERC165](https://eips.ethereum.org/EIPS/eip-165) + // (e.g. `bytes4(i.functionA.selector ^ i.functionB.selector ^ ...)`) + return ERC721A.supportsInterface(interfaceId) || interfaceId == type(IERC2981).interfaceId; + } + + /*////////////////////////////////////////////////////////////// + INTERNAL FUNCTIONS + //////////////////////////////////////////////////////////////*/ + + function _setMaxSupply(uint256 maxSupply_) internal { + maxSupply = maxSupply_ == 0 ? type(uint256).max : maxSupply_; + } +} diff --git a/src/actions/NFTGated/NFTGated.sol b/src/actions/NFTGated/NFTGated.sol new file mode 100644 index 0000000..d4e4af3 --- /dev/null +++ b/src/actions/NFTGated/NFTGated.sol @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {IERC721} from "@openzeppelin-4.8.0/interfaces/IERC721.sol"; +import {IERC1155} from "@openzeppelin-4.8.0/interfaces/IERC1155.sol"; +import {IProductsModule, OnchainAction, IOnchainAction} from "@/utils/OnchainAction.sol"; +import {TokenType, NFTGate, NFTGates} from "./types/NFTGate.sol"; + +/** + * @title NFTGated + * @notice Action with NFT gate requirement. + * @dev Implements NFT gate functionality to products. + * @author Slice + */ +contract NFTGated is OnchainAction { + /*////////////////////////////////////////////////////////////// + MUTABLE STORAGE + //////////////////////////////////////////////////////////////*/ + + mapping(uint256 slicerId => mapping(uint256 productId => NFTGates gates)) public nftGates; + + /*////////////////////////////////////////////////////////////// + CONSTRUCTOR + //////////////////////////////////////////////////////////////*/ + + constructor(IProductsModule productsModuleAddress) OnchainAction(productsModuleAddress) {} + + /*////////////////////////////////////////////////////////////// + CONFIGURATION + //////////////////////////////////////////////////////////////*/ + + /** + * @inheritdoc OnchainAction + * @dev Checks if `account` owns the required amount of NFT tokens. + */ + function isPurchaseAllowed( + uint256 slicerId, + uint256 productId, + address account, + uint256, + bytes memory, + bytes memory + ) public view override returns (bool isAllowed) { + NFTGates memory nftGates_ = nftGates[slicerId][productId]; + + uint256 totalOwned; + unchecked { + for (uint256 i; i < nftGates_.gates.length;) { + NFTGate memory gate = nftGates_.gates[i]; + + if (gate.tokenType == TokenType.ERC1155) { + if (IERC1155(gate.nft).balanceOf(account, gate.id) >= gate.minQuantity) { + ++totalOwned; + } + } else if (IERC721(gate.nft).balanceOf(account) >= gate.minQuantity) { + ++totalOwned; + } + + if (totalOwned >= nftGates_.minOwned) return true; + + ++i; + } + } + } + + /** + * @inheritdoc OnchainAction + * @dev Sets the NFT gates for a product. + */ + function _setProductAction(uint256 slicerId, uint256 productId, bytes memory params) internal override { + (NFTGates memory nftGates_) = abi.decode(params, (NFTGates)); + + nftGates[slicerId][productId].minOwned = nftGates_.minOwned; + for (uint256 i = 0; i < nftGates_.gates.length; i++) { + nftGates[slicerId][productId].gates.push(nftGates_.gates[i]); + } + } + + /** + * @inheritdoc IOnchainAction + */ + function actionParamsSchema() external pure returns (string memory) { + return "(address nft,uint8 tokenType,uint80 id,uint8 minQuantity)[] nftGates,uint256 minOwned"; + } +} diff --git a/src/actions/NFTGated/types/NFTGate.sol b/src/actions/NFTGated/types/NFTGate.sol new file mode 100644 index 0000000..ff2a95c --- /dev/null +++ b/src/actions/NFTGated/types/NFTGate.sol @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +enum TokenType { + ERC721, + ERC1155 +} + +struct NFTGate { + address nft; + TokenType tokenType; + uint80 id; + uint8 minQuantity; +} + +struct NFTGates { + NFTGate[] gates; + uint256 minOwned; +} diff --git a/src/interfaces/IOnchainAction.sol b/src/interfaces/IOnchainAction.sol new file mode 100644 index 0000000..a8fe341 --- /dev/null +++ b/src/interfaces/IOnchainAction.sol @@ -0,0 +1,4 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.20; + +import "slice/interfaces/hooks/IOnchainAction.sol"; diff --git a/src/interfaces/IPricingStrategy.sol b/src/interfaces/IPricingStrategy.sol new file mode 100644 index 0000000..b779cdf --- /dev/null +++ b/src/interfaces/IPricingStrategy.sol @@ -0,0 +1,4 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.20; + +import "slice/interfaces/hooks/IPricingStrategy.sol"; diff --git a/src/interfaces/IProductOnchainAction.sol b/src/interfaces/IProductOnchainAction.sol new file mode 100644 index 0000000..6e9aa8a --- /dev/null +++ b/src/interfaces/IProductOnchainAction.sol @@ -0,0 +1,4 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.20; + +import "slice/interfaces/hooks/IProductOnchainAction.sol"; diff --git a/src/interfaces/IProductPricingStrategy.sol b/src/interfaces/IProductPricingStrategy.sol new file mode 100644 index 0000000..1b7e38d --- /dev/null +++ b/src/interfaces/IProductPricingStrategy.sol @@ -0,0 +1,4 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.20; + +import "slice/interfaces/hooks/IProductPricingStrategy.sol"; diff --git a/src/pricingStrategyActions/FirstForFree/FirstForFree.sol b/src/pricingStrategyActions/FirstForFree/FirstForFree.sol new file mode 100644 index 0000000..acd55e5 --- /dev/null +++ b/src/pricingStrategyActions/FirstForFree/FirstForFree.sol @@ -0,0 +1,157 @@ +// SPDX-License-Identifier: Unlicense +pragma solidity ^0.8.20; + +import {IERC721} from "@openzeppelin-4.8.0/interfaces/IERC721.sol"; +import {IERC1155} from "@openzeppelin-4.8.0/interfaces/IERC1155.sol"; +import {IOnchainAction} from "@/interfaces/IOnchainAction.sol"; +import { + IProductPricingStrategy, + IProductsModule, + PricingStrategyAction, + OnchainAction +} from "@/utils/PricingStrategyAction.sol"; +import {ProductParams, TokenCondition} from "./types/ProductParams.sol"; +import {TokenType} from "./types/TokenCondition.sol"; +import {ITokenERC1155} from "./utils/ITokenERC1155.sol"; + +/** + * @title FirstForFree + * @notice Discounts the first purchase of a product for free, based on conditions. + * @author Slice + */ +contract FirstForFree is PricingStrategyAction { + /*////////////////////////////////////////////////////////////// + MUTABLE STORAGE + //////////////////////////////////////////////////////////////*/ + + mapping(uint256 slicerId => mapping(uint256 productId => ProductParams price)) public usdcPrices; + mapping(address buyer => mapping(uint256 slicerId => uint256 purchases)) public purchases; + + /*////////////////////////////////////////////////////////////// + CONSTRUCTOR + //////////////////////////////////////////////////////////////*/ + + constructor(IProductsModule productsModuleAddress) PricingStrategyAction(productsModuleAddress) {} + + /*////////////////////////////////////////////////////////////// + CONFIGURATION + //////////////////////////////////////////////////////////////*/ + + /** + * @inheritdoc IProductPricingStrategy + * @notice Applies discount only for first N purchases on a slicer. + */ + function productPrice(uint256 slicerId, uint256 productId, address, uint256 quantity, address buyer, bytes memory) + public + view + override + returns (uint256 ethPrice, uint256 currencyPrice) + { + ProductParams memory productParams = usdcPrices[slicerId][productId]; + + if (_isEligible(buyer, productParams.eligibleTokens)) { + uint256 totalPurchases = purchases[buyer][slicerId]; + if (totalPurchases < productParams.freeUnits) { + unchecked { + uint256 freeUnitsLeft = productParams.freeUnits - totalPurchases; + if (quantity <= freeUnitsLeft) { + return (0, 0); + } else { + return (0, usdcPrices[slicerId][productId].usdcPrice * (quantity - freeUnitsLeft)); + } + } + } + } + + return (0, usdcPrices[slicerId][productId].usdcPrice * quantity); + } + /** + * @inheritdoc OnchainAction + * @notice Mint `quantity` NFTs to `account` on purchase. Keeps track of total purchases. + */ + + function _onProductPurchase( + uint256 slicerId, + uint256 productId, + address buyer, + uint256 quantity, + bytes memory, + bytes memory + ) internal override { + purchases[buyer][slicerId] += quantity; + + ProductParams memory productParams = usdcPrices[slicerId][productId]; + if (productParams.mintToken != address(0)) { + ITokenERC1155(productParams.mintToken).mintTo(buyer, productParams.mintTokenId, "", quantity); + } + } + + /** + * @inheritdoc OnchainAction + * @notice Sets the product parameters. + */ + function _setProductAction(uint256 slicerId, uint256 productId, bytes memory params) internal override { + ( + uint256 usdcPrice, + TokenCondition[] memory eligibleTokens, + address mintToken, + uint88 mintTokenId, + uint8 freeUnits + ) = abi.decode(params, (uint256, TokenCondition[], address, uint88, uint8)); + + ProductParams storage productParams = usdcPrices[slicerId][productId]; + + productParams.usdcPrice = usdcPrice; + productParams.mintToken = mintToken; + productParams.mintTokenId = mintTokenId; + productParams.freeUnits = freeUnits; + + // Remove all discount tokens + delete productParams.eligibleTokens; + + for (uint256 i = 0; i < eligibleTokens.length;) { + productParams.eligibleTokens.push(eligibleTokens[i]); + + unchecked { + ++i; + } + } + } + + /** + * @inheritdoc IOnchainAction + */ + function actionParamsSchema() external pure returns (string memory) { + return + "uint256 usdcPrice,(address tokenAddress,uint8 tokenType,uint88 tokenId,uint8 minQuantity)[] eligibleTokens,address mintToken,uint88 mintTokenId,uint8 freeUnits"; + } + + /*////////////////////////////////////////////////////////////// + INTERNAL FUNCTIONS + //////////////////////////////////////////////////////////////*/ + + function _isEligible(address buyer, TokenCondition[] memory eligibleTokens) + internal + view + returns (bool isEligible) + { + isEligible = eligibleTokens.length == 0; + if (!isEligible) { + TokenCondition memory tokenCondition; + for (uint256 i = 0; i < eligibleTokens.length;) { + tokenCondition = eligibleTokens[i]; + + isEligible = tokenCondition.tokenType == TokenType.ERC721 + ? IERC721(tokenCondition.tokenAddress).balanceOf(buyer) >= tokenCondition.minQuantity + : IERC1155(tokenCondition.tokenAddress).balanceOf(buyer, tokenCondition.tokenId) + >= tokenCondition.minQuantity; + + if (isEligible) break; + + unchecked { + ++i; + } + } + } + } +} diff --git a/src/pricingStrategyActions/FirstForFree/types/ProductParams.sol b/src/pricingStrategyActions/FirstForFree/types/ProductParams.sol new file mode 100644 index 0000000..f772b20 --- /dev/null +++ b/src/pricingStrategyActions/FirstForFree/types/ProductParams.sol @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: Unlicense +pragma solidity ^0.8.20; + +import {TokenCondition} from "./TokenCondition.sol"; + +struct ProductParams { + uint256 usdcPrice; + TokenCondition[] eligibleTokens; + address mintToken; + uint88 mintTokenId; + uint8 freeUnits; +} diff --git a/src/pricingStrategyActions/FirstForFree/types/TokenCondition.sol b/src/pricingStrategyActions/FirstForFree/types/TokenCondition.sol new file mode 100644 index 0000000..cfc4981 --- /dev/null +++ b/src/pricingStrategyActions/FirstForFree/types/TokenCondition.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: Unlicense +pragma solidity ^0.8.20; + +struct TokenCondition { + address tokenAddress; + TokenType tokenType; + uint80 tokenId; + uint8 minQuantity; +} + +enum TokenType { + ERC721, + ERC1155 +} diff --git a/src/pricingStrategyActions/FirstForFree/utils/ITokenERC1155.sol b/src/pricingStrategyActions/FirstForFree/utils/ITokenERC1155.sol new file mode 100644 index 0000000..a4a9dca --- /dev/null +++ b/src/pricingStrategyActions/FirstForFree/utils/ITokenERC1155.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: Unlicense +pragma solidity ^0.8.20; + +interface ITokenERC1155 { + /** + * @notice Lets an account with MINTER_ROLE mint an NFT. + * + * @param to The address to mint the NFT to. + * @param tokenId The tokenId of the NFTs to mint + * @param uri The URI to assign to the NFT. + * @param amount The number of copies of the NFT to mint. + * + */ + function mintTo(address to, uint256 tokenId, string calldata uri, uint256 amount) external; +} diff --git a/src/productActions/BaseCafe_2.sol b/src/productActions/BaseCafe_2.sol new file mode 100644 index 0000000..ec75872 --- /dev/null +++ b/src/productActions/BaseCafe_2.sol @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {IProductsModule, ProductOnchainAction} from "@/utils/ProductOnchainAction.sol"; + +/** + * @title Base Cafe - Slice onchain action + * @author Slice + */ +contract BaseCafe is ProductOnchainAction { + /*////////////////////////////////////////////////////////////// + IMMUTABLE STORAGE + //////////////////////////////////////////////////////////////*/ + + ITokenERC1155 public constant MINT_NFT_COLLECTION = ITokenERC1155(0x8485A580A9975deF42F8C7c5C63E9a0FF058561D); + uint256 public constant MINT_NFT_TOKEN_ID = 9; + + /*////////////////////////////////////////////////////////////// + CONSTRUCTOR + //////////////////////////////////////////////////////////////*/ + + constructor(IProductsModule productsModuleAddress, uint256 slicerId) + ProductOnchainAction(productsModuleAddress, slicerId) + {} + + /*////////////////////////////////////////////////////////////// + FUNCTIONS + //////////////////////////////////////////////////////////////*/ + + /** + * @inheritdoc ProductOnchainAction + * @notice Mint `quantity` NFTs to `account` on purchase + */ + function _onProductPurchase(uint256, uint256, address buyer, uint256 quantity, bytes memory, bytes memory) + internal + override + { + MINT_NFT_COLLECTION.mintTo(buyer, MINT_NFT_TOKEN_ID, "", quantity); + } +} + +interface ITokenERC1155 { + function mintTo(address to, uint256 tokenId, string calldata uri, uint256 amount) external; +} diff --git a/src/productActions/BaseGirlsScout.sol b/src/productActions/BaseGirlsScout.sol new file mode 100644 index 0000000..5c980f5 --- /dev/null +++ b/src/productActions/BaseGirlsScout.sol @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import {IProductsModule, ProductOnchainAction} from "@/utils/ProductOnchainAction.sol"; +import {Ownable} from "@openzeppelin-4.8.0/access/Ownable.sol"; +import {IERC1155} from "@openzeppelin-4.8.0/interfaces/IERC1155.sol"; + +/** + * @title Base Girls Scout - Slice onchain action + * @notice Mints Base Girls Scout NFTs to the buyer. + * @author Slice + */ +contract BaseGirlsScout_SliceHook is ProductOnchainAction, Ownable { + /*////////////////////////////////////////////////////////////// + IMMUTABLE STORAGE + //////////////////////////////////////////////////////////////*/ + + ITokenERC1155 public MINT_NFT_COLLECTION = ITokenERC1155(0x7A110890DF5D95CefdB0151143E595b755B7c9b7); + uint256 public MINT_NFT_TOKEN_ID = 1; + + /*////////////////////////////////////////////////////////////// + STORAGE + //////////////////////////////////////////////////////////////*/ + + mapping(uint256 slicerId => bool allowed) public allowedSlicerIds; + + /*////////////////////////////////////////////////////////////// + CONSTRUCTOR + //////////////////////////////////////////////////////////////*/ + + constructor(IProductsModule productsModuleAddress, uint256 slicerId) + ProductOnchainAction(productsModuleAddress, slicerId) + { + allowedSlicerIds[2217] = true; + allowedSlicerIds[2218] = true; + } + + /*////////////////////////////////////////////////////////////// + FUNCTIONS + //////////////////////////////////////////////////////////////*/ + + /** + * @inheritdoc ProductOnchainAction + * @notice Mint `quantity` NFTs to `account` on purchase + */ + function _onProductPurchase(uint256, uint256, address buyer, uint256 quantity, bytes memory, bytes memory) + internal + override + { + MINT_NFT_COLLECTION.mintTo(buyer, MINT_NFT_TOKEN_ID, "", quantity); + } + + /*////////////////////////////////////////////////////////////// + FUNCTIONS + //////////////////////////////////////////////////////////////*/ + + /** + * @notice Called by contract owner to set allowed slicer Ids. + */ + function setAllowedSlicerId(uint256 slicerId, bool allowed) external onlyOwner { + allowedSlicerIds[slicerId] = allowed; + } + + /** + * @notice Called by contract owner to set the mint token collection and token ID. + */ + function setMintTokenId(address collection, uint256 tokenId) external onlyOwner { + MINT_NFT_COLLECTION = ITokenERC1155(collection); + MINT_NFT_TOKEN_ID = tokenId; + } +} + +interface ITokenERC1155 { + function mintTo(address to, uint256 tokenId, string calldata uri, uint256 amount) external; +} diff --git a/src/productActions/README.md b/src/productActions/README.md new file mode 100644 index 0000000..318f486 --- /dev/null +++ b/src/productActions/README.md @@ -0,0 +1,5 @@ +# Product Actions + +This folder contains custom smart contracts used for specific products. + +These simpler, focused examples can be used as templates for creating your own product-specific onchain actions, when you're not looking for integration on Slice frontends. \ No newline at end of file diff --git a/test/ERC721Discount.sol b/test/ERC721Discount.sol index 286ed6b..473743f 100644 --- a/test/ERC721Discount.sol +++ b/test/ERC721Discount.sol @@ -12,7 +12,7 @@ import { DiscountParams, CurrencyParams, NFTType -} from "@/pricing-strategies/TieredDiscount/NFTDiscount/NFTDiscount.sol"; +} from "@/pricingStrategies/TieredDiscount/NFTDiscount/NFTDiscount.sol"; import {MockERC721} from "./mocks/MockERC721.sol"; import {MockERC1155} from "./mocks/MockERC1155.sol"; diff --git a/test/mocks/MockLinearVRGDAPrices.sol b/test/mocks/MockLinearVRGDAPrices.sol index 59e7b5a..6b8032b 100644 --- a/test/mocks/MockLinearVRGDAPrices.sol +++ b/test/mocks/MockLinearVRGDAPrices.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; -import "@/pricing-strategies/VRGDA/LinearVRGDAPrices.sol"; +import "@/pricingStrategies/VRGDA/LinearVRGDAPrices.sol"; contract MockLinearVRGDAPrices is LinearVRGDAPrices { constructor(IProductsModule productsModule) LinearVRGDAPrices(productsModule) {} diff --git a/test/mocks/MockLogisticVRGDAPrices.sol b/test/mocks/MockLogisticVRGDAPrices.sol index 3e3d1f9..fb267ef 100644 --- a/test/mocks/MockLogisticVRGDAPrices.sol +++ b/test/mocks/MockLogisticVRGDAPrices.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; -import "@/pricing-strategies/VRGDA/LogisticVRGDAPrices.sol"; +import "@/pricingStrategies/VRGDA/LogisticVRGDAPrices.sol"; contract MockLogisticVRGDAPrices is LogisticVRGDAPrices { constructor(IProductsModule productsModule) LogisticVRGDAPrices(productsModule) {} From 7ba5a7e1de6aec76e0dfbbde41b84599b9724b7a Mon Sep 17 00:00:00 2001 From: jj_ranalli Date: Thu, 26 Jun 2025 02:44:05 +0200 Subject: [PATCH 14/28] fix script --- README.md | 5 ++- script/Deploy.s.sol | 14 ++++----- script/ScriptUtils.sol | 61 +++++++++++++++++++++++++++++++++++++ script/WriteAddresses.s.sol | 4 --- 4 files changed, 70 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 5acb6ba..2763a59 100644 --- a/README.md +++ b/README.md @@ -35,11 +35,10 @@ You will need a copy of [Foundry](https://github.com/foundry-rs/foundry) install \ No newline at end of file diff --git a/script/Deploy.s.sol b/script/Deploy.s.sol index 7a02e82..450acd7 100644 --- a/script/Deploy.s.sol +++ b/script/Deploy.s.sol @@ -18,14 +18,14 @@ contract DeployScript is BaseScript, SetUpContractsList { } function _promptContractName() internal returns (string memory contractName) { - string memory prompt = "\nPricing strategies available to deploy:\n"; - string memory lastFolder = ""; + string memory prompt = "\nContracts available to deploy:\n"; + string memory lastTopFolder = ""; for (uint256 i = 0; i < contractNames.length; i++) { - (, string memory folder) = _getFolderName(contractNames[i].path); - if (i == 0 || keccak256(bytes(folder)) != keccak256(bytes(lastFolder))) { - prompt = string.concat(prompt, "\n"); - prompt = string.concat(prompt, folder, "\n"); - lastFolder = folder; + (string memory topFolderName,) = _getFolderName(contractNames[i].path); + // Print top-level folder if changed + if (i == 0 || keccak256(bytes(topFolderName)) != keccak256(bytes(lastTopFolder))) { + prompt = string.concat(prompt, "\n", topFolderName, "\n"); + lastTopFolder = topFolderName; } prompt = string.concat(prompt, " ", vm.toString(contractNames[i].id), ") ", contractNames[i].name, "\n"); } diff --git a/script/ScriptUtils.sol b/script/ScriptUtils.sol index 247e98c..b54ef7c 100644 --- a/script/ScriptUtils.sol +++ b/script/ScriptUtils.sol @@ -228,11 +228,72 @@ abstract contract SetUpContractsList is Script { } } + // Helper to check if a path is or is under a 'utils' folder + function _isExcludedPath(string memory path) internal pure returns (bool) { + bytes memory pathBytes = bytes(path); + bytes memory utilsBytes = bytes("/utils"); + for (uint256 i = 0; i + utilsBytes.length <= pathBytes.length; i++) { + bool matchFound = true; + for (uint256 j = 0; j < utilsBytes.length; j++) { + if (pathBytes[i + j] != utilsBytes[j]) { + matchFound = false; + break; + } + } + if (matchFound) { + uint256 afterIdx = i + utilsBytes.length; + if (afterIdx == pathBytes.length || pathBytes[afterIdx] == 0x2f) { + return true; + } + } + } + return false; + } + + // Helper to get the last segment of a path (folder or file name) + function _getLastPathSegment(string memory path) internal pure returns (string memory) { + bytes memory pathBytes = bytes(path); + uint256 lastSlash = 0; + for (uint256 i = 0; i < pathBytes.length; i++) { + if (pathBytes[i] == 0x2f) { + // '/' + lastSlash = i + 1; + } + } + if (lastSlash >= pathBytes.length) return ""; + bytes memory segment = new bytes(pathBytes.length - lastSlash); + for (uint256 i = 0; i < segment.length; i++) { + segment[i] = pathBytes[lastSlash + i]; + } + return string(segment); + } + function _recordContractsOnPath(string memory path) internal { + // Exclude any path that is or is under a 'utils' folder + if (_isExcludedPath(path)) { + return; + } VmSafe.DirEntry[] memory files = vm.readDir(path); + bool isTopLevel = keccak256(bytes(path)) == keccak256(bytes(CONTRACT_PATH)); for (uint256 i = 0; i < files.length; i++) { VmSafe.DirEntry memory file = files[i]; + // Exclude any file or directory under a 'utils' folder + if (_isExcludedPath(file.path)) { + continue; + } if (file.isDir) { + if (isTopLevel) { + string memory folderName = _getLastPathSegment(file.path); + // Only include specific top-level folders + if ( + keccak256(bytes(folderName)) != keccak256(bytes("internal")) + && keccak256(bytes(folderName)) != keccak256(bytes("actions")) + && keccak256(bytes(folderName)) != keccak256(bytes("pricingStrategies")) + && keccak256(bytes(folderName)) != keccak256(bytes("pricingStrategyActions")) + ) { + continue; + } + } _recordContractsOnPath(file.path); } else if (_endsWith(file.path, ".sol")) { string memory content = vm.readFile(file.path); diff --git a/script/WriteAddresses.s.sol b/script/WriteAddresses.s.sol index f17603f..bb52cbd 100644 --- a/script/WriteAddresses.s.sol +++ b/script/WriteAddresses.s.sol @@ -9,8 +9,4 @@ contract WriteAddressesScript is SetUpContractsList { function run(string memory contractName) external { writeAddressesJson(contractName); } - - function run() external { - writeAddressesJson("NFTDiscount"); - } } From 1bf831efa38e91beef968d948110a7905dccb224 Mon Sep 17 00:00:00 2001 From: jj_ranalli Date: Thu, 26 Jun 2025 12:55:48 +0200 Subject: [PATCH 15/28] add support for IERC165 --- README.md | 2 +- foundry.toml | 5 +---- src/actions/Allowlisted/Allowlisted.sol | 2 +- src/actions/ERC20Gated/types/ERC20Gate.sol | 2 +- src/actions/ERC20Mint/utils/ERC20Mint_BaseToken.sol | 2 +- src/actions/ERC721AMint/utils/ERC721Mint_BaseToken.sol | 2 +- src/actions/NFTGated/NFTGated.sol | 4 ++-- .../TieredDiscount/NFTDiscount/NFTDiscount.sol | 4 ++-- src/pricingStrategyActions/FirstForFree/FirstForFree.sol | 4 ++-- src/productActions/BaseGirlsScout.sol | 5 +++-- test/mocks/MockERC1155.sol | 2 +- test/mocks/MockERC721.sol | 2 +- 12 files changed, 17 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 2763a59..6c11b08 100644 --- a/README.md +++ b/README.md @@ -35,9 +35,9 @@ You will need a copy of [Foundry](https://github.com/foundry-rs/foundry) install \ No newline at end of file +Product-specific can be attached via the `custom` pricing strategy / onchain action, by passing the deployment address. \ No newline at end of file diff --git a/src/examples/README.md b/src/examples/README.md index 318f486..6861892 100644 --- a/src/examples/README.md +++ b/src/examples/README.md @@ -1,5 +1,135 @@ -# Product Actions +# Example Implementations -This folder contains custom smart contracts used for specific products. +This folder contains product-specific smart contract implementations that demonstrate how to use Slice hooks for real-world use cases. These examples can be used as templates for creating your own custom implementations, without focusing on reusability and integration with Slice. -These simpler, focused examples can be used as templates for creating your own product-specific onchain actions, when you're not looking for integration on Slice frontends. \ No newline at end of file +## Key Interfaces + +**IPricingStrategy**: +```solidity +interface IPricingStrategy { + function productPrice( + uint256 slicerId, + uint256 productId, + address currency, + uint256 quantity, + address buyer, + bytes memory data + ) external view returns (uint256 ethPrice, uint256 currencyPrice); +} +``` + +**IOnchainAction**: +```solidity +interface IOnchainAction { + function isPurchaseAllowed( + uint256 slicerId, + uint256 productId, + address account, + uint256 quantity, + bytes memory slicerCustomData, + bytes memory buyerCustomData + ) external view returns (bool); + + function onProductPurchase( + uint256 slicerId, + uint256 productId, + address account, + uint256 quantity, + bytes memory slicerCustomData, + bytes memory buyerCustomData + ) external payable; +} +``` + +## Base Contracts + +- **OnchainAction**: Add arbitrary requirements and/or custom logic after product purchase. +- **PricingStrategy**: Customize product pricing logic. +- **PricingStrategyAction**: Provide functionality of both Onchain Actions and Pricing Strategies + +## Key Differences from Registry Hooks + +Unlike the reusable hooks in `/hooks/`, these examples: +- Are tailored for specific products/projects +- Inherit directly from base contracts (`OnchainAction`, `PricingStrategy`) +- Don't implement `IHookRegistry` (not intended for Slice frontend integration) +- Serve as reference implementations and starting points + +## Available Examples + +### Actions + +- **[BaseCafe_2](./actions/BaseCafe_2.sol)**: Onchain action that mints an NFT to the buyer on every purchase. +- **[BaseGirlsScout](./actions/BaseGirlsScout.sol)**: Onchain action that mints Base Girls Scout NFTs to the buyer on every purchase. + +## Creating Custom Product-Specific Hooks + +### Onchain Action + +To create a custom product-specific onchain action: + +1. **Inherit from OnchainAction**: +```solidity +import {OnchainAction, IProductsModule} from "@/utils/OnchainAction.sol"; + +contract MyProductAction is OnchainAction { + constructor(IProductsModule productsModule, uint256 slicerId) + OnchainAction(productsModule, slicerId) {} +} +``` + +2. **Implement required functions**: +```solidity +function _onProductPurchase( + uint256 slicerId, + uint256 productId, + address account, + uint256 quantity, + bytes memory slicerCustomData, + bytes memory buyerCustomData +) internal override { + // Your custom logic here - mint NFTs, track purchases, etc. +} + +// Optional: Add purchase restrictions +function isPurchaseAllowed( + uint256 slicerId, + uint256 productId, + address account, + uint256 quantity, + bytes memory slicerCustomData, + bytes memory buyerCustomData +) public view override returns (bool) { + // Your eligibility logic here +} +``` + +### Pricing Strategy + +To create a custom product-specific pricing strategy: + +1. **Inherit from PricingStrategy**: +```solidity +import {PricingStrategy, IProductsModule} from "@/utils/PricingStrategy.sol"; + +contract MyProductAction is PricingStrategy { + constructor(IProductsModule productsModule, uint256 slicerId) + PricingStrategy(productsModule, slicerId) {} +} +``` + +2. **Implement required functions**: +```solidity +function productPrice(...) public view override returns (uint256 ethPrice, uint256 currencyPrice) { + // Your pricing logic here +} +``` + +## Using These Examples + +These examples show common patterns for: +- Product-specific NFT minting +- Integration with external contracts +- Onchain rewards + +Copy and modify these examples to create your own product-specific implementations. \ No newline at end of file diff --git a/src/hooks/actions/README.md b/src/hooks/actions/README.md new file mode 100644 index 0000000..9adb197 --- /dev/null +++ b/src/hooks/actions/README.md @@ -0,0 +1,83 @@ +# Onchain Actions + +Onchain actions are smart contracts that execute custom logic when products are purchased on Slice. They implement the `IOnchainAction` interface and can control purchase eligibility and perform actions after purchases. + +## Key Interface: IOnchainAction + +```solidity +interface IOnchainAction { + function isPurchaseAllowed( + uint256 slicerId, + uint256 productId, + address account, + uint256 quantity, + bytes memory slicerCustomData, + bytes memory buyerCustomData + ) external view returns (bool); + + function onProductPurchase( + uint256 slicerId, + uint256 productId, + address account, + uint256 quantity, + bytes memory slicerCustomData, + bytes memory buyerCustomData + ) external payable; +} +``` + +## Base Contract: RegistryOnchainAction + +All actions in this directory inherit from `RegistryOnchainAction`, which provides: +- Registry functionality for reusable hooks across multiple products +- Implementation of `IHookRegistry` for Slice frontend integration +- Base implementations for common patterns + +## Available Actions + +- **[Allowlisted](./Allowlisted/Allowlisted.sol)**: Onchain action registry for allowlist requirement. +- **[ERC20Gated](./ERC20Gated/ERC20Gated.sol)**: Onchain action registry for ERC20 token gating. +- **[ERC20Mint](./ERC20Mint/ERC20Mint.sol)**: Onchain action registry that mints ERC20 tokens to buyers. +- **[ERC721AMint](./ERC721AMint/ERC721Mint.sol)**: Onchain action registry that mints ERC721A tokens to buyers. +- **[NFTGated](./NFTGated/NFTGated.sol)**: Onchain action registry for NFT gating. + +## Creating Custom Actions + +To create a custom onchain action: + +1. **Inherit from RegistryOnchainAction**: +```solidity +import {RegistryOnchainAction, IProductsModule} from "@/utils/RegistryOnchainAction.sol"; + +contract MyAction is RegistryOnchainAction { + constructor(IProductsModule productsModule) + RegistryOnchainAction(productsModule) {} +} +``` + +2. **Implement required functions**: +```solidity +function isPurchaseAllowed(...) public view override returns (bool) { + // Your eligibility logic here +} + +function _onProductPurchase(...) internal override { + // Custom logic to execute on purchase +} + +function _configureProduct(uint256 slicerId, uint256 productId, bytes memory params) + internal override { + // Handle product configuration +} + +function paramsSchema() external pure override returns (string memory) { + return "uint256 param1,address param2"; // Your parameter schema +} +``` + +## Integration with Slice + +Actions that inherit from `RegistryOnchainAction` are automatically compatible with Slice frontends through the `IHookRegistry` interface, enabling: +- Product configuration via `configureProduct()` +- Parameter validation via `paramsSchema()` +- Automatic discovery and integration \ No newline at end of file diff --git a/src/hooks/pricing/README.md b/src/hooks/pricing/README.md new file mode 100644 index 0000000..2534bba --- /dev/null +++ b/src/hooks/pricing/README.md @@ -0,0 +1,69 @@ +# Pricing Strategies + +Pricing strategies are smart contracts that calculate dynamic prices for products on Slice. They implement the `IPricingStrategy` interface to provide custom pricing logic based on arbitrary factors and conditions. + +## Key Interface: IPricingStrategy + +```solidity +interface IPricingStrategy { + function productPrice( + uint256 slicerId, + uint256 productId, + address currency, + uint256 quantity, + address buyer, + bytes memory data + ) external view returns (uint256 ethPrice, uint256 currencyPrice); +} +``` + +## Base Contract: RegistryPricingStrategy + +All pricing strategies in this directory inherit from `RegistryPricingStrategy`, which provides: +- Registry functionality for reusable pricing across multiple products +- Implementation of `IHookRegistry` for Slice frontend integration +- Base implementations for common patterns + +## Available Strategies + +- **[TieredDiscount](./TieredDiscount/TieredDiscount.sol)**: Tiered discounts based on asset ownership +- **[LinearVRGDAPrices](./VRGDA/LinearVRGDAPrices/LinearVRGDAPrices.sol)**: VRGDA with a linear issuance curve - Price library with different params for each Slice product +- **[LogisticVRGDAPrices](./VRGDA/LogisticVRGDAPrices/LogisticVRGDAPrices.sol)**: VRGDA with a logistic issuance curve - Price library with different params for each Slice product +- **[VRGDAPrices](./VRGDA/VRGDAPrices.sol)**: Variable Rate Gradual Dutch Auction + +## Creating Custom Pricing Strategies + +To create a custom pricing strategy: + +1. **Inherit from RegistryPricingStrategy**: +```solidity +import {RegistryPricingStrategy, IProductsModule} from "@/utils/RegistryPricingStrategy.sol"; + +contract MyPricingStrategy is RegistryPricingStrategy { + constructor(IProductsModule productsModule) + RegistryPricingStrategy(productsModule) {} +} +``` + +2. **Implement required functions**: +```solidity +function productPrice(...) public view override returns (uint256 ethPrice, uint256 currencyPrice) { + // Your pricing logic here +} + +function _configureProduct(uint256 slicerId, uint256 productId, bytes memory params) + internal override { + // Handle product configuration +} + +function paramsSchema() external pure override returns (string memory) { + return "uint256 basePrice,uint256 multiplier"; // Your parameter schema +} +``` + +## Integration with Slice + +Pricing strategies that inherit from `RegistryPricingStrategy` are automatically compatible with Slice frontends through the `IHookRegistry` interface, enabling: +- Product configuration via `configureProduct()` +- Parameter validation via `paramsSchema()` +- Automatic discovery and integration \ No newline at end of file diff --git a/src/hooks/pricingActions/README.md b/src/hooks/pricingActions/README.md new file mode 100644 index 0000000..3a1165d --- /dev/null +++ b/src/hooks/pricingActions/README.md @@ -0,0 +1,98 @@ +# Pricing Strategy Actions + +Pricing strategy actions combine both pricing strategies and onchain actions in a single contract. They implement both `IPricingStrategy` and `IOnchainAction` interfaces, allowing them to calculate dynamic prices AND execute custom logic during purchases. + +## Key Interfaces + +**IPricingStrategy**: +```solidity +interface IPricingStrategy { + function productPrice( + uint256 slicerId, + uint256 productId, + address currency, + uint256 quantity, + address buyer, + bytes memory data + ) external view returns (uint256 ethPrice, uint256 currencyPrice); +} +``` + +**IOnchainAction**: +```solidity +interface IOnchainAction { + function isPurchaseAllowed( + uint256 slicerId, + uint256 productId, + address account, + uint256 quantity, + bytes memory slicerCustomData, + bytes memory buyerCustomData + ) external view returns (bool); + + function onProductPurchase( + uint256 slicerId, + uint256 productId, + address account, + uint256 quantity, + bytes memory slicerCustomData, + bytes memory buyerCustomData + ) external payable; +} +``` + +## Base Contract: RegistryPricingStrategyAction + +All pricing strategy actions inherit from `RegistryPricingStrategyAction`, which provides: +- Combined functionality of both pricing strategies and onchain actions +- Registry functionality for reusable hooks across multiple products +- Implementation of `IHookRegistry` for Slice frontend integration + +## Available Pricing Strategy Actions + +- **[FirstForFree](./FirstForFree/FirstForFree.sol)**: Discounts the first purchase of a product for free, based on conditions. + +## Creating Custom Pricing Strategy Actions + +To create a custom pricing strategy action: + +1. **Inherit from RegistryPricingStrategyAction**: +```solidity +import {RegistryPricingStrategyAction, IProductsModule} from "@/utils/RegistryPricingStrategyAction.sol"; + +contract MyPricingStrategyAction is RegistryPricingStrategyAction { + constructor(IProductsModule productsModule) + RegistryPricingStrategyAction(productsModule) {} +} +``` + +2. **Implement required functions**: +```solidity +function productPrice(...) public view override returns (uint256 ethPrice, uint256 currencyPrice) { + // Your pricing logic here +} + +function isPurchaseAllowed(...) public view override returns (bool) { + // Your eligibility logic here +} + +function _onProductPurchase(...) internal override { + // Custom logic to execute on purchase +} + +function _configureProduct(uint256 slicerId, uint256 productId, bytes memory params) + internal override { + // Handle product configuration +} + +function paramsSchema() external pure override returns (string memory) { + return "uint256 param1,address param2"; // Your parameter schema +} +``` + +## Integration with Slice + +Pricing strategy actions that inherit from `RegistryPricingStrategyAction` are automatically compatible with Slice frontends through the `IHookRegistry` interface, enabling: +- Product configuration via `configureProduct()` +- Parameter validation via `paramsSchema()` +- Automatic discovery and integration \ No newline at end of file From 7a8b270911edf0c373d855aeaa02718b181d6164 Mon Sep 17 00:00:00 2001 From: jj_ranalli Date: Mon, 14 Jul 2025 03:01:37 +0200 Subject: [PATCH 25/28] readme wip --- README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index f3019d4..b44f69a 100644 --- a/README.md +++ b/README.md @@ -55,10 +55,10 @@ Tailored implementations for individual products: ## Quick Start -**For reusable actions**: See detailed guides in [`/src/hooks/actions`](./src/hooks/actions) -**For reusable pricing strategies**: See detailed guides in [`/src/hooks/pricing`](./src/hooks/pricing) -**For reusable pricing strategy actions**: See detailed guides in [`/src/hooks/pricingActions`](./src/hooks/pricingActions) -**For product-specific hooks**: See implementation examples in [`/src/examples/`](./src/examples/) +- **For reusable actions**: See detailed guides in [`/src/hooks/actions`](./src/hooks/actions) +- **For reusable pricing strategies**: See detailed guides in [`/src/hooks/pricing`](./src/hooks/pricing) +- **For reusable pricing strategy actions**: See detailed guides in [`/src/hooks/pricingActions`](./src/hooks/pricingActions) +- **For product-specific hooks**: See implementation examples in [`/src/examples/`](./src/examples/) ## Development From 7d90df2315d9413af9dcebf3c8c1e65d67a7a191 Mon Sep 17 00:00:00 2001 From: jj_ranalli Date: Mon, 14 Jul 2025 03:05:01 +0200 Subject: [PATCH 26/28] fix --- soldeer.lock | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/soldeer.lock b/soldeer.lock index bc41d27..a6e1762 100644 --- a/soldeer.lock +++ b/soldeer.lock @@ -1,9 +1,9 @@ [[dependencies]] name = "@openzeppelin-contracts" -version = "4.8.0" -url = "https://soldeer-revisions.s3.amazonaws.com/@openzeppelin-contracts/4_8_0_22-01-2024_13:13:40_contracts.zip" -checksum = "932598a6426b76e315bb9fac536011eb21a76984015efe9e8167c4fc9d7e32a3" -integrity = "954367e8adec93f80c6e795012955706347cdb0359360e7c835e4dd29e5a9c2f" +version = "5.3.0" +url = "https://soldeer-revisions.s3.amazonaws.com/@openzeppelin-contracts/5_3_0_10-04-2025_10:51:50_contracts.zip" +checksum = "fa2bc3db351137c4d5eb32b738a814a541b78e87fbcbfeca825e189c4c787153" +integrity = "d69addf252dfe0688dcd893a7821cbee2421f8ce53d95ca0845a59530043cfd1" [[dependencies]] name = "erc721a" @@ -21,7 +21,7 @@ integrity = "9e60fdba82bc374df80db7f2951faff6467b9091873004a3d314cf0c084b3c7d" [[dependencies]] name = "slice" -version = "0.0.3" -url = "https://soldeer-revisions.s3.amazonaws.com/slice/0_0_3_23-06-2025_14:43:22_src.zip" -checksum = "4cab5dd357deb274edcc50b78da5e4d42f7f0bc1f7fcf33b44bd798b7c7d190d" -integrity = "45bd7975e13a29f8a71fcc54d30ced5675faa90a72b0a8094cf5acc94a512b86" +version = "0.0.4" +url = "https://soldeer-revisions.s3.amazonaws.com/slice/0_0_4_14-07-2025_00:59:56_src.zip" +checksum = "b71b5ab29290b4683269547cbe571044f7938e8940a3996be6da882534c29f9e" +integrity = "07e086edca2dff96ddc168abeea5e11d3d7aa0f0a964163d3de4c0d1b7acbb0c" From 7a79b5cd3c2018ef0c6efc33faa31cc5fcbbce6d Mon Sep 17 00:00:00 2001 From: jj_ranalli Date: Mon, 14 Jul 2025 03:09:55 +0200 Subject: [PATCH 27/28] fix --- .gas-snapshot | 82 +++++++++++-------- .../NFTDiscount/NFTDiscount.sol | 2 +- .../VRGDA/mocks/MockLinearVRGDAPrices.sol | 2 +- .../VRGDA/mocks/MockLogisticVRGDAPrices.sol | 2 +- 4 files changed, 50 insertions(+), 38 deletions(-) diff --git a/.gas-snapshot b/.gas-snapshot index 9662a11..9ad7a2e 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -1,35 +1,47 @@ -LinearVRGDATest:testAlwaystargetPriceInRightConditions(uint256) (runs: 256, μ: 14544, ~: 14312) -LinearVRGDATest:testPricingAdjustedByQuantity() (gas: 18856) -LinearVRGDATest:testPricingBasic() (gas: 10516) -LinearVRGDATest:testPricingMin() (gas: 10480) -LinearVRGDATest:testProductPriceErc20() (gas: 24191) -LinearVRGDATest:testProductPriceEth() (gas: 24201) -LinearVRGDATest:testProductPriceMultiple() (gas: 27514) -LinearVRGDATest:testSetMultiplePrices() (gas: 145200) -LinearVRGDATest:testTargetPrice() (gas: 11382) -LogisticVRGDATest:testAlwaysTargetPriceInRightConditions(uint256) (runs: 256, μ: 16762, ~: 16910) -LogisticVRGDATest:testGetTargetSaleTimeDoesNotRevertEarly() (gas: 6589) -LogisticVRGDATest:testGetTargetSaleTimeRevertsWhenExpected() (gas: 8982) -LogisticVRGDATest:testNoOverflowForAllTokens(uint256,uint256) (runs: 256, μ: 13362, ~: 13593) -LogisticVRGDATest:testNoOverflowForMostTokens(uint256,uint256) (runs: 256, μ: 13172, ~: 13179) -LogisticVRGDATest:testPricingAdjustedByQuantity() (gas: 23932) -LogisticVRGDATest:testPricingBasic() (gas: 11432) -LogisticVRGDATest:testPricingMin() (gas: 11408) -LogisticVRGDATest:testProductPriceErc20() (gas: 26192) -LogisticVRGDATest:testProductPriceEth() (gas: 26203) -LogisticVRGDATest:testProductPriceMultiple() (gas: 32117) -LogisticVRGDATest:testSetMultiplePrices() (gas: 153272) -LogisticVRGDATest:testTargetPrice() (gas: 13278) -LogisticVRGDATest:test_RevertOverflow_BeyondLimitTokens(uint256,uint256) (runs: 256, μ: 14914, ~: 14902) -NFTDiscountTest:testDeploy() (gas: 5191) -NFTDiscountTest:testProductPrice__HigherDiscount() (gas: 222859) -NFTDiscountTest:testProductPrice__MinQuantity() (gas: 150773) -NFTDiscountTest:testProductPrice__MultipleBoughtQuantity() (gas: 114052) -NFTDiscountTest:testProductPrice__NotNFTOwner() (gas: 110898) -NFTDiscountTest:testProductPrice__Relative() (gas: 111135) -NFTDiscountTest:testSetProductPrice__ERC1155() (gas: 160924) -NFTDiscountTest:testSetProductPrice__ERC20() (gas: 110610) -NFTDiscountTest:testSetProductPrice__ETH() (gas: 110633) -NFTDiscountTest:testSetProductPrice__Edit_Add() (gas: 243646) -NFTDiscountTest:testSetProductPrice__Edit_Remove() (gas: 224335) -NFTDiscountTest:testSetProductPrice__MultipleCurrencies() (gas: 194142) \ No newline at end of file +LinearVRGDACorrectnessTest:testParamsSchema() (gas: 9581) +LinearVRGDACorrectnessTest:testSetup_HookInitialized() (gas: 5671) +LinearVRGDACorrectnessTest:testSupportsInterface_RegistryPricingStrategy() (gas: 9685) +LinearVRGDATest:testAlwaystargetPriceInRightConditions(uint256) (runs: 256, μ: 14604, ~: 14369) +LinearVRGDATest:testParamsSchema() (gas: 9564) +LinearVRGDATest:testPricingAdjustedByQuantity() (gas: 18788) +LinearVRGDATest:testPricingBasic() (gas: 10478) +LinearVRGDATest:testPricingMin() (gas: 10499) +LinearVRGDATest:testProductPriceErc20() (gas: 26425) +LinearVRGDATest:testProductPriceEth() (gas: 26435) +LinearVRGDATest:testProductPriceMultiple() (gas: 29695) +LinearVRGDATest:testSetMultiplePrices() (gas: 171215) +LinearVRGDATest:testSetup_HookInitialized() (gas: 5715) +LinearVRGDATest:testSupportsInterface_RegistryPricingStrategy() (gas: 9684) +LinearVRGDATest:testTargetPrice() (gas: 11418) +LogisticVRGDATest:testAlwaysTargetPriceInRightConditions(uint256) (runs: 256, μ: 16809, ~: 16994) +LogisticVRGDATest:testGetTargetSaleTimeDoesNotRevertEarly() (gas: 6630) +LogisticVRGDATest:testGetTargetSaleTimeRevertsWhenExpected() (gas: 8997) +LogisticVRGDATest:testNoOverflowForAllTokens(uint256,uint256) (runs: 256, μ: 13361, ~: 13589) +LogisticVRGDATest:testNoOverflowForMostTokens(uint256,uint256) (runs: 256, μ: 13212, ~: 13240) +LogisticVRGDATest:testParamsSchema() (gas: 9542) +LogisticVRGDATest:testPricingAdjustedByQuantity() (gas: 23930) +LogisticVRGDATest:testPricingBasic() (gas: 11473) +LogisticVRGDATest:testPricingMin() (gas: 11482) +LogisticVRGDATest:testProductPriceErc20() (gas: 28392) +LogisticVRGDATest:testProductPriceEth() (gas: 28425) +LogisticVRGDATest:testProductPriceMultiple() (gas: 34309) +LogisticVRGDATest:testSetMultiplePrices() (gas: 181254) +LogisticVRGDATest:testSetup_HookInitialized() (gas: 5693) +LogisticVRGDATest:testSupportsInterface_RegistryPricingStrategy() (gas: 9752) +LogisticVRGDATest:testTargetPrice() (gas: 13363) +LogisticVRGDATest:test_RevertOverflow_BeyondLimitTokens(uint256,uint256) (runs: 256, μ: 14963, ~: 14978) +NFTDiscountTest:testDeploy() (gas: 5246) +NFTDiscountTest:testParamsSchema() (gas: 9870) +NFTDiscountTest:testProductPrice__HigherDiscount() (gas: 226072) +NFTDiscountTest:testProductPrice__MinQuantity() (gas: 153469) +NFTDiscountTest:testProductPrice__MultipleBoughtQuantity() (gas: 116886) +NFTDiscountTest:testProductPrice__NotNFTOwner() (gas: 113731) +NFTDiscountTest:testProductPrice__Relative() (gas: 113994) +NFTDiscountTest:testSetProductPrice__ERC1155() (gas: 163895) +NFTDiscountTest:testSetProductPrice__ERC20() (gas: 113514) +NFTDiscountTest:testSetProductPrice__ETH() (gas: 113470) +NFTDiscountTest:testSetProductPrice__Edit_Add() (gas: 249711) +NFTDiscountTest:testSetProductPrice__Edit_Remove() (gas: 230421) +NFTDiscountTest:testSetProductPrice__MultipleCurrencies() (gas: 198172) +NFTDiscountTest:testSetup_HookInitialized() (gas: 5715) +NFTDiscountTest:testSupportsInterface_RegistryPricingStrategy() (gas: 9705) \ No newline at end of file diff --git a/test/pricingStrategies/NFTDiscount/NFTDiscount.sol b/test/pricingStrategies/NFTDiscount/NFTDiscount.sol index 088882e..3aff126 100644 --- a/test/pricingStrategies/NFTDiscount/NFTDiscount.sol +++ b/test/pricingStrategies/NFTDiscount/NFTDiscount.sol @@ -11,7 +11,7 @@ import { DiscountParams, CurrencyParams, NFTType -} from "@/pricingStrategies/TieredDiscount/NFTDiscount/NFTDiscount.sol"; +} from "@/hooks/pricing/TieredDiscount/NFTDiscount/NFTDiscount.sol"; import {MockERC721} from "./mocks/MockERC721.sol"; import {MockERC1155} from "./mocks/MockERC1155.sol"; diff --git a/test/pricingStrategies/VRGDA/mocks/MockLinearVRGDAPrices.sol b/test/pricingStrategies/VRGDA/mocks/MockLinearVRGDAPrices.sol index ee49e89..61226a7 100644 --- a/test/pricingStrategies/VRGDA/mocks/MockLinearVRGDAPrices.sol +++ b/test/pricingStrategies/VRGDA/mocks/MockLinearVRGDAPrices.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; -import "@/pricingStrategies/VRGDA/LinearVRGDAPrices/LinearVRGDAPrices.sol"; +import "@/hooks/pricing/VRGDA/LinearVRGDAPrices/LinearVRGDAPrices.sol"; contract MockLinearVRGDAPrices is LinearVRGDAPrices { constructor(IProductsModule productsModule) LinearVRGDAPrices(productsModule) {} diff --git a/test/pricingStrategies/VRGDA/mocks/MockLogisticVRGDAPrices.sol b/test/pricingStrategies/VRGDA/mocks/MockLogisticVRGDAPrices.sol index 96d436a..0f7d0f7 100644 --- a/test/pricingStrategies/VRGDA/mocks/MockLogisticVRGDAPrices.sol +++ b/test/pricingStrategies/VRGDA/mocks/MockLogisticVRGDAPrices.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; -import "@/pricingStrategies/VRGDA/LogisticVRGDAPrices/LogisticVRGDAPrices.sol"; +import "@/hooks/pricing/VRGDA/LogisticVRGDAPrices/LogisticVRGDAPrices.sol"; contract MockLogisticVRGDAPrices is LogisticVRGDAPrices { constructor(IProductsModule productsModule) LogisticVRGDAPrices(productsModule) {} From b9aeafc35a3584b7ca7ca925a09e15adc891d093 Mon Sep 17 00:00:00 2001 From: jj_ranalli Date: Mon, 14 Jul 2025 19:36:30 +0200 Subject: [PATCH 28/28] update openzeppelin dep version --- .gas-snapshot | 10 +++++----- README.md | 15 ++++++++++++++- foundry.toml | 3 +-- soldeer.lock | 8 ++++---- src/examples/actions/BaseGirlsScout.sol | 6 +++--- src/hooks/actions/Allowlisted/Allowlisted.sol | 2 +- src/hooks/actions/ERC20Gated/types/ERC20Gate.sol | 2 +- .../ERC20Mint/utils/ERC20Mint_BaseToken.sol | 2 +- .../ERC721AMint/utils/ERC721Mint_BaseToken.sol | 2 +- src/hooks/actions/NFTGated/NFTGated.sol | 4 ++-- .../TieredDiscount/NFTDiscount/NFTDiscount.sol | 4 ++-- .../pricingActions/FirstForFree/FirstForFree.sol | 4 ++-- .../NFTDiscount/mocks/MockERC1155.sol | 2 +- .../NFTDiscount/mocks/MockERC721.sol | 2 +- 14 files changed, 39 insertions(+), 27 deletions(-) diff --git a/.gas-snapshot b/.gas-snapshot index 9ad7a2e..d7386f6 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -32,16 +32,16 @@ LogisticVRGDATest:testTargetPrice() (gas: 13363) LogisticVRGDATest:test_RevertOverflow_BeyondLimitTokens(uint256,uint256) (runs: 256, μ: 14963, ~: 14978) NFTDiscountTest:testDeploy() (gas: 5246) NFTDiscountTest:testParamsSchema() (gas: 9870) -NFTDiscountTest:testProductPrice__HigherDiscount() (gas: 226072) -NFTDiscountTest:testProductPrice__MinQuantity() (gas: 153469) +NFTDiscountTest:testProductPrice__HigherDiscount() (gas: 226170) +NFTDiscountTest:testProductPrice__MinQuantity() (gas: 153567) NFTDiscountTest:testProductPrice__MultipleBoughtQuantity() (gas: 116886) NFTDiscountTest:testProductPrice__NotNFTOwner() (gas: 113731) NFTDiscountTest:testProductPrice__Relative() (gas: 113994) -NFTDiscountTest:testSetProductPrice__ERC1155() (gas: 163895) +NFTDiscountTest:testSetProductPrice__ERC1155() (gas: 163762) NFTDiscountTest:testSetProductPrice__ERC20() (gas: 113514) NFTDiscountTest:testSetProductPrice__ETH() (gas: 113470) -NFTDiscountTest:testSetProductPrice__Edit_Add() (gas: 249711) -NFTDiscountTest:testSetProductPrice__Edit_Remove() (gas: 230421) +NFTDiscountTest:testSetProductPrice__Edit_Add() (gas: 249809) +NFTDiscountTest:testSetProductPrice__Edit_Remove() (gas: 230519) NFTDiscountTest:testSetProductPrice__MultipleCurrencies() (gas: 198172) NFTDiscountTest:testSetup_HookInitialized() (gas: 5715) NFTDiscountTest:testSupportsInterface_RegistryPricingStrategy() (gas: 9705) \ No newline at end of file diff --git a/README.md b/README.md index b44f69a..5b8e76d 100644 --- a/README.md +++ b/README.md @@ -74,4 +74,17 @@ Requires [Foundry](https://book.getfoundry.sh/getting-started/installation). Registry hooks automatically integrate with Slice frontends through the `IHookRegistry` interface. -Product-specific can be attached via the `custom` pricing strategy / onchain action, by passing the deployment address. \ No newline at end of file +Product-specific can be attached via the `custom` pricing strategy / onchain action, by passing the deployment address. + +## Testing + +## Contributing + + + \ No newline at end of file diff --git a/foundry.toml b/foundry.toml index b27b632..531376e 100644 --- a/foundry.toml +++ b/foundry.toml @@ -8,7 +8,6 @@ libs = ["dependencies", "../core/src", "../core/dependencies"] fs_permissions = [{ access = "read", path = "./src"}, { access= "read", path = "./broadcast/Deploy.s.sol/8453/run-latest.json"}, { access = "read-write", path = "./deployments"}, { access = "read", path = "./out"}] remappings = [ "slice/=dependencies/slice-0.0.4/", - "@openzeppelin-5.3.0/=dependencies/@openzeppelin-contracts-5.3.0/", "@openzeppelin-4.8.0/=dependencies/@openzeppelin-contracts-4.8.0/", "@erc721a/=dependencies/erc721a-4.3.0/contracts/", "forge-std/=dependencies/forge-std-1.9.7/src/", @@ -46,6 +45,6 @@ remappings_regenerate = false [dependencies] slice = "0.0.4" forge-std = "1.9.7" -"@openzeppelin-contracts" = "5.3.0" +"@openzeppelin-contracts" = "4.8.0" erc721a = "4.3.0" diff --git a/soldeer.lock b/soldeer.lock index a6e1762..2b1717e 100644 --- a/soldeer.lock +++ b/soldeer.lock @@ -1,9 +1,9 @@ [[dependencies]] name = "@openzeppelin-contracts" -version = "5.3.0" -url = "https://soldeer-revisions.s3.amazonaws.com/@openzeppelin-contracts/5_3_0_10-04-2025_10:51:50_contracts.zip" -checksum = "fa2bc3db351137c4d5eb32b738a814a541b78e87fbcbfeca825e189c4c787153" -integrity = "d69addf252dfe0688dcd893a7821cbee2421f8ce53d95ca0845a59530043cfd1" +version = "4.8.0" +url = "https://soldeer-revisions.s3.amazonaws.com/@openzeppelin-contracts/4_8_0_22-01-2024_13:13:40_contracts.zip" +checksum = "932598a6426b76e315bb9fac536011eb21a76984015efe9e8167c4fc9d7e32a3" +integrity = "954367e8adec93f80c6e795012955706347cdb0359360e7c835e4dd29e5a9c2f" [[dependencies]] name = "erc721a" diff --git a/src/examples/actions/BaseGirlsScout.sol b/src/examples/actions/BaseGirlsScout.sol index b723232..a64517e 100644 --- a/src/examples/actions/BaseGirlsScout.sol +++ b/src/examples/actions/BaseGirlsScout.sol @@ -2,8 +2,8 @@ pragma solidity ^0.8.20; import {IProductsModule, OnchainAction} from "@/utils/OnchainAction.sol"; -import {Ownable} from "@openzeppelin-5.3.0/access/Ownable.sol"; -import {IERC1155} from "@openzeppelin-5.3.0/interfaces/IERC1155.sol"; +import {Ownable} from "@openzeppelin-4.8.0/access/Ownable.sol"; +import {IERC1155} from "@openzeppelin-4.8.0/interfaces/IERC1155.sol"; /** * @title BaseGirlsScout @@ -30,7 +30,7 @@ contract BaseGirlsScout is OnchainAction, Ownable { constructor(IProductsModule productsModuleAddress, uint256 slicerId) OnchainAction(productsModuleAddress, slicerId) - Ownable(msg.sender) + Ownable() { allowedSlicerIds[2217] = true; allowedSlicerIds[2218] = true; diff --git a/src/hooks/actions/Allowlisted/Allowlisted.sol b/src/hooks/actions/Allowlisted/Allowlisted.sol index cfd0f9f..cadb88d 100644 --- a/src/hooks/actions/Allowlisted/Allowlisted.sol +++ b/src/hooks/actions/Allowlisted/Allowlisted.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; -import {MerkleProof} from "@openzeppelin-5.3.0/utils/cryptography/MerkleProof.sol"; +import {MerkleProof} from "@openzeppelin-4.8.0/utils/cryptography/MerkleProof.sol"; import { IProductsModule, RegistryOnchainAction, diff --git a/src/hooks/actions/ERC20Gated/types/ERC20Gate.sol b/src/hooks/actions/ERC20Gated/types/ERC20Gate.sol index 706ad3f..9b75f39 100644 --- a/src/hooks/actions/ERC20Gated/types/ERC20Gate.sol +++ b/src/hooks/actions/ERC20Gated/types/ERC20Gate.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; -import {IERC20} from "@openzeppelin-5.3.0/interfaces/IERC20.sol"; +import {IERC20} from "@openzeppelin-4.8.0/interfaces/IERC20.sol"; struct ERC20Gate { IERC20 erc20; diff --git a/src/hooks/actions/ERC20Mint/utils/ERC20Mint_BaseToken.sol b/src/hooks/actions/ERC20Mint/utils/ERC20Mint_BaseToken.sol index bd57178..15d873c 100644 --- a/src/hooks/actions/ERC20Mint/utils/ERC20Mint_BaseToken.sol +++ b/src/hooks/actions/ERC20Mint/utils/ERC20Mint_BaseToken.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; -import {ERC20} from "@openzeppelin-5.3.0/token/ERC20/ERC20.sol"; +import {ERC20} from "@openzeppelin-4.8.0/token/ERC20/ERC20.sol"; /** * @title ERC20Mint_BaseToken diff --git a/src/hooks/actions/ERC721AMint/utils/ERC721Mint_BaseToken.sol b/src/hooks/actions/ERC721AMint/utils/ERC721Mint_BaseToken.sol index bc55683..b43f474 100644 --- a/src/hooks/actions/ERC721AMint/utils/ERC721Mint_BaseToken.sol +++ b/src/hooks/actions/ERC721AMint/utils/ERC721Mint_BaseToken.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.20; import {ERC721A} from "@erc721a/ERC721A.sol"; -import {IERC2981, IERC165} from "@openzeppelin-5.3.0/interfaces/IERC2981.sol"; +import {IERC2981, IERC165} from "@openzeppelin-4.8.0/interfaces/IERC2981.sol"; uint256 constant MAX_ROYALTY = 10_000; diff --git a/src/hooks/actions/NFTGated/NFTGated.sol b/src/hooks/actions/NFTGated/NFTGated.sol index 780e652..15d195b 100644 --- a/src/hooks/actions/NFTGated/NFTGated.sol +++ b/src/hooks/actions/NFTGated/NFTGated.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; -import {IERC721} from "@openzeppelin-5.3.0/interfaces/IERC721.sol"; -import {IERC1155} from "@openzeppelin-5.3.0/interfaces/IERC1155.sol"; +import {IERC721} from "@openzeppelin-4.8.0/interfaces/IERC721.sol"; +import {IERC1155} from "@openzeppelin-4.8.0/interfaces/IERC1155.sol"; import { IProductsModule, RegistryOnchainAction, diff --git a/src/hooks/pricing/TieredDiscount/NFTDiscount/NFTDiscount.sol b/src/hooks/pricing/TieredDiscount/NFTDiscount/NFTDiscount.sol index dd1d750..5ac9380 100644 --- a/src/hooks/pricing/TieredDiscount/NFTDiscount/NFTDiscount.sol +++ b/src/hooks/pricing/TieredDiscount/NFTDiscount/NFTDiscount.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; -import {IERC721} from "@openzeppelin-5.3.0/token/ERC721/IERC721.sol"; -import {IERC1155} from "@openzeppelin-5.3.0/token/ERC1155/IERC1155.sol"; +import {IERC721} from "@openzeppelin-4.8.0/token/ERC721/IERC721.sol"; +import {IERC1155} from "@openzeppelin-4.8.0/token/ERC1155/IERC1155.sol"; import {HookRegistry, IHookRegistry, IProductsModule} from "@/utils/RegistryPricingStrategy.sol"; import {DiscountParams, ProductDiscounts, DiscountType, TieredDiscount, NFTType} from "../TieredDiscount.sol"; import {CurrencyParams} from "../types/CurrencyParams.sol"; diff --git a/src/hooks/pricingActions/FirstForFree/FirstForFree.sol b/src/hooks/pricingActions/FirstForFree/FirstForFree.sol index cd14f59..8de82ed 100644 --- a/src/hooks/pricingActions/FirstForFree/FirstForFree.sol +++ b/src/hooks/pricingActions/FirstForFree/FirstForFree.sol @@ -1,8 +1,8 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; -import {IERC721} from "@openzeppelin-5.3.0/interfaces/IERC721.sol"; -import {IERC1155} from "@openzeppelin-5.3.0/interfaces/IERC1155.sol"; +import {IERC721} from "@openzeppelin-4.8.0/interfaces/IERC721.sol"; +import {IERC1155} from "@openzeppelin-4.8.0/interfaces/IERC1155.sol"; import {IOnchainAction} from "@/interfaces/IOnchainAction.sol"; import { IPricingStrategy, diff --git a/test/pricingStrategies/NFTDiscount/mocks/MockERC1155.sol b/test/pricingStrategies/NFTDiscount/mocks/MockERC1155.sol index 9a3f011..eeb4383 100644 --- a/test/pricingStrategies/NFTDiscount/mocks/MockERC1155.sol +++ b/test/pricingStrategies/NFTDiscount/mocks/MockERC1155.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; -import {ERC1155} from "@openzeppelin-5.3.0/token/ERC1155/ERC1155.sol"; +import {ERC1155} from "@openzeppelin-4.8.0/token/ERC1155/ERC1155.sol"; contract MockERC1155 is ERC1155 { uint256 public constant tokenId = 1; diff --git a/test/pricingStrategies/NFTDiscount/mocks/MockERC721.sol b/test/pricingStrategies/NFTDiscount/mocks/MockERC721.sol index 8a77d60..c3b4cc4 100644 --- a/test/pricingStrategies/NFTDiscount/mocks/MockERC721.sol +++ b/test/pricingStrategies/NFTDiscount/mocks/MockERC721.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; -import {ERC721} from "@openzeppelin-5.3.0/token/ERC721/ERC721.sol"; +import {ERC721} from "@openzeppelin-4.8.0/token/ERC721/ERC721.sol"; contract MockERC721 is ERC721 { uint256 public tokenId;