-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathBaseV1-core.sol
612 lines (501 loc) · 25.2 KB
/
BaseV1-core.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
// SPDX-License-Identifier: MIT
pragma solidity 0.8.11;
import "./BaseV1-libs.sol";
interface IBaseV1Callee {
function hook(address sender, uint amount0, uint amount1, bytes calldata data) external;
}
// The base pair of pools, either stable or volatile
contract BaseV1Pair {
string public name;
string public symbol;
uint8 public constant decimals = 18;
// Used to denote stable or volatile pair, not immutable since construction happens in the initialize method for CREATE2 deterministic addresses
bool public immutable stable;
uint public totalSupply = 0;
mapping(address => mapping (address => uint)) public allowance;
mapping(address => uint) public balanceOf;
bytes32 internal DOMAIN_SEPARATOR;
// keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
bytes32 internal constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;
mapping(address => uint) public nonces;
uint internal constant MINIMUM_LIQUIDITY = 10**3;
address public immutable token0;
address public immutable token1;
address immutable factory;
// Structure to capture time period obervations every 30 minutes, used for local oracles
struct Observation {
uint timestamp;
uint reserve0Cumulative;
uint reserve1Cumulative;
uint totalSupplyCumulative;
}
// Capture oracle reading every 30 minutes
uint public periodSize = 1800;
Observation[] public observations;
uint internal immutable decimals0;
uint internal immutable decimals1;
uint public reserve0;
uint public reserve1;
uint public blockTimestampLast;
uint public reserve0CumulativeLast;
uint public reserve1CumulativeLast;
uint public totalSupplyCumulativeLast;
// position assigned to each LP to track their current index0 & index1 vs the global position
mapping(address => uint) public supplyIndex0;
mapping(address => uint) public supplyIndex1;
event Mint(address indexed sender, uint amount0, uint amount1);
event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);
event Swap(
address indexed sender,
uint amount0In,
uint amount1In,
uint amount0Out,
uint amount1Out,
address indexed to
);
event Sync(uint reserve0, uint reserve1);
event Claim(address indexed sender, address indexed recipient, uint amount0, uint amount1);
event Transfer(address indexed from, address indexed to, uint amount);
event Approval(address indexed owner, address indexed spender, uint amount);
constructor() {
factory = msg.sender;
(address _token0, address _token1, bool _stable) = BaseV1Factory(msg.sender).getInitializable();
(token0, token1, stable) = (_token0, _token1, _stable);
if (_stable) {
name = string(abi.encodePacked("StableV1 AMM - ", erc20(_token0).symbol(), "/", erc20(_token1).symbol()));
symbol = string(abi.encodePacked("sAMM-", erc20(_token0).symbol(), "/", erc20(_token1).symbol()));
} else {
name = string(abi.encodePacked("VolatileV1 AMM - ", erc20(_token0).symbol(), "/", erc20(_token1).symbol()));
symbol = string(abi.encodePacked("vAMM-", erc20(_token0).symbol(), "/", erc20(_token1).symbol()));
}
decimals0 = 10**erc20(_token0).decimals();
decimals1 = 10**erc20(_token1).decimals();
observations.push(Observation(block.timestamp, 0, 0,0));
}
function setPeriodSize(uint periodSize_) external {
require(msg.sender == factory);
periodSize = periodSize_;
}
// simple re-entrancy check
uint internal _unlocked = 1;
modifier lock() {
require(_unlocked == 1);
_unlocked = 2;
_;
_unlocked = 1;
}
function observationLength() external view returns (uint) {
return observations.length;
}
function lastObservation() public view returns (Observation memory) {
return observations[observations.length-1];
}
function metadata() external view returns (uint dec0, uint dec1, uint r0, uint r1, bool st, address t0, address t1) {
return (decimals0, decimals1, reserve0, reserve1, stable, token0, token1);
}
function tokens() external view returns (address, address) {
return (token0, token1);
}
function getReserves() public view returns (uint _reserve0, uint _reserve1, uint _blockTimestampLast) {
_reserve0 = reserve0;
_reserve1 = reserve1;
_blockTimestampLast = blockTimestampLast;
}
// update reserves and, on the first call per block, price accumulators
function _update(uint balance0, uint balance1, uint _reserve0, uint _reserve1, uint _totalSupply) internal {
uint blockTimestamp = block.timestamp;
uint timeElapsed = blockTimestamp - blockTimestampLast;
if (timeElapsed > 0 && _reserve0 != 0 && _reserve1 != 0) {
reserve0CumulativeLast += _reserve0 * timeElapsed;
reserve1CumulativeLast += _reserve1 * timeElapsed;
totalSupplyCumulativeLast += _totalSupply * timeElapsed; //update totalSupply after each change in LP Token supply
}
Observation memory _point = lastObservation();
timeElapsed = blockTimestamp - _point.timestamp; // compare the last observation with current timestamp, if greater than 30 minutes, record a new event
if (timeElapsed > periodSize) {
observations.push(Observation(blockTimestamp, reserve0CumulativeLast, reserve1CumulativeLast, totalSupplyCumulativeLast));
}
reserve0 = balance0;
reserve1 = balance1;
blockTimestampLast = blockTimestamp;
emit Sync(reserve0, reserve1);
}
// produces the cumulative price using counterfactuals to save gas and avoid a call to sync.
function currentCumulativePrices() public view returns (uint reserve0Cumulative, uint reserve1Cumulative, uint blockTimestamp) {
blockTimestamp = block.timestamp;
reserve0Cumulative = reserve0CumulativeLast;
reserve1Cumulative = reserve1CumulativeLast;
// if time has elapsed since the last update on the pair, mock the accumulated price values
(uint _reserve0, uint _reserve1, uint _blockTimestampLast) = getReserves();
if (_blockTimestampLast != blockTimestamp) {
uint timeElapsed = blockTimestamp - _blockTimestampLast;
reserve0Cumulative += _reserve0 * timeElapsed;
reserve1Cumulative += _reserve1 * timeElapsed;
}
}
// gives the current twap price measured from amountIn * tokenIn gives amountOut
function current(address tokenIn, uint amountIn) external view returns (uint amountOut) {
Observation memory _observation = lastObservation();
(uint reserve0Cumulative, uint reserve1Cumulative,) = currentCumulativePrices();
if (block.timestamp == _observation.timestamp) {
_observation = observations[observations.length-2];
}
uint timeElapsed = block.timestamp - _observation.timestamp;
uint _reserve0 = (reserve0Cumulative - _observation.reserve0Cumulative) / timeElapsed;
uint _reserve1 = (reserve1Cumulative - _observation.reserve1Cumulative) / timeElapsed;
amountOut = _getAmountOut(amountIn, tokenIn, _reserve0, _reserve1);
}
// as per `current`, however allows user configured granularity, up to the full window size
function quote(address tokenIn, uint amountIn, uint granularity) external view returns (uint amountOut) {
uint [] memory _prices = sample(tokenIn, amountIn, granularity, 1);
uint priceAverageCumulative;
for (uint i = 0; i < _prices.length; i++) {
priceAverageCumulative += _prices[i];
}
return priceAverageCumulative / granularity;
}
// returns a memory set of twap prices
function prices(address tokenIn, uint amountIn, uint points) external view returns (uint[] memory) {
return sample(tokenIn, amountIn, points, 1);
}
function sample(address tokenIn, uint amountIn, uint points, uint window) public view returns (uint[] memory) {
uint[] memory _prices = new uint[](points);
uint lastIndex = observations.length-1;
require(lastIndex >= points * window, "PAIR::NOT READY FOR PRICING"); //log if the price is requested and there are not enough observations
uint i = lastIndex - (points * window); // point from which to begin the sample
uint nextIndex = 0;
uint index = 0;
for (; i < lastIndex; i+=window) {
nextIndex = i + window;
uint timeElapsed = observations[nextIndex].timestamp - observations[i].timestamp;
uint _reserve0 = (observations[nextIndex].reserve0Cumulative - observations[i].reserve0Cumulative) / timeElapsed;
uint _reserve1 = (observations[nextIndex].reserve1Cumulative - observations[i].reserve1Cumulative) / timeElapsed;
_prices[index] = _getAmountOut(amountIn, tokenIn, _reserve0, _reserve1);
index = index + 1;
}
return _prices;
}
function reserves(uint granularity) external view returns(uint, uint) {
(uint[] memory _reserves0, uint[] memory _reserves1)= sampleReserves(granularity, 1);
uint reserveAverageCumulative0;
uint reserveAverageCumulative1;
for (uint i = 0; i < _reserves0.length; ++i) {
reserveAverageCumulative0 += _reserves0[i]; //normalize the reserves for TWAP LP Oracle pricing,
reserveAverageCumulative1 += _reserves1[i]; //
}
return (reserveAverageCumulative0 / granularity, reserveAverageCumulative1 / granularity);
}
function sampleReserves(uint points, uint window) public view returns (uint[] memory, uint[] memory) {
uint[] memory _reserves0 = new uint[](points);
uint[] memory _reserves1 = new uint[](points);
uint lastIndex = observations.length-1;
require(lastIndex >= points * window, "PAIR::NOT READY FOR PRICING");
uint i = lastIndex - (points * window); // point from which to begin the sample
uint nextIndex = 0;
uint index = 0;
uint timeElapsed;
for(; i < lastIndex; i+=window) {
nextIndex = i + window;
timeElapsed = observations[nextIndex].timestamp - observations[i].timestamp;
_reserves0[index] = (observations[nextIndex].reserve0Cumulative - observations[i].reserve0Cumulative) / timeElapsed;
_reserves1[index] = (observations[nextIndex].reserve1Cumulative - observations[i].reserve1Cumulative) / timeElapsed;
index = index + 1;
}
return (_reserves0, _reserves1);
}
function totalSupplyAvg(uint granularity) external view returns(uint) {
uint[] memory _totalSupplyAvg = sampleSupply(granularity, 1);
uint totalSupplyCumulativeAvg;
for (uint i = 0; i < _totalSupplyAvg.length; ++i) {
totalSupplyCumulativeAvg += _totalSupplyAvg[i]; //totalSupply denominated in terms of 1e18
}
return (totalSupplyCumulativeAvg / granularity);
}
function sampleSupply(uint points, uint window) public view returns (uint[] memory) {
uint[] memory _totalSupply = new uint[](points);
uint lastIndex = observations.length-1;
require(lastIndex >= points * window, "PAIR::NOT READY FOR PRICING");
uint i = lastIndex - (points * window); // point from which to begin the sample
uint nextIndex = 0;
uint index = 0;
uint timeElapsed;
for(; i < lastIndex; i+=window) {
nextIndex = i + window;
timeElapsed = observations[nextIndex].timestamp - observations[i].timestamp;
_totalSupply[index] = (observations[nextIndex].totalSupplyCumulative - observations[i].totalSupplyCumulative) / timeElapsed;
index = index + 1;
}
return _totalSupply;
}
// this low-level function should be called from a contract which performs important safety checks
// standard uniswap v2 implementation
function mint(address to) external lock returns (uint liquidity) {
(uint _reserve0, uint _reserve1) = (reserve0, reserve1);
uint _balance0 = erc20(token0).balanceOf(address(this));
uint _balance1 = erc20(token1).balanceOf(address(this));
uint _amount0 = _balance0 - _reserve0;
uint _amount1 = _balance1 - _reserve1;
uint _totalSupply = totalSupply; // gas savings, must be defined here since totalSupply can update in _mintFee
if (_totalSupply == 0) {
liquidity = Math.sqrt(_amount0 * _amount1) - MINIMUM_LIQUIDITY;
_mint(address(0), MINIMUM_LIQUIDITY); // permanently lock the first MINIMUM_LIQUIDITY tokens
} else {
liquidity = Math.min(_amount0 * _totalSupply / _reserve0, _amount1 * _totalSupply / _reserve1);
}
require(liquidity > 0, "ILM"); // BaseV1: INSUFFICIENT_LIQUIDITY_MINTED
_mint(to, liquidity);
_update(_balance0, _balance1, _reserve0, _reserve1, _totalSupply);
emit Mint(msg.sender, _amount0, _amount1);
}
// this low-level function should be called from a contract which performs important safety checks
// standard uniswap v2 implementation
function burn(address to) external lock returns (uint amount0, uint amount1) {
(uint _reserve0, uint _reserve1) = (reserve0, reserve1);
(address _token0, address _token1) = (token0, token1);
uint _balance0 = erc20(_token0).balanceOf(address(this));
uint _balance1 = erc20(_token1).balanceOf(address(this));
uint _liquidity = balanceOf[address(this)];
uint _totalSupply = totalSupply; // gas savings, must be defined here since totalSupply can update in _mintFee
amount0 = _liquidity * _balance0 / _totalSupply; // using balances ensures pro-rata distribution
amount1 = _liquidity * _balance1 / _totalSupply; // using balances ensures pro-rata distribution
require(amount0 > 0 && amount1 > 0, "ILB"); // BaseV1: INSUFFICIENT_LIQUIDITY_BURNED
_burn(address(this), _liquidity);
_safeTransfer(_token0, to, amount0);
_safeTransfer(_token1, to, amount1);
_balance0 = erc20(_token0).balanceOf(address(this));
_balance1 = erc20(_token1).balanceOf(address(this));
_update(_balance0, _balance1, _reserve0, _reserve1, _totalSupply);
emit Burn(msg.sender, amount0, amount1, to);
}
// this low-level function should be called from a contract which performs important safety checks
function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external lock {
require(!BaseV1Factory(factory).isPaused());
require(amount0Out > 0 || amount1Out > 0, "IOA"); // BaseV1: INSUFFICIENT_OUTPUT_AMOUNT
(uint _reserve0, uint _reserve1) = (reserve0, reserve1);
require(amount0Out < _reserve0 && amount1Out < _reserve1, "IL"); // BaseV1: INSUFFICIENT_LIQUIDITY
uint _balance0;
uint _balance1;
{ // scope for _token{0,1}, avoids stack too deep errors
(address _token0, address _token1) = (token0, token1);
require(to != _token0 && to != _token1, "IT"); // BaseV1: INVALID_TO
if (amount0Out > 0) _safeTransfer(_token0, to, amount0Out); // optimistically transfer tokens
if (amount1Out > 0) _safeTransfer(_token1, to, amount1Out); // optimistically transfer tokens
if (data.length > 0) IBaseV1Callee(to).hook(msg.sender, amount0Out, amount1Out, data); // callback, used for flash loans
_balance0 = erc20(_token0).balanceOf(address(this));
_balance1 = erc20(_token1).balanceOf(address(this));
}
uint amount0In = _balance0 > _reserve0 - amount0Out ? _balance0 - (_reserve0 - amount0Out) : 0;
uint amount1In = _balance1 > _reserve1 - amount1Out ? _balance1 - (_reserve1 - amount1Out) : 0;
require(amount0In > 0 || amount1In > 0, "IIA"); // BaseV1: INSUFFICIENT_INPUT_AMOUNT
{ // scope for reserve{0,1}Adjusted, avoids stack too deep errors
(address _token0, address _token1) = (token0, token1);
_balance0 = erc20(_token0).balanceOf(address(this));
// since we removed tokens, we need to reconfirm balances, can also simply use previous balance - amountIn/ 10000, but doing balanceOf again as safety check
_balance1 = erc20(_token1).balanceOf(address(this));
// The curve, either x3y+y3x for stable pools, or x*y for volatile pools
require(_k(_balance0, _balance1) >= _k(_reserve0, _reserve1), "K"); // BaseV1: K
}
_update(_balance0, _balance1, _reserve0, _reserve1, totalSupply);
emit Swap(msg.sender, amount0In, amount1In, amount0Out, amount1Out, to);
}
// force balances to match reserves
function skim(address to) external lock {
(address _token0, address _token1) = (token0, token1);
_safeTransfer(_token0, to, erc20(_token0).balanceOf(address(this)) - (reserve0));
_safeTransfer(_token1, to, erc20(_token1).balanceOf(address(this)) - (reserve1));
}
// force reserves to match balances
function sync() external lock {
_update(erc20(token0).balanceOf(address(this)), erc20(token1).balanceOf(address(this)), reserve0, reserve1, totalSupply);
}
function _f(uint x0, uint y) internal pure returns (uint) {
return x0*(y*y/1e18*y/1e18)/1e18+(x0*x0/1e18*x0/1e18)*y/1e18;
}
function _d(uint x0, uint y) internal pure returns (uint) {
return 3*x0*(y*y/1e18)/1e18+(x0*x0/1e18*x0/1e18);
}
function _get_y(uint x0, uint xy, uint y) internal pure returns (uint) {
for (uint i = 0; i < 255; i++) {
uint y_prev = y;
uint k = _f(x0, y);
if (k < xy) {
uint dy = (xy - k)*1e18/_d(x0, y);
y = y + dy;
} else {
uint dy = (k - xy)*1e18/_d(x0, y);
y = y - dy;
}
if (y > y_prev) {
if (y - y_prev <= 1) {
return y;
}
} else {
if (y_prev - y <= 1) {
return y;
}
}
}
return y;
}
function getAmountOut(uint amountIn, address tokenIn) external view returns (uint) {
(uint _reserve0, uint _reserve1) = (reserve0, reserve1);
//amountIn -= amountIn / 10000; // remove fee from amount received
return _getAmountOut(amountIn, tokenIn, _reserve0, _reserve1);
}
function _getAmountOut(uint amountIn, address tokenIn, uint _reserve0, uint _reserve1) internal view returns (uint) {
if (stable) {
uint xy = _k(_reserve0, _reserve1);
_reserve0 = _reserve0 * 1e18 / decimals0;
_reserve1 = _reserve1 * 1e18 / decimals1;
(uint reserveA, uint reserveB) = tokenIn == token0 ? (_reserve0, _reserve1) : (_reserve1, _reserve0);
amountIn = tokenIn == token0 ? amountIn * 1e18 / decimals0 : amountIn * 1e18 / decimals1;
uint y = reserveB - _get_y(amountIn+reserveA, xy, reserveB);
return y * (tokenIn == token0 ? decimals1 : decimals0) / 1e18;
} else {
(uint reserveA, uint reserveB) = tokenIn == token0 ? (_reserve0, _reserve1) : (_reserve1, _reserve0);
return amountIn * reserveB / (reserveA + amountIn);
}
}
function _k(uint x, uint y) public view returns (uint) {
if (stable) {
uint _x = x * 1e18 / decimals0;
uint _y = y * 1e18 / decimals1;
uint _a = (_x * _y) / 1e18;
uint _b = ((_x * _x) / 1e18 + (_y * _y) / 1e18);
return _a * _b / 1e18; // x3y+y3x >= k
} else {
return x * y; // xy >= k
}
}
function _mint(address dst, uint amount) internal {
totalSupply += amount;
balanceOf[dst] += amount;
emit Transfer(address(0), dst, amount);
}
function _burn(address dst, uint amount) internal {
totalSupply -= amount;
balanceOf[dst] -= amount;
emit Transfer(dst, address(0), amount);
}
function approve(address spender, uint amount) external returns (bool) {
allowance[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
return true;
}
function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external {
require(deadline >= block.timestamp, "BaseV1: EXPIRED");
DOMAIN_SEPARATOR = keccak256(
abi.encode(
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
keccak256(bytes(name)),
keccak256("1"),
block.chainid,
address(this)
)
);
bytes32 digest = keccak256(
abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR,
keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline))
)
);
address recoveredAddress = ecrecover(digest, v, r, s);
require(recoveredAddress != address(0) && recoveredAddress == owner, "BaseV1: INVALID_SIGNATURE");
allowance[owner][spender] = value;
emit Approval(owner, spender, value);
}
function transfer(address dst, uint amount) external returns (bool) {
_transferTokens(msg.sender, dst, amount);
return true;
}
function transferFrom(address src, address dst, uint amount) external returns (bool) {
address spender = msg.sender;
uint spenderAllowance = allowance[src][spender];
if (spender != src && spenderAllowance != type(uint).max) {
uint newAllowance = spenderAllowance - amount;
allowance[src][spender] = newAllowance;
emit Approval(src, spender, newAllowance);
}
_transferTokens(src, dst, amount);
return true;
}
function _transferTokens(address src, address dst, uint amount) internal {
balanceOf[src] -= amount;
balanceOf[dst] += amount;
emit Transfer(src, dst, amount);
}
function _safeTransfer(address token,address to,uint256 value) internal {
require(token.code.length > 0);
(bool success, bytes memory data) =
token.call(abi.encodeWithSelector(erc20.transfer.selector, to, value));
require(success && (data.length == 0 || abi.decode(data, (bool))));
}
}
contract BaseV1Factory {
bool public isPaused;
address public pauser;
address public pendingPauser;
address public admin;
uint MaxPeriod = 3600;
mapping(address => mapping(address => mapping(bool => address))) public getPair;
address[] public allPairs;
mapping(address => bool) public isPair; // simplified check if its a pair, given that `stable` flag might not be available in peripherals
address internal _temp0;
address internal _temp1;
bool internal _temp;
event PairCreated(address indexed token0, address indexed token1, bool stable, address pair, uint);
constructor() {
pauser = msg.sender;
isPaused = false;
admin = msg.sender;
}
// admin for setting the periodSize in pairs
function setAdmin(address admin_) external {
require(msg.sender == admin);
admin = admin_;
}
function setPeriodSize(uint newPeriod) external {
require(msg.sender == admin);
require(newPeriod <= MaxPeriod);
for (uint i; i < allPairs.length; ) {
BaseV1Pair(allPairs[i]).setPeriodSize(newPeriod);
unchecked {++i;}
}
}
function allPairsLength() external view returns (uint) {
return allPairs.length;
}
function setPauser(address _pauser) external {
require(msg.sender == pauser);
pendingPauser = _pauser;
}
function acceptPauser() external {
require(msg.sender == pendingPauser);
pauser = pendingPauser;
}
function setPause(bool _state) external {
require(msg.sender == pauser);
isPaused = _state;
}
function pairCodeHash() external pure returns (bytes32) {
return keccak256(type(BaseV1Pair).creationCode);
}
function getInitializable() external view returns (address, address, bool) {
return (_temp0, _temp1, _temp);
}
function createPair(address tokenA, address tokenB, bool stable) external returns (address pair) {
require(tokenA != tokenB, "IA"); // BaseV1: IDENTICAL_ADDRESSES
(address token0, address token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);
require(token0 != address(0), "ZA"); // BaseV1: ZERO_ADDRESS
require(getPair[token0][token1][stable] == address(0), "PE"); // BaseV1: PAIR_EXISTS - single check is sufficient
bytes32 salt = keccak256(abi.encodePacked(token0, token1, stable)); // notice salt includes stable as well, 3 parameters
(_temp0, _temp1, _temp) = (token0, token1, stable);
pair = address(new BaseV1Pair{salt:salt}());
getPair[token0][token1][stable] = pair;
getPair[token1][token0][stable] = pair; // populate mapping in the reverse direction
allPairs.push(pair);
isPair[pair] = true;
emit PairCreated(token0, token1, stable, pair, allPairs.length);
}
}