Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 1 addition & 3 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,4 @@ RPC_URL_BASE_GOERLI=

ETHERSCAN_KEY=

PRIVATE_KEY=
SALT_VRGDA_LOGISTIC=
SALT_VRGDA_LINEAR=
PRIVATE_KEY=
93 changes: 47 additions & 46 deletions .gas-snapshot
Original file line number Diff line number Diff line change
@@ -1,46 +1,47 @@
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:testSetMultiplePrices() (gas: 145200)
LinearVRGDATest:testTargetPrice() (gas: 11316)
LogisticVRGDATest:testAlwaysTargetPriceInRightConditions(uint256) (runs: 258, μ: 16740, ~: 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: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:testTargetPrice() (gas: 13278)
LogisticVRGDATest:test_RevertOverflow_BeyondLimitTokens(uint256,uint256) (runs: 258, μ: 14917, ~: 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)
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: 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: 163762)
NFTDiscountTest:testSetProductPrice__ERC20() (gas: 113514)
NFTDiscountTest:testSetProductPrice__ETH() (gas: 113470)
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)
22 changes: 13 additions & 9 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,28 @@ 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
with:
version: stable

- name: Install dependencies
run: forge install
run: forge soldeer install

- name: Check contract sizes
run: forge build --sizes
- name: Show Forge version
run: forge --version

- name: Check gas snapshots
run: forge snapshot --check
- name: Build
run: forge build
env:
FOUNDRY_PROFILE: ci

- name: Run tests
run: forge test -vvv
run: forge test --isolate -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' }}
FOUNDRY_PROFILE: ci
FORGE_SNAPSHOT_CHECK: true
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ broadcast/
cache/
out/
.env

__pycache__


# Soldeer
/dependencies
remappings.txt
src/**/internal/
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -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
Expand Down
94 changes: 75 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,34 +1,90 @@
# Slice pricing strategies
# Slice Hooks

This repo contains custom pricing strategies for products sold on [Slice](https://slice.so).
Smart contracts for creating custom pricing strategies and onchain actions for [Slice](https://slice.so) products. Hooks enable dynamic pricing, purchase restrictions, rewards, and other custom behaviors when products are bought.

Each strategy inherits the [ISliceProductPrice](/src/Slice/interfaces/utils/ISliceProductPrice.sol) interface and serves two main purposes:
## Repository Structure

- Allow a product owner to set price params for a product via `setProductPrice`;
- Return product price via `productPrice`;
```
src/
├── hooks/ # Reusable hooks with registry support
│ ├── actions/ # Onchain actions (gating, rewards, etc.)
│ ├── pricing/ # Pricing strategies (NFT discounts, VRGDA, etc.)
│ └── pricingActions/ # Combined pricing + action hooks
├── examples/ # Product-specific reference implementations
├── interfaces/ # Core hook interfaces
└── utils/ # Base contracts and utilities
```

## Strategies
## Core Concepts

### VRGDA
Slice hooks are built around three main interfaces:

Variable Rate Gradual Dutch Auctions. Read the [whitepaper here](https://www.paradigm.xyz/2022/08/vrgda).
- **[`IOnchainAction`](./src/interfaces/IOnchainAction.sol)**: Execute custom logic during purchases (eligibility checks, rewards, etc.)
- **[`IPricingStrategy`](./src/interfaces/IPricingStrategy.sol)**: Calculate dynamic prices for products
- **[`IHookRegistry`](./src/interfaces/IHookRegistry.sol)**: Enable reusable hooks across multiple products with frontend integration

Slice-specific implementations modified from https://github.com/transmissions11/VRGDAs:
## Hook Types

- [Linear VRGDA](/src/VRGDA/LinearVRGDAPrices.sol)
- [Logistic VRGDA](/src/VRGDA/LogisticVRGDAPrices.sol)
### Registry Hooks (Reusable)

### ERC721 Gated Discount
Deploy once, use across multiple products with frontend integration:

A discount strategy that allows a product owner to set a discount for a product if the buyer owns a specific ERC721 token.
- **[Actions](./src/hooks/actions/)**: See available onchain actions and implementation guide
- **[Pricing](./src/hooks/pricing/)**: See available pricing strategies and implementation guide
- **[Pricing Actions](./src/hooks/pricingActions/)**: See combined pricing + action hooks

- [ERC721 Gated Discount](/src/ERC721GatedDiscount/ERC721GatedDiscount.sol)
### Product-Specific Hooks

## Contributing
Tailored implementations for individual products:

- **[Examples](./src/examples/)**: See real-world implementations and creation guide

## Base Contracts

### Registry (Reusable):

- **`RegistryOnchainAction`**: Base for reusable onchain actions
- **`RegistryPricingStrategy`**: Base for reusable pricing strategies
- **`RegistryPricingStrategyAction`**: Base for combined pricing + action hooks

### Product-Specific

- **`OnchainAction`**: Base for simple onchain actions
- **`PricingStrategy`**: Base for simple pricing strategies
- **`PricingStrategyAction`**: Base for combined hooks

## 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/)

```sh
cp .env.example .env
forge install
## Development

```bash
forge soldeer install # Install dependencies
forge test # Run tests
forge build # Build
```

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.
Requires [Foundry](https://book.getfoundry.sh/getting-started/installation).

## Integration

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.

## Testing

## Contributing


<!-- TODO:
- update openzeppelin dependency to latest (after core is upgraded to latest)

- add testing and contributing guidelines this readme
- finalize tests

-->
7 changes: 0 additions & 7 deletions TODO.md

This file was deleted.

91 changes: 3 additions & 88 deletions deployments/addresses.json
Original file line number Diff line number Diff line change
@@ -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": {}
}
Loading