├── .env ├── .gitignore ├── book.json ├── tests ├── test │ ├── vesting │ │ ├── index.js │ │ ├── vestingSchedule100.js │ │ └── vestingSchedule20-80.js │ ├── multi-pool.js │ ├── stake.js │ ├── erc20.js │ ├── nft-whitelist.js │ └── nft-erc20.js ├── index.js └── utils │ ├── index.js │ ├── numbers.js │ └── signer.js ├── help.txt ├── tooling └── docs │ └── jsdoc.json ├── index.js ├── docs ├── SUMMARY.md ├── README.md ├── WALLET.md ├── NUMBERS.md ├── NETWORK.md ├── APPLICATION.md ├── STAKING.md ├── SIGNER.md ├── IDOSTAKING.md ├── NFT_SWAP.md └── BASE_SWAP.md ├── src ├── interfaces │ ├── oldredeemmethod.json │ └── index.js ├── services │ └── DeploymentService.js ├── models │ ├── base │ │ ├── Addresses.js │ │ ├── Account.js │ │ ├── Contract.js │ │ ├── ERC20TokenContract.js │ │ └── Staking.js │ ├── index.js │ └── contracts │ │ ├── IDOStaking.js │ │ └── FixedNFTSwapContract.js └── utils │ ├── Wallet.js │ ├── Client.js │ ├── Network.js │ ├── Numbers.js │ ├── Chains.js │ └── Signer.js ├── VERSIONS.md ├── LICENSE ├── .circleci └── config.yml ├── README.md └── package.json /.env: -------------------------------------------------------------------------------- 1 | TEST_PRIVATE_KEY=0xfdf5475fe6be966cf39e533e5b478b2e10d04e5e966be18f45714550d2429d21 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | node_modules 3 | out 4 | _book 5 | .DS_Store 6 | .nyc_output -------------------------------------------------------------------------------- /book.json: -------------------------------------------------------------------------------- 1 | { 2 | "gitbook": "3.2.2", 3 | "root": "./docs", 4 | "structure": { 5 | "readme": "README.md" 6 | } 7 | } -------------------------------------------------------------------------------- /tests/test/vesting/index.js: -------------------------------------------------------------------------------- 1 | context('Vesting', async () => { 2 | require('./vestingSchedule20-80'); 3 | require('./vestingSchedule100'); 4 | }); -------------------------------------------------------------------------------- /help.txt: -------------------------------------------------------------------------------- 1 | // Update Github 2 | ``git push origin master`` 3 | 4 | // Create a new version 5 | ``npm version VERSION``` 6 | 7 | // Publish new version 8 | ``npm publish`` -------------------------------------------------------------------------------- /tooling/docs/jsdoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["node_modules/babel-core", "node_modules/babel-register"], 3 | "babel": { 4 | "presets": [ "es2015" ] 5 | } 6 | } -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | let index = { 2 | FixedSwapContract : require("./src/models/contracts/FixedSwapContract"), 3 | Application : require("./src/models/index"), 4 | ERC20TokenContract : require("./src/models/base/ERC20TokenContract") 5 | }; 6 | 7 | module.exports = index; 8 | -------------------------------------------------------------------------------- /docs/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | ### Getting Started 4 | 5 | * [Application](APPLICATION.md) 6 | * [Fixed Swap](SWAP.md) 7 | * [Numbers Utils](NUMBERS.md) 8 | * [Signer](SIGNER.md) 9 | * [Network Utils](NETWORK.md) 10 | * [Staking](STAKING.md) 11 | * [IDO Staking](IDOSTAKING.md) 12 | * [Wallet Utils](WALLET.md) -------------------------------------------------------------------------------- /src/interfaces/oldredeemmethod.json: -------------------------------------------------------------------------------- 1 | { 2 | "inputs": [], 3 | "name": "unsoldTokensReedemed", 4 | "outputs": [ 5 | { 6 | "internalType": "bool", 7 | "name": "", 8 | "type": "bool" 9 | } 10 | ], 11 | "stateMutability": "view", 12 | "type": "function" 13 | } -------------------------------------------------------------------------------- /VERSIONS.md: -------------------------------------------------------------------------------- 1 | ## Polkastarter JS Versions 2 | 3 | | PSJS | Swap SC | Compatible with SC versions | 4 | |:---------|:--------------|:-----------------------------| 5 | | 1.2.7 | v2.1 | v2.1, v2.0 | 6 | | 1.2.8 | v3.0-RC5 | v3.0-RC5, v2.1, v2.0 | 7 | | 2.0.0 | v3.0.0 | v3.0.0, v2.1, v2.0 | 8 | -------------------------------------------------------------------------------- /tests/index.js: -------------------------------------------------------------------------------- 1 | context('Tests', async () => { 2 | require('./utils/numbers'); 3 | require('./utils/signer'); 4 | require('./test/eth'); 5 | require('./test/stake'); 6 | require('./test/nft'); 7 | require('./test/nft-whitelist'); 8 | require('./test/nft-erc20'); 9 | //require('./test/vesting'); 10 | //require('./test/erc20'); 11 | }); 12 | -------------------------------------------------------------------------------- /src/services/DeploymentService.js: -------------------------------------------------------------------------------- 1 | 2 | class DeploymentService { 3 | 4 | async deploy(account, contract, params, callback) { 5 | return await contract.deploy( 6 | account, 7 | contract.getABI(), 8 | contract.getJSON().bytecode, 9 | params, 10 | callback 11 | ) 12 | }; 13 | 14 | } 15 | 16 | export default DeploymentService; -------------------------------------------------------------------------------- /tests/utils/index.js: -------------------------------------------------------------------------------- 1 | export const mochaAsync = (fn) => { 2 | return done => { 3 | fn.call().then(done, err => { 4 | done(err); 5 | }); 6 | }; 7 | }; 8 | 9 | export const detectValidationErrors = (res) => { 10 | if(res.message == 'Validation errors'){ 11 | console.log(res.errors[0]); 12 | return true; 13 | }else{ 14 | return false; 15 | } 16 | } -------------------------------------------------------------------------------- /src/models/base/Addresses.js: -------------------------------------------------------------------------------- 1 | class addresses { 2 | tokenAddresses = { 3 | 'BSC': '0x7e624fa0e1c4abfd309cc15719b7e2580887f570', 4 | 'ETH': '0x83e6f1E41cdd28eAcEB20Cb649155049Fac3D5Aa' 5 | }; 6 | tokenTestAddresses = { 7 | 'BSC': '0xcfd314B14cAB8c3e36852A249EdcAa1D3Dd05055', 8 | 'ETH': '0x03EF180c07D30E46CAc83e5b9E282a9B295ca8A9' 9 | }; 10 | } 11 | 12 | let Addresses = new addresses() 13 | 14 | export default Addresses -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | # Polkastarter JS Docs 2 | 3 | Welcome to the Polkastarter JS doc to integrate Polkastarter. 4 | 5 | #### [Application](/docs/APPLICATION.md) 6 | 7 | #### [FixedSwap](/docs/SWAP.md) 8 | 9 | #### [FixedNFTSwap](/docs/NFT_SWAP.md) 10 | 11 | #### [Numbers Utils](/docs/NUMBERS.md) 12 | 13 | #### [Signer](/docs/SIGNER.md) 14 | 15 | #### [Network Utils](/docs/NETWORK.md) 16 | 17 | #### [Staking](/docs/STAKING.md) 18 | 19 | #### [IDO Staking](/docs/IDOSTAKING.md) 20 | 21 | #### [Wallet](/docs/WALLET.md) -------------------------------------------------------------------------------- /src/interfaces/index.js: -------------------------------------------------------------------------------- 1 | const fixedswap = () => { 2 | const originalJson = require("./fixedswap.json"); 3 | originalJson.abi.push(require("./oldredeemmethod.json")); 4 | return originalJson; 5 | } 6 | 7 | let index = { 8 | fixedswap: fixedswap(), 9 | fixednftswap: require("./fixednftswap.json"), 10 | fixedswap_legacy: require("./fixedswap_legacy.json"), 11 | ierc20: require("./ierc20token.json"), 12 | staking: require("./staking.json"), 13 | idostaking: require("./idostaking.json"), 14 | }; 15 | 16 | module.exports = index; 17 | -------------------------------------------------------------------------------- /docs/WALLET.md: -------------------------------------------------------------------------------- 1 | ## Classes 2 | 3 |
4 |
Wallet
5 |
6 |
7 | 8 | ## Functions 9 | 10 |
11 |
addTokenToWallet()
12 |

Adds POLS token to user's wallet

13 |
14 |
15 | 16 | 17 | 18 | ## Wallet 19 | **Kind**: global class 20 | 21 | 22 | ### new Wallet([network], [test]) 23 | Wallet utils object 24 | 25 | 26 | | Param | Type | Description | 27 | | --- | --- | --- | 28 | | [network] | ETH \| BSC \| MATIC \| DOT | The network where the token contract is. (Default: ETH) | 29 | | [test] | Boolean | ? Specifies if we're on test env (Default: false) | 30 | 31 | 32 | 33 | ## addTokenToWallet() 34 | Adds POLS token to user's wallet 35 | 36 | **Kind**: global function 37 | -------------------------------------------------------------------------------- /src/models/base/Account.js: -------------------------------------------------------------------------------- 1 | class Account{ 2 | 3 | constructor(web3, account){ 4 | this.web3 = web3; 5 | this.account = account; 6 | } 7 | 8 | 9 | async getBalance(){ 10 | let wei = await this.web3.eth.getBalance(this.getAddress()); 11 | return this.web3.utils.fromWei(wei, 'ether') 12 | } 13 | 14 | getAddress(){ 15 | return this.account.address; 16 | } 17 | 18 | getPrivateKey(){ 19 | return this.account.privateKey; 20 | } 21 | 22 | getAccount(){ 23 | return this.account 24 | } 25 | 26 | async sendEther(amount, address, data=null){ 27 | let tx = { 28 | data : data, 29 | from : this.getAddress(), 30 | to : address, 31 | gas : 443000, 32 | value: amount 33 | } 34 | let result = await this.account.signTransaction(tx); 35 | let transaction = await this.web3.eth.sendSignedTransaction(result.rawTransaction); 36 | return transaction; 37 | } 38 | } 39 | 40 | 41 | export default Account; -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Polkastarter 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 | -------------------------------------------------------------------------------- /src/utils/Wallet.js: -------------------------------------------------------------------------------- 1 | /* istanbul ignore file */ 2 | 3 | import Addresses from "../models/base/Addresses"; 4 | 5 | import Chains from "./Chains"; 6 | 7 | /** 8 | * Wallet utils object 9 | * @constructor Wallet 10 | * @param {(ETH|BSC|MATIC|DOT)=} network The network where the token contract is. (Default: ETH) 11 | * @param {Boolean=} test ? Specifies if we're on test env (Default: false) 12 | */ 13 | class Wallet { 14 | 15 | constructor(network='ETH', test = false) { 16 | Chains.checkIfNetworkIsSupported(network); 17 | this.network = network; 18 | this.test = test; 19 | if (test) { 20 | this.tokenAddress = Addresses.tokenTestAddresses; 21 | } else { 22 | this.tokenAddress = Addresses.tokenAddresses; 23 | } 24 | 25 | } 26 | 27 | /** 28 | * @function addTokenToWallet 29 | * @description Adds POLS token to user's wallet 30 | */ 31 | async addTokenToWallet() { 32 | if (window.ethereum) { 33 | await window.ethereum.request({ 34 | method: 'metamask_watchAsset', 35 | params: { 36 | "type": "ERC20", 37 | "options": { 38 | "address": this.tokenAddress, 39 | "symbol": "POLS", 40 | "decimals": 18 41 | }, 42 | }, 43 | }); 44 | } 45 | } 46 | 47 | } 48 | 49 | export default Wallet; -------------------------------------------------------------------------------- /src/utils/Client.js: -------------------------------------------------------------------------------- 1 | /* istanbul ignore file */ 2 | 3 | /** 4 | * Client utils object 5 | * @constructor Network 6 | */ 7 | class Client { 8 | metamaskCall = async ({ f, acc, value, callback=()=> {} }) => { 9 | return new Promise( (resolve, reject) => { 10 | // Detect possible error on tx 11 | f.estimateGas({gas: 5000000}, (error, gasAmount) => { 12 | //if(error){reject("Transaction will fail : " + error);} 13 | if(gasAmount >= 5000000){ 14 | reject("Transaction will fail, too much gas"); 15 | } 16 | 17 | // all alright 18 | f.send({ 19 | from: acc, 20 | value: value, 21 | }) 22 | .on("confirmation", (confirmationNumber, receipt) => { 23 | callback(confirmationNumber) 24 | if (confirmationNumber > 0) { 25 | resolve(receipt); 26 | } 27 | }) 28 | .on("error", (err) => { 29 | reject(err); 30 | }); 31 | }); 32 | }); 33 | }; 34 | 35 | sendTx = async (web3, acc, contract, f, call = false, value, callback=()=>{}) => { 36 | var res; 37 | if (!acc && !call) { 38 | const accounts = await web3.eth.getAccounts(); 39 | res = await this.metamaskCall({ f, acc: accounts[0], value, callback }); 40 | } else if (acc && !call) { 41 | let data = f.encodeABI(); 42 | res = await contract.send( 43 | acc.getAccount(), 44 | data, 45 | value 46 | ); 47 | } else if (acc && call) { 48 | res = await f.call({ from: acc.getAddress() }); 49 | } else { 50 | res = await f.call(); 51 | } 52 | return res; 53 | }; 54 | } 55 | export default Client; 56 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2.1 # use CircleCI 2.1 2 | jobs: # a collection of steps 3 | build: # runs not using Workflows must have a `build` job as entry point 4 | working_directory: ~/mern-starter # directory where steps will run 5 | docker: # run the steps with Docker 6 | - image: circleci/node:10.13.0 # ...with this image as the primary container; this is where all `steps` will run 7 | steps: # a collection of executable commands 8 | - checkout # special step to check out source code to working directory 9 | - run: 10 | name: update-npm 11 | command: 'sudo npm install -g npm@latest' 12 | - restore_cache: # special step to restore the dependency cache 13 | # Read about caching dependencies: https://circleci.com/docs/2.0/caching/ 14 | key: dependency-cache-{{ checksum "package.json" }} 15 | - run: 16 | name: install-npm-wee 17 | command: npm install 18 | - run: # run tests 19 | name: test 20 | command: npm run test 21 | - save_cache: # special step to save the dependency cache 22 | key: dependency-cache-{{ checksum "package.json" }} 23 | paths: 24 | - ./node_modules 25 | - store_artifacts: # special step to save test results as as artifact 26 | # Upload test summary for display in Artifacts: https://circleci.com/docs/2.0/artifacts/ 27 | path: test-results.xml 28 | prefix: tests 29 | - store_artifacts: # for display in Artifacts: https://circleci.com/docs/2.0/artifacts/ 30 | path: coverage 31 | prefix: coverage 32 | - store_test_results: # for display in Test Summary: https://circleci.com/docs/2.0/collect-test-data/ 33 | path: test-results.xml 34 | # See https://circleci.com/docs/2.0/deployment-integrations/ for deploy examples 35 | -------------------------------------------------------------------------------- /tests/utils/numbers.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | import { Decimal } from 'decimal.js'; 3 | 4 | import chai from 'chai'; 5 | import Numbers from '../../src/utils/Numbers'; 6 | import { mochaAsync } from '../utils'; 7 | 8 | const ERC20TokenAddress = '0x7a7748bd6f9bac76c2f3fcb29723227e3376cbb2'; 9 | var contractAddress = '0x420751cdeb28679d8e336f2b4d1fc61df7439b5a'; 10 | var userPrivateKey = process.env.TEST_PRIVATE_KEY || '0x7f76de05082c4d578219ca35a905f8debe922f1f00b99315ebf0706afc97f132'; 11 | 12 | const expect = chai.expect; 13 | 14 | context('Numbers', async () => { 15 | var app; 16 | 17 | it('should get a Fixed Value', mochaAsync(async () => { 18 | expect(Numbers.toSmartContractDecimals(new Decimal(0.087).plus(0.01), 18)).to.equal("97000000000000000"); 19 | expect(Numbers.toSmartContractDecimals(new Decimal(0.087234523452345).plus(0.01), 18)).to.equal("97234523452345000"); 20 | expect(Numbers.toSmartContractDecimals(new Decimal(0.007234523453345333).plus(0.01), 18)).to.equal("17234523453345333"); 21 | expect(Numbers.fromDecimals("97000000000000000", 18)).to.equal("0.097"); 22 | })); 23 | 24 | it('should make divisions', mochaAsync(async () => { 25 | const res = Numbers.safeDivide(1, 1092.25); 26 | expect(Numbers.safeDivide(1, res)).to.equal(1092.25); 27 | })); 28 | 29 | it('should convert hex to string', mochaAsync(async () => { 30 | const res = Numbers.fromHex(0x01); 31 | expect(res).to.equal('1'); 32 | })); 33 | 34 | it('should convert to float', mochaAsync(async () => { 35 | const res = Numbers.toFloat('1.1413'); 36 | expect(res).to.equal(1.14); 37 | })); 38 | 39 | it('should convert time to smart contract time', mochaAsync(async () => { 40 | const res = Numbers.timeToSmartContractTime(new Date('2022-02-20 10:00:00')); 41 | expect(res).to.equal(1645347600); 42 | })); 43 | 44 | }); 45 | -------------------------------------------------------------------------------- /docs/NUMBERS.md: -------------------------------------------------------------------------------- 1 | ## Classes 2 | 3 |
4 |
Numbers
5 |
6 |
7 | 8 | ## Functions 9 | 10 |
11 |
toSmartContractDecimals(value, decimals)string
12 |

Converts a "human" number to the minimum unit.

13 |
14 |
safeDivide(value, valueToDivideTo)Float
15 |

Performs a safe division

16 |
17 |
fromDecimals(value, decimals)string
18 |

Converts a number from his minimum unit to a human readable one.

19 |
20 |
21 | 22 | 23 | 24 | ## Numbers 25 | **Kind**: global class 26 | 27 | 28 | ### new Numbers() 29 | Numbers object 30 | 31 | 32 | 33 | ## toSmartContractDecimals(value, decimals) ⇒ string 34 | Converts a "human" number to the minimum unit. 35 | 36 | **Kind**: global function 37 | 38 | | Param | Type | Description | 39 | | --- | --- | --- | 40 | | value | Float | The number that you want to convert | 41 | | decimals | Integer | Number of decimals | 42 | 43 | 44 | 45 | ## safeDivide(value, valueToDivideTo) ⇒ Float 46 | Performs a safe division 47 | 48 | **Kind**: global function 49 | 50 | | Param | Type | Description | 51 | | --- | --- | --- | 52 | | value | Float | The number that you want to divide | 53 | | valueToDivideTo | Float | The number that you want to divide to | 54 | 55 | 56 | 57 | ## fromDecimals(value, decimals) ⇒ string 58 | Converts a number from his minimum unit to a human readable one. 59 | 60 | **Kind**: global function 61 | 62 | | Param | Type | Description | 63 | | --- | --- | --- | 64 | | value | Float | The number that you want to convert | 65 | | decimals | Integer | Number of decimals | 66 | 67 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # polkastarter-js 2 | 3 | polkastarter-js is package to integrate Polkastarter Ethereum Integrations 4 | 5 | ## Installation 6 | 7 | Use the package manager [npm] to install npm. 8 | 9 | ```bash 10 | npm i polkastarter-js 11 | ``` 12 | 13 | ## Docs 14 | 15 | [Docs](/docs) 16 | 17 | ## Usage 18 | 19 | ```javascript 20 | import moment from 'moment'; 21 | import Application from 'polkastarter-js/src/models'; 22 | 23 | /* Test Version */ 24 | let app = new Application({test : true}); 25 | 26 | /* Create Contract */ 27 | let swapContract = app.getFixedSwapContract({tokenAddress : '0x3237fff7f25a354f68b2054a019c5a00135a8955', decimals : 18}); 28 | 29 | /* Deploy */ 30 | await swapContract.deploy({ 31 | tradeValue : 0.001, 32 | tokensForSale : 100, 33 | startDate : moment().add(6, 'hours'), 34 | endDate : moment().add(16, 'hours'), 35 | isETHTrade : true // isETHTrade, 36 | ERC20TradingAddress : // optional, 37 | isPOLSWhitelist : false // optional (default : false) 38 | }); 39 | 40 | /* User Swap */ 41 | 42 | /* a) (isETHTrade == false) */ 43 | /* 1 - swap */ 44 | await swapContract.swap({ 45 | tokenAmount : 10 46 | }); 47 | 48 | /* b) (isETHTrade == false) */ 49 | /* 1 - verify if approved */ 50 | await swapContract.isApprovedSwapERC20({ 51 | tokenAmount : 10 , 52 | address : /* address to be approved - user address */ 53 | }); 54 | 55 | /* 2 - approve tx (if not approved) */ 56 | await swapContract.approveSwapERC20({ 57 | tokenAmount : 10 58 | }); 59 | 60 | /* 3 - swap */ 61 | await swapContract.swap({ 62 | tokenAmount : 10 63 | }); 64 | 65 | ``` 66 | 67 | ## Testing 68 | 69 | To run the test suite vs a local chain (ganache): 70 | 71 | ```bash 72 | npm test 73 | ``` 74 | 75 | To run the test suite vs a testnet real chain (Kovan, BSC Testnet, etc): 76 | 77 | ```bash 78 | CHAIN_NAME=ETH npm test 79 | ``` 80 | 81 | ## Testing old deployed pools 82 | 83 | This test will check that the current PSJS doesn't break the read method from already deployed pools, legacy or not. 84 | 85 | ```bash 86 | npm run test-pools 87 | ``` 88 | 89 | ## License 90 | 91 | [MIT](https://choosealicense.com/licenses/mit/) -------------------------------------------------------------------------------- /src/utils/Network.js: -------------------------------------------------------------------------------- 1 | /* istanbul ignore file */ 2 | 3 | import Chains from "./Chains"; 4 | 5 | /** 6 | * Network utils object 7 | * @constructor Network 8 | * @param {(ETH|BSC|MATIC|DOT)=} network The network where the staking contract is. (Default: ETH) 9 | * @param {Boolean=} test ? Specifies if we're on test env (Default: false) 10 | */ 11 | class Network { 12 | 13 | constructor(network='ETH', test = false, getETHNetwork) { 14 | Chains.checkIfNetworkIsSupported(network); 15 | this.network = network; 16 | this.test = test; 17 | this.getETHNetwork = getETHNetwork; 18 | } 19 | 20 | /** 21 | * Callback when networks changes 22 | * 23 | * @callback onChainChangedCallback 24 | * @param {string} network - Network name 25 | */ 26 | 27 | 28 | /** 29 | * @function onChainChanged 30 | * @param {onChainChangedCallback} callback 31 | * @description Triggers the callback after the users changes their chain 32 | */ 33 | async onChainChanged({callback}) { 34 | window.ethereum.on('chainChanged', async () => { 35 | callback(await this.getETHNetwork()); 36 | }); 37 | } 38 | 39 | /** 40 | * @function changeToCurrentNetwork 41 | * @description Request the wallet to change to the current chain 42 | */ 43 | async changeToCurrentNetwork() { 44 | await switchToNetwork(this.network) 45 | } 46 | 47 | /** 48 | * @function switchToNetwork 49 | * @description Request switch to the Avalanche chain 50 | */ 51 | async switchToNetwork(chain) { 52 | if (window.ethereum && chain !== undefined) { 53 | var chain = Chains.getChainData(chain, !this.test) 54 | 55 | await window.ethereum.request({ 56 | method: 'wallet_addEthereumChain', 57 | params: [ 58 | { 59 | chainId: '0x' + chain.chainId.toString(16).toUpperCase(), 60 | chainName: chain.name, 61 | nativeCurrency: { 62 | name: chain.name, 63 | symbol: chain.currency, 64 | decimals: 18 65 | }, 66 | rpcUrls: [chain.rpc], 67 | blockExplorerUrls: [chain.explorer] 68 | }, 69 | ], 70 | }); 71 | } 72 | } 73 | } 74 | 75 | export default Network; -------------------------------------------------------------------------------- /src/models/base/Contract.js: -------------------------------------------------------------------------------- 1 | class Contract { 2 | constructor(web3, contract_json, address) { 3 | this.web3 = web3; 4 | this.json = contract_json; 5 | this.abi = contract_json.abi; 6 | this.address = address; 7 | this.contract = new web3.eth.Contract(contract_json.abi, address); 8 | } 9 | 10 | async deploy(account, abi, byteCode, args = [], callback=()=>{}) { 11 | try{ 12 | var res; 13 | this.contract = new this.web3.eth.Contract(abi); 14 | if(account){ 15 | let txSigned = await account.getAccount().signTransaction({ 16 | data : this.contract.deploy({ 17 | data : byteCode, 18 | arguments: args 19 | }).encodeABI(), 20 | from : account.getAddress(), 21 | gas : 6913388 22 | } 23 | ); 24 | res = await this.web3.eth.sendSignedTransaction(txSigned.rawTransaction); 25 | }else{ 26 | const accounts = await this.web3.eth.getAccounts(); 27 | res = await this.__metamaskDeploy({byteCode, args, acc : accounts[0], callback}); 28 | } 29 | this.address = res.contractAddress; 30 | return res; 31 | }catch(err){ 32 | console.log("err", err); 33 | throw err; 34 | } 35 | } 36 | 37 | __metamaskDeploy = async ({byteCode, args, acc, callback = () => {}}) => { 38 | return new Promise ((resolve, reject) => { 39 | try{ 40 | this.getContract() 41 | .deploy({ 42 | data: byteCode, 43 | arguments: args, 44 | }).send({from : acc}) 45 | .on('confirmation', (confirmationNumber, receipt) => { 46 | callback(confirmationNumber) 47 | if(confirmationNumber > 0){ 48 | resolve(receipt); 49 | } 50 | }) 51 | .on('error', err => {reject(err)}); 52 | }catch(err){ 53 | reject(err); 54 | } 55 | }) 56 | } 57 | 58 | async use(contract_json, address) { 59 | this.json = contract_json; 60 | this.abi = contract_json.abi; 61 | this.address = address ? address : this.address; 62 | this.contract = new this.web3.eth.Contract( 63 | contract_json.abi, 64 | this.address 65 | ); 66 | } 67 | 68 | async send(account, byteCode, value='0x0', callback=() => {}){ 69 | return new Promise( async (resolve, reject) => { 70 | let tx = { 71 | data : byteCode, 72 | from : account.address, 73 | to : this.address, 74 | gas : 4430000, 75 | gasPrice : 25000000000, 76 | value: value ? value : '0x0' 77 | } 78 | 79 | let result = await account.signTransaction(tx); 80 | this.web3.eth.sendSignedTransaction(result.rawTransaction) 81 | .on('confirmation', (confirmationNumber, receipt) => { 82 | callback(confirmationNumber); 83 | if(confirmationNumber > 0){ 84 | resolve(receipt); 85 | } 86 | }) 87 | .on('error', err => {reject(err)}); 88 | }) 89 | 90 | } 91 | 92 | getContract() { 93 | return this.contract; 94 | } 95 | 96 | getABI() { 97 | return this.abi; 98 | } 99 | 100 | getJSON() { 101 | return this.json; 102 | } 103 | 104 | getAddress() { 105 | return this.address; 106 | } 107 | } 108 | 109 | export default Contract; 110 | -------------------------------------------------------------------------------- /tests/test/multi-pool.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | 3 | import Web3 from "web3"; 4 | import chai from 'chai'; 5 | import { mochaAsync } from '../utils'; 6 | import Application from '../../src/models'; 7 | var userPrivateKey = '0x7f76de05082c4d578219ca35a905f8debe922f1f00b99315ebf0706afc97f132'; 8 | 9 | const expect = chai.expect; 10 | 11 | 12 | context('Multi pool tests', async () => { 13 | 14 | 15 | const getApp = ({chain}) => { 16 | return new Application({test: true, mainnet: true, network: chain, web3: undefined}) 17 | } 18 | 19 | const getPool = async ({contractAddress, chain}) => { 20 | let swapContract = await getApp({chain}).getFixedSwapContract({contractAddress}); 21 | swapContract.__init__(); 22 | await swapContract.assertERC20Info(); 23 | return swapContract; 24 | } 25 | 26 | const getUserAllocationsByPoolId = async ({ poolContract }) => { 27 | const purchases = await poolContract.getPurchaseIds(); 28 | return await Promise.all( 29 | purchases.map( 30 | async(p) => 31 | await poolContract.getPurchase({ 32 | purchase_id: p, 33 | }) 34 | ) 35 | ); 36 | } 37 | 38 | it('should read data from BSC', mochaAsync(async () => { 39 | const swapContract = await getPool({chain: 'BSC', contractAddress: '0xeE62650fA45aC0deb1B24Ec19f983A8f85B727aB'}); 40 | expect(await swapContract.minimumReached()).to.equal(true); 41 | expect(swapContract.version).to.equal("2.0"); 42 | expect(swapContract).to.not.equal(false); 43 | const allocation = (await getUserAllocationsByPoolId({poolContract: swapContract}))[3]; 44 | expect(allocation._id).to.equal(3); 45 | expect(allocation.amount).to.equal('1999.68912'); 46 | expect(allocation.purchaser).to.equal('0x458c107a047Bb98e5d7C4a574A742D5003baf957'); 47 | expect(allocation.costAmount).to.equal('0.873989999999999874'); 48 | })); 49 | 50 | it('should read data from old ETH pool', mochaAsync(async () => { 51 | const swapContract = await getPool({chain: 'ETH', contractAddress: '0x868Be4f80bB166a09489822C322deb3163B5025B'}); 52 | expect(await swapContract.minimumReached()).to.equal(true); 53 | expect(swapContract.version).to.equal("1.0"); 54 | expect(swapContract).to.not.equal(false); 55 | const allocation = (await getUserAllocationsByPoolId({poolContract: swapContract}))[6]; 56 | expect(allocation._id).to.equal('6'); 57 | expect(allocation.amount).to.equal('10683.89316'); 58 | expect(allocation.purchaser).to.equal('0xab5C49e981Fd7F3cA3bdfDE1fFEBF38723446629'); 59 | expect(allocation.costAmount).to.equal('0.224364'); 60 | })); 61 | 62 | it('should read data from polygon', mochaAsync(async () => { 63 | const swapContract = await getPool({chain: 'MATIC', contractAddress: '0x18E98c89d3D0E7eDe0f6E8af32c6117c3eDbb8C6'}); 64 | expect(await swapContract.minimumReached()).to.equal(true); 65 | expect(swapContract.version).to.equal("2.0"); 66 | expect(swapContract).to.not.equal(false); 67 | const allocation = (await getUserAllocationsByPoolId({poolContract: swapContract}))[3]; 68 | expect(allocation._id).to.equal('3'); 69 | expect(allocation.amount).to.equal('1041.65625'); 70 | expect(allocation.purchaser).to.equal('0xDab6AdACCaf60960E9B9C0c32C3C8C564F2db890'); 71 | expect(allocation.costAmount).to.equal('333.33'); 72 | })); 73 | }); 74 | -------------------------------------------------------------------------------- /src/models/base/ERC20TokenContract.js: -------------------------------------------------------------------------------- 1 | import contract from "./Contract"; 2 | import { ierc20 } from "../../interfaces"; 3 | import Numbers from "../../utils/Numbers"; 4 | import Client from "../../utils/Client"; 5 | let self; 6 | 7 | class ERC20TokenContract { 8 | constructor({contractAddress, web3, acc}) { 9 | if(acc){ 10 | this.acc = acc; 11 | } 12 | this.params = { 13 | web3 : web3, 14 | contractAddress : contractAddress, 15 | contract: new contract(web3, ierc20, contractAddress), 16 | decimals : null 17 | }; 18 | self = { 19 | contract: new contract(web3, ierc20, contractAddress) 20 | }; 21 | this.client = new Client(); 22 | } 23 | 24 | __assert() { 25 | this.params.contract.use(ierc20, this.getAddress()); 26 | } 27 | 28 | getContract() { 29 | return this.params.contract.getContract(); 30 | } 31 | 32 | getAddress() { 33 | return this.params.contractAddress; 34 | } 35 | 36 | setNewOwner = async ({ address }) => { 37 | try { 38 | return await this.client.sendTx( 39 | this.params.web3, 40 | this.acc, 41 | this.params.contract, 42 | this.params.contract 43 | .getContract() 44 | .methods.transferOwnership(address) 45 | ); 46 | } catch (err) { 47 | console.log(err); 48 | } 49 | }; 50 | 51 | async transferTokenAmount({ toAddress, tokenAmount}) { 52 | let amountWithDecimals = Numbers.toSmartContractDecimals( 53 | tokenAmount, 54 | await this.getDecimals() 55 | ); 56 | return await this.client.sendTx( 57 | 58 | this.params.web3, 59 | this.acc, 60 | this.params.contract, 61 | this.params.contract 62 | .getContract() 63 | .methods.transfer(toAddress, amountWithDecimals) 64 | ); 65 | } 66 | 67 | async getTokenAmount(address) { 68 | return Numbers.fromDecimals( 69 | await this.getContract().methods.balanceOf(address).call(), 70 | await this.getDecimals() 71 | ); 72 | } 73 | 74 | async totalSupply() { 75 | return await this.getContract().methods.totalSupply().call(); 76 | } 77 | 78 | getABI() { 79 | return self.contract; 80 | } 81 | 82 | async getDecimals(){ 83 | if (!this.params.decimals) { 84 | this.params.decimals = parseInt(await this.getContract().methods.decimals().call()); 85 | } 86 | return this.params.decimals; 87 | } 88 | 89 | async isApproved({ address, amount, spenderAddress, callback }) { 90 | try { 91 | let res = await this.client.sendTx( 92 | this.params.web3, 93 | this.acc, 94 | this.params.contract, 95 | this.params.contract 96 | .getContract() 97 | .methods.allowance(address, spenderAddress), 98 | true, 99 | null, 100 | callback 101 | ); 102 | 103 | let approvedAmount = Numbers.fromDecimals(res, await this.getDecimals()); 104 | if (typeof amount === 'string' || amount instanceof String) { 105 | amount = parseFloat(amount); 106 | } 107 | return (approvedAmount >= amount); 108 | } catch (err) { 109 | throw err; 110 | } 111 | } 112 | 113 | async approve({ address, amount, callback }) { 114 | try { 115 | let amountWithDecimals = Numbers.toSmartContractDecimals( 116 | amount, 117 | await this.getDecimals() 118 | ); 119 | return await this.client.sendTx( 120 | this.params.web3, 121 | this.acc, 122 | this.params.contract, 123 | this.params.contract 124 | .getContract() 125 | .methods.approve(address, amountWithDecimals), 126 | null, 127 | null, 128 | callback 129 | ); 130 | } catch (err) { 131 | throw err; 132 | } 133 | } 134 | } 135 | 136 | export default ERC20TokenContract; 137 | -------------------------------------------------------------------------------- /docs/NETWORK.md: -------------------------------------------------------------------------------- 1 | ## Classes 2 | 3 |
4 |
Network
5 |
6 |
7 | 8 | ## Functions 9 | 10 |
11 |
onChainChanged(callback)
12 |

Triggers the callback after the users changes their chain

13 |
14 |
changeToCurrentNetwork()
15 |

Request the wallet to change to the current chain

16 |
17 |
switchToEthereum()
18 |

Request switch to the ETH chain

19 |
20 |
switchToAvalanche()
21 |

Request switch to the Avalanche chain

22 |
23 |
switchToMoonbeam()
24 |

Request switch to the Moonbeam chain

25 |
26 |
switchToCelo()
27 |

Request switch to the Celo chain

28 |
29 |
switchToPolygon()
30 |

Request switch to the Polygon chain

31 |
32 |
switchToBsc()
33 |

Request switch to the Binance smart chain

34 |
35 |
36 | 37 | ## Typedefs 38 | 39 |
40 |
onChainChangedCallback : function
41 |

Callback when networks changes

42 |
43 |
44 | 45 | 46 | 47 | ## Network 48 | **Kind**: global class 49 | 50 | 51 | ### new Network([network], [test]) 52 | Network utils object 53 | 54 | 55 | | Param | Type | Description | 56 | | --- | --- | --- | 57 | | [network] | ETH \| BSC \| MATIC \| DOT | The network where the staking contract is. (Default: ETH) | 58 | | [test] | Boolean | ? Specifies if we're on test env (Default: false) | 59 | 60 | 61 | 62 | ## onChainChanged(callback) 63 | Triggers the callback after the users changes their chain 64 | 65 | **Kind**: global function 66 | 67 | | Param | Type | 68 | | --- | --- | 69 | | callback | [onChainChangedCallback](#onChainChangedCallback) | 70 | 71 | 72 | 73 | ## changeToCurrentNetwork() 74 | Request the wallet to change to the current chain 75 | 76 | **Kind**: global function 77 | 78 | 79 | ## switchToEthereum() 80 | Request switch to the ETH chain 81 | 82 | **Kind**: global function 83 | 84 | 85 | ## switchToAvalanche() 86 | Request switch to the Avalanche chain 87 | 88 | **Kind**: global function 89 | 90 | 91 | ## switchToMoonbeam() 92 | Request switch to the Moonbeam chain 93 | 94 | **Kind**: global function 95 | 96 | 97 | ## switchToCelo() 98 | Request switch to the Celo chain 99 | 100 | **Kind**: global function 101 | 102 | 103 | ## switchToPolygon() 104 | Request switch to the Polygon chain 105 | 106 | **Kind**: global function 107 | 108 | 109 | ## switchToBsc() 110 | Request switch to the Binance smart chain 111 | 112 | **Kind**: global function 113 | 114 | 115 | ## onChainChangedCallback : function 116 | Callback when networks changes 117 | 118 | **Kind**: global typedef 119 | 120 | | Param | Type | Description | 121 | | --- | --- | --- | 122 | | network | string | Network name | 123 | 124 | -------------------------------------------------------------------------------- /src/utils/Numbers.js: -------------------------------------------------------------------------------- 1 | import moment from 'moment' 2 | import accounting from 'accounting' 3 | import dayjs from 'dayjs' 4 | import web3 from "web3"; 5 | import { create, all } from 'mathjs'; 6 | 7 | Number.prototype.noExponents = function () { 8 | var data = String(this).split(/[eE]/) 9 | if (data.length == 1) return data[0] 10 | 11 | var z = '', 12 | sign = this < 0 ? '-' : '', 13 | str = data[0].replace('.', ''), 14 | mag = Number(data[1]) + 1 15 | 16 | if (mag < 0) { 17 | z = sign + '0.' 18 | while (mag++) z += '0' 19 | return z + str.replace(/^\-/, '') 20 | } 21 | mag -= str.length 22 | while (mag--) z += '0' 23 | return str + z 24 | } 25 | 26 | /** 27 | * Numbers object 28 | * @constructor Numbers 29 | */ 30 | class numbers { 31 | math = create(all, { 32 | number: 'BigNumber', 33 | precision: 64, 34 | }); 35 | 36 | constructor() {} 37 | 38 | fromDayMonthYear(date) { 39 | let mom = moment().dayOfYear(date.day) 40 | mom.set('hour', date.hour) 41 | mom.set('year', date.year) 42 | return mom.format('ddd, hA') 43 | } 44 | 45 | fromSmartContractTimeToMinutes(time) { 46 | return dayjs.unix(time).toDate(); 47 | } 48 | 49 | fromMinutesToSmartContracTime(time) { 50 | return time 51 | } 52 | 53 | fromHex(hex){ 54 | return hex.toString(); 55 | } 56 | 57 | toFloat(number) { 58 | return parseFloat(parseFloat(number).toFixed(2)) 59 | } 60 | 61 | timeToSmartContractTime(time) { 62 | return parseInt(new Date(time).getTime() / 1000) 63 | } 64 | 65 | toDate(date) { 66 | let mom = moment().dayOfYear(date.day) 67 | mom.set('hour', date.hour) 68 | mom.set('year', date.year) 69 | return mom.unix() 70 | } 71 | 72 | toMoney(number) { 73 | var _0xbea8=["\x45\x55\x52","\x25\x76","\x66\x6F\x72\x6D\x61\x74\x4D\x6F\x6E\x65\x79"];return accounting[_0xbea8[2]](number,{symbol:_0xbea8[0],format:_0xbea8[1]}) 74 | } 75 | 76 | formatNumber(number) { 77 | return accounting.formatNumber(number) 78 | } 79 | 80 | /** 81 | * @function toSmartContractDecimals 82 | * @description Converts a "human" number to the minimum unit. 83 | * @param {Float} value The number that you want to convert 84 | * @param {Integer} decimals Number of decimals 85 | * @returns {string} 86 | */ 87 | toSmartContractDecimals(value, decimals) { 88 | return this.math.chain(this.math.bignumber(value)).multiply(this.math.bignumber(10 ** decimals)).done().toFixed(0); 89 | } 90 | 91 | /** 92 | * @function safeDivide 93 | * @description Performs a safe division 94 | * @param {Float} value The number that you want to divide 95 | * @param {Float} valueToDivideTo The number that you want to divide to 96 | * @returns {Float} 97 | */ 98 | safeDivide(value, valueToDivideTo) { 99 | return parseFloat(this.math.chain(this.math.bignumber(value)).divide(this.math.bignumber(valueToDivideTo)).done()); 100 | } 101 | 102 | /** 103 | * @function fromDecimals 104 | * @description Converts a number from his minimum unit to a human readable one. 105 | * @param {Float} value The number that you want to convert 106 | * @param {Integer} decimals Number of decimals 107 | * @returns {string} 108 | */ 109 | fromDecimals(value, decimals) { 110 | if (value == null) { 111 | return 0; 112 | } 113 | return this.math.chain( 114 | this.math.bignumber(value.toString())).divide(this.math.bignumber(10 ** decimals) 115 | ).toString(); 116 | } 117 | 118 | fromExponential(x) { 119 | var _0xe3ed=["\x61\x62\x73","\x65\x2D","\x73\x70\x6C\x69\x74","\x70\x6F\x77","\x30\x2E","\x30","\x6A\x6F\x69\x6E","\x73\x75\x62\x73\x74\x72\x69\x6E\x67","\x2B"];if(Math[_0xe3ed[0]](x)< 1.0){var e=parseInt(x.toString()[_0xe3ed[2]](_0xe3ed[1])[1]);if(e){x*= Math[_0xe3ed[3]](10,e- 1);x= _0xe3ed[4]+ new Array(e)[_0xe3ed[6]](_0xe3ed[5])+ x.toString()[_0xe3ed[7]](2)}}else {var e=parseInt(x.toString()[_0xe3ed[2]](_0xe3ed[8])[1]);if(e> 20){e-= 20;x/= Math[_0xe3ed[3]](10,e);x+= new Array(e+ 1)[_0xe3ed[6]](_0xe3ed[5])}};return x 120 | } 121 | 122 | } 123 | 124 | let Numbers = new numbers() 125 | 126 | export default Numbers 127 | 128 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "polkastarter-js", 3 | "version": "4.1.0", 4 | "description": "", 5 | "main": "index.js", 6 | "engines": { 7 | "node": ">=10.13.0 <=^14" 8 | }, 9 | "directories": { 10 | "test": "test" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/polkastarter/polkastarter-js.git" 15 | }, 16 | "dependencies": { 17 | "@walletconnect/web3-provider": "^1.5.2", 18 | "abi-decoder": "^1.2.0", 19 | "accounting": "^0.4.1", 20 | "axios": "^0.21.1", 21 | "babel-cli": "^6.26.0", 22 | "babel-plugin-transform-runtime": "^6.23.0", 23 | "babel-preset-env": "^1.6.1", 24 | "babel-preset-es2017": "^6.24.1", 25 | "babel-preset-stage-2": "^6.24.1", 26 | "big-number": "^2.0.0", 27 | "bignumber.js": "^9.0.1", 28 | "bn.js": "^5.1.3", 29 | "chai": "^4.2.0", 30 | "crypto-js": "^3.1.9-1", 31 | "dayjs": "^1.9.4", 32 | "decimal.js": "^10.2.1", 33 | "delay": "^4.4.0", 34 | "dotenv": "^8.2.0", 35 | "ethers": "^5.3.1", 36 | "execution-time": "^1.4.1", 37 | "express": "^4.17.1", 38 | "left-pad": "^1.3.0", 39 | "lodash": "^4.17.10", 40 | "mathjs": "^9.4.2", 41 | "mocha": "^5.1.1", 42 | "moment": "^2.21.0", 43 | "mongoose": "^5.9.5", 44 | "randomhex": "^0.1.5", 45 | "web3": "1.0.0-beta.55", 46 | "web3modal": "^1.9.4" 47 | }, 48 | "scripts": { 49 | "test": "nyc ./node_modules/.bin/mocha ./tests/index.js --timeout 3000000 --require babel-core/register --require babel-polyfill", 50 | "test-pools": "nyc ./node_modules/.bin/mocha ./tests/test/multi-pool.js --timeout 3000000 --require babel-core/register --require babel-polyfill", 51 | "index": "babel-node ./index.js --presets es2015,stage-2", 52 | "docs:numbers": "jsdoc2md --configure tooling/docs/jsdoc.json --files src/utils/Numbers.js > docs/NUMBERS.md", 53 | "docs:app": "jsdoc2md --configure tooling/docs/jsdoc.json --files src/models/index.js > docs/APPLICATION.md", 54 | "docs:signer": "jsdoc2md --configure tooling/docs/jsdoc.json --files src/utils/Signer.js > docs/SIGNER.md", 55 | "docs:swap": "jsdoc2md --configure tooling/docs/jsdoc.json --files src/models/contracts/FixedSwapContract.js > docs/SWAP.md", 56 | "docs:nftswap": "jsdoc2md --configure tooling/docs/jsdoc.json --files src/models/contracts/FixedNFTSwapContract.js > docs/NFT_SWAP.md", 57 | "docs:baseswap": "jsdoc2md --configure tooling/docs/jsdoc.json --files src/models/contracts/base/BaseSwapContract.js > docs/BASE_SWAP.md", 58 | "docs:network": "jsdoc2md --configure tooling/docs/jsdoc.json --files src/utils/Network.js > docs/NETWORK.md", 59 | "docs:staking": "jsdoc2md --configure tooling/docs/jsdoc.json --files src/models/base/Staking.js > docs/STAKING.md", 60 | "docs:wallet": "jsdoc2md --configure tooling/docs/jsdoc.json --files src/utils/Wallet.js > docs/WALLET.md", 61 | "docs:idostaking": "jsdoc2md --configure tooling/docs/jsdoc.json --files src/models/contracts/IDOStaking.js > docs/IDOSTAKING.md", 62 | "docs": "npm run docs:numbers && npm run docs:signer && npm run docs:swap && npm run docs:network && npm run docs:app && npm run docs:staking && npm run docs:idostaking && npm run docs:wallet && npm run docs:nftswap && npm run docs:baseswap", 63 | "docs:prepare": "gitbook install", 64 | "docs:build": "npm run docs:prepare && npm run docs && gitbook build", 65 | "docs:serve": "npm run docs:prepare && npm run docs && gitbook serve", 66 | "docs:clean": "rimraf _book" 67 | }, 68 | "devDependencies": { 69 | "babel-plugin-transform-runtime": "^6.23.0", 70 | "babel-preset-env": "^1.6.1", 71 | "babel-preset-es2015": "^6.24.1", 72 | "babel-register": "^6.26.0", 73 | "ganache-core": "^2.13.2", 74 | "gitbook-cli": "^2.3.2", 75 | "jsdoc-babel": "^0.5.0", 76 | "jsdoc-md": "^11.0.2", 77 | "jsdoc-to-markdown": "^7.0.0", 78 | "nyc": "^15.1.0", 79 | "should": "^7.1.0", 80 | "supertest": "^1.0.0" 81 | }, 82 | "babel": { 83 | "presets": [ 84 | "env", 85 | "es2015", 86 | "stage-2" 87 | ], 88 | "plugins": [ 89 | "transform-runtime" 90 | ], 91 | "sourceMaps": true, 92 | "retainLines": true 93 | }, 94 | "author": "", 95 | "license": "ISC" 96 | } 97 | -------------------------------------------------------------------------------- /src/utils/Chains.js: -------------------------------------------------------------------------------- 1 | const networks = [ 2 | { name: 'Ethereum Main', chain: 'ETH', currency: 'ETH', chainId: 1, testnet: false, rpc: "https://mainnet.infura.io/v3/40e2d4f67005468a83e2bcace6427bc8", explorer: "https://etherscan.io/" }, 3 | { name: 'Ethereum Sepolia', chain: 'ETH', currency: 'ETH', chainId: 11155111, testnet: true, rpc: "https://ethereum-sepolia.publicnode.com", explorer: "https://sepolia.etherscan.io/" }, 4 | { name: 'BSC Main', chain: 'BSC', currency: 'BSC', chainId: 56, testnet: false, rpc: "https://bsc-dataseed1.binance.org:443", explorer: "https://bscscan.com/" }, 5 | { name: 'BSC Test', chain: 'BSC', currency: 'BSC', chainId: 97, testnet: true, rpc: "https://data-seed-prebsc-1-s1.binance.org:8545", explorer: "https://testnet.bscscan.com/" }, 6 | { name: 'Polygon', chain: 'MATIC', currency: 'MATIC', chainId: 137, testnet: false, rpc: "https://polygon-rpc.com", explorer: "https://polygonscan.com/" }, 7 | { name: 'Mumbai', chain: 'MATIC', currency: 'MATIC', chainId: 80001, testnet: true, rpc: "https://rpc-mumbai.maticvigil.com", explorer: "https://mumbai.polygonscan.com/" }, 8 | { name: 'Celo', chain: 'CELO', currency: 'CELO', chainId: 42220, testnet: false, rpc: "https://forno.celo.org", explorer: "https://explorer.celo.org" }, 9 | { name: 'Celo Testnet', chain: 'CELO', currency: 'CELO', chainId: 44787, testnet: true, rpc: "hhttps://alfajores-forno.celo-testnet.org", explorer: "https://alfajores-blockscout.celo-testnet.org" }, 10 | { name: 'Avalanche', chain: 'AVAX', currency: 'AVAX', chainId: 43114, testnet: false, rpc: "https://api.avax.network/ext/bc/C/rpc", explorer: "https://snowtrace.io/" }, 11 | { name: 'Avalanche Testnet', chain: 'AVAX', currency: 'AVAX', chainId: 43113, testnet: true, rpc: "https://api.avax-test.network/ext/bc/C/rpc", explorer: "https://testnet.snowtrace.io/" }, 12 | { name: 'Arbitrum', chain: 'AETH', currency: 'ETH', chainId: 42161, testnet: false, rpc: "https://arb1.arbitrum.io/rpc", explorer: "https://arbiscan.io/" }, 13 | { name: 'Arbitrum Sepolia', chain: 'AETH', currency: 'ETH', chainId: 421614, testnet: true, rpc: "https://sepolia-rollup.arbitrum.io/rpc", explorer: "https://sepolia.arbiscan.io/" }, 14 | { name: 'Mode', chain: 'METH', currency: 'ETH', chainId: 34443, testnet: false, rpc: "https://mainnet.mode.network", explorer: "https://explorer.mode.network/" }, 15 | { name: 'Mode Sepolia', chain: 'METH', currency: 'ETH', chainId: 919, testnet: true, rpc: "https://sepolia.mode.network", explorer: "https://sepolia.explorer.mode.network/" }, 16 | { name: 'Base', chain: 'BETH', currency: 'ETH', chainId: 8453, testnet: false, rpc: "https://mainnet.base.org", explorer: "https://basescan.org/" }, 17 | { name: 'Base Sepolia', chain: 'BETH', currency: 'ETH', chainId: 84532, testnet: true, rpc: "https://sepolia.base.org", explorer: "https://sepolia.basescan.org/" }, 18 | { name: 'Sei', chain: 'SEI', currency: 'SEI', chainId: 1329, testnet: false, rpc: "https://evm-rpc.sei-apis.com", explorer: "https://seitrace.com/" }, 19 | { name: 'Sei Testnet', chain: 'SEI', currency: 'SEI', chainId: 1328, testnet: true, rpc: "https://evm-rpc-testnet.sei-apis.com", explorer: "https://seitrace.com/" }, 20 | ] 21 | 22 | /** 23 | * Chains object 24 | * @constructor Chains 25 | */ 26 | class chains { 27 | constructor() {} 28 | 29 | checkIfNetworkIsSupported(network) { 30 | if(!this.isNetworkSupported(network)) { 31 | throw new Error("Network unsupported"); 32 | } 33 | } 34 | 35 | isNetworkSupported(chain) { 36 | return networks.map((network) => network.chain).includes(chain) 37 | } 38 | 39 | getChainData(chain, mainnet = true) { 40 | return networks.find((network) => network.chain == chain && network.testnet !== mainnet) 41 | } 42 | 43 | getRpcUrl(chain, mainnet = true) { 44 | return networks.find((network) => network.chain == chain && network.testnet !== mainnet).rpc 45 | } 46 | 47 | getExplorercUrl(chain, mainnet = true) { 48 | return networks.find((network) => network.chain == chain && network.testnet !== mainnet).explorer 49 | } 50 | 51 | getNetworksEnum() { 52 | return networks.reduce((hash, network) => { hash[network.chainId] = network.name; return hash; }, {}); 53 | } 54 | } 55 | 56 | let Chains = new chains() 57 | 58 | export default Chains 59 | 60 | -------------------------------------------------------------------------------- /tests/utils/signer.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | 3 | import chai from 'chai'; 4 | import Application from '../../src/models'; 5 | import { mochaAsync } from '../utils'; 6 | 7 | const expect = chai.expect; 8 | 9 | context('Signer', async () => { 10 | 11 | const app = new Application({test : true, mainnet : false, network : 'ETH'}); 12 | const signer = app.getSigner(); 13 | 14 | const jsonAccount = {"address":"46d0e9eafe3a1eb66fc54cf40d949fd711e54355","crypto":{"cipher":"aes-128-ctr","ciphertext":"e3565812298ec27625e5a64e40beaf31233ac3fd392e08dc67928c1899f85722","cipherparams":{"iv":"7f35572de5630547d405177850f912c8"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"8c75a1deec757088496a0f24408a13683acc55f935a37b6a202380072eabe2e2"},"mac":"73026bbba3027046412d8c70132b1b5104a04ea7402b19526c6b2284395e0207"},"id":"fa401ff0-7e2e-4fbd-af65-115985a0f4f7","version":3}; 15 | it('should generate valid accounts', mochaAsync(async () => { 16 | const account = await signer.generateSignerAccount({password: 'test1234'}); 17 | expect(signer.getAddressFromAccount({accountJson: account, password: 'test1234'}).toLowerCase()).to.equal(('0x' + JSON.parse(account).address).toLowerCase()); 18 | })); 19 | 20 | it('should sign addresses', mochaAsync(async () => { 21 | const signs = await signer.signAddresses({ 22 | addresses: [ 23 | '0xB9a83B0EB888bD041fE5704F75aAd88886A2bb0d', 24 | '0xA5E5fC6e75A19447544105995C0B6e8e405b63C2', 25 | '0x00' 26 | ], 27 | accountMaxAllocations: [ 28 | 10, 29 | 20, 30 | 10 31 | ], 32 | decimals: 18, 33 | contractAddress: '0xb8f7166496996a7da21cf1f1b04d9b3e26a3d077', 34 | accountJson: JSON.stringify(jsonAccount), 35 | password: 'test1234' 36 | }); 37 | 38 | expect(signs).to.have.deep.members([ 39 | { 40 | address: '0xB9a83B0EB888bD041fE5704F75aAd88886A2bb0d', 41 | signature: '0x74f1e4aad1ed1aff418413052a519b856292bdf752558ad5eb56e05dcc52126b28fdabd9db2a72e09a205246f95984500fc7e8ad6e7043809f2d4bd53960e7831c', 42 | allocation: '10000000000000000000' 43 | }, 44 | { 45 | address: '0xA5E5fC6e75A19447544105995C0B6e8e405b63C2', 46 | signature: '0x6f5f993225fdc5fc093f856ba658bc51bcd87bc4b1b3e91e526fd658982bfb5d4e1994d84eb39302af99d398d3a7cb890d06aedcdc429b48666795a433242e961c', 47 | allocation: '20000000000000000000' 48 | } 49 | ]); 50 | })); 51 | 52 | it('should verify signature', mochaAsync(async () => { 53 | const verified = await signer.verifySignature({ 54 | signature: '0x74f1e4aad1ed1aff418413052a519b856292bdf752558ad5eb56e05dcc52126b28fdabd9db2a72e09a205246f95984500fc7e8ad6e7043809f2d4bd53960e7831c', 55 | address: '0xB9a83B0EB888bD041fE5704F75aAd88886A2bb0d', 56 | signerAddress: '0x46d0e9eafe3a1eb66fc54cf40d949fd711e54355', 57 | accountMaxAllocation: '10000000000000000000', 58 | contractAddress: '0xb8f7166496996a7da21cf1f1b04d9b3e26a3d077', 59 | }); 60 | expect(verified).to.equal(true); 61 | 62 | 63 | const signedWithAnotherAccount = await signer.verifySignature({ 64 | signature: '0x76d620690fbc5324ed1602e617cbb185c33e93b0e895587a9402a41faefc9dca2d2e40809da5a04707727665219574021517d827a83903e8c1dc220f7da876831c', 65 | address: '0xB9a83B0EB888bD041fE5704F75aAd88886A2bb0d', 66 | signerAddress: '0x46d0e9eafe3a1eb66fc54cf40d949fd711e54355', 67 | accountMaxAllocation: '10000000000000000000', 68 | contractAddress: '0xb8f7166496996a7da21cf1f1b04d9b3e26a3d077', 69 | }); 70 | expect(signedWithAnotherAccount).to.equal(false); 71 | 72 | const signedWithAnotherAllocation = await signer.verifySignature({ 73 | signature: '0x74f1e4aad1ed1aff418413052a519b856292bdf752558ad5eb56e05dcc52126b28fdabd9db2a72e09a205246f95984500fc7e8ad6e7043809f2d4bd53960e7831c', 74 | address: '0xB9a83B0EB888bD041fE5704F75aAd88886A2bb0d', 75 | signerAddress: '0x46d0e9eafe3a1eb66fc54cf40d949fd711e54355', 76 | accountMaxAllocation: '50000000000000000000', 77 | contractAddress: '0xb8f7166496996a7da21cf1f1b04d9b3e26a3d077', 78 | }); 79 | expect(signedWithAnotherAllocation).to.equal(false); 80 | 81 | const invalidSig = await signer.verifySignature({ 82 | signature: '0x76d620690fbc5324ed1602e617cbb185c33e93b0e895587a9402a41faefc9dca2d2e40809da5a04707727665219574021517d827a83903e8c1dc220f7ba876831c', 83 | address: '0xB9a83B0EB888bD041fE5704F75aAd88886A2bb0d', 84 | signerAddress: '0x46d0e9eafe3a1eb66fc54cf40d949fd711e54355', 85 | accountMaxAllocation: '10000000000000000000', 86 | contractAddress: '0xb8f7166496996a7da21cf1f1b04d9b3e26a3d077', 87 | }); 88 | expect(invalidSig).to.equal(false); 89 | })); 90 | 91 | }); 92 | -------------------------------------------------------------------------------- /docs/APPLICATION.md: -------------------------------------------------------------------------------- 1 | ## Classes 2 | 3 |
4 |
Application
5 |
6 |
7 | 8 | ## Functions 9 | 10 |
11 |
startWithoutMetamask()
12 |

Starts an instance of web3 for read-only methods

13 |
14 |
start()
15 |

Starts an instance of web3

16 |
17 |
login()
18 |

Logins with metamask

19 |
20 |
getSigner()
21 |

Returns the Signer instance.

22 |
23 |
getNetworkUtils()
24 |

Returns the Network Utils instance.

25 |
26 |
getWalletUtils()
27 |

Returns the Wallet Utils instance.

28 |
29 |
getStaking([contractAddress], [tokenAddress])
30 |

Returns the Staking Model instance.

31 |
32 |
getFixedSwapContract(tokenAddress, [contractAddress])
33 |

Returns Fixed Swap instance

34 |
35 |
getFixedNFTSwapContract([contractAddress])
36 |

Returns Fixed NFT Swap instance

37 |
38 |
getERC20TokenContract(tokenAddress)
39 |

Returns ERC20 instance

40 |
41 |
getETHNetwork()
42 |

Returns the current network

43 |
44 |
getAddress()
45 |

Returns the connected user address

46 |
47 |
getETHBalance()
48 |

Returns the native currency of the connected user wallet.

49 |
50 |
51 | 52 | 53 | 54 | ## Application 55 | **Kind**: global class 56 | 57 | 58 | ### new Application([network], [mainnet], [test], [web3]) 59 | Polkastarter Application Object 60 | 61 | 62 | | Param | Type | Description | 63 | | --- | --- | --- | 64 | | [network] | ETH \| BSC \| MATIC \| DOT | Current network (Default = ETH) | 65 | | [mainnet] | Boolean | Specifies if we're on mainnet or tesnet (Default = true); | 66 | | [test] | Boolean | ? Specifies if we're on test env | 67 | | [web3] | Web3 | Custom Web3 instance. If not provided the Application will instance it for you. (Default: undefined) | 68 | 69 | 70 | 71 | ## startWithoutMetamask() 72 | Starts an instance of web3 for read-only methods 73 | 74 | **Kind**: global function 75 | 76 | 77 | ## start() 78 | Starts an instance of web3 79 | 80 | **Kind**: global function 81 | 82 | 83 | ## login() 84 | Logins with metamask 85 | 86 | **Kind**: global function 87 | 88 | 89 | ## getSigner() 90 | Returns the Signer instance. 91 | 92 | **Kind**: global function 93 | 94 | 95 | ## getNetworkUtils() 96 | Returns the Network Utils instance. 97 | 98 | **Kind**: global function 99 | 100 | 101 | ## getWalletUtils() 102 | Returns the Wallet Utils instance. 103 | 104 | **Kind**: global function 105 | 106 | 107 | ## getStaking([contractAddress], [tokenAddress]) 108 | Returns the Staking Model instance. 109 | 110 | **Kind**: global function 111 | 112 | | Param | Type | Description | 113 | | --- | --- | --- | 114 | | [contractAddress] | string | The staking contract address. (Default: Predefined addresses depending on the network) | 115 | | [tokenAddress] | string | The staking token address. (Default: Predefined addresses depending on the network) | 116 | 117 | 118 | 119 | ## getFixedSwapContract(tokenAddress, [contractAddress]) 120 | Returns Fixed Swap instance 121 | 122 | **Kind**: global function 123 | 124 | | Param | Type | Description | 125 | | --- | --- | --- | 126 | | tokenAddress | string | The token address we want to trade | 127 | | [contractAddress] | string | The swap contract address, in case t hat has already been instanced. (Default = null) | 128 | 129 | 130 | 131 | ## getFixedNFTSwapContract([contractAddress]) 132 | Returns Fixed NFT Swap instance 133 | 134 | **Kind**: global function 135 | 136 | | Param | Type | Description | 137 | | --- | --- | --- | 138 | | [contractAddress] | string | The swap contract address, in case t hat has already been instanced. (Default = null) | 139 | 140 | 141 | 142 | ## getERC20TokenContract(tokenAddress) 143 | Returns ERC20 instance 144 | 145 | **Kind**: global function 146 | 147 | | Param | Type | Description | 148 | | --- | --- | --- | 149 | | tokenAddress | string | The token address | 150 | 151 | 152 | 153 | ## getETHNetwork() 154 | Returns the current network 155 | 156 | **Kind**: global function 157 | 158 | 159 | ## getAddress() 160 | Returns the connected user address 161 | 162 | **Kind**: global function 163 | 164 | 165 | ## getETHBalance() 166 | Returns the native currency of the connected user wallet. 167 | 168 | **Kind**: global function 169 | -------------------------------------------------------------------------------- /docs/STAKING.md: -------------------------------------------------------------------------------- 1 | ## Classes 2 | 3 |
4 |
Staking
5 |
6 |
7 | 8 | ## Functions 9 | 10 |
11 |
stake(amount)
12 |

Stakes tokens inside the stake contract

13 |
14 |
approveStakeERC20(tokenAmount)
15 |

Approve the stake to use approved tokens

16 |
17 |
isApproved(tokenAmount, address)Boolean
18 |

Verify if the address has approved the staking to deposit

19 |
20 |
withdraw(amount)
21 |

Withdraw tokens from the stake contract

22 |
23 |
withdrawAll()
24 |

Withdraw all the tokens from the stake contract

25 |
26 |
claim()
27 |

Claim rewards from the staking contract

28 |
29 |
userAccumulatedRewards(address)Integer
30 |

Returns the accumulated rewards

31 |
32 |
stakeTime(address)Integer
33 |

Returns the stake time for a wallet

34 |
35 |
lockTimePeriod()Integer
36 |

Returns the lock time perdio

37 |
38 |
getUnlockTime(address)Integer
39 |

Returns the stake time for a wallet

40 |
41 |
stakeAmount(address)Integer
42 |

Returns the stake amount for a wallet

43 |
44 |
45 | 46 | 47 | 48 | ## Staking 49 | **Kind**: global class 50 | 51 | 52 | ### new Staking(web3, [contractAddress], acc, [tokenAddress], [network], [test]) 53 | Staking Object 54 | 55 | 56 | | Param | Type | Description | 57 | | --- | --- | --- | 58 | | web3 | Web3 | | 59 | | [contractAddress] | string | The staking contract address. (Default: Predefined addresses depending on the network) | 60 | | acc | Account | | 61 | | [tokenAddress] | string | The staking token address. (Default: Predefined addresses depending on the network) | 62 | | [network] | ETH \| BSC \| MATIC \| DOT | The network where the staking contract is. (Default: ETH) | 63 | | [test] | Boolean | ? Specifies if we're on test env (Default: false) | 64 | 65 | 66 | 67 | ## stake(amount) 68 | Stakes tokens inside the stake contract 69 | 70 | **Kind**: global function 71 | 72 | | Param | Type | Description | 73 | | --- | --- | --- | 74 | | amount | Integer | Amount | 75 | 76 | 77 | 78 | ## approveStakeERC20(tokenAmount) 79 | Approve the stake to use approved tokens 80 | 81 | **Kind**: global function 82 | 83 | | Param | Type | 84 | | --- | --- | 85 | | tokenAmount | Integer | 86 | 87 | 88 | 89 | ## isApproved(tokenAmount, address) ⇒ Boolean 90 | Verify if the address has approved the staking to deposit 91 | 92 | **Kind**: global function 93 | 94 | | Param | Type | 95 | | --- | --- | 96 | | tokenAmount | Integer | 97 | | address | Address | 98 | 99 | 100 | 101 | ## withdraw(amount) 102 | Withdraw tokens from the stake contract 103 | 104 | **Kind**: global function 105 | 106 | | Param | Type | 107 | | --- | --- | 108 | | amount | Integer | 109 | 110 | 111 | 112 | ## withdrawAll() 113 | Withdraw all the tokens from the stake contract 114 | 115 | **Kind**: global function 116 | 117 | 118 | ## claim() 119 | Claim rewards from the staking contract 120 | 121 | **Kind**: global function 122 | 123 | 124 | ## userAccumulatedRewards(address) ⇒ Integer 125 | Returns the accumulated rewards 126 | 127 | **Kind**: global function 128 | **Returns**: Integer - userAccumulatedRewards 129 | 130 | | Param | Type | 131 | | --- | --- | 132 | | address | string | 133 | 134 | 135 | 136 | ## stakeTime(address) ⇒ Integer 137 | Returns the stake time for a wallet 138 | 139 | **Kind**: global function 140 | **Returns**: Integer - stakeTime 141 | 142 | | Param | Type | 143 | | --- | --- | 144 | | address | string | 145 | 146 | 147 | 148 | ## lockTimePeriod() ⇒ Integer 149 | Returns the lock time perdio 150 | 151 | **Kind**: global function 152 | **Returns**: Integer - lockTimePeriod 153 | 154 | 155 | ## getUnlockTime(address) ⇒ Integer 156 | Returns the stake time for a wallet 157 | 158 | **Kind**: global function 159 | **Returns**: Integer - unlockTime 160 | 161 | | Param | Type | 162 | | --- | --- | 163 | | address | string | 164 | 165 | 166 | 167 | ## stakeAmount(address) ⇒ Integer 168 | Returns the stake amount for a wallet 169 | 170 | **Kind**: global function 171 | **Returns**: Integer - stakeAmount 172 | 173 | | Param | Type | 174 | | --- | --- | 175 | | address | string | 176 | 177 | -------------------------------------------------------------------------------- /tests/test/vesting/vestingSchedule100.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | 3 | import chai from 'chai'; 4 | import { mochaAsync, decimalAdjust } from '../../utils'; 5 | import moment from 'moment'; 6 | import Application from '../../../src/models'; 7 | import delay from 'delay'; 8 | const ERC20TokenAddress = '0x7a7748bd6f9bac76c2f3fcb29723227e3376cbb2'; 9 | var contractAddress = '0x420751cdeb28679d8e336f2b4d1fc61df7439b5a'; 10 | var userPrivateKey = process.env.TEST_PRIVATE_KEY || '0x7f76de05082c4d578219ca35a905f8debe922f1f00b99315ebf0706afc97f132'; 11 | 12 | const expect = chai.expect; 13 | const tokenPurchaseAmount = 0.01; 14 | const tokenFundAmount = 0.03; 15 | const tradeValue = 0.01; 16 | 17 | context('Vesting Time = 1 And Vesting Schedule = 100', async () => { 18 | var swapContract, app, isSaleOpen; 19 | 20 | before( async () => { 21 | app = new Application({test : true, mainnet : false}); 22 | }); 23 | 24 | it('should deploy Fixed Swap Contract', mochaAsync(async () => { 25 | 26 | app = new Application({test : true, mainnet : false}); 27 | /* Create Contract */ 28 | swapContract = await app.getFixedSwapContract({tokenAddress : ERC20TokenAddress, decimals : 18}); 29 | /* Deploy */ 30 | let res = await swapContract.deploy({ 31 | tradeValue : tradeValue, 32 | tokensForSale : tokenFundAmount, 33 | isTokenSwapAtomic : false, 34 | individualMaximumAmount : tokenFundAmount, 35 | startDate : moment().add(4, 'minutes'), 36 | endDate : moment().add(8, 'minutes'), 37 | hasWhitelisting : false, 38 | isETHTrade : true, 39 | vestingTime: 1, 40 | firstUnlock: 100 41 | }); 42 | contractAddress = swapContract.getAddress(); 43 | expect(res).to.not.equal(false); 44 | })); 45 | 46 | it('should get a Fixed Swap Contract From contractAddress', mochaAsync(async () => { 47 | 48 | /* Get Contract */ 49 | swapContract = await app.getFixedSwapContract({contractAddress}); 50 | swapContract.__init__(); 51 | await swapContract.assertERC20Info(); 52 | expect(swapContract).to.not.equal(false); 53 | })); 54 | 55 | it('should fund a Swap Contract and confirm balances', mochaAsync(async () => { 56 | /* Approve ERC20 Fund */ 57 | let res = await swapContract.approveFundERC20({tokenAmount : tokenFundAmount}); 58 | expect(res).to.not.equal(false); 59 | res = await swapContract.isApproved({address : app.account.getAddress(), tokenAmount : tokenFundAmount}); 60 | expect(res).to.equal(true); 61 | /* Fund */ 62 | res = await swapContract.hasStarted(); 63 | expect(res).to.equal(false); 64 | res = await swapContract.fund({tokenAmount : tokenFundAmount}); 65 | expect(res).to.not.equal(false); 66 | })); 67 | 68 | it('GET - isSaleOpen - before Start', mochaAsync(async () => { 69 | await delay(3*60*1000); 70 | let res = await swapContract.isOpen(); 71 | isSaleOpen = res; 72 | expect(res).to.equal(true); 73 | })); 74 | 75 | it('should do a non atomic swap on the Contract', mochaAsync(async () => { 76 | let res = await swapContract.swap({tokenAmount : tokenPurchaseAmount}); 77 | expect(res).to.not.equal(false); 78 | })); 79 | 80 | it('GET - Purchases', mochaAsync(async () => { 81 | let purchases = await swapContract.getPurchaseIds(); 82 | expect(purchases.length).to.equal(1); 83 | })); 84 | 85 | 86 | it('GET - My Purchases', mochaAsync(async () => { 87 | let purchases = await swapContract.getAddressPurchaseIds({address : app.account.getAddress()}); 88 | expect(purchases.length).to.equal(1); 89 | })); 90 | 91 | it('GET - Fixed Swap is Closed', mochaAsync(async () => { 92 | await delay(4*60*1000); 93 | let res = await swapContract.hasFinalized(); 94 | expect(res).to.equal(true); 95 | res = await swapContract.isOpen(); 96 | expect(res).to.equal(false); 97 | })); 98 | 99 | it('GET - Purchase ID', mochaAsync(async () => { 100 | let purchases = await swapContract.getAddressPurchaseIds({address : app.account.getAddress()}); 101 | let purchase = await swapContract.getPurchase({purchase_id : purchases[0]}); 102 | const amountPurchase = Number(purchase.amount).noExponents(); 103 | expect(Number(amountPurchase).toFixed(2)).to.equal(Number(tokenPurchaseAmount).noExponents()); 104 | expect(purchase.purchaser).to.equal(app.account.getAddress()); 105 | expect(purchase.wasFinalized).to.equal(false); 106 | expect(purchase.reverted).to.equal(false); 107 | })); 108 | 109 | it('Should Redeem Tokens', mochaAsync(async () => { 110 | let purchases = await swapContract.getAddressPurchaseIds({address : app.account.getAddress()}); 111 | let purchase = await swapContract.getPurchase({purchase_id : purchases[0]}); 112 | let redeemTokens = await swapContract.redeemTokens({purchase_id : purchase._id}); 113 | expect(redeemTokens.from).to.equal(app.account.getAddress()); 114 | expect(redeemTokens.status).to.equal(true); 115 | })); 116 | 117 | it('GET - Distribution Info', mochaAsync(async () => { 118 | let info = await swapContract.getDistributionInformation(); 119 | expect(Number(info.vestingTime).noExponents()).to.equal(Number(1).noExponents()); 120 | expect(info.vestingSchedule).to.deep.equal([100]); 121 | })); 122 | 123 | it('GET - Purchase ID After Redeem', mochaAsync(async () => { 124 | let purchases = await swapContract.getAddressPurchaseIds({address : app.account.getAddress()}); 125 | let purchase = await swapContract.getPurchase({purchase_id : purchases[0]}); 126 | const amountPurchase = Number(purchase.amount).noExponents(); 127 | expect(Number(amountPurchase).toFixed(2)).to.equal(Number(tokenPurchaseAmount).noExponents()); 128 | expect(purchase.purchaser).to.equal(app.account.getAddress()); 129 | expect(purchase.wasFinalized).to.equal(false); 130 | expect(purchase.reverted).to.equal(false); 131 | })); 132 | }); 133 | -------------------------------------------------------------------------------- /tests/test/stake.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | 3 | import Web3 from "web3"; 4 | import chai from 'chai'; 5 | import { mochaAsync } from '../utils'; 6 | import moment from 'moment'; 7 | import Application from '../../src/models'; 8 | import { ierc20, staking } from "../../src/interfaces"; 9 | import * as ethers from 'ethers'; 10 | 11 | var userPrivateKey = '0x7f76de05082c4d578219ca35a905f8debe922f1f00b99315ebf0706afc97f132'; 12 | 13 | const expect = chai.expect; 14 | 15 | 16 | context('Staking Contract', async () => { 17 | var ERC20TokenAddress; 18 | var StakingAddress; 19 | var stakeContract; 20 | var app; 21 | var ethersProvider; 22 | var currentTime; 23 | var tenMinutes = 10 * 60; 24 | var lockTime = tenMinutes; 25 | 26 | const forwardTime = async (time) => { 27 | // "Roads? Where we’re going, we don’t need roads." 28 | const date = parseInt(new Date().getTime()/1000); 29 | currentTime = date + await ethersProvider.send('evm_increaseTime', [ time ]); 30 | return await ethersProvider.send('evm_mine'); 31 | } 32 | 33 | before(mochaAsync(async () => { 34 | return new Promise(async (resolve) => { 35 | // Instance Application using ganache 36 | const ganacheProvider = require("ganache-core").provider({ 37 | gasLimit: 10000000000, 38 | 39 | gasPrice: 1, 40 | debug: true, 41 | accounts: [ 42 | { 43 | secretKey: userPrivateKey, 44 | balance: 10000000000000000000 45 | } 46 | ] 47 | }); 48 | app = new Application({test : true, mainnet : false, network : 'BSC', web3: 49 | new Web3(ganacheProvider) 50 | }); 51 | app.web3.eth.transactionConfirmationBlocks = 1; 52 | ethersProvider = new ethers.providers.Web3Provider(ganacheProvider); 53 | 54 | // Deploy the ERC20 55 | const contract = new app.web3.eth.Contract(ierc20.abi, null, {data: ierc20.bytecode}); 56 | contract.deploy() 57 | .send({ 58 | from: '0xe797860acFc4e06C1b2B96197a7dB1dFa518d5eB', 59 | gas: 4712388, 60 | }) 61 | .on('confirmation', function(confirmationNumber, receipt){ 62 | ERC20TokenAddress = receipt.contractAddress; 63 | // Deploy the stake contract 64 | const contractStake = new app.web3.eth.Contract(staking.abi, null, {data: staking.bytecode}); 65 | contractStake.deploy({ 66 | arguments: [ERC20TokenAddress, lockTime + ''] 67 | }) 68 | .send({ 69 | from: '0xe797860acFc4e06C1b2B96197a7dB1dFa518d5eB', 70 | gas: 4712388, 71 | }) 72 | .on('confirmation', function(confirmationNumber, receipt){ 73 | StakingAddress = receipt.contractAddress; 74 | resolve(); 75 | }).on('error', console.log); 76 | 77 | }).on('error', console.log); 78 | }); 79 | })); 80 | 81 | 82 | it('should automatically get addresses', mochaAsync(async () => { 83 | let stakeContract = await app.getStaking({}); 84 | expect(stakeContract).to.not.equal(false); 85 | expect(stakeContract.params.contractAddress).to.equal('0x1621AEC5D5B2e6eC6D9B58399E9D5253AF86DF5f'); 86 | expect(stakeContract.params.erc20TokenContract.params.contractAddress).to.equal('0xcfd314B14cAB8c3e36852A249EdcAa1D3Dd05055'); 87 | })); 88 | 89 | it('should get deployed contract', mochaAsync(async () => { 90 | stakeContract = await app.getStaking({contractAddress: StakingAddress, tokenAddress: ERC20TokenAddress}); 91 | expect(stakeContract).to.not.equal(false); 92 | })); 93 | 94 | it('should return empty stake amount at start', mochaAsync(async () => { 95 | const res = await stakeContract.stakeAmount({address: app.account.getAddress()}); 96 | expect(Number(res).noExponents()).to.equal(Number(0).noExponents()); 97 | })); 98 | 99 | it('should stake after approve', mochaAsync(async () => { 100 | expect(await stakeContract.isApproved({address: app.account.getAddress(), tokenAmount: 1000})).to.equal(false); 101 | await stakeContract.approveStakeERC20({tokenAmount: 1000}); 102 | expect(await stakeContract.isApproved({address: app.account.getAddress(), tokenAmount: '1000'})).to.equal(true); 103 | expect(await stakeContract.isApproved({address: app.account.getAddress(), tokenAmount: 1000})).to.equal(true); 104 | await stakeContract.stake({amount: 1000}); 105 | const res = await stakeContract.stakeAmount({address: app.account.getAddress()}); 106 | expect(Number(res).noExponents()).to.equal(Number(1000).noExponents()); 107 | 108 | let unlockTime = parseInt(await stakeContract.stakeTime({address: app.account.getAddress()})) + parseInt(await stakeContract.getLockTimePeriod()) 109 | 110 | expect(Number(await stakeContract.getUnlockTime({address: app.account.getAddress()})).noExponents()).to.equal(Number(unlockTime).noExponents()) 111 | })); 112 | 113 | 114 | it('should fail withdraw if we didnt reach time', mochaAsync(async () => { 115 | let failed = false; 116 | try { 117 | res = await stakeContract.withdrawAll() 118 | expect(res).to.not.equal(false); 119 | } catch (e) { 120 | failed = true; 121 | } 122 | expect(failed).to.equal(true); 123 | })); 124 | 125 | it('should withdraw after stake', mochaAsync(async () => { 126 | await forwardTime(lockTime + 30); 127 | await stakeContract.withdraw({amount: 400}); 128 | let res = await stakeContract.stakeAmount({address: app.account.getAddress()}); 129 | expect(Number(res).noExponents()).to.equal(Number(600).noExponents()); 130 | await stakeContract.withdrawAll(); 131 | res = await stakeContract.stakeAmount({address: app.account.getAddress()}); 132 | expect(Number(res).noExponents()).to.equal(Number(0).noExponents()); 133 | })); 134 | }); -------------------------------------------------------------------------------- /tests/test/vesting/vestingSchedule20-80.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | 3 | import chai from 'chai'; 4 | import { mochaAsync } from '../../utils'; 5 | import moment from 'moment'; 6 | import Application from '../../../src/models'; 7 | import delay from 'delay'; 8 | const ERC20TokenAddress = '0x7a7748bd6f9bac76c2f3fcb29723227e3376cbb2'; 9 | var contractAddress = '0x420751cdeb28679d8e336f2b4d1fc61df7439b5a'; 10 | var userPrivateKey = process.env.TEST_PRIVATE_KEY || '0x7f76de05082c4d578219ca35a905f8debe922f1f00b99315ebf0706afc97f132'; 11 | 12 | const expect = chai.expect; 13 | const tokenPurchaseAmount = 0.01; 14 | const tokenFundAmount = 0.03; 15 | const tradeValue = 0.01; 16 | 17 | context('Vesting Time = 2 And Vesting Schedule = 20% - 80%', async () => { 18 | var swapContract, app, isSaleOpen; 19 | 20 | before( async () => { 21 | app = new Application({test : true, mainnet : false}); 22 | }); 23 | 24 | it('should deploy Fixed Swap Contract', mochaAsync(async () => { 25 | 26 | app = new Application({test : true, mainnet : false}); 27 | /* Create Contract */ 28 | swapContract = await app.getFixedSwapContract({tokenAddress : ERC20TokenAddress, decimals : 18}); 29 | /* Deploy */ 30 | let res = await swapContract.deploy({ 31 | tradeValue : tradeValue, 32 | tokensForSale : tokenFundAmount, 33 | isTokenSwapAtomic : false, 34 | individualMaximumAmount : tokenFundAmount, 35 | startDate : moment().add(5, 'minutes'), 36 | endDate : moment().add(10, 'minutes'), 37 | hasWhitelisting : false, 38 | isETHTrade : true, 39 | vestingTime: 2, 40 | firstUnlock: 20 41 | }); 42 | contractAddress = swapContract.getAddress(); 43 | expect(res).to.not.equal(false); 44 | })); 45 | 46 | it('should get a Fixed Swap Contract From contractAddress', mochaAsync(async () => { 47 | 48 | /* Get Contract */ 49 | swapContract = await app.getFixedSwapContract({contractAddress}); 50 | swapContract.__init__(); 51 | await swapContract.assertERC20Info(); 52 | expect(swapContract).to.not.equal(false); 53 | })); 54 | 55 | it('should fund a Swap Contract and confirm balances', mochaAsync(async () => { 56 | /* Approve ERC20 Fund */ 57 | let res = await swapContract.approveFundERC20({tokenAmount : tokenFundAmount}); 58 | expect(res).to.not.equal(false); 59 | res = await swapContract.isApproved({address : app.account.getAddress(), tokenAmount : tokenFundAmount}); 60 | expect(res).to.equal(true); 61 | /* Fund */ 62 | res = await swapContract.hasStarted(); 63 | expect(res).to.equal(false); 64 | res = await swapContract.fund({tokenAmount : tokenFundAmount}); 65 | expect(res).to.not.equal(false); 66 | })); 67 | 68 | it('should do a non atomic swap on the Contract', mochaAsync(async () => { 69 | await delay(5*60*1000); 70 | let res = await swapContract.swap({tokenAmount : tokenPurchaseAmount}); 71 | expect(res).to.not.equal(false); 72 | })); 73 | 74 | it('GET - Purchases', mochaAsync(async () => { 75 | let purchases = await swapContract.getPurchaseIds(); 76 | expect(purchases.length).to.equal(1); 77 | })); 78 | 79 | 80 | it('GET - My Purchases', mochaAsync(async () => { 81 | let purchases = await swapContract.getAddressPurchaseIds({address : app.account.getAddress()}); 82 | expect(purchases.length).to.equal(1); 83 | })); 84 | 85 | it('GET - Purchase ID', mochaAsync(async () => { 86 | let purchases = await swapContract.getAddressPurchaseIds({address : app.account.getAddress()}); 87 | let purchase = await swapContract.getPurchase({purchase_id : purchases[0]}); 88 | const amountPurchase = Number(purchase.amount).noExponents(); 89 | expect(Number(amountPurchase).toFixed(2)).to.equal(Number(tokenPurchaseAmount).noExponents()); 90 | expect(purchase.purchaser).to.equal(app.account.getAddress()); 91 | expect(purchase.wasFinalized).to.equal(false); 92 | expect(purchase.reverted).to.equal(false); 93 | console.log(purchase) 94 | })); 95 | 96 | it('GET - Fixed Swap is Closed', mochaAsync(async () => { 97 | await delay(5*60*1000); 98 | let res = await swapContract.hasFinalized(); 99 | expect(res).to.equal(true); 100 | res = await swapContract.isOpen(); 101 | expect(res).to.equal(false); 102 | })); 103 | 104 | it('Should Redeem Tokens - First time', mochaAsync(async () => { 105 | let purchases = await swapContract.getAddressPurchaseIds({address : app.account.getAddress()}); 106 | let purchase = await swapContract.getPurchase({purchase_id : purchases[0]}); 107 | let redeemTokens = await swapContract.redeemTokens({purchase_id : purchase._id}); 108 | expect(redeemTokens.from).to.equal(app.account.getAddress()); 109 | expect(redeemTokens.status).to.equal(true); 110 | })); 111 | 112 | it('GET - Distribution Info', mochaAsync(async () => { 113 | let info = await swapContract.getDistributionInformation(); 114 | expect(Number(info.vestingTime).noExponents()).to.equal(Number(2).noExponents()); 115 | expect(info.vestingSchedule).to.deep.equal([20,80]); 116 | })); 117 | 118 | it('Shouldnt Redeem Tokens - Second time', mochaAsync(async () => { 119 | let purchases = await swapContract.getAddressPurchaseIds({address : app.account.getAddress()}); 120 | let purchase = await swapContract.getPurchase({purchase_id : purchases[0]}); 121 | let redeemTokens = await swapContract.redeemTokens({purchase_id : purchase._id}); 122 | expect(redeemTokens).toThrow(); 123 | expect(redeemTokens.from).to.equal(app.account.getAddress()); 124 | expect(redeemTokens.status).to.equal(false); 125 | })); 126 | 127 | it('GET - Purchase ID After Redeem', mochaAsync(async () => { 128 | let purchases = await swapContract.getAddressPurchaseIds({address : app.account.getAddress()}); 129 | let purchase = await swapContract.getPurchase({purchase_id : purchases[0]}); 130 | const amountPurchase = Number(purchase.amount).noExponents(); 131 | expect(Number(amountPurchase).toFixed(2)).to.equal(Number(tokenPurchaseAmount).noExponents()); 132 | expect(purchase.purchaser).to.equal(app.account.getAddress()); 133 | expect(purchase.wasFinalized).to.equal(false); 134 | expect(purchase.reverted).to.equal(false); 135 | })); 136 | }); 137 | -------------------------------------------------------------------------------- /src/utils/Signer.js: -------------------------------------------------------------------------------- 1 | import * as ethers from 'ethers'; 2 | import Numbers from "./Numbers"; 3 | 4 | /** 5 | * @typedef Signer 6 | * @type {object} 7 | * @property {Function} signMessage - Signs a message 8 | */ 9 | 10 | /** 11 | * @typedef SignedAddress 12 | * @type {object} 13 | * @property {string} address - Address. 14 | * @property {string} allocation - Max Allocation. 15 | * @property {string} signature - Signed Address 16 | */ 17 | 18 | 19 | /** 20 | * Signer object 21 | * @constructor Signer 22 | */ 23 | class Signer { 24 | 25 | /** 26 | * @function generateSignerAccount 27 | * @description Generates a new private key for signing the whitelist addresses 28 | * @param {string} password Password for encryption 29 | * @param {string=} entropy Add more entropy 30 | * @returns {string} JSON Account 31 | */ 32 | async generateSignerAccount({password, entropy}) { 33 | const wallet = ethers.Wallet.createRandom(entropy) 34 | 35 | return await wallet.encrypt(password); 36 | } 37 | 38 | /** 39 | * @function getAddressFromAccount 40 | * @description Recovers an account given a json file 41 | * @param {string} accountJson Account in a json format 42 | * @param {string} password Password to unlock the account 43 | * @returns {string} Address 44 | */ 45 | getAddressFromAccount({accountJson, password}) { 46 | const signer = ethers.Wallet.fromEncryptedJsonSync( 47 | accountJson, 48 | password 49 | ); 50 | return signer.address; 51 | } 52 | 53 | /** 54 | * @function signAddresses 55 | * @description Signs an array of addresses. Will ignore malformed addresses. 56 | * @param {string[]} addresses List of addresses to sign 57 | * @param {string} accountJson Account in a json format 58 | * @param {number[]} accountMaxAllocations List of mac allocations in wei 59 | * @param {number} decimals Decimals for the max allocation 60 | * @param {string} contractAddress Pool 61 | * @param {string} password Password to unlock the account 62 | * @returns {SignedAddress[]} signedAddresses 63 | */ 64 | async signAddresses({addresses, accountJson, password, accountMaxAllocations, contractAddress, decimals}) { 65 | const signer = ethers.Wallet.fromEncryptedJsonSync( 66 | accountJson, 67 | password 68 | ); 69 | return await this.signAddressesWithSigner({addresses, accountMaxAllocations, contractAddress, decimals, signer}); 70 | } 71 | 72 | /** 73 | * @function signAddressesWithSigner 74 | * @description Signs an array of addresses. Will ignore malformed addresses. 75 | * @param {string[]} addresses List of addresses to sign 76 | * @param {number[]} accountMaxAllocations List of mac allocations in wei 77 | * @param {number} decimals Decimals for the max allocation 78 | * @param {string} contractAddress Pool 79 | * @param {Signer} signer Signer object 80 | * @returns {SignedAddress[]} signedAddresses 81 | */ 82 | async signAddressesWithSigner({addresses, accountMaxAllocations, contractAddress, decimals, signer}) { 83 | const signedAddresses = []; 84 | let n = 0; 85 | let r = 0; 86 | let index = 0; 87 | for (const addr of addresses) { 88 | const allocation = Numbers.toSmartContractDecimals( 89 | accountMaxAllocations[index], 90 | decimals 91 | ); 92 | const result = await this._trySignAddress(signer, addr, allocation, contractAddress); 93 | if (result) { 94 | signedAddresses.push({ 95 | address: addr, 96 | signature: result, 97 | allocation: allocation, 98 | }); 99 | n++; 100 | } else { 101 | r++; 102 | } 103 | index++; 104 | } 105 | console.info(n, "lines successfully processed"); 106 | console.info(r, "lines rejected"); 107 | return signedAddresses; 108 | } 109 | 110 | /** 111 | * @function signMessage 112 | * @description Signs a message given an account 113 | * @param {Signer} signer Signer 114 | * @param {string} message String to sign 115 | * @returns {string} signedString 116 | */ 117 | async signMessage({signer, message}) { 118 | return await signer.signMessage(message); 119 | } 120 | 121 | /** 122 | * @function verifySignature 123 | * @description Verifies if an address has been signed with the signer address 124 | * @param {string} signature Signature 125 | * @param {string} address Address signed 126 | * @param {string} contractAddress Pool contract address 127 | * @param {string} accountMaxAllocation Max allocation 128 | * @param {string} signerAddress Address who signed the message 129 | * @returns {boolean} verified 130 | */ 131 | async verifySignature({signature, address, accountMaxAllocation, contractAddress, signerAddress}) { 132 | try { 133 | const actualAddress = ethers.utils.verifyMessage(this._addressToBytes32(address, accountMaxAllocation, contractAddress), signature); 134 | return signerAddress.toLowerCase() === actualAddress.toLowerCase(); 135 | } catch (e) { 136 | return false; 137 | } 138 | } 139 | 140 | /** 141 | * @function signAddress 142 | * @description Signs a address given an account 143 | * @param {Signer} signer Signer object 144 | * @param {string} address Address to sign 145 | * @param {string} contractAddress Pool contract address 146 | * @param {string} accountMaxAllocation Max allocation 147 | * @returns {string} signedString 148 | */ 149 | async signAddress({signer, address, accountMaxAllocation, contractAddress}) { 150 | return await this.signMessage({signer, message: this._addressToBytes32(address, accountMaxAllocation, contractAddress)}); 151 | } 152 | 153 | async _trySignAddress(signer, address, accountMaxAllocation, contractAddress) { 154 | if (ethers.utils.isAddress(address) && ethers.BigNumber.from(address) != 0) { 155 | return await this.signAddress({signer, address, accountMaxAllocation, contractAddress}); 156 | } else { 157 | console.error("address not valid - ignored :", address); 158 | return ""; 159 | } 160 | } 161 | 162 | 163 | _addressToBytes32(address, accountMaxAllocation, contractAddress) { 164 | const messageHash = ethers.utils.solidityKeccak256( 165 | ["address", "uint256", "address"], 166 | [address, accountMaxAllocation, contractAddress], 167 | ); 168 | 169 | return ethers.utils.arrayify(messageHash); 170 | } 171 | } 172 | 173 | export default Signer; -------------------------------------------------------------------------------- /docs/SIGNER.md: -------------------------------------------------------------------------------- 1 | ## Classes 2 | 3 |
4 |
Signer
5 |
6 |
7 | 8 | ## Functions 9 | 10 |
11 |
generateSignerAccount(password, [entropy])string
12 |

Generates a new private key for signing the whitelist addresses

13 |
14 |
getAddressFromAccount(accountJson, password)string
15 |

Recovers an account given a json file

16 |
17 |
signAddresses(addresses, accountJson, accountMaxAllocations, decimals, contractAddress, password)Array.<SignedAddress>
18 |

Signs an array of addresses. Will ignore malformed addresses.

19 |
20 |
signAddressesWithSigner(addresses, accountMaxAllocations, decimals, contractAddress, signer)Array.<SignedAddress>
21 |

Signs an array of addresses. Will ignore malformed addresses.

22 |
23 |
signMessage(signer, message)string
24 |

Signs a message given an account

25 |
26 |
verifySignature(signature, address, contractAddress, accountMaxAllocation, signerAddress)boolean
27 |

Verifies if an address has been signed with the signer address

28 |
29 |
signAddress(signer, address, contractAddress, accountMaxAllocation)string
30 |

Signs a address given an account

31 |
32 |
33 | 34 | ## Typedefs 35 | 36 |
37 |
Signer : object
38 |
39 |
SignedAddress : object
40 |
41 |
42 | 43 | 44 | 45 | ## Signer 46 | **Kind**: global class 47 | 48 | 49 | ### new Signer() 50 | Signer object 51 | 52 | 53 | 54 | ## generateSignerAccount(password, [entropy]) ⇒ string 55 | Generates a new private key for signing the whitelist addresses 56 | 57 | **Kind**: global function 58 | **Returns**: string - JSON Account 59 | 60 | | Param | Type | Description | 61 | | --- | --- | --- | 62 | | password | string | Password for encryption | 63 | | [entropy] | string | Add more entropy | 64 | 65 | 66 | 67 | ## getAddressFromAccount(accountJson, password) ⇒ string 68 | Recovers an account given a json file 69 | 70 | **Kind**: global function 71 | **Returns**: string - Address 72 | 73 | | Param | Type | Description | 74 | | --- | --- | --- | 75 | | accountJson | string | Account in a json format | 76 | | password | string | Password to unlock the account | 77 | 78 | 79 | 80 | ## signAddresses(addresses, accountJson, accountMaxAllocations, decimals, contractAddress, password) ⇒ [Array.<SignedAddress>](#SignedAddress) 81 | Signs an array of addresses. Will ignore malformed addresses. 82 | 83 | **Kind**: global function 84 | **Returns**: [Array.<SignedAddress>](#SignedAddress) - signedAddresses 85 | 86 | | Param | Type | Description | 87 | | --- | --- | --- | 88 | | addresses | Array.<string> | List of addresses to sign | 89 | | accountJson | string | Account in a json format | 90 | | accountMaxAllocations | Array.<number> | List of mac allocations in wei | 91 | | decimals | number | Decimals for the max allocation | 92 | | contractAddress | string | Pool | 93 | | password | string | Password to unlock the account | 94 | 95 | 96 | 97 | ## signAddressesWithSigner(addresses, accountMaxAllocations, decimals, contractAddress, signer) ⇒ [Array.<SignedAddress>](#SignedAddress) 98 | Signs an array of addresses. Will ignore malformed addresses. 99 | 100 | **Kind**: global function 101 | **Returns**: [Array.<SignedAddress>](#SignedAddress) - signedAddresses 102 | 103 | | Param | Type | Description | 104 | | --- | --- | --- | 105 | | addresses | Array.<string> | List of addresses to sign | 106 | | accountMaxAllocations | Array.<number> | List of mac allocations in wei | 107 | | decimals | number | Decimals for the max allocation | 108 | | contractAddress | string | Pool | 109 | | signer | [Signer](#Signer) | Signer object | 110 | 111 | 112 | 113 | ## signMessage(signer, message) ⇒ string 114 | Signs a message given an account 115 | 116 | **Kind**: global function 117 | **Returns**: string - signedString 118 | 119 | | Param | Type | Description | 120 | | --- | --- | --- | 121 | | signer | [Signer](#Signer) | Signer | 122 | | message | string | String to sign | 123 | 124 | 125 | 126 | ## verifySignature(signature, address, contractAddress, accountMaxAllocation, signerAddress) ⇒ boolean 127 | Verifies if an address has been signed with the signer address 128 | 129 | **Kind**: global function 130 | **Returns**: boolean - verified 131 | 132 | | Param | Type | Description | 133 | | --- | --- | --- | 134 | | signature | string | Signature | 135 | | address | string | Address signed | 136 | | contractAddress | string | Pool contract address | 137 | | accountMaxAllocation | string | Max allocation | 138 | | signerAddress | string | Address who signed the message | 139 | 140 | 141 | 142 | ## signAddress(signer, address, contractAddress, accountMaxAllocation) ⇒ string 143 | Signs a address given an account 144 | 145 | **Kind**: global function 146 | **Returns**: string - signedString 147 | 148 | | Param | Type | Description | 149 | | --- | --- | --- | 150 | | signer | [Signer](#Signer) | Signer object | 151 | | address | string | Address to sign | 152 | | contractAddress | string | Pool contract address | 153 | | accountMaxAllocation | string | Max allocation | 154 | 155 | 156 | 157 | ## Signer : object 158 | **Kind**: global typedef 159 | **Properties** 160 | 161 | | Name | Type | Description | 162 | | --- | --- | --- | 163 | | signMessage | function | Signs a message | 164 | 165 | 166 | 167 | ### new Signer() 168 | Signer object 169 | 170 | 171 | 172 | ## SignedAddress : object 173 | **Kind**: global typedef 174 | **Properties** 175 | 176 | | Name | Type | Description | 177 | | --- | --- | --- | 178 | | address | string | Address. | 179 | | allocation | string | Max Allocation. | 180 | | signature | string | Signed Address | 181 | 182 | -------------------------------------------------------------------------------- /src/models/base/Staking.js: -------------------------------------------------------------------------------- 1 | import Contract from "./Contract"; 2 | import { staking } from "../../interfaces"; 3 | import Numbers from "../../utils/Numbers"; 4 | import ERC20TokenContract from "./ERC20TokenContract"; 5 | import Client from "../../utils/Client"; 6 | import Addresses from "./Addresses"; 7 | import Chains from "../../utils/Chains"; 8 | 9 | /** 10 | * Staking Object 11 | * @constructor Staking 12 | * @param {Web3} web3 13 | * @param {string=} contractAddress The staking contract address. (Default: Predefined addresses depending on the network) 14 | * @param {Account} acc 15 | * @param {string=} tokenAddress The staking token address. (Default: Predefined addresses depending on the network) 16 | * @param {(ETH|BSC|MATIC|DOT)=} network The network where the staking contract is. (Default: ETH) 17 | * @param {Boolean=} test ? Specifies if we're on test env (Default: false) 18 | */ 19 | class Staking { 20 | 21 | stakingAddresses = { 22 | 'BSC': '0xD558675a8c8E1fd45002010BaC970B115163dE3a', 23 | 'ETH': '0xc24A365A870821EB83Fd216c9596eDD89479d8d7' 24 | }; 25 | stakingTestAddresses = { 26 | 'BSC': '0x1621AEC5D5B2e6eC6D9B58399E9D5253AF86DF5f', 27 | 'ETH': '0xa297c295aFcac59c749e25A02811a02B2f7D3Ab5' 28 | }; 29 | 30 | constructor({ 31 | web3, 32 | contractAddress, 33 | acc, 34 | tokenAddress, 35 | network = 'ETH', 36 | test = false 37 | }) { 38 | if (!web3) { 39 | throw new Error("Please provide a valid web3 provider"); 40 | } 41 | 42 | Chains.checkIfNetworkIsSupported(network); 43 | this.web3 = web3; 44 | this.version = "2.0"; 45 | if (acc) { 46 | this.acc = acc; 47 | } 48 | 49 | if (!contractAddress) { 50 | let stakingAddresses = this.stakingAddresses; 51 | if (test) { 52 | stakingAddresses = this.stakingTestAddresses; 53 | } 54 | contractAddress = stakingAddresses[network]; 55 | if (!contractAddress) { 56 | throw new Error('Staking not available on the network ' + network); 57 | } 58 | } 59 | 60 | this.params = { 61 | web3: web3, 62 | contractAddress: contractAddress, 63 | contract: new Contract(web3, staking, contractAddress), 64 | }; 65 | 66 | if (!tokenAddress) { 67 | let tokenAddresses = Addresses.tokenAddresses; 68 | if (test) { 69 | tokenAddresses = Addresses.tokenTestAddresses; 70 | } 71 | tokenAddress = tokenAddresses[network]; 72 | if (!tokenAddress) { 73 | throw new Error('Token not available on the network ' + network); 74 | } 75 | } 76 | 77 | this.params.erc20TokenContract = new ERC20TokenContract({ 78 | web3: web3, 79 | contractAddress: tokenAddress, 80 | acc 81 | }); 82 | this.client = new Client(); 83 | } 84 | 85 | /** 86 | * @function stake 87 | * @description Stakes tokens inside the stake contract 88 | * @param {Integer} amount Amount 89 | */ 90 | stake = async ({ amount }) => { 91 | amount = Numbers.toSmartContractDecimals( 92 | amount, 93 | await this.getDecimals() 94 | ) 95 | try { 96 | return await this.client.sendTx( 97 | this.params.web3, 98 | this.acc, 99 | this.params.contract, 100 | this.params.contract 101 | .getContract() 102 | .methods.stake(amount) 103 | ); 104 | } catch (err) { 105 | throw err; 106 | } 107 | }; 108 | 109 | /** 110 | * @function approveStakeERC20 111 | * @param {Integer} tokenAmount 112 | * @description Approve the stake to use approved tokens 113 | */ 114 | approveStakeERC20 = async ({ tokenAmount, callback }) => { 115 | return await this.getTokenContract().approve({ 116 | address: this.params.contractAddress, 117 | amount: tokenAmount, 118 | callback 119 | }); 120 | }; 121 | 122 | /** 123 | * @function isApproved 124 | * @description Verify if the address has approved the staking to deposit 125 | * @param {Integer} tokenAmount 126 | * @param {Address} address 127 | * @returns {Boolean} 128 | */ 129 | isApproved = async ({ tokenAmount, address }) => { 130 | return await this.getTokenContract().isApproved({ 131 | address: address, 132 | amount: tokenAmount, 133 | spenderAddress: this.params.contractAddress 134 | }); 135 | }; 136 | 137 | /** 138 | * @function withdraw 139 | * @param {Integer} amount 140 | * @description Withdraw tokens from the stake contract 141 | */ 142 | withdraw = async ({amount}) => { 143 | 144 | amount = Numbers.toSmartContractDecimals( 145 | amount, 146 | await this.getDecimals() 147 | ) 148 | try { 149 | return await this.client.sendTx( 150 | this.params.web3, 151 | this.acc, 152 | this.params.contract, 153 | this.params.contract 154 | .getContract() 155 | .methods.withdraw(amount) 156 | ); 157 | } catch (err) { 158 | throw err; 159 | } 160 | }; 161 | 162 | /** 163 | * @function withdrawAll 164 | * @description Withdraw all the tokens from the stake contract 165 | */ 166 | withdrawAll = async () => { 167 | try { 168 | return await this.client.sendTx( 169 | this.params.web3, 170 | this.acc, 171 | this.params.contract, 172 | this.params.contract 173 | .getContract() 174 | .methods.withdrawAll() 175 | ); 176 | } catch (err) { 177 | throw err; 178 | } 179 | }; 180 | 181 | /** 182 | * @function claim 183 | * @description Claim rewards from the staking contract 184 | */ 185 | claim = async () => { 186 | try { 187 | return await this.client.sendTx( 188 | this.params.web3, 189 | this.acc, 190 | this.params.contract, 191 | this.params.contract 192 | .getContract() 193 | .methods.claim() 194 | ); 195 | } catch (err) { 196 | throw err; 197 | } 198 | }; 199 | 200 | /** 201 | * @function userAccumulatedRewards 202 | * @description Returns the accumulated rewards 203 | * @param {string} address 204 | * @returns {Integer} userAccumulatedRewards 205 | */ 206 | userAccumulatedRewards = async ({address}) => { 207 | return await this.params.contract.getContract().methods.userAccumulatedRewards(address).call(); 208 | } 209 | 210 | /** 211 | * @function stakeTime 212 | * @description Returns the stake time for a wallet 213 | * @param {string} address 214 | * @returns {Integer} stakeTime 215 | */ 216 | stakeTime = async ({address}) => { 217 | return await this.params.contract.getContract().methods.stakeTime(address).call(); 218 | } 219 | 220 | /** 221 | * @function lockTimePeriod 222 | * @description Returns the lock time perdio 223 | * @returns {Integer} lockTimePeriod 224 | */ 225 | getLockTimePeriod = async () => { 226 | return await this.params.contract.getContract().methods.lockTimePeriod().call(); 227 | } 228 | 229 | /** 230 | * @function getUnlockTime 231 | * @description Returns the stake time for a wallet 232 | * @param {string} address 233 | * @returns {Integer} unlockTime 234 | */ 235 | getUnlockTime = async ({address}) => { 236 | return await this.params.contract.getContract().methods.getUnlockTime(address).call(); 237 | } 238 | 239 | /** 240 | * @function stakeAmount 241 | * @description Returns the stake amount for a wallet 242 | * @param {string} address 243 | * @returns {Integer} stakeAmount 244 | */ 245 | stakeAmount = async ({address}) => { 246 | return Numbers.fromDecimals( 247 | await this.params.contract.getContract().methods.stakeAmount(address).call(), 248 | await this.getDecimals() 249 | ); 250 | } 251 | 252 | getDecimals = async () => { 253 | return 18; 254 | } 255 | 256 | getTokenContract() { 257 | return this.params.erc20TokenContract; 258 | } 259 | } 260 | 261 | export default Staking; 262 | -------------------------------------------------------------------------------- /src/models/index.js: -------------------------------------------------------------------------------- 1 | import Web3 from "web3"; 2 | import FixedSwapContract from "./contracts/FixedSwapContract"; 3 | import Signer from "../utils/Signer"; 4 | import Network from "../utils/Network"; 5 | import Wallet from "../utils/Wallet"; 6 | import Account from './base/Account'; 7 | import ERC20TokenContract from "./base/ERC20TokenContract"; 8 | import FixedNFTSwapContract from "./contracts/FixedNFTSwapContract"; 9 | import Staking from "./base/Staking"; 10 | import FixedSwapContractLegacy from "./contracts/legacy/FixedSwapContractLegacy"; 11 | import Chains from "../utils/Chains"; 12 | 13 | const TEST_PRIVATE_KEY = 14 | "0x7f76de05082c4d578219ca35a905f8debe922f1f00b99315ebf0706afc97f132"; 15 | 16 | /** 17 | * Polkastarter Application Object 18 | * @constructor Application 19 | * @param {(ETH|BSC|MATIC|DOT)=} network Current network (Default = ETH) 20 | * @param {Boolean=} mainnet Specifies if we're on mainnet or tesnet (Default = true); 21 | * @param {Boolean=} test ? Specifies if we're on test env 22 | * @param {Web3=} web3 Custom Web3 instance. If not provided the Application will instance it for you. (Default: undefined) 23 | */ 24 | class Application { 25 | constructor({test=false, mainnet=true, network='ETH', web3=undefined}) { 26 | this.test = test; 27 | global.IS_TEST = !mainnet; 28 | this.mainnet = mainnet; 29 | Chains.checkIfNetworkIsSupported(network); 30 | this.network = network; 31 | 32 | if(this.test){ 33 | if (!web3) { 34 | this.start(); 35 | } else { 36 | this.web3 = web3; 37 | } 38 | // this.login(); 39 | this.account = new Account(this.web3, this.web3.eth.accounts.privateKeyToAccount(TEST_PRIVATE_KEY)); 40 | } 41 | } 42 | 43 | /** 44 | * @function startWithoutMetamask 45 | * @description Starts an instance of web3 for read-only methods 46 | */ 47 | startWithoutMetamask = () => { 48 | const rpc = Chains.getRpcUrl(this.network, this.mainnet); 49 | if (rpc) { 50 | this.web3 = new Web3(rpc); 51 | } 52 | } 53 | 54 | /** 55 | * @function start 56 | * @description Starts an instance of web3 57 | */ 58 | start = () => { 59 | const rpc = Chains.getRpcUrl(this.network, this.mainnet); 60 | if (rpc) { 61 | this.web3 = new Web3(rpc); 62 | } 63 | 64 | if((typeof window !== "undefined") && window.ethereum) { 65 | window.web3 = new Web3(window.ethereum); 66 | this.web3 = window.web3; 67 | } 68 | } 69 | 70 | 71 | /** 72 | * @function login 73 | * @description Logins with metamask 74 | */ 75 | login = async () => { 76 | try{ 77 | console.log("Login being done") 78 | if (typeof window === "undefined") { return false; } 79 | if (window.ethereum) { 80 | window.web3 = new Web3(window.ethereum); 81 | this.web3 = window.web3; 82 | await window.ethereum.enable(); 83 | return true; 84 | } 85 | return false; 86 | }catch(err){ 87 | throw err; 88 | } 89 | }; 90 | 91 | 92 | __getUserAccount = ({privateKey}) => { 93 | return new Account(this.web3, this.web3.eth.accounts.privateKeyToAccount(privateKey)); 94 | } 95 | 96 | /** 97 | * @function getSigner 98 | * @description Returns the Signer instance. 99 | */ 100 | getSigner = () => { 101 | return new Signer(); 102 | } 103 | 104 | /** 105 | * @function getNetworkUtils 106 | * @description Returns the Network Utils instance. 107 | */ 108 | getNetworkUtils = () => { 109 | return new Network(this.network, !this.mainnet, this.getETHNetwork); 110 | } 111 | 112 | /** 113 | * @function getWalletUtils 114 | * @description Returns the Wallet Utils instance. 115 | */ 116 | getWalletUtils = () => { 117 | return new Wallet(this.network, !this.mainnet); 118 | } 119 | 120 | /** 121 | * @function getStaking 122 | * @param {string=} contractAddress The staking contract address. (Default: Predefined addresses depending on the network) 123 | * @param {string=} tokenAddress The staking token address. (Default: Predefined addresses depending on the network) 124 | * @description Returns the Staking Model instance. 125 | */ 126 | getStaking = ({contractAddress=null, tokenAddress=null}) => { 127 | return new Staking({ 128 | web3: this.web3, 129 | acc : this.test ? this.account : null, 130 | contractAddress: contractAddress, 131 | tokenAddress: tokenAddress, 132 | network: this.network, 133 | test: !this.mainnet 134 | }); 135 | } 136 | 137 | /** 138 | * @function getFixedSwapContract 139 | * @param {string} tokenAddress The token address we want to trade 140 | * @param {string=} contractAddress The swap contract address, in case t hat has already been instanced. (Default = null) 141 | * @description Returns Fixed Swap instance 142 | */ 143 | getFixedSwapContract = async ({tokenAddress, contractAddress=null}) => { 144 | let contract; 145 | if(!contractAddress){ 146 | // Not deployed 147 | return new FixedSwapContract({ 148 | web3: this.web3, 149 | tokenAddress: tokenAddress, 150 | contractAddress: contractAddress, 151 | acc : this.test ? this.account : null 152 | }); 153 | }else{ 154 | // Deployed 155 | try{ 156 | contract = new FixedSwapContract({ 157 | web3: this.web3, 158 | tokenAddress: tokenAddress, 159 | contractAddress: contractAddress, 160 | acc : this.test ? this.account : null 161 | }); 162 | await contract.isETHTrade(); 163 | }catch(err){ 164 | try{ 165 | contract = new FixedSwapContractLegacy({ 166 | web3: this.web3, 167 | tokenAddress: tokenAddress, 168 | contractAddress: contractAddress, 169 | acc : this.test ? this.account : null 170 | }); 171 | }catch(err){ 172 | throw err; 173 | 174 | } 175 | } 176 | 177 | return contract; 178 | } 179 | }; 180 | 181 | /** 182 | * @function getFixedNFTSwapContract 183 | * @param {string=} contractAddress The swap contract address, in case t hat has already been instanced. (Default = null) 184 | * @description Returns Fixed NFT Swap instance 185 | */ 186 | getFixedNFTSwapContract = async ({contractAddress=null}) => { 187 | let contract; 188 | if(!contractAddress){ 189 | // Not deployed 190 | return new FixedNFTSwapContract({ 191 | web3: this.web3, 192 | contractAddress: contractAddress, 193 | acc : this.test ? this.account : null 194 | }); 195 | }else{ 196 | // Deployed 197 | try{ 198 | contract = new FixedNFTSwapContract({ 199 | web3: this.web3, 200 | contractAddress: contractAddress, 201 | acc : this.test ? this.account : null 202 | }); 203 | await contract.isETHTrade(); 204 | }catch(err){ 205 | throw err; 206 | } 207 | 208 | return contract; 209 | } 210 | }; 211 | 212 | /** 213 | * @function getERC20TokenContract 214 | * @param {string} tokenAddress The token address 215 | * @description Returns ERC20 instance 216 | */ 217 | getERC20TokenContract = ({tokenAddress}) => { 218 | try{ 219 | return new ERC20TokenContract({ 220 | web3: this.web3, 221 | contractAddress: tokenAddress, 222 | acc : this.test ? this.account : null 223 | }); 224 | }catch(err){ 225 | throw err; 226 | } 227 | }; 228 | 229 | /** 230 | * @function getETHNetwork 231 | * @description Returns the current network 232 | */ 233 | getETHNetwork = async () => { 234 | const netId = await this.web3.eth.net.getId(); 235 | const networksEnum = Chains.getNetworksEnum(); 236 | const networkName = networksEnum.hasOwnProperty(netId) 237 | ? networksEnum[netId] 238 | : "Unknown"; 239 | return networkName; 240 | }; 241 | 242 | /** 243 | * @function getAddress 244 | * @description Returns the connected user address 245 | */ 246 | getAddress = async () => { 247 | const accounts = await this.web3.eth.getAccounts(); 248 | return accounts[0]; 249 | }; 250 | 251 | 252 | /** 253 | * @function getETHBalance 254 | * @description Returns the native currency of the connected user wallet. 255 | */ 256 | getETHBalance = async () => { 257 | let wei = await this.web3.eth.getBalance(await this.getAddress()); 258 | return this.web3.utils.fromWei(wei, "ether"); 259 | }; 260 | } 261 | 262 | export default Application; 263 | -------------------------------------------------------------------------------- /docs/IDOSTAKING.md: -------------------------------------------------------------------------------- 1 | ## Classes 2 | 3 |
4 |
IDOStaking
5 |
6 |
7 | 8 | ## Functions 9 | 10 |
11 |
deploy(owner, rewardsDistribution, rewardsToken, stakingToken, rewardsDuration, tokenSaleAddress)string
12 |

Deploys the IDO Staking contracts

13 |
14 |
stake(amount)
15 |

Stakes tokens inside the stake contract

16 |
17 |
approveStakeERC20(tokenAmount)
18 |

Approve the stake to use approved tokens

19 |
20 |
isApproved(tokenAmount, address)Boolean
21 |

Verify if the address has approved the staking to deposit

22 |
23 |
getAPY()Integer
24 |

Returns the APY that this pool is giving

25 |
26 |
withdraw(amount)
27 |

Withdraw tokens from the stake contract

28 |
29 |
withdrawAll()
30 |

Withdraw all the tokens from the stake contract

31 |
32 |
exit()
33 |

Claims all the rewards and withdraws all the staked tokens

34 |
35 |
claim()
36 |

Claim rewards from the staking contract

37 |
38 |
notifyRewardAmountSamePeriod(amount)
39 |

add (more) rewards token to current/future period

40 |
41 |
transferRewardTokenSamePeriod(amount)
42 |

Transfer and add (more) rewards token to current/future period

43 |
44 |
userAccumulatedRewards(address)Integer
45 |

Returns the accumulated rewards

46 |
47 |
recoverERC20(address)
48 |

Emergency withdrawal of tokens

49 |
50 |
lastTimeRewardApplicable()Date
51 |

Get the last time rewards are applicable

52 |
53 |
totalStaked()Integer
54 |

Returns the total stake

55 |
56 |
balanceRewardsToken()Integer
57 |

substract staked amount if staked token is the same as rewards token

58 |
59 |
stakeAmount(address)Integer
60 |

Returns the stake amount for a wallet

61 |
62 |
setTokenSaleAddress(address)
63 |

Sets the token sale address

64 |
65 |
66 | 67 | 68 | 69 | ## IDOStaking 70 | **Kind**: global class 71 | 72 | 73 | ### new IDOStaking(web3, contractAddress, acc) 74 | IDO Staking Object 75 | 76 | 77 | | Param | Type | Description | 78 | | --- | --- | --- | 79 | | web3 | Web3 | | 80 | | contractAddress | string | The staking contract address. | 81 | | acc | Account | | 82 | 83 | 84 | 85 | ## deploy(owner, rewardsDistribution, rewardsToken, stakingToken, rewardsDuration, tokenSaleAddress) ⇒ string 86 | Deploys the IDO Staking contracts 87 | 88 | **Kind**: global function 89 | **Returns**: string - address The deployed contract address 90 | 91 | | Param | Type | Description | 92 | | --- | --- | --- | 93 | | owner | string | Address of the owner | 94 | | rewardsDistribution | string | Address of the distributor | 95 | | rewardsToken | string | Address of the token we want to reward | 96 | | stakingToken | string | Address of the token to be staked | 97 | | rewardsDuration | Integer | Duration of the rewards | 98 | | tokenSaleAddress | string | Address of the pool | 99 | 100 | 101 | 102 | ## stake(amount) 103 | Stakes tokens inside the stake contract 104 | 105 | **Kind**: global function 106 | 107 | | Param | Type | Description | 108 | | --- | --- | --- | 109 | | amount | Integer | Amount | 110 | 111 | 112 | 113 | ## approveStakeERC20(tokenAmount) 114 | Approve the stake to use approved tokens 115 | 116 | **Kind**: global function 117 | 118 | | Param | Type | 119 | | --- | --- | 120 | | tokenAmount | Integer | 121 | 122 | 123 | 124 | ## isApproved(tokenAmount, address) ⇒ Boolean 125 | Verify if the address has approved the staking to deposit 126 | 127 | **Kind**: global function 128 | 129 | | Param | Type | 130 | | --- | --- | 131 | | tokenAmount | Integer | 132 | | address | Address | 133 | 134 | 135 | 136 | ## getAPY() ⇒ Integer 137 | Returns the APY that this pool is giving 138 | 139 | **Kind**: global function 140 | 141 | 142 | ## withdraw(amount) 143 | Withdraw tokens from the stake contract 144 | 145 | **Kind**: global function 146 | 147 | | Param | Type | 148 | | --- | --- | 149 | | amount | Integer | 150 | 151 | 152 | 153 | ## withdrawAll() 154 | Withdraw all the tokens from the stake contract 155 | 156 | **Kind**: global function 157 | 158 | 159 | ## exit() 160 | Claims all the rewards and withdraws all the staked tokens 161 | 162 | **Kind**: global function 163 | 164 | 165 | ## claim() 166 | Claim rewards from the staking contract 167 | 168 | **Kind**: global function 169 | 170 | 171 | ## notifyRewardAmountSamePeriod(amount) 172 | add (more) rewards token to current/future period 173 | 174 | **Kind**: global function 175 | 176 | | Param | Type | 177 | | --- | --- | 178 | | amount | Integer | 179 | 180 | 181 | 182 | ## transferRewardTokenSamePeriod(amount) 183 | Transfer and add (more) rewards token to current/future period 184 | 185 | **Kind**: global function 186 | 187 | | Param | Type | 188 | | --- | --- | 189 | | amount | Integer | 190 | 191 | 192 | 193 | ## userAccumulatedRewards(address) ⇒ Integer 194 | Returns the accumulated rewards 195 | 196 | **Kind**: global function 197 | **Returns**: Integer - userAccumulatedRewards 198 | 199 | | Param | Type | 200 | | --- | --- | 201 | | address | string | 202 | 203 | 204 | 205 | ## recoverERC20(address) 206 | Emergency withdrawal of tokens 207 | 208 | **Kind**: global function 209 | 210 | | Param | Type | Description | 211 | | --- | --- | --- | 212 | | address | string | Token address | 213 | 214 | 215 | 216 | ## lastTimeRewardApplicable() ⇒ Date 217 | Get the last time rewards are applicable 218 | 219 | **Kind**: global function 220 | 221 | 222 | ## totalStaked() ⇒ Integer 223 | Returns the total stake 224 | 225 | **Kind**: global function 226 | **Returns**: Integer - totalStakeAmount 227 | 228 | 229 | ## balanceRewardsToken() ⇒ Integer 230 | substract staked amount if staked token is the same as rewards token 231 | 232 | **Kind**: global function 233 | **Returns**: Integer - totalRewardsAmount 234 | 235 | 236 | ## stakeAmount(address) ⇒ Integer 237 | Returns the stake amount for a wallet 238 | 239 | **Kind**: global function 240 | **Returns**: Integer - stakeAmount 241 | 242 | | Param | Type | 243 | | --- | --- | 244 | | address | string | 245 | 246 | 247 | 248 | ## setTokenSaleAddress(address) 249 | Sets the token sale address 250 | 251 | **Kind**: global function 252 | 253 | | Param | Type | 254 | | --- | --- | 255 | | address | string | 256 | 257 | -------------------------------------------------------------------------------- /tests/test/erc20.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | 3 | import chai from 'chai'; 4 | import { mochaAsync } from '../utils'; 5 | import moment, { isDate } from 'moment'; 6 | import Application from '../../src/models'; 7 | import delay from 'delay'; 8 | const ERC20TokenAddress = '0x7a7748bd6f9bac76c2f3fcb29723227e3376cbb2'; 9 | var contractAddress = '0x420751cdeb28679d8e336f2b4d1fc61df7439b5a'; 10 | var userPrivateKey = process.env.TEST_PRIVATE_KEY || '0x7f76de05082c4d578219ca35a905f8debe922f1f00b99315ebf0706afc97f132'; 11 | 12 | const expect = chai.expect; 13 | const tokenPurchaseAmount = 0.01; 14 | const tokenFundAmount = 0.03; 15 | const tradeValue = 0.01; 16 | 17 | context('ERC-20 Contract', async () => { 18 | var swapContract; 19 | var app; 20 | var isFunded, isSaleOpen, hasWhitelist, tokensLeft, indiviMinAmount, indivMaxAmount, cost, tokensAvailable 21 | 22 | before( async () => { 23 | app = new Application({test : true, mainnet : false}); 24 | }); 25 | 26 | it('should deploy Fixed Swap Contract', mochaAsync(async () => { 27 | 28 | app = new Application({test : true, mainnet : false}); 29 | /* Create Contract */ 30 | swapContract = await app.getFixedSwapContract({tokenAddress : ERC20TokenAddress, decimals : 18}); 31 | /* Deploy */ 32 | let res = await swapContract.deploy({ 33 | tradeValue : tradeValue, 34 | tokensForSale : tokenFundAmount, 35 | isTokenSwapAtomic : true, 36 | individualMaximumAmount : tokenFundAmount, 37 | startDate : moment().add(4, 'minutes'), 38 | endDate : moment().add(8, 'minutes'), 39 | hasWhitelisting : false, 40 | isETHTrade : false, 41 | ERC20TradingAddress: "0x59dd38615070ac185583a9a43059aa833685d49d", 42 | isPOLSWhitelist: false, 43 | tradingDecimals: 18, 44 | vestingTime: 1, 45 | vestingSchedule: [100] 46 | }); 47 | contractAddress = swapContract.getAddress(); 48 | expect(res).to.not.equal(false); 49 | })); 50 | 51 | it('should get a Fixed Swap Contract From contractAddress', mochaAsync(async () => { 52 | 53 | /* Get Contract */ 54 | swapContract = await app.getFixedSwapContract({contractAddress}); 55 | swapContract.__init__(); 56 | await swapContract.assertERC20Info(); 57 | expect(swapContract).to.not.equal(false); 58 | })); 59 | 60 | it('GET - isPreFunded', mochaAsync(async () => { 61 | let res = await swapContract.isPreStart(); 62 | expect(res).to.equal(true); 63 | })); 64 | 65 | it('GET - tokensAllocated', mochaAsync(async () => { 66 | let tokens = await swapContract.tokensAllocated(); 67 | expect(tokens).to.equal(Number(0).noExponents()); 68 | })); 69 | 70 | it('GET - tradeValue', mochaAsync(async () => { 71 | let td = await swapContract.tradeValue(); 72 | expect(td).to.equal(Number(tradeValue).noExponents()); 73 | })); 74 | 75 | it('GET - tokensAvailable', mochaAsync(async () => { 76 | let tokens = await swapContract.tokensAvailable(); 77 | expect(tokens).to.equal(Number(0).noExponents()); 78 | })); 79 | 80 | it('GET - owner', mochaAsync(async () => { 81 | let res = await swapContract.owner(); 82 | expect(res).to.equal('0xe797860acFc4e06C1b2B96197a7dB1dFa518d5eB'); 83 | })); 84 | 85 | it('GET - tokensForSale', mochaAsync(async () => { 86 | let tokens = await swapContract.tokensForSale(); 87 | expect(Number(tokens).noExponents()).to.equal(Number(tokenFundAmount).noExponents()); 88 | })); 89 | 90 | it('GET - tokensLeft', mochaAsync(async () => { 91 | let tokens = await swapContract.tokensLeft(); 92 | tokensLeft = tokens; 93 | expect(Number(tokens).noExponents()).to.equal(Number(tokenFundAmount).noExponents()); 94 | })); 95 | 96 | it('should fund a Swap Contract and confirm balances', mochaAsync(async () => { 97 | /* Approve ERC20 Fund */ 98 | let res = await swapContract.approveFundERC20({tokenAmount : tokenFundAmount}); 99 | expect(res).to.not.equal(false); 100 | res = await swapContract.isApproved({address : app.account.getAddress(), tokenAmount : tokenFundAmount}); 101 | expect(res).to.equal(true); 102 | /* Fund */ 103 | res = await swapContract.hasStarted(); 104 | expect(res).to.equal(false); 105 | res = await swapContract.fund({tokenAmount : tokenFundAmount}); 106 | expect(res).to.not.equal(false); 107 | })); 108 | 109 | 110 | it('GET - tokensAvailable', mochaAsync(async () => { 111 | let tokens = await swapContract.tokensAvailable(); 112 | expect(tokens).to.equal(Number(tokenFundAmount).noExponents()); 113 | })); 114 | 115 | it('GET - isFunded', mochaAsync(async () => { 116 | let res = await swapContract.isFunded(); 117 | isFunded = res; 118 | expect(res).to.equal(true); 119 | })); 120 | 121 | it('GET - isSaleOpen - before Start', mochaAsync(async () => { 122 | await delay(2*60*1000); 123 | let res = await swapContract.isOpen(); 124 | isSaleOpen = res; 125 | expect(res).to.equal(true); 126 | })); 127 | 128 | it('GET - tokensAvailable after fund', mochaAsync(async () => { 129 | let tokens = await swapContract.tokensAvailable(); 130 | expect(tokens).to.equal(Number(tokens).noExponents()); 131 | })); 132 | 133 | it('should approve ERC20 swap', mochaAsync(async () => { 134 | await delay(15*1000); 135 | let res = await swapContract.approveSwapERC20({tokenAmount : tokenPurchaseAmount}); 136 | expect(res).to.not.equal(false); 137 | })); 138 | 139 | it('should do a non atomic swap on the Contract', mochaAsync(async () => { 140 | await delay(15*1000); 141 | let res = await swapContract.swap({tokenAmount : tokenPurchaseAmount}); 142 | expect(res).to.not.equal(false); 143 | })); 144 | 145 | it('GET - Purchases', mochaAsync(async () => { 146 | let purchases = await swapContract.getPurchaseIds(); 147 | expect(purchases.length).to.equal(1); 148 | })); 149 | 150 | it('GET - My Purchases', mochaAsync(async () => { 151 | let purchases = await swapContract.getAddressPurchaseIds({address : app.account.getAddress()}); 152 | expect(purchases.length).to.equal(1); 153 | })); 154 | 155 | it('GET - Purchase ID', mochaAsync(async () => { 156 | let purchases = await swapContract.getAddressPurchaseIds({address : app.account.getAddress()}); 157 | let purchase = await swapContract.getPurchase({purchase_id : purchases[0]}); 158 | const amountPurchase = Number(purchase.amount).noExponents(); 159 | expect(Number(amountPurchase).toFixed(2)).to.equal(Number(tokenPurchaseAmount).noExponents()); 160 | expect(purchase.purchaser).to.equal(app.account.getAddress()); 161 | expect(purchase.wasFinalized).to.equal(true); 162 | expect(purchase.reverted).to.equal(false); 163 | })); 164 | 165 | it('GET - tokensAvailable after Swap', mochaAsync(async () => { 166 | let tokens = await swapContract.tokensAvailable(); 167 | tokens = Number(tokens).noExponents(); 168 | tokensAvailable = Number(tokenFundAmount-tokenPurchaseAmount).noExponents(); 169 | expect(Number(tokens).toFixed(2)).to.equal(Number(tokensAvailable).toFixed(2)); 170 | })); 171 | 172 | it('GET - Buyers', mochaAsync(async () => { 173 | let buyers = await swapContract.getBuyers(); 174 | expect(buyers.length).to.equal(1); 175 | })); 176 | 177 | it('GET - Fixed Swap is Closed', mochaAsync(async () => { 178 | await delay(4*60*1000); 179 | let res = await swapContract.hasFinalized(); 180 | expect(res).to.equal(true); 181 | res = await swapContract.isOpen(); 182 | expect(res).to.equal(false); 183 | })); 184 | 185 | it('GET - tokensAvailable after closed', mochaAsync(async () => { 186 | let res = await swapContract.tokensAvailable(); 187 | res = Number(res).noExponents() 188 | expect(Number(res).toFixed(2)).to.equal(Number(tokensAvailable).toFixed(2)); 189 | })); 190 | 191 | it('Remove Tokens From Purchases - Admin', mochaAsync(async () => { 192 | let res = await swapContract.withdrawFunds(); 193 | expect(res).to.not.equal(false); 194 | })); 195 | 196 | it('Remove Unsold Tokens - Admin', mochaAsync(async () => { 197 | let res = await swapContract.withdrawUnsoldTokens(); 198 | expect(res).to.not.equal(false); 199 | })); 200 | 201 | it('should deploy Fixed Swap Contract - isPOLSWhitelisted', mochaAsync(async () => { 202 | /* 203 | It is necessary to create in the future, as it is necessary to simulate a "uniswap" 204 | with the entire structure of the kovan network. 205 | */ 206 | })); 207 | }); 208 | -------------------------------------------------------------------------------- /docs/NFT_SWAP.md: -------------------------------------------------------------------------------- 1 | ## Classes 2 | 3 |
4 |
FixedNFTSwapContractBaseSwapContract
5 |
6 |
7 | 8 | ## Functions 9 | 10 |
11 |
deploy(startDate, endDate, distributionDate, [individualMaximumAmount], [minimumRaise], [feePercentage], [hasWhitelisting], [ERC20TradingAddress], categoryIds, categoriesSupply, categoriesPrice, [tradingDecimals])
12 |

Deploy the NFT swap contract

13 |
14 |
swap(tokenAmount, categoryId, maxAllocation, [signature])
15 |

Swap tokens by Ethereum or ERC20

16 |
17 |
setDistributionDate(distributionDate)admin
18 |

Modifies the distribution date for the pool

19 |
20 |
distributionDate()Date
21 |

Get Distribution Date of NFT

22 |
23 |
hasDistributed()Boolean
24 |

Verify if the NFTs are up for distribution, if the current date is after distributionDate

25 |
26 |
tokensForSale(categoryId)Integer
27 |

Get Total tokens for sale by category

28 |
29 |
soldByCategoryId(categoryId)Integer
30 |

Get Total tokens for sold by category

31 |
32 |
tokensLeft(categoryId)Integer
33 |

Get Total tokens owned by category

34 |
35 |
totalCost()Integer
36 |

Get Total cost for buying all the nfts

37 |
38 |
getCost(amount, categoryId)Integer
39 |

Get Cost for category and amount

40 |
41 |
safePullTradeToken()
42 |

Safe Pull all trading tokens

43 |
44 |
getUserPurchases(address)Array.<Object>
45 |
46 |
getPurchase(purchaseId)Integer | Integer | Integer | Integer | Address | Date | Boolean
47 |

Get Purchase based on ID

48 |
49 |
getBuyers()Array | Integer
50 |

Get Buyers

51 |
52 |
getPurchaseIds()Array | Integer
53 |

Get All Purchase Ids

54 |
55 |
getPurchaseIds(address)Array | Integer
56 |

Get All Purchase Ids filter by Address/Purchaser

57 |
58 |
getIsClaimedCategoryForUser(address, categoryId)boolean
59 |
60 |
setUserClaimedCategory(address, categoryId)admin
61 |

Sets user claimed category

62 |
63 |
categoryIds()Array.<Number>
64 |
65 |
setCategories(categoryIds, categoriesSupply, categoriesPrice, tradingDecimals)admin
66 |

Modifies the categories oon the contract

67 |
68 |
69 | 70 | 71 | 72 | ## FixedNFTSwapContract ⇐ BaseSwapContract 73 | **Kind**: global class 74 | **Extends**: BaseSwapContract 75 | 76 | 77 | ### new FixedNFTSwapContract(web3, contractAddress) 78 | Fixed NFT Swap Object 79 | 80 | 81 | | Param | Type | Description | 82 | | --- | --- | --- | 83 | | web3 | Web3 | | 84 | | contractAddress | Address | ? (opt) | 85 | 86 | 87 | 88 | ## deploy(startDate, endDate, distributionDate, [individualMaximumAmount], [minimumRaise], [feePercentage], [hasWhitelisting], [ERC20TradingAddress], categoryIds, categoriesSupply, categoriesPrice, [tradingDecimals]) 89 | Deploy the NFT swap contract 90 | 91 | **Kind**: global function 92 | 93 | | Param | Type | Description | 94 | | --- | --- | --- | 95 | | startDate | String | Start date | 96 | | endDate | String | End date | 97 | | distributionDate | String | Distribution date | 98 | | [individualMaximumAmount] | Float | Max cap per wallet. 0 to disable it. (Default: 0) | 99 | | [minimumRaise] | Float | Soft cap (Default: 0) | 100 | | [feePercentage] | Float | Fee percentage (Default: 1) | 101 | | [hasWhitelisting] | Boolean | Has White Listing. (Default: false) | 102 | | [ERC20TradingAddress] | String | Token to use in the swap (Default: 0x0000000000000000000000000000000000000000) | 103 | | categoryIds | Array.<Number> | Ids of the NFT categories | 104 | | categoriesSupply | Array.<Number> | Supply of every category of NFT in same order than Ids | 105 | | categoriesPrice | Array.<Float> | Price per unit of a category item, in same order than Ids | 106 | | [tradingDecimals] | Number | To be the decimals of the currency in case (ex : USDT -> 9; ETH -> 18) (Default: 0) | 107 | 108 | 109 | 110 | ## swap(tokenAmount, categoryId, maxAllocation, [signature]) 111 | Swap tokens by Ethereum or ERC20 112 | 113 | **Kind**: global function 114 | 115 | | Param | Type | Description | 116 | | --- | --- | --- | 117 | | tokenAmount | Integer | | 118 | | categoryId | Integer | | 119 | | maxAllocation | Integer | | 120 | | [signature] | string | Signature for the offchain whitelist | 121 | 122 | 123 | 124 | ## setDistributionDate(distributionDate) ⇒ admin 125 | Modifies the distribution date for the pool 126 | 127 | **Kind**: global function 128 | 129 | | Param | Type | 130 | | --- | --- | 131 | | distributionDate | Date | 132 | 133 | 134 | 135 | ## distributionDate() ⇒ Date 136 | Get Distribution Date of NFT 137 | 138 | **Kind**: global function 139 | 140 | 141 | ## hasDistributed() ⇒ Boolean 142 | Verify if the NFTs are up for distribution, if the current date is after distributionDate 143 | 144 | **Kind**: global function 145 | 146 | 147 | ## tokensForSale(categoryId) ⇒ Integer 148 | Get Total tokens for sale by category 149 | 150 | **Kind**: global function 151 | **Returns**: Integer - Amount in Tokens 152 | 153 | | Param | Type | 154 | | --- | --- | 155 | | categoryId | Integer | 156 | 157 | 158 | 159 | ## soldByCategoryId(categoryId) ⇒ Integer 160 | Get Total tokens for sold by category 161 | 162 | **Kind**: global function 163 | **Returns**: Integer - Amount in Tokens 164 | 165 | | Param | Type | 166 | | --- | --- | 167 | | categoryId | Integer | 168 | 169 | 170 | 171 | ## tokensLeft(categoryId) ⇒ Integer 172 | Get Total tokens owned by category 173 | 174 | **Kind**: global function 175 | **Returns**: Integer - Amount in Tokens 176 | 177 | | Param | Type | 178 | | --- | --- | 179 | | categoryId | Integer | 180 | 181 | 182 | 183 | ## totalCost() ⇒ Integer 184 | Get Total cost for buying all the nfts 185 | 186 | **Kind**: global function 187 | **Returns**: Integer - Amount in Tokens 188 | 189 | 190 | ## getCost(amount, categoryId) ⇒ Integer 191 | Get Cost for category and amount 192 | 193 | **Kind**: global function 194 | **Returns**: Integer - costAmount 195 | 196 | | Param | Type | 197 | | --- | --- | 198 | | amount | Integer | 199 | | categoryId | Integer | 200 | 201 | 202 | 203 | ## safePullTradeToken() 204 | Safe Pull all trading tokens 205 | 206 | **Kind**: global function 207 | 208 | 209 | ## getUserPurchases(address) ⇒ Array.<Object> 210 | **Kind**: global function 211 | **Returns**: Array.<Object> - purchases 212 | 213 | | Param | Type | 214 | | --- | --- | 215 | | address | Address | 216 | 217 | 218 | 219 | ## getPurchase(purchaseId) ⇒ Integer \| Integer \| Integer \| Integer \| Address \| Date \| Boolean 220 | Get Purchase based on ID 221 | 222 | **Kind**: global function 223 | **Returns**: Integer - _idInteger - categoryIdInteger - amountInteger - amountContributedAddress - purchaserDate - timestampBoolean - reverted 224 | 225 | | Param | Type | 226 | | --- | --- | 227 | | purchaseId | Integer | 228 | 229 | 230 | 231 | ## getBuyers() ⇒ Array \| Integer 232 | Get Buyers 233 | 234 | **Kind**: global function 235 | **Returns**: Array \| Integer - _ids 236 | 237 | 238 | ## getPurchaseIds() ⇒ Array \| Integer 239 | Get All Purchase Ids 240 | 241 | **Kind**: global function 242 | **Returns**: Array \| Integer - _ids 243 | 244 | 245 | ## getPurchaseIds(address) ⇒ Array \| Integer 246 | Get All Purchase Ids filter by Address/Purchaser 247 | 248 | **Kind**: global function 249 | **Returns**: Array \| Integer - _ids 250 | 251 | | Param | Type | 252 | | --- | --- | 253 | | address | Address | 254 | 255 | 256 | 257 | ## getIsClaimedCategoryForUser(address, categoryId) ⇒ boolean 258 | **Kind**: global function 259 | **Returns**: boolean - claimed 260 | 261 | | Param | Type | 262 | | --- | --- | 263 | | address | Address | 264 | | categoryId | Number | 265 | 266 | 267 | 268 | ## setUserClaimedCategory(address, categoryId) ⇒ admin 269 | Sets user claimed category 270 | 271 | **Kind**: global function 272 | 273 | | Param | Type | 274 | | --- | --- | 275 | | address | Address | 276 | | categoryId | Number | 277 | 278 | 279 | 280 | ## categoryIds() ⇒ Array.<Number> 281 | **Kind**: global function 282 | **Returns**: Array.<Number> - an array containig all category ids 283 | 284 | 285 | ## setCategories(categoryIds, categoriesSupply, categoriesPrice, tradingDecimals) ⇒ admin 286 | Modifies the categories oon the contract 287 | 288 | **Kind**: global function 289 | 290 | | Param | Type | Description | 291 | | --- | --- | --- | 292 | | categoryIds | Array.<Number> | Ids of the NFT categories | 293 | | categoriesSupply | Array.<Number> | Supply of every category of NFT in same order than Ids | 294 | | categoriesPrice | Array.<Float> | Price per unit of a category item, in same order than Ids | 295 | | tradingDecimals | Number | To be the decimals of the currency in case (ex : USDT -> 9; ETH -> 18) | 296 | 297 | -------------------------------------------------------------------------------- /src/models/contracts/IDOStaking.js: -------------------------------------------------------------------------------- 1 | import Contract from "../base/Contract"; 2 | import { idostaking } from "../../interfaces"; 3 | import Numbers from "../../utils/Numbers"; 4 | import ERC20TokenContract from "../base/ERC20TokenContract"; 5 | import Client from "../../utils/Client"; 6 | 7 | /** 8 | * IDO Staking Object 9 | * @constructor IDOStaking 10 | * @param {Web3} web3 11 | * @param {string} contractAddress The staking contract address. 12 | * @param {Account} acc 13 | */ 14 | class IDOStaking { 15 | 16 | constructor({ 17 | web3, 18 | contractAddress, 19 | acc, 20 | }) { 21 | if (!web3) { 22 | throw new Error("Please provide a valid web3 provider"); 23 | } 24 | this.web3 = web3; 25 | this.version = "2.0"; 26 | if (acc) { 27 | this.acc = acc; 28 | } 29 | 30 | this.params = { 31 | web3: web3, 32 | contractAddress: contractAddress, 33 | contract: new Contract(web3, idostaking, contractAddress), 34 | }; 35 | this.client = new Client(); 36 | } 37 | 38 | /** 39 | * @function deploy 40 | * @description Deploys the IDO Staking contracts 41 | * @param {string} owner Address of the owner 42 | * @param {string} rewardsDistribution Address of the distributor 43 | * @param {string} rewardsToken Address of the token we want to reward 44 | * @param {string} stakingToken Address of the token to be staked 45 | * @param {Integer} rewardsDuration Duration of the rewards 46 | * @param {string} tokenSaleAddress Address of the pool 47 | * @returns {string} address The deployed contract address 48 | */ 49 | deploy = async ({ 50 | owner, 51 | rewardsDistribution, 52 | rewardsToken, 53 | stakingToken, 54 | rewardsDuration, 55 | tokenSaleAddress = '0x0000000000000000000000000000000000000000', 56 | callback 57 | }) => { 58 | const params = [ 59 | owner, 60 | rewardsDistribution, 61 | rewardsToken, 62 | stakingToken, 63 | rewardsDuration, 64 | tokenSaleAddress 65 | ]; 66 | const res = await this.__deploy(params, callback); 67 | this.params.contractAddress = res.contractAddress; 68 | return res.contractAddress; 69 | } 70 | 71 | __deploy = async (params, callback) => { 72 | return await this.params.contract.deploy( 73 | this.acc, 74 | this.params.contract.getABI(), 75 | this.params.contract.getJSON().bytecode, 76 | params, 77 | callback 78 | ); 79 | }; 80 | 81 | /** 82 | * @function stake 83 | * @description Stakes tokens inside the stake contract 84 | * @param {Integer} amount Amount 85 | */ 86 | stake = async ({ amount }) => { 87 | amount = Numbers.toSmartContractDecimals( 88 | amount, 89 | await this.getDecimals() 90 | ) 91 | try { 92 | return await this.client.sendTx( 93 | this.params.web3, 94 | this.acc, 95 | this.params.contract, 96 | this.params.contract 97 | .getContract() 98 | .methods.stake(amount) 99 | ); 100 | } catch (err) { 101 | throw err; 102 | } 103 | }; 104 | 105 | /** 106 | * @function approveStakeERC20 107 | * @param {Integer} tokenAmount 108 | * @description Approve the stake to use approved tokens 109 | */ 110 | approveStakeERC20 = async ({ tokenAmount, callback }) => { 111 | return await (await this.getTokenContract()).approve({ 112 | address: this.params.contractAddress, 113 | amount: tokenAmount, 114 | callback 115 | }); 116 | }; 117 | 118 | /** 119 | * @function isApproved 120 | * @description Verify if the address has approved the staking to deposit 121 | * @param {Integer} tokenAmount 122 | * @param {Address} address 123 | * @returns {Boolean} 124 | */ 125 | isApproved = async ({ tokenAmount, address }) => { 126 | return await (await this.getTokenContract()).isApproved({ 127 | address: address, 128 | amount: tokenAmount, 129 | spenderAddress: this.params.contractAddress 130 | }); 131 | }; 132 | 133 | /** 134 | * @function getAPY 135 | * @description Returns the APY that this pool is giving 136 | * @returns {Integer} 137 | */ 138 | getAPY = async () => { 139 | const oneYear = 31556952; 140 | const duration = await this.params.contract 141 | .getContract() 142 | .methods.rewardsDuration() 143 | .call(); 144 | const rewardPerToken = await this.params.contract 145 | .getContract() 146 | .methods.rewardPerToken() 147 | .call(); 148 | 149 | return parseInt((parseInt(rewardPerToken) * 100) / (parseInt(duration) / oneYear)); 150 | } 151 | 152 | /** 153 | * @function withdraw 154 | * @param {Integer} amount 155 | * @description Withdraw tokens from the stake contract 156 | */ 157 | withdraw = async ({amount}) => { 158 | try { 159 | return await this.client.sendTx( 160 | this.params.web3, 161 | this.acc, 162 | this.params.contract, 163 | this.params.contract 164 | .getContract() 165 | .methods.withdraw(Numbers.toSmartContractDecimals( 166 | amount, 167 | await this.getDecimals() 168 | )) 169 | ); 170 | } catch (err) { 171 | throw err; 172 | } 173 | }; 174 | 175 | /** 176 | * @function withdrawAll 177 | * @description Withdraw all the tokens from the stake contract 178 | */ 179 | withdrawAll = async () => { 180 | try { 181 | return await this.client.sendTx( 182 | this.params.web3, 183 | this.acc, 184 | this.params.contract, 185 | this.params.contract 186 | .getContract() 187 | .methods.withdrawAll() 188 | ); 189 | } catch (err) { 190 | throw err; 191 | } 192 | }; 193 | 194 | /** 195 | * @function exit 196 | * @description Claims all the rewards and withdraws all the staked tokens 197 | */ 198 | exit = async () => { 199 | try { 200 | return await this.client.sendTx( 201 | this.params.web3, 202 | this.acc, 203 | this.params.contract, 204 | this.params.contract 205 | .getContract() 206 | .methods.exit() 207 | ); 208 | } catch (err) { 209 | throw err; 210 | } 211 | }; 212 | 213 | /** 214 | * @function claim 215 | * @description Claim rewards from the staking contract 216 | */ 217 | claim = async () => { 218 | try { 219 | return await this.client.sendTx( 220 | this.params.web3, 221 | this.acc, 222 | this.params.contract, 223 | this.params.contract 224 | .getContract() 225 | .methods.getReward() 226 | ); 227 | } catch (err) { 228 | throw err; 229 | } 230 | }; 231 | 232 | /** 233 | * @function notifyRewardAmountSamePeriod 234 | * @description add (more) rewards token to current/future period 235 | * @param {Integer} amount 236 | */ 237 | notifyRewardAmountSamePeriod = async ({reward}) => { 238 | try { 239 | const amount = Numbers.toSmartContractDecimals( 240 | reward, 241 | await this.getRewardsDecimals() 242 | ); 243 | return await this.client.sendTx( 244 | this.params.web3, 245 | this.acc, 246 | this.params.contract, 247 | this.params.contract 248 | .getContract() 249 | .methods.notifyRewardAmountSamePeriod(amount) 250 | ); 251 | } catch (err) { 252 | throw err; 253 | } 254 | }; 255 | 256 | /** 257 | * @function transferRewardTokenSamePeriod 258 | * @description Transfer and add (more) rewards token to current/future period 259 | * @param {Integer} amount 260 | */ 261 | transferRewardTokenSamePeriod = async ({reward}) => { 262 | try { 263 | const amount = Numbers.toSmartContractDecimals( 264 | reward, 265 | await this.getRewardsDecimals() 266 | ); 267 | return await this.client.sendTx( 268 | this.params.web3, 269 | this.acc, 270 | this.params.contract, 271 | this.params.contract 272 | .getContract() 273 | .methods.transferRewardTokenSamePeriod(amount) 274 | ); 275 | } catch (err) { 276 | throw err; 277 | } 278 | }; 279 | 280 | /** 281 | * @function userAccumulatedRewards 282 | * @description Returns the accumulated rewards 283 | * @param {string} address 284 | * @returns {Integer} userAccumulatedRewards 285 | */ 286 | userAccumulatedRewards = async ({address}) => { 287 | return Numbers.fromDecimals( 288 | await this.params.contract.getContract().methods.earned(address).call(), 289 | await this.getRewardsDecimals(), 290 | ); 291 | } 292 | 293 | /** 294 | * @function recoverERC20 295 | * @description Emergency withdrawal of tokens 296 | * @param {string} address Token address 297 | */ 298 | recoverERC20 = async ({address}) => { 299 | await this.client.sendTx( 300 | this.params.web3, 301 | this.acc, 302 | this.params.contract, 303 | this.params.contract 304 | .getContract() 305 | .methods.recoverERC20(address) 306 | ); 307 | } 308 | 309 | /** 310 | * @function lastTimeRewardApplicable 311 | * @description Get the last time rewards are applicable 312 | * @returns {Date} 313 | */ 314 | async lastTimeRewardApplicable() { 315 | return Numbers.fromSmartContractTimeToMinutes( 316 | await this.params.contract.getContract().methods.lastTimeRewardApplicable().call() 317 | ); 318 | } 319 | 320 | 321 | /** 322 | * @function totalStaked 323 | * @description Returns the total stake 324 | * @returns {Integer} totalStakeAmount 325 | */ 326 | totalStaked = async () => { 327 | return Numbers.fromDecimals( 328 | await this.params.contract.getContract().methods.totalSupply().call(), 329 | await this.getDecimals() 330 | ); 331 | } 332 | 333 | /** 334 | * @function balanceRewardsToken 335 | * @description substract staked amount if staked token is the same as rewards token 336 | * @returns {Integer} totalRewardsAmount 337 | */ 338 | balanceRewardsToken = async () => { 339 | return Numbers.fromDecimals( 340 | await this.params.contract.getContract().methods.balanceRewardsToken().call(), 341 | await this.getDecimals() 342 | ); 343 | } 344 | 345 | /** 346 | * @function stakeAmount 347 | * @description Returns the stake amount for a wallet 348 | * @param {string} address 349 | * @returns {Integer} stakeAmount 350 | */ 351 | stakeAmount = async ({address}) => { 352 | return Numbers.fromDecimals( 353 | await this.params.contract.getContract().methods.balanceOf(address).call(), 354 | await this.getDecimals() 355 | ); 356 | } 357 | 358 | /** 359 | * @function setTokenSaleAddress 360 | * @description Sets the token sale address 361 | * @param {string} address 362 | */ 363 | setTokenSaleAddress = async ({address}) => { 364 | try { 365 | await this.client.sendTx( 366 | this.params.web3, 367 | this.acc, 368 | this.params.contract, 369 | this.params.contract 370 | .getContract() 371 | .methods.setTokenSaleAddress(address) 372 | ); 373 | return true; 374 | } catch (err) { 375 | throw err; 376 | } 377 | }; 378 | 379 | getDecimals = async () => { 380 | return await (await this.getTokenContract()).getDecimals(); 381 | } 382 | 383 | getTokenContract = async () => { 384 | if (!this.params.erc20TokenContract) { 385 | this.params.erc20TokenContract = new ERC20TokenContract({ 386 | web3: this.params.web3, 387 | contractAddress: await this.params.contract.getContract().methods.stakingToken().call(), 388 | acc: this.acc 389 | }); 390 | } 391 | return this.params.erc20TokenContract; 392 | } 393 | 394 | getRewardsDecimals = async () => { 395 | return await (await this.getRewardsTokenContract()).getDecimals(); 396 | } 397 | 398 | getRewardsTokenContract = async () => { 399 | if (!this.params.erc20TokenRewardsContract) { 400 | this.params.erc20TokenRewardsContract = new ERC20TokenContract({ 401 | web3: this.params.web3, 402 | contractAddress: await this.params.contract.getContract().methods.rewardsToken().call(), 403 | acc: this.acc 404 | }); 405 | } 406 | return this.params.erc20TokenRewardsContract; 407 | } 408 | 409 | } 410 | 411 | export default IDOStaking; 412 | -------------------------------------------------------------------------------- /src/models/contracts/FixedNFTSwapContract.js: -------------------------------------------------------------------------------- 1 | import { fixednftswap } from "../../interfaces"; 2 | import Numbers from "../../utils/Numbers"; 3 | import _ from "lodash"; 4 | import moment from 'moment'; 5 | import BaseSwapContract from './base/BaseSwapContract'; 6 | import DeploymentService from "../../services/DeploymentService"; 7 | import ERC20TokenContract from "../base/ERC20TokenContract"; 8 | 9 | /** 10 | * Fixed NFT Swap Object 11 | * @constructor FixedNFTSwapContract 12 | * @param {Web3} web3 13 | * @param {Address} contractAddress ? (opt) 14 | * @extends BaseSwapContract 15 | */ 16 | class FixedNFTSwapContract extends BaseSwapContract { 17 | constructor({ 18 | web3, 19 | contractAddress = null /* If not deployed */, 20 | acc, 21 | }) { 22 | super({ web3, contractAddress, acc, contractInterface: fixednftswap }); 23 | } 24 | 25 | /** 26 | * 27 | * @function deploy 28 | * @description Deploy the NFT swap contract 29 | * @param {String} startDate Start date 30 | * @param {String} endDate End date 31 | * @param {String} distributionDate Distribution date 32 | * @param {Float=} individualMaximumAmount Max cap per wallet. 0 to disable it. (Default: 0) 33 | * @param {Float=} minimumRaise Soft cap (Default: 0) 34 | * @param {Float=} feePercentage Fee percentage (Default: 1) 35 | * @param {Boolean=} hasWhitelisting Has White Listing. (Default: false) 36 | * @param {String=} ERC20TradingAddress Token to use in the swap (Default: 0x0000000000000000000000000000000000000000) 37 | * @param {Number[]} categoryIds Ids of the NFT categories 38 | * @param {Number[]} categoriesSupply Supply of every category of NFT in same order than Ids 39 | * @param {Float[]} categoriesPrice Price per unit of a category item, in same order than Ids 40 | * @param {Number=} tradingDecimals To be the decimals of the currency in case (ex : USDT -> 9; ETH -> 18) (Default: 0) 41 | 42 | */ 43 | 44 | deploy = async ({ 45 | startDate, 46 | endDate, 47 | distributionDate, 48 | individualMaximumAmount = 0, 49 | minimumRaise = 0, 50 | feePercentage = 1, 51 | hasWhitelisting = false, 52 | ERC20TradingAddress = '0x0000000000000000000000000000000000000000', 53 | categoryIds, 54 | categoriesSupply, 55 | categoriesPrice, 56 | tradingDecimals = 0, /* To be the decimals of the currency in case (ex : USDT -> 9; ETH -> 18) */ 57 | callback 58 | }) => { 59 | if (feePercentage < 1) { 60 | throw new Error("Fee Amount has to be >= 1"); 61 | } 62 | 63 | if (ERC20TradingAddress != '0x0000000000000000000000000000000000000000' && (tradingDecimals == 0)) { 64 | throw new Error("If an ERC20 Trading Address please add the 'tradingDecimals' field to the trading address (Ex : USDT -> 6)"); 65 | } else { 66 | /* is ETH Trade */ 67 | tradingDecimals = 18; 68 | } 69 | 70 | if (ERC20TradingAddress) { 71 | this.params.erc20TokenContract = new ERC20TokenContract({ 72 | web3: this.web3, 73 | contractAddress: ERC20TradingAddress, 74 | acc: this.acc 75 | }); 76 | } 77 | 78 | let totalRaise = 0; 79 | let finalcategoriesPrice = []; 80 | for (let i = 0; i < categoriesSupply.length; i++) { 81 | totalRaise += categoriesSupply[i] * categoriesPrice[i]; 82 | finalcategoriesPrice[i] = Numbers.toSmartContractDecimals( 83 | categoriesPrice[i], 84 | tradingDecimals 85 | ) 86 | }; 87 | if (minimumRaise != 0 && (minimumRaise > totalRaise)) { 88 | throw new Error("Minimum Raise has to be smaller than total Raise") 89 | } 90 | if (Date.parse(startDate) >= Date.parse(endDate)) { 91 | throw new Error("Start Date has to be smaller than End Date") 92 | } 93 | if (Date.parse(endDate) >= Date.parse(distributionDate)) { 94 | throw new Error("End Date has to be smaller than Distribution Date") 95 | } 96 | if (Date.parse(startDate) <= Date.parse(moment(Date.now()).add(2, 'm').toDate())) { 97 | throw new Error("Start Date has to be higher (at least 2 minutes) than now") 98 | } 99 | if (individualMaximumAmount < 0) { 100 | throw new Error("Individual Maximum Amount should be bigger than 0") 101 | } 102 | 103 | if (individualMaximumAmount > 0) { 104 | /* If exists individualMaximumAmount */ 105 | if (individualMaximumAmount > totalRaise) { 106 | throw new Error("Individual Maximum Amount should be smaller than total Tokens For Sale") 107 | } 108 | } 109 | 110 | if (individualMaximumAmount == 0) { 111 | individualMaximumAmount = totalRaise; /* Set Max Amount to Unlimited if 0 */ 112 | } 113 | 114 | let params = [ 115 | Numbers.timeToSmartContractTime(startDate), 116 | Numbers.timeToSmartContractTime(endDate), 117 | Numbers.timeToSmartContractTime(distributionDate), 118 | Numbers.toSmartContractDecimals( 119 | individualMaximumAmount, 120 | await this.getTradingDecimals() 121 | ), 122 | Numbers.toSmartContractDecimals(minimumRaise, await this.getTradingDecimals()), 123 | parseInt(feePercentage), 124 | hasWhitelisting, 125 | ERC20TradingAddress, 126 | categoryIds, 127 | categoriesSupply, 128 | finalcategoriesPrice 129 | ]; 130 | let res = await new DeploymentService().deploy( 131 | this.acc, 132 | this.params.contract, 133 | params, 134 | callback 135 | ); 136 | this.params.contractAddress = res.contractAddress; 137 | /* Call to Backend API */ 138 | 139 | this.__assert(); 140 | return res; 141 | }; 142 | 143 | /** 144 | * @function swap 145 | * @description Swap tokens by Ethereum or ERC20 146 | * @param {Integer} tokenAmount 147 | * @param {Integer} categoryId 148 | * @param {Integer} maxAllocation 149 | * @param {string=} signature Signature for the offchain whitelist 150 | */ 151 | 152 | swap = async ({ tokenAmount, categoryId, maxAllocation, callback, signature }) => { 153 | 154 | let cost = await this.getCost({ 155 | amount: tokenAmount, 156 | categoryId 157 | }); 158 | 159 | let costToDecimals = Numbers.toSmartContractDecimals(cost, await this.getTradingDecimals()); 160 | 161 | if (!signature) { 162 | signature = '0x00'; 163 | } 164 | 165 | return await this.executeContractMethod( 166 | this.getContractMethods().swapWithSig(tokenAmount, categoryId, maxAllocation, signature), 167 | false, 168 | await this.isETHTrade() ? costToDecimals : 0, 169 | callback 170 | ); 171 | }; 172 | 173 | /************************************** 174 | * DATE METHODS 175 | **************************************/ 176 | 177 | /** 178 | * @function setDistributionDate 179 | * @type admin 180 | * @param {Date} distributionDate 181 | * @description Modifies the distribution date for the pool 182 | */ 183 | setDistributionDate = async ({ distributionDate }) => { 184 | return await this.executeContractMethod( 185 | this.getContractMethods().setDistributionDate(Numbers.timeToSmartContractTime(distributionDate)) 186 | ); 187 | } 188 | 189 | /** 190 | * @function distributionDate 191 | * @description Get Distribution Date of NFT 192 | * @returns {Date} 193 | */ 194 | async distributionDate() { 195 | return Numbers.fromSmartContractTimeToMinutes( 196 | await this.getContractMethods().distributionDate().call() 197 | ); 198 | } 199 | 200 | /** 201 | * @function hasDistributed 202 | * @description Verify if the NFTs are up for distribution, if the current date is after distributionDate 203 | * @returns {Boolean} 204 | */ 205 | async hasDistributed() { 206 | return await this.params.contract 207 | .getContract() 208 | .methods.hasDistributed() 209 | .call(); 210 | } 211 | 212 | /************************************** 213 | * TOKEN METHODS 214 | **************************************/ 215 | 216 | /** 217 | * @function tokensForSale 218 | * @description Get Total tokens for sale by category 219 | * @param {Integer} categoryId 220 | * @returns {Integer} Amount in Tokens 221 | */ 222 | async tokensForSale({ categoryId }) { 223 | return (await this.params.contract 224 | .getContract() 225 | .methods.categories(categoryId) 226 | .call()).supply; 227 | } 228 | 229 | /** 230 | * @function soldByCategoryId 231 | * @description Get Total tokens for sold by category 232 | * @param {Integer} categoryId 233 | * @returns {Integer} Amount in Tokens 234 | */ 235 | async soldByCategoryId({ categoryId }) { 236 | return await this.params.contract 237 | .getContract() 238 | .methods.soldByCategoryId(categoryId) 239 | .call(); 240 | } 241 | 242 | /** 243 | * @function tokensAllocated 244 | * @description Get Total tokens spent in the contract, therefore the tokens bought until now 245 | * @returns {Integer} Amount in Tokens 246 | */ 247 | async tokensAllocated() { 248 | return Numbers.fromDecimals( 249 | (await this.params.contract 250 | .getContract() 251 | .methods.tokensAllocated() 252 | .call()), 253 | await this.getTradingDecimals() 254 | ); 255 | } 256 | 257 | /** 258 | * @function tokensLeft 259 | * @description Get Total tokens owned by category 260 | * @param {Integer} categoryId 261 | * @returns {Integer} Amount in Tokens 262 | */ 263 | async tokensLeft({ categoryId }) { 264 | return await this.params.contract 265 | .getContract() 266 | .methods.tokensLeft(categoryId) 267 | .call(); 268 | } 269 | 270 | /** 271 | * @function totalCost 272 | * @description Get Total cost for buying all the nfts 273 | * @returns {Integer} Amount in Tokens 274 | */ 275 | async totalCost() { 276 | return await this.params.contract 277 | .getContract() 278 | .methods.maximumRaise() 279 | .call(); 280 | } 281 | 282 | /** 283 | * @function getCost 284 | * @description Get Cost for category and amount 285 | * @param {Integer} amount 286 | * @param {Integer} categoryId 287 | * @returns {Integer} costAmount 288 | */ 289 | getCost = async ({ amount, categoryId }) => { 290 | 291 | return Numbers.fromDecimals( 292 | await this.params.contract 293 | .getContract() 294 | .methods.cost(amount, categoryId) 295 | .call(), 296 | await this.getTradingDecimals() 297 | ); 298 | }; 299 | 300 | /** 301 | * @function safePullTradeToken 302 | * @description Safe Pull all trading tokens 303 | */ 304 | safePullTradeToken = async () => { 305 | return await this.executeContractMethod( 306 | this.getContractMethods().safePullTradeToken(), 307 | null, 308 | 0 309 | ); 310 | }; 311 | 312 | /* Legacy Call */ 313 | getETHCostFromTokens = () => { throw new Error("Please use 'getCost' instead") }; 314 | 315 | /************************************** 316 | * PURCHASE METHODS 317 | **************************************/ 318 | 319 | /** 320 | * @function getUserPurchases 321 | * @param {Address} address 322 | * @returns {Object[]} purchases 323 | */ 324 | getUserPurchases = async ({ address }) => { 325 | let purchaseIds = await this.params.contract 326 | .getContract() 327 | .methods 328 | .getMyPurchases(address) 329 | .call(); 330 | let purchases = []; 331 | 332 | for (let id of purchaseIds) { 333 | if (id != undefined) { 334 | purchases.push( 335 | await this.getPurchase({ purchaseId: Number(id) }) 336 | ); 337 | } 338 | }; 339 | return purchases; 340 | }; 341 | 342 | /** 343 | * @function getPurchase 344 | * @description Get Purchase based on ID 345 | * @param {Integer} purchaseId 346 | * @returns {Integer} _id 347 | * @returns {Integer} categoryId 348 | * @returns {Integer} amount 349 | * @returns {Integer} amountContributed 350 | * @returns {Address} purchaser 351 | * @returns {Date} timestamp 352 | * @returns {Boolean} reverted 353 | */ 354 | getPurchase = async ({ purchaseId }) => { 355 | let res = await this.params.contract 356 | .getContract() 357 | .methods.getPurchase(purchaseId) 358 | .call(); 359 | 360 | let amountContributed = Numbers.fromDecimals(res.amountContributed, await this.getTradingDecimals()); 361 | 362 | return { 363 | _id: purchaseId, 364 | categoryId: Number(res.categoryId), 365 | amount: Number(res.amountPurchased), 366 | amountContributed, 367 | purchaser: res.purchaser, 368 | timestamp: Numbers.fromSmartContractTimeToMinutes(res.timestamp), 369 | reverted: res.reverted, 370 | }; 371 | }; 372 | 373 | /** 374 | * @function getBuyers 375 | * @description Get Buyers 376 | * @returns {Array | Integer} _ids 377 | */ 378 | 379 | getBuyers = async () => 380 | await this.getContractMethods().getBuyers().call(); 381 | 382 | /** 383 | * @function getPurchaseIds 384 | * @description Get All Purchase Ids 385 | * @returns {(Array | Integer)} _ids 386 | */ 387 | getPurchaseIds = async () => { 388 | let res = await this.params.contract 389 | .getContract() 390 | .methods.getPurchasesCount() 391 | .call(); 392 | let ids = []; 393 | for (let i = 0; i < res; i++) { 394 | ids.push(i); 395 | } 396 | return ids; 397 | }; 398 | 399 | /** 400 | * @function getPurchaseIds 401 | * @description Get All Purchase Ids filter by Address/Purchaser 402 | * @param {Address} address 403 | * @returns {Array | Integer} _ids 404 | */ 405 | getAddressPurchaseIds = async ({ address }) => { 406 | let res = await this.executeContractMethod( 407 | this.getContractMethods().getMyPurchases(address), 408 | true 409 | ); 410 | return res.map((id) => Numbers.fromHex(id)); 411 | }; 412 | 413 | 414 | /************************************** 415 | * CATEGORIES METHODS 416 | **************************************/ 417 | 418 | /** 419 | * @function getIsClaimedCategoryForUser 420 | * @param {Address} address 421 | * @param {Number} categoryId 422 | * @returns {boolean} claimed 423 | */ 424 | getIsClaimedCategoryForUser = async ({ address, categoryId }) => { 425 | return await this.executeContractMethod( 426 | this.getContractMethods().getIsClaimedCategoryForUser(address, categoryId), 427 | true 428 | ); 429 | } 430 | 431 | /** 432 | * @function setUserClaimedCategory 433 | * @type admin 434 | * @param {Address} address 435 | * @param {Number} categoryId 436 | * @description Sets user claimed category 437 | */ 438 | setUserClaimedCategory = async ({ address, categoryId }) => { 439 | await this.executeContractMethod( 440 | this.getContractMethods().setUserClaimedCategory(address, categoryId) 441 | ); 442 | }; 443 | 444 | /** 445 | * @function categoryIds 446 | * @returns {Number[]} an array containig all category ids 447 | */ 448 | async categoryIds() { 449 | return await this.params.contract 450 | .getContract() 451 | .methods 452 | .getCategoryIds() 453 | .call(); 454 | } 455 | 456 | /** 457 | * @function setCategories 458 | * @type admin 459 | * @param {Number[]} categoryIds Ids of the NFT categories 460 | * @param {Number[]} categoriesSupply Supply of every category of NFT in same order than Ids 461 | * @param {Float[]} categoriesPrice Price per unit of a category item, in same order than Ids 462 | * @param {Number} tradingDecimals To be the decimals of the currency in case (ex : USDT -> 9; ETH -> 18) 463 | * @description Modifies the categories oon the contract 464 | */ 465 | setCategories = async ({ categoryIds, categoriesSupply, categoriesPrice }) => { 466 | let finalcategoriesPrice = []; 467 | for (let i = 0; i < categoriesPrice.length; i++) { 468 | finalcategoriesPrice[i] = Numbers.toSmartContractDecimals( 469 | categoriesPrice[i], 470 | await this.getTradingDecimals() 471 | ) 472 | }; 473 | return await this.executeContractMethod( 474 | this.getContractMethods().setCategories( 475 | categoryIds, 476 | categoriesSupply, 477 | categoriesPrice 478 | ) 479 | ); 480 | } 481 | 482 | } 483 | 484 | export default FixedNFTSwapContract; 485 | -------------------------------------------------------------------------------- /tests/test/nft-whitelist.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | 3 | import Web3 from "web3"; 4 | import chai from 'chai'; 5 | import { mochaAsync } from '../utils'; 6 | import moment, { isDate } from 'moment'; 7 | import Application from '../../src/models'; 8 | import { ierc20 } from "../../src/interfaces"; 9 | import Numbers from "../../src/utils/Numbers"; 10 | import Contract from "../../src/models/base/Contract"; 11 | import * as ethers from 'ethers'; 12 | 13 | var contractAddress = '0x420751cdeb28679d8e336f2b4d1fc61df7439b5a'; 14 | var userPrivateKey = '0x7f76de05082c4d578219ca35a905f8debe922f1f00b99315ebf0706afc97f132'; 15 | const expect = chai.expect; 16 | let tokenPurchaseAmount = 0.01; 17 | const tokenFundAmount = 5; 18 | const tradeValue = 0.01; 19 | let signs = []; 20 | context('NFT Contract With Whitelisting', async () => { 21 | var swapContract; 22 | var app; 23 | var ethersProvider; 24 | var isSaleOpen, tokensLeft, indivMaxAmount; 25 | var currentTime; 26 | 27 | var isRealChain = process.env.CHAIN_NAME; 28 | 29 | const getWeb3 = () => { 30 | // Instance Application using ganache 31 | const provider = require("ganache-core").provider({ 32 | gasLimit: 10000000000, 33 | 34 | gasPrice: 1, 35 | debug: true, 36 | accounts: [ 37 | { 38 | secretKey: userPrivateKey, 39 | balance: 1000000000000000000000 40 | } 41 | ] 42 | }); 43 | ethersProvider = new ethers.providers.Web3Provider(provider); 44 | return new Web3(provider); 45 | } 46 | 47 | const sleep = async (time) => { 48 | return new Promise((resolve) => { 49 | setTimeout(resolve, time * 1000) 50 | }); 51 | } 52 | 53 | const forwardTime = async (time) => { 54 | if (isRealChain) { 55 | await sleep(time); 56 | currentTime = parseInt(new Date().getTime()/1000); 57 | return; 58 | } 59 | // "Roads? Where we’re going, we don’t need roads." 60 | const date = parseInt(new Date().getTime()/1000); 61 | currentTime = date + await ethersProvider.send('evm_increaseTime', [ time ]); 62 | return await ethersProvider.send('evm_mine'); 63 | } 64 | 65 | before(mochaAsync(async () => { 66 | return new Promise(async (resolve) => { 67 | // Instance Application 68 | app = new Application({test : true, mainnet : false, network : isRealChain ? process.env.CHAIN_NAME : 'ETH', web3: 69 | isRealChain ? undefined : getWeb3() 70 | }); 71 | app.web3.eth.transactionConfirmationBlocks = 1; 72 | 73 | // Deploy the ERC20 74 | const contract = new Contract(app.web3, ierc20.abi); 75 | const response = await contract.deploy( 76 | app.account, 77 | ierc20.abi, 78 | ierc20.bytecode, 79 | [], 80 | undefined 81 | ); 82 | resolve(); 83 | }); 84 | })); 85 | 86 | 87 | it('should deploy Fixed NFT Swap Contract with whitelist', mochaAsync(async () => { 88 | /* Create Contract */ 89 | swapContract = await app.getFixedNFTSwapContract({}); 90 | /* Deploy */ 91 | let res = await swapContract.deploy({ 92 | individualMaximumAmount : 0.1, 93 | startDate : moment().add(4, 'minutes'), 94 | endDate : moment().add(8, 'minutes'), 95 | distributionDate: moment().add(9, 'minutes'), 96 | hasWhitelisting : true, 97 | categoryIds: [1, 2], 98 | categoriesSupply: [tokenFundAmount, 3], 99 | categoriesPrice: [tradeValue, 0.02] 100 | }); 101 | contractAddress = swapContract.getAddress(); 102 | expect(res).to.not.equal(false); 103 | 104 | const signer = app.getSigner(); 105 | const account = await signer.generateSignerAccount({password: 'test1234'}); 106 | 107 | signs = await signer.signAddresses({ 108 | addresses: [ 109 | '0xe797860acFc4e06C1b2B96197a7dB1dFa518d5eB' 110 | ], 111 | accountMaxAllocations: [ 112 | 2 113 | ], 114 | decimals: 18, 115 | contractAddress: contractAddress, 116 | accountJson: account, 117 | password: 'test1234' 118 | }); 119 | 120 | await swapContract.setSignerPublicAddress({ 121 | address: ('0x' + JSON.parse(account).address).toLowerCase() 122 | }); 123 | expect(await swapContract.getTradingDecimals()).to.equal(18); 124 | })); 125 | 126 | it('should get the correct smart contract version', mochaAsync(async () => { 127 | expect(await swapContract.getSmartContractVersion()).to.equal(3100000); 128 | })); 129 | 130 | it('should get a Fixed Swap Contract From contractAddress - 2.0', mochaAsync(async () => { 131 | /* Get Contract */ 132 | swapContract = await app.getFixedNFTSwapContract({contractAddress}); 133 | swapContract.__init__(); 134 | await swapContract.assertERC20Info(); 135 | expect(swapContract.version).to.equal("2.0"); 136 | expect(swapContract).to.not.equal(false); 137 | })); 138 | 139 | it('GET - isPreFunded', mochaAsync(async () => { 140 | let res = await swapContract.isPreStart(); 141 | expect(res).to.equal(true); 142 | })); 143 | 144 | it('GET - tokensLeft', mochaAsync(async () => { 145 | let tokens = await swapContract.tokensLeft({categoryId: 1}); 146 | tokensLeft = tokens; 147 | expect(Number(tokens).noExponents()).to.equal(Number(tokenFundAmount).noExponents()); 148 | })); 149 | 150 | it('should edit start Date', mochaAsync(async () => { 151 | let oldStartDate = await swapContract.startDate(); 152 | 153 | const newStartDate = new Date(oldStartDate.getTime() + (1 * 1000)); 154 | await swapContract.setStartDate({startDate: newStartDate}); 155 | let res = await swapContract.startDate(); 156 | expect(res.getTime()).to.equal(newStartDate.getTime()); 157 | 158 | await swapContract.setStartDate({startDate: oldStartDate}); 159 | res = await swapContract.startDate(); 160 | expect(res.getTime()).to.equal(oldStartDate.getTime()); 161 | 162 | })); 163 | it('should edit distribution date', mochaAsync(async () => { 164 | let oldDistributionDate = await swapContract.distributionDate(); 165 | const newDistributionDate = new Date(oldDistributionDate.getTime() + (86400 * 1000)); 166 | 167 | await swapContract.setDistributionDate({distributionDate: newDistributionDate}); 168 | let res = await swapContract.distributionDate(); 169 | expect(res.getTime()).to.equal(newDistributionDate.getTime()); 170 | 171 | await swapContract.setDistributionDate({distributionDate: oldDistributionDate}); 172 | res = await swapContract.distributionDate(); 173 | expect(res.getTime()).to.equal(oldDistributionDate.getTime()); 174 | })); 175 | 176 | 177 | it('GET - isSaleOpen - before Start', mochaAsync(async () => { 178 | await forwardTime(4*60); 179 | let res = await swapContract.isOpen(); 180 | isSaleOpen = res; 181 | expect(res).to.equal(true); 182 | })); 183 | 184 | it('GET - hasWhitelisting ', mochaAsync(async () => { 185 | let res = await swapContract.hasWhitelisting(); 186 | expect(res).to.equal(true); 187 | })); 188 | 189 | it('GET - startDate ', mochaAsync(async () => { 190 | let res = await swapContract.startDate(); 191 | res = isDate(res); 192 | expect(res).to.equal(true); 193 | })); 194 | 195 | it('GET - endDate ', mochaAsync(async () => { 196 | let res = await swapContract.endDate(); 197 | res = isDate(res); 198 | expect(res).to.equal(true); 199 | })); 200 | 201 | it('GET - category ids ', mochaAsync(async () => { 202 | let res = await swapContract.categoryIds(); 203 | expect(Number(res[0])).to.equal(1); 204 | expect(Number(res[1])).to.equal(2); 205 | })); 206 | 207 | it('GET - individualMaximumAmount ', mochaAsync(async () => { 208 | let res = await swapContract.individualMaximumAmount(); 209 | indivMaxAmount = res; 210 | expect(Number(res).noExponents()).to.equal(Number(0.1).noExponents()); 211 | })); 212 | 213 | it('GET - getCostFromTokens ', mochaAsync(async () => { 214 | let res = await swapContract.getCost({amount : 2, categoryId: 2}); 215 | expect(Number(res).noExponents()).to.equal(Number(0.04).noExponents()); 216 | })); 217 | 218 | it('check conditions for swap ', mochaAsync(async () => { 219 | let amount = Number(tokenPurchaseAmount).noExponents() > 0 ? true : false; 220 | tokensLeft = Number(tokenPurchaseAmount).noExponents() <= Number(tokensLeft).noExponents() ? true : false; 221 | indivMaxAmount = Number(tokenPurchaseAmount).noExponents() <= Number(indivMaxAmount).noExponents() ? true : false; 222 | expect(isSaleOpen).to.equal(true); 223 | expect(amount).to.equal(true); 224 | expect(tokensLeft).to.equal(true); 225 | expect(indivMaxAmount).to.equal(true); 226 | })); 227 | 228 | it('GET - hasStarted', mochaAsync(async () => { 229 | await forwardTime(1*60); 230 | let res = await swapContract.hasStarted(); 231 | expect(res).to.equal(true); 232 | })); 233 | 234 | it('GET - isSaleOpen', mochaAsync(async () => { 235 | let res = await swapContract.isOpen(); 236 | expect(res).to.equal(true); 237 | })); 238 | 239 | it('Edit max allocation - Admin', mochaAsync(async () => { 240 | let newMax = 500; 241 | let res = await swapContract.setIndividualMaximumAmount({individualMaximumAmount: newMax}); 242 | expect(res).to.not.equal(false); 243 | expect(await swapContract.individualMaximumAmount()).to.equal(newMax+''); 244 | })); 245 | 246 | it('should do a non atomic swap on the Contract with signature', mochaAsync(async () => { 247 | await forwardTime(5); 248 | let res = await swapContract.swap({tokenAmount : 1, categoryId: 1, maxAllocation: signs[0].allocation, signature: signs[0].signature}); 249 | expect(res).to.not.equal(false); 250 | })); 251 | 252 | it('should do a non atomic swap on the Contract after adding address to whitelist', mochaAsync(async () => { 253 | await swapContract.addWhitelistedAddress({address: app.account.getAddress()}); 254 | let res = await swapContract.swap({tokenAmount : 1, categoryId: 1, maxAllocation: signs[0].allocation}); 255 | expect(res).to.not.equal(false); 256 | })); 257 | 258 | 259 | it('GET - Purchases', mochaAsync(async () => { 260 | let purchases = await swapContract.getPurchaseIds(); 261 | expect(purchases.length).to.equal(2); 262 | })); 263 | 264 | it('GET - My Purchases', mochaAsync(async () => { 265 | let purchases = await swapContract.getAddressPurchaseIds({address : app.account.getAddress()}); 266 | expect(purchases.length).to.equal(2); 267 | })); 268 | 269 | it('GET - Purchase ID', mochaAsync(async () => { 270 | let purchases = await swapContract.getAddressPurchaseIds({address : app.account.getAddress()}); 271 | let purchase = await swapContract.getPurchase({purchaseId : purchases[0]}); 272 | 273 | const amountPurchase = Number(purchase.amount).noExponents(); 274 | 275 | expect(amountPurchase).to.equal(Number(1).noExponents()); 276 | expect(Number(purchase.amountContributed).toFixed(2)).to.equal(Number(tradeValue).noExponents()); 277 | expect(purchase.purchaser).to.equal(app.account.getAddress()); 278 | expect(purchase.reverted).to.equal(false); 279 | })); 280 | 281 | 282 | it('GET - tokensLeft after Swaps', mochaAsync(async () => { 283 | let tokens = await swapContract.tokensLeft({categoryId: 1}); 284 | tokens = Number(tokens).noExponents(); 285 | tokensLeft = Number(tokenFundAmount-2).noExponents(); 286 | expect(Number(tokens).toFixed(2)).to.equal(Number(tokensLeft).toFixed(2)); 287 | })); 288 | 289 | it('GET - soldByCategoryId', mochaAsync(async () => { 290 | let soldByCategoryId = await swapContract.soldByCategoryId({categoryId: 1}); 291 | expect(Number(soldByCategoryId)).to.equal(2); 292 | })); 293 | 294 | it('GET - Buyers', mochaAsync(async () => { 295 | let buyers = await swapContract.getBuyers(); 296 | expect(buyers.length).to.equal(2); 297 | })); 298 | 299 | it('GET - Fixed Swap is Closed', mochaAsync(async () => { 300 | await forwardTime(4*60); 301 | let res = await swapContract.hasFinalized(); 302 | expect(res).to.equal(true); 303 | res = await swapContract.isOpen(); 304 | expect(res).to.equal(false); 305 | })); 306 | 307 | it('GET - Purchase ID 2', mochaAsync(async () => { 308 | let purchases = await swapContract.getAddressPurchaseIds({address : app.account.getAddress()}); 309 | let purchase = await swapContract.getPurchase({purchaseId : purchases[0]}); 310 | expect(purchase.purchaser).to.equal(app.account.getAddress()); 311 | expect(purchase.reverted).to.equal(false); 312 | 313 | })); 314 | 315 | it("GET - HasMinimumRaise", mochaAsync(async () => { 316 | let hasMinimumRaise = await swapContract.hasMinimumRaise(); 317 | expect(hasMinimumRaise).to.equal(false); 318 | })); 319 | 320 | it("GET - Minimum Raise Not having", mochaAsync(async () => { 321 | let minimumRaise = await swapContract.minimumRaise(); 322 | expect(Number(minimumRaise)).to.equal(0); 323 | })); 324 | 325 | it("GET - MinimumReached with no minimum", mochaAsync(async () => { 326 | let minimumReached = await swapContract.minimumReached(); 327 | expect(minimumReached).to.equal(true); 328 | })); 329 | 330 | it("GET - MinimumReached with minimum not satisfied", mochaAsync(async () => { 331 | let minimumReached = await swapContract.minimumReached(); 332 | expect(minimumReached).to.equal(true); 333 | })); 334 | 335 | it("GET - Allocated tokens", mochaAsync(async () => { 336 | let tokensAllocated = await swapContract.tokensAllocated(); 337 | expect(Number(tokensAllocated)).to.equal(2 * tradeValue); 338 | })); 339 | 340 | it("GET - Tokens for sale", mochaAsync(async() => { 341 | let tokensForSale = await swapContract.tokensForSale({categoryId: 1}); 342 | expect(Number(tokensForSale)).to.equal(tokenFundAmount); 343 | })); 344 | 345 | it('Remove ETH From Purchases - Admin', mochaAsync(async () => { 346 | let res = await swapContract.withdrawFunds(); 347 | expect(res).to.not.equal(false); 348 | })); 349 | 350 | it('Add to blacklist - Admin', mochaAsync(async () => { 351 | let res = await swapContract.addToBlacklist({address: '0xfAadFace3FbD81CE37B0e19c0B65fF4234148132'}); 352 | expect(res).to.not.equal(false); 353 | expect(await swapContract.isBlacklisted({address: '0xfAadFace3FbD81CE37B0e19c0B65fF4234148132'})).to.equal(true); 354 | })); 355 | 356 | it('Remove from blacklist - Admin', mochaAsync(async () => { 357 | let res = await swapContract.removeFromBlacklist({address: '0xfAadFace3FbD81CE37B0e19c0B65fF4234148132'}); 358 | expect(res).to.not.equal(false); 359 | expect(await swapContract.isBlacklisted({address: '0xfAadFace3FbD81CE37B0e19c0B65fF4234148132'})).to.equal(false); 360 | })); 361 | 362 | it("GET User purchases", mochaAsync( async () => { 363 | let purchases = await swapContract.getUserPurchases({address: app.account.getAddress()}); 364 | expect(purchases.length).to.equal(2); 365 | expect(purchases[0].categoryId).to.equal(1); 366 | expect(purchases[0].amount).to.equal(1); 367 | })); 368 | 369 | it("GET User purchase", mochaAsync( async () => { 370 | let purchase = await swapContract.getPurchase({purchaseId: 0}); 371 | expect(purchase.categoryId).to.equal(1); 372 | expect(purchase.amount).to.equal(1); 373 | })); 374 | 375 | it("GET Is Claimed category", mochaAsync( async () => { 376 | let claimed = await swapContract.getIsClaimedCategoryForUser({address: app.account.getAddress(), categoryId: 1}); 377 | expect(claimed).to.equal(false); 378 | })); 379 | }); 380 | -------------------------------------------------------------------------------- /tests/test/nft-erc20.js: -------------------------------------------------------------------------------- 1 | require('dotenv').config(); 2 | 3 | import Web3 from "web3"; 4 | import chai from 'chai'; 5 | import { mochaAsync } from '../utils'; 6 | import moment, { isDate } from 'moment'; 7 | import Application from '../../src/models'; 8 | import { ierc20 } from "../../src/interfaces"; 9 | import Numbers from "../../src/utils/Numbers"; 10 | import * as ethers from 'ethers'; 11 | import Contract from "../../src/models/base/Contract"; 12 | import BigNumber from "bignumber.js"; 13 | 14 | // const ERC20TokenAddress = '0x7a7748bd6f9bac76c2f3fcb29723227e3376cbb2'; 15 | var contractAddress = '0x420751cdeb28679d8e336f2b4d1fc61df7439b5a'; 16 | var contractAddressWithMinimumRaise = '0x420751cdeb28679d8e336f2b4d1fc61df7439b5a'; 17 | var userPrivateKey = '0x7f76de05082c4d578219ca35a905f8debe922f1f00b99315ebf0706afc97f132'; 18 | const expect = chai.expect; 19 | let tokenPurchaseAmount = 0.01; 20 | const tokenFundAmount = 5; 21 | const tradeValue = 0.01; 22 | 23 | context('NFT ERC20 Contract', async () => { 24 | var ERC20TokenAddress; 25 | var swapContract; 26 | var app; 27 | var ethersProvider; 28 | var isFunded, isSaleOpen, tokensLeft, indiviMinAmount, indivMaxAmount, cost, tokensAvailable; 29 | var currentTime; 30 | 31 | var isRealChain = process.env.CHAIN_NAME; 32 | 33 | const getWeb3 = () => { 34 | // Instance Application using ganache 35 | const provider = require("ganache-core").provider({ 36 | gasLimit: 10000000000, 37 | 38 | gasPrice: 1, 39 | debug: true, 40 | accounts: [ 41 | { 42 | secretKey: userPrivateKey, 43 | balance: 1000000000000000000000 44 | } 45 | ] 46 | }); 47 | ethersProvider = new ethers.providers.Web3Provider(provider); 48 | return new Web3(provider); 49 | } 50 | 51 | const sleep = async (time) => { 52 | return new Promise((resolve) => { 53 | setTimeout(resolve, time * 1000) 54 | }); 55 | } 56 | 57 | const forwardTime = async (time) => { 58 | if (isRealChain) { 59 | await sleep(time); 60 | currentTime = parseInt(new Date().getTime()/1000); 61 | return; 62 | } 63 | // "Roads? Where we’re going, we don’t need roads." 64 | const date = parseInt(new Date().getTime()/1000); 65 | currentTime = date + await ethersProvider.send('evm_increaseTime', [ time ]); 66 | return await ethersProvider.send('evm_mine'); 67 | } 68 | 69 | before(mochaAsync(async () => { 70 | return new Promise(async (resolve) => { 71 | // Instance Application 72 | app = new Application({test : true, mainnet : false, network : isRealChain ? process.env.CHAIN_NAME : 'ETH', web3: 73 | isRealChain ? undefined : getWeb3() 74 | }); 75 | app.web3.eth.transactionConfirmationBlocks = 1; 76 | 77 | // Deploy the ERC20 78 | const contract = new Contract(app.web3, ierc20.abi); 79 | const response = await contract.deploy( 80 | app.account, 81 | ierc20.abi, 82 | ierc20.bytecode, 83 | [], 84 | undefined 85 | ); 86 | ERC20TokenAddress = response.contractAddress; 87 | resolve(); 88 | }); 89 | })); 90 | 91 | 92 | it('should deploy Fixed NFT Swap Contract', mochaAsync(async () => { 93 | /* Create Contract */ 94 | swapContract = await app.getFixedNFTSwapContract({}); 95 | /* Deploy */ 96 | let res = await swapContract.deploy({ 97 | ERC20TradingAddress: ERC20TokenAddress, 98 | tradingDecimals: 18, 99 | individualMaximumAmount : 0.1, 100 | startDate : moment().add(4, 'minutes'), 101 | endDate : moment().add(8, 'minutes'), 102 | distributionDate: moment().add(9, 'minutes'), 103 | hasWhitelisting : false, 104 | categoryIds: [1, 2], 105 | categoriesSupply: [tokenFundAmount, 3], 106 | categoriesPrice: [tradeValue, 0.02] 107 | }); 108 | contractAddress = swapContract.getAddress(); 109 | await swapContract.approveSwapERC20({tokenAmount: 1, callback: () => {}}); 110 | expect(res).to.not.equal(false); 111 | 112 | expect(await swapContract.getTradingDecimals()).to.equal(18); 113 | })); 114 | 115 | it('should get the correct smart contract version', mochaAsync(async () => { 116 | expect(await swapContract.getSmartContractVersion()).to.equal(3100000); 117 | })); 118 | 119 | it('should get a Fixed Swap Contract From contractAddress - 2.0', mochaAsync(async () => { 120 | /* Get Contract */ 121 | swapContract = await app.getFixedNFTSwapContract({contractAddress}); 122 | swapContract.__init__(); 123 | await swapContract.assertERC20Info(); 124 | expect(swapContract.version).to.equal("2.0"); 125 | expect(swapContract).to.not.equal(false); 126 | })); 127 | 128 | it('GET - isPreFunded', mochaAsync(async () => { 129 | let res = await swapContract.isPreStart(); 130 | expect(res).to.equal(true); 131 | })); 132 | 133 | it('GET - tokensLeft', mochaAsync(async () => { 134 | let tokens = await swapContract.tokensLeft({categoryId: 1}); 135 | tokensLeft = tokens; 136 | expect(Number(tokens).noExponents()).to.equal(Number(tokenFundAmount).noExponents()); 137 | })); 138 | 139 | it('should edit start Date', mochaAsync(async () => { 140 | let oldStartDate = await swapContract.startDate(); 141 | 142 | const newStartDate = new Date(oldStartDate.getTime() + (1 * 1000)); 143 | await swapContract.setStartDate({startDate: newStartDate}); 144 | let res = await swapContract.startDate(); 145 | expect(res.getTime()).to.equal(newStartDate.getTime()); 146 | 147 | await swapContract.setStartDate({startDate: oldStartDate}); 148 | res = await swapContract.startDate(); 149 | expect(res.getTime()).to.equal(oldStartDate.getTime()); 150 | 151 | })); 152 | 153 | it('should edit end Date', mochaAsync(async () => { 154 | let oldEndDate = await swapContract.endDate(); 155 | 156 | const newEndDate = new Date(oldEndDate.getTime() + (30 * 1000)); 157 | await swapContract.setEndDate({endDate: newEndDate}); 158 | let res = await swapContract.endDate(); 159 | expect(res.getTime()).to.equal(newEndDate.getTime()); 160 | 161 | await swapContract.setEndDate({endDate: oldEndDate}); 162 | res = await swapContract.endDate(); 163 | expect(res.getTime()).to.equal(oldEndDate.getTime()); 164 | 165 | })); 166 | 167 | it('should edit distribution date', mochaAsync(async () => { 168 | let oldDistributionDate = await swapContract.distributionDate(); 169 | const newDistributionDate = new Date(oldDistributionDate.getTime() + (86400 * 1000)); 170 | 171 | await swapContract.setDistributionDate({distributionDate: newDistributionDate}); 172 | let res = await swapContract.distributionDate(); 173 | expect(res.getTime()).to.equal(newDistributionDate.getTime()); 174 | 175 | await swapContract.setDistributionDate({distributionDate: oldDistributionDate}); 176 | res = await swapContract.distributionDate(); 177 | expect(res.getTime()).to.equal(oldDistributionDate.getTime()); 178 | })); 179 | 180 | 181 | it('GET - isSaleOpen - before Start', mochaAsync(async () => { 182 | await forwardTime(4*60); 183 | let res = await swapContract.isOpen(); 184 | isSaleOpen = res; 185 | expect(res).to.equal(true); 186 | })); 187 | 188 | it('GET - hasWhitelisting ', mochaAsync(async () => { 189 | let res = await swapContract.hasWhitelisting(); 190 | expect(res).to.equal(false); 191 | })); 192 | 193 | it('GET - startDate ', mochaAsync(async () => { 194 | let res = await swapContract.startDate(); 195 | res = isDate(res); 196 | expect(res).to.equal(true); 197 | })); 198 | 199 | it('GET - endDate ', mochaAsync(async () => { 200 | let res = await swapContract.endDate(); 201 | res = isDate(res); 202 | expect(res).to.equal(true); 203 | })); 204 | 205 | it('GET - category ids ', mochaAsync(async () => { 206 | let res = await swapContract.categoryIds(); 207 | expect(Number(res[0])).to.equal(1); 208 | expect(Number(res[1])).to.equal(2); 209 | })); 210 | 211 | it('GET - individualMaximumAmount ', mochaAsync(async () => { 212 | let res = await swapContract.individualMaximumAmount(); 213 | indivMaxAmount = res; 214 | expect(Number(res).noExponents()).to.equal(Number(0.1).noExponents()); 215 | })); 216 | 217 | it('GET - getCostFromTokens ', mochaAsync(async () => { 218 | let res = await swapContract.getCost({amount : 2, categoryId: 2}); 219 | expect(Number(res).noExponents()).to.equal(Number(0.04).noExponents()); 220 | })); 221 | 222 | it('check conditions for swap ', mochaAsync(async () => { 223 | let amount = Number(tokenPurchaseAmount).noExponents() > 0 ? true : false; 224 | tokensLeft = Number(tokenPurchaseAmount).noExponents() <= Number(tokensLeft).noExponents() ? true : false; 225 | indivMaxAmount = Number(tokenPurchaseAmount).noExponents() <= Number(indivMaxAmount).noExponents() ? true : false; 226 | expect(isSaleOpen).to.equal(true); 227 | expect(amount).to.equal(true); 228 | expect(tokensLeft).to.equal(true); 229 | expect(indivMaxAmount).to.equal(true); 230 | })); 231 | 232 | it('GET - hasStarted', mochaAsync(async () => { 233 | await forwardTime(1*60); 234 | let res = await swapContract.hasStarted(); 235 | expect(res).to.equal(true); 236 | })); 237 | 238 | it('GET - isSaleOpen', mochaAsync(async () => { 239 | let res = await swapContract.isOpen(); 240 | expect(res).to.equal(true); 241 | })); 242 | 243 | it('Edit max allocation - Admin', mochaAsync(async () => { 244 | let newMax = 500; 245 | let res = await swapContract.setIndividualMaximumAmount({individualMaximumAmount: newMax}); 246 | expect(res).to.not.equal(false); 247 | expect(await swapContract.individualMaximumAmount()).to.equal(newMax+''); 248 | })); 249 | 250 | it('should do a non atomic swap on the Contract', mochaAsync(async () => { 251 | await forwardTime(5); 252 | let res = await swapContract.swap({tokenAmount : 2, categoryId: 1, maxAllocation: 0}); 253 | expect(res).to.not.equal(false); 254 | res = await swapContract.swap({tokenAmount : 1, categoryId: 1, maxAllocation: 0}); 255 | expect(res).to.not.equal(false); 256 | })); 257 | 258 | it('GET - Purchases', mochaAsync(async () => { 259 | let purchases = await swapContract.getPurchaseIds(); 260 | expect(purchases.length).to.equal(2); 261 | })); 262 | 263 | it('GET - My Purchases', mochaAsync(async () => { 264 | let purchases = await swapContract.getAddressPurchaseIds({address : app.account.getAddress()}); 265 | expect(purchases.length).to.equal(2); 266 | })); 267 | 268 | it('GET - Purchase ID', mochaAsync(async () => { 269 | let purchases = await swapContract.getAddressPurchaseIds({address : app.account.getAddress()}); 270 | let purchase = await swapContract.getPurchase({purchaseId : purchases[0]}); 271 | 272 | const amountPurchase = Number(purchase.amount).noExponents(); 273 | 274 | expect(amountPurchase).to.equal(Number(2).noExponents()); 275 | expect(Number(purchase.amountContributed).toFixed(2)).to.equal(Number(tradeValue * 2).noExponents()); 276 | expect(purchase.purchaser).to.equal(app.account.getAddress()); 277 | expect(purchase.reverted).to.equal(false); 278 | })); 279 | 280 | 281 | it('GET - tokensLeft after Swap', mochaAsync(async () => { 282 | let tokens = await swapContract.tokensLeft({categoryId: 1}); 283 | tokens = Number(tokens).noExponents(); 284 | tokensLeft = Number(tokenFundAmount-3).noExponents(); 285 | expect(Number(tokens).toFixed(2)).to.equal(Number(tokensLeft).toFixed(2)); 286 | })); 287 | 288 | it('GET - soldByCategoryId', mochaAsync(async () => { 289 | let soldByCategoryId = await swapContract.soldByCategoryId({categoryId: 1}); 290 | expect(Number(soldByCategoryId)).to.equal(3); 291 | })); 292 | 293 | it('GET - Buyers', mochaAsync(async () => { 294 | let buyers = await swapContract.getBuyers(); 295 | expect(buyers.length).to.equal(2); 296 | })); 297 | 298 | it('GET - Fixed Swap is Closed', mochaAsync(async () => { 299 | await forwardTime(4*60); 300 | let res = await swapContract.hasFinalized(); 301 | expect(res).to.equal(true); 302 | res = await swapContract.isOpen(); 303 | expect(res).to.equal(false); 304 | })); 305 | 306 | it('GET - Purchase ID 2', mochaAsync(async () => { 307 | let purchases = await swapContract.getAddressPurchaseIds({address : app.account.getAddress()}); 308 | let purchase = await swapContract.getPurchase({purchaseId : purchases[0]}); 309 | expect(purchase.purchaser).to.equal(app.account.getAddress()); 310 | expect(purchase.reverted).to.equal(false); 311 | 312 | })); 313 | 314 | it("GET - HasMinimumRaise", mochaAsync(async () => { 315 | let hasMinimumRaise = await swapContract.hasMinimumRaise(); 316 | expect(hasMinimumRaise).to.equal(false); 317 | })); 318 | 319 | 320 | it("GET - Minimum Raise Not having", mochaAsync(async () => { 321 | let minimumRaise = await swapContract.minimumRaise(); 322 | expect(Number(minimumRaise)).to.equal(0); 323 | })); 324 | 325 | it("GET - MinimumReached with no minimum", mochaAsync(async () => { 326 | let minimumReached = await swapContract.minimumReached(); 327 | expect(minimumReached).to.equal(true); 328 | })); 329 | 330 | it("GET - MinimumReached with minimum not satisfied", mochaAsync(async () => { 331 | let minimumReached = await swapContract.minimumReached(); 332 | expect(minimumReached).to.equal(true); 333 | })); 334 | 335 | it("GET - Allocated tokens", mochaAsync(async () => { 336 | let tokensAllocated = await swapContract.tokensAllocated(); 337 | expect(Number(tokensAllocated)).to.equal(3 * tradeValue); 338 | })); 339 | 340 | it("GET - Tokens for sale", mochaAsync(async() => { 341 | let tokensForSale = await swapContract.tokensForSale({categoryId: 1}); 342 | expect(Number(tokensForSale)).to.equal(tokenFundAmount); 343 | })); 344 | 345 | it('Remove ETH From Purchases - Admin', mochaAsync(async () => { 346 | let res = await swapContract.withdrawFunds(); 347 | expect(res).to.not.equal(false); 348 | })); 349 | 350 | it('Add to blacklist - Admin', mochaAsync(async () => { 351 | let res = await swapContract.addToBlacklist({address: '0xfAadFace3FbD81CE37B0e19c0B65fF4234148132'}); 352 | expect(res).to.not.equal(false); 353 | expect(await swapContract.isBlacklisted({address: '0xfAadFace3FbD81CE37B0e19c0B65fF4234148132'})).to.equal(true); 354 | })); 355 | 356 | it('Remove from blacklist - Admin', mochaAsync(async () => { 357 | let res = await swapContract.removeFromBlacklist({address: '0xfAadFace3FbD81CE37B0e19c0B65fF4234148132'}); 358 | expect(res).to.not.equal(false); 359 | expect(await swapContract.isBlacklisted({address: '0xfAadFace3FbD81CE37B0e19c0B65fF4234148132'})).to.equal(false); 360 | })); 361 | 362 | it("GET User purchases", mochaAsync( async () => { 363 | let purchases = await swapContract.getUserPurchases({address: app.account.getAddress()}); 364 | expect(purchases.length).to.equal(2); 365 | expect(purchases[0].categoryId).to.equal(1); 366 | expect(purchases[0].amount).to.equal(2); 367 | })); 368 | 369 | it("GET User purchase", mochaAsync( async () => { 370 | let purchase = await swapContract.getPurchase({purchaseId: 0}); 371 | expect(purchase.categoryId).to.equal(1); 372 | expect(purchase.amount).to.equal(2); 373 | })); 374 | 375 | it("GET Is Claimed category", mochaAsync( async () => { 376 | let claimed = await swapContract.getIsClaimedCategoryForUser({address: app.account.getAddress(), categoryId: 1}); 377 | expect(claimed).to.equal(false); 378 | })); 379 | }); 380 | -------------------------------------------------------------------------------- /docs/BASE_SWAP.md: -------------------------------------------------------------------------------- 1 | ## Classes 2 | 3 |
4 |
BaseSwapContract
5 |
6 |
7 | 8 | ## Functions 9 | 10 |
11 |
hasWhitelisting()Boolean
12 |

Verify if swap has whitelisting

13 |
14 |
isWhitelisted(address)Boolean
15 |

Verify if address is whitelisted

16 |
17 |
setHasWhitelisting(hasWhitelist)admin
18 |

Modifies if the pool has whitelisting or not

19 |
20 |
addWhitelistedAddress(address)
21 |

add WhiteListed Address

22 |
23 |
removeWhitelistedAddress(addresses, index)
24 |

remove WhiteListed Address

25 |
26 |
setSignerPublicAddress(address)
27 |

Set the public address of the signer

28 |
29 |
signerPublicAddress()string
30 |

Get the public address of the signer

31 |
32 |
getWhiteListedAddresses()Array | Address
33 |

Get Whitelisted Addresses

34 |
35 |
getBalance(Balance)
36 |

Get Balance of Contract

37 |
38 |
removeOtherERC20Tokens(tokenAddress, toAddress)
39 |

Remove Tokens from other ERC20 Address (in case of accident)

40 |
41 |
minimumRaise()Integer
42 |

Get Minimum Raise amount for Token Sale

43 |
44 |
hasMinimumRaise()Boolean
45 |

See if hasMinimumRaise

46 |
47 |
minimumReached()Boolean
48 |

See if minimumRaise was Reached

49 |
50 |
tokensAllocated()Integer
51 |

Get Total tokens spent in the contract, therefore the tokens bought until now

52 |
53 |
safePull()
54 |

Safe Pull all tokens & ETH

55 |
56 |
withdrawFunds()
57 |

Withdraw all funds from tokens sold

58 |
59 |
withdrawableFunds()Integer
60 |

Get Total funds raised to be withdrawn by the admin

61 |
62 |
wereUnsoldTokensReedemed()Boolean
63 |

Verify if the admin already reemeded unsold tokens

64 |
65 |
redeemGivenMinimumGoalNotAchieved(purchaseId)
66 |

Reedem Ethereum from sale that did not achieve minimum goal

67 |
68 |
setIndividualMaximumAmount(individualMaximumAmount)admin
69 |

Modifies the max allocation

70 |
71 |
individualMaximumAmount()Integer
72 |

Get Individual Maximum Amount for each address

73 |
74 |
isApproved(tokenAmount, address)Boolean
75 |

Verify if the Admin has approved the pool to use receive the tokens for sale

76 |
77 |
isApprovedSwapERC20(tokenAmount, address)
78 |

Verify if it is approved to invest

79 |
80 |
approveSwapERC20(tokenAmount)
81 |

Approve the investor to use approved tokens for the sale

82 |
83 |
getTradingERC20Address()Address
84 |

Get Trading Address if ERC20

85 |
86 |
isETHTrade()Boolean
87 |

Verify if Token Sale is against Ethereum

88 |
89 |
getTradingDecimals()Integer
90 |

Get Trading Decimals (18 if isETHTrade, X if not)

91 |
92 |
startDate()Date
93 |

Get Start Date of Change

94 |
95 |
endDate()Date
96 |

Get End Date of Change

97 |
98 |
setEndDate(endDate)admin
99 |

Modifies the end date for the pool

100 |
101 |
setStartDate(startDate)admin
102 |

Modifies the start date for the pool

103 |
104 |
isFinalized()Boolean
105 |

To see if contract was finalized

106 |
107 |
isOpen()Boolean
108 |

Verify if the Token Sale is Open for Swap

109 |
110 |
hasStarted()Boolean
111 |

Verify if the Token Sale has started the Swap

112 |
113 |
hasFinalized()Boolean
114 |

Verify if the Token Sale has finalized, if the current date is after endDate

115 |
116 |
isPreStart()Boolean
117 |

Verify if the Token Sale in not open yet

118 |
119 |
addToBlacklist(address)
120 |

Adds an address to the blacklist

121 |
122 |
removeFromBlacklist(address)
123 |

Removes an address from the blacklist

124 |
125 |
isBlackListed(address)boolean
126 |

Returns true if the address is in the blacklist

127 |
128 |
isPaused()boolean
129 |

Returns if the contract is paused or not

130 |
131 |
pauseContract()admin
132 |

Pause Contract

133 |
134 |
unpauseContract()admin
135 |

Unpause Contract

136 |
137 |
getSmartContractVersion(Address)
138 |

Returns the version of the smart contract that is currently inside psjs

139 |
140 |
141 | 142 | 143 | 144 | ## BaseSwapContract 145 | **Kind**: global class 146 | 147 | 148 | ### new BaseSwapContract(web3, contractAddress) 149 | Base Swap Contract Object 150 | 151 | 152 | | Param | Type | Description | 153 | | --- | --- | --- | 154 | | web3 | Web3 | | 155 | | contractAddress | Address | ? (opt) | 156 | 157 | 158 | 159 | ## hasWhitelisting() ⇒ Boolean 160 | Verify if swap has whitelisting 161 | 162 | **Kind**: global function 163 | 164 | 165 | ## isWhitelisted(address) ⇒ Boolean 166 | Verify if address is whitelisted 167 | 168 | **Kind**: global function 169 | 170 | | Param | Type | 171 | | --- | --- | 172 | | address | string | 173 | 174 | 175 | 176 | ## setHasWhitelisting(hasWhitelist) ⇒ admin 177 | Modifies if the pool has whitelisting or not 178 | 179 | **Kind**: global function 180 | 181 | | Param | Type | 182 | | --- | --- | 183 | | hasWhitelist | boolean | 184 | 185 | 186 | 187 | ## addWhitelistedAddress(address) 188 | add WhiteListed Address 189 | 190 | **Kind**: global function 191 | 192 | | Param | Type | 193 | | --- | --- | 194 | | address | Address | 195 | 196 | 197 | 198 | ## removeWhitelistedAddress(addresses, index) 199 | remove WhiteListed Address 200 | 201 | **Kind**: global function 202 | 203 | | Param | Type | 204 | | --- | --- | 205 | | addresses | Array \| Addresses | 206 | | index | Integer | 207 | 208 | 209 | 210 | ## setSignerPublicAddress(address) 211 | Set the public address of the signer 212 | 213 | **Kind**: global function 214 | 215 | | Param | Type | 216 | | --- | --- | 217 | | address | string | 218 | 219 | 220 | 221 | ## signerPublicAddress() ⇒ string 222 | Get the public address of the signer 223 | 224 | **Kind**: global function 225 | **Returns**: string - address 226 | 227 | 228 | ## getWhiteListedAddresses() ⇒ Array \| Address 229 | Get Whitelisted Addresses 230 | 231 | **Kind**: global function 232 | **Returns**: Array \| Address - addresses 233 | 234 | 235 | ## getBalance(Balance) 236 | Get Balance of Contract 237 | 238 | **Kind**: global function 239 | 240 | | Param | Type | 241 | | --- | --- | 242 | | Balance | Integer | 243 | 244 | 245 | 246 | ## removeOtherERC20Tokens(tokenAddress, toAddress) 247 | Remove Tokens from other ERC20 Address (in case of accident) 248 | 249 | **Kind**: global function 250 | 251 | | Param | Type | 252 | | --- | --- | 253 | | tokenAddress | Address | 254 | | toAddress | Address | 255 | 256 | 257 | 258 | ## minimumRaise() ⇒ Integer 259 | Get Minimum Raise amount for Token Sale 260 | 261 | **Kind**: global function 262 | **Returns**: Integer - Amount in Tokens 263 | 264 | 265 | ## hasMinimumRaise() ⇒ Boolean 266 | See if hasMinimumRaise 267 | 268 | **Kind**: global function 269 | 270 | 271 | ## minimumReached() ⇒ Boolean 272 | See if minimumRaise was Reached 273 | 274 | **Kind**: global function 275 | 276 | 277 | ## tokensAllocated() ⇒ Integer 278 | Get Total tokens spent in the contract, therefore the tokens bought until now 279 | 280 | **Kind**: global function 281 | **Returns**: Integer - Amount in Tokens 282 | 283 | 284 | ## safePull() 285 | Safe Pull all tokens & ETH 286 | 287 | **Kind**: global function 288 | 289 | 290 | ## withdrawFunds() 291 | Withdraw all funds from tokens sold 292 | 293 | **Kind**: global function 294 | 295 | 296 | ## withdrawableFunds() ⇒ Integer 297 | Get Total funds raised to be withdrawn by the admin 298 | 299 | **Kind**: global function 300 | **Returns**: Integer - Amount in ETH 301 | 302 | 303 | ## wereUnsoldTokensReedemed() ⇒ Boolean 304 | Verify if the admin already reemeded unsold tokens 305 | 306 | **Kind**: global function 307 | 308 | 309 | ## redeemGivenMinimumGoalNotAchieved(purchaseId) 310 | Reedem Ethereum from sale that did not achieve minimum goal 311 | 312 | **Kind**: global function 313 | 314 | | Param | Type | 315 | | --- | --- | 316 | | purchaseId | Integer | 317 | 318 | 319 | 320 | ## setIndividualMaximumAmount(individualMaximumAmount) ⇒ admin 321 | Modifies the max allocation 322 | 323 | **Kind**: global function 324 | 325 | | Param | Type | 326 | | --- | --- | 327 | | individualMaximumAmount | Integer | 328 | 329 | 330 | 331 | ## individualMaximumAmount() ⇒ Integer 332 | Get Individual Maximum Amount for each address 333 | 334 | **Kind**: global function 335 | 336 | 337 | ## isApproved(tokenAmount, address) ⇒ Boolean 338 | Verify if the Admin has approved the pool to use receive the tokens for sale 339 | 340 | **Kind**: global function 341 | 342 | | Param | Type | 343 | | --- | --- | 344 | | tokenAmount | Integer | 345 | | address | Address | 346 | 347 | 348 | 349 | ## isApprovedSwapERC20(tokenAmount, address) 350 | Verify if it is approved to invest 351 | 352 | **Kind**: global function 353 | 354 | | Param | Type | 355 | | --- | --- | 356 | | tokenAmount | Integer | 357 | | address | Address | 358 | 359 | 360 | 361 | ## approveSwapERC20(tokenAmount) 362 | Approve the investor to use approved tokens for the sale 363 | 364 | **Kind**: global function 365 | 366 | | Param | Type | 367 | | --- | --- | 368 | | tokenAmount | Integer | 369 | 370 | 371 | 372 | ## getTradingERC20Address() ⇒ Address 373 | Get Trading Address if ERC20 374 | 375 | **Kind**: global function 376 | 377 | 378 | ## isETHTrade() ⇒ Boolean 379 | Verify if Token Sale is against Ethereum 380 | 381 | **Kind**: global function 382 | 383 | 384 | ## getTradingDecimals() ⇒ Integer 385 | Get Trading Decimals (18 if isETHTrade, X if not) 386 | 387 | **Kind**: global function 388 | 389 | 390 | ## startDate() ⇒ Date 391 | Get Start Date of Change 392 | 393 | **Kind**: global function 394 | 395 | 396 | ## endDate() ⇒ Date 397 | Get End Date of Change 398 | 399 | **Kind**: global function 400 | 401 | 402 | ## setEndDate(endDate) ⇒ admin 403 | Modifies the end date for the pool 404 | 405 | **Kind**: global function 406 | 407 | | Param | Type | 408 | | --- | --- | 409 | | endDate | Date | 410 | 411 | 412 | 413 | ## setStartDate(startDate) ⇒ admin 414 | Modifies the start date for the pool 415 | 416 | **Kind**: global function 417 | 418 | | Param | Type | 419 | | --- | --- | 420 | | startDate | Date | 421 | 422 | 423 | 424 | ## isFinalized() ⇒ Boolean 425 | To see if contract was finalized 426 | 427 | **Kind**: global function 428 | 429 | 430 | ## isOpen() ⇒ Boolean 431 | Verify if the Token Sale is Open for Swap 432 | 433 | **Kind**: global function 434 | 435 | 436 | ## hasStarted() ⇒ Boolean 437 | Verify if the Token Sale has started the Swap 438 | 439 | **Kind**: global function 440 | 441 | 442 | ## hasFinalized() ⇒ Boolean 443 | Verify if the Token Sale has finalized, if the current date is after endDate 444 | 445 | **Kind**: global function 446 | 447 | 448 | ## isPreStart() ⇒ Boolean 449 | Verify if the Token Sale in not open yet 450 | 451 | **Kind**: global function 452 | 453 | 454 | ## addToBlacklist(address) 455 | Adds an address to the blacklist 456 | 457 | **Kind**: global function 458 | 459 | | Param | Type | 460 | | --- | --- | 461 | | address | string | 462 | 463 | 464 | 465 | ## removeFromBlacklist(address) 466 | Removes an address from the blacklist 467 | 468 | **Kind**: global function 469 | 470 | | Param | Type | 471 | | --- | --- | 472 | | address | string | 473 | 474 | 475 | 476 | ## isBlackListed(address) ⇒ boolean 477 | Returns true if the address is in the blacklist 478 | 479 | **Kind**: global function 480 | **Returns**: boolean - isBlackListed 481 | 482 | | Param | Type | 483 | | --- | --- | 484 | | address | string | 485 | 486 | 487 | 488 | ## isPaused() ⇒ boolean 489 | Returns if the contract is paused or not 490 | 491 | **Kind**: global function 492 | 493 | 494 | ## pauseContract() ⇒ admin 495 | Pause Contract 496 | 497 | **Kind**: global function 498 | 499 | 500 | ## unpauseContract() ⇒ admin 501 | Unpause Contract 502 | 503 | **Kind**: global function 504 | 505 | 506 | ## getSmartContractVersion(Address) 507 | Returns the version of the smart contract that is currently inside psjs 508 | 509 | **Kind**: global function 510 | 511 | | Param | Type | 512 | | --- | --- | 513 | | Address | Address | 514 | 515 | --------------------------------------------------------------------------------