-
Notifications
You must be signed in to change notification settings - Fork 9
/
Exchange.sol
181 lines (146 loc) · 5.8 KB
/
Exchange.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
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
interface IRegistry {
function getExchange(address _tokenAddress) external returns (address);
}
contract Exchange is ERC20 {
address public tokenAddress;
address public registryAddress;
event TokenPurchase(address indexed buyer, uint256 indexed ethSold, uint256 indexed tokensBought);
event EthPurchase(address indexed buyer, uint256 indexed tokensSold, uint256 indexed ethBought);
event AddLiquidity(address indexed provider, uint256 indexed ethAmount, uint256 indexed tokenAmount);
event RemoveLiquidity(address indexed provider, uint256 indexed ethAmount, uint256 indexed tokenAmount);
constructor(address _token) ERC20("La Paternal", "LP") {
require(_token != address(0), "invalid token address");
tokenAddress = _token;
registryAddress = msg.sender;
}
function addLiquidity(uint256 _tokenAmount)
public
payable
returns (uint256)
{
uint256 liquidity;
if (getReserve() == 0) {
liquidity = address(this).balance;
} else {
// Enforce the ratio once the pool is initialized to preserve
// prices, but not before to allow initialization.
uint256 ethReserve = address(this).balance - msg.value;
uint256 tokenReserve = getReserve();
uint256 tokenAmount = (msg.value * tokenReserve) / ethReserve;
require(_tokenAmount >= tokenAmount, "insufficient token amount");
liquidity = (totalSupply() * msg.value) / ethReserve;
}
IERC20 token = IERC20(tokenAddress);
token.transferFrom(msg.sender, address(this), _tokenAmount);
_mint(msg.sender, liquidity);
emit AddLiquidity(msg.sender, msg.value, _tokenAmount);
return liquidity;
}
function removeLiquidity(uint256 _amount)
public
returns (uint256, uint256)
{
require(_amount > 0, "invalid amount");
uint256 supply = totalSupply();
uint256 ethAmount = (address(this).balance * _amount) / supply;
uint256 tokenAmount = (getReserve() * _amount) / supply;
_burn(msg.sender, _amount);
payable(msg.sender).transfer(ethAmount);
IERC20(tokenAddress).transfer(msg.sender, tokenAmount);
emit RemoveLiquidity(msg.sender, ethAmount, tokenAmount);
return (ethAmount, tokenAmount);
}
function getReserve() public view returns (uint256) {
return IERC20(tokenAddress).balanceOf(address(this));
}
function getAmount(
uint256 inputAmount,
uint256 inputReserve,
uint256 outputReserve
) private pure returns (uint256) {
require(inputReserve > 0 && outputReserve > 0, "invalid reserves");
uint256 inputAmountWithFee = inputAmount * 99;
uint256 numerator = inputAmountWithFee * outputReserve;
uint256 denominator = (inputReserve * 100) + inputAmountWithFee;
// return (inputAmount * outputReserve) / (inputReserve + inputAmount);
return numerator / denominator;
}
function getTokenAmount(uint256 _ethSold) public view returns (uint256) {
require(_ethSold > 0, "ethSold cannot be zero");
uint256 tokenReserve = getReserve();
return getAmount(_ethSold, address(this).balance, tokenReserve);
}
function getEthAmount(uint256 _tokenSold) public view returns (uint256) {
require(_tokenSold > 0, "tokenSold cannot be zero");
uint256 tokenReserve = getReserve();
return getAmount(_tokenSold, tokenReserve, address(this).balance);
}
function ethToToken(uint256 _minTokens, address recipient) private {
uint256 tokenReserve = getReserve();
uint256 tokensBought = getAmount(
msg.value,
address(this).balance - msg.value,
tokenReserve
);
require(tokensBought >= _minTokens, "insufficient output amount");
IERC20(tokenAddress).transfer(recipient, tokensBought);
emit TokenPurchase(msg.sender, msg.value, tokensBought);
}
function ethToTokenSwap(uint256 _minTokens) public payable {
ethToToken(_minTokens, msg.sender);
}
function ethToTokenTransfer(uint256 _minTokens, address _recipient)
public
payable
{
ethToToken(_minTokens, _recipient);
}
function tokenToEthSwap(uint256 _tokensSold, uint256 _minEth) public {
uint256 tokenReserve = getReserve();
uint256 ethBought = getAmount(
_tokensSold,
tokenReserve,
address(this).balance
);
require(ethBought >= _minEth, "insufficient output amount");
IERC20(tokenAddress).transferFrom(
msg.sender,
address(this),
_tokensSold
);
payable(msg.sender).transfer(ethBought);
emit EthPurchase(msg.sender, ethBought, _tokensSold);
}
function tokenToTokenSwap(
uint256 _tokensSold,
uint256 _minTokensBought,
address _tokenAddress
) public {
address exchangeAddress = IRegistry(registryAddress).getExchange(
_tokenAddress
);
require(
exchangeAddress != address(this) && exchangeAddress != address(0),
"invalid exchange address"
);
uint256 tokenReserve = getReserve();
uint256 ethBought = getAmount(
_tokensSold,
tokenReserve,
address(this).balance
);
IERC20(tokenAddress).transferFrom(
msg.sender,
address(this),
_tokensSold
);
Exchange(exchangeAddress).ethToTokenTransfer{value: ethBought}(
_minTokensBought,
msg.sender
);
}
}