From 3c6e0b99137ba93feb1759c9fe8804ea75957296 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Str=C3=A1nsk=C3=BD?= Date: Tue, 18 Feb 2020 08:42:09 +0100 Subject: [PATCH] Integration of Uniswap #1 --- contracts/DCZK.sol | 151 +++++++++++++++--------- migrations/{2_deploy.js => 1_deploy.js} | 5 +- migrations/1_initial_migration.js | 6 - package.json | 2 + truffle-config.js | 12 +- 5 files changed, 103 insertions(+), 73 deletions(-) rename migrations/{2_deploy.js => 1_deploy.js} (58%) delete mode 100644 migrations/1_initial_migration.js diff --git a/contracts/DCZK.sol b/contracts/DCZK.sol index 97e16f6..fb9ddea 100644 --- a/contracts/DCZK.sol +++ b/contracts/DCZK.sol @@ -2,7 +2,7 @@ pragma solidity ^0.5.16; -interface IERC20 { +contract IERC20 { function totalSupply() external view returns (uint256); function balanceOf(address account) external view returns (uint256); function transfer(address recipient, uint256 amount) external returns (bool); @@ -13,16 +13,16 @@ interface IERC20 { event Approval(address indexed owner, address indexed spender, uint256 value); } -interface IVat { +contract Vat { function hope(address) external; } -interface IDaiJoin { +contract DaiJoin { function join(address, uint) external; function exit(address, uint) external; } -interface IPot { +contract Pot { function chi() external view returns (uint256); function rho() external view returns (uint256); function dsr() external view returns (uint256); @@ -32,6 +32,16 @@ interface IPot { function exit(uint256) external; } +contract UniswapFactoryInterface { + function getExchange(address token) external view returns (address exchange); +} + +contract UniswapExchangeInterface { + function getEthToTokenInputPrice(uint256 eth_sold) external view returns (uint256 tokens_bought); + function ethToTokenSwapInput(uint256 min_tokens, uint256 deadline) external payable returns (uint256 tokens_bought); + function tokenToEthTransferInput(uint256 tokens_sold, uint256 min_eth, uint256 deadline, address recipient) external returns (uint256 eth_bought); +} + contract DCZK is IERC20 { // --- ERC20 Events --- @@ -46,32 +56,38 @@ contract DCZK is IERC20 { event Sell(address indexed seller, uint256 dczk, uint256 dai); event AddLiquidity(uint256 rate, uint256 amount); event RateUpdate(uint256 rate, address caller); + event BuyWithEther(address indexed buyer, uint256 dczk, uint256 dai, uint256 eth); + event SellForEther(address indexed seller, uint256 dczk, uint256 dai, uint256 eth); + event Debug(uint256 value); // --- ERC20 basic vars --- - string private _name = "dCZK Test v0.2"; - string private _symbol = "dCZK02"; - uint8 private _decimals = 18; + string public constant name = "dCZK Test v0.2.1"; + string public constant symbol = "dCZK021"; + uint8 public constant decimals = 18; mapping (address => uint256) private _balances; mapping (address => mapping (address => uint256)) private _allowances; uint256 private _totalSupply; // --- Data --- - IERC20 public depositToken; - IVat public vat; - IDaiJoin public daiJoin; - IPot public pot; + IERC20 public depositToken; + Vat public vat; + DaiJoin public daiJoin; + Pot public pot; + UniswapFactoryInterface public uniswapFactory; + UniswapExchangeInterface public depositTokenExchange; + uint256 public lrho; uint256 public lchi; - uint256 public rate = 22000000000000000000; // basic CZKDAI rate - uint256 public maxRate; - uint256 public totalVolume; - uint public lastUpdate; + uint256 public rate = 22000000000000000000; // fixed oracleAdress - will be not included in mainnet release - address public oracleAddress = 0x89188bE35B16AF852dC0A4a9e47e0cA871fadf9a; - - uint16 constant fee = 400; // 0.25% + address public constant oracleAddress = 0x89188bE35B16AF852dC0A4a9e47e0cA871fadf9a; + uint256 public constant fee = 400; // 0.25% + uint256 constant uniswapDeadline = 900 * 60; // 15 minutes + uint256 public maxRate; + uint256 public totalVolume; + uint256 public lastUpdate; struct Thread { uint next; uint amount; @@ -80,18 +96,22 @@ contract DCZK is IERC20 { // --- Init --- - constructor (address _dai, address _vat, address _daiJoin, address _pot) public { + constructor (address _dai, address _vat, address _daiJoin, address _pot, address _uniswapFactory) public { // set DAI address depositToken = IERC20(_dai); // DSR - DAI Savings Rate - daiJoin = IDaiJoin(_daiJoin); - vat = IVat(_vat); - pot = IPot(_pot); + daiJoin = DaiJoin(_daiJoin); + vat = Vat(_vat); + pot = Pot(_pot); // pot = new Pot(address(this)); // MakerDAO DSR `pot` (for testing purposes) vat.hope(address(daiJoin)); vat.hope(address(pot)); + // Uniswap + uniswapFactory = UniswapFactoryInterface(_uniswapFactory); + depositTokenExchange = UniswapExchangeInterface(uniswapFactory.getExchange(address(depositToken))); + depositToken.approve(address(daiJoin), uint(-1)); } @@ -99,7 +119,7 @@ contract DCZK is IERC20 { // Taken from official DSR contract: // https://github.com/makerdao/dss/blob/master/src/pot.sol - uint constant RAY = 10 ** 27; + /*uint constant RAY = 10 ** 27; function rpow(uint x, uint n, uint base) internal pure returns (uint z) { assembly { switch x case 0 {switch n case 0 {z := base} default {z := 0}} @@ -122,7 +142,7 @@ contract DCZK is IERC20 { } } } - } + }*/ function add(uint x, uint y) internal pure returns (uint z) { require((z = x + y) >= x); @@ -157,18 +177,6 @@ contract DCZK is IERC20 { // --- ERC20 Token --- - function name() public view returns (string memory) { - return _name; - } - - function symbol() public view returns (string memory) { - return _symbol; - } - - function decimals() public view returns (uint8) { - return _decimals; - } - function totalSupply() public view returns (uint256) { return rmul(_chi(), _totalSupply); } @@ -268,7 +276,7 @@ contract DCZK is IERC20 { return pot.pie(address(this)); } - // --- Minting (internal) --- + // --- Minting and burning (internal) --- function _mint(address dst, uint256 czk, uint256 dai) private { require(dst != address(0), "ERC20: mint to the zero address"); @@ -287,9 +295,7 @@ contract DCZK is IERC20 { emit Mint(dst, czk, spie); } - // --- Burning (internal) --- - - function _burn(address src, uint256 czk, uint256 dai) private { + function _burn(address src, uint256 czk, uint256 dai, address dst) private { require(src != address(0), "ERC20: burn from the zero address"); require(balanceOf(src) >= czk, "dczk/insufficient-balance"); @@ -298,17 +304,18 @@ contract DCZK is IERC20 { _balances[src] = sub(_balances[src], spie, "ERC20: burn amount exceeds balance"); _totalSupply = sub(_totalSupply, spie); + emit Transfer(src, address(0), czk); + emit Burn(src, czk, spie); uint pie = rdivup(dai, chi); if (pie != 0) { pot.exit(pie); - daiJoin.exit(msg.sender, rmul(chi, pie)); + daiJoin.exit(dst, rmul(chi, pie)); + } + if (dst != address(this)) { + _approve(src, dst, sub(_allowances[src][address(this)], czk, "ERC20: burn amount exceeds allowance")); } - - _approve(src, msg.sender, sub(_allowances[src][address(this)], czk, "ERC20: burn amount exceeds allowance")); - emit Transfer(src, address(0), czk); emit TransferPrincipal(src, address(0), spie, czk); - emit Burn(src, czk, spie); } // --- DEX Decentralized exchange --- @@ -341,14 +348,10 @@ contract DCZK is IERC20 { txs[rate].amount += amount; } - function buy(uint256 amount) public { + function _buyAndMint(uint256 amount) private returns(uint256 converted) { require(rate != 0, "rate cannot be 0"); - require(depositToken.allowance(msg.sender, address(this)) >= amount, "dczk/insufficient-allowance"); _drip(); - // transfer DAI to this contract - depositToken.transferFrom(msg.sender, address(this), amount); - // calculate fee - 0.25% uint _fee = amount / fee; uint rest = amount - _fee; @@ -358,18 +361,18 @@ contract DCZK is IERC20 { _addLiquidity(rest); // convert to stablecoin amount - uint _converted = (rest * rate) / 10 ** 18; + converted = (rest * rate) / 10 ** 18; // save amount to total volume - totalVolume += _converted; + totalVolume += converted; // mint tokens - _mint(address(msg.sender), _converted, rest); + _mint(address(msg.sender), converted, rest); - emit Buy(msg.sender, _converted, rest); + emit Buy(msg.sender, converted, rest); } - function sell(uint256 amount) public { + function _sell(uint256 amount) private returns(uint256 deposit) { require(maxRate != 0, "max_rate cannot be 0"); require(allowance(msg.sender, address(this)) >= amount, "czk/insufficient-allowance"); _drip(); @@ -379,7 +382,7 @@ contract DCZK is IERC20 { // calculate rate & deposit uint _amount = amount; - uint deposit = 0; + deposit = 0; uint currentRate = maxRate; while (_amount > 0) { uint full = (txs[currentRate].amount * currentRate) / 10 ** 18; @@ -398,9 +401,6 @@ contract DCZK is IERC20 { currentRate = txs[currentRate].next; } } - // burn coins - _burn(msg.sender, amount, deposit); - emit Sell(msg.sender, amount, deposit); } @@ -428,6 +428,39 @@ contract DCZK is IERC20 { } } + function buy(uint256 amount) public { + require(depositToken.allowance(msg.sender, address(this)) >= amount, "dczk/insufficient-allowance"); + depositToken.transferFrom(msg.sender, address(this), amount); + _buyAndMint(amount); + } + + function sell(uint256 amount) public { + uint256 deposit = _sell(amount); + _burn(msg.sender, amount, deposit, msg.sender); + } + + // --- Uniswap Integration --- + + function buyWithEther(uint256 minTokens) public payable returns(uint256 converted) { + uint256 deposit = depositTokenExchange.ethToTokenSwapInput.value(msg.value)(minTokens, now + uniswapDeadline); + converted = _buyAndMint(deposit); + emit BuyWithEther(msg.sender, converted, deposit, uint256(msg.value)); + } + + function sellForEther(uint256 amount, uint256 minEth) public returns(uint256 eth) { + uint256 deposit = _sell(amount); + _burn(msg.sender, amount, deposit, address(this)); + depositToken.approve(address(depositTokenExchange), deposit); + eth = depositTokenExchange.tokenToEthTransferInput(deposit, minEth, now + uniswapDeadline, msg.sender); + emit SellForEther(msg.sender, amount, deposit, eth); + } + + function () external payable { + require(msg.data.length == 0); + uint256 price = depositTokenExchange.getEthToTokenInputPrice(msg.value); + buyWithEther(sub(price, price / 40)); + } + // --- Oracle --- function updateRate(uint _rate) public { diff --git a/migrations/2_deploy.js b/migrations/1_deploy.js similarity index 58% rename from migrations/2_deploy.js rename to migrations/1_deploy.js index 780092f..190bc29 100644 --- a/migrations/2_deploy.js +++ b/migrations/1_deploy.js @@ -3,9 +3,10 @@ const DCZK = artifacts.require('DCZK') module.exports = async function (deployer) { await deployer.deploy( DCZK, - '0x4F96Fe3b7A6Cf9725f59d353F723c1bDb64CA6Aa', // DAI + '0x4F96Fe3b7A6Cf9725f59d353F723c1bDb64CA6Aa', // DAI token '0xbA987bDB501d131f766fEe8180Da5d81b34b69d9', // DAI/vat '0x5AA71a3ae1C0bd6ac27A1f28e1415fFFB6F15B8c', // DAI/daiJoin - '0xEA190DBDC7adF265260ec4dA6e9675Fd4f5A78bb' // DAi/pot + '0xEA190DBDC7adF265260ec4dA6e9675Fd4f5A78bb', // DAi/pot + '0xD3E51Ef092B2845f10401a0159B2B96e8B6c3D30' // Uniswap Factory ) } diff --git a/migrations/1_initial_migration.js b/migrations/1_initial_migration.js deleted file mode 100644 index 163042f..0000000 --- a/migrations/1_initial_migration.js +++ /dev/null @@ -1,6 +0,0 @@ -/* globals artifacts */ -const Migrations = artifacts.require('Migrations') - -module.exports = function (deployer) { - deployer.deploy(Migrations) -} diff --git a/package.json b/package.json index 43f5e08..336dbda 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,8 @@ "description": "", "main": "truffle-config.js", "scripts": { + "deploy": "truffle migrate --network kovan", + "verify": "truffle run verify DCZK --network kovan", "test": "echo \"Error: no test specified\" && exit 1" }, "author": "", diff --git a/truffle-config.js b/truffle-config.js index 1d8b66e..021d8a3 100644 --- a/truffle-config.js +++ b/truffle-config.js @@ -31,13 +31,13 @@ module.exports = { solc: { // version: "0.5.8", // Fetch exact version from solc-bin (default: truffle's version) // docker: true, // Use "0.5.1" you've installed locally with docker (default: false) - // settings: { // See the solidity docs for advice about optimization and evmVersion - // optimizer: { - // enabled: false, - // runs: 200 - // }, + settings: { // See the solidity docs for advice about optimization and evmVersion + optimizer: { + enabled: true, + runs: 100000 + } // evmVersion: "byzantium" - // } + } } }, plugins: [