@zoralabs/coins 2.5.0 → 2.6.1

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 (48) hide show
  1. package/.turbo/turbo-build$colon$js.log +136 -130
  2. package/CHANGELOG.md +28 -17
  3. package/abis/BaseCoin.json +5 -0
  4. package/abis/ContentCoin.json +5 -0
  5. package/abis/ICoin.json +5 -0
  6. package/abis/ICoinV3.json +5 -0
  7. package/abis/ITrendCoin.json +140 -0
  8. package/abis/ITrendCoinErrors.json +33 -0
  9. package/abis/IUniversalRouter.json +61 -0
  10. package/abis/IZoraFactory.json +237 -0
  11. package/abis/TrendCoin.json +2053 -0
  12. package/abis/ZoraFactoryImpl.json +242 -0
  13. package/dist/index.cjs +955 -138
  14. package/dist/index.cjs.map +1 -1
  15. package/dist/index.js +953 -138
  16. package/dist/index.js.map +1 -1
  17. package/dist/wagmiGenerated.d.ts +1388 -149
  18. package/dist/wagmiGenerated.d.ts.map +1 -1
  19. package/foundry.toml +1 -0
  20. package/package/wagmiGenerated.ts +962 -139
  21. package/package.json +2 -2
  22. package/src/BaseCoin.sol +12 -12
  23. package/src/ContentCoin.sol +20 -1
  24. package/src/CreatorCoin.sol +3 -0
  25. package/src/TrendCoin.sol +117 -0
  26. package/src/ZoraFactoryImpl.sol +142 -1
  27. package/src/hooks/ZoraV4CoinHook.sol +17 -7
  28. package/src/interfaces/ICoin.sol +5 -1
  29. package/src/interfaces/ICreatorCoin.sol +0 -3
  30. package/src/interfaces/IPoolManager.sol +13 -0
  31. package/src/interfaces/ITrendCoin.sol +26 -0
  32. package/src/interfaces/ITrendCoinErrors.sol +24 -0
  33. package/src/interfaces/IZoraFactory.sol +60 -1
  34. package/src/libs/CoinConstants.sol +13 -1
  35. package/src/libs/CoinRewardsV4.sol +82 -21
  36. package/src/libs/TickerUtils.sol +66 -0
  37. package/src/libs/UniV4SwapToCurrency.sol +2 -1
  38. package/src/version/ContractVersionBase.sol +1 -1
  39. package/test/CoinRewardsV4.t.sol +48 -0
  40. package/test/CreatorCoin.t.sol +2 -1
  41. package/test/Factory.t.sol +31 -5
  42. package/test/LaunchFee.t.sol +0 -2
  43. package/test/LiquidityMigration.t.sol +0 -2
  44. package/test/TrendCoin.t.sol +1128 -0
  45. package/test/Upgrades.t.sol +16 -3
  46. package/test/utils/FeeEstimatorHook.sol +36 -10
  47. package/test/utils/V4TestSetup.sol +36 -4
  48. package/wagmi.config.ts +2 -0
@@ -65,7 +65,8 @@ contract UpgradesTest is BaseTest, ForkedCoinsAddresses {
65
65
  seller: makeAddr("seller"),
66
66
  coinRecipient: makeAddr("coinRecipient"),
67
67
  tradeReferrer: makeAddr("tradeReferrer"),
68
- dopplerRecipient: makeAddr("dopplerRecipient")
68
+ dopplerRecipient: makeAddr("dopplerRecipient"),
69
+ metadataManager: makeAddr("metadataManager")
69
70
  });
70
71
  }
71
72
 
@@ -75,7 +76,13 @@ contract UpgradesTest is BaseTest, ForkedCoinsAddresses {
75
76
 
76
77
  factoryProxy = ZoraFactoryImpl(0x777777751622c0d3258f214F9DF38E35BF45baF3);
77
78
 
78
- ZoraFactoryImpl newImpl = new ZoraFactoryImpl(address(coinV4Impl), address(creatorCoinImpl), address(hook), address(zoraHookRegistry));
79
+ ZoraFactoryImpl newImpl = new ZoraFactoryImpl(
80
+ address(coinV4Impl),
81
+ address(creatorCoinImpl),
82
+ address(trendCoinImpl),
83
+ address(hook),
84
+ address(zoraHookRegistry)
85
+ );
79
86
 
80
87
  vm.prank(factoryProxy.owner());
81
88
  factoryProxy.upgradeToAndCall(address(newImpl), "");
@@ -90,7 +97,13 @@ contract UpgradesTest is BaseTest, ForkedCoinsAddresses {
90
97
 
91
98
  factoryProxy = ZoraFactoryImpl(0x777777751622c0d3258f214F9DF38E35BF45baF3);
92
99
 
93
- ZoraFactoryImpl newImpl = new ZoraFactoryImpl(address(coinV4Impl), address(creatorCoinImpl), address(hook), address(zoraHookRegistry));
100
+ ZoraFactoryImpl newImpl = new ZoraFactoryImpl(
101
+ address(coinV4Impl),
102
+ address(creatorCoinImpl),
103
+ address(trendCoinImpl),
104
+ address(hook),
105
+ address(zoraHookRegistry)
106
+ );
94
107
 
95
108
  vm.prank(factoryProxy.owner());
96
109
  factoryProxy.upgradeToAndCall(address(newImpl), "");
@@ -10,10 +10,11 @@ import {IHasRewardsRecipients} from "../../src/interfaces/IHasRewardsRecipients.
10
10
  import {Currency} from "@uniswap/v4-core/src/types/Currency.sol";
11
11
  import {SwapParams} from "@uniswap/v4-core/src/types/PoolOperation.sol";
12
12
  import {BalanceDelta} from "@uniswap/v4-core/src/types/BalanceDelta.sol";
13
+ import {TransientStateLibrary} from "@uniswap/v4-core/src/libraries/TransientStateLibrary.sol";
13
14
  import {CoinCommon} from "../../src/libs/CoinCommon.sol";
14
15
  import {V4Liquidity} from "../../src/libs/V4Liquidity.sol";
15
16
  import {BaseHook} from "@uniswap/v4-periphery/src/utils/BaseHook.sol";
16
- import {ICoin, IHasSwapPath} from "../../src/interfaces/ICoin.sol";
17
+ import {ICoin, IHasSwapPath, IHasCoinType} from "../../src/interfaces/ICoin.sol";
17
18
  import {UniV4SwapToCurrency} from "../../src/libs/UniV4SwapToCurrency.sol";
18
19
  import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
19
20
  import {CoinRewardsV4} from "../../src/libs/CoinRewardsV4.sol";
@@ -65,21 +66,46 @@ contract FeeEstimatorHook is ZoraV4CoinHook {
65
66
  uint256 coinBalanceBefore = IERC20(coin).balanceOf(address(this));
66
67
  uint256 currencyBalanceBefore = IERC20(ICoin(coin).currency()).balanceOf(address(this));
67
68
 
68
- IHasSwapPath.PayoutSwapPath memory payoutSwapPath = IHasSwapPath(coin).getPayoutSwapPath(coinVersionLookup);
69
-
70
69
  int128 fee0;
71
70
  int128 fee1;
72
71
 
73
72
  (fee0, fee1) = V4Liquidity.collectFees(poolManager, key, poolCoins[poolKeyHash].positions);
74
73
 
75
- (uint128 remainingFee0, uint128 remainingFee1) = CoinRewardsV4.mintLpReward(poolManager, key, fee0, fee1);
74
+ IHasCoinType.CoinType coinType = CoinRewardsV4.getCoinType(IHasRewardsRecipients(coin));
75
+ (uint128 remainingFee0, uint128 remainingFee1) = CoinRewardsV4.mintLpReward(poolManager, key, fee0, fee1, coinType);
76
+
77
+ // Execute the swap path to estimate the payout amount, but don't distribute
78
+ // This mirrors the logic in ZoraV4CoinHook._afterSwap
79
+ IHasSwapPath.PayoutSwapPath memory payoutSwapPath = IHasSwapPath(coin).getPayoutSwapPath(coinVersionLookup);
76
80
 
77
- (feeState.afterSwapCurrency, feeState.afterSwapCurrencyAmount) = CoinRewardsV4.convertToPayoutCurrency(
78
- poolManager,
79
- remainingFee0,
80
- remainingFee1,
81
- payoutSwapPath
82
- );
81
+ // Execute swap and track all deltas that result
82
+ UniV4SwapToCurrency.swapToPath(poolManager, remainingFee0, remainingFee1, payoutSwapPath.currencyIn, payoutSwapPath.path);
83
+
84
+ // Take all positive deltas and sum them to get total payout amount
85
+ uint128 totalPayout = 0;
86
+
87
+ // Check currencyIn for positive delta
88
+ int256 deltaIn = TransientStateLibrary.currencyDelta(poolManager, address(this), payoutSwapPath.currencyIn);
89
+ if (deltaIn > 0) {
90
+ uint128 amount = uint128(uint256(deltaIn));
91
+ poolManager.take(payoutSwapPath.currencyIn, address(this), amount);
92
+ totalPayout += amount;
93
+ }
94
+
95
+ // Check all intermediate currencies for positive deltas
96
+ for (uint256 i = 0; i < payoutSwapPath.path.length; i++) {
97
+ Currency intermediateCurrency = payoutSwapPath.path[i].intermediateCurrency;
98
+ int256 delta = TransientStateLibrary.currencyDelta(poolManager, address(this), intermediateCurrency);
99
+ if (delta > 0) {
100
+ uint128 amount = uint128(uint256(delta));
101
+ poolManager.take(intermediateCurrency, address(this), amount);
102
+ totalPayout += amount;
103
+ // Track the last positive currency as the payout currency
104
+ feeState.afterSwapCurrency = intermediateCurrency;
105
+ }
106
+ }
107
+
108
+ feeState.afterSwapCurrencyAmount = totalPayout;
83
109
 
84
110
  feeState.fees0 += uint128(fee0);
85
111
  feeState.fees1 += uint128(fee1);
@@ -29,6 +29,7 @@ import {ZoraFactoryImpl} from "../../src/ZoraFactoryImpl.sol";
29
29
  import {ZoraFactory} from "../../src/proxy/ZoraFactory.sol";
30
30
  import {ContentCoin} from "../../src/ContentCoin.sol";
31
31
  import {CreatorCoin} from "../../src/CreatorCoin.sol";
32
+ import {TrendCoin} from "../../src/TrendCoin.sol";
32
33
  import {CoinConfigurationVersions} from "../../src/libs/CoinConfigurationVersions.sol";
33
34
  import {CoinConstants} from "../../src/libs/CoinConstants.sol";
34
35
  import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
@@ -75,6 +76,7 @@ contract V4TestSetup is Test, ContractAddresses {
75
76
  address coinRecipient;
76
77
  address tradeReferrer;
77
78
  address dopplerRecipient;
79
+ address metadataManager;
78
80
  }
79
81
 
80
82
  // Fork management
@@ -101,6 +103,7 @@ contract V4TestSetup is Test, ContractAddresses {
101
103
  // Zora protocol contracts
102
104
  ContentCoin internal coinV4Impl;
103
105
  CreatorCoin internal creatorCoinImpl;
106
+ TrendCoin internal trendCoinImpl;
104
107
  ZoraFactoryImpl internal factoryImpl;
105
108
  IZoraFactory internal factory;
106
109
  ZoraV4CoinHook internal hook;
@@ -147,7 +150,8 @@ contract V4TestSetup is Test, ContractAddresses {
147
150
  seller: makeAddr("seller"),
148
151
  coinRecipient: makeAddr("coinRecipient"),
149
152
  tradeReferrer: makeAddr("tradeReferrer"),
150
- dopplerRecipient: makeAddr("dopplerRecipient")
153
+ dopplerRecipient: makeAddr("dopplerRecipient"),
154
+ metadataManager: makeAddr("metadataManager")
151
155
  });
152
156
 
153
157
  ProxyShim mockUpgradeableImpl = new ProxyShim();
@@ -168,12 +172,17 @@ contract V4TestSetup is Test, ContractAddresses {
168
172
 
169
173
  creatorCoinImpl = new CreatorCoin(users.feeRecipient, address(protocolRewards), IPoolManager(V4_POOL_MANAGER), DOPPLER_AIRLOCK);
170
174
 
171
- factoryImpl = new ZoraFactoryImpl(address(coinV4Impl), address(creatorCoinImpl), address(hook), address(zoraHookRegistry));
175
+ trendCoinImpl = new TrendCoin(users.feeRecipient, address(protocolRewards), IPoolManager(V4_POOL_MANAGER), DOPPLER_AIRLOCK, users.metadataManager);
176
+
177
+ factoryImpl = new ZoraFactoryImpl(address(coinV4Impl), address(creatorCoinImpl), address(trendCoinImpl), address(hook), address(zoraHookRegistry));
172
178
  UUPSUpgradeable(address(factory)).upgradeToAndCall(address(factoryImpl), "");
173
179
  factory = IZoraFactory(address(factory));
174
180
 
175
181
  ZoraFactoryImpl(address(factory)).initialize(users.factoryOwner);
176
182
 
183
+ // Set trend coin pool config
184
+ _setTrendCoinPoolConfig();
185
+
177
186
  vm.label(address(factory), "ZORA_FACTORY");
178
187
  vm.label(address(protocolRewards), "PROTOCOL_REWARDS");
179
188
  vm.label(address(nonfungiblePositionManager), "NONFUNGIBLE_POSITION_MANAGER");
@@ -208,7 +217,8 @@ contract V4TestSetup is Test, ContractAddresses {
208
217
  seller: makeAddr("seller"),
209
218
  coinRecipient: makeAddr("coinRecipient"),
210
219
  tradeReferrer: makeAddr("tradeReferrer"),
211
- dopplerRecipient: makeAddr("dopplerRecipient")
220
+ dopplerRecipient: makeAddr("dopplerRecipient"),
221
+ metadataManager: makeAddr("metadataManager")
212
222
  });
213
223
 
214
224
  // Deploy mock airlock with the dopplerRecipient as owner (for doppler rewards)
@@ -250,12 +260,16 @@ contract V4TestSetup is Test, ContractAddresses {
250
260
  // Deploy coin implementations
251
261
  coinV4Impl = new ContentCoin(users.feeRecipient, address(protocolRewards), poolManager, address(mockAirlock));
252
262
  creatorCoinImpl = new CreatorCoin(users.feeRecipient, address(protocolRewards), poolManager, address(mockAirlock));
263
+ trendCoinImpl = new TrendCoin(users.feeRecipient, address(protocolRewards), poolManager, address(mockAirlock), users.metadataManager);
253
264
 
254
265
  // Deploy and initialize factory implementation
255
- factoryImpl = new ZoraFactoryImpl(address(coinV4Impl), address(creatorCoinImpl), address(hook), address(zoraHookRegistry));
266
+ factoryImpl = new ZoraFactoryImpl(address(coinV4Impl), address(creatorCoinImpl), address(trendCoinImpl), address(hook), address(zoraHookRegistry));
256
267
  UUPSUpgradeable(address(factory)).upgradeToAndCall(address(factoryImpl), "");
257
268
  ZoraFactoryImpl(address(factory)).initialize(users.factoryOwner);
258
269
 
270
+ // Set trend coin pool config
271
+ _setTrendCoinPoolConfig();
272
+
259
273
  // Deploy mock V3 swap router for non-forked tests
260
274
  swapRouter = ISwapRouter(address(new MockSwapRouter()));
261
275
 
@@ -271,6 +285,24 @@ contract V4TestSetup is Test, ContractAddresses {
271
285
  vm.label(address(swapRouter), "MOCK_SWAP_ROUTER");
272
286
  }
273
287
 
288
+ // ============================================
289
+ // Trend Coin Pool Config Helper
290
+ // ============================================
291
+
292
+ function _setTrendCoinPoolConfig() internal {
293
+ (
294
+ ,
295
+ address currency,
296
+ int24[] memory tickLower,
297
+ int24[] memory tickUpper,
298
+ uint16[] memory numDiscoveryPositions,
299
+ uint256[] memory maxDiscoverySupplyShare
300
+ ) = CoinConfigurationVersions.decodeDopplerMultiCurveUniV4(CoinConstants.TREND_COIN_DEFAULT_POOL_CONFIG);
301
+
302
+ vm.prank(users.factoryOwner);
303
+ factory.setTrendCoinPoolConfig(currency, tickLower, tickUpper, numDiscoveryPositions, maxDiscoverySupplyShare);
304
+ }
305
+
274
306
  // ============================================
275
307
  // V4 Infrastructure Deployment (Non-Forked)
276
308
  // ============================================
package/wagmi.config.ts CHANGED
@@ -16,9 +16,11 @@ export default defineConfig({
16
16
  "IUniswapV3Pool",
17
17
  "IPoolConfigEncoding",
18
18
  "IPermit2",
19
+ "IPoolManager",
19
20
  "AutoSwapper",
20
21
  "BuySupplyWithV4SwapHook",
21
22
  "ZoraV4CoinHook",
23
+ "IUniversalRouter"
22
24
  ].map((contractName) => `${contractName}.json`),
23
25
  }),
24
26
  ],