@zoralabs/coins 0.9.0 → 1.0.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.
- package/.turbo/turbo-build.log +179 -114
- package/CHANGELOG.md +46 -0
- package/abis/BaseCoin.json +26 -118
- package/abis/BaseTest.json +47 -0
- package/abis/BuySupplyWithSwapRouterHook.json +40 -0
- package/abis/Coin.json +171 -63
- package/abis/CoinDopplerMultiCurve.json +38 -0
- package/abis/CoinRewardsV4.json +54 -0
- package/abis/CoinTest.json +53 -20
- package/abis/CoinUniV4Test.json +1091 -0
- package/abis/CoinV4.json +234 -211
- package/abis/DeployScript.json +47 -0
- package/abis/DeployedCoinVersionLookup.json +21 -0
- package/abis/DeployedCoinVersionLookupTest.json +716 -0
- package/abis/DifferentNamespaceVersionLookup.json +39 -0
- package/abis/DopplerUniswapV3Test.json +49 -93
- package/abis/ERC20.json +310 -0
- package/abis/FactoryTest.json +85 -7
- package/abis/FeeEstimatorHook.json +1515 -0
- package/abis/HooksDeployment.json +23 -0
- package/abis/HooksTest.json +60 -0
- package/abis/ICoin.json +40 -71
- package/abis/ICoinV3.json +879 -0
- package/abis/ICoinV4.json +915 -0
- package/abis/IDeployedCoinVersionLookup.json +21 -0
- package/abis/IERC721.json +36 -36
- package/abis/IHasPoolKey.json +42 -0
- package/abis/IHasRewardsRecipients.json +54 -0
- package/abis/IHasSwapPath.json +60 -0
- package/abis/IMsgSender.json +15 -0
- package/abis/IPoolConfigEncoding.json +46 -0
- package/abis/ISwapPathRouter.json +92 -0
- package/abis/IUniversalRouter.json +61 -0
- package/abis/IUnlockCallback.json +21 -0
- package/abis/IV4Quoter.json +310 -0
- package/abis/IZoraFactory.json +210 -11
- package/abis/IZoraV4CoinHook.json +348 -4
- package/abis/MockERC20.json +21 -0
- package/abis/MultiOwnableTest.json +47 -0
- package/abis/{CoinConfigurationVersions.json → Position.json} +1 -1
- package/abis/PrintUpgradeCommand.json +9 -0
- package/abis/ProxyShim.json +24 -0
- package/abis/StateLibrary.json +80 -0
- package/abis/TestDeployedCoinVersionLookupImplementation.json +39 -0
- package/abis/TestV4Swap.json +9 -0
- package/abis/UpgradeCoinImpl.json +47 -0
- package/abis/UpgradesTest.json +81 -0
- package/abis/Vm.json +1482 -111
- package/abis/VmSafe.json +856 -32
- package/abis/ZoraFactoryImpl.json +339 -1
- package/abis/ZoraV4CoinHook.json +442 -5
- package/addresses/8453.json +7 -4
- package/addresses/84532.json +8 -5
- package/addresses/dev/8453.json +10 -0
- package/dist/index.cjs +1932 -167
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1928 -167
- package/dist/index.js.map +1 -1
- package/dist/wagmiGenerated.d.ts +2606 -160
- package/dist/wagmiGenerated.d.ts.map +1 -1
- package/foundry.toml +1 -0
- package/package/wagmiGenerated.ts +1941 -164
- package/package.json +8 -3
- package/remappings.txt +6 -1
- package/script/Deploy.s.sol +1 -1
- package/script/DeployDevFactory.s.sol +21 -0
- package/script/DeployHooks.s.sol +1 -1
- package/script/PrintUpgradeCommand.s.sol +13 -0
- package/script/Simulate.s.sol +1 -10
- package/script/TestBackingCoinSwap.s.sol +147 -0
- package/script/TestV4Swap.s.sol +136 -0
- package/script/UpgradeCoinImpl.sol +2 -2
- package/script/UpgradeFactoryImpl.s.sol +2 -2
- package/src/BaseCoin.sol +190 -0
- package/src/Coin.sol +87 -202
- package/src/CoinV4.sol +121 -0
- package/src/ZoraFactoryImpl.sol +208 -36
- package/{script → src/deployment}/CoinsDeployerBase.sol +111 -17
- package/src/hooks/ZoraV4CoinHook.sol +212 -0
- package/src/hooks/{BaseCoinDeployHook.sol → deployment/BaseCoinDeployHook.sol} +3 -3
- package/src/hooks/deployment/BuySupplyWithSwapRouterHook.sol +140 -0
- package/src/interfaces/ICoin.sol +31 -39
- package/src/interfaces/ICoinV3.sol +71 -0
- package/src/interfaces/ICoinV4.sol +69 -0
- package/src/interfaces/IDeployedCoinVersionLookup.sol +11 -0
- package/src/interfaces/IMsgSender.sol +9 -0
- package/src/interfaces/IPoolConfigEncoding.sol +14 -0
- package/src/interfaces/ISwapPathRouter.sol +14 -0
- package/src/interfaces/IZoraFactory.sol +67 -28
- package/src/interfaces/IZoraV4CoinHook.sol +116 -0
- package/src/libs/CoinCommon.sol +15 -0
- package/src/libs/CoinConfigurationVersions.sol +116 -1
- package/src/libs/CoinConstants.sol +5 -0
- package/src/libs/CoinDopplerMultiCurve.sol +134 -0
- package/src/libs/CoinDopplerUniV3.sol +19 -171
- package/src/libs/CoinRewards.sol +195 -0
- package/src/libs/CoinRewardsV4.sol +179 -0
- package/src/libs/CoinSetup.sol +57 -0
- package/src/libs/CoinSetupV3.sol +6 -67
- package/src/libs/DopplerMath.sol +156 -0
- package/src/libs/HooksDeployment.sol +128 -0
- package/src/libs/MarketConstants.sol +4 -0
- package/src/libs/PoolStateReader.sol +22 -0
- package/src/libs/UniV3BuySell.sol +74 -292
- package/src/libs/UniV4SwapHelper.sol +65 -0
- package/src/libs/UniV4SwapToCurrency.sol +109 -0
- package/src/libs/V4Liquidity.sol +122 -0
- package/src/types/PoolConfiguration.sol +15 -0
- package/src/utils/DeployedCoinVersionLookup.sol +52 -0
- package/src/version/ContractVersionBase.sol +1 -1
- package/test/Coin.t.sol +78 -88
- package/test/CoinDopplerUniV3.t.sol +32 -171
- package/test/CoinUniV4.t.sol +777 -0
- package/test/{Hooks.t.sol → DeploymentHooks.t.sol} +53 -16
- package/test/Factory.t.sol +80 -47
- package/test/MultiOwnable.t.sol +6 -3
- package/test/Upgrades.t.sol +97 -5
- package/test/mocks/MockERC20.sol +12 -0
- package/test/utils/BaseTest.sol +162 -57
- package/test/utils/DeployedCoinVersionLookup.t.sol +127 -0
- package/test/utils/FeeEstimatorHook.sol +84 -0
- package/test/utils/ProxyShim.sol +17 -0
- package/wagmi.config.ts +4 -0
- package/.env +0 -1
- package/.turbo/turbo-update-contract-version.log +0 -22
- package/abis/CoinSetupV3.json +0 -7
- package/abis/HookDeployer.json +0 -68
- package/abis/IHookDeployer.json +0 -42
- package/src/hooks/BuySupplyWithSwapRouterHook.sol +0 -78
- package/src/libs/CoinLegacy.sol +0 -48
- package/src/libs/CoinLegacyMarket.sol +0 -182
|
@@ -13,21 +13,15 @@ import {LpPosition} from "../types/LpPosition.sol";
|
|
|
13
13
|
import {PoolConfiguration} from "../interfaces/ICoin.sol";
|
|
14
14
|
import {IUniswapV3Pool} from "../interfaces/IUniswapV3Pool.sol";
|
|
15
15
|
import {IAirlock} from "../interfaces/IAirlock.sol";
|
|
16
|
-
import {
|
|
16
|
+
import {CoinV3Config} from "./CoinSetupV3.sol";
|
|
17
17
|
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
|
|
18
|
-
import {CoinLegacyMarket} from "./CoinLegacyMarket.sol";
|
|
19
18
|
import {CoinDopplerUniV3} from "./CoinDopplerUniV3.sol";
|
|
20
19
|
import {CoinConfigurationVersions} from "./CoinConfigurationVersions.sol";
|
|
21
|
-
|
|
22
|
-
struct
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
address payoutRecipient;
|
|
27
|
-
address protocolRewards;
|
|
28
|
-
address poolAddress;
|
|
29
|
-
PoolConfiguration poolConfiguration;
|
|
30
|
-
UniV3Config uniswapV3Config;
|
|
20
|
+
import {CoinRewards, CoinConfig} from "./CoinRewards.sol";
|
|
21
|
+
struct SellResult {
|
|
22
|
+
uint256 payoutSize;
|
|
23
|
+
uint256 tradeReward;
|
|
24
|
+
uint256 trueOrderSize;
|
|
31
25
|
}
|
|
32
26
|
|
|
33
27
|
library UniV3BuySell {
|
|
@@ -36,14 +30,16 @@ library UniV3BuySell {
|
|
|
36
30
|
error AddressZero();
|
|
37
31
|
error InvalidPoolVersion();
|
|
38
32
|
|
|
39
|
-
function
|
|
33
|
+
function handleBuy(
|
|
40
34
|
address recipient,
|
|
41
35
|
uint256 orderSize,
|
|
42
36
|
uint256 minAmountOut,
|
|
43
37
|
uint160 sqrtPriceLimitX96,
|
|
44
38
|
address tradeReferrer,
|
|
45
|
-
|
|
46
|
-
|
|
39
|
+
CoinConfig memory coinConfig,
|
|
40
|
+
address currency,
|
|
41
|
+
ISwapRouter swapRouter,
|
|
42
|
+
IWETH weth
|
|
47
43
|
) internal returns (uint256 amountOut, uint256 tradeReward, uint256 trueOrderSize) {
|
|
48
44
|
if (recipient == address(0)) {
|
|
49
45
|
revert AddressZero();
|
|
@@ -56,12 +52,12 @@ library UniV3BuySell {
|
|
|
56
52
|
trueOrderSize = orderSize - tradeReward;
|
|
57
53
|
|
|
58
54
|
// Handle incoming currency
|
|
59
|
-
_handleIncomingCurrency(orderSize, trueOrderSize,
|
|
55
|
+
_handleIncomingCurrency(orderSize, trueOrderSize, currency, swapRouter, weth);
|
|
60
56
|
|
|
61
57
|
// Set up the swap parameters
|
|
62
58
|
ISwapRouter.ExactInputSingleParams memory params = ISwapRouter.ExactInputSingleParams({
|
|
63
|
-
tokenIn:
|
|
64
|
-
tokenOut:
|
|
59
|
+
tokenIn: currency,
|
|
60
|
+
tokenOut: address(this),
|
|
65
61
|
fee: MarketConstants.LP_FEE,
|
|
66
62
|
recipient: recipient,
|
|
67
63
|
amountIn: trueOrderSize,
|
|
@@ -70,60 +66,21 @@ library UniV3BuySell {
|
|
|
70
66
|
});
|
|
71
67
|
|
|
72
68
|
// Execute the swap
|
|
73
|
-
amountOut = ISwapRouter(
|
|
69
|
+
amountOut = ISwapRouter(swapRouter).exactInputSingle(params);
|
|
74
70
|
|
|
75
|
-
|
|
71
|
+
CoinRewards.handleTradeRewards(tradeReward, tradeReferrer, coinConfig, currency, weth);
|
|
76
72
|
}
|
|
77
73
|
|
|
78
|
-
|
|
79
|
-
/// @param recipient The recipient address of the coins
|
|
80
|
-
/// @param orderSize The amount of coins to buy
|
|
81
|
-
/// @param tradeReferrer The address of the trade referrer
|
|
82
|
-
/// @param sqrtPriceLimitX96 The price limit for Uniswap V3 pool swap
|
|
83
|
-
function buy(
|
|
84
|
-
address recipient,
|
|
74
|
+
function _executeSwap(
|
|
85
75
|
uint256 orderSize,
|
|
86
76
|
uint256 minAmountOut,
|
|
87
77
|
uint160 sqrtPriceLimitX96,
|
|
88
|
-
address
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
) internal returns (uint256, uint256) {
|
|
92
|
-
(uint256 amountOut, uint256 tradeReward, uint256 trueOrderSize) = _handleBuy(
|
|
93
|
-
recipient,
|
|
94
|
-
orderSize,
|
|
95
|
-
minAmountOut,
|
|
96
|
-
sqrtPriceLimitX96,
|
|
97
|
-
tradeReferrer,
|
|
98
|
-
coin,
|
|
99
|
-
coinConfig
|
|
100
|
-
);
|
|
101
|
-
|
|
102
|
-
handleMarketRewards(coin, coinConfig);
|
|
103
|
-
|
|
104
|
-
emit ICoin.CoinBuy(msg.sender, recipient, tradeReferrer, amountOut, coinConfig.currency, tradeReward, trueOrderSize);
|
|
105
|
-
|
|
106
|
-
return (orderSize, amountOut);
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
function _handleSell(
|
|
110
|
-
address recipient,
|
|
111
|
-
uint256 beforeCoinBalance,
|
|
112
|
-
uint256 orderSize,
|
|
113
|
-
uint256 minAmountOut,
|
|
114
|
-
uint160 sqrtPriceLimitX96,
|
|
115
|
-
address tradeReferrer,
|
|
116
|
-
CoinConfig memory coinConfig
|
|
117
|
-
) internal returns (uint256 payoutSize, uint256 tradeReward, uint256 trueOrderSize) {
|
|
118
|
-
// Ensure the recipient is not the zero address
|
|
119
|
-
if (recipient == address(0)) {
|
|
120
|
-
revert AddressZero();
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// Set the swap parameters
|
|
78
|
+
address currency,
|
|
79
|
+
ISwapRouter swapRouter
|
|
80
|
+
) internal returns (uint256 amountOut) {
|
|
124
81
|
ISwapRouter.ExactInputSingleParams memory params = ISwapRouter.ExactInputSingleParams({
|
|
125
82
|
tokenIn: address(this),
|
|
126
|
-
tokenOut:
|
|
83
|
+
tokenOut: currency,
|
|
127
84
|
fee: MarketConstants.LP_FEE,
|
|
128
85
|
recipient: address(this),
|
|
129
86
|
amountIn: orderSize,
|
|
@@ -131,78 +88,66 @@ library UniV3BuySell {
|
|
|
131
88
|
sqrtPriceLimitX96: sqrtPriceLimitX96
|
|
132
89
|
});
|
|
133
90
|
|
|
134
|
-
|
|
135
|
-
|
|
91
|
+
amountOut = swapRouter.exactInputSingle(params);
|
|
92
|
+
}
|
|
136
93
|
|
|
137
|
-
|
|
94
|
+
function _handleRefund(uint256 beforeCoinBalance, uint256 orderSize, address recipient) internal returns (uint256 trueOrderSize) {
|
|
138
95
|
uint256 afterCoinBalance = IERC20(address(this)).balanceOf(address(this));
|
|
139
|
-
|
|
140
96
|
trueOrderSize = orderSize;
|
|
141
97
|
|
|
142
|
-
// If the swap was partially executed:
|
|
143
98
|
if (afterCoinBalance > beforeCoinBalance) {
|
|
144
|
-
// Calculate the refund
|
|
145
99
|
uint256 coinRefund = afterCoinBalance - beforeCoinBalance;
|
|
146
|
-
|
|
147
|
-
// Update the order size
|
|
148
100
|
trueOrderSize -= coinRefund;
|
|
149
|
-
|
|
150
|
-
// Transfer the refund back to the seller
|
|
151
101
|
IERC20(address(this)).safeTransfer(recipient, coinRefund);
|
|
152
102
|
}
|
|
103
|
+
}
|
|
153
104
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
105
|
+
function _handlePayoutAndRewards(
|
|
106
|
+
uint256 amountOut,
|
|
107
|
+
address recipient,
|
|
108
|
+
address tradeReferrer,
|
|
109
|
+
CoinConfig memory coinConfig,
|
|
110
|
+
address currency,
|
|
111
|
+
IWETH weth
|
|
112
|
+
) internal returns (uint256 payoutSize, uint256 tradeReward) {
|
|
113
|
+
if (currency == address(weth)) {
|
|
114
|
+
weth.withdraw(amountOut);
|
|
157
115
|
}
|
|
158
116
|
|
|
159
|
-
// Calculate the trade reward
|
|
160
117
|
tradeReward = _calculateReward(amountOut, CoinConstants.TOTAL_FEE_BPS);
|
|
161
|
-
|
|
162
|
-
// Calculate the payout after the fee
|
|
163
118
|
payoutSize = amountOut - tradeReward;
|
|
164
119
|
|
|
165
|
-
_handlePayout(payoutSize, recipient,
|
|
120
|
+
_handlePayout(payoutSize, recipient, currency, weth);
|
|
166
121
|
|
|
167
|
-
|
|
122
|
+
CoinRewards.handleTradeRewards(tradeReward, tradeReferrer, coinConfig, currency, weth);
|
|
168
123
|
}
|
|
169
124
|
|
|
170
|
-
|
|
171
|
-
/// @param recipient The recipient of the currency
|
|
172
|
-
/// @param _orderSize The amount of coins to sell
|
|
173
|
-
/// @param minAmountOut The minimum amount of currency to receive
|
|
174
|
-
/// @param sqrtPriceLimitX96 The price limit for the swap
|
|
175
|
-
/// @param tradeReferrer The address of the trade referrer
|
|
176
|
-
function sell(
|
|
125
|
+
function handleSell(
|
|
177
126
|
address recipient,
|
|
178
127
|
uint256 beforeCoinBalance,
|
|
179
|
-
uint256
|
|
128
|
+
uint256 orderSize,
|
|
180
129
|
uint256 minAmountOut,
|
|
181
130
|
uint160 sqrtPriceLimitX96,
|
|
182
131
|
address tradeReferrer,
|
|
183
|
-
CoinConfig memory coinConfig
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
);
|
|
195
|
-
|
|
196
|
-
handleMarketRewards(address(this), coinConfig);
|
|
197
|
-
|
|
198
|
-
emit ICoin.CoinSell(msg.sender, recipient, tradeReferrer, trueOrderSize, coinConfig.currency, tradeReward, payoutSize);
|
|
132
|
+
CoinConfig memory coinConfig,
|
|
133
|
+
address currency,
|
|
134
|
+
ISwapRouter swapRouter,
|
|
135
|
+
IWETH weth
|
|
136
|
+
) internal returns (SellResult memory result) {
|
|
137
|
+
if (recipient == address(0)) {
|
|
138
|
+
revert AddressZero();
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
uint256 amountOut = _executeSwap(orderSize, minAmountOut, sqrtPriceLimitX96, currency, swapRouter);
|
|
142
|
+
result.trueOrderSize = _handleRefund(beforeCoinBalance, orderSize, recipient);
|
|
143
|
+
(result.payoutSize, result.tradeReward) = _handlePayoutAndRewards(amountOut, recipient, tradeReferrer, coinConfig, currency, weth);
|
|
199
144
|
}
|
|
200
145
|
|
|
201
146
|
/// @dev Handles incoming currency transfers for buy orders; if WETH is the currency the caller has the option to send native-ETH
|
|
202
147
|
/// @param orderSize The total size of the order in the currency
|
|
203
148
|
/// @param trueOrderSize The actual amount being used for the swap after fees
|
|
204
|
-
function _handleIncomingCurrency(uint256 orderSize, uint256 trueOrderSize, address currency,
|
|
205
|
-
if (currency == weth && msg.value > 0) {
|
|
149
|
+
function _handleIncomingCurrency(uint256 orderSize, uint256 trueOrderSize, address currency, ISwapRouter swapRouter, IWETH weth) internal {
|
|
150
|
+
if (currency == address(weth) && msg.value > 0) {
|
|
206
151
|
if (msg.value != orderSize) {
|
|
207
152
|
revert ICoin.EthAmountMismatch();
|
|
208
153
|
}
|
|
@@ -212,7 +157,7 @@ library UniV3BuySell {
|
|
|
212
157
|
}
|
|
213
158
|
|
|
214
159
|
IWETH(weth).deposit{value: trueOrderSize}();
|
|
215
|
-
IWETH(weth).approve(swapRouter, trueOrderSize);
|
|
160
|
+
IWETH(weth).approve(address(swapRouter), trueOrderSize);
|
|
216
161
|
} else {
|
|
217
162
|
// Ensure ETH is not sent with a non-ETH pair
|
|
218
163
|
if (msg.value != 0) {
|
|
@@ -227,100 +172,27 @@ library UniV3BuySell {
|
|
|
227
172
|
revert ICoin.ERC20TransferAmountMismatch();
|
|
228
173
|
}
|
|
229
174
|
|
|
230
|
-
IERC20(currency).approve(swapRouter, trueOrderSize);
|
|
175
|
+
IERC20(currency).approve(address(swapRouter), trueOrderSize);
|
|
231
176
|
}
|
|
232
177
|
}
|
|
233
178
|
|
|
234
179
|
/// @dev Handles sending ETH and ERC20 payouts and refunds to recipients
|
|
235
180
|
/// @param orderPayout The amount of currency to pay out
|
|
236
181
|
/// @param recipient The address to receive the payout
|
|
237
|
-
function _handlePayout(uint256 orderPayout, address recipient, address currency,
|
|
238
|
-
if (currency == weth) {
|
|
182
|
+
function _handlePayout(uint256 orderPayout, address recipient, address currency, IWETH weth) internal {
|
|
183
|
+
if (currency == address(weth)) {
|
|
239
184
|
Address.sendValue(payable(recipient), orderPayout);
|
|
240
185
|
} else {
|
|
241
186
|
IERC20(currency).safeTransfer(recipient, orderPayout);
|
|
242
187
|
}
|
|
243
188
|
}
|
|
244
189
|
|
|
245
|
-
|
|
246
|
-
function _handleTradeRewards(uint256 totalValue, address _tradeReferrer, CoinConfig memory coinConfig) internal {
|
|
247
|
-
address protocolRewardRecipient = coinConfig.protocolRewardRecipient;
|
|
248
|
-
address platformReferrer = coinConfig.platformReferrer;
|
|
249
|
-
address currency = coinConfig.currency;
|
|
250
|
-
address weth = coinConfig.uniswapV3Config.weth;
|
|
251
|
-
address payoutRecipient = coinConfig.payoutRecipient;
|
|
252
|
-
IProtocolRewards protocolRewards = IProtocolRewards(coinConfig.protocolRewards);
|
|
253
|
-
|
|
254
|
-
if (_tradeReferrer == address(0)) {
|
|
255
|
-
_tradeReferrer = protocolRewardRecipient;
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
uint256 tokenCreatorFee = _calculateReward(totalValue, CoinConstants.TOKEN_CREATOR_FEE_BPS);
|
|
259
|
-
uint256 platformReferrerFee = _calculateReward(totalValue, CoinConstants.PLATFORM_REFERRER_FEE_BPS);
|
|
260
|
-
uint256 tradeReferrerFee = _calculateReward(totalValue, CoinConstants.TRADE_REFERRER_FEE_BPS);
|
|
261
|
-
uint256 protocolFee = totalValue - tokenCreatorFee - platformReferrerFee - tradeReferrerFee;
|
|
262
|
-
|
|
263
|
-
if (currency == weth) {
|
|
264
|
-
address[] memory recipients = new address[](4);
|
|
265
|
-
uint256[] memory amounts = new uint256[](4);
|
|
266
|
-
bytes4[] memory reasons = new bytes4[](4);
|
|
267
|
-
|
|
268
|
-
recipients[0] = payoutRecipient;
|
|
269
|
-
amounts[0] = tokenCreatorFee;
|
|
270
|
-
reasons[0] = bytes4(keccak256("COIN_CREATOR_REWARD"));
|
|
271
|
-
|
|
272
|
-
recipients[1] = platformReferrer;
|
|
273
|
-
amounts[1] = platformReferrerFee;
|
|
274
|
-
reasons[1] = bytes4(keccak256("COIN_PLATFORM_REFERRER_REWARD"));
|
|
275
|
-
|
|
276
|
-
recipients[2] = _tradeReferrer;
|
|
277
|
-
amounts[2] = tradeReferrerFee;
|
|
278
|
-
reasons[2] = bytes4(keccak256("COIN_TRADE_REFERRER_REWARD"));
|
|
279
|
-
|
|
280
|
-
recipients[3] = protocolRewardRecipient;
|
|
281
|
-
amounts[3] = protocolFee;
|
|
282
|
-
reasons[3] = bytes4(keccak256("COIN_PROTOCOL_REWARD"));
|
|
283
|
-
|
|
284
|
-
IProtocolRewards(protocolRewards).depositBatch{value: totalValue}(recipients, amounts, reasons, "");
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
if (currency != weth) {
|
|
288
|
-
IERC20(currency).safeTransfer(payoutRecipient, tokenCreatorFee);
|
|
289
|
-
IERC20(currency).safeTransfer(platformReferrer, platformReferrerFee);
|
|
290
|
-
IERC20(currency).safeTransfer(_tradeReferrer, tradeReferrerFee);
|
|
291
|
-
IERC20(currency).safeTransfer(protocolRewardRecipient, protocolFee);
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
emit ICoin.CoinTradeRewards(
|
|
295
|
-
payoutRecipient,
|
|
296
|
-
platformReferrer,
|
|
297
|
-
_tradeReferrer,
|
|
298
|
-
protocolRewardRecipient,
|
|
299
|
-
tokenCreatorFee,
|
|
300
|
-
platformReferrerFee,
|
|
301
|
-
tradeReferrerFee,
|
|
302
|
-
protocolFee,
|
|
303
|
-
currency
|
|
304
|
-
);
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
function _distributeMarketRewards(
|
|
308
|
-
LpPosition[] memory positions,
|
|
309
|
-
address poolAddress,
|
|
310
|
-
address currency,
|
|
311
|
-
address coin,
|
|
312
|
-
CoinConfig memory coinConfig
|
|
313
|
-
) internal returns (ICoin.MarketRewards memory) {
|
|
314
|
-
uint256 totalAmountToken0;
|
|
315
|
-
uint256 totalAmountToken1;
|
|
316
|
-
uint256 amount0;
|
|
317
|
-
uint256 amount1;
|
|
318
|
-
|
|
190
|
+
function _collectFees(LpPosition[] storage positions, address poolAddress) internal returns (uint256 totalAmountToken0, uint256 totalAmountToken1) {
|
|
319
191
|
for (uint256 i; i < positions.length; i++) {
|
|
320
192
|
// Must burn to update the collect mapping on the pool
|
|
321
193
|
IUniswapV3Pool(poolAddress).burn(positions[i].tickLower, positions[i].tickUpper, 0);
|
|
322
194
|
|
|
323
|
-
(amount0, amount1) = IUniswapV3Pool(poolAddress).collect(
|
|
195
|
+
(uint256 amount0, uint256 amount1) = IUniswapV3Pool(poolAddress).collect(
|
|
324
196
|
address(this),
|
|
325
197
|
positions[i].tickLower,
|
|
326
198
|
positions[i].tickUpper,
|
|
@@ -331,119 +203,29 @@ library UniV3BuySell {
|
|
|
331
203
|
totalAmountToken0 += amount0;
|
|
332
204
|
totalAmountToken1 += amount1;
|
|
333
205
|
}
|
|
334
|
-
|
|
335
|
-
address token0 = currency < address(this) ? currency : address(this);
|
|
336
|
-
address token1 = currency < address(this) ? address(this) : currency;
|
|
337
|
-
|
|
338
|
-
ICoin.MarketRewards memory rewards;
|
|
339
|
-
|
|
340
|
-
rewards = _transferMarketRewards(token0, totalAmountToken0, rewards, coin, coinConfig);
|
|
341
|
-
rewards = _transferMarketRewards(token1, totalAmountToken1, rewards, coin, coinConfig);
|
|
342
|
-
|
|
343
|
-
emit ICoin.CoinMarketRewards(coinConfig.payoutRecipient, coinConfig.platformReferrer, coinConfig.protocolRewardRecipient, coinConfig.currency, rewards);
|
|
344
|
-
|
|
345
|
-
return rewards;
|
|
346
206
|
}
|
|
347
207
|
|
|
348
208
|
/// @dev Collects and distributes accrued fees from all LP positions
|
|
349
|
-
function handleMarketRewards(
|
|
350
|
-
|
|
351
|
-
address currency
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
209
|
+
function handleMarketRewards(
|
|
210
|
+
CoinConfig memory coinConfig,
|
|
211
|
+
address currency,
|
|
212
|
+
address poolAddress,
|
|
213
|
+
LpPosition[] storage positions,
|
|
214
|
+
IWETH weth,
|
|
215
|
+
address doppler
|
|
216
|
+
) internal returns (ICoin.MarketRewards memory rewards) {
|
|
217
|
+
address coin = address(this);
|
|
218
|
+
(uint256 totalAmountToken0, uint256 totalAmountToken1) = _collectFees(positions, poolAddress);
|
|
355
219
|
|
|
356
|
-
|
|
357
|
-
|
|
220
|
+
address token0 = currency < coin ? currency : coin;
|
|
221
|
+
address token1 = currency < coin ? coin : currency;
|
|
358
222
|
|
|
359
|
-
|
|
360
|
-
address token,
|
|
361
|
-
uint256 totalAmount,
|
|
362
|
-
ICoin.MarketRewards memory rewards,
|
|
363
|
-
address coin,
|
|
364
|
-
CoinConfig memory coinConfig
|
|
365
|
-
) internal returns (ICoin.MarketRewards memory) {
|
|
366
|
-
address payoutRecipient = coinConfig.payoutRecipient;
|
|
367
|
-
address platformReferrer = coinConfig.platformReferrer;
|
|
368
|
-
address protocolRewardRecipient = coinConfig.protocolRewardRecipient;
|
|
369
|
-
address currency = coinConfig.currency;
|
|
370
|
-
address weth = coinConfig.uniswapV3Config.weth;
|
|
371
|
-
address airlock = coinConfig.uniswapV3Config.airlock;
|
|
372
|
-
address protocolRewards = coinConfig.protocolRewards;
|
|
373
|
-
|
|
374
|
-
if (totalAmount > 0) {
|
|
375
|
-
address dopplerRecipient = IAirlock(airlock).owner();
|
|
376
|
-
uint256 dopplerPayout = _calculateReward(totalAmount, CoinConstants.DOPPLER_MARKET_REWARD_BPS);
|
|
377
|
-
uint256 creatorPayout = _calculateReward(totalAmount, CoinConstants.CREATOR_MARKET_REWARD_BPS);
|
|
378
|
-
uint256 platformReferrerPayout = _calculateReward(totalAmount, CoinConstants.PLATFORM_REFERRER_MARKET_REWARD_BPS);
|
|
379
|
-
uint256 protocolPayout = totalAmount - creatorPayout - platformReferrerPayout - dopplerPayout;
|
|
380
|
-
|
|
381
|
-
if (token == weth) {
|
|
382
|
-
IWETH(weth).withdraw(totalAmount);
|
|
383
|
-
|
|
384
|
-
rewards.totalAmountCurrency = totalAmount;
|
|
385
|
-
rewards.creatorPayoutAmountCurrency = creatorPayout;
|
|
386
|
-
rewards.platformReferrerAmountCurrency = platformReferrerPayout;
|
|
387
|
-
rewards.protocolAmountCurrency = protocolPayout;
|
|
388
|
-
|
|
389
|
-
address[] memory recipients = new address[](4);
|
|
390
|
-
recipients[0] = payoutRecipient;
|
|
391
|
-
recipients[1] = platformReferrer;
|
|
392
|
-
recipients[2] = protocolRewardRecipient;
|
|
393
|
-
recipients[3] = dopplerRecipient;
|
|
394
|
-
|
|
395
|
-
uint256[] memory amounts = new uint256[](4);
|
|
396
|
-
amounts[0] = rewards.creatorPayoutAmountCurrency;
|
|
397
|
-
amounts[1] = rewards.platformReferrerAmountCurrency;
|
|
398
|
-
amounts[2] = rewards.protocolAmountCurrency;
|
|
399
|
-
amounts[3] = dopplerPayout;
|
|
400
|
-
|
|
401
|
-
bytes4[] memory reasons = new bytes4[](4);
|
|
402
|
-
reasons[0] = bytes4(keccak256("COIN_CREATOR_MARKET_REWARD"));
|
|
403
|
-
reasons[1] = bytes4(keccak256("COIN_PLATFORM_REFERRER_MARKET_REWARD"));
|
|
404
|
-
reasons[2] = bytes4(keccak256("COIN_PROTOCOL_MARKET_REWARD"));
|
|
405
|
-
reasons[3] = bytes4(keccak256("COIN_DOPPLER_MARKET_REWARD"));
|
|
406
|
-
|
|
407
|
-
IProtocolRewards(protocolRewards).depositBatch{value: totalAmount}(recipients, amounts, reasons, "");
|
|
408
|
-
IProtocolRewards(protocolRewards).withdrawFor(dopplerRecipient, dopplerPayout);
|
|
409
|
-
} else if (token == coin) {
|
|
410
|
-
rewards.totalAmountCoin = totalAmount;
|
|
411
|
-
rewards.creatorPayoutAmountCoin = creatorPayout;
|
|
412
|
-
rewards.platformReferrerAmountCoin = platformReferrerPayout;
|
|
413
|
-
rewards.protocolAmountCoin = protocolPayout;
|
|
414
|
-
|
|
415
|
-
IERC20(coin).safeTransfer(payoutRecipient, rewards.creatorPayoutAmountCoin);
|
|
416
|
-
IERC20(coin).safeTransfer(platformReferrer, rewards.platformReferrerAmountCoin);
|
|
417
|
-
IERC20(coin).safeTransfer(protocolRewardRecipient, rewards.protocolAmountCoin);
|
|
418
|
-
IERC20(coin).safeTransfer(dopplerRecipient, dopplerPayout);
|
|
419
|
-
} else {
|
|
420
|
-
rewards.totalAmountCurrency = totalAmount;
|
|
421
|
-
rewards.creatorPayoutAmountCurrency = creatorPayout;
|
|
422
|
-
rewards.platformReferrerAmountCurrency = platformReferrerPayout;
|
|
423
|
-
rewards.protocolAmountCurrency = protocolPayout;
|
|
424
|
-
|
|
425
|
-
IERC20(currency).safeTransfer(payoutRecipient, creatorPayout);
|
|
426
|
-
IERC20(currency).safeTransfer(platformReferrer, platformReferrerPayout);
|
|
427
|
-
IERC20(currency).safeTransfer(protocolRewardRecipient, protocolPayout);
|
|
428
|
-
IERC20(currency).safeTransfer(dopplerRecipient, dopplerPayout);
|
|
429
|
-
}
|
|
430
|
-
}
|
|
223
|
+
rewards = CoinRewards.transferBothRewards(token0, totalAmountToken0, token1, totalAmountToken1, coin, coinConfig, currency, weth, doppler);
|
|
431
224
|
|
|
432
|
-
|
|
225
|
+
emit ICoin.CoinMarketRewards(coinConfig.payoutRecipient, coinConfig.platformReferrer, coinConfig.protocolRewardRecipient, currency, rewards);
|
|
433
226
|
}
|
|
434
227
|
|
|
435
|
-
/// @dev Utility for computing amounts in basis points.
|
|
436
228
|
function _calculateReward(uint256 amount, uint256 bps) internal pure returns (uint256) {
|
|
437
|
-
return (amount
|
|
438
|
-
}
|
|
439
|
-
|
|
440
|
-
function calculatePositions(bool isCoinToken0, PoolConfiguration memory poolConfiguration) internal pure returns (LpPosition[] memory positions) {
|
|
441
|
-
if (poolConfiguration.version == CoinConfigurationVersions.LEGACY_POOL_VERSION) {
|
|
442
|
-
positions = CoinLegacyMarket.calculatePositions(isCoinToken0, poolConfiguration);
|
|
443
|
-
} else if (poolConfiguration.version == CoinConfigurationVersions.DOPPLER_UNI_V3_POOL_VERSION) {
|
|
444
|
-
positions = CoinDopplerUniV3.calculatePositions(isCoinToken0, poolConfiguration);
|
|
445
|
-
} else {
|
|
446
|
-
revert InvalidPoolVersion();
|
|
447
|
-
}
|
|
229
|
+
return CoinRewards.calculateReward(amount, bps);
|
|
448
230
|
}
|
|
449
231
|
}
|
|
@@ -0,0 +1,65 @@
|
|
|
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 {Commands} from "@uniswap/universal-router/contracts/libraries/Commands.sol";
|
|
6
|
+
import {Actions} from "@uniswap/v4-periphery/src/libraries/Actions.sol";
|
|
7
|
+
import {IV4Router} from "@uniswap/v4-periphery/src/interfaces/IV4Router.sol";
|
|
8
|
+
import {CoinCommon} from "./CoinCommon.sol";
|
|
9
|
+
import {IPermit2} from "permit2/src/interfaces/IPermit2.sol";
|
|
10
|
+
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
|
11
|
+
import {Currency} from "@uniswap/v4-core/src/types/Currency.sol";
|
|
12
|
+
import {ISwapPathRouter} from "../interfaces/ISwapPathRouter.sol";
|
|
13
|
+
import {IHasPoolKey} from "../interfaces/ICoinV4.sol";
|
|
14
|
+
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
|
|
15
|
+
import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol";
|
|
16
|
+
import {PathKey} from "@uniswap/v4-periphery/src/libraries/PathKey.sol";
|
|
17
|
+
|
|
18
|
+
library UniV4SwapHelper {
|
|
19
|
+
function buildExactInputSingleSwapCommand(
|
|
20
|
+
address currencyIn,
|
|
21
|
+
uint128 amountIn,
|
|
22
|
+
address currencyOut,
|
|
23
|
+
uint128 minAmountOut,
|
|
24
|
+
PoolKey memory key,
|
|
25
|
+
bytes memory hookData
|
|
26
|
+
) internal pure returns (bytes memory commands, bytes[] memory inputs) {
|
|
27
|
+
bool zeroForOne = Currency.unwrap(key.currency0) == currencyIn;
|
|
28
|
+
|
|
29
|
+
// now buy some coin for usdc
|
|
30
|
+
commands = abi.encodePacked(uint8(Commands.V4_SWAP));
|
|
31
|
+
|
|
32
|
+
bytes memory actions = abi.encodePacked(uint8(Actions.SWAP_EXACT_IN_SINGLE), uint8(Actions.SETTLE_ALL), uint8(Actions.TAKE_ALL));
|
|
33
|
+
|
|
34
|
+
bytes[] memory params = new bytes[](3);
|
|
35
|
+
|
|
36
|
+
// First parameter: swap configuration
|
|
37
|
+
params[0] = abi.encode(
|
|
38
|
+
IV4Router.ExactInputSingleParams({
|
|
39
|
+
poolKey: key,
|
|
40
|
+
zeroForOne: zeroForOne,
|
|
41
|
+
amountIn: amountIn, // amount of tokens we're swapping
|
|
42
|
+
amountOutMinimum: minAmountOut, // minimum amount we expect to receive
|
|
43
|
+
hookData: hookData
|
|
44
|
+
})
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
// Second parameter: specify input tokens for the swap
|
|
48
|
+
// encode SETTLE_ALL parameters
|
|
49
|
+
params[1] = abi.encode(currencyIn, amountIn);
|
|
50
|
+
|
|
51
|
+
// Third parameter: specify output tokens from the swap
|
|
52
|
+
// encode TAKE_ALL parameters
|
|
53
|
+
params[2] = abi.encode(currencyOut, minAmountOut);
|
|
54
|
+
|
|
55
|
+
inputs = new bytes[](1);
|
|
56
|
+
|
|
57
|
+
// Combine actions and params into inputs
|
|
58
|
+
inputs[0] = abi.encode(actions, params);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function approveTokenWithPermit2(IPermit2 permit2, address router, address token, uint160 amount, uint48 expiration) internal {
|
|
62
|
+
IERC20(token).approve(address(permit2), type(uint256).max);
|
|
63
|
+
permit2.approve(token, router, amount, expiration);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
pragma solidity ^0.8.23;
|
|
3
|
+
|
|
4
|
+
import {Currency} from "@uniswap/v4-core/src/types/Currency.sol";
|
|
5
|
+
import {ISwapPathRouter} from "../interfaces/ISwapPathRouter.sol";
|
|
6
|
+
import {IHasPoolKey} from "../interfaces/ICoinV4.sol";
|
|
7
|
+
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
|
|
8
|
+
import {IPoolManager, PoolKey} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol";
|
|
9
|
+
import {SwapParams} from "@uniswap/v4-core/src/types/PoolOperation.sol";
|
|
10
|
+
import {BalanceDelta, BalanceDeltaLibrary} from "@uniswap/v4-core/src/types/BalanceDelta.sol";
|
|
11
|
+
import {IHasSwapPath} from "../interfaces/ICoinV4.sol";
|
|
12
|
+
import {TickMath} from "@uniswap/v4-core/src/libraries/TickMath.sol";
|
|
13
|
+
import {PathKey} from "@uniswap/v4-periphery/src/libraries/PathKey.sol";
|
|
14
|
+
import {IDeployedCoinVersionLookup} from "../interfaces/IDeployedCoinVersionLookup.sol";
|
|
15
|
+
import {IZoraV4CoinHook} from "../interfaces/IZoraV4CoinHook.sol";
|
|
16
|
+
import {CoinConfigurationVersions} from "./CoinConfigurationVersions.sol";
|
|
17
|
+
|
|
18
|
+
library UniV4SwapToCurrency {
|
|
19
|
+
using BalanceDeltaLibrary for BalanceDelta;
|
|
20
|
+
|
|
21
|
+
function swapToPath(
|
|
22
|
+
IPoolManager poolManager,
|
|
23
|
+
uint128 amount0,
|
|
24
|
+
uint128 amount1,
|
|
25
|
+
Currency currencyIn,
|
|
26
|
+
PathKey[] memory path
|
|
27
|
+
) internal returns (Currency lastCurrency, uint128 lastCurrencyBalance) {
|
|
28
|
+
require(path.length > 0, IZoraV4CoinHook.PathMustHaveAtLeastOneStep());
|
|
29
|
+
|
|
30
|
+
// do first swap - the first swap updates output the balance with the initial balance that existed before the swap
|
|
31
|
+
(lastCurrency, lastCurrencyBalance) = _doFirstSwapFromCoinToCurrency(poolManager, path[0], currencyIn, amount0, amount1);
|
|
32
|
+
|
|
33
|
+
// for each path, swap the currency to the next currency
|
|
34
|
+
for (uint256 i = 1; i < path.length; i++) {
|
|
35
|
+
(PoolKey memory poolKey, bool zeroForOne) = _getPoolAndSwapDirection(path[i], lastCurrency);
|
|
36
|
+
lastCurrencyBalance = uint128(_swap(poolManager, poolKey, zeroForOne, -int128(lastCurrencyBalance), ""));
|
|
37
|
+
lastCurrency = zeroForOne ? poolKey.currency1 : poolKey.currency0;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function _doFirstSwapFromCoinToCurrency(
|
|
42
|
+
IPoolManager poolManager,
|
|
43
|
+
PathKey memory pathKey,
|
|
44
|
+
Currency coin,
|
|
45
|
+
uint128 amount0,
|
|
46
|
+
uint128 amount1
|
|
47
|
+
) private returns (Currency outputCurrency, uint128 outputAmount) {
|
|
48
|
+
(PoolKey memory poolKey, bool zeroForOne) = _getPoolAndSwapDirection(pathKey, coin);
|
|
49
|
+
|
|
50
|
+
uint128 inputAmount = zeroForOne ? amount0 : amount1;
|
|
51
|
+
|
|
52
|
+
outputCurrency = zeroForOne ? poolKey.currency1 : poolKey.currency0;
|
|
53
|
+
|
|
54
|
+
uint128 initialAmountCurrency = zeroForOne ? amount1 : amount0;
|
|
55
|
+
|
|
56
|
+
// if not swapping any coin for currency, output amount is amount of currency
|
|
57
|
+
if (inputAmount == 0) {
|
|
58
|
+
outputAmount = initialAmountCurrency;
|
|
59
|
+
} else {
|
|
60
|
+
outputAmount = initialAmountCurrency + uint128(_swap(poolManager, poolKey, zeroForOne, -int128(inputAmount), bytes("")));
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function _swap(
|
|
65
|
+
IPoolManager poolManager,
|
|
66
|
+
PoolKey memory poolKey,
|
|
67
|
+
bool zeroForOne,
|
|
68
|
+
int256 amountSpecified,
|
|
69
|
+
bytes memory hookData
|
|
70
|
+
) private returns (int128 reciprocalAmount) {
|
|
71
|
+
// for protection of exactOut swaps, sqrtPriceLimit is not exposed as a feature in this contract
|
|
72
|
+
unchecked {
|
|
73
|
+
BalanceDelta delta = poolManager.swap(
|
|
74
|
+
poolKey,
|
|
75
|
+
SwapParams(zeroForOne, amountSpecified, zeroForOne ? TickMath.MIN_SQRT_PRICE + 1 : TickMath.MAX_SQRT_PRICE - 1),
|
|
76
|
+
hookData
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
reciprocalAmount = (zeroForOne == amountSpecified < 0) ? delta.amount1() : delta.amount0();
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/// @notice Get the pool and swap direction for a given PathKey
|
|
84
|
+
/// @param params the given PathKey
|
|
85
|
+
/// @param currencyIn the input currency
|
|
86
|
+
/// @return poolKey the pool key of the swap
|
|
87
|
+
/// @return zeroForOne the direction of the swap, true if currency0 is being swapped for currency1
|
|
88
|
+
function _getPoolAndSwapDirection(PathKey memory params, Currency currencyIn) internal pure returns (PoolKey memory poolKey, bool zeroForOne) {
|
|
89
|
+
Currency currencyOut = params.intermediateCurrency;
|
|
90
|
+
(Currency currency0, Currency currency1) = currencyIn < currencyOut ? (currencyIn, currencyOut) : (currencyOut, currencyIn);
|
|
91
|
+
|
|
92
|
+
zeroForOne = currencyIn == currency0;
|
|
93
|
+
poolKey = PoolKey(currency0, currency1, params.fee, params.tickSpacing, params.hooks);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function getSubSwapPath(address currency, IDeployedCoinVersionLookup coinVersionLookup) internal view returns (PathKey[] memory) {
|
|
97
|
+
if (!_hasSwapPath(currency, coinVersionLookup)) {
|
|
98
|
+
return new PathKey[](0);
|
|
99
|
+
}
|
|
100
|
+
return IHasSwapPath(currency).getPayoutSwapPath(coinVersionLookup).path;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function _hasSwapPath(address currency, IDeployedCoinVersionLookup coinVersionLookup) private view returns (bool) {
|
|
104
|
+
if (CoinConfigurationVersions.isV4(coinVersionLookup.getVersionForDeployedCoin(currency))) {
|
|
105
|
+
return IERC165(currency).supportsInterface(type(IHasSwapPath).interfaceId);
|
|
106
|
+
}
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
}
|