diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..c5054db
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,9 @@
+root=true
+
+[*]
+end_of_line = lf
+insert_final_newline = true
+charset = utf-8
+indent_size = 2
+indent_style = space
+trim_trailing_whitespace= true
\ No newline at end of file
diff --git a/.eslintrc.json b/.eslintrc.json
new file mode 100644
index 0000000..252ca3e
--- /dev/null
+++ b/.eslintrc.json
@@ -0,0 +1,28 @@
+{
+ "extends": [
+ "airbnb",
+ "plugin:prettier/recommended"
+ ],
+ "plugins": [
+ "prettier"
+ ],
+ "rules": {
+ "prettier/prettier": [
+ "error",
+ {
+ "endOfLine": "auto"
+ }
+ ],
+ "func-names": "off",
+ "eqeqeq": "off",
+ "class-methods-use-this": "off"
+ },
+ "env": {
+ "browser": true,
+ "es6": true,
+ "jest": true
+ },
+ "parserOptions": {
+ "ecmaVersion": 9
+ }
+ }
\ No newline at end of file
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..74c449d
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1 @@
+*.sol diff linguist-language=Solidity
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..0e97b3b
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,42 @@
+.embark
+chains.json
+config/development/mnemonic
+config/livenet/password
+config/production/password
+coverage
+embarkArtifacts
+node_modules
+package-lock.json
+dist
+
+# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
+
+# Editor artifacts
+.vscode
+.idea
+.project
+
+# dependencies
+/node_modules
+/.pnp
+.pnp.js
+
+# testing
+/coverage
+
+# production
+/build
+
+# misc
+.DS_Store
+.env.local
+.env.development.local
+.env.test.local
+.env.production.local
+
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+
+# Slither
+crytic-export/
diff --git a/.soliumignore b/.soliumignore
new file mode 100644
index 0000000..b512c09
--- /dev/null
+++ b/.soliumignore
@@ -0,0 +1 @@
+node_modules
\ No newline at end of file
diff --git a/.soliumrc.json b/.soliumrc.json
new file mode 100644
index 0000000..dcead38
--- /dev/null
+++ b/.soliumrc.json
@@ -0,0 +1,22 @@
+{
+ "extends": "solium:all",
+ "plugins": [
+ "security"
+ ],
+ "rules": {
+ "security/no-inline-assembly": "off",
+ "security/no-assign-params": "off",
+ "quotes": [
+ "error",
+ "double"
+ ],
+ "indentation": [
+ "error",
+ 4
+ ],
+ "arg-overflow": [
+ "warning",
+ 3
+ ]
+ }
+ }
\ No newline at end of file
diff --git a/Discover_Specification.md b/Discover_Specification.md
new file mode 100644
index 0000000..ff3a08f
--- /dev/null
+++ b/Discover_Specification.md
@@ -0,0 +1,179 @@
+# Discover SNT Ranking
+
+## Summary
+
+In order to fulfill one of our whitepaper promises, we need a mechanism that uses SNT to curate DApps. While this is not the only mechanism we will make available to users to find interesting and relevant DApps, it is one of the most important, both for SNT utility and because economic mechanisms are at the heart of how we buidl sustainable peer-to-peer networks.
+
+## Abstract
+
+We propose using an exponential [bonded curve](https://beta.observablehq.com/@andytudhope/dapp-store-snt-curation-mechanism), which operates only on downvotes, to implement a simple ranking game. It is the most radical market feasible: the more SNT a DApp stakes, the higher it ranks, with one caveat. The more SNT staked, the cheaper it is for the community to move that DApp down the rankings.
+
+## Motivation
+
+Token Curated Registries, and other bonded curve implementations try to incentivise the user with some kind of fungible reward token (often with governance rights/requirements attached to it) in order to decentralise the curation of interesting information. However, this creates mental overhead for users (who must manage multiple tokens, all with different on-chain transactions required) and is unlikely to see high adoption.
+
+Making the ranking algorithm transparent - and giving users an ability to affect it at a small cost to them should they feel very strongly - is potentially a more effective way to achieve decentralised curation.
+
+## User Stories
+
+An effective economic ranking mechanism, selected with the option `Ranked by SNT` (one of many filters), answers the following user stories from our [swarm doc](https://github.com/status-im/swarms/blob/master/ideas/317-dapps-store.md).
+
+1. **I want to be confident a DApp is usable / not a scam.**
+ 1. Having an economic mechanism ensures that the DApps which rank highly quite literally are those providing the "most value" to the community. This is because SNT staked to rank is locked out of circulation, meaning each SNT stakeholder's own holding of SNT should increase in value. Coincidentally, the more SNT staked in total in the store, the stronger the assurance that any given DApp which ranks highly is useful and not a scam.
+2. **As an SNT stakeholder, I would like to signal using SNT that I find a listing useful.**
+ 1. Achieved by "upvoting" in the UI. Importantly, upvotes do not effect the bonded curve, users simply donate SNT 1-1 directly to the DApp's `balance`.
+3. **As an SNT stakeholder, I would like to signal using SNT that I find a listing to be not useful/poor quality/etc.**
+ 1. Achieved, on an increasingly cheap basis the more well-resourced a DApp is, by "downvoting" in the UI. Uses an exponential bonded curve to mint downvotes.
+4. **As a DApp developer, I want to be able to propose/vote my DApp for inclusion.**
+ 1. Anybody can submit a DApp for inclusion and "vote" on it by calling `upvote` and adding SNT to its `balance`.
+
+## Specification
+
+#### Constants
+1. `uint total` - total SNT in circulation.
+2. `uint ceiling` - most influential parameter for [_shape_ of curves](https://beta.observablehq.com/@andytudhope/dapp-store-snt-curation-mechanism).
+3. `uint max` - max SNT that any one DApp can stake.
+4. `uint decimals` - the amount of decimal precision to use for the calculating `max`.
+5. `uint safeMax` - protect against overflows into infinity in votesMinted.
+
+#### Data Struct
+1. `address developer` - the developer of the DApp, used to send SNT to when `downvote` or `withdraw` is called.
+2. `bytes32 id` - a unique identifier for each DApp, potentially with other metadata associated with it, hence the `bytes32`.
+3. `bytes metadata` - the name, url, category and IPFS hash of the DApp so that we can resolve it in the store correctly.
+4. `uint balance` - keep track of the total staked on each DApp.
+5. `uint rate = 1 - (balance/max)` - used to calculate `available` and `votesMinted`.
+6. `uint available = balance * rate` - amount of SNT staked a developer can earn back. NB: this is equivalent to the `cost` of all downvotes.
+7. `uint votesMinted = available ** (1/rate)` - total downvotes that are "minted".
+8. `uint votesCast` - keep track of the downvotes already cast.
+9. `uint effectiveBalance = balance - ((votesCast/(1/rate))*(available/votesMinted))`- the Effective Balance each DApp is actually ranked by in the UI.
+
+### Constructor
+
+1. Sets the address for the SNT contract based on arg passed in.
+1. `uint total == 6804870174`
+2. `uint ceiling = 292`, as this means the max is close to 2M SNT, and is a local minima for votesMinted.
+4. `uint decimals = 1000000` - We're use 1/100th of the total SNT in circulation as our bound, based mostly on Twitter polls...
+3. `uint max = (total * ceiling)/decimals`
+5. `uint safeMax = 77 * max / 100` - 77% of the absolute max, due to limitations with bancor's power approximations in Solidity.
+
+#### Methods
+
+1. **createDapp** external
+ 1. params: `(bytes32 _id, uint _amount)`
+
+Calls internal method `_createDApp`, passing in `msg.sender`, `_id` and `_amount`.
+
+2. **upvote** external
+ 1. params:`(bytes32 _id, uint _amount)`
+
+Calls internal method `_upvote`, passing in `msg.sender`, `_id` and `_amount`.
+
+3. **downvote** external
+ 1. params: `bytes32 _id, uint _amount`
+
+Calls `downvoteCost` to check the `_amount`, then calls internal method `_downvote`, passing in `msg.sender`, `_id` and `_amount`.
+
+4. **withdraw** external
+ 1. params: `(bytes32 _id, uint _amount)`
+
+Allow developers to reduce thier stake/exit the store provided that `_amount <= available`. Recalculate `balance`, `rate`, `available` and `votesMinted`. If `votesCast > votesMinted`, then set them equal so the maths is future-proof, and recalculate `effectiveBalance`.
+
+Emit event containing new `effectiveBalance`.
+
+5. **setMetadata** external
+ 1. params: `(bytes32 _id, bytes calldata _metadata)`
+
+Checks that the person trying to set/update the metadata is the developer, then updates the metadata associated with the DApp at that `id` so that we can resolve it correctly client side.
+
+7. **receiveApproval** external
+ 1. params: `(address _from, uint256 _amount, address _token, bytes _data)`
+
+Included so that users need only sign one transaction when creating a DApp, upvoting or downvoting. Checks that the token (SNT), sender, and data are correct. Decodes the `_data` using `abiDecodeRegister`, checks the amount is correct and figures out which of the three "payable" functions (`createDApp`, `upvote`, and `downvote`) is being called by looking at the signature.
+
+2. **upvoteEffect** external views
+ 1. params: `(bytes32 _id, uint _amount)`
+
+Mock add `_amount` to `balance`, calculate `mRate`, `mAvailable`, `mVMinted`, and `mEBalance`.
+
+Returns the difference between `mEBalance` and the actual `effectiveBalance`.
+
+3. **downvoteCost** public view
+ 1. params: `(bytes32 _id)`
+
+Specifying that each downvote must move the DApp down by 1% allows us to calculate the `cost` without integrating anything. Calculate the `votesRequired` to effect the DApp by the specified %.
+
+Returns `balanceDownBy`, `votesRequired` and `cost`.
+
+4. **_createDApp** internal
+ 1. params: `(address _from, bytes32 _id, uint _amount)`
+
+Accepts some nominal amount of tokens (> 0) and creates a new Data struct with the `_id` passed to it, setting the new struct's `balance` and using that to calculate `balance`, `rate`, `available`, `votesMinted` and `effectiveBalance` (which is == `balance` at first).
+
+Emit event containing new `effectiveBalance`.
+
+4. **_upvote** internal
+ 1. params: `(address _from, bytes32 _id, uint _amount)`
+
+Transfer SNT directly to the contract, which means donating directly to the DApp's `balance`, no money to the developer. Though the votes don't use a curve, we still need to recalculate `rate`, `available`, `votesMinted` and `effectiveBalance`.
+
+Emit event containing new `effectiveBalance`.
+
+4. **_downvote** internal
+ 1. params: `(address _from, bytes32 _id, uint _amount)`
+
+Send SNT from user directly to developer in order to downvote. Call `downvoteCost` to get `balance_down_by`, `votes_required` and `cost`.
+
+Add `votesRequired` to `votesCast`, recalculate `effectiveBalance`, and subtract `cost` from `available` so that `withdraw` works correctly.
+
+Emit event containing new `effectiveBalance`.
+
+8. **abiDecodeRegister** private
+ 1. params: `(bytes memory _data)`
+
+Helps decode the data passed to `receiveApproval` using assembly magic.
+
+## Potential Attacks
+
+1. **Sybil resistance?**
+ 1. If I create a lot of accounts for one DApp, will that increase it's ranking?
+ 2. If I vote for one DApp from lots of different accounts, in small amounts, rather than in 1 big amount from a single account, what effect does it have?
+
+Creating many accounts for one DApp is not possible - each DApp is uniquely identified and by its `id` and ranked only by the amount of SNT staked on it. In the same way, there is no quadratic effect in this set up, so staking for a DApp from lots of different accounts in small amounts has no greater/lesser effect on its ranking than staking 1 large amount from a single account.
+
+2. **Incentives to stake bad DApps and "force" the community to spend SNT to downvote?**
+
+Remember, you never get back more SNT than you stake, so this is also economically sub-optimal. In addition, there will be a free "complaint" feature as part of the "downvote" screen. There is an important difference between "contractual" and "social" (i.e. the Status UI) reality. Status reserves the right to remove from our UI any DApp that actively violates [our principles](https://status.im/contribute/our_principles.html), though anyone else is free to fork the software and implement different social/UI rules for the same contractual reality. This protects even further against any incentive to submit bad/damaging DApps.
+
+However, at the beginning of the Store, this is an attack vector: ranking highly requires but a small stake, and this could conceivably result in a successful, cheap hype campaign until we change the UI. The price of freedom is eternal vigilance.
+
+3. **Stake a damaging DApp, force some downvotes, and then withdraw my stake?**
+
+You can still never earn back quite as much as you initially staked, enforced by the condition in the `withdraw` function: `require(_amount <= available)`.
+
+4. **What is left in the store when a DApp withdraws the SNT it staked?**
+
+Simply `balance - available`, i.e. some small amount of SNT not available to be withdrawn.
+
+## Rationale
+
+This is a simple economic mechanism that
+
+1. does not place high mental overheads on users and could conceivably be understood by a wider and non-technical audience and
+2. does not require a lot of screen real estate (important on mobile). All that is required is a balance for each DApp and up/downvote carrots to it's right or left, a pattern already well understood on sites like Reddit etc.
+
+Moreover, having SNT is not required to see (and benefit from) a well-curated list of DApps; only if you want to effect the rankings on that list do you require tokens, which also makes the UX considerably easier for non-technical users.
+
+From the perspective of DApp Developers - they must still spend some capital to rank well, just as they currently do with SEO and AdWords etc., but _they stand to earn most of that back_ if the community votes on their product/service, and they can withdraw their stake at any time. The algorithm is entirely transparent and they know where they stand and why at all times.
+
+## Notes
+
+The beauty of Ethereum to me, can be summed up simply:
+
+`By deploying immutable contracts to a shared, public computational surface - contracts whose data can be read deterministically by anyone with access to the internet - we can encode idealism into the way we run society.`
+
+What's more, **what's different this time**, is that the idealism exists independently of the people who encoded it, who inevitably become corrupted, because we are all human.
+
+However, there is hope in cryptoeconomics, which is not about egalitarianism, but about designing systems with no central point of control. Decentralisation is the goal; egalitarianism is a great success metric. But not the other way around, because egalitarianism is not something for which we can reasonably optimise.
+
+## Copyright
+Copyright and related rights for this specification waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/).
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..00d1ecd
--- /dev/null
+++ b/README.md
@@ -0,0 +1,44 @@
+
+# Discover
+
+Discover new and useful DApps that are mobile-friendly and easy to use. Viewing curated information does not require any special tools, though effecting the way information is ranked will require a web3 wallet, whether that is Status, MetaMask, Trust, Brave or whichever one you prefer.
+
+## Available Scripts
+
+This project is based on Embark v4.0.1, with a few things customised for React. Currently, you'll need to run the app and Embark separately, in different tabs in your terminal.
+
+**`npm run build`**
+
+Builds the app into the `build` directory.
+
+**Steps to run the app:**
+
+* ### `embark run testnet --noserver`
+ Will connect to the ropsten blockchain and IPFS through Infura
+
+ **Ropsten contracts:**
+
+ 1. SNT - 0x2764b5da3696E3613Ef9864E9B4613f9fA478E75
+ 2. Discover - 0x9591a20b9B601651eDF1072A1Dda994C0B1a5bBf
+
+ **Manual needed steps:**
+ Once embark is running:
+ 1. In embarkjs.js (row 532) -> change `this._ipfsConnection.id()` to be `this._ipfsConnection.version()`
+ This is needed because Infura's IPFS has deprecated `id` endpoint, but it was used in embark in order to check if the Infura IPFS API is active.. The workaround above do the same as the deprecated functionality.
+ 2. In embark.json -> Change the row `"generationDir": "src/embarkArtifacts"` to `"generationDir": "embarkArtifacts"`. In this way you should not need to do step 1 every time you run `embark run testnet`.
+
+**`npm run start`**
+
+Runs the app in the development mode.
+
+Open [http://localhost:3000](http://localhost:3000) to view it in the browser. The page will reload if you make edits. You will also see any lint errors in the console.
+
+ **Important!** If you get `can't establish a connection to a node` error, try to open [http://localhost:3000](http://localhost:3000) in chrome browser.
+
+### `embark test`
+
+Will compile your contracts, with hot-reloading, and let you test them locally to your heart's content.
+
+### slither . --exclude naming-convention --filter-paths token
+
+Make sure you get TrailofBits' [latest static analysis tool](https://securityonline.info/slither/), and do your own static analysis on the relevant contracts that will be deployed for Discover.
\ No newline at end of file
diff --git a/config/blockchain.js b/config/blockchain.js
new file mode 100644
index 0000000..a6944ec
--- /dev/null
+++ b/config/blockchain.js
@@ -0,0 +1,149 @@
+module.exports = {
+ // applies to all environments
+ default: {
+ enabled: true,
+ rpcHost: 'localhost', // HTTP-RPC server listening interface (default: "localhost")
+ rpcPort: 8545, // HTTP-RPC server listening port (default: 8545)
+ rpcCorsDomain: {
+ // Domains from which to accept cross origin requests (browser enforced). This can also be a comma separated list
+ auto: true, // When "auto" is true, Embark will automatically set the cors to the address of the webserver
+ additionalCors: [], // Additional CORS domains to add to the list. If "auto" is false, only those will be added
+ },
+ wsRPC: true, // Enable the WS-RPC server
+ wsOrigins: {
+ // Same thing as "rpcCorsDomain", but for WS origins
+ auto: true,
+ additionalCors: [],
+ },
+ wsHost: 'localhost', // WS-RPC server listening interface (default: "localhost")
+ wsPort: 8546, // WS-RPC server listening port (default: 8546)
+
+ // Accounts to use as node accounts
+ // The order here corresponds to the order of `web3.eth.getAccounts`, so the first one is the `defaultAccount`
+ // accounts: [
+ // {
+ // nodeAccounts: true, // Accounts use for the node
+ // numAddresses: '1', // Number of addresses/accounts (defaults to 1)
+ // password: 'config/development/password', // Password file for the accounts
+ // },
+ // Below are additional accounts that will count as `nodeAccounts` in the `deployment` section of your contract config
+ // Those will not be unlocked in the node itself
+ // {
+ // privateKeyFile: 'path/to/file', // Either a keystore or a list of keys, separated by , or ;
+ // password: 'passwordForTheKeystore', // Needed to decrypt the keystore file
+ // },
+ // {
+ // mnemonic: '12 word mnemonic',
+ // addressIndex: '0', // Optional. The index to start getting the address
+ // numAddresses: '1', // Optional. The number of addresses to get
+ // hdpath: "m/44'/60'/0'/0/", // Optional. HD derivation path
+ // },
+ // ],
+ },
+
+ // default environment, merges with the settings in default
+ // assumed to be the intended environment by `embark run` and `embark blockchain`
+ development: {
+ ethereumClientName: 'geth', // Can be geth or parity (default:geth)
+ // ethereumClientBin: "geth", // path to the client binary. Useful if it is not in the global PATH
+ networkType: 'custom', // Can be: testnet, rinkeby, livenet or custom, in which case, it will use the specified networkId
+ networkId: 1337, // Network id used when networkType is custom
+ isDev: true, // Uses and ephemeral proof-of-authority network with a pre-funded developer account, mining enabled
+ datadir: '.embark/development/datadir', // Data directory for the databases and keystore (Geth 1.8.15 and Parity 2.0.4 can use the same base folder, till now they does not conflict with each other)
+ mineWhenNeeded: true, // Uses our custom script (if isDev is false) to mine only when needed
+ nodiscover: true, // Disables the peer discovery mechanism (manual peer addition)
+ maxpeers: 0, // Maximum number of network peers (network disabled if set to 0) (default: 25)
+ proxy: true, // Proxy is used to present meaningful information about transactions
+ targetGasLimit: 9000000, // Target gas limit sets the artificial target gas floor for the blocks to mine
+ simulatorBlocktime: 0, // Specify blockTime in seconds for automatic mining. Default is 0 and no auto-mining.
+ },
+
+ // merges with the settings in default
+ // used with "embark run privatenet" and/or "embark blockchain privatenet"
+ privatenet: {
+ networkType: 'custom',
+ networkId: 1337,
+ isDev: false,
+ datadir: '.embark/privatenet/datadir',
+ // -- mineWhenNeeded --
+ // This options is only valid when isDev is false.
+ // Enabling this option uses our custom script to mine only when needed.
+ // Embark creates a development account for you (using `geth account new`) and funds the account. This account can be used for
+ // development (and even imported in to MetaMask). To enable correct usage, a password for this account must be specified
+ // in the `account > password` setting below.
+ // NOTE: once `mineWhenNeeded` is enabled, you must run an `embark reset` on your dApp before running
+ // `embark blockchain` or `embark run` for the first time.
+ mineWhenNeeded: true,
+ // -- genesisBlock --
+ // This option is only valid when mineWhenNeeded is true (which is only valid if isDev is false).
+ // When enabled, geth uses POW to mine transactions as it would normally, instead of using POA as it does in --dev mode.
+ // On the first `embark blockchain or embark run` after this option is enabled, geth will create a new chain with a
+ // genesis block, which can be configured using the `genesisBlock` configuration option below.
+ genesisBlock: 'config/privatenet/genesis.json', // Genesis block to initiate on first creation of a development node
+ nodiscover: true,
+ maxpeers: 0,
+ proxy: true,
+ accounts: [
+ {
+ nodeAccounts: true,
+ password: 'config/privatenet/password', // Password to unlock the account
+ },
+ ],
+ targetGasLimit: 8000000,
+ simulatorBlocktime: 0,
+ },
+
+ privateparitynet: {
+ ethereumClientName: 'parity',
+ networkType: 'custom',
+ networkId: 1337,
+ isDev: false,
+ genesisBlock: 'config/privatenet/genesis-parity.json', // Genesis block to initiate on first creation of a development node
+ datadir: '.embark/privatenet/datadir',
+ mineWhenNeeded: false,
+ nodiscover: true,
+ maxpeers: 0,
+ proxy: true,
+ accounts: [
+ {
+ nodeAccounts: true,
+ password: 'config/privatenet/password',
+ },
+ ],
+ targetGasLimit: 8000000,
+ simulatorBlocktime: 0,
+ },
+
+ // merges with the settings in default
+ // used with "embark run testnet" and/or "embark blockchain testnet"
+ testnet: {
+ networkType: 'testnet',
+ syncMode: 'light',
+ accounts: [
+ {
+ nodeAccounts: true,
+ password: 'config/testnet/password',
+ },
+ ],
+ },
+
+ // merges with the settings in default
+ // used with "embark run livenet" and/or "embark blockchain livenet"
+ livenet: {
+ networkType: 'livenet',
+ syncMode: 'light',
+ rpcCorsDomain: 'http://localhost:8000',
+ wsOrigins: 'http://localhost:8000',
+ accounts: [
+ {
+ nodeAccounts: true,
+ password: 'config/livenet/password',
+ },
+ ],
+ },
+
+ // you can name an environment with specific settings and then specify with
+ // "embark run custom_name" or "embark blockchain custom_name"
+ // custom_name: {
+ // }
+}
diff --git a/config/communication.js b/config/communication.js
new file mode 100644
index 0000000..c401dcd
--- /dev/null
+++ b/config/communication.js
@@ -0,0 +1,46 @@
+module.exports = {
+ // default applies to all environments
+ default: {
+ enabled: true,
+ provider: "whisper", // Communication provider. Currently, Embark only supports whisper
+ available_providers: ["whisper"], // Array of available providers
+ },
+
+ // default environment, merges with the settings in default
+ // assumed to be the intended environment by `embark run`
+ development: {
+ connection: {
+ host: "localhost", // Host of the blockchain node
+ port: 8546, // Port of the blockchain node
+ type: "ws" // Type of connection (ws or rpc)
+ }
+ },
+
+ // merges with the settings in default
+ // used with "embark run privatenet"
+ privatenet: {
+ },
+
+ // merges with the settings in default
+ // used with "embark run testnet"
+ testnet: {
+ },
+
+ // merges with the settings in default
+ // used with "embark run livenet"
+ livenet: {
+ },
+
+ // you can name an environment with specific settings and then specify with
+ // "embark run custom_name"
+ //custom_name: {
+ //}
+ // Use this section when you need a specific symmetric or private keys in whisper
+ /*
+ ,keys: {
+ symmetricKey: "your_symmetric_key",// Symmetric key for message decryption
+ privateKey: "your_private_key" // Private Key to be used as a signing key and for message decryption
+ }
+ */
+ //}
+};
diff --git a/config/contracts.js b/config/contracts.js
new file mode 100644
index 0000000..ec2727b
--- /dev/null
+++ b/config/contracts.js
@@ -0,0 +1,137 @@
+const wallet = require('./development/mnemonic')
+
+module.exports = {
+ // default applies to all environments
+ default: {
+ // Blockchain node to deploy the contracts
+ deployment: {
+ host: 'localhost', // Host of the blockchain node
+ port: 8545, // Port of the blockchain node
+ type: 'rpc', // Type of connection (ws or rpc),
+ // Accounts to use instead of the default account to populate your wallet
+ // The order here corresponds to the order of `web3.eth.getAccounts`, so the first one is the `defaultAccount`
+ /* ,accounts: [
+ {
+ privateKey: "your_private_key",
+ balance: "5 ether" // You can set the balance of the account in the dev environment
+ // Balances are in Wei, but you can specify the unit with its name
+ },
+ {
+ privateKeyFile: "path/to/file", // Either a keystore or a list of keys, separated by , or ;
+ password: "passwordForTheKeystore" // Needed to decrypt the keystore file
+ },
+ {
+ mnemonic: "12 word mnemonic",
+ addressIndex: "0", // Optional. The index to start getting the address
+ numAddresses: "1", // Optional. The number of addresses to get
+ hdpath: "m/44'/60'/0'/0/" // Optional. HD derivation path
+ },
+ {
+ "nodeAccounts": true // Uses the Ethereum node's accounts
+ }
+ ] */
+
+ accounts: [
+ {
+ mnemonic: wallet.mnemonic,
+ balance: '1534983463450 ether',
+ },
+ ],
+ },
+ // order of connections the dapp should connect to
+ // dappConnection: [
+ // '$WEB3', // uses pre existing web3 object if available (e.g in Mist)
+ // 'ws://localhost:8546',
+ // 'http://localhost:8545',
+ // ],
+
+ // Automatically call `ethereum.enable` if true.
+ // If false, the following code must run before sending any transaction: `await EmbarkJS.enableEthereum();`
+ // Default value is true.
+ // dappAutoEnable: true,
+
+ gas: 'auto',
+
+ // Strategy for the deployment of the contracts:
+ // - implicit will try to deploy all the contracts located inside the contracts directory
+ // or the directory configured for the location of the contracts. This is default one
+ // when not specified
+ // - explicit will only attempt to deploy the contracts that are explicitly specified inside the
+ // contracts section.
+ // strategy: 'implicit',
+
+ // contracts: {
+ // Discover: {
+ // args: { _SNT: '0x744d70fdbe2ba4cf95131626614a1763df805b9e' },
+ // },
+ // MiniMeToken: { deploy: false },
+ // TestBancorFormula: { deploy: false },
+ // },
+
+ contracts: {
+ MiniMeToken: { deploy: false },
+ BancorFormula: { deploy: false },
+ MiniMeTokenFactory: { deploy: false },
+ SafeMath: { deploy: false },
+ TestBancorFormula: { deploy: false },
+ SNT: {
+ instanceOf: 'MiniMeToken',
+ address: '0x2764b5da3696E3613Ef9864E9B4613f9fA478E75',
+ },
+ Discover: { address: '0x9591a20b9B601651eDF1072A1Dda994C0B1a5bBf' },
+ // SNT: {
+ // instanceOf: 'MiniMeToken',
+ // args: [
+ // '$MiniMeTokenFactory',
+ // '0x0000000000000000000000000000000000000000',
+ // 0,
+ // 'TestMiniMeToken',
+ // 18,
+ // 'SNT',
+ // true,
+ // ],
+ // },
+ // Discover: {
+ // args: ['$SNT'],
+ // },
+ },
+ },
+
+ // default environment, merges with the settings in default
+ // assumed to be the intended environment by `embark run`
+ development: {
+ dappConnection: [
+ 'ws://localhost:8546',
+ 'http://localhost:8545',
+ '$WEB3', // uses pre existing web3 object if available (e.g in Mist)
+ ],
+ },
+
+ // merges with the settings in default
+ // used with "embark run privatenet"
+ privatenet: {},
+
+ // merges with the settings in default
+ // used with "embark run testnet"
+ testnet: {
+ deployment: {
+ accounts: [{ mnemonic: wallet.mnemonic }],
+ host: `ropsten.infura.io/v3/8675214b97b44e96b70d05326c61fd6a`,
+ port: false,
+ type: 'rpc',
+ protocol: 'https',
+ },
+ dappConnection: [
+ 'https://ropsten.infura.io/v3/8675214b97b44e96b70d05326c61fd6a',
+ ],
+ },
+
+ // merges with the settings in default
+ // used with "embark run livenet"
+ livenet: {},
+
+ // you can name an environment with specific settings and then specify with
+ // "embark run custom_name" or "embark blockchain custom_name"
+ // custom_name: {
+ // }
+}
diff --git a/config/development/mnemonic.js b/config/development/mnemonic.js
new file mode 100644
index 0000000..c95294e
--- /dev/null
+++ b/config/development/mnemonic.js
@@ -0,0 +1,2 @@
+module.exports.mnemonic =
+ ''
diff --git a/config/development/password b/config/development/password
new file mode 100644
index 0000000..fca906b
--- /dev/null
+++ b/config/development/password
@@ -0,0 +1 @@
+dev_password
\ No newline at end of file
diff --git a/config/namesystem.js b/config/namesystem.js
new file mode 100644
index 0000000..f3d1446
--- /dev/null
+++ b/config/namesystem.js
@@ -0,0 +1,39 @@
+module.exports = {
+ // default applies to all environments
+ default: {
+ enabled: true,
+ available_providers: ["ens"],
+ provider: "ens"
+ },
+
+ // default environment, merges with the settings in default
+ // assumed to be the intended environment by `embark run`
+ development: {
+ register: {
+ rootDomain: "embark.eth",
+ subdomains: {
+ 'status': '0x1a2f3b98e434c02363f3dac3174af93c1d690914'
+ }
+ }
+ },
+
+ // merges with the settings in default
+ // used with "embark run privatenet"
+ privatenet: {
+ },
+
+ // merges with the settings in default
+ // used with "embark run testnet"
+ testnet: {
+ },
+
+ // merges with the settings in default
+ // used with "embark run livenet"
+ livenet: {
+ },
+
+ // you can name an environment with specific settings and then specify with
+ // "embark run custom_name" or "embark blockchain custom_name"
+ //custom_name: {
+ //}
+};
diff --git a/config/pipeline.js b/config/pipeline.js
new file mode 100644
index 0000000..7de7b28
--- /dev/null
+++ b/config/pipeline.js
@@ -0,0 +1,27 @@
+// Embark has support for Flow enabled by default in its built-in webpack
+// config: type annotations will automatically be stripped out of DApp sources
+// without any additional configuration. Note that type checking is not
+// performed during builds.
+
+// To enable Flow type checking refer to the preconfigured template:
+// https://github.com/embark-framework/embark-flow-template
+// A new DApp can be created from that template with:
+// embark new --template flow
+
+module.exports = {
+ typescript: false,
+ // Setting `typescript: true` in this config will disable Flow support in
+ // Embark's default webpack config and enable TypeScript support: .ts and
+ // .tsx sources will automatically be transpiled into JavaScript without any
+ // additional configuration. Note that type checking is not performed during
+ // builds.
+
+ // To enable TypeScript type checking refer to the preconfigured template:
+ // https://github.com/embark-framework/embark-typescript-template
+ // A new DApp can be created from that template with:
+ // embark new --template typescript
+ enabled: true
+ // Setting `enabled: false` in this config will disable Embark's built-in Webpack
+ // pipeline. The developer will need to use a different frontend build tool, such as
+ // `create-react-app` or Angular CLI to build their dapp
+};
diff --git a/config/privatenet/genesis-parity.json b/config/privatenet/genesis-parity.json
new file mode 100644
index 0000000..03f574b
--- /dev/null
+++ b/config/privatenet/genesis-parity.json
@@ -0,0 +1,147 @@
+{
+ "name": "DevelopmentChain",
+ "engine": {
+ "instantSeal": null
+ },
+ "params": {
+ "gasLimitBoundDivisor": "0x0400",
+ "accountStartNonce": "0x0",
+ "maximumExtraDataSize": "0x20",
+ "minGasLimit": "0x1388",
+ "networkID": "0x11",
+ "registrar": "0x0000000000000000000000000000000000001337",
+ "eip150Transition": "0x0",
+ "eip160Transition": "0x0",
+ "eip161abcTransition": "0x0",
+ "eip161dTransition": "0x0",
+ "eip155Transition": "0x0",
+ "eip98Transition": "0x7fffffffffffff",
+ "eip86Transition": "0x7fffffffffffff",
+ "maxCodeSize": 24576,
+ "maxCodeSizeTransition": "0x0",
+ "eip140Transition": "0x0",
+ "eip211Transition": "0x0",
+ "eip214Transition": "0x0",
+ "eip658Transition": "0x0",
+ "wasmActivationTransition": "0x0"
+ },
+ "genesis": {
+ "seal": {
+ "generic": "0x0"
+ },
+ "difficulty": "0x20000",
+ "author": "0x0000000000000000000000000000000000000000",
+ "timestamp": "0x00",
+ "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "extraData": "0x",
+ "gasLimit": "0x7A1200"
+ },
+ "accounts": {
+ "0000000000000000000000000000000000000001": {
+ "balance": "1",
+ "builtin": {
+ "name": "ecrecover",
+ "pricing": {
+ "linear": {
+ "base": 3000,
+ "word": 0
+ }
+ }
+ }
+ },
+ "0000000000000000000000000000000000000002": {
+ "balance": "1",
+ "builtin": {
+ "name": "sha256",
+ "pricing": {
+ "linear": {
+ "base": 60,
+ "word": 12
+ }
+ }
+ }
+ },
+ "0000000000000000000000000000000000000003": {
+ "balance": "1",
+ "builtin": {
+ "name": "ripemd160",
+ "pricing": {
+ "linear": {
+ "base": 600,
+ "word": 120
+ }
+ }
+ }
+ },
+ "0000000000000000000000000000000000000004": {
+ "balance": "1",
+ "builtin": {
+ "name": "identity",
+ "pricing": {
+ "linear": {
+ "base": 15,
+ "word": 3
+ }
+ }
+ }
+ },
+ "0000000000000000000000000000000000000005": {
+ "balance": "1",
+ "builtin": {
+ "name": "modexp",
+ "activate_at": 0,
+ "pricing": {
+ "modexp": {
+ "divisor": 20
+ }
+ }
+ }
+ },
+ "0000000000000000000000000000000000000006": {
+ "balance": "1",
+ "builtin": {
+ "name": "alt_bn128_add",
+ "activate_at": 0,
+ "pricing": {
+ "linear": {
+ "base": 500,
+ "word": 0
+ }
+ }
+ }
+ },
+ "0000000000000000000000000000000000000007": {
+ "balance": "1",
+ "builtin": {
+ "name": "alt_bn128_mul",
+ "activate_at": 0,
+ "pricing": {
+ "linear": {
+ "base": 40000,
+ "word": 0
+ }
+ }
+ }
+ },
+ "0000000000000000000000000000000000000008": {
+ "balance": "1",
+ "builtin": {
+ "name": "alt_bn128_pairing",
+ "activate_at": 0,
+ "pricing": {
+ "alt_bn128_pairing": {
+ "base": 100000,
+ "pair": 80000
+ }
+ }
+ }
+ },
+ "0000000000000000000000000000000000001337": {
+ "balance": "1",
+ "constructor": "0x606060405233600060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550670de0b6b3a764000060035534610000575b612904806100666000396000f3006060604052361561013c576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806306b2ff471461014157806313af40351461018c57806319362a28146101bf5780633f3935d114610248578063432ced04146102b75780634f39ca59146102eb5780636795dbcd1461032457806369fe0e2d146103c857806379ce9fac146103fd5780638da5cb5b1461045557806390b97fc1146104a457806392698814146105245780639890220b1461055d578063ac4e73f914610584578063ac72c12014610612578063c3a358251461064b578063ddca3f43146106c3578063deb931a2146106e6578063df57b74214610747578063e30bd740146107a8578063eadf976014610862578063ef5454d6146108e7578063f25eb5c114610975578063f6d339e414610984575b610000565b3461000057610172600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610a1f565b604051808215151515815260200191505060405180910390f35b34610000576101bd600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610a81565b005b346100005761022e60048080356000191690602001909190803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509190803560001916906020019091905050610ba2565b604051808215151515815260200191505060405180910390f35b346100005761029d600480803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091905050610dc9565b604051808215151515815260200191505060405180910390f35b6102d1600480803560001916906020019091905050611035565b604051808215151515815260200191505060405180910390f35b346100005761030a60048080356000191690602001909190505061115f565b604051808215151515815260200191505060405180910390f35b346100005761038660048080356000191690602001909190803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091905050611378565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34610000576103e3600480803590602001909190505061140d565b604051808215151515815260200191505060405180910390f35b346100005761043b60048080356000191690602001909190803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506114b4565b604051808215151515815260200191505060405180910390f35b34610000576104626115fb565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b346100005761050660048080356000191690602001909190803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091905050611621565b60405180826000191660001916815260200191505060405180910390f35b34610000576105436004808035600019169060200190919050506116b2565b604051808215151515815260200191505060405180910390f35b346100005761056a611715565b604051808215151515815260200191505060405180910390f35b34610000576105f8600480803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050611824565b604051808215151515815260200191505060405180910390f35b3461000057610631600480803560001916906020019091905050611d8b565b604051808215151515815260200191505060405180910390f35b34610000576106ad60048080356000191690602001909190803590602001908201803590602001908080601f01602080910402602001604051908101604052809392919081815260200183838082843782019150505050505091905050611dee565b6040518082815260200191505060405180910390f35b34610000576106d0611e83565b6040518082815260200191505060405180910390f35b3461000057610705600480803560001916906020019091905050611e89565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b3461000057610766600480803560001916906020019091905050611ed2565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34610000576107d9600480803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050611f1b565b6040518080602001828103825283818151815260200191508051906020019080838360008314610828575b80518252602083111561082857602082019150602081019050602083039250610804565b505050905090810190601f1680156108545780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34610000576108cd60048080356000191690602001909190803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509190803590602001909190505061200c565b604051808215151515815260200191505060405180910390f35b346100005761095b600480803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050612236565b604051808215151515815260200191505060405180910390f35b3461000057610982612425565b005b3461000057610a0560048080356000191690602001909190803590602001908201803590602001908080601f0160208091040260200160405190810160405280939291908181526020018383808284378201915050505050509190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050612698565b604051808215151515815260200191505060405180910390f35b60006000600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020805460018160011615610100020316600290049050141590505b919050565b600060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141515610add57610b9f565b8073ffffffffffffffffffffffffffffffffffffffff16600060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f70aea8d848e8a90fb7661b227dc522eb6395c3dac71b63cb59edd5c9899b236460405180905060405180910390a380600060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505b5b50565b6000833373ffffffffffffffffffffffffffffffffffffffff1660016000836000191660001916815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141515610c1d57610dc1565b82600160008760001916600019168152602001908152602001600020600201856040518082805190602001908083835b60208310610c705780518252602082019150602081019050602083039250610c4d565b6001836020036101000a03801982511681845116808217855250505050505090500191505090815260200160405180910390208160001916905550836040518082805190602001908083835b60208310610cdf5780518252602082019150602081019050602083039250610cbc565b6001836020036101000a038019825116818451168082178552505050505050905001915050604051809103902085600019167fb829c3e412537bbe794c048ccb9e4605bb4aaaa8e4d4c15c1a6e0c2adc1716ea866040518080602001828103825283818151815260200191508051906020019080838360008314610d82575b805182526020831115610d8257602082019150602081019050602083039250610d5e565b505050905090810190601f168015610dae5780820380516001836020036101000a031916815260200191505b509250505060405180910390a3600191505b5b509392505050565b6000813373ffffffffffffffffffffffffffffffffffffffff1660016000836040518082805190602001908083835b60208310610e1b5780518252602082019150602081019050602083039250610df8565b6001836020036101000a03801982511681845116808217855250505050505090500191505060405180910390206000191660001916815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141515610ea45761102f565b82600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000209080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f10610f2d57805160ff1916838001178555610f5b565b82800160010185558215610f5b579182015b82811115610f5a578251825591602001919060010190610f3f565b5b509050610f8091905b80821115610f7c576000816000905550600101610f64565b5090565b50503373ffffffffffffffffffffffffffffffffffffffff16836040518082805190602001908083835b60208310610fcd5780518252602082019150602081019050602083039250610faa565b6001836020036101000a03801982511681845116808217855250505050505090500191505060405180910390207f098ae8581bb8bd9af1beaf7f2e9f51f31a8e5a8bfada4e303a645d71d9c9192060405180905060405180910390a3600191505b5b50919050565b600081600060016000836000191660001916815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614151561109b57611159565b6003543410156110aa57611158565b3360016000856000191660001916815260200190815260200160002060000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055503373ffffffffffffffffffffffffffffffffffffffff1683600019167f4963513eca575aba66fdcd25f267aae85958fe6fb97e75fa25d783f1a091a22160405180905060405180910390a3600191505b5b5b50919050565b6000813373ffffffffffffffffffffffffffffffffffffffff1660016000836000191660001916815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415156111da57611372565b6002600060016000866000191660001916815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020805460018160011615610100020316600290046000825580601f1061127c57506112b3565b601f0160209004906000526020600020908101906112b291905b808211156112ae576000816000905550600101611296565b5090565b5b5060016000846000191660001916815260200190815260200160002060006000820160006101000a81549073ffffffffffffffffffffffffffffffffffffffff02191690556001820160006101000a81549073ffffffffffffffffffffffffffffffffffffffff021916905550503373ffffffffffffffffffffffffffffffffffffffff1683600019167fef1961b4d2909dc23643b309bfe5c3e5646842d98c3a58517037ef3871185af360405180905060405180910390a3600191505b5b50919050565b6000600160008460001916600019168152602001908152602001600020600201826040518082805190602001908083835b602083106113cc57805182526020820191506020810190506020830392506113a9565b6001836020036101000a0380198251168184511680821785525050505050509050019150509081526020016040518091039020546001900490505b92915050565b6000600060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561146b576114af565b816003819055507f6bbc57480a46553fa4d156ce702beef5f3ad66303b0ed1a5d4cb44966c6584c3826040518082815260200191505060405180910390a1600190505b5b919050565b6000823373ffffffffffffffffffffffffffffffffffffffff1660016000836000191660001916815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614151561152f576115f4565b8260016000866000191660001916815260200190815260200160002060000160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1685600019167f7b97c62130aa09acbbcbf7482630e756592496f1759eaf702f469cf64dfb779460405180905060405180910390a4600191505b5b5092915050565b600060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000600160008460001916600019168152602001908152602001600020600201826040518082805190602001908083835b602083106116755780518252602082019150602081019050602083039250611652565b6001836020036101000a03801982511681845116808217855250505050505090500191505090815260200160405180910390205490505b92915050565b6000600060016000846000191660001916815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141590505b919050565b6000600060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614151561177357611821565b7fdef931299fe61d176f949118058530c1f3f539dcb6950b4e372c9b835c33ca073073ffffffffffffffffffffffffffffffffffffffff16316040518082815260200191505060405180910390a13373ffffffffffffffffffffffffffffffffffffffff166108fc3073ffffffffffffffffffffffffffffffffffffffff16319081150290604051809050600060405180830381858888f19350505050151561181b57610000565b600190505b5b90565b60006000836040518082805190602001908083835b6020831061185c5780518252602082019150602081019050602083039250611839565b6001836020036101000a03801982511681845116808217855250505050505090500191505060405180910390203373ffffffffffffffffffffffffffffffffffffffff1660016000836000191660001916815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614151561190157611d83565b846040518082805190602001908083835b602083106119355780518252602082019150602081019050602083039250611912565b6001836020036101000a03801982511681845116808217855250505050505090500191505060405180910390209150600060016000846000191660001916815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614158015611ab4575081600019166002600060016000866000191660001916815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206040518082805460018160011615610100020316600290048015611aa15780601f10611a7f576101008083540402835291820191611aa1565b820191906000526020600020905b815481529060010190602001808311611a8d575b5050915050604051809103902060001916145b15611c79576002600060016000856000191660001916815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020805460018160011615610100020316600290046000825580601f10611b5b5750611b92565b601f016020900490600052602060002090810190611b9191905b80821115611b8d576000816000905550600101611b75565b5090565b5b5060016000836000191660001916815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16856040518082805190602001908083835b60208310611c1c5780518252602082019150602081019050602083039250611bf9565b6001836020036101000a03801982511681845116808217855250505050505090500191505060405180910390207f12491ad95fd945e444d88a894ffad3c21959880a4dcd8af99d4ae4ffc71d4abd60405180905060405180910390a35b8360016000846000191660001916815260200190815260200160002060010160006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508373ffffffffffffffffffffffffffffffffffffffff16856040518082805190602001908083835b60208310611d215780518252602082019150602081019050602083039250611cfe565b6001836020036101000a03801982511681845116808217855250505050505090500191505060405180910390207f728435a0031f6a04538fcdd24922a7e06bc7bc945db03e83d22122d1bc5f28df60405180905060405180910390a3600192505b5b505092915050565b6000600060016000846000191660001916815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141590505b919050565b6000600160008460001916600019168152602001908152602001600020600201826040518082805190602001908083835b60208310611e425780518252602082019150602081019050602083039250611e1f565b6001836020036101000a0380198251168184511680821785525050505050509050019150509081526020016040518091039020546001900490505b92915050565b60035481565b600060016000836000191660001916815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690505b919050565b600060016000836000191660001916815260200190815260200160002060010160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1690505b919050565b6020604051908101604052806000815250600260008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015611fff5780601f10611fd457610100808354040283529160200191611fff565b820191906000526020600020905b815481529060010190602001808311611fe257829003601f168201915b505050505090505b919050565b6000833373ffffffffffffffffffffffffffffffffffffffff1660016000836000191660001916815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff161415156120875761222e565b82600102600160008760001916600019168152602001908152602001600020600201856040518082805190602001908083835b602083106120dd57805182526020820191506020810190506020830392506120ba565b6001836020036101000a03801982511681845116808217855250505050505090500191505090815260200160405180910390208160001916905550836040518082805190602001908083835b6020831061214c5780518252602082019150602081019050602083039250612129565b6001836020036101000a038019825116818451168082178552505050505050905001915050604051809103902085600019167fb829c3e412537bbe794c048ccb9e4605bb4aaaa8e4d4c15c1a6e0c2adc1716ea8660405180806020018281038252838181518152602001915080519060200190808383600083146121ef575b8051825260208311156121ef576020820191506020810190506020830392506121cb565b505050905090810190601f16801561221b5780820380516001836020036101000a031916815260200191505b509250505060405180910390a3600191505b5b509392505050565b6000600060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161415156122945761241f565b82600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000209080519060200190828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061231d57805160ff191683800117855561234b565b8280016001018555821561234b579182015b8281111561234a57825182559160200191906001019061232f565b5b50905061237091905b8082111561236c576000816000905550600101612354565b5090565b50508173ffffffffffffffffffffffffffffffffffffffff16836040518082805190602001908083835b602083106123bd578051825260208201915060208101905060208303925061239a565b6001836020036101000a03801982511681845116808217855250505050505090500191505060405180910390207f098ae8581bb8bd9af1beaf7f2e9f51f31a8e5a8bfada4e303a645d71d9c9192060405180905060405180910390a3600190505b5b92915050565b3373ffffffffffffffffffffffffffffffffffffffff16600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060405180828054600181600116156101000203166002900480156124d65780601f106124b45761010080835404028352918201916124d6565b820191906000526020600020905b8154815290600101906020018083116124c2575b505091505060405180910390207f12491ad95fd945e444d88a894ffad3c21959880a4dcd8af99d4ae4ffc71d4abd60405180905060405180910390a360016000600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060405180828054600181600116156101000203166002900480156125b05780601f1061258e5761010080835404028352918201916125b0565b820191906000526020600020905b81548152906001019060200180831161259c575b505091505060405180910390206000191660001916815260200190815260200160002060010160006101000a81549073ffffffffffffffffffffffffffffffffffffffff0219169055600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020805460018160011615610100020316600290046000825580601f1061265d5750612694565b601f01602090049060005260206000209081019061269391905b8082111561268f576000816000905550600101612677565b5090565b5b505b565b6000833373ffffffffffffffffffffffffffffffffffffffff1660016000836000191660001916815260200190815260200160002060000160009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16141515612713576128d0565b8273ffffffffffffffffffffffffffffffffffffffff16600102600160008760001916600019168152602001908152602001600020600201856040518082805190602001908083835b6020831061277f578051825260208201915060208101905060208303925061275c565b6001836020036101000a03801982511681845116808217855250505050505090500191505090815260200160405180910390208160001916905550836040518082805190602001908083835b602083106127ee57805182526020820191506020810190506020830392506127cb565b6001836020036101000a038019825116818451168082178552505050505050905001915050604051809103902085600019167fb829c3e412537bbe794c048ccb9e4605bb4aaaa8e4d4c15c1a6e0c2adc1716ea866040518080602001828103825283818151815260200191508051906020019080838360008314612891575b8051825260208311156128915760208201915060208101905060208303925061286d565b505050905090810190601f1680156128bd5780820380516001836020036101000a031916815260200191505b509250505060405180910390a3600191505b5b5093925050505600a165627a7a7230582066b2da4773a0f1d81efe071c66b51c46868a871661efd18c0f629353ff4c1f9b0029"
+ },
+ "00a329c0648769a73afac7f9381e08fb43dbea72": {
+ "balance": "1606938044258990275541962092341162602522202993782792835301376"
+ }
+ }
+}
\ No newline at end of file
diff --git a/config/privatenet/genesis.json b/config/privatenet/genesis.json
new file mode 100644
index 0000000..a6c3940
--- /dev/null
+++ b/config/privatenet/genesis.json
@@ -0,0 +1,20 @@
+{
+ "config": {
+ "homesteadBlock": 0,
+ "byzantiumBlock": 0,
+ "eip155Block": 0,
+ "eip158Block": 0,
+ "daoForkSupport": true
+ },
+ "nonce": "0x0000000000000042",
+ "difficulty": "0x0",
+ "alloc": {
+ "0x3333333333333333333333333333333333333333": {"balance": "15000000000000000000"}
+ },
+ "mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "coinbase": "0x3333333333333333333333333333333333333333",
+ "timestamp": "0x00",
+ "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "extraData": "0x",
+ "gasLimit": "0x7a1200"
+}
diff --git a/config/privatenet/password b/config/privatenet/password
new file mode 100644
index 0000000..c747d67
--- /dev/null
+++ b/config/privatenet/password
@@ -0,0 +1 @@
+dev_password
diff --git a/config/storage.js b/config/storage.js
new file mode 100644
index 0000000..62484a0
--- /dev/null
+++ b/config/storage.js
@@ -0,0 +1,74 @@
+module.exports = {
+ // default applies to all environments
+ default: {
+ enabled: true,
+ ipfs_bin: 'ipfs',
+ provider: 'ipfs',
+ available_providers: ['ipfs'],
+ upload: {
+ host: 'localhost',
+ port: 5001,
+ },
+ dappConnection: [
+ {
+ provider: 'ipfs',
+ host: 'localhost',
+ port: 5001,
+ getUrl: 'http://localhost:8080/ipfs/',
+ },
+ ],
+ // Configuration to start Swarm in the same terminal as `embark run`
+ /* ,account: {
+ address: "YOUR_ACCOUNT_ADDRESS", // Address of account accessing Swarm
+ password: "PATH/TO/PASSWORD/FILE" // File containing the password of the account
+ },
+ swarmPath: "PATH/TO/SWARM/EXECUTABLE" // Path to swarm executable (default: swarm) */
+ },
+
+ // default environment, merges with the settings in default
+ // assumed to be the intended environment by `embark run`
+ development: {
+ enabled: true,
+ upload: {
+ provider: 'ipfs',
+ host: 'localhost',
+ port: 5001,
+ getUrl: 'http://localhost:8080/ipfs/',
+ },
+ },
+
+ // merges with the settings in default
+ // used with "embark run privatenet"
+ privatenet: {},
+
+ // merges with the settings in default
+ // used with "embark run testnet"
+ testnet: {
+ enabled: true,
+ ipfs_bin: 'ipfs',
+ provider: 'ipfs',
+ available_providers: ['ipfs'],
+ upload: {
+ host: 'localhost',
+ port: 5001,
+ },
+ dappConnection: [
+ {
+ provider: 'ipfs',
+ protocol: 'https',
+ host: 'ipfs.infura.io',
+ port: 5001,
+ getUrl: 'https://ipfs.infura.io/ipfs/',
+ },
+ ],
+ },
+
+ // merges with the settings in default
+ // used with "embark run livenet"
+ livenet: {},
+
+ // you can name an environment with specific settings and then specify with
+ // "embark run custom_name"
+ // custom_name: {
+ // }
+}
diff --git a/config/testnet/password b/config/testnet/password
new file mode 100644
index 0000000..414f849
--- /dev/null
+++ b/config/testnet/password
@@ -0,0 +1 @@
+test_password
diff --git a/config/webserver.js b/config/webserver.js
new file mode 100644
index 0000000..506490b
--- /dev/null
+++ b/config/webserver.js
@@ -0,0 +1,6 @@
+module.exports = {
+ enabled: true,
+ host: "localhost",
+ openBrowser: true,
+ port: 8000
+};
diff --git a/contracts/.gitkeep b/contracts/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/contracts/Discover.sol b/contracts/Discover.sol
new file mode 100644
index 0000000..f10b35a
--- /dev/null
+++ b/contracts/Discover.sol
@@ -0,0 +1,395 @@
+pragma solidity ^0.5.2;
+
+import "./token/MiniMeTokenInterface.sol";
+import "./token/ApproveAndCallFallBack.sol";
+import "./utils/SafeMath.sol";
+import "./utils/BancorFormula.sol";
+
+
+contract Discover is ApproveAndCallFallBack, BancorFormula {
+ using SafeMath for uint;
+
+ // Could be any MiniMe token
+ MiniMeTokenInterface SNT;
+
+ // Total SNT in circulation
+ uint public total;
+
+ // Parameter to calculate Max SNT any one DApp can stake
+ uint public ceiling;
+
+ // The max amount of tokens it is possible to stake, as a percentage of the total in circulation
+ uint public max;
+
+ // Decimal precision for this contract
+ uint public decimals;
+
+ // Prevents overflows in votesMinted
+ uint public safeMax;
+
+ // Whether we need more than an id param to identify arbitrary data must still be discussed.
+ struct Data {
+ address developer;
+ bytes32 id;
+ bytes32 metadata;
+ uint balance;
+ uint rate;
+ uint available;
+ uint votesMinted;
+ uint votesCast;
+ uint effectiveBalance;
+ }
+
+ Data[] public dapps;
+ mapping(bytes32 => uint) public id2index;
+ mapping(bytes32 => bool) existingIDs;
+
+ event DAppCreated(bytes32 indexed id, uint newEffectiveBalance);
+ event Upvote(bytes32 indexed id, uint newEffectiveBalance);
+ event Downvote(bytes32 indexed id, uint newEffectiveBalance);
+ event Withdraw(bytes32 indexed id, uint newEffectiveBalance);
+ event MetadataUpdated(bytes32 indexed id);
+
+ constructor(MiniMeTokenInterface _SNT) public {
+ SNT = _SNT;
+
+ total = 6804870174;
+
+ ceiling = 292; // See here for more: https://observablehq.com/@andytudhope/dapp-store-snt-curation-mechanism
+
+ decimals = 1000000; // 4 decimal points for %, 2 because we only use 1/100th of total in circulation
+
+ max = total.mul(ceiling).div(decimals);
+
+ safeMax = uint(77).mul(max).div(100); // Limited by accuracy of BancorFormula
+ }
+
+ /**
+ * @dev Anyone can create a DApp (i.e an arb piece of data this contract happens to care about).
+ * @param _id bytes32 unique identifier.
+ * @param _amount of tokens to stake on initial ranking.
+ * @param _metadata metadata hex string
+ */
+ function createDApp(bytes32 _id, uint _amount, bytes32 _metadata) external {
+ _createDApp(
+ msg.sender,
+ _id,
+ _amount,
+ _metadata);
+ }
+
+ /**
+ * @dev Sends SNT directly to the contract, not the developer. This gets added to the DApp's balance, no curve required.
+ * @param _id bytes32 unique identifier.
+ * @param _amount of tokens to stake on DApp's ranking. Used for upvoting + staking more.
+ */
+ function upvote(bytes32 _id, uint _amount) external {
+ _upvote(msg.sender, _id, _amount);
+ }
+
+ /**
+ * @dev Sends SNT to the developer and lowers the DApp's effective balance by 1%
+ * @param _id bytes32 unique identifier.
+ * @param _amount uint, included for approveAndCallFallBack
+ */
+ function downvote(bytes32 _id, uint _amount) external {
+ _downvote(msg.sender, _id, _amount);
+ }
+
+ /**
+ * @dev Developers can withdraw an amount not more than what was available of the
+ SNT they originally staked minus what they have already received back in downvotes.
+ * @param _id bytes32 unique identifier.
+ * @param _amount of tokens to withdraw from DApp's overall balance.
+ */
+ function withdraw(bytes32 _id, uint _amount) external {
+ Data storage d = _getDAppById(_id);
+
+ require(msg.sender == d.developer, "Only the developer can withdraw SNT staked on this data");
+ require(_amount <= d.available, "You can only withdraw a percentage of the SNT staked, less what you have already received");
+
+ uint precision;
+ uint result;
+
+ d.balance = d.balance.sub(_amount);
+ d.rate = decimals.sub(d.balance.mul(decimals).div(max));
+ d.available = d.balance.mul(d.rate);
+
+ (result, precision) = BancorFormula.power(
+ d.available,
+ decimals,
+ uint32(decimals),
+ uint32(d.rate));
+
+ d.votesMinted = result >> precision;
+ if (d.votesCast > d.votesMinted) {
+ d.votesCast = d.votesMinted;
+ }
+
+ uint temp1 = d.votesCast.mul(d.rate).mul(d.available);
+ uint temp2 = d.votesMinted.mul(decimals).mul(decimals);
+ uint effect = temp1.div(temp2);
+
+ d.effectiveBalance = d.balance.sub(effect);
+
+ require(SNT.transfer(d.developer, _amount), "Transfer failed");
+
+ emit Withdraw(_id, d.effectiveBalance);
+ }
+
+ /**
+ * dev Set the content for the dapp
+ * @param _id bytes32 unique identifier.
+ * @param _metadata metadata info
+ */
+ function setMetadata(bytes32 _id, bytes32 _metadata) external {
+ uint dappIdx = id2index[_id];
+ Data storage d = dapps[dappIdx];
+ require(d.developer == msg.sender, "Only the developer can update the metadata");
+ d.metadata = _metadata;
+ emit MetadataUpdated(_id);
+ }
+
+ /**
+ * @dev Used in UI in order to fetch all dapps
+ * @return dapps count
+ */
+ function getDAppsCount() external view returns(uint) {
+ return dapps.length;
+ }
+
+ /**
+ * @notice Support for "approveAndCall".
+ * @param _from Who approved.
+ * @param _amount Amount being approved, needs to be equal `_amount` or `cost`.
+ * @param _token Token being approved, needs to be `SNT`.
+ * @param _data Abi encoded data with selector of `register(bytes32,address,bytes32,bytes32)`.
+ */
+ function receiveApproval(
+ address _from,
+ uint256 _amount,
+ address _token,
+ bytes calldata _data
+ )
+ external
+ {
+ require(_token == address(SNT), "Wrong token");
+ require(_token == address(msg.sender), "Wrong account");
+ require(_data.length <= 196, "Incorrect data");
+
+ bytes4 sig;
+ bytes32 id;
+ uint256 amount;
+ bytes32 metadata;
+
+ (sig, id, amount, metadata) = abiDecodeRegister(_data);
+ require(_amount == amount, "Wrong amount");
+
+ if (sig == bytes4(0x7e38d973)) {
+ _createDApp(
+ _from,
+ id,
+ amount,
+ metadata);
+ } else if (sig == bytes4(0xac769090)) {
+ _downvote(_from, id, amount);
+ } else if (sig == bytes4(0x2b3df690)) {
+ _upvote(_from, id, amount);
+ } else {
+ revert("Wrong method selector");
+ }
+ }
+
+ /**
+ * @dev Used in UI to display effect on ranking of user's donation
+ * @param _id bytes32 unique identifier.
+ * @param _amount of tokens to stake/"donate" to this DApp's ranking.
+ * @return effect of donation on DApp's effectiveBalance
+ */
+ function upvoteEffect(bytes32 _id, uint _amount) external view returns(uint effect) {
+ Data memory d = _getDAppById(_id);
+ require(d.balance.add(_amount) <= safeMax, "You cannot upvote by this much, try with a lower amount");
+
+ // Special case - no downvotes yet cast
+ if (d.votesCast == 0) {
+ return _amount;
+ }
+
+ uint precision;
+ uint result;
+
+ uint mBalance = d.balance.add(_amount);
+ uint mRate = decimals.sub(mBalance.mul(decimals).div(max));
+ uint mAvailable = mBalance.mul(mRate);
+
+ (result, precision) = BancorFormula.power(
+ mAvailable,
+ decimals,
+ uint32(decimals),
+ uint32(mRate));
+
+ uint mVMinted = result >> precision;
+
+ uint temp1 = d.votesCast.mul(mRate).mul(mAvailable);
+ uint temp2 = mVMinted.mul(decimals).mul(decimals);
+ uint mEffect = temp1.div(temp2);
+
+ uint mEBalance = mBalance.sub(mEffect);
+
+ return (mEBalance.sub(d.effectiveBalance));
+ }
+
+ /**
+ * @dev Downvotes always remove 1% of the current ranking.
+ * @param _id bytes32 unique identifier.
+ * @return balance_down_by, votes_required, cost
+ */
+ function downvoteCost(bytes32 _id) public view returns(uint b, uint vR, uint c) {
+ Data memory d = _getDAppById(_id);
+ return _downvoteCost(d);
+ }
+
+ function _createDApp(
+ address _from,
+ bytes32 _id,
+ uint _amount,
+ bytes32 _metadata
+ )
+ internal
+ {
+ require(!existingIDs[_id], "You must submit a unique ID");
+
+ require(_amount > 0, "You must spend some SNT to submit a ranking in order to avoid spam");
+ require (_amount <= safeMax, "You cannot stake more SNT than the ceiling dictates");
+
+ uint dappIdx = dapps.length;
+
+ dapps.length++;
+
+ Data storage d = dapps[dappIdx];
+ d.developer = _from;
+ d.id = _id;
+ d.metadata = _metadata;
+
+ uint precision;
+ uint result;
+
+ d.balance = _amount;
+ d.rate = decimals.sub((d.balance).mul(decimals).div(max));
+ d.available = d.balance.mul(d.rate);
+
+ (result, precision) = BancorFormula.power(
+ d.available,
+ decimals,
+ uint32(decimals),
+ uint32(d.rate));
+
+ d.votesMinted = result >> precision;
+ d.votesCast = 0;
+ d.effectiveBalance = _amount;
+
+ id2index[_id] = dappIdx;
+ existingIDs[_id] = true;
+
+ require(SNT.allowance(_from, address(this)) >= _amount, "Not enough SNT allowance");
+ require(SNT.transferFrom(_from, address(this), _amount), "Transfer failed");
+
+ emit DAppCreated(_id, d.effectiveBalance);
+ }
+
+ function _upvote(address _from, bytes32 _id, uint _amount) internal {
+ require(_amount > 0, "You must send some SNT in order to upvote");
+
+ Data storage d = _getDAppById(_id);
+
+ require(d.balance.add(_amount) <= safeMax, "You cannot upvote by this much, try with a lower amount");
+
+ uint precision;
+ uint result;
+
+ d.balance = d.balance.add(_amount);
+ d.rate = decimals.sub((d.balance).mul(decimals).div(max));
+ d.available = d.balance.mul(d.rate);
+
+ (result, precision) = BancorFormula.power(
+ d.available,
+ decimals,
+ uint32(decimals),
+ uint32(d.rate));
+
+ d.votesMinted = result >> precision;
+
+ uint temp1 = d.votesCast.mul(d.rate).mul(d.available);
+ uint temp2 = d.votesMinted.mul(decimals).mul(decimals);
+ uint effect = temp1.div(temp2);
+
+ d.effectiveBalance = d.balance.sub(effect);
+
+ require(SNT.allowance(_from, address(this)) >= _amount, "Not enough SNT allowance");
+ require(SNT.transferFrom(_from, address(this), _amount), "Transfer failed");
+
+ emit Upvote(_id, d.effectiveBalance);
+ }
+
+ function _downvote(address _from, bytes32 _id, uint _amount) internal {
+ Data storage d = _getDAppById(_id);
+ (uint b, uint vR, uint c) = _downvoteCost(d);
+
+ require(_amount == c, "Incorrect amount: valid iff effect on ranking is 1%");
+
+ d.available = d.available.sub(_amount);
+ d.votesCast = d.votesCast.add(vR);
+ d.effectiveBalance = d.effectiveBalance.sub(b);
+
+ require(SNT.allowance(_from, address(this)) >= _amount, "Not enough SNT allowance");
+ require(SNT.transferFrom(_from, address(this), _amount), "Transfer failed");
+ require(SNT.transfer(d.developer, _amount), "Transfer failed");
+
+ emit Downvote(_id, d.effectiveBalance);
+ }
+
+ function _downvoteCost(Data memory d) internal view returns(uint b, uint vR, uint c) {
+ uint balanceDownBy = (d.effectiveBalance.div(100));
+ uint votesRequired = (balanceDownBy.mul(d.votesMinted).mul(d.rate)).div(d.available);
+ uint votesAvailable = d.votesMinted.sub(d.votesCast).sub(votesRequired);
+ uint temp = (d.available.div(votesAvailable)).mul(votesRequired);
+ uint cost = temp.div(decimals);
+ return (balanceDownBy, votesRequired, cost);
+ }
+
+ /**
+ * @dev Used internally in order to get a dapp while checking if it exists
+ * @return existing dapp
+ */
+ function _getDAppById(bytes32 _id) internal view returns(Data storage d) {
+ uint dappIdx = id2index[_id];
+ Data memory d = dapps[dappIdx];
+ require(d.id == _id, "Error fetching correct data");
+
+ return dapps[dappIdx];
+ }
+
+ /**
+ * @dev Decodes abi encoded data with selector for "functionName(bytes32,uint256)".
+ * @param _data Abi encoded data.
+ * @return Decoded registry call.
+ */
+ function abiDecodeRegister(
+ bytes memory _data
+ )
+ private
+ returns(
+ bytes4 sig,
+ bytes32 id,
+ uint256 amount,
+ bytes32 metadata
+ )
+ {
+ assembly {
+ sig := mload(add(_data, add(0x20, 0)))
+ id := mload(add(_data, 36))
+ amount := mload(add(_data, 68))
+ metadata := mload(add(_data, 100))
+ }
+ }
+}
\ No newline at end of file
diff --git a/contracts/common/Controlled.sol b/contracts/common/Controlled.sol
new file mode 100644
index 0000000..b7fd63d
--- /dev/null
+++ b/contracts/common/Controlled.sol
@@ -0,0 +1,23 @@
+pragma solidity ^0.5.2;
+
+
+contract Controlled {
+ /// @notice The address of the controller is the only address that can call
+ /// a function with this modifier
+ modifier onlyController {
+ require(msg.sender == controller, "Unauthorized");
+ _;
+ }
+
+ address payable public controller;
+
+ constructor() internal {
+ controller = msg.sender;
+ }
+
+ /// @notice Changes the controller of the contract
+ /// @param _newController The new controller of the contract
+ function changeController(address payable _newController) external onlyController {
+ controller = _newController;
+ }
+}
\ No newline at end of file
diff --git a/contracts/test/TestBancorFormula.sol b/contracts/test/TestBancorFormula.sol
new file mode 100644
index 0000000..3b66e07
--- /dev/null
+++ b/contracts/test/TestBancorFormula.sol
@@ -0,0 +1,44 @@
+pragma solidity ^0.5.2;
+import "../utils/BancorFormula.sol";
+
+
+contract TestBancorFormula is BancorFormula {
+
+ function powerTest(
+ uint256 _baseN,
+ uint256 _baseD,
+ uint32 _expN,
+ uint32 _expD)
+ external view returns (uint256, uint8)
+ {
+ return super.power(
+ _baseN,
+ _baseD,
+ _expN,
+ _expD);
+ }
+
+ function generalLogTest(uint256 x) external pure returns (uint256) {
+ return super.generalLog(x);
+ }
+
+ function floorLog2Test(uint256 _n) external pure returns (uint8) {
+ return super.floorLog2(_n);
+ }
+
+ function findPositionInMaxExpArrayTest(uint256 _x) external view returns (uint8) {
+ return super.findPositionInMaxExpArray(_x);
+ }
+
+ function generalExpTest(uint256 _x, uint8 _precision) external pure returns (uint256) {
+ return super.generalExp(_x, _precision);
+ }
+
+ function optimalLogTest(uint256 x) external pure returns (uint256) {
+ return super.optimalLog(x);
+ }
+
+ function optimalExpTest(uint256 x) external pure returns (uint256) {
+ return super.optimalExp(x);
+ }
+}
diff --git a/contracts/token/ApproveAndCallFallBack.sol b/contracts/token/ApproveAndCallFallBack.sol
new file mode 100644
index 0000000..be51cf0
--- /dev/null
+++ b/contracts/token/ApproveAndCallFallBack.sol
@@ -0,0 +1,10 @@
+pragma solidity ^0.5.2;
+
+
+contract ApproveAndCallFallBack {
+ function receiveApproval(
+ address from,
+ uint256 _amount,
+ address _token,
+ bytes calldata _data) external;
+}
diff --git a/contracts/token/ERC20Token.sol b/contracts/token/ERC20Token.sol
new file mode 100644
index 0000000..5f8c11f
--- /dev/null
+++ b/contracts/token/ERC20Token.sol
@@ -0,0 +1,53 @@
+pragma solidity ^0.5.2;
+
+// Abstract contract for the full ERC 20 Token standard
+// https://github.com/ethereum/EIPs/issues/20
+
+interface ERC20Token {
+
+ /**
+ * @notice send `_value` token to `_to` from `msg.sender`
+ * @param _to The address of the recipient
+ * @param _value The amount of token to be transferred
+ * @return Whether the transfer was successful or not
+ */
+ function transfer(address _to, uint256 _value) external returns (bool success);
+
+ /**
+ * @notice `msg.sender` approves `_spender` to spend `_value` tokens
+ * @param _spender The address of the account able to transfer the tokens
+ * @param _value The amount of tokens to be approved for transfer
+ * @return Whether the approval was successful or not
+ */
+ function approve(address _spender, uint256 _value) external returns (bool success);
+
+ /**
+ * @notice send `_value` token to `_to` from `_from` on the condition it is approved by `_from`
+ * @param _from The address of the sender
+ * @param _to The address of the recipient
+ * @param _value The amount of token to be transferred
+ * @return Whether the transfer was successful or not
+ */
+ function transferFrom(address _from, address _to, uint256 _value) external returns (bool success);
+
+ /**
+ * @param _owner The address from which the balance will be retrieved
+ * @return The balance
+ */
+ function balanceOf(address _owner) external view returns (uint256 balance);
+
+ /**
+ * @param _owner The address of the account owning tokens
+ * @param _spender The address of the account able to transfer the tokens
+ * @return Amount of remaining tokens allowed to spent
+ */
+ function allowance(address _owner, address _spender) external view returns (uint256 remaining);
+
+ /**
+ * @notice return total supply of tokens
+ */
+ function totalSupply() external view returns (uint256 supply);
+
+ event Transfer(address indexed _from, address indexed _to, uint256 _value);
+ event Approval(address indexed _owner, address indexed _spender, uint256 _value);
+}
diff --git a/contracts/token/MiniMeToken.sol b/contracts/token/MiniMeToken.sol
new file mode 100644
index 0000000..d2dbf21
--- /dev/null
+++ b/contracts/token/MiniMeToken.sol
@@ -0,0 +1,634 @@
+pragma solidity ^0.5.2;
+
+/*
+ Copyright 2016, Jordi Baylina
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+ */
+/**
+ * @title MiniMeToken Contract
+ * @author Jordi Baylina
+ * @dev This token contract's goal is to make it easy for anyone to clone this
+ * token using the token distribution at a given block, this will allow DAO's
+ * and DApps to upgrade their features in a decentralized manner without
+ * affecting the original token
+ * @dev It is ERC20 compliant, but still needs to under go further testing.
+ */
+
+import "../common/Controlled.sol";
+import "./TokenController.sol";
+import "./ApproveAndCallFallBack.sol";
+import "./MiniMeTokenInterface.sol";
+import "./TokenFactory.sol";
+
+/**
+ * @dev The actual token contract, the default controller is the msg.sender
+ * that deploys the contract, so usually this token will be deployed by a
+ * token controller contract, which Giveth will call a "Campaign"
+ */
+
+
+contract MiniMeToken is MiniMeTokenInterface, Controlled {
+
+ string public name; //The Token's name: e.g. DigixDAO Tokens
+ uint8 public decimals; //Number of decimals of the smallest unit
+ string public symbol; //An identifier: e.g. REP
+ string public constant VERSION = "MMT_0.1"; //An arbitrary versioning scheme
+
+ /**
+ * @dev `Checkpoint` is the structure that attaches a block number to a
+ * given value, the block number attached is the one that last changed the
+ * value
+ */
+ struct Checkpoint {
+
+ // `fromBlock` is the block number that the value was generated from
+ uint128 fromBlock;
+
+ // `value` is the amount of tokens at a specific block number
+ uint128 value;
+ }
+
+ // `parentToken` is the Token address that was cloned to produce this token;
+ // it will be 0x0 for a token that was not cloned
+ MiniMeToken public parentToken;
+
+ // `parentSnapShotBlock` is the block number from the Parent Token that was
+ // used to determine the initial distribution of the Clone Token
+ uint public parentSnapShotBlock;
+
+ // `creationBlock` is the block number that the Clone Token was created
+ uint public creationBlock;
+
+ // `balances` is the map that tracks the balance of each address, in this
+ // contract when the balance changes the block number that the change
+ // occurred is also included in the map
+ mapping (address => Checkpoint[]) balances;
+
+ // `allowed` tracks any extra transfer rights as in all ERC20 tokens
+ mapping (address => mapping (address => uint256)) allowed;
+
+ // Tracks the history of the `totalSupply` of the token
+ Checkpoint[] totalSupplyHistory;
+
+ // Flag that determines if the token is transferable or not.
+ bool public transfersEnabled;
+
+ // The factory used to create new clone tokens
+ TokenFactory public tokenFactory;
+
+////////////////
+// Constructor
+////////////////
+
+ /**
+ * @notice Constructor to create a MiniMeToken
+ * @param _tokenFactory The address of the MiniMeTokenFactory contract that
+ * will create the Clone token contracts, the token factory needs to be
+ * deployed first
+ * @param _parentToken Address of the parent token, set to 0x0 if it is a
+ * new token
+ * @param _parentSnapShotBlock Block of the parent token that will
+ * determine the initial distribution of the clone token, set to 0 if it
+ * is a new token
+ * @param _tokenName Name of the new token
+ * @param _decimalUnits Number of decimals of the new token
+ * @param _tokenSymbol Token Symbol for the new token
+ * @param _transfersEnabled If true, tokens will be able to be transferred
+ */
+ constructor(
+ address _tokenFactory,
+ address _parentToken,
+ uint _parentSnapShotBlock,
+ string memory _tokenName,
+ uint8 _decimalUnits,
+ string memory _tokenSymbol,
+ bool _transfersEnabled
+ )
+ public
+ {
+ tokenFactory = TokenFactory(_tokenFactory);
+ name = _tokenName; // Set the name
+ decimals = _decimalUnits; // Set the decimals
+ symbol = _tokenSymbol; // Set the symbol
+ parentToken = MiniMeToken(address(uint160(_parentToken)));
+ parentSnapShotBlock = _parentSnapShotBlock;
+ transfersEnabled = _transfersEnabled;
+ creationBlock = block.number;
+ }
+
+
+///////////////////
+// ERC20 Methods
+///////////////////
+
+ /**
+ * @notice Send `_amount` tokens to `_to` from `msg.sender`
+ * @param _to The address of the recipient
+ * @param _amount The amount of tokens to be transferred
+ * @return Whether the transfer was successful or not
+ */
+ function transfer(address _to, uint256 _amount) external returns (bool success) {
+ require(transfersEnabled);
+ return doTransfer(msg.sender, _to, _amount);
+ }
+
+ /**
+ * @notice Send `_amount` tokens to `_to` from `_from` on the condition it
+ * is approved by `_from`
+ * @param _from The address holding the tokens being transferred
+ * @param _to The address of the recipient
+ * @param _amount The amount of tokens to be transferred
+ * @return True if the transfer was successful
+ */
+ function transferFrom(
+ address _from,
+ address _to,
+ uint256 _amount
+ )
+ external
+ returns (bool success)
+ {
+
+ // The controller of this contract can move tokens around at will,
+ // this is important to recognize! Confirm that you trust the
+ // controller of this contract, which in most situations should be
+ // another open source smart contract or 0x0
+ if (msg.sender != controller) {
+ require(transfersEnabled);
+
+ // The standard ERC 20 transferFrom functionality
+ if (allowed[_from][msg.sender] < _amount) {
+ return false;
+ }
+ allowed[_from][msg.sender] -= _amount;
+ }
+ return doTransfer(_from, _to, _amount);
+ }
+
+ /**
+ * @dev This is the actual transfer function in the token contract, it can
+ * only be called by other functions in this contract.
+ * @param _from The address holding the tokens being transferred
+ * @param _to The address of the recipient
+ * @param _amount The amount of tokens to be transferred
+ * @return True if the transfer was successful
+ */
+ function doTransfer(
+ address _from,
+ address _to,
+ uint _amount
+ )
+ internal
+ returns(bool)
+ {
+
+ if (_amount == 0) {
+ return true;
+ }
+
+ require(parentSnapShotBlock < block.number);
+
+ // Do not allow transfer to 0x0 or the token contract itself
+ require((_to != address(0)) && (_to != address(this)));
+
+ // If the amount being transfered is more than the balance of the
+ // account the transfer returns false
+ uint256 previousBalanceFrom = balanceOfAt(_from, block.number);
+ if (previousBalanceFrom < _amount) {
+ return false;
+ }
+
+ // Alerts the token controller of the transfer
+ if (isContract(controller)) {
+ require(TokenController(controller).onTransfer(_from, _to, _amount));
+ }
+
+ // First update the balance array with the new value for the address
+ // sending the tokens
+ updateValueAtNow(balances[_from], previousBalanceFrom - _amount);
+
+ // Then update the balance array with the new value for the address
+ // receiving the tokens
+ uint256 previousBalanceTo = balanceOfAt(_to, block.number);
+ require(previousBalanceTo + _amount >= previousBalanceTo); // Check for overflow
+ updateValueAtNow(balances[_to], previousBalanceTo + _amount);
+
+ // An event to make the transfer easy to find on the blockchain
+ emit Transfer(_from, _to, _amount);
+
+ return true;
+ }
+
+ function doApprove(
+ address _from,
+ address _spender,
+ uint256 _amount
+ )
+ internal
+ returns (bool)
+ {
+ require(transfersEnabled);
+
+ // To change the approve amount you first have to reduce the addresses`
+ // allowance to zero by calling `approve(_spender,0)` if it is not
+ // already 0 to mitigate the race condition described here:
+ // https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
+ require((_amount == 0) || (allowed[_from][_spender] == 0));
+
+ // Alerts the token controller of the approve function call
+ if (isContract(controller)) {
+ require(TokenController(controller).onApprove(_from, _spender, _amount));
+ }
+
+ allowed[_from][_spender] = _amount;
+ emit Approval(_from, _spender, _amount);
+ return true;
+ }
+
+ /**
+ * @param _owner The address that's balance is being requested
+ * @return The balance of `_owner` at the current block
+ */
+ function balanceOf(address _owner) external view returns (uint256 balance) {
+ return balanceOfAt(_owner, block.number);
+ }
+
+ /**
+ * @notice `msg.sender` approves `_spender` to spend `_amount` tokens on
+ * its behalf. This is a modified version of the ERC20 approve function
+ * to be a little bit safer
+ * @param _spender The address of the account able to transfer the tokens
+ * @param _amount The amount of tokens to be approved for transfer
+ * @return True if the approval was successful
+ */
+ function approve(address _spender, uint256 _amount) external returns (bool success) {
+ doApprove(msg.sender, _spender, _amount);
+ }
+
+ /**
+ * @dev This function makes it easy to read the `allowed[]` map
+ * @param _owner The address of the account that owns the token
+ * @param _spender The address of the account able to transfer the tokens
+ * @return Amount of remaining tokens of _owner that _spender is allowed
+ * to spend
+ */
+ function allowance(
+ address _owner,
+ address _spender
+ )
+ external
+ view
+ returns (uint256 remaining)
+ {
+ return allowed[_owner][_spender];
+ }
+ /**
+ * @notice `msg.sender` approves `_spender` to send `_amount` tokens on
+ * its behalf, and then a function is triggered in the contract that is
+ * being approved, `_spender`. This allows users to use their tokens to
+ * interact with contracts in one function call instead of two
+ * @param _spender The address of the contract able to transfer the tokens
+ * @param _amount The amount of tokens to be approved for transfer
+ * @return True if the function call was successful
+ */
+ function approveAndCall(
+ address _spender,
+ uint256 _amount,
+ bytes calldata _extraData
+ )
+ external
+ returns (bool success)
+ {
+ require(doApprove(msg.sender, _spender, _amount));
+
+ ApproveAndCallFallBack(_spender).receiveApproval(
+ msg.sender,
+ _amount,
+ address(this),
+ _extraData
+ );
+
+ return true;
+ }
+
+ /**
+ * @dev This function makes it easy to get the total number of tokens
+ * @return The total number of tokens
+ */
+ function totalSupply() external view returns (uint) {
+ return totalSupplyAt(block.number);
+ }
+
+
+////////////////
+// Query balance and totalSupply in History
+////////////////
+
+ /**
+ * @dev Queries the balance of `_owner` at a specific `_blockNumber`
+ * @param _owner The address from which the balance will be retrieved
+ * @param _blockNumber The block number when the balance is queried
+ * @return The balance at `_blockNumber`
+ */
+ function balanceOfAt(
+ address _owner,
+ uint _blockNumber
+ )
+ public
+ view
+ returns (uint)
+ {
+
+ // These next few lines are used when the balance of the token is
+ // requested before a check point was ever created for this token, it
+ // requires that the `parentToken.balanceOfAt` be queried at the
+ // genesis block for that token as this contains initial balance of
+ // this token
+ if ((balances[_owner].length == 0) || (balances[_owner][0].fromBlock > _blockNumber)) {
+ if (address(parentToken) != address(0)) {
+ return parentToken.balanceOfAt(_owner, min(_blockNumber, parentSnapShotBlock));
+ } else {
+ // Has no parent
+ return 0;
+ }
+
+ // This will return the expected balance during normal situations
+ } else {
+ return getValueAt(balances[_owner], _blockNumber);
+ }
+ }
+
+ /**
+ * @notice Total amount of tokens at a specific `_blockNumber`.
+ * @param _blockNumber The block number when the totalSupply is queried
+ * @return The total amount of tokens at `_blockNumber`
+ */
+ function totalSupplyAt(uint _blockNumber) public view returns(uint) {
+
+ // These next few lines are used when the totalSupply of the token is
+ // requested before a check point was ever created for this token, it
+ // requires that the `parentToken.totalSupplyAt` be queried at the
+ // genesis block for this token as that contains totalSupply of this
+ // token at this block number.
+ if ((totalSupplyHistory.length == 0) || (totalSupplyHistory[0].fromBlock > _blockNumber)) {
+ if (address(parentToken) != address(0)) {
+ return parentToken.totalSupplyAt(min(_blockNumber, parentSnapShotBlock));
+ } else {
+ return 0;
+ }
+
+ // This will return the expected totalSupply during normal situations
+ } else {
+ return getValueAt(totalSupplyHistory, _blockNumber);
+ }
+ }
+
+////////////////
+// Clone Token Method
+////////////////
+
+ /**
+ * @notice Creates a new clone token with the initial distribution being
+ * this token at `snapshotBlock`
+ * @param _cloneTokenName Name of the clone token
+ * @param _cloneDecimalUnits Number of decimals of the smallest unit
+ * @param _cloneTokenSymbol Symbol of the clone token
+ * @param _snapshotBlock Block when the distribution of the parent token is
+ * copied to set the initial distribution of the new clone token;
+ * if the block is zero than the actual block, the current block is used
+ * @param _transfersEnabled True if transfers are allowed in the clone
+ * @return The address of the new MiniMeToken Contract
+ */
+ function createCloneToken(
+ string calldata _cloneTokenName,
+ uint8 _cloneDecimalUnits,
+ string calldata _cloneTokenSymbol,
+ uint _snapshotBlock,
+ bool _transfersEnabled
+ )
+ external
+ returns(address)
+ {
+ uint snapshotBlock = _snapshotBlock;
+ if (snapshotBlock == 0) {
+ snapshotBlock = block.number;
+ }
+ MiniMeToken cloneToken = MiniMeToken(
+ tokenFactory.createCloneToken(
+ address(this),
+ snapshotBlock,
+ _cloneTokenName,
+ _cloneDecimalUnits,
+ _cloneTokenSymbol,
+ _transfersEnabled
+ ));
+
+ cloneToken.changeController(msg.sender);
+
+ // An event to make the token easy to find on the blockchain
+ emit NewCloneToken(address(cloneToken), snapshotBlock);
+ return address(cloneToken);
+ }
+
+////////////////
+// Generate and destroy tokens
+////////////////
+
+ /**
+ * @notice Generates `_amount` tokens that are assigned to `_owner`
+ * @param _owner The address that will be assigned the new tokens
+ * @param _amount The quantity of tokens generated
+ * @return True if the tokens are generated correctly
+ */
+ function generateTokens(
+ address _owner,
+ uint _amount
+ )
+ external
+ onlyController
+ returns (bool)
+ {
+ uint curTotalSupply = totalSupplyAt(block.number);
+ require(curTotalSupply + _amount >= curTotalSupply); // Check for overflow
+ uint previousBalanceTo = balanceOfAt(_owner, block.number);
+ require(previousBalanceTo + _amount >= previousBalanceTo); // Check for overflow
+ updateValueAtNow(totalSupplyHistory, curTotalSupply + _amount);
+ updateValueAtNow(balances[_owner], previousBalanceTo + _amount);
+ emit Transfer(address(0), _owner, _amount);
+ return true;
+ }
+
+ /**
+ * @notice Burns `_amount` tokens from `_owner`
+ * @param _owner The address that will lose the tokens
+ * @param _amount The quantity of tokens to burn
+ * @return True if the tokens are burned correctly
+ */
+ function destroyTokens(
+ address _owner,
+ uint _amount
+ )
+ external
+ onlyController
+ returns (bool)
+ {
+ uint curTotalSupply = totalSupplyAt(block.number);
+ require(curTotalSupply >= _amount);
+ uint previousBalanceFrom = balanceOfAt(_owner, block.number);
+ require(previousBalanceFrom >= _amount);
+ updateValueAtNow(totalSupplyHistory, curTotalSupply - _amount);
+ updateValueAtNow(balances[_owner], previousBalanceFrom - _amount);
+ emit Transfer(_owner, address(0), _amount);
+ return true;
+ }
+
+////////////////
+// Enable tokens transfers
+////////////////
+
+ /**
+ * @notice Enables token holders to transfer their tokens freely if true
+ * @param _transfersEnabled True if transfers are allowed in the clone
+ */
+ function enableTransfers(bool _transfersEnabled) external onlyController {
+ transfersEnabled = _transfersEnabled;
+ }
+
+////////////////
+// Internal helper functions to query and set a value in a snapshot array
+////////////////
+
+ /**
+ * @dev `getValueAt` retrieves the number of tokens at a given block number
+ * @param checkpoints The history of values being queried
+ * @param _block The block number to retrieve the value at
+ * @return The number of tokens being queried
+ */
+ function getValueAt(
+ Checkpoint[] storage checkpoints,
+ uint _block
+ )
+ internal
+ view
+ returns (uint)
+ {
+ if (checkpoints.length == 0) {
+ return 0;
+ }
+
+ // Shortcut for the actual value
+ if (_block >= checkpoints[checkpoints.length-1].fromBlock) {
+ return checkpoints[checkpoints.length-1].value;
+ }
+ if (_block < checkpoints[0].fromBlock) {
+ return 0;
+ }
+
+ // Binary search of the value in the array
+ uint min = 0;
+ uint max = checkpoints.length-1;
+ while (max > min) {
+ uint mid = (max + min + 1) / 2;
+ if (checkpoints[mid].fromBlock<=_block) {
+ min = mid;
+ } else {
+ max = mid-1;
+ }
+ }
+ return checkpoints[min].value;
+ }
+
+ /**
+ * @dev `updateValueAtNow` used to update the `balances` map and the
+ * `totalSupplyHistory`
+ * @param checkpoints The history of data being updated
+ * @param _value The new number of tokens
+ */
+ function updateValueAtNow(Checkpoint[] storage checkpoints, uint _value) internal {
+ if ((checkpoints.length == 0) || (checkpoints[checkpoints.length - 1].fromBlock < block.number)) {
+ Checkpoint storage newCheckPoint = checkpoints[checkpoints.length++];
+ newCheckPoint.fromBlock = uint128(block.number);
+ newCheckPoint.value = uint128(_value);
+ } else {
+ Checkpoint storage oldCheckPoint = checkpoints[checkpoints.length-1];
+ oldCheckPoint.value = uint128(_value);
+ }
+ }
+
+ /**
+ * @dev Internal function to determine if an address is a contract
+ * @param _addr The address being queried
+ * @return True if `_addr` is a contract
+ */
+ function isContract(address _addr) internal returns(bool) {
+ uint size;
+ if (_addr == address(0)) {
+ return false;
+ }
+ assembly {
+ size := extcodesize(_addr)
+ }
+ return size>0;
+ }
+
+ /**
+ * @dev Helper function to return a min betwen the two uints
+ */
+ function min(uint a, uint b) internal pure returns (uint) {
+ return a < b ? a : b;
+ }
+
+ /**
+ * @notice The fallback function: If the contract's controller has not been
+ * set to 0, then the `proxyPayment` method is called which relays the
+ * ether and creates tokens as described in the token controller contract
+ */
+ function () external payable {
+ require(isContract(controller));
+ require(TokenController(controller).proxyPayment.value(msg.value)(msg.sender));
+ }
+
+//////////
+// Safety Methods
+//////////
+
+ /**
+ * @notice This method can be used by the controller to extract mistakenly
+ * sent tokens to this contract.
+ * @param _token The address of the token contract that you want to recover
+ * set to 0 in case you want to extract ether.
+ */
+ function claimTokens(address _token) external onlyController {
+ if (_token == address(0)) {
+ controller.transfer(address(this).balance);
+ return;
+ }
+
+ MiniMeToken token = MiniMeToken(address(uint160(_token)));
+ uint balance = token.balanceOf(address(this));
+ token.transfer(controller, balance);
+ emit ClaimedTokens(_token, controller, balance);
+ }
+
+////////////////
+// Events
+////////////////
+ event ClaimedTokens(address indexed _token, address indexed _controller, uint _amount);
+ event Transfer(address indexed _from, address indexed _to, uint256 _amount);
+ event NewCloneToken(address indexed _cloneToken, uint snapshotBlock);
+ event Approval(
+ address indexed _owner,
+ address indexed _spender,
+ uint256 _amount
+ );
+
+}
\ No newline at end of file
diff --git a/contracts/token/MiniMeTokenFactory.sol b/contracts/token/MiniMeTokenFactory.sol
new file mode 100644
index 0000000..cb63393
--- /dev/null
+++ b/contracts/token/MiniMeTokenFactory.sol
@@ -0,0 +1,48 @@
+pragma solidity ^0.5.2;
+
+import "./TokenFactory.sol";
+import "./MiniMeToken.sol";
+
+
+/**
+ * @dev This contract is used to generate clone contracts from a contract.
+ * In solidity this is the way to create a contract from a contract of the
+ * same class
+ */
+contract MiniMeTokenFactory is TokenFactory {
+
+ /**
+ * @notice Update the DApp by creating a new token with new functionalities
+ * the msg.sender becomes the controller of this clone token
+ * @param _parentToken Address of the token being cloned
+ * @param _snapshotBlock Block of the parent token that will
+ * determine the initial distribution of the clone token
+ * @param _tokenName Name of the new token
+ * @param _decimalUnits Number of decimals of the new token
+ * @param _tokenSymbol Token Symbol for the new token
+ * @param _transfersEnabled If true, tokens will be able to be transferred
+ * @return The address of the new token contract
+ */
+ function createCloneToken(
+ address _parentToken,
+ uint _snapshotBlock,
+ string calldata _tokenName,
+ uint8 _decimalUnits,
+ string calldata _tokenSymbol,
+ bool _transfersEnabled
+ ) external returns (address payable)
+ {
+ MiniMeToken newToken = new MiniMeToken(
+ address(this),
+ _parentToken,
+ _snapshotBlock,
+ _tokenName,
+ _decimalUnits,
+ _tokenSymbol,
+ _transfersEnabled
+ );
+
+ newToken.changeController(msg.sender);
+ return address(newToken);
+ }
+}
\ No newline at end of file
diff --git a/contracts/token/MiniMeTokenInterface.sol b/contracts/token/MiniMeTokenInterface.sol
new file mode 100644
index 0000000..0b2256b
--- /dev/null
+++ b/contracts/token/MiniMeTokenInterface.sol
@@ -0,0 +1,108 @@
+pragma solidity ^0.5.2;
+
+import "./ERC20Token.sol";
+
+
+contract MiniMeTokenInterface is ERC20Token {
+
+ /**
+ * @notice `msg.sender` approves `_spender` to send `_amount` tokens on
+ * its behalf, and then a function is triggered in the contract that is
+ * being approved, `_spender`. This allows users to use their tokens to
+ * interact with contracts in one function call instead of two
+ * @param _spender The address of the contract able to transfer the tokens
+ * @param _amount The amount of tokens to be approved for transfer
+ * @return True if the function call was successful
+ */
+ function approveAndCall(
+ address _spender,
+ uint256 _amount,
+ bytes calldata _extraData
+ )
+ external
+ returns (bool success);
+
+ /**
+ * @notice Creates a new clone token with the initial distribution being
+ * this token at `_snapshotBlock`
+ * @param _cloneTokenName Name of the clone token
+ * @param _cloneDecimalUnits Number of decimals of the smallest unit
+ * @param _cloneTokenSymbol Symbol of the clone token
+ * @param _snapshotBlock Block when the distribution of the parent token is
+ * copied to set the initial distribution of the new clone token;
+ * if the block is zero than the actual block, the current block is used
+ * @param _transfersEnabled True if transfers are allowed in the clone
+ * @return The address of the new MiniMeToken Contract
+ */
+ function createCloneToken(
+ string calldata _cloneTokenName,
+ uint8 _cloneDecimalUnits,
+ string calldata _cloneTokenSymbol,
+ uint _snapshotBlock,
+ bool _transfersEnabled
+ )
+ external
+ returns(address);
+
+ /**
+ * @notice Generates `_amount` tokens that are assigned to `_owner`
+ * @param _owner The address that will be assigned the new tokens
+ * @param _amount The quantity of tokens generated
+ * @return True if the tokens are generated correctly
+ */
+ function generateTokens(
+ address _owner,
+ uint _amount
+ )
+ external
+ returns (bool);
+
+ /**
+ * @notice Burns `_amount` tokens from `_owner`
+ * @param _owner The address that will lose the tokens
+ * @param _amount The quantity of tokens to burn
+ * @return True if the tokens are burned correctly
+ */
+ function destroyTokens(
+ address _owner,
+ uint _amount
+ )
+ external
+ returns (bool);
+
+ /**
+ * @notice Enables token holders to transfer their tokens freely if true
+ * @param _transfersEnabled True if transfers are allowed in the clone
+ */
+ function enableTransfers(bool _transfersEnabled) external;
+
+ /**
+ * @notice This method can be used by the controller to extract mistakenly
+ * sent tokens to this contract.
+ * @param _token The address of the token contract that you want to recover
+ * set to 0 in case you want to extract ether.
+ */
+ function claimTokens(address _token) external;
+
+ /**
+ * @dev Queries the balance of `_owner` at a specific `_blockNumber`
+ * @param _owner The address from which the balance will be retrieved
+ * @param _blockNumber The block number when the balance is queried
+ * @return The balance at `_blockNumber`
+ */
+ function balanceOfAt(
+ address _owner,
+ uint _blockNumber
+ )
+ public
+ view
+ returns (uint);
+
+ /**
+ * @notice Total amount of tokens at a specific `_blockNumber`.
+ * @param _blockNumber The block number when the totalSupply is queried
+ * @return The total amount of tokens at `_blockNumber`
+ */
+ function totalSupplyAt(uint _blockNumber) public view returns(uint);
+
+}
\ No newline at end of file
diff --git a/contracts/token/TokenController.sol b/contracts/token/TokenController.sol
new file mode 100644
index 0000000..ae55d58
--- /dev/null
+++ b/contracts/token/TokenController.sol
@@ -0,0 +1,35 @@
+pragma solidity ^0.5.2;
+
+
+/**
+ * @dev The token controller contract must implement these functions
+ */
+interface TokenController {
+ /**
+ * @notice Called when `_owner` sends ether to the MiniMe Token contract
+ * @param _owner The address that sent the ether to create tokens
+ * @return True if the ether is accepted, false if it throws
+ */
+ function proxyPayment(address _owner) external payable returns(bool);
+
+ /**
+ * @notice Notifies the controller about a token transfer allowing the
+ * controller to react if desired
+ * @param _from The origin of the transfer
+ * @param _to The destination of the transfer
+ * @param _amount The amount of the transfer
+ * @return False if the controller does not authorize the transfer
+ */
+ function onTransfer(address _from, address _to, uint _amount) external returns(bool);
+
+ /**
+ * @notice Notifies the controller about an approval allowing the
+ * controller to react if desired
+ * @param _owner The address that calls `approve()`
+ * @param _spender The spender in the `approve()` call
+ * @param _amount The amount in the `approve()` call
+ * @return False if the controller does not authorize the approval
+ */
+ function onApprove(address _owner, address _spender, uint _amount) external
+ returns(bool);
+}
\ No newline at end of file
diff --git a/contracts/token/TokenFactory.sol b/contracts/token/TokenFactory.sol
new file mode 100644
index 0000000..de62eef
--- /dev/null
+++ b/contracts/token/TokenFactory.sol
@@ -0,0 +1,13 @@
+pragma solidity ^0.5.2;
+
+
+contract TokenFactory {
+ function createCloneToken(
+ address _parentToken,
+ uint _snapshotBlock,
+ string calldata _tokenName,
+ uint8 _decimalUnits,
+ string calldata _tokenSymbol,
+ bool _transfersEnabled
+ ) external returns (address payable);
+}
diff --git a/contracts/utils/BancorFormula.sol b/contracts/utils/BancorFormula.sol
new file mode 100644
index 0000000..c93450a
--- /dev/null
+++ b/contracts/utils/BancorFormula.sol
@@ -0,0 +1,506 @@
+pragma solidity ^0.5.2;
+import "./SafeMath.sol";
+
+
+contract BancorFormula {
+ using SafeMath for uint256;
+
+ uint256 private constant ONE = 1;
+ uint8 private constant MIN_PRECISION = 32;
+ uint8 private constant MAX_PRECISION = 127;
+
+ /**
+ Auto-generated via 'PrintIntScalingFactors.py'
+ */
+ uint256 private constant FIXED_1 = 0x080000000000000000000000000000000;
+ uint256 private constant FIXED_2 = 0x100000000000000000000000000000000;
+ uint256 private constant MAX_NUM = 0x200000000000000000000000000000000;
+
+ /**
+ Auto-generated via 'PrintLn2ScalingFactors.py'
+ */
+ uint256 private constant LN2_NUMERATOR = 0x3f80fe03f80fe03f80fe03f80fe03f8;
+ uint256 private constant LN2_DENOMINATOR = 0x5b9de1d10bf4103d647b0955897ba80;
+
+ /**
+ Auto-generated via 'PrintFunctionOptimalLog.py' and 'PrintFunctionOptimalExp.py'
+ */
+ uint256 private constant OPT_LOG_MAX_VAL = 0x15bf0a8b1457695355fb8ac404e7a79e3;
+ uint256 private constant OPT_EXP_MAX_VAL = 0x800000000000000000000000000000000;
+
+ /**
+ Auto-generated via 'PrintFunctionConstructor.py'
+ */
+ uint256[128] private maxExpArray;
+ constructor() public {
+ // maxExpArray[0] = 0x6bffffffffffffffffffffffffffffffff;
+ // maxExpArray[1] = 0x67ffffffffffffffffffffffffffffffff;
+ // maxExpArray[2] = 0x637fffffffffffffffffffffffffffffff;
+ // maxExpArray[3] = 0x5f6fffffffffffffffffffffffffffffff;
+ // maxExpArray[4] = 0x5b77ffffffffffffffffffffffffffffff;
+ // maxExpArray[5] = 0x57b3ffffffffffffffffffffffffffffff;
+ // maxExpArray[6] = 0x5419ffffffffffffffffffffffffffffff;
+ // maxExpArray[7] = 0x50a2ffffffffffffffffffffffffffffff;
+ // maxExpArray[8] = 0x4d517fffffffffffffffffffffffffffff;
+ // maxExpArray[9] = 0x4a233fffffffffffffffffffffffffffff;
+ // maxExpArray[10] = 0x47165fffffffffffffffffffffffffffff;
+ // maxExpArray[11] = 0x4429afffffffffffffffffffffffffffff;
+ // maxExpArray[12] = 0x415bc7ffffffffffffffffffffffffffff;
+ // maxExpArray[13] = 0x3eab73ffffffffffffffffffffffffffff;
+ // maxExpArray[14] = 0x3c1771ffffffffffffffffffffffffffff;
+ // maxExpArray[15] = 0x399e96ffffffffffffffffffffffffffff;
+ // maxExpArray[16] = 0x373fc47fffffffffffffffffffffffffff;
+ // maxExpArray[17] = 0x34f9e8ffffffffffffffffffffffffffff;
+ // maxExpArray[18] = 0x32cbfd5fffffffffffffffffffffffffff;
+ // maxExpArray[19] = 0x30b5057fffffffffffffffffffffffffff;
+ // maxExpArray[20] = 0x2eb40f9fffffffffffffffffffffffffff;
+ // maxExpArray[21] = 0x2cc8340fffffffffffffffffffffffffff;
+ // maxExpArray[22] = 0x2af09481ffffffffffffffffffffffffff;
+ // maxExpArray[23] = 0x292c5bddffffffffffffffffffffffffff;
+ // maxExpArray[24] = 0x277abdcdffffffffffffffffffffffffff;
+ // maxExpArray[25] = 0x25daf6657fffffffffffffffffffffffff;
+ // maxExpArray[26] = 0x244c49c65fffffffffffffffffffffffff;
+ // maxExpArray[27] = 0x22ce03cd5fffffffffffffffffffffffff;
+ // maxExpArray[28] = 0x215f77c047ffffffffffffffffffffffff;
+ // maxExpArray[29] = 0x1fffffffffffffffffffffffffffffffff;
+ // maxExpArray[30] = 0x1eaefdbdabffffffffffffffffffffffff;
+ // maxExpArray[31] = 0x1d6bd8b2ebffffffffffffffffffffffff;
+ maxExpArray[32] = 0x1c35fedd14ffffffffffffffffffffffff;
+ maxExpArray[33] = 0x1b0ce43b323fffffffffffffffffffffff;
+ maxExpArray[34] = 0x19f0028ec1ffffffffffffffffffffffff;
+ maxExpArray[35] = 0x18ded91f0e7fffffffffffffffffffffff;
+ maxExpArray[36] = 0x17d8ec7f0417ffffffffffffffffffffff;
+ maxExpArray[37] = 0x16ddc6556cdbffffffffffffffffffffff;
+ maxExpArray[38] = 0x15ecf52776a1ffffffffffffffffffffff;
+ maxExpArray[39] = 0x15060c256cb2ffffffffffffffffffffff;
+ maxExpArray[40] = 0x1428a2f98d72ffffffffffffffffffffff;
+ maxExpArray[41] = 0x13545598e5c23fffffffffffffffffffff;
+ maxExpArray[42] = 0x1288c4161ce1dfffffffffffffffffffff;
+ maxExpArray[43] = 0x11c592761c666fffffffffffffffffffff;
+ maxExpArray[44] = 0x110a688680a757ffffffffffffffffffff;
+ maxExpArray[45] = 0x1056f1b5bedf77ffffffffffffffffffff;
+ maxExpArray[46] = 0x0faadceceeff8bffffffffffffffffffff;
+ maxExpArray[47] = 0x0f05dc6b27edadffffffffffffffffffff;
+ maxExpArray[48] = 0x0e67a5a25da4107fffffffffffffffffff;
+ maxExpArray[49] = 0x0dcff115b14eedffffffffffffffffffff;
+ maxExpArray[50] = 0x0d3e7a392431239fffffffffffffffffff;
+ maxExpArray[51] = 0x0cb2ff529eb71e4fffffffffffffffffff;
+ maxExpArray[52] = 0x0c2d415c3db974afffffffffffffffffff;
+ maxExpArray[53] = 0x0bad03e7d883f69bffffffffffffffffff;
+ maxExpArray[54] = 0x0b320d03b2c343d5ffffffffffffffffff;
+ maxExpArray[55] = 0x0abc25204e02828dffffffffffffffffff;
+ maxExpArray[56] = 0x0a4b16f74ee4bb207fffffffffffffffff;
+ maxExpArray[57] = 0x09deaf736ac1f569ffffffffffffffffff;
+ maxExpArray[58] = 0x0976bd9952c7aa957fffffffffffffffff;
+ maxExpArray[59] = 0x09131271922eaa606fffffffffffffffff;
+ maxExpArray[60] = 0x08b380f3558668c46fffffffffffffffff;
+ maxExpArray[61] = 0x0857ddf0117efa215bffffffffffffffff;
+ maxExpArray[62] = 0x07ffffffffffffffffffffffffffffffff;
+ maxExpArray[63] = 0x07abbf6f6abb9d087fffffffffffffffff;
+ maxExpArray[64] = 0x075af62cbac95f7dfa7fffffffffffffff;
+ maxExpArray[65] = 0x070d7fb7452e187ac13fffffffffffffff;
+ maxExpArray[66] = 0x06c3390ecc8af379295fffffffffffffff;
+ maxExpArray[67] = 0x067c00a3b07ffc01fd6fffffffffffffff;
+ maxExpArray[68] = 0x0637b647c39cbb9d3d27ffffffffffffff;
+ maxExpArray[69] = 0x05f63b1fc104dbd39587ffffffffffffff;
+ maxExpArray[70] = 0x05b771955b36e12f7235ffffffffffffff;
+ maxExpArray[71] = 0x057b3d49dda84556d6f6ffffffffffffff;
+ maxExpArray[72] = 0x054183095b2c8ececf30ffffffffffffff;
+ maxExpArray[73] = 0x050a28be635ca2b888f77fffffffffffff;
+ maxExpArray[74] = 0x04d5156639708c9db33c3fffffffffffff;
+ maxExpArray[75] = 0x04a23105873875bd52dfdfffffffffffff;
+ maxExpArray[76] = 0x0471649d87199aa990756fffffffffffff;
+ maxExpArray[77] = 0x04429a21a029d4c1457cfbffffffffffff;
+ maxExpArray[78] = 0x0415bc6d6fb7dd71af2cb3ffffffffffff;
+ maxExpArray[79] = 0x03eab73b3bbfe282243ce1ffffffffffff;
+ maxExpArray[80] = 0x03c1771ac9fb6b4c18e229ffffffffffff;
+ maxExpArray[81] = 0x0399e96897690418f785257fffffffffff;
+ maxExpArray[82] = 0x0373fc456c53bb779bf0ea9fffffffffff;
+ maxExpArray[83] = 0x034f9e8e490c48e67e6ab8bfffffffffff;
+ maxExpArray[84] = 0x032cbfd4a7adc790560b3337ffffffffff;
+ maxExpArray[85] = 0x030b50570f6e5d2acca94613ffffffffff;
+ maxExpArray[86] = 0x02eb40f9f620fda6b56c2861ffffffffff;
+ maxExpArray[87] = 0x02cc8340ecb0d0f520a6af58ffffffffff;
+ maxExpArray[88] = 0x02af09481380a0a35cf1ba02ffffffffff;
+ maxExpArray[89] = 0x0292c5bdd3b92ec810287b1b3fffffffff;
+ maxExpArray[90] = 0x0277abdcdab07d5a77ac6d6b9fffffffff;
+ maxExpArray[91] = 0x025daf6654b1eaa55fd64df5efffffffff;
+ maxExpArray[92] = 0x0244c49c648baa98192dce88b7ffffffff;
+ maxExpArray[93] = 0x022ce03cd5619a311b2471268bffffffff;
+ maxExpArray[94] = 0x0215f77c045fbe885654a44a0fffffffff;
+ maxExpArray[95] = 0x01ffffffffffffffffffffffffffffffff;
+ maxExpArray[96] = 0x01eaefdbdaaee7421fc4d3ede5ffffffff;
+ maxExpArray[97] = 0x01d6bd8b2eb257df7e8ca57b09bfffffff;
+ maxExpArray[98] = 0x01c35fedd14b861eb0443f7f133fffffff;
+ maxExpArray[99] = 0x01b0ce43b322bcde4a56e8ada5afffffff;
+ maxExpArray[100] = 0x019f0028ec1fff007f5a195a39dfffffff;
+ maxExpArray[101] = 0x018ded91f0e72ee74f49b15ba527ffffff;
+ maxExpArray[102] = 0x017d8ec7f04136f4e5615fd41a63ffffff;
+ maxExpArray[103] = 0x016ddc6556cdb84bdc8d12d22e6fffffff;
+ maxExpArray[104] = 0x015ecf52776a1155b5bd8395814f7fffff;
+ maxExpArray[105] = 0x015060c256cb23b3b3cc3754cf40ffffff;
+ maxExpArray[106] = 0x01428a2f98d728ae223ddab715be3fffff;
+ maxExpArray[107] = 0x013545598e5c23276ccf0ede68034fffff;
+ maxExpArray[108] = 0x01288c4161ce1d6f54b7f61081194fffff;
+ maxExpArray[109] = 0x011c592761c666aa641d5a01a40f17ffff;
+ maxExpArray[110] = 0x0110a688680a7530515f3e6e6cfdcdffff;
+ maxExpArray[111] = 0x01056f1b5bedf75c6bcb2ce8aed428ffff;
+ maxExpArray[112] = 0x00faadceceeff8a0890f3875f008277fff;
+ maxExpArray[113] = 0x00f05dc6b27edad306388a600f6ba0bfff;
+ maxExpArray[114] = 0x00e67a5a25da41063de1495d5b18cdbfff;
+ maxExpArray[115] = 0x00dcff115b14eedde6fc3aa5353f2e4fff;
+ maxExpArray[116] = 0x00d3e7a3924312399f9aae2e0f868f8fff;
+ maxExpArray[117] = 0x00cb2ff529eb71e41582cccd5a1ee26fff;
+ maxExpArray[118] = 0x00c2d415c3db974ab32a51840c0b67edff;
+ maxExpArray[119] = 0x00bad03e7d883f69ad5b0a186184e06bff;
+ maxExpArray[120] = 0x00b320d03b2c343d4829abd6075f0cc5ff;
+ maxExpArray[121] = 0x00abc25204e02828d73c6e80bcdb1a95bf;
+ maxExpArray[122] = 0x00a4b16f74ee4bb2040a1ec6c15fbbf2df;
+ maxExpArray[123] = 0x009deaf736ac1f569deb1b5ae3f36c130f;
+ maxExpArray[124] = 0x00976bd9952c7aa957f5937d790ef65037;
+ maxExpArray[125] = 0x009131271922eaa6064b73a22d0bd4f2bf;
+ maxExpArray[126] = 0x008b380f3558668c46c91c49a2f8e967b9;
+ maxExpArray[127] = 0x00857ddf0117efa215952912839f6473e6;
+ }
+
+ /**
+ General Description:
+ Determine a value of precision.
+ Calculate an integer approximation of (_baseN / _baseD) ^ (_expN / _expD) * 2 ^ precision.
+ Return the result along with the precision used.
+ Detailed Description:
+ Instead of calculating "base ^ exp", we calculate "e ^ (log(base) * exp)".
+ The value of "log(base)" is represented with an integer slightly smaller than "log(base) * 2 ^ precision".
+ The larger "precision" is, the more accurately this value represents the real value.
+ However, the larger "precision" is, the more bits are required in order to store this value.
+ And the exponentiation function, which takes "x" and calculates "e ^ x", is limited to a maximum exponent (maximum value of "x").
+ This maximum exponent depends on the "precision" used, and it is given by "maxExpArray[precision] >> (MAX_PRECISION - precision)".
+ Hence we need to determine the highest precision which can be used for the given input, before calling the exponentiation function.
+ This allows us to compute "base ^ exp" with maximum accuracy and without exceeding 256 bits in any of the intermediate computations.
+ This functions assumes that "_expN < 2 ^ 256 / log(MAX_NUM - 1)", otherwise the multiplication should be replaced with a "safeMul".
+ */
+ function power(
+ uint256 _baseN,
+ uint256 _baseD,
+ uint32 _expN,
+ uint32 _expD) internal view returns (uint256, uint8)
+ {
+ require(_baseN < MAX_NUM, "SNT available is invalid");
+
+ uint256 baseLog;
+ uint256 base = _baseN * FIXED_1 / _baseD;
+ if (base < OPT_LOG_MAX_VAL) {
+ baseLog = optimalLog(base);
+ } else {
+ baseLog = generalLog(base);
+ }
+
+ uint256 baseLogTimesExp = baseLog * _expN / _expD;
+ if (baseLogTimesExp < OPT_EXP_MAX_VAL) {
+ return (optimalExp(baseLogTimesExp), MAX_PRECISION);
+ } else {
+ uint8 precision = findPositionInMaxExpArray(baseLogTimesExp);
+ return (generalExp(baseLogTimesExp >> (MAX_PRECISION - precision), precision), precision);
+ }
+ }
+
+ /**
+ Compute log(x / FIXED_1) * FIXED_1.
+ This functions assumes that "x >= FIXED_1", because the output would be negative otherwise.
+ */
+ function generalLog(uint256 x) internal pure returns (uint256) {
+ uint256 res = 0;
+
+ // If x >= 2, then we compute the integer part of log2(x), which is larger than 0.
+ if (x >= FIXED_2) {
+ uint8 count = floorLog2(x / FIXED_1);
+ x >>= count; // now x < 2
+ res = count * FIXED_1;
+ }
+
+ // If x > 1, then we compute the fraction part of log2(x), which is larger than 0.
+ if (x > FIXED_1) {
+ for (uint8 i = MAX_PRECISION; i > 0; --i) {
+ x = (x * x) / FIXED_1; // now 1 < x < 4
+ if (x >= FIXED_2) {
+ x >>= 1; // now 1 < x < 2
+ res += ONE << (i - 1);
+ }
+ }
+ }
+
+ return res * LN2_NUMERATOR / LN2_DENOMINATOR;
+ }
+
+ /**
+ Compute the largest integer smaller than or equal to the binary logarithm of the input.
+ */
+ function floorLog2(uint256 _n) internal pure returns (uint8) {
+ uint8 res = 0;
+
+ if (_n < 256) {
+ // At most 8 iterations
+ while (_n > 1) {
+ _n >>= 1;
+ res += 1;
+ }
+ } else {
+ // Exactly 8 iterations
+ for (uint8 s = 128; s > 0; s >>= 1) {
+ if (_n >= (ONE << s)) {
+ _n >>= s;
+ res |= s;
+ }
+ }
+ }
+
+ return res;
+ }
+
+ /**
+ The global "maxExpArray" is sorted in descending order, and therefore the following statements are equivalent:
+ - This function finds the position of [the smallest value in "maxExpArray" larger than or equal to "x"]
+ - This function finds the highest position of [a value in "maxExpArray" larger than or equal to "x"]
+ */
+ function findPositionInMaxExpArray(uint256 _x) internal view returns (uint8) {
+ uint8 lo = MIN_PRECISION;
+ uint8 hi = MAX_PRECISION;
+
+ while (lo + 1 < hi) {
+ uint8 mid = (lo + hi) / 2;
+ if (maxExpArray[mid] >= _x) {
+ lo = mid;
+ } else {
+ hi = mid;
+ }
+ }
+
+ if (maxExpArray[hi] >= _x)
+ return hi;
+ if (maxExpArray[lo] >= _x)
+ return lo;
+
+ require(false, "Could not find a suitable position");
+ return 0;
+ }
+
+ /**
+ This function can be auto-generated by the script 'PrintFunctionGeneralExp.py'.
+ It approximates "e ^ x" via maclaurin summation: "(x^0)/0! + (x^1)/1! + ... + (x^n)/n!".
+ It returns "e ^ (x / 2 ^ precision) * 2 ^ precision", that is, the result is upshifted for accuracy.
+ The global "maxExpArray" maps each "precision" to "((maximumExponent + 1) << (MAX_PRECISION - precision)) - 1".
+ The maximum permitted value for "x" is therefore given by "maxExpArray[precision] >> (MAX_PRECISION - precision)".
+ */
+ function generalExp(uint256 _x, uint8 _precision) internal pure returns (uint256) {
+ uint256 xi = _x;
+ uint256 res = 0;
+
+ xi = (xi * _x) >> _precision;
+ res += xi * 0x3442c4e6074a82f1797f72ac0000000; // add x^02 * (33! / 02!)
+ xi = (xi * _x) >> _precision;
+ res += xi * 0x116b96f757c380fb287fd0e40000000; // add x^03 * (33! / 03!)
+ xi = (xi * _x) >> _precision;
+ res += xi * 0x045ae5bdd5f0e03eca1ff4390000000; // add x^04 * (33! / 04!)
+ xi = (xi * _x) >> _precision;
+ res += xi * 0x00defabf91302cd95b9ffda50000000; // add x^05 * (33! / 05!)
+ xi = (xi * _x) >> _precision;
+ res += xi * 0x002529ca9832b22439efff9b8000000; // add x^06 * (33! / 06!)
+ xi = (xi * _x) >> _precision;
+ res += xi * 0x00054f1cf12bd04e516b6da88000000; // add x^07 * (33! / 07!)
+ xi = (xi * _x) >> _precision;
+ res += xi * 0x0000a9e39e257a09ca2d6db51000000; // add x^08 * (33! / 08!)
+ xi = (xi * _x) >> _precision;
+ res += xi * 0x000012e066e7b839fa050c309000000; // add x^09 * (33! / 09!)
+ xi = (xi * _x) >> _precision;
+ res += xi * 0x000001e33d7d926c329a1ad1a800000; // add x^10 * (33! / 10!)
+ xi = (xi * _x) >> _precision;
+ res += xi * 0x0000002bee513bdb4a6b19b5f800000; // add x^11 * (33! / 11!)
+ xi = (xi * _x) >> _precision;
+ res += xi * 0x00000003a9316fa79b88eccf2a00000; // add x^12 * (33! / 12!)
+ xi = (xi * _x) >> _precision;
+ res += xi * 0x0000000048177ebe1fa812375200000; // add x^13 * (33! / 13!)
+ xi = (xi * _x) >> _precision;
+ res += xi * 0x0000000005263fe90242dcbacf00000; // add x^14 * (33! / 14!)
+ xi = (xi * _x) >> _precision;
+ res += xi * 0x000000000057e22099c030d94100000; // add x^15 * (33! / 15!)
+ xi = (xi * _x) >> _precision;
+ res += xi * 0x0000000000057e22099c030d9410000; // add x^16 * (33! / 16!)
+ xi = (xi * _x) >> _precision;
+ res += xi * 0x00000000000052b6b54569976310000; // add x^17 * (33! / 17!)
+ xi = (xi * _x) >> _precision;
+ res += xi * 0x00000000000004985f67696bf748000; // add x^18 * (33! / 18!)
+ xi = (xi * _x) >> _precision;
+ res += xi * 0x000000000000003dea12ea99e498000; // add x^19 * (33! / 19!)
+ xi = (xi * _x) >> _precision;
+ res += xi * 0x00000000000000031880f2214b6e000; // add x^20 * (33! / 20!)
+ xi = (xi * _x) >> _precision;
+ res += xi * 0x000000000000000025bcff56eb36000; // add x^21 * (33! / 21!)
+ xi = (xi * _x) >> _precision;
+ res += xi * 0x000000000000000001b722e10ab1000; // add x^22 * (33! / 22!)
+ xi = (xi * _x) >> _precision;
+ res += xi * 0x0000000000000000001317c70077000; // add x^23 * (33! / 23!)
+ xi = (xi * _x) >> _precision;
+ res += xi * 0x00000000000000000000cba84aafa00; // add x^24 * (33! / 24!)
+ xi = (xi * _x) >> _precision;
+ res += xi * 0x00000000000000000000082573a0a00; // add x^25 * (33! / 25!)
+ xi = (xi * _x) >> _precision;
+ res += xi * 0x00000000000000000000005035ad900; // add x^26 * (33! / 26!)
+ xi = (xi * _x) >> _precision;
+ res += xi * 0x000000000000000000000002f881b00; // add x^27 * (33! / 27!)
+ xi = (xi * _x) >> _precision;
+ res += xi * 0x0000000000000000000000001b29340; // add x^28 * (33! / 28!)
+ xi = (xi * _x) >> _precision;
+ res += xi * 0x00000000000000000000000000efc40; // add x^29 * (33! / 29!)
+ xi = (xi * _x) >> _precision;
+ res += xi * 0x0000000000000000000000000007fe0; // add x^30 * (33! / 30!)
+ xi = (xi * _x) >> _precision;
+ res += xi * 0x0000000000000000000000000000420; // add x^31 * (33! / 31!)
+ xi = (xi * _x) >> _precision;
+ res += xi * 0x0000000000000000000000000000021; // add x^32 * (33! / 32!)
+ xi = (xi * _x) >> _precision;
+ res += xi * 0x0000000000000000000000000000001; // add x^33 * (33! / 33!)
+
+ return res / 0x688589cc0e9505e2f2fee5580000000 + _x + (ONE << _precision); // divide by 33! and then add x^1 / 1! + x^0 / 0!
+ }
+
+ /**
+ Return log(x / FIXED_1) * FIXED_1
+ Input range: FIXED_1 <= x <= LOG_EXP_MAX_VAL - 1
+ Auto-generated via 'PrintFunctionOptimalLog.py'
+ Detailed description:
+ - Rewrite the input as a product of natural exponents and a single residual r, such that 1 < r < 2
+ - The natural logarithm of each (pre-calculated) exponent is the degree of the exponent
+ - The natural logarithm of r is calculated via Taylor series for log(1 + x), where x = r - 1
+ - The natural logarithm of the input is calculated by summing up the intermediate results above
+ - For example: log(250) = log(e^4 * e^1 * e^0.5 * 1.021692859) = 4 + 1 + 0.5 + log(1 + 0.021692859)
+ */
+ function optimalLog(uint256 x) internal pure returns (uint256) {
+ uint256 res = 0;
+
+ uint256 y = 0;
+ uint256 z;
+ uint256 w;
+
+ if (x >= 0xd3094c70f034de4b96ff7d5b6f99fcd8) {
+ res += 0x40000000000000000000000000000000;
+ x = x * FIXED_1 / 0xd3094c70f034de4b96ff7d5b6f99fcd8;} // add 1 / 2^1
+ if (x >= 0xa45af1e1f40c333b3de1db4dd55f29a7) {
+ res += 0x20000000000000000000000000000000;
+ x = x * FIXED_1 / 0xa45af1e1f40c333b3de1db4dd55f29a7;} // add 1 / 2^2
+ if (x >= 0x910b022db7ae67ce76b441c27035c6a1) {
+ res += 0x10000000000000000000000000000000;
+ x = x * FIXED_1 / 0x910b022db7ae67ce76b441c27035c6a1;} // add 1 / 2^3
+ if (x >= 0x88415abbe9a76bead8d00cf112e4d4a8) {
+ res += 0x08000000000000000000000000000000;
+ x = x * FIXED_1 / 0x88415abbe9a76bead8d00cf112e4d4a8;} // add 1 / 2^4
+ if (x >= 0x84102b00893f64c705e841d5d4064bd3) {
+ res += 0x04000000000000000000000000000000;
+ x = x * FIXED_1 / 0x84102b00893f64c705e841d5d4064bd3;} // add 1 / 2^5
+ if (x >= 0x8204055aaef1c8bd5c3259f4822735a2) {
+ res += 0x02000000000000000000000000000000;
+ x = x * FIXED_1 / 0x8204055aaef1c8bd5c3259f4822735a2;} // add 1 / 2^6
+ if (x >= 0x810100ab00222d861931c15e39b44e99) {
+ res += 0x01000000000000000000000000000000;
+ x = x * FIXED_1 / 0x810100ab00222d861931c15e39b44e99;} // add 1 / 2^7
+ if (x >= 0x808040155aabbbe9451521693554f733) {
+ res += 0x00800000000000000000000000000000;
+ x = x * FIXED_1 / 0x808040155aabbbe9451521693554f733;} // add 1 / 2^8
+
+ z = y = x - FIXED_1;
+ w = y * y / FIXED_1;
+ res += z * (0x100000000000000000000000000000000 - y) / 0x100000000000000000000000000000000;
+ z = z * w / FIXED_1; // add y^01 / 01 - y^02 / 02
+ res += z * (0x0aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa - y) / 0x200000000000000000000000000000000;
+ z = z * w / FIXED_1; // add y^03 / 03 - y^04 / 04
+ res += z * (0x099999999999999999999999999999999 - y) / 0x300000000000000000000000000000000;
+ z = z * w / FIXED_1; // add y^05 / 05 - y^06 / 06
+ res += z * (0x092492492492492492492492492492492 - y) / 0x400000000000000000000000000000000;
+ z = z * w / FIXED_1; // add y^07 / 07 - y^08 / 08
+ res += z * (0x08e38e38e38e38e38e38e38e38e38e38e - y) / 0x500000000000000000000000000000000;
+ z = z * w / FIXED_1; // add y^09 / 09 - y^10 / 10
+ res += z * (0x08ba2e8ba2e8ba2e8ba2e8ba2e8ba2e8b - y) / 0x600000000000000000000000000000000;
+ z = z * w / FIXED_1; // add y^11 / 11 - y^12 / 12
+ res += z * (0x089d89d89d89d89d89d89d89d89d89d89 - y) / 0x700000000000000000000000000000000;
+ z = z * w / FIXED_1; // add y^13 / 13 - y^14 / 14
+ res += z * (0x088888888888888888888888888888888 - y) / 0x800000000000000000000000000000000;
+ // add y^15 / 15 - y^16 / 16
+
+ return res;
+ }
+
+ /**
+ Return e ^ (x / FIXED_1) * FIXED_1
+ Input range: 0 <= x <= OPT_EXP_MAX_VAL - 1
+ Auto-generated via 'PrintFunctionOptimalExp.py'
+ Detailed description:
+ - Rewrite the input as a sum of binary exponents and a single residual r, as small as possible
+ - The exponentiation of each binary exponent is given (pre-calculated)
+ - The exponentiation of r is calculated via Taylor series for e^x, where x = r
+ - The exponentiation of the input is calculated by multiplying the intermediate results above
+ - For example: e^5.521692859 = e^(4 + 1 + 0.5 + 0.021692859) = e^4 * e^1 * e^0.5 * e^0.021692859
+ */
+ function optimalExp(uint256 x) internal pure returns (uint256) {
+ uint256 res = 0;
+
+ uint256 y = 0;
+ uint256 z;
+
+ z = y = x % 0x10000000000000000000000000000000; // get the input modulo 2^(-3)
+ z = z * y / FIXED_1;
+ res += z * 0x10e1b3be415a0000; // add y^02 * (20! / 02!)
+ z = z * y / FIXED_1;
+ res += z * 0x05a0913f6b1e0000; // add y^03 * (20! / 03!)
+ z = z * y / FIXED_1;
+ res += z * 0x0168244fdac78000; // add y^04 * (20! / 04!)
+ z = z * y / FIXED_1;
+ res += z * 0x004807432bc18000; // add y^05 * (20! / 05!)
+ z = z * y / FIXED_1;
+ res += z * 0x000c0135dca04000; // add y^06 * (20! / 06!)
+ z = z * y / FIXED_1;
+ res += z * 0x0001b707b1cdc000; // add y^07 * (20! / 07!)
+ z = z * y / FIXED_1;
+ res += z * 0x000036e0f639b800; // add y^08 * (20! / 08!)
+ z = z * y / FIXED_1;
+ res += z * 0x00000618fee9f800; // add y^09 * (20! / 09!)
+ z = z * y / FIXED_1;
+ res += z * 0x0000009c197dcc00; // add y^10 * (20! / 10!)
+ z = z * y / FIXED_1;
+ res += z * 0x0000000e30dce400; // add y^11 * (20! / 11!)
+ z = z * y / FIXED_1;
+ res += z * 0x000000012ebd1300; // add y^12 * (20! / 12!)
+ z = z * y / FIXED_1;
+ res += z * 0x0000000017499f00; // add y^13 * (20! / 13!)
+ z = z * y / FIXED_1;
+ res += z * 0x0000000001a9d480; // add y^14 * (20! / 14!)
+ z = z * y / FIXED_1;
+ res += z * 0x00000000001c6380; // add y^15 * (20! / 15!)
+ z = z * y / FIXED_1;
+ res += z * 0x000000000001c638; // add y^16 * (20! / 16!)
+ z = z * y / FIXED_1;
+ res += z * 0x0000000000001ab8; // add y^17 * (20! / 17!)
+ z = z * y / FIXED_1;
+ res += z * 0x000000000000017c; // add y^18 * (20! / 18!)
+ z = z * y / FIXED_1;
+ res += z * 0x0000000000000014; // add y^19 * (20! / 19!)
+ z = z * y / FIXED_1;
+ res += z * 0x0000000000000001; // add y^20 * (20! / 20!)
+ res = res / 0x21c3677c82b40000 + y + FIXED_1; // divide by 20! and then add y^1 / 1! + y^0 / 0!
+
+ if ((x & 0x010000000000000000000000000000000) != 0)
+ res = res * 0x1c3d6a24ed82218787d624d3e5eba95f9 / 0x18ebef9eac820ae8682b9793ac6d1e776; // multiply by e^2^(-3)
+ if ((x & 0x020000000000000000000000000000000) != 0)
+ res = res * 0x18ebef9eac820ae8682b9793ac6d1e778 / 0x1368b2fc6f9609fe7aceb46aa619baed4; // multiply by e^2^(-2)
+ if ((x & 0x040000000000000000000000000000000) != 0)
+ res = res * 0x1368b2fc6f9609fe7aceb46aa619baed5 / 0x0bc5ab1b16779be3575bd8f0520a9f21f; // multiply by e^2^(-1)
+ if ((x & 0x080000000000000000000000000000000) != 0)
+ res = res * 0x0bc5ab1b16779be3575bd8f0520a9f21e / 0x0454aaa8efe072e7f6ddbab84b40a55c9; // multiply by e^2^(+0)
+ if ((x & 0x100000000000000000000000000000000) != 0)
+ res = res * 0x0454aaa8efe072e7f6ddbab84b40a55c5 / 0x00960aadc109e7a3bf4578099615711ea; // multiply by e^2^(+1)
+ if ((x & 0x200000000000000000000000000000000) != 0)
+ res = res * 0x00960aadc109e7a3bf4578099615711d7 / 0x0002bf84208204f5977f9a8cf01fdce3d; // multiply by e^2^(+2)
+ if ((x & 0x400000000000000000000000000000000) != 0)
+ res = res * 0x0002bf84208204f5977f9a8cf01fdc307 / 0x0000003c6ab775dd0b95b4cbee7e65d11; // multiply by e^2^(+3)
+
+ return res;
+ }
+}
\ No newline at end of file
diff --git a/contracts/utils/SafeMath.sol b/contracts/utils/SafeMath.sol
new file mode 100644
index 0000000..1528830
--- /dev/null
+++ b/contracts/utils/SafeMath.sol
@@ -0,0 +1,56 @@
+pragma solidity ^0.5.2;
+
+
+library SafeMath {
+ /**
+ @dev returns the sum of _x and _y, reverts if the calculation overflows
+ @param _x value 1
+ @param _y value 2
+ @return sum
+ */
+ function add(uint256 _x, uint256 _y) internal pure returns (uint256) {
+ uint256 z = _x + _y;
+ require(z >= _x, "SafeMath failed");
+ return z;
+ }
+
+ /**
+ @dev returns the difference of _x minus _y, reverts if the calculation underflows
+ @param _x minuend
+ @param _y subtrahend
+ @return difference
+ */
+ function sub(uint256 _x, uint256 _y) internal pure returns (uint256) {
+ require(_x >= _y, "SafeMath failed");
+ return _x - _y;
+ }
+
+ /**
+ @dev returns the product of multiplying _x by _y, reverts if the calculation overflows
+ @param _x factor 1
+ @param _y factor 2
+ @return product
+ */
+ function mul(uint256 _x, uint256 _y) internal pure returns (uint256) {
+ // gas optimization
+ if (_x == 0)
+ return 0;
+
+ uint256 z = _x * _y;
+ require(z / _x == _y, "SafeMath failed");
+ return z;
+ }
+
+ /**
+ @dev Integer division of two numbers truncating the quotient, reverts on division by zero.
+ @param _x dividend
+ @param _y divisor
+ @return quotient
+ */
+ function div(uint256 _x, uint256 _y) internal pure returns (uint256) {
+ require(_y > 0, "SafeMath failed");
+ uint256 c = _x / _y;
+
+ return c;
+ }
+}
\ No newline at end of file
diff --git a/embark.json b/embark.json
new file mode 100644
index 0000000..870cde6
--- /dev/null
+++ b/embark.json
@@ -0,0 +1,26 @@
+{
+ "contracts": [
+ "contracts/**"
+ ],
+ "buildDir": "dist/",
+ "config": "config/",
+ "versions": {
+ "web3": "1.0.0-beta",
+ "solc": "0.5.2",
+ "ipfs-api": "17.2.4"
+ },
+ "plugins": {
+ "embark-solium": {},
+ "embarkjs-connector-web3": {},
+ "@trailofbits/embark-contract-info": {
+ "flags": ""
+ }
+ },
+ "options": {
+ "solc": {
+ "optimize": true,
+ "optimize-runs": 200
+ }
+ },
+ "generationDir": "embarkArtifacts"
+}
\ No newline at end of file
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..b30cd20
--- /dev/null
+++ b/package.json
@@ -0,0 +1,77 @@
+{
+ "name": "discover-dapps",
+ "homepage": "https://dap.ps",
+ "version": "0.1.0",
+ "private": true,
+ "dependencies": {
+ "@babel/runtime-corejs2": "^7.4.3",
+ "@trailofbits/embark-contract-info": "^1.0.0",
+ "bignumber.js": "^8.1.1",
+ "bs58": "^4.0.1",
+ "connected-react-router": "^6.3.2",
+ "debounce": "^1.2.0",
+ "embark": "^4.0.2",
+ "embark-solium": "0.0.1",
+ "decimal.js": "^10.0.2",
+ "history": "^4.7.2",
+ "moment": "^2.24.0",
+ "node-sass": "^4.11.0",
+ "prop-types": "^15.7.2",
+ "rc-slider": "8.6.9",
+ "rc-tooltip": "3.7.3",
+ "react": "^16.8.4",
+ "react-content-loader": "^4.2.1",
+ "react-dom": "^16.8.4",
+ "react-image-fallback": "^8.0.0",
+ "react-redux": "^6.0.1",
+ "react-router": "^4.3.1",
+ "react-router-dom": "^4.3.1",
+ "react-scripts": "2.1.8",
+ "redux": "^4.0.1",
+ "redux-thunk": "^2.3.0",
+ "reselect": "^4.0.0",
+ "web3-utils": "^1.0.0-beta.35",
+ "webpack": "4.28.3"
+ },
+ "scripts": {
+ "start": "react-scripts start",
+ "build": "react-scripts build",
+ "test": "react-scripts test",
+ "eject": "react-scripts eject",
+ "predeploy": "npm run build",
+ "deploy": "gh-pages -d build",
+ "slither": "slither . --exclude naming-convention --filter-paths token"
+ },
+ "husky": {
+ "hooks": {
+ "pre-commit": "lint-staged"
+ }
+ },
+ "lint-staged": {
+ "src/**/*.{js,jsx,ts,tsx,json,css,scss,md}": [
+ "prettier --single-quote --write",
+ "git add"
+ ]
+ },
+ "eslintConfig": {
+ "extends": "react-app"
+ },
+ "browserslist": [
+ ">0.2%",
+ "not dead",
+ "not ie <= 11",
+ "not op_mini all"
+ ],
+ "devDependencies": {
+ "bignumber.js": "^8.1.1",
+ "embarkjs-connector-web3": "^4.0.0",
+ "eslint-config-airbnb": "^17.1.0",
+ "eslint-config-prettier": "^4.1.0",
+ "eslint-plugin-prettier": "^3.0.1",
+ "gh-pages": "^2.0.1",
+ "husky": "^1.3.1",
+ "lint-staged": "^8.1.5",
+ "prettier": "^1.16.4",
+ "webpack": "4.28.3"
+ }
+}
diff --git a/public/favicon.ico b/public/favicon.ico
new file mode 100644
index 0000000..c371f52
Binary files /dev/null and b/public/favicon.ico differ
diff --git a/public/fonts/Inter-Black.woff b/public/fonts/Inter-Black.woff
new file mode 100644
index 0000000..b2c4e27
Binary files /dev/null and b/public/fonts/Inter-Black.woff differ
diff --git a/public/fonts/Inter-Black.woff2 b/public/fonts/Inter-Black.woff2
new file mode 100644
index 0000000..5e7bd9f
Binary files /dev/null and b/public/fonts/Inter-Black.woff2 differ
diff --git a/public/fonts/Inter-BlackItalic.woff b/public/fonts/Inter-BlackItalic.woff
new file mode 100644
index 0000000..ff10955
Binary files /dev/null and b/public/fonts/Inter-BlackItalic.woff differ
diff --git a/public/fonts/Inter-BlackItalic.woff2 b/public/fonts/Inter-BlackItalic.woff2
new file mode 100644
index 0000000..97f3c0b
Binary files /dev/null and b/public/fonts/Inter-BlackItalic.woff2 differ
diff --git a/public/fonts/Inter-Bold.woff b/public/fonts/Inter-Bold.woff
new file mode 100644
index 0000000..43dfb67
Binary files /dev/null and b/public/fonts/Inter-Bold.woff differ
diff --git a/public/fonts/Inter-Bold.woff2 b/public/fonts/Inter-Bold.woff2
new file mode 100644
index 0000000..b26180b
Binary files /dev/null and b/public/fonts/Inter-Bold.woff2 differ
diff --git a/public/fonts/Inter-BoldItalic.woff b/public/fonts/Inter-BoldItalic.woff
new file mode 100644
index 0000000..0aa33d0
Binary files /dev/null and b/public/fonts/Inter-BoldItalic.woff differ
diff --git a/public/fonts/Inter-BoldItalic.woff2 b/public/fonts/Inter-BoldItalic.woff2
new file mode 100644
index 0000000..07ad99d
Binary files /dev/null and b/public/fonts/Inter-BoldItalic.woff2 differ
diff --git a/public/fonts/Inter-ExtraBold.woff b/public/fonts/Inter-ExtraBold.woff
new file mode 100644
index 0000000..a814de5
Binary files /dev/null and b/public/fonts/Inter-ExtraBold.woff differ
diff --git a/public/fonts/Inter-ExtraBold.woff2 b/public/fonts/Inter-ExtraBold.woff2
new file mode 100644
index 0000000..fc1e3e2
Binary files /dev/null and b/public/fonts/Inter-ExtraBold.woff2 differ
diff --git a/public/fonts/Inter-ExtraBoldItalic.woff b/public/fonts/Inter-ExtraBoldItalic.woff
new file mode 100644
index 0000000..6eaf0b2
Binary files /dev/null and b/public/fonts/Inter-ExtraBoldItalic.woff differ
diff --git a/public/fonts/Inter-ExtraBoldItalic.woff2 b/public/fonts/Inter-ExtraBoldItalic.woff2
new file mode 100644
index 0000000..79a2452
Binary files /dev/null and b/public/fonts/Inter-ExtraBoldItalic.woff2 differ
diff --git a/public/fonts/Inter-ExtraLight-BETA.woff b/public/fonts/Inter-ExtraLight-BETA.woff
new file mode 100644
index 0000000..131a66f
Binary files /dev/null and b/public/fonts/Inter-ExtraLight-BETA.woff differ
diff --git a/public/fonts/Inter-ExtraLight-BETA.woff2 b/public/fonts/Inter-ExtraLight-BETA.woff2
new file mode 100644
index 0000000..e080a70
Binary files /dev/null and b/public/fonts/Inter-ExtraLight-BETA.woff2 differ
diff --git a/public/fonts/Inter-ExtraLightItalic-BETA.woff b/public/fonts/Inter-ExtraLightItalic-BETA.woff
new file mode 100644
index 0000000..257b53a
Binary files /dev/null and b/public/fonts/Inter-ExtraLightItalic-BETA.woff differ
diff --git a/public/fonts/Inter-ExtraLightItalic-BETA.woff2 b/public/fonts/Inter-ExtraLightItalic-BETA.woff2
new file mode 100644
index 0000000..0ccb9b6
Binary files /dev/null and b/public/fonts/Inter-ExtraLightItalic-BETA.woff2 differ
diff --git a/public/fonts/Inter-Italic.woff b/public/fonts/Inter-Italic.woff
new file mode 100644
index 0000000..7e07d71
Binary files /dev/null and b/public/fonts/Inter-Italic.woff differ
diff --git a/public/fonts/Inter-Italic.woff2 b/public/fonts/Inter-Italic.woff2
new file mode 100644
index 0000000..435fe82
Binary files /dev/null and b/public/fonts/Inter-Italic.woff2 differ
diff --git a/public/fonts/Inter-Light-BETA.woff b/public/fonts/Inter-Light-BETA.woff
new file mode 100644
index 0000000..0b46abc
Binary files /dev/null and b/public/fonts/Inter-Light-BETA.woff differ
diff --git a/public/fonts/Inter-Light-BETA.woff2 b/public/fonts/Inter-Light-BETA.woff2
new file mode 100644
index 0000000..c5fcddd
Binary files /dev/null and b/public/fonts/Inter-Light-BETA.woff2 differ
diff --git a/public/fonts/Inter-LightItalic-BETA.woff b/public/fonts/Inter-LightItalic-BETA.woff
new file mode 100644
index 0000000..d1101cf
Binary files /dev/null and b/public/fonts/Inter-LightItalic-BETA.woff differ
diff --git a/public/fonts/Inter-LightItalic-BETA.woff2 b/public/fonts/Inter-LightItalic-BETA.woff2
new file mode 100644
index 0000000..fafad9a
Binary files /dev/null and b/public/fonts/Inter-LightItalic-BETA.woff2 differ
diff --git a/public/fonts/Inter-Medium.woff b/public/fonts/Inter-Medium.woff
new file mode 100644
index 0000000..15079dc
Binary files /dev/null and b/public/fonts/Inter-Medium.woff differ
diff --git a/public/fonts/Inter-Medium.woff2 b/public/fonts/Inter-Medium.woff2
new file mode 100644
index 0000000..7d0fbe9
Binary files /dev/null and b/public/fonts/Inter-Medium.woff2 differ
diff --git a/public/fonts/Inter-MediumItalic.woff b/public/fonts/Inter-MediumItalic.woff
new file mode 100644
index 0000000..7d8c122
Binary files /dev/null and b/public/fonts/Inter-MediumItalic.woff differ
diff --git a/public/fonts/Inter-MediumItalic.woff2 b/public/fonts/Inter-MediumItalic.woff2
new file mode 100644
index 0000000..fa86742
Binary files /dev/null and b/public/fonts/Inter-MediumItalic.woff2 differ
diff --git a/public/fonts/Inter-Regular.woff b/public/fonts/Inter-Regular.woff
new file mode 100644
index 0000000..e8587fd
Binary files /dev/null and b/public/fonts/Inter-Regular.woff differ
diff --git a/public/fonts/Inter-Regular.woff2 b/public/fonts/Inter-Regular.woff2
new file mode 100644
index 0000000..46568fd
Binary files /dev/null and b/public/fonts/Inter-Regular.woff2 differ
diff --git a/public/fonts/Inter-SemiBold.woff b/public/fonts/Inter-SemiBold.woff
new file mode 100644
index 0000000..de40b73
Binary files /dev/null and b/public/fonts/Inter-SemiBold.woff differ
diff --git a/public/fonts/Inter-SemiBold.woff2 b/public/fonts/Inter-SemiBold.woff2
new file mode 100644
index 0000000..6bb8bee
Binary files /dev/null and b/public/fonts/Inter-SemiBold.woff2 differ
diff --git a/public/fonts/Inter-SemiBoldItalic.woff b/public/fonts/Inter-SemiBoldItalic.woff
new file mode 100644
index 0000000..47b0df3
Binary files /dev/null and b/public/fonts/Inter-SemiBoldItalic.woff differ
diff --git a/public/fonts/Inter-SemiBoldItalic.woff2 b/public/fonts/Inter-SemiBoldItalic.woff2
new file mode 100644
index 0000000..9bfbdb8
Binary files /dev/null and b/public/fonts/Inter-SemiBoldItalic.woff2 differ
diff --git a/public/fonts/Inter-Thin-BETA.woff b/public/fonts/Inter-Thin-BETA.woff
new file mode 100644
index 0000000..46d1472
Binary files /dev/null and b/public/fonts/Inter-Thin-BETA.woff differ
diff --git a/public/fonts/Inter-Thin-BETA.woff2 b/public/fonts/Inter-Thin-BETA.woff2
new file mode 100644
index 0000000..382a464
Binary files /dev/null and b/public/fonts/Inter-Thin-BETA.woff2 differ
diff --git a/public/fonts/Inter-ThinItalic-BETA.woff b/public/fonts/Inter-ThinItalic-BETA.woff
new file mode 100644
index 0000000..6c284f3
Binary files /dev/null and b/public/fonts/Inter-ThinItalic-BETA.woff differ
diff --git a/public/fonts/Inter-ThinItalic-BETA.woff2 b/public/fonts/Inter-ThinItalic-BETA.woff2
new file mode 100644
index 0000000..f9471a0
Binary files /dev/null and b/public/fonts/Inter-ThinItalic-BETA.woff2 differ
diff --git a/public/fonts/Inter-italic.var.woff2 b/public/fonts/Inter-italic.var.woff2
new file mode 100644
index 0000000..83fb631
Binary files /dev/null and b/public/fonts/Inter-italic.var.woff2 differ
diff --git a/public/fonts/Inter-upright.var.woff2 b/public/fonts/Inter-upright.var.woff2
new file mode 100644
index 0000000..db2b70c
Binary files /dev/null and b/public/fonts/Inter-upright.var.woff2 differ
diff --git a/public/fonts/Inter.var.woff2 b/public/fonts/Inter.var.woff2
new file mode 100644
index 0000000..e8d4309
Binary files /dev/null and b/public/fonts/Inter.var.woff2 differ
diff --git a/public/images/dapps/3Box.png b/public/images/dapps/3Box.png
new file mode 100644
index 0000000..649fffc
Binary files /dev/null and b/public/images/dapps/3Box.png differ
diff --git a/public/images/dapps/Dentacoin.png b/public/images/dapps/Dentacoin.png
new file mode 100644
index 0000000..9040a45
Binary files /dev/null and b/public/images/dapps/Dentacoin.png differ
diff --git a/public/images/dapps/airswap.png b/public/images/dapps/airswap.png
new file mode 100644
index 0000000..c6eb532
Binary files /dev/null and b/public/images/dapps/airswap.png differ
diff --git a/public/images/dapps/aragon.png b/public/images/dapps/aragon.png
new file mode 100644
index 0000000..27c70c5
Binary files /dev/null and b/public/images/dapps/aragon.png differ
diff --git a/public/images/dapps/astroledger.svg b/public/images/dapps/astroledger.svg
new file mode 100644
index 0000000..f28583b
--- /dev/null
+++ b/public/images/dapps/astroledger.svg
@@ -0,0 +1,108 @@
+
+
+
+
diff --git a/public/images/dapps/augur.png b/public/images/dapps/augur.png
new file mode 100644
index 0000000..5228e02
Binary files /dev/null and b/public/images/dapps/augur.png differ
diff --git a/public/images/dapps/augur.svg b/public/images/dapps/augur.svg
new file mode 100644
index 0000000..a5ad5cb
--- /dev/null
+++ b/public/images/dapps/augur.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/public/images/dapps/bancor.png b/public/images/dapps/bancor.png
new file mode 100644
index 0000000..291377f
Binary files /dev/null and b/public/images/dapps/bancor.png differ
diff --git a/public/images/dapps/bchat.png b/public/images/dapps/bchat.png
new file mode 100644
index 0000000..a31e185
Binary files /dev/null and b/public/images/dapps/bchat.png differ
diff --git a/public/images/dapps/bidali.png b/public/images/dapps/bidali.png
new file mode 100644
index 0000000..27ddedd
Binary files /dev/null and b/public/images/dapps/bidali.png differ
diff --git a/public/images/dapps/blockimmo.png b/public/images/dapps/blockimmo.png
new file mode 100644
index 0000000..ab82371
Binary files /dev/null and b/public/images/dapps/blockimmo.png differ
diff --git a/public/images/dapps/bounties-network.png b/public/images/dapps/bounties-network.png
new file mode 100644
index 0000000..c0f27c7
Binary files /dev/null and b/public/images/dapps/bounties-network.png differ
diff --git a/public/images/dapps/cent.png b/public/images/dapps/cent.png
new file mode 100644
index 0000000..3ea6c9b
Binary files /dev/null and b/public/images/dapps/cent.png differ
diff --git a/public/images/dapps/civitas.png b/public/images/dapps/civitas.png
new file mode 100644
index 0000000..bd53976
Binary files /dev/null and b/public/images/dapps/civitas.png differ
diff --git a/public/images/dapps/commiteth.png b/public/images/dapps/commiteth.png
new file mode 100644
index 0000000..8a43c9b
Binary files /dev/null and b/public/images/dapps/commiteth.png differ
diff --git a/public/images/dapps/compoundfinance.png b/public/images/dapps/compoundfinance.png
new file mode 100644
index 0000000..eb3637a
Binary files /dev/null and b/public/images/dapps/compoundfinance.png differ
diff --git a/public/images/dapps/console.png b/public/images/dapps/console.png
new file mode 100644
index 0000000..9aa3dea
Binary files /dev/null and b/public/images/dapps/console.png differ
diff --git a/public/images/dapps/cryptocare.jpg b/public/images/dapps/cryptocare.jpg
new file mode 100644
index 0000000..99ea73a
Binary files /dev/null and b/public/images/dapps/cryptocare.jpg differ
diff --git a/public/images/dapps/cryptocribs.png b/public/images/dapps/cryptocribs.png
new file mode 100644
index 0000000..6f82c2b
Binary files /dev/null and b/public/images/dapps/cryptocribs.png differ
diff --git a/public/images/dapps/cryptofighters.png b/public/images/dapps/cryptofighters.png
new file mode 100644
index 0000000..cbf5df6
Binary files /dev/null and b/public/images/dapps/cryptofighters.png differ
diff --git a/public/images/dapps/cryptographics.png b/public/images/dapps/cryptographics.png
new file mode 100644
index 0000000..6fc9e17
Binary files /dev/null and b/public/images/dapps/cryptographics.png differ
diff --git a/public/images/dapps/cryptokitties.png b/public/images/dapps/cryptokitties.png
new file mode 100644
index 0000000..4a93ca5
Binary files /dev/null and b/public/images/dapps/cryptokitties.png differ
diff --git a/public/images/dapps/cryptopunks.png b/public/images/dapps/cryptopunks.png
new file mode 100644
index 0000000..be0acdc
Binary files /dev/null and b/public/images/dapps/cryptopunks.png differ
diff --git a/public/images/dapps/cryptopurr.png b/public/images/dapps/cryptopurr.png
new file mode 100644
index 0000000..1385610
Binary files /dev/null and b/public/images/dapps/cryptopurr.png differ
diff --git a/public/images/dapps/cryptostrikers.png b/public/images/dapps/cryptostrikers.png
new file mode 100644
index 0000000..b3ad4db
Binary files /dev/null and b/public/images/dapps/cryptostrikers.png differ
diff --git a/public/images/dapps/cryptotakeovers.png b/public/images/dapps/cryptotakeovers.png
new file mode 100644
index 0000000..fe7c988
Binary files /dev/null and b/public/images/dapps/cryptotakeovers.png differ
diff --git a/public/images/dapps/dBay.png b/public/images/dapps/dBay.png
new file mode 100644
index 0000000..2379ebd
Binary files /dev/null and b/public/images/dapps/dBay.png differ
diff --git a/public/images/dapps/dai.png b/public/images/dapps/dai.png
new file mode 100644
index 0000000..8a3abc2
Binary files /dev/null and b/public/images/dapps/dai.png differ
diff --git a/public/images/dapps/ddex.png b/public/images/dapps/ddex.png
new file mode 100644
index 0000000..27901ea
Binary files /dev/null and b/public/images/dapps/ddex.png differ
diff --git a/public/images/dapps/decentraland.png b/public/images/dapps/decentraland.png
new file mode 100644
index 0000000..e3bd381
Binary files /dev/null and b/public/images/dapps/decentraland.png differ
diff --git a/public/images/dapps/dragonereum.png b/public/images/dapps/dragonereum.png
new file mode 100644
index 0000000..a8ddb6e
Binary files /dev/null and b/public/images/dapps/dragonereum.png differ
diff --git a/public/images/dapps/easytrade.png b/public/images/dapps/easytrade.png
new file mode 100644
index 0000000..5ce6275
Binary files /dev/null and b/public/images/dapps/easytrade.png differ
diff --git a/public/images/dapps/emoon.png b/public/images/dapps/emoon.png
new file mode 100644
index 0000000..d250ff4
Binary files /dev/null and b/public/images/dapps/emoon.png differ
diff --git a/public/images/dapps/eth2phone.png b/public/images/dapps/eth2phone.png
new file mode 100644
index 0000000..11e7594
Binary files /dev/null and b/public/images/dapps/eth2phone.png differ
diff --git a/public/images/dapps/ethcro.png b/public/images/dapps/ethcro.png
new file mode 100644
index 0000000..5fb9ff4
Binary files /dev/null and b/public/images/dapps/ethcro.png differ
diff --git a/public/images/dapps/etherbots.png b/public/images/dapps/etherbots.png
new file mode 100644
index 0000000..e9d265b
Binary files /dev/null and b/public/images/dapps/etherbots.png differ
diff --git a/public/images/dapps/etheremon.png b/public/images/dapps/etheremon.png
new file mode 100644
index 0000000..9f9ff49
Binary files /dev/null and b/public/images/dapps/etheremon.png differ
diff --git a/public/images/dapps/etherman.png b/public/images/dapps/etherman.png
new file mode 100644
index 0000000..2bebacd
Binary files /dev/null and b/public/images/dapps/etherman.png differ
diff --git a/public/images/dapps/etherplay.png b/public/images/dapps/etherplay.png
new file mode 100644
index 0000000..52992ee
Binary files /dev/null and b/public/images/dapps/etherplay.png differ
diff --git a/public/images/dapps/ethlance.png b/public/images/dapps/ethlance.png
new file mode 100644
index 0000000..80db5d7
Binary files /dev/null and b/public/images/dapps/ethlance.png differ
diff --git a/public/images/dapps/ethlend.png b/public/images/dapps/ethlend.png
new file mode 100644
index 0000000..c35ec23
Binary files /dev/null and b/public/images/dapps/ethlend.png differ
diff --git a/public/images/dapps/expotrading.png b/public/images/dapps/expotrading.png
new file mode 100644
index 0000000..2239e01
Binary files /dev/null and b/public/images/dapps/expotrading.png differ
diff --git a/public/images/dapps/fairhouse.png b/public/images/dapps/fairhouse.png
new file mode 100644
index 0000000..54ed24e
Binary files /dev/null and b/public/images/dapps/fairhouse.png differ
diff --git a/public/images/dapps/gnosis.png b/public/images/dapps/gnosis.png
new file mode 100644
index 0000000..abf790e
Binary files /dev/null and b/public/images/dapps/gnosis.png differ
diff --git a/public/images/dapps/hexel.png b/public/images/dapps/hexel.png
new file mode 100644
index 0000000..a2f7864
Binary files /dev/null and b/public/images/dapps/hexel.png differ
diff --git a/public/images/dapps/instadapp.jpg b/public/images/dapps/instadapp.jpg
new file mode 100644
index 0000000..5a69bfa
Binary files /dev/null and b/public/images/dapps/instadapp.jpg differ
diff --git a/public/images/dapps/kickback.png b/public/images/dapps/kickback.png
new file mode 100644
index 0000000..b6cd2b1
Binary files /dev/null and b/public/images/dapps/kickback.png differ
diff --git a/public/images/dapps/knownorigin.png b/public/images/dapps/knownorigin.png
new file mode 100644
index 0000000..b5686f5
Binary files /dev/null and b/public/images/dapps/knownorigin.png differ
diff --git a/public/images/dapps/kyber.png b/public/images/dapps/kyber.png
new file mode 100644
index 0000000..016d36b
Binary files /dev/null and b/public/images/dapps/kyber.png differ
diff --git a/public/images/dapps/livepeer.png b/public/images/dapps/livepeer.png
new file mode 100644
index 0000000..d1841c5
Binary files /dev/null and b/public/images/dapps/livepeer.png differ
diff --git a/public/images/dapps/local-ethereum.png b/public/images/dapps/local-ethereum.png
new file mode 100644
index 0000000..0c76b25
Binary files /dev/null and b/public/images/dapps/local-ethereum.png differ
diff --git a/public/images/dapps/melonport.png b/public/images/dapps/melonport.png
new file mode 100644
index 0000000..b216a92
Binary files /dev/null and b/public/images/dapps/melonport.png differ
diff --git a/public/images/dapps/mkr-market.png b/public/images/dapps/mkr-market.png
new file mode 100644
index 0000000..d132a6f
Binary files /dev/null and b/public/images/dapps/mkr-market.png differ
diff --git a/public/images/dapps/name-bazaar.png b/public/images/dapps/name-bazaar.png
new file mode 100644
index 0000000..bf99369
Binary files /dev/null and b/public/images/dapps/name-bazaar.png differ
diff --git a/public/images/dapps/nuo.png b/public/images/dapps/nuo.png
new file mode 100644
index 0000000..11c5ca7
Binary files /dev/null and b/public/images/dapps/nuo.png differ
diff --git a/public/images/dapps/oaken-water-meter.png b/public/images/dapps/oaken-water-meter.png
new file mode 100644
index 0000000..c83c115
Binary files /dev/null and b/public/images/dapps/oaken-water-meter.png differ
diff --git a/public/images/dapps/opensea.png b/public/images/dapps/opensea.png
new file mode 100644
index 0000000..a03cdd7
Binary files /dev/null and b/public/images/dapps/opensea.png differ
diff --git a/public/images/dapps/peepeth.png b/public/images/dapps/peepeth.png
new file mode 100644
index 0000000..052de5e
Binary files /dev/null and b/public/images/dapps/peepeth.png differ
diff --git a/public/images/dapps/slowtrade.png b/public/images/dapps/slowtrade.png
new file mode 100644
index 0000000..16c12f7
Binary files /dev/null and b/public/images/dapps/slowtrade.png differ
diff --git a/public/images/dapps/smartz.png b/public/images/dapps/smartz.png
new file mode 100644
index 0000000..19f0c2d
Binary files /dev/null and b/public/images/dapps/smartz.png differ
diff --git a/public/images/dapps/snt-voting.png b/public/images/dapps/snt-voting.png
new file mode 100644
index 0000000..2bc0e1e
Binary files /dev/null and b/public/images/dapps/snt-voting.png differ
diff --git a/public/images/dapps/superrare.png b/public/images/dapps/superrare.png
new file mode 100644
index 0000000..7d8faa4
Binary files /dev/null and b/public/images/dapps/superrare.png differ
diff --git a/public/images/dapps/uniswap.png b/public/images/dapps/uniswap.png
new file mode 100644
index 0000000..f5881e8
Binary files /dev/null and b/public/images/dapps/uniswap.png differ
diff --git a/public/index.html b/public/index.html
new file mode 100644
index 0000000..706a385
--- /dev/null
+++ b/public/index.html
@@ -0,0 +1,40 @@
+
+
+
+ Upload a name, url, description, category and image for your
+ DApp in the next step compulsory.
+
+
+ Stake the amount of SNT you want to rank your DApp optional.
+
+
Hit “submit”.
+
+ Our team will ensure that your DApp works well on mobile
+ devices and will then include it on the live site using the
+ information you provided in Step 1.
+
+
+
+
+
Staking
+
+ You need not stake anything to be included - your DApp just
+ won’t appear in the “Highest Ranked” section. If you do stake
+ SNT, your DApp will appear in that section immediately. The
+ DApp with the highest effective balance (that is, SNT staked
+ plus/minus votes cast for/against) ranks highest.
+
+
+ SNT you stake is locked in the Discover contract. You can, at
+ any time, withdraw a percentage of what you have staked. The
+ more you stake, the lower the percentage you can withdraw.
+ Withdrawals must be made from the same wallet as you submitted
+ with, so PLEASE SECURE THIS ADDRESS.
+
+
+
+
+
+ Staking 10 000 SNT returns a rate of{' '}
+ 99.5%, so you can withdraw up to{' '}
+ 9 950 SNT.
+
+
+ Staking 1 000 000 SNT returns a rate of
+ 50.99%, so you can withdraw up to{' '}
+ 509 958 SNT.
+
+
+
+
+
+ Furthermore, the operators of{' '}
+ https://dap.ps reserve the right
+ to remove any DApp from the UI for reasons including, but not
+ limited to:
+
+ Anyone is welcome to fork the software and implement different
+ UI choices for the same underlying contract. Note that
+ Discover is not affiliated with Status directly, we have
+ simply chosen to use SNT as a token of value, to abide by{' '}
+ Status’ principles, and to take a mobile-first approach
+ to development.
+
+ You must be over 13, agree that using our service is legal in your
+ jurisdiction, and that you won't do anything illegal with what we
+ provide.
+
+
+ We are not lawyers or financial advisors, and you use this
+ software at your own risk.
+
+
+
+
+
+
+ You accept the Terms by either (1) clicking to agree or accept
+ where these options are presented to you, or (2) actually using
+ Discover (“Discover”) at https://dap.ps
+
+
+ In order to use Discover you must be 13 years of age or older.
+ If you are between 13 and 18 years of age, you must have your
+ parent or legal guardian’s permission to use Discover.
+
+
+ By accessing Discover you accept the terms of use as set out
+ herein. All information is provided of a mere general nature for
+ informational purposes only. By accessing Discover you warrant
+ to the operators, contributors and the host thereof that you may
+ freely, without limitation, access the DApp store and all of its
+ contents in your jurisdiction and shall not use Discover and its
+ contents in any way that infringes on laws or the rights of
+ others including those of the aforementioned persons (including
+ the entities they may represent).
+
+
+ Neither Discover nor any of the persons or entities involved in
+ any way in respect of Discover, including its host and its
+ contributors, provide for specific legal, fiscal, economical
+ and/or any other kind of advice or recommendation that may be
+ relied upon. A visitor to Discover will therefore act at their
+ own risk in accessing or in any way relying on the content of
+ the Discover and the visitor is therefore solely responsible for
+ any consequences thereof.
+
You will protect your users' information, no matter what.
+
+ You will not use information you do not have permission to use,
+ and you may not hack anyone by submitting malicious code or
+ otherwise manipulating our service.
+
+
+
+
+
+
+ You agree that if You make Your DApp available through Discover,
+ You will protect the privacy and legal rights of users. If the
+ users provide You with, or Your DApp accesses or uses,
+ usernames, passwords, or other login information or personal
+ information, You agree to make the users aware that the
+ information will be available to Your DApp, and You agree to
+ provide legally adequate privacy notice and protection for those
+ users. Further, Your Dapp may only use that information for the
+ limited purposes for which the user has given You permission to
+ do so.
+
+
+ If Your DApp stores personal or sensitive information provided
+ by users, You agree to do so securely and only for as long as it
+ is needed. However, if the user has opted into a separate
+ agreement with You that allows You or Your DApp to store or use
+ personal or sensitive information directly related to Your DApp
+ (not including other products or applications), then the terms
+ of that separate agreement will govern Your use of such
+ information.
+
+
+ You will not engage in any activity with Discover, including
+ making Your Dapp available via Discover, that interferes with,
+ disrupts, damages, or accesses in an unauthorized manner the
+ devices, servers, networks, or other properties or services of
+ any third party including, but not limited to, Status or any
+ Authorized Provider. You may not use user information obtained
+ via Discover to sell or distribute DApp outside of Discover.
+
+ The people responsible for Discover are not liable for your
+ mistakes.
+
+
+
+
+
+
+ The content, data, materials and/or other services on Discover
+ are provided without any warranties of any kind regarding its
+ title, ownership, accuracy, completeness and correctness.
+
+
+ Specifically, unless otherwise required by law, in no event
+ shall the operators, contributors to or hosts of Discover be
+ liable for any damages of any kind, including, but not limited
+ to, loss of use, loss of assets or rights or privileges, loss of
+ profits, or loss of data arising out of or in any way connected
+ with the use of the DApps and the information thereon from time
+ to time.
+
+
+ In no way are the operators, contributors to or host of Discover
+ responsible for the actions, decisions, transactions, or other
+ behavior taken or not taken by You or person relying on You in
+ reliance upon Discover and its contents from time to time.
+
+
+ {limitationsHeight === DEFAULT_HEIGHT && (
+
+
+ Read more
+
+
+ )}
+
+
+
+
Limitation of liability
+
+
+
+ Swiss law exclusively applies to the use of content, data,
+ materials and/or other services provided for/on Discover. The
+ court of the Canton of Zug, Switzerland, will be the sole and
+ exclusive competent court regarding any dispute relating to or
+ stemming from the use of Discover including, without limitation,
+ in respect of any breach of or dispute in respect as referred
+ above, irrespective of the jurisdiction applicable thereto.
+
+
+
+
+
+
Last Amendment
+
+
+
+
+ These terms of use were amended for the last time on 15th April
+ 2019 and may be altered at any time without prior notice.
+
+ SNT you spend to upvote is locked in the contract and contributes
+ directly to {dapp.name}'s ranking.{' '}
+
+ Learn more↗
+
+
+ )}
+ {!isUpvote && (
+
+ SNT you spend to downvote goes directly back to {dapp.name}.
+ Downvoting moves their DApp down by 1% of the current ranking. The
+ cost is fixed by our unique bonded curve.{' '}
+
+ Learn more↗
+
+
+ )}
+
+
+
+ )
+ }
+}
+
+Vote.defaultProps = {
+ dapp: null,
+ afterVoteRating: null,
+}
+
+Vote.propTypes = {
+ dapp: PropTypes.shape(DappModel),
+ isUpvote: PropTypes.bool.isRequired,
+ visible: PropTypes.bool.isRequired,
+ sntValue: PropTypes.string.isRequired,
+ afterVoteRating: PropTypes.number,
+ onClickClose: PropTypes.func.isRequired,
+ onClickUpvote: PropTypes.func.isRequired,
+ onClickDownvote: PropTypes.func.isRequired,
+ onInputSntValue: PropTypes.func.isRequired,
+ fetchVoteRating: PropTypes.func.isRequired,
+ upVote: PropTypes.func.isRequired,
+ downVote: PropTypes.func.isRequired,
+}
+
+export default Vote
diff --git a/src/modules/Vote/Vote.module.scss b/src/modules/Vote/Vote.module.scss
new file mode 100644
index 0000000..c8ec269
--- /dev/null
+++ b/src/modules/Vote/Vote.module.scss
@@ -0,0 +1,216 @@
+@import '../../common/styles/variables';
+
+.modalWindow {
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+}
+
+@media (min-width: $desktop) {
+ .modalWindow {
+ height: auto;
+ }
+}
+
+.modalContent {
+ display: flex;
+ flex-direction: column;
+ flex: 1 1 auto;
+}
+
+@media (min-width: $desktop) {
+ .modalContent {
+ height: 512px;
+ }
+}
+
+.tabs {
+ width: 100%;
+ border-bottom: 1px solid #eef2f5;
+ text-align: center;
+}
+
+.tabs button {
+ color: #939ba1;
+ position: relative;
+ background: transparent;
+ border: none;
+ text-transform: uppercase;
+ font-family: $font;
+ font-size: 13px;
+ height: calculateRem(40);
+ letter-spacing: calculateRem(0.2);
+ display: inline-flex;
+ align-items: center;
+ cursor: pointer;
+ outline: none;
+
+ transition-property: color;
+ transition-duration: 0.4s;
+}
+
+.tabs button:first-child {
+ margin-right: 32px;
+}
+
+.tabs button:after {
+ display: block;
+ clear: both;
+ content: '';
+ position: absolute;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ width: 24px;
+ border-bottom: 2px solid transparent;
+ margin: 0 auto 0 auto;
+
+ transition-property: border-color;
+ transition-duration: 0.4s;
+}
+
+.tabs button.active {
+ color: $link-color;
+}
+
+.tabs button.active:after {
+ border-bottom-color: $link-color;
+}
+
+.dapp {
+ height: 48px;
+ display: flex;
+ align-items: center;
+ font-family: $font;
+ font-weight: 500;
+ padding: 0 calculateRem(12);
+}
+
+.dapp .image {
+ max-width: 24px;
+ max-height: 24px;
+ border-radius: 50%;
+ margin-right: calculateRem(12);
+}
+
+.items {
+ display: flex;
+ flex-direction: column;
+ font-family: $font;
+}
+
+.items .itemRow {
+ height: 32px;
+ display: flex;
+ align-items: center;
+ padding: 0 calculateRem(12);
+}
+
+.items .item {
+ display: flex;
+ align-items: center;
+}
+
+.items .item img {
+ margin-right: calculateRem(12);
+}
+
+.badge {
+ border-radius: 24px;
+ color: #ffffff;
+ font-family: $font;
+ font-size: calculateRem(15);
+ margin-right: calculateRem(16);
+ margin-left: auto;
+ padding: calculateRem(3) calculateRem(10);
+}
+
+.greenBadge {
+ @extend .badge;
+ background: #44d058;
+}
+
+.redBadge {
+ @extend .badge;
+ background: #f00;
+}
+
+.inputArea {
+ width: calc(100% - 2 * 16px);
+ height: 64px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ text-align: center;
+ margin: auto;
+}
+
+.inputArea.inputAreaBorder {
+ border-bottom: 1px solid #eef2f5;
+}
+
+.inputArea input {
+ width: 19px;
+ border: none;
+ text-align: center;
+ font-size: calculateRem(32);
+ line-height: calculateRem(28);
+ font-family: $font;
+ margin-right: calculateRem(6);
+}
+
+.inputArea input:focus {
+ outline: none;
+}
+
+.inputArea::after {
+ transition: all 0.05s ease-in-out;
+ content: 'SNT';
+ color: #939ba1;
+ font-size: calculateRem(32);
+ line-height: calculateRem(28);
+ font-family: $font;
+}
+
+.inputArea span {
+ font-size: calculateRem(32);
+ line-height: calculateRem(28);
+ font-family: $font;
+ margin-right: calculateRem(6);
+}
+
+.footer {
+ text-align: center;
+}
+
+.footer button {
+ background: $link-color;
+ border-radius: 8px;
+ color: #fff;
+ margin: calculateRem(10) auto;
+ border: none;
+ font-family: $font;
+ padding: calculateRem(11) calculateRem(38);
+ font-size: calculateRem(15);
+ cursor: pointer;
+}
+
+.footer button:disabled,
+.footer button[disabled] {
+ background: $text-color;
+}
+
+.footer .disclaimer {
+ font-size: calculateRem(15);
+ color: $text-color;
+ line-height: 22px;
+ font-family: $font;
+ padding: calculateRem(16);
+ border-bottom: 1px solid #eef2f5;
+ margin: 0;
+}
+
+.footer .disclaimer a {
+ text-decoration: none;
+ color: $link-color;
+}
diff --git a/src/modules/Vote/Vote.reducer.js b/src/modules/Vote/Vote.reducer.js
new file mode 100644
index 0000000..aa560f3
--- /dev/null
+++ b/src/modules/Vote/Vote.reducer.js
@@ -0,0 +1,264 @@
+import voteInitialState from '../../common/data/vote'
+import reducerUtil from '../../common/utils/reducer'
+import BlockchainSDK from '../../common/blockchain'
+
+import { showAlertAction } from '../Alert/Alert.reducer'
+import {
+ onStartProgressAction,
+ onReceiveTransactionInfoAction,
+ checkTransactionStatusAction,
+} from '../TransactionStatus/TransactionStatus.recuder'
+import {
+ TYPE_UPVOTE,
+ TYPE_DOWNVOTE,
+} from '../TransactionStatus/TransactionStatus.utilities'
+
+const SHOW_UP_VOTE_AFTER_CHECK = 'VOTE_SHOW_UP_VOTE_AFTER_CHECK'
+const SHOW_DOWN_VOTE_AFTER_CHEECK = 'VOTE_SHOW_DOWN_VOTE_AFTER_CHEECK'
+const CLOSE_VOTE = 'VOTE_CLOSE_VOTE'
+const SWITCH_TO_UPVOTE = 'VOTE_SWITCH_TO_UPVOTE'
+const SWITCH_TO_DOWNVOTE = 'VOTE_SWITCH_TO_DOWNVOTE'
+const ON_INPUT_SNT_VALUE = 'VOTE_ON_INPUT_SNT_VALUE'
+const UPDATE_AFTER_UP_VOTING_VALUES = 'VOTE_UPDATE_AFTER_UP_VOTING_VALUES'
+const UPDATE_AFTER_DOWN_VOTING_VALUES = 'VOTE_UPDATE_AFTER_DOWN_VOTING_VALUES'
+
+export const showUpVoteActionAfterCheck = dapp => {
+ window.location.hash = 'vote'
+ return {
+ type: SHOW_UP_VOTE_AFTER_CHECK,
+ payload: dapp,
+ }
+}
+
+export const showDownVoteActionAfterCheck = dapp => {
+ window.location.hash = 'vote'
+ return {
+ type: SHOW_DOWN_VOTE_AFTER_CHEECK,
+ payload: dapp,
+ }
+}
+
+export const showUpVoteAction = dapp => {
+ return (dispatch, getState) => {
+ const state = getState()
+ if (state.transactionStatus.progress) {
+ dispatch(
+ showAlertAction(
+ 'There is an active transaction. Please wait for it to finish and then you could be able to vote',
+ ),
+ )
+ } else dispatch(showUpVoteActionAfterCheck(dapp))
+ }
+}
+
+export const showDownVoteAction = dapp => {
+ return (dispatch, getState) => {
+ const state = getState()
+ if (state.transactionStatus.progress) {
+ dispatch(
+ showAlertAction(
+ 'There is an active transaction. Please wait for it to finish and then you could be able to vote',
+ ),
+ )
+ } else dispatch(showDownVoteActionAfterCheck(dapp))
+ }
+}
+
+export const switchToUpvoteAction = () => ({
+ type: SWITCH_TO_UPVOTE,
+ payload: null,
+})
+
+export const switchToUpDownvoteAction = () => ({
+ type: SWITCH_TO_DOWNVOTE,
+ payload: null,
+})
+
+export const closeVoteAction = () => {
+ window.history.back()
+ return {
+ type: CLOSE_VOTE,
+ payload: null,
+ }
+}
+
+export const onInputSntValueAction = sntValue => ({
+ type: ON_INPUT_SNT_VALUE,
+ payload: sntValue,
+})
+
+export const updateAfterUpVotingValuesAction = rating => ({
+ type: UPDATE_AFTER_UP_VOTING_VALUES,
+ payload: rating,
+})
+
+export const updateAfterDownVotingValuesAction = (rating, sntValue) => ({
+ type: UPDATE_AFTER_DOWN_VOTING_VALUES,
+ payload: { rating, sntValue },
+})
+
+export const fetchVoteRatingAction = (dapp, isUpvote, sntValue) => {
+ return async (dispatch, getState) => {
+ let rating
+ let downVoteSntValue = 0
+ if (isUpvote === true) {
+ try {
+ const blockchain = await BlockchainSDK.getInstance()
+ rating = await blockchain.DiscoverService.upVoteEffect(
+ dapp.id,
+ sntValue,
+ )
+ rating = parseInt(rating, 10)
+ } catch (e) {
+ return
+ }
+ } else {
+ // rating = parseInt(dapp.sntValue * 0.99, 10)
+ try {
+ const blockchain = await BlockchainSDK.getInstance()
+ const downVoteEffect = await blockchain.DiscoverService.downVoteCost(
+ dapp.id,
+ )
+ // balanceDownBy, votesRequired, cost
+ rating = parseInt(downVoteEffect.vR, 10)
+ downVoteSntValue = downVoteEffect.c
+ } catch (e) {
+ return
+ }
+ }
+
+ const state = getState()
+ const voteState = state.vote
+ if (voteState.dapp !== dapp) return
+ if (voteState.isUpvote !== isUpvote) return
+ if (isUpvote) {
+ if (voteState.sntValue !== sntValue.toString()) return
+ if (sntValue === 0) return
+ dispatch(updateAfterUpVotingValuesAction(rating))
+ } else {
+ dispatch(updateAfterDownVotingValuesAction(rating, downVoteSntValue))
+ }
+ }
+}
+
+export const upVoteAction = (dapp, amount) => {
+ return async dispatch => {
+ dispatch(closeVoteAction())
+ dispatch(
+ onStartProgressAction(
+ dapp.name,
+ dapp.image,
+ `↑ Upvote by ${amount} SNT`,
+ TYPE_UPVOTE,
+ ),
+ )
+ try {
+ const blockchain = await BlockchainSDK.getInstance()
+ const tx = await blockchain.DiscoverService.upVote(dapp.id, amount)
+ dispatch(onReceiveTransactionInfoAction(dapp.id, tx))
+ dispatch(checkTransactionStatusAction(tx))
+ } catch (e) {
+ dispatch(showAlertAction(e.message))
+ }
+ }
+}
+
+export const downVoteAction = (dapp, amount) => {
+ return async dispatch => {
+ const msg = amount !== '0' ? ` by ${amount} SNT` : ''
+
+ dispatch(closeVoteAction())
+ dispatch(
+ onStartProgressAction(
+ dapp.name,
+ dapp.image,
+ `↓ Downvote${msg}`,
+ TYPE_DOWNVOTE,
+ ),
+ )
+ try {
+ const blockchain = await BlockchainSDK.getInstance()
+ const tx = await blockchain.DiscoverService.downVote(dapp.id)
+ dispatch(onReceiveTransactionInfoAction(dapp.id, tx))
+ dispatch(checkTransactionStatusAction(tx))
+ } catch (e) {
+ dispatch(showAlertAction(e.message))
+ }
+ }
+}
+
+const showUpVoteAfterCheck = (state, dapp) => {
+ return Object.assign({}, state, {
+ visible: true,
+ dapp,
+ sntValue: '0',
+ isUpvote: true,
+ afterVoteRating: null,
+ })
+}
+
+const showDownVoteAfterCheck = (state, dapp) => {
+ return Object.assign({}, state, {
+ visible: true,
+ dapp,
+ sntValue: '0',
+ isUpvote: false,
+ afterVoteRating: null,
+ })
+}
+
+const closeVote = state => {
+ return Object.assign({}, state, {
+ visible: false,
+ dapp: null,
+ })
+}
+
+const switchToUpvote = state => {
+ return Object.assign({}, state, {
+ isUpvote: true,
+ sntValue: '0',
+ afterVoteRating: null,
+ })
+}
+
+const switchToDownvote = state => {
+ return Object.assign({}, state, {
+ isUpvote: false,
+ sntValue: '0',
+ afterVoteRating: null,
+ })
+}
+
+const onInputSntValue = (state, sntValue) => {
+ return Object.assign({}, state, {
+ sntValue,
+ })
+}
+
+const updateAfterUpVotingValues = (state, rating) => {
+ return Object.assign({}, state, {
+ afterVoteRating: rating,
+ })
+}
+
+const updateAfterDownVotingValues = (state, payload) => {
+ const { rating, sntValue } = payload
+ return Object.assign({}, state, {
+ afterVoteRating: rating,
+ sntValue,
+ })
+}
+
+const map = {
+ [SHOW_UP_VOTE_AFTER_CHECK]: showUpVoteAfterCheck,
+ [SHOW_DOWN_VOTE_AFTER_CHEECK]: showDownVoteAfterCheck,
+ [CLOSE_VOTE]: closeVote,
+ [SWITCH_TO_UPVOTE]: switchToUpvote,
+ [SWITCH_TO_DOWNVOTE]: switchToDownvote,
+ [ON_INPUT_SNT_VALUE]: onInputSntValue,
+ [UPDATE_AFTER_UP_VOTING_VALUES]: updateAfterUpVotingValues,
+ [UPDATE_AFTER_DOWN_VOTING_VALUES]: updateAfterDownVotingValues,
+}
+
+export default reducerUtil(map, voteInitialState)
diff --git a/src/modules/Vote/index.js b/src/modules/Vote/index.js
new file mode 100644
index 0000000..d84ca16
--- /dev/null
+++ b/src/modules/Vote/index.js
@@ -0,0 +1,3 @@
+import Vote from './Vote.container'
+
+export default Vote
diff --git a/test/BancorFormula_spec.js b/test/BancorFormula_spec.js
new file mode 100644
index 0000000..23c74be
--- /dev/null
+++ b/test/BancorFormula_spec.js
@@ -0,0 +1,258 @@
+/*global contract, config, it, assert, embark, web3, before, describe, beforeEach*/
+const BigNumber = require('bignumber.js');
+const constants = require('../utils/FormulaConstants.js');
+const catchRevert = require("../utils/Utils").catchRevert;
+const TestBancorFormula = embark.require('Embark/contracts/TestBancorFormula');
+
+config({
+ deployment: {
+ accounts: [
+ {
+ mnemonic: "foster gesture flock merge beach plate dish view friend leave drink valley shield list enemy",
+ balance: "5 ether",
+ numAddresses: "10"
+ }
+ ]
+ },
+ contracts: {
+ "TestBancorFormula": { }
+ }
+ }, (_err, web3_accounts) => {
+ accounts = web3_accounts
+});
+
+contract('TestBancorFormula', function () {
+ let ILLEGAL_VAL = new BigNumber(2).exponentiatedBy(256);
+ let MAX_BASE_N = new BigNumber(2).exponentiatedBy(256 - constants.MAX_PRECISION).minus(1);
+ let MIN_BASE_D = new BigNumber(1);
+ let MAX_EXPONENT = 1000000;
+
+ for (let percent = 1; percent <= 100; percent++) {
+ let baseN = web3.utils.toHex(MAX_BASE_N);
+ let baseD = web3.utils.toHex(MAX_BASE_N.minus(1));
+ let expN = MAX_EXPONENT * percent / 100;
+ let expD = MAX_EXPONENT;
+ let test = `Function power(0x${baseN.toString(16)}, 0x${baseD.toString(16)}, ${expN}, ${expD})`;
+
+ it(`${test}:`, async () => {
+ await TestBancorFormula.methods.powerTest(baseN, baseD, expN, expD).call();
+ });
+ }
+
+ for (let percent = 1; percent <= 100; percent++) {
+ let baseN = web3.utils.toHex(MAX_BASE_N);
+ let baseD = web3.utils.toHex(MAX_BASE_N.minus(1));
+ let expN = MAX_EXPONENT;
+ let expD = MAX_EXPONENT * percent / 100;
+ let test = `Function power(0x${baseN.toString(16)}, 0x${baseD.toString(16)}, ${expN}, ${expD})`;
+
+ it(`${test}:`, async () => {
+ await TestBancorFormula.methods.powerTest(baseN, baseD, expN, expD).call();
+ });
+ }
+
+ for (let percent = 1; percent <= 100; percent++) {
+ let baseN = web3.utils.toHex(MAX_BASE_N);
+ let baseD = web3.utils.toHex((MIN_BASE_D));
+ let expN = MAX_EXPONENT * percent / 100;
+ let expD = MAX_EXPONENT;
+ let test = `Function power(0x${baseN.toString(16)}, 0x${baseD.toString(16)}, ${expN}, ${expD})`;
+
+ it(`${test}:`, async () => {
+ if (percent < 64)
+ await TestBancorFormula.methods.powerTest(baseN, baseD, expN, expD).call();
+ else
+ await catchRevert(TestBancorFormula.methods.powerTest(baseN, baseD, expN, expD).call());
+ });
+ }
+
+ for (let percent = 1; percent <= 100; percent++) {
+ let baseN = web3.utils.toHex(MAX_BASE_N);
+ let baseD = web3.utils.toHex(MIN_BASE_D);
+ let expN = MAX_EXPONENT;
+ let expD = MAX_EXPONENT * percent / 100;
+ let test = `Function power(0x${baseN.toString(16)}, 0x${baseD.toString(16)}, ${expN}, ${expD})`;
+
+ it(`${test}:`, async () => {
+ await catchRevert(TestBancorFormula.methods.powerTest(baseN, baseD, expN, expD).call());
+ });
+ }
+
+ let values = [
+ MAX_BASE_N.dividedBy(MIN_BASE_D).integerValue(),
+ MAX_BASE_N.dividedBy(MAX_BASE_N.minus(1)).integerValue(),
+ MIN_BASE_D.plus(1).dividedBy(MIN_BASE_D).integerValue(),
+ ];
+
+ for (let index = 0; index < values.length; index++) {
+ let test = `Function generalLog(0x${values[index].toString(16)})`;
+
+ it(`${test}:`, async () => {
+ try {
+ let temp = web3.utils.toHex(values[index]);
+ let retVal = await TestBancorFormula.methods.generalLogTest(temp).call();
+ let check = new BigNumber(parseInt(retVal, 10) * MAX_EXPONENT);
+ assert(check.isLessThan(ILLEGAL_VAL), `${test}: output is too large`);
+ }
+ catch (error) {
+ assert(false, error.message);
+ }
+ });
+ }
+
+ for (let precision = constants.MIN_PRECISION; precision <= constants.MAX_PRECISION; precision++) {
+ let maxExp = new BigNumber(constants.maxExpArray[precision]);
+ let shlVal = new BigNumber(2).exponentiatedBy(constants.MAX_PRECISION - precision);
+ let tuples = [
+ {'input' : maxExp.plus(0).times(shlVal).minus(1), 'output' : new BigNumber(precision-0)},
+ {'input' : maxExp.plus(0).times(shlVal).minus(0), 'output' : new BigNumber(precision-0)},
+ {'input' : maxExp.plus(1).times(shlVal).minus(1), 'output' : new BigNumber(precision-0)},
+ {'input' : maxExp.plus(1).times(shlVal).minus(0), 'output' : new BigNumber(precision-1)},
+ ];
+
+ for (let index = 0; index < tuples.length; index++) {
+ let input = web3.utils.toHex(tuples[index]['input' ]);
+ let output = tuples[index]['output'];
+ let test = `Function findPositionInMaxExpArray(0x${input.toString(16)})`;
+
+ it(`${test}:`, async () => {
+ if (precision == constants.MIN_PRECISION && output.isLessThan(BigNumber(precision))) {
+ await catchRevert(TestBancorFormula.methods.findPositionInMaxExpArrayTest(input).call());
+ }
+ else {
+ let temp = await TestBancorFormula.methods.findPositionInMaxExpArrayTest(input).call();
+ let retVal = new BigNumber(temp);
+ assert(retVal.isEqualTo(output), `${test}: output should be ${output.toString(10)} but it is ${retVal.toString(10)}`);
+ }
+ });
+ }
+ }
+
+ for (let precision = constants.MIN_PRECISION; precision <= constants.MAX_PRECISION; precision++) {
+ let maxExp = new BigNumber(constants.maxExpArray[precision]);
+ let maxVal = new BigNumber(constants.maxValArray[precision]);
+ let errExp = maxExp.plus(1);
+ let maxExpi = web3.utils.toHex(maxExp);
+ let errExpi = web3.utils.toHex(errExp);
+ let test1 = `Function generalExp(0x${maxExp.toString(16)}, ${precision})`;
+ let test2 = `Function generalExp(0x${errExp.toString(16)}, ${precision})`;
+
+ it(`${test1}:`, async () => {
+ let temp = await TestBancorFormula.methods.generalExpTest(maxExpi, precision).call();
+ let retVal = new BigNumber(temp);
+ assert(retVal.isEqualTo(maxVal), `${test1}: output is wrong`);
+ });
+
+ it(`${test2}:`, async () => {
+ let temp = await TestBancorFormula.methods.generalExpTest(errExpi, precision).call();
+ let retVal = new BigNumber(temp);
+ assert(retVal.isLessThan(maxVal), `${test2}: output indicates that maxExpArray[${precision}] is wrong`);
+ });
+ }
+
+ for (let precision = constants.MIN_PRECISION; precision <= constants.MAX_PRECISION; precision++) {
+ let minExp = new BigNumber(constants.maxExpArray[precision-1]).plus(1);
+ let minVal = new BigNumber(2).exponentiatedBy(precision);
+ let minExpi = web3.utils.toHex(minExp);
+ let test = `Function generalExp(0x${minExp.toString(16)}, ${precision})`;
+
+ it(`${test}:`, async () => {
+ let temp = await TestBancorFormula.methods.generalExpTest(minExpi, precision).call();
+ let retVal = new BigNumber(temp);
+ assert(retVal.isGreaterThanOrEqualTo(minVal), `${test}: output is too small`);
+ });
+ }
+
+ for (let n = 1; n <= 255; n++) {
+ let tuples = [
+ {'input' : new BigNumber(2).exponentiatedBy(n) , 'output' : new BigNumber(n)},
+ {'input' : new BigNumber(2).exponentiatedBy(n).plus(1) , 'output' : new BigNumber(n)},
+ {'input' : new BigNumber(2).exponentiatedBy(n+1).minus(1), 'output' : new BigNumber(n)},
+ ];
+
+ for (let index = 0; index < tuples.length; index++) {
+ let input = web3.utils.toHex(tuples[index]['input' ]);
+ let output = tuples[index]['output'];
+ let test = `Function floorLog2(0x${input.toString(16)})`;
+
+ it(`${test}:`, async () => {
+ let temp = await TestBancorFormula.methods.floorLog2Test(input).call();
+ let retVal = new BigNumber(temp);
+ assert(retVal.isEqualTo(output), `${test}: output should be ${output.toString(10)} but it is ${retVal.toString(10)}`);
+ });
+ }
+ }
+
+ let Decimal = require("decimal.js");
+ Decimal.set({precision: 100, rounding: Decimal.ROUND_DOWN});
+ BigNumber.config({DECIMAL_PLACES: 100, ROUNDING_MODE: BigNumber.ROUND_DOWN});
+
+ let LOG_MIN = 1;
+ let EXP_MIN = 0;
+ let LOG_MAX = new BigNumber(Decimal.exp(1).toFixed());
+ let EXP_MAX = new BigNumber(Decimal.pow(2,4).toFixed());
+ let FIXED_1 = new BigNumber(2).exponentiatedBy(constants.MAX_PRECISION);
+
+ for (let percent = 0; percent < 100; percent++) {
+ let x = new BigNumber(percent).dividedBy(100).times(LOG_MAX.minus(LOG_MIN)).plus(LOG_MIN);
+
+ it(`Function optimalLog(${x.toFixed()})`, async () => {
+ try {
+ let tmp = web3.utils.toHex(FIXED_1.times(x).integerValue());
+ let temp = await TestBancorFormula.methods.optimalLogTest(tmp).call();
+ let fixedPoint = new BigNumber(temp);
+ let floatPoint = new BigNumber(Decimal(x.toFixed()).ln().times(FIXED_1.toFixed()).toFixed());
+ let ratio = fixedPoint.isEqualTo(floatPoint) ? BigNumber(1) : fixedPoint.dividedBy(floatPoint);
+ assert(ratio.isGreaterThanOrEqualTo("0.99999999999999999999999999999999999") && ratio.isLessThanOrEqualTo("1"), `ratio = ${ratio.toFixed()}`);
+ }
+ catch (error) {
+ assert(false, error.message);
+ }
+ });
+ }
+
+ for (let percent = 0; percent < 100; percent++) {
+ let x = new BigNumber(percent).dividedBy(100).times(EXP_MAX.minus(EXP_MIN)).plus(EXP_MIN);
+
+ it(`Function optimalExp(${x.toFixed()})`, async () => {
+ try {
+ let tmp = web3.utils.toHex(FIXED_1.times(x).integerValue());
+ let temp = await TestBancorFormula.methods.optimalExpTest(tmp).call();
+ let fixedPoint = new BigNumber(temp);
+ let floatPoint = new BigNumber(Decimal(x.toFixed()).exp().times(FIXED_1.toFixed()).toFixed());
+ let ratio = fixedPoint.isEqualTo(floatPoint) ? new BigNumber(1) : fixedPoint.dividedBy(floatPoint);
+ assert(ratio.isGreaterThanOrEqualTo("0.99999999999999999999999999999999999") && ratio.isLessThanOrEqualTo("1"), `ratio = ${ratio.toFixed()}`);
+ }
+ catch (error) {
+ assert(false, error.message);
+ }
+ });
+ }
+
+ for (let n = 0; n < 256 - constants.MAX_PRECISION; n++) {
+ let values = [
+ new BigNumber(2).exponentiatedBy(n),
+ new BigNumber(2).exponentiatedBy(n).plus(1),
+ new BigNumber(2).exponentiatedBy(n).times(1.5),
+ new BigNumber(2).exponentiatedBy(n+1).minus(1),
+ ];
+
+ for (let index = 0; index < values.length; index++) {
+ let x = values[index];
+
+ it(`Function generalLog(${x.toFixed()})`, async () => {
+ try {
+ let tmp = web3.utils.toHex(FIXED_1.times(x).integerValue());
+ let temp = await TestBancorFormula.methods.generalLogTest(tmp).call();
+ let fixedPoint = new BigNumber(temp);
+ let floatPoint = new BigNumber(Decimal(x.toFixed()).ln().times(FIXED_1.toFixed()).toFixed());
+ let ratio = fixedPoint.isEqualTo(floatPoint) ? new BigNumber(1) : fixedPoint.dividedBy(floatPoint);
+ assert(ratio.isGreaterThanOrEqualTo("0.99999999999999999999999999999999999") && ratio.isLessThanOrEqualTo("1"), `ratio = ${ratio.toFixed()}`);
+ }
+ catch (error) {
+ assert(false, error.message);
+ }
+ });
+ }
+ }
+});
\ No newline at end of file
diff --git a/test/Discover_spec.js b/test/Discover_spec.js
new file mode 100644
index 0000000..6472059
--- /dev/null
+++ b/test/Discover_spec.js
@@ -0,0 +1,648 @@
+const TestUtils = require("../utils/testUtils");
+
+const Discover = require('Embark/contracts/Discover');
+const SNT = embark.require('Embark/contracts/SNT');
+
+
+config({
+ deployment: {
+ accounts: [
+ {
+ mnemonic: "foster gesture flock merge beach plate dish view friend leave drink valley shield list enemy",
+ balance: "5 ether",
+ numAddresses: "10"
+ }
+ ]
+ },
+ contracts: {
+ "MiniMeToken": { "deploy": false },
+ "MiniMeTokenFactory": {},
+ "SNT": {
+ "instanceOf": "MiniMeToken",
+ "args": [
+ "$MiniMeTokenFactory",
+ "0x0000000000000000000000000000000000000000",
+ 0,
+ "TestMiniMeToken",
+ 18,
+ "SNT",
+ true
+ ]
+ },
+ "Discover": {
+ args: ["$SNT"]
+ },
+ "TestBancorFormula": {}
+ }
+}, (_err, web3_accounts) => {
+ accounts = web3_accounts
+});
+
+contract("Discover", function () {
+
+ this.timeout(0);
+
+ it("should set max and safeMax values correctly", async function () {
+ let resultMax = await Discover.methods.max().call();
+ let resultSafeMax = await Discover.methods.safeMax().call();
+ let expectedMax = 6804870174 * 292 / 1000000;
+ let expectedSafeMax = expectedMax * 77 / 100 - 1;
+ assert.strictEqual(parseInt(resultMax, 10), Math.round(expectedMax));
+ assert.strictEqual(parseInt(resultSafeMax, 10), Math.round(expectedSafeMax));
+ });
+
+ it("should create a new DApp and initialise it correctly", async function () {
+ let id = "0x7465737400000000000000000000000000000000000000000000000000000000";
+ let amount = 1000;
+ let metadata = "QmSmv5e5DYc2otwWcpUzuqmt389s3HHx651TbxDvKBFFue";
+
+ await SNT.methods.generateTokens(accounts[0], amount).send();
+ const encodedCall = Discover.methods.createDApp(id, amount, TestUtils.getBytes32FromIpfsHash(metadata)).encodeABI();
+ await SNT.methods.approveAndCall(Discover.options.address, amount, encodedCall).send({ from: accounts[0] });
+
+ let receipt = await Discover.methods.dapps(0).call();
+ let developer = accounts[0];
+
+ assert.strictEqual(developer, receipt.developer);
+ assert.strictEqual(id, receipt.id);
+ assert.strictEqual(metadata, TestUtils.getIpfsHashFromBytes32(receipt.metadata));
+
+ // Check Discover actually receives the SNT!
+ let bal_receipt = await SNT.methods.balanceOf(Discover.options.address).call();
+ assert.strictEqual(amount, parseInt(bal_receipt, 10));
+
+ // Having received the SNT, check that it updates the particular DApp's storage values
+ assert.strictEqual(amount, parseInt(receipt.balance, 10));
+
+ let max = await Discover.methods.max().call();
+ let decimals = await Discover.methods.decimals().call();
+ let rate = Math.round(decimals - (amount * decimals / max));
+ assert.strictEqual(rate, parseInt(receipt.rate, 10));
+
+ let available = amount * rate;
+ assert.strictEqual(available, parseInt(receipt.available, 10));
+
+ let votes_minted = Math.floor((available / decimals) ** (decimals / rate));
+ assert.strictEqual(votes_minted, parseInt(receipt.votesMinted, 10));
+
+ assert.strictEqual(0, parseInt(receipt.votesCast, 10));
+ assert.strictEqual(amount, parseInt(receipt.effectiveBalance, 10));
+ })
+
+ it("should not create a new DApp with the same ID", async function () {
+ let id = "0x7465737400000000000000000000000000000000000000000000000000000000";
+ let amount = 1000;
+ let metadata = 'QmSmv5e5DYc2otwWcpUzuqmt389s3HHx651TbxDvKBFFue';
+
+ await SNT.methods.generateTokens(accounts[0], amount).send();
+ const encodedCall = Discover.methods.createDApp(id, amount, TestUtils.getBytes32FromIpfsHash(metadata)).encodeABI();
+
+ try {
+ await SNT.methods.approveAndCall(Discover.options.address, amount, encodedCall).send({ from: accounts[0] });
+ assert.fail('should have reverted before');
+ } catch (error) {
+ TestUtils.assertJump(error);
+ }
+ })
+
+ it("should not create a new DApp when exceeding the ceiling or staking nothing", async function () {
+ let id = "0x7465737400000000000000000000000000000000000000000000000000000000";
+ let metadata = 'QmSmv5e5DYc2otwWcpUzuqmt389s3HHx651TbxDvKBFFue';
+ let initial = await Discover.methods.max().call();
+ let amount = parseInt(initial, 10);
+ let amount0 = 0;
+
+ await SNT.methods.generateTokens(accounts[0], amount).send();
+
+ const encodedCall = Discover.methods.createDApp(id, amount, TestUtils.getBytes32FromIpfsHash(metadata)).encodeABI();
+ try {
+ await SNT.methods.approveAndCall(Discover.options.address, amount, encodedCall).send({ from: accounts[0] });
+ assert.fail('should have reverted before');
+ } catch (error) {
+ TestUtils.assertJump(error);
+ }
+
+ const encodedCall0 = Discover.methods.createDApp(id, amount0, TestUtils.getBytes32FromIpfsHash(metadata)).encodeABI();
+ try {
+ await SNT.methods.approveAndCall(Discover.options.address, amount0, encodedCall0).send({ from: accounts[0] });
+ assert.fail('should have reverted before');
+ } catch (error) {
+ TestUtils.assertJump(error);
+ }
+ })
+
+ it("should update the metadata correctly", async function () {
+ let id = "0x7465737400000000000000000000000000000000000000000000000000000000";
+ let metadata = "QmSmv5e5DYc2otwWcpUzuqmt389s3HHx651TbxDvKBFFeu";
+ await Discover.methods.setMetadata(id, TestUtils.getBytes32FromIpfsHash(metadata)).send({ from: accounts[0] });
+ let receipt = await Discover.methods.dapps(0).call();
+ assert.strictEqual(TestUtils.getBytes32FromIpfsHash(metadata), receipt.metadata);
+ })
+
+ it("should not let anyone other than the developer update the metadata", async function () {
+ let id = "0x7465737400000000000000000000000000000000000000000000000000000000";
+ let metadata_actual = "QmSmv5e5DYc2otwWcpUzuqmt389s3HHx651TbxDvKBFFeu";
+ let metadata = "QmSmv5e5DYc2otwWcpUzuqmt389s3HHx651TbxDvKBDDeu";
+ try {
+ await Discover.methods.setMetadata(id, TestUtils.getBytes32FromIpfsHash(metadata)).send({ from: accounts[1] });
+ assert.fail('should have reverted before');
+ } catch (error) {
+ TestUtils.assertJump(error);
+ }
+ let receipt = await Discover.methods.dapps(0).call();
+ assert.strictEqual(TestUtils.getBytes32FromIpfsHash(metadata_actual), receipt.metadata);
+ })
+
+ it("should handle first upvote correctly", async function () {
+ let id = "0x7465737400000000000000000000000000000000000000000000000000000000";
+ let amount = 100;
+
+ let initial = await Discover.methods.dapps(0).call();
+ let before = await SNT.methods.balanceOf(Discover.options.address).call();
+ // This is the special case where no downvotes have yet been cast
+ let up_effect = await Discover.methods.upvoteEffect(id, amount).call();
+
+ await SNT.methods.generateTokens(accounts[0], amount).send();
+ const encodedCall = Discover.methods.upvote(id, amount).encodeABI();
+ await SNT.methods.approveAndCall(Discover.options.address, amount, encodedCall).send({ from: accounts[0] });
+
+ let receipt = await Discover.methods.dapps(0).call();
+
+ let developer = accounts[0];
+
+ assert.strictEqual(developer, receipt.developer);
+ assert.strictEqual(id, receipt.id);
+
+ // Check Discover actually receives the SNT!
+ let after = await SNT.methods.balanceOf(Discover.options.address).call();
+ let bal_effect = parseInt(after, 10) - parseInt(before, 10);
+ assert.strictEqual(bal_effect, amount);
+
+ // Having received the SNT, check that it updates the particular DApp's storage values
+ let upvotedBalance = parseInt(initial.balance, 10) + amount
+ assert.strictEqual(upvotedBalance, parseInt(receipt.balance, 10));
+
+ let max = await Discover.methods.max().call();
+ let decimals = await Discover.methods.decimals().call();
+ let rate = Math.ceil(decimals - (upvotedBalance * decimals / max));
+ assert.strictEqual(rate, parseInt(receipt.rate, 10));
+
+ let available = upvotedBalance * rate;
+ assert.strictEqual(available, parseInt(receipt.available, 10));
+
+ let votes_minted = Math.floor((available / decimals) ** (decimals / rate));
+ assert.strictEqual(votes_minted, parseInt(receipt.votesMinted, 10));
+
+ // Only true for upvotes made when there are no downvotes
+ assert.strictEqual(0, parseInt(receipt.votesCast, 10));
+
+ // The effective_balance should match upvoteEffect + initial.effectiveBalance
+ let e_balance = parseInt(up_effect, 10) + parseInt(initial.effectiveBalance, 10);
+ assert.strictEqual(e_balance, parseInt(receipt.effectiveBalance, 10));
+ })
+
+ it("should not let you upvote without spending SNT", async function () {
+ let id = "0x7465737400000000000000000000000000000000000000000000000000000000";
+ let amount = 0;
+
+ await SNT.methods.generateTokens(accounts[0], 10000).send();
+ const encodedCall = Discover.methods.upvote(id, amount).encodeABI();
+ try {
+ await SNT.methods.approveAndCall(Discover.options.address, amount, encodedCall).send({ from: accounts[0] });
+ assert.fail('should have reverted before');
+ } catch (error) {
+ TestUtils.assertJump(error);
+ }
+ })
+
+ it("should not let you upvote by an amount that exceeds the ceiling", async function () {
+ let id = "0x7465737400000000000000000000000000000000000000000000000000000000";
+ let initial = await Discover.methods.max().call();
+ let amount = parseInt(initial, 10);
+
+ await SNT.methods.generateTokens(accounts[0], amount).send();
+ const encodedCall = Discover.methods.upvote(id, amount).encodeABI();
+ try {
+ await SNT.methods.approveAndCall(Discover.options.address, amount, encodedCall).send({ from: accounts[0] });
+ assert.fail('should have reverted before');
+ } catch (error) {
+ TestUtils.assertJump(error);
+ }
+ })
+
+ it("should handle first downvote correctly", async function () {
+ let id = "0x7465737400000000000000000000000000000000000000000000000000000000";
+ let cost = await Discover.methods.downvoteCost(id).call()
+ let amount = parseInt(cost.c, 10);
+
+ let developer = accounts[0];
+ let initial = await Discover.methods.dapps(0).call();
+ let bal_before = await SNT.methods.balanceOf(developer).call();
+
+ await SNT.methods.generateTokens(accounts[1], amount).send();
+ const encodedCall = Discover.methods.downvote(id, amount).encodeABI();
+ await SNT.methods.approveAndCall(Discover.options.address, amount, encodedCall).send({ from: accounts[1] });
+
+ let receipt = await Discover.methods.dapps(0).call();
+
+ assert.strictEqual(developer, receipt.developer);
+ assert.strictEqual(id, receipt.id);
+
+ // Check the developer actually receives the SNT!
+ let bal_after = await SNT.methods.balanceOf(developer).call();
+ let bal_effect = parseInt(bal_after, 10) - parseInt(bal_before, 10)
+ assert.strictEqual(bal_effect, amount);
+
+ // Balance, rate, and votes_minted remain unchanged for downvotes
+ assert.strictEqual(initial.balance, receipt.balance);
+ assert.strictEqual(initial.rate, receipt.rate);
+ assert.strictEqual(initial.votesMinted, receipt.votesMinted);
+
+ let available = parseInt(initial.available, 10) - parseInt(cost.c, 10);
+ assert.strictEqual(available, parseInt(receipt.available, 10));
+
+ // This is only true for the first downvote
+ assert.strictEqual(parseInt(receipt.votesCast, 10), parseInt(cost.vR, 10));
+
+ let e_balance = parseInt(initial.effectiveBalance, 10) - parseInt(cost.b, 10);
+ assert.strictEqual(e_balance, parseInt(receipt.effectiveBalance, 10));
+ })
+
+ it("should handle second downvote correctly", async function () {
+ let id = "0x7465737400000000000000000000000000000000000000000000000000000000";
+ let cost = await Discover.methods.downvoteCost(id).call()
+ let amount = parseInt(cost.c, 10);
+ let developer = accounts[0];
+
+ let initial = await Discover.methods.dapps(0).call();
+ let bal_before = await SNT.methods.balanceOf(developer).call();
+
+ await SNT.methods.generateTokens(accounts[1], amount).send();
+ const encodedCall = Discover.methods.downvote(id, amount).encodeABI();
+ await SNT.methods.approveAndCall(Discover.options.address, amount, encodedCall).send({ from: accounts[1] });
+
+ let receipt = await Discover.methods.dapps(0).call();
+
+ assert.strictEqual(developer, receipt.developer);
+ assert.strictEqual(id, receipt.id);
+
+ // Check the developer actually receives the SNT!
+ let bal_after = await SNT.methods.balanceOf(developer).call();
+ let bal_effect = parseInt(bal_after, 10) - parseInt(bal_before, 10)
+ assert.strictEqual(bal_effect, amount);
+
+ // Balance, rate, and votes_minted remain unchanged for downvotes
+ assert.strictEqual(initial.balance, receipt.balance);
+ assert.strictEqual(initial.rate, receipt.rate);
+ assert.strictEqual(initial.votesMinted, receipt.votesMinted);
+
+ let available = parseInt(initial.available, 10) - parseInt(cost.c, 10);
+ assert.strictEqual(available, parseInt(receipt.available, 10));
+
+ let eff_v_cast = parseInt(receipt.votesCast, 10) - parseInt(initial.votesCast, 10);
+ assert.strictEqual(eff_v_cast, parseInt(cost.vR, 10));
+
+ let e_balance = parseInt(initial.effectiveBalance, 10) - parseInt(cost.b, 10);
+ assert.strictEqual(e_balance, parseInt(receipt.effectiveBalance, 10));
+ })
+
+ it("should not let you downvote by the wrong amount", async function () {
+ let id = "0x7465737400000000000000000000000000000000000000000000000000000000";
+ let amount_above = 10000;
+ let amount_below = 100;
+
+ await SNT.methods.generateTokens(accounts[1], amount_above + amount_below).send();
+
+ const encodedCall = Discover.methods.downvote(id, amount_above).encodeABI();
+ try {
+ await SNT.methods.approveAndCall(Discover.options.address, amount_above, encodedCall).send({ from: accounts[1] });
+ assert.fail('should have reverted before');
+ } catch (error) {
+ TestUtils.assertJump(error);
+ }
+
+ const encodedCall1 = Discover.methods.downvote(id, amount_below).encodeABI();
+ try {
+ await SNT.methods.approveAndCall(Discover.options.address, amount_below, encodedCall1).send({ from: accounts[1] });
+ assert.fail('should have reverted before');
+ } catch (error) {
+ TestUtils.assertJump(error);
+ }
+ })
+
+ it("should handle upvotes correctly when votes have been cast", async function () {
+ let id = "0x7465737400000000000000000000000000000000000000000000000000000000";
+ let amount = 500;
+
+ let initial = await Discover.methods.dapps(0).call();
+ let before = await SNT.methods.balanceOf(Discover.options.address).call();
+ let up_effect = await Discover.methods.upvoteEffect(id, amount).call();
+
+ await SNT.methods.generateTokens(accounts[0], amount).send();
+ const encodedCall = Discover.methods.upvote(id, amount).encodeABI();
+ await SNT.methods.approveAndCall(Discover.options.address, amount, encodedCall).send({ from: accounts[0] });
+
+ let receipt = await Discover.methods.dapps(0).call();
+ let developer = accounts[0];
+
+ assert.strictEqual(developer, receipt.developer);
+ assert.strictEqual(id, receipt.id);
+
+ // Check Discover actually receives the SNT!
+ let after = await SNT.methods.balanceOf(Discover.options.address).call();
+ let bal_effect = parseInt(after, 10) - parseInt(before, 10);
+ assert.strictEqual(bal_effect, amount);
+
+ // Having received the SNT, check that it updates the particular DApp's storage values
+ let upvotedBalance = parseInt(initial.balance, 10) + amount
+ assert.strictEqual(upvotedBalance, parseInt(receipt.balance, 10));
+
+ let max = await Discover.methods.max().call();
+ let decimals = await Discover.methods.decimals().call();
+ let rate = Math.round(decimals - (upvotedBalance * decimals / max));
+ assert.strictEqual(rate, parseInt(receipt.rate, 10));
+
+ let available = upvotedBalance * rate;
+ assert.strictEqual(available, parseInt(receipt.available, 10));
+
+ let votes_minted = parseInt(receipt.votesMinted, 10);
+
+ // Votes have been cast by this stage, so we need to check how many there are
+ // and confirm that `upvote` still calculates the effective_balance correctly
+ let votes_cast = parseInt(receipt.votesCast, 10);
+
+ let e_balance = Math.ceil(upvotedBalance - ((votes_cast * rate / decimals) * (available / decimals / votes_minted)));
+ assert.strictEqual(e_balance, parseInt(receipt.effectiveBalance, 10));
+
+ // The effective_balance should also match upvoteEffect + initial.effectiveBalance
+ let e_balance_2 = parseInt(up_effect, 10) + parseInt(initial.effectiveBalance, 10);
+ assert.strictEqual(e_balance_2, parseInt(receipt.effectiveBalance, 10));
+ })
+
+ it("should return correct upvoteEffect for use in UI", async function () {
+ let id = "0x7465737400000000000000000000000000000000000000000000000000000000";
+ let amount = 10;
+
+ let receipt = await Discover.methods.dapps(0).call();
+ let effect = await Discover.methods.upvoteEffect(id, amount).call();
+
+ // Mock receiving the SNT
+ let mBalance = parseInt(receipt.balance, 10) + amount
+
+ let max = await Discover.methods.max().call();
+ let decimals = await Discover.methods.decimals().call();
+
+ let mRate = Math.round(decimals - (mBalance * decimals / max));
+ let mAvailable = mBalance * mRate;
+ let mVMinted = Math.round((mAvailable / decimals) ** (decimals / mRate));
+
+ // Votes have been cast by this stage, so we need to check how many there are
+ // and confirm that `upvoteEffect` mocks the effect correctly
+ let votes_cast = parseInt(receipt.votesCast, 10);
+ let mEBalance = Math.ceil(mBalance - ((votes_cast * mRate / decimals) * (mAvailable / decimals / mVMinted)));
+ let effect_calc = mEBalance - receipt.effectiveBalance;
+
+ // Confirm that what is returned is (mEBalance - d.effective_balance)
+ assert.strictEqual(effect_calc, parseInt(effect, 10));
+ })
+
+ it("should throw already in upvoteEffect if you exceed the ceiling", async function () {
+ let id = "0x7465737400000000000000000000000000000000000000000000000000000000";
+ let initial = await Discover.methods.max().call();
+ let amount = parseInt(initial, 10);
+ try {
+ await Discover.methods.upvoteEffect(id, amount).call();
+ assert.fail('should have reverted before');
+ } catch (error) {
+ TestUtils.assertJump(error);
+ }
+ })
+
+ it("should handle withdrawals correctly", async function () {
+ let id = "0x7465737400000000000000000000000000000000000000000000000000000000";
+ let amount = 100;
+
+ let initial = await Discover.methods.dapps(0).call();
+ let before = await SNT.methods.balanceOf(Discover.options.address).call();
+ let before_dev = await SNT.methods.balanceOf(accounts[0]).call();
+ let receipt_obj = await Discover.methods.withdraw(id, amount).send({ from: accounts[0] });
+ let receipt = receipt_obj.events.Withdraw.returnValues;
+
+ assert.strictEqual(id, receipt.id);
+
+ // Check Discover actually sends SNT to the developer
+ let after = await SNT.methods.balanceOf(Discover.options.address).call();
+ let after_dev = await SNT.methods.balanceOf(accounts[0]).call();
+ let difference = parseInt(before, 10) - parseInt(after, 10);
+ let difference_dev = parseInt(after_dev, 10) - parseInt(before_dev, 10);
+
+ assert.strictEqual(difference, amount)
+ assert.strictEqual(difference_dev, amount)
+
+ // Recalculate e_balance manually and check it matches what is returned
+ let max = await Discover.methods.max().call();
+ let decimals = await Discover.methods.decimals().call();
+
+ let balance = parseInt(initial.balance, 10) - amount
+ let rate = Math.ceil(decimals - (balance * decimals / max));
+ let available = Math.round(balance * rate);
+ let v_minted = Math.round((available / decimals) ** (decimals / rate));
+ let v_cast = parseInt(initial.votesCast, 10);
+ let e_balance = Math.ceil(balance - ((v_cast * rate / decimals) * (available / decimals / v_minted)));
+
+ let effective_balance = parseInt(receipt.newEffectiveBalance, 10);
+
+ // We begin to run into precision limitations in the BancorFormula here.
+ // The higher the amount, the less accurate it becomes. Hence Math.ceil() for now.
+ assert.strictEqual(e_balance, effective_balance);
+
+ // Having withdrawn the SNT, check that it updates the particular DApp's storage values properly
+ let check = await Discover.methods.dapps(0).call();
+
+ let withdrawnBalance = parseInt(initial.balance, 10) - amount;
+ assert.strictEqual(parseInt(check.balance, 10), withdrawnBalance);
+
+ assert.strictEqual(parseInt(check.rate, 10), rate);
+ assert.strictEqual(parseInt(check.available, 10), available);
+ assert.strictEqual(parseInt(check.votesMinted, 10), v_minted);
+ })
+
+ it("should not allow withdrawing more than was staked", async function () {
+ let id = "0x7465737400000000000000000000000000000000000000000000000000000000";
+ let amount = 150000;
+ try {
+ await Discover.methods.withdraw(id, amount).send({ from: accounts[0] });
+ assert.fail('should have reverted before');
+ } catch (error) {
+ TestUtils.assertJump(error);
+ }
+ })
+
+ it("should not allow withdrawing more than was staked minus what has already been received", async function () {
+ let id = "0x7465737400000000000000000000000000000000000000000000000000000000";
+ let receipt = await Discover.methods.dapps(0).call();
+ let amount = parseInt(receipt.available, 10) + 1;
+ try {
+ await Discover.methods.withdraw(id, amount).send({ from: accounts[0] });
+ assert.fail('should have reverted before');
+ } catch (error) {
+ TestUtils.assertJump(error);
+ }
+ })
+
+ it("should not allow anyone other than the developer to withdraw", async function () {
+ let id = "0x7465737400000000000000000000000000000000000000000000000000000000";
+ let amount = 1000;
+ try {
+ await Discover.methods.withdraw(id, amount).send({ from: accounts[1] });
+ } catch (error) {
+ TestUtils.assertJump(error);
+ }
+ })
+
+ it("should handle downvotes after withdrawals correctly", async function () {
+ let id = "0x7465737400000000000000000000000000000000000000000000000000000000";
+ let cost = await Discover.methods.downvoteCost(id).call()
+ let amount = parseInt(cost.c, 10);
+ let developer = accounts[0];
+
+ let initial = await Discover.methods.dapps(0).call();
+ let bal_before = await SNT.methods.balanceOf(developer).call();
+
+ await SNT.methods.generateTokens(accounts[1], amount).send();
+ const encodedCall = Discover.methods.downvote(id, amount).encodeABI();
+ await SNT.methods.approveAndCall(Discover.options.address, amount, encodedCall).send({ from: accounts[1] });
+
+ let receipt = await Discover.methods.dapps(0).call();
+
+ assert.strictEqual(developer, receipt.developer);
+ assert.strictEqual(id, receipt.id);
+
+ // Check the developer actually receives the SNT!
+ let bal_after = await SNT.methods.balanceOf(developer).call();
+ let bal_effect = parseInt(bal_after, 10) - parseInt(bal_before, 10)
+ assert.strictEqual(bal_effect, amount);
+
+ // Balance, rate, and votes_minted remain unchanged for downvotes
+ assert.strictEqual(initial.balance, receipt.balance);
+ assert.strictEqual(initial.rate, receipt.rate);
+ assert.strictEqual(initial.votesMinted, receipt.votesMinted);
+
+ let available = parseInt(initial.available, 10) - parseInt(cost.c, 10);
+ assert.strictEqual(available, parseInt(receipt.available, 10));
+
+ let eff_v_cast = parseInt(receipt.votesCast, 10) - parseInt(initial.votesCast, 10);
+ assert.strictEqual(eff_v_cast, parseInt(cost.vR, 10));
+
+ let e_balance = parseInt(initial.effectiveBalance, 10) - parseInt(cost.b, 10);
+ assert.strictEqual(e_balance, parseInt(receipt.effectiveBalance, 10));
+ })
+
+ it("should handle upvotes after withdrawals correctly", async function () {
+ let id = "0x7465737400000000000000000000000000000000000000000000000000000000";
+ let amount = 100;
+
+ let initial = await Discover.methods.dapps(0).call();
+ let before = await SNT.methods.balanceOf(Discover.options.address).call();
+ let up_effect = await Discover.methods.upvoteEffect(id, amount).call();
+
+ await SNT.methods.generateTokens(accounts[0], amount).send();
+ const encodedCall = Discover.methods.upvote(id, amount).encodeABI();
+ await SNT.methods.approveAndCall(Discover.options.address, amount, encodedCall).send({ from: accounts[0] });
+
+ let receipt = await Discover.methods.dapps(0).call();
+ let developer = accounts[0];
+
+ assert.strictEqual(developer, receipt.developer);
+ assert.strictEqual(id, receipt.id);
+
+ // Check Discover actually receives the SNT!
+ let after = await SNT.methods.balanceOf(Discover.options.address).call();
+ let bal_effect = parseInt(after, 10) - parseInt(before, 10);
+ assert.strictEqual(bal_effect, amount);
+
+ // Having received the SNT, check that it updates the particular DApp's storage values
+ let upvotedBalance = parseInt(initial.balance, 10) + amount
+ assert.strictEqual(upvotedBalance, parseInt(receipt.balance, 10));
+
+ let max = await Discover.methods.max().call();
+ let decimals = await Discover.methods.decimals().call();
+ let rate = Math.round(decimals - (upvotedBalance * decimals / max));
+ assert.strictEqual(rate, parseInt(receipt.rate, 10));
+
+ let available = upvotedBalance * rate;
+ assert.strictEqual(available, parseInt(receipt.available, 10));
+
+ let votes_minted = parseInt(receipt.votesMinted, 10);
+
+ // Votes have been cast by this stage, so we need to check how many there are
+ // and confirm that `upvote` still calculates the effective_balance correctly
+ let votes_cast = parseInt(receipt.votesCast, 10);
+
+ let e_balance = Math.ceil(upvotedBalance - ((votes_cast * rate / decimals) * (available / decimals / votes_minted)));
+ assert.strictEqual(e_balance, parseInt(receipt.effectiveBalance, 10));
+
+ // The effective_balance should also match upvoteEffect + initial.effectiveBalance
+ let e_balance_2 = parseInt(up_effect, 10) + parseInt(initial.effectiveBalance, 10);
+ assert.strictEqual(e_balance_2, parseInt(receipt.effectiveBalance, 10));
+ })
+
+ it("should create a DApp without overflowing", async function () {
+ let id = "0x0000000000000000000000000000000000000000000000000000000000000001";
+ let temp = await Discover.methods.safeMax().call();
+ let amount = parseInt(temp, 10);
+ let metadata = "QmSmv5e5DYc2otwWcpUzuqmt389s3HHx651TbxDvKBFFue";
+
+ await SNT.methods.generateTokens(accounts[0], amount).send();
+ const encodedCall = Discover.methods.createDApp(id, amount, TestUtils.getBytes32FromIpfsHash(metadata)).encodeABI();
+ await SNT.methods.approveAndCall(Discover.options.address, amount, encodedCall).send({ from: accounts[0] });
+
+ let receipt = await Discover.methods.dapps(1).call();
+ let developer = accounts[0];
+
+ assert.strictEqual(developer, receipt.developer);
+ assert.strictEqual(id, receipt.id);
+
+ // Having received the SNT, check that it updates the particular DApp's storage values
+ assert.strictEqual(amount, parseInt(receipt.balance, 10));
+
+ let max = await Discover.methods.max().call();
+ let decimals = await Discover.methods.decimals().call();
+ let rate = Math.ceil(decimals - (amount * decimals / max));
+ assert.strictEqual(rate, parseInt(receipt.rate, 10));
+
+ let available = amount * rate;
+ assert.strictEqual(available, parseInt(receipt.available, 10));
+
+ // It's checking that votesMinted doesn't overflow which we're really interested in here
+ let votes_minted = Math.round((available / decimals) ** (decimals / rate));
+ let returned = parseInt(receipt.votesMinted, 10);
+
+ // Is going to be less than due to rounding inaccuracies at higher exponents
+ assert.ok(returned <= votes_minted);
+ })
+
+ // Comment out line 263 in the contract to run this test properly and see
+ // the BancorFormula fail to find a suitable position in the maxExponentArray
+ it("should prove we have the highest safeMax allowed for by Bancor's power approximation", async function () {
+ let id = "0x0000000000000000000000000000000000000000000000000000000000000002";
+ let max = await Discover.methods.max().call();
+ // Choose a safeMax 1% higher than is currently set
+ let percent = 78 / 100;
+ let amount = Math.round(parseInt(max, 10) * percent);
+ let metadata = "QmSmv5e5DYc2otwWcpUzuqmt389s3HHx651TbxDvKBFFue";
+
+ await SNT.methods.generateTokens(accounts[0], amount).send();
+ const encodedCall = Discover.methods.createDApp(id, amount, TestUtils.getBytes32FromIpfsHash(metadata)).encodeABI();
+
+ // Comment this try/catch block out to test the safeMax
+ try {
+ await SNT.methods.approveAndCall(Discover.options.address, amount, encodedCall).send({ from: accounts[0] });
+ } catch (error) {
+ TestUtils.assertJump(error);
+ }
+
+ // Uncomment the next line to check this test. Change percent to 77 or 76 to see it pass.
+ // await SNT.methods.approveAndCall(Discover.options.address, amount, encodedCall).send({ from: accounts[0] });
+ })
+});
diff --git a/utils/FormulaConstants.js b/utils/FormulaConstants.js
new file mode 100644
index 0000000..32108b3
--- /dev/null
+++ b/utils/FormulaConstants.js
@@ -0,0 +1,262 @@
+module.exports.MIN_PRECISION = 32;
+module.exports.MAX_PRECISION = 127;
+module.exports.maxExpArray = [
+ /* 0 */ '0xd7',
+ /* 1 */ '0x19f',
+ /* 2 */ '0x31b',
+ /* 3 */ '0x5f6',
+ /* 4 */ '0xb6e',
+ /* 5 */ '0x15ec',
+ /* 6 */ '0x2a0c',
+ /* 7 */ '0x50a2',
+ /* 8 */ '0x9aa2',
+ /* 9 */ '0x1288c',
+ /* 10 */ '0x238b2',
+ /* 11 */ '0x4429a',
+ /* 12 */ '0x82b78',
+ /* 13 */ '0xfaadc',
+ /* 14 */ '0x1e0bb8',
+ /* 15 */ '0x399e96',
+ /* 16 */ '0x6e7f88',
+ /* 17 */ '0xd3e7a3',
+ /* 18 */ '0x1965fea',
+ /* 19 */ '0x30b5057',
+ /* 20 */ '0x5d681f3',
+ /* 21 */ '0xb320d03',
+ /* 22 */ '0x15784a40',
+ /* 23 */ '0x292c5bdd',
+ /* 24 */ '0x4ef57b9b',
+ /* 25 */ '0x976bd995',
+ /* 26 */ '0x122624e32',
+ /* 27 */ '0x22ce03cd5',
+ /* 28 */ '0x42beef808',
+ /* 29 */ '0x7ffffffff',
+ /* 30 */ '0xf577eded5',
+ /* 31 */ '0x1d6bd8b2eb',
+ /* 32 */ '0x386bfdba29',
+ /* 33 */ '0x6c3390ecc8',
+ /* 34 */ '0xcf8014760f',
+ /* 35 */ '0x18ded91f0e7',
+ /* 36 */ '0x2fb1d8fe082',
+ /* 37 */ '0x5b771955b36',
+ /* 38 */ '0xaf67a93bb50',
+ /* 39 */ '0x15060c256cb2',
+ /* 40 */ '0x285145f31ae5',
+ /* 41 */ '0x4d5156639708',
+ /* 42 */ '0x944620b0e70e',
+ /* 43 */ '0x11c592761c666',
+ /* 44 */ '0x2214d10d014ea',
+ /* 45 */ '0x415bc6d6fb7dd',
+ /* 46 */ '0x7d56e76777fc5',
+ /* 47 */ '0xf05dc6b27edad',
+ /* 48 */ '0x1ccf4b44bb4820',
+ /* 49 */ '0x373fc456c53bb7',
+ /* 50 */ '0x69f3d1c921891c',
+ /* 51 */ '0xcb2ff529eb71e4',
+ /* 52 */ '0x185a82b87b72e95',
+ /* 53 */ '0x2eb40f9f620fda6',
+ /* 54 */ '0x5990681d961a1ea',
+ /* 55 */ '0xabc25204e02828d',
+ /* 56 */ '0x14962dee9dc97640',
+ /* 57 */ '0x277abdcdab07d5a7',
+ /* 58 */ '0x4bb5ecca963d54ab',
+ /* 59 */ '0x9131271922eaa606',
+ /* 60 */ '0x116701e6ab0cd188d',
+ /* 61 */ '0x215f77c045fbe8856',
+ /* 62 */ '0x3ffffffffffffffff',
+ /* 63 */ '0x7abbf6f6abb9d087f',
+ /* 64 */ '0xeb5ec597592befbf4',
+ /* 65 */ '0x1c35fedd14b861eb04',
+ /* 66 */ '0x3619c87664579bc94a',
+ /* 67 */ '0x67c00a3b07ffc01fd6',
+ /* 68 */ '0xc6f6c8f8739773a7a4',
+ /* 69 */ '0x17d8ec7f04136f4e561',
+ /* 70 */ '0x2dbb8caad9b7097b91a',
+ /* 71 */ '0x57b3d49dda84556d6f6',
+ /* 72 */ '0xa830612b6591d9d9e61',
+ /* 73 */ '0x1428a2f98d728ae223dd',
+ /* 74 */ '0x26a8ab31cb8464ed99e1',
+ /* 75 */ '0x4a23105873875bd52dfd',
+ /* 76 */ '0x8e2c93b0e33355320ead',
+ /* 77 */ '0x110a688680a7530515f3e',
+ /* 78 */ '0x20ade36b7dbeeb8d79659',
+ /* 79 */ '0x3eab73b3bbfe282243ce1',
+ /* 80 */ '0x782ee3593f6d69831c453',
+ /* 81 */ '0xe67a5a25da41063de1495',
+ /* 82 */ '0x1b9fe22b629ddbbcdf8754',
+ /* 83 */ '0x34f9e8e490c48e67e6ab8b',
+ /* 84 */ '0x6597fa94f5b8f20ac16666',
+ /* 85 */ '0xc2d415c3db974ab32a5184',
+ /* 86 */ '0x175a07cfb107ed35ab61430',
+ /* 87 */ '0x2cc8340ecb0d0f520a6af58',
+ /* 88 */ '0x55e129027014146b9e37405',
+ /* 89 */ '0xa4b16f74ee4bb2040a1ec6c',
+ /* 90 */ '0x13bd5ee6d583ead3bd636b5c',
+ /* 91 */ '0x25daf6654b1eaa55fd64df5e',
+ /* 92 */ '0x4898938c9175530325b9d116',
+ /* 93 */ '0x8b380f3558668c46c91c49a2',
+ /* 94 */ '0x10afbbe022fdf442b2a522507',
+ /* 95 */ '0x1ffffffffffffffffffffffff',
+ /* 96 */ '0x3d5dfb7b55dce843f89a7dbcb',
+ /* 97 */ '0x75af62cbac95f7dfa3295ec26',
+ /* 98 */ '0xe1aff6e8a5c30f58221fbf899',
+ /* 99 */ '0x1b0ce43b322bcde4a56e8ada5a',
+ /* 100 */ '0x33e0051d83ffe00feb432b473b',
+ /* 101 */ '0x637b647c39cbb9d3d26c56e949',
+ /* 102 */ '0xbec763f8209b7a72b0afea0d31',
+ /* 103 */ '0x16ddc6556cdb84bdc8d12d22e6f',
+ /* 104 */ '0x2bd9ea4eed422ab6b7b072b029e',
+ /* 105 */ '0x54183095b2c8ececf30dd533d03',
+ /* 106 */ '0xa14517cc6b9457111eed5b8adf1',
+ /* 107 */ '0x13545598e5c23276ccf0ede68034',
+ /* 108 */ '0x2511882c39c3adea96fec2102329',
+ /* 109 */ '0x471649d87199aa990756806903c5',
+ /* 110 */ '0x88534434053a9828af9f37367ee6',
+ /* 111 */ '0x1056f1b5bedf75c6bcb2ce8aed428',
+ /* 112 */ '0x1f55b9d9ddff141121e70ebe0104e',
+ /* 113 */ '0x3c1771ac9fb6b4c18e229803dae82',
+ /* 114 */ '0x733d2d12ed20831ef0a4aead8c66d',
+ /* 115 */ '0xdcff115b14eedde6fc3aa5353f2e4',
+ /* 116 */ '0x1a7cf47248624733f355c5c1f0d1f1',
+ /* 117 */ '0x32cbfd4a7adc790560b3335687b89b',
+ /* 118 */ '0x616a0ae1edcba5599528c20605b3f6',
+ /* 119 */ '0xbad03e7d883f69ad5b0a186184e06b',
+ /* 120 */ '0x16641a07658687a905357ac0ebe198b',
+ /* 121 */ '0x2af09481380a0a35cf1ba02f36c6a56',
+ /* 122 */ '0x5258b7ba7725d902050f6360afddf96',
+ /* 123 */ '0x9deaf736ac1f569deb1b5ae3f36c130',
+ /* 124 */ '0x12ed7b32a58f552afeb26faf21deca06',
+ /* 125 */ '0x244c49c648baa98192dce88b42f53caf',
+ /* 126 */ '0x459c079aac334623648e24d17c74b3dc',
+ /* 127 */ '0x857ddf0117efa215952912839f6473e6',
+];
+module.exports.maxValArray = [
+ /* 0 */ '0x2550a7d99147ce113d27f304d24a422c3d',
+ /* 1 */ '0x1745f7d567fdd8c93da354496cf4dddf34',
+ /* 2 */ '0xb5301cf4bf20167721bcdbe218a66f1e0',
+ /* 3 */ '0x5e2d2ca56fae9ef2e524ba4d0f75b8754',
+ /* 4 */ '0x2f45acad795bce6dcd748391bb828dcea',
+ /* 5 */ '0x17f631b6609d1047920e1a1f9613f870d',
+ /* 6 */ '0xc29d4a7745ae89ef20a05db656441649',
+ /* 7 */ '0x6242dea9277cf2d473468985313625bb',
+ /* 8 */ '0x31aef9b37fbc57d1ca51c53eb472c345',
+ /* 9 */ '0x1923b23c38638957faeb8b4fe57b5ead',
+ /* 10 */ '0xcb919ec79bf364210433b9b9680eadd',
+ /* 11 */ '0x67186c63186761709a96a91d44ff2bf',
+ /* 12 */ '0x343e6242f854acd626b78022c4a8002',
+ /* 13 */ '0x1a7efb7b1b687ccb2bb413b92d5e413',
+ /* 14 */ '0xd72d0627fadb6aa6e0f3c994a5592a',
+ /* 15 */ '0x6d4f32a7dcd0924c122312b7522049',
+ /* 16 */ '0x37947990f145344d736c1e7e5cff2f',
+ /* 17 */ '0x1c49d8ceb31e3ef3e98703e0e656cc',
+ /* 18 */ '0xe69cb6255a180e2ead170f676fa3c',
+ /* 19 */ '0x75a24620898b4a19aafdfa67d23e8',
+ /* 20 */ '0x3c1419351dd33d49e1ce203728e25',
+ /* 21 */ '0x1eb97e709f819575e656eefb8bd98',
+ /* 22 */ '0xfbc4a1f867f03d4c057d522b6523',
+ /* 23 */ '0x812507c14867d2237468ba955def',
+ /* 24 */ '0x425b9d8ca5a58142d5172c3eb2b5',
+ /* 25 */ '0x2228e76a368b75ea80882c9f6010',
+ /* 26 */ '0x119ed9f43c52cdd38348ee8d7b23',
+ /* 27 */ '0x91bfcff5e91c7f115393af54bad',
+ /* 28 */ '0x4b8845f19f7b4a93653588ce846',
+ /* 29 */ '0x273fa600431f30b0f21b619c797',
+ /* 30 */ '0x1474840ba4069691110ff1bb823',
+ /* 31 */ '0xab212322b671a11d3647e3ecaf',
+ /* 32 */ '0x59ce8876bf3a3b1b396ae19c95',
+ /* 33 */ '0x2f523e50d3b0d68a3e39f2f06e',
+ /* 34 */ '0x190c4f51698c5ee5c3b34928a0',
+ /* 35 */ '0xd537c5d5647f2a79965d56f94',
+ /* 36 */ '0x72169649d403b5b512b40d5c2',
+ /* 37 */ '0x3d713a141a21a93a218c980c1',
+ /* 38 */ '0x215544c77538e6de9275431a6',
+ /* 39 */ '0x123c0edc8bf784d147024b7df',
+ /* 40 */ '0xa11eada236d9ccb5d9a46757',
+ /* 41 */ '0x59f185464ae514ade263ef14',
+ /* 42 */ '0x32d507935c586248656e95cb',
+ /* 43 */ '0x1d2270a4f18efd8eab5a27d7',
+ /* 44 */ '0x10f7bfaf758e3c1010bead08',
+ /* 45 */ '0xa101f6bc5df6cc4cf4cb56d',
+ /* 46 */ '0x61773c45cb6403833991e6e',
+ /* 47 */ '0x3c5f563f3abca8034b91c7d',
+ /* 48 */ '0x265cd2a70d374397f75a844',
+ /* 49 */ '0x1911bbf62c34780ee22ce8e',
+ /* 50 */ '0x10e3053085e97a7710c2e6d',
+ /* 51 */ '0xbbfc0e61443560740fa601',
+ /* 52 */ '0x874f16aa407949aebced14',
+ /* 53 */ '0x64df208d66f55c59261f5d',
+ /* 54 */ '0x4dee90487e19a58fbf52e9',
+ /* 55 */ '0x3e679f9e3b2f65e9d9b0db',
+ /* 56 */ '0x33c719b34c57f9f7a922f6',
+ /* 57 */ '0x2c7c090c36927c216fe17c',
+ /* 58 */ '0x2789fc1ccdbd02af70650f',
+ /* 59 */ '0x2451aae7a1741e150c6ae0',
+ /* 60 */ '0x22700f74722225e8c308e6',
+ /* 61 */ '0x21aae2600cf1170129eb92',
+ /* 62 */ '0x21e552192ec12eccaa1d44',
+ /* 63 */ '0x231a0b6c2a250a15897b8a',
+ /* 64 */ '0x255901ff2640b9b00fef5e',
+ /* 65 */ '0x28c842993fe2877ca68b09',
+ /* 66 */ '0x2da7b7138200abf065bc12',
+ /* 67 */ '0x34584e19c1677771772dbf',
+ /* 68 */ '0x3d678fd12af3f51aa5828a',
+ /* 69 */ '0x49a16c994ca36bb50c32c9',
+ /* 70 */ '0x5a2b2d67887520aacedab6',
+ /* 71 */ '0x70ac191abaee2a72987db6',
+ /* 72 */ '0x8f8afbb1a74e96379df7b1',
+ /* 73 */ '0xba4bd6d86b43467101fd6c',
+ /* 74 */ '0xf61f8e0679ef553e95c271',
+ /* 75 */ '0x14ac1e3b06c9771ad8f351c',
+ /* 76 */ '0x1c3d320c47b0e10030f080e',
+ /* 77 */ '0x272f678a02b5bd5dcc145a7',
+ /* 78 */ '0x3732bb25f4914992758a3aa',
+ /* 79 */ '0x4ee25a85a30b4e758af15a0',
+ /* 80 */ '0x724dbc7344a886ed20dbae2',
+ /* 81 */ '0xa7d64de739a14a222daf692',
+ /* 82 */ '0xf99876906cf6526b6b82ecc',
+ /* 83 */ '0x177bbaca105a36b48757a319',
+ /* 84 */ '0x23c442370233418f33964a65',
+ /* 85 */ '0x3716c05776b217ecbb587d11',
+ /* 86 */ '0x55c42bb597ed985a9d69778e',
+ /* 87 */ '0x86e8f9efa6efeba9e16b0a90',
+ /* 88 */ '0xd651f2e547d194ee8b6d9a69',
+ /* 89 */ '0x157b681e454d31a35819b1989',
+ /* 90 */ '0x22c414309a2b397b4f8e0eb28',
+ /* 91 */ '0x38c1a2330fcf634a5db1378a0',
+ /* 92 */ '0x5d6efaaf8133556840468bbbb',
+ /* 93 */ '0x9b0c82dee2e1f20d0a157a7ae',
+ /* 94 */ '0x10347bdd997b95a7905d850436',
+ /* 95 */ '0x1b4c902e273a586783055cede8',
+ /* 96 */ '0x2e50642e85a0b7c589bac2651b',
+ /* 97 */ '0x4f1b7f75028232ad3258b8b742',
+ /* 98 */ '0x880028111c381b5279db2271c3',
+ /* 99 */ '0xeb454460fe475acef6b927865e',
+ /* 100 */ '0x1996fab0c95ac4a2b5cfa8f555d',
+ /* 101 */ '0x2cc9f3994685c8d3224acb9fea1',
+ /* 102 */ '0x4ed2e079d693966878c7149351a',
+ /* 103 */ '0x8b740d663b523dad8b67451d8fc',
+ /* 104 */ '0xf7f73c5d826e196ff66a259204c',
+ /* 105 */ '0x1bb0d7eb2857065dcad087986fa6',
+ /* 106 */ '0x31b4dfa1eedd2bd17d3504820344',
+ /* 107 */ '0x599fae8ac47c48cf034887f489bb',
+ /* 108 */ '0xa249948898a0e444bffa21361f42',
+ /* 109 */ '0x12711786051c98ca2acc4adf7ba6a',
+ /* 110 */ '0x21a98821bf01e72cc3f724b65a121',
+ /* 111 */ '0x3dad0dd7c71f7b443dddd56fede23',
+ /* 112 */ '0x716933ca69ac1b439f976665fafdf',
+ /* 113 */ '0xd143a4beebca9707458aad7b22dcd',
+ /* 114 */ '0x18369cb4cd8522c1b28abc22a3e805',
+ /* 115 */ '0x2cf816f46d1971ec18f0ffb6922e86',
+ /* 116 */ '0x53c58e5a59ee4d9fd7f747f67a3aac',
+ /* 117 */ '0x9c833e3c0364561037250933eab9a9',
+ /* 118 */ '0x1253c9d983f03e6a0955355049411cb',
+ /* 119 */ '0x226e05852615979ea99f6ef68dbab51',
+ /* 120 */ '0x40d8c81134ee9e16db1e0108defbb9f',
+ /* 121 */ '0x7a70173a27075f4b9482d36deadc951',
+ /* 122 */ '0xe7b966d76665f99c3fb1791404f62c6',
+ /* 123 */ '0x1b78e22c38ae6aa69d36b8ccfade23fd',
+ /* 124 */ '0x3439aeef615a970c9678397b6ad71179',
+ /* 125 */ '0x637d37d6cb204d7419ac094d7e89f0dd',
+ /* 126 */ '0xbde80a98943810876a7852209de22be2',
+ /* 127 */ '0x16b3160a3c604c6667ff40ff1882b0fcf',
+];
\ No newline at end of file
diff --git a/utils/Utils.js b/utils/Utils.js
new file mode 100644
index 0000000..997268c
--- /dev/null
+++ b/utils/Utils.js
@@ -0,0 +1,36 @@
+/* global assert */
+
+function isException(error) {
+ let strError = error.toString();
+ return strError.includes('VM Exception') || strError.includes('invalid opcode') || strError.includes('invalid JUMP');
+}
+
+function ensureException(error) {
+ assert(isException(error), error.toString());
+}
+
+const PREFIX = "VM Exception while processing transaction: ";
+
+async function tryCatch(promise, message) {
+ try {
+ await promise;
+ throw null;
+ }
+ catch (error) {
+ assert(error, "Expected an error but did not get one");
+ assert(error.message.startsWith(PREFIX + message), "Expected an error starting with '" + PREFIX + message + "' but got '" + error.message + "' instead");
+ }
+};
+
+module.exports = {
+ zeroAddress : '0x0000000000000000000000000000000000000000',
+ isException : isException,
+ ensureException : ensureException,
+ catchRevert : async function(promise) {await tryCatch(promise, "revert" );},
+ catchOutOfGas : async function(promise) {await tryCatch(promise, "out of gas" );},
+ catchInvalidJump : async function(promise) {await tryCatch(promise, "invalid JUMP" );},
+ catchInvalidOpcode : async function(promise) {await tryCatch(promise, "invalid opcode" );},
+ catchStackOverflow : async function(promise) {await tryCatch(promise, "stack overflow" );},
+ catchStackUnderflow : async function(promise) {await tryCatch(promise, "stack underflow" );},
+ catchStaticStateChange : async function(promise) {await tryCatch(promise, "static state change");},
+};
\ No newline at end of file
diff --git a/utils/testUtils.js b/utils/testUtils.js
new file mode 100644
index 0000000..afe32cc
--- /dev/null
+++ b/utils/testUtils.js
@@ -0,0 +1,182 @@
+/*global assert, web3 */
+const bs58 = require('bs58')
+
+// This has been tested with the real Ethereum network and Testrpc.
+// Copied and edited from: https://gist.github.com/xavierlepretre/d5583222fde52ddfbc58b7cfa0d2d0a9
+
+exports.assertReverts = (contractMethodCall, maxGasAvailable) => {
+ return new Promise((resolve, reject) => {
+ try {
+ resolve(contractMethodCall())
+ } catch (error) {
+ reject(error)
+ }
+ })
+ .then(tx => {
+ assert.equal(
+ tx.receipt.gasUsed,
+ maxGasAvailable,
+ 'tx successful, the max gas available was not consumed',
+ )
+ })
+ .catch(error => {
+ if (
+ String(error).indexOf('invalid opcode') < 0 &&
+ String(error).indexOf('out of gas') < 0
+ ) {
+ // Checks if the error is from TestRpc. If it is then ignore it.
+ // Otherwise relay/throw the error produced by the above assertion.
+ // Note that no error is thrown when using a real Ethereum network AND the assertion above is true.
+ throw error
+ }
+ })
+}
+
+exports.listenForEvent = event =>
+ new Promise((resolve, reject) => {
+ event({}, (error, response) => {
+ if (!error) {
+ resolve(response.args)
+ } else {
+ reject(error)
+ }
+ event.stopWatching()
+ })
+ })
+
+exports.eventValues = (receipt, eventName) => {
+ if (receipt.events[eventName]) return receipt.events[eventName].returnValues
+}
+
+exports.addressToBytes32 = address => {
+ const stringed =
+ '0000000000000000000000000000000000000000000000000000000000000000' +
+ address.slice(2)
+ return `0x${ stringed.substring(stringed.length - 64, stringed.length)}`;
+}
+
+// OpenZeppelin's expectThrow helper -
+// Source: https://github.com/OpenZeppelin/zeppelin-solidity/blob/master/test/helpers/expectThrow.js
+exports.expectThrow = async promise => {
+ try {
+ await promise
+ } catch (error) {
+ // TODO: Check jump destination to destinguish between a throw
+ // and an actual invalid jump.
+ const invalidOpcode = error.message.search('invalid opcode') >= 0
+ // TODO: When we contract A calls contract B, and B throws, instead
+ // of an 'invalid jump', we get an 'out of gas' error. How do
+ // we distinguish this from an actual out of gas event? (The
+ // testrpc log actually show an 'invalid jump' event.)
+ const outOfGas = error.message.search('out of gas') >= 0
+ const revert = error.message.search('revert') >= 0
+ assert(
+ invalidOpcode || outOfGas || revert,
+ `Expected throw, got '${ error }' instead`,
+ )
+ return
+ }
+ assert.fail('Expected throw not received')
+}
+
+exports.assertJump = error => {
+ assert(
+ error.message.search('VM Exception while processing transaction: revert') >
+ -1,
+ 'Revert should happen',
+ )
+}
+
+function callbackToResolve(resolve, reject) {
+ return function(error, value) {
+ if (error) {
+ reject(error)
+ } else {
+ resolve(value)
+ }
+ }
+}
+
+exports.promisify = func => (...args) => {
+ return new Promise((resolve, reject) => {
+ const callback = (err, data) => (err ? reject(err) : resolve(data))
+ func.apply(this, [...args, callback])
+ })
+}
+
+exports.zeroAddress = '0x0000000000000000000000000000000000000000'
+exports.zeroBytes32 =
+ '0x0000000000000000000000000000000000000000000000000000000000000000'
+exports.timeUnits = {
+ seconds: 1,
+ minutes: 60,
+ hours: 60 * 60,
+ days: 24 * 60 * 60,
+ weeks: 7 * 24 * 60 * 60,
+ years: 365 * 24 * 60 * 60,
+}
+
+exports.ensureException = function(error) {
+ assert(isException(error), error.toString())
+}
+
+function isException(error) {
+ const strError = error.toString()
+ return (
+ strError.includes('invalid opcode') ||
+ strError.includes('invalid JUMP') ||
+ strError.includes('revert')
+ )
+}
+
+const evmMethod = (method, params = []) => {
+ return new Promise(function(resolve, reject) {
+ const sendMethod = web3.currentProvider.sendAsync
+ ? web3.currentProvider.sendAsync.bind(web3.currentProvider)
+ : web3.currentProvider.send.bind(web3.currentProvider)
+ sendMethod(
+ {
+ jsonrpc: '2.0',
+ method,
+ params,
+ id: new Date().getSeconds(),
+ },
+ (error, res) => {
+ if (error) {
+ return reject(error)
+ }
+ resolve(res.result)
+ },
+ )
+ })
+}
+
+exports.evmSnapshot = async () => {
+ const result = await evmMethod('evm_snapshot')
+ return web3.utils.hexToNumber(result)
+}
+
+exports.evmRevert = id => {
+ const params = [id]
+ return evmMethod('evm_revert', params)
+}
+
+exports.increaseTime = async amount => {
+ await evmMethod('evm_increaseTime', [Number(amount)])
+ await evmMethod('evm_mine')
+}
+
+exports.getBytes32FromIpfsHash = ipfsListing => {
+ const decodedHash = bs58
+ .decode(ipfsListing)
+ .slice(2)
+ .toString('hex')
+ return `0x${decodedHash}`
+}
+
+exports.getIpfsHashFromBytes32 = bytes32Hex => {
+ const hashHex = `1220${bytes32Hex.slice(2)}`
+ const hashBytes = Buffer.from(hashHex, 'hex')
+ const hashStr = bs58.encode(hashBytes)
+ return hashStr
+}