@zoralabs/coins 2.1.2 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build$colon$js.log +152 -0
- package/CHANGELOG.md +93 -0
- package/README.md +4 -0
- package/abis/BaseCoin.json +26 -5
- package/abis/BaseTest.json +2 -7
- package/abis/ContentCoin.json +26 -5
- package/abis/CreatorCoin.json +30 -9
- package/abis/FeeEstimatorHook.json +94 -6
- package/abis/ICoin.json +26 -0
- package/abis/ICoinV3.json +26 -0
- package/abis/ICreatorCoin.json +39 -0
- package/abis/IERC721.json +36 -36
- package/abis/IHasCoinType.json +15 -0
- package/abis/IHasTotalSupplyForPositions.json +15 -0
- package/abis/{LiquidityMigrationReceiver.json → IUpgradeableDestinationV4HookWithUpdateableFee.json} +10 -18
- package/abis/IZoraFactory.json +121 -0
- package/abis/IZoraHookRegistry.json +188 -0
- package/abis/VmContractHelper226.json +233 -0
- package/abis/ZoraFactoryImpl.json +101 -6
- package/abis/ZoraHookRegistry.json +375 -0
- package/abis/{CreatorCoinHook.json → ZoraV4CoinHook.json} +95 -2
- package/addresses/8453.json +6 -5
- package/audits/report-cantinacode-zora-0827.pdf +3498 -4
- package/dist/index.cjs +93 -13
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +93 -13
- package/dist/index.js.map +1 -1
- package/dist/wagmiGenerated.d.ts +144 -22
- package/dist/wagmiGenerated.d.ts.map +1 -1
- package/foundry.toml +4 -1
- package/package/wagmiGenerated.ts +93 -13
- package/package.json +6 -4
- package/script/PrintRegisterUpgradePath.s.sol +0 -7
- package/script/TestBackingCoinSwap.s.sol +0 -3
- package/script/TestV4Swap.s.sol +0 -3
- package/script/UpgradeFactoryImpl.s.sol +1 -1
- package/src/BaseCoin.sol +19 -24
- package/src/ContentCoin.sol +11 -2
- package/src/CreatorCoin.sol +34 -15
- package/src/ZoraFactoryImpl.sol +163 -92
- package/src/deployment/CoinsDeployerBase.sol +24 -58
- package/src/hook-registry/ZoraHookRegistry.sol +97 -0
- package/src/hooks/{BaseZoraV4CoinHook.sol → ZoraV4CoinHook.sol} +77 -15
- package/src/interfaces/ICoin.sol +19 -1
- package/src/interfaces/ICreatorCoin.sol +4 -0
- package/src/interfaces/IUpgradeableV4Hook.sol +18 -0
- package/src/interfaces/IZoraFactory.sol +51 -10
- package/src/interfaces/IZoraHookRegistry.sol +47 -0
- package/src/libs/CoinConstants.sol +43 -32
- package/src/libs/CoinDopplerMultiCurve.sol +11 -11
- package/src/libs/CoinRewardsV4.sol +68 -37
- package/src/libs/CoinSetup.sol +2 -9
- package/src/libs/DopplerMath.sol +2 -2
- package/src/libs/HooksDeployment.sol +13 -65
- package/src/libs/V4Liquidity.sol +109 -15
- package/src/version/ContractVersionBase.sol +1 -1
- package/test/Coin.t.sol +5 -5
- package/test/CoinRewardsV4.t.sol +33 -0
- package/test/CoinUniV4.t.sol +32 -30
- package/test/ContentCoinRewards.t.sol +363 -0
- package/test/CreatorCoin.t.sol +53 -29
- package/test/CreatorCoinRewards.t.sol +375 -0
- package/test/DeploymentHooks.t.sol +64 -12
- package/test/Factory.t.sol +24 -7
- package/test/HooksDeployment.t.sol +4 -4
- package/test/LiquidityMigration.t.sol +149 -16
- package/test/Upgrades.t.sol +44 -48
- package/test/V4Liquidity.t.sol +178 -0
- package/test/ZoraHookRegistry.t.sol +266 -0
- package/test/utils/BaseTest.sol +25 -43
- package/test/utils/FeeEstimatorHook.sol +4 -6
- package/test/utils/RewardTestHelpers.sol +106 -0
- package/.turbo/turbo-build.log +0 -199
- package/abis/AutoSwapperTest.json +0 -618
- package/abis/BadImpl.json +0 -15
- package/abis/BaseZoraV4CoinHook.json +0 -1664
- package/abis/CoinConstants.json +0 -158
- package/abis/CoinRewardsV4.json +0 -67
- package/abis/CoinTest.json +0 -819
- package/abis/CoinUniV4Test.json +0 -1128
- package/abis/ContentCoinHook.json +0 -1733
- package/abis/CreatorCoinTest.json +0 -887
- package/abis/Deploy.json +0 -9
- package/abis/DeployHooks.json +0 -9
- package/abis/DeployScript.json +0 -35
- package/abis/DeployedCoinVersionLookupTest.json +0 -740
- package/abis/DifferentNamespaceVersionLookup.json +0 -39
- package/abis/FactoryTest.json +0 -748
- package/abis/FakeHookNoInterface.json +0 -21
- package/abis/GenerateDeterministicParams.json +0 -9
- package/abis/HooksDeploymentTest.json +0 -645
- package/abis/HooksTest.json +0 -709
- package/abis/InvalidLiquidityMigrationReceiver.json +0 -21
- package/abis/LiquidityMigrationTest.json +0 -889
- package/abis/MockBadFactory.json +0 -15
- package/abis/MultiOwnableTest.json +0 -766
- package/abis/PrintUpgradeCommand.json +0 -9
- package/abis/TestDeployedCoinVersionLookupImplementation.json +0 -39
- package/abis/TestV4Swap.json +0 -9
- package/abis/UpgradeFactoryImpl.json +0 -9
- package/abis/UpgradeHooks.json +0 -35
- package/abis/UpgradesTest.json +0 -723
- package/src/hooks/ContentCoinHook.sol +0 -27
- package/src/hooks/CreatorCoinHook.sol +0 -27
- package/src/libs/CreatorCoinConstants.sol +0 -16
- package/src/libs/CreatorCoinRewards.sol +0 -34
- package/src/libs/MarketConstants.sol +0 -15
|
@@ -8,53 +8,64 @@
|
|
|
8
8
|
pragma solidity ^0.8.23;
|
|
9
9
|
|
|
10
10
|
library CoinConstants {
|
|
11
|
+
/// @dev Constant used to increase precision during calculations
|
|
12
|
+
uint256 internal constant WAD = 1e18;
|
|
13
|
+
|
|
11
14
|
/// @notice The maximum total supply
|
|
12
15
|
/// @dev Set to 1 billion coins with 18 decimals
|
|
13
|
-
uint256
|
|
16
|
+
uint256 internal constant MAX_TOTAL_SUPPLY = 1_000_000_000e18;
|
|
17
|
+
|
|
18
|
+
/// @notice The total supply for creator coins (same as MAX_TOTAL_SUPPLY)
|
|
19
|
+
/// @dev 1 billion coins
|
|
20
|
+
uint256 internal constant TOTAL_SUPPLY = 1_000_000_000e18;
|
|
14
21
|
|
|
15
|
-
/// @notice The number of coins allocated to the liquidity pool
|
|
22
|
+
/// @notice The number of coins allocated to the liquidity pool for content coins
|
|
16
23
|
/// @dev 990 million coins
|
|
17
|
-
uint256
|
|
24
|
+
uint256 internal constant CONTENT_COIN_MARKET_SUPPLY = 990_000_000e18;
|
|
18
25
|
|
|
19
|
-
/// @notice The number of coins
|
|
26
|
+
/// @notice The number of coins allocated to the liquidity pool for creator coins
|
|
27
|
+
/// @dev 500 million coins
|
|
28
|
+
uint256 internal constant CREATOR_COIN_MARKET_SUPPLY = 500_000_000e18;
|
|
29
|
+
|
|
30
|
+
/// @notice The number of coins rewarded to the creator for content coins on launch
|
|
20
31
|
/// @dev 10 million coins
|
|
21
|
-
uint256
|
|
32
|
+
uint256 internal constant CONTENT_COIN_INITIAL_CREATOR_SUPPLY = TOTAL_SUPPLY - CONTENT_COIN_MARKET_SUPPLY;
|
|
33
|
+
|
|
34
|
+
/// @notice Creator coin vesting supply for creator
|
|
35
|
+
/// @dev 500 million coins
|
|
36
|
+
uint256 internal constant CREATOR_COIN_CREATOR_VESTING_SUPPLY = TOTAL_SUPPLY - CREATOR_COIN_MARKET_SUPPLY;
|
|
22
37
|
|
|
23
|
-
/// @notice
|
|
24
|
-
/// @dev
|
|
25
|
-
uint256
|
|
38
|
+
/// @notice Creator coin vesting duration
|
|
39
|
+
/// @dev 5 years with leap years accounted for
|
|
40
|
+
uint256 internal constant CREATOR_VESTING_DURATION = (5 * 365.25 days);
|
|
26
41
|
|
|
27
|
-
/// @notice The
|
|
28
|
-
/// @dev
|
|
29
|
-
|
|
42
|
+
/// @notice The backing currency for creator coins
|
|
43
|
+
/// @dev ETH backing currency address
|
|
44
|
+
address internal constant CREATOR_COIN_CURRENCY = 0x1111111111166b7FE7bd91427724B487980aFc69;
|
|
30
45
|
|
|
31
|
-
/// @notice The
|
|
32
|
-
/// @dev
|
|
33
|
-
|
|
46
|
+
/// @notice The LP fee
|
|
47
|
+
/// @dev 10000 basis points = 1%
|
|
48
|
+
uint24 internal constant LP_FEE_V4 = 10_000;
|
|
34
49
|
|
|
35
|
-
/// @notice The
|
|
36
|
-
/// @dev
|
|
37
|
-
|
|
50
|
+
/// @notice The spacing for 1% pools
|
|
51
|
+
/// @dev 200 ticks
|
|
52
|
+
int24 internal constant TICK_SPACING = 200;
|
|
38
53
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
uint256
|
|
54
|
+
// Creator gets 62.5% of market rewards (0.50% of total 1% fee)
|
|
55
|
+
// Market rewards = 80% of total fee (0.80% of 1%)
|
|
56
|
+
uint256 internal constant CREATOR_REWARD_BPS = 6250;
|
|
42
57
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
uint256 public constant TRADE_REFERRER_FEE_BPS = 1500;
|
|
58
|
+
// Platform referrer gets 25% of market rewards (0.20% of total 1% fee)
|
|
59
|
+
uint256 internal constant CREATE_REFERRAL_REWARD_BPS = 2500;
|
|
46
60
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
uint256 public constant CREATOR_MARKET_REWARD_BPS = 5000;
|
|
61
|
+
// Trade referrer gets 5% of market rewards (0.04% of total 1% fee)
|
|
62
|
+
uint256 internal constant TRADE_REFERRAL_REWARD_BPS = 500;
|
|
50
63
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
uint256 public constant PLATFORM_REFERRER_MARKET_REWARD_BPS = 2500;
|
|
64
|
+
// Doppler gets 1.25% of market rewards (0.01% of total 1% fee)
|
|
65
|
+
uint256 internal constant DOPPLER_REWARD_BPS = 125;
|
|
54
66
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
uint256 public constant DOPPLER_MARKET_REWARD_BPS = 500;
|
|
67
|
+
// LPs get 20% of total fee (0.20% of 1%)
|
|
68
|
+
uint256 internal constant LP_REWARD_BPS = 2000;
|
|
58
69
|
|
|
59
70
|
int24 internal constant DEFAULT_DISCOVERY_TICK_LOWER = -777000;
|
|
60
71
|
int24 internal constant DEFAULT_DISCOVERY_TICK_UPPER = 222000;
|
|
@@ -10,7 +10,7 @@ pragma solidity ^0.8.23;
|
|
|
10
10
|
import {PoolConfiguration} from "../interfaces/ICoin.sol";
|
|
11
11
|
import {CoinConfigurationVersions} from "./CoinConfigurationVersions.sol";
|
|
12
12
|
import {LpPosition} from "../types/LpPosition.sol";
|
|
13
|
-
import {
|
|
13
|
+
import {CoinConstants} from "./CoinConstants.sol";
|
|
14
14
|
import {FullMath} from "../utils/uniswap/FullMath.sol";
|
|
15
15
|
import {TickMath} from "../utils/uniswap/TickMath.sol";
|
|
16
16
|
import {IDopplerErrors} from "../interfaces/IDopplerErrors.sol";
|
|
@@ -56,8 +56,8 @@ library CoinDopplerMultiCurve {
|
|
|
56
56
|
uint256 totalDiscoverySupplyShare;
|
|
57
57
|
uint256 totalDiscoveryPositions;
|
|
58
58
|
|
|
59
|
-
int24 boundryTickLower = DopplerMath.alignTickToTickSpacing(isCoinToken0, TickMath.MAX_TICK,
|
|
60
|
-
int24 boundryTickUpper = DopplerMath.alignTickToTickSpacing(isCoinToken0, TickMath.MIN_TICK,
|
|
59
|
+
int24 boundryTickLower = DopplerMath.alignTickToTickSpacing(isCoinToken0, TickMath.MAX_TICK, CoinConstants.TICK_SPACING);
|
|
60
|
+
int24 boundryTickUpper = DopplerMath.alignTickToTickSpacing(isCoinToken0, TickMath.MIN_TICK, CoinConstants.TICK_SPACING);
|
|
61
61
|
|
|
62
62
|
// For each curve:
|
|
63
63
|
for (uint256 i; i < numCurves; i++) {
|
|
@@ -69,8 +69,8 @@ library CoinDopplerMultiCurve {
|
|
|
69
69
|
totalDiscoveryPositions += numDiscoveryPositions_[i];
|
|
70
70
|
totalDiscoverySupplyShare += maxDiscoverySupplyShare_[i];
|
|
71
71
|
|
|
72
|
-
int24 currentTickLower = DopplerMath.alignTickToTickSpacing(isCoinToken0, tickLower_[i],
|
|
73
|
-
int24 currentTickUpper = DopplerMath.alignTickToTickSpacing(isCoinToken0, tickUpper_[i],
|
|
72
|
+
int24 currentTickLower = DopplerMath.alignTickToTickSpacing(isCoinToken0, tickLower_[i], CoinConstants.TICK_SPACING);
|
|
73
|
+
int24 currentTickUpper = DopplerMath.alignTickToTickSpacing(isCoinToken0, tickUpper_[i], CoinConstants.TICK_SPACING);
|
|
74
74
|
|
|
75
75
|
require(currentTickLower < currentTickUpper, ConfigTickLowerMustBeLessThanTickUpper());
|
|
76
76
|
|
|
@@ -84,15 +84,15 @@ library CoinDopplerMultiCurve {
|
|
|
84
84
|
|
|
85
85
|
require(boundryTickLower < boundryTickUpper, InvalidTickRangeMisordered(boundryTickLower, boundryTickUpper));
|
|
86
86
|
require(totalDiscoveryPositions > 1 && totalDiscoveryPositions <= 200, IDopplerErrors.NumDiscoveryPositionsOutOfRange());
|
|
87
|
-
require(totalDiscoverySupplyShare <
|
|
87
|
+
require(totalDiscoverySupplyShare < CoinConstants.WAD, IDopplerErrors.MaxShareToBeSoldExceeded(totalDiscoverySupplyShare, CoinConstants.WAD));
|
|
88
88
|
|
|
89
89
|
sqrtPriceX96 = TickMath.getSqrtPriceAtTick(isCoinToken0 ? boundryTickLower : boundryTickUpper);
|
|
90
90
|
|
|
91
91
|
poolConfiguration = PoolConfiguration({
|
|
92
92
|
version: CoinConfigurationVersions.DOPPLER_MULTICURVE_UNI_V4_POOL_VERSION,
|
|
93
93
|
numPositions: uint16(totalDiscoveryPositions + 1), // Add one for the final tail position
|
|
94
|
-
fee:
|
|
95
|
-
tickSpacing:
|
|
94
|
+
fee: CoinConstants.LP_FEE_V4,
|
|
95
|
+
tickSpacing: CoinConstants.TICK_SPACING,
|
|
96
96
|
numDiscoveryPositions: numDiscoveryPositions_,
|
|
97
97
|
tickLower: tickLower_,
|
|
98
98
|
tickUpper: tickUpper_,
|
|
@@ -113,12 +113,12 @@ library CoinDopplerMultiCurve {
|
|
|
113
113
|
uint256 numCurves = poolConfiguration.tickLower.length;
|
|
114
114
|
|
|
115
115
|
for (uint256 i; i < numCurves; i++) {
|
|
116
|
-
uint256 curveSupply = FullMath.mulDiv(totalSupply, poolConfiguration.maxDiscoverySupplyShare[i],
|
|
116
|
+
uint256 curveSupply = FullMath.mulDiv(totalSupply, poolConfiguration.maxDiscoverySupplyShare[i], CoinConstants.WAD);
|
|
117
117
|
|
|
118
118
|
(positions, curveSupply) = DopplerMath.calculateLogNormalDistribution(
|
|
119
119
|
poolConfiguration.tickLower[i],
|
|
120
120
|
poolConfiguration.tickUpper[i],
|
|
121
|
-
|
|
121
|
+
CoinConstants.TICK_SPACING,
|
|
122
122
|
isCoinToken0,
|
|
123
123
|
curveSupply,
|
|
124
124
|
poolConfiguration.numDiscoveryPositions[i],
|
|
@@ -138,7 +138,7 @@ library CoinDopplerMultiCurve {
|
|
|
138
138
|
poolConfiguration.tickUpper[numCurves - 1],
|
|
139
139
|
isCoinToken0,
|
|
140
140
|
tailSupply,
|
|
141
|
-
|
|
141
|
+
CoinConstants.TICK_SPACING
|
|
142
142
|
);
|
|
143
143
|
}
|
|
144
144
|
}
|
|
@@ -29,29 +29,15 @@ import {IZoraV4CoinHook} from "../interfaces/IZoraV4CoinHook.sol";
|
|
|
29
29
|
import {IHasSwapPath} from "../interfaces/ICoin.sol";
|
|
30
30
|
import {V4Liquidity} from "./V4Liquidity.sol";
|
|
31
31
|
import {UniV4SwapToCurrency} from "./UniV4SwapToCurrency.sol";
|
|
32
|
+
import {ICreatorCoinHook} from "../interfaces/ICreatorCoinHook.sol";
|
|
33
|
+
import {IHasCoinType} from "../interfaces/ICoin.sol";
|
|
34
|
+
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
|
|
35
|
+
import {ICreatorCoin} from "../interfaces/ICreatorCoin.sol";
|
|
36
|
+
import {CoinConstants} from "./CoinConstants.sol";
|
|
32
37
|
|
|
33
38
|
library CoinRewardsV4 {
|
|
34
39
|
using SafeERC20 for IERC20;
|
|
35
40
|
|
|
36
|
-
// creator gets 50% of the market rewards
|
|
37
|
-
// market rewards are 2/3 of the total fee
|
|
38
|
-
uint256 public constant CREATOR_REWARD_BPS = 5000;
|
|
39
|
-
|
|
40
|
-
// create referrer gets 15% of the market rewards
|
|
41
|
-
// market rewards are 2/3 of the total fee
|
|
42
|
-
uint256 public constant CREATE_REFERRAL_REWARD_BPS = 1500;
|
|
43
|
-
|
|
44
|
-
// trade referrer gets 10% of the market rewards
|
|
45
|
-
// market rewards are 2/3 of the total fee
|
|
46
|
-
uint256 public constant TRADE_REFERRAL_REWARD_BPS = 1500;
|
|
47
|
-
|
|
48
|
-
// doppler gets 5% of the market rewards
|
|
49
|
-
// market rewards are 2/3 of the total fee
|
|
50
|
-
uint256 public constant DOPPLER_REWARD_BPS = 500;
|
|
51
|
-
|
|
52
|
-
// LPs get 1/3 of the total fee
|
|
53
|
-
uint256 public constant LP_REWARD_BPS = 3333;
|
|
54
|
-
|
|
55
41
|
function getTradeReferral(bytes calldata hookData) internal pure returns (address) {
|
|
56
42
|
return hookData.length >= 20 ? abi.decode(hookData, (address)) : address(0);
|
|
57
43
|
}
|
|
@@ -82,13 +68,16 @@ library CoinRewardsV4 {
|
|
|
82
68
|
|
|
83
69
|
/// @dev Computes the LP reward and remaining amount for market rewards from the total amount
|
|
84
70
|
function computeLpReward(uint128 totalBackingAmount) internal pure returns (uint128 lpRewardAmount) {
|
|
85
|
-
lpRewardAmount = uint128(calculateReward(uint256(totalBackingAmount), LP_REWARD_BPS));
|
|
71
|
+
lpRewardAmount = uint128(calculateReward(uint256(totalBackingAmount), CoinConstants.LP_REWARD_BPS));
|
|
86
72
|
}
|
|
87
73
|
|
|
88
74
|
function convertDeltaToPositiveUint128(int256 delta) internal pure returns (uint128) {
|
|
89
75
|
if (delta < 0) {
|
|
90
76
|
revert SafeCast.SafeCastOverflow();
|
|
91
77
|
}
|
|
78
|
+
if (delta > int256(uint256(type(uint128).max))) {
|
|
79
|
+
revert SafeCast.SafeCastOverflow();
|
|
80
|
+
}
|
|
92
81
|
return uint128(uint256(delta));
|
|
93
82
|
}
|
|
94
83
|
|
|
@@ -180,7 +169,14 @@ library CoinRewardsV4 {
|
|
|
180
169
|
/// @param fees The total amount of fees collected to be distributed
|
|
181
170
|
/// @param coin The coin contract instance that implements IHasRewardsRecipients to get recipient addresses
|
|
182
171
|
/// @param tradeReferrer The address of the trade referrer who should receive trade referral rewards (can be zero address)
|
|
183
|
-
|
|
172
|
+
/// @param coinType The type of coin (Creator or Content) which affects reward distribution percentages
|
|
173
|
+
function distributeMarketRewards(
|
|
174
|
+
Currency currency,
|
|
175
|
+
uint128 fees,
|
|
176
|
+
IHasRewardsRecipients coin,
|
|
177
|
+
address tradeReferrer,
|
|
178
|
+
IHasCoinType.CoinType coinType
|
|
179
|
+
) internal {
|
|
184
180
|
address payoutRecipient = coin.payoutRecipient();
|
|
185
181
|
address platformReferrer = coin.platformReferrer();
|
|
186
182
|
address protocolRewardRecipient = coin.protocolRewardRecipient();
|
|
@@ -219,6 +215,17 @@ library CoinRewardsV4 {
|
|
|
219
215
|
doppler,
|
|
220
216
|
marketRewards
|
|
221
217
|
);
|
|
218
|
+
|
|
219
|
+
if (coinType == IHasCoinType.CoinType.Creator) {
|
|
220
|
+
emit ICreatorCoinHook.CreatorCoinRewards(
|
|
221
|
+
address(coin),
|
|
222
|
+
Currency.unwrap(currency),
|
|
223
|
+
payoutRecipient,
|
|
224
|
+
protocolRewardRecipient,
|
|
225
|
+
rewards.creatorAmount,
|
|
226
|
+
rewards.protocolAmount
|
|
227
|
+
);
|
|
228
|
+
}
|
|
222
229
|
}
|
|
223
230
|
|
|
224
231
|
struct MarketRewards {
|
|
@@ -240,26 +247,30 @@ library CoinRewardsV4 {
|
|
|
240
247
|
) internal returns (MarketRewards memory rewards) {
|
|
241
248
|
rewards = _computeMarketRewards(fee, tradeReferral != address(0), platformReferrer != address(0));
|
|
242
249
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
_transferCurrency(currency, rewards.creatorAmount, payoutRecipient);
|
|
250
|
-
_transferCurrency(currency, rewards.dopplerAmount, doppler);
|
|
251
|
-
_transferCurrency(currency, rewards.protocolAmount, protocolRewardRecipient);
|
|
250
|
+
// Notes on ETH transfer fallback behavior:
|
|
251
|
+
// - If the platform referrer is immutable; if it is set to an address that cannot receive ETH, it can brick swaps on the coin, as they would revert.
|
|
252
|
+
// - Both the creator recipient and trade referral are changeable, the former via updating the payout recipient on the coin, and the latter via updating the trade referrer argument when doing a swap;
|
|
253
|
+
// therefore we do not need to worry about permanently bricking swaps, and don't need a backup recipient.
|
|
254
|
+
_transferCurrency(currency, rewards.platformReferrerAmount, platformReferrer, protocolRewardRecipient);
|
|
255
|
+
_transferCurrency(currency, rewards.tradeReferrerAmount, tradeReferral, address(0));
|
|
256
|
+
_transferCurrency(currency, rewards.creatorAmount, payoutRecipient, address(0));
|
|
257
|
+
_transferCurrency(currency, rewards.dopplerAmount, doppler, protocolRewardRecipient);
|
|
258
|
+
_transferCurrency(currency, rewards.protocolAmount, protocolRewardRecipient, address(0));
|
|
252
259
|
}
|
|
253
260
|
|
|
254
|
-
function _transferCurrency(Currency currency, uint256 amount, address to) internal {
|
|
255
|
-
if (amount == 0) {
|
|
261
|
+
function _transferCurrency(Currency currency, uint256 amount, address to, address backupRecipient) internal {
|
|
262
|
+
if (amount == 0 || to == address(0)) {
|
|
256
263
|
return;
|
|
257
264
|
}
|
|
258
265
|
|
|
259
266
|
if (currency.isAddressZero()) {
|
|
260
267
|
(bool success, ) = payable(to).call{value: amount}("");
|
|
261
268
|
if (!success) {
|
|
262
|
-
|
|
269
|
+
if (backupRecipient == address(0)) {
|
|
270
|
+
revert ICoin.EthTransferFailed();
|
|
271
|
+
} else {
|
|
272
|
+
_transferCurrency(currency, amount, backupRecipient, address(0));
|
|
273
|
+
}
|
|
263
274
|
}
|
|
264
275
|
} else {
|
|
265
276
|
IERC20(Currency.unwrap(currency)).safeTransfer(to, amount);
|
|
@@ -272,14 +283,34 @@ library CoinRewardsV4 {
|
|
|
272
283
|
}
|
|
273
284
|
|
|
274
285
|
uint256 totalAmount = uint256(fee);
|
|
275
|
-
rewards.platformReferrerAmount = hasCreateReferral ? calculateReward(totalAmount, CREATE_REFERRAL_REWARD_BPS) : 0;
|
|
276
|
-
rewards.tradeReferrerAmount = hasTradeReferral ? calculateReward(totalAmount, TRADE_REFERRAL_REWARD_BPS) : 0;
|
|
277
|
-
rewards.creatorAmount = calculateReward(totalAmount, CREATOR_REWARD_BPS);
|
|
278
|
-
rewards.dopplerAmount = calculateReward(totalAmount, DOPPLER_REWARD_BPS);
|
|
286
|
+
rewards.platformReferrerAmount = hasCreateReferral ? calculateReward(totalAmount, CoinConstants.CREATE_REFERRAL_REWARD_BPS) : 0;
|
|
287
|
+
rewards.tradeReferrerAmount = hasTradeReferral ? calculateReward(totalAmount, CoinConstants.TRADE_REFERRAL_REWARD_BPS) : 0;
|
|
288
|
+
rewards.creatorAmount = calculateReward(totalAmount, CoinConstants.CREATOR_REWARD_BPS);
|
|
289
|
+
rewards.dopplerAmount = calculateReward(totalAmount, CoinConstants.DOPPLER_REWARD_BPS);
|
|
279
290
|
rewards.protocolAmount = totalAmount - rewards.platformReferrerAmount - rewards.tradeReferrerAmount - rewards.creatorAmount - rewards.dopplerAmount;
|
|
280
291
|
}
|
|
281
292
|
|
|
282
293
|
function calculateReward(uint256 amount, uint256 bps) internal pure returns (uint256) {
|
|
283
294
|
return (amount * bps) / 10_000;
|
|
284
295
|
}
|
|
296
|
+
|
|
297
|
+
function getCoinType(IHasRewardsRecipients coin) internal view returns (IHasCoinType.CoinType) {
|
|
298
|
+
// first check if the coin supports the IHasCoinType interface - if it does, we can use that
|
|
299
|
+
if (IERC165(address(coin)).supportsInterface(type(IHasCoinType).interfaceId)) {
|
|
300
|
+
return IHasCoinType(address(coin)).coinType();
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// see if its a legacy creator coin
|
|
304
|
+
return isLegacyCreatorCoin(coin) ? IHasCoinType.CoinType.Creator : IHasCoinType.CoinType.Content;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
function isLegacyCreatorCoin(IHasRewardsRecipients coin) internal view returns (bool) {
|
|
308
|
+
// try to call the method `getClaimableAmount` on the legacy creator coin, if it succeeds, then it is a legacy creator coin,
|
|
309
|
+
// otherwise we can assume it is a content coin
|
|
310
|
+
try ICreatorCoin(address(coin)).getClaimableAmount() returns (uint256) {
|
|
311
|
+
return true;
|
|
312
|
+
} catch {
|
|
313
|
+
return false;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
285
316
|
}
|
package/src/libs/CoinSetup.sol
CHANGED
|
@@ -11,12 +11,11 @@ import {PoolConfigurationV4} from "../interfaces/ICoin.sol";
|
|
|
11
11
|
import {CoinConfigurationVersions} from "./CoinConfigurationVersions.sol";
|
|
12
12
|
import {ICoin} from "../interfaces/ICoin.sol";
|
|
13
13
|
import {CoinCommon} from "./CoinCommon.sol";
|
|
14
|
-
import {
|
|
14
|
+
import {CoinConstants} from "./CoinConstants.sol";
|
|
15
15
|
import {TickMath} from "../utils/uniswap/TickMath.sol";
|
|
16
16
|
import {IPoolManager, PoolKey, Currency, IHooks} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol";
|
|
17
17
|
import {Actions} from "@uniswap/v4-periphery/src/libraries/Actions.sol";
|
|
18
18
|
import {IPositionManager} from "@uniswap/v4-periphery/src/interfaces/IPositionManager.sol";
|
|
19
|
-
import {MarketConstants} from "./MarketConstants.sol";
|
|
20
19
|
import {LpPosition} from "../types/LpPosition.sol";
|
|
21
20
|
import {CoinDopplerMultiCurve, PoolConfiguration} from "./CoinDopplerMultiCurve.sol";
|
|
22
21
|
|
|
@@ -37,13 +36,7 @@ library CoinSetup {
|
|
|
37
36
|
Currency currency0 = isCoinToken0 ? Currency.wrap(coin) : Currency.wrap(currency);
|
|
38
37
|
Currency currency1 = isCoinToken0 ? Currency.wrap(currency) : Currency.wrap(coin);
|
|
39
38
|
|
|
40
|
-
poolKey = PoolKey({
|
|
41
|
-
currency0: currency0,
|
|
42
|
-
currency1: currency1,
|
|
43
|
-
fee: MarketConstants.LP_FEE_V4,
|
|
44
|
-
tickSpacing: MarketConstants.TICK_SPACING,
|
|
45
|
-
hooks: hooks
|
|
46
|
-
});
|
|
39
|
+
poolKey = PoolKey({currency0: currency0, currency1: currency1, fee: CoinConstants.LP_FEE_V4, tickSpacing: CoinConstants.TICK_SPACING, hooks: hooks});
|
|
47
40
|
}
|
|
48
41
|
|
|
49
42
|
function setupPoolWithVersion(
|
package/src/libs/DopplerMath.sol
CHANGED
|
@@ -13,7 +13,7 @@ import {FullMath} from "../utils/uniswap/FullMath.sol";
|
|
|
13
13
|
import {SqrtPriceMath} from "../utils/uniswap/SqrtPriceMath.sol";
|
|
14
14
|
import {LiquidityAmounts} from "../utils/uniswap/LiquidityAmounts.sol";
|
|
15
15
|
import {LpPosition} from "../types/LpPosition.sol";
|
|
16
|
-
import {
|
|
16
|
+
import {CoinConstants} from "./CoinConstants.sol";
|
|
17
17
|
|
|
18
18
|
/// @author Whetstone Research
|
|
19
19
|
/// @notice Calculates liquidity provisioning with Uniswap v3
|
|
@@ -54,7 +54,7 @@ library DopplerMath {
|
|
|
54
54
|
int24 spread = tickUpper - tickLower;
|
|
55
55
|
|
|
56
56
|
uint160 farSqrtPriceX96 = TickMath.getSqrtPriceAtTick(farTick);
|
|
57
|
-
uint256 amountPerPosition = FullMath.mulDiv(discoverySupply,
|
|
57
|
+
uint256 amountPerPosition = FullMath.mulDiv(discoverySupply, CoinConstants.WAD, totalPositions * CoinConstants.WAD);
|
|
58
58
|
uint256 totalAssetsSold;
|
|
59
59
|
|
|
60
60
|
for (uint256 i; i < totalPositions; i++) {
|
|
@@ -9,8 +9,7 @@ pragma solidity ^0.8.23;
|
|
|
9
9
|
|
|
10
10
|
import {Hooks} from "@uniswap/v4-core/src/libraries/Hooks.sol";
|
|
11
11
|
import {Vm} from "forge-std/Vm.sol";
|
|
12
|
-
import {
|
|
13
|
-
import {CreatorCoinHook} from "../hooks/CreatorCoinHook.sol";
|
|
12
|
+
import {ZoraV4CoinHook} from "../hooks/ZoraV4CoinHook.sol";
|
|
14
13
|
import {HookMiner} from "@uniswap/v4-periphery/src/utils/HookMiner.sol";
|
|
15
14
|
import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol";
|
|
16
15
|
import {Create2} from "@openzeppelin/contracts/utils/Create2.sol";
|
|
@@ -83,26 +82,14 @@ library HooksDeployment {
|
|
|
83
82
|
}
|
|
84
83
|
}
|
|
85
84
|
|
|
86
|
-
function
|
|
85
|
+
function mineForCoinSalt(
|
|
87
86
|
address deployer,
|
|
88
87
|
address poolManager,
|
|
89
88
|
address coinVersionLookup,
|
|
90
89
|
address[] memory trustedMessageSenders,
|
|
91
90
|
address upgradeGate
|
|
92
91
|
) internal returns (address hookAddress, bytes32 salt) {
|
|
93
|
-
bytes memory hookCreationCode =
|
|
94
|
-
(salt, ) = mineAndCacheSalt(deployer, hookCreationCode);
|
|
95
|
-
hookAddress = HookMinerWithCreationCodeArgs.deterministicHookAddress(deployer, salt, hookCreationCode);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
function mineForContentCoinSalt(
|
|
99
|
-
address deployer,
|
|
100
|
-
address poolManager,
|
|
101
|
-
address coinVersionLookup,
|
|
102
|
-
address[] memory trustedMessageSenders,
|
|
103
|
-
address upgradeGate
|
|
104
|
-
) internal returns (address hookAddress, bytes32 salt) {
|
|
105
|
-
bytes memory hookCreationCode = contentCoinCreationCode(poolManager, coinVersionLookup, trustedMessageSenders, upgradeGate);
|
|
92
|
+
bytes memory hookCreationCode = makeHookCreationCode(poolManager, coinVersionLookup, trustedMessageSenders, upgradeGate);
|
|
106
93
|
(salt, ) = mineAndCacheSalt(deployer, hookCreationCode);
|
|
107
94
|
hookAddress = HookMinerWithCreationCodeArgs.deterministicHookAddress(deployer, salt, hookCreationCode);
|
|
108
95
|
}
|
|
@@ -141,7 +128,7 @@ library HooksDeployment {
|
|
|
141
128
|
isDeployed = hookAddress.code.length > 0;
|
|
142
129
|
}
|
|
143
130
|
|
|
144
|
-
function
|
|
131
|
+
function hookConstructorArgs(
|
|
145
132
|
address poolManager,
|
|
146
133
|
address coinVersionLookup,
|
|
147
134
|
address[] memory trustedMessageSenders,
|
|
@@ -150,82 +137,43 @@ library HooksDeployment {
|
|
|
150
137
|
return abi.encode(poolManager, coinVersionLookup, trustedMessageSenders, upgradeGate);
|
|
151
138
|
}
|
|
152
139
|
|
|
153
|
-
function
|
|
154
|
-
address poolManager,
|
|
155
|
-
address coinVersionLookup,
|
|
156
|
-
address[] memory trustedMessageSenders,
|
|
157
|
-
address upgradeGate
|
|
158
|
-
) internal pure returns (bytes memory) {
|
|
159
|
-
return
|
|
160
|
-
abi.encodePacked(
|
|
161
|
-
type(ContentCoinHook).creationCode,
|
|
162
|
-
contentCoinConstructorArgs(poolManager, coinVersionLookup, trustedMessageSenders, upgradeGate)
|
|
163
|
-
);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
function creatorCoinCreationCode(
|
|
140
|
+
function makeHookCreationCode(
|
|
167
141
|
address poolManager,
|
|
168
142
|
address coinVersionLookup,
|
|
169
143
|
address[] memory trustedMessageSenders,
|
|
170
144
|
address upgradeGate
|
|
171
145
|
) internal pure returns (bytes memory) {
|
|
172
|
-
return
|
|
173
|
-
abi.encodePacked(
|
|
174
|
-
type(CreatorCoinHook).creationCode,
|
|
175
|
-
creatorCoinConstructorArgs(poolManager, coinVersionLookup, trustedMessageSenders, upgradeGate)
|
|
176
|
-
);
|
|
146
|
+
return abi.encodePacked(type(ZoraV4CoinHook).creationCode, hookConstructorArgs(poolManager, coinVersionLookup, trustedMessageSenders, upgradeGate));
|
|
177
147
|
}
|
|
178
148
|
|
|
179
149
|
/// @notice Deploys or returns existing ContentCoinHook using deterministic deployment. Ensures that if a hooks is already
|
|
180
150
|
/// deployed with the provided salt, it will be returned.
|
|
181
|
-
function
|
|
151
|
+
function deployZoraV4CoinHook(
|
|
182
152
|
address poolManager,
|
|
183
153
|
address coinVersionLookup,
|
|
184
154
|
address[] memory trustedMessageSenders,
|
|
185
155
|
address upgradeGate,
|
|
186
156
|
bytes32 salt
|
|
187
157
|
) internal returns (IHooks hook) {
|
|
188
|
-
bytes memory
|
|
189
|
-
return deployHookWithSalt(
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
function creatorCoinConstructorArgs(
|
|
193
|
-
address poolManager,
|
|
194
|
-
address coinVersionLookup,
|
|
195
|
-
address[] memory trustedMessageSenders,
|
|
196
|
-
address upgradeGate
|
|
197
|
-
) internal pure returns (bytes memory) {
|
|
198
|
-
return abi.encode(poolManager, coinVersionLookup, trustedMessageSenders, upgradeGate);
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
function creatorCoinHookCreationCode(
|
|
202
|
-
address poolManager,
|
|
203
|
-
address coinVersionLookup,
|
|
204
|
-
address[] memory trustedMessageSenders,
|
|
205
|
-
address upgradeGate
|
|
206
|
-
) internal pure returns (bytes memory) {
|
|
207
|
-
return
|
|
208
|
-
abi.encodePacked(
|
|
209
|
-
type(CreatorCoinHook).creationCode,
|
|
210
|
-
creatorCoinConstructorArgs(poolManager, coinVersionLookup, trustedMessageSenders, upgradeGate)
|
|
211
|
-
);
|
|
158
|
+
bytes memory creationCode = makeHookCreationCode(poolManager, coinVersionLookup, trustedMessageSenders, upgradeGate);
|
|
159
|
+
return deployHookWithSalt(creationCode, salt);
|
|
212
160
|
}
|
|
213
161
|
|
|
214
162
|
address constant FOUNDRY_SCRIPT_ADDRESS = 0x4e59b44847b379578588920cA78FbF26c0B4956C;
|
|
215
163
|
|
|
216
164
|
function deployHookWithExistingOrNewSalt(
|
|
217
165
|
address deployer,
|
|
218
|
-
bytes memory
|
|
166
|
+
bytes memory _hookCreationCode,
|
|
219
167
|
bytes32 salt
|
|
220
168
|
) internal returns (IHooks hook, bytes32 resultingSalt) {
|
|
221
|
-
(bool isDeployed, address existingHookAddress) = hooksIsDeployed(deployer,
|
|
169
|
+
(bool isDeployed, address existingHookAddress) = hooksIsDeployed(deployer, _hookCreationCode, salt);
|
|
222
170
|
|
|
223
171
|
if (isDeployed) {
|
|
224
172
|
hook = IHooks(existingHookAddress);
|
|
225
173
|
resultingSalt = salt;
|
|
226
174
|
} else {
|
|
227
|
-
(, resultingSalt) = mineForSalt(deployer,
|
|
228
|
-
hook = IHooks(Create2.deploy(0, resultingSalt,
|
|
175
|
+
(, resultingSalt) = mineForSalt(deployer, _hookCreationCode);
|
|
176
|
+
hook = IHooks(Create2.deploy(0, resultingSalt, _hookCreationCode));
|
|
229
177
|
}
|
|
230
178
|
}
|
|
231
179
|
}
|