Adds updated contract, tests and spec

This commit is contained in:
andytudhope 2019-05-27 17:22:45 +02:00
parent 86a7e29d5d
commit 2e2b8e46ec
327 changed files with 13259 additions and 0 deletions

9
.editorconfig Normal file
View File

@ -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

28
.eslintrc.json Normal file
View File

@ -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
}
}

1
.gitattributes vendored Normal file
View File

@ -0,0 +1 @@
*.sol diff linguist-language=Solidity

42
.gitignore vendored Normal file
View File

@ -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/

1
.soliumignore Normal file
View File

@ -0,0 +1 @@
node_modules

22
.soliumrc.json Normal file
View File

@ -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
]
}
}

179
Discover_Specification.md Normal file
View File

@ -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/).

44
README.md Normal file
View File

@ -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.

149
config/blockchain.js Normal file
View File

@ -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: {
// }
}

46
config/communication.js Normal file
View File

@ -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
}
*/
//}
};

137
config/contracts.js Normal file
View File

@ -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: {
// }
}

View File

@ -0,0 +1,2 @@
module.exports.mnemonic =
''

View File

@ -0,0 +1 @@
dev_password

39
config/namesystem.js Normal file
View File

@ -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: {
//}
};

27
config/pipeline.js Normal file
View File

@ -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
};

File diff suppressed because one or more lines are too long

View File

@ -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"
}

View File

@ -0,0 +1 @@
dev_password

74
config/storage.js Normal file
View File

@ -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: {
// }
}

1
config/testnet/password Normal file
View File

@ -0,0 +1 @@
test_password

6
config/webserver.js Normal file
View File

@ -0,0 +1,6 @@
module.exports = {
enabled: true,
host: "localhost",
openBrowser: true,
port: 8000
};

0
contracts/.gitkeep Normal file
View File

395
contracts/Discover.sol Normal file
View File

@ -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))
}
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -0,0 +1,10 @@
pragma solidity ^0.5.2;
contract ApproveAndCallFallBack {
function receiveApproval(
address from,
uint256 _amount,
address _token,
bytes calldata _data) external;
}

View File

@ -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);
}

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
/**
* @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
);
}

View File

@ -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);
}
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -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;
}
}

26
embark.json Normal file
View File

@ -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"
}

77
package.json Normal file
View File

@ -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"
}
}

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

View File

@ -0,0 +1,108 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="900px" height="900px" viewBox="84.5 810.5 900 900" enable-background="new 84.5 810.5 900 900" xml:space="preserve">
<g>
<circle fill="#475493" cx="535.993" cy="1264.794" r="345.105"/>
<g>
<g>
<line fill="none" stroke="#FFFFFF" stroke-width="6" stroke-linecap="round" stroke-linejoin="round" x1="535.994" y1="1000.116" x2="373.207" y2="1270.211"/>
<line fill="none" stroke="#FFFFFF" stroke-width="6" stroke-linecap="round" stroke-linejoin="round" x1="698.714" y1="1270.211" x2="535.994" y2="1196.177"/>
<line fill="none" stroke="#FFFFFF" stroke-width="6" stroke-linecap="round" stroke-linejoin="round" x1="373.207" y1="1270.211" x2="535.994" y2="1196.177"/>
<line fill="none" stroke="#FFFFFF" stroke-width="6" stroke-linecap="round" stroke-linejoin="round" x1="373.207" y1="1270.211" x2="535.994" y2="1366.386"/>
<line fill="none" stroke="#FFFFFF" stroke-width="6" stroke-linecap="round" stroke-linejoin="round" x1="535.994" y1="1000.116" x2="698.714" y2="1270.211"/>
<line fill="none" stroke="#FFFFFF" stroke-width="6" stroke-linecap="round" stroke-linejoin="round" x1="535.994" y1="1196.177" x2="535.994" y2="1366.386"/>
<line fill="none" stroke="#FFFFFF" stroke-width="6" stroke-linecap="round" stroke-linejoin="round" x1="535.994" y1="1000.116" x2="535.994" y2="1196.177"/>
<line fill="none" stroke="#FFFFFF" stroke-width="6" stroke-linecap="round" stroke-linejoin="round" x1="698.714" y1="1270.211" x2="535.994" y2="1366.386"/>
<polyline fill="none" stroke="#FFFFFF" stroke-width="6" stroke-linecap="round" stroke-linejoin="round" points="
535.994,1530.388 373.207,1301.054 535.994,1397.229 "/>
<polyline fill="none" stroke="#FFFFFF" stroke-width="6" stroke-linecap="round" stroke-linejoin="round" points="
535.994,1530.388 698.843,1301.054 535.994,1397.229 "/>
<line fill="none" stroke="#FFFFFF" stroke-width="6" stroke-linecap="round" stroke-linejoin="round" x1="535.994" y1="1530.388" x2="535.994" y2="1397.229"/>
</g>
<g>
<g>
<g>
<path fill="#FFFFFF" d="M375.898,1277.325c-4.567,0-8.283-3.716-8.283-8.282s3.716-8.282,8.283-8.282s8.282,3.716,8.282,8.282
S380.465,1277.325,375.898,1277.325z"/>
<path fill="#FFFFFF" d="M375.898,1262.261c3.745,0,6.782,3.036,6.782,6.782s-3.037,6.782-6.782,6.782
c-3.747,0-6.783-3.036-6.783-6.782S372.151,1262.261,375.898,1262.261 M375.898,1259.261c-5.394,0-9.783,4.389-9.783,9.782
s4.389,9.782,9.783,9.782s9.782-4.389,9.782-9.782S381.292,1259.261,375.898,1259.261L375.898,1259.261z"/>
</g>
<g>
<path fill="#FFFFFF" d="M696.087,1277.325c-4.566,0-8.281-3.716-8.281-8.282s3.715-8.282,8.281-8.282
c4.567,0,8.283,3.716,8.283,8.282S700.655,1277.325,696.087,1277.325z"/>
<path fill="#FFFFFF" d="M696.087,1262.261c3.748,0,6.783,3.036,6.783,6.782s-3.035,6.782-6.783,6.782
c-3.746,0-6.781-3.036-6.781-6.782S692.341,1262.261,696.087,1262.261 M696.087,1259.261c-5.394,0-9.781,4.389-9.781,9.782
s4.388,9.782,9.781,9.782c5.395,0,9.783-4.389,9.783-9.782S701.482,1259.261,696.087,1259.261L696.087,1259.261z"/>
</g>
</g>
<g>
<g>
<path fill="#FFFFFF" d="M375.898,1310.963c-4.567,0-8.283-3.715-8.283-8.281s3.716-8.282,8.283-8.282s8.282,3.716,8.282,8.282
S380.465,1310.963,375.898,1310.963z"/>
<path fill="#FFFFFF" d="M375.898,1295.899c3.745,0,6.782,3.036,6.782,6.782c0,3.745-3.037,6.781-6.782,6.781
c-3.747,0-6.783-3.036-6.783-6.781C369.115,1298.936,372.151,1295.899,375.898,1295.899 M375.898,1292.899
c-5.394,0-9.783,4.389-9.783,9.782s4.389,9.781,9.783,9.781s9.782-4.388,9.782-9.781S381.292,1292.899,375.898,1292.899
L375.898,1292.899z"/>
</g>
<g>
<path fill="#FFFFFF" d="M696.087,1310.963c-4.566,0-8.281-3.715-8.281-8.281s3.715-8.282,8.281-8.282
c4.567,0,8.283,3.716,8.283,8.282S700.655,1310.963,696.087,1310.963z"/>
<path fill="#FFFFFF" d="M696.087,1295.899c3.748,0,6.783,3.036,6.783,6.782c0,3.745-3.035,6.781-6.783,6.781
c-3.746,0-6.781-3.036-6.781-6.781C689.306,1298.936,692.341,1295.899,696.087,1295.899 M696.087,1292.899
c-5.394,0-9.781,4.389-9.781,9.782s4.388,9.781,9.781,9.781c5.395,0,9.783-4.388,9.783-9.781
S701.482,1292.899,696.087,1292.899L696.087,1292.899z"/>
</g>
</g>
<g>
<path fill="#FFFFFF" d="M535.994,1372.177c-4.567,0-8.283-3.716-8.283-8.282s3.716-8.282,8.283-8.282
c4.566,0,8.281,3.716,8.281,8.282S540.56,1372.177,535.994,1372.177z"/>
<path fill="#FFFFFF" d="M535.994,1357.112c3.744,0,6.781,3.036,6.781,6.782s-3.037,6.782-6.781,6.782
c-3.746,0-6.783-3.036-6.783-6.782S532.248,1357.112,535.994,1357.112 M535.994,1354.112c-5.394,0-9.783,4.389-9.783,9.782
s4.389,9.782,9.783,9.782c5.394,0,9.781-4.389,9.781-9.782S541.387,1354.112,535.994,1354.112L535.994,1354.112z"/>
</g>
<g>
<path fill="#FFFFFF" d="M535.994,1206.588c-5.74,0-10.411-4.671-10.411-10.411s4.67-10.41,10.411-10.41s10.41,4.67,10.41,10.41
S541.734,1206.588,535.994,1206.588z"/>
<path fill="#FFFFFF" d="M535.994,1187.267c4.92,0,8.91,3.988,8.91,8.91s-3.99,8.911-8.91,8.911
c-4.921,0-8.911-3.989-8.911-8.911S531.072,1187.267,535.994,1187.267 M535.994,1184.267c-6.567,0-11.911,5.343-11.911,11.91
s5.343,11.911,11.911,11.911s11.91-5.344,11.91-11.911S542.561,1184.267,535.994,1184.267L535.994,1184.267z"/>
</g>
<g>
<path fill="#FFFFFF" d="M535.994,1015.103c-5.74,0-10.411-4.67-10.411-10.41s4.67-10.41,10.411-10.41s10.41,4.67,10.41,10.41
S541.734,1015.103,535.994,1015.103z"/>
<path fill="#FFFFFF" d="M535.994,995.782c4.92,0,8.91,3.988,8.91,8.91c0,4.921-3.99,8.91-8.91,8.91
c-4.921,0-8.911-3.989-8.911-8.91C527.083,999.771,531.072,995.782,535.994,995.782 M535.994,992.782
c-6.567,0-11.911,5.343-11.911,11.91s5.343,11.91,11.911,11.91s11.91-5.343,11.91-11.91S542.561,992.782,535.994,992.782
L535.994,992.782z"/>
</g>
<g>
<path fill="#FFFFFF" d="M535.994,1407.639c-5.74,0-10.411-4.67-10.411-10.41s4.67-10.41,10.411-10.41s10.41,4.67,10.41,10.41
S541.734,1407.639,535.994,1407.639z"/>
<path fill="#FFFFFF" d="M535.994,1388.318c4.92,0,8.91,3.989,8.91,8.91s-3.99,8.91-8.91,8.91c-4.921,0-8.911-3.989-8.911-8.91
S531.072,1388.318,535.994,1388.318 M535.994,1385.318c-6.567,0-11.911,5.343-11.911,11.91s5.343,11.91,11.911,11.91
s11.91-5.343,11.91-11.91S542.561,1385.318,535.994,1385.318L535.994,1385.318z"/>
</g>
<g>
<path fill="#FFFFFF" d="M535.994,1535.307c-5.74,0-10.411-4.67-10.411-10.41s4.67-10.41,10.411-10.41s10.41,4.67,10.41,10.41
S541.734,1535.307,535.994,1535.307z"/>
<path fill="#FFFFFF" d="M535.994,1515.986c4.92,0,8.91,3.989,8.91,8.91c0,4.922-3.99,8.91-8.91,8.91
c-4.921,0-8.911-3.988-8.911-8.91C527.083,1519.976,531.072,1515.986,535.994,1515.986 M535.994,1512.986
c-6.567,0-11.911,5.343-11.911,11.91s5.343,11.91,11.911,11.91s11.91-5.343,11.91-11.91S542.561,1512.986,535.994,1512.986
L535.994,1512.986z"/>
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

View File

@ -0,0 +1 @@
<svg width="105" height="44" viewBox="0 0 105 44" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><title>Logo</title><defs><path id="a" d="M0 0h40.473v43.768H0z"/></defs><g fill="none" fill-rule="evenodd"><g><mask id="b" fill="#fff"><use xlink:href="#a"/></mask><path d="M27.302 24.204l.07.414.023.164c.294 2.279 1.18 2.945 3.38 3.916.005.002.01.001.014.003l7.977 3.514L20.755 42.47V36.6c0-2.153.13-2.54 2.046-3.596.376-.207.757-.423 1.174-.66l.26-.148c1.193-.636 2.165-1.575 2.882-2.69.026-.04.218-.329.243-.37a1.997 1.997 0 0 1-.411-.298 1.653 1.653 0 0 1-.33-.404c-.627 1.218-1.58 2.248-2.8 2.919l-.296.168c-.411.233-.83.462-1.206.668-2.35 1.295-2.537 2.09-2.537 4.412v5.876L1.602 32.035l7.779-3.393.506-.217c.198-.096.82-.35 1.3-.364.49-.03 1.234.001 1.635.51a3055.9 3055.9 0 0 0 1.112 1.405c.858 1.11 1.98 2.017 3.32 2.561a.099.099 0 0 0 .012.007c.353.144.733.227 1.11.315.078-.149.2-.322.312-.462.081-.101.202-.226.315-.343-.57-.111-1.225-.292-1.735-.536l-.002-.001c-1.018-.483-1.898-1.16-2.574-2.03a.939.939 0 0 1-.09-.12c-.069-.09-.138-.178-.201-.273-.18-.242-.454-.603-.837-1.085-.257-.326-.617-.541-1-.68l.001.002c-.413-.148-.89-.214-1.411-.21-.79.026-1.666.446-1.684.454l-.273.118-.021.008-.708.305c-.005.001-.005.006-.009.008L1.661 30.98 19.764 2.16V14.36c0 2.108-.155 2.569-2.088 3.634a8.262 8.262 0 0 1-.467.233l-.25.123c-.063.028-.123.061-.184.091l-.003.001h-.001c-2.536 1.215-4.307 3.679-4.461 6.554.166.045.418.116.559.159.103.03.259.108.39.178.025-2.657 1.633-4.949 3.956-6.055l.001-.001c.007-.003.012-.002.018-.005a14.2 14.2 0 0 1 .408-.2 9.82 9.82 0 0 0 1.414-.806c.071-.046.13-.095.195-.141.142-.103.275-.208.391-.31.068-.059.13-.114.173-.168.774-.783.926-1.665.926-3.287V2.155L38.9 31.241l-8.015-3.53h-.004c-1.778-.784-2.318-1.44-2.556-3.326-.004-.025-.011-.09-.017-.114-.458-3.015-2.716-5.434-5.682-6.28l-.4-.115a2.256 2.256 0 0 1-.198.442 2.387 2.387 0 0 1-.317.416l.373.083c2.667.65 4.73 2.742 5.218 5.387m13.1 7.62L20.675.227A.49.49 0 0 0 20.258 0a.49.49 0 0 0-.417.227L.071 31.697a.465.465 0 0 0 .168.647l19.77 11.358a.502.502 0 0 0 .496 0l19.728-11.23a.478.478 0 0 0 .225-.29.45.45 0 0 0-.056-.358" fill="#553580" mask="url(#b)"/></g><g opacity=".848" fill="#553580"><path d="M54.1 14.932l-3.991 10.586h.883l.945-2.572h5.057l.944 2.572h.884L54.83 14.932H54.1zm.365 1.07l2.27 6.23h-4.54l2.27-6.23zM65.748 25.607c-2.179 0-3.717-1.457-3.717-3.523v-7.152h.823v7.048c0 1.754 1.157 2.914 2.894 2.914 1.736 0 2.894-1.16 2.894-2.914v-7.048h.822v7.152c0 2.066-1.538 3.523-3.716 3.523M80.285 24.388a3.88 3.88 0 0 1-2.848 1.22 3.874 3.874 0 0 1-2.712-1.086c-.99-.967-1.005-1.903-1.005-4.297 0-2.393.016-3.33 1.005-4.296a3.874 3.874 0 0 1 2.712-1.085c1.904 0 3.366 1.219 3.701 3.091h-.822c-.32-1.456-1.417-2.378-2.88-2.378-.776 0-1.507.282-2.056.818-.776.758-.837 1.575-.837 3.85 0 2.275.061 3.093.837 3.85.549.536 1.28.819 2.057.819.853 0 1.69-.357 2.254-1.026.472-.565.655-1.19.655-2.097v-.951h-2.91v-.714h3.732v1.725c0 1.1-.243 1.888-.883 2.557M89.053 25.607c-2.179 0-3.717-1.457-3.717-3.523v-7.152h.823v7.048c0 1.754 1.157 2.914 2.894 2.914 1.736 0 2.894-1.16 2.894-2.914v-7.048h.822v7.152c0 2.066-1.538 3.523-3.716 3.523M101.388 15.646h-3.199v4.282h3.199c1.385 0 2.391-.684 2.391-2.141 0-1.457-1.006-2.14-2.391-2.14zm2.376 9.872l-2.59-4.877H98.19v4.877h-.823V14.933h4.128c1.767 0 3.107.996 3.107 2.84 0 1.575-.975 2.601-2.497 2.853l2.62 4.892h-.96z"/></g></g></svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 446 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Some files were not shown because too many files have changed in this diff Show More