diff --git a/text/Assets/web3/sales/group/en.json b/text/Assets/web3/sales/group/en.json new file mode 100644 index 0000000..617e00d --- /dev/null +++ b/text/Assets/web3/sales/group/en.json @@ -0,0 +1,19 @@ +{ + "sales": { + "group": { + "errors": { + "AddressInvalid":"Address invalid", + "GroupNameInvalid":"Group name invalid" + }, + "titles": { + "AddtoWhitelist": "Add to Whitelist", + "SetGroup":"Set Group", + "OnWhitelist":"On whitelist", + "NotYetOnWhitelist":"Not yet on whitelist" + }, + "placeholder": { + "ExactGroupName": "Exact Group Name" + } + } + } +} diff --git a/text/Assets/web3/sales/main/en.json b/text/Assets/web3/sales/main/en.json new file mode 100644 index 0000000..8ec5148 --- /dev/null +++ b/text/Assets/web3/sales/main/en.json @@ -0,0 +1,20 @@ +{ + "sales": { + "main": { + "test": "test", + "placeholder": { + "amountInUSD": "Amount in USD" + }, + + "titles": { + "condition1":"Condition, tokens", + "bonus":"Bonus, %", + "condition2":"Condition", + "price":"Price, $", + "buy": "Buy", + "bonuses": "See Bonuses", + "prices": "See Prices" + } + } + } +} diff --git a/views/Assets/templates/Uniswap/V2/Pair.abi.json b/views/Assets/templates/Uniswap/V2/Pair.abi.json new file mode 100644 index 0000000..8632967 --- /dev/null +++ b/views/Assets/templates/Uniswap/V2/Pair.abi.json @@ -0,0 +1,755 @@ +[ + { + "inputs":[ + + ], + "payable":false, + "stateMutability":"nonpayable", + "type":"constructor" + }, + { + "anonymous":false, + "inputs":[ + { + "indexed":true, + "internalType":"address", + "name":"owner", + "type":"address" + }, + { + "indexed":true, + "internalType":"address", + "name":"spender", + "type":"address" + }, + { + "indexed":false, + "internalType":"uint256", + "name":"value", + "type":"uint256" + } + ], + "name":"Approval", + "type":"event" + }, + { + "anonymous":false, + "inputs":[ + { + "indexed":true, + "internalType":"address", + "name":"sender", + "type":"address" + }, + { + "indexed":false, + "internalType":"uint256", + "name":"amount0", + "type":"uint256" + }, + { + "indexed":false, + "internalType":"uint256", + "name":"amount1", + "type":"uint256" + }, + { + "indexed":true, + "internalType":"address", + "name":"to", + "type":"address" + } + ], + "name":"Burn", + "type":"event" + }, + { + "anonymous":false, + "inputs":[ + { + "indexed":true, + "internalType":"address", + "name":"sender", + "type":"address" + }, + { + "indexed":false, + "internalType":"uint256", + "name":"amount0", + "type":"uint256" + }, + { + "indexed":false, + "internalType":"uint256", + "name":"amount1", + "type":"uint256" + } + ], + "name":"Mint", + "type":"event" + }, + { + "anonymous":false, + "inputs":[ + { + "indexed":true, + "internalType":"address", + "name":"sender", + "type":"address" + }, + { + "indexed":false, + "internalType":"uint256", + "name":"amount0In", + "type":"uint256" + }, + { + "indexed":false, + "internalType":"uint256", + "name":"amount1In", + "type":"uint256" + }, + { + "indexed":false, + "internalType":"uint256", + "name":"amount0Out", + "type":"uint256" + }, + { + "indexed":false, + "internalType":"uint256", + "name":"amount1Out", + "type":"uint256" + }, + { + "indexed":true, + "internalType":"address", + "name":"to", + "type":"address" + } + ], + "name":"Swap", + "type":"event" + }, + { + "anonymous":false, + "inputs":[ + { + "indexed":false, + "internalType":"uint112", + "name":"reserve0", + "type":"uint112" + }, + { + "indexed":false, + "internalType":"uint112", + "name":"reserve1", + "type":"uint112" + } + ], + "name":"Sync", + "type":"event" + }, + { + "anonymous":false, + "inputs":[ + { + "indexed":true, + "internalType":"address", + "name":"from", + "type":"address" + }, + { + "indexed":true, + "internalType":"address", + "name":"to", + "type":"address" + }, + { + "indexed":false, + "internalType":"uint256", + "name":"value", + "type":"uint256" + } + ], + "name":"Transfer", + "type":"event" + }, + { + "constant":true, + "inputs":[ + + ], + "name":"DOMAIN_SEPARATOR", + "outputs":[ + { + "internalType":"bytes32", + "name":"", + "type":"bytes32" + } + ], + "payable":false, + "stateMutability":"view", + "type":"function" + }, + { + "constant":true, + "inputs":[ + + ], + "name":"MINIMUM_LIQUIDITY", + "outputs":[ + { + "internalType":"uint256", + "name":"", + "type":"uint256" + } + ], + "payable":false, + "stateMutability":"view", + "type":"function" + }, + { + "constant":true, + "inputs":[ + + ], + "name":"PERMIT_TYPEHASH", + "outputs":[ + { + "internalType":"bytes32", + "name":"", + "type":"bytes32" + } + ], + "payable":false, + "stateMutability":"view", + "type":"function" + }, + { + "constant":true, + "inputs":[ + { + "internalType":"address", + "name":"", + "type":"address" + }, + { + "internalType":"address", + "name":"", + "type":"address" + } + ], + "name":"allowance", + "outputs":[ + { + "internalType":"uint256", + "name":"", + "type":"uint256" + } + ], + "payable":false, + "stateMutability":"view", + "type":"function" + }, + { + "constant":false, + "inputs":[ + { + "internalType":"address", + "name":"spender", + "type":"address" + }, + { + "internalType":"uint256", + "name":"value", + "type":"uint256" + } + ], + "name":"approve", + "outputs":[ + { + "internalType":"bool", + "name":"", + "type":"bool" + } + ], + "payable":false, + "stateMutability":"nonpayable", + "type":"function" + }, + { + "constant":true, + "inputs":[ + { + "internalType":"address", + "name":"", + "type":"address" + } + ], + "name":"balanceOf", + "outputs":[ + { + "internalType":"uint256", + "name":"", + "type":"uint256" + } + ], + "payable":false, + "stateMutability":"view", + "type":"function" + }, + { + "constant":false, + "inputs":[ + { + "internalType":"address", + "name":"to", + "type":"address" + } + ], + "name":"burn", + "outputs":[ + { + "internalType":"uint256", + "name":"amount0", + "type":"uint256" + }, + { + "internalType":"uint256", + "name":"amount1", + "type":"uint256" + } + ], + "payable":false, + "stateMutability":"nonpayable", + "type":"function" + }, + { + "constant":true, + "inputs":[ + + ], + "name":"decimals", + "outputs":[ + { + "internalType":"uint8", + "name":"", + "type":"uint8" + } + ], + "payable":false, + "stateMutability":"view", + "type":"function" + }, + { + "constant":true, + "inputs":[ + + ], + "name":"factory", + "outputs":[ + { + "internalType":"address", + "name":"", + "type":"address" + } + ], + "payable":false, + "stateMutability":"view", + "type":"function" + }, + { + "constant":true, + "inputs":[ + + ], + "name":"getReserves", + "outputs":[ + { + "internalType":"uint112", + "name":"_reserve0", + "type":"uint112" + }, + { + "internalType":"uint112", + "name":"_reserve1", + "type":"uint112" + }, + { + "internalType":"uint32", + "name":"_blockTimestampLast", + "type":"uint32" + } + ], + "payable":false, + "stateMutability":"view", + "type":"function" + }, + { + "constant":false, + "inputs":[ + { + "internalType":"address", + "name":"_token0", + "type":"address" + }, + { + "internalType":"address", + "name":"_token1", + "type":"address" + } + ], + "name":"initialize", + "outputs":[ + + ], + "payable":false, + "stateMutability":"nonpayable", + "type":"function" + }, + { + "constant":true, + "inputs":[ + + ], + "name":"kLast", + "outputs":[ + { + "internalType":"uint256", + "name":"", + "type":"uint256" + } + ], + "payable":false, + "stateMutability":"view", + "type":"function" + }, + { + "constant":false, + "inputs":[ + { + "internalType":"address", + "name":"to", + "type":"address" + } + ], + "name":"mint", + "outputs":[ + { + "internalType":"uint256", + "name":"liquidity", + "type":"uint256" + } + ], + "payable":false, + "stateMutability":"nonpayable", + "type":"function" + }, + { + "constant":true, + "inputs":[ + + ], + "name":"name", + "outputs":[ + { + "internalType":"string", + "name":"", + "type":"string" + } + ], + "payable":false, + "stateMutability":"view", + "type":"function" + }, + { + "constant":true, + "inputs":[ + { + "internalType":"address", + "name":"", + "type":"address" + } + ], + "name":"nonces", + "outputs":[ + { + "internalType":"uint256", + "name":"", + "type":"uint256" + } + ], + "payable":false, + "stateMutability":"view", + "type":"function" + }, + { + "constant":false, + "inputs":[ + { + "internalType":"address", + "name":"owner", + "type":"address" + }, + { + "internalType":"address", + "name":"spender", + "type":"address" + }, + { + "internalType":"uint256", + "name":"value", + "type":"uint256" + }, + { + "internalType":"uint256", + "name":"deadline", + "type":"uint256" + }, + { + "internalType":"uint8", + "name":"v", + "type":"uint8" + }, + { + "internalType":"bytes32", + "name":"r", + "type":"bytes32" + }, + { + "internalType":"bytes32", + "name":"s", + "type":"bytes32" + } + ], + "name":"permit", + "outputs":[ + + ], + "payable":false, + "stateMutability":"nonpayable", + "type":"function" + }, + { + "constant":true, + "inputs":[ + + ], + "name":"price0CumulativeLast", + "outputs":[ + { + "internalType":"uint256", + "name":"", + "type":"uint256" + } + ], + "payable":false, + "stateMutability":"view", + "type":"function" + }, + { + "constant":true, + "inputs":[ + + ], + "name":"price1CumulativeLast", + "outputs":[ + { + "internalType":"uint256", + "name":"", + "type":"uint256" + } + ], + "payable":false, + "stateMutability":"view", + "type":"function" + }, + { + "constant":false, + "inputs":[ + { + "internalType":"address", + "name":"to", + "type":"address" + } + ], + "name":"skim", + "outputs":[ + + ], + "payable":false, + "stateMutability":"nonpayable", + "type":"function" + }, + { + "constant":false, + "inputs":[ + { + "internalType":"uint256", + "name":"amount0Out", + "type":"uint256" + }, + { + "internalType":"uint256", + "name":"amount1Out", + "type":"uint256" + }, + { + "internalType":"address", + "name":"to", + "type":"address" + }, + { + "internalType":"bytes", + "name":"data", + "type":"bytes" + } + ], + "name":"swap", + "outputs":[ + + ], + "payable":false, + "stateMutability":"nonpayable", + "type":"function" + }, + { + "constant":true, + "inputs":[ + + ], + "name":"symbol", + "outputs":[ + { + "internalType":"string", + "name":"", + "type":"string" + } + ], + "payable":false, + "stateMutability":"view", + "type":"function" + }, + { + "constant":false, + "inputs":[ + + ], + "name":"sync", + "outputs":[ + + ], + "payable":false, + "stateMutability":"nonpayable", + "type":"function" + }, + { + "constant":true, + "inputs":[ + + ], + "name":"token0", + "outputs":[ + { + "internalType":"address", + "name":"", + "type":"address" + } + ], + "payable":false, + "stateMutability":"view", + "type":"function" + }, + { + "constant":true, + "inputs":[ + + ], + "name":"token1", + "outputs":[ + { + "internalType":"address", + "name":"", + "type":"address" + } + ], + "payable":false, + "stateMutability":"view", + "type":"function" + }, + { + "constant":true, + "inputs":[ + + ], + "name":"totalSupply", + "outputs":[ + { + "internalType":"uint256", + "name":"", + "type":"uint256" + } + ], + "payable":false, + "stateMutability":"view", + "type":"function" + }, + { + "constant":false, + "inputs":[ + { + "internalType":"address", + "name":"to", + "type":"address" + }, + { + "internalType":"uint256", + "name":"value", + "type":"uint256" + } + ], + "name":"transfer", + "outputs":[ + { + "internalType":"bool", + "name":"", + "type":"bool" + } + ], + "payable":false, + "stateMutability":"nonpayable", + "type":"function" + }, + { + "constant":false, + "inputs":[ + { + "internalType":"address", + "name":"from", + "type":"address" + }, + { + "internalType":"address", + "name":"to", + "type":"address" + }, + { + "internalType":"uint256", + "name":"value", + "type":"uint256" + } + ], + "name":"transferFrom", + "outputs":[ + { + "internalType":"bool", + "name":"", + "type":"bool" + } + ], + "payable":false, + "stateMutability":"nonpayable", + "type":"function" + } +] \ No newline at end of file diff --git a/web/css/tools/web3/sales/main.css b/web/css/tools/web3/sales/main.css new file mode 100644 index 0000000..6383a03 --- /dev/null +++ b/web/css/tools/web3/sales/main.css @@ -0,0 +1,3 @@ +.Assets_web3_sales_main_prices, .Assets_web3_sales_main_bonuses { + display:block; +} \ No newline at end of file diff --git a/web/js/Assets.js b/web/js/Assets.js index 4d3a46a..49f5722 100644 --- a/web/js/Assets.js +++ b/web/js/Assets.js @@ -401,6 +401,7 @@ var Assets = Q.Assets = Q.plugins.Assets = { getAll: new Q.Method(), getFundConfig: new Q.Method(), getWhitelisted: new Q.Method(), + getOwner: new Q.Method(), adjustFundConfig: function(infoConfig, options) { //make output data an userfriendly var infoConfigAdjusted = Object.assign({}, infoConfig); @@ -408,9 +409,11 @@ var Assets = Q.Assets = Q.plugins.Assets = { infoConfigAdjusted._endTs = new Date(parseInt(infoConfig._endTs) * 1000).toDateString(); infoConfigAdjusted._prices = infoConfig._prices.map( x => ethers.utils.formatUnits( - x.toString(), + (x/100).toString(), //div to 100 to get $ instead cents Q.isEmpty(options.priceDenom)?18:Math.log10(options.priceDenom) )); + + infoConfigAdjusted._amountRaised = infoConfig._amountRaised.map(x => Math.trunc(ethers.utils.formatUnits(x.toString(), 18))); infoConfigAdjusted._thresholds = infoConfig._thresholds.map(x => ethers.utils.formatUnits(x.toString(), 18)); infoConfigAdjusted._timestamps = infoConfig._timestamps.map(x => new Date(parseInt(x) * 1000).toDateString()); @@ -522,6 +525,7 @@ var Assets = Q.Assets = Q.plugins.Assets = { }} } }, '{{Assets}}/js/methods/Assets/Web3'), + }; @@ -656,6 +660,15 @@ Q.Tool.define({ js: "{{Assets}}/js/tools/web3/coin/admin.js", css: ["{{Assets}}/css/tools/web3/coin/admin.css", "{{Q}}/css/bootstrap-custom/bootstrap.css"] }, + "Assets/web3/sales/main": { + js: "{{Assets}}/js/tools/web3/sales/main.js", + css: ["{{Assets}}/css/tools/web3/sales/main.css"], + text: ["Assets/content", "Assets/web3/sales/main"] + }, + "Assets/web3/sales/group": { + js: "{{Assets}}/js/tools/web3/sales/group.js", + text: ["Assets/content", "Assets/web3/sales/group"] + }, "Assets/web3/coin/staking/start": { js: "{{Assets}}/js/tools/web3/coin/staking/start.js", css: ["{{Assets}}/css/tools/web3/coin/staking/start.css", "{{Q}}/css/bootstrap-custom/bootstrap.css"], diff --git a/web/js/methods/Assets/Funds/getOwner.js b/web/js/methods/Assets/Funds/getOwner.js new file mode 100644 index 0000000..c00a2f2 --- /dev/null +++ b/web/js/methods/Assets/Funds/getOwner.js @@ -0,0 +1,27 @@ +Q.exports(function () { + /** + * get contract owner address + * @param {type} contractAddress + * @param {type} chainId + * @param {type} callback + */ + return function getOwner(contractAddress, chainId, callback){ + return Q.Users.Web3.getContract( + 'Assets/templates/R1/Sales/contract', + { + contractAddress: contractAddress, + readOnly: true, + chainId: chainId + } + ).then(function (contract) { + return contract.owner(); + }).then(function (ret) { + Q.handle(callback, null, [null, ret]); + return ret; + }).catch(function(err){ + Q.handle(callback, null, [err]); + console.warn(err); + }); + } + +}); \ No newline at end of file diff --git a/web/js/tools/web3/sales/group.js b/web/js/tools/web3/sales/group.js new file mode 100644 index 0000000..621b297 --- /dev/null +++ b/web/js/tools/web3/sales/group.js @@ -0,0 +1,295 @@ +(function (window, Q, $, undefined) { + + /** + * @module Assets + */ + var Assets = Q.Assets; + + /** + * Sales + * @class Assets Web3 Sales Group + * @constructor + * @param {Object} options Override various options for this tool + * @param {String} [options.salesContractAddress] address of Sales contract + * @param {String} [options.chainId] chainId + * @param {String} [options.abiPath] ABI path for SalesWithStablePrice contract + */ + Q.Tool.define("Assets/web3/sales/group", function (options) { + var tool = this; + var state = this.state; + state.choosen = {}; + tool.loggedInUserXid = Q.Users.Web3.getLoggedInUserXid(); + + tool.refresh(); + + }, + + { // default options here + salesContractAddress: null, + chainId: null, + abiPath: 'Assets/templates/R1/Sales/contract' + }, + + { // methods go here + refresh: function () { + + var tool = this; + var state = tool.state; + + Q.Template.render("Assets/web3/sales/group/preloader", { + src: Q.url("{{Q}}/img/throbbers/loading.gif") + }, function (err, html) { + Q.replace(tool.element, html); + }); + + var p = []; + p.push(Q.Assets.Funds.getWhitelisted(state.salesContractAddress, tool.loggedInUserXid, state.chainId)); + p.push(Q.Assets.Funds.getOwner(state.salesContractAddress, state.chainId)); + + Promise.allSettled(p).then(function(_ref){ + + if (_ref[0].status == 'fulfilled' && _ref[1].status == 'fulfilled') { + + Q.Template.render("Assets/web3/sales/group", { + isOwner: _ref[1].value.toLowerCase() == tool.loggedInUserXid.toLowerCase(), + whitelisted: _ref[0].value + + }, function (err, html) { + Q.replace(tool.element, html); + tool.bindLinks(); + + }); + } else { + throw 'Can\'t get data'; + return; + } + }) + }, + bindLinks: function(){ + var tool = this; + var state = tool.state; + + $('.userWeb3Address', tool.element).tool('Users/web3/address', { + onAddress: function(address, userId, avatar) { + state.choosen.address = address; + }, + userChooser: { + onChoose: function(userId, avatar) { + + _getXid(tool, userId, function (err, xid) { + if (err) { + return Q.alert(err); + } + + state.choosen.address = xid; +// if (false !== Q.handle(state.onAddress, tool, [xid, userId, avatar])) { +// state.chosen.address = xid; +// } + }); + } + } + }).activate(function(){ + //console.log('activated'); + }); + $('.addToWhitelist', tool.element).off(Q.Pointer.click).on(Q.Pointer.fastclick, function(e){ + e.preventDefault(); + e.stopPropagation(); + + $(tool.element).addClass("Q_working"); + + var validated = true; + if ( + Q.Users.Web3.validate.notEmpty(state.choosen.address) && + Q.Users.Web3.validate.address(state.choosen.address) + ) { + // + } else { + + Q.Notices.add({ + content: tool.text.sales.group.errors.AddressInvalid, + timeout: 5 + }); + validated = false; + } + + if (!validated) { + $(tool.element).removeClass("Q_working"); + return; + } + + Q.Users.Web3.getContract( + state.abiPath, + { + contractAddress: state.salesContractAddress, + chainId: state.chainId + } + ).then(function (contract) { + return contract.whitelistAdd( + state.choosen.address, + ); + }).then(function (tx) { + return tx.wait(); + }).then(function (receipt) { + if (receipt.status == 0) { + throw 'Smth unexpected when approve'; + } + tool.refresh(); + }).finally(function(){ + $(tool.element).removeClass("Q_working"); + }) + + }); + + $('.addToGroup', tool.element).off(Q.Pointer.click).on(Q.Pointer.fastclick, function(e){ + e.preventDefault(); + e.stopPropagation(); + + $(tool.element).addClass("Q_working"); + + var validated = true; + var groupName = $('.groupName').val(); + if ( + Q.Users.Web3.validate.notEmpty(groupName) + ) { + // + } else { + + Q.Notices.add({ + content: tool.text.sales.group.errors.GroupNameInvalid, + timeout: 5 + }); + validated = false; + } + + if ( + Q.Users.Web3.validate.notEmpty(state.choosen.address) && + Q.Users.Web3.validate.address(state.choosen.address) + ) { + // + } else { + + Q.Notices.add({ + content: tool.text.sales.group.errors.AddressInvalid, + timeout: 5 + }); + validated = false; + } + + if (!validated) { + $(tool.element).removeClass("Q_working"); + return; + } + + Q.Users.Web3.getContract( + state.abiPath, + { + contractAddress: state.salesContractAddress, + chainId: state.chainId + } + ).then(function (contract) { + //function setGroup(address[] memory addresses, string memory groupName) public onlyOwner { + return contract.setGroup( + [state.choosen.address], + groupName + ); + }).then(function (tx) { + return tx.wait(); + }).then(function (receipt) { + if (receipt.status == 0) { + throw 'Smth unexpected when approve'; + } + tool.refresh(); + }).finally(function(){ + $(tool.element).removeClass("Q_working"); + }) + + }); + }, + Q: { + beforeRemove: function () { + + } + } + }); + + Q.Template.set("Assets/web3/sales/group/preloader", + ` + + `, + {text: ["Assets/content", "Assets/web3/sales/main"]} + ); + + Q.Template.set("Assets/web3/sales/group", + ` + {{#if this.isOwner}} +
+
+
+ +
+
+
+ +
+ +
+
+ {{else}} + {{/if}} +
+ {{#if this.whitelisted}} + {{sales.group.titles.OnWhitelist}} + {{else}} + {{sales.group.titles.NotYetOnWhitelist}} + {{/if}}
+ Group: TBD
+ `, + {text: ["Assets/content", "Assets/web3/sales/group"]} + ); + + // this function might eventually be moved to Q.Streams.Web3 + function _getXid(tool, userId, callback) { + Q.Streams.get(userId, "Streams/user/xid/web3", function (err) { + if (err) { + return callback(err); + } + + var wallet, walletError; + if (!this.testReadLevel("content")) { + walletError = tool.text.errors.NotEnoughPermissionsWallet; + } else { + wallet = this.fields.content; + if (!wallet) { + walletError = tool.text.errors.ThisUserHaveNoWallet; + } else if (!ethers.utils.isAddress(wallet)) { + walletError = tool.text.errors.TheWalletOfThisUserInvalid; + } + } + + if (walletError) { + return callback(walletError); + } + + callback(null, this.fields.content); + }); + } +})(window, Q, Q.jQuery); + /** + p.push(Q.Assets.Funds.getWhitelisted(contractAddress, userAddress, chainId)); + * +Make a second tool: +Assets/web3/sales/group +It can have an option to specify existing chainId and address. Otherwise it will have the following elements +Users/web3/chain tool for selecting chain +This one will have a Users/web3/address tool +It checks if logged in user has web3 wallet and this wallet is owner, otherwise it will be read-only and not show any buttons or input boxes, +just say “not yet on whitelist” and “not yet in a group”. +When this tool’s address changes, onUpdate event (or similar) you get the whitelist membership and group membership and re-render template +1) [Add to Whitelist] button or “Already on whitelist” +2) or show the group name if already in one +all text should come from Assets/content text, see how templates incorporate placeholders, the text is autoloaded by Qbix + */ + +/* + +*/ \ No newline at end of file diff --git a/web/js/tools/web3/sales/main.js b/web/js/tools/web3/sales/main.js new file mode 100644 index 0000000..71d8a9e --- /dev/null +++ b/web/js/tools/web3/sales/main.js @@ -0,0 +1,359 @@ +(function (window, Q, $, undefined) { + + /** + * Sales + * @class Assets Web3 Sales Main + * @constructor + * @param {Object} options Override various options for this tool + * @param {String} [options.salesContractAddress] address of Sales contract + * @param {String} [options.chainId] chainId + * @param {String} [options.uniswapPair] ABI path for uniswapPair contract + * @param {String} [options.abiPath] ABI path for SalesWithStablePrice contract + */ + Q.Tool.define("Assets/web3/sales/main", function (options) { + var tool = this; + var state = this.state; + + if (Q.isEmpty(state.salesContractAddress)) { + return console.warn("salesContractAddress required!"); + } + if (Q.isEmpty(state.chainId)) { + return console.warn("chainId required!"); + } + if (Q.isEmpty(state.uniswapPair)) { + return console.warn("uniswapPair required!"); + } + + tool.loggedInUserXid = Q.Users.Web3.getLoggedInUserXid(); + + // be carefull (1*10**8) it's for cents(Sales with stableprices) or for bative coins + tool.priceDenom = 100_000_000;//(1*10**8); + + tool.refresh(); + + }, + { // default options here + salesContractAddress: null, + chainId: null, + uniswapPair: null, + abiPath: 'Assets/templates/R1/Sales/contract' + }, + + { // methods go here + refresh: function () { + + var tool = this; + var state = tool.state; + + Q.Template.render("Assets/web3/sales/main/preloader", { + src: Q.url("{{Q}}/img/throbbers/loading.gif") + }, function (err, html) { + Q.replace(tool.element, html); + }); + Q.Assets.Funds.getFundConfig( state.salesContractAddress, state.chainId, ethers.utils.getAddress(tool.loggedInUserXid), function(err, infoConfig){ + if (err) { + console.warn('error happens') + return; + } + tool.infoConfig = infoConfig; + + Q.Template.render("Assets/web3/sales/main", {}, function (err, html) { + Q.replace(tool.element, html); + tool.refreshCurrentTokenPrice(); + tool.refreshCurrentUSDPrice(); + tool.bindLinks(); + }); + }); + + }, + updateNativeCoinBalance: function(){ + var tool = this; + var state = tool.state; + + Q.Assets.Currencies.balanceOf(tool.loggedInUserXid, state.chainId, function (err, moralisBalance) { + tool.nativeCoinBalance = moralisBalance[0].balance + },{tokenAddresses: null}); + }, + refreshCurrentUSDPrice: function(){ + var tool = this; + var state = tool.state; + return Q.Users.Web3.getContract( + 'Assets/templates/Uniswap/V2/Pair', { + chainId: state.chainId, + contractAddress: state.uniswapPair, + readOnly: true + } + ).then(function (contract) { + return contract.getReserves(); + }).then(function (reserves) { + tool.reserves = reserves; + }).catch(function(err){ + console.warn(err); + }) + }, + refreshCurrentTokenPrice: function(){ + var tool = this; + var state = tool.state; + + var $priceContainer = $(tool.element).find('.currentPriceContainer'); + Q.Template.render("Assets/web3/sales/main/preloader", { + src: Q.url("{{Q}}/img/throbbers/loading.gif") + }, function (err, html) { + Q.replace($priceContainer[0], html); + }); + + + return Q.Users.Web3.getContract( + state.abiPath, { + chainId: state.chainId, + contractAddress: state.salesContractAddress, + readOnly: true + } + ).then(function (contract) { + return contract.getTokenPrice(); + }).then(function (price) { + + tool.tokenPrice = price; + var adjustPrice = ethers.utils.formatUnits( + (price/100).toString(), //div to 100 to get $ instead cents + Q.isEmpty(tool.priceDenom)?18:Math.log10(tool.priceDenom) + ); + $priceContainer.html(`${adjustPrice} USD per ${tool.infoConfig.erc20TokenInfo.name}`); + return price; + }).catch(function(err){ + console.warn(err); + }) + }, + bindLinks: function(){ + var tool = this; + var state = tool.state; + var $toolElement = $(this.element); + + $('.Assets_web3_sales_main_prices', tool.element).off(Q.Pointer.click).on(Q.Pointer.fastclick, function(){ + + Q.Dialogs.push({ + title: tool.text.sales.main.titles.prices, + //className: "Assets_web3_transfer_transferTokens", + onActivate: function (dialog) { + + if (Q.isEmpty(tool.infoConfig)) { + Q.Template.render("Assets/web3/sales/main/preloader", { + src: Q.url("{{Q}}/img/throbbers/loading.gif") + }, function (err, html) { + Q.replace($(dialog).find('.Q_dialog_content').get(0), html); + }); + + // fill or refresh tool.infoConfig + } + + Q.Template.render("Assets/web3/sales/main/prices", { + //src: Q.url("{{Q}}/img/throbbers/loading.gif") + data: Q.Assets.Funds.adjustFundConfig(tool.infoConfig, {priceDenom: tool.priceDenom}) + }, function (err, html) { + Q.replace($(dialog).find('.Q_dialog_content').get(0), html); + }); + + } + }) + }); + + $('.Assets_web3_sales_main_bonuses', tool.element).off(Q.Pointer.click).on(Q.Pointer.fastclick, function(){ + + Q.Dialogs.push({ + title: tool.text.sales.main.titles.bonuses, + //className: "Assets_web3_transfer_transferTokens", + onActivate: function (dialog) { + + if (Q.isEmpty(tool.infoConfig)) { + Q.Template.render("Assets/web3/sales/main/preloader", { + src: Q.url("{{Q}}/img/throbbers/loading.gif") + }, function (err, html) { + Q.replace($(dialog).find('.Q_dialog_content').get(0), html); + }); + } + + Q.Template.render("Assets/web3/sales/main/bonuses", { + data: Q.Assets.Funds.adjustFundConfig(tool.infoConfig, {priceDenom: tool.priceDenom}) + }, function (err, html) { + Q.replace($(dialog).find('.Q_dialog_content').get(0), html); + }); + + } + }) + }); + + $('.buyContainer input', tool.element).off('keyup').on('keyup', function(){ + + var val = $(this).val(); + if (val != parseFloat(val)) { + return; + } + + Q.Template.render("Assets/web3/sales/main/ethEquivalentText", { + ethAmount: tool.reserves[1]/tool.reserves[0]*parseFloat(val) + }, function (err, html) { + $('.ethEquivalent', tool.element).html(html); + }); + }); + + $('.buyBtn', tool.element).off(Q.Pointer.click).on(Q.Pointer.fastclick, function(){ + $toolElement.addClass("Q_working"); + var amount = $('.buyContainer input', tool.element).val(); + var validated = true; + + if ( + !Q.Users.Web3.validate.notEmpty(amount) || + !Q.Users.Web3.validate.numeric(amount) + ) { + Q.Notices.add({ + content: "Amount invalid", + timeout: 5 + }); + validated = false; + } + + if (!tool.infoConfig.inWhitelist) { + Q.Notices.add({ + content: "Not in Whitelist", + timeout: 5 + }); + validated = false; + } + + if (!validated) { + $toolElement.removeClass("Q_working"); + return; + } + + Q.Users.Web3.transaction( + state.salesContractAddress, + tool.reserves[1]/tool.reserves[0]*parseFloat(amount), + function(err, transactionRequest, transactionReceipt){ + if (err) { + Q.alert(Q.Users.Web3.parseMetamaskError(err)); + return $toolElement.removeClass("Q_working"); + } + + $toolElement.removeClass("Q_working"); + tool.updateNativeCoinBalance(); + + }, + { + chainId: state.chainId + } + + ) + + }) + }, + + Q: { + beforeRemove: function () { + + } + } + }); + + Q.Template.set("Assets/web3/sales/main/preloader", + ` + + `, + {text: ["Assets/content", "Assets/web3/sales/main"]} + ); + + Q.Template.set("Assets/web3/sales/main/ethEquivalentText", + ` + {{ethAmount}} bnb + `, + {text: ["Assets/content", "Assets/web3/sales/main"]} + ); + + Q.Template.set("Assets/web3/sales/main/bonuses", + ` + + + + + + + + + + {{#each data._thresholds}} + + + + + {{/each}} + +
{{sales.main.titles.condition1}}{{sales.main.titles.bonus}}
+ {{this}} + + {{lookup ../data._bonuses @index}} +
+ `, + { + text: ["Assets/content", "Assets/web3/sales/main"] + } + ); + + Q.Template.set("Assets/web3/sales/main/prices", + ` + + + + + + + + + {{#each data._amountRaised}} + + + + + {{/each}} + +
{{sales.main.titles.condition2}}{{sales.main.titles.price}}
+ {{#ifEquals this 0}} + {{#ifEquals (lookup ../data._timestamps @index) "Thu Jan 01 1970"}} + error + {{else}} + From {{lookup ../data._timestamps @index}} + {{/ifEquals}} + {{else}} + {{#ifEquals (lookup ../data._timestamps @index) "Thu Jan 01 1970"}} + Reach {{this}} tokens + {{else}} + {{this}} - {{lookup ../data._timestamps @index}} + {{/ifEquals}} + {{/ifEquals}} + + {{lookup ../data._prices @index}} +
+ `, + { + text: ["Assets/content", "Assets/web3/sales/main"] + } + ); + + Q.Template.set("Assets/web3/sales/main", + ` +
+
+ + +
+
+ {{sales.main.titles.prices}} + {{sales.main.titles.bonuses}} + `, + { + text: ["Assets/content", "Assets/web3/sales/main"] + } + ); + + + + +})(window, Q, Q.jQuery); \ No newline at end of file