@oldzeppelin/contract 1.1.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/.docker/Dockerfile +17 -0
- package/.dockerignore +7 -0
- package/.env.sample +24 -0
- package/.gitlab-ci.yml +51 -0
- package/.gitmodules +15 -0
- package/.prettierrc +10 -0
- package/.solcover.js +4 -0
- package/.vscode/settings.json +23 -0
- package/LICENSE.MD +51 -0
- package/README.md +135 -0
- package/contracts/arbitrum/contracts/controllers/UniswapV2ControllerArbitrum.sol +37 -0
- package/contracts/arbitrum/contracts/controllers/UniswapV3ControllerArbitrum.sol +46 -0
- package/contracts/arbitrum/contracts/oracle/PriceOracleArbitrum.sol +51 -0
- package/contracts/main/contracts/controllers/Controller.sol +61 -0
- package/contracts/main/contracts/controllers/IController.sol +81 -0
- package/contracts/main/contracts/controllers/OneInchV5Controller.sol +332 -0
- package/contracts/main/contracts/controllers/UnoswapV2Controller.sol +789 -0
- package/contracts/main/contracts/controllers/UnoswapV3Controller.sol +1018 -0
- package/contracts/main/contracts/core/CoreWhitelist.sol +192 -0
- package/contracts/main/contracts/core/ICoreWhitelist.sol +92 -0
- package/contracts/main/contracts/core/IUFarmCore.sol +95 -0
- package/contracts/main/contracts/core/UFarmCore.sol +402 -0
- package/contracts/main/contracts/fund/FundFactory.sol +59 -0
- package/contracts/main/contracts/fund/IUFarmFund.sol +68 -0
- package/contracts/main/contracts/fund/UFarmFund.sol +504 -0
- package/contracts/main/contracts/oracle/ChainlinkedOracle.sol +71 -0
- package/contracts/main/contracts/oracle/IChainlinkAggregator.sol +18 -0
- package/contracts/main/contracts/oracle/IPriceOracle.sol +55 -0
- package/contracts/main/contracts/oracle/PriceOracle.sol +20 -0
- package/contracts/main/contracts/oracle/PriceOracleCore.sol +212 -0
- package/contracts/main/contracts/oracle/WstETHOracle.sol +64 -0
- package/contracts/main/contracts/permissions/Permissions.sol +54 -0
- package/contracts/main/contracts/permissions/UFarmPermissionsModel.sol +136 -0
- package/contracts/main/contracts/pool/IPoolAdmin.sol +57 -0
- package/contracts/main/contracts/pool/IUFarmPool.sol +304 -0
- package/contracts/main/contracts/pool/PerformanceFeeLib.sol +81 -0
- package/contracts/main/contracts/pool/PoolAdmin.sol +437 -0
- package/contracts/main/contracts/pool/PoolFactory.sol +74 -0
- package/contracts/main/contracts/pool/PoolWhitelist.sol +70 -0
- package/contracts/main/contracts/pool/UFarmPool.sol +959 -0
- package/contracts/main/shared/AssetController.sol +194 -0
- package/contracts/main/shared/ECDSARecover.sol +91 -0
- package/contracts/main/shared/NZGuard.sol +99 -0
- package/contracts/main/shared/SafeOPS.sol +128 -0
- package/contracts/main/shared/UFarmCoreLink.sol +83 -0
- package/contracts/main/shared/UFarmErrors.sol +16 -0
- package/contracts/main/shared/UFarmMathLib.sol +80 -0
- package/contracts/main/shared/UFarmOwnableUUPS.sol +59 -0
- package/contracts/main/shared/UFarmOwnableUUPSBeacon.sol +34 -0
- package/contracts/test/Block.sol +15 -0
- package/contracts/test/InchSwapTestProxy.sol +292 -0
- package/contracts/test/MockPoolAdmin.sol +8 -0
- package/contracts/test/MockUFarmPool.sol +8 -0
- package/contracts/test/MockV3wstETHstETHAgg.sol +128 -0
- package/contracts/test/MockedWETH9.sol +72 -0
- package/contracts/test/OneInchToUFarmTestEnv.sol +466 -0
- package/contracts/test/StableCoin.sol +25 -0
- package/contracts/test/UFarmMockSequencerUptimeFeed.sol +44 -0
- package/contracts/test/UFarmMockV3Aggregator.sol +145 -0
- package/contracts/test/UUPSBlock.sol +19 -0
- package/contracts/test/ufarmLocal/MulticallV3.sol +220 -0
- package/contracts/test/ufarmLocal/controllers/UniswapV2ControllerUFarm.sol +27 -0
- package/contracts/test/ufarmLocal/controllers/UniswapV3ControllerUFarm.sol +43 -0
- package/deploy/100_test_env_setup.ts +483 -0
- package/deploy/20_deploy_uniV2.ts +48 -0
- package/deploy/21_create_pairs_uniV2.ts +149 -0
- package/deploy/22_deploy_mocked_aggregators.ts +123 -0
- package/deploy/22_deploy_wsteth_oracle.ts +65 -0
- package/deploy/23_deploy_uniV3.ts +80 -0
- package/deploy/24_create_pairs_uniV3.ts +140 -0
- package/deploy/25_deploy_oneInch.ts +38 -0
- package/deploy/2_deploy_multicall.ts +34 -0
- package/deploy/30_deploy_price_oracle.ts +33 -0
- package/deploy/3_deploy_lido.ts +114 -0
- package/deploy/40_deploy_pool_beacon.ts +19 -0
- package/deploy/41_deploy_poolAdmin_beacon.ts +19 -0
- package/deploy/42_deploy_ufarmcore.ts +29 -0
- package/deploy/43_deploy_fund_beacon.ts +19 -0
- package/deploy/4_deploy_tokens.ts +76 -0
- package/deploy/50_deploy_poolFactory.ts +35 -0
- package/deploy/51_deploy_fundFactory.ts +29 -0
- package/deploy/60_init_contracts.ts +101 -0
- package/deploy/61_whitelist_tokens.ts +18 -0
- package/deploy/70_deploy_uniV2Controller.ts +70 -0
- package/deploy/71_deploy_uniV3Controller.ts +67 -0
- package/deploy/72_deploy_oneInchController.ts +25 -0
- package/deploy/79_whitelist_controllers.ts +125 -0
- package/deploy/ufarm/arbitrum/1_prepare_env.ts +82 -0
- package/deploy/ufarm/arbitrum/2_deploy_ufarm.ts +178 -0
- package/deploy/ufarm/arbitrum-sepolia/1000_prepare_arb_sepolia_env.ts +308 -0
- package/deploy-config.json +112 -0
- package/deploy-data/oracles.csv +32 -0
- package/deploy-data/protocols.csv +10 -0
- package/deploy-data/tokens.csv +32 -0
- package/docker-compose.yml +67 -0
- package/hardhat.config.ts +449 -0
- package/index.js +93 -0
- package/package.json +82 -0
- package/scripts/_deploy_helpers.ts +992 -0
- package/scripts/_deploy_network_options.ts +49 -0
- package/scripts/activatePool.ts +51 -0
- package/scripts/createPool.ts +62 -0
- package/scripts/deploy_1inch_proxy.ts +98 -0
- package/scripts/pool-data.ts +420 -0
- package/scripts/post-deploy.sh +24 -0
- package/scripts/setUniV2Rate.ts +252 -0
- package/scripts/swapOneInchV5.ts +94 -0
- package/scripts/swapUniswapV2.ts +65 -0
- package/scripts/swapUniswapV3.ts +71 -0
- package/scripts/test.ts +61 -0
- package/scripts/typings-copy-artifacts.ts +83 -0
- package/tasks/boostPool.ts +39 -0
- package/tasks/createFund.ts +44 -0
- package/tasks/deboostPool.ts +48 -0
- package/tasks/grantUFarmPermissions.ts +57 -0
- package/tasks/index.ts +7 -0
- package/tasks/mintUSDT.ts +62 -0
- package/test/Periphery.test.ts +640 -0
- package/test/PriceOracle.test.ts +82 -0
- package/test/TestCases.MD +109 -0
- package/test/UFarmCore.test.ts +331 -0
- package/test/UFarmFund.test.ts +406 -0
- package/test/UFarmPool.test.ts +4736 -0
- package/test/_fixtures.ts +783 -0
- package/test/_helpers.ts +2195 -0
- package/test/_oneInchTestData.ts +632 -0
- package/tsconfig.json +12 -0
@@ -0,0 +1,640 @@
|
|
1
|
+
// SPDX-License-Identifier: UNLICENSED
|
2
|
+
|
3
|
+
import { expect } from 'chai'
|
4
|
+
import { loadFixture, takeSnapshot } from '@nomicfoundation/hardhat-network-helpers'
|
5
|
+
import hre, { ethers } from 'hardhat'
|
6
|
+
import { Multicall, ContractCallResults, ContractCallContext } from 'ethereum-multicall'
|
7
|
+
import { tokensFixture, UniswapV3Fixture } from './_fixtures'
|
8
|
+
import {
|
9
|
+
IERC20Metadata,
|
10
|
+
IERC20Metadata__factory,
|
11
|
+
INonfungiblePositionManager,
|
12
|
+
Multicall3__factory,
|
13
|
+
} from '../typechain-types'
|
14
|
+
import {
|
15
|
+
constants,
|
16
|
+
getBlockchainTimestamp,
|
17
|
+
MintableToken,
|
18
|
+
mintTokens,
|
19
|
+
safeApprove,
|
20
|
+
} from './_helpers'
|
21
|
+
import { customSetTimeout } from '../scripts/_deploy_helpers'
|
22
|
+
|
23
|
+
import {
|
24
|
+
Pool,
|
25
|
+
TickMath,
|
26
|
+
nearestUsableTick,
|
27
|
+
Position,
|
28
|
+
maxLiquidityForAmounts,
|
29
|
+
} from '@uniswap/v3-sdk'
|
30
|
+
import { BigintIsh } from '@uniswap/sdk-core'
|
31
|
+
|
32
|
+
import JSBI from 'jsbi'
|
33
|
+
|
34
|
+
describe('Multicall', async () => {
|
35
|
+
it('Should return correct results for multiple calls', async () => {
|
36
|
+
const { tokens, deployer } = await loadFixture(tokensFixture)
|
37
|
+
|
38
|
+
const multicall_factory = (await hre.ethers.getContractFactory(
|
39
|
+
'Multicall3',
|
40
|
+
)) as Multicall3__factory
|
41
|
+
|
42
|
+
const multicall_instance = await multicall_factory.deploy()
|
43
|
+
|
44
|
+
// mint tokens to deployer and prepare for multicall
|
45
|
+
let multicall_contexts: ContractCallContext[] = []
|
46
|
+
|
47
|
+
const balanceOfCall: ContractCallContext = {
|
48
|
+
reference: 'testContract2',
|
49
|
+
contractAddress: '0x6795b15f3b16Cf8fB3E56499bbC07F6261e9b0C3',
|
50
|
+
abi: [
|
51
|
+
{
|
52
|
+
name: 'balanceOf',
|
53
|
+
type: 'function',
|
54
|
+
stateMutability: 'view',
|
55
|
+
inputs: [{ name: 'account', type: 'address' }],
|
56
|
+
outputs: [{ name: 'amounts', type: 'uint256' }],
|
57
|
+
},
|
58
|
+
],
|
59
|
+
calls: [
|
60
|
+
{
|
61
|
+
reference: 'balanceOfCall',
|
62
|
+
methodName: 'balanceOf',
|
63
|
+
methodParameters: [deployer.address],
|
64
|
+
},
|
65
|
+
],
|
66
|
+
}
|
67
|
+
|
68
|
+
await Promise.all(
|
69
|
+
Object.values(tokens).map(async (token) => {
|
70
|
+
// if WETH (has attribute 'deposit') then deposit 1 ETH
|
71
|
+
if ('deposit' in token) {
|
72
|
+
await token.deposit({ value: (10n ** 18n).toString() })
|
73
|
+
}
|
74
|
+
// else if USDT (has attribute 'mint') then mint 1 USDT
|
75
|
+
else if ('mint' in token) {
|
76
|
+
await token.mint(deployer.address, (10n ** 6n).toString())
|
77
|
+
} else {
|
78
|
+
throw new Error('Unknown token')
|
79
|
+
}
|
80
|
+
multicall_contexts.push({
|
81
|
+
...balanceOfCall,
|
82
|
+
reference: await token.symbol(),
|
83
|
+
contractAddress: token.address,
|
84
|
+
})
|
85
|
+
}),
|
86
|
+
)
|
87
|
+
|
88
|
+
const multicall = new Multicall({
|
89
|
+
ethersProvider: hre.ethers.provider,
|
90
|
+
tryAggregate: false,
|
91
|
+
multicallCustomContractAddress: multicall_instance.address,
|
92
|
+
})
|
93
|
+
|
94
|
+
expect(await multicall.call(multicall_contexts)).to.be.not.reverted
|
95
|
+
})
|
96
|
+
|
97
|
+
it.skip(`UniswapV3 tick calculation test`, async () => {
|
98
|
+
const all = await loadFixture(UniswapV3Fixture)
|
99
|
+
|
100
|
+
const UNIV3 = {
|
101
|
+
FEE_TICK: {
|
102
|
+
zero01: 5,
|
103
|
+
zero05: 10,
|
104
|
+
zero3: 60,
|
105
|
+
one: 200,
|
106
|
+
},
|
107
|
+
MIN_TICK: -887272,
|
108
|
+
MAX_TICK: 887272,
|
109
|
+
MIN_SQRT_RATIO: BigInt(4295128739),
|
110
|
+
MAX_SQRT_RATIO: BigInt('1461446703485210103287273052203988822378723970342'),
|
111
|
+
}
|
112
|
+
const ONE = BigInt(1e18)
|
113
|
+
|
114
|
+
const Q96 = 2n ** 96n // Equivalent to the Solidity's type uint256 for the Q96 format in sqrtPrice
|
115
|
+
|
116
|
+
async function getCurrentPrice(pool: string, decimalsA: number, decimalsB: number) {
|
117
|
+
const pool_instance = await ethers.getContractAt('UniswapV3Pool', pool, signer)
|
118
|
+
const slot0 = await pool_instance.slot0()
|
119
|
+
const sqrtPriceX96 = slot0.sqrtPriceX96.toBigInt()
|
120
|
+
const nonNormalisedPrice = (Number(sqrtPriceX96) / 2 ** 96) ** 2
|
121
|
+
const _buyOneOfToken0 = ((nonNormalisedPrice * 10 ** decimalsA) / 10 ** decimalsB).toFixed(
|
122
|
+
decimalsB,
|
123
|
+
)
|
124
|
+
const buyOneOfToken0 = ethers.utils.parseUnits(_buyOneOfToken0, decimalsB).toBigInt()
|
125
|
+
const _buyOneOfToken1 = (1 / Number(_buyOneOfToken0)).toFixed(decimalsA)
|
126
|
+
const buyOneOfToken1 = ethers.utils.parseUnits(_buyOneOfToken1, decimalsA).toBigInt()
|
127
|
+
|
128
|
+
console.log(`Price A of B: ${buyOneOfToken0}\nPrice B of A: ${buyOneOfToken1}`)
|
129
|
+
|
130
|
+
return { buyOneOfToken0, buyOneOfToken1 }
|
131
|
+
}
|
132
|
+
|
133
|
+
function getTickAtSqrtRatio(sqrtRatioX96: bigint): number {
|
134
|
+
const sqrtPrice = Number(sqrtRatioX96) / Number(Q96)
|
135
|
+
return Math.floor((Math.log(sqrtPrice) / Math.log(1.0001)) * 2)
|
136
|
+
}
|
137
|
+
|
138
|
+
function sqrtPriceX96ToPrice(sqrtPriceX96: bigint): number {
|
139
|
+
const sqrtPrice = Number(sqrtPriceX96) / Number(Q96)
|
140
|
+
return sqrtPrice * sqrtPrice
|
141
|
+
}
|
142
|
+
|
143
|
+
function priceToSqrtPriceX96(price: number): bigint {
|
144
|
+
const sqrtPrice = Math.sqrt(price)
|
145
|
+
return BigInt(Math.floor(sqrtPrice * Number(Q96)))
|
146
|
+
}
|
147
|
+
|
148
|
+
let tokenA: MintableToken = all.tokens.WETH
|
149
|
+
let tokenB: MintableToken = all.tokens.USDT
|
150
|
+
|
151
|
+
const reversed = BigInt(tokenA.address) > BigInt(tokenB.address)
|
152
|
+
|
153
|
+
if (reversed) {
|
154
|
+
;[tokenA, tokenB] = [tokenB, tokenA]
|
155
|
+
}
|
156
|
+
|
157
|
+
const [decimalsA, decimalsB, symbolA, symbolB] = await Promise.all([
|
158
|
+
tokenA.decimals(),
|
159
|
+
tokenB.decimals(),
|
160
|
+
tokenA.symbol(),
|
161
|
+
tokenB.symbol(),
|
162
|
+
])
|
163
|
+
|
164
|
+
const fee = 500
|
165
|
+
|
166
|
+
const uniswapV3Factory_instance = all.uniswapV3Factory_instance
|
167
|
+
const nfpm_instance = all.nonFungPosManager_instance
|
168
|
+
|
169
|
+
const signer = all.wallet
|
170
|
+
|
171
|
+
const pool = await uniswapV3Factory_instance.getPool(tokenA.address, tokenB.address, fee)
|
172
|
+
const pool_instance = await ethers.getContractAt('UniswapV3Pool', pool, signer)
|
173
|
+
const slot0_initial = await pool_instance.slot0()
|
174
|
+
|
175
|
+
function maxLiquidityForAmount0Precise(
|
176
|
+
sqrtRatioAX96: JSBI,
|
177
|
+
sqrtRatioBX96: JSBI,
|
178
|
+
amount0: BigintIsh,
|
179
|
+
): JSBI {
|
180
|
+
if (JSBI.greaterThan(sqrtRatioAX96, sqrtRatioBX96)) {
|
181
|
+
;[sqrtRatioAX96, sqrtRatioBX96] = [sqrtRatioBX96, sqrtRatioAX96]
|
182
|
+
}
|
183
|
+
|
184
|
+
const numerator = JSBI.multiply(
|
185
|
+
JSBI.multiply(JSBI.BigInt(amount0), sqrtRatioAX96),
|
186
|
+
sqrtRatioBX96,
|
187
|
+
)
|
188
|
+
const denominator = JSBI.multiply(
|
189
|
+
JSBI.BigInt(Q96.toString()),
|
190
|
+
JSBI.subtract(sqrtRatioBX96, sqrtRatioAX96),
|
191
|
+
)
|
192
|
+
|
193
|
+
return JSBI.divide(numerator, denominator)
|
194
|
+
}
|
195
|
+
|
196
|
+
/**
|
197
|
+
* Computes the maximum amount of liquidity received for a given amount of token1
|
198
|
+
* @param sqrtRatioAX96 The price at the lower tick boundary
|
199
|
+
* @param sqrtRatioBX96 The price at the upper tick boundary
|
200
|
+
* @param amount1 The token1 amount
|
201
|
+
* @returns liquidity for amount1
|
202
|
+
*/
|
203
|
+
function maxLiquidityForAmount1(
|
204
|
+
sqrtRatioAX96: JSBI,
|
205
|
+
sqrtRatioBX96: JSBI,
|
206
|
+
amount1: BigintIsh,
|
207
|
+
): JSBI {
|
208
|
+
if (JSBI.greaterThan(sqrtRatioAX96, sqrtRatioBX96)) {
|
209
|
+
;[sqrtRatioAX96, sqrtRatioBX96] = [sqrtRatioBX96, sqrtRatioAX96]
|
210
|
+
}
|
211
|
+
return JSBI.divide(
|
212
|
+
JSBI.multiply(JSBI.BigInt(amount1), JSBI.BigInt(Q96.toString())),
|
213
|
+
JSBI.subtract(sqrtRatioBX96, sqrtRatioAX96),
|
214
|
+
)
|
215
|
+
}
|
216
|
+
|
217
|
+
/**
|
218
|
+
* Gets the amount1 delta between two prices
|
219
|
+
* @dev Calculates liquidity * (sqrt(upper) - sqrt(lower))
|
220
|
+
* @param sqrtRatioAX96 The price at the lower tick boundary
|
221
|
+
* @param sqrtRatioBX96 The price at the upper tick boundary
|
222
|
+
* @param liquidity The liquidity amount
|
223
|
+
* @param roundUp Whether to round the amount up, or down
|
224
|
+
* @returns Amount of token1 required to cover a position of size liquidity between the two passed prices
|
225
|
+
*/
|
226
|
+
function getAmount1Delta(
|
227
|
+
sqrtRatioAX96: JSBI,
|
228
|
+
sqrtRatioBX96: JSBI,
|
229
|
+
liquidity: JSBI,
|
230
|
+
roundUp: boolean,
|
231
|
+
): JSBI {
|
232
|
+
if (JSBI.greaterThan(sqrtRatioAX96, sqrtRatioBX96)) {
|
233
|
+
;[sqrtRatioAX96, sqrtRatioBX96] = [sqrtRatioBX96, sqrtRatioAX96]
|
234
|
+
console.log(`Swapped prices`)
|
235
|
+
}
|
236
|
+
|
237
|
+
const answer = roundUp
|
238
|
+
? JSBI.divide(
|
239
|
+
JSBI.multiply(
|
240
|
+
JSBI.BigInt(liquidity.toString()),
|
241
|
+
JSBI.subtract(sqrtRatioBX96, sqrtRatioAX96),
|
242
|
+
),
|
243
|
+
JSBI.BigInt(Q96.toString()),
|
244
|
+
)
|
245
|
+
: JSBI.divide(
|
246
|
+
JSBI.multiply(
|
247
|
+
JSBI.BigInt(liquidity.toString()),
|
248
|
+
JSBI.subtract(sqrtRatioBX96, sqrtRatioAX96),
|
249
|
+
),
|
250
|
+
JSBI.BigInt(Q96.toString()),
|
251
|
+
)
|
252
|
+
|
253
|
+
return answer
|
254
|
+
}
|
255
|
+
|
256
|
+
/**
|
257
|
+
* Gets the amount0 delta between two prices
|
258
|
+
* @dev Calculates liquidity / sqrt(lower) - liquidity / sqrt(upper),
|
259
|
+
* @param sqrtRatioAX96 - A sqrt price
|
260
|
+
* @param sqrtRatioBX96 - Another sqrt price
|
261
|
+
* @param liquidity - The amount of usable liquidity
|
262
|
+
* @param roundUp - Whether to round the amount up or down
|
263
|
+
* @returns
|
264
|
+
*/
|
265
|
+
function getAmount0Delta(
|
266
|
+
sqrtRatioAX96: JSBI,
|
267
|
+
sqrtRatioBX96: JSBI,
|
268
|
+
liquidity: JSBI,
|
269
|
+
roundUp: boolean,
|
270
|
+
): JSBI {
|
271
|
+
if (JSBI.greaterThan(sqrtRatioAX96, sqrtRatioBX96)) {
|
272
|
+
;[sqrtRatioAX96, sqrtRatioBX96] = [sqrtRatioBX96, sqrtRatioAX96]
|
273
|
+
}
|
274
|
+
|
275
|
+
const numerator1 = JSBI.leftShift(JSBI.BigInt(liquidity.toString()), JSBI.BigInt(96))
|
276
|
+
const numerator2 = JSBI.subtract(sqrtRatioBX96, sqrtRatioAX96)
|
277
|
+
|
278
|
+
const answer = roundUp
|
279
|
+
? JSBI.divide(
|
280
|
+
JSBI.divide(JSBI.multiply(numerator1, numerator2), sqrtRatioBX96),
|
281
|
+
sqrtRatioAX96,
|
282
|
+
)
|
283
|
+
: JSBI.divide(
|
284
|
+
JSBI.divide(JSBI.multiply(numerator1, numerator2), sqrtRatioBX96),
|
285
|
+
sqrtRatioAX96,
|
286
|
+
)
|
287
|
+
|
288
|
+
return answer
|
289
|
+
}
|
290
|
+
|
291
|
+
// Set the tick spacing
|
292
|
+
const tickSpacing = UNIV3.FEE_TICK.zero05
|
293
|
+
|
294
|
+
const initialPrice = await getCurrentPrice(pool, decimalsA, decimalsB)
|
295
|
+
const calculatedTick = getTickAtSqrtRatio(slot0_initial.sqrtPriceX96.toBigInt())
|
296
|
+
|
297
|
+
// expect(calculatedTick).to.be.equal(
|
298
|
+
// slot0_initial.tick,
|
299
|
+
// 'Initial tick should be equal to calculated tick',
|
300
|
+
// )
|
301
|
+
|
302
|
+
const calculatedPrice = BigInt(TickMath.getSqrtRatioAtTick(slot0_initial.tick).toString())
|
303
|
+
const tickFromCalcualtedPrice = getTickAtSqrtRatio(calculatedPrice)
|
304
|
+
|
305
|
+
// expect(calculatedTick).to.be.equal(
|
306
|
+
// tickFromCalcualtedPrice,
|
307
|
+
// 'Initial tick should be equal to tickFromCalcualtedPrice',
|
308
|
+
// )
|
309
|
+
|
310
|
+
const [priceLowerBound, priceUpperBound] = [
|
311
|
+
initialPrice.buyOneOfToken0 / 2n,
|
312
|
+
initialPrice.buyOneOfToken0 * 2n,
|
313
|
+
]
|
314
|
+
|
315
|
+
const sqrtRatioAX96 = priceToSqrtPriceX96(Number(priceLowerBound))
|
316
|
+
const sqrtRatioBX96 = priceToSqrtPriceX96(Number(priceUpperBound))
|
317
|
+
|
318
|
+
const [tickLower, tickUpper] = [
|
319
|
+
getTickAtSqrtRatio(sqrtRatioAX96),
|
320
|
+
getTickAtSqrtRatio(sqrtRatioBX96),
|
321
|
+
]
|
322
|
+
|
323
|
+
console.log(
|
324
|
+
`tick upper: ${tickUpper}\n` +
|
325
|
+
`tick initial: ${slot0_initial.tick}\n` +
|
326
|
+
`tick lower: ${tickLower}\n`,
|
327
|
+
)
|
328
|
+
|
329
|
+
// Determine the usable ticks given a tick spacing
|
330
|
+
const roundedTickLower = nearestUsableTick(tickLower, tickSpacing)
|
331
|
+
const roundedTickUpper = nearestUsableTick(tickUpper, tickSpacing)
|
332
|
+
|
333
|
+
const roundedRatioAX96 = TickMath.getSqrtRatioAtTick(roundedTickLower)
|
334
|
+
const roundedRatioBX96 = TickMath.getSqrtRatioAtTick(roundedTickUpper)
|
335
|
+
|
336
|
+
const amountA = () => 10n ** BigInt(decimalsA) // like a user input, wait for it when met
|
337
|
+
const amountB = () => 10n ** BigInt(decimalsB) // like a user input, wait for it when met
|
338
|
+
|
339
|
+
// if (slot0_initial.tick < roundedTickLower) {
|
340
|
+
// only amount0
|
341
|
+
console.log(`Tick is less than lower bound`)
|
342
|
+
|
343
|
+
const tickAbovePrice0 = nearestUsableTick(slot0_initial.tick + 100, tickSpacing)
|
344
|
+
const sqrtPrice0Above = BigInt(TickMath.getSqrtRatioAtTick(tickAbovePrice0).toString())
|
345
|
+
const price0Above = sqrtPriceX96ToPrice(sqrtPrice0Above)
|
346
|
+
const price1Above = (price0Above * 3) / 2
|
347
|
+
const _sqrtPrice1Above = priceToSqrtPriceX96(price1Above)
|
348
|
+
const _tickAbovePrice1 = getTickAtSqrtRatio(_sqrtPrice1Above)
|
349
|
+
const tickAbovePrice1 = nearestUsableTick(_tickAbovePrice1, tickSpacing)
|
350
|
+
const sqrtPrice1Above = BigInt(TickMath.getSqrtRatioAtTick(tickAbovePrice1).toString())
|
351
|
+
|
352
|
+
const liquidityAbove = maxLiquidityForAmounts(
|
353
|
+
JSBI.BigInt(slot0_initial.sqrtPriceX96.toString()),
|
354
|
+
JSBI.BigInt(sqrtPrice0Above.toString()),
|
355
|
+
JSBI.BigInt(sqrtPrice1Above.toString()),
|
356
|
+
JSBI.BigInt(amountA().toString()),
|
357
|
+
JSBI.BigInt(0),
|
358
|
+
true,
|
359
|
+
)
|
360
|
+
|
361
|
+
console.log(`Liquidity when current price below borders: \n${liquidityAbove.toString()}\n`)
|
362
|
+
|
363
|
+
const amount0Above = getAmount0Delta(
|
364
|
+
JSBI.BigInt(sqrtPrice0Above.toString()),
|
365
|
+
JSBI.BigInt(sqrtPrice1Above.toString()),
|
366
|
+
liquidityAbove,
|
367
|
+
true,
|
368
|
+
)
|
369
|
+
|
370
|
+
const mintDataAbove: INonfungiblePositionManager.MintParamsStruct = {
|
371
|
+
token0: tokenA.address,
|
372
|
+
token1: tokenB.address,
|
373
|
+
fee: fee,
|
374
|
+
tickLower: tickAbovePrice0,
|
375
|
+
tickUpper: tickAbovePrice1,
|
376
|
+
amount0Desired: amount0Above.toString(),
|
377
|
+
amount1Desired: 0,
|
378
|
+
amount0Min: 0,
|
379
|
+
amount1Min: 0,
|
380
|
+
recipient: all.deployer.address,
|
381
|
+
deadline: (await getBlockchainTimestamp(ethers.provider)) + 100,
|
382
|
+
}
|
383
|
+
|
384
|
+
let snapshotBeforeMint = await takeSnapshot()
|
385
|
+
|
386
|
+
const aToSpend = amount0Above.toString()
|
387
|
+
await mintTokens(tokenA, aToSpend, all.deployer)
|
388
|
+
await safeApprove(tokenA, nfpm_instance.address, aToSpend, all.deployer)
|
389
|
+
|
390
|
+
await nfpm_instance.mint(mintDataAbove)
|
391
|
+
console.log(`Minted position upper price\n`)
|
392
|
+
|
393
|
+
await snapshotBeforeMint.restore()
|
394
|
+
snapshotBeforeMint = await takeSnapshot()
|
395
|
+
|
396
|
+
// } else if (slot0_initial.tick < roundedTickUpper) {
|
397
|
+
|
398
|
+
// amount0 and amount1
|
399
|
+
|
400
|
+
console.log(`Tick is between bounds`)
|
401
|
+
|
402
|
+
const liquidityInside = maxLiquidityForAmounts(
|
403
|
+
JSBI.BigInt(slot0_initial.sqrtPriceX96.toString()),
|
404
|
+
JSBI.BigInt(roundedRatioAX96.toString()),
|
405
|
+
JSBI.BigInt(roundedRatioBX96.toString()),
|
406
|
+
JSBI.BigInt(amountA().toString()),
|
407
|
+
JSBI.BigInt(amountB().toString()),
|
408
|
+
true,
|
409
|
+
)
|
410
|
+
|
411
|
+
const [amount0Inside, amount1Inside] = [
|
412
|
+
getAmount0Delta(
|
413
|
+
JSBI.BigInt(slot0_initial.sqrtPriceX96.toString()),
|
414
|
+
JSBI.BigInt(roundedRatioAX96.toString()),
|
415
|
+
liquidityInside,
|
416
|
+
true,
|
417
|
+
),
|
418
|
+
getAmount1Delta(
|
419
|
+
JSBI.BigInt(slot0_initial.sqrtPriceX96.toString()),
|
420
|
+
JSBI.BigInt(roundedRatioBX96.toString()),
|
421
|
+
liquidityInside,
|
422
|
+
true,
|
423
|
+
),
|
424
|
+
]
|
425
|
+
|
426
|
+
const mintDataInside: INonfungiblePositionManager.MintParamsStruct = {
|
427
|
+
token0: tokenA.address,
|
428
|
+
token1: tokenB.address,
|
429
|
+
fee: fee,
|
430
|
+
tickLower: roundedTickLower,
|
431
|
+
tickUpper: roundedTickUpper,
|
432
|
+
amount0Desired: amount0Inside.toString(),
|
433
|
+
amount1Desired: amount1Inside.toString(),
|
434
|
+
amount0Min: 0,
|
435
|
+
amount1Min: 0,
|
436
|
+
recipient: all.deployer.address,
|
437
|
+
deadline: (await getBlockchainTimestamp(ethers.provider)) + 100,
|
438
|
+
}
|
439
|
+
|
440
|
+
const aInsideToSpend = amount0Inside.toString()
|
441
|
+
const bInsideToSpend = amount1Inside.toString()
|
442
|
+
|
443
|
+
await mintTokens(tokenA, aInsideToSpend, all.deployer)
|
444
|
+
await mintTokens(tokenB, bInsideToSpend, all.deployer)
|
445
|
+
await safeApprove(tokenA, nfpm_instance.address, aInsideToSpend, all.deployer)
|
446
|
+
await safeApprove(tokenB, nfpm_instance.address, bInsideToSpend, all.deployer)
|
447
|
+
|
448
|
+
await nfpm_instance.mint(mintDataInside)
|
449
|
+
|
450
|
+
console.log(`Minted position inside price\n`)
|
451
|
+
|
452
|
+
await snapshotBeforeMint.restore()
|
453
|
+
|
454
|
+
// console.log(`Liquidity when current price above borders: \n${liquidityBelow.toString()}\n`)
|
455
|
+
// } else {
|
456
|
+
|
457
|
+
// amount1
|
458
|
+
console.log(`Tick is below bounds`)
|
459
|
+
|
460
|
+
console.log(`Tick is greater than upper bound`)
|
461
|
+
const _tickBelowPrice1 = slot0_initial.tick - 100
|
462
|
+
const tickBelowPrice1 = nearestUsableTick(_tickBelowPrice1, tickSpacing)
|
463
|
+
const sqrtPrice1Below = BigInt(TickMath.getSqrtRatioAtTick(tickBelowPrice1).toString())
|
464
|
+
const price1Below = sqrtPriceX96ToPrice(sqrtPrice1Below)
|
465
|
+
const _price0Below = (price1Below * 2) / 3
|
466
|
+
const _sqrtPrice0Below = priceToSqrtPriceX96(_price0Below)
|
467
|
+
const _tickBelowPrice0 = getTickAtSqrtRatio(_sqrtPrice0Below)
|
468
|
+
const tickBelowPrice0 = nearestUsableTick(_tickBelowPrice0, tickSpacing)
|
469
|
+
const sqrtPrice0Below = BigInt(TickMath.getSqrtRatioAtTick(tickBelowPrice0).toString())
|
470
|
+
const price0Below = sqrtPriceX96ToPrice(sqrtPrice0Below)
|
471
|
+
|
472
|
+
const liquidityBelow = maxLiquidityForAmounts(
|
473
|
+
JSBI.BigInt(slot0_initial.sqrtPriceX96.toString()),
|
474
|
+
JSBI.BigInt(sqrtPrice0Below.toString()),
|
475
|
+
JSBI.BigInt(sqrtPrice1Below.toString()),
|
476
|
+
JSBI.BigInt(0),
|
477
|
+
JSBI.BigInt(amountB().toString()), // user input like
|
478
|
+
true,
|
479
|
+
)
|
480
|
+
|
481
|
+
console.log(`Liquidity when current price above borders: \n${liquidityBelow.toString()}\n`)
|
482
|
+
|
483
|
+
const amount1Below = getAmount1Delta(
|
484
|
+
JSBI.BigInt(sqrtPrice0Below.toString()),
|
485
|
+
JSBI.BigInt(sqrtPrice1Below.toString()),
|
486
|
+
liquidityBelow,
|
487
|
+
false,
|
488
|
+
)
|
489
|
+
|
490
|
+
const mintDataBelow: INonfungiblePositionManager.MintParamsStruct = {
|
491
|
+
token0: tokenA.address,
|
492
|
+
token1: tokenB.address,
|
493
|
+
fee: fee,
|
494
|
+
tickLower: tickBelowPrice0,
|
495
|
+
tickUpper: tickBelowPrice1,
|
496
|
+
amount0Desired: 0,
|
497
|
+
amount1Desired: amount1Below.toString(),
|
498
|
+
amount0Min: 0,
|
499
|
+
amount1Min: 0,
|
500
|
+
recipient: all.deployer.address,
|
501
|
+
deadline: (await getBlockchainTimestamp(ethers.provider)) + 100,
|
502
|
+
}
|
503
|
+
|
504
|
+
const bBelowToSpend = amount1Below.toString()
|
505
|
+
await mintTokens(tokenB, bBelowToSpend, all.deployer)
|
506
|
+
await safeApprove(tokenB, nfpm_instance.address, bBelowToSpend, all.deployer)
|
507
|
+
|
508
|
+
await nfpm_instance.mint(mintDataBelow)
|
509
|
+
|
510
|
+
console.log(`Minted position lower price\n`)
|
511
|
+
|
512
|
+
await snapshotBeforeMint.restore()
|
513
|
+
|
514
|
+
// }
|
515
|
+
})
|
516
|
+
|
517
|
+
it.skip('one inch fork test', async () => {
|
518
|
+
const [deployer] = await hre.ethers.getSigners()
|
519
|
+
console.log(`Deplyer address: ${deployer.address}`)
|
520
|
+
|
521
|
+
const routerV6 = '0x111111125421ca6dc452d289314280a0f8842a65'
|
522
|
+
const aggregationRouterV5_orig = '0x1111111254eeb25477b68fb85ed929f73a960582'
|
523
|
+
const usdcAddress = '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48'
|
524
|
+
const usdtAddress = '0xdac17f958d2ee523a2206206994597c13d831ec7'
|
525
|
+
const usdtHolderAddr = '0xDFd5293D8e347dFe59E90eFd55b2956a1343963d'
|
526
|
+
const pureOneInchTx =
|
527
|
+
'0x0502b1c5000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec700000000000000000000000000000000000000000000000000000000000f374f00000000000000000000000000000000000000000000000000000000000f0d6d0000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000180000000000000003b6d03403041cbd36888becc7bbcbc0045e3b1f144466f5f8b1ccac8'
|
528
|
+
|
529
|
+
const oneInchTx = `0x4c9ddf69000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c80502b1c5000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec700000000000000000000000000000000000000000000000000000000000f374f00000000000000000000000000000000000000000000000000000000000f0d6d0000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000180000000000000003b6d03403041cbd36888becc7bbcbc0045e3b1f144466f5f8b1ccac8000000000000000000000000000000000000000000000000`
|
530
|
+
const selectorToController =
|
531
|
+
'0x4c9ddf69000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000c8'
|
532
|
+
|
533
|
+
// Get code of original myAggrRouterV5
|
534
|
+
const code = await hre.ethers.provider.getCode(aggregationRouterV5_orig)
|
535
|
+
if (code === '0x') {
|
536
|
+
throw new Error('AggregationRouterV5 not deployed')
|
537
|
+
} else {
|
538
|
+
console.log(`AggregationRouterV5 code: ${code.slice(0, 10)}...`)
|
539
|
+
}
|
540
|
+
|
541
|
+
await await hre.ethers.provider.send('hardhat_impersonateAccount', [usdtHolderAddr])
|
542
|
+
|
543
|
+
const usdtHolder = hre.ethers.provider.getSigner(usdtHolderAddr)
|
544
|
+
console.log(`Impersonated usdt holder: ${usdtHolderAddr}`)
|
545
|
+
|
546
|
+
const usdtInstance = (await hre.ethers.getContractAt(
|
547
|
+
'@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol:IERC20Metadata',
|
548
|
+
usdtAddress,
|
549
|
+
)) as IERC20Metadata
|
550
|
+
const usdcInstance = (await hre.ethers.getContractAt(
|
551
|
+
'@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol:IERC20Metadata',
|
552
|
+
usdcAddress,
|
553
|
+
)) as IERC20Metadata
|
554
|
+
|
555
|
+
const oneInchControllerFactory = await hre.ethers.getContractFactory('InchSwapTestController')
|
556
|
+
const testProxyFactory = await hre.ethers.getContractFactory('InchSwapTestProxy')
|
557
|
+
|
558
|
+
const aggregationRouterV5Factory = await hre.ethers.getContractFactory('AggregationRouterV5')
|
559
|
+
const myAggrRouterV5 = await aggregationRouterV5Factory.deploy(
|
560
|
+
'0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2',
|
561
|
+
)
|
562
|
+
console.log(`AggregationRouterV5 deployed at: ${myAggrRouterV5.address}`)
|
563
|
+
|
564
|
+
// const inchTarget = aggregationRouterV5_orig
|
565
|
+
const inchTarget = myAggrRouterV5.address
|
566
|
+
|
567
|
+
const testController = await oneInchControllerFactory.deploy(inchTarget)
|
568
|
+
console.log(`test controller deployed at: ${testController.address}`)
|
569
|
+
|
570
|
+
const testProxy = await testProxyFactory.deploy()
|
571
|
+
console.log(`TestProxy deployed at: ${testProxy.address}`)
|
572
|
+
|
573
|
+
const usdtInitialBalance = await usdtInstance.balanceOf(testProxy.address)
|
574
|
+
const usdcInitialBalance = await usdcInstance.balanceOf(testProxy.address)
|
575
|
+
|
576
|
+
console.log(`usdt initial balance: ${usdtInitialBalance.toString()}`)
|
577
|
+
console.log(`usdc initial balance: ${usdcInitialBalance.toString()}`)
|
578
|
+
|
579
|
+
await testProxy.addController(
|
580
|
+
constants.UFarm.prtocols.OneInchProtocolString,
|
581
|
+
testController.address,
|
582
|
+
)
|
583
|
+
console.log(`OneInchController added to TestProxy`)
|
584
|
+
|
585
|
+
const usdtAmountOut = constants.ONE_HUNDRED_BUCKS.mul(20)
|
586
|
+
|
587
|
+
if ((await usdtInstance.balanceOf(usdtHolderAddr)).lt(usdtAmountOut)) {
|
588
|
+
throw new Error('usdtAmountOut is greater than balance of holder')
|
589
|
+
}
|
590
|
+
|
591
|
+
await customSetTimeout(2)
|
592
|
+
|
593
|
+
await (await usdtInstance.connect(usdtHolder).transfer(testProxy.address, usdtAmountOut)).wait()
|
594
|
+
console.log(`Transferred ${usdtAmountOut} USDT to TestProxy`)
|
595
|
+
const logs = await (
|
596
|
+
await usdtInstance.connect(usdtHolder).transfer(deployer.address, usdtAmountOut)
|
597
|
+
).wait()
|
598
|
+
// console.log(`Transferred ${JSON.stringify(logs,null,2)} USDT to Deployer`)
|
599
|
+
|
600
|
+
await customSetTimeout(2)
|
601
|
+
|
602
|
+
await safeApprove(usdtInstance, inchTarget, usdtAmountOut, deployer)
|
603
|
+
|
604
|
+
console.log(`Approved ${usdtAmountOut} USDT to AggregationRouter from deployer`)
|
605
|
+
|
606
|
+
await customSetTimeout(2)
|
607
|
+
|
608
|
+
const usdtBalanceBeforeSwap = await usdtInstance.balanceOf(deployer.address)
|
609
|
+
const usdtProxyBalanceBeforeSwap = await usdtInstance.balanceOf(testProxy.address)
|
610
|
+
console.log(`usdt balance before deployer swap: ${usdtBalanceBeforeSwap.toString()}`)
|
611
|
+
console.log(`usdt balance before proxy swap: ${usdtProxyBalanceBeforeSwap.toString()}`)
|
612
|
+
|
613
|
+
const receipt1inch = await (
|
614
|
+
await deployer.sendTransaction({ to: inchTarget, data: pureOneInchTx })
|
615
|
+
).wait()
|
616
|
+
|
617
|
+
console.log(`1inch swap executed #1 \n`)
|
618
|
+
|
619
|
+
const usdcHolderBalanceAfter = await usdcInstance.balanceOf(deployer.address)
|
620
|
+
|
621
|
+
console.log(`\n\nusdc balance after deployer swap: ${usdcHolderBalanceAfter.toString()}`)
|
622
|
+
|
623
|
+
const controllerCall = testController.interface.encodeFunctionData(`delegated1InchSwap`, [
|
624
|
+
pureOneInchTx,
|
625
|
+
])
|
626
|
+
try {
|
627
|
+
await testProxy.protocolAction(constants.UFarm.prtocols.OneInchProtocolString, controllerCall)
|
628
|
+
console.log(`1inch swap executed #2 \n`)
|
629
|
+
} catch (e) {
|
630
|
+
console.log(`Error: ${e}`)
|
631
|
+
}
|
632
|
+
|
633
|
+
try {
|
634
|
+
await testProxy.justCall(usdtAddress, inchTarget, pureOneInchTx)
|
635
|
+
console.log(`1inch swap executed #3`)
|
636
|
+
} catch (e) {
|
637
|
+
console.log(`Error: ${e}`)
|
638
|
+
}
|
639
|
+
})
|
640
|
+
})
|