@zoralabs/coins 0.7.1 → 1.0.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.
Files changed (167) hide show
  1. package/.turbo/turbo-build.log +106 -84
  2. package/CHANGELOG.md +68 -0
  3. package/abis/BadImpl.json +15 -0
  4. package/abis/BalanceDeltaLibrary.json +15 -0
  5. package/abis/BaseCoin.json +1350 -0
  6. package/abis/BaseCoinDeployHook.json +78 -0
  7. package/abis/BaseHook.json +897 -0
  8. package/abis/BaseTest.json +60 -91
  9. package/abis/BeforeSwapDeltaLibrary.json +15 -0
  10. package/abis/BuySupplyWithSwapRouterHook.json +126 -0
  11. package/abis/Coin.json +214 -150
  12. package/abis/CoinConstants.json +65 -0
  13. package/abis/CoinDopplerMultiCurve.json +38 -0
  14. package/abis/CoinRewardsV4.json +54 -0
  15. package/abis/CoinTest.json +66 -111
  16. package/abis/CoinUniV4Test.json +1053 -0
  17. package/abis/CoinV4.json +1687 -0
  18. package/abis/CurrencyLibrary.json +25 -0
  19. package/abis/DeployHooks.json +9 -0
  20. package/abis/DeployScript.json +47 -0
  21. package/abis/DeployedCoinVersionLookup.json +21 -0
  22. package/abis/DeployedCoinVersionLookupTest.json +716 -0
  23. package/abis/DifferentNamespaceVersionLookup.json +39 -0
  24. package/abis/DopplerUniswapV3Test.json +62 -184
  25. package/abis/ERC20.json +310 -0
  26. package/abis/FactoryTest.json +98 -98
  27. package/abis/FakeHookNoInterface.json +21 -0
  28. package/abis/FeeEstimatorHook.json +1528 -0
  29. package/abis/Hooks.json +28 -0
  30. package/abis/HooksDeployment.json +23 -0
  31. package/abis/HooksTest.json +698 -0
  32. package/abis/IAllowanceTransfer.json +486 -0
  33. package/abis/ICoin.json +62 -69
  34. package/abis/ICoinDeployHook.json +31 -0
  35. package/abis/ICoinV3.json +879 -0
  36. package/abis/ICoinV4.json +915 -0
  37. package/abis/IContractMetadata.json +28 -0
  38. package/abis/IDeployedCoinVersionLookup.json +21 -0
  39. package/abis/IEIP712.json +15 -0
  40. package/abis/IEIP712_v4.json +15 -0
  41. package/abis/IERC20Minimal.json +172 -0
  42. package/abis/IERC6909Claims.json +288 -0
  43. package/abis/IERC721.json +36 -36
  44. package/abis/IERC721Permit_v4.json +88 -0
  45. package/abis/IExtsload.json +64 -0
  46. package/abis/IExttload.json +40 -0
  47. package/abis/IHasAfterCoinDeploy.json +31 -0
  48. package/abis/IHasContractName.json +15 -0
  49. package/abis/IHasPoolKey.json +42 -0
  50. package/abis/IHasRewardsRecipients.json +54 -0
  51. package/abis/IHasSwapPath.json +60 -0
  52. package/abis/IHooks.json +789 -0
  53. package/abis/IImmutableState.json +15 -0
  54. package/abis/IMsgSender.json +15 -0
  55. package/abis/IMulticall_v4.json +21 -0
  56. package/abis/INotifier.json +187 -0
  57. package/abis/IPermit2.json +865 -0
  58. package/abis/IPermit2Forwarder.json +138 -0
  59. package/abis/IPoolConfigEncoding.json +46 -0
  60. package/abis/IPoolInitializer_v4.json +53 -0
  61. package/abis/IPoolManager.json +1286 -0
  62. package/abis/IPositionManager.json +712 -0
  63. package/abis/IProtocolFees.json +174 -0
  64. package/abis/ISignatureTransfer.json +394 -0
  65. package/abis/ISubscriber.json +89 -0
  66. package/abis/ISwapPathRouter.json +92 -0
  67. package/abis/ISwapRouter.json +82 -0
  68. package/abis/IUniversalRouter.json +61 -0
  69. package/abis/IUnlockCallback.json +21 -0
  70. package/abis/IUnorderedNonce.json +44 -0
  71. package/abis/IV4Quoter.json +310 -0
  72. package/abis/IV4Router.json +47 -0
  73. package/abis/IZoraFactory.json +328 -4
  74. package/abis/IZoraV4CoinHook.json +427 -0
  75. package/abis/ImmutableState.json +36 -0
  76. package/abis/LPFeeLibrary.json +65 -0
  77. package/abis/MockERC20.json +21 -0
  78. package/abis/MultiOwnableTest.json +60 -91
  79. package/abis/{CoinConfigurationVersions.json → Position.json} +1 -1
  80. package/abis/PrintUpgradeCommand.json +9 -0
  81. package/abis/ProxyShim.json +24 -0
  82. package/abis/Simulate.json +0 -91
  83. package/abis/StateLibrary.json +80 -0
  84. package/abis/TestDeployedCoinVersionLookupImplementation.json +39 -0
  85. package/abis/TestV4Swap.json +9 -0
  86. package/abis/{CoinSetup.json → UniV3BuySell.json} +5 -0
  87. package/abis/UniV3Errors.json +32 -0
  88. package/abis/UpgradeCoinImpl.json +47 -0
  89. package/abis/UpgradeFactoryImpl.json +9 -0
  90. package/abis/UpgradesTest.json +671 -0
  91. package/abis/Vm.json +1482 -111
  92. package/abis/VmSafe.json +856 -32
  93. package/abis/ZoraFactoryImpl.json +450 -1
  94. package/abis/ZoraV4CoinHook.json +1439 -0
  95. package/addresses/8453.json +8 -3
  96. package/addresses/84532.json +8 -3
  97. package/dist/index.cjs +1998 -184
  98. package/dist/index.cjs.map +1 -1
  99. package/dist/index.js +1989 -178
  100. package/dist/index.js.map +1 -1
  101. package/dist/wagmiGenerated.d.ts +2852 -688
  102. package/dist/wagmiGenerated.d.ts.map +1 -1
  103. package/package/wagmiGenerated.ts +1992 -173
  104. package/package.json +7 -2
  105. package/remappings.txt +6 -1
  106. package/script/CoinsDeployerBase.sol +105 -10
  107. package/script/DeployDevFactory.s.sol +21 -0
  108. package/script/DeployHooks.s.sol +22 -0
  109. package/script/PrintUpgradeCommand.s.sol +13 -0
  110. package/script/Simulate.s.sol +4 -12
  111. package/script/TestBackingCoinSwap.s.sol +146 -0
  112. package/script/TestV4Swap.s.sol +136 -0
  113. package/script/UpgradeCoinImpl.sol +2 -2
  114. package/script/UpgradeFactoryImpl.s.sol +23 -0
  115. package/src/BaseCoin.sol +176 -0
  116. package/src/Coin.sol +93 -515
  117. package/src/CoinV4.sol +121 -0
  118. package/src/ZoraFactoryImpl.sol +257 -57
  119. package/src/hooks/ZoraV4CoinHook.sol +195 -0
  120. package/src/hooks/deployment/BaseCoinDeployHook.sol +62 -0
  121. package/src/hooks/deployment/BuySupplyWithSwapRouterHook.sol +80 -0
  122. package/src/interfaces/ICoin.sol +35 -39
  123. package/src/interfaces/ICoinDeployHook.sol +8 -0
  124. package/src/interfaces/ICoinV3.sol +71 -0
  125. package/src/interfaces/ICoinV4.sol +69 -0
  126. package/src/interfaces/IDeployedCoinVersionLookup.sol +11 -0
  127. package/src/interfaces/IMsgSender.sol +9 -0
  128. package/src/interfaces/IPoolConfigEncoding.sol +14 -0
  129. package/src/interfaces/ISwapPathRouter.sol +14 -0
  130. package/src/interfaces/ISwapRouter.sol +1 -35
  131. package/src/interfaces/IZoraFactory.sol +97 -7
  132. package/src/interfaces/IZoraV4CoinHook.sol +116 -0
  133. package/src/libs/CoinCommon.sol +15 -0
  134. package/src/libs/CoinConfigurationVersions.sol +116 -1
  135. package/src/{utils → libs}/CoinConstants.sol +11 -6
  136. package/src/libs/CoinDopplerMultiCurve.sol +134 -0
  137. package/src/libs/CoinDopplerUniV3.sol +19 -171
  138. package/src/libs/CoinRewards.sol +195 -0
  139. package/src/libs/CoinRewardsV4.sol +180 -0
  140. package/src/libs/CoinSetup.sol +40 -20
  141. package/src/libs/CoinSetupV3.sol +50 -0
  142. package/src/libs/DopplerMath.sol +156 -0
  143. package/src/libs/HooksDeployment.sol +84 -0
  144. package/src/libs/MarketConstants.sol +4 -0
  145. package/src/libs/PoolStateReader.sol +22 -0
  146. package/src/libs/UniV3BuySell.sol +231 -0
  147. package/src/libs/UniV3Errors.sol +11 -0
  148. package/src/libs/UniV4SwapHelper.sol +65 -0
  149. package/src/libs/UniV4SwapToCurrency.sol +109 -0
  150. package/src/libs/V4Liquidity.sol +129 -0
  151. package/src/types/PoolConfiguration.sol +15 -0
  152. package/src/utils/DeployedCoinVersionLookup.sol +52 -0
  153. package/src/version/ContractVersionBase.sol +1 -1
  154. package/test/Coin.t.sol +94 -101
  155. package/test/CoinDopplerUniV3.t.sol +35 -184
  156. package/test/CoinUniV4.t.sol +752 -0
  157. package/test/DeploymentHooks.t.sol +270 -0
  158. package/test/Factory.t.sol +84 -50
  159. package/test/MultiOwnable.t.sol +6 -3
  160. package/test/Upgrades.t.sol +68 -0
  161. package/test/mocks/MockERC20.sol +12 -0
  162. package/test/utils/BaseTest.sol +124 -59
  163. package/test/utils/DeployedCoinVersionLookup.t.sol +127 -0
  164. package/test/utils/FeeEstimatorHook.sol +84 -0
  165. package/test/utils/ProxyShim.sol +17 -0
  166. package/wagmi.config.ts +10 -9
  167. package/src/libs/CoinLegacy.sol +0 -48
@@ -1,38 +1,4 @@
1
1
  // SPDX-License-Identifier: GPL-2.0-or-later
2
2
  pragma solidity ^0.8.0;
3
3
 
4
- import {IUniswapV3SwapCallback} from "./IUniswapV3SwapCallback.sol";
5
-
6
- /// @title Router token swapping functionality
7
- /// @notice Functions for swapping tokens via Uniswap V3
8
- interface ISwapRouter is IUniswapV3SwapCallback {
9
- struct ExactInputSingleParams {
10
- address tokenIn;
11
- address tokenOut;
12
- uint24 fee;
13
- address recipient;
14
- uint256 amountIn;
15
- uint256 amountOutMinimum;
16
- uint160 sqrtPriceLimitX96;
17
- }
18
-
19
- struct ExactOutputSingleParams {
20
- address tokenIn;
21
- address tokenOut;
22
- uint24 fee;
23
- address recipient;
24
- uint256 amountOut;
25
- uint256 amountInMaximum;
26
- uint160 sqrtPriceLimitX96;
27
- }
28
-
29
- /// @notice Swaps `amountIn` of one token for as much as possible of another token
30
- /// @param params The parameters necessary for the swap, encoded as `ExactInputSingleParams` in calldata
31
- /// @return amountOut The amount of the received token
32
- function exactInputSingle(ExactInputSingleParams calldata params) external payable returns (uint256 amountOut);
33
-
34
- /// @notice Swaps as little as possible of one token for `amountOut` of another token
35
- /// @param params The parameters necessary for the swap, encoded as `ExactOutputSingleParams` in calldata
36
- /// @return amountIn The amount of the input token
37
- function exactOutputSingle(ExactOutputSingleParams calldata params) external payable returns (uint256 amountIn);
38
- }
4
+ import {ISwapRouter} from "@zoralabs/shared-contracts/interfaces/uniswap/ISwapRouter.sol";
@@ -1,6 +1,9 @@
1
1
  // SPDX-License-Identifier: MIT
2
2
  pragma solidity ^0.8.23;
3
3
 
4
+ import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol";
5
+ import {PoolKeyStruct} from "./ICoin.sol";
6
+
4
7
  interface IZoraFactory {
5
8
  /// @notice Emitted when a coin is created
6
9
  /// @param caller The msg.sender address
@@ -26,31 +29,118 @@ interface IZoraFactory {
26
29
  string version
27
30
  );
28
31
 
32
+ /// @notice Emitted when a coin is created
33
+ /// @param caller The msg.sender address
34
+ /// @param payoutRecipient The address of the creator payout recipient
35
+ /// @param platformReferrer The address of the platform referrer
36
+ /// @param currency The address of the currency
37
+ /// @param uri The URI of the coin
38
+ /// @param name The name of the coin
39
+ /// @param symbol The symbol of the coin
40
+ /// @param coin The address of the coin
41
+ /// @param poolKey The uniswap v4 pool key
42
+ /// @param version The coin contract version
43
+ event CoinCreatedV4(
44
+ address indexed caller,
45
+ address indexed payoutRecipient,
46
+ address indexed platformReferrer,
47
+ address currency,
48
+ string uri,
49
+ string name,
50
+ string symbol,
51
+ address coin,
52
+ PoolKey poolKey,
53
+ bytes32 poolKeyHash,
54
+ string version
55
+ );
56
+
29
57
  /// @notice Thrown when the amount of ERC20 tokens transferred does not match the expected amount
30
58
  error ERC20TransferAmountMismatch();
31
59
 
32
60
  /// @notice Thrown when ETH is sent with a transaction but the currency is not WETH
33
61
  error EthTransferInvalid();
34
62
 
35
- /// @notice Creates a new coin contract
63
+ /// @notice Creates a new coin contract with an optional hook that runs after the coin is deployed.
64
+ /// Requires a salt to be specified, which enabled the coin to be deployed deterministically, and at
65
+ /// a predictable address.
36
66
  /// @param payoutRecipient The recipient of creator reward payouts; this can be updated by an owner
37
67
  /// @param owners The list of addresses that will be able to manage the coin's payout address and metadata uri
38
68
  /// @param uri The coin metadata uri
39
69
  /// @param name The name of the coin
40
70
  /// @param symbol The symbol of the coin
41
- /// @param platformReferrer The address to receive platform referral rewards
42
- /// @param currency The address of the trading currency; address(0) for ETH/WETH
43
- /// @param tickLower The lower tick for the Uniswap V3 LP position; ignored for ETH/WETH pairs
44
- /// @param orderSize The order size for the first buy; must match msg.value for ETH/WETH pairs
71
+ /// @param poolConfig The config parameters for the coin's pool
72
+ /// @param platformReferrer The address of the platform referrer
73
+ /// @param postDeployHook The address of the hook to run after the coin is deployed
74
+ /// @param postDeployHookData The data to pass to the hook
75
+ /// @return coin The address of the deployed coin
76
+ /// @return postDeployHookDataOut The data returned by the hook
45
77
  function deploy(
46
78
  address payoutRecipient,
47
79
  address[] memory owners,
48
80
  string memory uri,
49
81
  string memory name,
50
82
  string memory symbol,
83
+ bytes memory poolConfig,
84
+ address platformReferrer,
85
+ address postDeployHook,
86
+ bytes calldata postDeployHookData,
87
+ bytes32 coinSalt
88
+ ) external payable returns (address coin, bytes memory postDeployHookDataOut);
89
+
90
+ /// @notice Predicts the address of a coin contract that will be deployed with the given parameters
91
+ /// @param msgSender The address of the msg.sender
92
+ /// @param name The name of the coin
93
+ /// @param symbol The symbol of the coin
94
+ /// @param poolConfig The pool config
95
+ /// @param platformReferrer The platform referrer
96
+ /// @param coinSalt The salt used to deploy the coin
97
+ /// @return The address of the coin contract
98
+ function coinAddress(
99
+ address msgSender,
100
+ string memory name,
101
+ string memory symbol,
102
+ bytes memory poolConfig,
103
+ address platformReferrer,
104
+ bytes32 coinSalt
105
+ ) external view returns (address);
106
+
107
+ /// @dev Deprecated: use `deploy` instead that has a salt and hook specified
108
+ function deploy(
109
+ address payoutRecipient,
110
+ address[] memory owners,
111
+ string memory uri,
112
+ string memory name,
113
+ string memory symbol,
114
+ bytes memory poolConfig,
51
115
  address platformReferrer,
52
- address currency,
53
- int24 tickLower,
54
116
  uint256 orderSize
55
117
  ) external payable returns (address, uint256);
118
+
119
+ /// @dev Deprecated: use `deploy` instead that has a salt and hook specified
120
+ function deployWithHook(
121
+ address payoutRecipient,
122
+ address[] memory owners,
123
+ string memory uri,
124
+ string memory name,
125
+ string memory symbol,
126
+ bytes memory poolConfig,
127
+ address platformReferrer,
128
+ address hook,
129
+ bytes calldata hookData
130
+ ) external payable returns (address coin, bytes memory hookDataOut);
131
+
132
+ function coinImpl() external view returns (address);
133
+
134
+ function implementation() external view returns (address);
135
+
136
+ /// @notice Thrown when the hook is invalid
137
+ error InvalidHook();
138
+
139
+ /// @notice Occurs when attempting to upgrade to a contract with a name that doesn't match the current contract's name
140
+ /// @param currentName The name of the current contract
141
+ /// @param newName The name of the contract being upgraded to
142
+ error UpgradeToMismatchedContractName(string currentName, string newName);
143
+
144
+ /// @notice Thrown when a method is deprecated
145
+ error Deprecated();
56
146
  }
@@ -0,0 +1,116 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.23;
3
+
4
+ import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol";
5
+ import {SwapParams} from "@uniswap/v4-core/src/types/PoolOperation.sol";
6
+ import {BalanceDelta} from "@uniswap/v4-core/src/types/BalanceDelta.sol";
7
+ import {LpPosition} from "../types/LpPosition.sol";
8
+ import {ICoin} from "./ICoin.sol";
9
+
10
+ interface IZoraV4CoinHook {
11
+ /// @notice Emitted when a swap is executed.
12
+ /// @param sender The address of the sender.
13
+ /// @param swapSender The address of the swap sender.
14
+ /// @param isTrustedSwapSenderAddress Whether the swap sender is a trusted address. (Based on a registry of trusted addresses)
15
+ /// @param key The pool key struct to identify the pool.
16
+ /// @param poolKeyHash The hash of the pool key for indexing.
17
+ /// @param params The swap parameters.
18
+ /// @param amount0 The amount of token0.
19
+ /// @param amount1 The amount of token1.
20
+ /// @param isCoinBuy Whether the swap is a coin buy.
21
+ /// @param hookData The data passed into the hook for the swap.
22
+ event Swapped(
23
+ address indexed sender,
24
+ address indexed swapSender,
25
+ bool isTrustedSwapSenderAddress,
26
+ PoolKey key,
27
+ bytes32 indexed poolKeyHash,
28
+ SwapParams params,
29
+ int128 amount0,
30
+ int128 amount1,
31
+ bool isCoinBuy,
32
+ bytes hookData,
33
+ uint160 sqrtPriceX96
34
+ );
35
+
36
+ /// @notice Thrown when a non-coin is used to initialize a pool with this hook.
37
+ /// @param coin The address of the coin.
38
+ error NotACoin(address coin);
39
+
40
+ /// @notice Coin version lookup cannot be the zero address.
41
+ error CoinVersionLookupCannotBeZeroAddress();
42
+
43
+ /// @notice Thrown when a pool is not initialized for the hook.
44
+ /// @param key The pool key struct to identify the pool.
45
+ error NoCoinForHook(PoolKey key);
46
+
47
+ /// @notice Thrown when a attempting to swap with a path that has no steps.
48
+ error PathMustHaveAtLeastOneStep();
49
+
50
+ /// @notice The pool coin struct. Lists all the contract-created positions for the coin.
51
+ struct PoolCoin {
52
+ /// @notice The address of the coin.
53
+ address coin;
54
+ /// @notice The positions of the pool coin.
55
+ LpPosition[] positions;
56
+ }
57
+
58
+ /// @notice The rewards accrued from the market's liquidity position
59
+ /// @param creatorPayoutAmountCurrency The amount of currency payed out to the creator
60
+ /// @param creatorPayoutAmountCoin The amount of coin payed out to the creator
61
+ /// @param platformReferrerAmountCurrency The amount of currency payed out to the platform referrer
62
+ /// @param platformReferrerAmountCoin The amount of coin payed out to the platform referrer
63
+ /// @param tradeReferrerAmountCurrency The amount of currency payed out to the trade referrer
64
+ /// @param tradeReferrerAmountCoin The amount of coin to pay to the trade referrer
65
+ /// @param protocolAmountCurrency The amount of currency to pay to the protocol
66
+ /// @param protocolAmountCoin The amount of coin to pay to the protocol
67
+ /// @param dopplerAmountCurrency The amount of currency to pay to doppler
68
+ /// @param dopplerAmountCoin The amount of coin to pay to doppler
69
+ struct MarketRewardsV4 {
70
+ uint256 creatorPayoutAmountCurrency;
71
+ uint256 creatorPayoutAmountCoin;
72
+ uint256 platformReferrerAmountCurrency;
73
+ uint256 platformReferrerAmountCoin;
74
+ uint256 tradeReferrerAmountCurrency;
75
+ uint256 tradeReferrerAmountCoin;
76
+ uint256 protocolAmountCurrency;
77
+ uint256 protocolAmountCoin;
78
+ uint256 dopplerAmountCurrency;
79
+ uint256 dopplerAmountCoin;
80
+ }
81
+
82
+ /// @notice Emitted when market rewards are distributed
83
+ /// @param coin The address of the coin
84
+ /// @param currency The address of the currency
85
+ /// @param payoutRecipient The address of the creator rewards payout recipient
86
+ /// @param platformReferrer The address of the platform referrer
87
+ /// @param protocolRewardRecipient The address of the protocol reward recipient
88
+ /// @param dopplerRecipient The address of the doppler recipient
89
+ /// @param tradeReferrer The address of the trade referrer
90
+ /// @param marketRewards The rewards accrued from the market's liquidity position
91
+ event CoinMarketRewardsV4(
92
+ address coin,
93
+ address currency,
94
+ address payoutRecipient,
95
+ address platformReferrer,
96
+ address tradeReferrer,
97
+ address protocolRewardRecipient,
98
+ address dopplerRecipient,
99
+ MarketRewardsV4 marketRewards
100
+ );
101
+
102
+ /// @notice Returns the pool coin for a given pool key hash.
103
+ /// @param poolKeyHash The hash of the pool key for indexing.
104
+ /// @return poolCoin The pool coin confirmation data.
105
+ function getPoolCoinByHash(bytes23 poolKeyHash) external view returns (IZoraV4CoinHook.PoolCoin memory);
106
+
107
+ /// @notice Returns the pool coin for a given pool key.
108
+ /// @param key The pool key.
109
+ /// @return poolCoin The pool coin confirmation data.
110
+ function getPoolCoin(PoolKey memory key) external view returns (IZoraV4CoinHook.PoolCoin memory);
111
+
112
+ /// @notice Returns whether the sender is a trusted message sender.
113
+ /// @param sender The address of the sender.
114
+ /// @return isTrusted Whether the sender is a trusted message sender.
115
+ function isTrustedMessageSender(address sender) external view returns (bool);
116
+ }
@@ -0,0 +1,15 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.23;
3
+
4
+ import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol";
5
+
6
+ library CoinCommon {
7
+ // Helper function to sort tokens and determine if coin is token0
8
+ function sortTokens(address coin, address currency) internal pure returns (bool isCoinToken0) {
9
+ return coin < currency;
10
+ }
11
+
12
+ function hashPoolKey(PoolKey memory key) internal pure returns (bytes32) {
13
+ return keccak256(abi.encode(key));
14
+ }
15
+ }
@@ -1,9 +1,124 @@
1
1
  // SPDX-License-Identifier: MIT
2
2
  pragma solidity ^0.8.23;
3
3
 
4
+ import {CoinConstants} from "./CoinConstants.sol";
5
+
4
6
  library CoinConfigurationVersions {
5
7
  uint8 constant LEGACY_POOL_VERSION = 1;
6
8
  uint8 constant DOPPLER_UNI_V3_POOL_VERSION = 2;
9
+ uint8 constant DOPPLER_MULTICURVE_UNI_V4_POOL_VERSION = 4;
10
+
11
+ function getVersion(bytes memory poolConfig) internal pure returns (uint8 version) {
12
+ return (version) = abi.decode(poolConfig, (uint8));
13
+ }
14
+
15
+ function isV3(uint8 version) internal pure returns (bool) {
16
+ return version == DOPPLER_UNI_V3_POOL_VERSION || version == LEGACY_POOL_VERSION;
17
+ }
18
+
19
+ function isV4(uint8 version) internal pure returns (bool) {
20
+ return version == DOPPLER_MULTICURVE_UNI_V4_POOL_VERSION;
21
+ }
22
+
23
+ function decodeVersionAndCurrency(bytes memory poolConfig) internal pure returns (uint8 version, address currency) {
24
+ (version, currency) = abi.decode(poolConfig, (uint8, address));
25
+ }
26
+
27
+ function decodeDopplerUniV3(
28
+ bytes memory poolConfig
29
+ )
30
+ internal
31
+ pure
32
+ returns (uint8 version, address currency, int24 tickLower_, int24 tickUpper_, uint16 numDiscoveryPositions_, uint256 maxDiscoverySupplyShare_)
33
+ {
34
+ (version, currency, tickLower_, tickUpper_, numDiscoveryPositions_, maxDiscoverySupplyShare_) = abi.decode(
35
+ poolConfig,
36
+ (uint8, address, int24, int24, uint16, uint256)
37
+ );
38
+ }
39
+
40
+ function encodeDopplerUniV3(
41
+ address currency,
42
+ int24 tickLower_,
43
+ int24 tickUpper_,
44
+ uint16 numDiscoveryPositions_,
45
+ uint256 maxDiscoverySupplyShare_
46
+ ) internal pure returns (bytes memory) {
47
+ return abi.encode(DOPPLER_UNI_V3_POOL_VERSION, currency, tickLower_, tickUpper_, numDiscoveryPositions_, maxDiscoverySupplyShare_);
48
+ }
49
+
50
+ function decodeLegacy(bytes memory poolConfig) internal pure returns (uint8 version, address currency, int24 tickLower_) {
51
+ (version, currency, tickLower_) = abi.decode(poolConfig, (uint8, address, int24));
52
+ }
53
+
54
+ function decodeVanillaUniV4(bytes memory poolConfig) internal pure returns (uint8 version, address currency, int24 tickLower_) {
55
+ (version, currency, tickLower_) = abi.decode(poolConfig, (uint8, address, int24));
56
+ }
57
+
58
+ function encodeDopplerMultiCurveUniV4(
59
+ address currency,
60
+ int24[] memory tickLower_,
61
+ int24[] memory tickUpper_,
62
+ uint16[] memory numDiscoveryPositions_,
63
+ uint256[] memory maxDiscoverySupplyShare_
64
+ ) internal pure returns (bytes memory) {
65
+ return abi.encode(DOPPLER_MULTICURVE_UNI_V4_POOL_VERSION, currency, tickLower_, tickUpper_, numDiscoveryPositions_, maxDiscoverySupplyShare_);
66
+ }
67
+
68
+ function decodeDopplerMultiCurveUniV4(
69
+ bytes memory poolConfig
70
+ )
71
+ internal
72
+ pure
73
+ returns (
74
+ uint8 version,
75
+ address currency,
76
+ int24[] memory tickLower_,
77
+ int24[] memory tickUpper_,
78
+ uint16[] memory numDiscoveryPositions_,
79
+ uint256[] memory maxDiscoverySupplyShare_
80
+ )
81
+ {
82
+ (version, currency, tickLower_, tickUpper_, numDiscoveryPositions_, maxDiscoverySupplyShare_) = abi.decode(
83
+ poolConfig,
84
+ (uint8, address, int24[], int24[], uint16[], uint256[])
85
+ );
86
+ }
87
+
88
+ function defaultDopplerUniV3(address currency) internal pure returns (bytes memory) {
89
+ return
90
+ encodeDopplerUniV3(
91
+ currency,
92
+ CoinConstants.DEFAULT_DISCOVERY_TICK_LOWER,
93
+ CoinConstants.DEFAULT_DISCOVERY_TICK_UPPER,
94
+ CoinConstants.DEFAULT_NUM_DISCOVERY_POSITIONS,
95
+ CoinConstants.DEFAULT_DISCOVERY_SUPPLY_SHARE
96
+ );
97
+ }
98
+
99
+ function defaultDopplerMultiCurveUniV4(address currency) internal pure returns (bytes memory) {
100
+ int24[] memory tickLower = new int24[](2);
101
+ int24[] memory tickUpper = new int24[](2);
102
+ uint16[] memory numDiscoveryPositions = new uint16[](2);
103
+ uint256[] memory maxDiscoverySupplyShare = new uint256[](2);
104
+
105
+ // todo: configure defaults
106
+ // Curve 1
107
+ tickLower[0] = -328_000;
108
+ tickUpper[0] = -300_000;
109
+ numDiscoveryPositions[0] = 2;
110
+ maxDiscoverySupplyShare[0] = 0.1e18;
111
+
112
+ // Curve 2
113
+ tickLower[1] = -200_000;
114
+ tickUpper[1] = -100_000;
115
+ numDiscoveryPositions[1] = 2;
116
+ maxDiscoverySupplyShare[1] = 0.1e18;
117
+
118
+ return encodeDopplerMultiCurveUniV4(currency, tickLower, tickUpper, numDiscoveryPositions, maxDiscoverySupplyShare);
119
+ }
7
120
 
8
- error InvalidPoolVersion();
121
+ function defaultConfig(address currency) internal pure returns (bytes memory) {
122
+ return defaultDopplerUniV3(currency);
123
+ }
9
124
  }
@@ -1,18 +1,18 @@
1
1
  // SPDX-License-Identifier: MIT
2
2
  pragma solidity ^0.8.23;
3
3
 
4
- abstract contract CoinConstants {
4
+ library CoinConstants {
5
5
  /// @notice The maximum total supply
6
6
  /// @dev Set to 1 billion coins with 18 decimals
7
7
  uint256 public constant MAX_TOTAL_SUPPLY = 1_000_000_000e18;
8
8
 
9
9
  /// @notice The number of coins allocated to the liquidity pool
10
10
  /// @dev 990 million coins
11
- uint256 internal constant POOL_LAUNCH_SUPPLY = 990_000_000e18;
11
+ uint256 public constant POOL_LAUNCH_SUPPLY = 990_000_000e18;
12
12
 
13
13
  /// @notice The number of coins rewarded to the creator
14
14
  /// @dev 10 million coins
15
- uint256 internal constant CREATOR_LAUNCH_REWARD = 10_000_000e18;
15
+ uint256 public constant CREATOR_LAUNCH_REWARD = 10_000_000e18;
16
16
 
17
17
  /// @notice The minimum order size allowed for trades
18
18
  /// @dev Set to 0.0000001 ETH to prevent dust transactions
@@ -40,13 +40,18 @@ abstract contract CoinConstants {
40
40
 
41
41
  /// @notice The percentage of the LP fee allocated to creators
42
42
  /// @dev 5000 basis points = 50% of the 1% LP FEE
43
- uint256 internal constant CREATOR_MARKET_REWARD_BPS = 5000;
43
+ uint256 public constant CREATOR_MARKET_REWARD_BPS = 5000;
44
44
 
45
45
  /// @notice The percentage of the LP fee allocated to platform referrers
46
46
  /// @dev 2500 basis points = 25% of the 1% LP FEE
47
- uint256 internal constant PLATFORM_REFERRER_MARKET_REWARD_BPS = 2500;
47
+ uint256 public constant PLATFORM_REFERRER_MARKET_REWARD_BPS = 2500;
48
48
 
49
49
  /// @notice The percentage of the LP fee allocated to the Doppler protocol
50
50
  /// @dev 500 basis points = 5% of the 1% LP FEE
51
- uint256 internal constant DOPPLER_MARKET_REWARD_BPS = 500;
51
+ uint256 public constant DOPPLER_MARKET_REWARD_BPS = 500;
52
+
53
+ int24 internal constant DEFAULT_DISCOVERY_TICK_LOWER = -777000;
54
+ int24 internal constant DEFAULT_DISCOVERY_TICK_UPPER = 222000;
55
+ uint16 internal constant DEFAULT_NUM_DISCOVERY_POSITIONS = 10; // will be 11 total with tail position
56
+ uint256 internal constant DEFAULT_DISCOVERY_SUPPLY_SHARE = 0.495e18; // half of the 990m total pool supply
52
57
  }
@@ -0,0 +1,134 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.23;
3
+
4
+ import {PoolConfiguration} from "../interfaces/ICoin.sol";
5
+ import {CoinConfigurationVersions} from "./CoinConfigurationVersions.sol";
6
+ import {LpPosition} from "../types/LpPosition.sol";
7
+ import {MarketConstants} from "./MarketConstants.sol";
8
+ import {FullMath} from "../utils/uniswap/FullMath.sol";
9
+ import {TickMath} from "../utils/uniswap/TickMath.sol";
10
+ import {IDopplerErrors} from "../interfaces/IDopplerErrors.sol";
11
+ import {DopplerMath} from "./DopplerMath.sol";
12
+
13
+ library CoinDopplerMultiCurve {
14
+ error ArrayLengthMismatch();
15
+ error ZeroDiscoveryPositions();
16
+ error ZeroDiscoverySupplyShare();
17
+ error InvalidTickRangeMisordered(int24 tickLower, int24 tickUpper);
18
+ error ConfigTickLowerMustBeLessThanTickUpper();
19
+
20
+ /**
21
+ * @notice Configures multi-curve liquidity based on the provided parameters.
22
+ * @param isCoinToken0 A boolean indicating if the coin is token0 (true) or token1 (false) in the pair.
23
+ * This affects tick ordering and price calculations.
24
+ * @param poolConfig_ ABI encoded data containing the pool configuration parameters.
25
+ * It is expected to be encoded in the following order:
26
+ * - version (uint8): The version of the pool configuration.
27
+ * (e.g., 2 for UniswapV3, 4 for Doppler/Uniswap V4).
28
+ * - currency (address): The address of the currency token (e.g., WETH) paired with the coin.
29
+ * - tickLower (int24[]): An array of lower tick boundaries for each liquidity curve.
30
+ * - tickUpper (int24[]): An array of upper tick boundaries for each liquidity curve.
31
+ * - numDiscoveryPositions (uint16[]): An array specifying the number of discrete liquidity
32
+ * positions within each curve's discovery phase.
33
+ * - maxDiscoverySupplyShare (uint256[]): An array of WAD-scaled values (1e18) representing
34
+ * the maximum share of the coin's total supply
35
+ * allocated to each curve's discovery phase.
36
+ * @return sqrtPriceX96 The initial square root price of the pool, scaled to X96 format.
37
+ * @return poolConfiguration A struct containing the configured pool parameters,
38
+ * including version, number of positions, fee, tick spacing,
39
+ * and arrays for discovery positions, tick boundaries, and supply shares.
40
+ */
41
+ function setupPool(bool isCoinToken0, bytes memory poolConfig_) internal pure returns (uint160 sqrtPriceX96, PoolConfiguration memory poolConfiguration) {
42
+ (, , int24[] memory tickLower_, int24[] memory tickUpper_, uint16[] memory numDiscoveryPositions_, uint256[] memory maxDiscoverySupplyShare_) = abi
43
+ .decode(poolConfig_, (uint8, address, int24[], int24[], uint16[], uint256[]));
44
+
45
+ uint256 numCurves = tickLower_.length;
46
+ if (numCurves != tickUpper_.length || numCurves != numDiscoveryPositions_.length || numCurves != maxDiscoverySupplyShare_.length) {
47
+ revert ArrayLengthMismatch();
48
+ }
49
+
50
+ uint256 totalDiscoverySupplyShare;
51
+ uint256 totalDiscoveryPositions;
52
+
53
+ int24 boundryTickLower = DopplerMath.alignTickToTickSpacing(isCoinToken0, TickMath.MAX_TICK, MarketConstants.TICK_SPACING);
54
+ int24 boundryTickUpper = DopplerMath.alignTickToTickSpacing(isCoinToken0, TickMath.MIN_TICK, MarketConstants.TICK_SPACING);
55
+
56
+ // For each curve:
57
+ for (uint256 i; i < numCurves; i++) {
58
+ // Ensure a value is specified
59
+ require(numDiscoveryPositions_[i] > 0, ZeroDiscoveryPositions());
60
+ require(maxDiscoverySupplyShare_[i] > 0, ZeroDiscoverySupplyShare());
61
+
62
+ // Aggregate the total discovery positions and supply across curves
63
+ totalDiscoveryPositions += numDiscoveryPositions_[i];
64
+ totalDiscoverySupplyShare += maxDiscoverySupplyShare_[i];
65
+
66
+ int24 currentTickLower = DopplerMath.alignTickToTickSpacing(isCoinToken0, tickLower_[i], MarketConstants.TICK_SPACING);
67
+ int24 currentTickUpper = DopplerMath.alignTickToTickSpacing(isCoinToken0, tickUpper_[i], MarketConstants.TICK_SPACING);
68
+
69
+ require(currentTickLower < currentTickUpper, ConfigTickLowerMustBeLessThanTickUpper());
70
+
71
+ // Sort the tick values based on token order
72
+ tickLower_[i] = isCoinToken0 ? currentTickLower : -currentTickUpper;
73
+ tickUpper_[i] = isCoinToken0 ? currentTickUpper : -currentTickLower;
74
+
75
+ boundryTickLower = boundryTickLower < tickLower_[i] ? boundryTickLower : tickLower_[i];
76
+ boundryTickUpper = boundryTickUpper > tickUpper_[i] ? boundryTickUpper : tickUpper_[i];
77
+ }
78
+
79
+ require(boundryTickLower < boundryTickUpper, InvalidTickRangeMisordered(boundryTickLower, boundryTickUpper));
80
+ require(totalDiscoveryPositions > 1 && totalDiscoveryPositions <= 200, IDopplerErrors.NumDiscoveryPositionsOutOfRange());
81
+ require(totalDiscoverySupplyShare < MarketConstants.WAD, IDopplerErrors.MaxShareToBeSoldExceeded(totalDiscoverySupplyShare, MarketConstants.WAD));
82
+
83
+ sqrtPriceX96 = TickMath.getSqrtPriceAtTick(isCoinToken0 ? boundryTickLower : boundryTickUpper);
84
+
85
+ poolConfiguration = PoolConfiguration({
86
+ version: CoinConfigurationVersions.DOPPLER_MULTICURVE_UNI_V4_POOL_VERSION,
87
+ numPositions: uint16(totalDiscoveryPositions + 1), // Add one for the final tail position
88
+ fee: MarketConstants.LP_FEE_V4,
89
+ tickSpacing: MarketConstants.TICK_SPACING,
90
+ numDiscoveryPositions: numDiscoveryPositions_,
91
+ tickLower: tickLower_,
92
+ tickUpper: tickUpper_,
93
+ maxDiscoverySupplyShare: maxDiscoverySupplyShare_
94
+ });
95
+ }
96
+
97
+ /// @notice Calculates the LP positions for a given multi-curve configuration
98
+ function calculatePositions(bool isCoinToken0, PoolConfiguration memory poolConfiguration) internal pure returns (LpPosition[] memory positions) {
99
+ positions = new LpPosition[](poolConfiguration.numPositions);
100
+
101
+ uint256 discoverySupply;
102
+ uint256 currentPositionOffset;
103
+ uint256 numCurves = poolConfiguration.tickLower.length;
104
+
105
+ for (uint256 i; i < numCurves; i++) {
106
+ uint256 curveSupply = FullMath.mulDiv(MarketConstants.POOL_LAUNCH_SUPPLY, poolConfiguration.maxDiscoverySupplyShare[i], MarketConstants.WAD);
107
+
108
+ (positions, curveSupply) = DopplerMath.calculateLogNormalDistribution(
109
+ poolConfiguration.tickLower[i],
110
+ poolConfiguration.tickUpper[i],
111
+ MarketConstants.TICK_SPACING,
112
+ isCoinToken0,
113
+ curveSupply,
114
+ poolConfiguration.numDiscoveryPositions[i],
115
+ positions,
116
+ currentPositionOffset
117
+ );
118
+
119
+ discoverySupply += curveSupply;
120
+ currentPositionOffset += poolConfiguration.numDiscoveryPositions[i];
121
+ }
122
+
123
+ uint256 tailSupply = MarketConstants.POOL_LAUNCH_SUPPLY - discoverySupply;
124
+
125
+ // Calculate the tail position (the last position in the array)
126
+ positions[poolConfiguration.numPositions - 1] = DopplerMath.calculateLpTail(
127
+ poolConfiguration.tickLower[numCurves - 1],
128
+ poolConfiguration.tickUpper[numCurves - 1],
129
+ isCoinToken0,
130
+ tailSupply,
131
+ MarketConstants.TICK_SPACING
132
+ );
133
+ }
134
+ }