-
Notifications
You must be signed in to change notification settings - Fork 0
/
Markets.sol
131 lines (114 loc) · 5.24 KB
/
Markets.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
// SPDX-License-Identifier: MIT
pragma solidity ^0.8;
uint256 constant MIN_LIQUIDITY_PER_RESERVE = 1e4;
uint256 constant MAX_LIQUIDITY_PER_RESERVE = 1e32;
// A multidimensional virtual AMM for 18-decimal asset tokens.
abstract contract AssetMarket {
error MinLiquidityError();
error MaxLiquidityError();
error InsufficientLiquidityError();
error InvalidAssetError();
error MinKError();
error PrecisionError();
uint8 internal immutable ASSET_COUNT;
mapping (uint8 => uint256) private _reserves;
constructor(uint8 assetCount) {
assert(assetCount <= type(uint8).max);
ASSET_COUNT = uint8(assetCount);
}
function _init(uint256[] memory initialReserveAmounts) internal {
assert(initialReserveAmounts.length == ASSET_COUNT);
uint256[] memory reserves;
// uint256 and 18-decimal tokens have identical bit representations so this is OK.
assembly ("memory-safe") { reserves := initialReserveAmounts }
for (uint256 i; i < reserves.length; ++i) {
if (reserves[i] < MIN_LIQUIDITY_PER_RESERVE) revert MinLiquidityError();
if (reserves[i] > MAX_LIQUIDITY_PER_RESERVE) revert MaxLiquidityError();
}
_storeReserves(reserves);
}
function _buy(uint8 fromIdx, uint8 toIdx, uint256 toAmt)
internal returns (uint256 fromAmt)
{
if (fromIdx >= ASSET_COUNT || toIdx >= ASSET_COUNT) revert InvalidAssetError();
if (fromIdx == toIdx) return toAmt;
uint256[] memory reserves = _loadReserves();
fromAmt = _quoteBuyFromReserves(reserves[fromIdx], reserves[toIdx], toAmt);
reserves[fromIdx] = reserves[fromIdx] + fromAmt;
reserves[toIdx] = reserves[toIdx] - toAmt;
_reserves[fromIdx] = reserves[fromIdx];
_reserves[toIdx] = reserves[toIdx];
}
function _sell(uint8 fromIdx, uint8 toIdx, uint256 fromAmt)
internal returns (uint256 toAmt)
{
if (fromIdx >= ASSET_COUNT || toIdx >= ASSET_COUNT) revert InvalidAssetError();
if (fromIdx == toIdx) return fromAmt;
uint256[] memory reserves = _loadReserves();
toAmt = _quoteSellFromReserves(reserves[fromIdx], reserves[toIdx], fromAmt);
reserves[fromIdx] = reserves[fromIdx] + fromAmt;
reserves[toIdx] = reserves[toIdx] - toAmt;
_reserves[fromIdx] = reserves[fromIdx];
_reserves[toIdx] = reserves[toIdx];
}
function _leak(uint8 idx, uint256 amt) internal {
if (idx >= ASSET_COUNT) revert InvalidAssetError();
uint256 toReserve = _getReserve(idx);
if (amt >= toReserve) revert InsufficientLiquidityError();
if (toReserve - amt < MIN_LIQUIDITY_PER_RESERVE) revert MinLiquidityError();
_reserves[idx] = toReserve - amt;
}
function _getReserve(uint8 idx) internal view returns (uint256 weiReserve) {
return _reserves[idx];
}
function _getRate(uint8 fromIdx, uint8 toIdx) internal view returns (uint256 toRate) {
if (fromIdx == toIdx) return 1e18;
return _reserves[toIdx] * 1e18 / _reserves[fromIdx];
}
function _quoteBuy(uint8 fromIdx, uint8 toIdx, uint256 toAmt)
internal view returns (uint256 fromAmt)
{
if (fromIdx >= ASSET_COUNT || toIdx >= ASSET_COUNT) revert InvalidAssetError();
if (fromIdx == toIdx) return toAmt;
uint256[] memory reserves = _loadReserves();
fromAmt = _quoteBuyFromReserves(reserves[fromIdx], reserves[toIdx], toAmt);
}
function _quoteSell(uint8 fromIdx, uint8 toIdx, uint256 fromAmt)
internal view returns (uint256 toAmt)
{
if (fromIdx >= ASSET_COUNT || toIdx >= ASSET_COUNT) revert InvalidAssetError();
if (fromIdx == toIdx) return fromAmt;
uint256[] memory reserves = _loadReserves();
toAmt = _quoteSellFromReserves(reserves[fromIdx], reserves[toIdx], fromAmt);
}
function _quoteBuyFromReserves(uint256 fromReserve, uint256 toReserve, uint256 toAmt)
private pure returns (uint256 fromAmt)
{
if (toAmt == 0) return 0;
if (toAmt >= toReserve) revert InsufficientLiquidityError();
if (toReserve - toAmt < MIN_LIQUIDITY_PER_RESERVE) revert MinLiquidityError();
uint256 d = toReserve - toAmt;
fromAmt = (toAmt * fromReserve + d - 1) / d;
if (fromReserve + fromAmt > MAX_LIQUIDITY_PER_RESERVE) revert MaxLiquidityError();
if (fromAmt == 0 || fromAmt < toAmt * fromReserve / toReserve) revert PrecisionError();
}
function _quoteSellFromReserves(uint256 fromReserve, uint256 toReserve, uint256 fromAmt)
private pure returns (uint256 toAmt)
{
if (fromAmt == 0) return 0;
if (fromReserve + fromAmt > MAX_LIQUIDITY_PER_RESERVE) revert MaxLiquidityError();
toAmt = (fromAmt * toReserve) / (fromReserve + fromAmt);
if (toReserve - toAmt < MIN_LIQUIDITY_PER_RESERVE) revert MinLiquidityError();
}
function _storeReserves(uint256[] memory reserves) internal {
for (uint8 i; i < reserves.length; ++i) {
_reserves[i] = reserves[i];
}
}
function _loadReserves() internal view returns (uint256[] memory reserves) {
reserves = new uint256[](ASSET_COUNT);
for (uint8 i; i < ASSET_COUNT; ++i) {
reserves[i] = _reserves[i];
}
}
}