├── Bank.sol ├── MdxGoblin.sol ├── MdxStrategyAddTwoSidesOptimal.sol ├── MdxStrategyWithdrawMinimizeTrading.sol └── README.md /Bank.sol: -------------------------------------------------------------------------------- 1 | // File: openzeppelin-solidity-2.3.0/contracts/utils/ReentrancyGuard.sol 2 | 3 | pragma solidity ^0.5.0; 4 | 5 | /** 6 | * @dev Contract module that helps prevent reentrant calls to a function. 7 | * 8 | * Inheriting from `ReentrancyGuard` will make the `nonReentrant` modifier 9 | * available, which can be aplied to functions to make sure there are no nested 10 | * (reentrant) calls to them. 11 | * 12 | * Note that because there is a single `nonReentrant` guard, functions marked as 13 | * `nonReentrant` may not call one another. This can be worked around by making 14 | * those functions `private`, and then adding `external` `nonReentrant` entry 15 | * points to them. 16 | */ 17 | contract ReentrancyGuard { 18 | /// @dev counter to allow mutex lock with only one SSTORE operation 19 | uint256 private _guardCounter; 20 | 21 | constructor () internal { 22 | // The counter starts at one to prevent changing it from zero to a non-zero 23 | // value, which is a more expensive operation. 24 | _guardCounter = 1; 25 | } 26 | 27 | /** 28 | * @dev Prevents a contract from calling itself, directly or indirectly. 29 | * Calling a `nonReentrant` function from another `nonReentrant` 30 | * function is not supported. It is possible to prevent this from happening 31 | * by making the `nonReentrant` function external, and make it call a 32 | * `private` function that does the actual work. 33 | */ 34 | modifier nonReentrant() { 35 | _guardCounter += 1; 36 | uint256 localCounter = _guardCounter; 37 | _; 38 | require(localCounter == _guardCounter, "ReentrancyGuard: reentrant call"); 39 | } 40 | } 41 | 42 | // File: openzeppelin-solidity-2.3.0/contracts/ownership/Ownable.sol 43 | 44 | pragma solidity ^0.5.0; 45 | 46 | /** 47 | * @dev Contract module which provides a basic access control mechanism, where 48 | * there is an account (an owner) that can be granted exclusive access to 49 | * specific functions. 50 | * 51 | * This module is used through inheritance. It will make available the modifier 52 | * `onlyOwner`, which can be aplied to your functions to restrict their use to 53 | * the owner. 54 | */ 55 | contract Ownable { 56 | address private _owner; 57 | 58 | event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); 59 | 60 | /** 61 | * @dev Initializes the contract setting the deployer as the initial owner. 62 | */ 63 | constructor () internal { 64 | _owner = msg.sender; 65 | emit OwnershipTransferred(address(0), _owner); 66 | } 67 | 68 | /** 69 | * @dev Returns the address of the current owner. 70 | */ 71 | function owner() public view returns (address) { 72 | return _owner; 73 | } 74 | 75 | /** 76 | * @dev Throws if called by any account other than the owner. 77 | */ 78 | modifier onlyOwner() { 79 | require(isOwner(), "Ownable: caller is not the owner"); 80 | _; 81 | } 82 | 83 | /** 84 | * @dev Returns true if the caller is the current owner. 85 | */ 86 | function isOwner() public view returns (bool) { 87 | return msg.sender == _owner; 88 | } 89 | 90 | /** 91 | * @dev Leaves the contract without owner. It will not be possible to call 92 | * `onlyOwner` functions anymore. Can only be called by the current owner. 93 | * 94 | * > Note: Renouncing ownership will leave the contract without an owner, 95 | * thereby removing any functionality that is only available to the owner. 96 | */ 97 | function renounceOwnership() public onlyOwner { 98 | emit OwnershipTransferred(_owner, address(0)); 99 | _owner = address(0); 100 | } 101 | 102 | /** 103 | * @dev Transfers ownership of the contract to a new account (`newOwner`). 104 | * Can only be called by the current owner. 105 | */ 106 | function transferOwnership(address newOwner) public onlyOwner { 107 | _transferOwnership(newOwner); 108 | } 109 | 110 | /** 111 | * @dev Transfers ownership of the contract to a new account (`newOwner`). 112 | */ 113 | function _transferOwnership(address newOwner) internal { 114 | require(newOwner != address(0), "Ownable: new owner is the zero address"); 115 | emit OwnershipTransferred(_owner, newOwner); 116 | _owner = newOwner; 117 | } 118 | } 119 | 120 | // File: openzeppelin-solidity-2.3.0/contracts/math/SafeMath.sol 121 | 122 | pragma solidity ^0.5.0; 123 | 124 | /** 125 | * @dev Wrappers over Solidity's arithmetic operations with added overflow 126 | * checks. 127 | * 128 | * Arithmetic operations in Solidity wrap on overflow. This can easily result 129 | * in bugs, because programmers usually assume that an overflow raises an 130 | * error, which is the standard behavior in high level programming languages. 131 | * `SafeMath` restores this intuition by reverting the transaction when an 132 | * operation overflows. 133 | * 134 | * Using this library instead of the unchecked operations eliminates an entire 135 | * class of bugs, so it's recommended to use it always. 136 | */ 137 | library SafeMath { 138 | /** 139 | * @dev Returns the addition of two unsigned integers, reverting on 140 | * overflow. 141 | * 142 | * Counterpart to Solidity's `+` operator. 143 | * 144 | * Requirements: 145 | * - Addition cannot overflow. 146 | */ 147 | function add(uint256 a, uint256 b) internal pure returns (uint256) { 148 | uint256 c = a + b; 149 | require(c >= a, "SafeMath: addition overflow"); 150 | 151 | return c; 152 | } 153 | 154 | /** 155 | * @dev Returns the subtraction of two unsigned integers, reverting on 156 | * overflow (when the result is negative). 157 | * 158 | * Counterpart to Solidity's `-` operator. 159 | * 160 | * Requirements: 161 | * - Subtraction cannot overflow. 162 | */ 163 | function sub(uint256 a, uint256 b) internal pure returns (uint256) { 164 | require(b <= a, "SafeMath: subtraction overflow"); 165 | uint256 c = a - b; 166 | 167 | return c; 168 | } 169 | 170 | /** 171 | * @dev Returns the multiplication of two unsigned integers, reverting on 172 | * overflow. 173 | * 174 | * Counterpart to Solidity's `*` operator. 175 | * 176 | * Requirements: 177 | * - Multiplication cannot overflow. 178 | */ 179 | function mul(uint256 a, uint256 b) internal pure returns (uint256) { 180 | // Gas optimization: this is cheaper than requiring 'a' not being zero, but the 181 | // benefit is lost if 'b' is also tested. 182 | // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522 183 | if (a == 0) { 184 | return 0; 185 | } 186 | 187 | uint256 c = a * b; 188 | require(c / a == b, "SafeMath: multiplication overflow"); 189 | 190 | return c; 191 | } 192 | 193 | /** 194 | * @dev Returns the integer division of two unsigned integers. Reverts on 195 | * division by zero. The result is rounded towards zero. 196 | * 197 | * Counterpart to Solidity's `/` operator. Note: this function uses a 198 | * `revert` opcode (which leaves remaining gas untouched) while Solidity 199 | * uses an invalid opcode to revert (consuming all remaining gas). 200 | * 201 | * Requirements: 202 | * - The divisor cannot be zero. 203 | */ 204 | function div(uint256 a, uint256 b) internal pure returns (uint256) { 205 | // Solidity only automatically asserts when dividing by 0 206 | require(b > 0, "SafeMath: division by zero"); 207 | uint256 c = a / b; 208 | // assert(a == b * c + a % b); // There is no case in which this doesn't hold 209 | 210 | return c; 211 | } 212 | 213 | /** 214 | * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), 215 | * Reverts when dividing by zero. 216 | * 217 | * Counterpart to Solidity's `%` operator. This function uses a `revert` 218 | * opcode (which leaves remaining gas untouched) while Solidity uses an 219 | * invalid opcode to revert (consuming all remaining gas). 220 | * 221 | * Requirements: 222 | * - The divisor cannot be zero. 223 | */ 224 | function mod(uint256 a, uint256 b) internal pure returns (uint256) { 225 | require(b != 0, "SafeMath: modulo by zero"); 226 | return a % b; 227 | } 228 | } 229 | 230 | // File: openzeppelin-solidity-2.3.0/contracts/token/ERC20/IERC20.sol 231 | 232 | pragma solidity ^0.5.0; 233 | 234 | /** 235 | * @dev Interface of the ERC20 standard as defined in the EIP. Does not include 236 | * the optional functions; to access them see `ERC20Detailed`. 237 | */ 238 | interface IERC20 { 239 | /** 240 | * @dev Returns the amount of tokens in existence. 241 | */ 242 | function totalSupply() external view returns (uint256); 243 | 244 | /** 245 | * @dev Returns the amount of tokens owned by `account`. 246 | */ 247 | function balanceOf(address account) external view returns (uint256); 248 | 249 | /** 250 | * @dev Moves `amount` tokens from the caller's account to `recipient`. 251 | * 252 | * Returns a boolean value indicating whether the operation succeeded. 253 | * 254 | * Emits a `Transfer` event. 255 | */ 256 | function transfer(address recipient, uint256 amount) external returns (bool); 257 | 258 | /** 259 | * @dev Returns the remaining number of tokens that `spender` will be 260 | * allowed to spend on behalf of `owner` through `transferFrom`. This is 261 | * zero by default. 262 | * 263 | * This value changes when `approve` or `transferFrom` are called. 264 | */ 265 | function allowance(address owner, address spender) external view returns (uint256); 266 | 267 | /** 268 | * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. 269 | * 270 | * Returns a boolean value indicating whether the operation succeeded. 271 | * 272 | * > Beware that changing an allowance with this method brings the risk 273 | * that someone may use both the old and the new allowance by unfortunate 274 | * transaction ordering. One possible solution to mitigate this race 275 | * condition is to first reduce the spender's allowance to 0 and set the 276 | * desired value afterwards: 277 | * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 278 | * 279 | * Emits an `Approval` event. 280 | */ 281 | function approve(address spender, uint256 amount) external returns (bool); 282 | 283 | /** 284 | * @dev Moves `amount` tokens from `sender` to `recipient` using the 285 | * allowance mechanism. `amount` is then deducted from the caller's 286 | * allowance. 287 | * 288 | * Returns a boolean value indicating whether the operation succeeded. 289 | * 290 | * Emits a `Transfer` event. 291 | */ 292 | function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); 293 | 294 | /** 295 | * @dev Emitted when `value` tokens are moved from one account (`from`) to 296 | * another (`to`). 297 | * 298 | * Note that `value` may be zero. 299 | */ 300 | event Transfer(address indexed from, address indexed to, uint256 value); 301 | 302 | /** 303 | * @dev Emitted when the allowance of a `spender` for an `owner` is set by 304 | * a call to `approve`. `value` is the new allowance. 305 | */ 306 | event Approval(address indexed owner, address indexed spender, uint256 value); 307 | } 308 | 309 | // File: openzeppelin-solidity-2.3.0/contracts/token/ERC20/ERC20.sol 310 | 311 | pragma solidity ^0.5.0; 312 | 313 | 314 | 315 | /** 316 | * @dev Implementation of the `IERC20` interface. 317 | * 318 | * This implementation is agnostic to the way tokens are created. This means 319 | * that a supply mechanism has to be added in a derived contract using `_mint`. 320 | * For a generic mechanism see `ERC20Mintable`. 321 | * 322 | * *For a detailed writeup see our guide [How to implement supply 323 | * mechanisms](https://forum.zeppelin.solutions/t/how-to-implement-erc20-supply-mechanisms/226).* 324 | * 325 | * We have followed general OpenZeppelin guidelines: functions revert instead 326 | * of returning `false` on failure. This behavior is nonetheless conventional 327 | * and does not conflict with the expectations of ERC20 applications. 328 | * 329 | * Additionally, an `Approval` event is emitted on calls to `transferFrom`. 330 | * This allows applications to reconstruct the allowance for all accounts just 331 | * by listening to said events. Other implementations of the EIP may not emit 332 | * these events, as it isn't required by the specification. 333 | * 334 | * Finally, the non-standard `decreaseAllowance` and `increaseAllowance` 335 | * functions have been added to mitigate the well-known issues around setting 336 | * allowances. See `IERC20.approve`. 337 | */ 338 | contract ERC20 is IERC20 { 339 | using SafeMath for uint256; 340 | 341 | mapping (address => uint256) private _balances; 342 | 343 | mapping (address => mapping (address => uint256)) private _allowances; 344 | 345 | uint256 private _totalSupply; 346 | 347 | /** 348 | * @dev See `IERC20.totalSupply`. 349 | */ 350 | function totalSupply() public view returns (uint256) { 351 | return _totalSupply; 352 | } 353 | 354 | /** 355 | * @dev See `IERC20.balanceOf`. 356 | */ 357 | function balanceOf(address account) public view returns (uint256) { 358 | return _balances[account]; 359 | } 360 | 361 | /** 362 | * @dev See `IERC20.transfer`. 363 | * 364 | * Requirements: 365 | * 366 | * - `recipient` cannot be the zero address. 367 | * - the caller must have a balance of at least `amount`. 368 | */ 369 | function transfer(address recipient, uint256 amount) public returns (bool) { 370 | _transfer(msg.sender, recipient, amount); 371 | return true; 372 | } 373 | 374 | /** 375 | * @dev See `IERC20.allowance`. 376 | */ 377 | function allowance(address owner, address spender) public view returns (uint256) { 378 | return _allowances[owner][spender]; 379 | } 380 | 381 | /** 382 | * @dev See `IERC20.approve`. 383 | * 384 | * Requirements: 385 | * 386 | * - `spender` cannot be the zero address. 387 | */ 388 | function approve(address spender, uint256 value) public returns (bool) { 389 | _approve(msg.sender, spender, value); 390 | return true; 391 | } 392 | 393 | /** 394 | * @dev See `IERC20.transferFrom`. 395 | * 396 | * Emits an `Approval` event indicating the updated allowance. This is not 397 | * required by the EIP. See the note at the beginning of `ERC20`; 398 | * 399 | * Requirements: 400 | * - `sender` and `recipient` cannot be the zero address. 401 | * - `sender` must have a balance of at least `value`. 402 | * - the caller must have allowance for `sender`'s tokens of at least 403 | * `amount`. 404 | */ 405 | function transferFrom(address sender, address recipient, uint256 amount) public returns (bool) { 406 | _transfer(sender, recipient, amount); 407 | _approve(sender, msg.sender, _allowances[sender][msg.sender].sub(amount)); 408 | return true; 409 | } 410 | 411 | /** 412 | * @dev Atomically increases the allowance granted to `spender` by the caller. 413 | * 414 | * This is an alternative to `approve` that can be used as a mitigation for 415 | * problems described in `IERC20.approve`. 416 | * 417 | * Emits an `Approval` event indicating the updated allowance. 418 | * 419 | * Requirements: 420 | * 421 | * - `spender` cannot be the zero address. 422 | */ 423 | function increaseAllowance(address spender, uint256 addedValue) public returns (bool) { 424 | _approve(msg.sender, spender, _allowances[msg.sender][spender].add(addedValue)); 425 | return true; 426 | } 427 | 428 | /** 429 | * @dev Atomically decreases the allowance granted to `spender` by the caller. 430 | * 431 | * This is an alternative to `approve` that can be used as a mitigation for 432 | * problems described in `IERC20.approve`. 433 | * 434 | * Emits an `Approval` event indicating the updated allowance. 435 | * 436 | * Requirements: 437 | * 438 | * - `spender` cannot be the zero address. 439 | * - `spender` must have allowance for the caller of at least 440 | * `subtractedValue`. 441 | */ 442 | function decreaseAllowance(address spender, uint256 subtractedValue) public returns (bool) { 443 | _approve(msg.sender, spender, _allowances[msg.sender][spender].sub(subtractedValue)); 444 | return true; 445 | } 446 | 447 | /** 448 | * @dev Moves tokens `amount` from `sender` to `recipient`. 449 | * 450 | * This is internal function is equivalent to `transfer`, and can be used to 451 | * e.g. implement automatic token fees, slashing mechanisms, etc. 452 | * 453 | * Emits a `Transfer` event. 454 | * 455 | * Requirements: 456 | * 457 | * - `sender` cannot be the zero address. 458 | * - `recipient` cannot be the zero address. 459 | * - `sender` must have a balance of at least `amount`. 460 | */ 461 | function _transfer(address sender, address recipient, uint256 amount) internal { 462 | require(sender != address(0), "ERC20: transfer from the zero address"); 463 | require(recipient != address(0), "ERC20: transfer to the zero address"); 464 | 465 | _balances[sender] = _balances[sender].sub(amount); 466 | _balances[recipient] = _balances[recipient].add(amount); 467 | emit Transfer(sender, recipient, amount); 468 | } 469 | 470 | /** @dev Creates `amount` tokens and assigns them to `account`, increasing 471 | * the total supply. 472 | * 473 | * Emits a `Transfer` event with `from` set to the zero address. 474 | * 475 | * Requirements 476 | * 477 | * - `to` cannot be the zero address. 478 | */ 479 | function _mint(address account, uint256 amount) internal { 480 | require(account != address(0), "ERC20: mint to the zero address"); 481 | 482 | _totalSupply = _totalSupply.add(amount); 483 | _balances[account] = _balances[account].add(amount); 484 | emit Transfer(address(0), account, amount); 485 | } 486 | 487 | /** 488 | * @dev Destoys `amount` tokens from `account`, reducing the 489 | * total supply. 490 | * 491 | * Emits a `Transfer` event with `to` set to the zero address. 492 | * 493 | * Requirements 494 | * 495 | * - `account` cannot be the zero address. 496 | * - `account` must have at least `amount` tokens. 497 | */ 498 | function _burn(address account, uint256 value) internal { 499 | require(account != address(0), "ERC20: burn from the zero address"); 500 | 501 | _totalSupply = _totalSupply.sub(value); 502 | _balances[account] = _balances[account].sub(value); 503 | emit Transfer(account, address(0), value); 504 | } 505 | 506 | /** 507 | * @dev Sets `amount` as the allowance of `spender` over the `owner`s tokens. 508 | * 509 | * This is internal function is equivalent to `approve`, and can be used to 510 | * e.g. set automatic allowances for certain subsystems, etc. 511 | * 512 | * Emits an `Approval` event. 513 | * 514 | * Requirements: 515 | * 516 | * - `owner` cannot be the zero address. 517 | * - `spender` cannot be the zero address. 518 | */ 519 | function _approve(address owner, address spender, uint256 value) internal { 520 | require(owner != address(0), "ERC20: approve from the zero address"); 521 | require(spender != address(0), "ERC20: approve to the zero address"); 522 | 523 | _allowances[owner][spender] = value; 524 | emit Approval(owner, spender, value); 525 | } 526 | 527 | /** 528 | * @dev Destoys `amount` tokens from `account`.`amount` is then deducted 529 | * from the caller's allowance. 530 | * 531 | * See `_burn` and `_approve`. 532 | */ 533 | function _burnFrom(address account, uint256 amount) internal { 534 | _burn(account, amount); 535 | _approve(account, msg.sender, _allowances[account][msg.sender].sub(amount)); 536 | } 537 | } 538 | 539 | // File: openzeppelin-solidity-2.3.0/contracts/math/Math.sol 540 | 541 | pragma solidity ^0.5.0; 542 | 543 | /** 544 | * @dev Standard math utilities missing in the Solidity language. 545 | */ 546 | library Math { 547 | /** 548 | * @dev Returns the largest of two numbers. 549 | */ 550 | function max(uint256 a, uint256 b) internal pure returns (uint256) { 551 | return a >= b ? a : b; 552 | } 553 | 554 | /** 555 | * @dev Returns the smallest of two numbers. 556 | */ 557 | function min(uint256 a, uint256 b) internal pure returns (uint256) { 558 | return a < b ? a : b; 559 | } 560 | 561 | /** 562 | * @dev Returns the average of two numbers. The result is rounded towards 563 | * zero. 564 | */ 565 | function average(uint256 a, uint256 b) internal pure returns (uint256) { 566 | // (a + b) / 2 can overflow, so we distribute 567 | return (a / 2) + (b / 2) + ((a % 2 + b % 2) / 2); 568 | } 569 | } 570 | 571 | // File: contracts/SafeToken.sol 572 | 573 | pragma solidity ^0.5.16; 574 | 575 | interface ERC20Interface { 576 | function balanceOf(address user) external view returns (uint256); 577 | } 578 | 579 | library SafeToken { 580 | function myBalance(address token) internal view returns (uint256) { 581 | return ERC20Interface(token).balanceOf(address(this)); 582 | } 583 | 584 | function balanceOf(address token, address user) internal view returns (uint256) { 585 | return ERC20Interface(token).balanceOf(user); 586 | } 587 | 588 | function safeApprove(address token, address to, uint256 value) internal { 589 | // bytes4(keccak256(bytes('approve(address,uint256)'))); 590 | (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x095ea7b3, to, value)); 591 | require(success && (data.length == 0 || abi.decode(data, (bool))), "!safeApprove"); 592 | } 593 | 594 | function safeTransfer(address token, address to, uint256 value) internal { 595 | // bytes4(keccak256(bytes('transfer(address,uint256)'))); 596 | (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0xa9059cbb, to, value)); 597 | require(success && (data.length == 0 || abi.decode(data, (bool))), "!safeTransfer"); 598 | } 599 | 600 | function safeTransferFrom(address token, address from, address to, uint256 value) internal { 601 | // bytes4(keccak256(bytes('transferFrom(address,address,uint256)'))); 602 | (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x23b872dd, from, to, value)); 603 | require(success && (data.length == 0 || abi.decode(data, (bool))), "!safeTransferFrom"); 604 | } 605 | 606 | function safeTransferETH(address to, uint256 value) internal { 607 | (bool success, ) = to.call.value(value)(new bytes(0)); 608 | require(success, "!safeTransferETH"); 609 | } 610 | } 611 | 612 | // File: contracts/Goblin.sol 613 | 614 | pragma solidity ^0.5.16; 615 | 616 | interface Goblin { 617 | 618 | /// @dev Work on a (potentially new) position. Optionally send surplus token back to Bank. 619 | function work(uint256 id, address user, address borrowToken, uint256 borrow, uint256 debt, bytes calldata data) external payable; 620 | 621 | /// @dev Return the amount of ETH wei to get back if we are to liquidate the position. 622 | function health(uint256 id, address borrowToken) external view returns (uint256); 623 | 624 | /// @dev Liquidate the given position to token need. Send all ETH back to Bank. 625 | function liquidate(uint256 id, address user, address borrowToken) external; 626 | } 627 | 628 | // File: contracts/interfaces/IBankConfig.sol 629 | 630 | pragma solidity ^0.5.16; 631 | 632 | interface IBankConfig { 633 | 634 | function getInterestRate(uint256 debt, uint256 floating) external view returns (uint256); 635 | 636 | function getReserveBps() external view returns (uint256); 637 | 638 | function getLiquidateBps() external view returns (uint256); 639 | } 640 | 641 | pragma solidity 0.5.16; 642 | 643 | interface InterestModel { 644 | function getInterestRate(uint256 debt, uint256 floating) external view returns (uint256); 645 | } 646 | 647 | contract TripleSlopeModel { 648 | using SafeMath for uint256; 649 | 650 | function getInterestRate(uint256 debt, uint256 floating) external pure returns (uint256) { 651 | uint256 total = debt.add(floating); 652 | uint256 utilization = total == 0? 0: debt.mul(10000).div(total); 653 | if (utilization < 5000) { 654 | // Less than 50% utilization - 10% APY 655 | return uint256(10e16) / 365 days; 656 | } else if (utilization < 9500) { 657 | // Between 50% and 95% - 10%-25% APY 658 | return (10e16 + utilization.sub(5000).mul(15e16).div(10000)) / 365 days; 659 | } else if (utilization < 10000) { 660 | // Between 95% and 100% - 25%-100% APY 661 | return (25e16 + utilization.sub(7500).mul(75e16).div(10000)) / 365 days; 662 | } else { 663 | // Not possible, but just in case - 100% APY 664 | return uint256(100e16) / 365 days; 665 | } 666 | } 667 | } 668 | 669 | contract BankConfig is IBankConfig, Ownable { 670 | 671 | uint256 public getReserveBps; 672 | uint256 public getLiquidateBps; 673 | InterestModel public interestModel; 674 | 675 | constructor() public {} 676 | 677 | function setParams(uint256 _getReserveBps, uint256 _getLiquidateBps, InterestModel _interestModel) public onlyOwner { 678 | getReserveBps = _getReserveBps; 679 | getLiquidateBps = _getLiquidateBps; 680 | interestModel = _interestModel; 681 | } 682 | 683 | function getInterestRate(uint256 debt, uint256 floating) external view returns (uint256) { 684 | return interestModel.getInterestRate(debt, floating); 685 | } 686 | } 687 | 688 | // File: contracts/PToken.sol 689 | 690 | pragma solidity ^0.5.16; 691 | 692 | contract PToken is ERC20, Ownable { 693 | using SafeToken for address; 694 | using SafeMath for uint256; 695 | 696 | string public name = ""; 697 | string public symbol = ""; 698 | uint8 public decimals = 18; 699 | 700 | event Mint(address sender, address account, uint amount); 701 | event Burn(address sender, address account, uint amount); 702 | 703 | constructor(string memory _symbol) public { 704 | name = _symbol; 705 | symbol = _symbol; 706 | } 707 | 708 | function mint(address account, uint256 amount) public onlyOwner { 709 | _mint(account, amount); 710 | emit Mint(msg.sender, account, amount); 711 | } 712 | 713 | function burn(address account, uint256 value) public onlyOwner { 714 | _burn(account, value); 715 | emit Burn(msg.sender, account, value); 716 | } 717 | } 718 | 719 | // File: contracts/PTokenFactory.sol 720 | 721 | pragma solidity ^0.5.16; 722 | 723 | 724 | contract PTokenFactory { 725 | 726 | function genPToken(string memory _symbol) public returns(address) { 727 | return address(new PToken(_symbol)); 728 | } 729 | } 730 | 731 | // File: contracts/Bank.sol 732 | 733 | pragma solidity ^0.5.16; 734 | 735 | contract Bank is PTokenFactory, Ownable, ReentrancyGuard { 736 | using SafeToken for address; 737 | using SafeMath for uint256; 738 | 739 | event OpPosition(uint256 indexed id, uint256 debt, uint back); 740 | event Liquidate(uint256 indexed id, address indexed killer, uint256 prize, uint256 left); 741 | 742 | struct TokenBank { 743 | address tokenAddr; 744 | address pTokenAddr; 745 | bool isOpen; 746 | bool canDeposit; 747 | bool canWithdraw; 748 | uint256 totalVal; 749 | uint256 totalDebt; 750 | uint256 totalDebtShare; 751 | uint256 totalReserve; 752 | uint256 lastInterestTime; 753 | } 754 | 755 | struct Production { 756 | address coinToken; 757 | address currencyToken; 758 | address borrowToken; 759 | bool isOpen; 760 | bool canBorrow; 761 | address goblin; 762 | uint256 minDebt; 763 | uint256 openFactor; 764 | uint256 liquidateFactor; 765 | } 766 | 767 | struct Position { 768 | address owner; 769 | uint256 productionId; 770 | uint256 debtShare; 771 | } 772 | 773 | IBankConfig config; 774 | 775 | mapping(address => TokenBank) public banks; 776 | 777 | mapping(uint256 => Production) public productions; 778 | uint256 public currentPid = 1; 779 | 780 | mapping(uint256 => Position) public positions; 781 | uint256 public currentPos = 1; 782 | 783 | modifier onlyEOA() { 784 | require(msg.sender == tx.origin, "not eoa"); 785 | _; 786 | } 787 | 788 | constructor() public {} 789 | 790 | /// read 791 | function positionInfo(uint256 posId) public view returns (uint256, uint256, uint256, address) { 792 | Position storage pos = positions[posId]; 793 | Production storage prod = productions[pos.productionId]; 794 | 795 | return (pos.productionId, Goblin(prod.goblin).health(posId, prod.borrowToken), 796 | debtShareToVal(prod.borrowToken, pos.debtShare), pos.owner); 797 | } 798 | 799 | function totalToken(address token) public view returns (uint256) { 800 | TokenBank storage bank = banks[token]; 801 | require(bank.isOpen, 'token not exists'); 802 | 803 | uint balance = token == address(0)? address(this).balance: SafeToken.myBalance(token); 804 | balance = bank.totalVal < balance? bank.totalVal: balance; 805 | 806 | return balance.add(bank.totalDebt).sub(bank.totalReserve); 807 | } 808 | 809 | function debtShareToVal(address token, uint256 debtShare) public view returns (uint256) { 810 | TokenBank storage bank = banks[token]; 811 | require(bank.isOpen, 'token not exists'); 812 | 813 | if (bank.totalDebtShare == 0) return debtShare; 814 | return debtShare.mul(bank.totalDebt).div(bank.totalDebtShare); 815 | } 816 | 817 | function debtValToShare(address token, uint256 debtVal) public view returns (uint256) { 818 | TokenBank storage bank = banks[token]; 819 | require(bank.isOpen, 'token not exists'); 820 | 821 | if (bank.totalDebt == 0) return debtVal; 822 | return debtVal.mul(bank.totalDebtShare).div(bank.totalDebt); 823 | } 824 | 825 | 826 | /// write 827 | function deposit(address token, uint256 amount) external payable nonReentrant { 828 | TokenBank storage bank = banks[token]; 829 | require(bank.isOpen && bank.canDeposit, 'Token not exist or cannot deposit'); 830 | 831 | calInterest(token); 832 | 833 | if (token == address(0)) {//HT 834 | amount = msg.value; 835 | } else { 836 | SafeToken.safeTransferFrom(token, msg.sender, address(this), amount); 837 | } 838 | 839 | bank.totalVal = bank.totalVal.add(amount); 840 | uint256 total = totalToken(token).sub(amount); 841 | uint256 pTotal = PToken(bank.pTokenAddr).totalSupply(); 842 | 843 | uint256 pAmount = (total == 0 || pTotal == 0) ? amount: amount.mul(pTotal).div(total); 844 | PToken(bank.pTokenAddr).mint(msg.sender, pAmount); 845 | } 846 | 847 | function withdraw(address token, uint256 pAmount) external nonReentrant { 848 | TokenBank storage bank = banks[token]; 849 | require(bank.isOpen && bank.canWithdraw, 'Token not exist or cannot withdraw'); 850 | 851 | calInterest(token); 852 | 853 | uint256 amount = pAmount.mul(totalToken(token)).div(PToken(bank.pTokenAddr).totalSupply()); 854 | bank.totalVal = bank.totalVal.sub(amount); 855 | 856 | PToken(bank.pTokenAddr).burn(msg.sender, pAmount); 857 | 858 | if (token == address(0)) {//HT 859 | SafeToken.safeTransferETH(msg.sender, amount); 860 | } else { 861 | SafeToken.safeTransfer(token, msg.sender, amount); 862 | } 863 | } 864 | 865 | function opPosition(uint256 posId, uint256 pid, uint256 borrow, bytes calldata data) 866 | external payable onlyEOA nonReentrant { 867 | 868 | if (posId == 0) { 869 | posId = currentPos; 870 | currentPos ++; 871 | positions[posId].owner = msg.sender; 872 | positions[posId].productionId = pid; 873 | 874 | } else { 875 | require(posId < currentPos, "bad position id"); 876 | require(positions[posId].owner == msg.sender, "not position owner"); 877 | 878 | pid = positions[posId].productionId; 879 | } 880 | 881 | Production storage production = productions[pid]; 882 | require(production.isOpen, 'Production not exists'); 883 | 884 | require(borrow == 0 || production.canBorrow, "Production can not borrow"); 885 | calInterest(production.borrowToken); 886 | 887 | uint256 debt = _removeDebt(positions[posId], production).add(borrow); 888 | bool isBorrowHt = production.borrowToken == address(0); 889 | 890 | uint256 sendHT = msg.value; 891 | uint256 beforeToken = 0; 892 | if (isBorrowHt) { 893 | sendHT = sendHT.add(borrow); 894 | require(sendHT <= address(this).balance && debt <= banks[production.borrowToken].totalVal, "insufficient HT in the bank"); 895 | beforeToken = address(this).balance.sub(sendHT); 896 | 897 | } else { 898 | beforeToken = SafeToken.myBalance(production.borrowToken); 899 | require(borrow <= beforeToken && debt <= banks[production.borrowToken].totalVal, "insufficient borrowToken in the bank"); 900 | beforeToken = beforeToken.sub(borrow); 901 | SafeToken.safeApprove(production.borrowToken, production.goblin, borrow); 902 | } 903 | 904 | Goblin(production.goblin).work.value(sendHT)(posId, msg.sender, production.borrowToken, borrow, debt, data); 905 | 906 | uint256 backToken = isBorrowHt? (address(this).balance.sub(beforeToken)) : 907 | SafeToken.myBalance(production.borrowToken).sub(beforeToken); 908 | 909 | if(backToken > debt) { //没有借款, 有剩余退款 910 | backToken = backToken.sub(debt); 911 | debt = 0; 912 | 913 | isBorrowHt? SafeToken.safeTransferETH(msg.sender, backToken): 914 | SafeToken.safeTransfer(production.borrowToken, msg.sender, backToken); 915 | 916 | } else if (debt > backToken) { //有借款 917 | debt = debt.sub(backToken); 918 | backToken = 0; 919 | 920 | require(debt >= production.minDebt, "too small debt size"); 921 | uint256 health = Goblin(production.goblin).health(posId, production.borrowToken); 922 | require(health.mul(production.openFactor) >= debt.mul(10000), "bad work factor"); 923 | 924 | _addDebt(positions[posId], production, debt); 925 | } 926 | emit OpPosition(posId, debt, backToken); 927 | } 928 | 929 | function liquidate(uint256 posId) external payable onlyEOA nonReentrant { 930 | Position storage pos = positions[posId]; 931 | require(pos.debtShare > 0, "no debt"); 932 | Production storage production = productions[pos.productionId]; 933 | 934 | uint256 debt = _removeDebt(pos, production); 935 | 936 | uint256 health = Goblin(production.goblin).health(posId, production.borrowToken); 937 | require(health.mul(production.liquidateFactor) < debt.mul(10000), "can't liquidate"); 938 | 939 | bool isHT = production.borrowToken == address(0); 940 | uint256 before = isHT? address(this).balance: SafeToken.myBalance(production.borrowToken); 941 | 942 | Goblin(production.goblin).liquidate(posId, pos.owner, production.borrowToken); 943 | 944 | uint256 back = isHT? address(this).balance: SafeToken.myBalance(production.borrowToken); 945 | back = back.sub(before); 946 | 947 | uint256 prize = back.mul(config.getLiquidateBps()).div(10000); 948 | uint256 rest = back.sub(prize); 949 | uint256 left = 0; 950 | 951 | if (prize > 0) { 952 | isHT? SafeToken.safeTransferETH(msg.sender, prize): SafeToken.safeTransfer(production.borrowToken, msg.sender, prize); 953 | } 954 | if (rest > debt) { 955 | left = rest.sub(debt); 956 | isHT? SafeToken.safeTransferETH(pos.owner, left): SafeToken.safeTransfer(production.borrowToken, pos.owner, left); 957 | } else { 958 | banks[production.borrowToken].totalVal = banks[production.borrowToken].totalVal.sub(debt).add(rest); 959 | } 960 | emit Liquidate(posId, msg.sender, prize, left); 961 | } 962 | 963 | function _addDebt(Position storage pos, Production storage production, uint256 debtVal) internal { 964 | if (debtVal == 0) { 965 | return; 966 | } 967 | 968 | TokenBank storage bank = banks[production.borrowToken]; 969 | 970 | uint256 debtShare = debtValToShare(production.borrowToken, debtVal); 971 | pos.debtShare = pos.debtShare.add(debtShare); 972 | 973 | bank.totalVal = bank.totalVal.sub(debtVal); 974 | bank.totalDebtShare = bank.totalDebtShare.add(debtShare); 975 | bank.totalDebt = bank.totalDebt.add(debtVal); 976 | } 977 | 978 | function _removeDebt(Position storage pos, Production storage production) internal returns (uint256) { 979 | TokenBank storage bank = banks[production.borrowToken]; 980 | 981 | uint256 debtShare = pos.debtShare; 982 | if (debtShare > 0) { 983 | uint256 debtVal = debtShareToVal(production.borrowToken, debtShare); 984 | pos.debtShare = 0; 985 | 986 | bank.totalVal = bank.totalVal.add(debtVal); 987 | bank.totalDebtShare = bank.totalDebtShare.sub(debtShare); 988 | bank.totalDebt = bank.totalDebt.sub(debtVal); 989 | return debtVal; 990 | } else { 991 | return 0; 992 | } 993 | } 994 | 995 | function updateConfig(IBankConfig _config) external onlyOwner { 996 | config = _config; 997 | } 998 | 999 | function addToken(address token, string calldata _symbol) external onlyOwner { 1000 | TokenBank storage bank = banks[token]; 1001 | require(!bank.isOpen, 'token already exists'); 1002 | 1003 | bank.isOpen = true; 1004 | address pToken = genPToken(_symbol); 1005 | bank.tokenAddr = token; 1006 | bank.pTokenAddr = pToken; 1007 | bank.canDeposit = true; 1008 | bank.canWithdraw = true; 1009 | bank.totalVal = 0; 1010 | bank.totalDebt = 0; 1011 | bank.totalDebtShare = 0; 1012 | bank.totalReserve = 0; 1013 | bank.lastInterestTime = now; 1014 | } 1015 | 1016 | function updateToken(address token, bool canDeposit, bool canWithdraw) external onlyOwner { 1017 | TokenBank storage bank = banks[token]; 1018 | require(bank.isOpen, 'token not exists'); 1019 | 1020 | bank.canDeposit = canDeposit; 1021 | bank.canWithdraw = canWithdraw; 1022 | } 1023 | 1024 | function opProduction(uint256 pid, bool isOpen, bool canBorrow, 1025 | address coinToken, address currencyToken, address borrowToken, address goblin, 1026 | uint256 minDebt, uint256 openFactor, uint256 liquidateFactor) external onlyOwner { 1027 | 1028 | if(pid == 0){ 1029 | pid = currentPid; 1030 | currentPid ++; 1031 | } else { 1032 | require(pid < currentPid, "bad production id"); 1033 | } 1034 | 1035 | Production storage production = productions[pid]; 1036 | production.isOpen = isOpen; 1037 | production.canBorrow = canBorrow; 1038 | // 地址一旦设置, 就不要再改, 可以添加新币对! 1039 | production.coinToken = coinToken; 1040 | production.currencyToken = currencyToken; 1041 | production.borrowToken = borrowToken; 1042 | production.goblin = goblin; 1043 | 1044 | production.minDebt = minDebt; 1045 | production.openFactor = openFactor; 1046 | production.liquidateFactor = liquidateFactor; 1047 | } 1048 | 1049 | function calInterest(address token) public { 1050 | TokenBank storage bank = banks[token]; 1051 | require(bank.isOpen, 'token not exists'); 1052 | 1053 | if (now > bank.lastInterestTime) { 1054 | uint256 timePast = now.sub(bank.lastInterestTime); 1055 | uint256 totalDebt = bank.totalDebt; 1056 | uint256 totalBalance = totalToken(token); 1057 | 1058 | uint256 ratePerSec = config.getInterestRate(totalDebt, totalBalance); 1059 | uint256 interest = ratePerSec.mul(timePast).mul(totalDebt).div(1e18); 1060 | 1061 | uint256 toReserve = interest.mul(config.getReserveBps()).div(10000); 1062 | bank.totalReserve = bank.totalReserve.add(toReserve); 1063 | bank.totalDebt = bank.totalDebt.add(interest); 1064 | bank.lastInterestTime = now; 1065 | } 1066 | } 1067 | 1068 | function withdrawReserve(address token, address to, uint256 value) external onlyOwner nonReentrant { 1069 | TokenBank storage bank = banks[token]; 1070 | require(bank.isOpen, 'token not exists'); 1071 | 1072 | uint balance = token == address(0)? address(this).balance: SafeToken.myBalance(token); 1073 | if(balance >= bank.totalVal.add(value)) { 1074 | //非deposit存入 1075 | } else { 1076 | bank.totalReserve = bank.totalReserve.sub(value); 1077 | bank.totalVal = bank.totalVal.sub(value); 1078 | } 1079 | 1080 | if (token == address(0)) { 1081 | SafeToken.safeTransferETH(to, value); 1082 | } else { 1083 | SafeToken.safeTransfer(token, to, value); 1084 | } 1085 | } 1086 | 1087 | function() external payable {} 1088 | } 1089 | -------------------------------------------------------------------------------- /MdxGoblin.sol: -------------------------------------------------------------------------------- 1 | // File: openzeppelin-solidity-2.3.0/contracts/ownership/Ownable.sol 2 | 3 | pragma solidity ^0.5.0; 4 | 5 | /** 6 | * @dev Contract module which provides a basic access control mechanism, where 7 | * there is an account (an owner) that can be granted exclusive access to 8 | * specific functions. 9 | * 10 | * This module is used through inheritance. It will make available the modifier 11 | * `onlyOwner`, which can be aplied to your functions to restrict their use to 12 | * the owner. 13 | */ 14 | contract Ownable { 15 | address private _owner; 16 | 17 | event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); 18 | 19 | /** 20 | * @dev Initializes the contract setting the deployer as the initial owner. 21 | */ 22 | constructor () internal { 23 | _owner = msg.sender; 24 | emit OwnershipTransferred(address(0), _owner); 25 | } 26 | 27 | /** 28 | * @dev Returns the address of the current owner. 29 | */ 30 | function owner() public view returns (address) { 31 | return _owner; 32 | } 33 | 34 | /** 35 | * @dev Throws if called by any account other than the owner. 36 | */ 37 | modifier onlyOwner() { 38 | require(isOwner(), "Ownable: caller is not the owner"); 39 | _; 40 | } 41 | 42 | /** 43 | * @dev Returns true if the caller is the current owner. 44 | */ 45 | function isOwner() public view returns (bool) { 46 | return msg.sender == _owner; 47 | } 48 | 49 | /** 50 | * @dev Leaves the contract without owner. It will not be possible to call 51 | * `onlyOwner` functions anymore. Can only be called by the current owner. 52 | * 53 | * > Note: Renouncing ownership will leave the contract without an owner, 54 | * thereby removing any functionality that is only available to the owner. 55 | */ 56 | function renounceOwnership() public onlyOwner { 57 | emit OwnershipTransferred(_owner, address(0)); 58 | _owner = address(0); 59 | } 60 | 61 | /** 62 | * @dev Transfers ownership of the contract to a new account (`newOwner`). 63 | * Can only be called by the current owner. 64 | */ 65 | function transferOwnership(address newOwner) public onlyOwner { 66 | _transferOwnership(newOwner); 67 | } 68 | 69 | /** 70 | * @dev Transfers ownership of the contract to a new account (`newOwner`). 71 | */ 72 | function _transferOwnership(address newOwner) internal { 73 | require(newOwner != address(0), "Ownable: new owner is the zero address"); 74 | emit OwnershipTransferred(_owner, newOwner); 75 | _owner = newOwner; 76 | } 77 | } 78 | 79 | // File: openzeppelin-solidity-2.3.0/contracts/math/SafeMath.sol 80 | 81 | pragma solidity ^0.5.0; 82 | 83 | /** 84 | * @dev Wrappers over Solidity's arithmetic operations with added overflow 85 | * checks. 86 | * 87 | * Arithmetic operations in Solidity wrap on overflow. This can easily result 88 | * in bugs, because programmers usually assume that an overflow raises an 89 | * error, which is the standard behavior in high level programming languages. 90 | * `SafeMath` restores this intuition by reverting the transaction when an 91 | * operation overflows. 92 | * 93 | * Using this library instead of the unchecked operations eliminates an entire 94 | * class of bugs, so it's recommended to use it always. 95 | */ 96 | library SafeMath { 97 | /** 98 | * @dev Returns the addition of two unsigned integers, reverting on 99 | * overflow. 100 | * 101 | * Counterpart to Solidity's `+` operator. 102 | * 103 | * Requirements: 104 | * - Addition cannot overflow. 105 | */ 106 | function add(uint256 a, uint256 b) internal pure returns (uint256) { 107 | uint256 c = a + b; 108 | require(c >= a, "SafeMath: addition overflow"); 109 | 110 | return c; 111 | } 112 | 113 | /** 114 | * @dev Returns the subtraction of two unsigned integers, reverting on 115 | * overflow (when the result is negative). 116 | * 117 | * Counterpart to Solidity's `-` operator. 118 | * 119 | * Requirements: 120 | * - Subtraction cannot overflow. 121 | */ 122 | function sub(uint256 a, uint256 b) internal pure returns (uint256) { 123 | require(b <= a, "SafeMath: subtraction overflow"); 124 | uint256 c = a - b; 125 | 126 | return c; 127 | } 128 | 129 | /** 130 | * @dev Returns the multiplication of two unsigned integers, reverting on 131 | * overflow. 132 | * 133 | * Counterpart to Solidity's `*` operator. 134 | * 135 | * Requirements: 136 | * - Multiplication cannot overflow. 137 | */ 138 | function mul(uint256 a, uint256 b) internal pure returns (uint256) { 139 | // Gas optimization: this is cheaper than requiring 'a' not being zero, but the 140 | // benefit is lost if 'b' is also tested. 141 | // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522 142 | if (a == 0) { 143 | return 0; 144 | } 145 | 146 | uint256 c = a * b; 147 | require(c / a == b, "SafeMath: multiplication overflow"); 148 | 149 | return c; 150 | } 151 | 152 | /** 153 | * @dev Returns the integer division of two unsigned integers. Reverts on 154 | * division by zero. The result is rounded towards zero. 155 | * 156 | * Counterpart to Solidity's `/` operator. Note: this function uses a 157 | * `revert` opcode (which leaves remaining gas untouched) while Solidity 158 | * uses an invalid opcode to revert (consuming all remaining gas). 159 | * 160 | * Requirements: 161 | * - The divisor cannot be zero. 162 | */ 163 | function div(uint256 a, uint256 b) internal pure returns (uint256) { 164 | // Solidity only automatically asserts when dividing by 0 165 | require(b > 0, "SafeMath: division by zero"); 166 | uint256 c = a / b; 167 | // assert(a == b * c + a % b); // There is no case in which this doesn't hold 168 | 169 | return c; 170 | } 171 | 172 | /** 173 | * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), 174 | * Reverts when dividing by zero. 175 | * 176 | * Counterpart to Solidity's `%` operator. This function uses a `revert` 177 | * opcode (which leaves remaining gas untouched) while Solidity uses an 178 | * invalid opcode to revert (consuming all remaining gas). 179 | * 180 | * Requirements: 181 | * - The divisor cannot be zero. 182 | */ 183 | function mod(uint256 a, uint256 b) internal pure returns (uint256) { 184 | require(b != 0, "SafeMath: modulo by zero"); 185 | return a % b; 186 | } 187 | } 188 | 189 | // File: openzeppelin-solidity-2.3.0/contracts/utils/ReentrancyGuard.sol 190 | 191 | pragma solidity ^0.5.0; 192 | 193 | /** 194 | * @dev Contract module that helps prevent reentrant calls to a function. 195 | * 196 | * Inheriting from `ReentrancyGuard` will make the `nonReentrant` modifier 197 | * available, which can be aplied to functions to make sure there are no nested 198 | * (reentrant) calls to them. 199 | * 200 | * Note that because there is a single `nonReentrant` guard, functions marked as 201 | * `nonReentrant` may not call one another. This can be worked around by making 202 | * those functions `private`, and then adding `external` `nonReentrant` entry 203 | * points to them. 204 | */ 205 | contract ReentrancyGuard { 206 | /// @dev counter to allow mutex lock with only one SSTORE operation 207 | uint256 private _guardCounter; 208 | 209 | constructor () internal { 210 | // The counter starts at one to prevent changing it from zero to a non-zero 211 | // value, which is a more expensive operation. 212 | _guardCounter = 1; 213 | } 214 | 215 | /** 216 | * @dev Prevents a contract from calling itself, directly or indirectly. 217 | * Calling a `nonReentrant` function from another `nonReentrant` 218 | * function is not supported. It is possible to prevent this from happening 219 | * by making the `nonReentrant` function external, and make it call a 220 | * `private` function that does the actual work. 221 | */ 222 | modifier nonReentrant() { 223 | _guardCounter += 1; 224 | uint256 localCounter = _guardCounter; 225 | _; 226 | require(localCounter == _guardCounter, "ReentrancyGuard: reentrant call"); 227 | } 228 | } 229 | 230 | // File: contracts/SafeToken.sol 231 | 232 | pragma solidity ^0.5.16; 233 | 234 | interface ERC20Interface { 235 | function balanceOf(address user) external view returns (uint256); 236 | } 237 | 238 | library SafeToken { 239 | function myBalance(address token) internal view returns (uint256) { 240 | return ERC20Interface(token).balanceOf(address(this)); 241 | } 242 | 243 | function balanceOf(address token, address user) internal view returns (uint256) { 244 | return ERC20Interface(token).balanceOf(user); 245 | } 246 | 247 | function safeApprove(address token, address to, uint256 value) internal { 248 | // bytes4(keccak256(bytes('approve(address,uint256)'))); 249 | (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x095ea7b3, to, value)); 250 | require(success && (data.length == 0 || abi.decode(data, (bool))), "!safeApprove"); 251 | } 252 | 253 | function safeTransfer(address token, address to, uint256 value) internal { 254 | // bytes4(keccak256(bytes('transfer(address,uint256)'))); 255 | (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0xa9059cbb, to, value)); 256 | require(success && (data.length == 0 || abi.decode(data, (bool))), "!safeTransfer"); 257 | } 258 | 259 | function safeTransferFrom(address token, address from, address to, uint256 value) internal { 260 | // bytes4(keccak256(bytes('transferFrom(address,address,uint256)'))); 261 | (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x23b872dd, from, to, value)); 262 | require(success && (data.length == 0 || abi.decode(data, (bool))), "!safeTransferFrom"); 263 | } 264 | 265 | function safeTransferETH(address to, uint256 value) internal { 266 | (bool success, ) = to.call.value(value)(new bytes(0)); 267 | require(success, "!safeTransferETH"); 268 | } 269 | } 270 | 271 | // File: contracts/Goblin.sol 272 | 273 | pragma solidity ^0.5.16; 274 | 275 | interface Goblin { 276 | 277 | /// @dev Work on a (potentially new) position. Optionally send surplus token back to Bank. 278 | function work(uint256 id, address user, address borrowToken, uint256 borrow, uint256 debt, bytes calldata data) external payable; 279 | 280 | /// @dev Return the amount of ETH wei to get back if we are to liquidate the position. 281 | function health(uint256 id, address borrowToken) external view returns (uint256); 282 | 283 | /// @dev Liquidate the given position to token need. Send all ETH back to Bank. 284 | function liquidate(uint256 id, address user, address borrowToken) external; 285 | } 286 | 287 | // File: contracts/Strategy.sol 288 | 289 | pragma solidity ^0.5.16; 290 | 291 | interface Strategy { 292 | 293 | /// @dev Execute worker strategy. Take LP tokens + debt token. Return LP tokens or debt token. 294 | /// @param user The original user that is interacting with the operator. 295 | /// @param borrowToken The token user want borrow. 296 | /// @param borrow The amount user borrow from bank. 297 | /// @param debt The user's total debt, for better decision making context. 298 | /// @param data Extra calldata information passed along to this strategy. 299 | /// @return token and amount need transfer back. 300 | function execute(address user, address borrowToken, uint256 borrow, uint256 debt, bytes calldata data) external payable; 301 | 302 | } 303 | 304 | // File: contracts/interfaces/IMdexFactory.sol 305 | 306 | /** 307 | *Submitted for verification at hecoinfo.com on 2021-02-25 308 | */ 309 | 310 | pragma solidity ^0.5.16; 311 | 312 | interface IMdexFactory { 313 | event PairCreated(address indexed token0, address indexed token1, address pair, uint); 314 | 315 | function feeTo() external view returns (address); 316 | 317 | function feeToSetter() external view returns (address); 318 | 319 | function feeToRate() external view returns (uint256); 320 | 321 | function initCodeHash() external view returns (bytes32); 322 | 323 | function getPair(address tokenA, address tokenB) external view returns (address pair); 324 | 325 | function allPairs(uint) external view returns (address pair); 326 | 327 | function allPairsLength() external view returns (uint); 328 | 329 | function createPair(address tokenA, address tokenB) external returns (address pair); 330 | 331 | function setFeeTo(address) external; 332 | 333 | function setFeeToSetter(address) external; 334 | 335 | function setFeeToRate(uint256) external; 336 | 337 | function setInitCodeHash(bytes32) external; 338 | 339 | function sortTokens(address tokenA, address tokenB) external pure returns (address token0, address token1); 340 | 341 | function pairFor(address tokenA, address tokenB) external view returns (address pair); 342 | 343 | function getReserves(address tokenA, address tokenB) external view returns (uint256 reserveA, uint256 reserveB); 344 | 345 | function quote(uint256 amountA, uint256 reserveA, uint256 reserveB) external pure returns (uint256 amountB); 346 | 347 | function getAmountOut(uint256 amountIn, uint256 reserveIn, uint256 reserveOut) external view returns (uint256 amountOut); 348 | 349 | function getAmountIn(uint256 amountOut, uint256 reserveIn, uint256 reserveOut) external view returns (uint256 amountIn); 350 | 351 | function getAmountsOut(uint256 amountIn, address[] calldata path) external view returns (uint256[] memory amounts); 352 | 353 | function getAmountsIn(uint256 amountOut, address[] calldata path) external view returns (uint256[] memory amounts); 354 | } 355 | 356 | // File: contracts/interfaces/IMdexRouter.sol 357 | 358 | pragma solidity ^0.5.16; 359 | 360 | interface IMdexRouter { 361 | function factory() external pure returns (address); 362 | 363 | function WHT() external pure returns (address); 364 | 365 | function swapMining() external pure returns (address); 366 | 367 | function addLiquidity( 368 | address tokenA, 369 | address tokenB, 370 | uint amountADesired, 371 | uint amountBDesired, 372 | uint amountAMin, 373 | uint amountBMin, 374 | address to, 375 | uint deadline 376 | ) external returns (uint amountA, uint amountB, uint liquidity); 377 | 378 | function addLiquidityETH( 379 | address token, 380 | uint amountTokenDesired, 381 | uint amountTokenMin, 382 | uint amountETHMin, 383 | address to, 384 | uint deadline 385 | ) external payable returns (uint amountToken, uint amountETH, uint liquidity); 386 | 387 | function removeLiquidity( 388 | address tokenA, 389 | address tokenB, 390 | uint liquidity, 391 | uint amountAMin, 392 | uint amountBMin, 393 | address to, 394 | uint deadline 395 | ) external returns (uint amountA, uint amountB); 396 | 397 | function removeLiquidityETH( 398 | address token, 399 | uint liquidity, 400 | uint amountTokenMin, 401 | uint amountETHMin, 402 | address to, 403 | uint deadline 404 | ) external returns (uint amountToken, uint amountETH); 405 | 406 | function removeLiquidityWithPermit( 407 | address tokenA, 408 | address tokenB, 409 | uint liquidity, 410 | uint amountAMin, 411 | uint amountBMin, 412 | address to, 413 | uint deadline, 414 | bool approveMax, uint8 v, bytes32 r, bytes32 s 415 | ) external returns (uint amountA, uint amountB); 416 | 417 | function removeLiquidityETHWithPermit( 418 | address token, 419 | uint liquidity, 420 | uint amountTokenMin, 421 | uint amountETHMin, 422 | address to, 423 | uint deadline, 424 | bool approveMax, uint8 v, bytes32 r, bytes32 s 425 | ) external returns (uint amountToken, uint amountETH); 426 | 427 | function swapExactTokensForTokens( 428 | uint amountIn, 429 | uint amountOutMin, 430 | address[] calldata path, 431 | address to, 432 | uint deadline 433 | ) external returns (uint[] memory amounts); 434 | 435 | function swapTokensForExactTokens( 436 | uint amountOut, 437 | uint amountInMax, 438 | address[] calldata path, 439 | address to, 440 | uint deadline 441 | ) external returns (uint[] memory amounts); 442 | 443 | function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline) 444 | external 445 | payable 446 | returns (uint[] memory amounts); 447 | 448 | function swapTokensForExactETH(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline) 449 | external 450 | returns (uint[] memory amounts); 451 | 452 | function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline) 453 | external 454 | returns (uint[] memory amounts); 455 | 456 | function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline) 457 | external 458 | payable 459 | returns (uint[] memory amounts); 460 | 461 | function quote(uint256 amountA, uint256 reserveA, uint256 reserveB) external view returns (uint256 amountB); 462 | 463 | function getAmountOut(uint256 amountIn, uint256 reserveIn, uint256 reserveOut) external view returns (uint256 amountOut); 464 | 465 | function getAmountIn(uint256 amountOut, uint256 reserveIn, uint256 reserveOut) external view returns (uint256 amountIn); 466 | 467 | function getAmountsOut(uint256 amountIn, address[] calldata path) external view returns (uint256[] memory amounts); 468 | 469 | function getAmountsIn(uint256 amountOut, address[] calldata path) external view returns (uint256[] memory amounts); 470 | 471 | function removeLiquidityETHSupportingFeeOnTransferTokens( 472 | address token, 473 | uint liquidity, 474 | uint amountTokenMin, 475 | uint amountETHMin, 476 | address to, 477 | uint deadline 478 | ) external returns (uint amountETH); 479 | 480 | function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens( 481 | address token, 482 | uint liquidity, 483 | uint amountTokenMin, 484 | uint amountETHMin, 485 | address to, 486 | uint deadline, 487 | bool approveMax, uint8 v, bytes32 r, bytes32 s 488 | ) external returns (uint amountETH); 489 | 490 | function swapExactTokensForTokensSupportingFeeOnTransferTokens( 491 | uint amountIn, 492 | uint amountOutMin, 493 | address[] calldata path, 494 | address to, 495 | uint deadline 496 | ) external; 497 | 498 | function swapExactETHForTokensSupportingFeeOnTransferTokens( 499 | uint amountOutMin, 500 | address[] calldata path, 501 | address to, 502 | uint deadline 503 | ) external payable; 504 | 505 | function swapExactTokensForETHSupportingFeeOnTransferTokens( 506 | uint amountIn, 507 | uint amountOutMin, 508 | address[] calldata path, 509 | address to, 510 | uint deadline 511 | ) external; 512 | } 513 | 514 | // File: contracts/interfaces/IMdexPair.sol 515 | 516 | pragma solidity ^0.5.16; 517 | 518 | interface IMdexPair { 519 | event Approval(address indexed owner, address indexed spender, uint value); 520 | event Transfer(address indexed from, address indexed to, uint value); 521 | 522 | function name() external pure returns (string memory); 523 | 524 | function symbol() external pure returns (string memory); 525 | 526 | function decimals() external pure returns (uint8); 527 | 528 | function totalSupply() external view returns (uint); 529 | 530 | function balanceOf(address owner) external view returns (uint); 531 | 532 | function allowance(address owner, address spender) external view returns (uint); 533 | 534 | function approve(address spender, uint value) external returns (bool); 535 | 536 | function transfer(address to, uint value) external returns (bool); 537 | 538 | function transferFrom(address from, address to, uint value) external returns (bool); 539 | 540 | function DOMAIN_SEPARATOR() external view returns (bytes32); 541 | 542 | function PERMIT_TYPEHASH() external pure returns (bytes32); 543 | 544 | function nonces(address owner) external view returns (uint); 545 | 546 | function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external; 547 | 548 | event Mint(address indexed sender, uint amount0, uint amount1); 549 | event Burn(address indexed sender, uint amount0, uint amount1, address indexed to); 550 | event Swap( 551 | address indexed sender, 552 | uint amount0In, 553 | uint amount1In, 554 | uint amount0Out, 555 | uint amount1Out, 556 | address indexed to 557 | ); 558 | event Sync(uint112 reserve0, uint112 reserve1); 559 | 560 | function MINIMUM_LIQUIDITY() external pure returns (uint); 561 | 562 | function factory() external view returns (address); 563 | 564 | function token0() external view returns (address); 565 | 566 | function token1() external view returns (address); 567 | 568 | function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast); 569 | 570 | function price0CumulativeLast() external view returns (uint); 571 | 572 | function price1CumulativeLast() external view returns (uint); 573 | 574 | function kLast() external view returns (uint); 575 | 576 | function mint(address to) external returns (uint liquidity); 577 | 578 | function burn(address to) external returns (uint amount0, uint amount1); 579 | 580 | function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external; 581 | 582 | function skim(address to) external; 583 | 584 | function sync() external; 585 | 586 | function price(address token, uint256 baseDecimal) external view returns (uint256); 587 | 588 | function initialize(address, address) external; 589 | } 590 | 591 | // File: contracts/interfaces/IStakingRewards.sol 592 | 593 | pragma solidity ^0.5.16; 594 | 595 | // Inheritance 596 | interface IStakingRewards { 597 | // Views 598 | function lastTimeRewardApplicable() external view returns (uint256); 599 | 600 | function rewardPerToken() external view returns (uint256); 601 | 602 | function earned(address account) external view returns (uint256); 603 | 604 | function getRewardForDuration() external view returns (uint256); 605 | 606 | function totalSupply() external view returns (uint256); 607 | 608 | function balanceOf(address account) external view returns (uint256); 609 | 610 | // Mutative 611 | 612 | function stake(uint256 amount, address user) external; 613 | 614 | function withdraw(uint256 amount, address user) external; 615 | 616 | function getReward(address user) external; 617 | 618 | function exit(address user) external; 619 | } 620 | 621 | // File: contracts/MdxGoblin.sol 622 | 623 | pragma solidity ^0.5.16; 624 | 625 | 626 | 627 | 628 | 629 | 630 | 631 | 632 | 633 | 634 | 635 | contract MdxGoblin is Ownable, ReentrancyGuard, Goblin { 636 | /// @notice Libraries 637 | using SafeToken for address; 638 | using SafeMath for uint256; 639 | 640 | /// @notice Events 641 | event AddPosition(uint256 indexed id, uint256 lpAmount); 642 | event RemovePosition(uint256 indexed id, uint256 lpAmount); 643 | event Liquidate(uint256 indexed id, address lpTokenAddress, uint256 lpAmount, address debtToken, uint256 liqAmount); 644 | 645 | /// @notice Immutable variables 646 | IStakingRewards public staking; 647 | IMdexFactory public factory; 648 | IMdexRouter public router; 649 | IMdexPair public lpToken; 650 | address public wht; 651 | address public token0; 652 | address public token1; 653 | address public operator; 654 | 655 | /// @notice Mutable state variables 656 | mapping(uint256 => uint256) public posLPAmount; 657 | mapping(address => bool) public okStrategies; 658 | uint256 public totalLPAmount; 659 | Strategy public liqStrategy; 660 | 661 | constructor( 662 | address _operator, 663 | IStakingRewards _staking, 664 | IMdexRouter _router, 665 | address _token0, 666 | address _token1, 667 | Strategy _liqStrategy 668 | ) public { 669 | operator = _operator; 670 | wht = _router.WHT(); 671 | staking = _staking; 672 | router = _router; 673 | factory = IMdexFactory(_router.factory()); 674 | 675 | _token0 = _token0 == address(0) ? wht : _token0; 676 | _token1 = _token1 == address(0) ? wht : _token1; 677 | 678 | lpToken = IMdexPair(factory.getPair(_token0, _token1)); 679 | token0 = lpToken.token0(); 680 | token1 = lpToken.token1(); 681 | 682 | liqStrategy = _liqStrategy; 683 | okStrategies[address(liqStrategy)] = true; 684 | 685 | // 100% trust in the staking pool 686 | lpToken.approve(address(_staking), uint256(-1)); 687 | } 688 | 689 | /// @dev Require that the caller must be the operator (the bank). 690 | modifier onlyOperator() { 691 | require(msg.sender == operator, "not operator"); 692 | _; 693 | } 694 | 695 | /// @dev Work on the given position. Must be called by the operator. 696 | /// @param id The position ID to work on. 697 | /// @param user The original user that is interacting with the operator. 698 | /// @param borrowToken The token user borrow from bank. 699 | /// @param borrow The amount user borrow form bank. 700 | /// @param debt The user's debt amount. 701 | /// @param data The encoded data, consisting of strategy address and bytes to strategy. 702 | function work(uint256 id, address user, address borrowToken, uint256 borrow, uint256 debt, bytes calldata data) 703 | external 704 | payable 705 | onlyOperator 706 | nonReentrant 707 | { 708 | require(borrowToken == token0 || borrowToken == token1 || borrowToken == address(0), "borrowToken not token0 and token1"); 709 | 710 | // 1. Convert this position back to LP tokens. 711 | _removePosition(id, user); 712 | // 2. Perform the worker strategy; sending LP tokens + borrowToken; expecting LP tokens. 713 | (address strategy, bytes memory ext) = abi.decode(data, (address, bytes)); 714 | require(okStrategies[strategy], "unapproved work strategy"); 715 | 716 | lpToken.transfer(strategy, lpToken.balanceOf(address(this))); 717 | 718 | // transfer the borrow token. 719 | if (borrow > 0 && borrowToken != address(0)) { 720 | borrowToken.safeTransferFrom(msg.sender, address(this), borrow); 721 | 722 | borrowToken.safeApprove(address(strategy), 0); 723 | borrowToken.safeApprove(address(strategy), uint256(-1)); 724 | } 725 | 726 | Strategy(strategy).execute.value(msg.value)(user, borrowToken, borrow, debt, ext); 727 | 728 | // 3. Add LP tokens back to the farming pool. 729 | _addPosition(id, user); 730 | 731 | if (borrowToken == address(0)) { 732 | SafeToken.safeTransferETH(msg.sender, address(this).balance); 733 | } else { 734 | uint256 borrowTokenAmount = borrowToken.myBalance(); 735 | if(borrowTokenAmount > 0){ 736 | SafeToken.safeTransfer(borrowToken, msg.sender, borrowTokenAmount); 737 | } 738 | } 739 | } 740 | 741 | /// @dev Return maximum output given the input amount and the status of Uniswap reserves. 742 | /// @param aIn The amount of asset to market sell. 743 | /// @param rIn the amount of asset in reserve for input. 744 | /// @param rOut The amount of asset in reserve for output. 745 | function getMktSellAmount(uint256 aIn, uint256 rIn, uint256 rOut) public pure returns (uint256) { 746 | if (aIn == 0) return 0; 747 | require(rIn > 0 && rOut > 0, "bad reserve values"); 748 | uint256 aInWithFee = aIn.mul(997); 749 | uint256 numerator = aInWithFee.mul(rOut); 750 | uint256 denominator = rIn.mul(1000).add(aInWithFee); 751 | return numerator / denominator; 752 | } 753 | 754 | /// @dev Return the amount of debt token to receive if we are to liquidate the given position. 755 | /// @param id The position ID to perform health check. 756 | /// @param borrowToken The token this position had debt. 757 | function health(uint256 id, address borrowToken) external view returns (uint256) { 758 | bool isDebtHt = borrowToken == address(0); 759 | require(borrowToken == token0 || borrowToken == token1 || isDebtHt, "borrowToken not token0 and token1"); 760 | 761 | // 1. Get the position's LP balance and LP total supply. 762 | uint256 lpBalance = posLPAmount[id]; 763 | uint256 lpSupply = lpToken.totalSupply(); 764 | // Ignore pending mintFee as it is insignificant 765 | // 2. Get the pool's total supply of token0 and token1. 766 | (uint256 totalAmount0, uint256 totalAmount1,) = lpToken.getReserves(); 767 | 768 | // 3. Convert the position's LP tokens to the underlying assets. 769 | uint256 userToken0 = lpBalance.mul(totalAmount0).div(lpSupply); 770 | uint256 userToken1 = lpBalance.mul(totalAmount1).div(lpSupply); 771 | 772 | if (isDebtHt) { 773 | borrowToken = token0 == wht ? token0 : token1; 774 | } 775 | 776 | // 4. Convert all farming tokens to debtToken and return total amount. 777 | if (borrowToken == token0) { 778 | return getMktSellAmount( 779 | userToken1, totalAmount1.sub(userToken1), totalAmount0.sub(userToken0) 780 | ).add(userToken0); 781 | } else { 782 | return getMktSellAmount( 783 | userToken0, totalAmount0.sub(userToken0), totalAmount1.sub(userToken1) 784 | ).add(userToken1); 785 | } 786 | } 787 | 788 | /// @dev Liquidate the given position by converting it to debtToken and return back to caller. 789 | /// @param id The position ID to perform liquidation. 790 | /// @param user The address than this position belong to. 791 | /// @param borrowToken The token user borrow from bank. 792 | function liquidate(uint256 id, address user, address borrowToken) 793 | external 794 | onlyOperator 795 | nonReentrant 796 | { 797 | bool isBorrowHt = borrowToken == address(0); 798 | require(borrowToken == token0 || borrowToken == token1 || isBorrowHt, "borrowToken not token0 and token1"); 799 | 800 | // 1. Convert the position back to LP tokens and use liquidate strategy. 801 | _removePosition(id, user); 802 | uint256 lpTokenAmount = lpToken.balanceOf(address(this)); 803 | lpToken.transfer(address(liqStrategy), lpTokenAmount); 804 | liqStrategy.execute(address(0), borrowToken, uint256(0), uint256(0), abi.encode(address(lpToken))); 805 | 806 | // 2. transfer borrowToken and user want back to goblin. 807 | uint256 tokenLiquidate; 808 | if (isBorrowHt){ 809 | tokenLiquidate = address(this).balance; 810 | SafeToken.safeTransferETH(msg.sender, tokenLiquidate); 811 | } else { 812 | tokenLiquidate = borrowToken.myBalance(); 813 | borrowToken.safeTransfer(msg.sender, tokenLiquidate); 814 | } 815 | 816 | emit Liquidate(id, address(lpToken), lpTokenAmount, borrowToken, tokenLiquidate); 817 | } 818 | 819 | /// @dev Internal function to stake all outstanding LP tokens to the given position ID. 820 | function _addPosition(uint256 id, address user) internal { 821 | uint256 lpBalance = lpToken.balanceOf(address(this)); 822 | if (lpBalance > 0) { 823 | // take lpToken to the pool2. 824 | staking.stake(lpBalance, user); 825 | posLPAmount[id] = posLPAmount[id].add(lpBalance); 826 | totalLPAmount = totalLPAmount.add(lpBalance); 827 | emit AddPosition(id, lpBalance); 828 | } 829 | } 830 | 831 | /// @dev Internal function to remove shares of the ID and convert to outstanding LP tokens. 832 | function _removePosition(uint256 id, address user) internal { 833 | uint256 lpAmount = posLPAmount[id]; 834 | if (lpAmount > 0) { 835 | staking.withdraw(lpAmount, user); 836 | totalLPAmount = totalLPAmount.sub(lpAmount); 837 | posLPAmount[id] = 0; 838 | emit RemovePosition(id, lpAmount); 839 | } 840 | } 841 | 842 | /// @dev Recover ERC20 tokens that were accidentally sent to this smart contract. 843 | /// @param token The token contract. Can be anything. This contract should not hold ERC20 tokens. 844 | /// @param to The address to send the tokens to. 845 | /// @param value The number of tokens to transfer to `to`. 846 | function recover(address token, address to, uint256 value) external onlyOwner nonReentrant { 847 | token.safeTransfer(to, value); 848 | } 849 | 850 | /// @dev Set the given strategies' approval status. 851 | /// @param strategies The strategy addresses. 852 | /// @param isOk Whether to approve or unapprove the given strategies. 853 | function setStrategyOk(address[] calldata strategies, bool isOk) external onlyOwner { 854 | uint256 len = strategies.length; 855 | for (uint256 idx = 0; idx < len; idx++) { 856 | okStrategies[strategies[idx]] = isOk; 857 | } 858 | } 859 | 860 | /// @dev Update critical strategy smart contracts. EMERGENCY ONLY. Bad strategies can steal funds. 861 | /// @param _liqStrategy The new liquidate strategy contract. 862 | function setCriticalStrategies(Strategy _liqStrategy) external onlyOwner { 863 | liqStrategy = _liqStrategy; 864 | } 865 | 866 | function() external payable {} 867 | } 868 | -------------------------------------------------------------------------------- /MdxStrategyAddTwoSidesOptimal.sol: -------------------------------------------------------------------------------- 1 | // File: openzeppelin-solidity-2.3.0/contracts/ownership/Ownable.sol 2 | 3 | pragma solidity ^0.5.0; 4 | 5 | /** 6 | * @dev Contract module which provides a basic access control mechanism, where 7 | * there is an account (an owner) that can be granted exclusive access to 8 | * specific functions. 9 | * 10 | * This module is used through inheritance. It will make available the modifier 11 | * `onlyOwner`, which can be aplied to your functions to restrict their use to 12 | * the owner. 13 | */ 14 | contract Ownable { 15 | address private _owner; 16 | 17 | event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); 18 | 19 | /** 20 | * @dev Initializes the contract setting the deployer as the initial owner. 21 | */ 22 | constructor () internal { 23 | _owner = msg.sender; 24 | emit OwnershipTransferred(address(0), _owner); 25 | } 26 | 27 | /** 28 | * @dev Returns the address of the current owner. 29 | */ 30 | function owner() public view returns (address) { 31 | return _owner; 32 | } 33 | 34 | /** 35 | * @dev Throws if called by any account other than the owner. 36 | */ 37 | modifier onlyOwner() { 38 | require(isOwner(), "Ownable: caller is not the owner"); 39 | _; 40 | } 41 | 42 | /** 43 | * @dev Returns true if the caller is the current owner. 44 | */ 45 | function isOwner() public view returns (bool) { 46 | return msg.sender == _owner; 47 | } 48 | 49 | /** 50 | * @dev Leaves the contract without owner. It will not be possible to call 51 | * `onlyOwner` functions anymore. Can only be called by the current owner. 52 | * 53 | * > Note: Renouncing ownership will leave the contract without an owner, 54 | * thereby removing any functionality that is only available to the owner. 55 | */ 56 | function renounceOwnership() public onlyOwner { 57 | emit OwnershipTransferred(_owner, address(0)); 58 | _owner = address(0); 59 | } 60 | 61 | /** 62 | * @dev Transfers ownership of the contract to a new account (`newOwner`). 63 | * Can only be called by the current owner. 64 | */ 65 | function transferOwnership(address newOwner) public onlyOwner { 66 | _transferOwnership(newOwner); 67 | } 68 | 69 | /** 70 | * @dev Transfers ownership of the contract to a new account (`newOwner`). 71 | */ 72 | function _transferOwnership(address newOwner) internal { 73 | require(newOwner != address(0), "Ownable: new owner is the zero address"); 74 | emit OwnershipTransferred(_owner, newOwner); 75 | _owner = newOwner; 76 | } 77 | } 78 | 79 | // File: openzeppelin-solidity-2.3.0/contracts/math/SafeMath.sol 80 | 81 | pragma solidity ^0.5.0; 82 | 83 | /** 84 | * @dev Wrappers over Solidity's arithmetic operations with added overflow 85 | * checks. 86 | * 87 | * Arithmetic operations in Solidity wrap on overflow. This can easily result 88 | * in bugs, because programmers usually assume that an overflow raises an 89 | * error, which is the standard behavior in high level programming languages. 90 | * `SafeMath` restores this intuition by reverting the transaction when an 91 | * operation overflows. 92 | * 93 | * Using this library instead of the unchecked operations eliminates an entire 94 | * class of bugs, so it's recommended to use it always. 95 | */ 96 | library SafeMath { 97 | /** 98 | * @dev Returns the addition of two unsigned integers, reverting on 99 | * overflow. 100 | * 101 | * Counterpart to Solidity's `+` operator. 102 | * 103 | * Requirements: 104 | * - Addition cannot overflow. 105 | */ 106 | function add(uint256 a, uint256 b) internal pure returns (uint256) { 107 | uint256 c = a + b; 108 | require(c >= a, "SafeMath: addition overflow"); 109 | 110 | return c; 111 | } 112 | 113 | /** 114 | * @dev Returns the subtraction of two unsigned integers, reverting on 115 | * overflow (when the result is negative). 116 | * 117 | * Counterpart to Solidity's `-` operator. 118 | * 119 | * Requirements: 120 | * - Subtraction cannot overflow. 121 | */ 122 | function sub(uint256 a, uint256 b) internal pure returns (uint256) { 123 | require(b <= a, "SafeMath: subtraction overflow"); 124 | uint256 c = a - b; 125 | 126 | return c; 127 | } 128 | 129 | /** 130 | * @dev Returns the multiplication of two unsigned integers, reverting on 131 | * overflow. 132 | * 133 | * Counterpart to Solidity's `*` operator. 134 | * 135 | * Requirements: 136 | * - Multiplication cannot overflow. 137 | */ 138 | function mul(uint256 a, uint256 b) internal pure returns (uint256) { 139 | // Gas optimization: this is cheaper than requiring 'a' not being zero, but the 140 | // benefit is lost if 'b' is also tested. 141 | // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522 142 | if (a == 0) { 143 | return 0; 144 | } 145 | 146 | uint256 c = a * b; 147 | require(c / a == b, "SafeMath: multiplication overflow"); 148 | 149 | return c; 150 | } 151 | 152 | /** 153 | * @dev Returns the integer division of two unsigned integers. Reverts on 154 | * division by zero. The result is rounded towards zero. 155 | * 156 | * Counterpart to Solidity's `/` operator. Note: this function uses a 157 | * `revert` opcode (which leaves remaining gas untouched) while Solidity 158 | * uses an invalid opcode to revert (consuming all remaining gas). 159 | * 160 | * Requirements: 161 | * - The divisor cannot be zero. 162 | */ 163 | function div(uint256 a, uint256 b) internal pure returns (uint256) { 164 | // Solidity only automatically asserts when dividing by 0 165 | require(b > 0, "SafeMath: division by zero"); 166 | uint256 c = a / b; 167 | // assert(a == b * c + a % b); // There is no case in which this doesn't hold 168 | 169 | return c; 170 | } 171 | 172 | /** 173 | * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), 174 | * Reverts when dividing by zero. 175 | * 176 | * Counterpart to Solidity's `%` operator. This function uses a `revert` 177 | * opcode (which leaves remaining gas untouched) while Solidity uses an 178 | * invalid opcode to revert (consuming all remaining gas). 179 | * 180 | * Requirements: 181 | * - The divisor cannot be zero. 182 | */ 183 | function mod(uint256 a, uint256 b) internal pure returns (uint256) { 184 | require(b != 0, "SafeMath: modulo by zero"); 185 | return a % b; 186 | } 187 | } 188 | 189 | // File: openzeppelin-solidity-2.3.0/contracts/utils/ReentrancyGuard.sol 190 | 191 | pragma solidity ^0.5.0; 192 | 193 | /** 194 | * @dev Contract module that helps prevent reentrant calls to a function. 195 | * 196 | * Inheriting from `ReentrancyGuard` will make the `nonReentrant` modifier 197 | * available, which can be aplied to functions to make sure there are no nested 198 | * (reentrant) calls to them. 199 | * 200 | * Note that because there is a single `nonReentrant` guard, functions marked as 201 | * `nonReentrant` may not call one another. This can be worked around by making 202 | * those functions `private`, and then adding `external` `nonReentrant` entry 203 | * points to them. 204 | */ 205 | contract ReentrancyGuard { 206 | /// @dev counter to allow mutex lock with only one SSTORE operation 207 | uint256 private _guardCounter; 208 | 209 | constructor () internal { 210 | // The counter starts at one to prevent changing it from zero to a non-zero 211 | // value, which is a more expensive operation. 212 | _guardCounter = 1; 213 | } 214 | 215 | /** 216 | * @dev Prevents a contract from calling itself, directly or indirectly. 217 | * Calling a `nonReentrant` function from another `nonReentrant` 218 | * function is not supported. It is possible to prevent this from happening 219 | * by making the `nonReentrant` function external, and make it call a 220 | * `private` function that does the actual work. 221 | */ 222 | modifier nonReentrant() { 223 | _guardCounter += 1; 224 | uint256 localCounter = _guardCounter; 225 | _; 226 | require(localCounter == _guardCounter, "ReentrancyGuard: reentrant call"); 227 | } 228 | } 229 | 230 | // File: @uniswap/v2-core/contracts/libraries/Math.sol 231 | 232 | pragma solidity =0.5.16; 233 | 234 | // a library for performing various math operations 235 | 236 | library Math { 237 | function min(uint x, uint y) internal pure returns (uint z) { 238 | z = x < y ? x : y; 239 | } 240 | 241 | // babylonian method (https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method) 242 | function sqrt(uint y) internal pure returns (uint z) { 243 | if (y > 3) { 244 | z = y; 245 | uint x = y / 2 + 1; 246 | while (x < z) { 247 | z = x; 248 | x = (y / x + x) / 2; 249 | } 250 | } else if (y != 0) { 251 | z = 1; 252 | } 253 | } 254 | } 255 | 256 | // File: contracts/SafeToken.sol 257 | 258 | pragma solidity ^0.5.16; 259 | 260 | interface ERC20Interface { 261 | function balanceOf(address user) external view returns (uint256); 262 | } 263 | 264 | library SafeToken { 265 | function myBalance(address token) internal view returns (uint256) { 266 | return ERC20Interface(token).balanceOf(address(this)); 267 | } 268 | 269 | function balanceOf(address token, address user) internal view returns (uint256) { 270 | return ERC20Interface(token).balanceOf(user); 271 | } 272 | 273 | function safeApprove(address token, address to, uint256 value) internal { 274 | // bytes4(keccak256(bytes('approve(address,uint256)'))); 275 | (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x095ea7b3, to, value)); 276 | require(success && (data.length == 0 || abi.decode(data, (bool))), "!safeApprove"); 277 | } 278 | 279 | function safeTransfer(address token, address to, uint256 value) internal { 280 | // bytes4(keccak256(bytes('transfer(address,uint256)'))); 281 | (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0xa9059cbb, to, value)); 282 | require(success && (data.length == 0 || abi.decode(data, (bool))), "!safeTransfer"); 283 | } 284 | 285 | function safeTransferFrom(address token, address from, address to, uint256 value) internal { 286 | // bytes4(keccak256(bytes('transferFrom(address,address,uint256)'))); 287 | (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x23b872dd, from, to, value)); 288 | require(success && (data.length == 0 || abi.decode(data, (bool))), "!safeTransferFrom"); 289 | } 290 | 291 | function safeTransferETH(address to, uint256 value) internal { 292 | (bool success, ) = to.call.value(value)(new bytes(0)); 293 | require(success, "!safeTransferETH"); 294 | } 295 | } 296 | 297 | // File: contracts/Strategy.sol 298 | 299 | pragma solidity ^0.5.16; 300 | 301 | interface Strategy { 302 | 303 | /// @dev Execute worker strategy. Take LP tokens + debt token. Return LP tokens or debt token. 304 | /// @param user The original user that is interacting with the operator. 305 | /// @param borrowToken The token user want borrow. 306 | /// @param borrow The amount user borrow from bank. 307 | /// @param debt The user's total debt, for better decision making context. 308 | /// @param data Extra calldata information passed along to this strategy. 309 | /// @return token and amount need transfer back. 310 | function execute(address user, address borrowToken, uint256 borrow, uint256 debt, bytes calldata data) external payable; 311 | 312 | } 313 | 314 | // File: contracts/interfaces/IWHT.sol 315 | 316 | pragma solidity ^0.5.16; 317 | 318 | interface IWHT { 319 | function balanceOf(address user) external returns (uint); 320 | 321 | function approve(address to, uint value) external returns (bool); 322 | 323 | function transfer(address to, uint value) external returns (bool); 324 | 325 | function deposit() external payable; 326 | 327 | function withdraw(uint) external; 328 | } 329 | 330 | // File: contracts/interfaces/IMdexPair.sol 331 | 332 | pragma solidity ^0.5.16; 333 | 334 | interface IMdexPair { 335 | event Approval(address indexed owner, address indexed spender, uint value); 336 | event Transfer(address indexed from, address indexed to, uint value); 337 | 338 | function name() external pure returns (string memory); 339 | 340 | function symbol() external pure returns (string memory); 341 | 342 | function decimals() external pure returns (uint8); 343 | 344 | function totalSupply() external view returns (uint); 345 | 346 | function balanceOf(address owner) external view returns (uint); 347 | 348 | function allowance(address owner, address spender) external view returns (uint); 349 | 350 | function approve(address spender, uint value) external returns (bool); 351 | 352 | function transfer(address to, uint value) external returns (bool); 353 | 354 | function transferFrom(address from, address to, uint value) external returns (bool); 355 | 356 | function DOMAIN_SEPARATOR() external view returns (bytes32); 357 | 358 | function PERMIT_TYPEHASH() external pure returns (bytes32); 359 | 360 | function nonces(address owner) external view returns (uint); 361 | 362 | function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external; 363 | 364 | event Mint(address indexed sender, uint amount0, uint amount1); 365 | event Burn(address indexed sender, uint amount0, uint amount1, address indexed to); 366 | event Swap( 367 | address indexed sender, 368 | uint amount0In, 369 | uint amount1In, 370 | uint amount0Out, 371 | uint amount1Out, 372 | address indexed to 373 | ); 374 | event Sync(uint112 reserve0, uint112 reserve1); 375 | 376 | function MINIMUM_LIQUIDITY() external pure returns (uint); 377 | 378 | function factory() external view returns (address); 379 | 380 | function token0() external view returns (address); 381 | 382 | function token1() external view returns (address); 383 | 384 | function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast); 385 | 386 | function price0CumulativeLast() external view returns (uint); 387 | 388 | function price1CumulativeLast() external view returns (uint); 389 | 390 | function kLast() external view returns (uint); 391 | 392 | function mint(address to) external returns (uint liquidity); 393 | 394 | function burn(address to) external returns (uint amount0, uint amount1); 395 | 396 | function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external; 397 | 398 | function skim(address to) external; 399 | 400 | function sync() external; 401 | 402 | function price(address token, uint256 baseDecimal) external view returns (uint256); 403 | 404 | function initialize(address, address) external; 405 | } 406 | 407 | // File: contracts/interfaces/IMdexRouter.sol 408 | 409 | pragma solidity ^0.5.16; 410 | 411 | interface IMdexRouter { 412 | function factory() external pure returns (address); 413 | 414 | function WHT() external pure returns (address); 415 | 416 | function swapMining() external pure returns (address); 417 | 418 | function addLiquidity( 419 | address tokenA, 420 | address tokenB, 421 | uint amountADesired, 422 | uint amountBDesired, 423 | uint amountAMin, 424 | uint amountBMin, 425 | address to, 426 | uint deadline 427 | ) external returns (uint amountA, uint amountB, uint liquidity); 428 | 429 | function addLiquidityETH( 430 | address token, 431 | uint amountTokenDesired, 432 | uint amountTokenMin, 433 | uint amountETHMin, 434 | address to, 435 | uint deadline 436 | ) external payable returns (uint amountToken, uint amountETH, uint liquidity); 437 | 438 | function removeLiquidity( 439 | address tokenA, 440 | address tokenB, 441 | uint liquidity, 442 | uint amountAMin, 443 | uint amountBMin, 444 | address to, 445 | uint deadline 446 | ) external returns (uint amountA, uint amountB); 447 | 448 | function removeLiquidityETH( 449 | address token, 450 | uint liquidity, 451 | uint amountTokenMin, 452 | uint amountETHMin, 453 | address to, 454 | uint deadline 455 | ) external returns (uint amountToken, uint amountETH); 456 | 457 | function removeLiquidityWithPermit( 458 | address tokenA, 459 | address tokenB, 460 | uint liquidity, 461 | uint amountAMin, 462 | uint amountBMin, 463 | address to, 464 | uint deadline, 465 | bool approveMax, uint8 v, bytes32 r, bytes32 s 466 | ) external returns (uint amountA, uint amountB); 467 | 468 | function removeLiquidityETHWithPermit( 469 | address token, 470 | uint liquidity, 471 | uint amountTokenMin, 472 | uint amountETHMin, 473 | address to, 474 | uint deadline, 475 | bool approveMax, uint8 v, bytes32 r, bytes32 s 476 | ) external returns (uint amountToken, uint amountETH); 477 | 478 | function swapExactTokensForTokens( 479 | uint amountIn, 480 | uint amountOutMin, 481 | address[] calldata path, 482 | address to, 483 | uint deadline 484 | ) external returns (uint[] memory amounts); 485 | 486 | function swapTokensForExactTokens( 487 | uint amountOut, 488 | uint amountInMax, 489 | address[] calldata path, 490 | address to, 491 | uint deadline 492 | ) external returns (uint[] memory amounts); 493 | 494 | function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline) 495 | external 496 | payable 497 | returns (uint[] memory amounts); 498 | 499 | function swapTokensForExactETH(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline) 500 | external 501 | returns (uint[] memory amounts); 502 | 503 | function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline) 504 | external 505 | returns (uint[] memory amounts); 506 | 507 | function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline) 508 | external 509 | payable 510 | returns (uint[] memory amounts); 511 | 512 | function quote(uint256 amountA, uint256 reserveA, uint256 reserveB) external view returns (uint256 amountB); 513 | 514 | function getAmountOut(uint256 amountIn, uint256 reserveIn, uint256 reserveOut) external view returns (uint256 amountOut); 515 | 516 | function getAmountIn(uint256 amountOut, uint256 reserveIn, uint256 reserveOut) external view returns (uint256 amountIn); 517 | 518 | function getAmountsOut(uint256 amountIn, address[] calldata path) external view returns (uint256[] memory amounts); 519 | 520 | function getAmountsIn(uint256 amountOut, address[] calldata path) external view returns (uint256[] memory amounts); 521 | 522 | function removeLiquidityETHSupportingFeeOnTransferTokens( 523 | address token, 524 | uint liquidity, 525 | uint amountTokenMin, 526 | uint amountETHMin, 527 | address to, 528 | uint deadline 529 | ) external returns (uint amountETH); 530 | 531 | function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens( 532 | address token, 533 | uint liquidity, 534 | uint amountTokenMin, 535 | uint amountETHMin, 536 | address to, 537 | uint deadline, 538 | bool approveMax, uint8 v, bytes32 r, bytes32 s 539 | ) external returns (uint amountETH); 540 | 541 | function swapExactTokensForTokensSupportingFeeOnTransferTokens( 542 | uint amountIn, 543 | uint amountOutMin, 544 | address[] calldata path, 545 | address to, 546 | uint deadline 547 | ) external; 548 | 549 | function swapExactETHForTokensSupportingFeeOnTransferTokens( 550 | uint amountOutMin, 551 | address[] calldata path, 552 | address to, 553 | uint deadline 554 | ) external payable; 555 | 556 | function swapExactTokensForETHSupportingFeeOnTransferTokens( 557 | uint amountIn, 558 | uint amountOutMin, 559 | address[] calldata path, 560 | address to, 561 | uint deadline 562 | ) external; 563 | } 564 | 565 | // File: contracts/interfaces/IMdexFactory.sol 566 | 567 | /** 568 | *Submitted for verification at hecoinfo.com on 2021-02-25 569 | */ 570 | 571 | pragma solidity ^0.5.16; 572 | 573 | interface IMdexFactory { 574 | event PairCreated(address indexed token0, address indexed token1, address pair, uint); 575 | 576 | function feeTo() external view returns (address); 577 | 578 | function feeToSetter() external view returns (address); 579 | 580 | function feeToRate() external view returns (uint256); 581 | 582 | function initCodeHash() external view returns (bytes32); 583 | 584 | function getPair(address tokenA, address tokenB) external view returns (address pair); 585 | 586 | function allPairs(uint) external view returns (address pair); 587 | 588 | function allPairsLength() external view returns (uint); 589 | 590 | function createPair(address tokenA, address tokenB) external returns (address pair); 591 | 592 | function setFeeTo(address) external; 593 | 594 | function setFeeToSetter(address) external; 595 | 596 | function setFeeToRate(uint256) external; 597 | 598 | function setInitCodeHash(bytes32) external; 599 | 600 | function sortTokens(address tokenA, address tokenB) external pure returns (address token0, address token1); 601 | 602 | function pairFor(address tokenA, address tokenB) external view returns (address pair); 603 | 604 | function getReserves(address tokenA, address tokenB) external view returns (uint256 reserveA, uint256 reserveB); 605 | 606 | function quote(uint256 amountA, uint256 reserveA, uint256 reserveB) external pure returns (uint256 amountB); 607 | 608 | function getAmountOut(uint256 amountIn, uint256 reserveIn, uint256 reserveOut) external view returns (uint256 amountOut); 609 | 610 | function getAmountIn(uint256 amountOut, uint256 reserveIn, uint256 reserveOut) external view returns (uint256 amountIn); 611 | 612 | function getAmountsOut(uint256 amountIn, address[] calldata path) external view returns (uint256[] memory amounts); 613 | 614 | function getAmountsIn(uint256 amountOut, address[] calldata path) external view returns (uint256[] memory amounts); 615 | } 616 | 617 | // File: contracts/MdxStrategyAddTwoSidesOptimal.sol 618 | 619 | pragma solidity ^0.5.16; 620 | 621 | 622 | 623 | 624 | 625 | 626 | 627 | 628 | 629 | 630 | 631 | contract MdxStrategyAddTwoSidesOptimal is Ownable, ReentrancyGuard, Strategy { 632 | using SafeToken for address; 633 | using SafeMath for uint256; 634 | 635 | IMdexFactory public factory; 636 | IMdexRouter public router; 637 | address public wht; 638 | address public goblin; 639 | 640 | /// @dev Create a new add two-side optimal strategy instance for mdx. 641 | /// @param _router The mdx router smart contract. 642 | /// @param _goblin The goblin can execute the smart contract. 643 | constructor(IMdexRouter _router, address _goblin) public { 644 | factory = IMdexFactory(_router.factory()); 645 | router = _router; 646 | 647 | wht = _router.WHT(); 648 | goblin = _goblin; 649 | } 650 | 651 | /// @dev Throws if called by any account other than the goblin. 652 | modifier onlyGoblin() { 653 | require(isGoblin(), "caller is not the goblin"); 654 | _; 655 | } 656 | 657 | /// @dev Returns true if the caller is the current goblin. 658 | function isGoblin() public view returns (bool) { 659 | return msg.sender == goblin; 660 | } 661 | 662 | /// @dev Compute optimal deposit amount 663 | /// @param amtA amount of token A desired to deposit 664 | /// @param amtB amonut of token B desired to deposit 665 | /// @param resA amount of token A in reserve 666 | /// @param resB amount of token B in reserve 667 | function optimalDeposit( 668 | uint256 amtA, 669 | uint256 amtB, 670 | uint256 resA, 671 | uint256 resB 672 | ) internal pure returns (uint256 swapAmt, bool isReversed) { 673 | if (amtA.mul(resB) >= amtB.mul(resA)) { 674 | swapAmt = _optimalDepositA(amtA, amtB, resA, resB); 675 | isReversed = false; 676 | } else { 677 | swapAmt = _optimalDepositA(amtB, amtA, resB, resA); 678 | isReversed = true; 679 | } 680 | } 681 | 682 | /// @dev Compute optimal deposit amount helper 683 | /// @param amtA amount of token A desired to deposit 684 | /// @param amtB amonut of token B desired to deposit 685 | /// @param resA amount of token A in reserve 686 | /// @param resB amount of token B in reserve 687 | function _optimalDepositA( 688 | uint256 amtA, 689 | uint256 amtB, 690 | uint256 resA, 691 | uint256 resB 692 | ) internal pure returns (uint256) { 693 | require(amtA.mul(resB) >= amtB.mul(resA), "Reversed"); 694 | 695 | uint256 a = 997; 696 | uint256 b = uint256(1997).mul(resA); 697 | uint256 _c = (amtA.mul(resB)).sub(amtB.mul(resA)); 698 | uint256 c = _c.mul(1000).div(amtB.add(resB)).mul(resA); 699 | 700 | uint256 d = a.mul(c).mul(4); 701 | uint256 e = Math.sqrt(b.mul(b).add(d)); 702 | 703 | uint256 numerator = e.sub(b); 704 | uint256 denominator = a.mul(2); 705 | 706 | return numerator.div(denominator); 707 | } 708 | 709 | /// @dev Execute worker strategy. Take LP tokens + debtToken. Return LP tokens. 710 | /// @param user User address 711 | /// @param borrowToken The token user borrow from bank. 712 | /// @param borrow The amount user borrow from bank. 713 | /// @param data Extra calldata information passed along to this strategy. 714 | function execute(address user, address borrowToken, uint256 borrow, uint256 /* debt */, bytes calldata data) 715 | external 716 | payable 717 | onlyGoblin 718 | nonReentrant 719 | { 720 | address token0; 721 | address token1; 722 | uint256 minLPAmount; 723 | { 724 | // 1. decode token and amount info, and transfer to contract. 725 | (address _token0, address _token1, uint256 token0Amount, uint256 token1Amount, uint256 _minLPAmount) = 726 | abi.decode(data, (address, address, uint256, uint256, uint256)); 727 | token0 = _token0; 728 | token1 = _token1; 729 | minLPAmount = _minLPAmount; 730 | 731 | require(borrowToken == token0 || borrowToken == token1, "borrowToken not token0 and token1"); 732 | if (token0Amount > 0 && _token0 != address(0)) { 733 | token0.safeTransferFrom(user, address(this), token0Amount); 734 | } 735 | if (token1Amount > 0 && token1 != address(0)) { 736 | token1.safeTransferFrom(user, address(this), token1Amount); 737 | } 738 | } 739 | 740 | address htRelative = address(0); 741 | { 742 | if (borrow > 0 && borrowToken != address(0)) { 743 | borrowToken.safeTransferFrom(msg.sender, address(this), borrow); 744 | } 745 | if (token0 == address(0)){ 746 | token0 = wht; 747 | htRelative = token1; 748 | } 749 | if (token1 == address(0)){ 750 | token1 = wht; 751 | htRelative = token0; 752 | } 753 | 754 | // change all ht to WHT if need. 755 | uint256 htBalance = address(this).balance; 756 | if (htBalance > 0) { 757 | IWHT(wht).deposit.value(htBalance)(); 758 | } 759 | } 760 | // tokens are all ERC20 token now. 761 | 762 | IMdexPair lpToken = IMdexPair(factory.getPair(token0, token1)); 763 | // 2. Compute the optimal amount of token0 and token1 to be converted. 764 | address tokenRelative; 765 | { 766 | borrowToken = borrowToken == address(0) ? wht : borrowToken; 767 | tokenRelative = borrowToken == lpToken.token0() ? token1 : token0; 768 | 769 | borrowToken.safeApprove(address(router), 0); 770 | borrowToken.safeApprove(address(router), uint256(-1)); 771 | 772 | tokenRelative.safeApprove(address(router), 0); 773 | tokenRelative.safeApprove(address(router), uint256(-1)); 774 | 775 | // 3. swap and mint LP tokens. 776 | calAndSwap(lpToken, borrowToken, tokenRelative); 777 | 778 | (,, uint256 moreLPAmount) = router.addLiquidity(token0, token1, token0.myBalance(), token1.myBalance(), 0, 0, address(this), now); 779 | require(moreLPAmount >= minLPAmount, "insufficient LP tokens received"); 780 | } 781 | 782 | // 4. send lpToken and borrowToken back to the sender. 783 | lpToken.transfer(msg.sender, lpToken.balanceOf(address(this))); 784 | 785 | if (htRelative == address(0)) { 786 | borrowToken.safeTransfer(msg.sender, borrowToken.myBalance()); 787 | tokenRelative.safeTransfer(user, tokenRelative.myBalance()); 788 | } else { 789 | safeUnWrapperAndAllSend(borrowToken, msg.sender); 790 | safeUnWrapperAndAllSend(tokenRelative, user); 791 | } 792 | } 793 | 794 | /// get token balance, if is WHT un wrapper to HT and send to 'to' 795 | function safeUnWrapperAndAllSend(address token, address to) internal { 796 | uint256 total = SafeToken.myBalance(token); 797 | if (total > 0) { 798 | if (token == wht) { 799 | IWHT(wht).withdraw(total); 800 | SafeToken.safeTransferETH(to, total); 801 | } else { 802 | SafeToken.safeTransfer(token, to, total); 803 | } 804 | } 805 | } 806 | 807 | /// Compute amount and swap between borrowToken and tokenRelative. 808 | function calAndSwap(IMdexPair lpToken, address borrowToken, address tokenRelative) internal { 809 | (uint256 token0Reserve, uint256 token1Reserve,) = lpToken.getReserves(); 810 | (uint256 debtReserve, uint256 relativeReserve) = borrowToken == 811 | lpToken.token0() ? (token0Reserve, token1Reserve) : (token1Reserve, token0Reserve); 812 | (uint256 swapAmt, bool isReversed) = optimalDeposit(borrowToken.myBalance(), tokenRelative.myBalance(), 813 | debtReserve, relativeReserve); 814 | 815 | if (swapAmt > 0){ 816 | address[] memory path = new address[](2); 817 | (path[0], path[1]) = isReversed ? (tokenRelative, borrowToken) : (borrowToken, tokenRelative); 818 | router.swapExactTokensForTokens(swapAmt, 0, path, address(this), now); 819 | } 820 | } 821 | 822 | /// @dev Recover ERC20 tokens that were accidentally sent to this smart contract. 823 | /// @param token The token contract. Can be anything. This contract should not hold ERC20 tokens. 824 | /// @param to The address to send the tokens to. 825 | /// @param value The number of tokens to transfer to `to`. 826 | function recover(address token, address to, uint256 value) external onlyOwner nonReentrant { 827 | token.safeTransfer(to, value); 828 | } 829 | 830 | function() external payable {} 831 | } 832 | -------------------------------------------------------------------------------- /MdxStrategyWithdrawMinimizeTrading.sol: -------------------------------------------------------------------------------- 1 | // File: openzeppelin-solidity-2.3.0/contracts/ownership/Ownable.sol 2 | 3 | pragma solidity ^0.5.0; 4 | 5 | /** 6 | * @dev Contract module which provides a basic access control mechanism, where 7 | * there is an account (an owner) that can be granted exclusive access to 8 | * specific functions. 9 | * 10 | * This module is used through inheritance. It will make available the modifier 11 | * `onlyOwner`, which can be aplied to your functions to restrict their use to 12 | * the owner. 13 | */ 14 | contract Ownable { 15 | address private _owner; 16 | 17 | event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); 18 | 19 | /** 20 | * @dev Initializes the contract setting the deployer as the initial owner. 21 | */ 22 | constructor () internal { 23 | _owner = msg.sender; 24 | emit OwnershipTransferred(address(0), _owner); 25 | } 26 | 27 | /** 28 | * @dev Returns the address of the current owner. 29 | */ 30 | function owner() public view returns (address) { 31 | return _owner; 32 | } 33 | 34 | /** 35 | * @dev Throws if called by any account other than the owner. 36 | */ 37 | modifier onlyOwner() { 38 | require(isOwner(), "Ownable: caller is not the owner"); 39 | _; 40 | } 41 | 42 | /** 43 | * @dev Returns true if the caller is the current owner. 44 | */ 45 | function isOwner() public view returns (bool) { 46 | return msg.sender == _owner; 47 | } 48 | 49 | /** 50 | * @dev Leaves the contract without owner. It will not be possible to call 51 | * `onlyOwner` functions anymore. Can only be called by the current owner. 52 | * 53 | * > Note: Renouncing ownership will leave the contract without an owner, 54 | * thereby removing any functionality that is only available to the owner. 55 | */ 56 | function renounceOwnership() public onlyOwner { 57 | emit OwnershipTransferred(_owner, address(0)); 58 | _owner = address(0); 59 | } 60 | 61 | /** 62 | * @dev Transfers ownership of the contract to a new account (`newOwner`). 63 | * Can only be called by the current owner. 64 | */ 65 | function transferOwnership(address newOwner) public onlyOwner { 66 | _transferOwnership(newOwner); 67 | } 68 | 69 | /** 70 | * @dev Transfers ownership of the contract to a new account (`newOwner`). 71 | */ 72 | function _transferOwnership(address newOwner) internal { 73 | require(newOwner != address(0), "Ownable: new owner is the zero address"); 74 | emit OwnershipTransferred(_owner, newOwner); 75 | _owner = newOwner; 76 | } 77 | } 78 | 79 | // File: openzeppelin-solidity-2.3.0/contracts/utils/ReentrancyGuard.sol 80 | 81 | pragma solidity ^0.5.0; 82 | 83 | /** 84 | * @dev Contract module that helps prevent reentrant calls to a function. 85 | * 86 | * Inheriting from `ReentrancyGuard` will make the `nonReentrant` modifier 87 | * available, which can be aplied to functions to make sure there are no nested 88 | * (reentrant) calls to them. 89 | * 90 | * Note that because there is a single `nonReentrant` guard, functions marked as 91 | * `nonReentrant` may not call one another. This can be worked around by making 92 | * those functions `private`, and then adding `external` `nonReentrant` entry 93 | * points to them. 94 | */ 95 | contract ReentrancyGuard { 96 | /// @dev counter to allow mutex lock with only one SSTORE operation 97 | uint256 private _guardCounter; 98 | 99 | constructor () internal { 100 | // The counter starts at one to prevent changing it from zero to a non-zero 101 | // value, which is a more expensive operation. 102 | _guardCounter = 1; 103 | } 104 | 105 | /** 106 | * @dev Prevents a contract from calling itself, directly or indirectly. 107 | * Calling a `nonReentrant` function from another `nonReentrant` 108 | * function is not supported. It is possible to prevent this from happening 109 | * by making the `nonReentrant` function external, and make it call a 110 | * `private` function that does the actual work. 111 | */ 112 | modifier nonReentrant() { 113 | _guardCounter += 1; 114 | uint256 localCounter = _guardCounter; 115 | _; 116 | require(localCounter == _guardCounter, "ReentrancyGuard: reentrant call"); 117 | } 118 | } 119 | 120 | // File: openzeppelin-solidity-2.3.0/contracts/math/SafeMath.sol 121 | 122 | pragma solidity ^0.5.0; 123 | 124 | /** 125 | * @dev Wrappers over Solidity's arithmetic operations with added overflow 126 | * checks. 127 | * 128 | * Arithmetic operations in Solidity wrap on overflow. This can easily result 129 | * in bugs, because programmers usually assume that an overflow raises an 130 | * error, which is the standard behavior in high level programming languages. 131 | * `SafeMath` restores this intuition by reverting the transaction when an 132 | * operation overflows. 133 | * 134 | * Using this library instead of the unchecked operations eliminates an entire 135 | * class of bugs, so it's recommended to use it always. 136 | */ 137 | library SafeMath { 138 | /** 139 | * @dev Returns the addition of two unsigned integers, reverting on 140 | * overflow. 141 | * 142 | * Counterpart to Solidity's `+` operator. 143 | * 144 | * Requirements: 145 | * - Addition cannot overflow. 146 | */ 147 | function add(uint256 a, uint256 b) internal pure returns (uint256) { 148 | uint256 c = a + b; 149 | require(c >= a, "SafeMath: addition overflow"); 150 | 151 | return c; 152 | } 153 | 154 | /** 155 | * @dev Returns the subtraction of two unsigned integers, reverting on 156 | * overflow (when the result is negative). 157 | * 158 | * Counterpart to Solidity's `-` operator. 159 | * 160 | * Requirements: 161 | * - Subtraction cannot overflow. 162 | */ 163 | function sub(uint256 a, uint256 b) internal pure returns (uint256) { 164 | require(b <= a, "SafeMath: subtraction overflow"); 165 | uint256 c = a - b; 166 | 167 | return c; 168 | } 169 | 170 | /** 171 | * @dev Returns the multiplication of two unsigned integers, reverting on 172 | * overflow. 173 | * 174 | * Counterpart to Solidity's `*` operator. 175 | * 176 | * Requirements: 177 | * - Multiplication cannot overflow. 178 | */ 179 | function mul(uint256 a, uint256 b) internal pure returns (uint256) { 180 | // Gas optimization: this is cheaper than requiring 'a' not being zero, but the 181 | // benefit is lost if 'b' is also tested. 182 | // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522 183 | if (a == 0) { 184 | return 0; 185 | } 186 | 187 | uint256 c = a * b; 188 | require(c / a == b, "SafeMath: multiplication overflow"); 189 | 190 | return c; 191 | } 192 | 193 | /** 194 | * @dev Returns the integer division of two unsigned integers. Reverts on 195 | * division by zero. The result is rounded towards zero. 196 | * 197 | * Counterpart to Solidity's `/` operator. Note: this function uses a 198 | * `revert` opcode (which leaves remaining gas untouched) while Solidity 199 | * uses an invalid opcode to revert (consuming all remaining gas). 200 | * 201 | * Requirements: 202 | * - The divisor cannot be zero. 203 | */ 204 | function div(uint256 a, uint256 b) internal pure returns (uint256) { 205 | // Solidity only automatically asserts when dividing by 0 206 | require(b > 0, "SafeMath: division by zero"); 207 | uint256 c = a / b; 208 | // assert(a == b * c + a % b); // There is no case in which this doesn't hold 209 | 210 | return c; 211 | } 212 | 213 | /** 214 | * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), 215 | * Reverts when dividing by zero. 216 | * 217 | * Counterpart to Solidity's `%` operator. This function uses a `revert` 218 | * opcode (which leaves remaining gas untouched) while Solidity uses an 219 | * invalid opcode to revert (consuming all remaining gas). 220 | * 221 | * Requirements: 222 | * - The divisor cannot be zero. 223 | */ 224 | function mod(uint256 a, uint256 b) internal pure returns (uint256) { 225 | require(b != 0, "SafeMath: modulo by zero"); 226 | return a % b; 227 | } 228 | } 229 | 230 | // File: contracts/SafeToken.sol 231 | 232 | pragma solidity ^0.5.16; 233 | 234 | interface ERC20Interface { 235 | function balanceOf(address user) external view returns (uint256); 236 | } 237 | 238 | library SafeToken { 239 | function myBalance(address token) internal view returns (uint256) { 240 | return ERC20Interface(token).balanceOf(address(this)); 241 | } 242 | 243 | function balanceOf(address token, address user) internal view returns (uint256) { 244 | return ERC20Interface(token).balanceOf(user); 245 | } 246 | 247 | function safeApprove(address token, address to, uint256 value) internal { 248 | // bytes4(keccak256(bytes('approve(address,uint256)'))); 249 | (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x095ea7b3, to, value)); 250 | require(success && (data.length == 0 || abi.decode(data, (bool))), "!safeApprove"); 251 | } 252 | 253 | function safeTransfer(address token, address to, uint256 value) internal { 254 | // bytes4(keccak256(bytes('transfer(address,uint256)'))); 255 | (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0xa9059cbb, to, value)); 256 | require(success && (data.length == 0 || abi.decode(data, (bool))), "!safeTransfer"); 257 | } 258 | 259 | function safeTransferFrom(address token, address from, address to, uint256 value) internal { 260 | // bytes4(keccak256(bytes('transferFrom(address,address,uint256)'))); 261 | (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x23b872dd, from, to, value)); 262 | require(success && (data.length == 0 || abi.decode(data, (bool))), "!safeTransferFrom"); 263 | } 264 | 265 | function safeTransferETH(address to, uint256 value) internal { 266 | (bool success, ) = to.call.value(value)(new bytes(0)); 267 | require(success, "!safeTransferETH"); 268 | } 269 | } 270 | 271 | // File: contracts/Strategy.sol 272 | 273 | pragma solidity ^0.5.16; 274 | 275 | interface Strategy { 276 | 277 | /// @dev Execute worker strategy. Take LP tokens + debt token. Return LP tokens or debt token. 278 | /// @param user The original user that is interacting with the operator. 279 | /// @param borrowToken The token user want borrow. 280 | /// @param borrow The amount user borrow from bank. 281 | /// @param debt The user's total debt, for better decision making context. 282 | /// @param data Extra calldata information passed along to this strategy. 283 | /// @return token and amount need transfer back. 284 | function execute(address user, address borrowToken, uint256 borrow, uint256 debt, bytes calldata data) external payable; 285 | 286 | } 287 | 288 | // File: contracts/interfaces/IWHT.sol 289 | 290 | pragma solidity ^0.5.16; 291 | 292 | interface IWHT { 293 | function balanceOf(address user) external returns (uint); 294 | 295 | function approve(address to, uint value) external returns (bool); 296 | 297 | function transfer(address to, uint value) external returns (bool); 298 | 299 | function deposit() external payable; 300 | 301 | function withdraw(uint) external; 302 | } 303 | 304 | // File: contracts/interfaces/IMdexPair.sol 305 | 306 | pragma solidity ^0.5.16; 307 | 308 | interface IMdexPair { 309 | event Approval(address indexed owner, address indexed spender, uint value); 310 | event Transfer(address indexed from, address indexed to, uint value); 311 | 312 | function name() external pure returns (string memory); 313 | 314 | function symbol() external pure returns (string memory); 315 | 316 | function decimals() external pure returns (uint8); 317 | 318 | function totalSupply() external view returns (uint); 319 | 320 | function balanceOf(address owner) external view returns (uint); 321 | 322 | function allowance(address owner, address spender) external view returns (uint); 323 | 324 | function approve(address spender, uint value) external returns (bool); 325 | 326 | function transfer(address to, uint value) external returns (bool); 327 | 328 | function transferFrom(address from, address to, uint value) external returns (bool); 329 | 330 | function DOMAIN_SEPARATOR() external view returns (bytes32); 331 | 332 | function PERMIT_TYPEHASH() external pure returns (bytes32); 333 | 334 | function nonces(address owner) external view returns (uint); 335 | 336 | function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external; 337 | 338 | event Mint(address indexed sender, uint amount0, uint amount1); 339 | event Burn(address indexed sender, uint amount0, uint amount1, address indexed to); 340 | event Swap( 341 | address indexed sender, 342 | uint amount0In, 343 | uint amount1In, 344 | uint amount0Out, 345 | uint amount1Out, 346 | address indexed to 347 | ); 348 | event Sync(uint112 reserve0, uint112 reserve1); 349 | 350 | function MINIMUM_LIQUIDITY() external pure returns (uint); 351 | 352 | function factory() external view returns (address); 353 | 354 | function token0() external view returns (address); 355 | 356 | function token1() external view returns (address); 357 | 358 | function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast); 359 | 360 | function price0CumulativeLast() external view returns (uint); 361 | 362 | function price1CumulativeLast() external view returns (uint); 363 | 364 | function kLast() external view returns (uint); 365 | 366 | function mint(address to) external returns (uint liquidity); 367 | 368 | function burn(address to) external returns (uint amount0, uint amount1); 369 | 370 | function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external; 371 | 372 | function skim(address to) external; 373 | 374 | function sync() external; 375 | 376 | function price(address token, uint256 baseDecimal) external view returns (uint256); 377 | 378 | function initialize(address, address) external; 379 | } 380 | 381 | // File: contracts/interfaces/IMdexRouter.sol 382 | 383 | pragma solidity ^0.5.16; 384 | 385 | interface IMdexRouter { 386 | function factory() external pure returns (address); 387 | 388 | function WHT() external pure returns (address); 389 | 390 | function swapMining() external pure returns (address); 391 | 392 | function addLiquidity( 393 | address tokenA, 394 | address tokenB, 395 | uint amountADesired, 396 | uint amountBDesired, 397 | uint amountAMin, 398 | uint amountBMin, 399 | address to, 400 | uint deadline 401 | ) external returns (uint amountA, uint amountB, uint liquidity); 402 | 403 | function addLiquidityETH( 404 | address token, 405 | uint amountTokenDesired, 406 | uint amountTokenMin, 407 | uint amountETHMin, 408 | address to, 409 | uint deadline 410 | ) external payable returns (uint amountToken, uint amountETH, uint liquidity); 411 | 412 | function removeLiquidity( 413 | address tokenA, 414 | address tokenB, 415 | uint liquidity, 416 | uint amountAMin, 417 | uint amountBMin, 418 | address to, 419 | uint deadline 420 | ) external returns (uint amountA, uint amountB); 421 | 422 | function removeLiquidityETH( 423 | address token, 424 | uint liquidity, 425 | uint amountTokenMin, 426 | uint amountETHMin, 427 | address to, 428 | uint deadline 429 | ) external returns (uint amountToken, uint amountETH); 430 | 431 | function removeLiquidityWithPermit( 432 | address tokenA, 433 | address tokenB, 434 | uint liquidity, 435 | uint amountAMin, 436 | uint amountBMin, 437 | address to, 438 | uint deadline, 439 | bool approveMax, uint8 v, bytes32 r, bytes32 s 440 | ) external returns (uint amountA, uint amountB); 441 | 442 | function removeLiquidityETHWithPermit( 443 | address token, 444 | uint liquidity, 445 | uint amountTokenMin, 446 | uint amountETHMin, 447 | address to, 448 | uint deadline, 449 | bool approveMax, uint8 v, bytes32 r, bytes32 s 450 | ) external returns (uint amountToken, uint amountETH); 451 | 452 | function swapExactTokensForTokens( 453 | uint amountIn, 454 | uint amountOutMin, 455 | address[] calldata path, 456 | address to, 457 | uint deadline 458 | ) external returns (uint[] memory amounts); 459 | 460 | function swapTokensForExactTokens( 461 | uint amountOut, 462 | uint amountInMax, 463 | address[] calldata path, 464 | address to, 465 | uint deadline 466 | ) external returns (uint[] memory amounts); 467 | 468 | function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline) 469 | external 470 | payable 471 | returns (uint[] memory amounts); 472 | 473 | function swapTokensForExactETH(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline) 474 | external 475 | returns (uint[] memory amounts); 476 | 477 | function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline) 478 | external 479 | returns (uint[] memory amounts); 480 | 481 | function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline) 482 | external 483 | payable 484 | returns (uint[] memory amounts); 485 | 486 | function quote(uint256 amountA, uint256 reserveA, uint256 reserveB) external view returns (uint256 amountB); 487 | 488 | function getAmountOut(uint256 amountIn, uint256 reserveIn, uint256 reserveOut) external view returns (uint256 amountOut); 489 | 490 | function getAmountIn(uint256 amountOut, uint256 reserveIn, uint256 reserveOut) external view returns (uint256 amountIn); 491 | 492 | function getAmountsOut(uint256 amountIn, address[] calldata path) external view returns (uint256[] memory amounts); 493 | 494 | function getAmountsIn(uint256 amountOut, address[] calldata path) external view returns (uint256[] memory amounts); 495 | 496 | function removeLiquidityETHSupportingFeeOnTransferTokens( 497 | address token, 498 | uint liquidity, 499 | uint amountTokenMin, 500 | uint amountETHMin, 501 | address to, 502 | uint deadline 503 | ) external returns (uint amountETH); 504 | 505 | function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens( 506 | address token, 507 | uint liquidity, 508 | uint amountTokenMin, 509 | uint amountETHMin, 510 | address to, 511 | uint deadline, 512 | bool approveMax, uint8 v, bytes32 r, bytes32 s 513 | ) external returns (uint amountETH); 514 | 515 | function swapExactTokensForTokensSupportingFeeOnTransferTokens( 516 | uint amountIn, 517 | uint amountOutMin, 518 | address[] calldata path, 519 | address to, 520 | uint deadline 521 | ) external; 522 | 523 | function swapExactETHForTokensSupportingFeeOnTransferTokens( 524 | uint amountOutMin, 525 | address[] calldata path, 526 | address to, 527 | uint deadline 528 | ) external payable; 529 | 530 | function swapExactTokensForETHSupportingFeeOnTransferTokens( 531 | uint amountIn, 532 | uint amountOutMin, 533 | address[] calldata path, 534 | address to, 535 | uint deadline 536 | ) external; 537 | } 538 | 539 | // File: contracts/interfaces/IMdexFactory.sol 540 | 541 | /** 542 | *Submitted for verification at hecoinfo.com on 2021-02-25 543 | */ 544 | 545 | pragma solidity ^0.5.16; 546 | 547 | interface IMdexFactory { 548 | event PairCreated(address indexed token0, address indexed token1, address pair, uint); 549 | 550 | function feeTo() external view returns (address); 551 | 552 | function feeToSetter() external view returns (address); 553 | 554 | function feeToRate() external view returns (uint256); 555 | 556 | function initCodeHash() external view returns (bytes32); 557 | 558 | function getPair(address tokenA, address tokenB) external view returns (address pair); 559 | 560 | function allPairs(uint) external view returns (address pair); 561 | 562 | function allPairsLength() external view returns (uint); 563 | 564 | function createPair(address tokenA, address tokenB) external returns (address pair); 565 | 566 | function setFeeTo(address) external; 567 | 568 | function setFeeToSetter(address) external; 569 | 570 | function setFeeToRate(uint256) external; 571 | 572 | function setInitCodeHash(bytes32) external; 573 | 574 | function sortTokens(address tokenA, address tokenB) external pure returns (address token0, address token1); 575 | 576 | function pairFor(address tokenA, address tokenB) external view returns (address pair); 577 | 578 | function getReserves(address tokenA, address tokenB) external view returns (uint256 reserveA, uint256 reserveB); 579 | 580 | function quote(uint256 amountA, uint256 reserveA, uint256 reserveB) external pure returns (uint256 amountB); 581 | 582 | function getAmountOut(uint256 amountIn, uint256 reserveIn, uint256 reserveOut) external view returns (uint256 amountOut); 583 | 584 | function getAmountIn(uint256 amountOut, uint256 reserveIn, uint256 reserveOut) external view returns (uint256 amountIn); 585 | 586 | function getAmountsOut(uint256 amountIn, address[] calldata path) external view returns (uint256[] memory amounts); 587 | 588 | function getAmountsIn(uint256 amountOut, address[] calldata path) external view returns (uint256[] memory amounts); 589 | } 590 | 591 | // File: contracts/MdxStrategyWithdrawMinimizeTrading.sol 592 | 593 | pragma solidity ^0.5.16; 594 | 595 | 596 | 597 | 598 | 599 | 600 | 601 | 602 | 603 | 604 | contract MdxStrategyWithdrawMinimizeTrading is Ownable, ReentrancyGuard, Strategy { 605 | using SafeToken for address; 606 | using SafeMath for uint256; 607 | 608 | IMdexFactory public factory; 609 | IMdexRouter public router; 610 | address public wht; 611 | 612 | /// @dev Create a new withdraw minimize trading strategy instance for mdx. 613 | /// @param _router The mdx router smart contract. 614 | constructor(IMdexRouter _router) public { 615 | factory = IMdexFactory(_router.factory()); 616 | router = _router; 617 | wht = _router.WHT(); 618 | } 619 | 620 | /// @dev Execute worker strategy. Take LP tokens. Return debt token + token want back. 621 | /// @param user User address to withdraw liquidity. 622 | /// @param borrowToken The token user borrow from bank. 623 | /// @param debt User's debt amount. 624 | /// @param data Extra calldata information passed along to this strategy. 625 | function execute(address user, address borrowToken, uint256 /* borrow */, uint256 debt, bytes calldata data) 626 | external 627 | payable 628 | nonReentrant 629 | { 630 | // 1. Find out lpToken and liquidity. 631 | // whichWantBack: 0:token0;1:token1;2:token what surplus. 632 | (address token0, address token1, uint whichWantBack) = abi.decode(data, (address, address, uint)); 633 | 634 | // is borrowToken is ht. 635 | bool isBorrowHt = borrowToken == address(0); 636 | require(borrowToken == token0 || borrowToken == token1 || isBorrowHt, "borrowToken not token0 and token1"); 637 | // the relative token when token0 or token1 is ht. 638 | address htRelative = address(0); 639 | { 640 | if (token0 == address(0)){ 641 | token0 = wht; 642 | htRelative = token1; 643 | } 644 | if (token1 == address(0)){ 645 | token1 = wht; 646 | htRelative = token0; 647 | } 648 | } 649 | address tokenUserWant = whichWantBack == uint(0) ? token0 : token1; 650 | 651 | IMdexPair lpToken = IMdexPair(factory.getPair(token0, token1)); 652 | token0 = lpToken.token0(); 653 | token1 = lpToken.token1(); 654 | 655 | { 656 | lpToken.approve(address(router), uint256(-1)); 657 | router.removeLiquidity(token0, token1, lpToken.balanceOf(address(this)), 0, 0, address(this), now); 658 | } 659 | { 660 | borrowToken = isBorrowHt ? wht : borrowToken; 661 | address tokenRelative = borrowToken == token0 ? token1 : token0; 662 | 663 | swapIfNeed(borrowToken, tokenRelative, debt); 664 | 665 | if (isBorrowHt) { 666 | IWHT(wht).withdraw(debt); 667 | SafeToken.safeTransferETH(msg.sender, debt); 668 | } else { 669 | SafeToken.safeTransfer(borrowToken, msg.sender, debt); 670 | } 671 | } 672 | 673 | // 2. swap remaining token to what user want. 674 | if (whichWantBack != uint(2)) { 675 | address tokenAnother = tokenUserWant == token0 ? token1 : token0; 676 | uint256 anotherAmount = tokenAnother.myBalance(); 677 | if(anotherAmount > 0){ 678 | tokenAnother.safeApprove(address(router), 0); 679 | tokenAnother.safeApprove(address(router), uint256(-1)); 680 | 681 | address[] memory path = new address[](2); 682 | path[0] = tokenAnother; 683 | path[1] = tokenUserWant; 684 | router.swapExactTokensForTokens(anotherAmount, 0, path, address(this), now); 685 | } 686 | } 687 | 688 | // 3. send all tokens back. 689 | if (htRelative == address(0)) { 690 | token0.safeTransfer(user, token0.myBalance()); 691 | token1.safeTransfer(user, token1.myBalance()); 692 | } else { 693 | safeUnWrapperAndAllSend(wht, user); 694 | safeUnWrapperAndAllSend(htRelative, user); 695 | } 696 | } 697 | 698 | /// swap if need. 699 | function swapIfNeed(address borrowToken, address tokenRelative, uint256 debt) internal { 700 | uint256 borrowTokenAmount = borrowToken.myBalance(); 701 | if (debt > borrowTokenAmount) { 702 | tokenRelative.safeApprove(address(router), 0); 703 | tokenRelative.safeApprove(address(router), uint256(-1)); 704 | 705 | uint256 remainingDebt = debt.sub(borrowTokenAmount); 706 | address[] memory path = new address[](2); 707 | path[0] = tokenRelative; 708 | path[1] = borrowToken; 709 | router.swapTokensForExactTokens(remainingDebt, tokenRelative.myBalance(), path, address(this), now); 710 | } 711 | } 712 | 713 | /// get token balance, if is WHT un wrapper to HT and send to 'to' 714 | function safeUnWrapperAndAllSend(address token, address to) internal { 715 | uint256 total = SafeToken.myBalance(token); 716 | if (total > 0) { 717 | if (token == wht) { 718 | IWHT(wht).withdraw(total); 719 | SafeToken.safeTransferETH(to, total); 720 | } else { 721 | SafeToken.safeTransfer(token, to, total); 722 | } 723 | } 724 | } 725 | 726 | /// @dev Recover ERC20 tokens that were accidentally sent to this smart contract. 727 | /// @param token The token contract. Can be anything. This contract should not hold ERC20 tokens. 728 | /// @param to The address to send the tokens to. 729 | /// @param value The number of tokens to transfer to `to`. 730 | function recover(address token, address to, uint256 value) external onlyOwner nonReentrant { 731 | token.safeTransfer(to, value); 732 | } 733 | 734 | function() external payable {} 735 | } 736 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # contract 2 | Smart Contract of Pilot finance lab 3 | --------------------------------------------------------------------------------