-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathAMM.sol
165 lines (134 loc) · 7.77 KB
/
AMM.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
// SPDX-License-Identifier: MIT
pragma solidity >=0.7.0 <0.9.0;
import "@openzeppelin/contracts/utils/math/SafeMath.sol";
contract AMM {
using SafeMath for uint256;
uint256 totalShares; // Stores the total amount of share issued for the pool
uint256 totalToken1; // Stores the amount of Token1 locked in the pool
uint256 totalToken2; // Stores the amount of Token2 locked in the pool
uint256 K; // Algorithmic constant used to determine price
uint256 constant PRECISION = 1_000_000; // Precision of 6 digits
mapping(address => uint256) shares; // Stores the share holding of each provider
// Stores the available balance of user outside of the AMM
// For simplicity purpose, We are maintaining our own internal
// balance mapping instead of dealing with ERC-20 tokens
mapping(address => uint256) token1Balance;
mapping(address => uint256) token2Balance;
// Ensures that the _qty is non-zero and the user has enough balance
modifier validAmountCheck(mapping(address => uint256) storage _balance, uint256 _qty) {
require(_qty > 0, "Amount cannot be zero!");
require(_qty <= _balance[msg.sender], "Insufficient amount");
_;
}
// Restricts withdraw, swap feature till liquidity is added to the pool
modifier activePool() {
require(totalShares > 0, "Zero Liquidity");
_;
}
// Sends free token(s) to the invoker
function faucet(uint256 _amountToken1, uint256 _amountToken2) external {
token1Balance[msg.sender] = token1Balance[msg.sender].add(_amountToken1);
token2Balance[msg.sender] = token2Balance[msg.sender].add(_amountToken2);
}
// Returns the balance of the user
function getMyHoldings() external view returns(uint256 amountToken1, uint256 amountToken2, uint256 myShare) {
amountToken1 = token1Balance[msg.sender];
amountToken2 = token2Balance[msg.sender];
myShare = shares[msg.sender];
}
// Returns the total amount of tokens locked in the pool and the total shares issued corresponding to it
function getPoolDetails() external view returns(uint256, uint256, uint256) {
return (totalToken1, totalToken2, totalShares);
}
// Returns amount of Token1 required when providing liquidity with _amountToken2 quantity of Token2
function getEquivalentToken1Estimate(uint256 _amountToken2) public view activePool returns(uint256 reqToken1) {
reqToken1 = totalToken1.mul(_amountToken2).div(totalToken2);
}
// Returns amount of Token2 required when providing liquidity with _amountToken1 quantity of Token1
function getEquivalentToken2Estimate(uint256 _amountToken1) public view activePool returns(uint256 reqToken2) {
reqToken2 = totalToken2.mul(_amountToken1).div(totalToken1);
}
// Adding new liquidity in the pool
// Returns the amount of share issued for locking given assets
function provide(uint256 _amountToken1, uint256 _amountToken2) external validAmountCheck(token1Balance, _amountToken1) validAmountCheck(token2Balance, _amountToken2) returns(uint256 share) {
if(totalShares == 0) { // Genesis liquidity is issued 100 Shares
share = 100*PRECISION;
} else{
uint256 share1 = totalShares.mul(_amountToken1).div(totalToken1);
uint256 share2 = totalShares.mul(_amountToken2).div(totalToken2);
require(share1 == share2, "Equivalent value of tokens not provided...");
share = share1;
}
require(share > 0, "Asset value less than threshold for contribution!");
token1Balance[msg.sender] -= _amountToken1;
token2Balance[msg.sender] -= _amountToken2;
totalToken1 += _amountToken1;
totalToken2 += _amountToken2;
K = totalToken1.mul(totalToken2);
totalShares += share;
shares[msg.sender] += share;
}
// Returns the estimate of Token1 & Token2 that will be released on burning given _share
function getWithdrawEstimate(uint256 _share) public view activePool returns(uint256 amountToken1, uint256 amountToken2) {
require(_share <= totalShares, "Share should be less than totalShare");
amountToken1 = _share.mul(totalToken1).div(totalShares);
amountToken2 = _share.mul(totalToken2).div(totalShares);
}
// Removes liquidity from the pool and releases corresponding Token1 & Token2 to the withdrawer
function withdraw(uint256 _share) external activePool validAmountCheck(shares, _share) returns(uint256 amountToken1, uint256 amountToken2) {
(amountToken1, amountToken2) = getWithdrawEstimate(_share);
shares[msg.sender] -= _share;
totalShares -= _share;
totalToken1 -= amountToken1;
totalToken2 -= amountToken2;
K = totalToken1.mul(totalToken2);
token1Balance[msg.sender] += amountToken1;
token2Balance[msg.sender] += amountToken2;
}
// Returns the amount of Token2 that the user will get when swapping a given amount of Token1 for Token2
function getSwapToken1Estimate(uint256 _amountToken1) public view activePool returns(uint256 amountToken2) {
uint256 token1After = totalToken1.add(_amountToken1);
uint256 token2After = K.div(token1After);
amountToken2 = totalToken2.sub(token2After);
// To ensure that Token2's pool is not completely depleted leading to inf:0 ratio
if(amountToken2 == totalToken2) amountToken2--;
}
// Returns the amount of Token1 that the user should swap to get _amountToken2 in return
function getSwapToken1EstimateGivenToken2(uint256 _amountToken2) public view activePool returns(uint256 amountToken1) {
require(_amountToken2 < totalToken2, "Insufficient pool balance");
uint256 token2After = totalToken2.sub(_amountToken2);
uint256 token1After = K.div(token2After);
amountToken1 = token1After.sub(totalToken1);
}
// Swaps given amount of Token1 to Token2 using algorithmic price determination
function swapToken1(uint256 _amountToken1) external activePool validAmountCheck(token1Balance, _amountToken1) returns(uint256 amountToken2) {
amountToken2 = getSwapToken1Estimate(_amountToken1);
token1Balance[msg.sender] -= _amountToken1;
totalToken1 += _amountToken1;
totalToken2 -= amountToken2;
token2Balance[msg.sender] += amountToken2;
}
// Returns the amount of Token2 that the user will get when swapping a given amount of Token1 for Token2
function getSwapToken2Estimate(uint256 _amountToken2) public view activePool returns(uint256 amountToken1) {
uint256 token2After = totalToken2.add(_amountToken2);
uint256 token1After = K.div(token2After);
amountToken1 = totalToken1.sub(token1After);
// To ensure that Token1's pool is not completely depleted leading to inf:0 ratio
if(amountToken1 == totalToken1) amountToken1--;
}
// Returns the amount of Token2 that the user should swap to get _amountToken1 in return
function getSwapToken2EstimateGivenToken1(uint256 _amountToken1) public view activePool returns(uint256 amountToken2) {
require(_amountToken1 < totalToken1, "Insufficient pool balance");
uint256 token1After = totalToken1.sub(_amountToken1);
uint256 token2After = K.div(token1After);
amountToken2 = token2After.sub(totalToken2);
}
// Swaps given amount of Token2 to Token1 using algorithmic price determination
function swapToken2(uint256 _amountToken2) external activePool validAmountCheck(token2Balance, _amountToken2) returns(uint256 amountToken1) {
amountToken1 = getSwapToken2Estimate(_amountToken2);
token2Balance[msg.sender] -= _amountToken2;
totalToken2 += _amountToken2;
totalToken1 -= amountToken1;
token1Balance[msg.sender] += amountToken1;
}
}