@zoralabs/coins 0.9.0 → 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 (124) hide show
  1. package/.turbo/turbo-build.log +131 -114
  2. package/CHANGELOG.md +40 -0
  3. package/abis/BaseCoin.json +26 -118
  4. package/abis/BaseTest.json +47 -0
  5. package/abis/Coin.json +171 -63
  6. package/abis/CoinDopplerMultiCurve.json +38 -0
  7. package/abis/CoinRewardsV4.json +54 -0
  8. package/abis/CoinTest.json +53 -20
  9. package/abis/CoinUniV4Test.json +1053 -0
  10. package/abis/CoinV4.json +234 -211
  11. package/abis/DeployScript.json +47 -0
  12. package/abis/DeployedCoinVersionLookup.json +21 -0
  13. package/abis/DeployedCoinVersionLookupTest.json +716 -0
  14. package/abis/DifferentNamespaceVersionLookup.json +39 -0
  15. package/abis/DopplerUniswapV3Test.json +49 -93
  16. package/abis/ERC20.json +310 -0
  17. package/abis/FactoryTest.json +85 -7
  18. package/abis/FeeEstimatorHook.json +1528 -0
  19. package/abis/HooksDeployment.json +23 -0
  20. package/abis/HooksTest.json +47 -0
  21. package/abis/ICoin.json +40 -71
  22. package/abis/ICoinV3.json +879 -0
  23. package/abis/ICoinV4.json +915 -0
  24. package/abis/IDeployedCoinVersionLookup.json +21 -0
  25. package/abis/IERC721.json +36 -36
  26. package/abis/IHasPoolKey.json +42 -0
  27. package/abis/IHasRewardsRecipients.json +54 -0
  28. package/abis/IHasSwapPath.json +60 -0
  29. package/abis/IMsgSender.json +15 -0
  30. package/abis/IPoolConfigEncoding.json +46 -0
  31. package/abis/ISwapPathRouter.json +92 -0
  32. package/abis/IUniversalRouter.json +61 -0
  33. package/abis/IUnlockCallback.json +21 -0
  34. package/abis/IV4Quoter.json +310 -0
  35. package/abis/IZoraFactory.json +191 -11
  36. package/abis/IZoraV4CoinHook.json +348 -4
  37. package/abis/MockERC20.json +21 -0
  38. package/abis/MultiOwnableTest.json +47 -0
  39. package/abis/{CoinConfigurationVersions.json → Position.json} +1 -1
  40. package/abis/PrintUpgradeCommand.json +9 -0
  41. package/abis/ProxyShim.json +24 -0
  42. package/abis/StateLibrary.json +80 -0
  43. package/abis/TestDeployedCoinVersionLookupImplementation.json +39 -0
  44. package/abis/TestV4Swap.json +9 -0
  45. package/abis/UpgradeCoinImpl.json +47 -0
  46. package/abis/UpgradesTest.json +67 -0
  47. package/abis/Vm.json +1482 -111
  48. package/abis/VmSafe.json +856 -32
  49. package/abis/ZoraFactoryImpl.json +339 -1
  50. package/abis/ZoraV4CoinHook.json +455 -5
  51. package/addresses/8453.json +8 -4
  52. package/addresses/84532.json +8 -4
  53. package/dist/index.cjs +1920 -169
  54. package/dist/index.cjs.map +1 -1
  55. package/dist/index.js +1916 -169
  56. package/dist/index.js.map +1 -1
  57. package/dist/wagmiGenerated.d.ts +2599 -183
  58. package/dist/wagmiGenerated.d.ts.map +1 -1
  59. package/package/wagmiGenerated.ts +1928 -165
  60. package/package.json +8 -3
  61. package/remappings.txt +6 -1
  62. package/script/CoinsDeployerBase.sol +74 -11
  63. package/script/DeployDevFactory.s.sol +21 -0
  64. package/script/PrintUpgradeCommand.s.sol +13 -0
  65. package/script/Simulate.s.sol +1 -10
  66. package/script/TestBackingCoinSwap.s.sol +146 -0
  67. package/script/TestV4Swap.s.sol +136 -0
  68. package/script/UpgradeFactoryImpl.s.sol +1 -1
  69. package/src/BaseCoin.sol +176 -0
  70. package/src/Coin.sol +87 -202
  71. package/src/CoinV4.sol +121 -0
  72. package/src/ZoraFactoryImpl.sol +208 -36
  73. package/src/hooks/ZoraV4CoinHook.sol +195 -0
  74. package/src/hooks/{BaseCoinDeployHook.sol → deployment/BaseCoinDeployHook.sol} +3 -3
  75. package/src/hooks/{BuySupplyWithSwapRouterHook.sol → deployment/BuySupplyWithSwapRouterHook.sol} +7 -5
  76. package/src/interfaces/ICoin.sol +31 -39
  77. package/src/interfaces/ICoinV3.sol +71 -0
  78. package/src/interfaces/ICoinV4.sol +69 -0
  79. package/src/interfaces/IDeployedCoinVersionLookup.sol +11 -0
  80. package/src/interfaces/IMsgSender.sol +9 -0
  81. package/src/interfaces/IPoolConfigEncoding.sol +14 -0
  82. package/src/interfaces/ISwapPathRouter.sol +14 -0
  83. package/src/interfaces/IZoraFactory.sol +65 -27
  84. package/src/interfaces/IZoraV4CoinHook.sol +116 -0
  85. package/src/libs/CoinCommon.sol +15 -0
  86. package/src/libs/CoinConfigurationVersions.sol +116 -1
  87. package/src/libs/CoinConstants.sol +5 -0
  88. package/src/libs/CoinDopplerMultiCurve.sol +134 -0
  89. package/src/libs/CoinDopplerUniV3.sol +19 -171
  90. package/src/libs/CoinRewards.sol +195 -0
  91. package/src/libs/CoinRewardsV4.sol +180 -0
  92. package/src/libs/CoinSetup.sol +57 -0
  93. package/src/libs/CoinSetupV3.sol +6 -67
  94. package/src/libs/DopplerMath.sol +156 -0
  95. package/src/libs/HooksDeployment.sol +84 -0
  96. package/src/libs/MarketConstants.sol +4 -0
  97. package/src/libs/PoolStateReader.sol +22 -0
  98. package/src/libs/UniV3BuySell.sol +74 -292
  99. package/src/libs/UniV4SwapHelper.sol +65 -0
  100. package/src/libs/UniV4SwapToCurrency.sol +109 -0
  101. package/src/libs/V4Liquidity.sol +129 -0
  102. package/src/types/PoolConfiguration.sol +15 -0
  103. package/src/utils/DeployedCoinVersionLookup.sol +52 -0
  104. package/src/version/ContractVersionBase.sol +1 -1
  105. package/test/Coin.t.sol +78 -88
  106. package/test/CoinDopplerUniV3.t.sol +32 -171
  107. package/test/CoinUniV4.t.sol +752 -0
  108. package/test/{Hooks.t.sol → DeploymentHooks.t.sol} +2 -6
  109. package/test/Factory.t.sol +80 -47
  110. package/test/MultiOwnable.t.sol +6 -3
  111. package/test/Upgrades.t.sol +6 -5
  112. package/test/mocks/MockERC20.sol +12 -0
  113. package/test/utils/BaseTest.sol +106 -56
  114. package/test/utils/DeployedCoinVersionLookup.t.sol +127 -0
  115. package/test/utils/FeeEstimatorHook.sol +84 -0
  116. package/test/utils/ProxyShim.sol +17 -0
  117. package/wagmi.config.ts +4 -0
  118. package/.env +0 -1
  119. package/.turbo/turbo-update-contract-version.log +0 -22
  120. package/abis/CoinSetupV3.json +0 -7
  121. package/abis/HookDeployer.json +0 -68
  122. package/abis/IHookDeployer.json +0 -42
  123. package/src/libs/CoinLegacy.sol +0 -48
  124. package/src/libs/CoinLegacyMarket.sol +0 -182
@@ -0,0 +1,127 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.23;
3
+
4
+ import {BaseTest} from "../utils/BaseTest.sol";
5
+ import {DeployedCoinVersionLookup} from "../../src/utils/DeployedCoinVersionLookup.sol";
6
+
7
+ contract TestDeployedCoinVersionLookupImplementation is DeployedCoinVersionLookup {
8
+ function setVersionForTesting(address coin, uint8 version) external {
9
+ _setVersionForDeployedCoin(coin, version);
10
+ }
11
+ }
12
+
13
+ /**
14
+ * @title Mock implementation with different namespace
15
+ * @dev Used to verify that different namespaces don't collide
16
+ */
17
+ contract DifferentNamespaceVersionLookup {
18
+ /// @custom:storage-location erc7201:different.namespace
19
+ struct DeployedCoinVersionStorage {
20
+ mapping(address => uint8) deployedCoinWithVersion;
21
+ }
22
+
23
+ // keccak256(abi.encode(uint256(keccak256("different.namespace")) - 1)) & ~bytes32(uint256(0xff))
24
+ bytes32 private constant DEPLOYED_COIN_VERSION_STORAGE_LOCATION = 0xf0ec9c7ea8b861b539967dd0659fb8887a9724eca55e932839a2a8e01f50c400;
25
+
26
+ function _getDeployedCoinVersionStorage() private pure returns (DeployedCoinVersionStorage storage $) {
27
+ assembly {
28
+ $.slot := DEPLOYED_COIN_VERSION_STORAGE_LOCATION
29
+ }
30
+ }
31
+
32
+ function getVersionForDeployedCoin(address coin) public view returns (uint8) {
33
+ return _getDeployedCoinVersionStorage().deployedCoinWithVersion[coin];
34
+ }
35
+
36
+ function setVersionForTesting(address coin, uint8 version) external {
37
+ _getDeployedCoinVersionStorage().deployedCoinWithVersion[coin] = version;
38
+ }
39
+ }
40
+
41
+ contract DeployedCoinVersionLookupTest is BaseTest {
42
+ TestDeployedCoinVersionLookupImplementation public versionLookup;
43
+ DifferentNamespaceVersionLookup public differentNamespaceLookup;
44
+ address public testCoin1;
45
+ address public testCoin2;
46
+ address public testContractAddress;
47
+
48
+ function setUp() public override {
49
+ super.setUp();
50
+ versionLookup = new TestDeployedCoinVersionLookupImplementation();
51
+ differentNamespaceLookup = new DifferentNamespaceVersionLookup();
52
+ testCoin1 = makeAddr("testCoin1");
53
+ testCoin2 = makeAddr("testCoin2");
54
+ testContractAddress = makeAddr("testContractAddress");
55
+ }
56
+
57
+ function test_getAndSetVersionForDeployedCoin() public {
58
+ // Default version should be 0
59
+ assertEq(versionLookup.getVersionForDeployedCoin(testCoin1), 0);
60
+
61
+ // Set version and verify
62
+ versionLookup.setVersionForTesting(testCoin1, 1);
63
+ assertEq(versionLookup.getVersionForDeployedCoin(testCoin1), 1);
64
+
65
+ // Set version for a different coin
66
+ versionLookup.setVersionForTesting(testCoin2, 2);
67
+ assertEq(versionLookup.getVersionForDeployedCoin(testCoin2), 2);
68
+
69
+ // First coin's version should remain unchanged
70
+ assertEq(versionLookup.getVersionForDeployedCoin(testCoin1), 1);
71
+
72
+ // Update version and verify
73
+ versionLookup.setVersionForTesting(testCoin1, 3);
74
+ assertEq(versionLookup.getVersionForDeployedCoin(testCoin1), 3);
75
+ }
76
+
77
+ function test_differentNamespaceIndependence() public {
78
+ // First deploy the original implementation at a fixed address
79
+ TestDeployedCoinVersionLookupImplementation originalImpl = new TestDeployedCoinVersionLookupImplementation();
80
+ bytes memory originalBytecode = address(originalImpl).code;
81
+
82
+ // Deploy a different implementation
83
+ DifferentNamespaceVersionLookup differentImpl = new DifferentNamespaceVersionLookup();
84
+ bytes memory differentBytecode = address(differentImpl).code;
85
+
86
+ // Etch the original implementation to the test address
87
+ vm.etch(testContractAddress, originalBytecode);
88
+
89
+ // Test setting values with the first implementation
90
+ TestDeployedCoinVersionLookupImplementation(testContractAddress).setVersionForTesting(testCoin1, 42);
91
+ assertEq(TestDeployedCoinVersionLookupImplementation(testContractAddress).getVersionForDeployedCoin(testCoin1), 42);
92
+
93
+ // Save the bytecode location for the first implementation
94
+ bytes32 firstSlot = vm.load(
95
+ testContractAddress,
96
+ bytes32(uint256(keccak256(abi.encode(testCoin1, 0x9a79df0b86f39d0543c14aee714123562f798115071e932933bcc3e29cc86f00))))
97
+ );
98
+ assertEq(uint256(firstSlot), 42);
99
+
100
+ // Now replace the code with the different namespace implementation
101
+ vm.etch(testContractAddress, differentBytecode);
102
+
103
+ // Set a value with the different implementation
104
+ DifferentNamespaceVersionLookup(testContractAddress).setVersionForTesting(testCoin1, 99);
105
+
106
+ // This should use a different storage slot, so it shouldn't affect the original value
107
+ assertEq(DifferentNamespaceVersionLookup(testContractAddress).getVersionForDeployedCoin(testCoin1), 99);
108
+
109
+ // Verify the original storage slot still has the original value
110
+ bytes32 secondSlot = vm.load(
111
+ testContractAddress,
112
+ bytes32(uint256(keccak256(abi.encode(testCoin1, 0xf0ec9c7ea8b861b539967dd0659fb8887a9724eca55e932839a2a8e01f50c400))))
113
+ );
114
+ assertEq(uint256(secondSlot), 99);
115
+
116
+ // Switch back to the original implementation to verify its storage is unchanged
117
+ vm.etch(testContractAddress, originalBytecode);
118
+ assertEq(TestDeployedCoinVersionLookupImplementation(testContractAddress).getVersionForDeployedCoin(testCoin1), 42);
119
+
120
+ // Change the value in the original implementation
121
+ TestDeployedCoinVersionLookupImplementation(testContractAddress).setVersionForTesting(testCoin1, 123);
122
+
123
+ // Switch back to the different namespace implementation to verify its storage is unchanged
124
+ vm.etch(testContractAddress, differentBytecode);
125
+ assertEq(DifferentNamespaceVersionLookup(testContractAddress).getVersionForDeployedCoin(testCoin1), 99);
126
+ }
127
+ }
@@ -0,0 +1,84 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.13;
3
+
4
+ import {ZoraV4CoinHook} from "../../src/hooks/ZoraV4CoinHook.sol";
5
+ import {IPoolManager, PoolKey} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol";
6
+ import {IDeployedCoinVersionLookup} from "../../src/interfaces/IDeployedCoinVersionLookup.sol";
7
+ import {IHasRewardsRecipients} from "../../src/interfaces/ICoin.sol";
8
+ import {Currency} from "@uniswap/v4-core/src/types/Currency.sol";
9
+ import {SwapParams} from "@uniswap/v4-core/src/types/PoolOperation.sol";
10
+ import {BalanceDelta} from "@uniswap/v4-core/src/types/BalanceDelta.sol";
11
+ import {CoinCommon} from "../../src/libs/CoinCommon.sol";
12
+ import {V4Liquidity} from "../../src/libs/V4Liquidity.sol";
13
+ import {BaseHook} from "@uniswap/v4-periphery/src/utils/BaseHook.sol";
14
+ import {ICoinV4, IHasSwapPath} from "../../src/interfaces/ICoinV4.sol";
15
+ import {UniV4SwapToCurrency} from "../../src/libs/UniV4SwapToCurrency.sol";
16
+ import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
17
+ import {CoinRewardsV4} from "../../src/libs/CoinRewardsV4.sol";
18
+
19
+ /// @dev Test util - meant to be able to etched where a normal zora hook is, to gather the fees from swaps but not distribute them
20
+ contract FeeEstimatorHook is ZoraV4CoinHook {
21
+ struct FeeEstimatorState {
22
+ uint128 fees0;
23
+ uint128 fees1;
24
+ Currency afterSwapCurrency;
25
+ uint128 afterSwapCurrencyAmount;
26
+ BalanceDelta lastDelta;
27
+ SwapParams lastSwapParams;
28
+ uint256 currencyBalanceChange;
29
+ uint256 coinBalanceChange;
30
+ }
31
+
32
+ constructor(IPoolManager _poolManager, IDeployedCoinVersionLookup _coinVersionLookup) ZoraV4CoinHook(_poolManager, _coinVersionLookup, new address[](0)) {}
33
+
34
+ FeeEstimatorState public feeState;
35
+
36
+ function getFeeState() public view returns (FeeEstimatorState memory) {
37
+ return feeState;
38
+ }
39
+
40
+ function _afterSwap(
41
+ address,
42
+ PoolKey calldata key,
43
+ SwapParams calldata params,
44
+ BalanceDelta _delta,
45
+ bytes calldata
46
+ ) internal override returns (bytes4, int128) {
47
+ bytes32 poolKeyHash = CoinCommon.hashPoolKey(key);
48
+
49
+ // get the coin address and positions for the pool key; they must have been set in the afterInitialize callback
50
+ address coin = poolCoins[poolKeyHash].coin;
51
+ require(coin != address(0), NoCoinForHook(key));
52
+
53
+ {
54
+ uint256 coinBalanceBefore = IERC20(coin).balanceOf(address(this));
55
+ uint256 currencyBalanceBefore = IERC20(ICoinV4(coin).currency()).balanceOf(address(this));
56
+
57
+ IHasSwapPath.PayoutSwapPath memory payoutSwapPath = IHasSwapPath(coin).getPayoutSwapPath(coinVersionLookup);
58
+
59
+ int128 fee0;
60
+ int128 fee1;
61
+
62
+ (fee0, fee1, feeState.afterSwapCurrency, feeState.afterSwapCurrencyAmount) = CoinRewardsV4.collectFeesAndConvertToPayout(
63
+ poolManager,
64
+ key,
65
+ poolCoins[poolKeyHash].positions,
66
+ payoutSwapPath
67
+ );
68
+
69
+ feeState.fees0 += uint128(fee0);
70
+ feeState.fees1 += uint128(fee1);
71
+
72
+ uint256 coinBalanceAfter = IERC20(coin).balanceOf(address(this));
73
+ uint256 currencyBalanceAfter = IERC20(ICoinV4(coin).currency()).balanceOf(address(this));
74
+
75
+ feeState.coinBalanceChange = coinBalanceAfter - coinBalanceBefore;
76
+ feeState.currencyBalanceChange = currencyBalanceAfter - currencyBalanceBefore;
77
+ }
78
+
79
+ feeState.lastDelta = _delta;
80
+ feeState.lastSwapParams = params;
81
+
82
+ return (BaseHook.afterSwap.selector, 0);
83
+ }
84
+ }
@@ -0,0 +1,17 @@
1
+ // SPDX-License-Identifier: MIT
2
+ pragma solidity ^0.8.18;
3
+
4
+ import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
5
+
6
+ /// Used to deploy the factory before we know the impl address
7
+ contract ProxyShim is UUPSUpgradeable {
8
+ address immutable canUpgrade;
9
+
10
+ constructor() {
11
+ canUpgrade = msg.sender;
12
+ }
13
+
14
+ function _authorizeUpgrade(address) internal view override {
15
+ require(msg.sender == canUpgrade, "not authorized");
16
+ }
17
+ }
package/wagmi.config.ts CHANGED
@@ -10,9 +10,13 @@ export default defineConfig({
10
10
  },
11
11
  include: [
12
12
  "Coin",
13
+ "CoinV4",
13
14
  "ZoraFactoryImpl",
14
15
  "IUniswapV3Pool",
15
16
  "BuySupplyWithSwapRouterHook",
17
+ "IPoolConfigEncoding",
18
+ "IUniversalRouter",
19
+ "IPermit2",
16
20
  ].map((contractName) => `${contractName}.json`),
17
21
  }),
18
22
  ],
package/.env DELETED
@@ -1 +0,0 @@
1
- ALCHEMY_KEY=Ebx4-biYR4T-p-1BOId9DozVRrr3nHu4
@@ -1,22 +0,0 @@
1
-
2
- 
3
- > @zoralabs/coins@0.8.0 update-contract-version /Users/danovedzora/source/zora-protocol/packages/coins
4
- > pnpm exec update-contract-version
5
-
6
- updating contract version to 0.8.0
7
- generated contract version code: // This file is automatically generated by code; do not manually update
8
- // SPDX-License-Identifier: MIT
9
- pragma solidity ^0.8.23;
10
-
11
- import {IVersionedContract} from "@zoralabs/shared-contracts/interfaces/IVersionedContract.sol";
12
-
13
- /// @title ContractVersionBase
14
- /// @notice Base contract for versioning contracts
15
- contract ContractVersionBase is IVersionedContract {
16
- /// @notice The version of the contract
17
- function contractVersion() external pure override returns (string memory) {
18
- return "0.8.0";
19
- }
20
- }
21
-
22
- writing file to /Users/danovedzora/source/zora-protocol/packages/coins/src/version/ContractVersionBase.sol
@@ -1,7 +0,0 @@
1
- [
2
- {
3
- "type": "error",
4
- "name": "InvalidPoolVersion",
5
- "inputs": []
6
- }
7
- ]
@@ -1,68 +0,0 @@
1
- [
2
- {
3
- "type": "function",
4
- "name": "deployZoraV4CoinHookFromContract",
5
- "inputs": [
6
- {
7
- "name": "poolManager",
8
- "type": "address",
9
- "internalType": "address"
10
- }
11
- ],
12
- "outputs": [
13
- {
14
- "name": "hook",
15
- "type": "address",
16
- "internalType": "contract IHooks"
17
- }
18
- ],
19
- "stateMutability": "nonpayable"
20
- },
21
- {
22
- "type": "error",
23
- "name": "Create2EmptyBytecode",
24
- "inputs": []
25
- },
26
- {
27
- "type": "error",
28
- "name": "Create2FailedDeployment",
29
- "inputs": []
30
- },
31
- {
32
- "type": "error",
33
- "name": "Create2InsufficientBalance",
34
- "inputs": [
35
- {
36
- "name": "balance",
37
- "type": "uint256",
38
- "internalType": "uint256"
39
- },
40
- {
41
- "name": "needed",
42
- "type": "uint256",
43
- "internalType": "uint256"
44
- }
45
- ]
46
- },
47
- {
48
- "type": "error",
49
- "name": "HookNotDeployed",
50
- "inputs": []
51
- },
52
- {
53
- "type": "error",
54
- "name": "InvalidHookAddress",
55
- "inputs": [
56
- {
57
- "name": "expected",
58
- "type": "address",
59
- "internalType": "address"
60
- },
61
- {
62
- "name": "actual",
63
- "type": "address",
64
- "internalType": "address"
65
- }
66
- ]
67
- }
68
- ]
@@ -1,42 +0,0 @@
1
- [
2
- {
3
- "type": "function",
4
- "name": "deployZoraV4CoinHookFromContract",
5
- "inputs": [
6
- {
7
- "name": "poolManager",
8
- "type": "address",
9
- "internalType": "address"
10
- }
11
- ],
12
- "outputs": [
13
- {
14
- "name": "hook",
15
- "type": "address",
16
- "internalType": "contract IHooks"
17
- }
18
- ],
19
- "stateMutability": "nonpayable"
20
- },
21
- {
22
- "type": "error",
23
- "name": "HookNotDeployed",
24
- "inputs": []
25
- },
26
- {
27
- "type": "error",
28
- "name": "InvalidHookAddress",
29
- "inputs": [
30
- {
31
- "name": "expected",
32
- "type": "address",
33
- "internalType": "address"
34
- },
35
- {
36
- "name": "actual",
37
- "type": "address",
38
- "internalType": "address"
39
- }
40
- ]
41
- }
42
- ]
@@ -1,48 +0,0 @@
1
- // SPDX-License-Identifier: MIT
2
- pragma solidity ^0.8.23;
3
-
4
- import {PoolConfiguration, ICoin} from "../interfaces/ICoin.sol";
5
- import {TickMath} from "../utils/uniswap/TickMath.sol";
6
- import {MarketConstants} from "./MarketConstants.sol";
7
- import {LiquidityAmounts} from "../utils/uniswap/LiquidityAmounts.sol";
8
- import {LpPosition} from "../types/LpPosition.sol";
9
- import {CoinConfigurationVersions} from "./CoinConfigurationVersions.sol";
10
-
11
- library CoinLegacy {
12
- function setupPool(
13
- bool isCoinToken0,
14
- bytes memory poolConfig_,
15
- address weth
16
- ) internal pure returns (uint160 sqrtPriceX96, PoolConfiguration memory poolConfiguration) {
17
- (, address currency, int24 tickLower_) = abi.decode(poolConfig_, (uint8, address, int24));
18
-
19
- // If WETH is the pool's currency, validate the lower tick
20
- if ((currency == weth || currency == address(0)) && tickLower_ > MarketConstants.LP_TICK_LOWER_WETH) {
21
- revert ICoin.InvalidWethLowerTick();
22
- }
23
-
24
- int24 savedTickLower = isCoinToken0 ? tickLower_ : -MarketConstants.LP_TICK_UPPER;
25
- int24 savedTickUpper = isCoinToken0 ? MarketConstants.LP_TICK_UPPER : -tickLower_;
26
-
27
- sqrtPriceX96 = TickMath.getSqrtPriceAtTick(isCoinToken0 ? savedTickLower : savedTickUpper);
28
-
29
- poolConfiguration = PoolConfiguration({
30
- version: CoinConfigurationVersions.LEGACY_POOL_VERSION,
31
- tickLower: savedTickLower,
32
- tickUpper: savedTickUpper,
33
- numPositions: 1,
34
- maxDiscoverySupplyShare: 0
35
- });
36
- }
37
-
38
- function calculatePositions(bool isCoinToken0, int24 tickLower, int24 tickUpper) internal pure returns (LpPosition[] memory positions) {
39
- positions = new LpPosition[](1);
40
-
41
- uint160 sqrtPriceX96 = TickMath.getSqrtPriceAtTick(isCoinToken0 ? tickLower : tickUpper);
42
- uint160 farSqrtPriceX96 = TickMath.getSqrtPriceAtTick(isCoinToken0 ? tickUpper : tickLower);
43
- uint128 liquidity = isCoinToken0
44
- ? LiquidityAmounts.getLiquidityForAmount0(sqrtPriceX96, farSqrtPriceX96, MarketConstants.POOL_LAUNCH_SUPPLY)
45
- : LiquidityAmounts.getLiquidityForAmount1(sqrtPriceX96, farSqrtPriceX96, MarketConstants.POOL_LAUNCH_SUPPLY);
46
- positions[0] = LpPosition({tickLower: tickLower, tickUpper: tickUpper, liquidity: liquidity});
47
- }
48
- }
@@ -1,182 +0,0 @@
1
- // SPDX-License-Identifier: MIT
2
- pragma solidity ^0.8.23;
3
-
4
- import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
5
- import {IUniswapV3Factory} from "../interfaces/IUniswapV3Factory.sol";
6
- import {IUniswapV3Pool} from "../interfaces/IUniswapV3Pool.sol";
7
- import {ISwapRouter} from "../interfaces/ISwapRouter.sol";
8
- import {PoolConfiguration, ICoin} from "../interfaces/ICoin.sol";
9
- import {TickMath} from "../utils/uniswap/TickMath.sol";
10
- import {MarketConstants} from "./MarketConstants.sol";
11
- import {LiquidityAmounts} from "../utils/uniswap/LiquidityAmounts.sol";
12
- import {LpPosition} from "../types/LpPosition.sol";
13
- import {CoinConfigurationVersions} from "./CoinConfigurationVersions.sol";
14
- import {UniV3Errors} from "./UniV3Errors.sol";
15
-
16
- library CoinLegacyMarket {
17
- struct State {
18
- address poolAddress;
19
- uint160 sqrtPriceX96;
20
- PoolConfiguration poolConfiguration;
21
- address pairedCurrency;
22
- }
23
-
24
- struct MarketConfig {
25
- address uniswapv3Factory;
26
- address weth;
27
- address pairedCurrency;
28
- int24 tickLower;
29
- }
30
-
31
- function _validateMarketConfig(MarketConfig memory marketConfig) internal pure {
32
- if (marketConfig.uniswapv3Factory == address(0)) {
33
- revert UniV3Errors.InvalidUniswapV3Factory();
34
- }
35
-
36
- if (marketConfig.weth == address(0)) {
37
- revert UniV3Errors.InvalidWeth();
38
- }
39
-
40
- address weth = marketConfig.weth;
41
- address currency = marketConfig.pairedCurrency;
42
- int24 tickLower = marketConfig.tickLower;
43
-
44
- // If WETH is the pool's currency, validate the lower tick
45
- if ((currency == weth || currency == address(0)) && tickLower > MarketConstants.LP_TICK_LOWER_WETH) {
46
- revert ICoin.InvalidWethLowerTick();
47
- }
48
- }
49
-
50
- function _isCoinToken0(address coin, address currency) internal pure returns (bool isCoinToken0, address token0, address token1) {
51
- token0 = coin < currency ? coin : currency;
52
- token1 = token1 = coin < currency ? currency : coin;
53
- isCoinToken0 = token0 == coin;
54
- }
55
-
56
- function setupMarket(bytes memory _marketConfig, address coin) internal returns (bytes memory) {
57
- MarketConfig memory marketConfig = abi.decode(_marketConfig, (MarketConfig));
58
- _validateMarketConfig(marketConfig);
59
-
60
- (bool isCoinToken0, address token0, address token1) = _isCoinToken0(coin, marketConfig.pairedCurrency);
61
-
62
- (uint160 sqrtPriceX96, PoolConfiguration memory poolConfiguration) = setupPool(
63
- isCoinToken0,
64
- marketConfig.pairedCurrency,
65
- marketConfig.tickLower,
66
- marketConfig.weth
67
- );
68
-
69
- address poolAddress = _createPool(token0, token1, sqrtPriceX96, marketConfig.uniswapv3Factory);
70
-
71
- LpPosition[] memory positions = calculatePositions(isCoinToken0, poolConfiguration);
72
-
73
- _mintPositions(positions, poolAddress);
74
-
75
- State memory state = State({
76
- poolAddress: poolAddress,
77
- sqrtPriceX96: sqrtPriceX96,
78
- poolConfiguration: poolConfiguration,
79
- pairedCurrency: marketConfig.pairedCurrency
80
- });
81
-
82
- return abi.encode(state);
83
- }
84
-
85
- function buy(bytes memory _state, address recipient, uint256 orderSize, uint256 minAmountOut, bytes memory tradeData) internal returns (uint256, uint256) {
86
- State memory state = abi.decode(_state, (State));
87
- (uint160 sqrtPriceLimitX96, address swapRouter, ) = abi.decode(tradeData, (uint160, address, address));
88
-
89
- // Calculate the trade reward
90
- // uint256 tradeReward = _calculateReward(orderSize, TOTAL_FEE_BPS);
91
-
92
- ISwapRouter.ExactInputSingleParams memory params = ISwapRouter.ExactInputSingleParams({
93
- tokenIn: state.pairedCurrency,
94
- tokenOut: address(this),
95
- fee: MarketConstants.LP_FEE,
96
- recipient: recipient,
97
- amountIn: orderSize,
98
- amountOutMinimum: minAmountOut,
99
- sqrtPriceLimitX96: sqrtPriceLimitX96
100
- });
101
-
102
- uint256 amountOut = ISwapRouter(swapRouter).exactInputSingle(params);
103
-
104
- return (orderSize, amountOut);
105
- }
106
-
107
- function sell(bytes memory _state, uint256 orderSize, uint256 minAmountOut, bytes memory tradeData) internal returns (uint256, uint256) {
108
- State memory state = abi.decode(_state, (State));
109
- (uint160 sqrtPriceLimitX96, address swapRouter) = abi.decode(tradeData, (uint160, address));
110
-
111
- // Approve the swap router to spend the coin
112
- IERC20(address(this)).approve(swapRouter, orderSize);
113
-
114
- // Set the swap parameters
115
- ISwapRouter.ExactInputSingleParams memory params = ISwapRouter.ExactInputSingleParams({
116
- tokenIn: address(this),
117
- tokenOut: state.pairedCurrency,
118
- fee: MarketConstants.LP_FEE,
119
- recipient: address(this),
120
- amountIn: orderSize,
121
- amountOutMinimum: minAmountOut,
122
- sqrtPriceLimitX96: sqrtPriceLimitX96
123
- });
124
-
125
- // Execute the swap
126
- uint256 amountOut = ISwapRouter(swapRouter).exactInputSingle(params);
127
-
128
- return (orderSize, amountOut);
129
- }
130
-
131
- /// @dev Creates the Uniswap V3 pool for the coin/currency pair
132
- function _createPool(address token0, address token1, uint160 sqrtPriceX96, address v3Factory) internal returns (address pool) {
133
- pool = IUniswapV3Factory(v3Factory).createPool(token0, token1, MarketConstants.LP_FEE);
134
-
135
- // This pool should be new, if it has already been initialized
136
- // then we will fail the creation step prompting the user to try again.
137
- IUniswapV3Pool(pool).initialize(sqrtPriceX96);
138
- }
139
-
140
- function setupPool(
141
- bool isCoinToken0,
142
- address currency,
143
- int24 tickLower_,
144
- address weth
145
- ) internal pure returns (uint160 sqrtPriceX96, PoolConfiguration memory poolConfiguration) {
146
- // If WETH is the pool's currency, validate the lower tick
147
- if ((currency == weth || currency == address(0)) && tickLower_ > MarketConstants.LP_TICK_LOWER_WETH) {
148
- revert ICoin.InvalidWethLowerTick();
149
- }
150
-
151
- int24 savedTickLower = isCoinToken0 ? tickLower_ : -MarketConstants.LP_TICK_UPPER;
152
- int24 savedTickUpper = isCoinToken0 ? MarketConstants.LP_TICK_UPPER : -tickLower_;
153
-
154
- sqrtPriceX96 = TickMath.getSqrtPriceAtTick(isCoinToken0 ? savedTickLower : savedTickUpper);
155
-
156
- poolConfiguration = PoolConfiguration({
157
- version: CoinConfigurationVersions.LEGACY_POOL_VERSION,
158
- tickLower: savedTickLower,
159
- tickUpper: savedTickUpper,
160
- numPositions: 1,
161
- maxDiscoverySupplyShare: 0
162
- });
163
- }
164
-
165
- function calculatePositions(bool isCoinToken0, PoolConfiguration memory poolConfiguration) internal pure returns (LpPosition[] memory positions) {
166
- positions = new LpPosition[](1);
167
-
168
- uint160 sqrtPriceX96 = TickMath.getSqrtPriceAtTick(isCoinToken0 ? poolConfiguration.tickLower : poolConfiguration.tickUpper);
169
- uint160 farSqrtPriceX96 = TickMath.getSqrtPriceAtTick(isCoinToken0 ? poolConfiguration.tickUpper : poolConfiguration.tickLower);
170
- uint128 liquidity = isCoinToken0
171
- ? LiquidityAmounts.getLiquidityForAmount0(sqrtPriceX96, farSqrtPriceX96, MarketConstants.POOL_LAUNCH_SUPPLY)
172
- : LiquidityAmounts.getLiquidityForAmount1(sqrtPriceX96, farSqrtPriceX96, MarketConstants.POOL_LAUNCH_SUPPLY);
173
- positions[0] = LpPosition({tickLower: poolConfiguration.tickLower, tickUpper: poolConfiguration.tickUpper, liquidity: liquidity});
174
- }
175
-
176
- /// @dev Mints the calculated liquidity positions into the Uniswap V3 pool
177
- function _mintPositions(LpPosition[] memory lbpPositions, address poolAddress) internal {
178
- for (uint256 i; i < lbpPositions.length; i++) {
179
- IUniswapV3Pool(poolAddress).mint(address(this), lbpPositions[i].tickLower, lbpPositions[i].tickUpper, lbpPositions[i].liquidity, "");
180
- }
181
- }
182
- }