├── .secret ├── .gitignore ├── app ├── .gitignore ├── webpack.config.js ├── package.json └── src │ ├── SimpleTokenABI.json │ ├── index.html │ └── index.js ├── readme_image1.jpg ├── readme_image2.jpg ├── readme_image3.PNG ├── migrations ├── 1_initial_migration.js └── 2_deploy_contracts.js ├── contracts ├── UniswapFactoryInterface.sol ├── DonateTokenInterface.sol ├── TokenswapInterface.sol ├── KyberNetworkProxyInterface.sol ├── CTokenInterface.sol ├── Migrations.sol ├── EIP20Interface.sol ├── IERC165.sol ├── UniswapExchangeInterface.sol ├── Context.sol ├── IERC721Receiver.sol ├── Counters.sol ├── ERC165.sol ├── Address.sol ├── IERC721.sol ├── GrowdropCall.sol ├── SafeMath.sol ├── DonateToken.sol ├── ERC721.sol ├── Tokenswap.sol └── Growdrop.sol ├── LICENSE ├── truffle-config.js └── README.md /.secret: -------------------------------------------------------------------------------- 1 | //mnemonic key -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | build 3 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | -------------------------------------------------------------------------------- /readme_image1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lite/Growdrop/master/readme_image1.jpg -------------------------------------------------------------------------------- /readme_image2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lite/Growdrop/master/readme_image2.jpg -------------------------------------------------------------------------------- /readme_image3.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lite/Growdrop/master/readme_image3.PNG -------------------------------------------------------------------------------- /migrations/1_initial_migration.js: -------------------------------------------------------------------------------- 1 | const Migrations = artifacts.require("Migrations"); 2 | 3 | module.exports = function(deployer) { 4 | deployer.deploy(Migrations); 5 | }; 6 | -------------------------------------------------------------------------------- /contracts/UniswapFactoryInterface.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.11; 2 | 3 | contract UniswapFactoryInterface { 4 | /** 5 | * @dev UniswapFactory interface 6 | */ 7 | function createExchange(address token) external returns (address exchange); 8 | function getExchange(address token) external view returns (address exchange); 9 | } -------------------------------------------------------------------------------- /contracts/DonateTokenInterface.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.11; 2 | 3 | contract DonateTokenInterface { 4 | /** 5 | * @dev DonateToken interface 6 | */ 7 | mapping(uint256 => address) public DonateIdOwner; 8 | function mint(address supporter, address beneficiary, address token, uint256 amount, uint256 donateId) public returns (uint256); 9 | } -------------------------------------------------------------------------------- /app/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const CopyWebpackPlugin = require("copy-webpack-plugin"); 3 | 4 | module.exports = { 5 | mode: 'development', 6 | entry: "./src/index.js", 7 | output: { 8 | filename: "index.js", 9 | path: path.resolve(__dirname, "dist"), 10 | }, 11 | plugins: [ 12 | new CopyWebpackPlugin([{ from: "./src/index.html", to: "index.html" }]), 13 | ], 14 | devServer: { contentBase: path.join(__dirname, "dist"), compress: true }, 15 | }; 16 | -------------------------------------------------------------------------------- /contracts/TokenswapInterface.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.11; 2 | 3 | contract TokenswapInterface { 4 | /** 5 | * @dev Tokenswap interface 6 | */ 7 | function kyberswapEthToToken(address tokenAddress) public payable; 8 | function uniswapToken(address fromTokenAddress, address toTokenAddress, uint256 fromTokenAmount) public returns (uint256); 9 | function addPoolToUniswap(address ethSwapTokenAddress, address uniswapAddPoolTokenAddress, address beneficiary, uint256 ethSwapTokenAmount, uint256 uniswapAddPoolTokenAmount) public returns (bool); 10 | } -------------------------------------------------------------------------------- /app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "app", 3 | "version": "1.0.0", 4 | "description": "", 5 | "private": true, 6 | "scripts": { 7 | "build": "webpack", 8 | "dev": "webpack-dev-server" 9 | }, 10 | "devDependencies": { 11 | "copy-webpack-plugin": "^4.6.0", 12 | "webpack": "^4.28.1", 13 | "webpack-cli": "^3.2.1", 14 | "webpack-dev-server": "^3.1.14" 15 | }, 16 | "dependencies": { 17 | "@toruslabs/torus-embed": "^0.1.2", 18 | "bigdecimal": "^0.6.1", 19 | "ipfs-http-client": "^39.0.2", 20 | "web3": "^1.0.0-beta.37" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /contracts/KyberNetworkProxyInterface.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.11; 2 | 3 | import "./EIP20Interface.sol"; 4 | 5 | contract KyberNetworkProxyInterface { 6 | /** 7 | * @dev KyberNetworkProxy interface 8 | */ 9 | function getExpectedRate(EIP20Interface src, EIP20Interface dest, uint srcQty) public view returns (uint expectedRate, uint slippageRate); 10 | function swapTokenToEther(EIP20Interface token, uint srcAmount, uint minConversionRate) public returns (uint); 11 | function swapEtherToToken(EIP20Interface token, uint minConversionRate) public payable returns (uint); 12 | } -------------------------------------------------------------------------------- /contracts/CTokenInterface.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.11; 2 | 3 | contract CTokenInterface { 4 | /** 5 | * @dev Compound CToken interface 6 | */ 7 | function mint(uint mintAmount) external returns (uint _error); 8 | function redeem(uint redeemTokens) external returns (uint _error); 9 | function redeemUnderlying(uint redeemAmount) external returns (uint _error); 10 | function balanceOf(address owner) external view returns (uint256 balance); 11 | function exchangeRateStored() public view returns (uint); 12 | function transfer(address dst, uint256 amount) external returns (bool); 13 | function exchangeRateCurrent() public returns (uint); 14 | } -------------------------------------------------------------------------------- /contracts/Migrations.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.4.21 <0.6.0; 2 | 3 | contract Migrations { 4 | address public owner; 5 | uint public last_completed_migration; 6 | 7 | constructor() public { 8 | owner = msg.sender; 9 | } 10 | 11 | modifier restricted() { 12 | if (msg.sender == owner) _; 13 | } 14 | 15 | function setCompleted(uint completed) public restricted { 16 | last_completed_migration = completed; 17 | } 18 | 19 | function upgrade(address new_address) public restricted { 20 | Migrations upgraded = Migrations(new_address); 21 | upgraded.setCompleted(last_completed_migration); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /contracts/EIP20Interface.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.11; 2 | 3 | contract EIP20Interface { 4 | /** 5 | * @dev EIP20 interface 6 | */ 7 | function totalSupply() external view returns (uint256); 8 | function balanceOf(address account) external view returns (uint256); 9 | function transfer(address recipient, uint256 amount) external returns (bool); 10 | function allowance(address owner, address spender) external view returns (uint256); 11 | function approve(address spender, uint256 amount) external returns (bool); 12 | function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); 13 | event Transfer(address indexed from, address indexed to, uint256 value); 14 | event Approval(address indexed owner, address indexed spender, uint256 value); 15 | } 16 | -------------------------------------------------------------------------------- /contracts/IERC165.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | 3 | /** 4 | * @dev Interface of the ERC165 standard, as defined in the 5 | * https://eips.ethereum.org/EIPS/eip-165[EIP]. 6 | * 7 | * Implementers can declare support of contract interfaces, which can then be 8 | * queried by others ({ERC165Checker}). 9 | * 10 | * For an implementation, see {ERC165}. 11 | */ 12 | interface IERC165 { 13 | /** 14 | * @dev Returns true if this contract implements the interface defined by 15 | * `interfaceId`. See the corresponding 16 | * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] 17 | * to learn more about how these ids are created. 18 | * 19 | * This function call must use less than 30 000 gas. 20 | */ 21 | function supportsInterface(bytes4 interfaceId) external view returns (bool); 22 | } -------------------------------------------------------------------------------- /contracts/UniswapExchangeInterface.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.11; 2 | 3 | contract UniswapExchangeInterface { 4 | /** 5 | * @dev UniswapExchange interface 6 | */ 7 | function addLiquidity(uint256 min_liquidity, uint256 max_tokens, uint256 deadline) external payable returns (uint256); 8 | function getTokenToEthInputPrice(uint256 tokens_sold) external view returns (uint256 eth_bought); 9 | function getTokenToEthOutputPrice(uint256 eth_bought) external view returns (uint256 tokens_sold); 10 | function getEthToTokenInputPrice(uint256 eth_sold) external view returns (uint256 tokens_bought); 11 | function ethToTokenTransferInput(uint256 min_tokens, uint256 deadline, address recipient) external payable returns (uint256 tokens_bought); 12 | function tokenToEthSwapInput(uint256 tokens_sold, uint256 min_eth, uint256 deadline) external returns (uint256 eth_bought); 13 | function transfer(address _to, uint256 _value) external returns (bool); 14 | function balanceOf(address _owner) external view returns (uint256); 15 | function totalSupply() external view returns (uint256); 16 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 bannplayer 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /contracts/Context.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | 3 | /* 4 | * @dev Provides information about the current execution context, including the 5 | * sender of the transaction and its data. While these are generally available 6 | * via msg.sender and msg.data, they should not be accessed in such a direct 7 | * manner, since when dealing with GSN meta-transactions the account sending and 8 | * paying for execution may not be the actual sender (as far as an application 9 | * is concerned). 10 | * 11 | * This contract is only required for intermediate, library-like contracts. 12 | */ 13 | contract Context { 14 | // Empty internal constructor, to prevent people from mistakenly deploying 15 | // an instance of this contract, which should be used via inheritance. 16 | constructor () internal { } 17 | // solhint-disable-previous-line no-empty-blocks 18 | 19 | function _msgSender() internal view returns (address payable) { 20 | return msg.sender; 21 | } 22 | 23 | function _msgData() internal view returns (bytes memory) { 24 | this; // silence state mutability warning without generating bytecode - see https://github.com/ethereum/solidity/issues/2691 25 | return msg.data; 26 | } 27 | } -------------------------------------------------------------------------------- /contracts/IERC721Receiver.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | 3 | /** 4 | * @title ERC721 token receiver interface 5 | * @dev Interface for any contract that wants to support safeTransfers 6 | * from ERC721 asset contracts. 7 | */ 8 | contract IERC721Receiver { 9 | /** 10 | * @notice Handle the receipt of an NFT 11 | * @dev The ERC721 smart contract calls this function on the recipient 12 | * after a {IERC721-safeTransferFrom}. This function MUST return the function selector, 13 | * otherwise the caller will revert the transaction. The selector to be 14 | * returned can be obtained as `this.onERC721Received.selector`. This 15 | * function MAY throw to revert and reject the transfer. 16 | * Note: the ERC721 contract address is always the message sender. 17 | * @param operator The address which called `safeTransferFrom` function 18 | * @param from The address which previously owned the token 19 | * @param tokenId The NFT identifier which is being transferred 20 | * @param data Additional data with no specified format 21 | * @return bytes4 `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))` 22 | */ 23 | function onERC721Received(address operator, address from, uint256 tokenId, bytes memory data) 24 | public returns (bytes4); 25 | } -------------------------------------------------------------------------------- /contracts/Counters.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | 3 | import "./SafeMath.sol"; 4 | 5 | /** 6 | * @title Counters 7 | * @author Matt Condon (@shrugs) 8 | * @dev Provides counters that can only be incremented or decremented by one. This can be used e.g. to track the number 9 | * of elements in a mapping, issuing ERC721 ids, or counting request ids. 10 | * 11 | * Include with `using Counters for Counters.Counter;` 12 | * Since it is not possible to overflow a 256 bit integer with increments of one, `increment` can skip the {SafeMath} 13 | * overflow check, thereby saving gas. This does assume however correct usage, in that the underlying `_value` is never 14 | * directly accessed. 15 | */ 16 | library Counters { 17 | using SafeMath for uint256; 18 | 19 | struct Counter { 20 | // This variable should never be directly accessed by users of the library: interactions must be restricted to 21 | // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add 22 | // this feature: see https://github.com/ethereum/solidity/issues/4637 23 | uint256 _value; // default: 0 24 | } 25 | 26 | function current(Counter storage counter) internal view returns (uint256) { 27 | return counter._value; 28 | } 29 | 30 | function increment(Counter storage counter) internal { 31 | // The {SafeMath} overflow check can be skipped here, see the comment at the top 32 | counter._value += 1; 33 | } 34 | 35 | function decrement(Counter storage counter) internal { 36 | counter._value = counter._value.sub(1); 37 | } 38 | } -------------------------------------------------------------------------------- /contracts/ERC165.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | 3 | import "./IERC165.sol"; 4 | 5 | /** 6 | * @dev Implementation of the {IERC165} interface. 7 | * 8 | * Contracts may inherit from this and call {_registerInterface} to declare 9 | * their support of an interface. 10 | */ 11 | contract ERC165 is IERC165 { 12 | /* 13 | * bytes4(keccak256('supportsInterface(bytes4)')) == 0x01ffc9a7 14 | */ 15 | bytes4 private constant _INTERFACE_ID_ERC165 = 0x01ffc9a7; 16 | 17 | /** 18 | * @dev Mapping of interface ids to whether or not it's supported. 19 | */ 20 | mapping(bytes4 => bool) private _supportedInterfaces; 21 | 22 | constructor () internal { 23 | // Derived contracts need only register support for their own interfaces, 24 | // we register support for ERC165 itself here 25 | _registerInterface(_INTERFACE_ID_ERC165); 26 | } 27 | 28 | /** 29 | * @dev See {IERC165-supportsInterface}. 30 | * 31 | * Time complexity O(1), guaranteed to always use less than 30 000 gas. 32 | */ 33 | function supportsInterface(bytes4 interfaceId) external view returns (bool) { 34 | return _supportedInterfaces[interfaceId]; 35 | } 36 | 37 | /** 38 | * @dev Registers the contract as an implementer of the interface defined by 39 | * `interfaceId`. Support of the actual ERC165 interface is automatic and 40 | * registering its interface id is not required. 41 | * 42 | * See {IERC165-supportsInterface}. 43 | * 44 | * Requirements: 45 | * 46 | * - `interfaceId` cannot be the ERC165 invalid interface (`0xffffffff`). 47 | */ 48 | function _registerInterface(bytes4 interfaceId) internal { 49 | require(interfaceId != 0xffffffff, "ERC165: invalid interface id"); 50 | _supportedInterfaces[interfaceId] = true; 51 | } 52 | } -------------------------------------------------------------------------------- /contracts/Address.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.5; 2 | 3 | /** 4 | * @dev Collection of functions related to the address type 5 | */ 6 | library Address { 7 | /** 8 | * @dev Returns true if `account` is a contract. 9 | * 10 | * This test is non-exhaustive, and there may be false-negatives: during the 11 | * execution of a contract's constructor, its address will be reported as 12 | * not containing a contract. 13 | * 14 | * IMPORTANT: It is unsafe to assume that an address for which this 15 | * function returns false is an externally-owned account (EOA) and not a 16 | * contract. 17 | */ 18 | function isContract(address account) internal view returns (bool) { 19 | // This method relies in extcodesize, which returns 0 for contracts in 20 | // construction, since the code is only stored at the end of the 21 | // constructor execution. 22 | 23 | // According to EIP-1052, 0x0 is the value returned for not-yet created accounts 24 | // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned 25 | // for accounts without code, i.e. `keccak256('')` 26 | bytes32 codehash; 27 | bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470; 28 | // solhint-disable-next-line no-inline-assembly 29 | assembly { codehash := extcodehash(account) } 30 | return (codehash != 0x0 && codehash != accountHash); 31 | } 32 | 33 | /** 34 | * @dev Converts an `address` into `address payable`. Note that this is 35 | * simply a type cast: the actual underlying value is not changed. 36 | * 37 | * NOTE: This is a feature of the next version of OpenZeppelin Contracts. 38 | * @dev Get it via `npm install @openzeppelin/contracts@next`. 39 | */ 40 | function toPayable(address account) internal pure returns (address payable) { 41 | return address(uint160(account)); 42 | } 43 | } -------------------------------------------------------------------------------- /contracts/IERC721.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | 3 | import "./IERC165.sol"; 4 | 5 | /** 6 | * @dev Required interface of an ERC721 compliant contract. 7 | */ 8 | contract IERC721 is IERC165 { 9 | event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); 10 | event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); 11 | event ApprovalForAll(address indexed owner, address indexed operator, bool approved); 12 | 13 | /** 14 | * @dev Returns the number of NFTs in `owner`'s account. 15 | */ 16 | function balanceOf(address owner) public view returns (uint256 balance); 17 | 18 | /** 19 | * @dev Returns the owner of the NFT specified by `tokenId`. 20 | */ 21 | function ownerOf(uint256 tokenId) public view returns (address owner); 22 | 23 | /** 24 | * @dev Transfers a specific NFT (`tokenId`) from one account (`from`) to 25 | * another (`to`). 26 | * 27 | * 28 | * 29 | * Requirements: 30 | * - `from`, `to` cannot be zero. 31 | * - `tokenId` must be owned by `from`. 32 | * - If the caller is not `from`, it must be have been allowed to move this 33 | * NFT by either {approve} or {setApprovalForAll}. 34 | */ 35 | function safeTransferFrom(address from, address to, uint256 tokenId) public; 36 | /** 37 | * @dev Transfers a specific NFT (`tokenId`) from one account (`from`) to 38 | * another (`to`). 39 | * 40 | * Requirements: 41 | * - If the caller is not `from`, it must be approved to move this NFT by 42 | * either {approve} or {setApprovalForAll}. 43 | */ 44 | function transferFrom(address from, address to, uint256 tokenId) public; 45 | function approve(address to, uint256 tokenId) public; 46 | function getApproved(uint256 tokenId) public view returns (address operator); 47 | 48 | function setApprovalForAll(address operator, bool _approved) public; 49 | function isApprovedForAll(address owner, address operator) public view returns (bool); 50 | 51 | 52 | function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public; 53 | } -------------------------------------------------------------------------------- /migrations/2_deploy_contracts.js: -------------------------------------------------------------------------------- 1 | const Growdrop = artifacts.require("Growdrop"); 2 | const Growdrop_Kovan = artifacts.require("Growdrop_kovan"); 3 | const Tokenswap = artifacts.require("Tokenswap"); 4 | const DonateToken = artifacts.require("DonateToken"); 5 | const GrowdropCall = artifacts.require("GrowdropCall"); 6 | 7 | module.exports = function(deployer, network) { 8 | var uniswapfactory; 9 | var kybernetworkproxy; 10 | var kyberdai; 11 | var dai; 12 | var kyberminimum; 13 | if(network == "kovan") { 14 | uniswapfactory="0xD3E51Ef092B2845f10401a0159B2B96e8B6c3D30"; 15 | kybernetworkproxy="0x692f391bCc85cefCe8C237C01e1f636BbD70EA4D"; 16 | kyberdai="0xC4375B7De8af5a38a93548eb8453a498222C4fF2"; 17 | dai="0xbF7A7169562078c96f0eC1A8aFD6aE50f12e5A99"; 18 | kyberminimum="1000000000"; 19 | 20 | deployer.deploy(Growdrop_Kovan) 21 | .then(function() { 22 | return deployer.deploy(GrowdropCall, Growdrop_Kovan.address); 23 | }).then(function() { 24 | return deployer.deploy( 25 | Tokenswap, 26 | Growdrop_Kovan.address, 27 | uniswapfactory, 28 | kybernetworkproxy, 29 | dai, 30 | kyberdai, 31 | kyberminimum 32 | ); 33 | }).then(function() { 34 | return deployer.deploy(DonateToken, Growdrop_Kovan.address); 35 | }) 36 | } else if (network == "mainnet") { 37 | uniswapfactory="0xc0a47dFe034B400B47bDaD5FecDa2621de6c4d95" 38 | kybernetworkproxy="0x818E6FECD516Ecc3849DAf6845e3EC868087B755"; 39 | dai="0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359"; 40 | kyberdai="0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359"; 41 | kyberminimum="1000000000000000"; 42 | 43 | deployer.deploy(Growdrop) 44 | .then(function() { 45 | return deployer.deploy(GrowdropCall, Growdrop.address); 46 | }).then(function() { 47 | return deployer.deploy( 48 | Tokenswap, 49 | Growdrop.address, 50 | uniswapfactory, 51 | kybernetworkproxy, 52 | dai, 53 | kyberdai, 54 | kyberminimum 55 | ); 56 | }).then(function() { 57 | return deployer.deploy(DonateToken, Growdrop.address); 58 | }) 59 | } 60 | } -------------------------------------------------------------------------------- /contracts/GrowdropCall.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.11; 2 | 3 | import "./Growdrop.sol"; 4 | import "./CTokenInterface.sol"; 5 | import "./EIP20Interface.sol"; 6 | 7 | contract GrowdropCall { 8 | Growdrop growdrop; 9 | 10 | mapping(address => bool) public CheckOwner; 11 | 12 | constructor(address payable _Growdrop) public { 13 | growdrop = Growdrop(_Growdrop); 14 | CheckOwner[msg.sender] = true; 15 | } 16 | 17 | function addOwner(address _Owner) public { 18 | require(CheckOwner[msg.sender], "not owner"); 19 | CheckOwner[_Owner] = !CheckOwner[_Owner]; 20 | } 21 | 22 | function setGrowdrop(address payable _Growdrop) public { 23 | require(CheckOwner[msg.sender], "not owner"); 24 | growdrop = Growdrop(_Growdrop); 25 | } 26 | 27 | function getGrowdropData(uint256 _GrowdropCount) public view returns ( 28 | address, 29 | address, 30 | uint256, 31 | uint256, 32 | uint256, 33 | bool 34 | ) { 35 | return ( 36 | address(growdrop.GrowdropToken(_GrowdropCount)), 37 | growdrop.Beneficiary(_GrowdropCount), 38 | growdrop.GrowdropAmount(_GrowdropCount), 39 | growdrop.ToUniswapTokenAmount(_GrowdropCount), 40 | growdrop.ToUniswapInterestRate(_GrowdropCount), 41 | growdrop.AddToUniswap(_GrowdropCount) 42 | ); 43 | } 44 | 45 | function getGrowdropStateData(uint256 _GrowdropCount) public view returns ( 46 | uint256, 47 | uint256, 48 | bool, 49 | bool, 50 | uint256 51 | ) { 52 | return ( 53 | growdrop.GrowdropStartTime(_GrowdropCount), 54 | growdrop.GrowdropEndTime(_GrowdropCount), 55 | growdrop.GrowdropStart(_GrowdropCount), 56 | growdrop.GrowdropOver(_GrowdropCount), 57 | growdrop.ExchangeRateOver(_GrowdropCount)==0 ? growdrop.CToken(_GrowdropCount).exchangeRateStored() : growdrop.ExchangeRateOver(_GrowdropCount) 58 | ); 59 | } 60 | 61 | function getGrowdropAmountData(uint256 _GrowdropCount) public view returns ( 62 | uint256, 63 | uint256, 64 | uint256, 65 | uint256, 66 | bool, 67 | uint256, 68 | uint256 69 | ) { 70 | return ( 71 | growdrop.TotalCTokenAmount(_GrowdropCount), 72 | growdrop.TotalMintedAmount(_GrowdropCount), 73 | growdrop.CTokenPerAddress(_GrowdropCount, msg.sender), 74 | growdrop.InvestAmountPerAddress(_GrowdropCount, msg.sender), 75 | growdrop.WithdrawOver(_GrowdropCount, msg.sender), 76 | growdrop.ActualCTokenPerAddress(_GrowdropCount, msg.sender), 77 | growdrop.ActualPerAddress(_GrowdropCount, msg.sender) 78 | ); 79 | } 80 | 81 | function getTokenInfo(uint256 _GrowdropCount) public view returns (uint256, uint256, uint256) { 82 | EIP20Interface token = EIP20Interface(growdrop.Token(_GrowdropCount)); 83 | EIP20Interface growdropToken = EIP20Interface(growdrop.GrowdropToken(_GrowdropCount)); 84 | return ( 85 | token.balanceOf(msg.sender), 86 | token.allowance(msg.sender, address(growdrop)), 87 | address(growdropToken)==address(0x0) ? 0 : (growdrop.DonateId(_GrowdropCount)==0 ? growdropToken.allowance(msg.sender, address(growdrop)) : 1) 88 | ); 89 | } 90 | } -------------------------------------------------------------------------------- /truffle-config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Use this file to configure your truffle project. It's seeded with some 3 | * common settings for different networks and features like migrations, 4 | * compilation and testing. Uncomment the ones you need or modify 5 | * them to suit your project as necessary. 6 | * 7 | * More information about configuration can be found at: 8 | * 9 | * truffleframework.com/docs/advanced/configuration 10 | * 11 | * To deploy via Infura you'll need a wallet provider (like truffle-hdwallet-provider) 12 | * to sign your transactions before they're sent to a remote public node. Infura accounts 13 | * are available for free at: infura.io/register. 14 | * 15 | * You'll also need a mnemonic - the twelve word phrase the wallet uses to generate 16 | * public/private key pairs. If you're publishing your code to GitHub make sure you load this 17 | * phrase from a file you've .gitignored so it doesn't accidentally become public. 18 | * 19 | */ 20 | 21 | const HDWalletProvider = require('truffle-hdwallet-provider'); 22 | // const infuraKey = "fj4jll3k....."; 23 | // 24 | const fs = require('fs'); 25 | const mnemonic = fs.readFileSync(".secret").toString().trim(); 26 | 27 | module.exports = { 28 | /** 29 | * Networks define how you connect to your ethereum client and let you set the 30 | * defaults web3 uses to send transactions. If you don't specify one truffle 31 | * will spin up a development blockchain for you on port 9545 when you 32 | * run `develop` or `test`. You can ask a truffle command to use a specific 33 | * network from the command line, e.g 34 | * 35 | * $ truffle test --network 36 | */ 37 | 38 | networks: { 39 | // Useful for testing. The `development` name is special - truffle uses it by default 40 | // if it's defined here and no other network is specified at the command line. 41 | // You should run a client (like ganache-cli, geth or parity) in a separate terminal 42 | // tab if you use this network and you must also set the `host`, `port` and `network_id` 43 | // options below to some value. 44 | // 45 | // development: { 46 | // host: "127.0.0.1", // Localhost (default: none) 47 | // port: 8545, // Standard Ethereum port (default: none) 48 | // network_id: "*", // Any network (default: none) 49 | // }, 50 | 51 | develop: { 52 | port: 8545 53 | }, 54 | 55 | // Another network with more advanced options... 56 | // advanced: { 57 | // port: 8777, // Custom port 58 | // network_id: 1342, // Custom network 59 | // gas: 8500000, // Gas sent with each transaction (default: ~6700000) 60 | // gasPrice: 20000000000, // 20 gwei (in wei) (default: 100 gwei) 61 | // from:
, // Account to send txs from (default: accounts[0]) 62 | // websockets: true // Enable EventEmitter interface for web3 (default: false) 63 | // }, 64 | 65 | // Useful for deploying to a public network. 66 | // NB: It's important to wrap the provider as a function. 67 | kovan: { 68 | provider: () => new HDWalletProvider(mnemonic, `https://kovan.infura.io/v3/infura_project_id`), 69 | network_id: 42, // Ropsten's id 70 | gas: 6700000, // Ropsten has a lower block limit than mainnet 71 | // confirmations: 2, // # of confs to wait between deployments. (default: 0) 72 | // timeoutBlocks: 200, // # of blocks before a deployment times out (minimum/default: 50) 73 | skipDryRun: true // Skip dry run before migrations? (default: false for public nets ) 74 | }, 75 | 76 | // Useful for private networks 77 | // private: { 78 | // provider: () => new HDWalletProvider(mnemonic, `https://network.io`), 79 | // network_id: 2111, // This network is yours, in the cloud. 80 | // production: true // Treats this network as if it was a public net. (default: false) 81 | // } 82 | }, 83 | 84 | // Set default mocha options here, use special reporters etc. 85 | mocha: { 86 | // timeout: 100000 87 | }, 88 | 89 | // Configure your compilers 90 | compilers: { 91 | solc: { 92 | version: "0.5.11", // Fetch exact version from solc-bin (default: truffle's version) 93 | // docker: true, // Use "0.5.1" you've installed locally with docker (default: false) 94 | // settings: { // See the solidity docs for advice about optimization and evmVersion 95 | // optimizer: { 96 | // enabled: false, 97 | // runs: 200 98 | // }, 99 | // evmVersion: "byzantium" 100 | // } 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /contracts/SafeMath.sol: -------------------------------------------------------------------------------- 1 | 2 | pragma solidity ^0.5.11; 3 | 4 | /** 5 | * @dev Wrappers over Solidity's arithmetic operations with added overflow 6 | * checks. 7 | * 8 | * Arithmetic operations in Solidity wrap on overflow. This can easily result 9 | * in bugs, because programmers usually assume that an overflow raises an 10 | * error, which is the standard behavior in high level programming languages. 11 | * `SafeMath` restores this intuition by reverting the transaction when an 12 | * operation overflows. 13 | * 14 | * Using this library instead of the unchecked operations eliminates an entire 15 | * class of bugs, so it's recommended to use it always. 16 | */ 17 | library SafeMath { 18 | /** 19 | * @dev Returns the addition of two unsigned integers, reverting on 20 | * overflow. 21 | * 22 | * Counterpart to Solidity's `+` operator. 23 | * 24 | * Requirements: 25 | * - Addition cannot overflow. 26 | */ 27 | function add(uint256 a, uint256 b) internal pure returns (uint256) { 28 | uint256 c = a + b; 29 | require(c >= a, "SafeMath: addition overflow"); 30 | 31 | return c; 32 | } 33 | 34 | /** 35 | * @dev Returns the subtraction of two unsigned integers, reverting on 36 | * overflow (when the result is negative). 37 | * 38 | * Counterpart to Solidity's `-` operator. 39 | * 40 | * Requirements: 41 | * - Subtraction cannot overflow. 42 | */ 43 | function sub(uint256 a, uint256 b) internal pure returns (uint256) { 44 | return sub(a, b, "SafeMath: subtraction overflow"); 45 | } 46 | 47 | /** 48 | * @dev Returns the subtraction of two unsigned integers, reverting with custom message on 49 | * overflow (when the result is negative). 50 | * 51 | * Counterpart to Solidity's `-` operator. 52 | * 53 | * Requirements: 54 | * - Subtraction cannot overflow. 55 | * 56 | * NOTE: This is a feature of the next version of OpenZeppelin Contracts. 57 | * @dev Get it via `npm install @openzeppelin/contracts@next`. 58 | */ 59 | function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { 60 | require(b <= a, errorMessage); 61 | uint256 c = a - b; 62 | 63 | return c; 64 | } 65 | 66 | /** 67 | * @dev Returns the multiplication of two unsigned integers, reverting on 68 | * overflow. 69 | * 70 | * Counterpart to Solidity's `*` operator. 71 | * 72 | * Requirements: 73 | * - Multiplication cannot overflow. 74 | */ 75 | function mul(uint256 a, uint256 b) internal pure returns (uint256) { 76 | // Gas optimization: this is cheaper than requiring 'a' not being zero, but the 77 | // benefit is lost if 'b' is also tested. 78 | // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 79 | if (a == 0) { 80 | return 0; 81 | } 82 | 83 | uint256 c = a * b; 84 | require(c / a == b, "SafeMath: multiplication overflow"); 85 | 86 | return c; 87 | } 88 | 89 | /** 90 | * @dev Returns the integer division of two unsigned integers. Reverts on 91 | * division by zero. The result is rounded towards zero. 92 | * 93 | * Counterpart to Solidity's `/` operator. Note: this function uses a 94 | * `revert` opcode (which leaves remaining gas untouched) while Solidity 95 | * uses an invalid opcode to revert (consuming all remaining gas). 96 | * 97 | * Requirements: 98 | * - The divisor cannot be zero. 99 | */ 100 | function div(uint256 a, uint256 b) internal pure returns (uint256) { 101 | return div(a, b, "SafeMath: division by zero"); 102 | } 103 | 104 | /** 105 | * @dev Returns the integer division of two unsigned integers. Reverts with custom message on 106 | * division by zero. The result is rounded towards zero. 107 | * 108 | * Counterpart to Solidity's `/` operator. Note: this function uses a 109 | * `revert` opcode (which leaves remaining gas untouched) while Solidity 110 | * uses an invalid opcode to revert (consuming all remaining gas). 111 | * 112 | * Requirements: 113 | * - The divisor cannot be zero. 114 | * NOTE: This is a feature of the next version of OpenZeppelin Contracts. 115 | * @dev Get it via `npm install @openzeppelin/contracts@next`. 116 | */ 117 | function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { 118 | // Solidity only automatically asserts when dividing by 0 119 | require(b > 0, errorMessage); 120 | uint256 c = a / b; 121 | // assert(a == b * c + a % b); // There is no case in which this doesn't hold 122 | 123 | return c; 124 | } 125 | 126 | /** 127 | * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), 128 | * Reverts when dividing by zero. 129 | * 130 | * Counterpart to Solidity's `%` operator. This function uses a `revert` 131 | * opcode (which leaves remaining gas untouched) while Solidity uses an 132 | * invalid opcode to revert (consuming all remaining gas). 133 | * 134 | * Requirements: 135 | * - The divisor cannot be zero. 136 | */ 137 | function mod(uint256 a, uint256 b) internal pure returns (uint256) { 138 | return mod(a, b, "SafeMath: modulo by zero"); 139 | } 140 | 141 | /** 142 | * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo), 143 | * Reverts with custom message when dividing by zero. 144 | * 145 | * Counterpart to Solidity's `%` operator. This function uses a `revert` 146 | * opcode (which leaves remaining gas untouched) while Solidity uses an 147 | * invalid opcode to revert (consuming all remaining gas). 148 | * 149 | * Requirements: 150 | * - The divisor cannot be zero. 151 | * 152 | * NOTE: This is a feature of the next version of OpenZeppelin Contracts. 153 | * @dev Get it via `npm install @openzeppelin/contracts@next`. 154 | */ 155 | function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) { 156 | require(b != 0, errorMessage); 157 | return a % b; 158 | } 159 | } -------------------------------------------------------------------------------- /app/src/SimpleTokenABI.json: -------------------------------------------------------------------------------- 1 | {"abi":[ 2 | { 3 | "inputs": [], 4 | "payable": false, 5 | "stateMutability": "nonpayable", 6 | "type": "constructor" 7 | }, 8 | { 9 | "anonymous": false, 10 | "inputs": [ 11 | { 12 | "indexed": true, 13 | "internalType": "address", 14 | "name": "owner", 15 | "type": "address" 16 | }, 17 | { 18 | "indexed": true, 19 | "internalType": "address", 20 | "name": "spender", 21 | "type": "address" 22 | }, 23 | { 24 | "indexed": false, 25 | "internalType": "uint256", 26 | "name": "value", 27 | "type": "uint256" 28 | } 29 | ], 30 | "name": "Approval", 31 | "type": "event" 32 | }, 33 | { 34 | "constant": false, 35 | "inputs": [ 36 | { 37 | "internalType": "address", 38 | "name": "spender", 39 | "type": "address" 40 | }, 41 | { 42 | "internalType": "uint256", 43 | "name": "amount", 44 | "type": "uint256" 45 | } 46 | ], 47 | "name": "approve", 48 | "outputs": [ 49 | { 50 | "internalType": "bool", 51 | "name": "", 52 | "type": "bool" 53 | } 54 | ], 55 | "payable": false, 56 | "stateMutability": "nonpayable", 57 | "type": "function" 58 | }, 59 | { 60 | "constant": false, 61 | "inputs": [ 62 | { 63 | "internalType": "address", 64 | "name": "spender", 65 | "type": "address" 66 | }, 67 | { 68 | "internalType": "uint256", 69 | "name": "subtractedValue", 70 | "type": "uint256" 71 | } 72 | ], 73 | "name": "decreaseAllowance", 74 | "outputs": [ 75 | { 76 | "internalType": "bool", 77 | "name": "", 78 | "type": "bool" 79 | } 80 | ], 81 | "payable": false, 82 | "stateMutability": "nonpayable", 83 | "type": "function" 84 | }, 85 | { 86 | "constant": false, 87 | "inputs": [ 88 | { 89 | "internalType": "address", 90 | "name": "spender", 91 | "type": "address" 92 | }, 93 | { 94 | "internalType": "uint256", 95 | "name": "addedValue", 96 | "type": "uint256" 97 | } 98 | ], 99 | "name": "increaseAllowance", 100 | "outputs": [ 101 | { 102 | "internalType": "bool", 103 | "name": "", 104 | "type": "bool" 105 | } 106 | ], 107 | "payable": false, 108 | "stateMutability": "nonpayable", 109 | "type": "function" 110 | }, 111 | { 112 | "constant": false, 113 | "inputs": [], 114 | "name": "mint", 115 | "outputs": [], 116 | "payable": false, 117 | "stateMutability": "nonpayable", 118 | "type": "function" 119 | }, 120 | { 121 | "constant": false, 122 | "inputs": [ 123 | { 124 | "internalType": "address", 125 | "name": "recipient", 126 | "type": "address" 127 | }, 128 | { 129 | "internalType": "uint256", 130 | "name": "amount", 131 | "type": "uint256" 132 | } 133 | ], 134 | "name": "transfer", 135 | "outputs": [ 136 | { 137 | "internalType": "bool", 138 | "name": "", 139 | "type": "bool" 140 | } 141 | ], 142 | "payable": false, 143 | "stateMutability": "nonpayable", 144 | "type": "function" 145 | }, 146 | { 147 | "anonymous": false, 148 | "inputs": [ 149 | { 150 | "indexed": true, 151 | "internalType": "address", 152 | "name": "from", 153 | "type": "address" 154 | }, 155 | { 156 | "indexed": true, 157 | "internalType": "address", 158 | "name": "to", 159 | "type": "address" 160 | }, 161 | { 162 | "indexed": false, 163 | "internalType": "uint256", 164 | "name": "value", 165 | "type": "uint256" 166 | } 167 | ], 168 | "name": "Transfer", 169 | "type": "event" 170 | }, 171 | { 172 | "constant": false, 173 | "inputs": [ 174 | { 175 | "internalType": "address", 176 | "name": "sender", 177 | "type": "address" 178 | }, 179 | { 180 | "internalType": "address", 181 | "name": "recipient", 182 | "type": "address" 183 | }, 184 | { 185 | "internalType": "uint256", 186 | "name": "amount", 187 | "type": "uint256" 188 | } 189 | ], 190 | "name": "transferFrom", 191 | "outputs": [ 192 | { 193 | "internalType": "bool", 194 | "name": "", 195 | "type": "bool" 196 | } 197 | ], 198 | "payable": false, 199 | "stateMutability": "nonpayable", 200 | "type": "function" 201 | }, 202 | { 203 | "constant": true, 204 | "inputs": [ 205 | { 206 | "internalType": "address", 207 | "name": "owner", 208 | "type": "address" 209 | }, 210 | { 211 | "internalType": "address", 212 | "name": "spender", 213 | "type": "address" 214 | } 215 | ], 216 | "name": "allowance", 217 | "outputs": [ 218 | { 219 | "internalType": "uint256", 220 | "name": "", 221 | "type": "uint256" 222 | } 223 | ], 224 | "payable": false, 225 | "stateMutability": "view", 226 | "type": "function" 227 | }, 228 | { 229 | "constant": true, 230 | "inputs": [ 231 | { 232 | "internalType": "address", 233 | "name": "account", 234 | "type": "address" 235 | } 236 | ], 237 | "name": "balanceOf", 238 | "outputs": [ 239 | { 240 | "internalType": "uint256", 241 | "name": "", 242 | "type": "uint256" 243 | } 244 | ], 245 | "payable": false, 246 | "stateMutability": "view", 247 | "type": "function" 248 | }, 249 | { 250 | "constant": true, 251 | "inputs": [], 252 | "name": "decimals", 253 | "outputs": [ 254 | { 255 | "internalType": "uint8", 256 | "name": "", 257 | "type": "uint8" 258 | } 259 | ], 260 | "payable": false, 261 | "stateMutability": "view", 262 | "type": "function" 263 | }, 264 | { 265 | "constant": true, 266 | "inputs": [], 267 | "name": "name", 268 | "outputs": [ 269 | { 270 | "internalType": "string", 271 | "name": "", 272 | "type": "string" 273 | } 274 | ], 275 | "payable": false, 276 | "stateMutability": "view", 277 | "type": "function" 278 | }, 279 | { 280 | "constant": true, 281 | "inputs": [], 282 | "name": "symbol", 283 | "outputs": [ 284 | { 285 | "internalType": "string", 286 | "name": "", 287 | "type": "string" 288 | } 289 | ], 290 | "payable": false, 291 | "stateMutability": "view", 292 | "type": "function" 293 | }, 294 | { 295 | "constant": true, 296 | "inputs": [], 297 | "name": "totalSupply", 298 | "outputs": [ 299 | { 300 | "internalType": "uint256", 301 | "name": "", 302 | "type": "uint256" 303 | } 304 | ], 305 | "payable": false, 306 | "stateMutability": "view", 307 | "type": "function" 308 | } 309 | ]} 310 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Growdrop:Funding the revolution of blockchain ecosystem 2 | 3 | # Problem 4 | Blockchain financing method is not a structure in which everyone can invest 5 | equally. Crypto-fund or angel investors have a better SAFT or indiscriminate EXIT, which is 6 | why the average investors know and are deceived. In addition, the fraud token economy is 7 | formed by the unfounded projects preparing for the ICO in a token-free structure, where users 8 | buy tokens and have no effect on the crypto network. 9 | 10 | # Blockchain and Community 11 | ![|432x256](https://lh5.googleusercontent.com/bmyKZCt74dB3hN3tq2PL7ljZxVD7KuAFAdxbro-BRQPdlZsB1hzjHEAiU53d-CtXfI2sikvO_pC1OAQi1VntS_UEY_i0NFYGsr1HUE-arh376mOI3GaS_Cf92HM8aGk6VbGlQ1h-) 12 | 13 | Blockchains have their values formed in the protocol layer, unlike values that come from the 14 | application layer of the past internet industry. The value we talked about comes not just from 15 | the value of cryptocurrency, but from the birth of Bitcoin and Ethereum as a support of many communities. The value of the community provides a way to fork in the evolution of blockchain or to go forward through governance proposals. This process forms the core of the blockchain, the consensus value, which enables the sustainable development of protocols in accordance with reasonable governance rules for many people in the community. At some point, consensus value made a way to raise the value of instant community participants (speculators) through the funding method of ICO and IEO. This speculative bubble is extinguished for a moment. Enthusiastic supporters gave a lot of feedback through BIP and EIP to form the initial consensus value, while the speculative bubble is blowing and the supporters of the blockchain project are concentrated only on pump and dump. This means that the blockchain protocol stays thin before fat, and even the application layer (EX: Dapp) will not inherit its stability and decentralized value. That's why Bitcoin, whose consensus value is fat, is not the first cryptocurrency in history, but the most successful case. The mechanism designed by Satoshi Nakamoto allows the Bitcoin community to achieve effective self-organization and governance, which motivates community members to continue to benefit from mutual benefits. 16 | 17 | # Open source community funding 18 | Currently, in the case of fat Ethereum, compared to other blockchain protocols, there are about 20 to 30 true contributors to development. There are many factors to this problem, but the open source community lacks the motivation to maintain the ecosystem voluntarily. Therefore, various methods of financing are proposed to protect investors and supporters in the current industry. From the DAICO proposed by Ethereum founder Vitalik Buterin, there are Inflation Funding, Gitcoin, SEICO, Ensured ICO. Equivalent token distribution methods include Lock Drop, Livepeer, WorkLock. 19 | 20 | # Growdrop 21 | What we think so far is that the cost of trust in financing is very high. It is expensive and pays a lot of money for maintenance of all kinds of unnecessary surveillance systems. Growdrop is a crowdfunding solution that guarantees the principal and deposits funds into the liquidity pool of Lending Protocol by minimizing the trust costs of existing third parties. 22 | ![|602x408](./readme_image1.jpg) 23 | 24 | # Growdrop's Mechanism 25 | Given that Growdrop allows third parties to securely operate smart contracts, Growdrop can 26 | invest in projects, greatly reduce the risk of supporters' seed investments, and projects will 27 | slowly build communities to help promote public relations and technology development. 28 | Here, in the early Ethereum community, it is very similar to the nature of DAICO, and 29 | projects can start as a community and receive seed investment and promotion effects. 30 | Here's how Growdrop works. 31 | 32 | • Token projects will deploy their Growdrop's smart contracts. Then a Growdrop pool 33 | is created, allowing you to send crypto assets. In the smart contract, funding period / 34 | token quantity should be entered. 35 | 36 | • Interest earned from crypto assets sent by supporters to the Growdrop pool will be 37 | sent to the token project at the end of the funding period. 38 | 39 | • After funding, tokens in the smart contract are sent to the supporter according to the 40 | supporter interest. 41 | 42 | • (Optional) Token projects can create exchanges and add liquidity pool to uniswap by setting certain tokens and interests after Growdrop funding ends. The project's token will be deposited in the uniswap liquidity pool after Growdrop funding is completed, creating an environment where traders can trade with supporters before or after listing on a centralized exchange, and value will be formed by supporters. If adding liquidity pool fails, all remained tokens and interests go to the token project. 43 | 44 | Example: Mike, Ryan, and Bob can stake a crypto asset on a project called CKB to offer interest and receive CKB tokens in return. The CKB project sets a token amount of 100,000 CKB and one month of funding period when Growdrop distributes smart contracts. Mike sends 300,000 DAI, Ryan sends 500,000 DAI, Bob sends 200,000 DAI to the Growdrop pool, and assumes that the Lending Protocol's annual interest rate is 12% at that time. After one month of funding period, the accumulated interest is sent to the CKB project and CKB tokens are paid to the supporter according to interest rate. 45 | ![5484512100|690x387](./readme_image2.jpg) 46 | 47 | # Uniswap and Kyberswap 48 | If the initial token project is not listed or there is no use, the token from Growdrop can be added to uniswap to set the listing and price. You can create exchange contract and add liquidity pool for a certain amount of tokens and interest. Here's how to provide Growdrop. 49 | 50 | 1. The Growdrop Distributor sets a certain amount of tokens and interest rate for the uniswap. 51 | 52 | 2. At the end of the Growdrop funding period, swap the interest token calculated by interest rate to ETH by kyberswap. 53 | 54 | 3. List the swapped ETH and tokens to uniswap. 55 | 56 | Listed ETH / Tokens are priced according to the size of the pool, Growdrop's supporters can make exchange or add pool by putting the funded tokens and ETH into the liquidity pool. 57 | 58 | # Donation 59 | Donation has so far been donated through third parties. Donation is done with good intentions 60 | but can be used in a malicious way. It is also difficult to ensure that the funding is funded 61 | properly, unless the financial situation of each foundation is transparent. 62 | Growdrop supports not only token distribution but also Donation. The Donation model is 63 | similar to the token distribution model, where supporters secure tokens in Growdrop so that 64 | the interest generated there is raised to the donated foundation. Supporters will receive 65 | ERC-721-based donate certificates and principals. This will help to realize a donating model 66 | that minimizes risk through principal guarantees. A simple flowchart of Donation follows. 67 | 68 | ![690x387](./readme_image3.PNG) 69 | 70 | The Donation certificate can also be used in media, meetups and conferences. Supporter's 71 | Donation certificate is transferable and its use is unlimited depending on the project. 72 | In the traditional Donation model, users lose their principals, which leads to reliability issues 73 | for each foundation. 74 | This can change the stagnant support culture through the support of Growdrop. The merit of 75 | principal guarantees allows anyone to feel free to supporter, and through decentralized 76 | Donation, the financial inclusion of the real world can be expanded. 77 | 78 | # Limitation of Growdrop 79 | • The DeFi ecosystem is still in its infancy and there are risks that can have a big impact. 80 | 81 | • Because we contribute to the project with interest, the heart of Growdrop is a model that depends on funding scale. 82 | 83 | • Due to the current Lending Protocol, interest volatility can be very high. 84 | 85 | # Difference from other Interest rate donate projects 86 | Interest rate donate models must bring in a lot of money to generate a lot of interest. In the case of PIP, the price per token is fixed and the token is issued according to the amount of interest generated. There is no limit on the number of token issuances here, and the tokens of the project can be issued indefinitely. As a result, if the supply continues to increase in the secondary market, the corresponding token market will collapse. This has a significant limitation on the token economy of the project. The Growdrop model has no token price and lockup period, and the project can flexibly set the number of tokens to proceed with the existing token economy plan. Supporters can make deposits and withdrawals autonomously depending on the project situation. 87 | 88 | # Conclusion 89 | Since DeFi's market is still a new market, there are risks that can cause side effects from interest volatility, and it is still active only in the Ethereum ecosystem. Through Growdrop open source projects can get seed funding and form a community culture with supporters who are naturally valued rather than very expensive financing such as ICO and IEO. Growdrop is a new funding model. This makes Growdrop a new option for contributing to open source projects and breaking the vicious circle of financing. 90 | 91 | ## Demo:https://www.youtube.com/watch?v=T7DaTtlCnXA&feature=youtu.be 92 | ## Telegram:https://t.me/Growdrop 93 | ## Homepage:http://dev.growdrop.io (beta) 94 | 95 | # Reference 96 | EIP1789:https://github.com/ethereum/EIPs/issues/1789 97 | 98 | Funding the Evolution of Blockchains: https://medium.com/@FEhrsam/funding-the-evolution-of-blockchains-87d160988481 99 | 100 | *Please give us feedback as it is a new defi model !! 101 | 102 | 103 | # You can test it on here, currently updating : http://dev.growdrop.io 104 | # If you have any issues, please message to Telegram! 105 | -------------------------------------------------------------------------------- /app/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Growdrop 5 | 6 | 12 | 13 |

Growdrop

14 | 15 | 16 |

You have loading... DAI

17 |

You have loading... Kyber DAI

18 |

You have loading... SimpleToken

19 | 20 |

SimpleToken uniswap eth pool : loading... ETH

21 |

SimpleToken uniswap token pool : loading... SimpleToken

22 | 23 |

24 | 25 |

Add Donate Label To Ipfs And Contract

26 |
27 | 28 |

Kyberswap Eth To Dai

29 | 30 | 31 | 32 | 33 |

Uniswap KyberDai To Compound Dai (not cDai)

34 | 35 | 36 | 37 | 38 |

Get Expected Kyberswap amount

39 | 40 | 45 | 46 | 47 | 48 | 49 |

Get Donated Token Amount

50 | 51 | 56 | 57 | 58 | 59 | 60 |

Get Owner Of Donate Label Id

61 | 62 | 63 | 64 | 65 |

Get Ipfshash Of Donate Label Id

66 | 67 | 68 | 69 | 70 |

Get Donate Label Id Of Ipfshash

71 | 72 | 73 | 74 | 75 |

Mint SimpleToken

76 | 77 | 78 |

Approve DAI to Growdrop contract

79 | 80 | 81 |

Approve SimpleToken to Growdrop contract

82 | 83 | 84 |

New Growdrop Contract

85 | 86 | 87 | 92 | 93 | 94 | 95 | 96 | 101 | 102 | 103 | 108 | 109 | 110 | 115 | 116 | 117 | 122 | 123 | 124 | 129 | 130 | 131 | 132 |

Start Growdrop

133 | 134 | 135 |
136 | Growdrop is : pending / running / ended 137 |
138 |
139 | New token address: 0x.... 140 |
141 |
142 | Beneficiary: 0x.... 143 |
144 |
145 | Funding Start Time: 0 146 |
147 | 148 |
149 | Funding End Time: 0 150 |
151 |
152 | New token amount: 0 153 |
154 | 155 |
156 | All balance (funds + interests to cdai) : 0 157 |
158 |
159 | All Funds: 0 160 |
161 | 162 |
163 | Your current balance (funds + interests to cdai) : 0 164 |
165 |
166 | Your funds: 0 167 |
168 | 169 |
170 | 171 | 172 |
173 |
174 | 175 | 176 |
177 | 178 |
179 | 180 | 185 | 186 |
187 | 188 |
189 |
190 |

Your minted events

191 |
192 |
193 |

Your redeemed events

194 |
195 |
196 | 197 | 219 | 220 | 230 | 231 | 241 |

242 | Hint: open the browser developer console to view any 243 | errors and warnings. 244 |

245 | 246 | 247 | 248 | -------------------------------------------------------------------------------- /contracts/DonateToken.sol: -------------------------------------------------------------------------------- 1 | 2 | pragma solidity ^0.5.11; 3 | 4 | import "./ERC721.sol"; 5 | import "./Growdrop.sol"; 6 | 7 | /** 8 | * @dev Implementation of ERC721 token which is used as donation deed, 9 | * and includes storing IPFS hash to identify Growdrop's donation. 10 | */ 11 | contract DonateToken is ERC721 { 12 | /** 13 | * @notice Check whether address is admin. 14 | */ 15 | mapping(address => bool) public CheckOwner; 16 | 17 | /** 18 | * @notice Current deployed Growdrop. 19 | */ 20 | Growdrop growdrop; 21 | 22 | /** 23 | * @notice Container for IPFS hash. 24 | * @member hash digest of IPFS hash format 25 | * @member hash_function hash function code of IPFS hash format 26 | * @member size digest size of IPFS hash format 27 | */ 28 | struct Multihash { 29 | bytes32 hash; 30 | uint8 hash_function; 31 | uint8 size; 32 | } 33 | 34 | /** 35 | * @notice Container for ERC721 token's donation information. 36 | * @member supporter address of Growdrop investor 37 | * @member beneficiary address of Growdrop investee or beneficiary 38 | * @member tokenAddress address of Growdrop's funding token 39 | * @member tokenAmount accrued interest amount of Growdrop investor 40 | * @member donateId Growdrop's donation identifier 41 | */ 42 | struct DonateInfo { 43 | address supporter; 44 | address beneficiary; 45 | address tokenAddress; 46 | uint256 tokenAmount; 47 | uint256 donateId; 48 | } 49 | 50 | /** 51 | * @notice Accrued interest amount from supporter to beneficiary with token address and Growdrop's donation identifier. 52 | */ 53 | mapping( 54 | address => mapping( 55 | address => mapping( 56 | address => mapping( 57 | uint256 => uint256)))) public DonateInfoToTokenAmount; 58 | 59 | /** 60 | * @notice Donation information of ERC721 token identifier. 61 | */ 62 | mapping(uint256 => DonateInfo) private TokenIdToDonateInfo; 63 | 64 | /** 65 | * @notice IPFS hash of Growdrop's donation identifier. 66 | */ 67 | mapping(uint256 => Multihash) private DonateIdToMultihash; 68 | 69 | /** 70 | * @notice Owner of Growdrop's donation identifier. 71 | */ 72 | mapping(uint256 => address) public DonateIdOwner; 73 | 74 | /** 75 | * @notice Growdrop's donation identifier of IPFS hash. 76 | */ 77 | mapping( 78 | bytes32 => mapping( 79 | uint8 => mapping ( 80 | uint8 => uint256))) public MultihashToDonateId; 81 | 82 | /** 83 | * @notice Event emitted when IPFS hash is stored. 84 | */ 85 | event DonateEvent( 86 | uint256 indexed event_idx, 87 | address indexed from_address, 88 | uint256 indexed donate_id, 89 | bytes32 hash, 90 | uint8 hash_function, 91 | uint8 size 92 | ); 93 | 94 | /** 95 | * @dev Constructor, storing deployer as admin, 96 | * and storing deployed Growdrop address. 97 | * @param _Growdrop Growdrop's deployed address 98 | */ 99 | constructor (address payable _Growdrop) public { 100 | CheckOwner[msg.sender] = true; 101 | growdrop = Growdrop(_Growdrop); 102 | } 103 | 104 | /** 105 | * @dev Adds new admin address 106 | * @param _Owner new admin address 107 | */ 108 | function addOwner(address _Owner) public { 109 | require(CheckOwner[msg.sender], "not owner"); 110 | CheckOwner[_Owner] = !CheckOwner[_Owner]; 111 | } 112 | 113 | /** 114 | * @dev Set new Growdrop's deployed address. 115 | * @param _Growdrop new Growdrop's deployed address 116 | */ 117 | function setGrowdrop(address payable _Growdrop) public { 118 | require(CheckOwner[msg.sender], "not owner"); 119 | growdrop = Growdrop(_Growdrop); 120 | } 121 | 122 | /** 123 | * @dev Stores new IPFS hash as a donation identifier 124 | * and owner as caller. 125 | * 126 | * Emits {Growdrop-DonateAction} event indicating owner and donation identifier as 'donateId'. 127 | * 128 | * @param _hash digest of IPFS hash format 129 | * @param hash_function hash function code of IPFS hash format 130 | * @param size digest size of IPFS hash format 131 | */ 132 | function setMultihash(bytes32 _hash, uint8 hash_function, uint8 size) public { 133 | uint256 donateId = uint256(keccak256(abi.encode(_hash, hash_function, size))); 134 | MultihashToDonateId[_hash][hash_function][size] = donateId; 135 | DonateIdToMultihash[donateId] = Multihash(_hash,hash_function,size); 136 | DonateIdOwner[donateId] = msg.sender; 137 | 138 | uint256 eventIdx = growdrop.emitDonateActionEvent( 139 | msg.sender, 140 | address(0x0), 141 | address(0x0), 142 | address(0x0), 143 | address(0x0), 144 | donateId, 145 | 0, 146 | 0, 147 | 2 148 | ); 149 | emit DonateEvent(eventIdx, msg.sender, donateId, _hash, hash_function, size); 150 | } 151 | 152 | /** 153 | * @dev Mint a ERC721 token to 'supporter'. 154 | * 155 | * Emits {Growdrop-DonateAction} event indicating new ERC721 token information. 156 | * 157 | * @param supporter address of Growdrop's investor 158 | * @param beneficiary address of Growdrop's investee or 'beneficiary' 159 | * @param token address of Growdrop's funding token 160 | * @param amount accrued interest amount of investor 161 | * @param donateId Growdrop's donation identifier 162 | * @return tokenId new ERC721 token's identifier 163 | */ 164 | function mint(address supporter, address beneficiary, address token, uint256 amount, uint256 donateId) public returns (uint256) { 165 | require(msg.sender==address(growdrop), "not growdrop contract"); 166 | if(amount==0) return 0; 167 | 168 | uint256 tokenId = uint256(keccak256(abi.encode(supporter, beneficiary, token, amount, donateId))); 169 | TokenIdToDonateInfo[tokenId] = DonateInfo(supporter,beneficiary,token,amount,donateId); 170 | 171 | _mint(supporter, tokenId); 172 | 173 | DonateInfoToTokenAmount[supporter][beneficiary][token][donateId] = DonateInfoToTokenAmount[supporter][beneficiary][token][donateId].add(amount); 174 | growdrop.emitDonateActionEvent(address(0x0), supporter, supporter, beneficiary, token, donateId, tokenId, amount, 0); 175 | return tokenId; 176 | } 177 | 178 | /** 179 | * @dev Transfer 'tokenId' ERC721 token from '_from' to 'to'. 180 | * After transferring token, 'DonateInfoToTokenAmount' changes. 181 | * This does not change 'DonateInfo' of ERC721 token. 182 | * 183 | * Emits {Growdrop-DonateAction} event indicating transfer of ERC721 token. 184 | * 185 | * @param _from address of token owner 186 | * @param to address of token receiver 187 | * @param tokenId identifier of token 188 | */ 189 | function transferFrom(address _from, address to, uint256 tokenId) public { 190 | super.transferFrom(_from,to,tokenId); 191 | setInfoToTokenId(_from,to,tokenId); 192 | } 193 | 194 | /** 195 | * @dev Safe Transfer 'tokenId' ERC721 token from '_from' to 'to'. 196 | * After transferring token, 'DonateInfoToTokenAmount' changes. 197 | * This does not change 'DonateInfo' of ERC721 token. 198 | * 199 | * Emits {Growdrop-DonateAction} event indicating transfer of ERC721 token. 200 | * 201 | * @param _from address of token owner 202 | * @param to address of token receiver 203 | * @param tokenId identifier of token 204 | */ 205 | function safeTransferFrom(address _from, address to, uint256 tokenId, bytes memory _data) public { 206 | super.safeTransferFrom(_from,to,tokenId,_data); 207 | setInfoToTokenId(_from,to,tokenId); 208 | } 209 | 210 | /** 211 | * @dev Changes donation amount recalculated with '_from' and 'to'. 212 | * 213 | * Emits {Growdrop-DonateAction} event. 214 | * 215 | * @param _from address of token owner 216 | * @param to address of token receiver 217 | * @param tokenId identifier of token 218 | */ 219 | function setInfoToTokenId(address _from, address to, uint256 tokenId) private { 220 | DonateInfo memory donateInfo = TokenIdToDonateInfo[tokenId]; 221 | 222 | DonateInfoToTokenAmount[_from][donateInfo.beneficiary][donateInfo.tokenAddress][donateInfo.donateId] = DonateInfoToTokenAmount[_from][donateInfo.beneficiary][donateInfo.tokenAddress][donateInfo.donateId].sub(donateInfo.tokenAmount); 223 | DonateInfoToTokenAmount[to][donateInfo.beneficiary][donateInfo.tokenAddress][donateInfo.donateId] = DonateInfoToTokenAmount[to][donateInfo.beneficiary][donateInfo.tokenAddress][donateInfo.donateId].add(donateInfo.tokenAmount); 224 | growdrop.emitDonateActionEvent(_from, 225 | to, 226 | donateInfo.supporter, 227 | donateInfo.beneficiary, 228 | donateInfo.tokenAddress, 229 | donateInfo.donateId, 230 | tokenId, 231 | donateInfo.tokenAmount, 232 | 1); 233 | } 234 | 235 | /** 236 | * @dev Get donation information of ERC721 token identifier. 237 | * @param tokenId identifier of token 238 | * @return donateInfo.supporter stored investor address of 'tokenId' 239 | * @return donateInfo.beneficiary stored investee or beneficiary address of 'tokenId' 240 | * @return donateInfo.tokenAddress stored funding token's address of 'tokenId' 241 | * @return donateInfo.tokenAmount stored accrued interest amount of 'tokenId' 242 | * @return donateInfo.donateId Growdrop's donation identifier of 'tokenId' 243 | */ 244 | function getDonateInfo(uint256 tokenId) public view returns (address, address, address, uint256, uint256) { 245 | DonateInfo memory donateInfo = TokenIdToDonateInfo[tokenId]; 246 | return (donateInfo.supporter, 247 | donateInfo.beneficiary, 248 | donateInfo.tokenAddress, 249 | donateInfo.tokenAmount, 250 | donateInfo.donateId); 251 | } 252 | 253 | /** 254 | * @dev Get stored IPFS hash of Growdrop's donation identifier. 255 | * @param donateId Growdrop's donation identifier 256 | * @return multihash.hash stored digest of IPFS hash format 257 | * @return multihash.hash_function stored hash function code of IPFS hash format 258 | * @return multihash.size stored digest size of IPFS hash format 259 | */ 260 | function getMultihash(uint256 donateId) public view returns (bytes32, uint8, uint8) { 261 | Multihash memory multihash = DonateIdToMultihash[donateId]; 262 | return (multihash.hash, multihash.hash_function, multihash.size); 263 | } 264 | } 265 | -------------------------------------------------------------------------------- /contracts/ERC721.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | 3 | import "./Context.sol"; 4 | import "./IERC721.sol"; 5 | import "./IERC721Receiver.sol"; 6 | import "./SafeMath.sol"; 7 | import "./Address.sol"; 8 | import "./Counters.sol"; 9 | import "./ERC165.sol"; 10 | 11 | /** 12 | * @title ERC721 Non-Fungible Token Standard basic implementation 13 | * @dev see https://eips.ethereum.org/EIPS/eip-721 14 | */ 15 | contract ERC721 is Context, ERC165, IERC721 { 16 | using SafeMath for uint256; 17 | using Address for address; 18 | using Counters for Counters.Counter; 19 | 20 | // Equals to `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))` 21 | // which can be also obtained as `IERC721Receiver(0).onERC721Received.selector` 22 | bytes4 private constant _ERC721_RECEIVED = 0x150b7a02; 23 | 24 | // Mapping from token ID to owner 25 | mapping (uint256 => address) private _tokenOwner; 26 | 27 | // Mapping from token ID to approved address 28 | mapping (uint256 => address) private _tokenApprovals; 29 | 30 | // Mapping from owner to number of owned token 31 | mapping (address => Counters.Counter) private _ownedTokensCount; 32 | 33 | // Mapping from owner to operator approvals 34 | mapping (address => mapping (address => bool)) private _operatorApprovals; 35 | 36 | /* 37 | * bytes4(keccak256('balanceOf(address)')) == 0x70a08231 38 | * bytes4(keccak256('ownerOf(uint256)')) == 0x6352211e 39 | * bytes4(keccak256('approve(address,uint256)')) == 0x095ea7b3 40 | * bytes4(keccak256('getApproved(uint256)')) == 0x081812fc 41 | * bytes4(keccak256('setApprovalForAll(address,bool)')) == 0xa22cb465 42 | * bytes4(keccak256('isApprovedForAll(address,address)')) == 0xe985e9c5 43 | * bytes4(keccak256('transferFrom(address,address,uint256)')) == 0x23b872dd 44 | * bytes4(keccak256('safeTransferFrom(address,address,uint256)')) == 0x42842e0e 45 | * bytes4(keccak256('safeTransferFrom(address,address,uint256,bytes)')) == 0xb88d4fde 46 | * 47 | * => 0x70a08231 ^ 0x6352211e ^ 0x095ea7b3 ^ 0x081812fc ^ 48 | * 0xa22cb465 ^ 0xe985e9c ^ 0x23b872dd ^ 0x42842e0e ^ 0xb88d4fde == 0x80ac58cd 49 | */ 50 | bytes4 private constant _INTERFACE_ID_ERC721 = 0x80ac58cd; 51 | 52 | constructor () public { 53 | // register the supported interfaces to conform to ERC721 via ERC165 54 | _registerInterface(_INTERFACE_ID_ERC721); 55 | } 56 | 57 | /** 58 | * @dev Gets the balance of the specified address. 59 | * @param owner address to query the balance of 60 | * @return uint256 representing the amount owned by the passed address 61 | */ 62 | function balanceOf(address owner) public view returns (uint256) { 63 | require(owner != address(0), "ERC721: balance query for the zero address"); 64 | 65 | return _ownedTokensCount[owner].current(); 66 | } 67 | 68 | /** 69 | * @dev Gets the owner of the specified token ID. 70 | * @param tokenId uint256 ID of the token to query the owner of 71 | * @return address currently marked as the owner of the given token ID 72 | */ 73 | function ownerOf(uint256 tokenId) public view returns (address) { 74 | address owner = _tokenOwner[tokenId]; 75 | require(owner != address(0), "ERC721: owner query for nonexistent token"); 76 | 77 | return owner; 78 | } 79 | 80 | /** 81 | * @dev Approves another address to transfer the given token ID 82 | * The zero address indicates there is no approved address. 83 | * There can only be one approved address per token at a given time. 84 | * Can only be called by the token owner or an approved operator. 85 | * @param to address to be approved for the given token ID 86 | * @param tokenId uint256 ID of the token to be approved 87 | */ 88 | function approve(address to, uint256 tokenId) public { 89 | address owner = ownerOf(tokenId); 90 | require(to != owner, "ERC721: approval to current owner"); 91 | 92 | require(_msgSender() == owner || isApprovedForAll(owner, _msgSender()), 93 | "ERC721: approve caller is not owner nor approved for all" 94 | ); 95 | 96 | _tokenApprovals[tokenId] = to; 97 | emit Approval(owner, to, tokenId); 98 | } 99 | 100 | /** 101 | * @dev Gets the approved address for a token ID, or zero if no address set 102 | * Reverts if the token ID does not exist. 103 | * @param tokenId uint256 ID of the token to query the approval of 104 | * @return address currently approved for the given token ID 105 | */ 106 | function getApproved(uint256 tokenId) public view returns (address) { 107 | require(_exists(tokenId), "ERC721: approved query for nonexistent token"); 108 | 109 | return _tokenApprovals[tokenId]; 110 | } 111 | 112 | /** 113 | * @dev Sets or unsets the approval of a given operator 114 | * An operator is allowed to transfer all tokens of the sender on their behalf. 115 | * @param to operator address to set the approval 116 | * @param approved representing the status of the approval to be set 117 | */ 118 | function setApprovalForAll(address to, bool approved) public { 119 | require(to != _msgSender(), "ERC721: approve to caller"); 120 | 121 | _operatorApprovals[_msgSender()][to] = approved; 122 | emit ApprovalForAll(_msgSender(), to, approved); 123 | } 124 | 125 | /** 126 | * @dev Tells whether an operator is approved by a given owner. 127 | * @param owner owner address which you want to query the approval of 128 | * @param operator operator address which you want to query the approval of 129 | * @return bool whether the given operator is approved by the given owner 130 | */ 131 | function isApprovedForAll(address owner, address operator) public view returns (bool) { 132 | return _operatorApprovals[owner][operator]; 133 | } 134 | 135 | /** 136 | * @dev Transfers the ownership of a given token ID to another address. 137 | * Usage of this method is discouraged, use {safeTransferFrom} whenever possible. 138 | * Requires the msg.sender to be the owner, approved, or operator. 139 | * @param from current owner of the token 140 | * @param to address to receive the ownership of the given token ID 141 | * @param tokenId uint256 ID of the token to be transferred 142 | */ 143 | function transferFrom(address from, address to, uint256 tokenId) public { 144 | //solhint-disable-next-line max-line-length 145 | require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved"); 146 | 147 | _transferFrom(from, to, tokenId); 148 | } 149 | 150 | /** 151 | * @dev Safely transfers the ownership of a given token ID to another address 152 | * If the target address is a contract, it must implement {IERC721Receiver-onERC721Received}, 153 | * which is called upon a safe transfer, and return the magic value 154 | * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise, 155 | * the transfer is reverted. 156 | * Requires the msg.sender to be the owner, approved, or operator 157 | * @param from current owner of the token 158 | * @param to address to receive the ownership of the given token ID 159 | * @param tokenId uint256 ID of the token to be transferred 160 | */ 161 | function safeTransferFrom(address from, address to, uint256 tokenId) public { 162 | safeTransferFrom(from, to, tokenId, ""); 163 | } 164 | 165 | /** 166 | * @dev Safely transfers the ownership of a given token ID to another address 167 | * If the target address is a contract, it must implement {IERC721Receiver-onERC721Received}, 168 | * which is called upon a safe transfer, and return the magic value 169 | * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise, 170 | * the transfer is reverted. 171 | * Requires the _msgSender() to be the owner, approved, or operator 172 | * @param from current owner of the token 173 | * @param to address to receive the ownership of the given token ID 174 | * @param tokenId uint256 ID of the token to be transferred 175 | * @param _data bytes data to send along with a safe transfer check 176 | */ 177 | function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory _data) public { 178 | require(_isApprovedOrOwner(_msgSender(), tokenId), "ERC721: transfer caller is not owner nor approved"); 179 | _safeTransferFrom(from, to, tokenId, _data); 180 | } 181 | 182 | /** 183 | * @dev Safely transfers the ownership of a given token ID to another address 184 | * If the target address is a contract, it must implement `onERC721Received`, 185 | * which is called upon a safe transfer, and return the magic value 186 | * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise, 187 | * the transfer is reverted. 188 | * Requires the msg.sender to be the owner, approved, or operator 189 | * @param from current owner of the token 190 | * @param to address to receive the ownership of the given token ID 191 | * @param tokenId uint256 ID of the token to be transferred 192 | * @param _data bytes data to send along with a safe transfer check 193 | */ 194 | function _safeTransferFrom(address from, address to, uint256 tokenId, bytes memory _data) internal { 195 | _transferFrom(from, to, tokenId); 196 | require(_checkOnERC721Received(from, to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer"); 197 | } 198 | 199 | /** 200 | * @dev Returns whether the specified token exists. 201 | * @param tokenId uint256 ID of the token to query the existence of 202 | * @return bool whether the token exists 203 | */ 204 | function _exists(uint256 tokenId) internal view returns (bool) { 205 | address owner = _tokenOwner[tokenId]; 206 | return owner != address(0); 207 | } 208 | 209 | /** 210 | * @dev Returns whether the given spender can transfer a given token ID. 211 | * @param spender address of the spender to query 212 | * @param tokenId uint256 ID of the token to be transferred 213 | * @return bool whether the msg.sender is approved for the given token ID, 214 | * is an operator of the owner, or is the owner of the token 215 | */ 216 | function _isApprovedOrOwner(address spender, uint256 tokenId) internal view returns (bool) { 217 | require(_exists(tokenId), "ERC721: operator query for nonexistent token"); 218 | address owner = ownerOf(tokenId); 219 | return (spender == owner || getApproved(tokenId) == spender || isApprovedForAll(owner, spender)); 220 | } 221 | 222 | /** 223 | * @dev Internal function to safely mint a new token. 224 | * Reverts if the given token ID already exists. 225 | * If the target address is a contract, it must implement `onERC721Received`, 226 | * which is called upon a safe transfer, and return the magic value 227 | * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise, 228 | * the transfer is reverted. 229 | * @param to The address that will own the minted token 230 | * @param tokenId uint256 ID of the token to be minted 231 | */ 232 | function _safeMint(address to, uint256 tokenId) internal { 233 | _safeMint(to, tokenId, ""); 234 | } 235 | 236 | /** 237 | * @dev Internal function to safely mint a new token. 238 | * Reverts if the given token ID already exists. 239 | * If the target address is a contract, it must implement `onERC721Received`, 240 | * which is called upon a safe transfer, and return the magic value 241 | * `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`; otherwise, 242 | * the transfer is reverted. 243 | * @param to The address that will own the minted token 244 | * @param tokenId uint256 ID of the token to be minted 245 | * @param _data bytes data to send along with a safe transfer check 246 | */ 247 | function _safeMint(address to, uint256 tokenId, bytes memory _data) internal { 248 | _mint(to, tokenId); 249 | require(_checkOnERC721Received(address(0), to, tokenId, _data), "ERC721: transfer to non ERC721Receiver implementer"); 250 | } 251 | 252 | /** 253 | * @dev Internal function to mint a new token. 254 | * Reverts if the given token ID already exists. 255 | * @param to The address that will own the minted token 256 | * @param tokenId uint256 ID of the token to be minted 257 | */ 258 | function _mint(address to, uint256 tokenId) internal { 259 | require(to != address(0), "ERC721: mint to the zero address"); 260 | require(!_exists(tokenId), "ERC721: token already minted"); 261 | 262 | _tokenOwner[tokenId] = to; 263 | _ownedTokensCount[to].increment(); 264 | 265 | emit Transfer(address(0), to, tokenId); 266 | } 267 | 268 | /** 269 | * @dev Internal function to burn a specific token. 270 | * Reverts if the token does not exist. 271 | * Deprecated, use {_burn} instead. 272 | * @param owner owner of the token to burn 273 | * @param tokenId uint256 ID of the token being burned 274 | */ 275 | function _burn(address owner, uint256 tokenId) internal { 276 | require(ownerOf(tokenId) == owner, "ERC721: burn of token that is not own"); 277 | 278 | _clearApproval(tokenId); 279 | 280 | _ownedTokensCount[owner].decrement(); 281 | _tokenOwner[tokenId] = address(0); 282 | 283 | emit Transfer(owner, address(0), tokenId); 284 | } 285 | 286 | /** 287 | * @dev Internal function to burn a specific token. 288 | * Reverts if the token does not exist. 289 | * @param tokenId uint256 ID of the token being burned 290 | */ 291 | function _burn(uint256 tokenId) internal { 292 | _burn(ownerOf(tokenId), tokenId); 293 | } 294 | 295 | /** 296 | * @dev Internal function to transfer ownership of a given token ID to another address. 297 | * As opposed to {transferFrom}, this imposes no restrictions on msg.sender. 298 | * @param from current owner of the token 299 | * @param to address to receive the ownership of the given token ID 300 | * @param tokenId uint256 ID of the token to be transferred 301 | */ 302 | function _transferFrom(address from, address to, uint256 tokenId) internal { 303 | require(ownerOf(tokenId) == from, "ERC721: transfer of token that is not own"); 304 | require(to != address(0), "ERC721: transfer to the zero address"); 305 | 306 | _clearApproval(tokenId); 307 | 308 | _ownedTokensCount[from].decrement(); 309 | _ownedTokensCount[to].increment(); 310 | 311 | _tokenOwner[tokenId] = to; 312 | 313 | emit Transfer(from, to, tokenId); 314 | } 315 | 316 | /** 317 | * @dev Internal function to invoke {IERC721Receiver-onERC721Received} on a target address. 318 | * The call is not executed if the target address is not a contract. 319 | * 320 | * This function is deprecated. 321 | * @param from address representing the previous owner of the given token ID 322 | * @param to target address that will receive the tokens 323 | * @param tokenId uint256 ID of the token to be transferred 324 | * @param _data bytes optional data to send along with the call 325 | * @return bool whether the call correctly returned the expected magic value 326 | */ 327 | function _checkOnERC721Received(address from, address to, uint256 tokenId, bytes memory _data) 328 | internal returns (bool) 329 | { 330 | if (!to.isContract()) { 331 | return true; 332 | } 333 | 334 | bytes4 retval = IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, _data); 335 | return (retval == _ERC721_RECEIVED); 336 | } 337 | 338 | /** 339 | * @dev Private function to clear current approval of a given token ID. 340 | * @param tokenId uint256 ID of the token to be transferred 341 | */ 342 | function _clearApproval(uint256 tokenId) private { 343 | if (_tokenApprovals[tokenId] != address(0)) { 344 | _tokenApprovals[tokenId] = address(0); 345 | } 346 | } 347 | } -------------------------------------------------------------------------------- /contracts/Tokenswap.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.11; 2 | 3 | import "./EIP20Interface.sol"; 4 | import "./UniswapFactoryInterface.sol"; 5 | import "./UniswapExchangeInterface.sol"; 6 | import "./KyberNetworkProxyInterface.sol"; 7 | import "./Growdrop.sol"; 8 | 9 | /** 10 | * @dev Implementation of ERC20 token adding liquidity, swapping and transferring with KyberSwap and Uniswap. 11 | */ 12 | contract Tokenswap { 13 | 14 | /** 15 | * @notice Current UniswapFactory. 16 | */ 17 | UniswapFactoryInterface UniswapFactory; 18 | 19 | /** 20 | * @notice Current KyberNetworkProxy. 21 | */ 22 | KyberNetworkProxyInterface KyberNetworkProxy; 23 | 24 | /** 25 | * @notice UniswapExchange for adding liquidity one time. 26 | */ 27 | UniswapExchangeInterface UniswapAddPoolTokenExchange; 28 | 29 | /** 30 | * @notice Current Kyber Eth Token. 31 | */ 32 | EIP20Interface KyberEthToken; 33 | 34 | /** 35 | * @notice ERC20 token to change as ether for adding liquidity one time. 36 | */ 37 | EIP20Interface EthSwapToken; 38 | 39 | /** 40 | * @notice ERC20 token for adding liquidity one time. 41 | */ 42 | EIP20Interface UniswapAddPoolToken; 43 | 44 | /** 45 | * @notice Current deployed Growdrop. 46 | */ 47 | Growdrop growdrop; 48 | 49 | /** 50 | * @notice Address of added liquidity receiver for adding liquidity one time. 51 | */ 52 | address Beneficiary; 53 | 54 | /** 55 | * @notice Check whether address is admin. 56 | */ 57 | mapping(address => bool) public CheckOwner; 58 | 59 | /** 60 | * @notice Amount of ERC20 token to add liquidity to Uniswap for one time. 61 | */ 62 | uint256 UniswapAddPoolTokenAmount; 63 | 64 | /** 65 | * @notice Amount of ERC20 token to swap ether for adding liquidity to Uniswap one time. 66 | */ 67 | uint256 EthSwapTokenAmount; 68 | 69 | /** 70 | * @notice Constant for calculation. 71 | */ 72 | uint256 constant Precision = 10**18; 73 | 74 | /** 75 | * @notice Minimum amount to swap or transfer to KyberSwap. 76 | */ 77 | uint256 public KyberSwapMinimum; 78 | 79 | /** 80 | * @notice Check whether token is registered by admin. 81 | */ 82 | mapping (address => bool) CheckTokenAddress; 83 | 84 | 85 | /** 86 | * @dev Constructor, storing deployer as admin, 87 | * and setting UniswapFactory, KyberNetworkProxy and 'KyberSwapMinimum' 88 | * @param _Growdrop current Growdrop contract 89 | * @param uniswapFactoryAddress current UniswapFactory contract 90 | * @param kyberNetworkProxyAddress current KyberNetworkProxy contract 91 | * @param kyberSwapMinimum minimum amount to swap or transfer to KyberSwap 92 | */ 93 | constructor ( 94 | address payable _Growdrop, 95 | address uniswapFactoryAddress, 96 | address kyberNetworkProxyAddress, 97 | uint256 kyberSwapMinimum) public { 98 | 99 | CheckOwner[msg.sender] = true; 100 | 101 | growdrop = Growdrop(_Growdrop); 102 | UniswapFactory = UniswapFactoryInterface(uniswapFactoryAddress); 103 | KyberNetworkProxy = KyberNetworkProxyInterface(kyberNetworkProxyAddress); 104 | 105 | KyberSwapMinimum = kyberSwapMinimum; 106 | 107 | KyberEthToken = EIP20Interface(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE); 108 | } 109 | 110 | /** 111 | * @dev Adds new admin address 112 | * @param _Owner new admin address 113 | */ 114 | function addOwner(address _Owner) public { 115 | require(CheckOwner[msg.sender], "not owner"); 116 | CheckOwner[_Owner] = !CheckOwner[_Owner]; 117 | } 118 | 119 | /** 120 | * @dev Set new Growdrop's deployed address. 121 | * @param _Growdrop new Growdrop's deployed address 122 | */ 123 | function setGrowdrop(address payable _Growdrop) public { 124 | require(CheckOwner[msg.sender], "not owner"); 125 | growdrop = Growdrop(_Growdrop); 126 | } 127 | 128 | /** 129 | * @dev Register new ERC20 token address to swap and transfer with KyberSwap and Uniswap. 130 | * @param tokenAddress new ERC20 token address to register 131 | */ 132 | function addTokenAddress(address tokenAddress) public { 133 | require(CheckOwner[msg.sender], "not owner"); 134 | CheckTokenAddress[tokenAddress] = true; 135 | } 136 | 137 | /** 138 | * @dev Swap ether to ERC20 token with KyberSwap. 139 | * If cannot be swapped, revert. 140 | * @param tokenAddress ERC20 token address to receive 141 | */ 142 | function kyberswapEthToToken(address tokenAddress) public payable { 143 | uint256 minConversionRate; 144 | uint256 slippageRate; 145 | (minConversionRate,slippageRate) = KyberNetworkProxy.getExpectedRate(KyberEthToken, EIP20Interface(tokenAddress), msg.value); 146 | require(slippageRate != 0, "kyberswap slippageRate error"); 147 | uint256 destAmount = KyberNetworkProxy.swapEtherToToken.value(msg.value)(EIP20Interface(tokenAddress), minConversionRate); 148 | require(EIP20Interface(tokenAddress).transfer(msg.sender, destAmount), "transfer token error"); 149 | } 150 | 151 | /** 152 | * @dev Get UniswapExchange of ERC20 token. 153 | * If UniswapExchange does not exist, then create UniswapExchange. 154 | * @param token ERC20 token address to get UniswapExchange 155 | * @return tokenExchangeAddr created or existing UniswapExchange address 156 | */ 157 | function getUniswapExchangeAddress(address token) private returns (address) { 158 | address tokenExchangeAddr = UniswapFactory.getExchange(token); 159 | if(tokenExchangeAddr==address(0x0)) { 160 | tokenExchangeAddr = UniswapFactory.createExchange(token); 161 | } 162 | return tokenExchangeAddr; 163 | } 164 | 165 | /** 166 | * @dev Swap ERC20 token to ERC20 token. 167 | * If sending ERC20 token is not registered, revert. 168 | * If receiving ERC20 token is not registered, revert. 169 | * Swaps sending ERC20 token to ether and swaps ether to receiving ERC20 token. 170 | * @param fromTokenAddress ERC20 token address to send token to UniswapExchange 171 | * @param toTokenAddress ERC20 token address to receive token from UniswapExchange 172 | * @param fromTokenAmount ERC20 token amount to send token to UniswapExchange 173 | * @return token_amount ERC20 token amount to receive token from UniswapExchange 174 | */ 175 | function uniswapToken(address fromTokenAddress, address toTokenAddress, uint256 fromTokenAmount) public returns (uint256) { 176 | require(CheckTokenAddress[fromTokenAddress] && CheckTokenAddress[toTokenAddress], "not checked token"); 177 | 178 | UniswapExchangeInterface fromTokenEx = UniswapExchangeInterface(getUniswapExchangeAddress(fromTokenAddress)); 179 | UniswapExchangeInterface toTokenEx = UniswapExchangeInterface(getUniswapExchangeAddress(toTokenAddress)); 180 | EIP20Interface fromToken = EIP20Interface(fromTokenAddress); 181 | 182 | require(fromToken.transferFrom(msg.sender, address(this), fromTokenAmount), "transfer token error"); 183 | uint256 eth_price = fromTokenEx.getTokenToEthInputPrice(fromTokenAmount); 184 | require(fromToken.approve(address(fromTokenEx), fromTokenAmount), "approve token error"); 185 | require(eth_price == fromTokenEx.tokenToEthSwapInput(fromTokenAmount,eth_price,1739591241), "token to eth error"); 186 | uint256 token_amount = toTokenEx.getEthToTokenInputPrice(eth_price); 187 | require(token_amount == toTokenEx.ethToTokenTransferInput.value(eth_price)(token_amount,1739591241,msg.sender), "eth to token error"); 188 | return token_amount; 189 | } 190 | 191 | function Add(uint256 a, uint256 b) private pure returns (uint256) { 192 | require(a+b>=a, "add overflow"); 193 | return a+b; 194 | } 195 | 196 | function Sub(uint256 a, uint256 b) private pure returns (uint256) { 197 | require(a>=b, "subtract overflow"); 198 | return a-b; 199 | } 200 | 201 | function MulAndDiv(uint256 a, uint256 b, uint256 c) private pure returns (uint256) { 202 | uint256 temp = a*b; 203 | require(temp/b==a && c>0, "arithmetic error"); 204 | return temp/c; 205 | } 206 | 207 | /** 208 | * @dev Add liquidity pool to UniswapExchange. 209 | * If there is no UniswapExchange, create it. 210 | * @param ethSwapTokenAddress ERC20 token address to swap ether to add liquidity 211 | * @param uniswapAddPoolTokenAddress ERC20 token address to add liquidity 212 | * @param beneficiary address to receive liquidity 213 | * @param ethSwapTokenAmount ERC20 token amount to swap ether to add liquidity 214 | * @param uniswapAddPoolTokenAmount ERC20 token amount to add liquidity 215 | * @return if adding liquidity pool success then return true, else return false 216 | */ 217 | function addPoolToUniswap( 218 | address ethSwapTokenAddress, 219 | address uniswapAddPoolTokenAddress, 220 | address beneficiary, 221 | uint256 ethSwapTokenAmount, 222 | uint256 uniswapAddPoolTokenAmount) public returns (bool) { 223 | require(address(growdrop)==msg.sender, "not growdrop contract"); 224 | 225 | UniswapAddPoolTokenExchange = UniswapExchangeInterface(getUniswapExchangeAddress(uniswapAddPoolTokenAddress)); 226 | 227 | EthSwapToken = EIP20Interface(ethSwapTokenAddress); 228 | UniswapAddPoolToken = EIP20Interface(uniswapAddPoolTokenAddress); 229 | Beneficiary = beneficiary; 230 | EthSwapTokenAmount = ethSwapTokenAmount; 231 | UniswapAddPoolTokenAmount = uniswapAddPoolTokenAmount; 232 | 233 | if(ethSwapTokenAmount==0) { 234 | return false; 235 | } 236 | 237 | uint256 minConversionRate; 238 | uint256 slippageRate; 239 | (minConversionRate,slippageRate) = KyberNetworkProxy.getExpectedRate(EthSwapToken, KyberEthToken, EthSwapTokenAmount); 240 | uint256 TokenToEthAmount = MulAndDiv(minConversionRate, EthSwapTokenAmount, Precision); 241 | //if 'EthSwapTokenAmount' cannot be swapped to ether, return false 242 | if(TokenToEthAmount<=KyberSwapMinimum || slippageRate == 0) { 243 | return false; 244 | } 245 | 246 | uint256 min_liquidity; 247 | uint256 eth_reserve = address(UniswapAddPoolTokenExchange).balance; 248 | uint256 total_liquidity = UniswapAddPoolTokenExchange.totalSupply(); 249 | 250 | //if there is no liquidity in UniswapExchange, add both tokens all to liquidity 251 | if (total_liquidity==0) { 252 | min_liquidity = Add(eth_reserve,TokenToEthAmount); 253 | changeTokenToEth_AddLiquidity_Transfer( 254 | minConversionRate, 255 | EthSwapTokenAmount, 256 | TokenToEthAmount, 257 | min_liquidity, 258 | UniswapAddPoolTokenAmount 259 | ); 260 | } else { 261 | //if not, calculate ether and token amount to add liquidity 262 | uint256 max_token; 263 | uint256 token_reserve = UniswapAddPoolToken.balanceOf(address(UniswapAddPoolTokenExchange)); 264 | max_token = Add(MulAndDiv(token_reserve, TokenToEthAmount, eth_reserve),1); 265 | min_liquidity = MulAndDiv(total_liquidity, TokenToEthAmount, eth_reserve); 266 | //if max token amount calculated by calculated ether is bigger than current token amount, recalculate 267 | if(max_token>UniswapAddPoolTokenAmount) { 268 | //recalculate ether amount based on current token amount 269 | uint256 lowerEthAmount = MulAndDiv(eth_reserve, UniswapAddPoolTokenAmount-1, token_reserve); 270 | 271 | //recalculate token amount based on recalculated ether amount 272 | uint256 lowerTokenAmount; 273 | if(lowerEthAmount<=KyberSwapMinimum) { 274 | (minConversionRate, lowerTokenAmount) = calculateTokenAmountByEthAmountKyber(address(0x0), KyberSwapMinimum); 275 | } else { 276 | (minConversionRate, lowerTokenAmount) = calculateTokenAmountByEthAmountKyber(address(0x0), lowerEthAmount); 277 | } 278 | //if recalculated ether swapping token cannot be swapped or cannot be added to liquidity, return false 279 | if(minConversionRate==0 || lowerEthAmount<1000000000) { 280 | return false; 281 | } 282 | 283 | max_token = Add(MulAndDiv(token_reserve, lowerEthAmount, eth_reserve),1); 284 | min_liquidity = MulAndDiv(total_liquidity, lowerEthAmount, eth_reserve); 285 | 286 | //if not, add liquidity with recalculated ether and token 287 | changeTokenToEth_AddLiquidity_Transfer( 288 | minConversionRate, 289 | lowerTokenAmount, 290 | lowerEthAmount, 291 | min_liquidity, 292 | max_token 293 | ); 294 | 295 | require(EthSwapToken.transfer(Beneficiary, EthSwapTokenAmount-lowerTokenAmount), "transfer left interests error"); 296 | require(UniswapAddPoolToken.transfer(Beneficiary, UniswapAddPoolTokenAmount-max_token), "transfer left growdrop error"); 297 | } else { 298 | //if not, add all tokens and ether to liquidity 299 | changeTokenToEth_AddLiquidity_Transfer( 300 | minConversionRate, 301 | EthSwapTokenAmount, 302 | TokenToEthAmount, 303 | min_liquidity, 304 | max_token 305 | ); 306 | require(UniswapAddPoolToken.transfer(Beneficiary, UniswapAddPoolTokenAmount-max_token), "transfer left growdrop error"); 307 | } 308 | } 309 | return true; 310 | } 311 | 312 | /** 313 | * @dev Calculate ERC20 token amount to send with ether amount input using KyberSwap 314 | * @param ethSwapTokenAddress ERC20 token address to swap ether 315 | * @param ethAmount ether amount to use as input 316 | * @return minConversionRate KyberSwap's conversion rate. if can be swapped, return value with not 0, else return 0 317 | * @return tokenAmount KyberSwap's sending token amount. if can be swapped, return value with not 0, else return 0 318 | */ 319 | function calculateTokenAmountByEthAmountKyber(address ethSwapTokenAddress, uint256 ethAmount) private view returns (uint256, uint256) { 320 | EIP20Interface ethSwapToken = EthSwapToken; 321 | if(ethSwapTokenAddress!=address(0x0)) { 322 | ethSwapToken = EIP20Interface(ethSwapTokenAddress); 323 | } 324 | 325 | uint256 minConversionRate; 326 | uint256 slippageRate; 327 | (minConversionRate,slippageRate) = KyberNetworkProxy.getExpectedRate(ethSwapToken, KyberEthToken, Precision); 328 | 329 | uint256 precisionMinConversionRate = minConversionRate; 330 | uint256 tokenAmount = MulAndDiv(ethAmount, Precision, minConversionRate); 331 | 332 | (minConversionRate,slippageRate) = KyberNetworkProxy.getExpectedRate(ethSwapToken, KyberEthToken, tokenAmount); 333 | if(slippageRate==0) { 334 | return (0, 0); 335 | } 336 | 337 | uint256 approximateEth = MulAndDiv(tokenAmount,minConversionRate,Precision); 338 | uint256 reapproximateEth = ethAmount*2; 339 | require(reapproximateEth>ethAmount, "multiply overflow"); 340 | 341 | tokenAmount = MulAndDiv( 342 | Sub(reapproximateEth,approximateEth), 343 | Precision, 344 | precisionMinConversionRate 345 | ); 346 | (minConversionRate,slippageRate) = KyberNetworkProxy.getExpectedRate(ethSwapToken, KyberEthToken, tokenAmount); 347 | reapproximateEth = MulAndDiv(minConversionRate, tokenAmount, Precision); 348 | 349 | if(slippageRate==0 || reapproximateEth bool) public CheckOwner; 24 | 25 | /** 26 | * @notice Current DonateToken contract 27 | */ 28 | DonateTokenInterface public DonateToken; 29 | 30 | /** 31 | * @notice Current Tokenswap contract 32 | */ 33 | TokenswapInterface public Tokenswap; 34 | 35 | /** 36 | * @notice Growdrop's sequential number 37 | */ 38 | uint256 public GrowdropCount; 39 | 40 | /** 41 | * @notice Growdrop event's sequential number 42 | */ 43 | uint256 public EventIdx; 44 | 45 | /** 46 | * @notice Address of receiving accrued interest address by Growdrop's identifier 47 | */ 48 | mapping(uint256 => address) public Beneficiary; 49 | 50 | /** 51 | * @notice Compound CToken amount per investor address by Growdrop's identifier 52 | */ 53 | mapping(uint256 => mapping(address => uint256)) public CTokenPerAddress; 54 | 55 | /** 56 | * @notice Funded ERC20 token amount per investor address by Growdrop's identifier 57 | */ 58 | mapping(uint256 => mapping(address => uint256)) public InvestAmountPerAddress; 59 | 60 | /** 61 | * @notice Actual amount per investor address by Growdrop's identifier 62 | */ 63 | mapping(uint256 => mapping(address => uint256)) public ActualPerAddress; 64 | 65 | /** 66 | * @notice Actual Compound CToken amount per investor address by Growdrop's identifier 67 | */ 68 | mapping(uint256 => mapping(address => uint256)) public ActualCTokenPerAddress; 69 | 70 | /** 71 | * @notice Check whether address withdrawn or not by Growdrop's identifier 72 | */ 73 | mapping(uint256 => mapping(address => bool)) public WithdrawOver; 74 | 75 | /** 76 | * @notice ERC20 token amount to send to investors by Growdrop's identifier 77 | */ 78 | mapping(uint256 => uint256) public GrowdropAmount; 79 | 80 | /** 81 | * @notice Growdrop's start timestamp by Growdrop's identifier 82 | */ 83 | mapping(uint256 => uint256) public GrowdropStartTime; 84 | 85 | /** 86 | * @notice Growdrop's end timestamp by Growdrop's identifier 87 | */ 88 | mapping(uint256 => uint256) public GrowdropEndTime; 89 | 90 | /** 91 | * @notice Growdrop's total funded ERC20 token amount by Growdrop's identifier 92 | */ 93 | mapping(uint256 => uint256) public TotalMintedAmount; 94 | 95 | /** 96 | * @notice Growdrop's total Compound CToken amount by Growdrop's identifier 97 | */ 98 | mapping(uint256 => uint256) public TotalCTokenAmount; 99 | 100 | /** 101 | * @notice Growdrop's total actual amount by Growdrop's identifier 102 | */ 103 | mapping(uint256 => uint256) public TotalMintedActual; 104 | 105 | /** 106 | * @notice Growdrop's total actual Compound CToken amount by Growdrop's identifier 107 | */ 108 | mapping(uint256 => uint256) public TotalCTokenActual; 109 | 110 | /** 111 | * @notice Compound's exchange rate when Growdrop is over by Growdrop's identifier 112 | */ 113 | mapping(uint256 => uint256) public ExchangeRateOver; 114 | 115 | /** 116 | * @notice Growdrop's total accrued interest when Growdrop is over by Growdrop's identifier 117 | */ 118 | mapping(uint256 => uint256) public TotalInterestOver; 119 | 120 | /** 121 | * @notice Growdrop's total actual accrued interest when Growdrop is over by Growdrop's identifier 122 | */ 123 | mapping(uint256 => uint256) public TotalInterestOverActual; 124 | 125 | /** 126 | * @notice ERC20 token amount to add to UniswapExchange by Growdrop's identifier 127 | */ 128 | mapping(uint256 => uint256) public ToUniswapTokenAmount; 129 | 130 | /** 131 | * @notice Percentage of Growdrop's total accrued interest to add to UniswapExchage by Growdrop's identifier 132 | */ 133 | mapping(uint256 => uint256) public ToUniswapInterestRate; 134 | 135 | /** 136 | * @notice Check whether Growdrop is over by Growdrop's identifier 137 | */ 138 | mapping(uint256 => bool) public GrowdropOver; 139 | 140 | /** 141 | * @notice Check whether Growdrop is started by Growdrop's identifier 142 | */ 143 | mapping(uint256 => bool) public GrowdropStart; 144 | 145 | /** 146 | * @notice Growdrop's Donation identifier by Growdrop's identifier 147 | */ 148 | mapping(uint256 => uint256) public DonateId; 149 | 150 | /** 151 | * @notice ERC20 Token to fund by Growdrop's identifier 152 | */ 153 | mapping(uint256 => EIP20Interface) public Token; 154 | 155 | /** 156 | * @notice ERC20 Token to send to investors by Growdrop's identifier 157 | */ 158 | mapping(uint256 => EIP20Interface) public GrowdropToken; 159 | 160 | /** 161 | * @notice Compound CToken by Growdrop's identifier 162 | */ 163 | mapping(uint256 => CTokenInterface) public CToken; 164 | 165 | /** 166 | * @notice Percentage of owner fee to get from Growdrop's total accrued interest by Growdrop's identifier 167 | */ 168 | mapping(uint256 => uint256) public GrowdropOwnerFeePercent; 169 | 170 | /** 171 | * @notice Check whether Growdrop adds liquidity to UniswapExchange 172 | */ 173 | mapping(uint256 => bool) public AddToUniswap; 174 | 175 | /** 176 | * @notice Current percentage of owner fee 177 | */ 178 | uint256 public CurrentOwnerFeePercent; 179 | 180 | /** 181 | * @notice Total funded ERC20 token amount with investor address and ERC20 token address 182 | */ 183 | mapping(address => mapping(address => uint256)) public TotalUserInvestedAmount; 184 | 185 | /** 186 | * @notice Event emitted when new Growdrop is created 187 | */ 188 | event NewGrowdrop( 189 | uint256 indexed event_idx, 190 | uint256 indexed growdrop_count, 191 | address indexed from_address, 192 | uint256 timestamp 193 | ); 194 | 195 | /** 196 | * @notice Event emitted when Growdrop's event occurred 197 | */ 198 | event GrowdropAction( 199 | uint256 indexed event_idx, 200 | uint256 indexed growdrop_count, 201 | address indexed from_address, 202 | uint256 amount1, 203 | uint256 amount2, 204 | uint256 action_idx, 205 | uint256 timestamp 206 | ); 207 | 208 | /** 209 | * @notice Event emitted when Growdrop's donation ERC721 token event occurred 210 | */ 211 | event DonateAction( 212 | uint256 indexed event_idx, 213 | address indexed from_address, 214 | address indexed to_address, 215 | address supporter, 216 | address beneficiary, 217 | address token_address, 218 | uint256 donate_id, 219 | uint256 token_id, 220 | uint256 amount, 221 | uint256 action_idx, 222 | uint256 timestamp 223 | ); 224 | 225 | /** 226 | * @dev Constructor, set 'owner' and set 'CurrentOwnerFeePercent'. 227 | */ 228 | constructor () public { 229 | owner = msg.sender; 230 | CheckOwner[msg.sender] = true; 231 | CurrentOwnerFeePercent = 3; 232 | } 233 | 234 | /** 235 | * @dev Create new Growdrop. 236 | * Only Address that 'CheckOwner' is true can call. 237 | * 'GrowdropTokenAddr' cannot be tokens which is in Compound's available markets. 238 | * 239 | * Emits {NewGrowdrop} event indicating Growdrop's identifier. 240 | * 241 | * @param TokenAddr ERC20 token address to fund tokens 242 | * @param CTokenAddr Compound CToken address which is pair of 'TokenAddr' 243 | * @param GrowdropTokenAddr ERC20 token address to send tokens to investors 244 | * @param BeneficiaryAddr address to receive Growdrop's accrued interest amount 245 | * @param _GrowdropAmount ERC20 token amount to send to investors 246 | * @param GrowdropPeriod period timestamp to get funds 247 | * @param _ToUniswapTokenAmount ERC20 token amount to add to UniswapExchange. If project does not want to add liquidity to UniswapExchage at all, 0. 248 | * @param _ToUniswapInterestRate percentage of Growdrop's accrued interest amount to add liquidity to UniswapExchange. 249 | * @param _DonateId Growdrop's donation identifier. If Growdrop is donation, not 0. else 0 250 | */ 251 | function newGrowdrop( 252 | address TokenAddr, 253 | address CTokenAddr, 254 | address GrowdropTokenAddr, 255 | address BeneficiaryAddr, 256 | uint256 _GrowdropAmount, 257 | uint256 GrowdropPeriod, 258 | uint256 _ToUniswapTokenAmount, 259 | uint256 _ToUniswapInterestRate, 260 | uint256 _DonateId) public { 261 | 262 | require(CheckOwner[msg.sender]); 263 | require(DonateToken.DonateIdOwner(_DonateId)==BeneficiaryAddr || _DonateId==0); 264 | require(_ToUniswapTokenAmount==0 || (_ToUniswapInterestRate>0 && _ToUniswapInterestRate<101-CurrentOwnerFeePercent && _ToUniswapTokenAmount>1e4)); 265 | require(_DonateId!=0 || _GrowdropAmount>1e6); 266 | Add(_GrowdropAmount,_ToUniswapTokenAmount); 267 | 268 | GrowdropCount += 1; 269 | GrowdropOwnerFeePercent[GrowdropCount] = CurrentOwnerFeePercent; 270 | AddToUniswap[GrowdropCount] = _ToUniswapTokenAmount==0 ? false : true; 271 | 272 | Token[GrowdropCount] = EIP20Interface(TokenAddr); 273 | CToken[GrowdropCount] = CTokenInterface(CTokenAddr); 274 | GrowdropToken[GrowdropCount] = EIP20Interface(GrowdropTokenAddr); 275 | Beneficiary[GrowdropCount] = BeneficiaryAddr; 276 | GrowdropAmount[GrowdropCount] = _GrowdropAmount; 277 | 278 | GrowdropEndTime[GrowdropCount] = GrowdropPeriod; 279 | 280 | ToUniswapTokenAmount[GrowdropCount] = _ToUniswapTokenAmount; 281 | ToUniswapInterestRate[GrowdropCount] = _ToUniswapInterestRate; 282 | 283 | DonateId[GrowdropCount] = _DonateId; 284 | 285 | EventIdx += 1; 286 | emit NewGrowdrop(EventIdx, GrowdropCount, BeneficiaryAddr, now); 287 | } 288 | 289 | /** 290 | * @dev Start Growdrop by Growdrop's identifier. 291 | * Only 'Beneficiary' address can call. 292 | * Transfers ERC20 token amount of 'GrowdropAmount' and 'ToUniswapTokenAmount' to this contract. 293 | * 294 | * Emits {GrowdropAction} event indicating Growdrop's identifier and event information. 295 | * 296 | * @param _GrowdropCount Growdrop's identifier 297 | */ 298 | function StartGrowdrop(uint256 _GrowdropCount) public { 299 | require(msg.sender==Beneficiary[_GrowdropCount], "not beneficiary"); 300 | require(!GrowdropStart[_GrowdropCount], "already started"); 301 | GrowdropStart[_GrowdropCount] = true; 302 | 303 | if(DonateId[_GrowdropCount]==0) { 304 | require(GrowdropToken[_GrowdropCount].transferFrom(msg.sender, address(this), GrowdropAmount[_GrowdropCount]+ToUniswapTokenAmount[_GrowdropCount]), "transfer growdrop error"); 305 | } 306 | 307 | GrowdropStartTime[_GrowdropCount] = now; 308 | 309 | GrowdropEndTime[_GrowdropCount] = Add(GrowdropEndTime[_GrowdropCount], now); 310 | 311 | EventIdx += 1; 312 | emit GrowdropAction(EventIdx, _GrowdropCount, address(0x0), 0, 0, 5, now); 313 | } 314 | 315 | /** 316 | * @dev Investor funds ERC20 token to Growdrop by Growdrop's identifier. 317 | * Should be approved before call. 318 | * Funding amount will be calculated as CToken and recalculated to minimum which has same value as calculated CToken before funding. 319 | * Funding ctoken amount should be bigger than 0. 320 | * Can be funded only with Growdrop's 'Token'. 321 | * Can fund only after started and before ended. 322 | * 323 | * Emits {GrowdropAction} event indicating Growdrop's identifier and event information. 324 | * 325 | * @param _GrowdropCount Growdrop's identifier 326 | * @param Amount ERC20 token amount to fund to Growdrop 327 | */ 328 | function Mint(uint256 _GrowdropCount, uint256 Amount) public { 329 | require(GrowdropStart[_GrowdropCount], "not started"); 330 | require(now0, "amount too low"); 338 | uint256 actualAmount; 339 | uint256 actualCToken; 340 | (actualCToken, actualAmount) = toActualAmount(_toMinAmount, _exchangeRateCurrent); 341 | 342 | CTokenPerAddress[_GrowdropCount][msg.sender] = Add(CTokenPerAddress[_GrowdropCount][msg.sender], _ctoken); 343 | TotalCTokenAmount[_GrowdropCount] = Add(TotalCTokenAmount[_GrowdropCount], _ctoken); 344 | 345 | ActualCTokenPerAddress[_GrowdropCount][msg.sender] = Add(ActualCTokenPerAddress[_GrowdropCount][msg.sender], actualCToken); 346 | TotalCTokenActual[_GrowdropCount] = Add(TotalCTokenActual[_GrowdropCount], actualCToken); 347 | 348 | 349 | InvestAmountPerAddress[_GrowdropCount][msg.sender] = Add(InvestAmountPerAddress[_GrowdropCount][msg.sender], _toMinAmount); 350 | TotalMintedAmount[_GrowdropCount] = Add(TotalMintedAmount[_GrowdropCount], _toMinAmount); 351 | 352 | ActualPerAddress[_GrowdropCount][msg.sender] = Add(ActualPerAddress[_GrowdropCount][msg.sender], actualAmount); 353 | TotalMintedActual[_GrowdropCount] = Add(TotalMintedActual[_GrowdropCount], actualAmount); 354 | 355 | require(Token[_GrowdropCount].transferFrom(msg.sender, address(this), _toMinAmount), "transfer token error"); 356 | require(Token[_GrowdropCount].approve(address(CToken[_GrowdropCount]), _toMinAmount), "approve token error"); 357 | require(CToken[_GrowdropCount].mint(_toMinAmount)==0, "error in mint"); 358 | 359 | TotalUserInvestedAmount[msg.sender][address(Token[_GrowdropCount])] = Add(TotalUserInvestedAmount[msg.sender][address(Token[_GrowdropCount])],_toMinAmount); 360 | EventIdx += 1; 361 | emit GrowdropAction(EventIdx,_GrowdropCount, msg.sender, InvestAmountPerAddress[_GrowdropCount][msg.sender], CTokenPerAddress[_GrowdropCount][msg.sender], 0, now); 362 | } 363 | 364 | /** 365 | * @dev Investor refunds ERC20 token to Growdrop by Growdrop's identifier. 366 | * Refunding CToken amount should be bigger than 0. 367 | * Refunding amount calculated as CToken should be smaller than investor's funded amount calculated as CToken by Growdrop's identifier. 368 | * Can refund only after started and before ended. 369 | * 370 | * Emits {GrowdropAction} event indicating Growdrop's identifier and event information. 371 | * 372 | * @param _GrowdropCount Growdrop's identifier 373 | * @param Amount ERC20 token amount to refund to Growdrop 374 | */ 375 | function Redeem(uint256 _GrowdropCount, uint256 Amount) public { 376 | require(GrowdropStart[_GrowdropCount], "not started"); 377 | require(now0 && _ctoken<=MulAndDiv(InvestAmountPerAddress[_GrowdropCount][msg.sender], 1e18, _exchangeRateCurrent), "redeem error"); 385 | 386 | uint256 actualAmount; 387 | uint256 actualCToken; 388 | (actualCToken, actualAmount) = toActualAmount(_toMinAmount, _exchangeRateCurrent); 389 | 390 | CTokenPerAddress[_GrowdropCount][msg.sender] = Sub(CTokenPerAddress[_GrowdropCount][msg.sender], _ctoken); 391 | TotalCTokenAmount[_GrowdropCount] = Sub(TotalCTokenAmount[_GrowdropCount],_ctoken); 392 | 393 | ActualCTokenPerAddress[_GrowdropCount][msg.sender] = Sub(ActualCTokenPerAddress[_GrowdropCount][msg.sender], actualCToken); 394 | TotalCTokenActual[_GrowdropCount] = Sub(TotalCTokenActual[_GrowdropCount], actualCToken); 395 | 396 | 397 | InvestAmountPerAddress[_GrowdropCount][msg.sender] = Sub(InvestAmountPerAddress[_GrowdropCount][msg.sender], _toMinAmount); 398 | TotalMintedAmount[_GrowdropCount] = Sub(TotalMintedAmount[_GrowdropCount], _toMinAmount); 399 | 400 | ActualPerAddress[_GrowdropCount][msg.sender] = Sub(ActualPerAddress[_GrowdropCount][msg.sender], actualAmount); 401 | TotalMintedActual[_GrowdropCount] = Sub(TotalMintedActual[_GrowdropCount], actualAmount); 402 | 403 | require(CToken[_GrowdropCount].redeemUnderlying(Amount)==0, "error in redeem"); 404 | require(Token[_GrowdropCount].transfer(msg.sender, Amount), "transfer token error"); 405 | 406 | TotalUserInvestedAmount[msg.sender][address(Token[_GrowdropCount])] = Sub(TotalUserInvestedAmount[msg.sender][address(Token[_GrowdropCount])], _toMinAmount); 407 | 408 | EventIdx += 1; 409 | emit GrowdropAction(EventIdx, _GrowdropCount, msg.sender, InvestAmountPerAddress[_GrowdropCount][msg.sender], CTokenPerAddress[_GrowdropCount][msg.sender], 1, now); 410 | } 411 | 412 | /** 413 | * @dev Investor and Investee withdraws from Growdrop by Growdrop's identifier. 414 | * Investor withdraws investor's all funded ERC20 token amount and 'GrowdropToken' calculated by percentage of investor's accrued interest amount. 415 | * If Growdrop is donation, Investor withdraws investor's all funded ERC20 token amount and ERC721 token from 'DonateToken'. 416 | * Investee withdraws all investor's accrued interest if 'ToUniswap' is false. 417 | * Else add liquidity to Uniswap with 'ToUniswapInterestRate' percentage of all investor's accrued interest amount and 'ToUniswapTokenAmount' ERC20 token 418 | * and withdraw rest of all investor's accured interest amount. 419 | * If Growdrop is donation, Investee withdraws investor's all funded ERC20 token amount. 420 | * Owner fee is transferred when Investee withdraws. 421 | * Can withdraw only once per address. 422 | * Can withdraw only after ended. 423 | * 424 | * Emits {GrowdropAction} event indicating Growdrop's identifier and event information. 425 | * Emits {DonateAction} event indicating ERC721 token information from 'DonateToken' if Growdrop is donation. 426 | * 427 | * @param _GrowdropCount Growdrop's identifier 428 | * @param ToUniswap if investee wants to add liquidity to UniswapExchange, true. Else false. 429 | */ 430 | function Withdraw(uint256 _GrowdropCount, bool ToUniswap) public { 431 | require(!WithdrawOver[_GrowdropCount][msg.sender], "already done"); 432 | 433 | WithdrawOver[_GrowdropCount][msg.sender] = true; 434 | 435 | EndGrowdrop(_GrowdropCount); 436 | //If investee did not want to add to UniswapExchange, does not add to UniswapExchange. 437 | if(!AddToUniswap[_GrowdropCount]) { 438 | ToUniswap = false; 439 | } 440 | //If caller is investee 441 | if(msg.sender==Beneficiary[_GrowdropCount]) { 442 | uint256 beneficiaryinterest; 443 | bool success; 444 | if(TotalInterestOverActual[_GrowdropCount]==0) { 445 | ToUniswap=false; 446 | success=true; 447 | } 448 | uint256 OwnerFee = MulAndDiv(TotalInterestOver[_GrowdropCount], GrowdropOwnerFeePercent[_GrowdropCount], 100); 449 | if(ToUniswap) { 450 | uint256 ToUniswapInterestRateCalculated = MulAndDiv(TotalInterestOver[_GrowdropCount], ToUniswapInterestRate[_GrowdropCount], 100); 451 | beneficiaryinterest = TotalInterestOver[_GrowdropCount]-ToUniswapInterestRateCalculated-OwnerFee; 452 | 453 | require(Token[_GrowdropCount].approve(address(Tokenswap), ToUniswapInterestRateCalculated), "approve token error"); 454 | require(GrowdropToken[_GrowdropCount].approve(address(Tokenswap), ToUniswapTokenAmount[_GrowdropCount]), "approve growdrop error"); 455 | success = Tokenswap.addPoolToUniswap( 456 | address(Token[_GrowdropCount]), 457 | address(GrowdropToken[_GrowdropCount]), 458 | Beneficiary[_GrowdropCount], 459 | ToUniswapInterestRateCalculated, 460 | ToUniswapTokenAmount[_GrowdropCount] 461 | ); 462 | 463 | if(!success) { 464 | beneficiaryinterest += ToUniswapInterestRateCalculated; 465 | } 466 | 467 | } else { 468 | beneficiaryinterest = TotalInterestOver[_GrowdropCount]-OwnerFee; 469 | if(DonateId[_GrowdropCount]!=0) { 470 | success=true; 471 | } 472 | } 473 | sendTokenInWithdraw(_GrowdropCount, Beneficiary[_GrowdropCount], beneficiaryinterest, success ? 0 : ToUniswapTokenAmount[_GrowdropCount]); 474 | require(Token[_GrowdropCount].transfer(owner, OwnerFee), "transfer fee error"); 475 | 476 | EventIdx += 1; 477 | emit GrowdropAction(EventIdx, _GrowdropCount, msg.sender, beneficiaryinterest, success ? 1 : 0, 2, now); 478 | } else { 479 | //If caller is investor 480 | uint256 investorTotalInterest = MulAndDiv(ActualCTokenPerAddress[_GrowdropCount][msg.sender], ExchangeRateOver[_GrowdropCount], 1) - ActualPerAddress[_GrowdropCount][msg.sender]; 481 | 482 | uint256 tokenByInterest = DonateId[_GrowdropCount]==0 ? MulAndDiv( 483 | investorTotalInterest, 484 | GrowdropAmount[_GrowdropCount], 485 | (TotalInterestOverActual[_GrowdropCount]==0 ? 1 : TotalInterestOverActual[_GrowdropCount]) 486 | ) : investorTotalInterest; 487 | tokenByInterest = sendTokenInWithdraw(_GrowdropCount, msg.sender, InvestAmountPerAddress[_GrowdropCount][msg.sender], tokenByInterest); 488 | 489 | TotalUserInvestedAmount[msg.sender][address(Token[_GrowdropCount])] = Sub(TotalUserInvestedAmount[msg.sender][address(Token[_GrowdropCount])], InvestAmountPerAddress[_GrowdropCount][msg.sender]); 490 | EventIdx += 1; 491 | emit GrowdropAction(EventIdx, _GrowdropCount, msg.sender, InvestAmountPerAddress[_GrowdropCount][msg.sender], tokenByInterest, 3, now); 492 | } 493 | } 494 | 495 | /** 496 | * @dev Transfers ERC20 tokens to 'To' address. 497 | * If Growdrop by '_GrowdropCount' is donation, 'DonateToken' mints new ERC721 token. 498 | * 499 | * Emits {DonateAction} event indicating ERC721 token information from 'DonateToken' if Growdrop is donation. 500 | * 501 | * @param _GrowdropCount Growdrop's identifier 502 | * @param To address to send ERC20 tokens 503 | * @param TokenAmount ERC20 token amount of 'Token' 504 | * @param GrowdropTokenAmount ERC20 token amount of 'GrowdropToken' 505 | * @return if Growdrop by '_GrowdropCount' is donation, return new ERC721 token's identifier. Else return ERC20 token amount of 'GrowdropToken' 506 | */ 507 | function sendTokenInWithdraw(uint256 _GrowdropCount, address To, uint256 TokenAmount, uint256 GrowdropTokenAmount) private returns (uint256) { 508 | require(Token[_GrowdropCount].transfer(To, TokenAmount), "transfer token error"); 509 | if(DonateId[_GrowdropCount]==0) { 510 | require(GrowdropToken[_GrowdropCount].transfer(To, GrowdropTokenAmount), "transfer growdrop error"); 511 | return GrowdropTokenAmount; 512 | } else { 513 | return DonateToken.mint(msg.sender, Beneficiary[_GrowdropCount], address(Token[_GrowdropCount]), GrowdropTokenAmount, DonateId[_GrowdropCount]); 514 | } 515 | } 516 | 517 | /** 518 | * @dev Ends Growdrop by '_GrowdropCount'. 519 | * If total actual accrued interest is 0 and Growdrop is not donation, transfers 'GrowdropAmount' and 'ToUniswapTokenAmount' back to 'Beneficiary'. 520 | * Total accrued interest is calculated -> maximum amount of Growdrop's all CToken to ERC20 token - total funded ERC20 amount of Growdrop. 521 | * 522 | * Emits {GrowdropAction} event indicating Growdrop's identifier and event information. 523 | * 524 | * @param _GrowdropCount Growdrop's identifier 525 | */ 526 | function EndGrowdrop(uint256 _GrowdropCount) private { 527 | require(GrowdropStart[_GrowdropCount] && GrowdropEndTime[_GrowdropCount]<=now, "cannot end now"); 528 | if(!GrowdropOver[_GrowdropCount]) { 529 | GrowdropOver[_GrowdropCount] = true; 530 | 531 | ExchangeRateOver[_GrowdropCount] = CToken[_GrowdropCount].exchangeRateCurrent(); 532 | uint256 _toAmount = TotalCTokenAmount[_GrowdropCount]>0 ? MulAndDiv(TotalCTokenAmount[_GrowdropCount]+1, ExchangeRateOver[_GrowdropCount], 1e18) : 0; 533 | 534 | if(TotalCTokenAmount[_GrowdropCount]!=0) { 535 | require(CToken[_GrowdropCount].redeemUnderlying(_toAmount)==0, "error in redeem"); 536 | } 537 | TotalInterestOverActual[_GrowdropCount] = MulAndDiv(TotalCTokenActual[_GrowdropCount], ExchangeRateOver[_GrowdropCount], 1) - TotalMintedActual[_GrowdropCount]; 538 | 539 | TotalInterestOver[_GrowdropCount] = _toAmount>TotalMintedAmount[_GrowdropCount] ? _toAmount-TotalMintedAmount[_GrowdropCount] : 0; 540 | if(TotalInterestOverActual[_GrowdropCount]==0) { 541 | if(DonateId[_GrowdropCount]==0) { 542 | require( 543 | GrowdropToken[_GrowdropCount].transfer( 544 | Beneficiary[_GrowdropCount], 545 | GrowdropAmount[_GrowdropCount]+ToUniswapTokenAmount[_GrowdropCount] 546 | ) 547 | ); 548 | } 549 | } 550 | 551 | EventIdx += 1; 552 | emit GrowdropAction(EventIdx, _GrowdropCount, msg.sender, TotalInterestOverActual[_GrowdropCount]==0 ? 1 : 0, 0, 6, now); 553 | } 554 | } 555 | 556 | /** 557 | * @dev Calculates CToken and maximum amount of ERC20 token from ERC20 token amount and exchange rate of Compound CToken. 558 | * @param tokenAmount ERC20 token amount 559 | * @param exchangeRate exchange rate of Compound CToken 560 | * @return calculated CToken amount 561 | * @return calculated maximum amount of ERC20 token 562 | */ 563 | function toMaxAmount(uint256 tokenAmount, uint256 exchangeRate) private pure returns (uint256, uint256) { 564 | uint256 _ctoken = MulAndDiv(tokenAmount, 1e18, exchangeRate); 565 | return (_ctoken, MulAndDiv( 566 | Add(_ctoken, 1), 567 | exchangeRate, 568 | 1e18 569 | )); 570 | } 571 | 572 | /** 573 | * @dev Calculates CToken and minimum amount of ERC20 token from ERC20 token amount and exchange rate of Compound CToken. 574 | * @param tokenAmount ERC20 token amount 575 | * @param exchangeRate exchange rate of Compound CToken 576 | * @return calculated CToken amount 577 | * @return calculated minimum amount of ERC20 token 578 | */ 579 | function toMinAmount(uint256 tokenAmount, uint256 exchangeRate) private pure returns (uint256, uint256) { 580 | uint256 _ctoken = MulAndDiv(tokenAmount, 1e18, exchangeRate); 581 | return (_ctoken, Add( 582 | MulAndDiv( 583 | _ctoken, 584 | exchangeRate, 585 | 1e18 586 | ), 587 | 1 588 | )); 589 | } 590 | 591 | /** 592 | * @dev Calculates actual CToken and amount of ERC20 token from ERC20 token amount and exchange rate of Compound CToken. 593 | * Need for calculating percentage of interest accrued. 594 | * @param tokenAmount ERC20 token amount 595 | * @param exchangeRate exchange rate of Compound CToken 596 | * @return calculated actual CToken amount 597 | * @return calculated actual minimum amount of ERC20 token 598 | */ 599 | function toActualAmount(uint256 tokenAmount, uint256 exchangeRate) private pure returns (uint256, uint256) { 600 | uint256 _ctoken = MulAndDiv(tokenAmount, 1e29, exchangeRate); 601 | uint256 _token = MulAndDiv(_ctoken, exchangeRate, 1); 602 | return (_ctoken, _token); 603 | } 604 | 605 | function MulAndDiv(uint256 a, uint256 b, uint256 c) private pure returns (uint256) { 606 | uint256 temp = a*b; 607 | require(temp/b==a && c>0, "arithmetic error"); 608 | return temp/c; 609 | } 610 | 611 | function Add(uint256 a, uint256 b) private pure returns (uint256) { 612 | require(a+b>=a, "add overflow"); 613 | return a+b; 614 | } 615 | 616 | function Sub(uint256 a, uint256 b) private pure returns (uint256) { 617 | require(a>=b, "subtract overflow"); 618 | return a-b; 619 | } 620 | 621 | /** 622 | * @dev Emits {DonateAction} event from 'DonateToken' contract. 623 | * Only 'DonateToken' contract can call. 624 | * 625 | * Emits {DonateAction} event indicating ERC721 token information from 'DonateToken'. 626 | * 627 | * @param From 'DonateToken' ERC721 token's previous owner 628 | * @param To 'DonateToken' ERC721 token's next owner 629 | * @param Supporter 'DonateToken' ERC721 token's 'supporter' 630 | * @param beneficiary 'DonateToken' ERC721 token's 'beneficiary' 631 | * @param token 'DonateToken' ERC721 token's 'tokenAddress' 632 | * @param donateId 'DonateToken' ERC721 token's 'donateId' 633 | * @param tokenId 'DonateToken' ERC721 token's identifier 634 | * @param Amount 'DonateToken' ERC721 token's 'tokenAmount' 635 | * @param ActionIdx DonateAction event identifier 636 | * @return EventIdx event sequential identifier 637 | */ 638 | function emitDonateActionEvent( 639 | address From, 640 | address To, 641 | address Supporter, 642 | address beneficiary, 643 | address token, 644 | uint256 donateId, 645 | uint256 tokenId, 646 | uint256 Amount, 647 | uint256 ActionIdx) public returns (uint256) { 648 | require(msg.sender==address(DonateToken), "not donatetoken contract"); 649 | EventIdx += 1; 650 | emit DonateAction(EventIdx, From, To, Supporter, beneficiary, token, donateId, tokenId, Amount, ActionIdx, now); 651 | return EventIdx; 652 | } 653 | 654 | /** 655 | * @dev Set 'DonateToken' contract address. 656 | * @param DonateTokenAddress 'DonateToken' contract address 657 | */ 658 | function setDonateToken(address DonateTokenAddress) public { 659 | require(CheckOwner[msg.sender]); 660 | DonateToken = DonateTokenInterface(DonateTokenAddress); 661 | } 662 | 663 | /** 664 | * @dev Set 'Tokenswap' contract address. 665 | * @param TokenswapAddress 'Tokenswap' contract address 666 | */ 667 | function setTokenswap(address TokenswapAddress) public { 668 | require(CheckOwner[msg.sender]); 669 | Tokenswap = TokenswapInterface(TokenswapAddress); 670 | } 671 | 672 | /** 673 | * @dev Change 'CheckOwner' state of address . 674 | * @param _Owner address to change state 675 | */ 676 | function addOwner(address _Owner) public { 677 | require(CheckOwner[msg.sender]); 678 | CheckOwner[_Owner] = !CheckOwner[_Owner]; 679 | } 680 | 681 | /** 682 | * @dev Set 'owner' address . 683 | * @param _Owner address to set 684 | */ 685 | function setOwner(address _Owner) public { 686 | require(CheckOwner[msg.sender] && CheckOwner[_Owner], "not proper owner"); 687 | owner=_Owner; 688 | } 689 | 690 | /** 691 | * @dev Set 'CurrentOwnerFeePercent'. 692 | * @param _OwnerFeePercent value to set 693 | */ 694 | function setOwnerFeePercent(uint256 _OwnerFeePercent) public { 695 | require(CheckOwner[msg.sender], "not owner"); 696 | require(_OwnerFeePercent>0 && _OwnerFeePercent<100, "not proper percent"); 697 | CurrentOwnerFeePercent=_OwnerFeePercent; 698 | } 699 | 700 | function () external payable { 701 | 702 | } 703 | } 704 | -------------------------------------------------------------------------------- /app/src/index.js: -------------------------------------------------------------------------------- 1 | import Web3 from "web3"; 2 | import GrowdropArtifact from "../../build/contracts/Growdrop.json"; 3 | import GrowdropKovanArtifact from "../../build/contracts/Growdrop_kovan.json"; 4 | import EIP20Interface from "../../build/contracts/EIP20Interface.json"; 5 | import SimpleTokenABI from "./SimpleTokenABI.json"; 6 | import DonateToken from "../../build/contracts/DonateToken.json"; 7 | import Tokenswap from "../../build/contracts/Tokenswap.json"; 8 | import GrowdropCall from "../../build/contracts/GrowdropCall.json"; 9 | import KyberNetworkProxy from "../../build/contracts/KyberNetworkProxyInterface.json"; 10 | import Torus from "@toruslabs/torus-embed"; 11 | import bs58 from 'bs58'; 12 | var bigdecimal = require("bigdecimal"); 13 | var ipfsClient = require('ipfs-http-client'); 14 | 15 | const App = { 16 | web3: null, 17 | account: null, 18 | DAI: null, 19 | KyberDAI: null, 20 | Growdrop: null, 21 | SimpleToken: null, 22 | DonateToken: null, 23 | Tokenswap: null, 24 | GrowdropCall: null, 25 | KyberNetworkProxy: null, 26 | latestGrowdrop: null, 27 | UniswapSimpleTokenExchangeAddress: "0xfC8c8f7040b3608A74184D664853f5f30F53CbA8", 28 | DAIAddress:"0xbF7A7169562078c96f0eC1A8aFD6aE50f12e5A99", 29 | KyberDAIAddress:"0xC4375B7De8af5a38a93548eb8453a498222C4fF2", 30 | KyberNetworkProxyAddress: "0x692f391bCc85cefCe8C237C01e1f636BbD70EA4D", 31 | SimpleTokenAddress: "0x53cc0b020c7c8bbb983d0537507d2c850a22fa4c", 32 | KyberEthTokenAddress: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", 33 | cDAIAddress: "0x0a1e4d0b5c71b955c0a5993023fc48ba6e380496", 34 | 35 | MetamaskLogin: async function() { 36 | App.setStatus("login to metamask... please wait"); 37 | await App.MetamaskProvider(); 38 | await App.start(); 39 | App.setStatus("login to metamask done"); 40 | }, 41 | 42 | TorusLogin: async function() { 43 | App.setStatus("login to torus... please wait"); 44 | await App.TorusProvider(); 45 | await App.start(); 46 | App.setStatus("login to torus done"); 47 | }, 48 | 49 | MetamaskProvider: async function() { 50 | if (window.ethereum) { 51 | App.web3 = new Web3(window.ethereum); 52 | await window.ethereum.enable(); 53 | } else if (window.web3) { 54 | App.web3 = new Web3(window.web3.currentProvider); 55 | } 56 | }, 57 | 58 | TorusProvider: async function() { 59 | const torus = new Torus(); 60 | await torus.init({ 61 | network: { 62 | host: 'kovan', 63 | chainId: 42, 64 | networkName: 'kovan Test Network' 65 | } 66 | /* 67 | network: { 68 | host: 'mainnet', 69 | chainId: 1, 70 | networkName: 'Main Ethereum Network' 71 | } 72 | */ 73 | }); 74 | await torus.login(); 75 | App.web3 = await new Web3(torus.provider); 76 | }, 77 | 78 | SimpleTokenMint: async function() { 79 | return await App.SimpleToken.methods.mint().send({from:App.account}); 80 | }, 81 | 82 | withDecimal: function(number) { 83 | return String(App.toBigInt(number).divide(new bigdecimal.BigDecimal("1000000000000000000"))); 84 | }, 85 | 86 | withCTokenDecimal: function(number) { 87 | return String(App.toBigInt(number).divide(new bigdecimal.BigDecimal("100000000"))); 88 | }, 89 | 90 | toBigInt: function(number) { 91 | return new bigdecimal.BigDecimal(String(number)); 92 | }, 93 | 94 | /* 95 | new contract instance 96 | abi => contract abi (json type) 97 | address => contract address (String) 98 | return => contract instance 99 | */ 100 | contractInit: function(abi, address) { 101 | const { web3 } = App; 102 | return new web3.eth.Contract(abi, address); 103 | }, 104 | 105 | /* 106 | get metamask current account (address) 107 | return => metamask current account address (String) 108 | */ 109 | getProviderCurrentAccount: async function () { 110 | const { web3 } = App; 111 | const accounts = await web3.eth.getAccounts(); 112 | return accounts[0]; 113 | }, 114 | 115 | /* 116 | get balance of account 117 | account => account address to get balance (String) 118 | return => account eth balance (Number) 119 | */ 120 | GetBalanceCall: async function (account) { 121 | const { web3 } = App; 122 | return await web3.eth.getBalance(account); 123 | }, 124 | 125 | start: async function() { 126 | const { web3 } = this; 127 | 128 | const networkId = await web3.eth.net.getId(); 129 | if(networkId==1) { 130 | //UniswapSimpleTokenExchangeAddress="0xfC8c8f7040b3608A74184D664853f5f30F53CbA8"; 131 | this.DAIAddress="0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359"; 132 | this.KyberDAIAddress="0x89d24a6b4ccb1b6faa2625fe562bdd9a23260359"; 133 | this.KyberNetworkProxyAddress="0x818E6FECD516Ecc3849DAf6845e3EC868087B755"; 134 | //SimpleTokenAddress="0x53cc0b020c7c8bbb983d0537507d2c850a22fa4c"; 135 | this.KyberEthTokenAddress="0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"; 136 | this.cDAIAddress="0xf5dce57282a584d2746faf1593d3121fcac444dc"; 137 | } 138 | const deployedNetwork_growdrop = GrowdropArtifact.networks[networkId]; 139 | const deployedNetwork_donatetoken = DonateToken.networks[networkId]; 140 | const deployedNetwork_tokenswap = Tokenswap.networks[networkId]; 141 | const deployedNetwork_growdropcall = GrowdropCall.networks[networkId]; 142 | 143 | if(networkId==42) { 144 | const deployedNetwork_growdrop_kovan = GrowdropKovanArtifact.networks[networkId]; 145 | this.Growdrop = this.contractInit(GrowdropKovanArtifact.abi, deployedNetwork_growdrop_kovan.address); 146 | this.SimpleToken = this.contractInit(SimpleTokenABI.abi, this.SimpleTokenAddress); 147 | } else if (networkId==1) { 148 | this.Growdrop = this.contractInit(GrowdropArtifact.abi, deployedNetwork_growdrop.address); 149 | } 150 | this.DonateToken = this.contractInit(DonateToken.abi, deployedNetwork_donatetoken.address); 151 | this.Tokenswap = this.contractInit(Tokenswap.abi, deployedNetwork_tokenswap.address); 152 | this.GrowdropCall = this.contractInit(GrowdropCall.abi, deployedNetwork_growdropcall.address); 153 | 154 | this.DAI = this.contractInit(EIP20Interface.abi, this.DAIAddress); 155 | this.KyberDAI = this.contractInit(EIP20Interface.abi, this.KyberDAIAddress); 156 | this.KyberNetworkProxy = this.contractInit(KyberNetworkProxy.abi, this.KyberNetworkProxyAddress); 157 | this.ipfs = ipfsClient('ipfs.infura.io', '5001', {protocol:'https'}) 158 | 159 | this.account = await this.getProviderCurrentAccount(); 160 | await this.refresh(); 161 | }, 162 | 163 | KyberswapEthToTokenTx: async function(contractinstance, token, amount, account) { 164 | return contractinstance.methods.kyberswapEthToToken(token).send({from:account, value:amount}) 165 | .on('transactionHash', function(hash) { 166 | console.log("transaction hash : "+hash); 167 | }).on('confirmation', function(confirmationNumber) { 168 | if(confirmationNumber==6) { 169 | console.log("confirmed+ "+confirmationNumber); 170 | } 171 | }).on('receipt', function(receipt) { 172 | return receipt; 173 | }).on('error', function(error) { 174 | return error; 175 | }); 176 | }, 177 | 178 | UniswapTokenTx: async function(contractinstance, fromtoken, totoken, amount, account) { 179 | return contractinstance.methods.uniswapToken(fromtoken, totoken, amount).send({from:account}) 180 | .on('transactionHash', function(hash) { 181 | console.log("transaction hash : "+hash); 182 | }).on('confirmation', function(confirmationNumber) { 183 | if(confirmationNumber==6) { 184 | console.log("confirmed+ "+confirmationNumber); 185 | } 186 | }).on('receipt', function(receipt) { 187 | return receipt; 188 | }).on('error', function(error) { 189 | return error; 190 | }); 191 | }, 192 | 193 | GetGrowdropCountCall: async function(contractinstance) { 194 | return await contractinstance.methods.GrowdropCount().call(); 195 | }, 196 | 197 | GetExpectedAmountCall: async function(contractinstance, ethortoken,token,amount) { 198 | return await contractinstance.methods.getExpectedAmount(ethortoken,token,amount).call(); 199 | }, 200 | 201 | GetUniswapLiquidityPoolCall: async function(contractinstance, token) { 202 | return await contractinstance.methods.getUniswapLiquidityPool(token).call(); 203 | }, 204 | 205 | DonateInfoToTokenAmountCall: async function(contractinstance, from, to, token, donateid) { 206 | return await contractinstance.methods.DonateInfoToTokenAmount(from,to,token,donateid).call(); 207 | }, 208 | 209 | MultihashToDonateIdCall: async function(contractinstance, hash, hashfunction, size) { 210 | return await contractinstance.methods.MultihashToDonateId(hash,hashfunction,size).call(); 211 | }, 212 | 213 | DonateIdOwnerCall: async function(contractinstance, tokenid) { 214 | return await contractinstance.methods.DonateIdOwner(tokenid).call(); 215 | }, 216 | 217 | GetMultihashCall: async function(contractinstance, donateid) { 218 | return await contractinstance.methods.getMultihash(donateid).call(); 219 | }, 220 | 221 | SetMultihashTx: async function(contractinstance,hash,hashfunction,size,account) { 222 | return contractinstance.methods.setMultihash(hash,hashfunction,size).send({from:account}) 223 | .on('transactionHash', function(hash) { 224 | console.log("transaction hash : "+hash); 225 | }).on('confirmation', function(confirmationNumber) { 226 | if(confirmationNumber==6) { 227 | console.log("confirmed+ "+confirmationNumber); 228 | } 229 | }).on('receipt', function(receipt) { 230 | return receipt; 231 | }).on('error', function(error) { 232 | return error; 233 | }); 234 | }, 235 | 236 | /* 237 | 238 | */ 239 | getDonateEvent: function(receipt) { 240 | var ret = { 241 | event_idx: receipt.events.DonateEvent.returnValues.event_idx, 242 | from: receipt.events.DonateEvent.returnValues.from, 243 | donate_id: receipt.events.DonateEvent.returnValues.donate_id, 244 | hash: receipt.events.DonateEvent.returnValues.hash, 245 | hash_function: receipt.events.DonateEvent.returnValues.hash_function, 246 | size: receipt.events.DonateEvent.returnValues.size, 247 | }; 248 | return ret; 249 | }, 250 | 251 | getDonateActionEvent: function(receipt) { 252 | var ret = { 253 | event_idx: receipt.events.DonateAction.returnValues.event_idx, 254 | from: receipt.events.DonateAction.returnValues.from, 255 | to: receipt.events.DonateAction.returnValues.to, 256 | supporter: receipt.events.DonateAction.returnValues.supporter, 257 | beneficiary: receipt.events.DonateAction.returnValues.beneficiary, 258 | token_address: receipt.events.DonateAction.returnValues.token_address, 259 | donate_id: receipt.events.DonateAction.returnValues.donate_id, 260 | token_id: receipt.events.DonateAction.returnValues.token_id, 261 | amount: receipt.events.DonateAction.returnValues.amount, 262 | action_idx: receipt.events.DonateAction.returnValues.action_idx, 263 | } 264 | return ret; 265 | }, 266 | 267 | /* 268 | 269 | */ 270 | getGrowdropEvent: function(receipt) { 271 | if(receipt.events.GrowdropAction.length==2) { 272 | var ret = [{ 273 | event_idx: receipt.events.GrowdropAction[0].returnValues.event_idx, 274 | growdrop_count: receipt.events.GrowdropAction[0].returnValues.growdrop_count, 275 | action_idx: receipt.events.GrowdropAction[0].returnValues.action_idx, 276 | from: receipt.events.GrowdropAction[0].returnValues.from, 277 | amount1: receipt.events.GrowdropAction[0].returnValues.amount1, 278 | amount2: receipt.events.GrowdropAction[0].returnValues.amount2, 279 | }, 280 | { 281 | event_idx: receipt.events.GrowdropAction[1].returnValues.event_idx, 282 | growdrop_count: receipt.events.GrowdropAction[1].returnValues.growdrop_count, 283 | action_idx: receipt.events.GrowdropAction[1].returnValues.action_idx, 284 | from: receipt.events.GrowdropAction[1].returnValues.from, 285 | amount1: receipt.events.GrowdropAction[1].returnValues.amount1, 286 | amount2: receipt.events.GrowdropAction[1].returnValues.amount2, 287 | }] 288 | return ret; 289 | } 290 | var ret = { 291 | event_idx: receipt.events.GrowdropAction.returnValues.event_idx, 292 | growdrop_count: receipt.events.GrowdropAction.returnValues.growdrop_count, 293 | action_idx: receipt.events.GrowdropAction.returnValues.action_idx, 294 | from: receipt.events.GrowdropAction.returnValues.from, 295 | amount1: receipt.events.GrowdropAction.returnValues.amount1, 296 | amount2: receipt.events.GrowdropAction.returnValues.amount2 297 | }; 298 | return ret; 299 | }, 300 | 301 | 302 | /* 303 | Uniswap token to eth price call 304 | contractinstance => Uniswap Exchange contract instance 305 | amount => amount of token to get eth price (Number) 306 | return => amount of eth (Number) 307 | */ 308 | UniswapTokenToEthInputPriceCall: async function(contractinstance, amount) { 309 | return await contractinstance.methods.getTokenToEthInputPrice(amount).call(); 310 | }, 311 | 312 | /* 313 | ERC20 Token's balance call 314 | contractinstance => ERC20 contract instance 315 | account => address to get balance of (String) 316 | return => account balance of ERC20 Token (Number) 317 | */ 318 | TokenBalanceOfCall: async function(contractinstance, account) { 319 | return await contractinstance.methods.balanceOf(account).call(); 320 | }, 321 | 322 | /* 323 | ERC20 Token's allowance call (approved amount) 324 | contractinstance => ERC20 contract instance 325 | from => address who approved (String) 326 | to => address from approved to (String) 327 | return => approved amount of 'to' address from 'from' address 328 | */ 329 | TokenAllowanceCall: async function(contractinstance, from, to) { 330 | return await contractinstance.methods.allowance(from, to).call(); 331 | }, 332 | 333 | /* 334 | Growdrop Contract Data call 335 | contractinstance => Growdrop contract instance 336 | GrowdropCount => Growdrop contract index 337 | return => 338 | address of Growdrop Token Contract (String) 339 | address of beneficiary (String) 340 | selling amount of Growdrop Token (Number) 341 | Growdrop contract start time (Number unix timestamp) 342 | Growdrop contract end time (Number unix timestamp) 343 | Growdrop contract total interest + total invested amount (Number) 344 | Growdrop contract total invested amount (Number) 345 | Growdrop contract over (Boolean true : over, false : not over) 346 | Growdrop contract Start (Boolean true : start, false : not start) 347 | Growdrop token amount to uniswap pool (Number) 348 | Growdrop interest percentage to uniswap pool (Number 1~99) 349 | */ 350 | GetGrowdropDataCall: async function(contractinstance, GrowdropCount, account) { 351 | return await contractinstance.methods.getGrowdropData(GrowdropCount).call({from:account}); 352 | }, 353 | 354 | GetGrowdropStateDataCall: async function(contractinstance, GrowdropCount, account) { 355 | return await contractinstance.methods.getGrowdropStateData(GrowdropCount).call({from:account}); 356 | }, 357 | 358 | GetGrowdropAmountDataCall: async function(contractinstance, GrowdropCount, account) { 359 | return await contractinstance.methods.getGrowdropAmountData(GrowdropCount).call({from:account}); 360 | }, 361 | 362 | /* 363 | address's invested amount call 364 | contractinstance => Growdrop contract instance 365 | GrowdropCount => Growdrop index 366 | account => address to get data (String) 367 | return => 368 | user's invested amount to Growdrop contract (Number) 369 | */ 370 | InvestAmountPerAddressCall: async function(contractinstance, GrowdropCount, account) { 371 | return await contractinstance.methods.InvestAmountPerAddress(GrowdropCount, account).call(); 372 | }, 373 | 374 | /* 375 | address's total invested amount call (all Growdrop contracts) 376 | contractinstance => GrowdropManager contract instance 377 | account => address to get data (String) 378 | return => 379 | user's invested amount to all Growdrop contracts (Number) 380 | */ 381 | TotalUserInvestedAmountCall: async function(contractinstance, account) { 382 | return await contractinstance.methods.TotalUserInvestedAmount(account).call(); 383 | }, 384 | 385 | /* 386 | Growdrop contract's total user count call 387 | contractinstance => GrowdropManager contract instance 388 | GrowdropCount => Growdrop index 389 | return => 390 | user count to Growdrop contract (Number) 391 | */ 392 | TotalUserCountCall: async function(contractinstance, GrowdropCount) { 393 | return await contractinstance.methods.TotalUserCount(GrowdropCount).call(); 394 | }, 395 | 396 | /* 397 | Growdrop contract's total invested amount call 398 | contractinstance => Growdrop contract instance 399 | GrowdropCount => Growdrop index 400 | return => 401 | total invested amount to Growdrop contract (Number) 402 | */ 403 | TotalMintedAmountCall: async function(contractinstance, GrowdropCount) { 404 | return await contractinstance.methods.TotalMintedAmount(GrowdropCount).call(); 405 | }, 406 | 407 | /* 408 | Growdrop contract's selling token contract address call 409 | contractinstance => Growdrop contract instance 410 | GrowdropCount => Growdrop index 411 | return => 412 | address of Growdrop token contract (String) 413 | */ 414 | GrowdropTokenCall: async function(contractinstance, GrowdropCount) { 415 | return await contractinstance.methods.GrowdropToken(GrowdropCount).call(); 416 | }, 417 | 418 | /* 419 | Growdrop contract's end time call 420 | contractinstance => Growdrop contract instance 421 | GrowdropCount => Growdrop index 422 | return => 423 | Growdrop end time (Number unix timestamp) 424 | */ 425 | GrowdropEndTimeCall: async function(contractinstance, GrowdropCount) { 426 | return await contractinstance.methods.GrowdropEndTime(GrowdropCount).call(); 427 | }, 428 | 429 | /* 430 | Growdrop contract's beneficiary call 431 | contractinstance => Growdrop contract instance 432 | GrowdropCount => Growdrop index 433 | return => 434 | address of beneficiary (String) 435 | */ 436 | BeneficiaryCall: async function(contractinstance, GrowdropCount) { 437 | return await contractinstance.methods.Beneficiary(GrowdropCount).call(); 438 | }, 439 | 440 | /* 441 | Growdrop contract's owner check call 442 | contractinstance => Growdrop contract instance 443 | account => address to check if it's owner 444 | return => 445 | (Boolean true : owner, false : not owner) 446 | */ 447 | CheckOwnerCall: async function(contractinstance, account) { 448 | return await contractinstance.methods.CheckOwner(account).call(); 449 | }, 450 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 451 | getExpectedRateCall: async function(contractinstance, src_token, dest_token, amount) { 452 | return await contractinstance.methods.getExpectedRate(src_token, dest_token, amount).call(); 453 | }, 454 | 455 | /* 456 | ERC20 token approve transaction 457 | contractinstance => ERC20 token contract instance 458 | to => address to approve (String) 459 | amount => amount to approve (Number) 460 | account => address approve to "to" address (String) 461 | return => 462 | (Boolean true : success, false : failed) 463 | */ 464 | TokenApproveTx: async function(contractinstance, to, amount, account) { 465 | return contractinstance.methods.approve(to, amount).send({from:account}) 466 | .on('transactionHash', function(hash) { 467 | console.log("transaction hash : "+hash); 468 | }).on('confirmation', function(confirmationNumber) { 469 | if(confirmationNumber==6) { 470 | console.log("confirmed+ "+confirmationNumber); 471 | } 472 | }).on('receipt', function(receipt) { 473 | return receipt; 474 | }).on('error', function(error) { 475 | return error; 476 | }); 477 | }, 478 | 479 | /* 480 | ERC20, ERC721 token transferFrom transaction 481 | contractinstance => ERC20, ERC721 token contract instance 482 | from => address sending token (String) 483 | to => address receiving token (String) 484 | amount => amount to transfer (Number) 485 | account => address calling transfer (String) 486 | 487 | return => 488 | (Boolean true : success, false : failed) 489 | */ 490 | TokenTransferFromTx: async function (contractinstance, from, to, amount, account) { 491 | return await contractinstance.methods.transferFrom(from, to, amount).send({from: account}) 492 | .on('transactionHash', function(hash) { 493 | ////console.log("transaction hash : "+hash); 494 | }).on('confirmation', function(confirmationNumber) { 495 | if(confirmationNumber==6) { 496 | //console.log(confirmed+" "+confirmationNumber); 497 | } 498 | }).on('receipt', function(receipt) { 499 | return receipt; 500 | }).on('error', function(error) { 501 | return error; 502 | }); 503 | }, 504 | 505 | /* 506 | make new Growdrop Contract transaction (only owner can) 507 | contractinstance => Growdrop contract instance 508 | tokenaddress => dai contract address (set, String) 509 | ctokenaddress => compound cdai contract address (set, String) 510 | growdroptokenaddress => selling token address (String) 511 | beneficiaryaddress => beneficiary address (String) 512 | growdroptokenamount => selling token amount (Number) 513 | GrowdropPeriod => Growdrop contract's selling period (Number) 514 | ToUniswapGrowdropTokenAmount => selling token amount to add Uniswap (Number) 515 | ToUniswapInterestRate => beneficiary's interest percentage to add Uniswap (1~99, Number) 516 | DonateId => Donate Label Id (Number) 517 | account => address calling (owner, String) 518 | return => 519 | (Boolean true : success, false : failed) 520 | */ 521 | NewGrowdropTx: async function( 522 | contractinstance, 523 | tokenaddress, 524 | ctokenaddress, 525 | growdroptokenaddress, 526 | beneficiaryaddress, 527 | growdroptokenamount, 528 | GrowdropPeriod, 529 | ToUniswapGrowdropTokenAmount, 530 | ToUniswapInterestRate, 531 | DonateId, 532 | account) { 533 | return contractinstance.methods.newGrowdrop( 534 | tokenaddress, 535 | ctokenaddress, 536 | growdroptokenaddress, 537 | beneficiaryaddress, 538 | growdroptokenamount, 539 | GrowdropPeriod, 540 | ToUniswapGrowdropTokenAmount, 541 | ToUniswapInterestRate, 542 | DonateId 543 | ).send({from:account}) 544 | .on('transactionHash', function(hash) { 545 | console.log("transaction hash : "+hash); 546 | }).on('confirmation', function(confirmationNumber) { 547 | if(confirmationNumber==6) { 548 | console.log("confirmed+ "+confirmationNumber); 549 | } 550 | }).on('receipt', function(receipt) { 551 | return receipt; 552 | }).on('error', function(error) { 553 | return error; 554 | }); 555 | }, 556 | 557 | /* 558 | Start Growdrop Contract transaction (only beneficiary can) 559 | contractinstance => Growdrop contract instance 560 | GrowdropCount => Growdrop index 561 | account => address calling (beneficiary, String) 562 | return => 563 | (Boolean true : success, false : failed) 564 | */ 565 | StartGrowdropTx: async function(contractinstance, GrowdropCount, account) { 566 | return contractinstance.methods.StartGrowdrop(GrowdropCount).send({from:account}) 567 | .on('transactionHash', function(hash) { 568 | console.log("transaction hash : "+hash); 569 | }).on('confirmation', function(confirmationNumber) { 570 | if(confirmationNumber==6) { 571 | console.log("confirmed+ "+confirmationNumber); 572 | } 573 | }).on('receipt', function(receipt) { 574 | return receipt; 575 | }).on('error', function(error) { 576 | return error; 577 | }); 578 | }, 579 | 580 | /* 581 | add investing amount 582 | contractinstance => Growdrop contract instance 583 | GrowdropCount => Growdrop index 584 | amount => amount to add investing (Number) 585 | account => address adding (String) 586 | return => 587 | (Boolean true : success, false : failed) 588 | */ 589 | MintTx: async function(contractinstance, GrowdropCount, amount, account) { 590 | return contractinstance.methods.Mint(GrowdropCount, amount).send({from:account}) 591 | .on('transactionHash', function(hash) { 592 | console.log("transaction hash : "+hash); 593 | }).on('confirmation', function(confirmationNumber) { 594 | if(confirmationNumber==6) { 595 | console.log("confirmed+ "+confirmationNumber); 596 | } 597 | }).on('receipt', function(receipt) { 598 | return receipt; 599 | }).on('error', function(error) { 600 | return error; 601 | }); 602 | }, 603 | 604 | /* 605 | subtract investing amount 606 | contractinstance => Growdrop contract instance 607 | GrowdropCount => Growdrop index 608 | amount => amount to subtract investing (Number) 609 | account => address subtracting (String) 610 | return => 611 | (Boolean true : success, false : failed) 612 | */ 613 | RedeemTx: async function(contractinstance, GrowdropCount, amount, account) { 614 | return contractinstance.methods.Redeem(GrowdropCount, amount).send({from:account}) 615 | .on('transactionHash', function(hash) { 616 | console.log("transaction hash : "+hash); 617 | }).on('confirmation', function(confirmationNumber) { 618 | if(confirmationNumber==6) { 619 | console.log("confirmed+ "+confirmationNumber); 620 | } 621 | }).on('receipt', function(receipt) { 622 | return receipt; 623 | }).on('error', function(error) { 624 | return error; 625 | }); 626 | }, 627 | 628 | /* 629 | Withdraw interest (beneficiary) 630 | Withdraw invested amount and get selling token (investor) 631 | contractinstance => Growdrop contract instance 632 | GrowdropCount => Growdrop index 633 | ToUniswap => true : add to uniswap, false : not add to uniswap (only for beneficiary, investor doesn't care, Boolean) 634 | account => address calling (String) 635 | return => 636 | (Boolean true : success, false : failed) 637 | */ 638 | WithdrawTx: async function(contractinstance, GrowdropCount, ToUniswap, account) { 639 | return contractinstance.methods.Withdraw(GrowdropCount, ToUniswap).send({from:account}) 640 | .on('transactionHash', function(hash) { 641 | console.log("transaction hash : "+hash); 642 | }).on('confirmation', function(confirmationNumber) { 643 | if(confirmationNumber==6) { 644 | console.log("confirmed+ "+confirmationNumber); 645 | } 646 | }).on('receipt', function(receipt) { 647 | return receipt; 648 | }).on('error', function(error) { 649 | return error; 650 | }); 651 | }, 652 | 653 | setElement_innerHTML: async function(element, text) { 654 | element.innerHTML = App.withDecimal(text); 655 | }, 656 | 657 | allPastEvents: async function(contractinstance) { 658 | /* 659 | filter: no filtering 660 | */ 661 | contractinstance.getPastEvents("allEvents", {fromBlock: 0, toBlock: 'latest'}).then(function(events) { 662 | /* 663 | events[i].event => event name 664 | events[i].returnValues... => event results 665 | */ 666 | console.log(events); 667 | }); 668 | }, 669 | 670 | refresh: async function() { 671 | this.latestGrowdrop = await this.GetGrowdropCountCall(this.Growdrop); 672 | const DAIbalance = await this.TokenBalanceOfCall(this.DAI, App.account); 673 | const KyberDAIbalance = await this.TokenBalanceOfCall(this.KyberDAI, App.account); 674 | if(this.SimpleToken!=null) { 675 | const SimpleTokenbalance = await this.TokenBalanceOfCall(this.SimpleToken, App.account); 676 | 677 | const GetUniswapLiquidityPoolRes =await App.GetUniswapLiquidityPoolCall( 678 | App.Tokenswap, 679 | App.SimpleToken._address 680 | ); 681 | 682 | const SimpleTokenbalanceElement = document.getElementsByClassName("SimpleTokenbalance")[0]; 683 | this.setElement_innerHTML(SimpleTokenbalanceElement, SimpleTokenbalance); 684 | 685 | const UniswapSimpleTokenEthPoolElement = document.getElementsByClassName("UniswapSimpleTokenEthPool")[0]; 686 | App.setElement_innerHTML(UniswapSimpleTokenEthPoolElement, GetUniswapLiquidityPoolRes[0]); 687 | 688 | const UniswapSimpleTokenTokenPoolElement = document.getElementsByClassName("UniswapSimpleTokenTokenPool")[0]; 689 | App.setElement_innerHTML(UniswapSimpleTokenTokenPoolElement, GetUniswapLiquidityPoolRes[1]); 690 | } 691 | 692 | const DAIbalanceElement = document.getElementsByClassName("DAIbalance")[0]; 693 | this.setElement_innerHTML(DAIbalanceElement, DAIbalance); 694 | const KyberDAIbalanceElement = document.getElementsByClassName("KyberDAIbalance")[0]; 695 | this.setElement_innerHTML(KyberDAIbalanceElement, KyberDAIbalance); 696 | 697 | const GrowdropData_value = await App.GetGrowdropDataCall(App.GrowdropCall, App.latestGrowdrop, App.account); 698 | const GrowdropStateData_value = await App.GetGrowdropStateDataCall(App.GrowdropCall, App.latestGrowdrop, App.account); 699 | const GrowdropAmountData_value = await App.GetGrowdropAmountDataCall(App.GrowdropCall, App.latestGrowdrop, App.account); 700 | 701 | if(GrowdropStateData_value[2]==false) { 702 | $(document).find('.GrowdropStatusdisplay').text("pending"); 703 | } else if (GrowdropStateData_value[3]==false) { 704 | $(document).find('.GrowdropStatusdisplay').text("running"); 705 | } else { 706 | $(document).find('.GrowdropStatusdisplay').text("ended"); 707 | } 708 | 709 | $(document).find('.GrowdropTokendisplay').text(GrowdropData_value[0]); 710 | $(document).find('.Beneficiarydisplay').text(GrowdropData_value[1]); 711 | $(document).find('.GrowdropStartTimedisplay').text(new Date(parseInt(GrowdropStateData_value[0]*1000))); 712 | $(document).find('.GrowdropEndTimedisplay').text(new Date(parseInt(GrowdropStateData_value[1]*1000))); 713 | $(document).find('.GrowdropAmountdisplay').text(App.withDecimal(GrowdropData_value[2])); 714 | 715 | $(document).find('.TotalBalancedisplay').text(App.withCTokenDecimal(GrowdropAmountData_value[0])); 716 | $(document).find('.TotalMintedAmountdisplay').text(App.withDecimal(GrowdropAmountData_value[1])); 717 | $(document).find('.TotalPerAddressdisplay').text(App.withCTokenDecimal(GrowdropAmountData_value[2])); 718 | $(document).find('.InvestAmountPerAddressdisplay').text(App.withDecimal(GrowdropAmountData_value[3])); 719 | }, 720 | 721 | Mint: async function() { 722 | var MintAmount = parseInt(document.getElementById("Mintinput").value); 723 | const Mint_res = await App.MintTx(App.Growdrop, App.latestGrowdrop, String(MintAmount), App.account); 724 | if(Mint_res.status) { 725 | var GrowdropEventRes = App.getGrowdropEvent(Mint_res); 726 | console.log(GrowdropEventRes); 727 | } 728 | }, 729 | 730 | Redeem: async function() { 731 | var RedeemAmount = parseInt(document.getElementById("Redeeminput").value); 732 | const Redeem_res = await App.RedeemTx(App.Growdrop, App.latestGrowdrop, String(RedeemAmount), App.account); 733 | if(Redeem_res.status) { 734 | var GrowdropEventRes = App.getGrowdropEvent(Redeem_res); 735 | console.log(GrowdropEventRes); 736 | } 737 | }, 738 | 739 | Withdraw: async function() { 740 | const add_to_uniswap = parseInt(document.getElementById("AddToUniswap").value); 741 | var touniswap=false; 742 | if(add_to_uniswap==1) { 743 | touniswap=true; 744 | } 745 | const Withdraw_res = await App.WithdrawTx(App.Growdrop, App.latestGrowdrop, touniswap, App.account); 746 | if(Withdraw_res.status) { 747 | var GrowdropEventRes = App.getGrowdropEvent(Withdraw_res); 748 | console.log(GrowdropEventRes); 749 | } 750 | }, 751 | 752 | approveDAI: async function() { 753 | const approve_res = await App.TokenApproveTx( 754 | App.DAI, 755 | App.Growdrop._address, 756 | String("115792089237316195423570985008687907853269984665640564039457584007913129639935"), 757 | App.account 758 | ); 759 | if(approve_res.status) { 760 | console.log("DAI approved"); 761 | } 762 | }, 763 | 764 | approveSimpleToken: async function() { 765 | App.setStatus("Initiating approveSimpleToken transaction... (please wait)"); 766 | const approve_res = await App.TokenApproveTx( 767 | App.SimpleToken, 768 | App.Growdrop._address, 769 | String("115792089237316195423570985008687907853269984665640564039457584007913129639935"), 770 | App.account 771 | ); 772 | if(approve_res.status) { 773 | console.log("SimpleToken approved"); 774 | } 775 | }, 776 | 777 | NewGrowdrop: async function() { 778 | const amount = parseInt(document.getElementById("NewGrowdropamount").value); 779 | const beneficiary = document.getElementById("NewGrowdropbeneficiary").value; 780 | const period = document.getElementById("GrowdropPeriod").value; 781 | const ToUniswapGrowdropTokenAmount = document.getElementById("ToUniswapGrowdropTokenAmount").value; 782 | const ToUniswapInterestRate = document.getElementById("ToUniswapInterestRate").value; 783 | const donateid = document.getElementById("NewGrowdropDonateId").value; 784 | var growdroptoken = App.SimpleTokenAddress; 785 | if(donateid!="0") { 786 | growdroptoken = App.DonateToken._address; 787 | } 788 | App.setStatus("Initiating NewGrowdrop transaction... (please wait)"); 789 | const newGrowdrop_res = await App.NewGrowdropTx( 790 | App.Growdrop, 791 | App.DAI._address, 792 | App.cDAIAddress, 793 | growdroptoken, 794 | beneficiary, 795 | String(amount), 796 | period, 797 | String(ToUniswapGrowdropTokenAmount), 798 | ToUniswapInterestRate, 799 | donateid, 800 | App.account 801 | ); 802 | 803 | await App.refresh(); 804 | console.log(newGrowdrop_res); 805 | }, 806 | 807 | StartGrowdrop: async function() { 808 | App.setStatus("Initiating StartGrowdrop transaction... (please wait)"); 809 | const StartGrowdrop_res = await App.StartGrowdropTx(App.Growdrop, App.latestGrowdrop, App.account); 810 | if(StartGrowdrop_res.status) { 811 | var GrowdropEventRes = App.getGrowdropEvent(StartGrowdrop_res); 812 | console.log(GrowdropEventRes); 813 | } 814 | }, 815 | 816 | setStatus: function(message) { 817 | const status = document.getElementById("status"); 818 | status.innerHTML = message; 819 | }, 820 | 821 | bindEvents: function() { 822 | $(document).on('change', '#IpfsFileAdd', App.AddFileToIpfs); 823 | }, 824 | 825 | AddFileToIpfs: async function(event) { 826 | event.preventDefault(); 827 | var ipfsId; 828 | //App.setStatus("ipfs saving..."); 829 | //console.log("ipfs saving"); 830 | const response = await App.ipfs.add([...event.target.files]) 831 | ipfsId=response[0].hash; 832 | //console.log(ipfsId); 833 | await App.ipfs.pin.add(ipfsId); 834 | //console.log("https://ipfs.io/ipfs/"+ipfsId); 835 | const donateId = await App.CheckDonateIdAndSet(ipfsId); 836 | return donateId; 837 | }, 838 | 839 | CheckDonateIdAndSet: async function(ipfshash) { 840 | const { digest, hashFunction, size } = App.getBytes32FromMultihash(ipfshash); 841 | const MultihashToDonateIdRes = await App.MultihashToDonateIdCall( 842 | App.DonateToken, 843 | digest, 844 | hashFunction, 845 | size 846 | ); 847 | if(MultihashToDonateIdRes=="0") { 848 | const SetMultihashRes = await App.SetMultihashTx( 849 | App.DonateToken, 850 | digest, 851 | hashFunction, 852 | size, 853 | App.account 854 | ); 855 | if(SetMultihashRes.status) { 856 | var DonateEventRes = App.getDonateEvent(SetMultihashRes); 857 | console.log(DonateEventRes); 858 | } 859 | } else { 860 | App.setStatus(MultihashToDonateIdRes); 861 | } 862 | }, 863 | 864 | getBytes32FromMultihash: function(multihash) { 865 | const decoded = bs58.decode(multihash); 866 | 867 | return { 868 | digest: `0x${decoded.slice(2).toString('hex')}`, 869 | hashFunction: decoded[0], 870 | size: decoded[1], 871 | }; 872 | }, 873 | 874 | getMultihashFromBytes32: function(multihash) { 875 | const { digest, hashFunction, size } = multihash; 876 | if (size === 0) return null; 877 | 878 | const hashBytes = Buffer.from(digest.slice(2), 'hex'); 879 | 880 | const multihashBytes = new (hashBytes.constructor)(2 + hashBytes.length); 881 | multihashBytes[0] = hashFunction; 882 | multihashBytes[1] = size; 883 | multihashBytes.set(hashBytes, 2); 884 | 885 | return bs58.encode(multihashBytes); 886 | }, 887 | 888 | KyberswapEthToToken: async function() { 889 | const amount = document.getElementById("KyberswapEthToTokenAmount").value; 890 | const KyberswapEthToTokenRes = await this.KyberswapEthToTokenTx( 891 | App.Tokenswap, 892 | App.KyberDAIAddress, 893 | amount, 894 | App.account 895 | ); 896 | console.log(KyberswapEthToTokenRes); 897 | }, 898 | 899 | UniswapToken: async function() { 900 | const amount = document.getElementById("UniswapTokenAmount").value; 901 | const UniswapTokenRes = await this.UniswapTokenTx( 902 | App.Tokenswap, 903 | App.KyberDAIAddress, 904 | App.DAIAddress, 905 | amount, 906 | App.account 907 | ); 908 | console.log(UniswapTokenRes); 909 | }, 910 | 911 | GetExpectedAmount: async function() { 912 | const ethordai = document.getElementById("GetExpectedAmountEthOrDai").value; 913 | const amount = document.getElementById("GetExpectedAmountAmount").value; 914 | var ethordaibool=true; 915 | if(ethordai=="eth") { 916 | ethordaibool=false; 917 | } 918 | const GetExpectedAmountRes = await this.GetExpectedAmountCall( 919 | App.Tokenswap, 920 | ethordaibool, 921 | App.KyberDAIAddress, 922 | amount 923 | ); 924 | this.setStatus(GetExpectedAmountRes); 925 | }, 926 | 927 | DonateInfoToTokenAmount: async function() { 928 | const beneficiary = document.getElementById("DonateInfoToTokenAmountTo").value; 929 | const donateid = document.getElementById("DonateInfoToTokenAmountDonateId").value; 930 | const DonateInfoToTokenAmountRes = await this.DonateInfoToTokenAmountCall( 931 | App.DonateToken, 932 | App.account, 933 | beneficiary, 934 | App.DAIAddress, 935 | donateid 936 | ); 937 | this.setStatus(DonateInfoToTokenAmountRes); 938 | }, 939 | 940 | DonateIdOwner: async function() { 941 | const donateid = document.getElementById("DonateIdOwnerDonateId").value; 942 | const DonateIdOwnerRes = await this.DonateIdOwnerCall(App.DonateToken,donateid); 943 | this.setStatus(DonateIdOwnerRes); 944 | }, 945 | 946 | GetMultihash: async function() { 947 | const donateid = document.getElementById("GetMultihashDonateId").value; 948 | const GetMultihashRes = await this.GetMultihashCall(App.DonateToken, donateid); 949 | const toBytes32 = this.parseContractResponse(GetMultihashRes); 950 | const toIpfshash = this.getMultihashFromBytes32(toBytes32); 951 | this.setStatus(toIpfshash); 952 | }, 953 | 954 | MultihashToDonateId: async function() { 955 | const ipfshash = document.getElementById("MultihashToDonateIdIpfshash").value; 956 | const { digest, hashFunction, size } = this.getBytes32FromMultihash(ipfshash); 957 | const MultihashToDonateIdRes = await this.MultihashToDonateIdCall( 958 | App.DonateToken, 959 | digest, 960 | hashFunction, 961 | size 962 | ); 963 | this.setStatus(MultihashToDonateIdRes); 964 | }, 965 | 966 | parseContractResponse: function(response) { 967 | return { 968 | digest: response[0], 969 | hashFunction: response[1], 970 | size: response[2], 971 | }; 972 | }, 973 | 974 | }; 975 | 976 | window.App = App; 977 | 978 | window.addEventListener("load", function() { 979 | App.bindEvents(); 980 | }); --------------------------------------------------------------------------------