├── ABI ├── version14 │ ├── MathUint.abi │ ├── AddressUtil.abi │ ├── MathBytes32.abi │ ├── MathUint8.abi │ ├── Ownable.abi │ ├── Claimable.abi │ ├── ERC20.abi │ ├── TokenFactory.abi │ ├── LoopringProtocol.abi │ ├── TokenTransferDelegate.abi │ ├── TokenRegistry.abi │ ├── ERC20Token.abi │ ├── NameRegistry.abi │ └── LoopringProtocolImpl.abi ├── version15 │ ├── MathUint.abi │ ├── AddressUtil.abi │ ├── MathBytes32.abi │ ├── MathUint8.abi │ ├── StringUtil.abi │ ├── Ownable.abi │ ├── TransferableMultsig.abi │ ├── TokenFactory.abi │ ├── Migrations.abi │ ├── Claimable.abi │ ├── ERC20.abi │ ├── TokenFactoryImpl.abi │ ├── TransferableMultsigImpl.abi │ ├── TokenRegistry.abi │ ├── LoopringProtocol.abi │ ├── TokenRegistryImpl.abi │ ├── ERC20Token.abi │ ├── TokenTransferDelegate.abi │ ├── LoopringProtocolImpl.abi │ └── TokenTransferDelegateImpl.abi ├── version151 │ ├── AddressUtil.abi │ ├── MathBytes32.abi │ ├── MathUint.abi │ ├── MathUint8.abi │ ├── StringUtil.abi │ ├── Ownable.abi │ ├── TransferableMultsig.abi │ ├── TokenFactory.abi │ ├── Migrations.abi │ ├── Claimable.abi │ ├── TokenFactoryImpl.abi │ ├── ERC20.abi │ ├── TransferableMultsigImpl.abi │ ├── TokenRegistry.abi │ ├── LoopringProtocol.abi │ ├── TokenRegistryImpl.abi │ ├── ERC20Token.abi │ ├── TokenTransferDelegate.abi │ ├── LoopringProtocolImpl.abi │ └── TokenTransferDelegateImpl.abi ├── version10 │ ├── TransferableMultsig.abi │ ├── RinghashRegistry.abi │ ├── TokenTransferDelegate.abi │ ├── TokenRegistry.abi │ └── LoopringProtocolImpl.abi └── version12 │ ├── TransferableMultsig.abi │ ├── TokenTransferDelegate.abi │ ├── TokenRegistry.abi │ ├── NameRegistry.abi │ └── LoopringProtocolImpl.abi ├── docs ├── _config.yml ├── index.md ├── gas_consume_report_1025.md ├── gas_consume_report_1108.md └── gas_consume_report_1026.md ├── .soliumignore ├── .gitignore ├── .dockerignore ├── migrations ├── 1_initial_migration.js ├── 2_deploy_registries.js ├── config │ └── tokens.js ├── 3_deploy_tokens.js └── 4_deploy_protocol.js ├── Dockerfile.testrpc ├── .soliumrc.json ├── bounty ├── 1_setOptimization2Env.sh ├── 3_recoverLatestEnv.sh ├── 2_testOptimization2.sh └── bug_submissions.md ├── assets └── css │ └── style.scss ├── docker-compose.yml ├── Dockerfile.test ├── tsconfig.json ├── util ├── artifacts.ts ├── bitstream.ts ├── parseTx.ts ├── types.ts ├── order.ts ├── chain_reader.ts ├── ring_factory.ts └── ring.ts ├── tslint.json ├── globals.d.ts ├── ganache.sh ├── truffle.js ├── contracts ├── lib │ ├── MathUint8.sol │ ├── AddressUtil.sol │ ├── MathBytes32.sol │ ├── StringUtil.sol │ ├── ERC20.sol │ ├── Claimable.sol │ ├── MemoryUtil.sol │ ├── Ownable.sol │ └── MathUint.sol ├── Migrations.sol ├── TransferableMultsig.sol ├── TokenFactory.sol ├── test │ └── DummyToken.sol ├── TokenFactoryImpl.sol ├── TokenRegistry.sol ├── TokenTransferDelegate.sol ├── TransferableMultsigImpl.sol └── TokenRegistryImpl.sol ├── README.md ├── package.json ├── CODE_OF_CONDUCT.md └── test ├── testTokenRegistry.ts └── ringConfig.ts /ABI/version14/MathUint.abi: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /ABI/version15/MathUint.abi: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /ABI/version14/AddressUtil.abi: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /ABI/version14/MathBytes32.abi: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /ABI/version14/MathUint8.abi: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /ABI/version15/AddressUtil.abi: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /ABI/version15/MathBytes32.abi: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /ABI/version15/MathUint8.abi: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /ABI/version15/StringUtil.abi: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /ABI/version151/AddressUtil.abi: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /ABI/version151/MathBytes32.abi: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /ABI/version151/MathUint.abi: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /ABI/version151/MathUint8.abi: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /ABI/version151/StringUtil.abi: -------------------------------------------------------------------------------- 1 | [] -------------------------------------------------------------------------------- /docs/_config.yml: -------------------------------------------------------------------------------- 1 | remote_theme: dong77/minimal 2 | -------------------------------------------------------------------------------- /.soliumignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | deploy 3 | test 4 | transpiled 5 | build -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | build/ 3 | transpiled/ 4 | .DS_Store 5 | .node-* 6 | *.log 7 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | Dockerfile 3 | docker-compose.yml 4 | .vscode 5 | .git 6 | .gitignore 7 | .soliumignore 8 | .soliumrc.json -------------------------------------------------------------------------------- /migrations/1_initial_migration.js: -------------------------------------------------------------------------------- 1 | var Migrations = artifacts.require("./Migrations.sol"); 2 | 3 | module.exports = function(deployer) { 4 | deployer.deploy(Migrations); 5 | }; 6 | -------------------------------------------------------------------------------- /Dockerfile.testrpc: -------------------------------------------------------------------------------- 1 | FROM mhart/alpine-node:9.7 2 | 3 | RUN npm install -g ganache-cli@6.0.3 4 | 5 | ADD ganache.sh ganache.sh 6 | 7 | RUN chmod +x ganache.sh 8 | 9 | EXPOSE 8545 10 | 11 | CMD sh ganache.sh -------------------------------------------------------------------------------- /.soliumrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "solium:recommended", 3 | "rules": { 4 | "imports-on-top": 0, 5 | "variable-declarations": 0, 6 | "indentation": ["error", 4], 7 | "quotes": ["error", "double"] 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /bounty/1_setOptimization2Env.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ## This script should run from the protocol root directory. 4 | 5 | npm uninstall -g truffle 6 | npm uninstall -g ethereumjs-testrpc 7 | npm install -g truffle@3.4.11 8 | npm install -g ethereumjs-testrpc@4.1.3 9 | -------------------------------------------------------------------------------- /assets/css/style.scss: -------------------------------------------------------------------------------- 1 | --- 2 | --- 3 | 4 | @import "jekyll-theme-minimal"; 5 | 6 | a { 7 | color:#1d70ff; 8 | } 9 | 10 | a:hover, a:focus { 11 | color:#1c60ff; 12 | } 13 | 14 | .markdown-body table tr:nth-child(2n) { 15 | background-color: #1d70ff20; 16 | } -------------------------------------------------------------------------------- /bounty/3_recoverLatestEnv.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ## This script should run from the protocol root directory. 4 | npm uninstall -g truffle 5 | npm uninstall -g ethereumjs-testrpc 6 | npm install -g truffle@4.0.1 7 | npm install -g ethereumjs-testrpc@6.0.1 8 | 9 | npm uninstall &>/dev/null 10 | npm install &>/dev/null -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | services: 3 | testrpc: 4 | container_name: testrpc 5 | build: 6 | context: . 7 | dockerfile: Dockerfile.testrpc 8 | expose: 9 | - "8545" 10 | ports: 11 | - "8545:8545" 12 | test: 13 | container_name: test 14 | depends_on: 15 | - testrpc 16 | build: 17 | context: . 18 | dockerfile: Dockerfile.test -------------------------------------------------------------------------------- /Dockerfile.test: -------------------------------------------------------------------------------- 1 | FROM mhart/alpine-node:9.7 2 | 3 | RUN apk add --no-cache git curl make gcc g++ python linux-headers 4 | 5 | RUN npm install -g truffle@4.0.1 typescript@2.4.2 6 | 7 | ADD package.json package.json 8 | 9 | ADD package-lock.json package-lock.json 10 | 11 | RUN npm install 12 | 13 | RUN apk del git curl make gcc g++ linux-headers 14 | 15 | ADD . . 16 | 17 | RUN npm run compile 18 | 19 | CMD npm run testdocker -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "outDir": "./transpiled/", 4 | "sourceMap": true, 5 | "noImplicitAny": true, 6 | "module": "commonjs", 7 | "target": "es6", 8 | "baseUrl": ".", 9 | "allowJs": true 10 | }, 11 | "include": [ 12 | "node_modules/web3-typescript-typings/index.d.ts", 13 | "./globals.d.ts", 14 | "./util/**/*", 15 | "./test/**/*", 16 | "./migrations/**/*" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /migrations/2_deploy_registries.js: -------------------------------------------------------------------------------- 1 | var TokenRegistryImpl = artifacts.require("./TokenRegistryImpl"); 2 | var TokenTransferDelegateImpl = artifacts.require("./TokenTransferDelegateImpl"); 3 | var TokenFactoryImpl = artifacts.require("./TokenFactoryImpl"); 4 | 5 | module.exports = function(deployer, network, accounts) { 6 | deployer.deploy(TokenTransferDelegateImpl); 7 | deployer.deploy(TokenRegistryImpl); 8 | deployer.deploy(TokenFactoryImpl); 9 | 10 | }; 11 | -------------------------------------------------------------------------------- /util/artifacts.ts: -------------------------------------------------------------------------------- 1 | 2 | export class Artifacts { 3 | public TokenRegistry: any; 4 | public LoopringProtocolImpl: any; 5 | public TokenTransferDelegate: any; 6 | public DummyToken: any; 7 | constructor(artifacts: any) { 8 | this.TokenRegistry = artifacts.require("TokenRegistryImpl"); 9 | this.LoopringProtocolImpl = artifacts.require("LoopringProtocolImpl"); 10 | this.TokenTransferDelegate = artifacts.require("TokenTransferDelegateImpl"); 11 | this.DummyToken = artifacts.require("test/DummyToken"); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "defaultSeverity": "error", 3 | "extends": [ 4 | "tslint:recommended" 5 | ], 6 | "cliOptions": { 7 | "exclude": [ 8 | "./util/submit_rings.ts" 9 | ] 10 | }, 11 | "jsRules": {}, 12 | "rules": { 13 | "no-console": false, 14 | "no-bitwise": false, 15 | "object-literal-sort-keys": false, 16 | "interface-name": false, 17 | "max-line-length": [true, {"limit": 120, "ignore-pattern": "^import |^export {(.*?)}|class [a-zA-Z]+ implements |//"}] 18 | }, 19 | "rulesDirectory": [] 20 | } 21 | -------------------------------------------------------------------------------- /globals.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'bn.js'; 2 | declare module 'ethereumjs-abi'; 3 | declare module 'ethereumjs-util'; 4 | declare module 'es6-promisify'; 5 | declare module 'ethereum-tx-decoder'; 6 | 7 | // Truffle injects the following into the global scope 8 | declare var web3: any; 9 | declare var artifacts: any; 10 | declare var contract: any; 11 | declare var before: any; 12 | declare var beforeEach: any; 13 | declare var describe: any; 14 | declare var it: any; 15 | declare var assert: any; 16 | 17 | declare module "*.json" { 18 | const value: any; 19 | export default value; 20 | } 21 | -------------------------------------------------------------------------------- /ABI/version14/Ownable.abi: -------------------------------------------------------------------------------- 1 | [{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"previousOwner","type":"address"},{"indexed":true,"name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"}] -------------------------------------------------------------------------------- /ABI/version15/Ownable.abi: -------------------------------------------------------------------------------- 1 | [{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"previousOwner","type":"address"},{"indexed":true,"name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"}] -------------------------------------------------------------------------------- /ABI/version151/Ownable.abi: -------------------------------------------------------------------------------- 1 | [{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"previousOwner","type":"address"},{"indexed":true,"name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"}] -------------------------------------------------------------------------------- /migrations/config/tokens.js: -------------------------------------------------------------------------------- 1 | 2 | module.exports = { 3 | development: [ 4 | { 5 | decimals: 18, 6 | name: "Loopring Coin", 7 | symbol: "LRC", 8 | }, 9 | { 10 | decimals: 18, 11 | name: "EOS", 12 | symbol: "EOS", 13 | }, 14 | { 15 | decimals: 18, 16 | name: "Augur Reputation Token", 17 | symbol: "REP", 18 | }, 19 | { 20 | decimals: 18, 21 | name: "RDN", 22 | symbol: "RDN", 23 | }, 24 | { 25 | decimals: 18, 26 | name: "GTO", 27 | symbol: "GTO", 28 | }, 29 | ], 30 | 31 | live: [ ], 32 | }; 33 | -------------------------------------------------------------------------------- /ABI/version15/TransferableMultsig.abi: -------------------------------------------------------------------------------- 1 | [{"constant":false,"inputs":[{"name":"sigV","type":"uint8[]"},{"name":"sigR","type":"bytes32[]"},{"name":"sigS","type":"bytes32[]"},{"name":"_threshold","type":"uint256"},{"name":"_owners","type":"address[]"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"sigV","type":"uint8[]"},{"name":"sigR","type":"bytes32[]"},{"name":"sigS","type":"bytes32[]"},{"name":"destination","type":"address"},{"name":"value","type":"uint256"},{"name":"data","type":"bytes"}],"name":"execute","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}] -------------------------------------------------------------------------------- /ABI/version151/TransferableMultsig.abi: -------------------------------------------------------------------------------- 1 | [{"constant":false,"inputs":[{"name":"sigV","type":"uint8[]"},{"name":"sigR","type":"bytes32[]"},{"name":"sigS","type":"bytes32[]"},{"name":"_threshold","type":"uint256"},{"name":"_owners","type":"address[]"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"sigV","type":"uint8[]"},{"name":"sigR","type":"bytes32[]"},{"name":"sigS","type":"bytes32[]"},{"name":"destination","type":"address"},{"name":"value","type":"uint256"},{"name":"data","type":"bytes"}],"name":"execute","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"}] -------------------------------------------------------------------------------- /ABI/version15/TokenFactory.abi: -------------------------------------------------------------------------------- 1 | [{"constant":false,"inputs":[{"name":"name","type":"string"},{"name":"symbol","type":"string"},{"name":"decimals","type":"uint8"},{"name":"totalSupply","type":"uint256"}],"name":"createToken","outputs":[{"name":"addr","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"addr","type":"address"},{"indexed":false,"name":"name","type":"string"},{"indexed":false,"name":"symbol","type":"string"},{"indexed":false,"name":"decimals","type":"uint8"},{"indexed":false,"name":"totalSupply","type":"uint256"},{"indexed":false,"name":"firstHolder","type":"address"}],"name":"TokenCreated","type":"event"}] -------------------------------------------------------------------------------- /ABI/version151/TokenFactory.abi: -------------------------------------------------------------------------------- 1 | [{"constant":false,"inputs":[{"name":"name","type":"string"},{"name":"symbol","type":"string"},{"name":"decimals","type":"uint8"},{"name":"totalSupply","type":"uint256"}],"name":"createToken","outputs":[{"name":"addr","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"addr","type":"address"},{"indexed":false,"name":"name","type":"string"},{"indexed":false,"name":"symbol","type":"string"},{"indexed":false,"name":"decimals","type":"uint8"},{"indexed":false,"name":"totalSupply","type":"uint256"},{"indexed":false,"name":"firstHolder","type":"address"}],"name":"TokenCreated","type":"event"}] -------------------------------------------------------------------------------- /ABI/version15/Migrations.abi: -------------------------------------------------------------------------------- 1 | [{"constant":false,"inputs":[{"name":"newAddress","type":"address"}],"name":"upgrade","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"last_completed_migration","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"completed","type":"uint256"}],"name":"setCompleted","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"}] -------------------------------------------------------------------------------- /ABI/version151/Migrations.abi: -------------------------------------------------------------------------------- 1 | [{"constant":false,"inputs":[{"name":"newAddress","type":"address"}],"name":"upgrade","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"last_completed_migration","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"completed","type":"uint256"}],"name":"setCompleted","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"}] -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | ## Design 2 | 3 | Please check out our [whitepaper](https://github.com/Loopring/whitepaper/raw/master/en_whitepaper.pdf) to understand the design and features of the Loopring Protocol. 4 | 5 | ## Optimizations 6 | 7 | We have performed several smart contract gas usage optimization with very good results: 8 | 9 | - [gas_consume_report_1025](gas_consume_report_1025.md) 10 | - [gas_consume_report_1026](gas_consume_report_1026.md) 11 | - [gas_consume_report_1108](gas_consume_report_1108.md) 12 | 13 | ## Deployments 14 | 15 | Please visit [this](https://loopring.github.io/docs/) site for various versions of the protocol deployed on top of Ethereum mainnet and testnets, as well as ERC20 tokens that can be traded with Loopring. 16 | -------------------------------------------------------------------------------- /ABI/version14/Claimable.abi: -------------------------------------------------------------------------------- 1 | [{"constant":false,"inputs":[],"name":"claimOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"pendingOwner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"previousOwner","type":"address"},{"indexed":true,"name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"}] -------------------------------------------------------------------------------- /ABI/version15/Claimable.abi: -------------------------------------------------------------------------------- 1 | [{"constant":false,"inputs":[],"name":"claimOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"pendingOwner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"previousOwner","type":"address"},{"indexed":true,"name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"}] -------------------------------------------------------------------------------- /ABI/version151/Claimable.abi: -------------------------------------------------------------------------------- 1 | [{"constant":false,"inputs":[],"name":"claimOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"pendingOwner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"previousOwner","type":"address"},{"indexed":true,"name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"}] -------------------------------------------------------------------------------- /bounty/2_testOptimization2.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | 4 | ## This script should run from the protocol root directory. 5 | 6 | kill $(ps aux | grep '[t]estrpc' | awk '{print $2}') 7 | LATEST_COMMIT=`git log | head -n 1 | awk '{print $2}'` 8 | 9 | ## Before optimization #x PR 10 | echo 11 | git reset --hard 8f279792062d9dde1ef6dc0e2f9167e40c96a72e 12 | npm uninstall &>/dev/null 13 | npm install &>/dev/null 14 | npm run testrpc &>/dev/null & 15 | echo "Before optimization" 16 | npm run test | grep cumulativeGasUsed 17 | 18 | ## After optimization#x PR 19 | echo 20 | git reset --hard 618d8c49cfcf52be6a1c418c8bc9b603c59e73f7 21 | echo "After optimization" 22 | npm run test | grep cumulativeGasUsed 23 | 24 | kill $(ps aux | grep '[t]estrpc' | awk '{print $2}') 25 | 26 | ## reset to latest commit 27 | echo 28 | git reset --hard $LATEST_COMMIT -------------------------------------------------------------------------------- /ganache.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | ganache-cli --networkId 100 -u 0x249acd967f6eb5b8907e5c888cbd8a005d0b23f4 \ 4 | --account="0xeeef92b67945beb696c5d141fb777d0d706a14d3217734cb5df0db754979120c, 100000000000000000000" \ 5 | --account="0x780037896f0891260da668e59813a26e94e8cb1c85e9559437a702987b410daa, 100000000000000000000" \ 6 | --account="0x768f9ec6fc1983de15fa2104366e539254082b76cda9abe59dd36eda1f1f6911, 100000000000000000000" \ 7 | --account="0x957adcddfb5fc683254a665f0e6decc425ce791920d98bb64747ef2c84ce9b83, 100000000000000000000" \ 8 | --account="0xf5e2dda85d62b4133f127b6d6daa2b371286ce36baefbe14c7c22a56d40ef221, 100000000000000000000" \ 9 | --account="0xfc1cae6e64116725dfd947bc65b476ee46b59c997f47d0bb7ad88276ce853889, 100000000000000000000" \ 10 | --account="0x5e48724e02869a78df83177dba8e0711931a3f04acb332fd7ddaa78800b90bf2, 100000000000000000000" 11 | -------------------------------------------------------------------------------- /ABI/version151/TokenFactoryImpl.abi: -------------------------------------------------------------------------------- 1 | [{"constant":true,"inputs":[{"name":"","type":"bytes10"}],"name":"tokens","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"name","type":"string"},{"name":"symbol","type":"string"},{"name":"decimals","type":"uint8"},{"name":"totalSupply","type":"uint256"}],"name":"createToken","outputs":[{"name":"addr","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":true,"name":"addr","type":"address"},{"indexed":false,"name":"name","type":"string"},{"indexed":false,"name":"symbol","type":"string"},{"indexed":false,"name":"decimals","type":"uint8"},{"indexed":false,"name":"totalSupply","type":"uint256"},{"indexed":false,"name":"firstHolder","type":"address"}],"name":"TokenCreated","type":"event"}] -------------------------------------------------------------------------------- /ABI/version14/ERC20.abi: -------------------------------------------------------------------------------- 1 | [{"constant":false,"inputs":[{"name":"spender","type":"address"},{"name":"value","type":"uint256"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"from","type":"address"},{"name":"to","type":"address"},{"name":"value","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"who","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"to","type":"address"},{"name":"value","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"owner","type":"address"},{"name":"spender","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"}] -------------------------------------------------------------------------------- /ABI/version15/ERC20.abi: -------------------------------------------------------------------------------- 1 | [{"constant":false,"inputs":[{"name":"spender","type":"address"},{"name":"value","type":"uint256"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"from","type":"address"},{"name":"to","type":"address"},{"name":"value","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"who","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"to","type":"address"},{"name":"value","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"owner","type":"address"},{"name":"spender","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"}] -------------------------------------------------------------------------------- /ABI/version151/ERC20.abi: -------------------------------------------------------------------------------- 1 | [{"constant":false,"inputs":[{"name":"spender","type":"address"},{"name":"value","type":"uint256"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"from","type":"address"},{"name":"to","type":"address"},{"name":"value","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"who","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"to","type":"address"},{"name":"value","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"owner","type":"address"},{"name":"spender","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"}] -------------------------------------------------------------------------------- /truffle.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | solc: { 3 | optimizer: { 4 | enabled: true, 5 | runs: 10000 6 | } 7 | }, 8 | networks: { 9 | live: { 10 | host: "localhost", 11 | port: 8546, 12 | network_id: '1', // main-net 13 | gasPrice: 5000000000 14 | }, 15 | testnet: { 16 | host: "localhost", 17 | port: 8545, 18 | network_id: '2', // main-net 19 | gasPrice: 21000000000 20 | }, 21 | priv: { 22 | host: "localhost", 23 | port: 8545, 24 | network_id: '50', // main-net 25 | gasPrice: 5000000000, 26 | gas: 4500000 27 | }, 28 | development: { 29 | host: "localhost", 30 | port: 8545, 31 | network_id: "*", // Match any network id 32 | gasPrice: 21000000000, 33 | gas: 4500000 34 | }, 35 | docker: { 36 | host: "testrpc", 37 | port: 8545, 38 | network_id: "*", // Match any network id 39 | gasPrice: 21000000000, 40 | gas: 4500000 41 | } 42 | }, 43 | test_directory: 'transpiled/test', 44 | migrations_directory: 'transpiled/migrations', 45 | }; 46 | -------------------------------------------------------------------------------- /migrations/3_deploy_tokens.js: -------------------------------------------------------------------------------- 1 | var tokenInfo = require("./config/tokens.js"); 2 | var Bluebird = require("bluebird"); 3 | var _ = require("lodash"); 4 | var DummyToken = artifacts.require("./test/DummyToken"); 5 | var TokenRegistry = artifacts.require("./TokenRegistryImpl"); 6 | 7 | module.exports = function(deployer, network, accounts) { 8 | if (network === "live") { 9 | // ignore 10 | } else { 11 | var devTokenInfos = tokenInfo.development; 12 | var totalSupply = 1e+26; 13 | deployer.then(() => { 14 | return TokenRegistry.deployed(); 15 | }).then((tokenRegistry) => { 16 | return Bluebird.each(devTokenInfos.map(token => DummyToken.new( 17 | token.name, 18 | token.symbol, 19 | token.decimals, 20 | totalSupply, 21 | )), _.noop).then(dummyTokens => { 22 | return Bluebird.each(dummyTokens.map((tokenContract, i) => { 23 | var token = devTokenInfos[i]; 24 | return tokenRegistry.registerToken(tokenContract.address, token.symbol); 25 | }), _.noop); 26 | }); 27 | 28 | }); 29 | } 30 | 31 | }; 32 | -------------------------------------------------------------------------------- /contracts/lib/MathUint8.sol: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright 2017 Loopring Project Ltd (Loopring Foundation). 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | pragma solidity 0.4.21; 18 | 19 | 20 | /// @title Utility Functions for uint8 21 | /// @author Kongliang Zhong - , 22 | /// @author Daniel Wang - . 23 | library MathUint8 { 24 | function xorReduce( 25 | uint8[] arr, 26 | uint len 27 | ) 28 | internal 29 | pure 30 | returns (uint8 res) 31 | { 32 | res = arr[0]; 33 | for (uint i = 1; i < len; i++) { 34 | res ^= arr[i]; 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /contracts/lib/AddressUtil.sol: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright 2017 Loopring Project Ltd (Loopring Foundation). 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | pragma solidity 0.4.21; 18 | 19 | 20 | /// @title Utility Functions for address 21 | /// @author Daniel Wang - 22 | library AddressUtil { 23 | function isContract( 24 | address addr 25 | ) 26 | internal 27 | view 28 | returns (bool) 29 | { 30 | if (addr == 0x0) { 31 | return false; 32 | } else { 33 | uint size; 34 | assembly { size := extcodesize(addr) } 35 | return size > 0; 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /contracts/lib/MathBytes32.sol: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright 2017 Loopring Project Ltd (Loopring Foundation). 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | pragma solidity 0.4.21; 18 | 19 | 20 | /// @title Utility Functions for byte32 21 | /// @author Kongliang Zhong - , 22 | /// @author Daniel Wang - . 23 | library MathBytes32 { 24 | 25 | function xorReduce( 26 | bytes32[] arr, 27 | uint len 28 | ) 29 | internal 30 | pure 31 | returns (bytes32 res) 32 | { 33 | res = arr[0]; 34 | for (uint i = 1; i < len; i++) { 35 | res ^= arr[i]; 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /util/bitstream.ts: -------------------------------------------------------------------------------- 1 | import { BigNumber } from "bignumber.js"; 2 | import Web3 = require("web3"); 3 | 4 | export class Bitstream { 5 | private data: string; 6 | 7 | constructor() { 8 | this.data = ""; 9 | } 10 | 11 | public getData() { 12 | if (this.data.length === 0) { 13 | return "0x0"; 14 | } else { 15 | return "0x" + this.data; 16 | } 17 | } 18 | 19 | public addBigNumber(x: BigNumber, numBytes = 32) { 20 | this.data += this.padString(web3.toHex(x).slice(2), numBytes * 2); 21 | } 22 | 23 | public addNumber(x: number, numBytes = 4) { 24 | this.addBigNumber(new BigNumber(x), numBytes); 25 | } 26 | 27 | public addAddress(x: string, numBytes = 20) { 28 | this.data += this.padString(x.slice(2), numBytes * 2); 29 | } 30 | 31 | public addHex(x: string) { 32 | if (x.startsWith("0x")) { 33 | this.data += x.slice(2); 34 | } else { 35 | this.data += x; 36 | } 37 | } 38 | 39 | private padString(x: string, targetLength: number) { 40 | if (x.length > targetLength) { 41 | throw Error("0x" + x + " is too big to fit in the requested length (" + targetLength + ")"); 42 | } 43 | while (x.length < targetLength) { 44 | x = "0" + x; 45 | } 46 | return x; 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /ABI/version10/TransferableMultsig.abi: -------------------------------------------------------------------------------- 1 | [{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"owners","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"threshold","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"nonce","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"sigV","type":"uint8[]"},{"name":"sigR","type":"bytes32[]"},{"name":"sigS","type":"bytes32[]"},{"name":"_threshold","type":"uint256"},{"name":"_owners","type":"address[]"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"sigV","type":"uint8[]"},{"name":"sigR","type":"bytes32[]"},{"name":"sigS","type":"bytes32[]"},{"name":"destination","type":"address"},{"name":"value","type":"uint256"},{"name":"data","type":"bytes"}],"name":"execute","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"_threshold","type":"uint256"},{"name":"_owners","type":"address[]"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"payable":true,"stateMutability":"payable","type":"fallback"}] -------------------------------------------------------------------------------- /ABI/version12/TransferableMultsig.abi: -------------------------------------------------------------------------------- 1 | [{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"owners","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"threshold","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"nonce","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"sigV","type":"uint8[]"},{"name":"sigR","type":"bytes32[]"},{"name":"sigS","type":"bytes32[]"},{"name":"_threshold","type":"uint256"},{"name":"_owners","type":"address[]"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"sigV","type":"uint8[]"},{"name":"sigR","type":"bytes32[]"},{"name":"sigS","type":"bytes32[]"},{"name":"destination","type":"address"},{"name":"value","type":"uint256"},{"name":"data","type":"bytes"}],"name":"execute","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"inputs":[{"name":"_threshold","type":"uint256"},{"name":"_owners","type":"address[]"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"payable":true,"stateMutability":"payable","type":"fallback"}] -------------------------------------------------------------------------------- /ABI/version15/TokenFactoryImpl.abi: -------------------------------------------------------------------------------- 1 | [{"constant":true,"inputs":[{"name":"","type":"bytes10"}],"name":"tokens","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"name","type":"string"},{"name":"symbol","type":"string"},{"name":"decimals","type":"uint8"},{"name":"totalSupply","type":"uint256"}],"name":"createToken","outputs":[{"name":"addr","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"tokenRegistry","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"tokenTransferDelegate","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"_tokenRegistry","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":true,"name":"addr","type":"address"},{"indexed":false,"name":"name","type":"string"},{"indexed":false,"name":"symbol","type":"string"},{"indexed":false,"name":"decimals","type":"uint8"},{"indexed":false,"name":"totalSupply","type":"uint256"},{"indexed":false,"name":"firstHolder","type":"address"}],"name":"TokenCreated","type":"event"}] -------------------------------------------------------------------------------- /ABI/version15/TransferableMultsigImpl.abi: -------------------------------------------------------------------------------- 1 | [{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"owners","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"threshold","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_threshold","type":"uint256"},{"name":"_owners","type":"address[]"}],"name":"TransferableMultsig","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"nonce","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"sigV","type":"uint8[]"},{"name":"sigR","type":"bytes32[]"},{"name":"sigS","type":"bytes32[]"},{"name":"_threshold","type":"uint256"},{"name":"_owners","type":"address[]"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"sigV","type":"uint8[]"},{"name":"sigR","type":"bytes32[]"},{"name":"sigS","type":"bytes32[]"},{"name":"destination","type":"address"},{"name":"value","type":"uint256"},{"name":"data","type":"bytes"}],"name":"execute","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"payable":true,"stateMutability":"payable","type":"fallback"}] -------------------------------------------------------------------------------- /ABI/version151/TransferableMultsigImpl.abi: -------------------------------------------------------------------------------- 1 | [{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"owners","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"threshold","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_threshold","type":"uint256"},{"name":"_owners","type":"address[]"}],"name":"TransferableMultsig","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"nonce","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"sigV","type":"uint8[]"},{"name":"sigR","type":"bytes32[]"},{"name":"sigS","type":"bytes32[]"},{"name":"_threshold","type":"uint256"},{"name":"_owners","type":"address[]"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"sigV","type":"uint8[]"},{"name":"sigR","type":"bytes32[]"},{"name":"sigS","type":"bytes32[]"},{"name":"destination","type":"address"},{"name":"value","type":"uint256"},{"name":"data","type":"bytes"}],"name":"execute","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"payable":true,"stateMutability":"payable","type":"fallback"}] -------------------------------------------------------------------------------- /migrations/4_deploy_protocol.js: -------------------------------------------------------------------------------- 1 | var TokenRegistry = artifacts.require("./TokenRegistryImpl"); 2 | var TokenTransferDelegate = artifacts.require("./TokenTransferDelegateImpl"); 3 | var LoopringProtocolImpl = artifacts.require("./LoopringProtocolImpl"); 4 | 5 | module.exports = function(deployer, network, accounts) { 6 | 7 | if (network === "live") { 8 | deployer.then(() => { 9 | return Promise.all([ 10 | TokenRegistry.deployed(), 11 | TokenTransferDelegate.deployed(), 12 | ]); 13 | }).then((contracts) => { 14 | var lrcAddr = "0xEF68e7C694F40c8202821eDF525dE3782458639f"; 15 | return deployer.deploy( 16 | LoopringProtocolImpl, 17 | lrcAddr, 18 | TokenRegistry.address, 19 | TokenTransferDelegate.address, 20 | 62500, 21 | 20); 22 | }); 23 | } else { 24 | deployer.then(() => { 25 | return Promise.all([ 26 | TokenRegistry.deployed(), 27 | TokenTransferDelegate.deployed(), 28 | ]); 29 | }).then((contracts) => { 30 | var [tokenRegistry] = contracts; 31 | return tokenRegistry.getAddressBySymbol("LRC"); 32 | }).then(lrcAddr => { 33 | return deployer.deploy( 34 | LoopringProtocolImpl, 35 | lrcAddr, 36 | TokenRegistry.address, 37 | TokenTransferDelegate.address, 38 | 62500, 39 | 20); 40 | }); 41 | 42 | } 43 | }; 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Loopring Protocol Smart Contracts 2 | 3 | ## Compile 4 | 5 | 6 | If you are using Windows: 7 | ``` 8 | npm install --global --production windows-build-tools 9 | ``` 10 | 11 | Then run the following commands from project's root directory: 12 | 13 | ``` 14 | npm install 15 | npm run compile 16 | ``` 17 | 18 | ## Deployment on Ethereum Mainnet: 19 | - LRC - https://etherscan.io/address/0xEF68e7C694F40c8202821eDF525dE3782458639f 20 | - Protocol deployments: https://github.com/Loopring/docs/blob/master/docs/deployment/ethereum/protocol-deployment.md#v151-livemainnet 21 | 22 | ## Run Unit Tests 23 | * run `npm run testrpc` from project's root directory in terminal. 24 | * run `npm run test` from project's root directory in another terminal window. 25 | * run single test: `npm run test -- transpiled/test/xxx.js` 26 | 27 | ## Run Unit Tests inside Docker 28 | 29 | If you prefer to use docker, you can install docker first, then run the following: 30 | 31 | ``` 32 | npm run docker 33 | ``` 34 | 35 | If you do not have node/npm installed but still wish to use docker, you can run the commands manually: 36 | 37 | ``` 38 | docker-compose up --build --abort-on-container-exit 39 | docker-compose logs -f test 40 | ``` 41 | 42 | The logs command is optional but will give you an easy to read output of the tests without the output from testrpc mixed in (though the combination of both is good for debugging and is why they're not being silenced.) 43 | -------------------------------------------------------------------------------- /contracts/Migrations.sol: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright 2017 Loopring Project Ltd (Loopring Foundation). 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | pragma solidity 0.4.21; 18 | 19 | 20 | contract Migrations { 21 | address public owner; 22 | uint public last_completed_migration; 23 | 24 | modifier restricted() 25 | { 26 | if (msg.sender == owner) { 27 | _; 28 | } 29 | } 30 | 31 | function Migrations() 32 | public 33 | { 34 | owner = msg.sender; 35 | } 36 | 37 | function setCompleted(uint completed) 38 | restricted 39 | public 40 | { 41 | last_completed_migration = completed; 42 | } 43 | 44 | function upgrade(address newAddress) 45 | restricted 46 | public 47 | { 48 | Migrations upgraded = Migrations(newAddress); 49 | upgraded.setCompleted(last_completed_migration); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /docs/gas_consume_report_1025.md: -------------------------------------------------------------------------------- 1 | # Gas Usage at Oct-25-2017 2 | 3 | The following measurements usge 21Gwei as gas price. 4 | 5 | ## A Ring of 3 Orders: 6 | 7 | | Function | ETH | 8 | | ------ | ------ | 9 | | code before verifyInputDataIntegrity | 0.001742391000006656 | 10 | | Verifyinputdataintegrity | 0.002311238999998464 | 11 | | verifyTokensRegistered | 0.00281586900000768 | 12 | | calculateRinghash | 0.003747051000004608 | 13 | | check ringhashRegistry.canSubmit | 0.003839093999992832 | 14 | | verifySignature for ring | 0.003971372999999488 | 15 | | assembleOrders | 0.006713952 | 16 | | handleRing-verifyRingHasNoSubRing | 0.006886088999993344 | 17 | | handleRing-verifyMinerSuppliedFillRates | 0.007175867999993856 | 18 | | handleRing-scaleRingBasedOnHistoricalRecords | 0.007673106000003072 | 19 | | handleRing-calculateRingFillAmount | 0.00777247800000512 | 20 | | handleRing-calculateRingFees | 0.008277926999998464 | 21 | | handleRing-settleRing | 0.013680218999996416 | 22 | | RingMined event | 0.013910337000013824 | 23 | | total | 0.013910337000013824 | 24 | 25 | -------------------------------------------------------------------------------- /ABI/version14/TokenFactory.abi: -------------------------------------------------------------------------------- 1 | [{"constant":false,"inputs":[{"name":"name","type":"string"},{"name":"symbol","type":"string"},{"name":"decimals","type":"uint8"},{"name":"totalSupply","type":"uint256"}],"name":"createToken","outputs":[{"name":"addr","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_tokenRegistry","type":"address"},{"name":"_tokenTransferDelegate","type":"address"}],"name":"initialize","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"tokens","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"tokenRegistry","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"tokenTransferDelegate","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":true,"name":"addr","type":"address"},{"indexed":false,"name":"name","type":"string"},{"indexed":false,"name":"symbol","type":"string"},{"indexed":false,"name":"decimals","type":"uint8"},{"indexed":false,"name":"totalSupply","type":"uint256"},{"indexed":false,"name":"firstHolder","type":"address"},{"indexed":false,"name":"tokenTransferDelegate","type":"address"}],"name":"TokenCreated","type":"event"}] -------------------------------------------------------------------------------- /contracts/TransferableMultsig.sol: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright 2017 Loopring Project Ltd (Loopring Foundation). 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | pragma solidity 0.4.21; 18 | 19 | 20 | /// @title Transferable Multisignature Contract 21 | /// @author Daniel Wang - . 22 | contract TransferableMultsig { 23 | // Note that address recovered from signatures must be strictly increasing. 24 | function execute( 25 | uint8[] sigV, 26 | bytes32[] sigR, 27 | bytes32[] sigS, 28 | address destination, 29 | uint value, 30 | bytes data 31 | ) 32 | external; 33 | 34 | // Note that address recovered from signatures must be strictly increasing. 35 | function transferOwnership( 36 | uint8[] sigV, 37 | bytes32[] sigR, 38 | bytes32[] sigS, 39 | uint _threshold, 40 | address[] _owners 41 | ) 42 | external; 43 | } 44 | -------------------------------------------------------------------------------- /contracts/lib/StringUtil.sol: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright 2017 Loopring Project Ltd (Loopring Foundation). 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | pragma solidity 0.4.21; 18 | 19 | 20 | /// @title Utility Functions for address 21 | /// @author Kongliang Zhong - 22 | library StringUtil { 23 | function stringToBytes12(string str) 24 | internal 25 | pure 26 | returns (bytes12 result) 27 | { 28 | assembly { 29 | result := mload(add(str, 32)) 30 | } 31 | } 32 | 33 | function stringToBytes10(string str) 34 | internal 35 | pure 36 | returns (bytes10 result) 37 | { 38 | assembly { 39 | result := mload(add(str, 32)) 40 | } 41 | } 42 | 43 | /// check length >= min && <= max 44 | function checkStringLength(string name, uint min, uint max) 45 | internal 46 | pure 47 | returns (bool) 48 | { 49 | bytes memory temp = bytes(name); 50 | return temp.length >= min && temp.length <= max; 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /contracts/lib/ERC20.sol: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright 2017 Loopring Project Ltd (Loopring Foundation). 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | pragma solidity 0.4.21; 18 | 19 | 20 | /// @title ERC20 Token Interface 21 | /// @dev see https://github.com/ethereum/EIPs/issues/20 22 | /// @author Daniel Wang - 23 | contract ERC20 { 24 | function balanceOf( 25 | address who 26 | ) 27 | view 28 | public 29 | returns (uint256); 30 | 31 | function allowance( 32 | address owner, 33 | address spender 34 | ) 35 | view 36 | public 37 | returns (uint256); 38 | 39 | function transfer( 40 | address to, 41 | uint256 value 42 | ) 43 | public 44 | returns (bool); 45 | 46 | function transferFrom( 47 | address from, 48 | address to, 49 | uint256 value 50 | ) 51 | public 52 | returns (bool); 53 | 54 | function approve( 55 | address spender, 56 | uint256 value 57 | ) 58 | public 59 | returns (bool); 60 | } 61 | -------------------------------------------------------------------------------- /ABI/version151/TokenRegistry.abi: -------------------------------------------------------------------------------- 1 | [{"constant":false,"inputs":[{"name":"addr","type":"address"},{"name":"symbol","type":"string"}],"name":"unregisterToken","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"symbol","type":"string"}],"name":"getAddressBySymbol","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"addressList","type":"address[]"}],"name":"areAllTokensRegistered","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"addr","type":"address"}],"name":"isTokenRegistered","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"start","type":"uint256"},{"name":"count","type":"uint256"}],"name":"getTokens","outputs":[{"name":"addressList","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"addr","type":"address"},{"name":"symbol","type":"string"}],"name":"registerToken","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"symbol","type":"string"}],"name":"isTokenRegisteredBySymbol","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"addr","type":"address"},{"indexed":false,"name":"symbol","type":"string"}],"name":"TokenRegistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"addr","type":"address"},{"indexed":false,"name":"symbol","type":"string"}],"name":"TokenUnregistered","type":"event"}] -------------------------------------------------------------------------------- /ABI/version10/RinghashRegistry.abi: -------------------------------------------------------------------------------- 1 | [{"constant":false,"inputs":[{"name":"ringminerList","type":"address[]"},{"name":"ringhashList","type":"bytes32[]"}],"name":"batchSubmitRinghash","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"ringSize","type":"uint256"},{"name":"ringminer","type":"address"},{"name":"vList","type":"uint8[]"},{"name":"rList","type":"bytes32[]"},{"name":"sList","type":"bytes32[]"}],"name":"computeAndGetRinghashInfo","outputs":[{"name":"ringhash","type":"bytes32"},{"name":"attributes","type":"bool[2]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"ringhash","type":"bytes32"},{"name":"ringminer","type":"address"}],"name":"isReserved","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"ringminer","type":"address"},{"name":"ringhash","type":"bytes32"}],"name":"submitRinghash","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"blocksToLive","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"ringhash","type":"bytes32"},{"name":"ringminer","type":"address"}],"name":"canSubmit","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"_blocksToLive","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_ringminer","type":"address"},{"indexed":true,"name":"_ringhash","type":"bytes32"}],"name":"RinghashSubmitted","type":"event"}] -------------------------------------------------------------------------------- /contracts/TokenFactory.sol: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright 2017 Loopring Project Ltd (Loopring Foundation). 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | pragma solidity 0.4.21; 18 | 19 | 20 | /// @title ERC20 Token Mint 21 | /// @dev This contract deploys ERC20 token contract and registered the contract 22 | /// so the token can be traded with Loopring Protocol. 23 | /// @author Kongliang Zhong - , 24 | /// @author Daniel Wang - . 25 | contract TokenFactory { 26 | event TokenCreated( 27 | address indexed addr, 28 | string name, 29 | string symbol, 30 | uint8 decimals, 31 | uint totalSupply, 32 | address firstHolder 33 | ); 34 | 35 | /// @dev Deploy an ERC20 token contract, register it with TokenRegistry, 36 | /// and returns the new token's address. 37 | /// @param name The name of the token 38 | /// @param symbol The symbol of the token. 39 | /// @param decimals The decimals of the token. 40 | /// @param totalSupply The total supply of the token. 41 | function createToken( 42 | string name, 43 | string symbol, 44 | uint8 decimals, 45 | uint totalSupply 46 | ) 47 | external 48 | returns (address addr); 49 | } 50 | -------------------------------------------------------------------------------- /ABI/version15/TokenRegistry.abi: -------------------------------------------------------------------------------- 1 | [{"constant":false,"inputs":[{"name":"addr","type":"address"},{"name":"symbol","type":"string"}],"name":"unregisterToken","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"addr","type":"address"},{"name":"symbol","type":"string"}],"name":"registerMintedToken","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"symbol","type":"string"}],"name":"getAddressBySymbol","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"addressList","type":"address[]"}],"name":"areAllTokensRegistered","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"addr","type":"address"}],"name":"isTokenRegistered","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"start","type":"uint256"},{"name":"count","type":"uint256"}],"name":"getTokens","outputs":[{"name":"addressList","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"addr","type":"address"},{"name":"symbol","type":"string"}],"name":"registerToken","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"symbol","type":"string"}],"name":"isTokenRegisteredBySymbol","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"addr","type":"address"},{"indexed":false,"name":"symbol","type":"string"}],"name":"TokenRegistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"addr","type":"address"},{"indexed":false,"name":"symbol","type":"string"}],"name":"TokenUnregistered","type":"event"}] -------------------------------------------------------------------------------- /contracts/lib/Claimable.sol: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright 2017 Loopring Project Ltd (Loopring Foundation). 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | pragma solidity 0.4.21; 18 | 19 | import "./Ownable.sol"; 20 | 21 | 22 | /// @title Claimable 23 | /// @dev Extension for the Ownable contract, where the ownership needs 24 | /// to be claimed. This allows the new owner to accept the transfer. 25 | contract Claimable is Ownable { 26 | address public pendingOwner; 27 | 28 | /// @dev Modifier throws if called by any account other than the pendingOwner. 29 | modifier onlyPendingOwner() { 30 | require(msg.sender == pendingOwner); 31 | _; 32 | } 33 | 34 | /// @dev Allows the current owner to set the pendingOwner address. 35 | /// @param newOwner The address to transfer ownership to. 36 | function transferOwnership( 37 | address newOwner 38 | ) 39 | onlyOwner 40 | public 41 | { 42 | require(newOwner != 0x0 && newOwner != owner); 43 | pendingOwner = newOwner; 44 | } 45 | 46 | /// @dev Allows the pendingOwner address to finalize the transfer. 47 | function claimOwnership() 48 | onlyPendingOwner 49 | public 50 | { 51 | emit OwnershipTransferred(owner, pendingOwner); 52 | owner = pendingOwner; 53 | pendingOwner = 0x0; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /contracts/lib/MemoryUtil.sol: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright 2017 Loopring Project Ltd (Loopring Foundation). 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | pragma solidity 0.4.21; 18 | 19 | 20 | /// @title Utility Functions for memory related operations 21 | /// @author Brechtpd https://github.com/Brechtpd 22 | library MemoryUtil { 23 | 24 | function getBytes32Pointer( 25 | bytes32[] arr, 26 | uint offsetInWords 27 | ) 28 | internal 29 | pure 30 | returns (uint ptr) 31 | { 32 | assembly { 33 | ptr := add(add(arr, 32), mul(offsetInWords, 32)) 34 | } 35 | } 36 | 37 | function copyCallDataBytesInArray( 38 | uint parameterIndex, 39 | uint dst, 40 | uint offsetInBytes, 41 | uint numBytes 42 | ) 43 | internal 44 | pure 45 | { 46 | assembly { 47 | let parameterOffset := add(calldataload(add(4, mul(parameterIndex, 32))), 4) 48 | calldatacopy(dst, add(add(parameterOffset, 32), offsetInBytes), numBytes) 49 | } 50 | } 51 | 52 | function bytesToUint( 53 | bytes b, 54 | uint offset 55 | ) 56 | internal 57 | pure 58 | returns (uint data) 59 | { 60 | assembly { 61 | data := mload(add(add(b, 0x20), offset)) 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /contracts/lib/Ownable.sol: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright 2017 Loopring Project Ltd (Loopring Foundation). 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | pragma solidity 0.4.21; 18 | 19 | 20 | /// @title Ownable 21 | /// @dev The Ownable contract has an owner address, and provides basic 22 | /// authorization control functions, this simplifies the implementation of 23 | /// "user permissions". 24 | contract Ownable { 25 | address public owner; 26 | 27 | event OwnershipTransferred( 28 | address indexed previousOwner, 29 | address indexed newOwner 30 | ); 31 | 32 | /// @dev The Ownable constructor sets the original `owner` of the contract 33 | /// to the sender. 34 | function Ownable() 35 | public 36 | { 37 | owner = msg.sender; 38 | } 39 | 40 | /// @dev Throws if called by any account other than the owner. 41 | modifier onlyOwner() 42 | { 43 | require(msg.sender == owner); 44 | _; 45 | } 46 | 47 | /// @dev Allows the current owner to transfer control of the contract to a 48 | /// newOwner. 49 | /// @param newOwner The address to transfer ownership to. 50 | function transferOwnership( 51 | address newOwner 52 | ) 53 | onlyOwner 54 | public 55 | { 56 | require(newOwner != 0x0); 57 | emit OwnershipTransferred(owner, newOwner); 58 | owner = newOwner; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /contracts/test/DummyToken.sol: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright 2017 Loopring Project Ltd (Loopring Foundation). 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | pragma solidity 0.4.21; 18 | 19 | import "../lib/ERC20Token.sol"; 20 | import "../lib/MathUint.sol"; 21 | 22 | 23 | /// @author Kongliang Zhong - 24 | contract DummyToken is ERC20Token { 25 | using MathUint for uint; 26 | 27 | /// constructor. 28 | function DummyToken( 29 | string _name, 30 | string _symbol, 31 | uint8 _decimals, 32 | uint _totalSupply 33 | ) ERC20Token( 34 | _name, 35 | _symbol, 36 | _decimals, 37 | _totalSupply, 38 | msg.sender 39 | ) 40 | public 41 | { 42 | } 43 | 44 | function setBalance( 45 | address _target, 46 | uint _value 47 | ) 48 | public 49 | { 50 | uint currBalance = balanceOf(_target); 51 | if (_value < currBalance) { 52 | totalSupply_ = totalSupply_.sub(currBalance.sub(_value)); 53 | } else { 54 | totalSupply_ = totalSupply_.add(_value.sub(currBalance)); 55 | } 56 | balances[_target] = _value; 57 | } 58 | 59 | function addBalance( 60 | address _target, 61 | uint _value 62 | ) 63 | public 64 | { 65 | uint currBalance = balanceOf(_target); 66 | totalSupply_ = totalSupply_.add(_value); 67 | balances[_target] = currBalance.add(_value); 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /ABI/version151/LoopringProtocol.abi: -------------------------------------------------------------------------------- 1 | [{"constant":true,"inputs":[],"name":"MARGIN_SPLIT_PERCENTAGE_BASE","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"token1","type":"address"},{"name":"token2","type":"address"},{"name":"cutoff","type":"uint256"}],"name":"cancelAllOrdersByTradingPair","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"addresses","type":"address[5]"},{"name":"orderValues","type":"uint256[6]"},{"name":"buyNoMoreThanAmountB","type":"bool"},{"name":"marginSplitPercentage","type":"uint8"},{"name":"v","type":"uint8"},{"name":"r","type":"bytes32"},{"name":"s","type":"bytes32"}],"name":"cancelOrder","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"cutoff","type":"uint256"}],"name":"cancelAllOrders","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"data","type":"bytes"}],"name":"submitRing","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_ringIndex","type":"uint256"},{"indexed":true,"name":"_ringHash","type":"bytes32"},{"indexed":false,"name":"_miner","type":"address"},{"indexed":false,"name":"_feeRecipient","type":"address"},{"indexed":false,"name":"_orderInfoList","type":"bytes32[]"}],"name":"RingMined","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_orderHash","type":"bytes32"},{"indexed":false,"name":"_amountCancelled","type":"uint256"}],"name":"OrderCancelled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_address","type":"address"},{"indexed":false,"name":"_cutoff","type":"uint256"}],"name":"AllOrdersCancelled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_address","type":"address"},{"indexed":false,"name":"_token1","type":"address"},{"indexed":false,"name":"_token2","type":"address"},{"indexed":false,"name":"_cutoff","type":"uint256"}],"name":"OrdersCancelled","type":"event"}] 2 | -------------------------------------------------------------------------------- /docs/gas_consume_report_1108.md: -------------------------------------------------------------------------------- 1 | # Gas Usage at Nov-08-2017 2 | 3 | The following measurements usge 21Gwei as gas price. 4 | 5 | ## A Ring of 2 Orders: 6 | 7 | 8 | | Function or Code Segment | GAS | 9 | | ------ | ------ | 10 | | code before verifyInputDataIntegrity | 82451 | 11 | | Verifyinputdataintegrity | 83140 | 12 | | verifyTokensRegistered | 87117 | 13 | | computeAndGetRinghashInfo | 93385 | 14 | | verifySignature for ring | 97858 | 15 | | assembleOrders | 128653 | 16 | | handleRing-verifyRingHasNoSubRing | 129157 | 17 | | handleRing-verifyMinerSuppliedFillRates | 133143 | 18 | | handleRing-scaleRingBasedOnHistoricalRecords | 136692 | 19 | | handleRing-calculateRingFillAmount | 139947 | 20 | | handleRing-calculateRingFees | 164508 | 21 | | handleRing-settleRing | 366630 | 22 | | RingMined event | 369718 | 23 | | total | 369718 | 24 | 25 | 26 | ## A Ring of 3 Orders: 27 | 28 | | Function or Code Segment | GAS | 29 | | ------ | ------ | 30 | | code before verifyInputDataIntegrity | 63790 | 31 | | Verifyinputdataintegrity | 64769 | 32 | | verifyTokensRegistered | 69499 | 33 | | computeAndGetRinghashInfo | 76334 | 34 | | verifySignature for ring | 80743 | 35 | | assembleOrders | 126341 | 36 | | handleRing-verifyRingHasNoSubRing | 127446 | 37 | | handleRing-verifyMinerSuppliedFillRates | 133036 | 38 | | handleRing-scaleRingBasedOnHistoricalRecords | 137770 | 39 | | handleRing-calculateRingFillAmount | 141900 | 40 | | handleRing-calculateRingFees | 174817 | 41 | | handleRing-settleRing | 409617 | 42 | | RingMined event | 412897 | 43 | | total | 412897 | 44 | 45 | -------------------------------------------------------------------------------- /contracts/TokenFactoryImpl.sol: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright 2017 Loopring Project Ltd (Loopring Foundation). 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | pragma solidity 0.4.21; 18 | 19 | import "./lib/AddressUtil.sol"; 20 | import "./lib/StringUtil.sol"; 21 | import "./lib/ERC20Token.sol"; 22 | import "./TokenFactory.sol"; 23 | 24 | 25 | /// @title An Implementation of TokenFactory. 26 | /// @author Kongliang Zhong - , 27 | /// @author Daniel Wang - . 28 | contract TokenFactoryImpl is TokenFactory { 29 | using AddressUtil for address; 30 | using StringUtil for string; 31 | 32 | mapping(bytes10 => address) public tokens; 33 | 34 | /// @dev Disable default function. 35 | function () 36 | payable 37 | public 38 | { 39 | revert(); 40 | } 41 | 42 | function createToken( 43 | string name, 44 | string symbol, 45 | uint8 decimals, 46 | uint totalSupply 47 | ) 48 | external 49 | returns (address addr) 50 | { 51 | require(symbol.checkStringLength(3, 10)); 52 | 53 | bytes10 symbolBytes = symbol.stringToBytes10(); 54 | require(tokens[symbolBytes] == 0x0); 55 | 56 | ERC20Token token = new ERC20Token( 57 | name, 58 | symbol, 59 | decimals, 60 | totalSupply, 61 | tx.origin 62 | ); 63 | 64 | addr = address(token); 65 | tokens[symbolBytes] = addr; 66 | 67 | emit TokenCreated( 68 | addr, 69 | name, 70 | symbol, 71 | decimals, 72 | totalSupply, 73 | tx.origin 74 | ); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "loopring-smart-contracts", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "directories": { 7 | "test": "test" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git://github.com/Loopring/protocol.git" 12 | }, 13 | "scripts": { 14 | "transpile": "rm -rf ./transpiled; copyfiles ./build/**/* ./transpiled && tsc", 15 | "pretest": "solium --dir contracts && tslint --project .", 16 | "test": "npm run transpile && truffle test", 17 | "docker": "docker-compose up --build --abort-on-container-exit; docker-compose logs -f test", 18 | "testdocker": "npm run transpile && truffle test --network docker", 19 | "compile": "rm -rf build/contracts && truffle compile", 20 | "build": "rm -rf build/contracts && truffle compile", 21 | "migrate": "npm run transpile && truffle migrate", 22 | "deploy": "npm run migrate --network kovan", 23 | "testrpc": "ganache-cli" 24 | }, 25 | "license": "ISC", 26 | "devDependencies": { 27 | "@types/bignumber.js": "^4.0.3", 28 | "@types/bluebird": "^3.5.20", 29 | "@types/lodash": "^4.14.107", 30 | "@types/node": "9.3.0", 31 | "@types/request-promise-native": "^1.0.14", 32 | "ajv": "^6.0.0", 33 | "chai": "^2.2.1", 34 | "copyfiles": "^1.2.0", 35 | "dirty-chai": "^2.0.0", 36 | "ganache-cli": "^6.1.0", 37 | "solium": "^1.1.6", 38 | "truffle": "4.1.5", 39 | "tslint": "5.8.0", 40 | "typescript": "^2.8.3", 41 | "web3-typescript-typings": "^0.10.2", 42 | "webpack": "^4.0.0" 43 | }, 44 | "dependencies": { 45 | "@types/bitwise-xor": "0.0.30", 46 | "bignumber.js": "^4.1.0", 47 | "bitwise-xor": "0.0.0", 48 | "bluebird": "^3.5.1", 49 | "bn.js": "^4.11.8", 50 | "es6-iterator": "^2.0.3", 51 | "es6-map": "^0.1.5", 52 | "es6-promisify": "^5.0.0", 53 | "escape-string-regexp": "^1.0.5", 54 | "estraverse": "^4.2.0", 55 | "esutils": "^2.0.2", 56 | "ethereum-tx-decoder": "^1.0.2", 57 | "ethereumjs-abi": "^0.6.5", 58 | "ethereumjs-util": "^5.1.5", 59 | "event-emitter": "^0.3.5", 60 | "events": "^1.1.1", 61 | "js-sha3": "^0.7.0", 62 | "lodash": "^4.17.5", 63 | "npm": "^5.8.0", 64 | "web3": "0.20.2" 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /contracts/TokenRegistry.sol: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright 2017 Loopring Project Ltd (Loopring Foundation). 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | pragma solidity 0.4.21; 18 | 19 | 20 | /// @title Token Register Contract 21 | /// @dev This contract maintains a list of tokens the Protocol supports. 22 | /// @author Kongliang Zhong - , 23 | /// @author Daniel Wang - . 24 | contract TokenRegistry { 25 | event TokenRegistered( 26 | address indexed addr, 27 | string symbol 28 | ); 29 | 30 | event TokenUnregistered( 31 | address indexed addr, 32 | string symbol 33 | ); 34 | 35 | function registerToken( 36 | address addr, 37 | string symbol 38 | ) 39 | external; 40 | 41 | function unregisterToken( 42 | address addr, 43 | string symbol 44 | ) 45 | external; 46 | 47 | function areAllTokensRegistered( 48 | address[] addressList 49 | ) 50 | external 51 | view 52 | returns (bool); 53 | 54 | function getAddressBySymbol( 55 | string symbol 56 | ) 57 | external 58 | view 59 | returns (address); 60 | 61 | function isTokenRegisteredBySymbol( 62 | string symbol 63 | ) 64 | public 65 | view 66 | returns (bool); 67 | 68 | function isTokenRegistered( 69 | address addr 70 | ) 71 | public 72 | view 73 | returns (bool); 74 | 75 | function getTokens( 76 | uint start, 77 | uint count 78 | ) 79 | public 80 | view 81 | returns (address[] addressList); 82 | } 83 | -------------------------------------------------------------------------------- /ABI/version15/LoopringProtocol.abi: -------------------------------------------------------------------------------- 1 | [{"constant":true,"inputs":[],"name":"MARGIN_SPLIT_PERCENTAGE_BASE","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"token1","type":"address"},{"name":"token2","type":"address"},{"name":"cutoff","type":"uint256"}],"name":"cancelAllOrdersByTradingPair","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"addresses","type":"address[5]"},{"name":"orderValues","type":"uint256[6]"},{"name":"buyNoMoreThanAmountB","type":"bool"},{"name":"marginSplitPercentage","type":"uint8"},{"name":"v","type":"uint8"},{"name":"r","type":"bytes32"},{"name":"s","type":"bytes32"}],"name":"cancelOrder","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"cutoff","type":"uint256"}],"name":"cancelAllOrders","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"addressList","type":"address[4][]"},{"name":"uintArgsList","type":"uint256[6][]"},{"name":"uint8ArgsList","type":"uint8[1][]"},{"name":"buyNoMoreThanAmountBList","type":"bool[]"},{"name":"vList","type":"uint8[]"},{"name":"rList","type":"bytes32[]"},{"name":"sList","type":"bytes32[]"},{"name":"miner","type":"address"},{"name":"feeSelections","type":"uint16"}],"name":"submitRing","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_ringIndex","type":"uint256"},{"indexed":true,"name":"_ringHash","type":"bytes32"},{"indexed":false,"name":"_feeRecipient","type":"address"},{"indexed":false,"name":"_orderHashList","type":"bytes32[]"},{"indexed":false,"name":"_amountsList","type":"uint256[6][]"}],"name":"RingMined","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_orderHash","type":"bytes32"},{"indexed":false,"name":"_amountCancelled","type":"uint256"}],"name":"OrderCancelled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_address","type":"address"},{"indexed":false,"name":"_cutoff","type":"uint256"}],"name":"AllOrdersCancelled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_address","type":"address"},{"indexed":false,"name":"_token1","type":"address"},{"indexed":false,"name":"_token2","type":"address"},{"indexed":false,"name":"_cutoff","type":"uint256"}],"name":"OrdersCancelled","type":"event"}] -------------------------------------------------------------------------------- /util/parseTx.ts: -------------------------------------------------------------------------------- 1 | import BN = require("bn.js"); 2 | import txDecoder = require("ethereum-tx-decoder"); 3 | import abi = require("ethereumjs-abi"); 4 | import {RawTx, RingInfo} from "./types"; 5 | 6 | export class TxParser { 7 | public protocolAbi: string; 8 | 9 | constructor(protocolAbi: string) { 10 | this.protocolAbi = protocolAbi; 11 | } 12 | 13 | public parseSubmitRingTx(txRaw: RawTx) { 14 | const fnDecoder = new txDecoder.FunctionDecoder(this.protocolAbi); 15 | const decodedTx = txDecoder.decodeTx(txRaw.content); 16 | const arrayish = fnDecoder.decodeFn(decodedTx.data); 17 | 18 | const miner = arrayish[7]; 19 | 20 | const orderOwners: string[] = []; 21 | const tokenAddressList: string[] = []; 22 | for (const addrs of arrayish[0]) { 23 | orderOwners.push(addrs[0]); 24 | tokenAddressList.push(addrs[1]); 25 | } 26 | 27 | const amountSList: number[] = []; 28 | const amountBList: number[] = []; 29 | const lrcFeeAmountList: number[] = []; 30 | for (const amounts of arrayish[1]) { 31 | amountSList.push(this.bnToNumber(amounts[0])); 32 | amountBList.push(this.bnToNumber(amounts[1])); 33 | lrcFeeAmountList.push(this.bnToNumber(amounts[4])); 34 | } 35 | 36 | const marginSplitPercentageList = [].concat(...arrayish[2]); // flatten array 37 | const buyNoMoreThanAmountBList = arrayish[3]; 38 | 39 | const feeSelectionNumber = arrayish[8]; 40 | const ringSize = tokenAddressList.length; 41 | const feeSelections = this.feeSelectionNumberToArray(feeSelectionNumber, ringSize); 42 | 43 | const ringInfo: RingInfo = { 44 | amountSList, 45 | amountBList, 46 | lrcFeeAmountList, 47 | miner, 48 | orderOwners, 49 | tokenAddressList, 50 | marginSplitPercentageList, 51 | buyNoMoreThanAmountBList, 52 | feeSelections, 53 | description: "raw tx " + txRaw.id, 54 | id: txRaw.id, 55 | verbose: txRaw.verbose, 56 | }; 57 | 58 | return ringInfo; 59 | } 60 | 61 | private feeSelectionNumberToArray(fsn: number, ringSize: number) { 62 | const feeSelectionList: number[] = []; 63 | for (let i = ringSize - 1; i >= 0; i--) { 64 | const feeSelection = (fsn >> i) % 2; 65 | feeSelectionList.push(feeSelection); 66 | } 67 | return feeSelectionList; 68 | } 69 | 70 | private bnToNumber(bn: any) { 71 | const bnStr = bn.toString(10); 72 | return parseInt(bnStr, 10); 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /ABI/version14/LoopringProtocol.abi: -------------------------------------------------------------------------------- 1 | [{"constant":false,"inputs":[{"name":"addressList","type":"address[3][]"},{"name":"uintArgsList","type":"uint256[7][]"},{"name":"uint8ArgsList","type":"uint8[1][]"},{"name":"buyNoMoreThanAmountBList","type":"bool[]"},{"name":"vList","type":"uint8[]"},{"name":"rList","type":"bytes32[]"},{"name":"sList","type":"bytes32[]"},{"name":"minerId","type":"uint256"},{"name":"feeSelections","type":"uint16"}],"name":"submitRing","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"MARGIN_SPLIT_PERCENTAGE_BASE","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"token1","type":"address"},{"name":"token2","type":"address"},{"name":"cutoff","type":"uint256"}],"name":"cancelAllOrdersByTradingPair","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"addresses","type":"address[4]"},{"name":"orderValues","type":"uint256[7]"},{"name":"buyNoMoreThanAmountB","type":"bool"},{"name":"marginSplitPercentage","type":"uint8"},{"name":"v","type":"uint8"},{"name":"r","type":"bytes32"},{"name":"s","type":"bytes32"}],"name":"cancelOrder","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"cutoff","type":"uint256"}],"name":"cancelAllOrders","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_ringIndex","type":"uint256"},{"indexed":true,"name":"_ringHash","type":"bytes32"},{"indexed":false,"name":"_miner","type":"address"},{"indexed":false,"name":"_feeRecipient","type":"address"},{"indexed":false,"name":"_orderHashList","type":"bytes32[]"},{"indexed":false,"name":"_amountsList","type":"uint256[6][]"}],"name":"RingMined","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_orderHash","type":"bytes32"},{"indexed":false,"name":"_amountCancelled","type":"uint256"}],"name":"OrderCancelled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_address","type":"address"},{"indexed":false,"name":"_cutoff","type":"uint256"}],"name":"AllOrdersCancelled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_address","type":"address"},{"indexed":false,"name":"_token1","type":"address"},{"indexed":false,"name":"_token2","type":"address"},{"indexed":false,"name":"_cutoff","type":"uint256"}],"name":"OrdersCancelled","type":"event"}] -------------------------------------------------------------------------------- /ABI/version10/TokenTransferDelegate.abi: -------------------------------------------------------------------------------- 1 | [{"constant":true,"inputs":[],"name":"latestAddress","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"max","type":"uint256"}],"name":"getLatestAuthorizedAddresses","outputs":[{"name":"addresses","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"token","type":"address"},{"name":"from","type":"address"},{"name":"to","type":"address"},{"name":"value","type":"uint256"}],"name":"transferToken","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"addr","type":"address"}],"name":"authorizeAddress","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"claimOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"lrcTokenAddress","type":"address"},{"name":"feeRecipient","type":"address"},{"name":"batch","type":"bytes32[]"}],"name":"batchTransferToken","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"addr","type":"address"}],"name":"isAddressAuthorized","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"pendingOwner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"addr","type":"address"}],"name":"deauthorizeAddress","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":true,"name":"addr","type":"address"},{"indexed":false,"name":"number","type":"uint32"}],"name":"AddressAuthorized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"addr","type":"address"},{"indexed":false,"name":"number","type":"uint32"}],"name":"AddressDeauthorized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"previousOwner","type":"address"},{"indexed":true,"name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"}] -------------------------------------------------------------------------------- /ABI/version12/TokenTransferDelegate.abi: -------------------------------------------------------------------------------- 1 | [{"constant":true,"inputs":[],"name":"latestAddress","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"max","type":"uint256"}],"name":"getLatestAuthorizedAddresses","outputs":[{"name":"addresses","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"token","type":"address"},{"name":"from","type":"address"},{"name":"to","type":"address"},{"name":"value","type":"uint256"}],"name":"transferToken","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"addr","type":"address"}],"name":"authorizeAddress","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"claimOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"lrcTokenAddress","type":"address"},{"name":"feeRecipient","type":"address"},{"name":"batch","type":"bytes32[]"}],"name":"batchTransferToken","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"addr","type":"address"}],"name":"isAddressAuthorized","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"pendingOwner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"addr","type":"address"}],"name":"deauthorizeAddress","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":true,"name":"addr","type":"address"},{"indexed":false,"name":"number","type":"uint32"}],"name":"AddressAuthorized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"addr","type":"address"},{"indexed":false,"name":"number","type":"uint32"}],"name":"AddressDeauthorized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"previousOwner","type":"address"},{"indexed":true,"name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"}] -------------------------------------------------------------------------------- /ABI/version14/TokenTransferDelegate.abi: -------------------------------------------------------------------------------- 1 | [{"constant":true,"inputs":[],"name":"latestAddress","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"max","type":"uint256"}],"name":"getLatestAuthorizedAddresses","outputs":[{"name":"addresses","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"token","type":"address"},{"name":"from","type":"address"},{"name":"to","type":"address"},{"name":"value","type":"uint256"}],"name":"transferToken","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"lrcTokenAddress","type":"address"},{"name":"minerFeeRecipient","type":"address"},{"name":"walletSplitPercentage","type":"uint8"},{"name":"batch","type":"bytes32[]"}],"name":"batchTransferToken","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"addr","type":"address"}],"name":"authorizeAddress","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"claimOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"addr","type":"address"}],"name":"isAddressAuthorized","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"pendingOwner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"addr","type":"address"}],"name":"deauthorizeAddress","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":true,"name":"addr","type":"address"},{"indexed":false,"name":"number","type":"uint32"}],"name":"AddressAuthorized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"addr","type":"address"},{"indexed":false,"name":"number","type":"uint32"}],"name":"AddressDeauthorized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"previousOwner","type":"address"},{"indexed":true,"name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"}] -------------------------------------------------------------------------------- /util/types.ts: -------------------------------------------------------------------------------- 1 | import { BigNumber } from "bignumber.js"; 2 | import { Ring } from "./ring"; 3 | 4 | export interface OrderParams { 5 | delegateContract: string; 6 | tokenS: string; 7 | tokenB: string; 8 | amountS: BigNumber; 9 | amountB: BigNumber; 10 | validSince: BigNumber; 11 | validUntil: BigNumber; 12 | lrcFee: BigNumber; 13 | buyNoMoreThanAmountB: boolean; 14 | marginSplitPercentage: number; 15 | authAddr: string; 16 | walletAddr: string; 17 | tokenSSymbol?: string; 18 | scaledAmountS?: number; 19 | scaledAmountB?: number; 20 | rateAmountS?: number; 21 | rateAmountB?: number; 22 | fillAmountS?: number; 23 | orderHashHex?: string; 24 | v?: number; 25 | r?: string; 26 | s?: string; 27 | } 28 | 29 | export interface LoopringSubmitParams { 30 | addressList: string[][]; 31 | uintArgsList: BigNumber[][]; 32 | uint8ArgsList: number[][]; 33 | buyNoMoreThanAmountBList: boolean[]; 34 | vList: number[]; 35 | rList: string[]; 36 | sList: string[]; 37 | ringOwner: string; 38 | feeRecepient: string; 39 | feeSelections: number; 40 | } 41 | 42 | export interface FeeItem { 43 | feeS: number; 44 | feeB: number; 45 | feeLrc: number; 46 | lrcReward: number; 47 | } 48 | 49 | export interface BalanceItem { 50 | balanceS: number; 51 | balanceB: number; 52 | } 53 | 54 | export interface RingInfo { 55 | amountSList: number[]; 56 | amountBList: number[]; 57 | lrcFeeAmountList?: number[]; 58 | miner?: string; 59 | orderOwners?: string[]; 60 | tokenAddressList?: string[]; 61 | marginSplitPercentageList?: number[]; 62 | buyNoMoreThanAmountBList?: boolean[]; 63 | feeSelections?: number[]; 64 | spendableAmountSList?: number[]; 65 | spendableLrcFeeAmountList?: number[]; 66 | orderFilledOrCancelledAmountList?: number[]; 67 | description?: string; 68 | salt?: number; 69 | verbose?: boolean; 70 | id?: string; 71 | } 72 | 73 | export interface RingBalanceInfo { 74 | participiants: string[]; 75 | tokenAddressList: string[]; 76 | tokenSymbolList: string[]; 77 | tokenBalances: number[][]; 78 | } 79 | 80 | export interface SimulatorReport { 81 | ring: Ring; 82 | feeItems: FeeItem[]; 83 | transferList: TransferItem[]; 84 | ringBalanceInfo?: RingBalanceInfo; 85 | } 86 | 87 | export interface TransferItem { 88 | description: string; 89 | tokenAddress: string; 90 | tokenSymbol?: string; 91 | fromAddress: string; 92 | toAddress: string; 93 | amount: number; 94 | } 95 | 96 | export interface RawTx { 97 | content: string; 98 | id?: string; 99 | url?: string; 100 | verbose?: boolean; 101 | } 102 | -------------------------------------------------------------------------------- /ABI/version12/TokenRegistry.abi: -------------------------------------------------------------------------------- 1 | [{"constant":false,"inputs":[{"name":"addr","type":"address"},{"name":"symbol","type":"string"}],"name":"unregisterToken","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"symbol","type":"string"}],"name":"getAddressBySymbol","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"addressList","type":"address[]"}],"name":"areAllTokensRegistered","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"addr","type":"address"}],"name":"isTokenRegistered","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"start","type":"uint256"},{"name":"count","type":"uint256"}],"name":"getTokens","outputs":[{"name":"addressList","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"claimOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"addr","type":"address"},{"name":"symbol","type":"string"}],"name":"registerToken","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"pendingOwner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"addresses","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"symbol","type":"string"}],"name":"isTokenRegisteredBySymbol","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":false,"name":"addr","type":"address"},{"indexed":false,"name":"symbol","type":"string"}],"name":"TokenRegistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"addr","type":"address"},{"indexed":false,"name":"symbol","type":"string"}],"name":"TokenUnregistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"previousOwner","type":"address"},{"indexed":true,"name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"}] -------------------------------------------------------------------------------- /docs/gas_consume_report_1026.md: -------------------------------------------------------------------------------- 1 | # Gas Usage at Oct-26-2017 2 | 3 | The following measurements usge 21Gwei as gas price. 4 | 5 | ## A Ring of 2 Orders: 6 | 7 | | Function | ETH | 8 | | ------ | ------ | 9 | | code before verifyInputDataIntegrity | 0.001422435000008704 | 10 | | Verifyinputdataintegrity | 0.001468109999996928 | 11 | | verifyTokensRegistered | 0.001697177999998976 | 12 | | calculateRinghash | 0.002285702999998464 | 13 | | check ringhashRegistry.canSubmit | 0.002342087999995904 | 14 | | verifySignature for ring | 0.002436 | 15 | | assembleOrders | 0.003235365000003584 | 16 | | handleRing-verifyRingHasNoSubRing | 0.003260627999997952 | 17 | | handleRing-verifyMinerSuppliedFillRates | 0.003361364999995392 | 18 | | handleRing-scaleRingBasedOnHistoricalRecords | 0.003469178999996416 | 19 | | handleRing-calculateRingFillAmount | 0.003518256000008192 | 20 | | handleRing-calculateRingFees | 0.004114298999996416 | 21 | | handleRing-settleRing | 0.008177547000004608 | 22 | | RingMined event | 0.008721321000009727 | 23 | | total | 0.008721321000009727 | 24 | 25 | 26 | ## A Ring of 3 Orders: 27 | 28 | | Function | ETH | 29 | | ------ | ------ | 30 | | code before verifyInputDataIntegrity | 0.00166460700000256 | 31 | | Verifyinputdataintegrity | 0.001716623999991808 | 32 | | verifyTokensRegistered | 0.00209993699999744 | 33 | | calculateRinghash | 0.00318345300000768 | 34 | | check ringhashRegistry.canSubmit | 0.003237149999988736 | 35 | | verifySignature for ring | 0.003335094000009216 | 36 | | assembleOrders | 0.004525751999987712 | 37 | | handleRing-verifyRingHasNoSubRing | 0.00457401000001536 | 38 | | handleRing-verifyMinerSuppliedFillRates | 0.004705386000007168 | 39 | | handleRing-scaleRingBasedOnHistoricalRecords | 0.004853897999990784 | 40 | | handleRing-calculateRingFillAmount | 0.00495423600001024 | 41 | | handleRing-calculateRingFees | 0.005421338999996416 | 42 | | handleRing-settleRing | 0.010511991000006656 | 43 | | RingMined event | 0.01074076500000768 | 44 | | total | 0.01074076500000768 | 45 | 46 | -------------------------------------------------------------------------------- /contracts/lib/MathUint.sol: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright 2017 Loopring Project Ltd (Loopring Foundation). 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | pragma solidity 0.4.21; 18 | 19 | 20 | /// @title Utility Functions for uint 21 | /// @author Daniel Wang - 22 | library MathUint { 23 | 24 | function mul( 25 | uint a, 26 | uint b 27 | ) 28 | internal 29 | pure 30 | returns (uint c) 31 | { 32 | c = a * b; 33 | require(a == 0 || c / a == b); 34 | } 35 | 36 | function sub( 37 | uint a, 38 | uint b 39 | ) 40 | internal 41 | pure 42 | returns (uint) 43 | { 44 | require(b <= a); 45 | return a - b; 46 | } 47 | 48 | function add( 49 | uint a, 50 | uint b 51 | ) 52 | internal 53 | pure 54 | returns (uint c) 55 | { 56 | c = a + b; 57 | require(c >= a); 58 | } 59 | 60 | function tolerantSub( 61 | uint a, 62 | uint b 63 | ) 64 | internal 65 | pure 66 | returns (uint c) 67 | { 68 | return (a >= b) ? a - b : 0; 69 | } 70 | 71 | /// @dev calculate the square of Coefficient of Variation (CV) 72 | /// https://en.wikipedia.org/wiki/Coefficient_of_variation 73 | function cvsquare( 74 | uint[] arr, 75 | uint scale 76 | ) 77 | internal 78 | pure 79 | returns (uint) 80 | { 81 | uint len = arr.length; 82 | require(len > 1); 83 | require(scale > 0); 84 | 85 | uint avg = 0; 86 | for (uint i = 0; i < len; i++) { 87 | avg = add(avg, arr[i]); 88 | } 89 | 90 | avg = avg / len; 91 | 92 | if (avg == 0) { 93 | return 0; 94 | } 95 | 96 | uint cvs = 0; 97 | uint s; 98 | uint item; 99 | for (i = 0; i < len; i++) { 100 | item = arr[i]; 101 | s = item > avg ? item - avg : avg - item; 102 | cvs = add(cvs, mul(s, s)); 103 | } 104 | 105 | return ((mul(mul(cvs, scale), scale) / avg) / avg) / (len - 1); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /ABI/version15/TokenRegistryImpl.abi: -------------------------------------------------------------------------------- 1 | [{"constant":false,"inputs":[{"name":"addr","type":"address"},{"name":"symbol","type":"string"}],"name":"unregisterToken","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"addr","type":"address"},{"name":"symbol","type":"string"}],"name":"registerMintedToken","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"symbol","type":"string"}],"name":"getAddressBySymbol","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"addressList","type":"address[]"}],"name":"areAllTokensRegistered","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"addr","type":"address"}],"name":"isTokenRegistered","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"start","type":"uint256"},{"name":"count","type":"uint256"}],"name":"getTokens","outputs":[{"name":"addressList","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"claimOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"addr","type":"address"},{"name":"symbol","type":"string"}],"name":"registerToken","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"pendingOwner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"addresses","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"symbol","type":"string"}],"name":"isTokenRegisteredBySymbol","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":true,"name":"previousOwner","type":"address"},{"indexed":true,"name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"addr","type":"address"},{"indexed":false,"name":"symbol","type":"string"}],"name":"TokenRegistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"addr","type":"address"},{"indexed":false,"name":"symbol","type":"string"}],"name":"TokenUnregistered","type":"event"}] -------------------------------------------------------------------------------- /ABI/version151/TokenRegistryImpl.abi: -------------------------------------------------------------------------------- 1 | [{"constant":false,"inputs":[{"name":"addr","type":"address"},{"name":"symbol","type":"string"}],"name":"unregisterToken","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"symbol","type":"string"}],"name":"getAddressBySymbol","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"addressList","type":"address[]"}],"name":"areAllTokensRegistered","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"addr","type":"address"}],"name":"isTokenRegistered","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"start","type":"uint256"},{"name":"count","type":"uint256"}],"name":"getTokens","outputs":[{"name":"addressList","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"claimOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"addr","type":"address"},{"name":"symbol","type":"string"}],"name":"registerToken","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"addressMap","outputs":[{"name":"pos","type":"uint256"},{"name":"symbol","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"pendingOwner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"addresses","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"symbol","type":"string"}],"name":"isTokenRegisteredBySymbol","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":true,"name":"previousOwner","type":"address"},{"indexed":true,"name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"addr","type":"address"},{"indexed":false,"name":"symbol","type":"string"}],"name":"TokenRegistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"addr","type":"address"},{"indexed":false,"name":"symbol","type":"string"}],"name":"TokenUnregistered","type":"event"}] -------------------------------------------------------------------------------- /ABI/version15/ERC20Token.abi: -------------------------------------------------------------------------------- 1 | [{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply_","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_subtractedValue","type":"uint256"}],"name":"decreaseApproval","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_addedValue","type":"uint256"}],"name":"increaseApproval","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"},{"name":"_spender","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"_name","type":"string"},{"name":"_symbol","type":"string"},{"name":"_decimals","type":"uint8"},{"name":"_totalSupply","type":"uint256"},{"name":"_firstHolder","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"},{"indexed":true,"name":"spender","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Approval","type":"event"}] -------------------------------------------------------------------------------- /ABI/version151/ERC20Token.abi: -------------------------------------------------------------------------------- 1 | [{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply_","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_subtractedValue","type":"uint256"}],"name":"decreaseApproval","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_addedValue","type":"uint256"}],"name":"increaseApproval","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"},{"name":"_spender","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"_name","type":"string"},{"name":"_symbol","type":"string"},{"name":"_decimals","type":"uint8"},{"name":"_totalSupply","type":"uint256"},{"name":"_firstHolder","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"},{"indexed":true,"name":"spender","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Approval","type":"event"}] -------------------------------------------------------------------------------- /ABI/version14/TokenRegistry.abi: -------------------------------------------------------------------------------- 1 | [{"constant":false,"inputs":[{"name":"addr","type":"address"},{"name":"symbol","type":"string"}],"name":"unregisterToken","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"addr","type":"address"},{"name":"symbol","type":"string"}],"name":"registerMintedToken","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"symbol","type":"string"}],"name":"getAddressBySymbol","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"addressList","type":"address[]"}],"name":"areAllTokensRegistered","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"addr","type":"address"}],"name":"isTokenRegistered","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"start","type":"uint256"},{"name":"count","type":"uint256"}],"name":"getTokens","outputs":[{"name":"addressList","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"claimOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"addr","type":"address"},{"name":"symbol","type":"string"}],"name":"registerToken","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"pendingOwner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"addresses","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"symbol","type":"string"}],"name":"isTokenRegisteredBySymbol","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"_tokenMintAddr","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":false,"name":"addr","type":"address"},{"indexed":false,"name":"symbol","type":"string"}],"name":"TokenRegistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"addr","type":"address"},{"indexed":false,"name":"symbol","type":"string"}],"name":"TokenUnregistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"previousOwner","type":"address"},{"indexed":true,"name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"}] -------------------------------------------------------------------------------- /ABI/version14/ERC20Token.abi: -------------------------------------------------------------------------------- 1 | [{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply_","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_subtractedValue","type":"uint256"}],"name":"decreaseApproval","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"tokenTransferDelegate","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_addedValue","type":"uint256"}],"name":"increaseApproval","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"},{"name":"_spender","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"_name","type":"string"},{"name":"_symbol","type":"string"},{"name":"_decimals","type":"uint8"},{"name":"_totalSupply","type":"uint256"},{"name":"_firstHolder","type":"address"},{"name":"_tokenTransferDelegate","type":"address"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"},{"indexed":true,"name":"spender","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Approval","type":"event"}] -------------------------------------------------------------------------------- /util/order.ts: -------------------------------------------------------------------------------- 1 | import { BigNumber } from "bignumber.js"; 2 | import xor = require("bitwise-xor"); 3 | import BN = require("bn.js"); 4 | import promisify = require("es6-promisify"); 5 | import ABI = require("ethereumjs-abi"); 6 | import ethUtil = require("ethereumjs-util"); 7 | import * as _ from "lodash"; 8 | import Web3 = require("web3"); 9 | import { OrderParams } from "./types"; 10 | 11 | export class Order { 12 | public owner: string; 13 | public params: OrderParams; 14 | 15 | public web3Instance: Web3; 16 | 17 | constructor(owner: string, params: OrderParams) { 18 | this.owner = owner; 19 | this.params = params; 20 | 21 | try { 22 | if (web3) { 23 | this.web3Instance = web3; 24 | } 25 | } catch (err) { 26 | console.log("get web3 instance in Order class failed. err:", err); 27 | } 28 | } 29 | 30 | public isValidSignature() { 31 | const { v, r, s } = this.params; 32 | if (_.isUndefined(v) || _.isUndefined(r) || _.isUndefined(s)) { 33 | throw new Error("Cannot call isValidSignature on unsigned order"); 34 | } 35 | const orderHash = this.getOrderHash(); 36 | // console.log("hash len:", orderHash.length.toString()); 37 | const msgHash = ethUtil.hashPersonalMessage(orderHash); 38 | try { 39 | const pubKey = ethUtil.ecrecover(msgHash, v, ethUtil.toBuffer(r), ethUtil.toBuffer(s)); 40 | const recoveredAddress = ethUtil.bufferToHex(ethUtil.pubToAddress(pubKey)); 41 | return recoveredAddress === this.owner; 42 | } catch (err) { 43 | return false; 44 | } 45 | } 46 | 47 | public async signAsync() { 48 | const orderHash = this.getOrderHash(); 49 | 50 | const signature = await promisify(this.web3Instance.eth.sign)(this.owner, ethUtil.bufferToHex(orderHash)); 51 | const { v, r, s } = ethUtil.fromRpcSig(signature); 52 | this.params = _.assign(this.params, { 53 | orderHashHex: ethUtil.bufferToHex(orderHash), 54 | r: ethUtil.bufferToHex(r), 55 | s: ethUtil.bufferToHex(s), 56 | v, 57 | }); 58 | } 59 | 60 | public getOrderHash() { 61 | const args = [ 62 | this.params.delegateContract, 63 | this.owner, 64 | this.params.tokenS, 65 | this.params.tokenB, 66 | this.params.walletAddr, 67 | this.params.authAddr, 68 | this.toBN(this.params.amountS), 69 | this.toBN(this.params.amountB), 70 | this.toBN(this.params.validSince), 71 | this.toBN(this.params.validUntil), 72 | this.toBN(this.params.lrcFee), 73 | this.params.buyNoMoreThanAmountB, 74 | this.params.marginSplitPercentage, 75 | ]; 76 | 77 | const argTypes = [ 78 | "address", 79 | "address", 80 | "address", 81 | "address", 82 | "address", 83 | "address", 84 | "uint256", 85 | "uint256", 86 | "uint256", 87 | "uint256", 88 | "uint256", 89 | "bool", 90 | "uint8", 91 | ]; 92 | const orderHash = ABI.soliditySHA3(argTypes, args); 93 | return orderHash; 94 | } 95 | 96 | private toBN(bg: BigNumber) { 97 | return new BN(bg.toString(10), 10); 98 | } 99 | 100 | } 101 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at foundation@loopring.org. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /ABI/version15/TokenTransferDelegate.abi: -------------------------------------------------------------------------------- 1 | [{"constant":true,"inputs":[{"name":"owners","type":"address[]"},{"name":"tradingPairs","type":"bytes20[]"},{"name":"validSince","type":"uint256[]"}],"name":"checkCutoffsBatch","outputs":[],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"max","type":"uint256"}],"name":"getLatestAuthorizedAddresses","outputs":[{"name":"addresses","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"orderHash","type":"bytes32"},{"name":"cancelOrFillAmount","type":"uint256"}],"name":"addCancelledOrFilled","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"bytes32"}],"name":"cancelled","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"token","type":"address"},{"name":"from","type":"address"},{"name":"to","type":"address"},{"name":"value","type":"uint256"}],"name":"transferToken","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"lrcTokenAddress","type":"address"},{"name":"minerFeeRecipient","type":"address"},{"name":"walletSplitPercentage","type":"uint8"},{"name":"batch","type":"bytes32[]"}],"name":"batchTransferToken","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"addr","type":"address"}],"name":"authorizeAddress","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"tokenPair","type":"bytes20"},{"name":"t","type":"uint256"}],"name":"setTradingPairCutoffs","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"bytes32"}],"name":"cancelledOrFilled","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"},{"name":"","type":"bytes20"}],"name":"tradingPairCutoffs","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"orderHash","type":"bytes32"},{"name":"cancelAmount","type":"uint256"}],"name":"addCancelled","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"addr","type":"address"}],"name":"isAddressAuthorized","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"cutoffs","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"addr","type":"address"}],"name":"deauthorizeAddress","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"t","type":"uint256"}],"name":"setCutoffs","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"addr","type":"address"},{"indexed":false,"name":"number","type":"uint32"}],"name":"AddressAuthorized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"addr","type":"address"},{"indexed":false,"name":"number","type":"uint32"}],"name":"AddressDeauthorized","type":"event"}] -------------------------------------------------------------------------------- /ABI/version10/TokenRegistry.abi: -------------------------------------------------------------------------------- 1 | [{"constant":false,"inputs":[{"name":"addr","type":"address"},{"name":"symbol","type":"string"}],"name":"unregisterToken","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"symbol","type":"string"}],"name":"getAddressBySymbol","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"addressList","type":"address[]"}],"name":"areAllTokensRegistered","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"addr","type":"address"}],"name":"isTokenRegistered","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"TOKEN_STANDARD_ERC223","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"addr","type":"address"}],"name":"getTokenStandard","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"start","type":"uint256"},{"name":"count","type":"uint256"}],"name":"getTokens","outputs":[{"name":"addressList","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"claimOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"TOKEN_STANDARD_ERC20","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"addr","type":"address"},{"name":"symbol","type":"string"},{"name":"standard","type":"uint8"}],"name":"registerStandardToken","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"addr","type":"address"},{"name":"symbol","type":"string"}],"name":"registerToken","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"pendingOwner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"addresses","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"symbol","type":"string"}],"name":"isTokenRegisteredBySymbol","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":false,"name":"addr","type":"address"},{"indexed":false,"name":"symbol","type":"string"}],"name":"TokenRegistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"addr","type":"address"},{"indexed":false,"name":"symbol","type":"string"}],"name":"TokenUnregistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"previousOwner","type":"address"},{"indexed":true,"name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"}] -------------------------------------------------------------------------------- /ABI/version12/NameRegistry.abi: -------------------------------------------------------------------------------- 1 | [{"constant":false,"inputs":[{"name":"name","type":"string"}],"name":"unregisterName","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"name","type":"string"}],"name":"registerName","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"name","type":"string"},{"name":"start","type":"uint256"},{"name":"count","type":"uint256"}],"name":"getParticipantIds","outputs":[{"name":"idList","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"name","type":"string"}],"name":"getOwner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"id","type":"uint256"}],"name":"getParticipantById","outputs":[{"name":"feeRecipient","type":"address"},{"name":"signer","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"nextId","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"participantId","type":"uint256"}],"name":"removeParticipant","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"nameInfoMap","outputs":[{"name":"name","type":"bytes12"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"bytes12"}],"name":"ownerMap","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"nameMap","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"feeRecipient","type":"address"},{"name":"singer","type":"address"}],"name":"addParticipant","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"participantMap","outputs":[{"name":"feeRecipient","type":"address"},{"name":"signer","type":"address"},{"name":"name","type":"bytes12"},{"name":"owner","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"name","type":"string"},{"indexed":true,"name":"owner","type":"address"}],"name":"NameRegistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"name","type":"string"},{"indexed":true,"name":"owner","type":"address"}],"name":"NameUnregistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"name","type":"bytes12"},{"indexed":false,"name":"oldOwner","type":"address"},{"indexed":false,"name":"newOwner","type":"address"}],"name":"OwnershipTransfered","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"name","type":"bytes12"},{"indexed":true,"name":"owner","type":"address"},{"indexed":true,"name":"participantId","type":"uint256"},{"indexed":false,"name":"singer","type":"address"},{"indexed":false,"name":"feeRecipient","type":"address"}],"name":"ParticipantRegistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"participantId","type":"uint256"},{"indexed":false,"name":"owner","type":"address"}],"name":"ParticipantUnregistered","type":"event"}] -------------------------------------------------------------------------------- /test/testTokenRegistry.ts: -------------------------------------------------------------------------------- 1 | import { Artifacts } from "../util/artifacts"; 2 | 3 | const { 4 | TokenRegistry, 5 | DummyToken, 6 | } = new Artifacts(artifacts); 7 | 8 | contract("TokenRegistry", (accounts: string[]) => { 9 | 10 | const owner = accounts[0]; 11 | const user = accounts[1]; 12 | 13 | let tokenRegistry: any; 14 | let testTokenAddr: string; 15 | let lrcAddress: string; 16 | let eosAddress: string; 17 | let gtoAddress: string; 18 | let rdnAddress: string; 19 | 20 | before(async () => { 21 | tokenRegistry = await TokenRegistry.deployed(); 22 | testTokenAddr = "0x8d01f9bcca92e63a1b2752b22d16e1962aa3c920"; 23 | lrcAddress = await tokenRegistry.getAddressBySymbol("LRC"); 24 | eosAddress = await tokenRegistry.getAddressBySymbol("EOS"); 25 | gtoAddress = await tokenRegistry.getAddressBySymbol("GTO"); 26 | rdnAddress = await tokenRegistry.getAddressBySymbol("RDN"); 27 | }); 28 | 29 | describe("owner", () => { 30 | 31 | it("should be able to register a token", async () => { 32 | await tokenRegistry.registerToken(testTokenAddr, "TEST", {from: owner}); 33 | const isRegistered = await tokenRegistry.isTokenRegistered(testTokenAddr); 34 | assert.equal(isRegistered, true, "token should be registered"); 35 | }); 36 | 37 | it("should be able to unregister a token", async () => { 38 | let isRegistered = await tokenRegistry.isTokenRegistered(testTokenAddr); 39 | let isRegisteredBySymbol = await tokenRegistry.isTokenRegisteredBySymbol("TEST"); 40 | assert.equal(isRegistered, true, "token should be registered on start"); 41 | assert.equal(isRegisteredBySymbol, true, "token should be registered on start"); 42 | 43 | await tokenRegistry.unregisterToken(testTokenAddr, "TEST", {from: owner}); 44 | isRegistered = await tokenRegistry.isTokenRegistered(testTokenAddr); 45 | isRegisteredBySymbol = await tokenRegistry.isTokenRegisteredBySymbol("TEST"); 46 | assert.equal(isRegistered, false, "token should be unregistered"); 47 | assert.equal(isRegisteredBySymbol, false, "token should be unregistered"); 48 | }); 49 | 50 | it("should be able to check all tokens registered in array", async () => { 51 | const tokenList = [lrcAddress, gtoAddress, eosAddress, rdnAddress]; 52 | const allRegistered = await tokenRegistry.areAllTokensRegistered(tokenList); 53 | assert.equal(allRegistered, true, "all token registered in migration script."); 54 | 55 | tokenList.push(testTokenAddr); 56 | const allRegistered2 = await tokenRegistry.areAllTokensRegistered(tokenList); 57 | assert.equal(allRegistered2, false, "not all token registered"); 58 | }); 59 | 60 | it("should be able to getTokens with start index and count", async () => { 61 | const allTokens = await tokenRegistry.getTokens(0, 1000); // token length < 1000. 62 | // console.log("allTokens:", allTokens); 63 | 64 | const size = allTokens.length; 65 | const tokenSlice1 = await tokenRegistry.getTokens(1, 2); // token length < 1000. 66 | const tokenSlice2 = await tokenRegistry.getTokens(2, 2); // token length < 1000. 67 | 68 | assert.deepEqual(tokenSlice1, allTokens.slice(1, 3), "get tokens should return expected tokens"); 69 | assert.deepEqual(tokenSlice2, allTokens.slice(2, 4), "get tokens should return expected tokens"); 70 | }); 71 | 72 | }); 73 | 74 | describe("any user", () => { 75 | it("should be able to check a token registered or not", async () => { 76 | const isRegistered = await tokenRegistry.isTokenRegistered(testTokenAddr, {from: user}); 77 | assert.equal(isRegistered, isRegistered, "any one should be able to check token registered or not "); 78 | }); 79 | }); 80 | 81 | }); 82 | -------------------------------------------------------------------------------- /ABI/version14/NameRegistry.abi: -------------------------------------------------------------------------------- 1 | [{"constant":false,"inputs":[{"name":"name","type":"string"}],"name":"unregisterName","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"name","type":"string"}],"name":"registerName","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"name","type":"string"},{"name":"start","type":"uint256"},{"name":"count","type":"uint256"}],"name":"getParticipantIds","outputs":[{"name":"idList","type":"uint256[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"name","type":"string"}],"name":"getOwner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"id","type":"uint256"}],"name":"getParticipantById","outputs":[{"name":"feeRecipient","type":"address"},{"name":"signer","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"nextId","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"participantId","type":"uint256"}],"name":"removeParticipant","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"nameInfoMap","outputs":[{"name":"name","type":"bytes12"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"bytes12"}],"name":"ownerMap","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"nameMap","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"feeRecipient","type":"address"},{"name":"singer","type":"address"}],"name":"addParticipant","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"uint256"}],"name":"participantMap","outputs":[{"name":"feeRecipient","type":"address"},{"name":"signer","type":"address"},{"name":"name","type":"bytes12"},{"name":"owner","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"id","type":"uint256"}],"name":"getFeeRecipientById","outputs":[{"name":"feeRecipient","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"anonymous":false,"inputs":[{"indexed":false,"name":"name","type":"string"},{"indexed":true,"name":"owner","type":"address"}],"name":"NameRegistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"name","type":"string"},{"indexed":true,"name":"owner","type":"address"}],"name":"NameUnregistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"name","type":"bytes12"},{"indexed":false,"name":"oldOwner","type":"address"},{"indexed":false,"name":"newOwner","type":"address"}],"name":"OwnershipTransfered","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"name","type":"bytes12"},{"indexed":true,"name":"owner","type":"address"},{"indexed":true,"name":"participantId","type":"uint256"},{"indexed":false,"name":"singer","type":"address"},{"indexed":false,"name":"feeRecipient","type":"address"}],"name":"ParticipantRegistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"participantId","type":"uint256"},{"indexed":false,"name":"owner","type":"address"}],"name":"ParticipantUnregistered","type":"event"}] -------------------------------------------------------------------------------- /util/chain_reader.ts: -------------------------------------------------------------------------------- 1 | import { BigNumber } from "bignumber.js"; 2 | import fs = require("fs"); 3 | import Web3 = require("web3"); 4 | 5 | export class ChainReader { 6 | private web3Instance: Web3; 7 | private ERC20Contract: any; 8 | private DelegateContract: any; 9 | private TokenRegistryContract: any; 10 | private connected: boolean; 11 | 12 | constructor() { 13 | try { 14 | if (web3) { 15 | this.web3Instance = web3; // inject by truffle. 16 | } else { 17 | this.web3Instance = new Web3(new Web3.providers.HttpProvider("http://localhost:8545")); 18 | // connect to main-net via a public node: 19 | // new Web3.providers.HttpProvider('https://api.myetherapi.com/eth') 20 | } 21 | this.connected = true; 22 | } catch (err) { 23 | console.log("get web3 instance in class ChainReader failed. err:", err); 24 | this.connected = false; 25 | throw err; 26 | } 27 | 28 | const erc20Abi = fs.readFileSync("ABI/version151/ERC20.abi", "ascii"); 29 | const delegateAbi = fs.readFileSync("ABI/version151/TokenTransferDelegate.abi", "ascii"); 30 | const tokenRegistryAbi = fs.readFileSync("ABI/version151/TokenRegistryImpl.abi", "ascii"); 31 | this.ERC20Contract = this.web3Instance.eth.contract(JSON.parse(erc20Abi)); 32 | this.DelegateContract = this.web3Instance.eth.contract(JSON.parse(delegateAbi)); 33 | this.TokenRegistryContract = this.web3Instance.eth.contract(JSON.parse(tokenRegistryAbi)); 34 | } 35 | 36 | public isConnected() { 37 | return this.connected; 38 | } 39 | 40 | public async getERC20TokenBalance(tokenAddr: string, ownerAddr: string) { 41 | const tokenContractInstance = this.ERC20Contract.at(tokenAddr); 42 | const balance = await tokenContractInstance.balanceOf(ownerAddr); 43 | const balanceBN = new BigNumber(balance); 44 | return balanceBN.toNumber(); 45 | } 46 | 47 | public async getERC20TokenAllowance(tokenAddr: string, 48 | ownerAddr: string, 49 | spenderAddr: string) { 50 | const tokenContractInstance = this.ERC20Contract.at(tokenAddr); 51 | const balance = await tokenContractInstance.allowance(ownerAddr, spenderAddr); 52 | const balanceBN = new BigNumber(balance); 53 | return balanceBN.toNumber(); 54 | } 55 | 56 | public async getERC20TokenSpendable(tokenAddr: string, 57 | ownerAddr: string, 58 | spenderAddr: string) { 59 | const balance = await this.getERC20TokenBalance(tokenAddr, ownerAddr); 60 | const allowance = await this.getERC20TokenAllowance(tokenAddr, ownerAddr, spenderAddr); 61 | return Math.min(balance, allowance); 62 | } 63 | 64 | public async getOrderCancelledOrFilledAmount(orderHash: string, delegateAddr: string) { 65 | const delegateContractInstance = this.DelegateContract.at(delegateAddr); 66 | const amount = await delegateContractInstance.cancelledOrFilled(orderHash); 67 | const amountBN = new BigNumber(amount); 68 | return amount.toNumber(); 69 | } 70 | 71 | public async getTokenAddressBySymbol(tokenRegistryAddr: string, 72 | symbol: string) { 73 | const tokenRegistryInstance = this.TokenRegistryContract.at(tokenRegistryAddr); 74 | const tokenAddr = await tokenRegistryInstance.getAddressBySymbol(symbol); 75 | return tokenAddr; 76 | } 77 | 78 | public async getTokenSymbolByAddress(tokenRegistryAddr: string, 79 | tokenAddr: string) { 80 | const tokenRegistryInstance = this.TokenRegistryContract.at(tokenRegistryAddr); 81 | const tokenInfo = await tokenRegistryInstance.addressMap(tokenAddr); 82 | return tokenInfo[1]; 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /ABI/version151/TokenTransferDelegate.abi: -------------------------------------------------------------------------------- 1 | [{"constant":true,"inputs":[{"name":"owners","type":"address[]"},{"name":"tradingPairs","type":"bytes20[]"},{"name":"validSince","type":"uint256[]"}],"name":"checkCutoffsBatch","outputs":[],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"resume","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"max","type":"uint256"}],"name":"getLatestAuthorizedAddresses","outputs":[{"name":"addresses","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"orderHash","type":"bytes32"},{"name":"cancelOrFillAmount","type":"uint256"}],"name":"addCancelledOrFilled","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"bytes32"}],"name":"cancelled","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"token","type":"address"},{"name":"from","type":"address"},{"name":"to","type":"address"},{"name":"value","type":"uint256"}],"name":"transferToken","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"kill","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"lrcTokenAddress","type":"address"},{"name":"miner","type":"address"},{"name":"minerFeeRecipient","type":"address"},{"name":"walletSplitPercentage","type":"uint8"},{"name":"batch","type":"bytes32[]"}],"name":"batchTransferToken","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"addr","type":"address"}],"name":"authorizeAddress","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"tokenPair","type":"bytes20"},{"name":"t","type":"uint256"}],"name":"setTradingPairCutoffs","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"bytes32"}],"name":"cancelledOrFilled","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"batch","type":"bytes32[]"}],"name":"batchAddCancelledOrFilled","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"},{"name":"","type":"bytes20"}],"name":"tradingPairCutoffs","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"orderHash","type":"bytes32"},{"name":"cancelAmount","type":"uint256"}],"name":"addCancelled","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"addr","type":"address"}],"name":"isAddressAuthorized","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"cutoffs","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"suspend","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"addr","type":"address"}],"name":"deauthorizeAddress","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"t","type":"uint256"}],"name":"setCutoffs","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"addr","type":"address"},{"indexed":false,"name":"number","type":"uint32"}],"name":"AddressAuthorized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"addr","type":"address"},{"indexed":false,"name":"number","type":"uint32"}],"name":"AddressDeauthorized","type":"event"}] -------------------------------------------------------------------------------- /ABI/version12/LoopringProtocolImpl.abi: -------------------------------------------------------------------------------- 1 | [{"constant":false,"inputs":[{"name":"addressList","type":"address[3][]"},{"name":"uintArgsList","type":"uint256[7][]"},{"name":"uint8ArgsList","type":"uint8[1][]"},{"name":"buyNoMoreThanAmountBList","type":"bool[]"},{"name":"vList","type":"uint8[]"},{"name":"rList","type":"bytes32[]"},{"name":"sList","type":"bytes32[]"},{"name":"minerId","type":"uint256"},{"name":"feeSelections","type":"uint16"}],"name":"submitRing","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"ENTERED_MASK","outputs":[{"name":"","type":"uint64"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"bytes32"}],"name":"cancelled","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"MARGIN_SPLIT_PERCENTAGE_BASE","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"ringIndex","outputs":[{"name":"","type":"uint64"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"RATE_RATIO_SCALE","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"bytes32"}],"name":"cancelledOrFilled","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"orderOwner","type":"address"},{"name":"token1","type":"address"},{"name":"token2","type":"address"}],"name":"getTradingPairCutoffs","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"token1","type":"address"},{"name":"token2","type":"address"},{"name":"cutoff","type":"uint256"}],"name":"cancelAllOrdersByTradingPair","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"addresses","type":"address[4]"},{"name":"orderValues","type":"uint256[7]"},{"name":"buyNoMoreThanAmountB","type":"bool"},{"name":"marginSplitPercentage","type":"uint8"},{"name":"v","type":"uint8"},{"name":"r","type":"bytes32"},{"name":"s","type":"bytes32"}],"name":"cancelOrder","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"MAX_RING_SIZE","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"},{"name":"","type":"bytes20"}],"name":"tradingPairCutoffs","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"cutoff","type":"uint256"}],"name":"cancelAllOrders","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"cutoffs","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_ringIndex","type":"uint256"},{"indexed":true,"name":"_ringHash","type":"bytes32"},{"indexed":false,"name":"_miner","type":"address"},{"indexed":false,"name":"_feeRecipient","type":"address"},{"indexed":false,"name":"_orderHashList","type":"bytes32[]"},{"indexed":false,"name":"_amountsList","type":"uint256[6][]"}],"name":"RingMined","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_orderHash","type":"bytes32"},{"indexed":false,"name":"_amountCancelled","type":"uint256"}],"name":"OrderCancelled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_address","type":"address"},{"indexed":false,"name":"_cutoff","type":"uint256"}],"name":"AllOrdersCancelled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_address","type":"address"},{"indexed":false,"name":"_token1","type":"address"},{"indexed":false,"name":"_token2","type":"address"},{"indexed":false,"name":"_cutoff","type":"uint256"}],"name":"OrdersCancelled","type":"event"}] -------------------------------------------------------------------------------- /ABI/version151/LoopringProtocolImpl.abi: -------------------------------------------------------------------------------- 1 | [{"constant":true,"inputs":[],"name":"MARGIN_SPLIT_PERCENTAGE_BASE","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"ringIndex","outputs":[{"name":"","type":"uint64"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"RATE_RATIO_SCALE","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"lrcTokenAddress","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"tokenRegistryAddress","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"delegateAddress","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"orderOwner","type":"address"},{"name":"token1","type":"address"},{"name":"token2","type":"address"}],"name":"getTradingPairCutoffs","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"token1","type":"address"},{"name":"token2","type":"address"},{"name":"cutoff","type":"uint256"}],"name":"cancelAllOrdersByTradingPair","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"addresses","type":"address[5]"},{"name":"orderValues","type":"uint256[6]"},{"name":"buyNoMoreThanAmountB","type":"bool"},{"name":"marginSplitPercentage","type":"uint8"},{"name":"v","type":"uint8"},{"name":"r","type":"bytes32"},{"name":"s","type":"bytes32"}],"name":"cancelOrder","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"MAX_RING_SIZE","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"cutoff","type":"uint256"}],"name":"cancelAllOrders","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"rateRatioCVSThreshold","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"data","type":"bytes"}],"name":"submitRing","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"walletSplitPercentage","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"_lrcTokenAddress","type":"address"},{"name":"_tokenRegistryAddress","type":"address"},{"name":"_delegateAddress","type":"address"},{"name":"_rateRatioCVSThreshold","type":"uint256"},{"name":"_walletSplitPercentage","type":"uint8"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":false,"name":"n1","type":"uint256"},{"indexed":false,"name":"n2","type":"uint256"},{"indexed":false,"name":"n3","type":"uint256"}],"name":"LogUint2","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_ringIndex","type":"uint256"},{"indexed":true,"name":"_ringHash","type":"bytes32"},{"indexed":false,"name":"_miner","type":"address"},{"indexed":false,"name":"_feeRecipient","type":"address"},{"indexed":false,"name":"_orderInfoList","type":"bytes32[]"}],"name":"RingMined","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_orderHash","type":"bytes32"},{"indexed":false,"name":"_amountCancelled","type":"uint256"}],"name":"OrderCancelled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_address","type":"address"},{"indexed":false,"name":"_cutoff","type":"uint256"}],"name":"AllOrdersCancelled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_address","type":"address"},{"indexed":false,"name":"_token1","type":"address"},{"indexed":false,"name":"_token2","type":"address"},{"indexed":false,"name":"_cutoff","type":"uint256"}],"name":"OrdersCancelled","type":"event"}] 2 | -------------------------------------------------------------------------------- /ABI/version15/LoopringProtocolImpl.abi: -------------------------------------------------------------------------------- 1 | [{"constant":true,"inputs":[],"name":"MARGIN_SPLIT_PERCENTAGE_BASE","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"ringIndex","outputs":[{"name":"","type":"uint64"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"RATE_RATIO_SCALE","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"lrcTokenAddress","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"tokenRegistryAddress","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"delegateAddress","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"orderOwner","type":"address"},{"name":"token1","type":"address"},{"name":"token2","type":"address"}],"name":"getTradingPairCutoffs","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"token1","type":"address"},{"name":"token2","type":"address"},{"name":"cutoff","type":"uint256"}],"name":"cancelAllOrdersByTradingPair","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"addresses","type":"address[5]"},{"name":"orderValues","type":"uint256[6]"},{"name":"buyNoMoreThanAmountB","type":"bool"},{"name":"marginSplitPercentage","type":"uint8"},{"name":"v","type":"uint8"},{"name":"r","type":"bytes32"},{"name":"s","type":"bytes32"}],"name":"cancelOrder","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"MAX_RING_SIZE","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"cutoff","type":"uint256"}],"name":"cancelAllOrders","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"rateRatioCVSThreshold","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"addressList","type":"address[4][]"},{"name":"uintArgsList","type":"uint256[6][]"},{"name":"uint8ArgsList","type":"uint8[1][]"},{"name":"buyNoMoreThanAmountBList","type":"bool[]"},{"name":"vList","type":"uint8[]"},{"name":"rList","type":"bytes32[]"},{"name":"sList","type":"bytes32[]"},{"name":"miner","type":"address"},{"name":"feeSelections","type":"uint16"}],"name":"submitRing","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"walletSplitPercentage","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"_lrcTokenAddress","type":"address"},{"name":"_tokenRegistryAddress","type":"address"},{"name":"_delegateAddress","type":"address"},{"name":"_rateRatioCVSThreshold","type":"uint256"},{"name":"_walletSplitPercentage","type":"uint8"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_ringIndex","type":"uint256"},{"indexed":true,"name":"_ringHash","type":"bytes32"},{"indexed":false,"name":"_feeRecipient","type":"address"},{"indexed":false,"name":"_orderHashList","type":"bytes32[]"},{"indexed":false,"name":"_amountsList","type":"uint256[6][]"}],"name":"RingMined","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_orderHash","type":"bytes32"},{"indexed":false,"name":"_amountCancelled","type":"uint256"}],"name":"OrderCancelled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_address","type":"address"},{"indexed":false,"name":"_cutoff","type":"uint256"}],"name":"AllOrdersCancelled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_address","type":"address"},{"indexed":false,"name":"_token1","type":"address"},{"indexed":false,"name":"_token2","type":"address"},{"indexed":false,"name":"_cutoff","type":"uint256"}],"name":"OrdersCancelled","type":"event"}] -------------------------------------------------------------------------------- /contracts/TokenTransferDelegate.sol: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright 2017 Loopring Project Ltd (Loopring Foundation). 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | pragma solidity 0.4.21; 18 | 19 | 20 | /// @title TokenTransferDelegate 21 | /// @dev Acts as a middle man to transfer ERC20 tokens on behalf of different 22 | /// versions of Loopring protocol to avoid ERC20 re-authorization. 23 | /// @author Daniel Wang - . 24 | /// Recognized contributing developers from the community: 25 | /// https://github.com/Brechtpd 26 | contract TokenTransferDelegate { 27 | event AddressAuthorized( 28 | address indexed addr, 29 | uint32 number 30 | ); 31 | 32 | event AddressDeauthorized( 33 | address indexed addr, 34 | uint32 number 35 | ); 36 | 37 | // The following map is used to keep trace of order fill and cancellation 38 | // history. 39 | mapping (bytes32 => uint) public cancelledOrFilled; 40 | 41 | // This map is used to keep trace of order's cancellation history. 42 | mapping (bytes32 => uint) public cancelled; 43 | 44 | // A map from address to its cutoff timestamp. 45 | mapping (address => uint) public cutoffs; 46 | 47 | // A map from address to its trading-pair cutoff timestamp. 48 | mapping (address => mapping (bytes20 => uint)) public tradingPairCutoffs; 49 | 50 | /// @dev Add a Loopring protocol address. 51 | /// @param addr A loopring protocol address. 52 | function authorizeAddress( 53 | address addr 54 | ) 55 | external; 56 | 57 | /// @dev Remove a Loopring protocol address. 58 | /// @param addr A loopring protocol address. 59 | function deauthorizeAddress( 60 | address addr 61 | ) 62 | external; 63 | 64 | function getLatestAuthorizedAddresses( 65 | uint max 66 | ) 67 | external 68 | view 69 | returns (address[] addresses); 70 | 71 | /// @dev Invoke ERC20 transferFrom method. 72 | /// @param token Address of token to transfer. 73 | /// @param from Address to transfer token from. 74 | /// @param to Address to transfer token to. 75 | /// @param value Amount of token to transfer. 76 | function transferToken( 77 | address token, 78 | address from, 79 | address to, 80 | uint value 81 | ) 82 | external; 83 | 84 | function batchUpdateHistoryAndTransferTokens( 85 | address lrcTokenAddress, 86 | address miner, 87 | address minerFeeRecipient, 88 | uint8 walletSplitPercentage, 89 | bytes32[] batch 90 | ) 91 | external; 92 | 93 | function isAddressAuthorized( 94 | address addr 95 | ) 96 | public 97 | view 98 | returns (bool); 99 | 100 | function addCancelled(bytes32 orderHash, uint cancelAmount) 101 | external; 102 | 103 | function addCancelledOrFilled(bytes32 orderHash, uint cancelOrFillAmount) 104 | public; 105 | 106 | function getCancelledOrFilledBatch(bytes32 orderHashA, bytes32 orderHashB, bytes32 orderHashC) 107 | external 108 | view 109 | returns (uint[3] amounts); 110 | 111 | function setCutoffs(uint t) 112 | external; 113 | 114 | function setTradingPairCutoffs(bytes20 tokenPair, uint t) 115 | external; 116 | 117 | function suspend() external; 118 | 119 | function resume() external; 120 | 121 | function kill() external; 122 | } 123 | 124 | library TokenTransfer { 125 | struct OrderSettleData { 126 | address owner; 127 | address tokenS; 128 | uint amount; 129 | uint split; 130 | uint lrcReward; 131 | uint lrcFee; 132 | address wallet; 133 | bytes32 orderHash; 134 | uint fillAmount; 135 | uint validSince; 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /ABI/version10/LoopringProtocolImpl.abi: -------------------------------------------------------------------------------- 1 | [{"constant":true,"inputs":[],"name":"ENTERED_MASK","outputs":[{"name":"","type":"uint64"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FEE_SELECT_MAX_VALUE","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"MARGIN_SPLIT_PERCENTAGE_BASE","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"ringIndex","outputs":[{"name":"","type":"uint64"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"addresses","type":"address[3]"},{"name":"orderValues","type":"uint256[7]"},{"name":"buyNoMoreThanAmountB","type":"bool"},{"name":"marginSplitPercentage","type":"uint8"},{"name":"v","type":"uint8"},{"name":"r","type":"bytes32"},{"name":"s","type":"bytes32"}],"name":"cancelOrder","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"RATE_RATIO_SCALE","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"lrcTokenAddress","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"bytes32"}],"name":"cancelledOrFilled","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"tokenRegistryAddress","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"delegateAddress","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"addressList","type":"address[2][]"},{"name":"uintArgsList","type":"uint256[7][]"},{"name":"uint8ArgsList","type":"uint8[2][]"},{"name":"buyNoMoreThanAmountBList","type":"bool[]"},{"name":"vList","type":"uint8[]"},{"name":"rList","type":"bytes32[]"},{"name":"sList","type":"bytes32[]"},{"name":"ringminer","type":"address"},{"name":"feeRecipient","type":"address"}],"name":"submitRing","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"maxRingSize","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"ringhashRegistryAddress","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"cutoff","type":"uint256"}],"name":"setCutoff","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"FEE_SELECT_LRC","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"cutoffs","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"rateRatioCVSThreshold","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"FEE_SELECT_MARGIN_SPLIT","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"_lrcTokenAddress","type":"address"},{"name":"_tokenRegistryAddress","type":"address"},{"name":"_ringhashRegistryAddress","type":"address"},{"name":"_delegateAddress","type":"address"},{"name":"_maxRingSize","type":"uint256"},{"name":"_rateRatioCVSThreshold","type":"uint256"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_ringIndex","type":"uint256"},{"indexed":true,"name":"_ringhash","type":"bytes32"},{"indexed":false,"name":"_miner","type":"address"},{"indexed":false,"name":"_feeRecipient","type":"address"},{"indexed":false,"name":"_isRinghashReserved","type":"bool"},{"indexed":false,"name":"_orderHashList","type":"bytes32[]"},{"indexed":false,"name":"_amountsList","type":"uint256[6][]"}],"name":"RingMined","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_orderHash","type":"bytes32"},{"indexed":false,"name":"_amountCancelled","type":"uint256"}],"name":"OrderCancelled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_address","type":"address"},{"indexed":false,"name":"_cutoff","type":"uint256"}],"name":"AllOrdersCancelled","type":"event"}] -------------------------------------------------------------------------------- /ABI/version15/TokenTransferDelegateImpl.abi: -------------------------------------------------------------------------------- 1 | [{"constant":true,"inputs":[{"name":"owners","type":"address[]"},{"name":"tradingPairs","type":"bytes20[]"},{"name":"validSince","type":"uint256[]"}],"name":"checkCutoffsBatch","outputs":[],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"latestAddress","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"max","type":"uint256"}],"name":"getLatestAuthorizedAddresses","outputs":[{"name":"addresses","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"orderHash","type":"bytes32"},{"name":"cancelOrFillAmount","type":"uint256"}],"name":"addCancelledOrFilled","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"bytes32"}],"name":"cancelled","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"token","type":"address"},{"name":"from","type":"address"},{"name":"to","type":"address"},{"name":"value","type":"uint256"}],"name":"transferToken","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"lrcTokenAddress","type":"address"},{"name":"minerFeeRecipient","type":"address"},{"name":"walletSplitPercentage","type":"uint8"},{"name":"batch","type":"bytes32[]"}],"name":"batchTransferToken","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"addr","type":"address"}],"name":"authorizeAddress","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"tokenPair","type":"bytes20"},{"name":"t","type":"uint256"}],"name":"setTradingPairCutoffs","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"claimOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"bytes32"}],"name":"cancelledOrFilled","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"},{"name":"","type":"bytes20"}],"name":"tradingPairCutoffs","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"orderHash","type":"bytes32"},{"name":"cancelAmount","type":"uint256"}],"name":"addCancelled","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"addressInfos","outputs":[{"name":"previous","type":"address"},{"name":"index","type":"uint32"},{"name":"authorized","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"addr","type":"address"}],"name":"isAddressAuthorized","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"cutoffs","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"pendingOwner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"addr","type":"address"}],"name":"deauthorizeAddress","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"t","type":"uint256"}],"name":"setCutoffs","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":true,"name":"previousOwner","type":"address"},{"indexed":true,"name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"addr","type":"address"},{"indexed":false,"name":"number","type":"uint32"}],"name":"AddressAuthorized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"addr","type":"address"},{"indexed":false,"name":"number","type":"uint32"}],"name":"AddressDeauthorized","type":"event"}] -------------------------------------------------------------------------------- /test/ringConfig.ts: -------------------------------------------------------------------------------- 1 | import { RingInfo } from "../util/types"; 2 | 3 | export const ringInfoList: RingInfo[] = [ 4 | { 5 | description: "ring with 2 orders - prices exactly match", 6 | amountSList: [1e17, 300e18], 7 | amountBList: [300e18, 1e17], 8 | buyNoMoreThanAmountBList: [false, false], 9 | feeSelections: [0, 0], 10 | salt: 10, 11 | }, 12 | { 13 | description: "ring with 2 orders - prices exactly match", 14 | amountSList: [1e17, 300e18], 15 | amountBList: [300e18, 1e17], 16 | buyNoMoreThanAmountBList: [false, false], 17 | feeSelections: [0, 0], 18 | orderFilledOrCancelledAmountList: [5e16, 0], 19 | salt: 11, 20 | verbose: false, 21 | }, 22 | { 23 | description: "ring with 2 orders - no margin split", 24 | amountSList: [1000e18, 100e18], 25 | amountBList: [100e18, 1000e18], 26 | lrcFeeAmountList: [10e18, 5e18], 27 | marginSplitPercentageList: [0, 0], 28 | buyNoMoreThanAmountBList: [false, false], 29 | feeSelections: [0, 0], 30 | salt: 20, 31 | }, 32 | { 33 | description: "ring with 2 orders - same order owner", 34 | amountSList: [1000e18, 100e18], 35 | amountBList: [100e18, 1000e18], 36 | lrcFeeAmountList: [2e18, 6e18], 37 | orderOwners: ["##sameOwners##"], 38 | marginSplitPercentageList: [0, 0], 39 | buyNoMoreThanAmountBList: [false, false], 40 | feeSelections: [0, 0], 41 | salt: 30, 42 | verbose: false, 43 | }, 44 | { 45 | description: "ring with 2 orders - asymmetric orders & margin-split", 46 | amountSList: [1000e18, 100e18], 47 | amountBList: [50e18, 450e18], 48 | lrcFeeAmountList: [0, 0], 49 | marginSplitPercentageList: [100, 45], 50 | buyNoMoreThanAmountBList: [false, false], 51 | feeSelections: [1, 1], 52 | salt: 40, 53 | verbose: false, 54 | }, 55 | { 56 | description: "ring with 2 orders - lrcFee & margin-split", 57 | amountSList: [1000e18, 50e18], 58 | amountBList: [100e18, 450e18], 59 | lrcFeeAmountList: [2e18, 0], 60 | marginSplitPercentageList: [0, 45], 61 | buyNoMoreThanAmountBList: [true, false], 62 | feeSelections: [0, 1], 63 | salt: 50, 64 | }, 65 | { 66 | description: "ring with 2 orders - lrcFee & margin-split", 67 | amountSList: [1000e18, 50e18], 68 | amountBList: [100e18, 450e18], 69 | lrcFeeAmountList: [0, 5e17], 70 | marginSplitPercentageList: [65, 45], 71 | buyNoMoreThanAmountBList: [true, false], 72 | feeSelections: [1, 0], 73 | salt: 60, 74 | }, 75 | { 76 | description: "ring with 3 orders ", 77 | amountSList: [80000e18, 234e18, 6780], 78 | amountBList: [12345e18, 543e18, 18100], 79 | lrcFeeAmountList: [0, 6e18, 0], 80 | marginSplitPercentageList: [55, 0, 60], 81 | buyNoMoreThanAmountBList: [true, false, false], 82 | feeSelections: [1, 0, 1], 83 | salt: 70, 84 | verbose: false, 85 | }, 86 | { 87 | description: "ring with 3 orders - insufficient lrc balance for fee", 88 | amountSList: [80000e18, 234e18, 6780e18], 89 | amountBList: [12345e18, 543e18, 18100e18], 90 | lrcFeeAmountList: [0, 6e18, 0], 91 | marginSplitPercentageList: [55, 0, 60], 92 | buyNoMoreThanAmountBList: [true, false, false], 93 | feeSelections: [1, 0, 1], 94 | spendableLrcFeeAmountList: [0, 5e18, 0, 0], 95 | salt: 80, 96 | verbose: false, 97 | }, 98 | { 99 | description: "ring with 3 orders - insufficient lrc balance for fee", 100 | amountSList: [1000e18, 2000e18, 20e18], 101 | amountBList: [8000e18, 10e18, 450e18], 102 | lrcFeeAmountList: [10e18, 6e18, 1e18], 103 | marginSplitPercentageList: [55, 0, 60], 104 | buyNoMoreThanAmountBList: [false, true, true], 105 | feeSelections: [0, 1, 0], 106 | // spendableAmountSList: [10000e18, 100e18, 10000e18], 107 | spendableLrcFeeAmountList: [0, 6e18, 0, 0], 108 | salt: 90, 109 | verbose: false, 110 | }, 111 | { 112 | description: "ring with 3 orders - can pay lrc fee when receiving lrc", 113 | amountSList: [1000e18, 2000e18, 20e18], 114 | amountBList: [8000e18, 10e18, 450e18], 115 | lrcFeeAmountList: [10e18, 6e18, 1e18], 116 | marginSplitPercentageList: [55, 0, 60], 117 | buyNoMoreThanAmountBList: [false, true, false], 118 | feeSelections: [0, 0, 1], 119 | spendableLrcFeeAmountList: [0, 6e18, 0, 0], 120 | salt: 100, 121 | verbose: false, 122 | }, 123 | { 124 | description: "ring with 3 orders - choose margin split(100%) when spendable lrc is 0", 125 | amountSList: [1000e18, 2000e18, 20e18], 126 | amountBList: [8000e18, 10e18, 450e18], 127 | lrcFeeAmountList: [10e18, 6e18, 1e18], 128 | marginSplitPercentageList: [55, 0, 60], 129 | buyNoMoreThanAmountBList: [false, true, false], 130 | feeSelections: [0, 0, 0], 131 | spendableLrcFeeAmountList: [0, 6e18, 0, 0], 132 | salt: 110, 133 | verbose: false, 134 | }, 135 | 136 | ]; 137 | -------------------------------------------------------------------------------- /contracts/TransferableMultsigImpl.sol: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright 2017 Loopring Project Ltd (Loopring Foundation). 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | pragma solidity 0.4.21; 18 | 19 | import "./TransferableMultsig.sol"; 20 | 21 | 22 | /// @title An Implementation of TransferableMultsig。 23 | /// @author Daniel Wang - . 24 | contract TransferableMultsigImpl is TransferableMultsig { 25 | 26 | uint public nonce; // (only) mutable state 27 | uint public threshold; // immutable state 28 | mapping (address => bool) ownerMap; // immutable state 29 | address[] public owners; // immutable state 30 | 31 | function TransferableMultsig( 32 | uint _threshold, 33 | address[] _owners 34 | ) 35 | public 36 | { 37 | updateOwners(_threshold, _owners); 38 | } 39 | 40 | // default function does nothing. 41 | function () 42 | payable 43 | public 44 | { 45 | } 46 | 47 | function execute( 48 | uint8[] sigV, 49 | bytes32[] sigR, 50 | bytes32[] sigS, 51 | address destination, 52 | uint value, 53 | bytes data 54 | ) 55 | external 56 | { 57 | // Follows ERC191 signature scheme: 58 | // https://github.com/ethereum/EIPs/issues/191 59 | bytes32 txHash = keccak256( 60 | byte(0x19), 61 | byte(0), 62 | this, 63 | nonce++, 64 | destination, 65 | value, 66 | data 67 | ); 68 | 69 | verifySignatures( 70 | sigV, 71 | sigR, 72 | sigS, 73 | txHash 74 | ); 75 | 76 | require( 77 | destination.call.value(value)(data) 78 | ); 79 | } 80 | 81 | function transferOwnership( 82 | uint8[] sigV, 83 | bytes32[] sigR, 84 | bytes32[] sigS, 85 | uint _threshold, 86 | address[] _owners 87 | ) 88 | external 89 | { 90 | // Follows ERC191 signature scheme: 91 | // https://github.com/ethereum/EIPs/issues/191 92 | bytes32 txHash = keccak256( 93 | byte(0x19), 94 | byte(0), 95 | this, 96 | nonce++, 97 | _threshold, 98 | _owners 99 | ); 100 | 101 | verifySignatures( 102 | sigV, 103 | sigR, 104 | sigS, 105 | txHash 106 | ); 107 | updateOwners(_threshold, _owners); 108 | } 109 | 110 | function verifySignatures( 111 | uint8[] sigV, 112 | bytes32[] sigR, 113 | bytes32[] sigS, 114 | bytes32 txHash 115 | ) 116 | view 117 | internal 118 | { 119 | uint _threshold = threshold; 120 | require(_threshold == sigR.length); 121 | require(_threshold == sigS.length); 122 | require(_threshold == sigV.length); 123 | 124 | address lastAddr = 0x0; // cannot have 0x0 as an owner 125 | for (uint i = 0; i < threshold; i++) { 126 | address recovered = ecrecover( 127 | txHash, 128 | sigV[i], 129 | sigR[i], 130 | sigS[i] 131 | ); 132 | 133 | require(recovered > lastAddr && ownerMap[recovered]); 134 | lastAddr = recovered; 135 | } 136 | } 137 | 138 | function updateOwners( 139 | uint _threshold, 140 | address[] _owners 141 | ) 142 | internal 143 | { 144 | require(_owners.length <= 10); 145 | require(_threshold <= _owners.length); 146 | require(_threshold != 0); 147 | 148 | // remove all current owners from ownerMap. 149 | address[] memory currentOwners = owners; 150 | for (uint i = 0; i < currentOwners.length; i++) { 151 | ownerMap[currentOwners[i]] = false; 152 | } 153 | 154 | address lastAddr = 0x0; 155 | for (i = 0; i < _owners.length; i++) { 156 | address owner = _owners[i]; 157 | require(owner > lastAddr); 158 | 159 | ownerMap[owner] = true; 160 | lastAddr = owner; 161 | } 162 | owners = _owners; 163 | threshold = _threshold; 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /util/ring_factory.ts: -------------------------------------------------------------------------------- 1 | import { BigNumber } from "bignumber.js"; 2 | import BN = require("bn.js"); 3 | import { LoopringSubmitParams, OrderParams, RingInfo } from "../util/types"; 4 | import { Bitstream } from "./bitstream"; 5 | import { Order } from "./order"; 6 | import { Ring } from "./ring"; 7 | 8 | export class RingFactory { 9 | public delegateContractAddr: string; 10 | public currBlockTimeStamp: number; 11 | public authAddress: string; 12 | public walletAddr: string; 13 | 14 | constructor(delegateContractAddr: string, 15 | authAddress: string, 16 | currBlockTimeStamp: number) { 17 | this.delegateContractAddr = delegateContractAddr; 18 | this.authAddress = authAddress; 19 | this.currBlockTimeStamp = currBlockTimeStamp; 20 | } 21 | 22 | public async generateRing(ringInfo: RingInfo) { 23 | const ringSize = ringInfo.amountSList.length; 24 | const salt = ringInfo.salt ? ringInfo.salt : 0; 25 | 26 | const orders: Order[] = []; 27 | for (let i = 0; i < ringSize; i ++) { 28 | const nextIndex = (i + 1) % ringSize; 29 | 30 | const orderParam: OrderParams = { 31 | delegateContract: this.delegateContractAddr, 32 | tokenS: ringInfo.tokenAddressList[i], 33 | tokenB: ringInfo.tokenAddressList[nextIndex], 34 | amountS: new BigNumber(ringInfo.amountSList[i]), 35 | amountB: new BigNumber(ringInfo.amountBList[i]), 36 | validSince: new BigNumber(this.currBlockTimeStamp - 60), 37 | validUntil: new BigNumber(this.currBlockTimeStamp + 3600 + salt), 38 | lrcFee: new BigNumber(ringInfo.lrcFeeAmountList[i]), 39 | buyNoMoreThanAmountB: ringInfo.buyNoMoreThanAmountBList[i], 40 | marginSplitPercentage: ringInfo.marginSplitPercentageList[i], 41 | authAddr: this.authAddress, 42 | walletAddr: this.walletAddr, 43 | }; 44 | 45 | const order = new Order(ringInfo.orderOwners[i], orderParam); 46 | await order.signAsync(); 47 | orders.push(order); 48 | } 49 | 50 | const ring = new Ring(ringInfo.miner, orders, ringInfo.feeSelections); 51 | await ring.signAsync(); 52 | 53 | return ring; 54 | } 55 | 56 | public ringToSubmitableParams(ring: Ring) { 57 | const ringSize = ring.orders.length; 58 | ring.caculateAndSetRateAmount(); 59 | 60 | const bitstream = new Bitstream(); 61 | 62 | bitstream.addNumber(ringSize, 1); 63 | bitstream.addAddress(ring.owner); 64 | bitstream.addNumber(this.feeSelectionListToNumber(ring.feeSelections), 2); 65 | 66 | for (let i = 0; i < ringSize; i++) { 67 | const order = ring.orders[i]; 68 | 69 | let authAddr = order.params.authAddr; 70 | let walletAddr = order.params.walletAddr; 71 | let ringAuthR = ring.authR[i]; 72 | let ringAuthS = ring.authS[i]; 73 | let ringAuthV = ring.authV[i]; 74 | if (i > 0) { 75 | const previousOrder = ring.orders[i - 1]; 76 | 77 | // Do simple XOR compression using values of the previous order 78 | authAddr = this.xor(previousOrder.params.authAddr, order.params.authAddr, 20); 79 | walletAddr = this.xor(previousOrder.params.walletAddr, order.params.walletAddr, 20); 80 | ringAuthR = this.xor(ring.authR[i - 1], ring.authR[i], 32); 81 | ringAuthS = this.xor(ring.authS[i - 1], ring.authS[i], 32); 82 | ringAuthV = ring.authV[i - 1] ^ ring.authV[i]; 83 | } 84 | 85 | bitstream.addAddress(order.owner, 32); 86 | bitstream.addAddress(order.params.tokenS, 32); 87 | bitstream.addAddress(walletAddr, 32); 88 | bitstream.addAddress(authAddr, 32); 89 | bitstream.addBigNumber(order.params.amountS); 90 | bitstream.addBigNumber(order.params.amountB); 91 | bitstream.addBigNumber(order.params.lrcFee); 92 | bitstream.addBigNumber(new BigNumber(order.params.rateAmountS.toPrecision(15)), 32); 93 | bitstream.addHex(order.params.r); 94 | bitstream.addHex(order.params.s); 95 | bitstream.addHex(ringAuthR); 96 | bitstream.addHex(ringAuthS); 97 | bitstream.addBigNumber(order.params.validSince, 4); 98 | bitstream.addBigNumber(order.params.validUntil.minus(order.params.validSince), 4); 99 | bitstream.addNumber(order.params.v, 1); 100 | bitstream.addNumber(ringAuthV, 1); 101 | bitstream.addNumber(((order.params.buyNoMoreThanAmountB ? 1 : 0) << 7) + order.params.marginSplitPercentage, 1); 102 | } 103 | 104 | const submitParams = { 105 | data: bitstream.getData(), 106 | }; 107 | 108 | return submitParams; 109 | } 110 | 111 | public feeSelectionListToNumber(feeSelections: number[]) { 112 | let res = 0; 113 | for (let i = 0; i < feeSelections.length; i ++) { 114 | res += feeSelections[i] << i; 115 | } 116 | 117 | return res; 118 | } 119 | 120 | private xor(s1: string, s2: string, numBytes: number) { 121 | const x1 = new BN(s1.slice(2), 16); 122 | const x2 = new BN(s2.slice(2), 16); 123 | const result = x1.xor(x2); 124 | return "0x" + result.toString(16, numBytes * 2); 125 | } 126 | 127 | } 128 | -------------------------------------------------------------------------------- /ABI/version151/TokenTransferDelegateImpl.abi: -------------------------------------------------------------------------------- 1 | [{"constant":true,"inputs":[{"name":"owners","type":"address[]"},{"name":"tradingPairs","type":"bytes20[]"},{"name":"validSince","type":"uint256[]"}],"name":"checkCutoffsBatch","outputs":[],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"resume","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"max","type":"uint256"}],"name":"getLatestAuthorizedAddresses","outputs":[{"name":"addresses","type":"address[]"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"orderHash","type":"bytes32"},{"name":"cancelOrFillAmount","type":"uint256"}],"name":"addCancelledOrFilled","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"bytes32"}],"name":"cancelled","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"token","type":"address"},{"name":"from","type":"address"},{"name":"to","type":"address"},{"name":"value","type":"uint256"}],"name":"transferToken","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"kill","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"lrcTokenAddress","type":"address"},{"name":"miner","type":"address"},{"name":"feeRecipient","type":"address"},{"name":"walletSplitPercentage","type":"uint8"},{"name":"batch","type":"bytes32[]"}],"name":"batchTransferToken","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"addr","type":"address"}],"name":"authorizeAddress","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"tokenPair","type":"bytes20"},{"name":"t","type":"uint256"}],"name":"setTradingPairCutoffs","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"claimOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"bytes32"}],"name":"cancelledOrFilled","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"suspended","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"batch","type":"bytes32[]"}],"name":"batchAddCancelledOrFilled","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"},{"name":"","type":"bytes20"}],"name":"tradingPairCutoffs","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"orderHash","type":"bytes32"},{"name":"cancelAmount","type":"uint256"}],"name":"addCancelled","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"addressInfos","outputs":[{"name":"previous","type":"address"},{"name":"index","type":"uint32"},{"name":"authorized","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"addr","type":"address"}],"name":"isAddressAuthorized","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"cutoffs","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"pendingOwner","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[],"name":"suspend","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"addr","type":"address"}],"name":"deauthorizeAddress","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"t","type":"uint256"}],"name":"setCutoffs","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":true,"name":"previousOwner","type":"address"},{"indexed":true,"name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"addr","type":"address"},{"indexed":false,"name":"number","type":"uint32"}],"name":"AddressAuthorized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"addr","type":"address"},{"indexed":false,"name":"number","type":"uint32"}],"name":"AddressDeauthorized","type":"event"}] -------------------------------------------------------------------------------- /contracts/TokenRegistryImpl.sol: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright 2017 Loopring Project Ltd (Loopring Foundation). 4 | 5 | Licensed under the Apache License, Version 2.0 (the "License"); 6 | you may not use this file except in compliance with the License. 7 | You may obtain a copy of the License at 8 | 9 | http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | pragma solidity 0.4.21; 18 | 19 | import "./lib/AddressUtil.sol"; 20 | import "./lib/Claimable.sol"; 21 | import "./TokenRegistry.sol"; 22 | 23 | 24 | /// @title An Implementation of TokenRegistry. 25 | /// @author Kongliang Zhong - , 26 | /// @author Daniel Wang - . 27 | contract TokenRegistryImpl is TokenRegistry, Claimable { 28 | using AddressUtil for address; 29 | 30 | address[] public addresses; 31 | mapping (address => TokenInfo) public addressMap; 32 | mapping (string => address) symbolMap; 33 | 34 | struct TokenInfo { 35 | uint pos; // 0 mens unregistered; if > 0, pos + 1 is the 36 | // token's position in `addresses`. 37 | string symbol; // Symbol of the token 38 | } 39 | 40 | /// @dev Disable default function. 41 | function () 42 | payable 43 | public 44 | { 45 | revert(); 46 | } 47 | 48 | function registerToken( 49 | address addr, 50 | string symbol 51 | ) 52 | external 53 | onlyOwner 54 | { 55 | registerTokenInternal(addr, symbol); 56 | } 57 | 58 | function unregisterToken( 59 | address addr, 60 | string symbol 61 | ) 62 | external 63 | onlyOwner 64 | { 65 | require(addr != 0x0); 66 | require(symbolMap[symbol] == addr); 67 | delete symbolMap[symbol]; 68 | 69 | uint pos = addressMap[addr].pos; 70 | require(pos != 0); 71 | delete addressMap[addr]; 72 | 73 | // We will replace the token we need to unregister with the last token 74 | // Only the pos of the last token will need to be updated 75 | address lastToken = addresses[addresses.length - 1]; 76 | 77 | // Don't do anything if the last token is the one we want to delete 78 | if (addr != lastToken) { 79 | // Swap with the last token and update the pos 80 | addresses[pos - 1] = lastToken; 81 | addressMap[lastToken].pos = pos; 82 | } 83 | addresses.length--; 84 | 85 | emit TokenUnregistered(addr, symbol); 86 | } 87 | 88 | function areAllTokensRegistered( 89 | address[] addressList 90 | ) 91 | external 92 | view 93 | returns (bool) 94 | { 95 | for (uint i = 0; i < addressList.length; i++) { 96 | if (addressMap[addressList[i]].pos == 0) { 97 | return false; 98 | } 99 | } 100 | return true; 101 | } 102 | 103 | function getAddressBySymbol( 104 | string symbol 105 | ) 106 | external 107 | view 108 | returns (address) 109 | { 110 | return symbolMap[symbol]; 111 | } 112 | 113 | function isTokenRegisteredBySymbol( 114 | string symbol 115 | ) 116 | public 117 | view 118 | returns (bool) 119 | { 120 | return symbolMap[symbol] != 0x0; 121 | } 122 | 123 | function isTokenRegistered( 124 | address addr 125 | ) 126 | public 127 | view 128 | returns (bool) 129 | { 130 | return addressMap[addr].pos != 0; 131 | } 132 | 133 | function getTokens( 134 | uint start, 135 | uint count 136 | ) 137 | public 138 | view 139 | returns (address[] addressList) 140 | { 141 | uint num = addresses.length; 142 | 143 | if (start >= num) { 144 | return; 145 | } 146 | 147 | uint end = start + count; 148 | if (end > num) { 149 | end = num; 150 | } 151 | 152 | addressList = new address[](end - start); 153 | for (uint i = start; i < end; i++) { 154 | addressList[i - start] = addresses[i]; 155 | } 156 | } 157 | 158 | function registerTokenInternal( 159 | address addr, 160 | string symbol 161 | ) 162 | internal 163 | { 164 | require(0x0 != addr); 165 | require(bytes(symbol).length > 0); 166 | require(0x0 == symbolMap[symbol]); 167 | require(0 == addressMap[addr].pos); 168 | 169 | addresses.push(addr); 170 | symbolMap[symbol] = addr; 171 | addressMap[addr] = TokenInfo(addresses.length, symbol); 172 | 173 | emit TokenRegistered(addr, symbol); 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /ABI/version14/LoopringProtocolImpl.abi: -------------------------------------------------------------------------------- 1 | [{"constant":false,"inputs":[{"name":"addressList","type":"address[3][]"},{"name":"uintArgsList","type":"uint256[7][]"},{"name":"uint8ArgsList","type":"uint8[1][]"},{"name":"buyNoMoreThanAmountBList","type":"bool[]"},{"name":"vList","type":"uint8[]"},{"name":"rList","type":"bytes32[]"},{"name":"sList","type":"bytes32[]"},{"name":"minerId","type":"uint256"},{"name":"feeSelections","type":"uint16"}],"name":"submitRing","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"ENTERED_MASK","outputs":[{"name":"","type":"uint64"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"nameRegistryAddress","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"bytes32"}],"name":"cancelled","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"MARGIN_SPLIT_PERCENTAGE_BASE","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"ringIndex","outputs":[{"name":"","type":"uint64"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"RATE_RATIO_SCALE","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"lrcTokenAddress","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"bytes32"}],"name":"cancelledOrFilled","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"tokenRegistryAddress","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"delegateAddress","outputs":[{"name":"","type":"address"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"orderOwner","type":"address"},{"name":"token1","type":"address"},{"name":"token2","type":"address"}],"name":"getTradingPairCutoffs","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"token1","type":"address"},{"name":"token2","type":"address"},{"name":"cutoff","type":"uint256"}],"name":"cancelAllOrdersByTradingPair","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"addresses","type":"address[4]"},{"name":"orderValues","type":"uint256[7]"},{"name":"buyNoMoreThanAmountB","type":"bool"},{"name":"marginSplitPercentage","type":"uint8"},{"name":"v","type":"uint8"},{"name":"r","type":"bytes32"},{"name":"s","type":"bytes32"}],"name":"cancelOrder","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"MAX_RING_SIZE","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"},{"name":"","type":"bytes20"}],"name":"tradingPairCutoffs","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"cutoff","type":"uint256"}],"name":"cancelAllOrders","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"cutoffs","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"rateRatioCVSThreshold","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"walletSplitPercentage","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[{"name":"_lrcTokenAddress","type":"address"},{"name":"_tokenRegistryAddress","type":"address"},{"name":"_delegateAddress","type":"address"},{"name":"_nameRegistryAddress","type":"address"},{"name":"_rateRatioCVSThreshold","type":"uint256"},{"name":"_walletSplitPercentage","type":"uint8"}],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":false,"name":"_ringIndex","type":"uint256"},{"indexed":true,"name":"_ringHash","type":"bytes32"},{"indexed":false,"name":"_miner","type":"address"},{"indexed":false,"name":"_feeRecipient","type":"address"},{"indexed":false,"name":"_orderHashList","type":"bytes32[]"},{"indexed":false,"name":"_amountsList","type":"uint256[6][]"}],"name":"RingMined","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_orderHash","type":"bytes32"},{"indexed":false,"name":"_amountCancelled","type":"uint256"}],"name":"OrderCancelled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_address","type":"address"},{"indexed":false,"name":"_cutoff","type":"uint256"}],"name":"AllOrdersCancelled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"_address","type":"address"},{"indexed":false,"name":"_token1","type":"address"},{"indexed":false,"name":"_token2","type":"address"},{"indexed":false,"name":"_cutoff","type":"uint256"}],"name":"OrdersCancelled","type":"event"}] -------------------------------------------------------------------------------- /util/ring.ts: -------------------------------------------------------------------------------- 1 | import { BigNumber } from "bignumber.js"; 2 | import promisify = require("es6-promisify"); 3 | import ABI = require("ethereumjs-abi"); 4 | import ethUtil = require("ethereumjs-util"); 5 | import * as _ from "lodash"; 6 | import Web3 = require("web3"); 7 | import { Order } from "./order"; 8 | 9 | export class Ring { 10 | public owner: string; 11 | public orders: Order[]; 12 | 13 | public feeSelections: number[]; 14 | 15 | public v: number; 16 | public r: string; 17 | public s: string; 18 | 19 | public authV: number[] = []; 20 | public authR: string[] = []; 21 | public authS: string[] = []; 22 | 23 | private web3Instance: Web3; 24 | 25 | private rate: number; 26 | 27 | constructor(owner: string, 28 | orders: Order[], 29 | feeSelections: number[]) { 30 | this.owner = owner; 31 | this.orders = orders; 32 | this.feeSelections = feeSelections; 33 | 34 | try { 35 | if (web3) { 36 | this.web3Instance = web3; 37 | } 38 | } catch (err) { 39 | // ignore. 40 | } 41 | } 42 | 43 | public isValidSignature() { 44 | if (_.isUndefined(this.v) || _.isUndefined(this.r) || _.isUndefined(this.s)) { 45 | throw new Error("Cannot call isValidSignature on unsigned order"); 46 | } 47 | const ringHash = this.getRingHash(); 48 | const msgHash = ethUtil.hashPersonalMessage(ringHash); 49 | try { 50 | const pubKey = ethUtil.ecrecover(msgHash, this.v, 51 | ethUtil.toBuffer(this.r), 52 | ethUtil.toBuffer(this.s)); 53 | const recoveredAddress = ethUtil.bufferToHex(ethUtil.pubToAddress(pubKey)); 54 | return recoveredAddress === this.owner; 55 | } catch (err) { 56 | return false; 57 | } 58 | } 59 | 60 | public async signAsync() { 61 | const ringHash = this.getRingHash(); 62 | // console.log("ring hash: ", ethUtil.bufferToHex(ringHash)); 63 | 64 | const signer = promisify(this.web3Instance.eth.sign); 65 | const signature = await signer(this.owner, ethUtil.bufferToHex(ringHash)); 66 | const { v, r, s } = ethUtil.fromRpcSig(signature); 67 | this.v = v; 68 | this.r = ethUtil.bufferToHex(r); 69 | this.s = ethUtil.bufferToHex(s); 70 | 71 | for (const order of this.orders) { 72 | const authSig = await signer(order.params.authAddr, ethUtil.bufferToHex(ringHash)); 73 | const sigRes = ethUtil.fromRpcSig(authSig); 74 | 75 | this.authV.push(sigRes.v); 76 | this.authR.push(ethUtil.bufferToHex(sigRes.r)); 77 | this.authS.push(ethUtil.bufferToHex(sigRes.s)); 78 | } 79 | } 80 | 81 | public getRingHash() { 82 | const orderHashList: string[] = []; 83 | 84 | for (const order of this.orders) { 85 | const orderHash = order.getOrderHash(); 86 | orderHashList.push(ethUtil.bufferToHex(orderHash)); 87 | } 88 | 89 | const ringHash = ABI.soliditySHA3( 90 | [ 91 | "string", 92 | "address", 93 | "uint16", 94 | ], 95 | [ 96 | this.xorReduceStr(orderHashList), 97 | this.owner, 98 | this.feeSelectionListToNumber(this.feeSelections), 99 | ]); 100 | 101 | return ringHash; 102 | } 103 | 104 | public feeSelectionListToNumber(feeSelections: number[]) { 105 | let res: number = 0; 106 | for (let i = 0; i < feeSelections.length; i ++) { 107 | res += feeSelections[i] << i; 108 | } 109 | 110 | return res; 111 | } 112 | 113 | public getRingHashHex() { 114 | const ringHash = this.getRingHash(); 115 | const ringHashHex = ethUtil.bufferToHex(ringHash); 116 | return ringHashHex; 117 | } 118 | 119 | public caculateAndSetRateAmount() { 120 | const size = this.orders.length; 121 | this.rate = 1; 122 | for (const order of this.orders) { 123 | this.rate = this.rate * order.params.amountS.toNumber() / order.params.amountB.toNumber(); 124 | } 125 | this.rate = Math.pow(this.rate, 1 / size); 126 | 127 | for (const order of this.orders) { 128 | order.params.rateAmountB = order.params.amountB.toNumber(); 129 | order.params.rateAmountS = Math.floor(order.params.amountS.toNumber() / this.rate); 130 | } 131 | } 132 | 133 | public printToConsole() { 134 | console.log("-".repeat(80)); 135 | console.log("ring miner:", this.owner); 136 | console.log("rate:", this.rate); 137 | for (const order of this.orders) { 138 | console.log("-".repeat(80)); 139 | console.log("order owner:", order.owner); 140 | console.log("order params:", order.params); 141 | } 142 | 143 | console.log("fee Selections:", this.feeSelections); 144 | console.log("-".repeat(80)); 145 | } 146 | 147 | private xorReduce(numberArr: number[]) { 148 | const n0 = numberArr[0]; 149 | const tail = numberArr.slice(1); 150 | const intRes = tail.reduce((n1: number, n2: number) => n1 ^ n2, n0); 151 | return intRes; 152 | } 153 | 154 | private xorReduceStr(strArr: string[]) { 155 | const s0 = strArr[0]; 156 | const tail = strArr.slice(1); 157 | const strXor = (s1: string, s2: string) => { 158 | const buf1 = Buffer.from(s1.slice(2), "hex"); 159 | const buf2 = Buffer.from(s2.slice(2), "hex"); 160 | const res = Buffer.alloc(32); 161 | for (let i = 0; i < 32; i++) { 162 | res[i] = buf1[i] ^ buf2[i]; 163 | } 164 | 165 | const strRes = ethUtil.bufferToHex(res); 166 | return strRes; 167 | }; 168 | 169 | const reduceRes = tail.reduce((a, b) => strXor(a, b), s0); 170 | return Buffer.from(reduceRes.slice(2), "hex"); 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /bounty/bug_submissions.md: -------------------------------------------------------------------------------- 1 | # Bug Bounty Submissions 2 | 3 | We'll be collecting bug bounty submissions and their responses here. Please be sure to take a look before making a submission. Thank you! 4 | 5 | ## #01 [Rejected] 6 | 7 | - From: Akash Bansal 8 | - Time: 01:57 03/11/2017 Beijing Time 9 | - PR: https://github.com/Loopring/protocol/pull/41 10 | - Resoluton: Will not implemented. See https://blog.coinfabrik.com/smart-contract-short-address-attack-mitigation-failure/ 11 | 12 | ## #02 [Rejected] LOW 13 | 14 | - From: Akash Bansal 15 | - Time: 22:30 04/11/2017 Beijing Time 16 | - PR: https://github.com/Loopring/protocol/pull/50 17 | 18 | Description : According to ERC-20 token standards, 19 | Transfers of 0 values MUST be treated as normal transfers and fire the Transfer event. 20 | (https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20-token-standard.md) 21 | But in your method "transferToken" TokenTransferDelegate you have put a check against it. Either it should be allowed or a note should be added. 22 | 23 | ### Remark: 24 | This really isn't a bug. In our protocol, if the value to be transfered is 0, we don't call the transfer method at all. 25 | 26 | ## #03 [Merged] LOW 27 | 28 | - From: Paul 29 | - Time: 23:41 04/11/2017 Beijing Time 30 | - PR: Kongliang's PR 31 | 32 | Hello, 33 | 34 | Here are my initial suggestions from a static analysis of your smart contracts code. Please note that I am just an enthusiast of Solidity and Bug Bounty campaigns, so you shouldn't take my suggestions for granted. Please review it carefully and only if you agree with it, implement the necessary changes. If you are satisfied with it, I can continue the testing to provide you more feedback. 35 | 36 | There are some usages of "var" which infers the variable type from the right hand of the assignment. I recommend avoiding this feature because in some cases it might infer a smaller integer type than the developer might think. It is best to be explicit regarding types; 37 | Consider avoiding the usage of "for" loop as iterating through the array of unknown size might consume all the gas provided (run out of gas). Especially, try to avoid loops in your functions or actions that modify large areas of storage (this includes clearing or copying arrays in storage). For example, here: 38 | 39 | for (uint i = 0; i < tokens.length; i++) { 40 | if (tokens[i] == _token) { 41 | tokens[i] == tokens[tokens.length - 1]; 42 | tokens.length --; 43 | break; 44 | } 45 | } 46 | 47 | if the tokens array becomes very large, the transaction to unregisterTokenwill never succeed. 48 | assert(ERC20(token).transferFrom(from, to, value)); 49 | Please make sure that "assert" is used intentionally, as if it fails, all the gas is consumed. Consider replacing "assert" with "require". Require() is used for checking that the input of the function is well formatted, while assert() function is used to validate contract state after making changes . Read more: https://media.consensys.net/when-to-use-revert-assert-and-require-in-solidity-61fb2c0e5a 50 | That’s all for now. Please, let me know if you find any of these relevant. Good luck with your project! 51 | Regards, 52 | 53 | ### Remark: 54 | The unregistraton function will be called very rarely; "not use assert" is a known optimization for the team. 55 | 56 | ## #04 [Rejected] 57 | 58 | - From: Brecht Devos 59 | - Time: 09:17 05/11/2017 Beijing Time 60 | - PR: https://github.com/Loopring/protocol/pull/54 61 | - Resolution: We consider the current math to be safe. 62 | 63 | Hi, 64 | 65 | So it seems like every calculation you’re doing that is critical (i.e. involves money in some way) is done using SafeMath functions, which is great. Though there are a few places that this isn’t the case: 66 | In settleRing there’s a (state.fillAmountS - prev.splitB) and a (prev.splitB + state.splitS) to calculate transfer balances. Seems like this is very critical and should be using SafeMath functions, even though you could argue all code before should make this unnecessary, it still seems like the safe thing to do. You never know how the code might be modified in the future. 67 | In calculateRingFees there’s (minerLrcSpendable += lrcSpendable). Again, probably unnecessary now, but the calculation involves real money so probably smart to take all precautions. 68 | 69 | 70 | Brecht Devos 71 | 72 | ## #05 [MERGED] - LOW 73 | 74 | - From: jonasshen 75 | - Resolution: https://github.com/Loopring/protocol/issues/70 76 | 77 | ### Remark: 78 | This is more of a incomplete feature rather than a bug. But we decided to reward this report. 79 | 80 | 81 | ## #06 [MERGED] - LOW 82 | 83 | - From: p-lb 84 | 85 | Hi Team 86 | 87 | In the unregister function, we have below check on whether the token/symbol is already registerred or not. 88 | 89 | require(tokenSymbolMap[_symbol] == _token); 90 | 91 | Only for registerred symbol/token, the check will give one pass and then the unregister codes will be executed. 92 | However, for the case when `_token` is gave one value of address(0), 93 | the check will also give one pass even if the symbol is not registerred. 94 | We'd better add the check `require(_token != address(0));`. This will make our contract stronger. 95 | 96 | ``` 97 | ----------------------------------------------------------- 98 | function unregisterToken(address _token, string _symbol) 99 | external 100 | onlyOwner 101 | { 102 | require(tokenSymbolMap[_symbol] == _token); 103 | delete tokenSymbolMap[_symbol]; 104 | delete tokenMap[_token]; 105 | for (uint i = 0; i < tokens.length; i++) { 106 | if (tokens[i] == _token) { 107 | tokens[i] = tokens[tokens.length - 1]; 108 | tokens.length --; 109 | break; 110 | } 111 | } 112 | } 113 | ----------------------------------------------------------- 114 | ``` 115 | 116 | ### Remark: 117 | The point is valid. But even without a fix, the unregisterToken still works but it will cost more gas. 118 | --------------------------------------------------------------------------------